【问题标题】:get object at end of method chain in ruby在 ruby​​ 方法链的末尾获取对象
【发布时间】:2015-05-26 12:10:17
【问题描述】:

已编辑

假设我有以下代码:

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

一些额外的限制:

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

原版

如果我有

object.foo

那么selffoo 的上下文中的值是object。但如果我有:

object.foo.bar

如何在bar 的定义中引用objectself 显然不起作用,因为它只是等于 object.foo 的返回值。

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

【问题讨论】:

  • "self 显然是行不通的,因为它只是等于object.foo" 的返回值。除非object.foo 返回self,例如def foo; #some logic here; self; end; 在这种情况下,#foo 的返回值为 object。或object.foo 返回同一类的对象。以"String".gsub('St','B').downcase #=&gt; 'bring' 为例,因为 gsub 返回一个字符串,而 downcase 是一个字符串方法,所以虽然它不是作用于同一个字符串而是一个副本,但它仍然在对象 String 的上下文中。如果这没有帮助,我建议让您的问题更具体一些。
  • @engineersmnky:谢谢!不幸的是,我无法更改foo 的实现。刚刚更新了问题。不,object.foo 不会返回与object 相同类的对象。我也会澄清这一点。感谢您的反馈!
  • 这似乎是一个坏掉的设计。 binding_of_caller 可能会有所帮助
  • @FrederickCheung 谢谢!这可能确实有帮助。

标签: ruby-on-rails ruby metaprogramming


【解决方案1】:

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

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

object.foo
object.bar

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

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

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

object.tap(&:foo).bar

【讨论】:

  • 谢谢!不幸的是,foobar 不能在同一个对象上调用。 foo 返回与 object 不同类的对象。
  • 是的,这就是我的答案。你真的尝试过任何方法吗?您应该使用tap,我认为它可以满足您的需求。
  • 感谢您对我的包容。对不起,如果我不清楚。我已经尝试过这些方法并且非常感谢它们。 object.tap(&amp;:foo).bar 似乎在object 上调用foo,然后在object 上也调用bar。这不是我希望做的。您能否给我一个示例,说明如何从 bar 的定义中引用对象?
  • 如果你有一个方法bar,你可以把object作为参数传递,然后调用bar(object.tap(&amp;:foo)),或者object.foo,然后在下一行调用bar(object)。或者只是将object 传递给bar 并在bar 方法中调用foo
  • 太好了,很有帮助。仍然不是我需要的——但我不认为那是你的错!我认为我需要更好地充实我对问题的描述。我会在上面尝试这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
  • 2015-07-17
  • 1970-01-01
  • 2018-02-09
  • 2022-01-18
  • 2010-11-23
  • 1970-01-01
相关资源
最近更新 更多