【问题标题】:When chaining methods can the child method access and use the results of the 'parent' method? (i.e. Object.parent.child)当链接方法时,子方法可以访问和使用“父”方法的结果吗? (即 Object.parent.child)
【发布时间】:2016-05-02 18:15:05
【问题描述】:

说明 我创建了一个 Report 对象,它具有不同的环境/组 (:live & :demo),它们只是将填充有 ReportItem 对象的数组,这些对象具有以下属性 :currency, :gross, :net (并且 Gross & Net 持有 Ruby金钱对象)。

Report.rb 类中有两个方法叫做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

【问题讨论】:

    标签: ruby-on-rails ruby method-chaining


    【解决方案1】:

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

    考虑演示方法:

    def demo
      @demo
      self
    end
    

    @demo 行确实是空操作。你没有在这里“返回”任何东西,没有分配任何东西,它只是......在那里。这里唯一返回的是self。

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

    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 Report
      def initialize
        @demo = ReportItemArray.new
        @live = ReportItemArray.new
      end
      def live
        @live
      end
      def demo
        @demo
      end
    end
    

    注意,livedemo 这两个方法现在实际上已经过时了,因为您使用 attr_accessor 并且它们不再返回任何特殊内容,因此您实际上不需要覆盖它们,只需这样做要明确。 livedemo 对象本身仍然是数组,因此应该与您的其余代码兼容,但 add_money 方法也附加到这些 Array 对象,所以现在您可以这样做:

    report.live.add_money_to_gross(money)
    

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

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

    最后一个不相关的注释,您可能已经在上面找到了我的解决方法,但您的 add_money 方法总是添加到 ReportItemnet,即使最初的调用是从 add_money_to_gross 发出的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多