使用Rails中缺少的方法

时间:2012-01-16 20:22:24

标签: ruby-on-rails activerecord override method-missing dynamic-attributes

我有一个包含多个日期属性的模型。我希望能够设置并获取值作为字符串。我过度使用其中一种方法(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

这对我的需求很有帮助,但是我想对其他几个日期属性做同样的事情......我如何利用缺少的方法,以便可以设置/得到任何日期属性?

2 个答案:

答案 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_missingrespond_to等内容。唯一的缺点是所有列都存在这些_human方法。