xbar 0.0.1 → 0.4.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.
Files changed (80) hide show
  1. data/Appraisals +25 -0
  2. data/README.mkdn +215 -0
  3. data/Rakefile +337 -1
  4. data/examples/README +5 -0
  5. data/examples/config/simple.json +22 -0
  6. data/examples/example1.rb +34 -0
  7. data/examples/migrations/1_create_users.rb +10 -0
  8. data/examples/setup.rb +43 -0
  9. data/gemfiles/rails3.gemfile +8 -0
  10. data/gemfiles/rails3.gemfile.lock +74 -0
  11. data/gemfiles/rails31.gemfile +8 -0
  12. data/gemfiles/rails31.gemfile.lock +83 -0
  13. data/gemfiles/rails32.gemfile +7 -0
  14. data/gemfiles/rails32.gemfile.lock +117 -0
  15. data/gemfiles/rails4.gemfile +9 -0
  16. data/gemfiles/rails4.gemfile.lock +134 -0
  17. data/lib/migrations/1_create_usage_statistics.rb +23 -0
  18. data/lib/xbar/association.rb +49 -0
  19. data/lib/xbar/association_collection.rb +69 -0
  20. data/lib/xbar/colors.rb +32 -0
  21. data/lib/xbar/has_and_belongs_to_many_association.rb +17 -0
  22. data/lib/xbar/logger.rb +14 -0
  23. data/lib/xbar/mapper.rb +304 -0
  24. data/lib/xbar/migration.rb +76 -0
  25. data/lib/xbar/model.rb +165 -0
  26. data/lib/xbar/proxy.rb +249 -0
  27. data/lib/xbar/rails2/association.rb +133 -0
  28. data/lib/xbar/rails2/persistence.rb +39 -0
  29. data/lib/xbar/rails3/arel.rb +13 -0
  30. data/lib/xbar/rails3/association.rb +112 -0
  31. data/lib/xbar/rails3/persistence.rb +37 -0
  32. data/lib/xbar/rails3.1/singular_association.rb +34 -0
  33. data/lib/xbar/scope_proxy.rb +55 -0
  34. data/lib/xbar/shard.rb +95 -0
  35. data/lib/xbar/version.rb +2 -2
  36. data/lib/xbar.rb +121 -2
  37. data/run +27 -0
  38. data/spec/config/acme.json +53 -0
  39. data/spec/config/connection.rb +2 -0
  40. data/spec/config/default.json +160 -0
  41. data/spec/config/duplicate_shard.json +21 -0
  42. data/spec/config/missing_key.json +20 -0
  43. data/spec/config/new_shards.json +29 -0
  44. data/spec/config/no_master_shard.json +19 -0
  45. data/spec/config/not_entire_sharded.json +23 -0
  46. data/spec/config/octopus.json +27 -0
  47. data/spec/config/octopus_rails.json +25 -0
  48. data/spec/config/production_fully_replicated.json +21 -0
  49. data/spec/config/production_raise_error.json +17 -0
  50. data/spec/config/simple.json +22 -0
  51. data/spec/config/single_adapter.json +20 -0
  52. data/spec/console.rb +15 -0
  53. data/spec/migrations/10_create_users_using_replication.rb +12 -0
  54. data/spec/migrations/11_add_field_in_all_slaves.rb +11 -0
  55. data/spec/migrations/12_create_users_using_block.rb +23 -0
  56. data/spec/migrations/13_create_users_using_block_and_using.rb +15 -0
  57. data/spec/migrations/1_create_users_on_master.rb +9 -0
  58. data/spec/migrations/2_create_users_on_canada.rb +11 -0
  59. data/spec/migrations/3_create_users_on_both_shards.rb +11 -0
  60. data/spec/migrations/4_create_users_on_shards_of_a_group.rb +11 -0
  61. data/spec/migrations/5_create_users_on_multiples_groups.rb +11 -0
  62. data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +11 -0
  63. data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +11 -0
  64. data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +11 -0
  65. data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +11 -0
  66. data/spec/spec_helper.rb +25 -0
  67. data/spec/support/database_models.rb +78 -0
  68. data/spec/support/xbar_helper.rb +42 -0
  69. data/spec/xbar/association_spec.rb +660 -0
  70. data/spec/xbar/controller_spec.rb +40 -0
  71. data/spec/xbar/logger_spec.rb +22 -0
  72. data/spec/xbar/mapper_spec.rb +283 -0
  73. data/spec/xbar/migration_spec.rb +110 -0
  74. data/spec/xbar/model_spec.rb +434 -0
  75. data/spec/xbar/proxy_spec.rb +124 -0
  76. data/spec/xbar/replication_spec.rb +94 -0
  77. data/spec/xbar/scope_proxy_spec.rb +22 -0
  78. data/spec/xbar/shard_spec.rb +36 -0
  79. data/xbar.gemspec +13 -3
  80. metadata +231 -10
