The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Ruby: Invoking a method with a ampersand-parameter

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Jay Fields

Posts: 765
Nickname: jayfields
Registered: Sep, 2006

Jay Fields is a software developer for ThoughtWorks
Ruby: Invoking a method with a ampersand-parameter Posted: Mar 15, 2007 9:54 PM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Jay Fields.
Original Post: Ruby: Invoking a method with a ampersand-parameter
Feed Title: Jay Fields Thoughts
Feed URL: http://feeds.feedburner.com/jayfields/mjKQ
Feed Description: Blog about Ruby, Agile, Testing and other topics related to software development.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Jay Fields
Latest Posts From Jay Fields Thoughts

Advertisement
On a few ocassions recently I've been asked what the & does when it is used in the context of a method invocation parameter. For example, consider the following code, specifically line 6 where collect is called.
1. # traditional
2. [1,2,3].collect { |number| number % 2 } #=> [1,0,1]
3.
4. # collect with a previously created block
5. block = lambda { |number| number % 2 }
6. [1,2,3].collect &block #=> [1,0,1]
It's fairly easy to see what is going on from the above example; however, more often I run into code that looks like the following code.
def clone_collect(&block)
clones = collect { |item| item.dup }
clones.collect &block
end
The previous example shows a method that clones each item and then passes on the block to the collect method of the clones array. The above code seems to be a tripping point, I assume it's because the block is defined outside the method definition.

Using a ¶meter with a method invocation is explained very well within PickAxe:
Invoking a Method
[ receiver. ] name [ parameters ] [ block ] 
parameters: ( [ param, ... ] [ , hashlist ] [ *array ] [ &a_proc ] )
block: { blockbody }
do blockbody end
...

A block may be associated with a method call using either a literal block (which must start on the same source line as the last line of the method call) or a parameter containing a reference to a Proc or Method object prefixed with an ampersand character.
Another good explanation can also be found in PickAxe:
If the last argument to a method is preceded by an ampersand, Ruby assumes that it is a Proc object. It removes it from the parameter list, converts the Proc object into a block, and associates it with the method.
An understanding of the above is crucial if you want to comprehend how the following code works within a Rails codebase.
area_codes = phone_numbers.collect &:area_code
Reducing the following statement you can see that area_codes is being set equal to the result of collect being called on phone_numbers. The phone_numbers variable is an array of PhoneNumber instances. The PhoneNumber class has an area_code attribute.

So, the only mystery is what does &:area_code do? As previously stated, if the last parameter is prefixed with an &, ruby removes it from the parameter list, and converts the Proc object into a block. The conversion from a parameter into a block is done through the to_proc method of the parameter. Therefore, by defining the to_proc method it is possible to alter the behavior of any parameter passed to a method that expects a block.
class Greeter
def to_proc
lambda { "hello world" }
end
end

def greet
yield
end

greet &Greeter.new #=> "hello world"
Applying this idea to the String class you could define to_proc as below and create blocks using strings and eval.
class String
def to_proc
text = self
lambda { eval text }
end
end

instance_eval &"2 + 2" #=> 4
Along the same lines, Rails defines the Symbol.to_proc method to create a new proc which takes args. The first element of args is expected to be the item from the collection. The item from the collection is sent the symbol (self from the code below) which is the symbol that specifies which attribute to return. The full code can be seen below.
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
The result is a proc that allows you to iterate the collection and return an array of the attribute that is specified by the symbol (:area_code in our example).

The following code should be fully executable and show the concept in entirety.
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end

PhoneNumber = Struct.new :area_code
*phone_numbers = PhoneNumber.new(904), PhoneNumber.new(646), PhoneNumber.new(616)
area_codes = phone_numbers.collect &:area_code #=> [904, 646, 616]

Read: Ruby: Invoking a method with a ampersand-parameter

Topic: Ruby on Rails + Ajax HowTo: create a unique beahvior for Ajax callbacks Previous Topic   Next Topic Topic: Cruise Control - A continuous integration tool for Ruby on Rails

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use