What's This &Block in Ruby? and How Does It Get Passed in a Method Here

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

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.

In ruby, what does &block do?

Blocks give you an opportunity to state a callback to pass on to a method.

The & is key here - like @pst mentioned, it "promotes" the block to a Proc and binds the Proc to the variable with the given name.

With &

def time(&block)
puts block
end

time
# => nil
time { foo }
# => #<Proc:0x00029bbc>

Without &

def time(block)
puts block
end

time { foo }
# => ArgumentError: wrong number of arguments (0 for 1)
# Because & isn't included, the method instead expected an arguement,
# but as a block isn't a arguement an error is returned.

Difference between block and &block in Ruby

block is just a local variable, &block is a reference to the block passed to the method.

def foo(block = nil)
p block
end

foo # => nil
foo("test") # => test
foo { puts "this block will not be called" } # => nil

def foo(&block)
p block
end

foo # => nil
foo("test") # => ArgumentError: wrong number of arguments (1 for 0)
foo { puts "This block won't get called, but you'll se it referenced as a proc." }
# => #<Proc:0x0000000100124ea8@untitled:20>

You can also use &block when calling methods to pass a proc as a block to a method, so that you can use procs just as you use blocks.

my_proc = proc {|i| i.upcase }

p ["foo", "bar", "baz"].map(&my_proc)
# => ["FOO", "BAR", "BAZ"]

p ["foo", "bar", "baz"].map(my_proc)
# => ArgumentError: wrong number of arguments (1 for 0)

The variable name block doesn't mean anything special. You can use &strawberries if you like, the ampersand is the key here.

You might find this article helpful.

How to use &block in Ruby

After think and searched in google I found the best way to do this, and understood how to use &blocks and yields in Ruby.

In the bigining, my problem was this repeted code:

  def horizontally_position
res = []
for x in position[0]..end_coord(0)
res << [x, position[1]]
end
return res
end

def vertically_position
res = []
for y in position[1]..end_coord(1)
res << [position[0], y]
end
return res
end

The first thing I do to aboid the repeated code was use a yield:

  def position_by(coord) # ²
res = []
for n in position[coord]..end_coord(coord)
res << yield(n) # ³
end
return res
end

def horizontally_position # ¹
position_by(0) { |n| [n, position[1]] } # ⁴
end

def vertically_position # ¹
position_by(1) { |n| [position[0], n] } # ⁴
end

¹ When this method will called, it will call to position_by(1) or position_by(0).

² This method will start to execute and when it comes to << yield(n)...

³ It will come again to vertically_position" or horizontally_position and will replace the yield in position_by for the code in brackets in horizontally_position (or vertically_position). Sorry for the redundancy.

After do that I saw the method position_by(coord) and wish to refactor it a little.

First refactor, using yield:

  def position_by(end_coor)
position[end_coor]..end_coord(end_coor).map do |x|
yield x
end
end

Then use a &block:

  def position_by(coord, &block) # ⁵
(position[coord]..end_coord(coord)).map &block
end

⁵ Here we are mapping each position to a & block , and a block can be [ x , position ( 1) ] or [position ( 0 ) , y] , Where x or and will be replace for the position [ coord ] corresponding.

What does with(&block) mean in Ruby?

It's defined as part of the connection_pool gem which is used by sidekiq, and it's source is below. It looks like it's purpose is to obtain a connection from the pool, yield it to the provided block, and then release the connection back to the pool.

here's how I found that out:

 pry> redis = Sidekiq::RedisConnection.create({})
pry> redis.method(:with).source

def with
conn = checkout
begin
yield conn
ensure
checkin
end
end

pry> redis.method(:with).source_location

["./ruby/gems/2.0.0/gems/connection_pool-1.1.0/lib/connection_pool.rb", 46]

And to identify the dependency:

~$ bundle exec gem dependency connection_pool --reverse-dependencies

Gem connection_pool-1.1.0
minitest (>= 5.0.0, development)
Used by
sidekiq-2.16.0 (connection_pool (>= 1.0.0))

Pass block passed to method to another method in Ruby

You can reference the block explicitly

def discard(&block)
self - self.keep(&block)
end

or implicitly

def discard
self - self.keep(&Proc.new {})
end

