Applying Ruby’s Blocks

  • Posted by Mike Naberezny in Ruby

    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 and file_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’s end 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.