Most of my work involves grinding out new features for my web-app, but then on monday I came to several items in a row in my backlog notes that were going to be outside this realm. Although I have several specific examples of my own, you could probably think of hundreds of tasks like this: purging database tables, recieving emails into the database, sending users SMS or email notifications based on time-sensitive data, processing batches, generating nightly reports...I'm sure the list goes on and on. What's disturbing is something I saw far too frequently as I was crawling the web for a good solution was this: a random script full of copy-and-paste code that manually loads the rails environment ahead of time (or worse, PIECES of rails). Let me say right now: "Oh god, please don't ever do that". Yes, it works, but there's no REASON to go to that sort of trouble.
There are two ways I know of that you can load the rails environment easily: script/runner, and Rake.
If you're writing a script that you just want to run once, and it needs to hook into your rails app, just write it the way you would inside your application (go ahead and assume the presence of all the rails libraries) and then run it from the home directory of your application like this:
$> ruby script/runner my_script.rb
If you're building something that you want to run frequently, or even at regular intervals, just write the code the way you would in a rails model and package it into a rake task like this (this file goes in the "lib/tasks" directory of your rails app):
#server_tasks.rake
namespace :server do
desc 'Send SMS Notifications'
task :send_notifications => :environment do
Notification.find(:all).each do |n|
n.send
n.destroy
end
end
desc 'Build Batch File'
task :build_batch => :environment do
file = File.new("new_batch.txt","w+");
t = Translator.new()
data_string = t.translate(Notification.find(:all))
file.puts(data_string)
end
desc 'process emails'
task :check_mail => :environment do
MailFetcher.check_mail
end
end
Now you can run it any time with the following:
$> rake server:check_mail
or you can have a cron job run it regularly with this entry in your crontab file:
5 0 * * * cd /path/to/your/app && /path/to/your/rake server:check_mail RAILS_ENV='production'
MUCH better.

3 comments:
I've just implemented some background processing using spawn.
blog post about spawn
for certain maintenance tasks that are run on a schedule I just call the action in the admin panel with cron. I'm using the basic http auth in rails to protect my actions. I like this because then i can also run these tasks any time from the admin panel. I'm not sure how secure this is compared to other approaches, but for me it's simple and adequate till I learn more about some of the other techniques.
Thanks for the post. very useful.
@element
I checked out spawn, that's a pretty neat plugin. Perfect for anything that is kicked off by a webrequest, but should really be processed in the background. Thanks for the insight.
Check out Skynet, a pure ruby MapReduce framework. http://skynet.rubyforge.org
Post a Comment