sequel 3.11.0 → 3.12.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/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
|