开发者

Wrapping ActiveRecord methods with additional functionality

I want to enhance the ActiveRecord setters in Rails to ensure only valid values are saved. One such place where this is needed is phone numbers. A user may enter a phone number in a variety of formats such as,

(123) 456-7890
+1 123-456-7890

but I only want to store the digits and discard the rest when it goes to the database. The approach I am using right now is to override the setter methods using alias_method. Also, I am trying to put this into a module so any model class that contains phone number can include this module, and define the fields that should be cleaned up. The kind of interface I am hoping to use is,

# Person has a "phone" attribute to store phone numbers
class Person < ActiveRecord::Base
  # first include this module
  include PhoneSanitizer

  # then call the class method and tell it which 
  # fields need need to be sanitized
  sanitize_phone_field :phone
end

The only thing I'm doing inside my model classes i开发者_Python百科s to include the PhoneSanitizer module (which adds a class method - sanitize_phone_field in the Person class). That method is now responsible for overriding the setter phone= method. This is the part I haven't gotten to work.

module PhoneSanitizer

  module ClassMethods
    # wrap each of the passed-in fields with setters that
    # clean up the phone number value of non-digits.
    def sanitize_phone(*fields)
      fields.each do |field|
        new_method = "original_#{field}=".to_sym
        original_method = "#{field}=".to_sym
        alias_method new_method, original_method
        define_method(original_method) do |value|
          self.send(new_method, phone_to_number(value))
        end
      end
    end
  end

  def self.included(base)
    base.extend(ClassMethods)
  end

  def phone_to_number(number)
    number.gsub(/[^\d]/, '')
  end

end

When sanitize_phone is called, it throws an error saying :phone= is not defined for the Person class, and that makes sense. How would I go about aliasing the method for the instances of Person instead?


I think your error was not undefined method alias_method it was something different and you misinterpreted it(?)

The real problem is that the getter and setter methods on ActiveRecord are dynamic. The getter and setter methods (i.e. phone and phone=) are not created until the actual AR object is loaded from the database. At that point AR enumerates the DB fields and creates the corresponding field methods.

These field methods are not available at the time the class is being defined in your source, so you can't alias_method a method that does not exist. You could however do something like this instead (not tested):

module PhoneSanitizer
  module ClassMethods
    def sanitize_phone(*fields)
      fields.each do |field|
        original_method = "#{field}=".to_sym
        define_method(original_method) do |value|
          self.write_attribute(field, phone_to_number(value))
        end
      end
    end
  end

  ...
end

That should accomplish pretty much the same thing as you originally intended:
http://apidock.com/rails/ActiveRecord/AttributeMethods/Write/write_attribute

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