Home arrow Ruby-on-Rails arrow Page 3 - Code Blocks and Iteration
RUBY-ON-RAILS

Code Blocks and Iteration


Code blocks can be very confusing to newcomers to Ruby, despite the fact that many computer languages have something that functions in a similar manner. This article, the first of three parts, introduces you to code blocks. It is excerpted from chapter eight of the Ruby Cookbook, written by Lucas Carlson and Leonard Richardson (O'Reilly, 2006; ISBN: 0596523696). Copyright © 2006 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Author Info:
By: O'Reilly Media
Rating: 5 stars5 stars5 stars5 stars5 stars / 4
March 01, 2007
TABLE OF CONTENTS:
  1. · Code Blocks and Iteration
  2. · 7.1 Creating and Invoking a Block
  3. · 7.2 Writing a Method That Accepts a Block
  4. · 7.3 Binding a Block Argument to a Variable

print this article
SEARCH DEVARTICLES

TOOLS YOU CAN USE

advertisement
Code Blocks and Iteration - 7.2 Writing a Method That Accepts a Block
(Page 3 of 4 )

Problem

You want to write a method that can accept and call an attached code block: a method that works like Array#each,Fixnum#upto, and other built-in Ruby methods.

Solution

You don’t need to do anything special to make your method capable of accepting a block. Any method can use a block if the caller passes one in. At any time in your method, you can call the block with yield:

  def call_twice
    puts "I'm about to call your block."
    yield
    puts "I'm about to call your block again."
    yield 
 
end

  call_twice { puts "Hi, I'm a talking code block." }
  # I'm about to call your block.
  # Hi, I'm a talking code block.
  # I'm about to call your block again.
  # Hi, I'm a talking code block.

Another example:

  def repeat(n)
   if block_given?
     n.times { yield }
    else
      raise ArgumentError.new("I can't repeat a block you don't give me!")
    end
  end

  repeat(4) { puts "Hello." }
  # Hello.
  # Hello.
  # Hello.
  # Hello.

  repeat(4) #
  ArgumentError: I can't repeat a block you don't give me!

Discussion

Since Ruby focuses so heavily on iterator methods and other methods that accept code blocks, it’s important to know how to use code blocks in your own methods.

You don’t have to do anything special to make your method capable of taking a code block. A caller can pass a code block into any Ruby method; it’s just that there’s no point in doing that if the method never invokesyield.

  puts("Print this message.") { puts "And also run this code block!" }
  # Print this message.

Theyieldkeyword acts like a special method, a stand-in for whatever code block was passed in. When you call it, it’s exactly as the code block were aProc object and you had invoked itscallmethod.

This may seem mysterious if you’re unfamiliar with the practice of passing blocks around, but it is usually the preferred method of calling blocks in Ruby. If you feel more comfortable receiving a code block as a “real” argument to your method, see Recipe 7.3.

You can pass in arguments toyield(they’ll be passed to the block) and you can do things with the value of theyield statement (this is the value of the last statement in the block).

Here’s a method that passes arguments into its code block, and uses the value of the block:

  def call_twice
    puts "Calling your block."
    ret1 = yield("very first")
    puts "The value of your block: #{ret1}"

    puts "Calling your block again."
    ret2 = yield("second")
    puts "The value of your block: #{ret2}"
  end

  call_twice do |which_time|
    puts "I'm a code block, called for the #{which_time} time."
    which_time == "very first" ? 1 : 2
  end
  # Calling your block.
  # I'm a code block, called for the very first time.
  # The value of your block: 1
  # Calling your block again.
  # I'm a code block, called for the second time.
  # The value of your block: 2

Here’s a more realistic example. The methodHash#findtakes a code block, passes each of a hash’s key-value pairs into the code block, and returns the first key-value pair for which the code block evaluates to true.

  squares = {0=>0, 1=>1, 2=>4, 3=>9}
  squares.find { |key, value| key > 1 }   # => [2, 4]

Suppose we want a method that works likeHash#find, but returns a new hash containing all the key-value pairs for which the code block evaluates to true. We can do this by passing arguments into theyieldstatement and using its result:

  class Hash
   
def find_all
      new_hash = Hash.new
      each { |k,v| new_hash[k] = v if yield(k, v) }
      new_hash
    end
  end

  squares.find_all { |key, value| key > 1 } # => {2=>4, 3=>9}

As it turns out, the Hash#delete_if method already does the inverse of what we want. By negating the result of our code block, we can makeHash#delete_ifdo the job ofHash#find_all. We just need to work off of a duplicate of our hash, becausedelete_ ifis a destructive method:

  squares.dup.delete_if { |key, value| key > 1 }  # => {0=>0, 1=>1}
  squares.dup.delete_if { |key, value| key <= 1 } # => {2=>4, 3=>9}

Hash#find_allturns out to be unnecessary, but it made for a good example.

You can write a method that takes an optional code block by callingKernel#block_ given?from within your method. That method returns true only if the caller of your method passed in a code block. If it returns false, you can raise an exception, or you can fall back to behavior that doesn’t need a block and never uses theyieldkeyword.

If your method callsyieldand the caller didn’t pass in a code block, Ruby will throw an exception:

  [1, 2, 3].each
  # LocalJumpError: no block given

See Also

   • Recipe 7.3, “Binding a Block Argument to a Variable”


blog comments powered by Disqus
RUBY-ON-RAILS ARTICLES

- Adding Style with Action Pack
- Handling HTML in Templates with Action Pack
- Filters, Controllers and Helpers in Action P...
- Action Pack and Controller Filters
- Action Pack Categories and Events
- Logging Out, Events and Templates with Actio...
- Action Pack Sessions and Architecture
- More on Action Pack Partial Templates
- Action Pack Partial Templates
- Displaying Error Messages with the Action Pa...
- Action Pack Request Parameters
- Creating an Action Pack Registration Form
- Ruby on Rails Templates and Layouts
- Action Pack Controller Creation
- Writing an Action Pack Controller

Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 



© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 5 - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials