How to Use Ruby BEGIN and END Blocks
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?
- When to Use BEGIN and END Blocks?
- The More Common Ruby begin/end blocks
- Ruby Rescue, Else, and Ensure Keywords
- Conclusion
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.