【问题标题】:Using method missing in Rails使用 Rails 中缺少的方法
【发布时间】:2012-02-11 17:40:24
【问题描述】:

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

这非常适合我的需求,但我想对其他几个日期属性做同样的事情......我将如何利用缺少的方法以便可以像这样设置/获取任何日期属性?

【问题讨论】:

  • method_missing 是你应该采取的最后一根稻草。实际上,定义方法更简洁,可以带来更好的代码设计,清晰的关注点分离,更容易理解,也更快。所以如果你可以定义你的方法,你应该总是这样做。
  • 从 KL-7 了解到,有比 method_missing 更好的方法,但考虑到我有 4 个不同的日期属性用于此模型,手动定义每个属性不是解决方案。干燥
  • 嗯,KL-7的方法其实是这里的首选。因为他提出的正是我的意思:定义方法。

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


【解决方案1】:

正如您已经知道所需方法的签名,最好定义它们而不是使用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?)。

【讨论】:

  • 太好了!我之前没有遇到过 define_method :) 我仍然希望看到该方法缺少实现,但是 +1 以获得更好的解决方案
  • 添加了method_missing版本,但如果可以定义这些方法就不要使用了。
  • @KL-7 在您的method_missing 中,您应该在不处理该方法时直接返回super。现在,总是执行下面的属性处理。
【解决方案2】:

您可能对 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 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-06
    • 1970-01-01
    相关资源
    最近更新 更多