An elegant way to log with a Ruby block

Imagine you created a generator much like Rails. It creates a series of directories and copy a set of files to a specific location. Some things may fail, and you need a nice way to show what’s going on behind the scenes.

I was browsing some code on GitHub and found an elegant approach to this problem.


module Glue
  module Generator
    extend self
    extend Glue::Helper

    def generate!(options={})
      templates = File.expand_path(File.dirname(__FILE__) + "/../../templates")
      output = File.expand_path(options[:path])

      # create root directory
      run "creating #{Colorize.yellow(output, :style => :underscore)} directory" do
        FileUtils.mkdir_p(output)
      end

      # create javascripts directory
      run %(creating #{Colorize.yellow("public/javascripts")} directory) do
        FileUtils.mkdir_p File.join(output, "public/javascripts")
      end

      # create images directory
      run %(creating #{Colorize.yellow("public/images")} directory) do
        FileUtils.mkdir_p File.join(output, "public/images")
      end

      # create views directory
      run %(creating #{Colorize.yellow("views/layouts")} directory) do
        FileUtils.mkdir_p File.join(output, "views/layouts")
      end
    end
  end
end

module Glue
  module Helper
    def run(message, &block)
      $stdout << "\n#{message}... "

      begin
        yield
        $stdout << Colorize.green("done!")
      rescue Exception => e
        $stdout << Colorize.red("error ~ #{e.message}")
      end
    end
  end
end

This code does a few interesting things:

  • the “run” method is defined inside a helper module. It begins by receiving a message (what are you doing right now?) and a block. It starts by giving control to the caller with a yield (now the execution inside the “run” method is halted). So the block passed to the run method is now executed. This block is actually the lines you see above between “run … / end”;
  • So in this block you can “do your thing”. For example, create a directory, request some info from a remote API, or whatever;
  • When the block is finished running, the lines after yield (in the “run” method) are executed. If no exception is raised a Ok message is printed, otherwise we’ll see what went wrong with the specific exception message.

This is very cool. You can now execute failure-prone tasks in a very elegant way, and with a really nice syntax :)

This code was created by Nando Vieira in an interesting project called Glue, which is a simple static site generator for Ruby that uses Haml, Sass and Textile/Markdown. The documentation is here.

You can see the code samples above, here and here.

Tags:

Leave a Reply