This exotic spell is popular among black-belt Ruby programmers. When I stumbled upon this spell for the first time, I had trouble understanding the reasoning behind it. It’s easier to get there by taking one small step at a time.
Look at this code:
| names = ['bob', 'bill', 'heather'] |
| names.map {|name| name.capitalize } # => ["Bob", "Bill", "Heather"] |
Focus on the block—a simple “one-call block” that takes a single argument and calls a single method on that argument. One-call blocks are very common in Ruby, especially (but not exclusively) when you’re dealing with arrays.
In a language such as Ruby, which prides itself on being succinct and to the point, even a one-call block looks verbose. Why do you have to go through the trouble of creating a block, with curly braces and all, just to ask Ruby to call a method? The idea of Symbol#to_proc is that you can replace a one-call block with a shorter construct. Let’s start with the smallest piece of information you need, which is the name of the method that you want to call, as a symbol:
| :capitalize |
You want to convert the symbol to a one-call block like this:
| {|x| x.capitalize } |
As a first step, you can add a method to the Symbol class, which converts the symbol to a Proc object:
| class Symbol |
* | def to_proc |
* | Proc.new {|x| x.send(self) } |
* | end |
| end |
See how this method works? If you call it on, say, the :capitalize symbol, it returns a proc that takes an argument and calls capitalize on the argument. Now you can use to_proc and the & operator to convert a symbol to a Proc and then to a block:
| names = ['bob', 'bill', 'heather'] |
* | names.map(&:capitalize.to_proc) # => ["Bob", "Bill", "Heather"] |
You can make this code even shorter. As it turns out, you can apply the & operator to any object, and it will take care of converting that object to a Proc by calling to_proc. (You didn’t think we picked the name of the to_proc method randomly, did you?) So, you can simply write the following:
| names = ['bob', 'bill', 'heather'] |
* | names.map(&:capitalize) # => ["Bob", "Bill", "Heather"] |
That’s the trick known as Spell: . Neat, huh?
The good news is that you don’t have to write Symbol#to_proc, because it’s already provided by Ruby. In fact, Ruby’s implementation of Symbol#to_proc also supports blocks with more than one argument, which are required by methods such as inject:
| # without Symbol#to_proc: |
| [1, 2, 5].inject(0) {|memo, obj| memo + obj } # => 8 |
| |
| # with Symbol#to_proc: |
| [1, 2, 5].inject(0, &:+) # => 8 |
| |
| # cool! |