Chaining validate method in a plugin, completely stumped

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:

alias_method :orig_validate, :validate alias_method :validate, :new_validate

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,

Mike

Mike Garey wrote:

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?)

Thanks,

Mike

Mike Garey wrote:

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.

Thanks for the response Mark,

Mike

Mike Garey wrote:

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

Thanks again for the help,

Mike