How can I set custom methods in Ruby correctly?

I am trying to make a chaining method for the following two methods. After running this code, I continued to get the following output:

#<SimpleMath:0x007fc85898ab70>% 

My question is: what is the correct way to chain methods in Ruby ?

Here are my codes:

 class SimpleMath def add(a,b=0) a + b return self end def subtract(a,b=0) a - b return self end end newNumber = SimpleMath.new() print newNumber.add(2,3).add(2) 
+8
methods ruby ruby-on-rails method-chaining
source share
4 answers

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) 
+10
source share

Let me define an instance of your SimpleMath class:

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

Three notes:

  • sm is a variable. In Ruby, variables are lowercase letters, optionally separated by underscores (for example, my_var ).
  • while it’s OK, to add () after new , when new has no arguments (otherwise called “parameters”), this is optional and usually fails.
  • If the return keyword is missing, Ruby returns the last calculation performed by the method. Here you usually write the last line as just self , and this will be returned. Alas, this does not matter, since returning self with or without the return keyword is not what you want.

Try the following on IRB:

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

You undoubtedly expected this to return 2+0 #=> 2 , but instead return self , which, as you can see above, is actually sm ( #<SimpleMath:0x000001020ca820> ).

You can fix this by simply deleting 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 bind 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 Fixnum class, of which 2 is an instance, does not have an instance method named add . This is because you defined it for the SimpleMath class, and not for Fixnum .

When Ruby executes sm.add(2).add(3,4) , it first evaluates sm.add(2) #=> 2 , which reduces the expression to 2.add(3,4) . Then he tries to send the add method (with its two parameters) to 2 , but finds that the class 2.class #=> Fixnum does not have an add instance method; hence the exception.

We can fix this error by defining these methods for the Fixnum class:

 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 one more problem:

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

Oh my, the Fixnum class Fixnum not have a new method! This is because Fixnum instances are integers that cannot be created. You can easily confirm that integers are instances of Fixnum :

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

So, we can call the add method by sending it to any instance of Fixnum :

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

Now try linking the add operations:

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

No exceptions, but no chains. A way to fix this is to change the methods 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 added the puts statement in each method if necessary additional debugging. We will remove them when the code works correctly. Let the 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?

+4
source share

This person ( tjackiw.tumblr.com ) uses this as an interview question and gives a very clear idea of ​​how and 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 
+1
source share

Another way is to build a pipeline using 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 
+1
source share

All Articles