Rake: December 2007 Archives

Heckle your ActiveRecord objects

|

I was recently watching Geoffrey Grosenbach's third PeepCode episode focused on rSpec: rSpec Controllers and Tools.

At one point during the screencast, while working with Heckle, Geoffrey mentioned that it is easiest to run heckle against a single instance method of your own making due to the fact that all objects that are subclassed from ActiveRecord::Base have all the AR methods globbed in. No need to heckle Rails itself, eh? By the way, if you are unfamiliar with Heckle, Kevin Clark has a great overview.

The thing is, I want to heckle all my methods at once. A little A - B and a rake task is all it took.

  
    namespace :spec do
      desc "Heckle a class"
      task :heckle => :environment do
        unless ENV.include?("model")
          raise "usage: rake test:heckle model=User"
        end

        #The user is allowed to test a single method
        #with User#activate! or similar
        inputs = ENV["model"].split("#")

        model = inputs[0].capitalize
        test_prefix = "#{model.downcase}"
        klass = Object.const_get("#{model}")

        #First check to see if user provided
        #an instance method name. ex: User#activate!
        methods = [inputs[1]] if inputs.size == 2

        #If the user only passed in a model name,
        #we need to get the instance methods that
        #are not part of ActiveRecord::Base
        methods ||= klass.instance_methods - ActiveRecord::Base.instance_methods

        methods.each do |method|
          cmd = "spec spec -H #{model}##{method}"
          system(cmd)
        end
      end  
    end
  

If you would like to heckle all the methods for an object (we'll user a User object as an example), run the following:

  
    > rake spec:heckle model=User
  

You can heckle a single method like so:

  
    > rake spec:heckle model=User#encrypt
  

You can download this task as well as it's Test::Unit counterpart here: heckle.rake

The rake tasks have beend DRYed up a little so that we can run most of the same code regardless of whether you are using rpsec or Test::Unit.

Enjoy!

Rake bootstrap:all

|

Sometimes when I'm building a Rails application, I want to rebuild my database from scratch. No problem you say? Just provide a lil' lighting? A lil' thunder? Or perhaps:

  
     rake db:migrate VERSION=0
     rake db:migrate
  

Well, yeah. That works if you only need to rebuild the database structure. But I often find myself writing plenty of rake tasks to bootstrap different parts of the system. For example:

  
  namespace :bootstrap do
    desc "Bootstrap a set of admin users"
    task :admins => :environment do
      puts "Bootstrapping admins"
    end
  
    task :categories => :environment do
      puts "Bootstrapping categories"
    end
  
    task :zip_codes => :environment do
      puts "Bootstrapping zip_codes"
    end
  end
  

Now, how would I go about running all these tasks at once? I could create an :all task that called each of bootstrap tasks:

  
    namespace :bootstrap do
      desc "Call all the bootstrap tasks"
      task :all do
        tasks = %w[admins categories zip_codes]
        tasks.each do |task|
          Rake::Task["bootstrap:#{task}"].invoke
        end
      end
    end
  

This is fine if you only have a few tasks. But what if you add a new bootstrap task and fail to add it to the task list in :all?

My solution was to find a way to get all the tasks in a particular namespace. After reading through the Rake rdocs and fumbling around a bit, I finally resorted to bugging Mr. Rake himself, Jim Weirich, who provided the hint I needed:

Task manager objects are just objects that respond to the protocol required to hold and manage tasks. Currently, only the Rake::Application class responds to this protocol, but the task manager responsibilities were separated out into a module to make the list of required responsibilities more clear. You can get the current rake application object via: Rake.application

Ah! Rake.application. That's the ticket! So, here is what I ended up with:

  
    namespace :bootstrap do
      desc "Call all the bootstrap tasks"
      task :all do
        tasks = tasks_in_namespace("bootstrap")
        tasks.each do |task|
          Rake::Task["#{task.name}"].invoke
        end
      end  
    end

   private
     def tasks_in_namespace(ns)
       #grab all tasks in the supplied namespace
       tasks = Rake.application.tasks.select { |t| t.name =~ /^#{ns}:/ }
       
       #make sure we don't include the :all task
       tasks.reject! { |t| t.name =~ /:all/ }
    end
  

You can get the sample file here: bootstrap.rake

A little rake luv is all you need:

  
  > rake bootstrap:all
  Bootstrapping admins
  Bootstrapping categories
  Bootstrapping zip_codes
  

Enjoy!