conditional chaining in ruby

后端 未结 9 2163
一生所求
一生所求 2020-12-13 20:30

Is there a good way to chain methods conditionally in Ruby?

What I want to do functionally is

if a && b && c
 my_object.some_method_b         


        
相关标签:
9条回答
  • 2020-12-13 20:49

    Maybe your situation is more complicated than this, but why not:

    my_object.method_a if a
    my_object.method_b if b
    my_object.method_c if c
    
    0 讨论(0)
  • 2020-12-13 20:50

    You could put your methods into an arry and then execute everything in this array

    l= []
    l << :method_a if a
    l << :method_b if b
    l << :method_c if c
    
    l.inject(object) { |obj, method| obj.send(method) }
    

    Object#send executes the method with the given name. Enumerable#inject iterates over the array, while giving the block the last returned value and the current array item.

    If you want your method to take arguments you could also do it this way

    l= []
    l << [:method_a, arg_a1, arg_a2] if a
    l << [:method_b, arg_b1] if b
    l << [:method_c, arg_c1, arg_c2, arg_c3] if c
    
    l.inject(object) { |obj, method_and_args| obj.send(*method_and_args) }
    
    0 讨论(0)
  • 2020-12-13 20:51

    I ended up writing the following:

    class Object
    
      # A naïve Either implementation.
      # Allows for chainable conditions.
      # (a -> Bool), Symbol, Symbol, ...Any -> Any
      def either(pred, left, right, *args)
    
        cond = case pred
               when Symbol
                 self.send(pred)
               when Proc
                 pred.call
               else
                 pred
               end
    
        if cond
          self.send right, *args
        else
          self.send left
        end
      end
    
      # The up-coming identity method...
      def itself
        self
      end
    end
    
    
    a = []
    # => []
    a.either(:empty?, :itself, :push, 1)
    # => [1]
    a.either(:empty?, :itself, :push, 1)
    # => [1]
    a.either(true, :itself, :push, 2)
    # => [1, 2]
    
    0 讨论(0)
  • 2020-12-13 20:57

    You can use tap:

    my_object.tap{|o|o.method_a if a}.tap{|o|o.method_b if b}.tap{|o|o.method_c if c}
    
    0 讨论(0)
  • 2020-12-13 21:02

    If you're using Rails, you can use #try. Instead of

    foo ? (foo.bar ? foo.bar.baz : nil) : nil
    

    write:

    foo.try(:bar).try(:baz)
    

    or, with arguments:

    foo.try(:bar, arg: 3).try(:baz)
    

    Not defined in vanilla ruby, but it isn't a lot of code.

    What I wouldn't give for CoffeeScript's ?. operator.

    0 讨论(0)
  • 2020-12-13 21:11

    I use this pattern:

    class A
      def some_method_because_of_a
         ...
         return self
      end
    
      def some_method_because_of_b
         ...
         return self
      end
    end
    
    a = A.new
    a.some_method_because_of_a().some_method_because_of_b()
    
    0 讨论(0)
提交回复
热议问题