Validation problem with a Redmine hook
I am having a validation problem when writing a Redmine plugin.
I'm writing a hook for the issue model, and as a part of the hook method, i would like to invalidate the creation of the issue, by adding a custom error:
def controller_issues_new_before_save( context = { } )
context[:issue].errors.add(:due_date, "A custom error")
end
For testing purposes, I have written a patch that overwrites Issue.validate_on_create, but it seems that every time when entering validate_on_create errors.count is set to zero.
I need to stop the creation of the issue object , but only when an attribute is set into another model obje开发者_如何转开发ct.
I thought about writing this in the validate_on_create method, but then I would need to pass it the other object.
The first solution that I thought about would be to insert an additional field in the Issue model, and modify it inside the hook.
Something like :
def controller_issues_new_before_save( context = { } )
context[:issue].can_validate = false
end
def validate_on_create
unless can_validate
errors.add("error", "A custom error")
end
end
where Issue.can_validate is an addition to the Issue model
However, this does not seem the best approach here. Is there an easier way?
If you are wanting to validate data you should patch the models directly and not use hooks. Hooks are meant to be used to insert HTML onto a page or change the control flow of a controller. Using hooks also means that your code will only work for that one path through the application, so if someone creates an issue somewhere else then you code will not run.
To create a patch you just need to do two things:
- Create a module that has your code
- Make Redmine include that module in it's Issue class
I've done this exact thing in a plugin that adds a validation on an Issue to require that due dates are set in the future. The patch for it is rather simple so I'll include it here:
module RedmineRequireIssueDueDateInFuture
module Patches
module IssuePatch
def self.included(base)
base.class_eval do
unloadable
validate :due_date_in_future
protected
def due_date_in_future
return true if due_date.nil?
if due_date.to_time < Date.today.beginning_of_day
errors.add :due_date, :not_in_future
end
end
end
end
end
end
end
Inside of the class_eval is where you would put your own code, I'd recommend using a different name than validate_on_create. Otherwise you might have problems with other code if they want to use that method too.
The second part (including the module into Redmine) is rather easy. Just require the Issue class and use include to add it to the class.
# init.rb
require 'dispatcher'
Dispatcher.to_prepare :redmine_require_issue_due_date_in_future do
require_dependency 'issue'
Issue.send(:include, RedmineRequireIssueDueDateInFuture::Patches::IssuePatch)
end
You need to wrap this in the Dispatcher to keep things working in development mode. I've written about it on my blog.
Feel free to copy my plugin from github to make your changes, it's pretty simple. https://github.com/edavis10/redmine_require_issue_due_date_in_future
Since Redmine 2.0, you should replace the code in init.rb by this in Eric Davis response :
#init.rb
ActionDispatch::Callbacks.to_prepare do
require_dependency 'issue'
Issue.send(:include, RedmineRequireIssueDueDateInFuture::Patches::IssuePatch)
end
精彩评论