Applying Ruby’s Blocks
-
Not long ago, we took a look at the basics of Ruby Block Scope. When you’re first getting started with Ruby’s blocks (closures), little things like that can be frustrating. Blocks can seem so foreign that you might be tempted to think that they’ll make your code more difficult to read or understand. Once you get past the learning curve, blocks can be leveraged to improve the readability and maintainability of code in some situations.
Here’s a few examples of applying Ruby’s blocks to everyday problems.
Handling Timeouts
Ruby’s
Timeout
library is used to ensure that a block of code doesn’t execute longer than a certain amount of time. This is a handy feature since the block can wrap many operations which might not otherwise support a configurable timeout.require 'timeout' Timeout::timeout(20) do # Potentially long-running code end
The
Timeout::timeout
method takes a timeout value in seconds. If the block takes longer to execute than the timeout value, it will be interrupted.Working with Files
In PHP,
file_get_contents
andfile_put_contents
provide convenient ways to quickly read and write files. Often, we need to do a bit more. It’s common to open a file, perform some operations on it, and then close the file.$f = fopen('/path/to/foo', 'w') ; fwrite($f, 'foo') ; fclose($f);
Ruby’s
File.open
can be used like above but it can also be passed a block.File.open('/path/to/foobar', 'w') do |f| f.write 'chars' end
When the block exits, the file will automatically be closed. This is a best practice for working with files in Rails applications.
The block version is nice from a maintenance perspective. It’s easy to accidentally remove the call to
fclose
and doing so won’t produce an obvious error. However, removing the block’send
will cause a parse error. In PHP, this is not so much of an issue because PHP will close any file handles left open at the end of each request.Performing Benchmarks
Within the context of the Rails framework, you can call the
benchmark
method within a controller action. Given this action:def show @user = User.find(params[:id], :include => [:preferences]) # ... end
Just put a block around a piece of code to measure it:
def show benchmark do @user = User.find(params[:id], :include => [:preferences]) end # ... end
The result of the benchmark will be output to the log, such as
log/development.rb
. There is also a benchmark helper method available in all views that does the same. The Ruby standard library also has a Benchmark library you can use outside of Rails.Changing the Directory
If you’ve ever written a command line script that had to temporarily change the working directory to perform some operations, it might have looked something like this:
$here = getcwd(); chdir('/path/to/somewhere/else'); // perform some operations chdir($here);
You can do the above almost verbatim in Ruby also. However, a better way to temporarily change the directory is to pass a block to
Dir.chdir
.Dir.chdir('/path/to/somewhere/else') do # perform some operations end
The working directory will be changed before entering the block, and automatically changed back when the block closes. Not only is it cleaner, it makes your scripts easier to maintain since you can’t accidentally remove the returning
Dir.chdir
.