sequel 3.11.0 → 3.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +70 -0
- data/Rakefile +1 -1
- data/doc/active_record.rdoc +896 -0
- data/doc/advanced_associations.rdoc +46 -31
- data/doc/association_basics.rdoc +14 -9
- data/doc/dataset_basics.rdoc +3 -3
- data/doc/migration.rdoc +1011 -0
- data/doc/model_hooks.rdoc +198 -0
- data/doc/querying.rdoc +811 -86
- data/doc/release_notes/3.12.0.txt +304 -0
- data/doc/sharding.rdoc +17 -0
- data/doc/sql.rdoc +537 -0
- data/doc/validations.rdoc +501 -0
- data/lib/sequel/adapters/jdbc.rb +19 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -7
- data/lib/sequel/adapters/mysql.rb +5 -4
- data/lib/sequel/adapters/odbc.rb +3 -2
- data/lib/sequel/adapters/shared/mssql.rb +7 -6
- data/lib/sequel/adapters/shared/mysql.rb +2 -7
- data/lib/sequel/adapters/shared/postgres.rb +2 -8
- data/lib/sequel/adapters/shared/sqlite.rb +2 -5
- data/lib/sequel/adapters/sqlite.rb +4 -4
- data/lib/sequel/core.rb +0 -1
- data/lib/sequel/database.rb +2 -1060
- data/lib/sequel/database/connecting.rb +227 -0
- data/lib/sequel/database/dataset.rb +58 -0
- data/lib/sequel/database/dataset_defaults.rb +127 -0
- data/lib/sequel/database/logging.rb +62 -0
- data/lib/sequel/database/misc.rb +246 -0
- data/lib/sequel/database/query.rb +390 -0
- data/lib/sequel/database/schema_generator.rb +7 -3
- data/lib/sequel/database/schema_methods.rb +351 -7
- data/lib/sequel/dataset/actions.rb +9 -2
- data/lib/sequel/dataset/misc.rb +6 -2
- data/lib/sequel/dataset/mutation.rb +3 -11
- data/lib/sequel/dataset/query.rb +49 -6
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/migration.rb +395 -113
- data/lib/sequel/extensions/schema_dumper.rb +21 -13
- data/lib/sequel/model.rb +27 -25
- data/lib/sequel/model/associations.rb +72 -34
- data/lib/sequel/model/base.rb +74 -18
- data/lib/sequel/model/errors.rb +8 -1
- data/lib/sequel/plugins/active_model.rb +8 -0
- data/lib/sequel/plugins/association_pks.rb +87 -0
- data/lib/sequel/plugins/association_proxies.rb +8 -0
- data/lib/sequel/plugins/boolean_readers.rb +12 -6
- data/lib/sequel/plugins/caching.rb +14 -7
- data/lib/sequel/plugins/class_table_inheritance.rb +15 -9
- data/lib/sequel/plugins/composition.rb +2 -1
- data/lib/sequel/plugins/force_encoding.rb +10 -7
- data/lib/sequel/plugins/hook_class_methods.rb +12 -11
- data/lib/sequel/plugins/identity_map.rb +9 -0
- data/lib/sequel/plugins/instance_hooks.rb +23 -13
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +18 -4
- data/lib/sequel/plugins/nested_attributes.rb +1 -0
- data/lib/sequel/plugins/optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +9 -8
- data/lib/sequel/plugins/schema.rb +8 -0
- data/lib/sequel/plugins/serialization.rb +1 -3
- data/lib/sequel/plugins/sharding.rb +135 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +117 -25
- data/lib/sequel/plugins/skip_create_refresh.rb +35 -0
- data/lib/sequel/plugins/string_stripper.rb +26 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -0
- data/lib/sequel/plugins/timestamps.rb +15 -2
- data/lib/sequel/plugins/touch.rb +13 -0
- data/lib/sequel/plugins/update_primary_key.rb +48 -0
- data/lib/sequel/plugins/validation_class_methods.rb +8 -0
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/sql.rb +17 -20
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +5 -5
- data/spec/core/core_sql_spec.rb +17 -1
- data/spec/core/database_spec.rb +17 -5
- data/spec/core/dataset_spec.rb +31 -8
- data/spec/core/schema_generator_spec.rb +8 -1
- data/spec/core/schema_spec.rb +13 -0
- data/spec/extensions/association_pks_spec.rb +85 -0
- data/spec/extensions/hook_class_methods_spec.rb +9 -9
- data/spec/extensions/migration_spec.rb +339 -219
- data/spec/extensions/schema_dumper_spec.rb +28 -17
- data/spec/extensions/sharding_spec.rb +272 -0
- data/spec/extensions/single_table_inheritance_spec.rb +92 -4
- data/spec/extensions/skip_create_refresh_spec.rb +17 -0
- data/spec/extensions/string_stripper_spec.rb +23 -0
- data/spec/extensions/update_primary_key_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +5 -5
- data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
- data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
- data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
- data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
- data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
- data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +4 -0
- data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
- data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
- data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
- data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
- data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
- data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +4 -0
- data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
- data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
- data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
- data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +9 -0
- data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +9 -0
- data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +4 -0
- data/spec/integration/eager_loader_test.rb +20 -20
- data/spec/integration/migrator_test.rb +187 -0
- data/spec/integration/plugin_test.rb +150 -0
- data/spec/integration/schema_test.rb +13 -2
- data/spec/model/associations_spec.rb +41 -14
- data/spec/model/base_spec.rb +69 -0
- data/spec/model/eager_loading_spec.rb +7 -3
- data/spec/model/record_spec.rb +79 -4
- data/spec/model/validations_spec.rb +21 -9
- metadata +66 -5
- data/doc/schema.rdoc +0 -36
- data/lib/sequel/database/schema_sql.rb +0 -320
@@ -69,7 +69,7 @@ describe "Sequel::Database dump methods" do
|
|
69
69
|
@d.meta_def(:tables){|o| [:t1, :t2]}
|
70
70
|
@d.meta_def(:schema) do |t, *o|
|
71
71
|
case t
|
72
|
-
when :t1
|
72
|
+
when :t1, 't__t1', :t__t1.identifier
|
73
73
|
[[:c1, {:db_type=>'integer', :primary_key=>true, :allow_null=>false}],
|
74
74
|
[:c2, {:db_type=>'varchar(20)', :allow_null=>true}]]
|
75
75
|
when :t2
|
@@ -92,6 +92,14 @@ describe "Sequel::Database dump methods" do
|
|
92
92
|
@d.dump_table_schema(:t1).should == "create_table(:t1) do\n primary_key :c1\n String :c2, :size=>20\nend"
|
93
93
|
end
|
94
94
|
|
95
|
+
it "should support dumping table schemas when given a string" do
|
96
|
+
@d.dump_table_schema('t__t1').should == "create_table(\"t__t1\") do\n primary_key :c1\n String :c2, :size=>20\nend"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should support dumping table schemas when given an identifier" do
|
100
|
+
@d.dump_table_schema(:t__t1.identifier).should == "create_table(\"t__t1\") do\n primary_key :c1\n String :c2, :size=>20\nend"
|
101
|
+
end
|
102
|
+
|
95
103
|
it "should dump non-Integer primary key columns with explicit :type" do
|
96
104
|
@d.dump_table_schema(:t6).should == "create_table(:t6) do\n primary_key :c1, :type=>Bignum\nend"
|
97
105
|
end
|
@@ -114,8 +122,8 @@ describe "Sequel::Database dump methods" do
|
|
114
122
|
|
115
123
|
it "should support dumping the whole database as a migration" do
|
116
124
|
@d.dump_schema_migration.should == <<-END_MIG
|
117
|
-
|
118
|
-
|
125
|
+
Sequel.migration do
|
126
|
+
up do
|
119
127
|
create_table(:t1) do
|
120
128
|
primary_key :c1
|
121
129
|
String :c2, :size=>20
|
@@ -129,7 +137,7 @@ Class.new(Sequel::Migration) do
|
|
129
137
|
end
|
130
138
|
end
|
131
139
|
|
132
|
-
|
140
|
+
down do
|
133
141
|
drop_table(:t1, :t2)
|
134
142
|
end
|
135
143
|
end
|
@@ -139,8 +147,8 @@ END_MIG
|
|
139
147
|
it "should sort table names when dumping a migration" do
|
140
148
|
@d.meta_def(:tables){|o| [:t2, :t1]}
|
141
149
|
@d.dump_schema_migration.should == <<-END_MIG
|
142
|
-
|
143
|
-
|
150
|
+
Sequel.migration do
|
151
|
+
up do
|
144
152
|
create_table(:t1) do
|
145
153
|
primary_key :c1
|
146
154
|
String :c2, :size=>20
|
@@ -154,7 +162,7 @@ Class.new(Sequel::Migration) do
|
|
154
162
|
end
|
155
163
|
end
|
156
164
|
|
157
|
-
|
165
|
+
down do
|
158
166
|
drop_table(:t1, :t2)
|
159
167
|
end
|
160
168
|
end
|
@@ -164,8 +172,8 @@ END_MIG
|
|
164
172
|
it "should honor the :same_db option to not convert types" do
|
165
173
|
@d.dump_table_schema(:t1, :same_db=>true).should == "create_table(:t1) do\n primary_key :c1\n column :c2, \"varchar(20)\"\nend"
|
166
174
|
@d.dump_schema_migration(:same_db=>true).should == <<-END_MIG
|
167
|
-
|
168
|
-
|
175
|
+
Sequel.migration do
|
176
|
+
up do
|
169
177
|
create_table(:t1) do
|
170
178
|
primary_key :c1
|
171
179
|
column :c2, "varchar(20)"
|
@@ -179,7 +187,7 @@ Class.new(Sequel::Migration) do
|
|
179
187
|
end
|
180
188
|
end
|
181
189
|
|
182
|
-
|
190
|
+
down do
|
183
191
|
drop_table(:t1, :t2)
|
184
192
|
end
|
185
193
|
end
|
@@ -193,8 +201,8 @@ END_MIG
|
|
193
201
|
end
|
194
202
|
@d.dump_table_schema(:t1, :indexes=>false).should == "create_table(:t1) do\n primary_key :c1\n String :c2, :size=>20\nend"
|
195
203
|
@d.dump_schema_migration(:indexes=>false).should == <<-END_MIG
|
196
|
-
|
197
|
-
|
204
|
+
Sequel.migration do
|
205
|
+
up do
|
198
206
|
create_table(:t1) do
|
199
207
|
primary_key :c1
|
200
208
|
String :c2, :size=>20
|
@@ -208,7 +216,7 @@ Class.new(Sequel::Migration) do
|
|
208
216
|
end
|
209
217
|
end
|
210
218
|
|
211
|
-
|
219
|
+
down do
|
212
220
|
drop_table(:t1, :t2)
|
213
221
|
end
|
214
222
|
end
|
@@ -222,13 +230,13 @@ END_MIG
|
|
222
230
|
:t1_c2_c1_index=>{:columns=>[:c2, :c1], :unique=>true}}
|
223
231
|
end
|
224
232
|
@d.dump_indexes_migration.should == <<-END_MIG
|
225
|
-
|
226
|
-
|
233
|
+
Sequel.migration do
|
234
|
+
up do
|
227
235
|
add_index :t1, [:c1], :ignore_errors=>true, :name=>:i1
|
228
236
|
add_index :t1, [:c2, :c1], :ignore_errors=>true, :unique=>true
|
229
237
|
end
|
230
238
|
|
231
|
-
|
239
|
+
down do
|
232
240
|
drop_index :t1, [:c1], :ignore_errors=>true, :name=>:i1
|
233
241
|
drop_index :t1, [:c2, :c1], :ignore_errors=>true, :unique=>true
|
234
242
|
end
|
@@ -282,7 +290,8 @@ END_MIG
|
|
282
290
|
blob varbinary varbinary(10) binary binary(20) year" +
|
283
291
|
["double precision", "timestamp with time zone", "timestamp without time zone",
|
284
292
|
"time with time zone", "time without time zone", "character varying(20)"] +
|
285
|
-
%w"nvarchar ntext smalldatetime smallmoney binary varbinary nchar"
|
293
|
+
%w"nvarchar ntext smalldatetime smallmoney binary varbinary nchar" +
|
294
|
+
["timestamp(6) without time zone", "timestamp(6) with time zone"]
|
286
295
|
@d.meta_def(:schema) do |t, *o|
|
287
296
|
i = 0
|
288
297
|
types.map{|x| [:"c#{i+=1}", {:db_type=>x, :allow_null=>true}]}
|
@@ -350,6 +359,8 @@ create_table(:x) do
|
|
350
359
|
File :c59
|
351
360
|
File :c60
|
352
361
|
String :c61, :fixed=>true
|
362
|
+
DateTime :c62, :size=>6
|
363
|
+
DateTime :c63, :size=>6
|
353
364
|
end
|
354
365
|
END_MIG
|
355
366
|
@d.dump_table_schema(:x).should == table.chomp
|
@@ -0,0 +1,272 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "sharding plugin" do
|
4
|
+
before do
|
5
|
+
@db = Sequel::Model.db.clone
|
6
|
+
@Artist = Class.new(Sequel::Model(@db[:artists]))
|
7
|
+
@Artist.class_eval do
|
8
|
+
columns :id, :name
|
9
|
+
|
10
|
+
def self.y
|
11
|
+
{:id=>2, :name=>'YJM'}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
@Album = Class.new(Sequel::Model(@db[:albums]))
|
15
|
+
@Album.class_eval do
|
16
|
+
columns :id, :artist_id, :name
|
17
|
+
|
18
|
+
def self.ds_ext(m=nil)
|
19
|
+
@ds_ext = m if m
|
20
|
+
@ds_ext
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.y
|
24
|
+
{:id=>1, :name=>'RF', :artist_id=>2}
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def _join_table_dataset(opts)
|
30
|
+
ds = super
|
31
|
+
m = model
|
32
|
+
ds.meta_def(:model){m}
|
33
|
+
ds.extend model.ds_ext
|
34
|
+
ds
|
35
|
+
end
|
36
|
+
end
|
37
|
+
@Tag = Class.new(Sequel::Model(@db[:tags]))
|
38
|
+
@Tag.class_eval do
|
39
|
+
columns :id, :name
|
40
|
+
|
41
|
+
def self.y
|
42
|
+
{:id=>3, :name=>'M'}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
models = [@Artist, @Album, @Tag]
|
46
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
|
47
|
+
@Album.many_to_one :artist, :class=>@Artist
|
48
|
+
@Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:albums_tags
|
49
|
+
m = Module.new do
|
50
|
+
def actions
|
51
|
+
@actions ||= []
|
52
|
+
end
|
53
|
+
end
|
54
|
+
models.each do |model|
|
55
|
+
model.extend m
|
56
|
+
model.plugin :sharding
|
57
|
+
model.dataset.extend(ds_ext = Module.new do
|
58
|
+
def insert(h={})
|
59
|
+
model.actions << [:insert, h.dup, opts[:server]]
|
60
|
+
1
|
61
|
+
end
|
62
|
+
def delete
|
63
|
+
model.actions << [:delete,(literal(opts[:where]) if opts[:where]), opts[:server]]
|
64
|
+
1
|
65
|
+
end
|
66
|
+
def update(h={})
|
67
|
+
model.actions << [:update, h.dup, (literal(opts[:where]) if opts[:where]), opts[:server]]
|
68
|
+
1
|
69
|
+
end
|
70
|
+
def fetch_rows(sql)
|
71
|
+
model.actions << [:fetch, (literal(opts[:where] || opts[:join]) if opts[:where] || opts[:join]), opts[:server]]
|
72
|
+
yield(model.y)
|
73
|
+
end
|
74
|
+
end)
|
75
|
+
@Album.ds_ext(ds_ext)
|
76
|
+
end
|
77
|
+
def @db.actions; @actions ||= []; end
|
78
|
+
def @db.transaction(opts)
|
79
|
+
actions << [:transaction, opts[:server]]
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
specify "should allow you to instantiate a new object for a specified shard" do
|
85
|
+
@Album.new_using_server(:s1, :name=>'RF').save
|
86
|
+
@Album.actions.should == [[:insert, {:name=>"RF"}, :s1], [:fetch, "(id = 1)", :s1]]
|
87
|
+
|
88
|
+
@Album.actions.clear
|
89
|
+
@Album.new_using_server(:s2){|o| o.name = 'MO'}.save
|
90
|
+
@Album.actions.should == [[:insert, {:name=>"MO"}, :s2], [:fetch, "(id = 1)", :s2]]
|
91
|
+
end
|
92
|
+
|
93
|
+
specify "should allow you to create and save a new object for a specified shard" do
|
94
|
+
@Album.create_using_server(:s1, :name=>'RF')
|
95
|
+
@Album.actions.should == [[:insert, {:name=>"RF"}, :s1], [:fetch, "(id = 1)", :s1]]
|
96
|
+
|
97
|
+
@Album.actions.clear
|
98
|
+
@Album.create_using_server(:s2){|o| o.name = 'MO'}
|
99
|
+
@Album.actions.should == [[:insert, {:name=>"MO"}, :s2], [:fetch, "(id = 1)", :s2]]
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "should have objects retrieved from a specific shard update that shard" do
|
103
|
+
@Album.server(:s1).first.update(:name=>'MO')
|
104
|
+
@Album.actions.should == [[:fetch, nil, :s1], [:update, {:name=>"MO"}, "(id = 1)", :s1]]
|
105
|
+
end
|
106
|
+
|
107
|
+
specify "should have objects retrieved from a specific shard delete from that shard" do
|
108
|
+
@Album.server(:s1).first.delete
|
109
|
+
@Album.actions.should == [[:fetch, nil, :s1], [:delete, "(id = 1)", :s1]]
|
110
|
+
end
|
111
|
+
|
112
|
+
specify "should have objects retrieved from a specific shard reload from that shard" do
|
113
|
+
@Album.server(:s1).first.reload
|
114
|
+
@Album.actions.should == [[:fetch, nil, :s1], [:fetch, "(id = 1)", :s1]]
|
115
|
+
end
|
116
|
+
|
117
|
+
specify "should use current dataset's shard when eager loading if eagerly loaded dataset doesn't have its own shard" do
|
118
|
+
albums = @Album.server(:s1).eager(:artist).all
|
119
|
+
@Album.actions.should == [[:fetch, nil, :s1]]
|
120
|
+
@Artist.actions.should == [[:fetch, "(artists.id IN (2))", :s1]]
|
121
|
+
@Artist.actions.clear
|
122
|
+
albums.length == 1
|
123
|
+
albums.first.artist.save
|
124
|
+
@Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s1]]
|
125
|
+
end
|
126
|
+
|
127
|
+
specify "should not use current dataset's shard when eager loading if eagerly loaded dataset has its own shard" do
|
128
|
+
@Artist.dataset.opts[:server] = :s2
|
129
|
+
albums = @Album.server(:s1).eager(:artist).all
|
130
|
+
@Album.actions.should == [[:fetch, nil, :s1]]
|
131
|
+
@Artist.actions.should == [[:fetch, "(artists.id IN (2))", :s2]]
|
132
|
+
@Artist.actions.clear
|
133
|
+
albums.length == 1
|
134
|
+
albums.first.artist.save
|
135
|
+
@Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s2]]
|
136
|
+
end
|
137
|
+
|
138
|
+
specify "should use current dataset's shard when eager graphing if eagerly graphed dataset doesn't have its own shard" do
|
139
|
+
ds = @Album.server(:s1).eager_graph(:artist)
|
140
|
+
def ds.fetch_rows(sql)
|
141
|
+
super(sql)
|
142
|
+
yield({:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'})
|
143
|
+
end
|
144
|
+
albums = ds.all
|
145
|
+
@Album.actions.should == [[:fetch, "( LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id))", :s1]]
|
146
|
+
albums.length == 1
|
147
|
+
albums.first.artist.save
|
148
|
+
@Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s1]]
|
149
|
+
end
|
150
|
+
|
151
|
+
specify "should not use current dataset's shard when eager graphing if eagerly graphed dataset has its own shard" do
|
152
|
+
@Artist.dataset.opts[:server] = :s2
|
153
|
+
ds = @Album.server(:s1).eager_graph(:artist)
|
154
|
+
def ds.fetch_rows(sql)
|
155
|
+
super(sql)
|
156
|
+
yield({:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'})
|
157
|
+
end
|
158
|
+
albums = ds.all
|
159
|
+
@Album.actions.should == [[:fetch, "( LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id))", :s1]]
|
160
|
+
albums.length == 1
|
161
|
+
albums.first.artist.save
|
162
|
+
@Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s2]]
|
163
|
+
end
|
164
|
+
|
165
|
+
specify "should use eagerly graphed dataset shard for eagerly graphed objects even if current dataset does not have a shard" do
|
166
|
+
@Artist.dataset.opts[:server] = :s2
|
167
|
+
ds = @Album.eager_graph(:artist)
|
168
|
+
def ds.fetch_rows(sql)
|
169
|
+
super(sql)
|
170
|
+
yield({:id=>1, :artist_id=>2, :name=>'RF', :artist_id_0=>2, :artist_name=>'YJM'})
|
171
|
+
end
|
172
|
+
albums = ds.all
|
173
|
+
@Album.actions.should == [[:fetch, "( LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id))", nil]]
|
174
|
+
albums.length == 1
|
175
|
+
albums.first.artist.save
|
176
|
+
@Artist.actions.should == [[:update, {:name=>"YJM"}, "(id = 2)", :s2]]
|
177
|
+
end
|
178
|
+
|
179
|
+
specify "should have objects retrieved from a specific shard use associated objects from that shard, with modifications to the associated objects using that shard" do
|
180
|
+
album = @Album.server(:s1).first
|
181
|
+
@Album.actions.should == [[:fetch, nil, :s1]]
|
182
|
+
album.artist.update(:name=>'AS')
|
183
|
+
@Artist.actions.should == [[:fetch, "(artists.id = 2)", :s1], [:update, {:name=>"AS"}, "(id = 2)", :s1]]
|
184
|
+
album.tags.map{|a| a.update(:name=>'SR')}
|
185
|
+
@Tag.actions.should == [[:fetch, "( INNER JOIN albums_tags ON ((albums_tags.tag_id = tags.id) AND (albums_tags.album_id = 1)))", :s1], [:update, {:name=>"SR"}, "(id = 3)", :s1]]
|
186
|
+
|
187
|
+
@Album.actions.clear
|
188
|
+
@Artist.actions.clear
|
189
|
+
@Artist.server(:s2).first.albums.map{|a| a.update(:name=>'MO')}
|
190
|
+
@Artist.actions.should == [[:fetch, nil, :s2]]
|
191
|
+
@Album.actions.should == [[:fetch, "(albums.artist_id = 2)", :s2], [:update, {:name=>"MO"}, "(id = 1)", :s2]]
|
192
|
+
end
|
193
|
+
|
194
|
+
specify "should have objects retrieved from a specific shard add associated objects to that shard" do
|
195
|
+
album = @Album.server(:s1).first
|
196
|
+
artist = @Artist.server(:s2).first
|
197
|
+
@Album.actions.clear
|
198
|
+
@Artist.actions.clear
|
199
|
+
|
200
|
+
artist.add_album(:name=>'MO')
|
201
|
+
@Album.actions.should == [[:insert, {:name=>"MO", :artist_id=>2}, :s2], [:fetch, "(id = 1)", :s2]]
|
202
|
+
@Album.actions.clear
|
203
|
+
|
204
|
+
album.add_tag(:name=>'SR')
|
205
|
+
@Tag.actions.should == [[:insert, {:name=>"SR"}, :s1], [:fetch, "(id = 1)", :s1]]
|
206
|
+
@Album.actions.should == [[:insert, {:album_id=>1, :tag_id=>3}, :s1]]
|
207
|
+
end
|
208
|
+
|
209
|
+
specify "should have objects retrieved from a specific shard remove associated objects from that shard" do
|
210
|
+
album = @Album.server(:s1).first
|
211
|
+
artist = @Artist.server(:s2).first
|
212
|
+
@Album.actions.clear
|
213
|
+
@Artist.actions.clear
|
214
|
+
|
215
|
+
artist.remove_album(1)
|
216
|
+
@Album.actions.should == [[:fetch, "((albums.artist_id = 2) AND (albums.id = 1))", :s2], [:update, {:name=>"RF", :artist_id=>nil}, "(id = 1)", :s2]]
|
217
|
+
@Album.actions.clear
|
218
|
+
|
219
|
+
album.remove_tag(3)
|
220
|
+
@Tag.actions.should == [[:fetch, "(tags.id = 3)", :s1]]
|
221
|
+
@Album.actions.should == [[:delete, "((album_id = 1) AND (tag_id = 3))", :s1]]
|
222
|
+
end
|
223
|
+
|
224
|
+
specify "should have objects retrieved from a specific shard remove all associated objects from that shard" do
|
225
|
+
album = @Album.server(:s1).first
|
226
|
+
artist = @Artist.server(:s2).first
|
227
|
+
@Album.actions.clear
|
228
|
+
@Artist.actions.clear
|
229
|
+
|
230
|
+
artist.remove_all_albums
|
231
|
+
@Album.actions.should == [[:update, {:artist_id=>nil}, "(artist_id = 2)", :s2]]
|
232
|
+
@Album.actions.clear
|
233
|
+
|
234
|
+
album.remove_all_tags
|
235
|
+
@Album.actions.should == [[:delete, "(album_id = 1)", :s1]]
|
236
|
+
end
|
237
|
+
|
238
|
+
specify "should not override a server already set on an associated object" do
|
239
|
+
album = @Album.server(:s1).first
|
240
|
+
artist = @Artist.server(:s2).first
|
241
|
+
@Album.actions.clear
|
242
|
+
@Artist.actions.clear
|
243
|
+
|
244
|
+
artist.add_album(@Album.load(:id=>4, :name=>'MO').set_server(:s3))
|
245
|
+
@Album.actions.should == [[:update, {:name=>"MO", :artist_id=>2}, "(id = 4)", :s3]]
|
246
|
+
@Album.actions.clear
|
247
|
+
|
248
|
+
artist.remove_album(@Album.load(:id=>5, :name=>'T', :artist_id=>2).set_server(:s4))
|
249
|
+
# Should select from current object's shard to check existing association, but update associated object's shard
|
250
|
+
@Album.actions.should == [[:fetch, "((albums.artist_id = 2) AND (id = 5))", :s2], [:update, {:name=>"T", :artist_id=>nil}, "(id = 5)", :s4]]
|
251
|
+
@Album.actions.clear
|
252
|
+
end
|
253
|
+
|
254
|
+
specify "should be able to set a shard to use for any object using set_server" do
|
255
|
+
@Album.server(:s1).first.set_server(:s2).reload
|
256
|
+
@Album.actions.should == [[:fetch, nil, :s1], [:fetch, "(id = 1)", :s2]]
|
257
|
+
end
|
258
|
+
|
259
|
+
specify "should use transactions on the correct shard" do
|
260
|
+
@Album.use_transactions = true
|
261
|
+
@Album.server(:s2).first.save
|
262
|
+
@Album.actions.should == [[:fetch, nil, :s2], [:update, {:name=>"RF", :artist_id=>2}, "(id = 1)", :s2]]
|
263
|
+
@db.actions.should == [[:transaction, :s2]]
|
264
|
+
end
|
265
|
+
|
266
|
+
specify "should use not override shard given when saving" do
|
267
|
+
@Album.use_transactions = true
|
268
|
+
@Album.server(:s2).first.save(:server=>:s1)
|
269
|
+
@Album.actions.should == [[:fetch, nil, :s2], [:update, {:name=>"RF", :artist_id=>2}, "(id = 1)", :s2]]
|
270
|
+
@db.actions.should == [[:transaction, :s1]]
|
271
|
+
end
|
272
|
+
end
|
@@ -41,8 +41,8 @@ describe Sequel::Model, "#sti_key" do
|
|
41
41
|
end
|
42
42
|
StiTest.all.collect{|x| x.class}.should == [StiTest, StiTestSub1, StiTestSub2]
|
43
43
|
StiTest.dataset.sql.should == "SELECT * FROM sti_tests"
|
44
|
-
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.blah
|
45
|
-
StiTestSub2.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.blah
|
44
|
+
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.blah IN ('StiTestSub1'))"
|
45
|
+
StiTestSub2.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.blah IN ('StiTestSub2'))"
|
46
46
|
end
|
47
47
|
|
48
48
|
it "should return rows with the correct class based on the polymorphic_key value" do
|
@@ -90,7 +90,95 @@ describe Sequel::Model, "#sti_key" do
|
|
90
90
|
|
91
91
|
it "should add a filter to model datasets inside subclasses hook to only retreive objects with the matching key" do
|
92
92
|
StiTest.dataset.sql.should == "SELECT * FROM sti_tests"
|
93
|
-
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind
|
94
|
-
StiTestSub2.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind
|
93
|
+
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub1'))"
|
94
|
+
StiTestSub2.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub2'))"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should add a correct filter for multiple levels of subclasses" do
|
98
|
+
class ::StiTestSub1A < StiTestSub1; end
|
99
|
+
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub1', 'StiTestSub1A'))"
|
100
|
+
StiTestSub1A.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub1A'))"
|
101
|
+
class ::StiTestSub2A < StiTestSub2; end
|
102
|
+
StiTestSub2.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub2', 'StiTestSub2A'))"
|
103
|
+
StiTestSub2A.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub2A'))"
|
104
|
+
class ::StiTestSub1B < StiTestSub1A; end
|
105
|
+
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub1', 'StiTestSub1A', 'StiTestSub1B'))"
|
106
|
+
StiTestSub1A.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub1A', 'StiTestSub1B'))"
|
107
|
+
StiTestSub1B.dataset.sql.should == "SELECT * FROM sti_tests WHERE (sti_tests.kind IN ('StiTestSub1B'))"
|
108
|
+
end
|
109
|
+
|
110
|
+
context "with custom options" do
|
111
|
+
before do
|
112
|
+
class ::StiTest2 < Sequel::Model
|
113
|
+
columns :id, :kind
|
114
|
+
def _refresh(x); end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
after do
|
118
|
+
Object.send(:remove_const, :StiTest2)
|
119
|
+
Object.send(:remove_const, :StiTest3)
|
120
|
+
Object.send(:remove_const, :StiTest4)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should work with custom procs with strings" do
|
124
|
+
StiTest2.plugin :single_table_inheritance, :kind, :model_map=>proc{|v| v == 1 ? 'StiTest3' : 'StiTest4'}, :key_map=>proc{|klass| klass.name == 'StiTest3' ? 1 : 2}
|
125
|
+
class ::StiTest3 < ::StiTest2; end
|
126
|
+
class ::StiTest4 < ::StiTest2; end
|
127
|
+
StiTest2.dataset.row_proc.call(:kind=>0).should be_a_instance_of(StiTest4)
|
128
|
+
StiTest2.dataset.row_proc.call(:kind=>1).should be_a_instance_of(StiTest3)
|
129
|
+
StiTest2.dataset.row_proc.call(:kind=>2).should be_a_instance_of(StiTest4)
|
130
|
+
|
131
|
+
StiTest2.create.kind.should == 2
|
132
|
+
StiTest3.create.kind.should == 1
|
133
|
+
StiTest4.create.kind.should == 2
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should work with custom procs with symbols" do
|
137
|
+
StiTest2.plugin :single_table_inheritance, :kind, :model_map=>proc{|v| v == 1 ? :StiTest3 : :StiTest4}, :key_map=>proc{|klass| klass.name == 'StiTest3' ? 1 : 2}
|
138
|
+
class ::StiTest3 < ::StiTest2; end
|
139
|
+
class ::StiTest4 < ::StiTest2; end
|
140
|
+
StiTest2.dataset.row_proc.call(:kind=>0).should be_a_instance_of(StiTest4)
|
141
|
+
StiTest2.dataset.row_proc.call(:kind=>1).should be_a_instance_of(StiTest3)
|
142
|
+
StiTest2.dataset.row_proc.call(:kind=>2).should be_a_instance_of(StiTest4)
|
143
|
+
|
144
|
+
StiTest2.create.kind.should == 2
|
145
|
+
StiTest3.create.kind.should == 1
|
146
|
+
StiTest4.create.kind.should == 2
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should work with custom hashes" do
|
150
|
+
StiTest2.plugin :single_table_inheritance, :kind, :model_map=>{0=>StiTest2, 1=>:StiTest3, 2=>'StiTest4'}, :key_map=>{StiTest2=>4, 'StiTest3'=>5, 'StiTest4'=>6}
|
151
|
+
class ::StiTest3 < ::StiTest2; end
|
152
|
+
class ::StiTest4 < ::StiTest2; end
|
153
|
+
StiTest2.dataset.row_proc.call(:kind=>0).should be_a_instance_of(StiTest2)
|
154
|
+
StiTest2.dataset.row_proc.call(:kind=>1).should be_a_instance_of(StiTest3)
|
155
|
+
StiTest2.dataset.row_proc.call(:kind=>2).should be_a_instance_of(StiTest4)
|
156
|
+
|
157
|
+
StiTest2.create.kind.should == 4
|
158
|
+
StiTest3.create.kind.should == 5
|
159
|
+
StiTest4.create.kind.should == 6
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should infer key_map from model_map if provided as a hash" do
|
163
|
+
StiTest2.plugin :single_table_inheritance, :kind, :model_map=>{0=>StiTest2, 1=>'StiTest3', 2=>:StiTest4}
|
164
|
+
class ::StiTest3 < ::StiTest2; end
|
165
|
+
class ::StiTest4 < ::StiTest2; end
|
166
|
+
StiTest2.dataset.row_proc.call(:kind=>0).should be_a_instance_of(StiTest2)
|
167
|
+
StiTest2.dataset.row_proc.call(:kind=>1).should be_a_instance_of(StiTest3)
|
168
|
+
StiTest2.dataset.row_proc.call(:kind=>2).should be_a_instance_of(StiTest4)
|
169
|
+
|
170
|
+
StiTest2.create.kind.should == 0
|
171
|
+
StiTest3.create.kind.should == 1
|
172
|
+
StiTest4.create.kind.should == 2
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should raise exceptions if a bad model value is used" do
|
176
|
+
StiTest2.plugin :single_table_inheritance, :kind, :model_map=>{0=>1,1=>1.5, 2=>Date.today}
|
177
|
+
class ::StiTest3 < ::StiTest2; end
|
178
|
+
class ::StiTest4 < ::StiTest2; end
|
179
|
+
proc{StiTest2.dataset.row_proc.call(:kind=>0)}.should raise_error(Sequel::Error)
|
180
|
+
proc{StiTest2.dataset.row_proc.call(:kind=>1)}.should raise_error(Sequel::Error)
|
181
|
+
proc{StiTest2.dataset.row_proc.call(:kind=>2)}.should raise_error(Sequel::Error)
|
182
|
+
end
|
95
183
|
end
|
96
184
|
end
|