ActiveRecord::Base.establish_connection

WARNING

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:

  • Single Table Inheritance is not currently supported
  • No connection pooling for alternate databases

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

<%= @mod::Person %>

<%= @mod::Person.active_connection_name %>

    <% @people.each do |person| -%>
  1. <%= "#{person.name} - #{person.class}" %>
  2. <% end -%>

 

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

  • Original blog article – Magic Multi-Connections: A “facility in Rails to talk to more than one database at a time
  • Discussed by DHH

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

ActiveRecord是Rails提供的一个对象关系映射(ORM)层,从这篇开始,我们来了解Active Record的一些基础内容,连接数据库,映射表,访问数据等。

Active Record使用基本的ORM模式:表映射成类,行映射成为对象,列映射成对象的属性。与很多大量使用配置的ORM库不同,Active Record最小化了配置。想象一下,有一个使用Active Record的程序把Mysql数据库中的orders表转换到类,通过制定的ID查找到order,设定order的名称,然后保存回数据库:

require "rubygems"

require_gem "activerecord"

ActiveRecord::Base.establish_connection(:adapter => "mysql",

:host => "localhost", :database => "railsdb")

class Order < ActiveRecord::Base

end

order = Order.find(123)

order.name = "Dave Thomas"

order.save

 

在上面的例子里不需要任何配置,Active Record为我们做了这些事情,下面我们来看看ActiveRecord是怎样工作的。

 

表和类

    当你创建了一个ActiveRecord::Base类的子类,Active Record假定表名是复数的,而类名是单数的,当类名包括多个单词时,表名被假定为单词间带有下划线,复数形式不规则,例如:

类名                表名                    类名               表名

Order               orders                LineItem           line_items

TaxAgency          tax_agencies          Person             people

Diagnosis           diagnoses             Quantity           quantities

Batch               batches               Datum              data

默认的,Active Record的表名是复数的,类名是单数的,如果你不太习惯,可以通过设置一个全局标记来禁用它,在config目录的environment.rb文件中设置:

ActiveRecord::Base.pluralize_table_names = false

 

单复数规则可以对付大部分情况,对于一些特殊情况,Active Record允许我们覆盖默认的生成的表名,使用set_table_name命令,例如:

class Sheep < ActiveRecord::Base

set_table_name "sheep" # Not "sheeps"

end

class Order < ActiveRecord::Base

set_table_name "ord_rev99_x" # Wrap a legacy table...

end

 Active Record抽象了数据库连接的概念,帮助应用程序来处理底层的数据库链接的细节,作为替代,Active Record使用通用的调用,将细节委托给一组数据库适配器。

  可以使用establish_connection( )方法来制定连接,下面的例子创建了一个mysql数据库连接,数据库的名字是railsdb,服务器的Host名为dbserver.com,用户名为railsuser,密码为railspw。

ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "dbserver.com",
:database => "railsdb",
:username => "railsuser",
:password => "railspw"
)

 

  Active Record支持DB2,MySql,Oracle,Postgres,SqlServer,以及SqlLite,每一种数据库适配器在链接的参数上都有一些细小的差别,下表列出了常用的参数:

ActiveRecord::Base.establish_connection_第1张图片

  注意Oracle适配器的名字为oci。

  数据库连接和Model类是关联的,每个类都从父类那里继承了链接,ActiveRecord::Base作为所有的Active Record类的父类,设置这里的数据库连接就给所有的活动记录类设置了链接,当然,如果需要的话,你也可以复写(override)链接配置。

  下面的例子里,我们的大多数表都在MySql数据库中,库名为online,由于一些历史原因,customers表在名为backend的数据库中,

ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "dbserver.com",
:database => "online",
:username => "groucho",
:password => "swordfish")
class LineItem < ActiveRecord::Base
# ...
end
class Order < ActiveRecord::Base
# ...
end
class Product < ActiveRecord::Base
# ...
end
class Customer < ActiveRecord::Base
# ...
end
Customer.establish_connection(
:adapter => "mysql",
:host => "dbserver.com",
:database => "backend",
:username => "chicho",
:password => "piano")

 

  在我们前面所写的depot程序中,我们没有使用establish_connection方法,而是在config/database.yaml文件中指定了数据库连接的参数信息,对于大多数rails程序来说,这是首选的方式,不仅因为将配置信息和代码分离,而且在测试和部署时也能带来方便,上面的表格里列出的参数都可以应用在YAML文件中,这一点我们在前面的配置文件一节已经有介绍。

  最后,如果你通过一个标记访问establish_connection(),Rails会在database.yaml文件中查找名字对应的配置节,来获取链接的参数,这样就可以将所有的数据库连接配置从代码中分离出来。

 

config = YAML::load(File.open('config/database.yml'))
    ActiveRecord::Base.establish_connection(config["production"]) 

 

你可能感兴趣的:(ActiveRecord::Base.establish_connection)