views:

129

answers:

2

Hi there,

I'm creating a plugin and am having a hard time defining a before_save filter that calls an instance method I've just defined. Here's a quick sample:

module ValidatesAndFormatsPhones
  def self.included(base)
    base.send :extend, ClassMethods
  end

  module ClassMethods

    def validates_and_formats_phones(field_names = [:phone])
      send :include, InstanceMethods

      # the following variations on calls to :format_phone_fields fail

      before_save send(:format_phone_fields, field_names)

      before_save format_phone_fields(field_names)

      before_save lambda { send(:format_phone_fields, field_names) }

      # EACH OF THE ABOVE RETURNS 'undefined_method :format_phone_fields'
    end
  end

  module InstanceMethods

    def format_phone_fields(fields = [:phone], *args)
      do stuff...
    end

  end
end

ActiveRecord::Base.send :include, ValidatesAndFormatsPhones

I guess the question is, how do I change the context to the instance, instead of the class?

I'd prefer to call the instance method because the class shouldn't really have a method called 'format_phone_fields' but the instance should.

Thanks!

+1  A: 

as you are using callback macros, you can only pass a symbol for the method you want to run, passing arguments is not possible. the 'workaround' from the rails documentation is to use a 'method string' that gets evaluated in the right context:

before_save 'self.format_phone_fields(....)'

another possibility: store your field names as a class variable and access that one in your instance, then you can use before_save :format_phone_fields

roman
Cool, this works too. Thanks Roman
btelles
+2  A: 

Include your method at the right moment: when you're extending the base class:

module ValidatesAndFormatsPhones
  def self.included(base)
    base.send :extend, ClassMethods
    base.send :include, InstanceMethods
  end

  module ClassMethods
    def validates_and_formats_phones(field_names = [:phone])
      before_save {|r| r.format_phone_fields(field_names)}
    end
  end

  module InstanceMethods
    def format_phone_fields(fields = [:phone], *args)
      # do stuff...
    end
  end
end

ActiveRecord::Base.send :include, ValidatesAndFormatsPhones

I haven't run the code, but it should work. I've done similar things often enough.

François Beausoleil
Excellent. Thanks Francois...I really wish the American keyboard could do the squiggle under the C. Looks fun.
btelles