Fun With Rakefiles

I recently ran into an ‘interesting’ issue in a set of  Rakefiles.

Here’s a part of the global Rakefile, which imports the *.rake files from a tasks folder:

$LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')

Dir.glob('tasks/*.rake').each { |r| import r }

Some of these Rakefiles require other Ruby files (which is stored in the lib folder). Since executing one task involves loading all that code, there’s a chance that an error in one supporting Ruby file may cause an error in an entirely unrelated task.

Let’s look at an example, the code is available over at GitHub: https://github.com/s2k/fun_with_rakefiles

For simplicity there are just two rake files within tasks and a ruby file in lib. What we like to run is this:

rake make_it_so

However what we get is an error message pointing to some rather unrelated part of the code:

$rake make_it_so
rake aborted!
Errno::ENOENT: No such file or directory @ rb_sysopen - partlist.txt
…dev/fun_with_rakefiles/lib/thing.rb:7:in `readlines'
…dev/fun_with_rakefiles/lib/thing.rb:7:in `<class:Thing>'
…dev/fun_with_rakefiles/lib/thing.rb:1:in `<top (required)>'
…dev/fun_with_rakefiles/tasks/unrelated_thing_tasks.rake:1:in `require'
…dev/fun_with_rakefiles/tasks/unrelated_thing_tasks.rake:1:in `<top (required)>'
…/.rvm/gems/ruby-2.1.1/bin/ruby_executable_hooks:15:in `eval'
…/.rvm/gems/ruby-2.1.1/bin/ruby_executable_hooks:15:in `'
(See full trace by running task with --trace)

Hmmm… When I first saw the error message I really wondered why suddenly there’s a file partlist.txt missing and then I asked myself where that Thing class was used in the code.

In this example it’s relatively easy to see what’s going on: The ‘central’ Rakefile gathers all the other .rake files that may be necessary (which, in turn, require all the files they need). However, if there are a few (sub) rake files around things can get a bit more confusing.

In my case, it was simple enough to repair the issue, since the missing file could be added to source control and everything worked again. But still there are a few things to think about:

  1. Should the be code in a Ruby class definition that’s executed while defining the class? Especially: Should it be there, in case you can avoid it?
    In my opinion it’s a good idea to only have code like that if it’s really necessary. In the example given, one could load the file when an instance of the class is created. Doing so will reduce the consequences of the missing file drastically: Only the rake tasks actually using that class would fail, not every rake task.
  2. Could one avoid the failure and still be able to run the desired task?
    It turns out that you can:

    $rake -f tasks/what_we_want_to_do.rake  make_it_so
    Processing something…

    In some cases you may also need to supply this command line parameter, in order to allow rake to find other Ruby files:

    -I, --libdir LIBDIR      Include LIBDIR in the search path for required modules.
  3. Can we check that nothing’s broken?
    In fact, we can:

    rake -T

    If that actually lists the available rake tasks and doesn’t exit with an error code, it looks OK, at least it didn’t break everything. However, if a file is missing form the version control system, it’s a works-on-my-machine situation.

In a project with a large number of rake files and supporting library code, it may be worth while to have rake -T as part of a smoke test on the continuous integration system.

 

 

Advertisements

One thought on “Fun With Rakefiles

  1. Pingback: More Fun With Rakefiles | Ph values

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s