How to access ActiveRecord & Migrations outside of Ruby on Rails

Rails migrations are an extremely handy way of capturing changes to your database and rolling them back if they are incorrect.

Manipulating or querying a database is a pretty useful tool and ActiveRecord turns your tables into objects so I looked into how to access these features outside of a Rails environment.

I wanted to be able to either specify the database connection details directly in the script if I was running it outside of a Rails application, or to be able to bind into the Rails application's database.yml database configuration file.

To run it as a standalone script with the database details described inside the script run:

ruby play_with_the_database.rb
To run it within the context of a Rails application place the script inside a new directory called db/script and run:
ruby play_with_the_database.rb development
If you want to run the above script against the production database run:
ruby play_with_the_database.rb production
I'll show you the ruby code that I came up with and then explain the important bits below.
require 'active_record'
require 'pp'

database_type = ARGV[0]

if database_type.nil?
database_type = 'custom'
database_yaml = <<-YAML
#{database_type}:
adapter: oracle
database: database.andrewbeacock.com/beacock01
username: my_schema
password: my_schema
YAML
else
# if you have a Rails project then place your scripts in db/script
database_yaml = IO.read('../../config/database.yml')
end

class MyTable < ActiveRecord::Base; set_table_name :my_table; end

class Script < ActiveRecord::Migration
def self.run
execute('update my_table set updated = true')
pp MyTable.find(:all)
end
end

databases = YAML::load(database_yaml)
ActiveRecord::Base.establish_connection(databases[database_type])
Script.run
  • database_type - this is the RAILS_ENV that you want to run the database script against if you are running within a Rails application, it's empty at the start of the script and set to the value that you pass as the first argument on the command line. If an argument is present it looks up that value in the database.yml file otherwise it uses the inlined database connection details within the script.

  • class MyTable - any ActiveRecord object definitions go here allowing you to map objects to tables.

  • def self.run - Place the guts of your database script within this method so that it has access to all the ActiveRecord objects as well as any methods that migrations have access to (such as execute, create_table, add_column, remove_index, etc.).
This script was spawned from two things, a requirement to change the values in a database table without it being a fully tracked migration and a post on Dave Thomas's blog regarding Migrations Outside Rails.

Enjoy!

Technorati Tags: , , , , , ,

2 comments:

bryan thompson said...

Very cool. I usually do things like this with either rake tasks or by loading my environment.rb in a script I lay in doc/, but I will give this a try sometime.

idimitar said...

I am interested in how exactly can I mimic Rails: it remembers which step of the DB migrations you are currently on, and thus it doesn't try to create tables on every application start.

Can you elaborate on what would be the way of doing this? Obviously, reusing another piece of Rails would be the ideal solution, because obviously (again), everyone can manage a file remembering what was the last DB migration you ran and thus not start over every time.

An info piece like this would be highly appreciated.

Take care!