【问题标题】:Conditionally call chained method有条件地调用链式方法
【发布时间】:2018-03-03 04:49:59
【问题描述】:

链式方法只能在以下代码中的特定情况下调用。

class Klass
  def foo
    puts 'foo'
    self
  end
  def bar
    puts 'bar'
    self
  end
end

klass = Klass.new
a = 2
id = klass.foo{conditionally chain bar if a == 2}.bar

您能否在有条件地继续或停止方法链的链式方法之间插入表达式或方法?

【问题讨论】:

    标签: ruby


    【解决方案1】:

    这很简单,谁来了你马上就明白了:

    klass = klass.foo
    klass = klass.bar if a == 2
    etc...
    

    如果链式方法不带参数,这很有效

    klass.define_singleton_method :chain_if do |b, *s|
      return unless b
      klass = self
      s.each do |x|
        klass = klass.send x
      end
      klass
    end
    
    klass.foo.chain_if(true, :foo, :bar).chain_if(false, :bar)
    

    这里有一些重复的线程!

    conditional chaining in ruby

    Add method to an instanced object

    在这里我找到了另一个我个人喜欢的解决方案:

    my_object.tap{|o|o.method_a if a}.tap{|o|o.method_b if b}.tap{|o|o.method_c if c}
    

    编辑:

    注意tap定义如下:

    class Object
      def tap
        yield self
        self
      end
    end
    

    如果链式方法返回一个新的不可变对象,您需要的可能如下所示:

    class Object
      def tap_and_chain
        yield self
      end
    end
    

    【讨论】:

    • tap 将返回 my_objecto.method_a 的结果将被丢弃。
    • 但是tap 可以使用像o.method_a! 这样的爆炸方法。很好的答案!
    【解决方案2】:
    def chain_maybe(klass, condition, *args)
      args[1..-1].reduce(klass.send(args.first)) { |r,m| (condition && r.send(m)) || r }
    end
    

    或:

    def chain_maybe(klass, condition, *args)
      first, *others = args
      others = [] unless condition
      others.reduce(klass.send(first)) { |r,m| r.send(m) }
    end
    

    为:

    class Klass
      def foo
        puts 'foo'
        self
      end
    
      def bar
        puts 'bar'
        self
      end
    
      def baz
        puts 'baz'
        self
      end
    end
    
    c = Klass.new
    
    chain_maybe(c, true, :foo, :bar, :baz)
    foo
    bar
    baz
      #=> #<Klass:0x007fccea8da388> 
    chain_maybe(c, false, :foo, :bar, :baz)
    foo
      #=> #<Klass:0x007fccea8da388> 
    chain_maybe(c, true, :foo, :bar)
    foo
    bar
      #=> #<Klass:0x007fccea8da388>
    chain_maybe(c, true, :bar, :baz)
    bar
    baz
      #=> #<Klass:0x007fccea8da388> 
    

    如果每个参数都有一个条件,这可以概括为:

    def chain_maybe(klass, conditions, args)
      return nil if conditions.empty?
      first, *others = args.zip(conditions).select(&:last).map(&:first)
      others.reduce(klass.send(first)) { |r,m| r.send(m) }
    end
    
    args = [:foo, :bar, :baz]
    chain_maybe(c, [true,  true,  true], args)
    foo
    bar
    baz
      #=> #<Klass:0x007fccea8da388> 
    chain_maybe(c, [false, true,  true], args)
    bar
    baz
      #=> #<Klass:0x007fccea8da388> 
    chain_maybe(c, [true,  false, true], args)
    foo
    baz
      #=> #<Klass:0x007fccea8da388> 
    

    【讨论】:

      【解决方案3】:

      如果你链接的块是可变的,你可以使用tap,即:它将改变self的值(如here所解释的) 但是,如果您要链接 QueryMethods 之类的不可变块,则可以使用 try,例如:

      data
        .try { |d| group ? d.group(group) : d }
        .try { |d| group ? d.order(order => :desc) : d }
      

      try 基本上只有一个块:

      def try
        yield self
      end
      

      (参考:#try

      【讨论】:

        猜你喜欢
        • 2020-07-03
        • 2017-11-03
        • 1970-01-01
        • 1970-01-01
        • 2017-11-01
        • 2023-01-27
        • 1970-01-01
        • 1970-01-01
        • 2016-04-09
        相关资源
        最近更新 更多