当链接方法时,子方法可以访问和使用 'parent' 方法的结果吗? (即 Object.parent.child)

When chaining methods can the child method access and use the results of the 'parent' method? (i.e. Object.parent.child)

描述 我创建了一个 Report 对象,它具有不同的 environments/groups (:live & :demo),它们只是数组,将填充 ReportItem 具有以下属性的对象 :currency, :gross, :net(并且毛额和净额持有 Ruby 货币对象)。

Report.rbclass中有两个方法叫做add_money_to_netadd_money_to_gross都有一个变量我暂时命名为@group

问题 (report = Report.new) 我想要做的是像这样的链式方法 report.demo.add_money_to_gross(<params>) 如果调用此方法我想访问 report.demo (代替我的 @group 变量)。或者调用 report.live.add_money_to_gross(<params>) 应该让我访问 report.live。这可能吗?

我希望这是有道理的。如果有任何不清楚的地方,请告诉我。

Report.rb

class Report
  include ActiveModel::Model
  attr_accessor :demo, :live

  def initialize
    @demo = []
    @live = []
  end

  def demo
    @demo
    self
  end

  def live
    @live
    self
  end

  def add_money_to_net(money)
    add_money(group: @group, money: money)
  end

  def add_money_to_gross(money)
    add_money(group: @group, money: money)
  end

  private

  def add_money(group:, money:)
    item = group.find {|s| s.currency == money.currency }
    if item
      item.net += money
    else
      group << ReportItem.new(currency: money.currency.to_s, net: money)
    end
  end
end

ReportItem.rb

class ReportItem
  include ActiveModel::Model
  attr_accessor :currency, :gross, :net

  def initialize(currency:, gross: nil, net: nil)
    @currency = currency
    @gross = gross ? gross : Money.new(0, currency)
    @net = net ? net : Money.new(0, currency)
  end

  def gross=(value)
    @gross = value if @gross.currency == @currency
  end

  def net=(value)
    @net = value if @net.currency == @currency
  end
end

我明白你想做什么,几乎就像命令行管道操作一样。而且,不,你的方法不可能(或者,恕我直言,推荐)。

考虑演示方法:

def demo
  @demo
  self
end

@demo 行确实是空操作。你不是 "returning" 这里的任何东西,没有分配任何东西,它只是......那里。 return这里唯一的东西就是自己。

现在,您可以执行以下操作:

def demo
  @group = @demo
  self
end

然后打电话

report.live.add_money_to_gross(money)

现在它应该可以正常工作了。在这里,livedemo 更像是状态管理器,在调用操作方法之前设置 @group 的状态。

也就是说,这有点脆弱,需要您按特定顺序调用事物,而临时变量似乎是一个等待发生的陷阱。为什么?因为这样调用:

report.add_money_to_gross(money)

是一个有效的调用,但除非首先调用了 livedemo,否则不应如此。不会抛出任何错误,它只会添加到最后设置的 @group 中。

相反,实际上创建一个新对象来封装所有这些对我来说更有意义:

class ReportItemArray < Array
  def add_money_to_net money
    item = find_item(money)
    if item
      item.net += money
    else
      self << ReportItem.new(currency: money.currency.to_s, net: money)
    end
  end
  def add_money_to_gross money
    item = find_item(money)
    if item
      item.gross += money
    else
      self << ReportItem.new(currency: money.currency.to_s, gross: money)
    end
  end
  def find_item money
    self.find {|s| s.currency == money.currency }
  end
end

然后,在你的报告中class:

class Report
  def initialize
    @demo = ReportItemArray.new
    @live = ReportItemArray.new
  end
  def live
    @live
  end
  def demo
    @demo
  end
end

请注意,livedemo 这两种方法现在实际上已经过时了,因为您使用 attr_accessor 并且它们不再 return 有什么特别之处,所以您实际上不需要覆盖它们,只是为了明确。 livedemo 对象本身仍然是数组,因此应该与您的其余代码兼容,但是 add_money 方法也附加到这些数组对象,所以现在您可以做:

report.live.add_money_to_gross(money)

现在通过调用 live,您将获得数组,并且由于方法是该数组对象的一部分,您不再需要传递上下文,它们本身就知道它(group变成 self)。因此,调用方法作用于给定的数组。

这样做消除了调用 report.add_money_to_gross 的可能性(如果这样做会引发错误),这很好,因为这里的调用需要更多上下文(组)才能完成。这也消除了操作顺序冲突,因为不再需要状态。

最后一个不相关的说明,您可能已经注意到我上面对此的修复,但是您的 add_money 方法总是添加到 ReportItemnet,即使原始调用是由 add_money_to_gross.

制成