What to put into Rake tasks

Posted by Jonathan

As a consultant for Peritor I often review other peoples code. What I often encounter and always address is custom logic in Rake tasks.

Rake is perfect for automating tasks and making calls into your application from the command line. People often use it for doing maintenance, executing periodic jobs, or starting and stopping services. Nothing wrong with that, this is what Rake in Rails apps is for.

What I resent is having all this logic in the Rakefile, either inline in the task body or as a method in the Rakefile.

  # Rakefile
  
  task :ping_stale_users do
    stale_users = User.find(:all, 
                            :conditions => ["last_login < ? ", Time.now - 3.months])
    ...
    ...
    stale_users.each do |user|
      if user.check_something
        user.mail_invite
      end
    end
  end

This is just wrong for the following reasons:

  • Testing is hard: methods and Rake tasks are hard to test. The environment is hard to create and you may have to shell-out to call rake.
  • Reusing is hard: you cannot easily reuse this functionality from the console or an admin controller.
  • POLA: Rakefile are not were people look for domain logic

The solution is simple and yet it seems underused. Just put your domain logic into a model or library and call it from the Rake task. This way it is much easier to test, reuse, and read.

  # Rakefile
  
  task :ping_stale_users do
    User.ping_stale_users
  end
  
  # User.rb
  def self.ping_stale_users
    stale_users = User.find(:all, 
                            :conditions => ["last_login < ? ", Time.now - 3.months])
    ...
    ...
    stale_users.each do |user|
      if user.check_something
        user.mail_invite
      end
    end
  end

Of course the method in the model can be cleaned up a bit more but this is not the point. The point is that we removed domain logic from the Rakefile and made it shorter and easier to understand. The logic is now somewhere where we can test and reuse it.

Review: Design Patterns in Ruby

Posted by Jonathan

Design Patterns in Ruby by Russ Olsen is an introduction to Design Patterns. It covers 14 out of the 23 patterns of the GoF Design Patters: Elements of Reusable Object-Oriented Software book and adds three Ruby-related patterns. The book examines each pattern in general, shows how it applies to a dynamic language like Ruby and explains when to use or not use the pattern.

The fact that the book is written in an informal style with lots of examples makes it really easy to read and follow. I really liked the format of the pattern examination and find it an excellent overview of and introduction to Design Patterns. The covered GoF patterns (Template Method, Strategy, Observer, Composite, Iterator, Command, Adapter, Proxy, Decorator, Singleton, Factory Method, Abstract Factory Method, Builder, and Interpreter) are the most important ones and are easy to apply in Ruby. Especially the chapter on Interpreter made a very good job of explaining a widely under-utilized pattern. It even showed how to build a parser and didn't stop at the AST.

Apart from those classic patterns, the book introduces Internal Domain-Specific Languages, Meta-Programming, and Convention Over Configuration as newer patterns that are closely related to dynamic languages like Ruby. Those chapters cover nothing new to Rails programmers but are a nice addition to a general Ruby Patterns book. In my opinion the discussion of Meta-Programming could be a bit longer as it only covers class_eval and Ruby has more to offer.

In general, Design Patterns in Ruby is a very good overview of Design Patterns in the modern, dynamic world of Ruby. The book makes sure that the reader understands where a pattern arises and is very good in explaining its usage by example. Further, I cannot praise enough the fact that the author also tells you when NOT to use a pattern and warns about over-usage of inheritance or patterns.

With 338 pages of informal, easy to read examples and explanations, the book is easy to read in two or three afternoons. If you look for an introduction to Design Patterns or want to know how they apply in Ruby, I really recommend Design Patterns in Ruby.