Archive for the ‘Ruby’ Category

An elegant way to log with a Ruby block

Saturday, January 2nd, 2010

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.

Which methods in Ruby should contain a bang sign?

Sunday, February 1st, 2009

I always thought the bang sign (!) in Ruby methods were meant to say: “I’m destructive, I’ll change your object”. Like this:


def do_something
end

def do_something!
end

Now I know better. Actually the bang sign (!) is only meant to give you and other developers a warning, like “I’m more dangerous than the other version, the non-bang one.”

Which methods should have a bang sign?

Matz says, on January 28, 2009 (EST):

The bang (!) does not mean “destructive” nor lack of it mean non destructive either. The bang sign means “the bang version is more dangerous than its non bang counterpart; handle with care”. Since Ruby has a lot of “destructive” methods, if bang signs follow your opinion, every Ruby program would be full of bangs, thus ugly.

And I (David A. Black) say:

So please stop writing bang methods that have no non-bang equivalent! The bang in isolation literally means nothing. It’s purely ornamental, and dilutes the conventional usage (see above) to the point where it’s
unrecognizable and pointless.

(Exception: things like Builder, where the bang/non-bang distinction is completely redefined, and documented as having been redefined, for the sake of domain-specific semantics.)

Source: this comment.