master_slave_adapter 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,563 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'rspec'
4
+
5
+ ActiveRecord::Base.logger =
6
+ Logger.new(STDOUT).tap { |l| l.level = Logger::DEBUG }
7
+
8
+ $LOAD_PATH << File.expand_path(File.join( File.dirname( __FILE__ ), '..', 'lib' ))
9
+
10
+ require 'active_record/connection_adapters/master_slave_adapter'
11
+
12
+ class ActiveRecord::Base
13
+ cattr_accessor :master_mock, :slave_mock
14
+ def self.test_connection(config)
15
+ config[:database] == 'slave' ? slave_mock : master_mock
16
+ end
17
+ end
18
+
19
+ describe ActiveRecord::ConnectionAdapters::MasterSlaveAdapter do
20
+ let(:default_database_setup) do
21
+ {
22
+ :adapter => 'master_slave',
23
+ :username => 'root',
24
+ :database => 'slave',
25
+ :connection_adapter => 'test',
26
+ :master => { :username => 'root', :database => 'master' },
27
+ :slaves => [{ :database => 'slave' }],
28
+ }
29
+ end
30
+
31
+ let(:database_setup) { default_database_setup }
32
+
33
+ let(:mocked_methods) do
34
+ {
35
+ :reconnect! => true,
36
+ :disconnect! => true,
37
+ }
38
+ end
39
+
40
+ let!(:master_connection) do
41
+ mock(
42
+ 'master connection',
43
+ mocked_methods.merge(:open_transactions => 0)
44
+ ).tap do |conn|
45
+ conn.stub!(:uncached).and_yield
46
+ ActiveRecord::Base.master_mock = conn
47
+ end
48
+ end
49
+
50
+ let!(:slave_connection) do
51
+ mock('slave connection', mocked_methods).tap do |conn|
52
+ conn.stub!(:uncached).and_yield
53
+ ActiveRecord::Base.slave_mock = conn
54
+ end
55
+ end
56
+
57
+ def adapter_connection
58
+ ActiveRecord::Base.connection
59
+ end
60
+
61
+ SchemaStatements = ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods.map(&:to_sym)
62
+ SelectMethods = [ :select_all, :select_one, :select_rows, :select_value, :select_values ]
63
+ Clock = ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::Clock
64
+
65
+ before do
66
+ unless database_setup[:disable_connection_test] == 'true'
67
+ [ master_connection, slave_connection ].each do |c|
68
+ c.should_receive(:active?).exactly(2).times.and_return(true)
69
+ end
70
+ end
71
+ ActiveRecord::Base.establish_connection(database_setup)
72
+ end
73
+
74
+ after do
75
+ ActiveRecord::Base.connection_handler.clear_all_connections!
76
+ end
77
+
78
+ describe 'common configuration' do
79
+ before do
80
+ [ master_connection, slave_connection ].each do |c|
81
+ c.stub!( :select_value ).with( "SELECT 1", "test select" ).and_return( true )
82
+ end
83
+ end
84
+
85
+ it "should call 'columns' on master" do
86
+ master_connection.should_receive(:columns)
87
+ adapter_connection.columns
88
+ end
89
+
90
+ SelectMethods.each do |method|
91
+ it "should send the method '#{method}' to the slave connection" do
92
+ master_connection.stub!( :open_transactions ).and_return( 0 )
93
+ slave_connection.should_receive( method ).with('testing').and_return( true )
94
+ adapter_connection.send( method, 'testing' )
95
+ end
96
+
97
+ it "should send the method '#{method}' to the master connection if with_master was specified" do
98
+ master_connection.should_receive( method ).with('testing').and_return( true )
99
+ ActiveRecord::Base.with_master do
100
+ adapter_connection.send( method, 'testing' )
101
+ end
102
+ end
103
+
104
+ it "should send the method '#{method}' to the slave connection if with_slave was specified" do
105
+ slave_connection.should_receive( method ).with('testing').and_return( true )
106
+ ActiveRecord::Base.with_slave do
107
+ adapter_connection.send( method, 'testing' )
108
+ end
109
+ end
110
+
111
+ it "should send the method '#{method}' to the master connection if there are open transactions" do
112
+ master_connection.stub!( :open_transactions ).and_return( 1 )
113
+ master_connection.should_receive( method ).with('testing').and_return( true )
114
+ ActiveRecord::Base.with_master do
115
+ adapter_connection.send( method, 'testing' )
116
+ end
117
+ end
118
+
119
+ it "should send the method '#{method}' to the master connection if there are open transactions, even in with_slave" do
120
+ master_connection.stub!( :open_transactions ).and_return( 1 )
121
+ master_connection.should_receive( method ).with('testing').and_return( true )
122
+ ActiveRecord::Base.with_slave do
123
+ adapter_connection.send( method, 'testing' )
124
+ end
125
+ end
126
+ end # /SelectMethods.each
127
+
128
+ SchemaStatements.each do |method|
129
+ it "should send the method '#{method}' from ActiveRecord::ConnectionAdapters::SchemaStatements to the master" do
130
+ master_connection.should_receive( method ).and_return( true )
131
+ adapter_connection.send( method )
132
+ end
133
+ end
134
+
135
+ (SchemaStatements - SelectMethods).each do |method|
136
+ it "should send the method '#{method}' from ActiveRecord::ConnectionAdapters::DatabaseStatements to the master" do
137
+ master_connection.should_receive( method ).and_return( true )
138
+ adapter_connection.send( method )
139
+ end
140
+ end
141
+
142
+ it "should call #visitor on master connection" do
143
+ master_connection.should_receive(:visitor)
144
+ adapter_connection.visitor
145
+ end
146
+
147
+ it 'should be a master slave connection' do
148
+ adapter_connection.class.should == ActiveRecord::ConnectionAdapters::MasterSlaveAdapter
149
+ end
150
+
151
+ it 'should have a master connection' do
152
+ adapter_connection.master_connection.should == master_connection
153
+ end
154
+
155
+ it 'should have a slave connection' do
156
+ master_connection.stub!( :open_transactions ).and_return( 0 )
157
+ adapter_connection.slave_connection!.should == slave_connection
158
+ end
159
+ end
160
+
161
+ describe "connection testing" do
162
+ context "disabled" do
163
+ let(:database_setup) do
164
+ default_database_setup.merge(:disable_connection_test => 'true')
165
+ end
166
+
167
+ context "on master" do
168
+ SchemaStatements.each do |method|
169
+ it "should not perform the testing when #{method} is called" do
170
+ master_connection.tap do |c|
171
+ c.should_not_receive(:active?)
172
+ c.should_receive(method).with('testing').and_return(true)
173
+ end
174
+ adapter_connection.send(method, 'testing')
175
+ end
176
+ end
177
+ end
178
+
179
+ context "on slave" do
180
+ SelectMethods.each do |method|
181
+ it "should not perform the testing when #{method} is called" do
182
+ slave_connection.tap do |c|
183
+ c.should_not_receive(:active?)
184
+ c.should_receive(method).with('testing').and_return(true)
185
+ end
186
+ adapter_connection.send(method, 'testing')
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ describe 'with connection eager loading enabled' do
194
+ it 'should eager load the connections' do
195
+ adapter_connection.connections.should include(master_connection, slave_connection)
196
+ end
197
+ end
198
+
199
+ describe 'consistency' do
200
+ before do
201
+ ActiveRecord::ConnectionAdapters::MasterSlaveAdapter.reset!
202
+
203
+ [ master_connection, slave_connection ].each do |c|
204
+ c.stub!(:select_value).with("SELECT 1", "test select").and_return(true)
205
+ end
206
+ end
207
+
208
+ def zero
209
+ Clock.zero
210
+ end
211
+
212
+ def master_position(pos)
213
+ Clock.new('', pos)
214
+ end
215
+
216
+ def slave_should_report_clock(pos)
217
+ pos = Array(pos)
218
+ values = pos.map { |p| { 'Relay_Master_Log_File' => '', 'Exec_Master_Log_Pos' => p } }
219
+ slave_connection.
220
+ should_receive('select_one').exactly(pos.length).with('SHOW SLAVE STATUS').
221
+ and_return(*values)
222
+ end
223
+
224
+ def master_should_report_clock(pos)
225
+ pos = Array(pos)
226
+ values = pos.map { |p| { 'File' => '', 'Position' => p } }
227
+ master_connection.
228
+ should_receive('select_one').exactly(pos.length).with('SHOW MASTER STATUS').
229
+ and_return(*values)
230
+ end
231
+
232
+ SelectMethods.each do |method|
233
+ it "should raise an exception if consistency is nil" do
234
+ lambda do
235
+ ActiveRecord::Base.with_consistency(nil) do
236
+ end
237
+ end.should raise_error(ArgumentError)
238
+ end
239
+
240
+ it "should send the method '#{method}' to the slave if clock.zero is given" do
241
+ slave_should_report_clock(0)
242
+ slave_connection.should_receive(method).with('testing').and_return(true)
243
+ old_clock = zero
244
+ new_clock = ActiveRecord::Base.with_consistency(old_clock) do
245
+ adapter_connection.send(method, 'testing')
246
+ end
247
+ new_clock.should be_a(zero.class)
248
+ new_clock.should equal(zero)
249
+ end
250
+
251
+ it "should send the method '#{method}' to the master if slave hasn't cought up to required clock yet" do
252
+ slave_should_report_clock(0)
253
+ master_connection.should_receive(method).with('testing').and_return(true)
254
+ old_clock = master_position(1)
255
+ new_clock = ActiveRecord::Base.with_consistency(old_clock) do
256
+ adapter_connection.send(method, 'testing' )
257
+ end
258
+ new_clock.should be_a(zero.class)
259
+ new_clock.should equal(old_clock)
260
+ end
261
+
262
+ it "should send the method '#{method}' to the master connection if there are open transactions" do
263
+ master_connection.stub!(:open_transactions).and_return(1)
264
+ master_connection.should_receive(method).with('testing').and_return(true)
265
+ old_clock = zero
266
+ new_clock = ActiveRecord::Base.with_consistency(old_clock) do
267
+ adapter_connection.send(method, 'testing')
268
+ end
269
+ new_clock.should be_a(zero.class)
270
+ new_clock.should equal(zero)
271
+ end
272
+
273
+ it "should send the method '#{method}' to the master after a write operation" do
274
+ slave_should_report_clock(0)
275
+ master_should_report_clock(2)
276
+ slave_connection.should_receive(method).with('testing').and_return(true)
277
+ master_connection.should_receive('update').with('testing').and_return(true)
278
+ master_connection.should_receive(method).with('testing').and_return(true)
279
+ old_clock = zero
280
+ new_clock = ActiveRecord::Base.with_consistency(old_clock) do
281
+ adapter_connection.send(method, 'testing') # slave
282
+ adapter_connection.send('update', 'testing') # master
283
+ adapter_connection.send(method, 'testing') # master
284
+ end
285
+ new_clock.should be_a(zero.class)
286
+ new_clock.should > old_clock
287
+ end
288
+ end
289
+
290
+ it "should update the clock after a transaction" do
291
+ slave_should_report_clock(0)
292
+ master_should_report_clock([0, 1, 1])
293
+
294
+ slave_connection.
295
+ should_receive('select_all').exactly(1).times.with('testing').
296
+ and_return(true)
297
+
298
+ master_connection.
299
+ should_receive('update').exactly(3).times.with('testing').
300
+ and_return(true)
301
+ master_connection.
302
+ should_receive('select_all').exactly(5).times.with('testing').
303
+ and_return(true)
304
+ %w(begin_db_transaction
305
+ commit_db_transaction
306
+ increment_open_transactions
307
+ decrement_open_transactions
308
+ outside_transaction?).each do |txstmt|
309
+ master_connection.should_receive(txstmt).exactly(1).times
310
+ end
311
+
312
+ master_connection.
313
+ should_receive('open_transactions').exactly(13).times.
314
+ and_return(
315
+ # adapter: with_consistency, select_all, update, select_all
316
+ 0, 0, 0, 0,
317
+ # connection: transaction
318
+ 0,
319
+ # adapter: select_all, update, select_all, commit_db_transaction
320
+ 1, 1, 1, 0,
321
+ # connection: transaction (ensure)
322
+ 0,
323
+ # adapter: select_all, update, select_all
324
+ 0, 0, 0
325
+ )
326
+
327
+ old_clock = zero
328
+ new_clock = ActiveRecord::Base.with_consistency(old_clock) do
329
+ adapter_connection.send('select_all', 'testing') # slave s=0 m=0
330
+ adapter_connection.send('update', 'testing') # master s=0 m=1
331
+ adapter_connection.send('select_all', 'testing') # master s=0 m=1
332
+
333
+ ActiveRecord::Base.transaction do
334
+ adapter_connection.send('select_all', 'testing') # master s=0 m=1
335
+ adapter_connection.send('update', 'testing') # master s=0 m=1
336
+ adapter_connection.send('select_all', 'testing') # master s=0 m=1
337
+ end
338
+
339
+ adapter_connection.send('select_all', 'testing') # master s=0 m=2
340
+ adapter_connection.send('update', 'testing') # master s=0 m=3
341
+ adapter_connection.send('select_all', 'testing') # master s=0 m=3
342
+ end
343
+
344
+ new_clock.should > old_clock
345
+ end
346
+
347
+ context "with nested with_consistency" do
348
+ it "should return the same clock if not writing and no lag" do
349
+ slave_should_report_clock(0) # note: tests memoizing slave clock
350
+ slave_connection.
351
+ should_receive('select_one').exactly(3).times.with('testing').
352
+ and_return(true)
353
+
354
+ old_clock = zero
355
+ new_clock = ActiveRecord::Base.with_consistency(old_clock) do
356
+ adapter_connection.send('select_one', 'testing')
357
+ ActiveRecord::Base.with_consistency(old_clock) do
358
+ adapter_connection.send('select_one', 'testing')
359
+ end
360
+ adapter_connection.send('select_one', 'testing')
361
+ end
362
+ new_clock.should equal(old_clock)
363
+ end
364
+
365
+ it "requesting a newer clock should return a new clock" do
366
+ adapter_connection.
367
+ should_receive('slave_consistent?').exactly(2).times.
368
+ and_return(true, false)
369
+ slave_connection.
370
+ should_receive('select_all').exactly(2).times.with('testing').
371
+ and_return(true)
372
+ master_connection.
373
+ should_receive('select_all').exactly(1).times.with('testing').
374
+ and_return(true)
375
+
376
+ start_clock = zero
377
+ inner_clock = zero
378
+ outer_clock = ActiveRecord::Base.with_consistency(start_clock) do
379
+ adapter_connection.send('select_all', 'testing') # slave
380
+ inner_clock = ActiveRecord::Base.with_consistency(master_position(1)) do
381
+ adapter_connection.send('select_all', 'testing') # master
382
+ end
383
+ adapter_connection.send('select_all', 'testing') # slave
384
+ end
385
+
386
+ start_clock.should equal(outer_clock)
387
+ inner_clock.should > start_clock
388
+ end
389
+ end
390
+
391
+ it "should do the right thing when nested inside with_master" do
392
+ slave_should_report_clock(0)
393
+ slave_connection.should_receive('select_all').exactly(1).times.with('testing').and_return(true)
394
+ master_connection.should_receive('select_all').exactly(2).times.with('testing').and_return(true)
395
+ ActiveRecord::Base.with_master do
396
+ adapter_connection.send('select_all', 'testing') # master
397
+ ActiveRecord::Base.with_consistency(zero) do
398
+ adapter_connection.send('select_all', 'testing') # slave
399
+ end
400
+ adapter_connection.send('select_all', 'testing') # master
401
+ end
402
+ end
403
+
404
+ it "should do the right thing when nested inside with_slave" do
405
+ slave_should_report_clock(0)
406
+ slave_connection.should_receive('select_all').exactly(3).times.with('testing').and_return(true)
407
+ ActiveRecord::Base.with_slave do
408
+ adapter_connection.send('select_all', 'testing') # slave
409
+ ActiveRecord::Base.with_consistency(zero) do
410
+ adapter_connection.send('select_all', 'testing') # slave
411
+ end
412
+ adapter_connection.send('select_all', 'testing') # slave
413
+ end
414
+ end
415
+
416
+ it "should do the right thing when wrapping with_master" do
417
+ slave_should_report_clock(0)
418
+ slave_connection.should_receive('select_all').exactly(2).times.with('testing').and_return(true)
419
+ master_connection.should_receive('select_all').exactly(1).times.with('testing').and_return(true)
420
+ ActiveRecord::Base.with_consistency(zero) do
421
+ adapter_connection.send('select_all', 'testing') # slave
422
+ ActiveRecord::Base.with_master do
423
+ adapter_connection.send('select_all', 'testing') # master
424
+ end
425
+ adapter_connection.send('select_all', 'testing') # slave
426
+ end
427
+ end
428
+
429
+ it "should do the right thing when wrapping with_slave" do
430
+ slave_should_report_clock(0)
431
+ slave_connection.should_receive('select_all').exactly(1).times.with('testing').and_return(true)
432
+ master_connection.should_receive('select_all').exactly(2).times.with('testing').and_return(true)
433
+ ActiveRecord::Base.with_consistency(master_position(1)) do
434
+ adapter_connection.send('select_all', 'testing') # master
435
+ ActiveRecord::Base.with_slave do
436
+ adapter_connection.send('select_all', 'testing') # slave
437
+ end
438
+ adapter_connection.send('select_all', 'testing') # master
439
+ end
440
+ end
441
+ end # /with_consistency
442
+
443
+ describe "transaction callbacks" do
444
+ before do
445
+ ActiveRecord::ConnectionAdapters::MasterSlaveAdapter.reset!
446
+ end
447
+
448
+ def run_tx
449
+ adapter_connection.
450
+ should_receive('master_clock').
451
+ and_return(Clock.new('', 1))
452
+ %w(begin_db_transaction
453
+ commit_db_transaction
454
+ increment_open_transactions
455
+ decrement_open_transactions
456
+ outside_transaction?).each do |txstmt|
457
+ master_connection.
458
+ should_receive(txstmt).exactly(1).times
459
+ end
460
+ master_connection.
461
+ should_receive('open_transactions').exactly(4).times.
462
+ and_return(0, 1, 0, 0)
463
+
464
+ master_connection.
465
+ should_receive('update').with('testing').
466
+ and_return(true)
467
+
468
+ ActiveRecord::Base.transaction do
469
+ adapter_connection.send('update', 'testing')
470
+ end
471
+ end
472
+
473
+ def fail_tx
474
+ %w(begin_db_transaction
475
+ rollback_db_transaction
476
+ increment_open_transactions
477
+ decrement_open_transactions).each do |txstmt|
478
+ master_connection.
479
+ should_receive(txstmt).exactly(1).times
480
+ end
481
+ master_connection.
482
+ should_receive('outside_transaction?').exactly(2).times
483
+ master_connection.
484
+ should_receive('open_transactions').exactly(3).times.
485
+ and_return(0, 1, 0)
486
+ master_connection.
487
+ should_receive('update').with('testing').
488
+ and_return(true)
489
+
490
+ ActiveRecord::Base.transaction do
491
+ adapter_connection.send('update', 'testing')
492
+ raise "rollback"
493
+ end
494
+ rescue
495
+ nil
496
+ end
497
+
498
+ context "on commit" do
499
+ it "on_commit callback should be called" do
500
+ x = false
501
+ adapter_connection.on_commit { x = true }
502
+ lambda { run_tx }.should change { x }.to(true)
503
+ end
504
+
505
+ it "on_rollback callback should not be called" do
506
+ x = false
507
+ adapter_connection.on_rollback { x = true }
508
+ lambda { run_tx }.should_not change { x }
509
+ end
510
+ end
511
+
512
+ context "on rollback" do
513
+ it "on_commit callback should not be called" do
514
+ x = false
515
+ adapter_connection.on_commit { x = true }
516
+ lambda { fail_tx }.should_not change { x }
517
+ end
518
+
519
+ it "on_rollback callback should be called" do
520
+ x = false
521
+ adapter_connection.on_rollback { x = true }
522
+ lambda { fail_tx }.should change { x }.to(true)
523
+ end
524
+ end
525
+ end
526
+
527
+ describe "query cache" do
528
+ describe "#cache" do
529
+ it "activities query caching on all connections" do
530
+ master_connection.should_receive(:cache).and_yield
531
+ slave_connection.should_receive(:cache).and_yield
532
+ master_connection.should_not_receive(:select_value)
533
+ slave_connection.should_receive(:select_value)
534
+
535
+ adapter_connection.cache do
536
+ adapter_connection.select_value("SELECT 42")
537
+ end
538
+ end
539
+ end
540
+
541
+ describe "#uncached" do
542
+ it "deactivates query caching on all connections" do
543
+ master_connection.should_receive(:uncached).and_yield
544
+ slave_connection.should_receive(:uncached).and_yield
545
+ master_connection.should_not_receive(:select_value)
546
+ slave_connection.should_receive(:select_value)
547
+
548
+ adapter_connection.uncached do
549
+ adapter_connection.select_value("SELECT 42")
550
+ end
551
+ end
552
+ end
553
+
554
+ describe "#clear_query_cache" do
555
+ it "clears the query cache on all connections" do
556
+ master_connection.should_receive(:clear_query_cache)
557
+ slave_connection.should_receive(:clear_query_cache)
558
+
559
+ adapter_connection.clear_query_cache
560
+ end
561
+ end
562
+ end
563
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: master_slave_adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mauricio Linhares
9
+ - Torsten Curdt
10
+ - Kim Altintop
11
+ - Omid Aladini
12
+ - SoundCloud
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+ date: 2012-05-21 00:00:00.000000000 Z
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: rspec
20
+ requirement: !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ type: :development
27
+ prerelease: false
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ - !ruby/object:Gem::Dependency
35
+ name: rake
36
+ requirement: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ type: :development
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ - !ruby/object:Gem::Dependency
51
+ name: activerecord
52
+ requirement: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: 2.3.9
58
+ type: :runtime
59
+ prerelease: false
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 2.3.9
66
+ description: (MySQL) Replication Aware Master/Slave Database Adapter for Rails/ActiveRecord
67
+ email: kim@soundcloud.com tcurdt@soundcloud.com omid@soundcloud.com
68
+ executables: []
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - .gitignore
73
+ - .travis.yml
74
+ - CHANGELOG.md
75
+ - Gemfile
76
+ - LICENSE
77
+ - Rakefile
78
+ - Readme.md
79
+ - TODO.txt
80
+ - VERSION
81
+ - lib/active_record/connection_adapters/master_slave_adapter.rb
82
+ - lib/active_record/connection_adapters/mysql_master_slave_adapter.rb
83
+ - lib/master_slave_adapter.rb
84
+ - master_slave_adapter.gemspec
85
+ - spec/master_slave_adapter_spec.rb
86
+ homepage: http://github.com/soundcloud/master_slave_adapter
87
+ licenses: []
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: 1.9.2
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.3.7
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 1.8.21
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Replication Aware Master/Slave Database Adapter for Rails/ActiveRecord
110
+ test_files:
111
+ - spec/master_slave_adapter_spec.rb