↩ More Magic

Magic Multi-Connections

Get Version 1.2.0

→ Ruby on Rails

→ ActiveRecords

WARNING

Sponsors

Despite the 1.1.0 version number, this gem is not quite production ready. Various people have experienced problems using the 1.0.0 version. A solution was found to deal with this issue but it has not been fully tested, so please subscribe to the forum or RubyForge news for any updates.

What

ActiveRecord models are allowed one connection to a database at a time, per class. Ruby on Rails sets up the default connection based on your database.yml configuration to automatically select development, test or production.

But, what if you want to access two or more databases – have 2+ connections open – at the same time. ActiveRecord requires that you subclass ActiveRecord::Base.

That prevents you doing migrations from one database to another. It prevents you using one set of model classes on two or more databases with the same schema.

Magic Multi-Connections allows you to write your models once, and use them for multiple database Rails databases at the same time. How? Using magical namespacing.

class Person < ActiveRecord::Base; end
ActiveRecord::Base.establish_connection :production
Person.connection # => production

module ContactRepository
  establish_connection :contact_repo
end
ContactRepository::Person.connection # => contact_repo

old_person = ContactRepository::Person.find_by_email(email)
person = old_person.create_as(Person)

You do not have to redefine your models for the multi-connection module ContactRepository, they are automatically picked up for you. Magically.

TODO: Example about Associations

Issues

Despite the 1.1.0 version of this gem there are still a number of issues with this gem:

Any help would be greatly appreciated

Installing

sudo gem install magic_multi_connections

Rails: Add the following to the bottom of your environment.rb file

require 'magic_multi_connections'

Ruby scripts: Add the following to the top of your script

require 'rubygems'
require 'magic_multi_connections'

Demonstration with Rails

A quick demonstration within Rails to provide a parallel “private” database for an application.

1. Create rails app

Using sqlite3 here, but use your preferred db:

> rails privacy -d sqlite3
> cd privacy
> ruby script/generate model Person
> cp config/environments/development.rb config/environments/private.rb

The last line allows us to play with our private database within the console and rake tasks.

2. Edit config/database.yml and add our private database:

Add the following to the bottom of config/database.yml

private:
  adapter: sqlite3
  database: db/private.sqlite3

3. Create a database schema

Edit db/migrate/001_create_people.rb

class CreatePeople < ActiveRecord::Migration
  def self.up
    create_table :people do |t|
      t.column :name, :string
    end
  end

  def self.down
    drop_table :people
  end
end

From the command line, migrate this to our development and private databases:

> rake db:migrate
> rake db:migrate RAILS_ENV=private

4. Add some data to databases

> ruby script/console development
>> Person.create(:name => 'Nic')
>> Person.create(:name => 'Banjo')
>> exit
> ruby script/console private
>> Person.create(:name => 'Super Magical Nic')
>> exit

Now it should be obvious which database our app is accessing.

5. Update environment.rb

Edit config/environment.rb to include the library and create the Private module.

Add the following to the end of the file.

require "magic_multi_connections"

module Private
  establish_connection :private
end

This tells the Private module that any model class that is requested will be assigned a connection to the private database, as defined in the config/database.yml specification.

6. Setup a controller

Create a people controller with a index action

> ruby script/generate controller people index

Edit your controller app/controllers/people_controller.rb

class PeopleController < ApplicationController

  before_filter :check_private
  
  def index
    @people = @mod::Person.find(:all)
  end

  private
  def check_private
    @mod = params[:private] ? Private : Object
  end
end

The check_private action is a hack to demonstrate one method for selecting the database. In reality, a stupid one for hiding a “private” database, but you get the point.

After check_private is called, @mod is either the Object (default) module or the Private module. The Person class is accessible through either of them.

Yes, @mod::Person is uglier than just Person. Sorry.

7. Setup the index.rhtml view

Edit app/views/people/index.rhtml

<h1><%= @mod::Person %></h1>
<h2><%= @mod::Person.active_connection_name %></h2>

<ol>
  <% @people.each do |person| -%>
  <li><%= "#{person.name} - #{person.class}" %></li>
  <% end -%>
</ol>

8. Test our multi-connection Rails app

Launch the app

> mongrel_rails start

In your browser, go to http://localhost:3000/people and see the list of people: Nic and Banjo.

Now, to see the private database, go to http://localhost:3000/people?private=1 and see the private list. Its so private, I won’t show it here.

Note: you may need to refresh the private url to see the results. Perhaps Rails is caching the SQL even though its a different connection to a different database. If you know what’s happening here, please email me or the forum. Thanks.

9. End

There ends our example of a Rails application using one model class to access multiple databases cleanly.

Pre-existing modules

In Rails, model files are placed in the app/models folder. If you place them in a subfolder, say app/models/admin, then those model classes are access via module namespaces.

So, app/models/admin/page.rb represents the Admin::Page class.

Magic Multi-Connections works for these model classes as well.

Admin.establish_connection :admin_dev
Admin::Page.active_connection_name # => "Admin::Page"

Related articles

Dr Nic’s Blog

http://www.drnicwilliams.com – for future announcements and other stories and things.

Forum

Discussion about the Magic Multi-Connections is on the Magic Models forum:

http://groups.google.com/group/magicmodels

Licence

This code is free to use under the terms of the MIT licence.

Contact

Comments are welcome. Send an email to Dr Nic Williams.

Dr Nic, 2nd January 2008
Theme extended from Paul Battley