dynashard 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.
- data/.document +5 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +100 -0
- data/LICENSE.txt +20 -0
- data/README.md +72 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/lib/dynashard/arel_sql_engine.rb +22 -0
- data/lib/dynashard/associations.rb +19 -0
- data/lib/dynashard/connection_handler.rb +45 -0
- data/lib/dynashard/model.rb +92 -0
- data/lib/dynashard/validations.rb +23 -0
- data/lib/dynashard.rb +167 -0
- data/spec/arel_sql_engine_spec.rb +51 -0
- data/spec/associations_spec.rb +182 -0
- data/spec/connection_handler_spec.rb +61 -0
- data/spec/db/schema.rb +67 -0
- data/spec/dynashard_spec.rb +126 -0
- data/spec/model_spec.rb +104 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/support/factories.rb +32 -0
- data/spec/support/models.rb +99 -0
- data/spec/validation_spec.rb +101 -0
- metadata +188 -0
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Dynashard::ProxyExtensions' do
|
4
|
+
before(:each) do
|
5
|
+
Dynashard.enable
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'with a sharding proxy owner' do
|
9
|
+
before(:each) do
|
10
|
+
@owner = Factory(:sharding_owner)
|
11
|
+
@shard = @owner.shard_dsn
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'and a sharding proxy target' do
|
15
|
+
it 'uses a sharded reflection class' do
|
16
|
+
orig_reflection = @owner.class.reflections[:sharded_has_manys]
|
17
|
+
new_reflection = Dynashard.reflection_for(@owner, orig_reflection)
|
18
|
+
Dynashard.should_receive(:reflection_for).once.and_return(new_reflection)
|
19
|
+
proxy = @owner.sharded_has_manys
|
20
|
+
proxy.proxy_reflection.klass.dynashard_klass.should_not be_nil
|
21
|
+
proxy.proxy_reflection.should == new_reflection
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'using a :has_one reflection' do
|
25
|
+
before(:each) do
|
26
|
+
# Ensure that all sharded model connections happen on the owner shard
|
27
|
+
Dynashard.shard_context[:owner] = @shard
|
28
|
+
|
29
|
+
@has_one = Factory(:sharded_has_one, :sharding_owner => @owner, :name => "Owned by #{@owner.name}")
|
30
|
+
end
|
31
|
+
|
32
|
+
after(:each) do
|
33
|
+
Dynashard.shard_context.clear
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: figure out what to do about the classes being different
|
37
|
+
it 'reads from the shard' do
|
38
|
+
@owner.sharded_has_one.attributes.should == @has_one.attributes
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'destroys from the shard' do
|
42
|
+
lambda do
|
43
|
+
@owner.sharded_has_one.destroy
|
44
|
+
end.should change(ShardedHasOne, :count).by(-1)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'creates_other on the shard' do
|
48
|
+
new_owner = Factory(:sharding_owner, :shard => @owner.shard)
|
49
|
+
lambda do
|
50
|
+
new_owner.create_sharded_has_one(:name => "Owned by #{new_owner.name}")
|
51
|
+
end.should change(ShardedHasOne, :count).by(1)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'saves built associations on the shard' do
|
55
|
+
new_owner = Factory(:sharding_owner, :shard => @owner.shard)
|
56
|
+
new_sharded_has_one = new_owner.build_sharded_has_one(:name => "Owned by #{new_owner.name}")
|
57
|
+
lambda do
|
58
|
+
new_sharded_has_one.save
|
59
|
+
end.should change(ShardedHasOne, :count).by(1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'using a :has_many reflection' do
|
64
|
+
before(:each) do
|
65
|
+
# Ensure that all sharded model connections happen on the owner shard
|
66
|
+
Dynashard.shard_context[:owner] = @shard
|
67
|
+
|
68
|
+
@one_of_many = Factory(:sharded_has_many, :sharding_owner => @owner, :name => "Owned by #{@owner.name}")
|
69
|
+
end
|
70
|
+
|
71
|
+
after(:each) do
|
72
|
+
Dynashard.shard_context.clear
|
73
|
+
end
|
74
|
+
|
75
|
+
# TODO: figure out what to do about the classes being different
|
76
|
+
it 'reads from the shard' do
|
77
|
+
@owner.sharded_has_manys.detect{|m| m.attributes == @one_of_many.attributes}.should_not be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'destroys from the shard' do
|
81
|
+
lambda do
|
82
|
+
one_of_many = @owner.sharded_has_manys.detect{|m| m.attributes == @one_of_many.attributes}
|
83
|
+
one_of_many.destroy
|
84
|
+
end.should change(ShardedHasMany, :count).by(-1)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'creates_other on the shard' do
|
88
|
+
new_owner = Factory(:sharding_owner, :shard => @owner.shard)
|
89
|
+
lambda do
|
90
|
+
new_owner.sharded_has_manys.create(:name => "Owned by #{new_owner.name}")
|
91
|
+
end.should change(ShardedHasMany, :count).by(1)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'saves built associations on the shard' do
|
95
|
+
new_owner = Factory(:sharding_owner, :shard => @owner.shard)
|
96
|
+
new_one_of_many = new_owner.sharded_has_manys.build(:name => "Owned by #{new_owner.name}")
|
97
|
+
lambda do
|
98
|
+
new_one_of_many.save
|
99
|
+
end.should change(ShardedHasMany, :count).by(1)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'using a :has_many_through reflection' do
|
104
|
+
before(:each) do
|
105
|
+
# Ensure that all sharded model connections happen on the owner shard
|
106
|
+
Dynashard.shard_context[:owner] = @shard
|
107
|
+
|
108
|
+
@one_of_many = Factory(:sharded_has_many_through, :name => "Owned by #{@owner.name}")
|
109
|
+
join = Factory(:sharded_join, :sharding_owner => @owner, :sharded_has_many_through => @one_of_many)
|
110
|
+
end
|
111
|
+
|
112
|
+
after(:each) do
|
113
|
+
Dynashard.shard_context.clear
|
114
|
+
end
|
115
|
+
|
116
|
+
# TODO: figure out what to do about the classes being different
|
117
|
+
it 'reads from the shard' do
|
118
|
+
@owner.sharded_has_many_throughs.detect{|m| m.attributes == @one_of_many.attributes}.should_not be_nil
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'destroys from the shard' do
|
122
|
+
lambda do
|
123
|
+
one_of_many = @owner.sharded_has_many_throughs.detect{|m| m.attributes == @one_of_many.attributes}
|
124
|
+
one_of_many.destroy
|
125
|
+
end.should change(ShardedHasManyThrough, :count).by(-1)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'creates_other on the shard' do
|
129
|
+
new_owner = Factory(:sharding_owner, :shard => @owner.shard)
|
130
|
+
lambda do
|
131
|
+
new_owner.sharded_has_many_throughs.create(:name => "Owned by #{new_owner.name}")
|
132
|
+
end.should change(ShardedHasManyThrough, :count).by(1)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'saves built associations on the shard' do
|
136
|
+
new_owner = Factory(:sharding_owner, :shard => @owner.shard)
|
137
|
+
new_one_of_many = new_owner.sharded_has_many_throughs.build(:name => "Owned by #{new_owner.name}")
|
138
|
+
lambda do
|
139
|
+
new_one_of_many.save
|
140
|
+
end.should change(ShardedHasManyThrough, :count).by(1)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'and a non-sharding proxy target' do
|
146
|
+
it 'uses a non-sharded reflection class' do
|
147
|
+
orig_reflection = @owner.class.reflections[:non_sharded_has_manys]
|
148
|
+
Dynashard.should_receive(:reflection_for).never
|
149
|
+
proxy = @owner.non_sharded_has_manys
|
150
|
+
proxy.proxy_reflection.klass.should_not respond_to(:dynashard_klass)
|
151
|
+
proxy.proxy_reflection.should == orig_reflection
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'with a non-sharding proxy owner' do
|
158
|
+
before(:each) do
|
159
|
+
@owner = Factory(:non_sharding_owner)
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'and a sharding proxy target' do
|
163
|
+
it 'uses a non-sharded reflection class' do
|
164
|
+
orig_reflection = @owner.class.reflections[:sharded_has_manys]
|
165
|
+
Dynashard.should_receive(:reflection_for).never
|
166
|
+
proxy = Dynashard.with_context(:owner => Shard.find(:first).dsn){@owner.sharded_has_manys}
|
167
|
+
proxy.proxy_reflection.klass.should_not respond_to(:dynashard_klass)
|
168
|
+
proxy.proxy_reflection.should == orig_reflection
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'and a non-sharding proxy target' do
|
173
|
+
it 'uses a non-sharded reflection class' do
|
174
|
+
orig_reflection = @owner.class.reflections[:non_sharded_has_manys]
|
175
|
+
Dynashard.should_receive(:reflection_for).never
|
176
|
+
proxy = @owner.non_sharded_has_manys
|
177
|
+
proxy.proxy_reflection.klass.should_not respond_to(:dynashard_klass)
|
178
|
+
proxy.proxy_reflection.should == orig_reflection
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Dynashard::ConnectionHandlerExtensions' do
|
4
|
+
before(:each) do
|
5
|
+
Dynashard.enable
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'defining connection pool aliases' do
|
9
|
+
after(:each) do
|
10
|
+
ActiveRecord::Base.connection_handler.connection_pools.delete('Foo')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'succeeds' do
|
14
|
+
connection_handler = ActiveRecord::Base.connection_handler
|
15
|
+
connection_handler.dynashard_pool_alias('Foo', 'ActiveRecord::Base')
|
16
|
+
connection_handler.connection_pools['Foo'].should == connection_handler.connection_pools['ActiveRecord::Base']
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with a sharded association reflection' do
|
21
|
+
it 'retrieves the connection pool' do
|
22
|
+
owner = Factory(:sharding_owner)
|
23
|
+
reflection = Dynashard.reflection_for(owner, ShardingOwner.reflections[:sharded_has_one])
|
24
|
+
dynashard_klass = reflection.klass.dynashard_klass
|
25
|
+
pool = ActiveRecord::Base.connection_handler.connection_pools['ActiveRecord::Base']
|
26
|
+
ActiveRecord::Base.connection_handler.should_receive(:retrieve_connection_pool_without_dynashard).with(dynashard_klass).and_return(pool)
|
27
|
+
reflection.klass.connection.should be_kind_of(ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with a sharded model' do
|
32
|
+
before(:each) do
|
33
|
+
@shard_klass = Dynashard.class_for('shard1')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'retrieves the connection' do
|
37
|
+
dsn = Shard.find(:first).dsn
|
38
|
+
shard_klass = Dynashard.class_for(dsn)
|
39
|
+
pool = ActiveRecord::Base.connection_handler.connection_pools['ActiveRecord::Base']
|
40
|
+
ActiveRecord::Base.connection_handler.should_receive(:retrieve_connection_pool_without_dynashard).with(shard_klass).and_return(pool)
|
41
|
+
connection = Dynashard.with_context(:owner => dsn) {ShardedHasOne.connection}
|
42
|
+
connection.should be_kind_of(ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'requires a defined shard context' do
|
46
|
+
Dynashard.shard_context.clear
|
47
|
+
ShardedHasOne.sharding_enabled?.should be_true
|
48
|
+
lambda do
|
49
|
+
connection = ShardedHasOne.find(:first)
|
50
|
+
end.should raise_error(RuntimeError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with a non-sharded model' do
|
55
|
+
it 'retrieves the connection' do
|
56
|
+
pool = ActiveRecord::Base.connection_handler.connection_pools['ActiveRecord::Base']
|
57
|
+
ActiveRecord::Base.connection_handler.should_receive(:retrieve_connection_pool_without_dynashard).with(NonShardedHasOne).and_return(pool)
|
58
|
+
NonShardedHasOne.connection.should be_kind_of(ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/spec/db/schema.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 1) do
|
2
|
+
|
3
|
+
create_table "shards", :force => true do |t|
|
4
|
+
t.string "name"
|
5
|
+
t.string "adapter"
|
6
|
+
t.string "host"
|
7
|
+
t.string "username"
|
8
|
+
t.string "password"
|
9
|
+
t.string "database"
|
10
|
+
t.integer "port"
|
11
|
+
t.integer "pool"
|
12
|
+
t.integer "timeout"
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table "non_sharding_owners", :force => true do |t|
|
16
|
+
t.string "name"
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table "sharding_owners", :force => true do |t|
|
20
|
+
t.string "name"
|
21
|
+
t.integer "shard_id"
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table "non_sharded_has_ones", :force => true do |t|
|
25
|
+
t.integer "sharding_owner_id"
|
26
|
+
t.integer "non_sharding_owner_id"
|
27
|
+
t.string "name"
|
28
|
+
end
|
29
|
+
|
30
|
+
create_table "non_sharded_has_manies", :force => true do |t|
|
31
|
+
t.integer "sharding_owner_id"
|
32
|
+
t.integer "non_sharding_owner_id"
|
33
|
+
t.string "name"
|
34
|
+
end
|
35
|
+
|
36
|
+
create_table "non_sharded_joins", :force => true do |t|
|
37
|
+
t.integer "sharding_owner_id"
|
38
|
+
t.integer "non_sharding_owner_id"
|
39
|
+
t.integer "non_sharded_has_many_through_id"
|
40
|
+
end
|
41
|
+
|
42
|
+
create_table "non_sharded_has_many_throughs", :force => true do |t|
|
43
|
+
t.string "name"
|
44
|
+
end
|
45
|
+
|
46
|
+
create_table "sharded_has_ones", :force => true do |t|
|
47
|
+
t.integer "sharding_owner_id"
|
48
|
+
t.integer "non_sharding_owner_id"
|
49
|
+
t.string "name"
|
50
|
+
end
|
51
|
+
|
52
|
+
create_table "sharded_has_manies", :force => true do |t|
|
53
|
+
t.integer "sharding_owner_id"
|
54
|
+
t.integer "non_sharding_owner_id"
|
55
|
+
t.string "name"
|
56
|
+
end
|
57
|
+
|
58
|
+
create_table "sharded_joins", :force => true do |t|
|
59
|
+
t.integer "sharding_owner_id"
|
60
|
+
t.integer "non_sharding_owner_id"
|
61
|
+
t.integer "sharded_has_many_through_id"
|
62
|
+
end
|
63
|
+
|
64
|
+
create_table "sharded_has_many_throughs", :force => true do |t|
|
65
|
+
t.string "name"
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Dynashard' do
|
4
|
+
it 'can be enabled' do
|
5
|
+
Dynashard.should respond_to(:enable)
|
6
|
+
Dynashard.enable
|
7
|
+
Dynashard.should be_enabled
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'can be disabled' do
|
11
|
+
Dynashard.should respond_to(:disable)
|
12
|
+
Dynashard.disable
|
13
|
+
Dynashard.should_not be_enabled
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#shard_context' do
|
17
|
+
it 'is threadsafe' do
|
18
|
+
Dynashard.shard_context[:test] = true
|
19
|
+
t = Thread.new {Dynashard.shard_context[:test] = false}
|
20
|
+
t.join
|
21
|
+
Dynashard.shard_context[:test].should be_true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#class_for' do
|
26
|
+
before(:each) do
|
27
|
+
@shard_klass = Dynashard.class_for('shard1')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns a class' do
|
31
|
+
@shard_klass.should be_a(Class)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has an established connection' do
|
35
|
+
ActiveRecord::Base.connection_handler.connection_pools.should have_key(@shard_klass.name)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'does not regenerate existing classes' do
|
39
|
+
other_klass = Dynashard.class_for('shard1')
|
40
|
+
other_klass.should == @shard_klass
|
41
|
+
end
|
42
|
+
|
43
|
+
# Ie. from config/database.yml
|
44
|
+
it 'accepts a string reference to a configured database' do
|
45
|
+
lambda do
|
46
|
+
Dynashard.class_for('shard2')
|
47
|
+
end.should_not raise_error
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'accepts a hash with valid database connection parameters' do
|
51
|
+
lambda do
|
52
|
+
Dynashard.class_for({:adapter => 'sqlite3', :database => 'db/shard3.sqlite3'})
|
53
|
+
end.should_not raise_error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#reflection_for' do
|
58
|
+
before(:each) do
|
59
|
+
@owner = Factory(:sharding_owner)
|
60
|
+
@reflection = Dynashard.reflection_for(@owner, ShardingOwner.reflections[:sharded_has_one])
|
61
|
+
@shard_klass = Dynashard.class_for(@owner.shard_dsn)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns a reflection' do
|
65
|
+
@reflection.should be_kind_of(ActiveRecord::Reflection::MacroReflection)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'has a sharded class' do
|
69
|
+
@reflection.klass.should respond_to(:dynashard_klass)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#sharded_model_class' do
|
74
|
+
before(:each) do
|
75
|
+
@owner = Factory(:sharding_owner)
|
76
|
+
@reflection = Dynashard.reflection_for(@owner, ShardingOwner.reflections[:sharded_has_one])
|
77
|
+
@shard_klass = Dynashard.class_for(@owner.shard_dsn)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'identifies as a sharded model' do
|
81
|
+
@reflection.klass.should respond_to(:dynashard_model?)
|
82
|
+
@reflection.klass.dynashard_model?.should be_true
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'returns the shard class' do
|
86
|
+
@reflection.klass.dynashard_klass.should == @shard_klass
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'returns the shard class connection' do
|
90
|
+
@reflection.klass.connection.should == @shard_klass.connection
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'are not regenerated' do
|
94
|
+
other_reflection = Dynashard.reflection_for(@owner, ShardingOwner.reflections[:sharded_has_one])
|
95
|
+
other_reflection.klass.should == @reflection.klass
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#with_context' do
|
100
|
+
before(:each) do
|
101
|
+
@test_context = {:foo => 'shard1'}
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'sets the shard context for the given block' do
|
105
|
+
Dynashard.with_context(@test_context) do
|
106
|
+
Dynashard.shard_context[:foo].should == 'shard1'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'resets the shard context' do
|
111
|
+
Dynashard.with_context(@test_context) {}
|
112
|
+
Dynashard.shard_context.should_not have_key(:foo)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns the block result' do
|
116
|
+
Dynashard.with_context(@test_context) { 'foo' }.should == 'foo'
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when the block raises an exception' do
|
120
|
+
it 'resets the shard context' do
|
121
|
+
Dynashard.with_context(@test_context) { raise 'Aah!' } rescue nil
|
122
|
+
Dynashard.shard_context.should_not have_key(:foo)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ActiveRecord Models' do
|
4
|
+
|
5
|
+
context 'with invalid sharding arguments' do
|
6
|
+
after(:each) do
|
7
|
+
Object.send(:remove_const, :DynashardTestClass) if Object.const_defined?(:DynashardTestClass)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'for association owners' do
|
11
|
+
it 'raises an exception' do
|
12
|
+
lambda do
|
13
|
+
class DynashardTestClass < ActiveRecord::Base
|
14
|
+
shard :associated, :without_using_arg => true
|
15
|
+
end
|
16
|
+
end.should raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'for sharded models' do
|
21
|
+
it 'raises an exception' do
|
22
|
+
lambda do
|
23
|
+
class DynashardTestClass < ActiveRecord::Base
|
24
|
+
shard :without_using_by => true
|
25
|
+
end
|
26
|
+
end.should raise_error(ArgumentError)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with Dynashard disabled' do
|
32
|
+
before(:each) do
|
33
|
+
Dynashard.disable
|
34
|
+
end
|
35
|
+
|
36
|
+
after(:each) do
|
37
|
+
Dynashard.enable
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'uses the ActiveRecord::Base connection' do
|
41
|
+
ShardedHasOne.connection.should == ActiveRecord::Base.connection
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'do not shard' do
|
45
|
+
ShardedHasOne.sharding_enabled?.should be_false
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'do not shard associations' do
|
49
|
+
ShardingOwner.shards_associated?.should be_false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with Dynashard enabled' do
|
54
|
+
before(:each) do
|
55
|
+
Dynashard.enable
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'can be sharded' do
|
59
|
+
ShardedHasOne.sharding_enabled?.should be_true
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'can have sharded associations' do
|
63
|
+
ShardingOwner.shards_associated?.should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'shards associations using a specified arg' do
|
67
|
+
class DynashardTestClass < ActiveRecord::Base
|
68
|
+
shard :associated, :using => :foo
|
69
|
+
end
|
70
|
+
DynashardTestClass.dynashard_association_using.should == :foo
|
71
|
+
Object.send(:remove_const, :DynashardTestClass)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'shards models using a specified context' do
|
75
|
+
class DynashardTestClass < ActiveRecord::Base
|
76
|
+
shard :by => :foo
|
77
|
+
end
|
78
|
+
DynashardTestClass.dynashard_context.should == :foo
|
79
|
+
Object.send(:remove_const, :DynashardTestClass)
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'and no shard context defined' do
|
83
|
+
it 'raises an exception' do
|
84
|
+
lambda do
|
85
|
+
ShardedHasOne.connection
|
86
|
+
end.should raise_error
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'and the shard context defined' do
|
91
|
+
it 'uses the sharded connection' do
|
92
|
+
test_shard = 'shard1'
|
93
|
+
Dynashard.with_context(:owner => test_shard) do
|
94
|
+
shard_class = Dynashard.class_for(test_shard)
|
95
|
+
shard_config = shard_class.connection.instance_variable_get('@config')
|
96
|
+
ar_config = ActiveRecord::Base.connection.instance_variable_get('@config')
|
97
|
+
model_config = ShardedHasOne.connection.instance_variable_get('@config')
|
98
|
+
model_config.should_not == ar_config
|
99
|
+
model_config.should == shard_config
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
plugin_test_dir = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'erb'
|
7
|
+
|
8
|
+
require 'rspec'
|
9
|
+
require 'logger'
|
10
|
+
|
11
|
+
require 'active_record'
|
12
|
+
require 'factory_girl_rails'
|
13
|
+
|
14
|
+
require 'dynashard'
|
15
|
+
|
16
|
+
ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log")
|
17
|
+
|
18
|
+
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(plugin_test_dir + "/db/database.yml")).result(binding))
|
19
|
+
ActiveRecord::Base.establish_connection("test")
|
20
|
+
ActiveRecord::Migration.verbose = false
|
21
|
+
|
22
|
+
test_database = ActiveRecord::Base.configurations['test']['database']
|
23
|
+
File.unlink(test_database) if File.exists?(test_database)
|
24
|
+
load(File.join(plugin_test_dir, "db", "schema.rb"))
|
25
|
+
|
26
|
+
# create shards and databases that they point to
|
27
|
+
base_config = ActiveRecord::Base.configurations['test']
|
28
|
+
%w{shard1 shard2 shard3}.each do |shard|
|
29
|
+
database = "#{plugin_test_dir}/db/#{shard}.sqlite3"
|
30
|
+
File.unlink(database) if File.exists?(database)
|
31
|
+
shard_config = base_config.merge('database' => database)
|
32
|
+
ActiveRecord::Base.configurations['test'] = shard_config
|
33
|
+
ActiveRecord::Base.establish_connection("test")
|
34
|
+
load(File.join(plugin_test_dir, "db", "schema.rb"))
|
35
|
+
end
|
36
|
+
ActiveRecord::Base.configurations['test'] = base_config
|
37
|
+
ActiveRecord::Base.establish_connection("test")
|
38
|
+
|
39
|
+
require 'support/models'
|
40
|
+
|
41
|
+
%w{shard1 shard2 shard3}.each do |shard|
|
42
|
+
Shard.create(:adapter => 'sqlite3', :database => "#{plugin_test_dir}/db/#{shard}.sqlite3")
|
43
|
+
end
|
44
|
+
|
45
|
+
# This has to happen after the models have been defined and the shards have been created
|
46
|
+
require 'support/factories'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Sequence for generating unique names
|
2
|
+
Factory.sequence :name do |n|
|
3
|
+
"Test Owner #{n}"
|
4
|
+
end
|
5
|
+
|
6
|
+
Factory.define(:sharding_owner) do |owner|
|
7
|
+
owner.name {Factory.next :name}
|
8
|
+
owner.shard Shard.find(:first)
|
9
|
+
end
|
10
|
+
|
11
|
+
Factory.define(:non_sharding_owner) do |owner|
|
12
|
+
owner.name {Factory.next :name}
|
13
|
+
end
|
14
|
+
|
15
|
+
Factory.define(:sharded_has_one) do |one|
|
16
|
+
one.name {Factory.next :name}
|
17
|
+
one.sharding_owner
|
18
|
+
end
|
19
|
+
|
20
|
+
Factory.define(:sharded_has_many) do |one_of_many|
|
21
|
+
one_of_many.name {Factory.next :name}
|
22
|
+
one_of_many.sharding_owner
|
23
|
+
end
|
24
|
+
|
25
|
+
Factory.define(:sharded_join) do |join|
|
26
|
+
join.sharding_owner
|
27
|
+
join.sharded_has_many_through
|
28
|
+
end
|
29
|
+
|
30
|
+
Factory.define(:sharded_has_many_through) do |one_of_many|
|
31
|
+
one_of_many.name {Factory.next :name}
|
32
|
+
end
|