Rake task with an external script using ActiveRecord

Posted on December 18, 2011

Using Rake to call an external script is quite straightforward: you can simply load it, or call “system”, or “exec”. By the same token, using ActiveRecord classes in a Rake task is easy: you just need to state your task depends on the environment (with => :environment , as shown here).

Where it gets interesting is having an external script that can be used as a standalone, or called from within a Rake task. In my case, I wanted a script to load data from a file that would use ActiveRecord if possible (since it’s much faster), and fallback to Mechanize (to upload the data via the web forms).

The Rake task

First, let's take a quick look at the complete rake task:

  
  
    namespace :retail_stock do
  script_base_path = File.join(File.dirname(File.expand_path(__FILE__)), "../../support/")
  
  desc "Load retail stock from CSV file"
  task :load => [:environment] do |task|
    path = File.join(script_base_path, "retail_stock_upload_script.rb")
    
    # Referencing an ActiveRecord class triggers the actual loading
    # of that class so it can then be used within the script.
    # Without this, the ActiveRecord models aren't loaded,
    # even with the ":environment" dependency
    CatalogItem
    Site
    Watch
    
    puts "Loading retail stock with script at #{path}"
    load path
  end
end
  
  

First, on line 5 we depend on the environment to make ActiveRecord classes available within the task:

  
  
    
    task :load => [:environment] do |task|
  
    
  

But before loading the external script on line 17, we call each ActiveRecord class once (lines 12 through 14). Without this step, the classes aren’t actually loaded, and won’t be available from within the external script.

The script

As I said, I wanted to use ActiveRecord, but fall back on Mechanize (using the web forms) to upload the data. How do we achieve this? By checking for the ActiveRecord classes we want to use:

  
  
    def watch_exists?(case_number)
  if defined? Watch
    Watch.find_by_case_number(case_number)
  else
    @a.get('http://localhost:3000/watches') do |watch_index_page|
      return true if watch_index_page.link_with(:text => @case_number)
    end
  end
end
  
  

If the ActiveRecord class we need is defined, we use it, otherwise, we leverage Mechanize to do the work. This simple trick allows us to have a script that can be called from with a Rake task, or directly (e.g. from a shell).


Would you like to see more Elixir content like this? Sign up to my mailing list so I can gauge how much interest there is in this type of content.