In your case, I would suggest the first approach.

How does to_enum(:method) receive its block here?

The answer is but a click away: the documentation for Enumerator:

Most [Enumerator] methods [but presumably also Kernel#to_enum and Kernel#enum_for] have two forms: a block form where the contents are evaluated for each item in the enumeration, and a non-block form which returns a new Enumerator wrapping the iteration.

It is the second that applies here:

enum = [4, 1, 2, 0].to_enum(:count) # => #<Enumerator: [4, 1, 2, 0]:count> 
enum.class # => Enumerator
enum_ewi = enum.each_with_index
# => #<Enumerator: #<Enumerator: [4, 1, 2, 0]:count>:each_with_index>
enum_ewi.class # => Enumerator
enum_ewi.each {|elem, index| elem == index} # => 2

Note in particular irb's return from the third line. It goes on say, "This allows you to chain Enumerators together." and gives map.with_index as an example.

Why stop here?

    enum_ewi == enum_ewi.each.each.each # => true
yet_another = enum_ewi.each_with_index
# => #<Enumerator: #<Enumerator: #<Enumerator: [4, 1, 2, 0]:count>:each_with_index>:each_with_index>
yet_another.each_with_index {|e,i| puts "e = #{e}, i = #{i}"}

e = [4, 0], i = 0
e = [1, 1], i = 1
e = [2, 2], i = 2
e = [0, 3], i = 3

yet_another.each_with_index {|e,i| e.first.first == i} # => 2

(Edit 1: replaced example from docs with one pertinent to the question. Edit 2: added "Why stop here?)

Ruby - Passing Blocks To Methods

The code by David will work fine, but this is an easier and shorter solution:

foo = Proc.new { |prompt| prompt.echo = false }
new_pass = ask("Enter your new password: ", &foo)
verify_pass = ask("Enter again to verify: ", &foo)

You can also use an ampersand to assign a block to a variable when defining a method:

def ask(msg, &block)
puts block.inspect
end

Blocks and yields in Ruby

Yes, it is a bit puzzling at first.

In Ruby, methods can receive a code block in order to perform arbitrary segments of code.

When a method expects a block, you can invoke it by calling the yield function.

Example:

Take Person, a class with a name attribute and a do_with_name method. When the method is invoked it will pass the name attribute to the block.

class Person 
def initialize( name )
@name = name
end

def do_with_name # expects a block
yield( @name ) # invoke the block and pass the `@name` attribute
end
end

Now you can invoke this method and pass an arbitrary code block.

person = Person.new("Oscar")

# Invoking the method passing a block to print the value
person.do_with_name do |value|
puts "Got: #{value}"
end

Would print:

Got: Oscar

Notice the block receives as a parameter a variable called value. When the code invokes yield it passes as argument the value of @name.

yield( @name )

The same method can be invoked with a different block.

For instance to reverse the name:

reversed_name = ""

# Invoke the method passing a different block
person.do_with_name do |value|
reversed_name = value.reverse
end

puts reversed_name

=> "racsO"

Other more interesting real life examples:

Filter elements in an array:

 days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]  

# Select those which start with 'T'
days.select do | item |
item.match /^T/
end

=> ["Tuesday", "Thursday"]

Or sort by name length:

 days.sort do |x,y|
x.size <=> y.size
end

=> ["Monday", "Friday", "Tuesday", "Thursday", "Wednesday"]

If the block is optional you can use:

yield(value) if block_given?

If is not optional, just invoke it.

You can try these examples on your computer with irb (Interactive Ruby Shell)

Here are all the examples in a copy/paste ready form:

class Person 
def initialize( name )
@name = name
end

def do_with_name # expects a block
yield( @name ) # invoke the block and pass the `@name` attribute
end
end


person = Person.new("Oscar")

# Invoking the method passing a block to print the value
person.do_with_name do |value|
puts "Got: #{value}"
end


reversed_name = ""

# Invoke the method passing a different block
person.do_with_name do |value|
reversed_name = value.reverse
end

puts reversed_name



# Filter elements in an array:
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]

# Select those which start with 'T'
days.select do | item |
item.match /^T/
end



# Sort by name length:
days.sort do |x,y|
x.size <=> y.size
end


Related Topics



Leave a reply



Submit