Effectively using __method__ and __callee__ in Ruby
Ruby provides many useful methods out of the box which allows for some very effective programming techniques. Today, we are taking a look at the __method__
and __callee__
helper methods and how they can help us produce DRY code.
Generally, both methods return the name of the currently running method as a Symbol. Using these methods, we can access the current method name without requiring us to duplicate the name in our code.
Now let’s have a look at them in action. Consider the following example where we create a Greeter
class which allows us to give personalized greetings:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Greeter def initialize(name) @name = name end def greetings "#{__method__.to_s.capitalize}, #{@name}!" end end greeter = Greeter.new('Ben') greeter.greetings # => "Greetings, Ben!" |
As you can see, using the __method__
helper we don’t have to duplicate the name of the method in our code, thus keeping it DRY. In case we later decide to change the formality of the greeting and thus changing the method name, we wouldn’t also have to remember to change the method body to return the new greeting.
This with working and our software starting to get popular all over the world, we might decide to introduce a more familiar way to greet people using different localizations. If we are in Australia, people might prefer to use
Oi, Mick!
while in Sweden, it might be more appropriate to use
Hej, Anja!
The first approach to implement this could be to add multiple new independent methods, one for each greeting variant. However, this would quickly get out of hand and would effectively duplicate lots of code.
Since we are already using __method__
in our implementation, we could try to leverage this and use aliases instead.
1 2 3 4 5 6 7 8 9 10 11 | class Greeter def initialize(name) @name = name end def greetings "#{__method__.to_s.capitalize}, #{@name}!" end alias oi greetings alias hej greetings end |
Now, when calling one of the alias methods, it turns out that unfortunately (for our use-case) __method__
always returns the name of the actual method, completely ignoring the alias we used:
1 2 3 4 5 | Greeter.new('Mick').oi # => "Greetings, Mick!" Greeter.new('Anja').hej # => "Greetings, Anja!" |
Fortunately, there is a very similar helper method named __callee__
which helps us solve our use-case.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Greeter def initialize(name) @name = name end def greetings "#{__callee__.to_s.capitalize}, #{@name}!" end alias oi greetings alias hej greetings end Greeter.new('friend').greetings # => "Greetings, friend!" Greeter.new('Mick').oi # => "Oi, Mick!" Greeter.new('Anja').hej # => "Hej, Anja!" |
Using the __callee__
helper, we can get the name of the current method exactly as it was called. For normal methods, this returns the same name as __method__
. Once we use methods aliases however, the returned value differs since __callee__
always returns the name of the method as we actually called it, whichever alias that might be.
Since both helper methods are provided by Ruby’s Kernel
module, they are directly available in about all Ruby objects. Using them, we can keep our code nice and DRY but still add new functionality with just one line of code without having to change any existing methods.