Connecting to multiple DBs in Ruby On Rails
Autor: AJ O. Alves | Publicado: 2012-11-27T20:54:00-03:00

Ruby on Rails is very handy framework for various situations of everyday in a&nbsp; WEB developer day. However some time it is necessary  to do some not common tasks, such as connect our application  in multiple databases.


Image from www.askqtp.com
So here we go! I will show to you how to set ActiveRecord (Class responsible to abstract the database level) to connect to multiple relational databases (In a future post I'll show you how to work with NoSQL), similar, or differents like SQLite, MySQL, PostgreSQL, OracleSQL, SQL Server ...

Application Example: I want to create an application to manage projects. However I want to put user table in a external data base of application to other future applications also use this unified user base.

Step 1: Configure your database.yml

The first step is to configure your "config/database.yml" to make their connections, for example, we have this file:

# SQLite version 3.x
#&nbsp;&nbsp; gem install sqlite3
#
#&nbsp;&nbsp; Ensure the SQLite 3 gem is defined in your Gemfile
#&nbsp;&nbsp; gem 'sqlite3'
development:
&nbsp; adapter: sqlite3
&nbsp; database: db/development.sqlite3
&nbsp; pool: 5
&nbsp; timeout: 5000

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
&nbsp; adapter: sqlite3
&nbsp; database: db/test.sqlite3
&nbsp; pool: 5
&nbsp; timeout: 5000

production:
&nbsp; adapter: sqlite3
&nbsp; database: db/production.sqlite3
&nbsp; pool: 5
&nbsp; timeout: 5000

This is the default setting with a base in SQLite, by default, configured with the name of the execution environments. Now we build a new configuration, I called it "external_database_1" note that we will connect to a MySQL database, which is perfectly possible as the hard work of how to deal with each type of database is carry by ActiveRecord. Also remember to reference the adapters GENs you are using, in this example "gem 'sqlite3 '; gem 'mysql2 '; "

development:
&nbsp; adapter: sqlite3
&nbsp; database: db/development.sqlite3
&nbsp; pool: 5
&nbsp; timeout: 5000

test:
&nbsp; adapter: sqlite3
&nbsp; database: db/test.sqlite3
&nbsp; pool: 5
&nbsp; timeout: 5000

production:
&nbsp; adapter: sqlite3
&nbsp; database: db/production.sqlite3
&nbsp; pool: 5
&nbsp; timeout: 5000

external_database_1:
&nbsp; adapter: mysql2
&nbsp; encoding: utf8
&nbsp; database: test_db
&nbsp; pool: 5
&nbsp; username: root
&nbsp; password: root
&nbsp; host: localhost

Step 2: Create a class to handle the connection

The second step is to create a class that served to manipulate the new connections, let's first create a file called "my_databases.rb" inside of folder "config/initializers/", all we need now is to create a class that inherits ActiveRecord::Base, for example:

class ProjectDB &lt; ActiveRecord::Base
&nbsp;&nbsp; &nbsp;self.abstract_class = true
&nbsp;&nbsp; &nbsp;establish_connection Rails.env.to_sym
end

class TestDB &lt; ActiveRecord::Base
&nbsp;&nbsp; &nbsp;self.abstract_class = true
&nbsp;&nbsp; &nbsp;establish_connection :external_database_1
end

Much attention at this point!! Why two class?!
Basically for organization and simplicity, so that each model be explicitly tied to which connection it belongs!

The first class "ProjectdDB" refers connections standards (:development, :test, :production) in SQLite. Note the value that is passed to the method "establish_connection" which is Rails.env.to_sym.

The second class "TestDB" refers to our external database in MySQL

Obs:. "self.abstract_class = true" means that the class is abstract, it can not be instantiated only inherited.

At this point we already have all our application configured to work with different database. Now I will show by example how we have to work with it.&nbsp;

Example 1: Create the users table.

rails generate scaffold user name:string email:string
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; active_record
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp; db/migrate/20121127205353_create_users.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp; app/models/user.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; test_unit
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test/unit/user_test.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test/fixtures/users.yml
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; resource_route
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; route&nbsp;&nbsp;&nbsp; resources :users
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; scaffold_controller
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp; app/controllers/users_controller.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/users
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/users/index.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/users/edit.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/users/show.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/users/new.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/users/_form.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; test_unit
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test/functional/users_controller_test.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; helper
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/helpers/users_helper.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test_unit
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test/unit/helpers/users_helper_test.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; assets
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; coffee
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/assets/javascripts/users.js.coffee
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; scss
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/assets/stylesheets/users.css.scss
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; scss
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp; app/assets/stylesheets/scaffolds.css.scss

As the users table should be in an external database, then:

1º - Changing the file "app/models/user.rb"
class User &lt; ActiveRecord::Base
&nbsp;&nbsp;&nbsp; attr_accessible :email, :name
end

to: 

class User &lt; TestDB
&nbsp;&nbsp;&nbsp; attr_accessible :email, :name
end

replace ActiveRecord::Base by its subclass TestDB thus the application knows where users table will be created.

2º - Run migration appropriately
rake db:migrate RAILS_ENV=database_externa_1
&nbsp;&nbsp; ==&nbsp; CreateUsers: migrating
&nbsp;&nbsp; ====================================================
&nbsp;&nbsp; -- create_table(:users)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; 0.0711s
&nbsp;&nbsp; ==&nbsp; CreateUsers: migrated (0.0712s)
&nbsp;&nbsp; ===========================================

passing the connection "external_database_1" as a parameter, so the table will be created in the outer database.

Example 2: Create the design table.
rails generate scaffold project user_id:integer name:string description:text
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; active_record
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp; db/migrate/20121127212538_create_projects.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp; app/models/project.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; test_unit
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test/unit/project_test.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test/fixtures/projects.yml
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; resource_route
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; route&nbsp;&nbsp;&nbsp; resources :projects
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; scaffold_controller
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp; app/controllers/projects_controller.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/projects
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/projects/index.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/projects/edit.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/projects/show.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/projects/new.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/views/projects/_form.html.erb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; test_unit
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test/functional/projects_controller_test.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; helper
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/helpers/projects_helper.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test_unit
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test/unit/helpers/projects_helper_test.rb
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; assets
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; coffee
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/assets/javascripts/projects.js.coffee
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp;&nbsp;&nbsp; scss
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; app/assets/stylesheets/projects.css.scss
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; invoke&nbsp; scss
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; identical&nbsp;&nbsp;&nbsp; app/assets/stylesheets/scaffolds.css.scss

As the projects table must be in the local database:

1º - Changing the file "app/models/project.rb"
class Project &lt; ActiveRecord::Base
&nbsp;&nbsp; &nbsp;attr_accessible :description, :name, :user_id
end

to: 

class Project &lt; ProjectDB
&nbsp;&nbsp; &nbsp;attr_accessible :description, :name, :user_id

&nbsp;&nbsp; &nbsp;belongs_to :user
end

replace ActiveRecord::Base subclass by its ProjectDB. Note that all relations between the models can be built normally, for the application it not makes more difference the location or type of databases. Then configure the model "user" as well:

class User &lt; TestDB
&nbsp;&nbsp;&nbsp; attr_accessible :email, :name

&nbsp;&nbsp;&nbsp; has_many :projects
end

2º - Run migration appropriately

rake db:migrate
&nbsp;&nbsp; ==&nbsp; CreateUsers: migrating
&nbsp;&nbsp; ====================================================
&nbsp;&nbsp; -- create_table(:users)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; 0.0014s
&nbsp;&nbsp; ==&nbsp; CreateUsers: migrated (0.0015s)
&nbsp;&nbsp; ===========================================
&nbsp;&nbsp; ==&nbsp; CreateProjects: migrating
&nbsp;&nbsp; =================================================
&nbsp;&nbsp; -- create_table(:projects)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; 0.0014s
&nbsp;&nbsp; ==&nbsp; CreateProjects: migrated (0.0015s)
&nbsp;&nbsp; ========================================

See it not need to pass the connection parameter, it will uses the default of each environment.
Now, all models of your project must inherit from ProjecDB and not more of ActiveRecord::Base.
Okay, that's all you need to know to work with multiple relational databases in your Ruby on Rails project.
To test, go to terminal:

rails c
Loading development environment (Rails 3.2.8)
1.9.3p194 :001 &gt; User.create(:name =&gt; "AJ Alves", :email =&gt; "aj.alves@zerokol.com")
&nbsp;&nbsp; (0.1ms)&nbsp; BEGIN
&nbsp; SQL (0.3ms)&nbsp; INSERT INTO `users` (`created_at`, `email`, `name`, `updated_at`) VALUES ('2012-11-27 21:37:28', 'aj.alves@zerokol.com', 'AJ Alves', '2012-11-27 21:37:28')
&nbsp;&nbsp; (36.6ms)&nbsp; COMMIT
&nbsp;=&gt; #&lt;user 21:37:28="21:37:28" 2="2" aj.alves="aj.alves" alves="alves" created_at:="created_at:" email:="email:" id:="id:" name:="name:" updated_at:="updated_at:" zerokol.com="zerokol.com"&gt; 
1.9.3p194 :002 &gt; Project.create(:user_id =&gt; 1, :name =&gt; "Teste", :description =&gt; "Um projeto de teste")
&nbsp;&nbsp; (0.0ms)&nbsp; begin transaction
&nbsp; SQL (0.4ms)&nbsp; INSERT INTO "projects" ("created_at", "description", "name", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?)&nbsp; [["created_at", Tue, 27 Nov 2012 21:38:16 UTC +00:00], ["description", "Um projeto de teste"], ["name", "Teste"], ["updated_at", Tue, 27 Nov 2012 21:38:16 UTC +00:00], ["user_id", 1]]
&nbsp;&nbsp; (117.6ms)&nbsp; commit transaction
&nbsp;=&gt; #&lt;project 1="1" 21:38:16="21:38:16" 2="2" created_at:="created_at:" de="de" description:="description:" este="este" id:="id:" m="m" name:="name:" projeto="projeto" teste="teste" updated_at:="updated_at:" user_id:="user_id:"&gt; 
1.9.3p194 :003 &gt; u = User.first
&nbsp; User Load (0.4ms)&nbsp; SELECT `users`.* FROM `users` LIMIT 1
&nbsp;=&gt; #&lt;user 1="1" 21:33:42="21:33:42" aj.zerokol="aj.zerokol" alves="alves" created_at:="created_at:" email:="email:" gmail.com="gmail.com" id:="id:" name:="name:" updated_at:="updated_at:"&gt; 
1.9.3p194 :004 &gt; u.projects
&nbsp; Project Load (0.2ms)&nbsp; SELECT "projects".* FROM "projects" WHERE "projects"."user_id" = 1
&nbsp;=&gt; [#&lt;project 1="1" 21:33:58="21:33:58" created_at:="created_at:" d="d" description:="description:" dsfsdf="dsfsdf" este="este" id:="id:" name:="name:" sdf="sdf" sdfs="sdfs" updated_at:="updated_at:" user_id:="user_id:"&gt;, #&lt;project 1="1" 21:38:16="21:38:16" 2="2" created_at:="created_at:" de="de" description:="description:" este="este" id:="id:" m="m" name:="name:" projeto="projeto" teste="teste" updated_at:="updated_at:" user_id:="user_id:"&gt;] 
1.9.3p194 :005 &gt; p = Project.first
&nbsp; Project Load (0.2ms)&nbsp; SELECT "projects".* FROM "projects" LIMIT 1
&nbsp;=&gt; #&lt;project 1="1" 21:33:58="21:33:58" created_at:="created_at:" d="d" description:="description:" dsfsdf="dsfsdf" este="este" id:="id:" name:="name:" sdf="sdf" sdfs="sdfs" updated_at:="updated_at:" user_id:="user_id:"&gt; 
1.9.3p194 :006 &gt; p.user
&nbsp; User Load (0.4ms)&nbsp; SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
&nbsp;=&gt; #&lt;user 1="1" 21:33:42="21:33:42" aj.zerokol="aj.zerokol" alves="alves" created_at:="created_at:" email:="email:" gmail.com="gmail.com" id:="id:" name:="name:" updated_at:="updated_at:"&gt; 
1.9.3p194 :007 &gt;