I'm trying to write a plugin which adds some new logic to the validate
method of an ActiveRecord model. However, I still want the user to be
able to define their own validate method in the model, and to call
that as well as my new validate method. What's happening now is if I
define a validate method and the user defines a validate method as
well, the validate method defined by the user will be called and the
method in my plugin will not be called.
I've tried to use alias_method to install my validate method in the
place of the previous validate method, while still allowing me to call
the original method as follows:
but when I place this after my validate method definition in my
plugin, I get the following error when starting my server:
undefined method `validate' for module `AttachmentHandler::InstanceMethods'
I need to instruct rails that I'm redefining the validate method from
ActiveRecord::Validations::ClassMethods but I don't know how to do
this.
My end goal is to be able to add some conditions that a model must
satisfy before being saved, and if it doesn't pass these conditions,
then I need to add errors to the object. If there's a better way to
do this than overriding validate, that would also be an option.. But
right now, I'm completely stuck and can't figure this out. Any help
would be greatly appreciated. Thanks,
My end goal is to be able to add some conditions that a model must
satisfy before being saved, and if it doesn't pass these conditions,
then I need to add errors to the object. If there's a better way to
do this than overriding validate, that would also be an option.. But
right now, I'm completely stuck and can't figure this out. Any help
would be greatly appreciated. Thanks,
How about adding a before_validation or after_validation callback.
if I use before_validation, the errors that I add are not carried
through to when the validate method is called. If I return false from
my before_validation method, the error messages added are available,
but the validate method is not called (I guess returning false in
before_validate halts the filter chain). If I use after_validation,
the errors are added to the object, but the object is still allowed to
be saved, even if I return false in after_validation.
So it doesn't look like I can use the before/after validation
callbacks (and also, are these callbacks chained? ie if I defined my
own before/after validation, and the user defines their own as well,
won't their definition override mine?)
if I use before_validation, the errors that I add are not carried
through to when the validate method is called. If I return false from
my before_validation method, the error messages added are available,
but the validate method is not called (I guess returning false in
before_validate halts the filter chain). If I use after_validation,
the errors are added to the object, but the object is still allowed to
be saved, even if I return false in after_validation.
Yes, you're right. Neither before_validation nor after_validation
will give you what you want.
So it doesn't look like I can use the before/after validation
callbacks (and also, are these callbacks chained? ie if I defined my
own before/after validation, and the user defines their own as well,
won't their definition override mine?)
No, callbacks are properly chained.
You can get your alias_method calls to work if you call them
on the user class after your module has been included, either in
the module "included" method or after the include statement
in your capability-adding method.
yeah, I noticed that works fine, but since this is a plugin, I wanted
to make this as invisible as possible.. Requiring the user to add the
alias_method_chain calls after they define their own validate method
is more obtrusive than I wanted. Since this is the ruby/rails world,
I figured there would have to be a better way, but unfortunately I
can't figure it out.
It's a bit of a catch-22.. I can't use alias_method in my plugin,
since the plugin is loaded before the entire User class is loaded,
which means that the validate method isn't available to be aliased..
I wish there was a way of telling rails to alias the method
regardless, under the assumption that the method will be available.
Or to figure out some other way to hook into the validation class.
Mike Garey wrote:
You can get your alias_method calls to work if you call them
on the user class after your module has been included, either in
the module "included" method or after the include statement
in your capability-adding method.
yeah, I noticed that works fine, but since this is a plugin, I wanted
to make this as invisible as possible.. Requiring the user to add the
alias_method_chain calls after they define their own validate method
is more obtrusive than I wanted. Since this is the ruby/rails world,
I figured there would have to be a better way, but unfortunately I
can't figure it out.
It's a bit of a catch-22.. I can't use alias_method in my plugin,
since the plugin is loaded before the entire User class is loaded,
which means that the validate method isn't available to be aliased..
I wish there was a way of telling rails to alias the method
regardless, under the assumption that the method will be available.
Or to figure out some other way to hook into the validation class.
By putting it in your capability-adding method i meant:
module ActsAsMike
module ClassMethods
...
end
module InstanceMethods
def new_validate
...
validate # Call AR::B validate or UserClass validate
end
...
end
end
class ActiveRecord::Base
def self.acts_as_mike
extend ActsAsMike::ClassMethods
include ActsAsMike::InstanceMethods
alias_method :orig_validate, :validate
alias_method :validate, :new_validate
end
end
thanks for the respone Mark, I seem to be getting closer, as I can now
override the default validate method and still call the original
validate method from within new_validate. The only problem is that
the user _must_ place the 'acts_as_validatable' class method after
their validate method, otherwise the alias_method call won't override
their method, and my new_validate method will not be called. Ideally,
I'd like to avoid having the restriction that the user must call
acts_as_validatable after they define their own validate method - is
there any way to get around this?
Here's the minimal code for the plugin I've got right now:
acts_as_validatable.rb
module ActsAsValidatable
module InstanceMethods
def new_validate
logger.warn("NEW VALIDATE CALLED")
orig_validate
end
end
end
# ActsAsValidatable
class ActiveRecord::Base
def self.acts_as_validatable
include ActsAsValidatable::InstanceMethods
alias_method :orig_validate, :validate
alias_method :validate, :new_validate
end
end
the following works perfectly (the logger shows both "NEW VALIDATE
CALLED" and then "ORIGINAL VALIDATE CALLED")
my_class.rb
class MyClass < ActiveRecord::Base
def validate
logger.warn("ORIGINAL VALIDATE CALLED")
end
acts_as_validatable
end
the following doesn't work (the only thing that gets logged is
"ORIGINAL VALIDATE CALLED"):
my_class.rb
class MyClass < ActiveRecord::Base
acts_as_validatable
def validate
logger.warn("ORIGINAL VALIDATE CALLED")
end
end