After learning a bit about Ruby and the basic way that data is stored and manipulated I came across the concept of the Block.
I was enthralled...and bewildered.
The concept seemed simple enough. Blocks are pieces of code, or instructions, that are passed to a function.
Amazing! Passing functions to other functions! This sounds highly advanced. I confess my first thought "Why would I ever want to pass a function to another function"?
This blog entry will begin with the syntax used to pass a Block to a function and finish with a discussion on the usefullness of being able to pass code to a function.
So first a bit about Blocks in general. A Block is a bit of code that follows a function call and is contained within a set of braces {}
or do end
delimeters. There is a common convention that braces should be used where the Block is only one line and do end
delimeters are used where the code extends over multiple lines. Examples below...
some_function(arguments) { puts "Short Code Example" }
some_function(arguments) do
puts "This is a style"
puts "commonly used for code in a block"
puts "fitting over multiple lines"
end
Jim Weirich discusses another convention specific to the presence of a return value in his
blog entry.
Regardless of how the Block is contained it is always listed after a message on the same line as the message call.
Example 0:
object.message(arguments) {block}
Example 1:
3.each { puts "I can't stop printing!"}
Console...
I can't stop printing!
I can't stop printing!
I can't stop printing!
Example 2:
array = %w[ geography art science ]
array.each do |subject|
puts "I love #{subject}"
end
Console:
I love art
I love science
I love math
The second example demonstrates how a Block can accept an argument passed to it from the function. In this case it is the subject or value of the array.
So a Block is a piece of logic that is passed to a function. But other than uitlizing simple pre-built iterators how can I unleash a Block within my own code? How can I write a function that accepts a Block?
Blocks are implicitly accepted into any function. The function can be written to accomodate the presence of a Block and default to a specific behavior if no Block is provided if block_given?
To utilize a Block within a function the yield
tag is used. When the function reaches yield
the Block is invoked. If arguments are to be passed to the Block they are listed after yield
. ( yield arguments
)
def type_of_day
if block_given?
puts yield
else
puts "I havn't used a block all day, this code sucks"
end
end
type_of_day { "I'm using a Block just like a real Ruby programmer!" }
So you can allow for there later use while still setting a default in case one isn't included in the message call.
Second... Blocks are particulary usefull when you find yourself writing lots of functions containing the same code. If you have five functions with code that differs only by one line... use a Block. Condense all of your functions into one well written function that contains a yield statement that accepts a Block with the differing code passed in.
Another similar example would be writing a function that contains a bit of logic that might change in the future. If you build a function that iterates through an array and performs a bit of logic on each index you could write a function that accepts a Block containing the logic to perform.
The following example shows the code that might exist for a program designed to automate yard maintenance. The Yard function has a public interface called maintain_garden
that contains several functions needed for garden upkeep.
Example:
class Yard
def maintain_garden
water_garden
weed_garden
yield
end
end
Utilizing a Block the maintain_garden
function is dynamically able to increase its functionality by accepting additional messages in the form of a Block. If we decide to add functions like fertilize_garden
and harvest_garden
we can pass them into the function without having to modify its default behavior.
Example:
first_yard = Yard.new
### During the winter
first_yard.maintain_garden
### During the spring
first_yard.maintain_garden { fertilize_garden }
### During the fall
first_yard.maintain_garden do
harvest_garden
till_garden
end
During the spring and the fall the default behavior of the maintain_garden function is called with the added functionality specified by the Block listed after the function call.
Block's can be used to make your functions more versatile allowing you to reduce duplication of code
(making your code more DRY). They can also be utilized where logic may change in the future. Instead of having to go back and manipulate the code within a function a Block can be used to pass new logic to the existing function.
👍 Great job! This is excellent stuff, can't wait to get it up. I'm going to enter nitpick mode now, so don't take anything personally. 😄 Just want to help you with your message.
Global find and replace
methodwith message. Being pedantic here.4th paragraph, make the quote italic
Transition between 4th ( Amazing!... ) and 5th ( So first a bit... ) Needs another sentence or something to smooth out. Related, paragraph 5 seems to be related to the Syntax section. Consider adding a transition paragraph, then moving paragraph 5 to under the Syntax header.
Also, in paragraph 5 make
do
/end
each look like a code tag.Consider discussing (even using this link) using braces {} for blocks that return values. This provides semantic cues to intent of block. Also, chaining looks nicer:
But this also has to do with operator precedence (see also do end vs curly braces)
To use the block you list it after a method...should say, To pass a block, you start it after the message on the same line...In the syntax section, make the console output a code block.
In the 1st console output section, the first
Printing
is capitalized, when it should beprint
lower case.So a block is a piece of logic that is passed to a function. But other than
uitlizingutilizing simple pre-built iterators how can I unleash a Block within my own code. How can I write a function thats takes a block. <== Missing some question marks here.Get ride of the
Well...Note, that ALL methods take an implicit block. It's up to you to decide to use it.
type_of_day
code sample is missing anend
after theelse
statementIndent spacing is off in the last two code samples.
Last two code samples are a bit long to show the usage of the block. It's not very clear when it's used. Trying making the example a little more terse.
spelling in 2nd to last paragraph
versitilewith versatileNot sure about the last paragraph. Seems a bit jarring / out of context.