How can I properly chain custom methods in Ruby?

后端 未结 4 2013
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-08 10:49

I am trying to do a chaining method for the following two methods. After running this code, I kept getting the following output:

#

        
相关标签:
4条回答
  • 2021-02-08 11:12

    This fellow (tjackiw.tumblr.com) uses this as an interview question and gives a very clean walkthough of how & why the correct answer is similar to the following:

    class Interpreter
    
      def initialize(&block)
        instance_eval(&block)
      end
    
      def at(time)
        @time = time
        self
      end
    
      def when(date)
        @date = date
        self
      end
    
      def we(*people)
        @people = people
        self
      end
    
      def going(where)
        @where = where
        self
      end
    
      def output
        [@people.join(' and '), "are going", @where, @date, "at", @time].join(' ')
      end
    
    end
    
    0 讨论(0)
  • 2021-02-08 11:15

    Are you trying to do something like this?

    class SimpleMath
      def initialize
        @result = 0
      end
    
      #1 add function
      def add(val)
        @result += val
        self
      end
    
      #2 Subtract function
      def subtract(val)
        @result -= val
        self
      end
    
      def to_s
        @result
      end
    end
    
    newNumber = SimpleMath.new
    p newNumber.add(2).add(2).subtract(1)
    

    For any number of arguments

    class SimpleMath
      def initialize
        @result = 0
      end
    
      #1 add function
      def add(*val)
        @result += val.inject(&:+)
        self
      end
    
      #2 Subtract function
      def subtract(*val)
        @result -= val.inject(&:+)
        self
      end
    
      def to_s
        @result
      end
    end
    
    newNumber = SimpleMath.new
    p newNumber.add(1, 1).add(1, 1, 1, 1).subtract(1)
    
    0 讨论(0)
  • 2021-02-08 11:23

    Another way is to build pipeline via chainable_methods gem.

    Described in the article

    require 'chainable_methods'
    
    module SimpleMath
      include ChainableMethods
    
      def add(a, b=0)
        a + b
      end
    
      def subtract(a, b=0)
        a - b    
      end
    end
    
    SimpleMath.
      chain_from(5).
      add(5).
      add(5).
      subtract(3).
      unwrap
    
    0 讨论(0)
  • 2021-02-08 11:33

    Let's define an instance of your class SimpleMath:

    sm = SimpleMath.new #=> #<SimpleMath:0x000001020ca820>
    

    Three things to note here:

    • sm is a variable. In Ruby, variables are represented by lower case letters, optionally separated with underscores (e.g., my_var).
    • while it's OK to add () after new, when new has no arguments (aka "parameters"), that's optional and not usually done.
    • if the keyword return is not present, Ruby returns the last calculation performed by the method. Here you would generally write the last line as simply self, and that would be returned. Alas, that matters not, as returning self, with or without the keyword return, is not what you want.

    Try the following in IRB:

    sm.add(2) #=> #<SimpleMath:0x000001020ca820>
    

    You no doubt were expecting this to return 2+0 #=> 2, but instead it returned self, which, as you can see above, is in fact sm (#<SimpleMath:0x000001020ca820>).

    You can fix this by simply removing the line:

    return self
    

    from add and subtract:

    class SimpleMath
      def add(a,b=0)
        a + b
      end
    
      def subtract(a,b=0)
        a - b
      end
    end
    

    Now

    sm = SimpleMath.new
    sm.add(2) #=> 2
    

    However, if we try to chain another add, we have another problem:

    sm.add(2).add(2,3) #=> NoMethodError: undefined method `add' for 2:Fixnum
    

    This message is very clear: the class Fixnum, of which 2 is an instance, has no instance method named add. That's because you defined it for the class SimpleMath, not for Fixnum.

    When Ruby executes sm.add(2).add(3,4), it first computes sm.add(2) #=> 2, which reduces the expression to 2.add(3,4). It then attempts to send the method add (with its two parameters) to 2, but finds the class 2.class #=> Fixnum has no instance method add; hence the exception.

    We can correct that error by defining these methods for class Fixnum instead:

    class Fixnum
      def add(a,b=0)
        a + b
      end
    
      def subtract(a,b=0)
        a - b
      end
    end
    

    You can confirm that these methods have been added to the Fixnum class by running:

    Fixnum.instance_methods.sort
    

    Now, another problem:

    sm = Fixnum.new #=> NoMethodError: undefined method `new' for Fixnum:Class
    

    Oh, my, the class Fixnum has no new method! That's because the instances of Fixnum are integers, which cannot be created. You can easily confirm that integers are instances of Fixnum:

    72.class #=> Fixnum
    -3.class #=> Fixnum
    

    So we can invoke the add method by sending it to any Fixnum instance:

    72.add(2) #=> 2
    -3.add(2) #=> 2
    

    Now let's try to chain add operations:

    72.add(2).add(3,4)       #=> 7
    72.add(2000000).add(3,4) #=> 7
    

    No exception, but no chaining. The way to fix this is to change the methods yet again:

    class Fixnum
      def add(b=0)
        puts "in add, self = #{self}, b = #{b}"
        self + b
      end
    
      def subtract(b=0)
        puts "in subtract, self = #{self}, b = #{b}"
        self - b
      end
    end
    

    I've added a puts statement in each method in case more debugging is needed. We'll remove these when the code works properly. Let's test:

    2.add(3)                    #=> 5
      in add, self = 2, b = 3
    5.add                       #=> 5
      in add, self = 5, b = 0
    5.add(7)                    #=> 12
      in add, self = 5, b = 7
    2.add(3).add.add(7)         #=> 12
      in add, self = 2, b = 3
      in add, self = 5, b = 0
      in add, self = 5, b = 7
    
    2.subtract(5)               #=> -3
      in subtract, self = 2, b = 5
    -3.subtract                 #=> -3
      in subtract, self = -3, b = 0
    2.subtract(5).subtract      #=> -3
      in subtract, self = 2, b = 5
      in subtract, self = -3, b = 0
    
    2.add(3).subtract(5).add(7) #=>  7
      in add, self = 2, b = 3
      in subtract, self = 5, b = 5
      in add, self = 0, b = 7
    

    Success! Get it?

    0 讨论(0)
提交回复
热议问题