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.
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
Any help would be greatly appreciated
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'
A quick demonstration within Rails to provide a parallel “private” database for an application.
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.
Add the following to the bottom of config/database.yml
private: adapter: sqlite3 database: db/private.sqlite3
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
> 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.
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.
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.
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>
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.
There ends our example of a Rails application using one model class to access multiple databases cleanly.
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"
http://www.drnicwilliams.com/ – for future announcements and other stories and things.
Discussion about the Magic Multi-Connections is on the Magic Models forum:
http://groups.google.com/group/magicmodels
This code is free to use under the terms of the MIT licence.
Comments are welcome. Send an email to Dr Nic Williams.