使用附加功能包装 ActiveRecord 方法

发布于 2024-12-02 12:55:34 字数 1567 浏览 1 评论 0原文

我想增强 Rails 中的 ActiveRecord setter 以确保仅保存有效值。需要此功能的地方之一就是电话号码。用户可以以各种格式输入电话号码,例如,

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

但我只想存储数字并在进入数据库时​​丢弃其余部分。我现在使用的方法是使用 alias_method 覆盖 setter 方法。另外,我试图将其放入一个模块中,以便任何包含电话号码的模型类都可以包含该模块,并定义应清理的字段。我希望使用的接口类型是,

# 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

我在模型类中所做的唯一一件事就是包含 PhoneSanitizer 模块(它添加了一个类方法 - sanitize_phone_fieldPerson 类中)。该方法现在负责重写 setter phone= 方法。这是我还没有开始工作的部分。

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

当调用 sanitize_phone 时,它会抛出一个错误,指出 :phone= 没有为 Person 类定义,这是有道理的。我该如何为 Person 实例的方法添加别名呢?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

嘿哥们儿 2024-12-09 12:55:34

我认为你的错误不是未定义的方法alias_method,它是不同的东西,你误解了它(?)

真正的问题是ActiveRecord上的getter和setter方法是动态。 getter 和 setter 方法(即 phonephone=)在从数据库加载实际 AR 对象之前不会创建。此时,AR 会枚举数据库字段并创建相应的字段方法。

在源中定义类时,这些字段方法不可用,因此您不能 alias_method 不存在的方法。然而,你可以做这样的事情(未经测试):

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

这应该完成与你最初想要的几乎相同的事情:
http://apidock.com/rails/ActiveRecord/AttributeMethods/Write/write_attribute

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文