How to Use Ruby BEGIN and END Blocks

There are two types of Ruby Begin and End blocks. A simple Google search of “Ruby begin end blocks” will lead you to either of these two kinds of articles – the ones in all caps (BEGIN and END) and the ones usually separated by a slash (begin/end). Both of these are different things but confused with the same name. In this post, we will dive into both. 

Here’s an outline of what we’ll be covering so you can easily navigate or skip ahead in the guide

What are Ruby BEGIN and END Blocks?

Ruby’s BEGIN and END blocks (in uppercase) are reserved keywords in Ruby and are pretty straightforward to use. They enable you to specify blocks of code that you want your program to run at the beginning and end of its execution, regardless of their position in the source file. They were incorporated from the Perl programming language and can often be confused with the (lowercase) begin/end statements used to define generic code blocks. HOWEVER, uppercase BEGIN and END create these specialized blocks that run at the beginning or end.

They use curly braces {} to enclose the block’s code after the respective (BEGIN or END) keywords and have the following syntax

BEGIN {
    # code to run at the beginning
}

END {
    # code to run at the end
}


The code you put inside a BEGIN block unconditionally executes before everything else when the source file loads.Ruby BEGIN Block

# ruby_begin.rb

puts "Hello world! (1)"

BEGIN { # to be executed first, when the file loads up
  puts "<begin block>"
}

puts "Hello world! (2)"

Output:

$ ruby my-ruby.rb
<begin block>
Hello world! (1)
Hello world! (2)


Multiple BEGIN blocks

As you can see, the BEGIN block executes at the beginning, regardless of its position in the code and Ruby’s usual top-down execution sequence.

You can also use multiple BEGIN blocks to segregate chunks of code. These blocks execute according to their sequence in the code, i.e., blocks defined earlier (higher up) in the source file execute earlier. Here’s a small example

# ruby_begin.rb

puts "Hello world! (1)"

BEGIN {  # to be executed first, when the file loads up
  puts "<BEGIN block (1)>"
}

BEGIN { # to be executed second
  puts "<BEGIN block (2)>"
}

puts "Hello world! (2)"

Output:

$ ruby rub_begin.rb
<BEGIN block (1)>
<BEGIN block (2)>
Hello world! (1)
Hello world! (2)


Variable Scope

As expected, the first BEGIN block executes the earliest.

As shown in the example below, variables declared inside the BEGIN block are available in the global scope.

# ruby_begin.rb

BEGIN {
    a = "Hello world!"
}

BEGIN {
    puts a
    a += " Jell-O world!"
}

puts a

Output:

$ ruby ruby_begin.rb
Hello world!
Hello world! Jell-O world!

Ruby END Block

The counterpart END block executes at the end after the rest of your source program’s execution, right before termination.

# ruby_end.rb

puts "Hello world! (1)"

END {
  puts "<END block>" # to execute last
}

puts "Hello world! (2)" # to execute before END block


Output:

$ ruby ruby_end.rb
Hello world! (1)
Hello world! (2)
<END block>

Multiple END blocks

When using multiple END blocks, it is essential to note that these blocks execute in the reverse order of their specification, i.e. the END blocks defined later (towards the end of) the code run earlier. Here’s a small example

# ruby_end.rb

puts "Hello world! (1)"

END { # to be executed last
  puts "<END block (1)>"
  puts "Fin."
}

END { # to be executed second to last
  puts "<END block (2)>" 

}

puts "Hello world! (2)" # to execute before END blocks

Output:

$ ruby ruby_end.rb
Hello world! (1)
Hello world! (2)
<END block (2)>
<END block (1)>

Fin.

As you can see, the second END block executes before the first one, contrary to their sequence in the code. In simple terms the END block defined first always goes last.

Variable Scope

Similar to BEGIN, the END block also operates on the global scope.

# ruby_end.rb

c = 20

END { # executes after
    puts "<END block (1)>"
    c += 10
    puts c
    puts "---------------"
}

END { # executes before
  puts "<END block (2)>"
  c += 20
  puts c
  puts "---------------"
}

Output:

$ ruby ruby_end.rb
<END block (2)>
40
---------------
<END block (1)>
50
---------------

As you can see from the output here, the end blocks modify the variable c in its global scope -- first by the second block, followed by the first one.

Example

Here’s a basic shopping invoice example with BEGIN and END blocks used together.

# ruby_example.rb
BEGIN {
    puts "**Invoice copy**"
    puts "-------------------"
}

