使用附加功能包装 ActiveRecord 方法
我想增强 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_field
在 Person
类中)。该方法现在负责重写 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为你的错误不是
未定义的方法alias_method
,它是不同的东西,你误解了它(?)真正的问题是
ActiveRecord
上的getter和setter方法是动态。 getter 和 setter 方法(即phone
和phone=
)在从数据库加载实际 AR 对象之前不会创建。此时,AR 会枚举数据库字段并创建相应的字段方法。在源中定义类时,这些字段方法不可用,因此您不能
alias_method
不存在的方法。然而,你可以做这样的事情(未经测试):这应该完成与你最初想要的几乎相同的事情:
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
andphone=
) 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):That should accomplish pretty much the same thing as you originally intended:
http://apidock.com/rails/ActiveRecord/AttributeMethods/Write/write_attribute