What's this &block in Ruby? And how does it get passed in a method here?

Saw this piece of code in a Ruby on Rails book. This first one is from a view and the second one is a helper module. I don't understand how that &block and the attributes={} thing work. Can anyone guide me to a tutorial of some kind explaining this?

<% hidden_div_if(@cart.items.empty?, :id => "cart") do %>
 <%= render(:partial => "cart", :object => @cart) %>
<% end %>

module StoreHelper
 def hidden_div_if(condition, attributes = {}, &block)
  if condition
   attributes["style"] = "display: none"
  end
   content_tag("div", attributes, &block)
  end
end

Solution 1:

Blocks are a fairly basic part of ruby. They're delimited by either do |arg0,arg1| ... end or { |arg0,arg1,arg2| ... }.

They allow you to specify a callback to pass to a method. This callback can be invoked two ways - either by capturing it by specifying a final argument prefixed with &, or by using the yield keyword:

irb> def meth_captures(arg, &block)
       block.call( arg, 0 ) + block.call( arg.reverse , 1 )
     end
#=> nil
irb> meth_captures('pony') do |word, num|
       puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
       word + num.to_s
     end
in callback! word = "pony" num = 0
in callback! word = "ynop" num = 1
#=> "pony0ynop1" 
irb> def meth_yields(arg)
       yield(arg, 0) + yield(arg.upcase, 1)
     end
#=> nil
irb> meth_yields('frog') do |word, num|
       puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
       word + num.to_s
     end
in callback! word = "frog", num = 0
in callback! word = "FROG", num = 1
#=> "frog0FROG1"

Note that our callback was the same in each case - we can remove repetition by saving our callback in an object, and then passing it to each method. This can be done using lambda to capture the callback in an object, and then passed to a method by prefixing it with &.

irb> callback = lambda do |word, num|
       puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
       word + num.to_s
     end
#=> #<Proc:0x0052e3d8@(irb):22>
irb> meth_captures('unicorn', &callback)
in callback! word = "unicorn", num = 0
in callback! word = "nrocinu", num = 1
#=> "unicorn0nrocinu1"
irb> meth_yields('plate', &callback)
in callback! word = "plate", num = 0
in callback! word = "PLATE", num = 1
#=> "plate0PLATE1"

It's important to understand the different uses of & here as a prefix to the last argument of a function

  • in a function definition, it captures any passed block into that object
  • in a function call, it expands the given callback object into a block

If you look around blocks are used all over the place, especially in iterators, like Array#each.

Solution 2:

The &block is a way of sending a piece of Ruby code in to a method and then evaluating that code in the scope of that method. In your example code above it means a partial named cart will be rendered in a div. I think the term closure is used for this in computer science.

So in your example the &block is:

<%= render(:partial => "cart", :object => @cart) %>

Some good reading and an explanation of blocks, procs and lamdas can be found at Robert Sosinski's blog.