我有一个包含多个日期属性的模型。我希望能够设置并获取值作为字符串。我过度使用其中一种方法(bill_date),如下所示:
def bill_date_human
date = self.bill_date || Date.today
date.strftime('%b %d, %Y')
end
def bill_date_human=(date_string)
self.bill_date = Date.strptime(date_string, '%b %d, %Y')
end
这对我的需求很有帮助,但是我想对其他几个日期属性做同样的事情......我如何利用缺少的方法,以便可以设置/得到任何日期属性?
答案 0 :(得分:10)
由于您已经知道所需方法的签名,因此最好定义它们而不是使用method_missing
。你可以这样做(在你的类定义中):
[:bill_date, :registration_date, :some_other_date].each do |attr|
define_method("#{attr}_human") do
(send(attr) || Date.today).strftime('%b %d, %Y')
end
define_method("#{attr}_human=") do |date_string|
self.send "#{attr}=", Date.strptime(date_string, '%b %d, %Y')
end
end
如果列出所有日期属性不是问题,那么这种方法会更好,因为你正在处理常规方法而不是method_missing
内的一些魔法。
如果要将其应用于名称以_date
结尾的所有属性,可以像这样检索它们(在类定义中):
column_names.grep(/_date$/)
这里是method_missing
解决方案(未经测试,但前一个未经过测试):
def method_missing(method_name, *args, &block)
# delegate to superclass if you're not handling that method_name
return super unless /^(.*)_date(=?)/ =~ method_name
# after match we have attribute name in $1 captured group and '' or '=' in $2
if $2.blank?
(send($1) || Date.today).strftime('%b %d, %Y')
else
self.send "#{$1}=", Date.strptime(args[0], '%b %d, %Y')
end
end
此外,覆盖respond_to?
方法并返回true
方法名称是很好的,您可以在method_missing
内处理(在1.9中你应该覆盖respond_to_missing?
)。< / p>
答案 1 :(得分:5)
你可能对ActiveModel的AttributeMethods
模块感兴趣(活动记录已经用于一堆东西),这几乎(但不完全)你需要的东西。
简而言之,你应该可以做到
class MyModel < ActiveRecord::Base
attribute_method_suffix '_human'
def attribute_human(attr_name)
date = self.send(attr_name) || Date.today
date.strftime('%b %d, %Y')
end
end
完成此操作后,my_instance.bill_date_human
将调用attribute_human
并将attr_name设置为'bill_date'。 ActiveModel会为您处理method_missing
,respond_to
等内容。唯一的缺点是所有列都存在这些_human方法。