octoball 0.1.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,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Octoball do
4
+ describe 'method dispatch' do
5
+ before :each do
6
+ @client = Client.using(:canada).create!
7
+ @client.items << Item.using(:canada).create!
8
+ @client.reload
9
+ end
10
+
11
+ it 'computes the size of the collection without loading it' do
12
+ expect(@client.items.size).to eq(1)
13
+
14
+ expect(@client.items.loaded?).to be false
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Octoball::LogSubscriber, :shards => [:canada] do
4
+ before :each do
5
+ @out = StringIO.new
6
+ @log = Logger.new(@out)
7
+ ActiveRecord::Base.logger = @log
8
+ ActiveRecord::Base.logger.level = Logger::DEBUG
9
+ end
10
+
11
+ after :each do
12
+ ActiveRecord::Base.logger = Logger.new(File.open('database.log', 'a'))
13
+ end
14
+
15
+ it 'should add to the default logger the shard name the query was sent to' do
16
+ User.using(:canada).create!(:name => 'test')
17
+ expect(@out.string).to match(/Shard: canada/)
18
+ end
19
+ end
@@ -0,0 +1,688 @@
1
+ require 'spec_helper'
2
+
3
+ describe Octoball do
4
+ describe '#using method' do
5
+ it 'should allow to send a block to the master shard' do
6
+ Octoball.using(:master) do
7
+ User.create!(:name => 'Block test')
8
+ end
9
+
10
+ expect(User.using(:master).find_by_name('Block test')).not_to be_nil
11
+ end
12
+
13
+ it 'should allow to pass a string as the shard name to a AR subclass' do
14
+ User.using('canada').create!(:name => 'Rafael Pilha')
15
+
16
+ expect(User.using('canada').find_by_name('Rafael Pilha')).not_to be_nil
17
+ end
18
+
19
+ it 'should allow comparison of a string shard name with symbol shard name' do
20
+ u = User.using('canada').create!(:name => 'Rafael Pilha')
21
+ expect(u).to eq(User.using(:canada).find_by_name('Rafael Pilha'))
22
+ end
23
+
24
+ it 'should allow comparison of a symbol shard name with string shard name' do
25
+ u = User.using(:canada).create!(:name => 'Rafael Pilha')
26
+ expect(u).to eq(User.using('canada').find_by_name('Rafael Pilha'))
27
+ end
28
+
29
+ it 'should allow to pass a string as the shard name to a block' do
30
+ Octoball.using('canada') do
31
+ User.create!(:name => 'Rafael Pilha')
32
+ end
33
+
34
+ expect(User.using('canada').find_by_name('Rafael Pilha')).not_to be_nil
35
+ end
36
+
37
+ it 'should allow selecting the shards on scope' do
38
+ User.using(:canada).create!(:name => 'oi')
39
+ expect(User.using(:canada).count).to eq(1)
40
+ expect(User.count).to eq(0)
41
+ end
42
+
43
+ it 'should allow selecting the shard using #new' do
44
+ u = User.using(:canada).new
45
+ u.name = 'Thiago'
46
+ u.save
47
+
48
+ expect(User.using(:canada).count).to eq(1)
49
+ expect(User.using(:brazil).count).to eq(0)
50
+
51
+ u1 = User.new
52
+ u1.name = 'Joaquim'
53
+ u2 = User.using(:canada).new
54
+ u2.name = 'Manuel'
55
+ u1.save
56
+ u2.save
57
+
58
+ expect(User.using(:canada).all).to eq([u, u2])
59
+ expect(User.all).to eq([u1])
60
+ end
61
+
62
+ it "should allow the #select method to fetch the correct data when using a block" do
63
+ canadian_user = User.using(:canada).create!(:name => 'Rafael Pilha')
64
+
65
+ Octoball.using('canada') do
66
+ @all_canadian_user_ids = User.select('id').to_a
67
+ end
68
+
69
+ expect(@all_canadian_user_ids).to eq([canadian_user])
70
+ end
71
+
72
+ it "should allow objects to be fetch using different blocks - GH #306" do
73
+ canadian_user = User.using(:canada).create!(:name => 'Rafael Pilha')
74
+
75
+ Octoball.using(:canada) { @users = User.where('id is not null') }
76
+ Octoball.using(:canada) { @user = @users.first }
77
+
78
+ Octoball.using(:canada) { @user2 = User.where('id is not null').first }
79
+
80
+ expect(@user).to eq(canadian_user)
81
+ expect(@user2).to eq(canadian_user)
82
+ end
83
+
84
+ describe 'multiple calls to the same scope' do
85
+ it 'works with nil response' do
86
+ scope = User.using(:canada)
87
+ expect(scope.count).to eq(0)
88
+ expect(scope.first).to be_nil
89
+ end
90
+
91
+ it 'works with non-nil response' do
92
+ user = User.using(:canada).create!(:name => 'oi')
93
+ scope = User.using(:canada)
94
+ expect(scope.count).to eq(1)
95
+ expect(scope.first).to eq(user)
96
+ end
97
+ end
98
+
99
+ it 'should select the correct shard' do
100
+ User.using(:canada)
101
+ User.create!(:name => 'oi')
102
+ expect(User.count).to eq(1)
103
+ end
104
+
105
+ it 'should ensure that the connection will be cleaned' do
106
+ expect(ActiveRecord::Base.connection.current_shard).to eq(:default)
107
+ expect do
108
+ Octoball.using(:canada) do
109
+ fail 'Some Exception'
110
+ end
111
+ end.to raise_error(RuntimeError)
112
+
113
+ expect(ActiveRecord::Base.connection.current_shard).to eq(:default)
114
+ end
115
+
116
+ it 'should allow creating more than one user' do
117
+ User.using(:canada).create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
118
+ User.create!(:name => 'Thiago')
119
+ expect(User.using(:canada).find_by_name('America User 1')).not_to be_nil
120
+ expect(User.using(:canada).find_by_name('America User 2')).not_to be_nil
121
+ expect(User.using(:master).find_by_name('Thiago')).not_to be_nil
122
+ end
123
+
124
+ it 'should clean #current_shard from proxy when using execute' do
125
+ User.using(:canada).connection.execute('select * from users limit 1;')
126
+ expect(User.connection.current_shard).to eq(:default)
127
+ end
128
+
129
+ it 'should allow scoping dynamically' do
130
+ User.using(:canada).using(:master).using(:canada).create!(:name => 'oi')
131
+ expect(User.using(:canada).using(:master).count).to eq(0)
132
+ expect(User.using(:master).using(:canada).count).to eq(1)
133
+ end
134
+
135
+ it 'should allow find inside blocks' do
136
+ @user = User.using(:brazil).create!(:name => 'Thiago')
137
+
138
+ Octoball.using(:brazil) do
139
+ expect(User.first).to eq(@user)
140
+ end
141
+
142
+ expect(User.using(:brazil).find_by_name('Thiago')).to eq(@user)
143
+ end
144
+
145
+ it 'should clean the current_shard after executing the current query' do
146
+ User.using(:canada).create!(:name => 'oi')
147
+ expect(User.count).to eq(0)
148
+ end
149
+
150
+ it 'should support both groups and alone shards' do
151
+ _u = User.using(:alone_shard).create!(:name => 'Alone')
152
+ expect(User.using(:alone_shard).count).to eq(1)
153
+ expect(User.using(:canada).count).to eq(0)
154
+ expect(User.using(:brazil).count).to eq(0)
155
+ expect(User.count).to eq(0)
156
+ end
157
+
158
+ it 'should work with named scopes' do
159
+ u = User.using(:brazil).create!(:name => 'Thiago')
160
+
161
+ expect(User.thiago.using(:brazil).first).to eq(u)
162
+ expect(User.using(:brazil).thiago.first).to eq(u)
163
+
164
+ Octoball.using(:brazil) do
165
+ expect(User.thiago.first).to eq(u)
166
+ end
167
+ end
168
+
169
+ describe '#current_shard attribute' do
170
+ it 'should store the attribute when you create or find an object' do
171
+ u = User.using(:alone_shard).create!(:name => 'Alone')
172
+ expect(u.current_shard).to eq(:alone_shard)
173
+ User.using(:canada).create!(:name => 'oi')
174
+ u = User.using(:canada).find_by_name('oi')
175
+ expect(u.current_shard).to eq(:canada)
176
+ end
177
+
178
+ it 'should store the attribute when you find multiple instances' do
179
+ 5.times { User.using(:alone_shard).create!(:name => 'Alone') }
180
+
181
+ User.using(:alone_shard).all.each do |u|
182
+ expect(u.current_shard).to eq(:alone_shard)
183
+ end
184
+ end
185
+
186
+ it 'should works when you find, and after that, alter that object' do
187
+ alone_user = User.using(:alone_shard).create!(:name => 'Alone')
188
+ _mstr_user = User.using(:master).create!(:name => 'Master')
189
+ alone_user.name = 'teste'
190
+ alone_user.save
191
+ expect(User.using(:master).first.name).to eq('Master')
192
+ expect(User.using(:alone_shard).first.name).to eq('teste')
193
+ end
194
+
195
+ it 'should work for the reload method' do
196
+ User.using(:alone_shard).create!(:name => 'Alone')
197
+ u = User.using(:alone_shard).find_by_name('Alone')
198
+ u.reload
199
+ expect(u.name).to eq('Alone')
200
+ end
201
+
202
+ it 'should work passing some arguments to reload method' do
203
+ User.using(:alone_shard).create!(:name => 'Alone')
204
+ u = User.using(:alone_shard).find_by_name('Alone')
205
+ u.reload(:lock => true)
206
+ expect(u.name).to eq('Alone')
207
+ end
208
+ end
209
+
210
+ describe 'passing a block' do
211
+ it 'should allow queries be executed inside the block, ponting to a specific shard' do
212
+ Octoball.using(:canada) do
213
+ User.create(:name => 'oi')
214
+ end
215
+
216
+ expect(User.using(:canada).count).to eq(1)
217
+ expect(User.using(:master).count).to eq(0)
218
+ expect(User.count).to eq(0)
219
+ end
220
+
221
+ it 'should allow execute queries inside a model' do
222
+ u = User.new
223
+ u.awesome_queries
224
+ expect(User.using(:canada).count).to eq(1)
225
+ expect(User.count).to eq(0)
226
+ end
227
+ end
228
+
229
+ describe 'raising errors' do
230
+ it "should raise a error when you specify a shard that doesn't exist" do
231
+ expect { User.using(:crazy_shard).create!(:name => 'Thiago') }.to raise_error(ActiveRecord::ConnectionNotEstablished)
232
+ end
233
+ end
234
+
235
+ describe 'equality' do
236
+ let(:canada1) do
237
+ User.using(:canada).new(id: 1)
238
+ end
239
+
240
+ let(:canada1_dup) do
241
+ User.using(:canada).new(id: 1)
242
+ end
243
+
244
+ let(:brazil1) do
245
+ User.using(:brazil).new(id: 1)
246
+ end
247
+
248
+ it 'should work with persisted objects' do
249
+ u = User.using(:brazil).create(:name => 'Mike')
250
+ expect(User.using(:brazil).find_by_name('Mike')).to eq(u)
251
+ end
252
+
253
+ it 'should check current_shard when determining equality' do
254
+ expect(canada1).not_to eq(brazil1)
255
+ expect(canada1).to eq(canada1_dup)
256
+ end
257
+
258
+ it 'delegates equality check on scopes' do
259
+ u = User.using(:brazil).create!(:name => 'Mike')
260
+ expect(User.using(:brazil).where(:name => 'Mike')).to eq([u])
261
+ end
262
+ end
263
+ end
264
+
265
+ describe 'AR basic methods' do
266
+ it 'connects_to' do
267
+ expect(CustomConnection.connection.current_database).to eq('octoball_shard_2')
268
+ end
269
+
270
+ it 'reuses parent model connection' do
271
+ klass = Class.new(CustomConnection)
272
+
273
+ expect(klass.connection).to be klass.connection
274
+ end
275
+
276
+ it 'should not mess with custom connection table names' do
277
+ expect(Advert.connection.current_database).to eq('octoball_shard_1')
278
+ Advert.create!(:name => 'Teste')
279
+ end
280
+
281
+ it 'increment' do
282
+ _ = User.using(:brazil).create!(:name => 'Teste', :number => 10)
283
+ u = User.using(:brazil).find_by_number(10)
284
+ u.increment(:number)
285
+ u.save
286
+ expect(User.using(:brazil).find_by_number(11)).not_to be_nil
287
+ end
288
+
289
+ it 'increment!' do
290
+ _ = User.using(:brazil).create!(:name => 'Teste', :number => 10)
291
+ u = User.using(:brazil).find_by_number(10)
292
+ u.increment!(:number)
293
+ expect(User.using(:brazil).find_by_number(11)).not_to be_nil
294
+ end
295
+
296
+ it 'decrement' do
297
+ _ = User.using(:brazil).create!(:name => 'Teste', :number => 10)
298
+ u = User.using(:brazil).find_by_number(10)
299
+ u.decrement(:number)
300
+ u.save
301
+ expect(User.using(:brazil).find_by_number(9)).not_to be_nil
302
+ end
303
+
304
+ it 'decrement!' do
305
+ _ = User.using(:brazil).create!(:name => 'Teste', :number => 10)
306
+ u = User.using(:brazil).find_by_number(10)
307
+ u.decrement!(:number)
308
+ expect(User.using(:brazil).find_by_number(9)).not_to be_nil
309
+ end
310
+
311
+ it 'toggle' do
312
+ _ = User.using(:brazil).create!(:name => 'Teste', :admin => false)
313
+ u = User.using(:brazil).find_by_name('Teste')
314
+ u.toggle(:admin)
315
+ u.save
316
+ expect(User.using(:brazil).find_by_name('Teste').admin).to be true
317
+ end
318
+
319
+ it 'toggle!' do
320
+ _ = User.using(:brazil).create!(:name => 'Teste', :admin => false)
321
+ u = User.using(:brazil).find_by_name('Teste')
322
+ u.toggle!(:admin)
323
+ expect(User.using(:brazil).find_by_name('Teste').admin).to be true
324
+ end
325
+
326
+ it 'count' do
327
+ _u = User.using(:brazil).create!(:name => 'User1')
328
+ _v = User.using(:brazil).create!(:name => 'User2')
329
+ _w = User.using(:brazil).create!(:name => 'User3')
330
+ expect(User.using(:brazil).where(:name => 'User2').all.count).to eq(1)
331
+ end
332
+
333
+ it 'maximum' do
334
+ _u = User.using(:brazil).create!(:name => 'Teste', :number => 11)
335
+ _v = User.using(:master).create!(:name => 'Teste', :number => 12)
336
+
337
+ expect(User.using(:brazil).maximum(:number)).to eq(11)
338
+ expect(User.using(:master).maximum(:number)).to eq(12)
339
+ end
340
+
341
+ it 'sum' do
342
+ u = User.using(:brazil).create!(:name => 'Teste', :number => 11)
343
+ v = User.using(:master).create!(:name => 'Teste', :number => 12)
344
+
345
+ expect(User.using(:master).sum(:number)).to eq(12)
346
+ expect(User.using(:brazil).sum(:number)).to eq(11)
347
+
348
+ expect(User.where(id: v.id).sum(:number)).to eq(12)
349
+ expect(User.using(:brazil).where(id: u.id).sum(:number)).to eq(11)
350
+ expect(User.using(:master).where(id: v.id).sum(:number)).to eq(12)
351
+ end
352
+
353
+ describe 'any?' do
354
+ before { User.using(:brazil).create!(:name => 'User1') }
355
+
356
+ it 'works when true' do
357
+ scope = User.using(:brazil).where(:name => 'User1')
358
+ expect(scope.any?).to be true
359
+ end
360
+
361
+ it 'works when false' do
362
+ scope = User.using(:brazil).where(:name => 'User2')
363
+ expect(scope.any?).to be false
364
+ end
365
+ end
366
+
367
+ it 'exists?' do
368
+ @user = User.using(:brazil).create!(:name => 'User1')
369
+
370
+ expect(User.using(:brazil).where(:name => 'User1').exists?).to be true
371
+ expect(User.using(:brazil).where(:name => 'User2').exists?).to be false
372
+ end
373
+
374
+ describe 'touch' do
375
+ it 'updates updated_at by default' do
376
+ @user = User.using(:brazil).create!(:name => 'User1')
377
+ User.using(:brazil).where(:id => @user.id).update_all(:updated_at => Time.now - 3.months)
378
+ @user.touch
379
+ expect(@user.reload.updated_at.in_time_zone('GMT').to_date).to eq(Time.now.in_time_zone('GMT').to_date)
380
+ end
381
+
382
+ it 'updates passed in attribute name' do
383
+ @user = User.using(:brazil).create!(:name => 'User1')
384
+ User.using(:brazil).where(:id => @user.id).update_all(:created_at => Time.now - 3.months)
385
+ @user.touch(:created_at)
386
+ expect(@user.reload.created_at.in_time_zone('GMT').to_date).to eq(Time.now.in_time_zone('GMT').to_date)
387
+ end
388
+ end
389
+
390
+ describe '#pluck' do
391
+ before { User.using(:brazil).create!(:name => 'User1') }
392
+
393
+ it 'should works from scope proxy' do
394
+ names = User.using(:brazil).pluck(:name)
395
+ expect(names).to eq(['User1'])
396
+ expect(User.using(:master).pluck(:name)).to eq([])
397
+ end
398
+ end
399
+
400
+ it 'update_column' do
401
+ @user = User.using(:brazil).create!(:name => 'User1')
402
+ @user2 = User.using(:brazil).find(@user.id)
403
+ @user2.update_column(:name, 'Joaquim Shard Brazil')
404
+ expect(User.using(:brazil).find_by_name('Joaquim Shard Brazil')).not_to be_nil
405
+ end
406
+
407
+ it 'update' do
408
+ @user = User.using(:brazil).create!(:name => 'User1')
409
+ @user2 = User.using(:brazil).find(@user.id)
410
+ @user2.update(:name => 'Joaquim')
411
+ expect(User.using(:brazil).find_by_name('Joaquim')).not_to be_nil
412
+ end
413
+
414
+ it 'using update inside a block' do
415
+ Octoball.using(:brazil) do
416
+ @user = User.create!(:name => 'User1')
417
+ @user2 = User.find(@user.id)
418
+ @user2.update(:name => 'Joaquim')
419
+ end
420
+
421
+ expect(User.find_by_name('Joaquim')).to be_nil
422
+ expect(User.using(:brazil).find_by_name('Joaquim')).not_to be_nil
423
+ end
424
+
425
+ it 'update' do
426
+ @user = User.using(:brazil).create!(:name => 'User1')
427
+ @user2 = User.using(:brazil).find(@user.id)
428
+ @user2.update(:name => 'Joaquim')
429
+ expect(User.using(:brazil).find_by_name('Joaquim')).not_to be_nil
430
+ end
431
+
432
+ it 'as_json' do
433
+ ActiveRecord::Base.include_root_in_json = false
434
+
435
+ Octoball.using(:brazil) do
436
+ User.create!(:name => 'User1')
437
+ end
438
+
439
+ user = User.using(:brazil).where(:name => 'User1').first
440
+ expect(user.as_json(:except => [:created_at, :updated_at, :id])).to eq('admin' => nil, 'name' => 'User1', 'number' => nil)
441
+ end
442
+
443
+ describe 'transaction' do
444
+ context 'without assigning a database' do
445
+ it 'works as expected' do
446
+ _u = User.create!(:name => 'Thiago')
447
+
448
+ expect(User.using(:brazil).count).to eq(0)
449
+ expect(User.using(:master).count).to eq(1)
450
+
451
+ User.using(:brazil).transaction do
452
+ expect(User.find_by_name('Thiago')).to be_nil
453
+ User.create!(:name => 'Brazil')
454
+ end
455
+
456
+ expect(User.using(:brazil).count).to eq(1)
457
+ expect(User.using(:master).count).to eq(1)
458
+ end
459
+ end
460
+
461
+ context 'when assigning a database' do
462
+ it 'works as expected' do
463
+ klass = User.using(:brazil)
464
+
465
+ klass.transaction do
466
+ klass.create!(:name => 'Brazil')
467
+ end
468
+
469
+ expect(klass.find_by_name('Brazil')).to be_present
470
+ end
471
+ end
472
+ end
473
+
474
+ describe "#finder methods" do
475
+ before(:each) do
476
+ @user1 = User.using(:brazil).create!(:name => 'User1')
477
+ @user2 = User.using(:brazil).create!(:name => 'User2')
478
+ @user3 = User.using(:brazil).create!(:name => 'User3')
479
+ end
480
+
481
+ it "#find_each should work with a block" do
482
+ result_array = []
483
+
484
+ User.using(:brazil).where("name is not NULL").find_each do |user|
485
+ result_array << user
486
+ end
487
+
488
+ expect(result_array).to eq([@user1, @user2, @user3])
489
+ end
490
+
491
+ it "#find_each should work with a where.not(...)" do
492
+ result_array = []
493
+
494
+ User.using(:brazil).where.not(:name => 'User2').find_each do |user|
495
+ result_array << user
496
+ end
497
+
498
+ expect(result_array).to eq([@user1, @user3])
499
+ end
500
+
501
+ it "#find_each should work as an enumerator" do
502
+ result_array = []
503
+
504
+ User.using(:brazil).where("name is not NULL").find_each.each do |user|
505
+ result_array << user
506
+ end
507
+
508
+ expect(result_array).to eq([@user1, @user2, @user3])
509
+ end
510
+
511
+ it "#find_each should work as a lazy enumerator" do
512
+ result_array = []
513
+
514
+ User.using(:brazil).where("name is not NULL").find_each.lazy.each do |user|
515
+ result_array << user
516
+ end
517
+
518
+ expect(result_array).to eq([@user1, @user2, @user3])
519
+ end
520
+
521
+ it "#find_in_batches should work with a block" do
522
+ result_array = []
523
+
524
+ User.using(:brazil).where("name is not NULL").find_in_batches(batch_size: 1) do |user|
525
+ result_array << user
526
+ end
527
+
528
+ expect(result_array).to eq([[@user1], [@user2], [@user3]])
529
+ end
530
+
531
+ it "#find_in_batches should work as an enumerator" do
532
+ result_array = []
533
+
534
+ User.using(:brazil).where("name is not NULL").find_in_batches(batch_size: 1).each do |user|
535
+ result_array << user
536
+ end
537
+
538
+ expect(result_array).to eq([[@user1], [@user2], [@user3]])
539
+ end
540
+
541
+ it "#find_in_batches should work as a lazy enumerator" do
542
+ result_array = []
543
+
544
+ User.using(:brazil).where("name is not NULL").find_in_batches(batch_size: 1).lazy.each do |user|
545
+ result_array << user
546
+ end
547
+
548
+ expect(result_array).to eq([[@user1], [@user2], [@user3]])
549
+ end
550
+ end
551
+
552
+ describe 'deleting a record' do
553
+ before(:each) do
554
+ @user = User.using(:brazil).create!(:name => 'User1')
555
+ @user2 = User.using(:brazil).find(@user.id)
556
+ end
557
+
558
+ it 'delete' do
559
+ @user2.delete
560
+ expect { User.using(:brazil).find(@user2.id) }.to raise_error(ActiveRecord::RecordNotFound)
561
+ end
562
+
563
+ it "delete within block shouldn't lose shard" do
564
+ Octoball.using(:brazil) do
565
+ @user2.delete
566
+ @user3 = User.create(:name => 'User3')
567
+
568
+ expect(User.connection.current_shard).to eq(:brazil)
569
+ expect(User.find(@user3.id)).to eq(@user3)
570
+ end
571
+ end
572
+
573
+ it 'destroy' do
574
+ @user2.destroy
575
+ expect { User.using(:brazil).find(@user2.id) }.to raise_error(ActiveRecord::RecordNotFound)
576
+ end
577
+
578
+ it "destroy within block shouldn't lose shard" do
579
+ Octoball.using(:brazil) do
580
+ @user2.destroy
581
+ @user3 = User.create(:name => 'User3')
582
+
583
+ expect(User.connection.current_shard).to eq(:brazil)
584
+ expect(User.find(@user3.id)).to eq(@user3)
585
+ end
586
+ end
587
+ end
588
+ end
589
+
590
+ describe 'custom connection' do
591
+ context 'by default' do
592
+ it 'with plain call should use custom connection' do
593
+ expect(CustomConnection.connection.current_database).to eq('octoball_shard_2')
594
+ end
595
+
596
+ it 'should use model-specific shard' do
597
+ expect(CustomConnection.using(:custom_shard).connection.current_database).to eq('octoball_shard_3')
598
+ end
599
+
600
+ it 'should use model-specific shard by Octoball.using block' do
601
+ Octoball.using(:custom_shard) do
602
+ expect(CustomConnection.connection.current_database).to eq('octoball_shard_3')
603
+ end
604
+ end
605
+
606
+ it 'should save to correct shard' do
607
+ expect { CustomConnection.create(:value => 'custom value') }.to change {
608
+ CustomConnection
609
+ .connection
610
+ .execute("select count(*) as ct from custom where value = 'custom value'")
611
+ .to_a.first.first
612
+ }.by 1
613
+ end
614
+ end
615
+
616
+ describe 'clear_active_connections!' do
617
+ it 'should not leak connection' do
618
+ CustomConnection.create(:value => 'custom value')
619
+
620
+ # This is what Rails, Sidekiq etc call--this normally handles all connection pools in the app
621
+ expect { ActiveRecord::Base.clear_active_connections! }
622
+ .to change { CustomConnection.connection_pool.active_connection? }
623
+
624
+ expect(CustomConnection.connection_pool.active_connection?).to be_falsey
625
+ end
626
+ end
627
+ end
628
+
629
+ describe 'when using set_table_name' do
630
+ it 'should work correctly' do
631
+ Bacon.using(:brazil).create!(:name => 'YUMMMYYYY')
632
+ end
633
+
634
+ it 'should work correctly with a block' do
635
+ Cheese.using(:brazil).create!(:name => 'YUMMMYYYY')
636
+ end
637
+ end
638
+
639
+ describe 'when using table_name=' do
640
+ it 'should work correctly' do
641
+ Ham.using(:brazil).create!(:name => 'YUMMMYYYY')
642
+ end
643
+ end
644
+
645
+ describe 'when you have joins/include' do
646
+ before(:each) do
647
+ @client1 = Client.using(:brazil).create(:name => 'Thiago')
648
+
649
+ Octoball.using(:canada) do
650
+ @client2 = Client.create(:name => 'Mike')
651
+ @client3 = Client.create(:name => 'Joao')
652
+ @item1 = Item.create(:client => @client2, :name => 'Item 1')
653
+ @item2 = Item.create(:client => @client2, :name => 'Item 2')
654
+ @item3 = Item.create(:client => @client3, :name => 'Item 3')
655
+ @part1 = Part.create(:item => @item1, :name => 'Part 1')
656
+ @part2 = Part.create(:item => @item1, :name => 'Part 2')
657
+ @part3 = Part.create(:item => @item2, :name => 'Part 3')
658
+ end
659
+
660
+ @item4 = Item.using(:brazil).create(:client => @client1, :name => 'Item 4')
661
+ end
662
+
663
+ it 'should work using the rails 3.x syntax' do
664
+ items = Item.using(:canada).joins(:client).where("clients.id = #{@client2.id}").all
665
+ expect(items).to eq([@item1, @item2])
666
+ end
667
+
668
+ it 'should work for include also, rails 3.x syntax' do
669
+ items = Item.using(:canada).includes(:client).where(:clients => { :id => @client2.id }).all
670
+ expect(items).to eq([@item1, @item2])
671
+ end
672
+ end
673
+
674
+ describe 'ActiveRecord::Base Validations' do
675
+ it 'should work correctly when using validations' do
676
+ @key = Keyboard.create!(:name => 'Key')
677
+ expect { Keyboard.using(:brazil).create!(:name => 'Key') }.not_to raise_error
678
+ expect { Keyboard.create!(:name => 'Key') }.to raise_error(ActiveRecord::RecordInvalid)
679
+ end
680
+
681
+ it 'should work correctly when using validations with using syntax' do
682
+ @key = Keyboard.using(:brazil).create!(:name => 'Key')
683
+ expect { Keyboard.create!(:name => 'Key') }.not_to raise_error
684
+ expect { Keyboard.using(:brazil).create!(:name => 'Key') }
685
+ .to raise_error(ActiveRecord::RecordInvalid)
686
+ end
687
+ end
688
+ end