Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explain Iterator Syntax on Ruby on Rails

I started learning Ruby on Rails and found myself confounded by the syntax, so I had to read about somet of the Ruby syntax. I learned the syntax from http://www.cs.auckland.ac.nz/references/ruby/doc_bundle/Manual/man-1.4/syntax.html:

method_call do [`|' expr...`|'] expr...end

They call it an Iterator. I understand an iterator runs through a loop, but I don't understand how exactly I'm supposed to read this or what's going on in in this syntax. I see it all the time in RoR screencasts and the words make sense, but I actually have no idea what's going on. Could anyone explain this to me?

edit: example

respond_to do |format|
    format.json
    format.xml { render :xml => @posts }
end
like image 234
Gio Borje Avatar asked Dec 28 '22 09:12

Gio Borje


2 Answers

Methods can take a construct called "Blocks". These are anonymous methods that get passed into the method.

Another syntax for this is:

method_call { |var| do_something(var) }

Basically, you are saying that for each item in an iteration, name it "var" and do something with that item. The method simply calls your block that you passed in as it "yields" items to it.

Does this help?

edit: In your example, you they are using the iterator pattern in a funny way... probably only passing one format object into your block, so you can then tell it which formats to handle, and what to do when you see it.

In other words, they are using the pattern to create a DSL of sorts that lets you configure what you respond to.

like image 134
Brian Genisio Avatar answered Dec 30 '22 23:12

Brian Genisio


In the case of iterators, think of them like an interface in Java: you can do a for-loop in Ruby, but all the objects that you might want to iterate over (should) implement the 'each' method which takes a block (i.e. a closure, an anonymous function).

Blocks are used all over the place in Ruby. Imagine you have this array:

[1, 2, 3, 4, 5, 6].each do |i| puts i.to_s end

Here, you are creating the array and then you are calling the 'each' method on it. You pass the block to it. You could separate this out, like this:

arr = [1, 2, 3, 4, 5, 6]
string_printer = lambda do |i| puts i.to_s end
arr.each(&string_printer)

This kind of interface is implemented in other things: the Hash collection lets you iterate over the key-value pairs:

{:name => "Tom", :gender => :male}.each do |key, value| puts key end

The do..end can be replaced with braces, like this:

[1, 2, 3, 4, 5, 6].each {|i| puts i.to_s }

This kind of iteration is made possible because of the functional-programming that Ruby employs: if you are creating a class that needs to iterate over something, you can also implement the each method. Consider:

class AddressBook
  attr_accessor :addresses
  def each(&block)
    @addresses.each {|i| yield i }
  end
end

All sorts of classes implement interesting functionality through this block pattern: look at String's each_line and each_byte method, for instance.

like image 30
Tom Morris Avatar answered Dec 30 '22 23:12

Tom Morris