sequel 2.0.1 → 2.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/CHANGELOG +38 -0
- data/README +3 -4
- data/Rakefile +4 -4
- data/lib/sequel_model.rb +22 -2
- data/lib/sequel_model/association_reflection.rb +2 -9
- data/lib/sequel_model/associations.rb +184 -91
- data/lib/sequel_model/base.rb +117 -22
- data/lib/sequel_model/caching.rb +1 -1
- data/lib/sequel_model/dataset_methods.rb +26 -0
- data/lib/sequel_model/eager_loading.rb +16 -20
- data/lib/sequel_model/hooks.rb +1 -1
- data/lib/sequel_model/plugins.rb +1 -1
- data/lib/sequel_model/record.rb +125 -39
- data/lib/sequel_model/validations.rb +101 -115
- data/spec/association_reflection_spec.rb +6 -6
- data/spec/associations_spec.rb +205 -37
- data/spec/base_spec.rb +161 -1
- data/spec/dataset_methods_spec.rb +66 -0
- data/spec/eager_loading_spec.rb +36 -25
- data/spec/model_spec.rb +51 -6
- data/spec/record_spec.rb +172 -62
- data/spec/schema_spec.rb +7 -0
- data/spec/validations_spec.rb +152 -51
- metadata +5 -3
data/spec/base_spec.rb
CHANGED
@@ -182,7 +182,7 @@ describe Sequel::Model, "dataset" do
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
-
describe Sequel::Model, "def_dataset_method" do
|
185
|
+
describe Sequel::Model, ".def_dataset_method" do
|
186
186
|
setup do
|
187
187
|
@c = Class.new(Sequel::Model(:items)) do
|
188
188
|
@dataset = Object.new
|
@@ -210,6 +210,25 @@ describe Sequel::Model, "def_dataset_method" do
|
|
210
210
|
@c.return_3.should == 3
|
211
211
|
@c.return_4.should == 4
|
212
212
|
end
|
213
|
+
|
214
|
+
it "should cache calls and readd methods if set_dataset is used" do
|
215
|
+
@c.instance_eval do
|
216
|
+
def_dataset_method(:return_3){3}
|
217
|
+
end
|
218
|
+
@c.set_dataset :items
|
219
|
+
@c.return_3.should == 3
|
220
|
+
@c.dataset.return_3.should == 3
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should readd methods to subclasses, if set_dataset is used in a subclass" do
|
224
|
+
@c.instance_eval do
|
225
|
+
def_dataset_method(:return_3){3}
|
226
|
+
end
|
227
|
+
c = Class.new(@c)
|
228
|
+
c.set_dataset :items
|
229
|
+
c.return_3.should == 3
|
230
|
+
c.dataset.return_3.should == 3
|
231
|
+
end
|
213
232
|
end
|
214
233
|
|
215
234
|
describe "A model class with implicit table name" do
|
@@ -256,3 +275,144 @@ describe "Model.db=" do
|
|
256
275
|
end
|
257
276
|
end
|
258
277
|
|
278
|
+
describe Sequel::Model, ".(allowed|restricted)_columns " do
|
279
|
+
setup do
|
280
|
+
@c = Class.new(Sequel::Model(:blahblah)) do
|
281
|
+
columns :x, :y, :z
|
282
|
+
def refresh
|
283
|
+
self
|
284
|
+
end
|
285
|
+
end
|
286
|
+
@c.strict_param_setting = false
|
287
|
+
@c.instance_variable_set(:@columns, [:x, :y, :z])
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should set the allowed columns correctly" do
|
291
|
+
@c.allowed_columns.should == nil
|
292
|
+
@c.set_allowed_columns :x
|
293
|
+
@c.allowed_columns.should == [:x]
|
294
|
+
@c.set_allowed_columns :x, :y
|
295
|
+
@c.allowed_columns.should == [:x, :y]
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should set the restricted columns correctly" do
|
299
|
+
@c.restricted_columns.should == nil
|
300
|
+
@c.set_restricted_columns :x
|
301
|
+
@c.restricted_columns.should == [:x]
|
302
|
+
@c.set_restricted_columns :x, :y
|
303
|
+
@c.restricted_columns.should == [:x, :y]
|
304
|
+
end
|
305
|
+
|
306
|
+
it "should only set allowed columns by default" do
|
307
|
+
@c.set_allowed_columns :x, :y
|
308
|
+
i = @c.new(:x => 1, :y => 2, :z => 3)
|
309
|
+
i.values.should == {:x => 1, :y => 2}
|
310
|
+
i.set(:x => 4, :y => 5, :z => 6)
|
311
|
+
i.values.should == {:x => 4, :y => 5}
|
312
|
+
i.update(:x => 7, :y => 8, :z => 9)
|
313
|
+
i.values.delete(:id) # stupid specs
|
314
|
+
i.values.should == {:x => 7, :y => 8}
|
315
|
+
end
|
316
|
+
|
317
|
+
it "should not set restricted columns by default" do
|
318
|
+
@c.set_restricted_columns :z
|
319
|
+
i = @c.new(:x => 1, :y => 2, :z => 3)
|
320
|
+
i.values.should == {:x => 1, :y => 2}
|
321
|
+
i.set(:x => 4, :y => 5, :z => 6)
|
322
|
+
i.values.should == {:x => 4, :y => 5}
|
323
|
+
i.update(:x => 7, :y => 8, :z => 9)
|
324
|
+
i.values.delete(:id) # stupid specs
|
325
|
+
i.values.should == {:x => 7, :y => 8}
|
326
|
+
end
|
327
|
+
|
328
|
+
it "should have allowed take precedence over restricted" do
|
329
|
+
@c.set_allowed_columns :x, :y
|
330
|
+
@c.set_restricted_columns :y, :z
|
331
|
+
i = @c.new(:x => 1, :y => 2, :z => 3)
|
332
|
+
i.values.should == {:x => 1, :y => 2}
|
333
|
+
i.set(:x => 4, :y => 5, :z => 6)
|
334
|
+
i.values.should == {:x => 4, :y => 5}
|
335
|
+
i.update(:x => 7, :y => 8, :z => 9)
|
336
|
+
i.values.delete(:id) # stupid specs
|
337
|
+
i.values.should == {:x => 7, :y => 8}
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
describe Sequel::Model, ".(un)?restrict_primary_key\\??" do
|
342
|
+
setup do
|
343
|
+
@c = Class.new(Sequel::Model(:blahblah)) do
|
344
|
+
set_primary_key :id
|
345
|
+
columns :x, :y, :z, :id
|
346
|
+
def refresh
|
347
|
+
self
|
348
|
+
end
|
349
|
+
end
|
350
|
+
@c.strict_param_setting = false
|
351
|
+
@c.instance_variable_set(:@columns, [:x, :y, :z])
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should restrict updates to primary key by default" do
|
355
|
+
i = @c.new(:x => 1, :y => 2, :id => 3)
|
356
|
+
i.values.should == {:x => 1, :y => 2}
|
357
|
+
i.set(:x => 4, :y => 5, :id => 6)
|
358
|
+
i.values.should == {:x => 4, :y => 5}
|
359
|
+
end
|
360
|
+
|
361
|
+
it "should allow updates to primary key if unrestrict_primary_key is used" do
|
362
|
+
@c.unrestrict_primary_key
|
363
|
+
i = @c.new(:x => 1, :y => 2, :id => 3)
|
364
|
+
i.values.should == {:x => 1, :y => 2, :id=>3}
|
365
|
+
i.set(:x => 4, :y => 5, :id => 6)
|
366
|
+
i.values.should == {:x => 4, :y => 5, :id=>6}
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should have restrict_primary_key? return true or false depending" do
|
370
|
+
@c.restrict_primary_key?.should == true
|
371
|
+
@c.unrestrict_primary_key
|
372
|
+
@c.restrict_primary_key?.should == false
|
373
|
+
c1 = Class.new(@c)
|
374
|
+
c1.restrict_primary_key?.should == false
|
375
|
+
@c.restrict_primary_key
|
376
|
+
@c.restrict_primary_key?.should == true
|
377
|
+
c1.restrict_primary_key?.should == false
|
378
|
+
c2 = Class.new(@c)
|
379
|
+
c2.restrict_primary_key?.should == true
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
describe Sequel::Model, ".strict_param_setting" do
|
384
|
+
setup do
|
385
|
+
@c = Class.new(Sequel::Model(:blahblah)) do
|
386
|
+
columns :x, :y, :z, :id
|
387
|
+
set_restricted_columns :z
|
388
|
+
def refresh
|
389
|
+
self
|
390
|
+
end
|
391
|
+
end
|
392
|
+
@c.instance_variable_set(:@columns, [:x, :y, :z])
|
393
|
+
end
|
394
|
+
|
395
|
+
it "should be enabled by default" do
|
396
|
+
@c.strict_param_setting.should == true
|
397
|
+
end
|
398
|
+
|
399
|
+
it "should raise an error if a missing/restricted column/method is accessed" do
|
400
|
+
proc{@c.new(:z=>1)}.should raise_error(Sequel::Error)
|
401
|
+
proc{@c.create(:z=>1)}.should raise_error(Sequel::Error)
|
402
|
+
c = @c.new
|
403
|
+
proc{c.set(:z=>1)}.should raise_error(Sequel::Error)
|
404
|
+
proc{c.set_all(:id=>1)}.should raise_error(Sequel::Error)
|
405
|
+
proc{c.set_only({:x=>1}, :y)}.should raise_error(Sequel::Error)
|
406
|
+
proc{c.set_except({:x=>1}, :x)}.should raise_error(Sequel::Error)
|
407
|
+
proc{c.update(:z=>1)}.should raise_error(Sequel::Error)
|
408
|
+
proc{c.update_all(:id=>1)}.should raise_error(Sequel::Error)
|
409
|
+
proc{c.update_only({:x=>1}, :y)}.should raise_error(Sequel::Error)
|
410
|
+
proc{c.update_except({:x=>1}, :x)}.should raise_error(Sequel::Error)
|
411
|
+
end
|
412
|
+
|
413
|
+
it "should be disabled by strict_param_setting = false" do
|
414
|
+
@c.strict_param_setting = false
|
415
|
+
@c.strict_param_setting.should == false
|
416
|
+
proc{@c.new(:z=>1)}.should_not raise_error
|
417
|
+
end
|
418
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe Sequel::Model::DatasetMethods, "#destroy" do
|
4
|
+
before do
|
5
|
+
@c = Class.new(Sequel::Model(:items)) do
|
6
|
+
@@destroyed = []
|
7
|
+
def destroy
|
8
|
+
@@destroyed << self
|
9
|
+
end
|
10
|
+
def self.destroyed
|
11
|
+
@@destroyed
|
12
|
+
end
|
13
|
+
end
|
14
|
+
@d = @c.dataset
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should instantiate objects in the dataset and call destroy on each" do
|
18
|
+
def @d.fetch_rows(sql)
|
19
|
+
yield({:id=>1})
|
20
|
+
yield({:id=>2})
|
21
|
+
end
|
22
|
+
@d.destroy
|
23
|
+
@c.destroyed.collect{|x| x.values}.should == [{:id=>1}, {:id=>2}]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return the number of records destroyed" do
|
27
|
+
def @d.fetch_rows(sql)
|
28
|
+
yield({:id=>1})
|
29
|
+
yield({:id=>2})
|
30
|
+
end
|
31
|
+
@d.destroy.should == 2
|
32
|
+
def @d.fetch_rows(sql)
|
33
|
+
yield({:id=>1})
|
34
|
+
end
|
35
|
+
@d.destroy.should == 1
|
36
|
+
def @d.fetch_rows(sql)
|
37
|
+
end
|
38
|
+
@d.destroy.should == 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe Sequel::Model::DatasetMethods, "#to_hash" do
|
43
|
+
before do
|
44
|
+
@c = Class.new(Sequel::Model(:items)) do
|
45
|
+
set_primary_key :name
|
46
|
+
end
|
47
|
+
@d = @c.dataset
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should result in a hash with primary key value keys and model object values" do
|
51
|
+
def @d.fetch_rows(sql)
|
52
|
+
yield({:name=>1})
|
53
|
+
yield({:name=>2})
|
54
|
+
end
|
55
|
+
h = @d.to_hash
|
56
|
+
h.should be_a_kind_of(Hash)
|
57
|
+
a = h.to_a
|
58
|
+
a.collect{|x| x[1].class}.should == [@c, @c]
|
59
|
+
[[[1, {:name=>1}], [2, {:name=>2}]], [[2, {:name=>2}], [1, {:name=>1}]]].should(include(a.collect{|x| [x[0], x[1].values]}))
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should raise an error if the class doesn't have a primary key" do
|
63
|
+
@c.no_primary_key
|
64
|
+
proc{@d.to_hash}.should raise_error(Sequel::Error)
|
65
|
+
end
|
66
|
+
end
|
data/spec/eager_loading_spec.rb
CHANGED
@@ -277,7 +277,7 @@ describe Sequel::Model, "#eager" do
|
|
277
277
|
a.first.values.should == {:id => 101, :band_id => 101}
|
278
278
|
MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (id = 101)', 'SELECT bands.* FROM bands WHERE (id IN (101))']
|
279
279
|
a = a.first
|
280
|
-
a.
|
280
|
+
a.associations.fetch(:band, 2).should == nil
|
281
281
|
a.band.should == nil
|
282
282
|
MODEL_DB.sqls.length.should == 2
|
283
283
|
end
|
@@ -290,10 +290,10 @@ describe Sequel::Model, "#eager" do
|
|
290
290
|
a.first.values.should == {:id => 101}
|
291
291
|
a.last.values.should == {:id => 102}
|
292
292
|
MODEL_DB.sqls.should == ['SELECT * FROM bands WHERE (id > 100)', 'SELECT albums.* FROM albums WHERE (band_id IN (101, 102))', "SELECT tracks.* FROM tracks WHERE (album_id IN (101))"]
|
293
|
-
a.first.
|
293
|
+
a.first.associations[:albums].should be_a_kind_of(Array)
|
294
294
|
a.first.albums.length.should == 1
|
295
295
|
a.first.albums.first.should be_a_kind_of(EagerAlbum)
|
296
|
-
a.last.
|
296
|
+
a.last.associations[:albums].should == []
|
297
297
|
a.last.albums.should == []
|
298
298
|
MODEL_DB.sqls.length.should == 3
|
299
299
|
end
|
@@ -384,7 +384,7 @@ describe Sequel::Model, "#eager_graph" do
|
|
384
384
|
|
385
385
|
it "should eagerly load a single many_to_one association" do
|
386
386
|
ds = GraphAlbum.eager_graph(:band)
|
387
|
-
ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands band ON (band.id = albums.band_id)'
|
387
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
|
388
388
|
def ds.fetch_rows(sql, &block)
|
389
389
|
yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
|
390
390
|
end
|
@@ -436,7 +436,7 @@ describe Sequel::Model, "#eager_graph" do
|
|
436
436
|
|
437
437
|
it "should eagerly load multiple associations" do
|
438
438
|
ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
|
439
|
-
ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands band ON (band.id = albums.band_id)'
|
439
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
|
440
440
|
def ds.fetch_rows(sql, &block)
|
441
441
|
yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
|
442
442
|
end
|
@@ -460,7 +460,7 @@ describe Sequel::Model, "#eager_graph" do
|
|
460
460
|
|
461
461
|
it "should allow cascading of eager loading for associations of associated models" do
|
462
462
|
ds = GraphTrack.eager_graph(:album=>{:band=>:members})
|
463
|
-
ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN albums album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
|
463
|
+
ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN albums AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
|
464
464
|
def ds.fetch_rows(sql, &block)
|
465
465
|
yield({:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6})
|
466
466
|
end
|
@@ -505,7 +505,7 @@ describe Sequel::Model, "#eager_graph" do
|
|
505
505
|
|
506
506
|
it "should eager load multiple associations from the same table" do
|
507
507
|
ds = GraphBand.eager_graph(:vocalist, :members)
|
508
|
-
ds.sql.should == 'SELECT bands.id, bands.vocalist_id, vocalist.id AS vocalist_id_0, members.id AS members_id FROM bands LEFT OUTER JOIN members vocalist ON (vocalist.id = bands.vocalist_id) LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
|
508
|
+
ds.sql.should == 'SELECT bands.id, bands.vocalist_id, vocalist.id AS vocalist_id_0, members.id AS members_id FROM bands LEFT OUTER JOIN members AS vocalist ON (vocalist.id = bands.vocalist_id) LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
|
509
509
|
def ds.fetch_rows(sql, &block)
|
510
510
|
yield({:id=>2, :vocalist_id=>6, :vocalist_id_0=>6, :members_id=>5})
|
511
511
|
end
|
@@ -525,16 +525,16 @@ describe Sequel::Model, "#eager_graph" do
|
|
525
525
|
|
526
526
|
it "should give you a graph of tables when called without .all" do
|
527
527
|
ds = GraphAlbum.eager_graph(:band)
|
528
|
-
ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands band ON (band.id = albums.band_id)'
|
528
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
|
529
529
|
def ds.fetch_rows(sql, &block)
|
530
530
|
yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
|
531
531
|
end
|
532
|
-
ds.first.should == {:albums=>GraphAlbum.
|
532
|
+
ds.first.should == {:albums=>GraphAlbum.load(:id => 1, :band_id => 2), :band=>GraphBand.load(:id => 2, :vocalist_id=>3)}
|
533
533
|
end
|
534
534
|
|
535
535
|
it "should not drop any associated objects if the graph could not be a cartesian product" do
|
536
536
|
ds = GraphBand.eager_graph(:members, :vocalist)
|
537
|
-
ds.sql.should == 'SELECT bands.id, bands.vocalist_id, members.id AS members_id, vocalist.id AS vocalist_id_0 FROM bands LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id) LEFT OUTER JOIN members vocalist ON (vocalist.id = bands.vocalist_id)'
|
537
|
+
ds.sql.should == 'SELECT bands.id, bands.vocalist_id, members.id AS members_id, vocalist.id AS vocalist_id_0 FROM bands LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id) LEFT OUTER JOIN members AS vocalist ON (vocalist.id = bands.vocalist_id)'
|
538
538
|
def ds.fetch_rows(sql, &block)
|
539
539
|
yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
|
540
540
|
yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
|
@@ -615,7 +615,7 @@ describe Sequel::Model, "#eager_graph" do
|
|
615
615
|
|
616
616
|
it "should handle no associated records for a single many_to_one association" do
|
617
617
|
ds = GraphAlbum.eager_graph(:band)
|
618
|
-
ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands band ON (band.id = albums.band_id)'
|
618
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
|
619
619
|
def ds.fetch_rows(sql, &block)
|
620
620
|
yield({:id=>1, :band_id=>2, :band_id_0=>nil, :vocalist_id=>nil})
|
621
621
|
end
|
@@ -624,7 +624,7 @@ describe Sequel::Model, "#eager_graph" do
|
|
624
624
|
a.size.should == 1
|
625
625
|
a.first.should be_a_kind_of(GraphAlbum)
|
626
626
|
a.first.values.should == {:id => 1, :band_id => 2}
|
627
|
-
a.first.
|
627
|
+
a.first.associations.fetch(:band, 2).should == nil
|
628
628
|
end
|
629
629
|
|
630
630
|
it "should handle no associated records for a single one_to_many association" do
|
@@ -657,7 +657,7 @@ describe Sequel::Model, "#eager_graph" do
|
|
657
657
|
|
658
658
|
it "should handle missing associated records when loading multiple associations" do
|
659
659
|
ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
|
660
|
-
ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands band ON (band.id = albums.band_id)'
|
660
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
|
661
661
|
def ds.fetch_rows(sql, &block)
|
662
662
|
yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>3, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
|
663
663
|
yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>4, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
|
@@ -674,13 +674,13 @@ describe Sequel::Model, "#eager_graph" do
|
|
674
674
|
a.tracks.size.should == 4
|
675
675
|
a.tracks.first.should be_a_kind_of(GraphTrack)
|
676
676
|
a.tracks.collect{|x|x[:id]}.should == [3,4,5,6]
|
677
|
-
a.
|
677
|
+
a.associations.fetch(:band, 2).should == nil
|
678
678
|
a.genres.should == []
|
679
679
|
end
|
680
680
|
|
681
681
|
it "should handle missing associated records when cascading eager loading for associations of associated models" do
|
682
682
|
ds = GraphTrack.eager_graph(:album=>{:band=>:members})
|
683
|
-
ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN albums album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
|
683
|
+
ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN albums AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
|
684
684
|
def ds.fetch_rows(sql, &block)
|
685
685
|
yield({:id=>2, :album_id=>2, :album_id_0=>nil, :band_id=>nil, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
|
686
686
|
yield({:id=>3, :album_id=>3, :album_id_0=>3, :band_id=>3, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
|
@@ -693,10 +693,10 @@ describe Sequel::Model, "#eager_graph" do
|
|
693
693
|
a.size.should == 4
|
694
694
|
a.first.should be_a_kind_of(GraphTrack)
|
695
695
|
a.collect{|x|x[:id]}.should == [2,3,4,5]
|
696
|
-
a[0].
|
696
|
+
a[0].associations.fetch(:album, 2).should == nil
|
697
697
|
a[1].album.should be_a_kind_of(GraphAlbum)
|
698
698
|
a[1].album.values.should == {:id => 3, :band_id => 3}
|
699
|
-
a[1].album.
|
699
|
+
a[1].album.associations.fetch(:band, 2).should == nil
|
700
700
|
a[2].album.should be_a_kind_of(GraphAlbum)
|
701
701
|
a[2].album.values.should == {:id => 4, :band_id => 2}
|
702
702
|
a[2].album.band.should be_a_kind_of(GraphBand)
|
@@ -713,33 +713,44 @@ describe Sequel::Model, "#eager_graph" do
|
|
713
713
|
a[3].album.band.members.last.values.should == {:id => 6}
|
714
714
|
end
|
715
715
|
|
716
|
+
it "should respect the association's :graph_select option" do
|
717
|
+
GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_select=>:vocalist_id
|
718
|
+
GraphAlbum.eager_graph(:inner_band).sql.should == 'SELECT albums.id, albums.band_id, inner_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS inner_band ON (inner_band.id = albums.band_id)'
|
719
|
+
|
720
|
+
GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_select=>[:album_id]
|
721
|
+
GraphAlbum.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.album_id FROM albums LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id)'
|
722
|
+
|
723
|
+
GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_select=>[]
|
724
|
+
GraphAlbum.eager_graph(:inner_genres).sql.should == 'SELECT albums.id, albums.band_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS inner_genres ON (inner_genres.id = ag.genre_id)'
|
725
|
+
end
|
726
|
+
|
716
727
|
it "should respect the association's :graph_join_type option" do
|
717
728
|
GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_join_type=>:inner
|
718
|
-
GraphAlbum.eager_graph(:inner_band).sql.should == 'SELECT albums.id, albums.band_id, inner_band.id AS inner_band_id, inner_band.vocalist_id FROM albums INNER JOIN bands inner_band ON (inner_band.id = albums.band_id)'
|
729
|
+
GraphAlbum.eager_graph(:inner_band).sql.should == 'SELECT albums.id, albums.band_id, inner_band.id AS inner_band_id, inner_band.vocalist_id FROM albums INNER JOIN bands AS inner_band ON (inner_band.id = albums.band_id)'
|
719
730
|
|
720
731
|
GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_join_type=>:right_outer
|
721
|
-
GraphAlbum.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums RIGHT OUTER JOIN tracks right_tracks ON (right_tracks.album_id = albums.id)'
|
732
|
+
GraphAlbum.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums RIGHT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id)'
|
722
733
|
|
723
734
|
GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_type=>:inner
|
724
|
-
GraphAlbum.eager_graph(:inner_genres).sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres inner_genres ON (inner_genres.id = ag.genre_id)'
|
735
|
+
GraphAlbum.eager_graph(:inner_genres).sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres AS inner_genres ON (inner_genres.id = ag.genre_id)'
|
725
736
|
end
|
726
737
|
|
727
738
|
it "should respect the association's :graph_conditions option" do
|
728
739
|
GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_conditions=>{:active=>true}
|
729
|
-
GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands active_band ON ((active_band.id = albums.band_id) AND (active_band.active = 't'))"
|
740
|
+
GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS active_band ON ((active_band.id = albums.band_id) AND (active_band.active = 't'))"
|
730
741
|
|
731
742
|
GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_conditions=>{true=>:active}
|
732
|
-
GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
|
743
|
+
GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
|
733
744
|
|
734
745
|
GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_conditions=>{:id=>(0..100)}
|
735
|
-
GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands active_band ON ((active_band.id = albums.band_id) AND ((active_band.id >= 0) AND (active_band.id <= 100)))"
|
746
|
+
GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS active_band ON ((active_band.id = albums.band_id) AND ((active_band.id >= 0) AND (active_band.id <= 100)))"
|
736
747
|
end
|
737
748
|
|
738
749
|
it "should respect the association's :graph_join_table_conditions option" do
|
739
750
|
GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_table_conditions=>{:active=>true}
|
740
|
-
GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND (ag.active = 't')) LEFT OUTER JOIN genres active_genres ON (active_genres.id = ag.genre_id)"
|
751
|
+
GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND (ag.active = 't')) LEFT OUTER JOIN genres AS active_genres ON (active_genres.id = ag.genre_id)"
|
741
752
|
|
742
753
|
GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_conditions=>{true=>:active}, :graph_join_table_conditions=>{true=>:active}
|
743
|
-
GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND ('t' = albums.active)) LEFT OUTER JOIN genres active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
|
754
|
+
GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND ('t' = albums.active)) LEFT OUTER JOIN genres AS active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
|
744
755
|
end
|
745
756
|
end
|
data/spec/model_spec.rb
CHANGED
@@ -25,7 +25,6 @@ describe Sequel::Model do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
describe Sequel::Model, "dataset & schema" do
|
28
|
-
|
29
28
|
before do
|
30
29
|
@model = Class.new(Sequel::Model(:items))
|
31
30
|
end
|
@@ -80,9 +79,57 @@ describe Sequel::Model, "dataset & schema" do
|
|
80
79
|
@model.primary_key.should == :id
|
81
80
|
@model.table_name.should == :foo
|
82
81
|
end
|
82
|
+
end
|
83
83
|
|
84
|
-
|
85
|
-
|
84
|
+
describe Sequel::Model, "#sti_key" do
|
85
|
+
before do
|
86
|
+
class StiTest < Sequel::Model
|
87
|
+
def kind=(x); self[:kind] = x; end
|
88
|
+
def refresh; end
|
89
|
+
set_sti_key :kind
|
90
|
+
end
|
91
|
+
class StiTestSub1 < StiTest
|
92
|
+
end
|
93
|
+
class StiTestSub2 < StiTest
|
94
|
+
end
|
95
|
+
@ds = StiTest.dataset
|
96
|
+
MODEL_DB.reset
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return rows with the correct class based on the polymorphic_key value" do
|
100
|
+
def @ds.fetch_rows(sql)
|
101
|
+
yield({:kind=>'StiTest'})
|
102
|
+
yield({:kind=>'StiTestSub1'})
|
103
|
+
yield({:kind=>'StiTestSub2'})
|
104
|
+
end
|
105
|
+
StiTest.all.collect{|x| x.class}.should == [StiTest, StiTestSub1, StiTestSub2]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should fallback to the main class if polymophic_key value is NULL" do
|
109
|
+
def @ds.fetch_rows(sql)
|
110
|
+
yield({:kind=>nil})
|
111
|
+
end
|
112
|
+
StiTest.all.collect{|x| x.class}.should == [StiTest]
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should fallback to the main class if the given class does not exist" do
|
116
|
+
def @ds.fetch_rows(sql)
|
117
|
+
yield({:kind=>'StiTestSub3'})
|
118
|
+
end
|
119
|
+
StiTest.all.collect{|x| x.class}.should == [StiTest]
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should add a before_create hook that sets the model class name for the key" do
|
123
|
+
StiTest.new.save
|
124
|
+
StiTestSub1.new.save
|
125
|
+
StiTestSub2.new.save
|
126
|
+
MODEL_DB.sqls.should == ["INSERT INTO sti_tests (kind) VALUES ('StiTest')", "INSERT INTO sti_tests (kind) VALUES ('StiTestSub1')", "INSERT INTO sti_tests (kind) VALUES ('StiTestSub2')"]
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should add a filter to model datasets inside subclasses hook to only retreive objects with the matching key" do
|
130
|
+
StiTest.dataset.sql.should == "SELECT * FROM sti_tests"
|
131
|
+
StiTestSub1.dataset.sql.should == "SELECT * FROM sti_tests WHERE (kind = 'StiTestSub1')"
|
132
|
+
StiTestSub2.dataset.sql.should == "SELECT * FROM sti_tests WHERE (kind = 'StiTestSub2')"
|
86
133
|
end
|
87
134
|
end
|
88
135
|
|
@@ -152,8 +199,7 @@ describe Sequel::Model, "new" do
|
|
152
199
|
end
|
153
200
|
|
154
201
|
describe Sequel::Model, ".subset" do
|
155
|
-
|
156
|
-
before(:each) do
|
202
|
+
before do
|
157
203
|
MODEL_DB.reset
|
158
204
|
|
159
205
|
@c = Class.new(Sequel::Model(:items))
|
@@ -172,7 +218,6 @@ describe Sequel::Model, ".subset" do
|
|
172
218
|
@c.pricey.sql.should == "SELECT * FROM items WHERE (price > 100)"
|
173
219
|
@c.dataset.pricey.sql.should == "SELECT * FROM items WHERE (price > 100)"
|
174
220
|
|
175
|
-
# check if subsets are composable
|
176
221
|
@c.pricey.new_only.sql.should == "SELECT * FROM items WHERE ((price > 100) AND (age = 'new'))"
|
177
222
|
@c.new_only.pricey.sql.should == "SELECT * FROM items WHERE ((age = 'new') AND (price > 100))"
|
178
223
|
end
|