Share

Procs and Blocs is the part of Ruby which is not clear to many. This post should help you clear your doubts about Procs and Blocks.

Procs

Like variables hold data, procs hold the code itself.
Procs are first class objects of ruby with class Proc
We define a Proc as follows:

my_proc = Proc.new{ puts “I am a Proc}
#or
my_proc = Proc.new do
puts “I am a Proc
end

The first syntax is generally used for one liner Procs and the second one for multiline procs. Though there will be no errors if you do the other way, it is a good convention to follow.
Executing a Proc
To execute the Proc we do

my_proc.call # I am a Proc

Procs can accept parameters:

#Proc accepting  parmeters
#single parameter, single line proc

print_name = Proc.new{|name| puts “my name is #{name}”}
print_name.call(“pankaj”) # “my name is pankaj”

#multiple parameters, single line proc
print_full_name = Proc.new{|first_name,last_name| puts "My name is #{first_name} #{last_name}"}
print_full_name.call("Pankaj","Bhageria") # My name is Pankaj Bhageria

#single parameter, multiple line proc
display_mult_table = Proc.new do |number|
for i in 1..5
puts "#{number} * #{i} = #{number*i}"
end
end
display_mult_table.call(2)
# 2 * 1 = 2
# 2 * 2 = 4
# 2 * 3 = 6
# 2 * 4 = 8
# 2 * 5 = 10

#multiple parameter, multiple line proc
display_mult_table = Proc.new do |number,limit|
 for i in 1..limit
  puts "#{number} * #{limit} = #{number*i}"
 end
end
display_mult_table.call(2,3)
# 2 * 1 = 2
# 2 * 2 = 4
# 2 * 3 = 6

Note how the parameters are passed to the Proc definition in between the two vertical bars.

Passing a Proc to a method
As a proc is just another object, it can be passed around in methods, returned by methods etc. Suppose we want a block of code to be executed a certain number of times.

def run_code(times, code)
 for i in 1..times # note the use of  range here
  code.call(i)
 end
end
my_proc = Proc.new{|x| puts “I have been called #{x} times”}
run_code(5,my_proc)
#output:
#   I have been called 1 times
#   I have been called 2 times
#   I have been called 3 times
#   I have been called 4 times
#   I have been called 5 times



Blocks
Blocks like procs are used to pass code to methods, but they are like undefined procs. ie they are not assigned to any variable, they are just passed to the methods. It will be more clearer when you see the examples.
They can be passed to any method, the method may or may not use them.
Then how does the method execute the block, as there is no parameter to reference it. The method can call the block, by giving the command “yield” which executes the code in the block.

def some_method
# body of the method
end
#This is how to pass a block
some_method(){puts "I am passing a block"}
some_method{puts "I am passing a block"}

#Let us define a method which uses a block

def run_my_code
 yield
end

run_my_code{puts "I am passing a block"}
# I am passing a block

#passing multiline blocks
run_my_code do
 puts "I am passing a multiline  block"
 puts "This is the second line of the block"
end
#I am passing a multiline  block
#This is the second line of the block

Blocks can be passed along with the other parameters of the method.

def run_my_code times
 for i in 1..times
  yield
 end
end

run_my_code(2) { puts "passing a block along with params"}

#passing a block along with params
#passing a block along with params

run_my_code(2) do
puts "line1: passing a multi-line block along with params"
puts "line2: passing a multi-line block along with params"
end
#line1: passing a multi-line block along with params
#line2: passing a multi-line block along with params
#line1: passing a multi-line block along with params
#line2: passing a multi-line block along with params

Blocks can accept parameters like methods

def print_names users
 for user in users
  name = user[:first_name] + " " + user[:last_name]
  yield(name)
 end
end
print_names [{:first_name=> "Pankaj", :last_name=>"Bhageria"},{:first_name=> "David", :last_name=>"Jones"}] { |x| "Name is #{x} "}
#Name is Pankaj Bhageria
#Name is David Jones

Note that in the above example you can change the format of printing anytime by just changing the call to the method. You donot need to modify the method.
So this seperates the 2 things -

Working on the data: Here we are simply calculating the full name of the user, in real life it could have been more complex -

Using the result: Here we are passing the result to the output stream. We can store it to a database/file system or something.

If we need to store the result in a database/file we just need to modify the block passing along with the method.

Use of Ampersand(&)
Ampersand is used to convert a proc to a block and a block to a proc.
If a method want to accept a block but wants to use it as a proc, then add an & in front of the last parameter(the parameter which will receive the block.

def run_my_code(&my_code)
 my_code.call
end
run_my_code { puts "passing a block, accepting a proc"}
#passing a block, accepting a proc

Note above that we are using “my_code.call” and not yield.

If you have a proc and you want to pass it to a method which accepts block, then when passing use a ampersand before the proc parameter.

def run_my_code
 yield
end
my_proc = Proc.new  { puts "passing a proc instead of block"}
run_my_code my_proc #error, wrong number of arguments
run_my_code &my_proc # passing a proc instead of block

So we have successfully passed a proc to a method which accepts a block

Important points
Only one block can be accepted by a method.
A block can be passed to any method, no need to define it.
To pass a proc instead of a block append an “&” in front of the Proc.

Lambda

my_lambda = lambda {|x| puts x}
my_lambda.call("Ruby")
#Ruby

Lambda is exactly same as a Proc with some minor difference, which we will take up in another post.

Share