@@ -0,0 +1,434 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe XBar::Model do
4
+ before(:each) do
5
+ set_xbar_env('default', 'test')
6
+ end
7
+ describe "#using method" do
8
+ it "should return self after calling the #using method" do
9
+ User.using(:canada).should == XBar::ScopeProxy.new(:canada, User)
10
+ end
11
+
12
+ it "should allow to send a block to the master shard" do
13
+ XBar.using(:master) do
14
+ User.create!(:name => "Block test")
15
+ end
16
+
17
+ User.using(:master).find_by_name("Block test").should_not be_nil
18
+ end
19
+
20
+ it 'should allow to pass a string as the shard name to a AR subclass' do
21
+ User.using('canada').create!(:name => 'Rafael Pilha')
22
+
23
+ User.using('canada').find_by_name('Rafael Pilha').should_not be_nil
24
+ end
25
+
26
+ it 'should allow to pass a string as the shard name to a block' do
27
+ XBar.using('canada') do
28
+ User.create!(:name => 'Rafael Pilha')
29
+ end
30
+
31
+ User.using('canada').find_by_name('Rafael Pilha').should_not be_nil
32
+ end
33
+
34
+ it "should allow selecting the shards on scope" do
35
+ User.using(:canada).create!(:name => 'oi')
36
+ User.using(:canada).count.should == 1
37
+ User.count.should == 0
38
+ end
39
+
40
+ it "should allow selecting the shard using #new" do
41
+
42
+ u = User.using(:canada).new
43
+ u.name = "Thiago"
44
+ u.save
45
+
46
+ User.using(:canada).count.should == 1
47
+ User.using(:brazil).count.should == 0
48
+
49
+ u1 = User.new
50
+ u1.name = "Joaquim"
51
+
52
+ u2 = User.using(:canada).new
53
+ u2.name = "Manuel"
54
+ u1.save
55
+ u2.save
56
+ User.using(:canada).all
57
+
58
+ User.using(:canada).all.should == [u, u2] # XXX
59
+ User.all.should == [u1]
60
+
61
+ end
62
+
63
+ it "should select the correct shard" do
64
+ User.using(:canada)
65
+ User.create!(:name => 'oi')
66
+ User.count.should == 1
67
+ end
68
+
69
+ it "should ensure that the connection will be cleaned" do
70
+ ActiveRecord::Base.connection.current_shard.should == :master
71
+ begin
72
+ XBar.using(:canada) do
73
+ raise "Some Exception"
74
+ end
75
+ rescue
76
+ end
77
+
78
+ ActiveRecord::Base.connection.current_shard.should == :master
79
+ end
80
+
81
+ it "should allow creating more than one user" do
82
+ XBar.debug = true
83
+ User.using(:canada).create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
84
+ User.create!(:name => "Thiago")
85
+ User.using(:canada).find_by_name("America User 1").should_not be_nil
86
+ User.using(:canada).find_by_name("America User 2")#.should_not be_nil
87
+ User.using(:master).find_by_name("Thiago").should_not be_nil
88
+ User.all.size.should == 1 # fail -- America User 2 is being created on master!!!
89
+ XBar.debug = false
90
+ end
91
+
92
+ it "should work when you have a SQLite3 shard" do
93
+ u = User.using(:paris).create!(:name => "Sqlite3")
94
+ User.using(:paris).find_by_name("Sqlite3").should == u
95
+ end
96
+
97
+ it "should clean #current_shard from proxy when using execute" do
98
+ User.using(:canada).connection().execute("select * from users limit 1;")
99
+ User.connection.current_shard.should == :master
100
+ end
101
+
102
+ it "should allow scoping dynamically" do
103
+ User.using(:canada).using(:master).using(:canada).create!(:name => 'oi')
104
+ User.using(:canada).using(:master).count.should == 0
105
+ User.using(:master).using(:canada).count.should == 1
106
+ end
107
+
108
+ it "should allow find inside blocks" do
109
+ @user = User.using(:brazil).create!(:name => "Thiago")
110
+
111
+ XBar.using(:brazil) do
112
+ User.first.should == @user
113
+ end
114
+
115
+ User.using(:brazil).find_by_name("Thiago").should == @user
116
+ end
117
+
118
+ it "should clean the current_shard after executing the current query" do
119
+ User.using(:canada).create!(:name => "oi")
120
+ User.count.should == 0
121
+ end
122
+
123
+ it "should support both groups and alone shards" do
124
+ u = User.using(:london).create!(:name => "Alone")
125
+ User.using(:london).count.should == 1
126
+ User.using(:canada).count.should == 0
127
+ User.using(:brazil).count.should == 0
128
+ User.count.should == 0
129
+ end
130
+
131
+ describe "#current_shard attribute" do
132
+ it "should store the attribute when you create or find an object" do
133
+ u = User.using(:london).create!(:name => "Alone")
134
+ u.current_shard.should == :london
135
+ User.using(:canada).create!(:name => 'oi')
136
+ u = User.using(:canada).find_by_name("oi")
137
+ u.current_shard.should == :canada
138
+ end
139
+
140
+ it "should store the attribute when you find multiple instances" do
141
+ 5.times { User.using(:london).create!(:name => "Alone") }
142
+
143
+ User.using(:london).all.each do |u|
144
+ u.current_shard.should == :london
145
+ end
146
+ end
147
+
148
+ it "should works when you find, and after that, alter that object" do
149
+ alone_user = User.using(:london).create!(:name => "Alone")
150
+ master_user = User.using(:master).create!(:name => "Master")
151
+ alone_user.name = "teste"
152
+ alone_user.save
153
+ User.using(:master).find(:first).name.should == "Master"
154
+ User.using(:london).find(:first).name.should == "teste"
155
+ end
156
+
157
+ it "should work for the reload method" do
158
+ User.using(:london).create!(:name => "Alone")
159
+ u = User.using(:london).find_by_name("Alone")
160
+ u.reload
161
+ u.name.should == "Alone"
162
+ end
163
+
164
+ it "should work passing some arguments to reload method" do
165
+ User.using(:london).create!(:name => "Alone")
166
+ u = User.using(:london).find_by_name("Alone")
167
+ u.reload(:lock => true)
168
+ u.name.should == "Alone"
169
+ end
170
+ end
171
+
172
+ describe "passing a block" do
173
+ it "should allow queries be executed inside the block, ponting to a specific shard" do
174
+ XBar.using(:canada) do
175
+ User.create(:name => "oi")
176
+ end
177
+
178
+ User.using(:canada).count.should == 1
179
+ User.using(:master).count.should == 0
180
+ User.count.should == 0
181
+ end
182
+
183
+ it "should allow execute queries inside a model" do
184
+ u = User.new
185
+ u.awesome_queries()
186
+ User.using(:canada).count.should == 1
187
+ User.count.should == 0
188
+ end
189
+ end
190
+
191
+ describe "raising errors" do
192
+ it "should raise a error when you specify a shard that doesn't exist" do
193
+ lambda { User.using(:crazy_shard).create!(:name => 'Thiago') }.should raise_error("Nonexistent Shard Name: crazy_shard")
194
+ end
195
+ end
196
+ end
197
+
198
+ describe "using a postgresql shard" do
199
+ it "should update the Arel Engine" do
200
+ if ActiveRecord::VERSION::STRING > '2.4.0'
201
+ User.using(:moscow).arel_engine.connection.adapter_name.should == "PostgreSQL"
202
+ User.using(:london).arel_engine.connection.adapter_name.should == "Mysql2"
203
+ end
204
+ end
205
+
206
+ it "should works with writes and reads" do
207
+ u = User.using(:moscow).create!(:name => "PostgreSQL User")
208
+ User.using(:moscow).find(:all).should == [u]
209
+ User.using(:london).find(:all).should == []
210
+ User.connection_handler.connection_pools["ActiveRecord::Base"] = User.connection.shards[:master]
211
+ end
212
+ end
213
+
214
+ describe "AR basic methods" do
215
+ it "xbar_establish_connection" do
216
+ CustomConnection.connection.current_database.should == "rogue"
217
+ end
218
+
219
+ it "increment" do
220
+ u = User.using(:brazil).create!(:name => "Teste", :number => 10)
221
+ u = User.using(:brazil).find_by_number(10)
222
+ u.increment(:number)
223
+ u.save
224
+ u = User.using(:brazil).find_by_number(11).should_not be_nil
225
+ end
226
+
227
+ it "increment!" do
228
+ u = User.using(:brazil).create!(:name => "Teste", :number => 10)
229
+ u = User.using(:brazil).find_by_number(10)
230
+ u.increment!(:number)
231
+ u = User.using(:brazil).find_by_number(11).should_not be_nil
232
+ end
233
+
234
+ it "toggle" do
235
+ u = User.using(:brazil).create!(:name => "Teste", :admin => false)
236
+ u = User.using(:brazil).find_by_name('Teste')
237
+ u.toggle(:admin)
238
+ u.save
239
+ u = User.using(:brazil).find_by_name('Teste').admin.should be_true
240
+ end
241
+
242
+ it "toggle!" do
243
+ u = User.using(:brazil).create!(:name => "Teste", :admin => false)
244
+ u = User.using(:brazil).find_by_name('Teste')
245
+ u.toggle!(:admin)
246
+ u = User.using(:brazil).find_by_name('Teste').admin.should be_true
247
+ end
248
+
249
+ it "count" do
250
+ u = User.using(:brazil).create!(:name => "User1")
251
+ u2 = User.using(:brazil).create!(:name => "User2")
252
+ u3 = User.using(:brazil).create!(:name => "User3")
253
+ User.using(:brazil).find(:all, :conditions => {:name => "User2"}).count.should == 1
254
+ end
255
+
256
+ it "update_attributes" do
257
+ @user = User.using(:brazil).create!(:name => "User1")
258
+ @user2 = User.using(:brazil).find(@user.id)
259
+ @user2.update_attributes(:name => "Joaquim")
260
+ User.using(:brazil).find_by_name("Joaquim").should_not be_nil
261
+ end
262
+
263
+ it "using update_attributes inside a block" do
264
+ XBar.using(:brazil) do
265
+ @user = User.create!(:name => "User1")
266
+ @user2 = User.find(@user.id)
267
+ @user2.update_attributes(:name => "Joaquim")
268
+ end
269
+
270
+ User.find_by_name("Joaquim").should be_nil
271
+ User.using(:brazil).find_by_name("Joaquim").should_not be_nil
272
+ end
273
+
274
+ it "update_attribute" do
275
+ @user = User.using(:brazil).create!(:name => "User1")
276
+ @user2 = User.using(:brazil).find(@user.id)
277
+ @user2.update_attribute(:name, "Joaquim")
278
+ User.using(:brazil).find_by_name("Joaquim").should_not be_nil
279
+ end
280
+
281
+ it "transaction" do
282
+ u = User.create!(:name => "Thiago")
283
+
284
+ User.using(:brazil).count.should == 0
285
+ User.using(:master).count.should == 1
286
+
287
+ User.using(:brazil).transaction do
288
+ User.find_by_name("Thiago").should be_nil
289
+ User.create!(:name => "Brazil")
290
+ end
291
+
292
+ User.using(:brazil).count.should == 1
293
+ User.using(:master).count.should == 1
294
+ end
295
+
296
+ describe "deleting a record" do
297
+ before(:each) do
298
+ @user = User.using(:brazil).create!(:name => "User1")
299
+ @user2 = User.using(:brazil).find(@user.id)
300
+ end
301
+
302
+ it "delete" do
303
+ @user2.delete
304
+ lambda { User.using(:brazil).find(@user2.id) }.should raise_error(ActiveRecord::RecordNotFound)
305
+ end
306
+
307
+ it "delete within block shouldn't lose shard" do
308
+ XBar.using(:brazil) do
309
+ @user2.delete
310
+ @user3 = User.create(:name => "User3")
311
+
312
+ User.connection.current_shard.should == :brazil
313
+ User.find(@user3.id).should == @user3
314
+ end
315
+ end
316
+
317
+ it "destroy" do
318
+ @user2.destroy
319
+ lambda { User.using(:brazil).find(@user2.id) }.should raise_error(ActiveRecord::RecordNotFound)
320
+ end
321
+
322
+ it "destroy within block shouldn't lose shard" do
323
+ XBar.using(:brazil) do
324
+ @user2.destroy
325
+ @user3 = User.create(:name => "User3")
326
+
327
+ User.connection.current_shard.should == :brazil
328
+ User.find(@user3.id).should == @user3
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ describe "when using set_table_name" do
335
+ it 'should work correctly' do
336
+ Bacon.using(:brazil).create!(:name => "YUMMMYYYY")
337
+ end
338
+ end
339
+
340
+ describe "when using a environment with a single adapter" do
341
+ before (:each) do
342
+ set_xbar_env('single_adapter', 'test')
343
+ @proxy = XBar::Proxy.new
344
+ end
345
+
346
+ it 'should_clean_table_name? should return false' do
347
+ @proxy.should_clean_table_name?.should == false
348
+ end
349
+
350
+ it 'model object should not receive call to clean table name' do
351
+ begin
352
+
353
+ Keyboard.using(:master).create!(:name => "Master Cat")
354
+ # Keyboard.create!(:name => "Master Cat")
355
+ Keyboard.should_not_receive(:reset_table_name)
356
+
357
+ rescue
358
+ fail 'Keyboard.using(:master).create!(:name => "Master Cat")'
359
+ end
360
+ end
361
+ end
362
+
363
+ describe "when you have joins/include" do
364
+
365
+ before(:each) do
366
+
367
+ @client1 = Client.using(:brazil).create(:name => "Thiago")
368
+
369
+ XBar.using(:canada) do
370
+ @client2 = Client.create(:name => "Mike")
371
+ @client3 = Client.create(:name => "Joao")
372
+ @item1 = Item.create(:client => @client2, :name => "Item 1")
373
+ @item2 = Item.create(:client => @client2, :name => "Item 2")
374
+ @item3 = Item.create(:client => @client3, :name => "Item 3")
375
+ @part1 = Part.create(:item => @item1, :name => "Part 1")
376
+ @part2 = Part.create(:item => @item1, :name => "Part 2")
377
+ @part3 = Part.create(:item => @item2, :name => "Part 3")
378
+ end
379
+
380
+ @item4 = Item.using(:brazil).create(:client => @client1, :name => "Item 4")
381
+ end
382
+
383
+ it "should work with the rails 2.x syntax" do
384
+ items = Item.using(:canada).find(:all, :joins => :client, :conditions => { :clients => { :id => @client2.id } })
385
+ items.should == [@item1, @item2]
386
+ end
387
+
388
+ it "should work using the rails 3.x syntax" do
389
+ if XBar.rails3?
390
+ items = Item.using(:canada).joins(:client).where("clients.id = #{@client2.id}").all
391
+ items.should == [@item1, @item2]
392
+ end
393
+ end
394
+
395
+ it "should work for include also, rails 2.x syntax" do
396
+ items = Item.using(:canada).find(:all, :include => :client, :conditions => { :clients => { :id => @client2.id } })
397
+ items.should == [@item1, @item2]
398
+ end
399
+
400
+ it "should work for include also, rails 3.x syntax" do
401
+ if XBar.rails3?
402
+ items = Item.using(:canada).includes(:client).where("clients.id = #{@client2.id}").all
403
+ items.should == [@item1, @item2]
404
+ end
405
+ end
406
+
407
+ it "should work for multiple includes, with rails 2.x syntax" do
408
+ parts = Part.using(:canada).find(:all, :include => {:item => :client}, :conditions => {:clients => { :id => @client2.id}})
409
+ parts.should == [@part1, @part2, @part3]
410
+ parts.first.item.client.should == @client2
411
+ end
412
+
413
+ it "should work for multiple join, with rails 2.x syntax" do
414
+ parts = Part.using(:canada).find(:all, :joins => {:item => :client}, :conditions => {:clients => { :id => @client2.id}})
415
+ parts.should == [@part1, @part2, @part3]
416
+ parts.first.item.client.should == @client2
417
+ end
418
+ end
419
+
420
+ describe "ActiveRecord::Base Validations" do
421
+ it "should work correctly when using validations" do
422
+ @key = Keyboard.create!(:name => "Key")
423
+ lambda { Keyboard.using(:brazil).create!(:name => "Key") }.should_not raise_error()
424
+ lambda { Keyboard.create!(:name => "Key") }.should raise_error()
425
+ end
426
+
427
+ it "should work correctly when using validations with using syntax" do
428
+ @key = Keyboard.using(:brazil).create!(:name => "Key")
429
+ lambda { Keyboard.create!(:name => "Key") }.should_not raise_error()
430
+ lambda { Keyboard.using(:brazil).create!(:name => "Key") }.should raise_error()
431
+ end
432
+ end
433
+
434
+ end
@@ -0,0 +1,124 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe XBar::Proxy do
4
+ let(:proxy) { XBar::Proxy.new }
5
+
6
+ describe "creating a new instance" do
7
+ before(:all) do
8
+ set_xbar_env('default', 'test')
9
+ end
10
+
11
+ it "should have all the configured shards" do
12
+ proxy.shards.keys.to_set.should == [
13
+ "master", "london", "paris", "moscow", "russia", "russia_east",
14
+ "russia_central", "russia_west", "canada", "canada_east",
15
+ "canada_central", "canada_west", "brazil", "china", "china_east",
16
+ "china_west"].to_set
17
+ end
18
+
19
+ it "should have all the configured shards in the shards list" do
20
+ proxy.shard_list.keys.to_set.should == [
21
+ "master", "london", "paris", "moscow", "russia", "russia_east",
22
+ "russia_central", "russia_west", "canada", "canada_east",
23
+ "canada_central", "canada_west", "brazil", "china", "china_east",
24
+ "china_west"].to_set
25
+ end
26
+
27
+ it "every shard in the shard list should have the correct name" do
28
+ proxy.shard_list.each do |name, shard|
29
+ shard.instance_variable_get(:@shard_name).should == name
30
+ end
31
+ end
32
+
33
+ it "should initialize the block attribute as false" do
34
+ proxy.in_block_scope?.should be_false
35
+ end
36
+
37
+ it "should not verify connections for default" do
38
+ proxy.verify_connection.should be_false
39
+ end
40
+
41
+ it 'should respond correctly to respond_to?(:pk_and_sequence_for)' do
42
+ pending
43
+ proxy.respond_to?(:pk_and_sequence_for).should be_true
44
+ end
45
+
46
+ it 'should respond correctly to respond_to?(:primary_key)' do
47
+ pending
48
+ proxy.respond_to?(:primary_key).should be_true
49
+ end
50
+
51
+ describe "#should_clean_table_name?" do
52
+ it 'should return true when you have a environment with multiple database types' do
53
+ proxy.should_clean_table_name?.should be_true
54
+ end
55
+
56
+ context "when using a environment with a single adapter" do
57
+ before(:each) do
58
+ set_xbar_env("single_adapter", "test")
59
+ end
60
+
61
+ it 'should return false' do
62
+ proxy.should_clean_table_name?.should be_false
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "using a Rails environment where XBar is not enabled" do
69
+ before(:each) do
70
+ Rails = mock
71
+ # XBar is not enabled for the 'staging' environment
72
+ Rails.stub(:env).and_return('staging')
73
+ set_xbar_env('default')
74
+ end
75
+
76
+ it "should use the master connection" do
77
+ user = User.create!(:name =>"Thiago")
78
+ user.name = "New Thiago"
79
+ user.save
80
+ User.find_by_name("New Thiago").should_not be_nil
81
+ end
82
+
83
+ it "should work with the 'using' syntax" do
84
+ user = User.using(:russia).create!(:name =>"Thiago")
85
+ user.name = "New Thiago"
86
+ user.save
87
+ User.using(:russia).find_by_name("New Thiago").should == user
88
+ User.find_by_name("New Thiago").should == user
89
+ end
90
+
91
+ it "should work when using blocks" do
92
+ XBar.using(:russia) do
93
+ @user = User.create!(:name =>"Thiago")
94
+ end
95
+ User.find_by_name("Thiago").should == @user
96
+ end
97
+
98
+ it "should work with associations" do
99
+ u = Client.create!(:name => "Thiago")
100
+ i = Item.create(:name => "Item")
101
+ u.items << i
102
+ u.save
103
+ end
104
+
105
+ after(:each) do
106
+ Object.send(:remove_const, :Rails)
107
+ end
108
+ end
109
+
110
+ describe "connections should be managed properly" do
111
+
112
+ before(:each) do
113
+ set_xbar_env('default', 'test')
114
+ end
115
+
116
+ describe "should return the shard name" do
117
+ it "when current_shard not explicitly set" do
118
+ proxy.current_shard.should == :master
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+ end
@@ -0,0 +1,94 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "when the database is replicated" do
4
+ it "should send all writes/reads queries to master when you have a non replicated model" do
5
+ using_environment :production_replicated do
6
+ u = User.create!(:name => "Replicated")
7
+ User.count.should == 1
8
+ User.find(u).should == u
9
+ end
10
+ end
11
+
12
+ it "should send all writes queries to master" do
13
+ using_environment :production_replicated do
14
+ Cat.create!(:name => "Slave Cat")
15
+ # The next line will break if the database really is replicated.
16
+ # This test is assuming that no external mechanism is actually
17
+ # propagating the writes.
18
+ Cat.find_by_name("Slave Cat").should be_nil # will read from slave
19
+ Client.create!(:name => "Slave Client")
20
+ Client.find_by_name("Slave Client").should_not be_nil # will read from master
21
+ end
22
+ end
23
+
24
+ it "should allow to create multiple models on the master" do
25
+ using_environment :production_replicated do
26
+ Cat.create!([{:name => "Slave Cat 1"}, {:name => "Slave Cat 2"}])
27
+ Cat.find_by_name("Slave Cat 1").should be_nil
28
+ Cat.find_by_name("Slave Cat 2").should be_nil
29
+ Cat.using(:master).find_by_name("Slave Cat 1").should_not be_nil # MBS
30
+ Cat.using(:master).find_by_name("Slave Cat 2").should_not be_nil # MBS
31
+ end
32
+ end
33
+
34
+ it "should allow #using syntax to send queries to master" do
35
+ Cat.create!(:name => "Master Cat")
36
+
37
+ using_environment :production_fully_replicated do
38
+ Cat.using(:master).find_by_name("Master Cat").should_not be_nil
39
+ end
40
+ end
41
+
42
+ it "should send the count query to a slave" do
43
+ pending()
44
+ # using_environment :production_replicated do
45
+ # Cat.create!(:name => "Slave Cat")
46
+ # Cat.count.should == 0
47
+ # end
48
+ end
49
+ end
50
+
51
+
52
+ describe "when the database is replicated and the entire application is replicated" do
53
+ before(:each) do
54
+ XBar.stub!(:env).and_return("production_fully_replicated")
55
+ clean_connection_proxy()
56
+ end
57
+
58
+ it "should send all writes queries to master" do
59
+ using_environment :production_fully_replicated do
60
+ Cat.create!(:name => "Slave Cat")
61
+ Cat.find_by_name("Slave Cat").should be_nil
62
+ Client.create!(:name => "Slave Client")
63
+ Client.find_by_name("Slave Client").should be_nil
64
+ end
65
+ end
66
+
67
+ it "should work with validate_uniquess_of" do
68
+ Keyboard.create!(:name => "thiago")
69
+
70
+ using_environment :production_fully_replicated do
71
+ k = Keyboard.new(:name => "thiago")
72
+ k.save.should be_false
73
+ if XBar.rails31?
74
+ k.errors.messages[:name].first.should == "has already been taken"
75
+ else
76
+ k.errors.should == {:name=>["has already been taken"]}
77
+ end
78
+ end
79
+ end
80
+
81
+ it "should reset current shard if slave throws an exception" do
82
+
83
+ using_environment :production_fully_replicated do
84
+ Cat.create!(:name => "Slave Cat")
85
+ Cat.connection.current_shard.should eql(:master)
86
+ begin
87
+ Cat.find(:all, :conditions => 'rubbish = true')
88
+ rescue
89
+ end
90
+ Cat.connection.current_shard.should eql(:master)
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe XBar::ScopeProxy do
4
+ before(:each) do
5
+ set_xbar_env('default', 'test')
6
+ end
7
+ it "should allow nested queries" do
8
+ if XBar.rails3?
9
+ @user1 = User.using(:brazil).create!(:name => "Thiago P", :number => 3)
10
+ @user2 = User.using(:brazil).create!(:name => "Thiago", :number => 1)
11
+ @user3 = User.using(:brazil).create!(:name => "Thiago", :number => 2)
12
+
13
+ User.using(:brazil).where(:name => "Thiago").where(:number => 4).order(:number).all.should == []
14
+ User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 2).using(:brazil).order(:number).all.should == [@user3]
15
+ User.using(:brazil).where(:name => "Thiago").using(:canada).where(:number => 4).using(:brazil).order(:number).all.should == []
16
+ end
17
+ end
18
+
19
+ it "should raise a exception when trying to send a query to a shard that don't exists" do
20
+ lambda { User.using(:dont_exists).all }.should raise_exception("Nonexistent Shard Name: dont_exists")
21
+ end
22
+ end