views:

1127

answers:

8

I have a Rails app with some basic models. The website displays data retrieved from other sources. So I need to write a Ruby script that creates new instances in my database. I know I can do that with the test hooks, but I'm not sure that makes sense here.

I'm not sure what this task should look like, how I can invoke it, or where it should go in my source tree (lib\tasks?).

For example, here's my first try:

require 'active_record'
require '../app/models/mymodel.rb'

test = MyModel.new
test.name = 'test'
test.save

This fails because it can't get a connection to the database. This makes sense in a vague way to my newbie brain, since presumably Rails is doing all the magic work behind the scenes to set all that stuff up. So how do I set up my little script?

+1  A: 

You can open a connection in your scripts as such:

ActiveRecord::Base.establish_connection(
    :adapter  => "mysql",
    :username => "root",
    :host     => "localhost",
    :password => "******",
    :database => "******" 
)

I'm sure there is a more elegant way to do it, so that it grabs the info from your database.yml.

Hates_
+1  A: 

I'd suggest creating custom rake tasks (lib/task/foo.rake). This give you easy access to most of the functionality of your rails app.

namespace :foo do
  desc 'do something cool'
  def something_cool
     test = MyModel.new
     test.name = 'test'
     test.save
  end
end

Then:

$ rake -T foo
rake foo:something_cool       # do something cool

You can even run the tasks via a cronjob.

Michael Larocque
+6  A: 

You can load the entire rails environment in any ruby script by simply requiring environment.rb:

require "#{ENV['RAILS_ROOT']}/config/environment"

This assumes the RAILS_ROOT environment variable is set, see my comment for other ways of doing this.

This has the added bonus of giving you all the nice classes and objects that you have in the rest of your rails code.

To kick off your processes it sounds like cron will do what you want, and I would also add a task to your capistrano recipe that would add your script to the crontab to periodically get the data from the external source and update your DB. This can easily be done with the cronedit gem.

The cron approach does have some drawbacks, mostly overhead and control, for other more sophisticated options see HowToRunBackgroundJobsInRails from the rails wiki.

csexton
You do realise that RAILS_ROOT isn't defined until environment.rb has already been loaded? :-)
Orion Edwards
Orion is very much correct. So I edited the above to look for the environment variable. You could also provide a full path or use one relative to your script with "#{File.dirname(__FILE__)}/path/to/env"
csexton
+2  A: 

I agree with the answer above but you have to include => :environment in your task or it will not load the Rails environment.

e.g.,

namespace :send do
  namespace :trial do
    namespace :expiry do
      desc "Sends out emails to people who's accounts are about to expire"
      task :warnings => :environment do
        User.trial_about_to_expire.has_not_been_notified_of_trial_expiry.each do |user|
          UserMailer.deliver_trial_expiring_warning(user)
          user.notified_of_trial_expiry = true
          user.save
        end
      end
    end
  end
end
Tim Harding
+1  A: 

There are few steps to this and more details needed to really answer well.

You say your site retrieves data from other sources? How often? If it is semi-regularly you definitely want to look into background processing/messaging. If it is frequently you really want to avoid loading your rails environment every time your script runs since you will be paying too high a startup tax each time.

There are a multitude of options out there you will want to research. Reading about each of them, particularly reviews from people who post about why they made the choice they did, will give you a good feel for what questions you need to ask yourself before you make your choice. How big a job is loading the data? etc...

Off the top of my head these are some of the things you may want to look into

Script/Runner & Cron Background/RB Starling Workling MemcacheQ Beanstalk Background Job (Bj) delayed_job (Dj) Daemon Generator

srboisvert
+1  A: 

I wrote up a post about this a while back.

http://www.rawblock.com/2007/06/14/ruby-oracle-mac-os-x-pain-jruby-and-activerecord-jdbc-to-the-rescue/

madlep
A: 

Nice Joyent write up of using rake to run rails tasks from a cron job - http://wiki.joyent.com/accelerators:kb:rails:cron

David Burrows
A: 

Check out a relatively short answer I gave on another question.

It contains 2 examples for using cron to run rake tasks and class methods (via script/runner). In both cases, Rails is loaded and you can use your models.

webmat