BEGIN {
    cost = 200
    qty = 15
    tax = 0.11
}

puts "Cost: %d" % cost
puts "Quantity: %d" % qty
puts "-------------------"
amt = cost * qty

END {
    puts "**Thank you for shopping at XYZ.**"
}

END {
    tax_amt = amt * tax
    puts "Amount: %d" % amt
    puts "Tax: %f" % tax_amt
    puts "Total Amount: %f" %(amt + tax_amt)
    puts "-------------------"
}

Output:

$ ruby ruby_example.rb
**Invoice copy**

-------------------

Cost: 200

Quantity: 15

-------------------

Amount: 3000

Tax: 330.000000

Total Amount: 3330.000000

-------------------

**Thank you for shopping at XYZ.**

When to Use BEGIN and END Blocks

Though BEGIN and END blocks are not very common in practice, they can be helpful in various situations. BEGIN blocks enable you to logically segregate parts of code used to open network connections (e.g., to a database) and data streams (e.g., for logging), fetch configuration options and perform other global initializations. On the other hand, END blocks can help clean up unwanted objects, perform customized garbage collection, close any open network connections and data stream handlers, and other global shutdown operations.

The More Common Ruby begin/end Blocks

Now let’s look at the (lowercase) begin/end blocks.

Ruby’s begin and end keywords enable us to declare many code statements and group them into a block-like control structure.

begin
.
# code statements come here
.
end

It is interesting that Ruby methods also implicitly use a begin/end-like construct to group code statements. Also, like in methods, a begin/end block returns the value of the last statement in it. Here’s an example

# ruby_begin_end.rb

puts begin
    a = "Hello "
    b = "world!"
    a + b # returned value
end

Output:

$ ruby ruby_begin_end.rb
Hello world!

Here, as you can see, we used a block to concatenate two strings and return their value to a print statement. We can also save the block as a variable and invoke it wherever we like.

# ruby_begin_end.rb

my_hello_world_str = begin
    a = "Hello "
    b = "world!"
    a + b
end

puts my_hello_world_str

This gives the same output as before. However, it is worth mentioning that begin/end blocks (unlike do/end ones) can not be passed to Ruby functions. 

Here’s an example showing how the block returns only the last statement.

# ruby_begin_end.rb

puts begin
    "foo"
    "bar"
    "baz" # returned value
end

Output:

 $ ruby ruby_begin_end.rb

baz

It is also quite common to find begin/end blocks used with Ruby’s ||= operator. For example

# ruby_begin_end.rb

area ||= begin
    radius = 14
    area = Math::PI * radius * radius
    area
end




puts area

Output:

$ ruby ruby_begin_end.rb

615.7521601035994

Ruby Rescue, Else, and Ensure Keywords

Begin/end blocks are often used in conjunction with the rescue, else, and ensure statements to allow them to handle erroneous events in your code. This brings us into the realms of exception handling in Ruby.

Ruby Rescue

Exception handling is the process of making fail-safe programmable accommodations for errors that can otherwise cause your program to fail. It allows you to plan alternative strategies to deal with the raised errors in an amicable way without compromising the user’s experience.

Ruby uses the rescue keyword to capture and handle exceptions that show up in the begin blocks. 

begin
  # error encountered here
rescue
  # error captured and handled here
end

In this sense, rescue is equivalent to the catch function in try/catch mechanisms in most programming languages. Let’s write some erroneous code and see what the handling looks like

# ruby_error.rb

puts "Hello world!"

begin
file = File.open("/path/to/invalid_file.txt", "r") # raises error

 data = file.read

 file.close
rescue
  puts "An error encountered while opening the file. Moving on.."
end

puts "Jell-O world!"

Output:

$ ruby ruby_error.rb
Hello world!
An error encountered while opening the file. Moving on.
Jell-O world!

The rescue statement successfully caught (or rescued) the exception and saved our code from terminating midway. 

Furthermore, you can also extract information about the raised error by using an error class argument as shown below

# ruby_error.rb
puts "Hello world!"

begin
  file = File.open("/path/to/invalid_file.txt", "r") # raises error

  data = file.read

  file.close
rescue StandardError => e
  puts "An error came up while opening the file. Moving on."
  puts "Error message: " + e.message # error string
  puts "Error backtrace: " + e.backtrace.inspect
end

puts "Jell-O world!"

Output:

