Posts Tagged ‘ampersand’

Ruby Enumerable Magic: Unary Ampersand Operator

December 1, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

You’ve probably seen this example in my previous post, or in other Ruby code:

irb(main):001:0> ['a', 'b', 'c'].map(&:capitalize)
=> ["A", "B", "C"]

This is a cool trick, which equates to:

['a', 'b', 'c'].map{|letter| letter.capitalize}

Most Ruby developers have seen this idiom, and even used it, but you may not know how it works. This is *not* strictly a piece of Enumerable magic, even though this is probably where you see it used most often. We can all harness this power, but much like the Force it can be used for good or evil.

There are really two pieces of magic used together to make this possible. The first is the unary ampersand operator. Contrary to what you might think, there is no “&:” operator in Ruby. What you’re seeing in my first example above is actually “&” and “:capitalize”, pushed together. That first character is the unary ampersand. The second piece is a Ruby symbol that is being passed to the operator.

When Ruby sees the unary ampersand on the last argument of a method, it tries to convert it to a proc (an executable block of code), and run it. It does this by calling to_proc on the object. This is where the second piece of magic comes in. Rails 1.1 added a to_proc method to the Symbol class, so that you could pass a symbol to the unary ampersand (ie, &:capitalize) and it would convert it for you. Ruby 1.8.7 rolled this change into Ruby itself, so you can use it outside of Rails.

Digging into “The Why” can be fun, but what I really wanted to know is how I could harness this Ruby voodoo (or, if you will, “rooboo”) for my own selfish gain. That was, by far, the hardest part of putting together this guide. The good news is, once I figured it out, it’s easy to pass on.

Let’s use the example of a Translator class with one public method: speak. This class has a method for each of the languages it knows, and a catch-all method (Ruby’s method_missing) for languages it doesn’t understand:

class Translator
  def speak &language
    language.call(self)
  end
  
  protected
  
  def french
    'bon jour'
  end
  
  def spanish
    'hola'
  end
  
  def turkey
    'gobble'
  end
  
  def method_missing *args
    'awkward silence'
  end
end

This is awesome! Let’s play with it in irb:

irb(main):001:0> require 'translator.rb'
=> true
irb(main):002:0> translator = Translator.new
=> #<Translator:0x10038ff08>
irb(main):003:0> translator.speak(&:spanish)
=> "hola"
irb(main):004:0> translator.speak(&:turkey)
=> "gobble"
irb(main):005:0> translator.speak(&:italian)
=> "awkward silence"

The key to this magic is in the speak method:

  def speak &language
    language.call(self)
  end

There’s a lot going on here. When Ruby converts :turkey into a proc from a symbol, it makes the assumption that a receiver will be passed into the proc, and the proc will be called *on* that receiver. In this case, we pass self into the proc above. This is like calling self.turkey. Gobble.

Is this a super useful example? Maybe not. But it lets you play with the smallest possible working example, and get comfortable. If you have any questions, please leave a comment below.

Ruby Enumerable Magic: The Basics

November 30, 2010

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates
 

You’re probably familiar with Ruby’s Enumerable module, even if you don’t know it by that name. It adds neat methods to arrays, like map, inject, select, and so on. You might have thought (like I did for a long time) that these methods were *array* methods, but they’re not.

Ruby’s arrays use Enumerable, and you can as well, in any class you want. Of course, the class should represent a collection of things, or all the iterative methods in Enumerable wouldn’t make much sense. Let’s say we have a Team class, that manages a group of members:

class Team
  include Enumerable
  
  attr_accessor :members
  
  def initialize
    @members = []
  end
  
  def each &block
    @members.each{|member| block.call(member)}
  end
end

Enumerable requires that your class contain an each method that serves up the items in the collection. All the other Enumerable methods rely on this. Now we can use the map method, for instance:

irb(main):001:0> require 'team.rb'
=> true
irb(main):002:0> team = Team.new
=> #<Team:0x100391088 @members=[]>
irb(main):003:0> team.members = ['joshua', 'gabriel', 'jacob']
=> ["joshua", "gabriel", "jacob"]
irb(main):004:0> team.map{|member| member.capitalize}
=> ["Joshua", "Gabriel", "Jacob"]

Now we can call any Enumerable methods on our team itself, and it will assume we want to work with the members array within. Enumerable can be a powerful mix-in to your own classes. We can even take this a step further, and clean up our call to map:

team.map(&:capitalize)

If you haven’t seen this before, it’s called the unary ampersand operator, and it’s the subject of my next post.


Follow

Get every new post delivered to your Inbox.