Explain Ruby on Rails Iterator Syntax

I started learning Ruby on Rails and found myself confused by the syntax, so I had to read about somet of the Ruby syntax. I learned the syntax of 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 that an iterator works through a loop, but I donโ€™t understand how exactly I should read this or what happens in this syntax. I see this all the time in RoR screencasts, and the words make sense, but I really don't know what is going on. Can anyone explain this to me?

edit: example

 respond_to do |format| format.json format.xml { render :xml => @posts } end 
+4
source share
7 answers

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

Another syntax for this is:

 method_call { |var| do_something(var) } 

Basically, you say that for each element in the iteration, name it โ€œvarโ€ and do something with that element. The method simply calls your block, which you passed in, because it "gives" elements to it.

Does it help?

edit: In your example, you use the iterator template in a funny way ... perhaps only by passing one format object to your block, so you can tell it which formats to process and what to do when you see it.

In other words, they use a template to create a DSL collation that allows you to customize what you are responding to.

+4
source

In the case of iterators, think of them as 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 "everyone" method, which takes a block (i.e. closure, anonymous function).

Blocks are used universally in Ruby. Imagine you have this array:

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

Here you create an array, and then you call the "each" method on it. You give him a block. You can separate it 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 allows you to iterate over key-value pairs:

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

Do..end can be replaced with curly braces, for example:

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

This iteration is made possible by the functional programming that Ruby uses: if you create a class that needs to iterate over something, you can also implement each method. Consider:

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

All kinds of classes implement interesting functionality using this block template: for example, look at the String method each_line and each_byte.

+2
source
 method_call do [`|' expr...`|'] expr...end 

Not limited to iterative functions.

In ruby, any method can take a block as an argument. Then the block can be called by the method. In the case of an iterator, the method looks something like this:

 def iter for i in [:x,:y,:z] yield i end end 

If you call iter with a block, it will iterate over [:x, :y, :z] and give each of them to the block, which can then do whatever you want. for example print them:

 iter { |z| puts z } 

You can also use this to hide initialization and cleaning steps, such as opening and closing files. e.g. File.open . File.open, if it was implemented in pure ruby โ€‹โ€‹(it is in C for performance), will do something like this.

 def File.open filename, opts f = File.new filename, opts yield f f.close end 

That is why you can use

 File.open 'foobar', 'w' do |f| f.write 'awesome' end 

respond_to similar. It works something like this :( check out the real implementation here )

  def respond_to responder = Responder.new(self) block.call(responder) responder.respond end 

Creates a responder object that has methods of type html that take a block and pass it to you. This is very convenient because it allows you to do things like:

 def action @foo = Foo.new params[:foo] respond_to do |format| if @foo.save format.html { redirect_to foo_path @foo } format.xml { render :xml => @foo.to_xml } else flash[:error] = "Foo could not be saved!" format.html { render :new } format.xml { render :xml => {:errors => @foo.errors }.to_xml} end end end 

See how I change the behavior depending on persistence inside the block? Doing this would be much more annoying without him.

+2
source
 <function> do |<temp variable>| <code to operate on temp variable> end 

This creates a temporary anonymous function that takes an element into a temporary variable, and then allows things to work with that element. An anonymous function is passed to the original <function> specified to work with the elements provided by this function.

+1
source

As you can see, there is a block of code, the syntax is a bit inconvenient on the first look.

So, basically, with iterators, you have a โ€œthingโ€ that can be repeated, and it gets a block to know what to do.

For example, the Range class has a method called โ€œeach,โ€ which gets a block of code to execute on each element in a range.

Say you want to print it:

 range = 1..10 #range literal range.each {|i| puts i } 

Code: {|i| puts i} {|i| puts i} is a block that says what to do when this range of iteration over each of its elements. The alternative syntax is the one you posted:

  range.each do |i| puts i end 

These blocks are used with iterators, but they are not limited to the "iteration" code, you can use them in other scripts, for example:

 class Person def initialize( with_name ) @name = with_name end # executes a block def greet yield @name #passes private attribute name to the block end end p = Person.new "Oscar" p.greet { |n| puts "Name length = #{n.length}" puts "Hello, #{n}" } 

Fingerprints:

 Name length = 5 Hello, Oscar 

So, instead of having a greet method with fixed behavior using a block, let the developer indicate what to do, which is very useful for iterators, but since you are simply not witnessing more than one place. In your case, this block allows you to specify what to do in the respond_to method.

+1
source

The documentation you are reading is ancient - almost prehistoric. If web pages could collect dust, it would have a thick layer.

Try the reference material on the ruby-lang website . In addition, the book Programming Ruby (pickaxe) is an important reference.

0
source

I think you could call it an iterator, because often a block function is called more than once. How in:

 5.times do |i| puts "#{i} " end 

Behind the scenes, the following steps are taken:

  • The times method of the instance of object 5 is called, passing the code puts "#{i} " in the instance of the Proc object.
  • Inside the times method, this code is called inside the loop, passing the current index as a parameter. What times may look like ( it's in C, actually ):

 class Fixnum def times_2(&block) # Specifying &block as a parameter is optional return self unless block_given? i = 0 while(i < self) do yield i # Here the proc instance "block" is called i += 1 end return self end end 

Note that the region (i.e. local variables, etc.) is copied to the block function:

 x = ' ' 5.times do { |i| puts "#{i}" + x } 
0
source

Source: https://habr.com/ru/post/1316335/


All Articles