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.
Files changed (44) hide show
  1. data/CHANGELOG.txt +0 -0
  2. data/History.txt +0 -0
  3. data/Manifest.txt +43 -32
  4. data/README.txt +0 -0
  5. data/Rakefile +21 -15
  6. data/lib/ext_active_record/association_extensions.rb +90 -0
  7. data/lib/ext_active_record/connection_specification.rb +0 -0
  8. data/lib/magic_multi_connections.rb +22 -21
  9. data/lib/magic_multi_connections/connected.rb +91 -29
  10. data/lib/magic_multi_connections/module.rb +27 -26
  11. data/lib/magic_multi_connections/version.rb +9 -9
  12. data/scripts/txt2html +0 -0
  13. data/scripts/txt2js +1 -1
  14. data/setup.rb +0 -0
  15. data/test/connections/native_mysql/connection.rb +24 -0
  16. data/test/connections/native_postgresql/connection.rb +0 -0
  17. data/test/fixtures/address.rb +3 -0
  18. data/test/fixtures/addresses.yml +4 -0
  19. data/test/fixtures/assignment.rb +7 -0
  20. data/test/fixtures/classified.rb +5 -0
  21. data/test/fixtures/contact_repository.rb +2 -2
  22. data/test/fixtures/db_definitions/mysql.sql +19 -0
  23. data/test/fixtures/db_definitions/postgresql.sql +7 -1
  24. data/test/fixtures/habit.rb +3 -0
  25. data/test/fixtures/paycheck.rb +3 -0
  26. data/test/fixtures/people.yml +0 -0
  27. data/test/fixtures/person.rb +3 -1
  28. data/test/fixtures/soldier.rb +8 -0
  29. data/test/test_helper.rb +71 -71
  30. data/test/test_magic_multi_connections.rb +27 -26
  31. data/test/test_mmc_associations.rb +46 -0
  32. data/test/test_parent_module.rb +0 -0
  33. data/test/test_preexisting_module.rb +0 -0
  34. data/website/index.html +354 -340
  35. data/website/index.txt +286 -223
  36. data/website/javascripts/rounded_corners_lite.inc.js +0 -0
  37. data/website/stylesheets/screen.css +0 -0
  38. data/website/template.js +0 -0
  39. data/website/template.rhtml +0 -0
  40. data/website/version-raw.js +2 -2
  41. data/website/version-raw.txt +1 -1
  42. data/website/version.js +1 -1
  43. data/website/version.txt +0 -0
  44. metadata +63 -37
