Rake task with an external script using ActiveRecord
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.