Blocks
A block is one of those blobs of code we see all the time:
``` ruby
1..10.each do |n|
puts "The #{n.ordinalize} time around..."
end
```
or
``` ruby
1..10.each {|n| puts "The #{n.ordinalize} time around"}
```
How do blocks work?
Methods that take blocks can call yield to execute them.
``` ruby
def take_a_block
puts "Before the yield..."
yield
puts "... after the yield"
end
```
What if there's no block?
``` ruby
take_a_block
# => throws a LocalJumpError: no block given (yield)
```
To fix this, check first!
``` ruby
def take_a_block
puts "Before the yield..."
yield if block_given?
puts "... after the yield"
end
```
Block parameters
Blocks can take arguments:
``` ruby
def countdown
(1..99).reverse_each do |n|
yield n if block_given?
end
end
countdown {|n| puts "#{n} bottles of beer..."}
99 bottles of beer...
98 bottles of beer...
...
```
Reference a block
Blocks can be declared as arguments and re-used:
Can’t yield in this case, use call instead.
``` ruby
def countdown(&block)
(1..99).reverse_each do |n|
block.call n if block_given?
if n < 10
puts " Oy, I'm starting to repeat myself!"
block.call n if block_given?
end
end
end
countdown {|n| puts "#{n} bottles of beer..."}
...
1 bottles of beer...
Oy, I am starting to repeat myself!
1 bottles of beer...
```
Procs
"Proc objects are blocks of code that have been bound to a set of local variables. Once bound, the code may be called in different contexts and still access those variables."
Source: https://ruby-doc.org/core-2.4.1/Proc.html
Are Blocks Procs?
``` ruby
def take_a_block(&block)
puts "My block is a #{block.class}!"
end
take_a_block { "foo" }
My block is a Proc!
```
Save a proc to a variable
Procs can be saved to a var and passed as an argument to a method in lieu of a block.
``` ruby
my_proc = Proc.new {|n| puts "#{n} bottles of beer..."}
```
Proc.new takes a block... wha?
``` ruby
my_proc.call("Lots of ")
Lots of bottles of beer...
countdown(&my_proc)
99 bottles of beer...
98 bottles of beer...
...
```
Blocks vs. Procs
Blocks are not procs, procs are not blocks.
Procs are objects that take a block on creation and execute that block on Proc#call.
Blocks are syntax that allows us to pass a blob of code into a method expecting it.
But our block was a Proc...
``` ruby
def take_a_block(&foo)
puts "My block is a #{foo.class}!"
end
take_a_block { "foo" }
My block is a Proc!
```
The &foo syntax tells the method to take the block given and convert it implicitly into a Proc object!
Procs to Blocks?
``` ruby
def countdown_yield
(1..99).reverse_each do |n|
yield n if block_given?
end
end
countdown_yield(&my_proc)
99 bottles of beer...
98 bottles of beer...
...
```
The &my_proc syntax "does two things: it triggers a call to my_proc's to_proc method, and it tells Ruby that the resulting Proc object is serving as a code block stand-in." - The Well-Grounded Rubyist
Fun with procs
Let's repeat that: & triggers to_proc
``` ruby
Instead of
('a'..'z').map {|char| char.capitalize}
=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", ...]
```
``` ruby
Try this
('a'..'z').map &:capitalize
=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", ...]
```
The & triggers Symbol#to_proc which returns a Proc object that responds with the method named by the symbol.
Symbol#to_proc
How does Symbol#to_proc work?
``` ruby
class Symbol
def to_proc
Proc.new do |arg0, *args|
arg0.send(self, *args)
end
end
end
```
Thanks, Nick!