@@ -1,223 +1,286 @@
1
- h1. Magic Multi-Connections
2
-
3
- h1. → Ruby on Rails
4
-
5
- h1. → ActiveRecords
6
-
7
- h2. What
8
-
9
- 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*.
10
-
11
- 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>.
12
-
13
- 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.
14
-
15
- 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.
16
-
17
- <pre syntax="ruby">class Person < ActiveRecord::Base; end
18
- ActiveRecord::Base.establish_connection :production
19
- Person.connection # => production
20
-
21
- module ContactRepository
22
- establish_connection :contact_repo
23
- end
24
- ContactRepository::Person.connection # => contact_repo
25
-
26
- old_person = ContactRepository::Person.find_by_email(email)
27
- person = old_person.create_as(Person)
28
- </pre>
29
-
30
- You do not have to redefine your models for the multi-connection module <code>ContactRepository</code>, they are automatically picked up for you. Magically.
31
-
32
- h2. Installing
33
-
34
- <pre syntax="ruby">sudo gem install magic_multi_connections</pre>
35
-
36
- Rails: Add the following to the bottom of your <code>environment.rb</code> file
37
-
38
- <pre syntax="ruby">require 'magic_multi_connections'</pre>
39
-
40
- Ruby scripts: Add the following to the top of your script
41
-
42
- <pre syntax="ruby">require 'rubygems'
43
- require 'magic_multi_connections'</pre>
44
-
45
-
46
- h2. Demonstration with Rails
47
-
48
- A quick demonstration within Rails to provide a parallel "private" database for an application.
49
-
50
- h3. 1. Create rails app
51
-
52
- Using sqlite3 here, but use your preferred db:
53
-
54
- <pre>> rails privacy -d sqlite3
55
- > cd privacy
56
- > ruby script/generate model Person
57
- > cp config/environments/development.rb config/environments/private.rb
58
- </pre>
59
-
60
- The last line allows us to play with our *private* database within the console and rake tasks.
61
-
62
- h3. 2. Edit *config/database.yml* and add our private database:
63
-
64
- Add the following to the bottom of *config/database.yml*
65
-
66
- <pre syntax="yaml">
67
- private:
68
- adapter: sqlite3
69
- database: db/private.sqlite3
70
- </pre>
71
-
72
- h3. 3. Create a database schema
73
-
74
- Edit *db/migrate/001_create_people.rb*
75
-
76
- <pre syntax="ruby">class CreatePeople < ActiveRecord::Migration
77
- def self.up
78
- create_table :people do |t|
79
- t.column :name, :string
80
- end
81
- end
82
-
83
- def self.down
84
- drop_table :people
85
- end
86
- end
87
- </pre>
88
-
89
- From the command line, migrate this to our *development* and *private* databases:
90
-
91
- <pre>> rake db:migrate
92
- > rake db:migrate RAILS_ENV=private</pre>
93
-
94
- h3. 4. Add some data to databases
95
-
96
- <pre syntax="ruby">> ruby script/console development
97
- >> Person.create(:name => 'Nic')
98
- >> Person.create(:name => 'Banjo')
99
- >> exit
100
- > ruby script/console private
101
- >> Person.create(:name => 'Super Magical Nic')
102
- >> exit</pre>
103
-
104
- Now it should be obvious which database our app is accessing.
105
-
106
- h3. 5. Update environment.rb
107
-
108
- Edit *config/environment.rb* to include the library and create the *Private* module.
109
-
110
- Add the following to the end of the file.
111
-
112
- <pre syntax="ruby">
113
- require "magic_multi_connections"
114
-
115
- module Private
116
- establish_connection :private
117
- end
118
- </pre>
119
-
120
- 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.
121
-
122
- h3. 6. Setup a controller
123
-
124
- Create a *people* controller with a *index* action
125
-
126
- <pre>> ruby script/generate controller people index</pre>
127
-
128
- Edit your controller *app/controllers/people_controller.rb*
129
-
130
- <pre syntax="ruby">class PeopleController < ApplicationController
131
-
132
- before_filter :check_private
133
-
134
- def index
135
- @people = @mod::Person.find(:all)
136
- end
137
-
138
- private
139
- def check_private
140
- @mod = params[:private] ? Private : Object
141
- end
142
- end
143
- </pre>
144
-
145
- 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.
146
-
147
- 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.
148
-
149
- Yes, <code>@mod::Person</code> is uglier than just <code>Person</code>. Sorry.
150
-
151
- h3. 7. Setup the index.rhtml view
152
-
153
- Edit *app/views/people/index.rhtml*
154
-
155
- <pre syntax="html"><h1><%= @mod::Person %></h1>
156
- <h2><%= @mod::Person.active_connection_name %></h2>
157
-
158
- <ol>
159
- <% @people.each do |person| -%>
160
- <li><%= "#{person.name} - #{person.class}" %></li>
161
- <% end -%>
162
- </ol>
163
- </pre>
164
-
165
- h3. 8. Test our multi-connection Rails app
166
-
167
- Launch the app
168
-
169
- <pre>> mongrel_rails start</pre>
170
-
171
- In your browser, go to "http://localhost:3000/people":http://localhost:3000/people and see the list of people: Nic and Banjo.
172
-
173
- 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.
174
-
175
- 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.
176
-
177
- h3. 9. End
178
-
179
- There ends our example of a Rails application using one model class to access multiple databases cleanly.
180
-
181
- h2. Pre-existing modules
182
-
183
- 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.
184
-
185
- So, *app/models/admin/page.rb* represents the <code>Admin::Page</code> class.
186
-
187
- Magic Multi-Connections works for these model classes as well.
188
-
189
- <pre syntax="ruby">Admin.establish_connection :admin_dev
190
- Admin::Page.active_connection_name # => "Admin::Page"
191
- </pre>
192
-
193
- h2. Other tricks
194
-
195
- The Magic Multi-Connections includes <code>diff</code> and <code>diff?</code> methods extracted from the "Riff plugin":http://tfletcher.com/svn/rails-plugins/riff/README by "Tim Fletcher":http://tfletcher.com/, so that <code>id</code> values are ignored when comparing objects from different connections/namespaces.
196
-
197
- <pre syntax="ruby"># Detect differences between two activerecords, e.g.
198
- new_person = Person.find_by_username('drnic')
199
- old_person = ContactRepository::Person.find_by_username('drnic')
200
-
201
- new_person.diff?(old_person) # => true
202
-
203
- new_person.diff(old_person) # => { :name => ['Dr Nic', 'Nic'], :age => [32, 20] }
204
- </pre>
205
-
206
- h2. Dr Nic's Blog
207
-
208
- "http://www.drnicwilliams.com":http://www.drnicwilliams.com - for future announcements and
209
- other stories and things.
210
-
211
- h2. Forum
212
-
213
- Discussion about the Magic Multi-Connections is on the Magic Models forum:
214
-
215
- "http://groups.google.com/group/magicmodels":http://groups.google.com/group/magicmodels
216
-
217
- h2. Licence
218
-
219
- This code is free to use under the terms of the MIT licence.
220
-
221
- h2. Contact
222
-
223
- Comments are welcome. Send an email to "Dr Nic Williams":mailto:drnicwilliams@gmail.com.
1
+ h1. Magic Multi-Connections
2
+
3
+ h1. &#x2192; Ruby on Rails
4
+
5
+ h1. &#x2192; 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.