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

Ruby on-RailsRuby

Ruby on-Rails Problem Overview


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

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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 - Ruby on-Rails

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 http://en.wikipedia.org/wiki/Closure_(computer_science)">closure</a> 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 http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/">Robert Sosinski's blog.

Solution 3 - Ruby on-Rails

Re attributes = {}, that's just a method argument with a default value. So if you call hidden_div_if(whatever), that is, passing only the first argument, attributes would default to an empty hash.

That's useful because it simplifies setting attributes["style"] later, as attributes doesn't have to be initialized to a hash first. (Which might nonetheless be done simply as (attributes ||= {})["style"] = ….)


&block is only slightly more complicated.

Ruby methods can take a last argument that is a block, using the special syntax method(args) { |block_args| block_code }. &block basically captures that block into the block variable as a Proc object. So block is just a variable pointing to an anonymous procedure here.

When later content_tag is called, and &block is passed as its last argument, it's expanded into a block, like if the call was indeed content_tag(…) { block originally passed to hidden_if_div }


So maybe I was really confusing here. What you should google for is "ruby default arguments" and "ruby blocks".

Solution 4 - Ruby on-Rails

Ruby implements Blocks, Procs and lambdas which are referred to as closures in the computer science community. If you beginning to learn Ruby you will quickly run into code that looks like this.

a = ["dog", "cat", "bird"]
a.alter_each! do |n, i|
  "#{i}_#{n}"
end

So whats going on here?

We start off with an array of animal names and call the alter_each! method passing a block. In this block of code we can specify how we want to alter each item. Our example will prefix each animal name with it's position in the array. As the alter_each! method iterates through each item it will execute our block passing the value and index. Our block captures these params, prefixes the index to the name and returns the result. Now lets look at the alter_each! method.

Notice the method doesn't specify any params, that's because a block is automatically assigned to yield keyword. yield is called like a function passing in the value and index of each item in the array and overriding the original value.

class Array
  def alter_each!
    self.each_with_index do |n, i|
      self[i] = yield(n,i)
    end
  end
end

What if you need to pass a param to this method?

You can modify the method signature to accept params and finally catch the block with a param starting with an ampersand. In the example below our block will be captured with the &block param which we will invoke the call method. This is in place of using yield

class Array
  def modify_each!(add_one = true, &block)
    self.each_with_index do |n, i|
      j = (add_one) ? (i + 1) : i
      self[i] = block.call(n,j)
    end
  end
end

Full article on ruby blocks

Solution 5 - Ruby on-Rails

It works like this:

@cart.items.empty? is the codition

:id => "cart" Becomes attributes as per convention you can remove {} on a param hash if it is last one.

the block is

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

so within the function if the cart is empty it will add the attribute style with value "display: none"

Then it will create a div tag filled with the content of the result of executing the block which will be the result of rendering the partial view cart with the contents of @cart.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMalik Daud Ahmad KhokharView Question on Stackoverflow
Solution 1 - Ruby on-RailsrampionView Answer on Stackoverflow
Solution 2 - Ruby on-RailsgaqziView Answer on Stackoverflow
Solution 3 - Ruby on-RailskchView Answer on Stackoverflow
Solution 4 - Ruby on-RailsjspoonerView Answer on Stackoverflow
Solution 5 - Ruby on-RailsMiquelView Answer on Stackoverflow