An Intro To Ruby Blocks

Joseph Bianchi
6 min readNov 19, 2020

--

Gosh darn-it — what in tarnation are these free flowing pieces of code adjacent to this #method being invoked (scratches head). Hmm, I’m sure I must have seen something like this before.. somewhere; ah, but of-course doesn’t Array#each, oh and even perhaps Array#map have adjacent code next to the method after method invocation?

Ruby blocks can be found in every Ruby application and or script. Chances are you’ve used Ruby blocks without even knowing it — assuming you’ve used methods such as Array#each.

exhibit 1

We will dive into this code snippet as we progress through this article. But for now — it is important to recognize we have an array with 5 integers: [1, 2, 3, 4, 5] ; this instance of the Array class calls the #each method; adjacent to the #each method we find.. sound trumpets … a Ruby block. That’s it. We’re done! Although, you ARE welcome to read on if you’d please — I’m begging you.

What are Ruby Blocks?

  • In short: Ruby Blocks are anonymous functions that are invoked when a method is called.
  • In the above example (exhibit 1) using Array#each, the adjacent Ruby block took the form of (exhibit 2) :

exhibit 2

  • In the above code snippet, note the block’s syntax - this is one form of expressing a block (typically used when a block’s logic spans across multiple lines) — alternatively a block can be created using curly braces (as seen below). Both blocks are essentially the same.

exhibit 3

  • Between the ‘do/end’ or ‘{ }’ lives a block’s logic.
  • Ruby is full of syntactical sugar, and Ruby blocks are no exception. Ruby blocks are part of Ruby’s syntax — when the Ruby parser reads a block it automatically associates it with the method being invoked (#each) in our above example.

Basic Example

  • Here is a basic example of a Ruby block being used.
  • Here a method named #pass_a_block is defined . The method is then invoked, and passed an adjacent block. The block then gets executed from within the invoked method’s body.
  • Note the yield keyword is responsible for executing the block’s logic.

exhibit 4

How does a Block get Invoked within a Method’s Body?

  • Ok, now that we’ve created a method, and passed that method a block on invocation — you may be asking yourself: how did the block get executed?
  • As mentioned above, as soon as the Ruby parser recognizes a block next to a method being invoked, it will associate the block with the method being executed.
  • If the method being executed has the keyword yield within its body — yield will act as a liaison and execute the block within the context of the method.
  • In short, when the method is executed, the keyword yield is replaced with the logic from the bound block.
  • Essentially, you are providing mutable code to a defined method — sort of like the below example, although the original method is not being re-defined.

exhibit 5

  • Note: A block can always be passed or bound to a method — although, if the yield keyword is not in the respective method’s body — then the block will not be used (see below)

exhibit 6

The Return Value of a Block

  • Within a block the return key word is not explicitly used.
  • The return value of a block is implicit.
  • The last line of execution from within a block will be the returned value.
  • If yield is the last line executed in a method, then the method’s returned value will be the block’s returned value.
  • You are also able to assign the return value of yield (or the block execution) to a variable and further use it upon sequential logic within your method’s body (see below).

exhibit 6

Blocks and their Arguments

I purposefully omitted this topic in the last few sections — for the purpose of providing a familiar example (Array#each).

But, the time has come — there’s no point in arguing 😉.

  • Blocks can be written either incorporating arguments or not incorporating arguments. This is best demonstrated with an example (see below).

exhibit 7

exhibit 8

  • Blocks can be passed multiple arguments.
  • A block’s expected arguments are defined in a comma separated list between a set of pipes i.e. |argument_one, argument_two|
  • The arguments passed to yield i.e. yield(my_block_arg) are respective to a block’s arguments defined between its pipes i.e. |my_block_arg|
  • Let’s put some of the information we’ve learned together and write our own Array#each method.

exhibit 9

  • We first open the Array class and override the Ruby Array#each method with our own simplified #each method.
  • In this method we are simply creating an index variable, declared as i
  • We then loop over self (an instance of Array).
  • In the loop we call yield — passing in each element sequentially as we loop through the instance of Array.
  • When we invoke our #each method on the instance of Array i.e.[1, 2, 3, 4, 5].each — we pass a block to #each that accepts an argument (which will be the array element passed to yield from within the method’s body).
  • From there we simply output each Array element * 10 .

Lexical Environment of a Block

  • Blocks have access to all variables from within the environment in-which they are defined (see below).

exhibit 10

Implicit Blocks and Yield

  • In all our examples so far we pass blocks to methods implicitly i.e. never in our methods have we explicitly defined our method to expect a block.
  • This is because yield is a special Ruby keyword, it finds and calls a block that has been passed to a method.
  • We can explicitly pass blocks to methods — well sort of. I will discuss this in a future tutorial — or feel free to do some further research on blocks and procs if you simply can’t wait.

Making ‘yield’ Optional in Our Methods

  • The yield keyword will seek out and execute a blocks logic AND if yield is present in a method’s body — and that method is invoked without a block, you better believe it.. yield will complain.
  • To prevent an error you can write a method to optionally call yield only if the method is invoked with a block (see below).

exhibit 11

Explicit Blocks and Procs

  • Here is a sneak peak for those curious about methods called with explicit blocks:
  • I will dive deeper into the subject of Procs in a future tutorial.

note: if you are invoking a method without parenthesis you will come across strange behaviour if you use the ‘{ }’ block syntax; as the block will be bound to the last argument opposed to the method invoked. Less Important — and almost no need to be mentioned at this point. Feel free to ignore this point.

Thanks for taking the time to read my article! I’m a Full Stack Developer located in Toronto, Canada 🇨🇦 . Currently I’m working at Swift Medical, a leading MedTech application.

--

--

Joseph Bianchi
Joseph Bianchi

Written by Joseph Bianchi

I’m a Toronto based Full Stack Developer. I have a love for programming, health, and self expression.

No responses yet