【发布时间】:2011-09-01 01:12:13
【问题描述】:
我想增强 Rails 中的 ActiveRecord 设置器以确保只保存有效值。需要这样做的地方之一就是电话号码。用户可以输入多种格式的电话号码,例如,
(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 模块(它在Person 类中添加了一个类方法-sanitize_phone_field)。该方法现在负责覆盖 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 实例的方法设置别名?
【问题讨论】:
-
在我看来,简单地在 PhoneSanitizer 模块中定义实例方法,将模块包含在您的 Person 类中,然后从 before_save 回调中调用这些方法会更干净、更安全。
-
其实我很喜欢这个主意。不知道为什么我之前没有想过在验证回调中这样做。这比玩弄方法调配要干净得多。我已经将其作为解决方案实施。但是,为了学习和提高我的元编程业力点,我仍然有兴趣找出我在上面做错了什么。
-
嗯。我似乎无法重现您的问题:gist.github.com/1185316。在 ruby 1.8 和 1.9 上测试。虽然我知道你的问题出在哪里..我认为你所说的错误并不是 Ruby 实际告诉你的真正错误。
-
@Casper - 很抱歉造成混乱。我的意思是写
:person=方法是未定义的,而不是alias_method。
标签: ruby-on-rails ruby metaprogramming