在ruby

时间:2015-05-26 12:10:18

标签: ruby-on-rails ruby metaprogramming

EDITED

假设我有以下代码:

class Kid < ActiveRecord::Base
  has_one :calculator
  attr_accessible :favorite_num
end

class Teacher < ActiveRecord::Base
  has_one :calculator
  # does not have a favorite_num
end

class Calculator < ActiveRecord::Base
  belongs_to :kid
  belongs_to :teacher

  def compute(number)
    # takes the number passed in and multiplies it by
    # the favorite_num if it exists, otherwise returns 42
  end
end

我需要做的是定义上面的compute,以便调用以下工作。

kid = Kid.first
=> #<User id: 1, favorite_num: 4, . . . >
kid.calculator.compute(5)
=> 20
teacher = Teacher.first
=> #<Teacher id: 1, . . . >
teacher.calculator.compute(5)
=> 42

一些额外的限制:

  • 我不想在computeKid类中添加Teacher方法。这是因为:
    • 我希望尽可能保持干燥,其他几个类的对象也与Calculators相关联
    • 这样做需要对我正在使用的遗留代码进行大量重构(代码周围散布着许多calculator.compute个调用,通常以不太明显的方式调用)

原始版本

如果我有

object.foo

然后self上下文中foo的值为object。但是,如果我有:

object.foo.bar

如何在object的定义中引用barself显然不会工作,因为它只等于object.foo的返回值。

我无法更改foo的实施。并且无法在同一类的对象上调用foobar

1 个答案:

答案 0 :(得分:0)

您描述的链接方法类型要求方法foo返回self。为了实现这一点,您必须更改方法定义或修改它。更改方法定义很简单,只需将self写为方法的最后一个语句即可。当然,这意味着您无法从方法中返回任何其他内容,因此对于每种方法而言,它都不是可行的模式。我强烈建议不要使用猴子补丁,因为我认为由此产生的丑陋代码是不值得的。

然而,有一些解决这个问题的方法,不需要修改原始方法。最简单的解决方案是将方法调用写在彼此之下。它可能不那么“性感”,但它可以完成工作。

object.foo
object.bar

如果您想省略重复object,可以使用Object#tap

object.tap{|o| o.foo }.bar

使用Symbol#to_proc可以缩短一点。

object.tap(&:foo).bar