Ruby: Methods defined in Rake tasks

Ruby: Methods defined in Rake tasks

Last week during a code review at work, this thing became a topic again. This time I’ve decided to write a post about it so other colleagues can learn from it and be aware of the potential risks it implies to define methods in your rake files. The next time I need to refer to this post I will be able to come back here and have everything explained.

In my case I learned it the hard way. Some time ago, we defined a method called logger to  initialize and memoize a Logger instance that should write to a task’s dedicated log file. The surprise came when we realized that instead of writing log messages to the file we had conceived for this task, it logged everything to another file. It turned out to be that we had already defined another method named logger in a different *.rake file that pointed to other log file!

Lets see what happens when you define methods with the same name in rake tasks with the following example.

Image we have a Rakefile that defines some rake tasks. To make it a little more real, instead of defining the tasks inside the Rakefile, we will have them in two separate files called foo.rake and bar.rake, yes, I know I am super original with naming 😀

We use the Rakefile to load the contents of the *.rake files. In each *.rake file we define two things: a method called greet, and a task that uses this method. Lets check first that everything is defined and that all tasks are available.

Open a terminal inside this “project” and run:

And now run each task:

WAT? Everyone (or at least me :D) would expect to have seen “Hello from bar.rake!” when running rake bar, but instead, the greet method definition at foo.rake prevailed over the one inside bar.rake.

Having namespaces doesn’t solve the issue

In the last example, I used a very simple setup with no namespaces, but when I faced the error that made me learn this, it had namespaces. Lets put some namespaces to the example above, leaving the Rakefile as it was, and see what happens. Note I have placed the definition of greet inside each namespace.

Now everything is within a namespace, lets run each task again, this time prefixed with the corresponding namespace:

As you can see, rake namespaces don’t help us isolating the methods we have defined inside. They have no effect for this purpose. What can we do then?

How to work around this?

If you though about naming each task differently, I hope you don’t trust your eloquence that much, mine has been proven to fail. Thinking about using a different name each time is a bad idea, and the sooner or later names will clash. Lets see the alternatives I suggested to my peer during that code review:

1. Extract your methods to some library

If your methods have enough coherence to be grouped together or you plan to reuse them elsewhere, you may consider extracting them to some module. Once extracted, invoke them inside your rake tasks. Here we also have a great chance to add unit tests for our new extracted classes. Lets see it applied to our example:

If we run it, we now get what we expected:

2. Put everything inline

Sometimes taking the easy path in life is OK. If none of the reasons we mentioned above for extracting the methods apply, it is perfectly fine to go simple and put your methods inline. In our example that would look as it follows:

And if we run them, we get again what we expected:

In order to quickly explain this topic, I have used a simple rake setting. If you try this within a Rails application, the result will be the same, is a rake issue, in fact, when I first experienced this error, it was inside a Rails application.

If you liked this post or it helped you to save time, please share it with your friends and peers or maybe send me a tweet to make me smile 🙂 Two months ago I still got some “thank you” tweets because of a Java post I wrote about years ago, it felt incredibly great to know it stills help other people! See you next week!

No Comments

Sorry, the comment form is closed at this time.