Chamila Wijayarathna
Chamila Wijayarathna

Reputation: 1941

Understanding how ruby methods are invoked

I have a java background. I came up with a method in ruby which can be invoked as

Oj::Doc.open('[1,2,3]')

as well as

Oj::Doc.open('[1,2,3]') { |doc| doc.size() }

The first is similar to java method, but I don't understand what's happening in 2nd case. Can some one give me some clarification on this?

Upvotes: 0

Views: 43

Answers (3)

kiddorails
kiddorails

Reputation: 13014

Everything in Ruby is an Object. Below is an example of block. Block is nothing else but a block again.

 { |doc| doc.size() }

A method in ruby can accepts any number of arguments, and the arguments are nothing but objects. So passing a block to a method is just like passing an object to a method.

def a_method(arg)
  return arg + yield(arg) if block_given?
  arg
end
a_method(3) #=> 3
a_method(3) { |x| x + 4 } #=> 10

In above example, block_given? evaluates to true if a block is supplied. yield evaluates the block in context of self. self is nothing but current object. The block is evaluated and a closure can be passed to block (above, as x) which is evaluated and returned back to a_method.

Proving that block is an object is fairly easy with a special & operator:

def return_block(&block)
  block
end
return_block { |x, y| x + y }
#=> #<Proc:0x007fe28b53aa50@(pry):65>

Proc is object representation of a block.
In first example, you saw that a block was not explicitly mentioned in arguments list of a_method, but was available with special yield keyword. &block is the way to acquire the block as argument.

The first example can be re-written as following:

def a_method(arg, &block)
  return arg + block.call(arg) if block
  arg
end

a_method(3) #=> 3
a_method(3) { |x| x + 4 } #=> 10

Another way to create block object:

block = Proc.new { |x| x + 4 }
# Similarly, you can pass the block (object) as explicit argument
a_method(3, &block)

For your original question, you can also do:

block = Proc.new { |doc| doc.size() }
Oj::Doc.open('[1,2,3]', &block)

This enables you to manage blocks, objects and methods dynamically.

Pure awesome Ruby. <3

Upvotes: 2

vgoff
vgoff

Reputation: 11323

In Ruby, every method can take a block. Your 2nd form of code has an unnamed block being passed to the method, in addition to the arguments that you are passing in the 1st form of your code.

Many methods do not do anything with a block. But this will not cause an error. This method, if it actually uses the block, will call yield to evaluate the block from within the method.

When a Ruby method uses a block, it may look like this:

def ruby_method
  yield if block_given?
end

This example uses an 'anonymous' block and the yield returns control to the program from where the ruby_method was called.

Upvotes: 1

SteveTurczyn
SteveTurczyn

Reputation: 36860

You can pass a block to a method. A block is a closure that contains executable lines.

Blocks can be defined with a do end or with curly braces {} you've seen the curly braces version, here's the alternative...

Oj::Doc.open('[1,2,3]') do |doc| 
  doc.size()  
end

Not all methods need a block, but if it's passed it can be executed within the method using the yield keyword. They can also be converted into Procs or Lambdas which are objects (a plain block is not an object)

If the block takes one or more arguments, they're passed within the vertical bars. In this example...

[1, 2, 3].each { |number| print number }

... the block will be yielded to three times, once for each element in the array, and the array element is referenced by the number argument..

Upvotes: 1

Related Questions