有条件地调用链式方法

时间:2015-11-16 20:51:14

标签: ruby

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

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

可以在有条件地继续或停止方法链的链式方法之间插入表达式或方法吗?

3 个答案:

答案 0 :(得分:5)

这很简单,在您立即理解之后将会出现:

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}

编辑:

小心点击定义如下:

class Object
  def tap
    yield self
    self
  end
end

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

class Object
  def tap_and_chain
    yield self
  end
end

答案 1 :(得分:1)

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> 

答案 2 :(得分:0)

如果您正在链接的块是可变的,则可以使用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