magic_multi_connections 1.0.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +0 -0
- data/History.txt +0 -0
- data/Manifest.txt +43 -32
- data/README.txt +0 -0
- data/Rakefile +21 -15
- data/lib/ext_active_record/association_extensions.rb +90 -0
- data/lib/ext_active_record/connection_specification.rb +0 -0
- data/lib/magic_multi_connections.rb +22 -21
- data/lib/magic_multi_connections/connected.rb +91 -29
- data/lib/magic_multi_connections/module.rb +27 -26
- data/lib/magic_multi_connections/version.rb +9 -9
- data/scripts/txt2html +0 -0
- data/scripts/txt2js +1 -1
- data/setup.rb +0 -0
- data/test/connections/native_mysql/connection.rb +24 -0
- data/test/connections/native_postgresql/connection.rb +0 -0
- data/test/fixtures/address.rb +3 -0
- data/test/fixtures/addresses.yml +4 -0
- data/test/fixtures/assignment.rb +7 -0
- data/test/fixtures/classified.rb +5 -0
- data/test/fixtures/contact_repository.rb +2 -2
- data/test/fixtures/db_definitions/mysql.sql +19 -0
- data/test/fixtures/db_definitions/postgresql.sql +7 -1
- data/test/fixtures/habit.rb +3 -0
- data/test/fixtures/paycheck.rb +3 -0
- data/test/fixtures/people.yml +0 -0
- data/test/fixtures/person.rb +3 -1
- data/test/fixtures/soldier.rb +8 -0
- data/test/test_helper.rb +71 -71
- data/test/test_magic_multi_connections.rb +27 -26
- data/test/test_mmc_associations.rb +46 -0
- data/test/test_parent_module.rb +0 -0
- data/test/test_preexisting_module.rb +0 -0
- data/website/index.html +354 -340
- data/website/index.txt +286 -223
- data/website/javascripts/rounded_corners_lite.inc.js +0 -0
- data/website/stylesheets/screen.css +0 -0
- data/website/template.js +0 -0
- data/website/template.rhtml +0 -0
- data/website/version-raw.js +2 -2
- data/website/version-raw.txt +1 -1
- data/website/version.js +1 -1
- data/website/version.txt +0 -0
- metadata +63 -37
data/website/index.txt
CHANGED
@@ -1,223 +1,286 @@
|
|
1
|
-
h1. Magic Multi-Connections
|
2
|
-
|
3
|
-
h1. → Ruby on Rails
|
4
|
-
|
5
|
-
h1. → ActiveRecords
|
6
|
-
|
7
|
-
h2.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
<
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
>
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
<
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
Add the following to the
|
111
|
-
|
112
|
-
<pre syntax="ruby">
|
113
|
-
require
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
</
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
1
|
+
h1. Magic Multi-Connections
|
2
|
+
|
3
|
+
h1. → Ruby on Rails
|
4
|
+
|
5
|
+
h1. → ActiveRecords
|
6
|
+
|
7
|
+
h2. WARNING
|
8
|
+
|
9
|
+
Despite the 1.2.0 version number, this gem is not quite production ready. Various people have "experienced problems":http://joshknowles.com/2007/6/17/migrations-pains 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":http://groups.google.com/group/magicmodels or "RubyForge news":http://rubyforge.org/export/rss_sfnewreleases.php for any updates.
|
10
|
+
|
11
|
+
h2. What
|
12
|
+
|
13
|
+
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*.
|
14
|
+
|
15
|
+
But, what if you want to access two or more databases - have 2+ connections open - at the same time. ActiveRecord requires that you subclass <code>ActiveRecord::Base</code>.
|
16
|
+
|
17
|
+
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.
|
18
|
+
|
19
|
+
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.
|
20
|
+
|
21
|
+
<pre syntax="ruby">class Person < ActiveRecord::Base; end
|
22
|
+
ActiveRecord::Base.establish_connection :production
|
23
|
+
Person.connection # => production
|
24
|
+
|
25
|
+
module ContactRepository
|
26
|
+
establish_connection :contact_repo
|
27
|
+
end
|
28
|
+
ContactRepository::Person.connection # => contact_repo
|
29
|
+
|
30
|
+
old_person = ContactRepository::Person.find_by_email(email)
|
31
|
+
person = old_person.create_as(Person)
|
32
|
+
</pre>
|
33
|
+
|
34
|
+
You do not have to redefine your models for the multi-connection module <code>ContactRepository</code>, they are automatically picked up for you. Magically.
|
35
|
+
|
36
|
+
h3. But what about associations, do they change over as well?
|
37
|
+
|
38
|
+
Yup! These are now supported, though maybe not as magically:
|
39
|
+
|
40
|
+
<pre syntax="ruby">class Person < ActiveRecord::Base
|
41
|
+
has_many :habits, :mirrors_db_connection => true
|
42
|
+
end
|
43
|
+
|
44
|
+
class Habit < ActiveRecord::Base
|
45
|
+
belongs_to :person, :mirrors_db_connection => true
|
46
|
+
end
|
47
|
+
ActiveRecord::Base.establish_connection :production
|
48
|
+
|
49
|
+
module ContactRepository
|
50
|
+
establish_connection :contact_repo
|
51
|
+
end
|
52
|
+
|
53
|
+
old_person = ContactRepository::Person.find_by_email(email)
|
54
|
+
old_person.habits.first.connection # => contact_repo
|
55
|
+
</pre>
|
56
|
+
|
57
|
+
h3. Hey wait a minute, wasn't there some kind of automatic namespacing association?
|
58
|
+
|
59
|
+
Yes but that has been deprecated. In the previous version of this gem associations were handled by automatically changing any assocation whose target class was in the same namespace. While it is still possible to do this, this automatic namespace association was at best clunky and will be removed in the 2.0 release. Here is an example of how it works:
|
60
|
+
|
61
|
+
<pre syntax="ruby">ActiveRecord::Base.establish_connection :production
|
62
|
+
|
63
|
+
module Military
|
64
|
+
class Soldier < ActiveRecord::Base
|
65
|
+
has_many :assignments
|
66
|
+
has_many :paychecks
|
67
|
+
end
|
68
|
+
|
69
|
+
class Assignment < ActiveRecord::Base
|
70
|
+
belongs_to :person, :mirrors_db_connection => true
|
71
|
+
end
|
72
|
+
|
73
|
+
module Classified
|
74
|
+
establish_connection :classified
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Paycheck
|
79
|
+
belongs_to :soldier
|
80
|
+
end
|
81
|
+
|
82
|
+
Military::Classified::Soldier.reflections[:assignments].klass # => Military::Classified::Assignment
|
83
|
+
Military::Classified::Soldier.reflections[:assignments].klass.connection # => :classified
|
84
|
+
Military::Classified::Soldier.reflections[:paycheck].klass # => Paycheck
|
85
|
+
Military::Classified::Soldier.reflections[:paycheck].klass.connection # => :production
|
86
|
+
</pre>
|
87
|
+
|
88
|
+
Again this method will be deprecated in the 2.0 release so try and avoid using this.
|
89
|
+
|
90
|
+
h3. HOW TO TURN OFF DEFAULT NAMESPACE REFLECTION MIRRORING
|
91
|
+
|
92
|
+
h2. Issues
|
93
|
+
|
94
|
+
Despite the 1.1.0 version of this gem there are still a number of issues with this gem:
|
95
|
+
<ul>
|
96
|
+
<li>Single Table Inheritance is not currently supported</li>
|
97
|
+
<li>No connection pooling for alternate databases</li>
|
98
|
+
</ul>
|
99
|
+
|
100
|
+
Any help would be greatly appreciated
|
101
|
+
|
102
|
+
h2. Installing
|
103
|
+
|
104
|
+
<pre syntax="ruby">sudo gem install magic_multi_connections</pre>
|
105
|
+
|
106
|
+
Rails: Add the following to the bottom of your <code>environment.rb</code> file
|
107
|
+
|
108
|
+
<pre syntax="ruby">require 'magic_multi_connections'</pre>
|
109
|
+
|
110
|
+
Ruby scripts: Add the following to the top of your script
|
111
|
+
|
112
|
+
<pre syntax="ruby">require 'rubygems'
|
113
|
+
require 'magic_multi_connections'</pre>
|
114
|
+
|
115
|
+
|
116
|
+
h2. Demonstration with Rails
|
117
|
+
|
118
|
+
A quick demonstration within Rails to provide a parallel "private" database for an application.
|
119
|
+
|
120
|
+
h3. 1. Create rails app
|
121
|
+
|
122
|
+
Using sqlite3 here, but use your preferred db:
|
123
|
+
|
124
|
+
<pre>> rails privacy -d sqlite3
|
125
|
+
> cd privacy
|
126
|
+
> ruby script/generate model Person
|
127
|
+
> cp config/environments/development.rb config/environments/private.rb
|
128
|
+
</pre>
|
129
|
+
|
130
|
+
The last line allows us to play with our *private* database within the console and rake tasks.
|
131
|
+
|
132
|
+
h3. 2. Edit *config/database.yml* and add our private database:
|
133
|
+
|
134
|
+
Add the following to the bottom of *config/database.yml*
|
135
|
+
|
136
|
+
<pre syntax="yaml">
|
137
|
+
private:
|
138
|
+
adapter: sqlite3
|
139
|
+
database: db/private.sqlite3
|
140
|
+
</pre>
|
141
|
+
|
142
|
+
h3. 3. Create a database schema
|
143
|
+
|
144
|
+
Edit *db/migrate/001_create_people.rb*
|
145
|
+
|
146
|
+
<pre syntax="ruby">class CreatePeople < ActiveRecord::Migration
|
147
|
+
def self.up
|
148
|
+
create_table :people do |t|
|
149
|
+
t.column :name, :string
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.down
|
154
|
+
drop_table :people
|
155
|
+
end
|
156
|
+
end
|
157
|
+
</pre>
|
158
|
+
|
159
|
+
From the command line, migrate this to our *development* and *private* databases:
|
160
|
+
|
161
|
+
<pre>> rake db:migrate
|
162
|
+
> rake db:migrate RAILS_ENV=private</pre>
|
163
|
+
|
164
|
+
h3. 4. Add some data to databases
|
165
|
+
|
166
|
+
<pre syntax="ruby">> ruby script/console development
|
167
|
+
>> Person.create(:name => 'Nic')
|
168
|
+
>> Person.create(:name => 'Banjo')
|
169
|
+
>> exit
|
170
|
+
> ruby script/console private
|
171
|
+
>> Person.create(:name => 'Super Magical Nic')
|
172
|
+
>> exit</pre>
|
173
|
+
|
174
|
+
Now it should be obvious which database our app is accessing.
|
175
|
+
|
176
|
+
h3. 5. Update environment.rb
|
177
|
+
|
178
|
+
Edit *config/environment.rb* to include the library and create the *Private* module.
|
179
|
+
|
180
|
+
Add the following to the end of the file.
|
181
|
+
|
182
|
+
<pre syntax="ruby">
|
183
|
+
require "magic_multi_connections"
|
184
|
+
|
185
|
+
module Private
|
186
|
+
establish_connection :private
|
187
|
+
end
|
188
|
+
</pre>
|
189
|
+
|
190
|
+
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.
|
191
|
+
|
192
|
+
h3. 6. Setup a controller
|
193
|
+
|
194
|
+
Create a *people* controller with a *index* action
|
195
|
+
|
196
|
+
<pre>> ruby script/generate controller people index</pre>
|
197
|
+
|
198
|
+
Edit your controller *app/controllers/people_controller.rb*
|
199
|
+
|
200
|
+
<pre syntax="ruby">class PeopleController < ApplicationController
|
201
|
+
|
202
|
+
before_filter :check_private
|
203
|
+
|
204
|
+
def index
|
205
|
+
@people = @mod::Person.find(:all)
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
def check_private
|
210
|
+
@mod = params[:private] ? Private : Object
|
211
|
+
end
|
212
|
+
end
|
213
|
+
</pre>
|
214
|
+
|
215
|
+
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.
|
216
|
+
|
217
|
+
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.
|
218
|
+
|
219
|
+
Yes, <code>@mod::Person</code> is uglier than just <code>Person</code>. Sorry.
|
220
|
+
|
221
|
+
h3. 7. Setup the index.rhtml view
|
222
|
+
|
223
|
+
Edit *app/views/people/index.rhtml*
|
224
|
+
|
225
|
+
<pre syntax="html"><h1><%= @mod::Person %></h1>
|
226
|
+
<h2><%= @mod::Person.active_connection_name %></h2>
|
227
|
+
|
228
|
+
<ol>
|
229
|
+
<% @people.each do |person| -%>
|
230
|
+
<li><%= "#{person.name} - #{person.class}" %></li>
|
231
|
+
<% end -%>
|
232
|
+
</ol>
|
233
|
+
</pre>
|
234
|
+
|
235
|
+
h3. 8. Test our multi-connection Rails app
|
236
|
+
|
237
|
+
Launch the app
|
238
|
+
|
239
|
+
<pre>> mongrel_rails start</pre>
|
240
|
+
|
241
|
+
In your browser, go to "http://localhost:3000/people":http://localhost:3000/people and see the list of people: Nic and Banjo.
|
242
|
+
|
243
|
+
Now, to see the private database, go to "http://localhost:3000/people?private=1":http://localhost:3000/people?private=1 and see the private list. Its so private, I won't show it here.
|
244
|
+
|
245
|
+
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":mailto:drnicwilliams@gmail.com?subject=MMC+caching+problem or the "forum":mailto:magicmodels@googlegroups.com?subject=MMC+caching+problem. Thanks.
|
246
|
+
|
247
|
+
h3. 9. End
|
248
|
+
|
249
|
+
There ends our example of a Rails application using one model class to access multiple databases cleanly.
|
250
|
+
|
251
|
+
h2. Pre-existing modules
|
252
|
+
|
253
|
+
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.
|
254
|
+
|
255
|
+
So, *app/models/admin/page.rb* represents the <code>Admin::Page</code> class.
|
256
|
+
|
257
|
+
Magic Multi-Connections works for these model classes as well.
|
258
|
+
|
259
|
+
<pre syntax="ruby">Admin.establish_connection :admin_dev
|
260
|
+
Admin::Page.active_connection_name # => "Admin::Page"
|
261
|
+
</pre>
|
262
|
+
|
263
|
+
h2. Related articles
|
264
|
+
|
265
|
+
* Original blog article - "Magic Multi-Connections: A “facility in Rails to talk to more than one database at a time":http://drnicwilliams.com/2007/04/12/magic-multi-connections-a-facility-in-rails-to-talk-to-more-than-one-database-at-a-time/
|
266
|
+
* "Discussed by DHH":http://www.loudthinking.com/arc/000610.html
|
267
|
+
|
268
|
+
|
269
|
+
h2. Dr Nic's Blog
|
270
|
+
|
271
|
+
"http://www.drnicwilliams.com":http://www.drnicwilliams.com - for future announcements and
|
272
|
+
other stories and things.
|
273
|
+
|
274
|
+
h2. Forum
|
275
|
+
|
276
|
+
Discussion about the Magic Multi-Connections is on the Magic Models forum:
|
277
|
+
|
278
|
+
"http://groups.google.com/group/magicmodels":http://groups.google.com/group/magicmodels
|
279
|
+
|
280
|
+
h2. Licence
|
281
|
+
|
282
|
+
This code is free to use under the terms of the MIT licence.
|
283
|
+
|
284
|
+
h2. Contact
|
285
|
+
|
286
|
+
Comments are welcome. Send an email to "Dr Nic Williams":mailto:drnicwilliams@gmail.com.
|