$ ruby ruby_error.rb
Hello world!
An error came up while opening the file. Moving on.
Error information: No such file or directory @ rb_sysopen - /path/to/invalid_file.txt
Error backtrace: ["ruby_error.rb:4:in `initialize'", "ruby_error.rb:4:in `open'", "ruby_error.rb:4:in `<main>'"]
Jell-O world!

Here, we captured a generic error using the StandardError class. Similarly, we can also have multiple handlers for handling specific types of errors differently in the same block. Let’s extend our example to also handle any file permissions related error that might come up when trying to read the file

# ruby_error.rb
puts "Hello world!"

begin
  file = File.open("/path/to/invalid_file.txt", "r") # raises error
  data = file.read
  file.close
rescue Errno::ENOENT => e # missing file error
  puts "Missing file error encountered while opening the file. Moving on.."
  puts "Error information: " + e.message
  puts "Error backtrace: " + e.backtrace.inspect
rescue Errno::EACCES => e # file permissions error
  puts "File permission error encountered while opening the file. Moving on .."
  puts "Error information: " + e.message
  puts "Error backtrace: " + e.backtrace.inspect
end
puts "Jell-O world!"

This gives us the same output as before because the specified file doesn’t exist, and therefore the first handler (for Errno::ENOENT) is in charge here. Now let’s see what else we can do if no error comes up.

Ruby Else

Ruby also provides a mechanism for if no errors come up. It is called the else keyword and follows the below control flow

begin
  # error encountered here
rescue
  # error captured and handled here
else
  # code to run if no error raised
end

Let’s reuse our previous example and try to read an actual file on our system instead of a non-existent one, and see how else works.

# ruby_error.rb
puts "Hello world!"

begin
  file = File.open("/path/to/file.txt", "r") # actual file
  data = file.read
  file.close
rescue Errno::ENOENT => e # missing file error
  puts "Missing file error encountered while opening the file. Moving on."
else # if there’s nothing to rescue
  puts "No error encountered. You are good to go!"
end

puts "Jell-O world!"

Output:

$ ruby ruby_error.rb
Hello world!
No error encountered. You are good to go!
Jell-O world!

As you can see, because the file reading didn’t raise any error, the else block code runs instead of the rescue one.

Ruby Ensure

Finally, before we close, let’s look at the “finally” equivalent in Ruby.

When an error comes up inside our begin block, the control goes to the rescue statements before exiting the block. However, if no exception comes up, the control goes to the else statements before exiting. But you see both are mutually exclusive?

So what if there’s some code that we want to execute in both the erroneous and errorless situations before we exit the begin/end block? This is exactly what Ruby ensure is for.

The ensure keyword allows you to specify code that you want to run after your begin block (with likely erroneous code), your rescue code or your else code execute. This is what the program flow looks like

begin
  # error encountered here
rescue
  # captured and handled here
else
  # code to run if no error raised
ensure
  # code to always run at the end
end

The code in the ensure block executes regardless of whether an error comes up or not. Therefore it can help clean up unwanted objects or close any open connections or data streams before closing shop. Let’s add an ensure block to our previous example with a silly print statement as a proof of concept.

# ruby_error.rb
puts "Hello world!"

begin
  file = File.open("/path/to/file.txt", "r") # actual file
  data = file.read
  file.close
rescue Errno::ENOENT => e # missing file error
  puts "Missing file error encountered while opening the file. Moving on."
else
  puts "No error encountered. You are good to go!"
ensure # executes regardless
  puts "Ruby 'ensures' that I execute!"
end

puts "Jell-O world!"

Output:

$ ruby ruby_error.rb
Hello world!
No error encountered. You are good to go!
Ruby 'ensures' that I execute!
Jell-O world!

Wrapping Up

In this post, we covered two types of Begin and End blocks uppercase and lowercase. The uppercase BEGIN and END blocks specify code statements to run at the beginning and end of a program. In contrast, the lowercase begin/end blocks enable us to group code statements to create generic reusable code blocks. 

Now that you’ve learned and revisited some of these particular Ruby keywords and statements go ahead and think about where your code could benefit from these and clean things up. If you are interested in learning more about Ruby and exception handling, check out our Exception Handling in Ruby post.

And if you are looking to go advanced and be alerted about issues in your application that begin/rescue blocks in your code can’t pick up, consider getting an APM tool for your applications. Make sure to check out Scout’s APM tool for alerts about bottlenecks, memory bloat, leaks, slow database queries, real-time performance insights, and much more. Get started with a 14-day free trial (no credit card needed)! Also, check out our blog at scoutapm.com/blog for more software programming and web development content.