sequel 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|