sequel 2.6.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +64 -0
- data/Rakefile +1 -1
- data/lib/sequel_core/adapters/jdbc.rb +6 -2
- data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel_core/adapters/oracle.rb +4 -77
- data/lib/sequel_core/adapters/postgres.rb +39 -26
- data/lib/sequel_core/adapters/shared/mssql.rb +0 -1
- data/lib/sequel_core/adapters/shared/mysql.rb +1 -1
- data/lib/sequel_core/adapters/shared/oracle.rb +82 -0
- data/lib/sequel_core/adapters/shared/postgres.rb +65 -46
- data/lib/sequel_core/core_ext.rb +10 -0
- data/lib/sequel_core/core_sql.rb +7 -0
- data/lib/sequel_core/database.rb +22 -0
- data/lib/sequel_core/database/schema.rb +1 -1
- data/lib/sequel_core/dataset.rb +29 -11
- data/lib/sequel_core/dataset/sql.rb +27 -7
- data/lib/sequel_core/migration.rb +20 -2
- data/lib/sequel_core/object_graph.rb +24 -10
- data/lib/sequel_core/schema/generator.rb +22 -9
- data/lib/sequel_core/schema/sql.rb +13 -9
- data/lib/sequel_core/sql.rb +27 -2
- data/lib/sequel_model/association_reflection.rb +251 -141
- data/lib/sequel_model/associations.rb +114 -61
- data/lib/sequel_model/base.rb +25 -21
- data/lib/sequel_model/eager_loading.rb +17 -40
- data/lib/sequel_model/hooks.rb +25 -24
- data/lib/sequel_model/record.rb +29 -51
- data/lib/sequel_model/schema.rb +1 -1
- data/lib/sequel_model/validations.rb +13 -3
- data/spec/adapters/postgres_spec.rb +104 -18
- data/spec/adapters/spec_helper.rb +4 -1
- data/spec/integration/eager_loader_test.rb +5 -4
- data/spec/integration/spec_helper.rb +4 -1
- data/spec/sequel_core/connection_pool_spec.rb +24 -24
- data/spec/sequel_core/core_sql_spec.rb +12 -0
- data/spec/sequel_core/dataset_spec.rb +77 -2
- data/spec/sequel_core/expression_filters_spec.rb +6 -0
- data/spec/sequel_core/object_graph_spec.rb +40 -2
- data/spec/sequel_core/schema_spec.rb +13 -0
- data/spec/sequel_model/association_reflection_spec.rb +8 -8
- data/spec/sequel_model/associations_spec.rb +164 -3
- data/spec/sequel_model/caching_spec.rb +2 -1
- data/spec/sequel_model/eager_loading_spec.rb +107 -3
- data/spec/sequel_model/hooks_spec.rb +38 -22
- data/spec/sequel_model/model_spec.rb +11 -35
- data/spec/sequel_model/plugins_spec.rb +4 -2
- data/spec/sequel_model/record_spec.rb +8 -5
- data/spec/sequel_model/validations_spec.rb +25 -0
- data/spec/spec_config.rb +4 -3
- metadata +21 -19
@@ -18,21 +18,21 @@ describe Sequel::Model::Associations::AssociationReflection, "#associated_class"
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
describe Sequel::Model::Associations::AssociationReflection, "#
|
21
|
+
describe Sequel::Model::Associations::AssociationReflection, "#primary_key" do
|
22
22
|
before do
|
23
23
|
@c = Class.new(Sequel::Model)
|
24
24
|
class ::ParParent < Sequel::Model; end
|
25
25
|
end
|
26
26
|
|
27
|
-
it "should use the :
|
28
|
-
@c.many_to_one :c, :class=>ParParent, :
|
29
|
-
@c.association_reflection(:c).should include(:
|
30
|
-
@c.association_reflection(:c).
|
27
|
+
it "should use the :primary_key value if present" do
|
28
|
+
@c.many_to_one :c, :class=>ParParent, :primary_key=>:blah__blah
|
29
|
+
@c.association_reflection(:c).should include(:primary_key)
|
30
|
+
@c.association_reflection(:c).primary_key.should == :blah__blah
|
31
31
|
end
|
32
|
-
it "should use the associated table's primary key if :
|
32
|
+
it "should use the associated table's primary key if :primary_key is not present" do
|
33
33
|
@c.many_to_one :c, :class=>'ParParent'
|
34
|
-
@c.association_reflection(:c).should_not include(:
|
35
|
-
@c.association_reflection(:c).
|
34
|
+
@c.association_reflection(:c).should_not include(:primary_key)
|
35
|
+
@c.association_reflection(:c).primary_key.should == :id
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -46,7 +46,7 @@ end
|
|
46
46
|
describe Sequel::Model, "many_to_one" do
|
47
47
|
before do
|
48
48
|
MODEL_DB.reset
|
49
|
-
|
49
|
+
|
50
50
|
@c2 = Class.new(Sequel::Model(:nodes)) do
|
51
51
|
unrestrict_primary_key
|
52
52
|
columns :id, :parent_id, :par_parent_id, :blah
|
@@ -105,6 +105,12 @@ describe Sequel::Model, "many_to_one" do
|
|
105
105
|
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
|
106
106
|
end
|
107
107
|
|
108
|
+
it "should use :primary_key option if given" do
|
109
|
+
@c2.many_to_one :parent, :class => @c2, :key => :blah, :primary_key => :pk
|
110
|
+
@c2.new(:id => 1, :blah => 567).parent
|
111
|
+
MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.pk = 567) LIMIT 1"]
|
112
|
+
end
|
113
|
+
|
108
114
|
it "should use :select option if given" do
|
109
115
|
@c2.many_to_one :parent, :class => @c2, :key => :blah, :select=>[:id, :name]
|
110
116
|
@c2.new(:id => 1, :blah => 567).parent
|
@@ -157,6 +163,21 @@ describe Sequel::Model, "many_to_one" do
|
|
157
163
|
d.values.should == {:id => 1, :parent_id => 6677}
|
158
164
|
end
|
159
165
|
|
166
|
+
it "should have the setter method respect the :primary_key option" do
|
167
|
+
@c2.many_to_one :parent, :class => @c2, :primary_key=>:blah
|
168
|
+
|
169
|
+
d = @c2.new(:id => 1)
|
170
|
+
d.parent = @c2.new(:id => 4321, :blah=>444)
|
171
|
+
d.values.should == {:id => 1, :parent_id => 444}
|
172
|
+
|
173
|
+
d.parent = nil
|
174
|
+
d.values.should == {:id => 1, :parent_id => nil}
|
175
|
+
|
176
|
+
e = @c2.new(:id => 6677, :blah=>8)
|
177
|
+
d.parent = e
|
178
|
+
d.values.should == {:id => 1, :parent_id => 8}
|
179
|
+
end
|
180
|
+
|
160
181
|
it "should not persist changes until saved" do
|
161
182
|
@c2.many_to_one :parent, :class => @c2
|
162
183
|
|
@@ -289,6 +310,14 @@ describe Sequel::Model, "many_to_one" do
|
|
289
310
|
@c2.instance_methods.collect{|x| x.to_s}.should_not(include('parent='))
|
290
311
|
end
|
291
312
|
|
313
|
+
it "should not add associations methods directly to class" do
|
314
|
+
@c2.many_to_one :parent, :class => @c2
|
315
|
+
@c2.instance_methods.collect{|x| x.to_s}.should(include('parent'))
|
316
|
+
@c2.instance_methods.collect{|x| x.to_s}.should(include('parent='))
|
317
|
+
@c2.instance_methods(false).collect{|x| x.to_s}.should_not(include('parent'))
|
318
|
+
@c2.instance_methods(false).collect{|x| x.to_s}.should_not(include('parent='))
|
319
|
+
end
|
320
|
+
|
292
321
|
it "should raise an error if trying to set a model object that doesn't have a valid primary key" do
|
293
322
|
@c2.many_to_one :parent, :class => @c2
|
294
323
|
p = @c2.new
|
@@ -520,6 +549,18 @@ describe Sequel::Model, "one_to_many" do
|
|
520
549
|
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
|
521
550
|
end
|
522
551
|
|
552
|
+
it "should have add_ method respect the :primary_key option" do
|
553
|
+
@c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
|
554
|
+
|
555
|
+
n = @c2.new(:id => 1234, :xxx=>5)
|
556
|
+
a = @c1.new(:id => 2345)
|
557
|
+
a.save!
|
558
|
+
MODEL_DB.reset
|
559
|
+
a.should == n.add_attribute(a)
|
560
|
+
MODEL_DB.sqls.should == ['UPDATE attributes SET node_id = 5 WHERE (id = 2345)']
|
561
|
+
end
|
562
|
+
|
563
|
+
|
523
564
|
it "should raise an error in add_ and remove_ if the passed object returns false to save (is not valid)" do
|
524
565
|
@c2.one_to_many :attributes, :class => @c1
|
525
566
|
n = @c2.new(:id => 1234)
|
@@ -540,6 +581,12 @@ describe Sequel::Model, "one_to_many" do
|
|
540
581
|
proc{a.remove_all_attributes}.should raise_error(Sequel::Error)
|
541
582
|
end
|
542
583
|
|
584
|
+
it "should use :primary_key option if given" do
|
585
|
+
@c1.one_to_many :nodes, :class => @c2, :primary_key => :node_id, :key=>:id
|
586
|
+
n = @c1.load(:id => 1234, :node_id=>4321)
|
587
|
+
n.nodes_dataset.sql.should == "SELECT * FROM nodes WHERE (nodes.id = 4321)"
|
588
|
+
end
|
589
|
+
|
543
590
|
it "should support a select option" do
|
544
591
|
@c2.one_to_many :attributes, :class => @c1, :select => [:id, :name]
|
545
592
|
|
@@ -727,6 +774,22 @@ describe Sequel::Model, "one_to_many" do
|
|
727
774
|
im.should_not(include('remove_all_attributes'))
|
728
775
|
end
|
729
776
|
|
777
|
+
it "should not add associations methods directly to class" do
|
778
|
+
@c2.one_to_many :attributes, :class => @c1
|
779
|
+
im = @c2.instance_methods.collect{|x| x.to_s}
|
780
|
+
im.should(include('attributes'))
|
781
|
+
im.should(include('attributes_dataset'))
|
782
|
+
im.should(include('add_attribute'))
|
783
|
+
im.should(include('remove_attribute'))
|
784
|
+
im.should(include('remove_all_attributes'))
|
785
|
+
im2 = @c2.instance_methods(false).collect{|x| x.to_s}
|
786
|
+
im2.should_not(include('attributes'))
|
787
|
+
im2.should_not(include('attributes_dataset'))
|
788
|
+
im2.should_not(include('add_attribute'))
|
789
|
+
im2.should_not(include('remove_attribute'))
|
790
|
+
im2.should_not(include('remove_all_attributes'))
|
791
|
+
end
|
792
|
+
|
730
793
|
it "should have has_many alias" do
|
731
794
|
@c2.has_many :attributes, :class => @c1
|
732
795
|
|
@@ -777,6 +840,12 @@ describe Sequel::Model, "one_to_many" do
|
|
777
840
|
MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)'
|
778
841
|
end
|
779
842
|
|
843
|
+
it "should have the remove_all_ method respect the :primary_key option" do
|
844
|
+
@c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
|
845
|
+
@c2.new(:id => 1234, :xxx=>5).remove_all_attributes
|
846
|
+
MODEL_DB.sqls.first.should == 'UPDATE attributes SET node_id = NULL WHERE (node_id = 5)'
|
847
|
+
end
|
848
|
+
|
780
849
|
it "remove_all should set the cached instance variable to []" do
|
781
850
|
@c2.one_to_many :attributes, :class => @c1
|
782
851
|
node = @c2.new(:id => 1234)
|
@@ -828,7 +897,7 @@ describe Sequel::Model, "one_to_many" do
|
|
828
897
|
att.values.should == {}
|
829
898
|
end
|
830
899
|
|
831
|
-
it "should not add a
|
900
|
+
it "should not add a setter method if the :one_to_one option is true and :read_only option is true" do
|
832
901
|
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :read_only=>true
|
833
902
|
im = @c2.instance_methods.collect{|x| x.to_s}
|
834
903
|
im.should(include('attribute'))
|
@@ -861,6 +930,25 @@ describe Sequel::Model, "one_to_many" do
|
|
861
930
|
MODEL_DB.sqls.last.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))'
|
862
931
|
end
|
863
932
|
|
933
|
+
it "should have the setter method for the :one_to_one option respect the :primary_key option" do
|
934
|
+
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true, :primary_key=>:xxx
|
935
|
+
attrib = @c1.new(:id=>3)
|
936
|
+
d = @c1.dataset
|
937
|
+
def d.fetch_rows(s); yield({:id=>3}) end
|
938
|
+
@c2.new(:id => 1234, :xxx=>5).attribute = attrib
|
939
|
+
['INSERT INTO attributes (node_id, id) VALUES (5, 3)',
|
940
|
+
'INSERT INTO attributes (id, node_id) VALUES (3, 5)'].should(include(MODEL_DB.sqls.first))
|
941
|
+
MODEL_DB.sqls.last.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))'
|
942
|
+
MODEL_DB.sqls.length.should == 2
|
943
|
+
@c2.new(:id => 321, :xxx=>5).attribute.should == attrib
|
944
|
+
MODEL_DB.sqls.clear
|
945
|
+
attrib = @c1.load(:id=>3)
|
946
|
+
@c2.new(:id => 621, :xxx=>5).attribute = attrib
|
947
|
+
MODEL_DB.sqls.length.should == 2
|
948
|
+
MODEL_DB.sqls.first.should =~ /UPDATE attributes SET (node_id = 5, id = 3|id = 3, node_id = 5) WHERE \(id = 3\)/
|
949
|
+
MODEL_DB.sqls.last.should == 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))'
|
950
|
+
end
|
951
|
+
|
864
952
|
it "should raise an error if the one_to_one getter would be the same as the association name" do
|
865
953
|
proc{@c2.one_to_many :song, :class => @c1, :one_to_one=>true}.should raise_error(Sequel::Error)
|
866
954
|
end
|
@@ -873,7 +961,7 @@ describe Sequel::Model, "one_to_many" do
|
|
873
961
|
|
874
962
|
it "should make non getter and setter methods private if :one_to_one option is used" do
|
875
963
|
@c2.one_to_many :attributes, :class => @c1, :one_to_one=>true do |ds| end
|
876
|
-
meths = @c2.private_instance_methods
|
964
|
+
meths = @c2.private_instance_methods.collect{|x| x.to_s}
|
877
965
|
meths.should(include("attributes"))
|
878
966
|
meths.should(include("add_attribute"))
|
879
967
|
meths.should(include("attributes_dataset"))
|
@@ -994,6 +1082,7 @@ describe Sequel::Model, "many_to_many" do
|
|
994
1082
|
|
995
1083
|
@c1 = Class.new(Sequel::Model(:attributes)) do
|
996
1084
|
unrestrict_primary_key
|
1085
|
+
attr_accessor :yyy
|
997
1086
|
def self.name; 'Attribute'; end
|
998
1087
|
def self.to_s; 'Attribute'; end
|
999
1088
|
columns :id
|
@@ -1081,6 +1170,11 @@ describe Sequel::Model, "many_to_many" do
|
|
1081
1170
|
a.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.id) AND (attributes_nodes.node_id = 1234)) ORDER BY blah1, blah2'
|
1082
1171
|
end
|
1083
1172
|
|
1173
|
+
it "should support :left_primary_key and :right_primary_key options" do
|
1174
|
+
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
|
1175
|
+
@c2.new(:id => 1234, :xxx=>5).attributes_dataset.sql.should == 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.attribute_id = attributes.yyy) AND (attributes_nodes.node_id = 5))'
|
1176
|
+
end
|
1177
|
+
|
1084
1178
|
it "should support a select option" do
|
1085
1179
|
@c2.many_to_many :attributes, :class => @c1, :select => :blah
|
1086
1180
|
|
@@ -1182,6 +1276,26 @@ describe Sequel::Model, "many_to_many" do
|
|
1182
1276
|
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))'
|
1183
1277
|
end
|
1184
1278
|
|
1279
|
+
it "should have the add_ method respect the :left_primary_key and :right_primary_key options" do
|
1280
|
+
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
|
1281
|
+
|
1282
|
+
n = @c2.new(:id => 1234, :xxx=>5)
|
1283
|
+
a = @c1.new(:id => 2345, :yyy=>8)
|
1284
|
+
a.should == n.add_attribute(a)
|
1285
|
+
['INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (5, 8)',
|
1286
|
+
'INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (8, 5)'
|
1287
|
+
].should(include(MODEL_DB.sqls.first))
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
it "should have the remove_ method respect the :left_primary_key and :right_primary_key options" do
|
1291
|
+
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
|
1292
|
+
|
1293
|
+
n = @c2.new(:id => 1234, :xxx=>5)
|
1294
|
+
a = @c1.new(:id => 2345, :yyy=>8)
|
1295
|
+
a.should == n.remove_attribute(a)
|
1296
|
+
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))'
|
1297
|
+
end
|
1298
|
+
|
1185
1299
|
it "should raise an error if the model object doesn't have a valid primary key" do
|
1186
1300
|
@c2.many_to_many :attributes, :class => @c1
|
1187
1301
|
a = @c2.new
|
@@ -1300,6 +1414,22 @@ describe Sequel::Model, "many_to_many" do
|
|
1300
1414
|
im.should_not(include('remove_all_attributes'))
|
1301
1415
|
end
|
1302
1416
|
|
1417
|
+
it "should not add associations methods directly to class" do
|
1418
|
+
@c2.many_to_many :attributes, :class => @c1
|
1419
|
+
im = @c2.instance_methods.collect{|x| x.to_s}
|
1420
|
+
im.should(include('attributes'))
|
1421
|
+
im.should(include('attributes_dataset'))
|
1422
|
+
im.should(include('add_attribute'))
|
1423
|
+
im.should(include('remove_attribute'))
|
1424
|
+
im.should(include('remove_all_attributes'))
|
1425
|
+
im2 = @c2.instance_methods(false).collect{|x| x.to_s}
|
1426
|
+
im2.should_not(include('attributes'))
|
1427
|
+
im2.should_not(include('attributes_dataset'))
|
1428
|
+
im2.should_not(include('add_attribute'))
|
1429
|
+
im2.should_not(include('remove_attribute'))
|
1430
|
+
im2.should_not(include('remove_all_attributes'))
|
1431
|
+
end
|
1432
|
+
|
1303
1433
|
it "should have has_and_belongs_to_many alias" do
|
1304
1434
|
@c2.has_and_belongs_to_many :attributes, :class => @c1
|
1305
1435
|
|
@@ -1315,6 +1445,12 @@ describe Sequel::Model, "many_to_many" do
|
|
1315
1445
|
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 1234)'
|
1316
1446
|
end
|
1317
1447
|
|
1448
|
+
it "should have the remove_all_ method respect the :left_primary_key option" do
|
1449
|
+
@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx
|
1450
|
+
@c2.new(:id => 1234, :xxx=>5).remove_all_attributes
|
1451
|
+
MODEL_DB.sqls.first.should == 'DELETE FROM attributes_nodes WHERE (node_id = 5)'
|
1452
|
+
end
|
1453
|
+
|
1318
1454
|
it "remove_all should set the cached instance variable to []" do
|
1319
1455
|
@c2.many_to_many :attributes, :class => @c1
|
1320
1456
|
node = @c2.new(:id => 1234)
|
@@ -1463,6 +1599,20 @@ describe Sequel::Model, "many_to_many" do
|
|
1463
1599
|
p.remove_attribute(c).should == nil
|
1464
1600
|
p.attributes.should == [c]
|
1465
1601
|
end
|
1602
|
+
|
1603
|
+
it "should support a :uniq option that removes duplicates from the association" do
|
1604
|
+
h = []
|
1605
|
+
@c2.many_to_many :attributes, :class => @c1, :uniq=>true
|
1606
|
+
@c1.class_eval do
|
1607
|
+
def @dataset.fetch_rows(sql)
|
1608
|
+
yield({:id=>20})
|
1609
|
+
yield({:id=>30})
|
1610
|
+
yield({:id=>20})
|
1611
|
+
yield({:id=>30})
|
1612
|
+
end
|
1613
|
+
end
|
1614
|
+
@c2.load(:id=>10, :parent_id=>20).attributes.should == [@c1.load(:id=>20), @c1.load(:id=>30)]
|
1615
|
+
end
|
1466
1616
|
end
|
1467
1617
|
|
1468
1618
|
describe Sequel::Model, " association reflection methods" do
|
@@ -1514,4 +1664,15 @@ describe Sequel::Model, " association reflection methods" do
|
|
1514
1664
|
@c1.associate :one_to_many, :children, :class => @c1
|
1515
1665
|
@c1.associations.sort_by{|x|x.to_s}.should == [:children, :parent]
|
1516
1666
|
end
|
1667
|
+
|
1668
|
+
it "association reflections should be copied upon subclasing" do
|
1669
|
+
@c1.associate :many_to_one, :parent, :class => @c1
|
1670
|
+
c = Class.new(@c1)
|
1671
|
+
@c1.associations.should == [:parent]
|
1672
|
+
c.associations.should == [:parent]
|
1673
|
+
c.associate :many_to_one, :parent2, :class => @c1
|
1674
|
+
@c1.associations.should == [:parent]
|
1675
|
+
c.associations.sort_by{|x| x.to_s}.should == [:parent, :parent2]
|
1676
|
+
c.instance_methods.map{|x| x.to_s}.should include('parent')
|
1677
|
+
end
|
1517
1678
|
end
|
@@ -18,7 +18,7 @@ describe Sequel::Model, "#eager" do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class ::EagerBand < Sequel::Model(:bands)
|
21
|
-
columns :id
|
21
|
+
columns :id, :p_k
|
22
22
|
one_to_many :albums, :class=>'EagerAlbum', :key=>:band_id, :eager=>:tracks
|
23
23
|
one_to_many :graph_albums, :class=>'EagerAlbum', :key=>:band_id, :eager_graph=>:tracks
|
24
24
|
many_to_many :members, :class=>'EagerBandMember', :left_key=>:band_id, :right_key=>:member_id, :join_table=>:bm
|
@@ -38,7 +38,7 @@ describe Sequel::Model, "#eager" do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
class ::EagerGenre < Sequel::Model(:genres)
|
41
|
-
columns :id
|
41
|
+
columns :id, :xxx
|
42
42
|
many_to_many :albums, :class=>'EagerAlbum', :left_key=>:genre_id, :right_key=>:album_id, :join_table=>:ag
|
43
43
|
end
|
44
44
|
|
@@ -418,6 +418,47 @@ describe Sequel::Model, "#eager" do
|
|
418
418
|
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
|
419
419
|
end
|
420
420
|
|
421
|
+
it "should respect the association's :primary_key option" do
|
422
|
+
EagerAlbum.many_to_one :special_band, :class=>:EagerBand, :primary_key=>:p_k, :key=>:band_id
|
423
|
+
EagerBand.dataset.extend(Module.new {
|
424
|
+
def fetch_rows(sql)
|
425
|
+
MODEL_DB.sqls << sql
|
426
|
+
yield({:p_k=>2, :id=>1})
|
427
|
+
end
|
428
|
+
})
|
429
|
+
as = EagerAlbum.eager(:special_band).all
|
430
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE (bands.p_k IN (2))"]
|
431
|
+
as.length.should == 1
|
432
|
+
as.first.special_band.should == EagerBand.load(:p_k=>2, :id=>1)
|
433
|
+
MODEL_DB.sqls.clear
|
434
|
+
EagerAlbum.one_to_many :special_tracks, :class=>:EagerTrack, :primary_key=>:band_id, :key=>:album_id
|
435
|
+
EagerTrack.dataset.extend(Module.new {
|
436
|
+
def fetch_rows(sql)
|
437
|
+
MODEL_DB.sqls << sql
|
438
|
+
yield({:album_id=>2, :id=>1})
|
439
|
+
end
|
440
|
+
})
|
441
|
+
as = EagerAlbum.eager(:special_tracks).all
|
442
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (2))"]
|
443
|
+
as.length.should == 1
|
444
|
+
as.first.special_tracks.should == [EagerTrack.load(:album_id=>2, :id=>1)]
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
|
448
|
+
EagerAlbum.many_to_many :special_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_primary_key=>:xxx, :right_key=>:genre_id, :join_table=>:ag
|
449
|
+
EagerGenre.dataset.extend(Module.new {
|
450
|
+
def fetch_rows(sql)
|
451
|
+
MODEL_DB.sqls << sql
|
452
|
+
yield({:x_foreign_key_x=>2, :id=>5})
|
453
|
+
yield({:x_foreign_key_x=>2, :id=>6})
|
454
|
+
end
|
455
|
+
})
|
456
|
+
as = EagerAlbum.eager(:special_genres).all
|
457
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.xxx) AND (ag.album_id IN (2)))"]
|
458
|
+
as.length.should == 1
|
459
|
+
as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
|
460
|
+
end
|
461
|
+
|
421
462
|
it "should use the :eager_loader association option when eager loading" do
|
422
463
|
EagerAlbum.many_to_one :special_band, :eager_loader=>(proc do |key_hash, records, assocs|
|
423
464
|
item = EagerBand.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).order(:name).first
|
@@ -427,7 +468,7 @@ describe Sequel::Model, "#eager" do
|
|
427
468
|
items = EagerTrack.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).all
|
428
469
|
records.each{|r| r.associations[:special_tracks] = items}
|
429
470
|
end)
|
430
|
-
EagerAlbum.many_to_many :special_genres, :eager_loader=>(proc do |key_hash, records, assocs|
|
471
|
+
EagerAlbum.many_to_many :special_genres, :class=>:EagerGenre, :eager_loader=>(proc do |key_hash, records, assocs|
|
431
472
|
items = EagerGenre.inner_join(:ag, [:genre_id]).filter(:album_id=>records.collect{|r| r.pk}).all
|
432
473
|
records.each{|r| r.associations[:special_genres] = items}
|
433
474
|
end)
|
@@ -455,6 +496,16 @@ describe Sequel::Model, "#eager" do
|
|
455
496
|
MODEL_DB.sqls.length.should == 4
|
456
497
|
end
|
457
498
|
|
499
|
+
it "should respect :after_load callbacks on associations when eager loading" do
|
500
|
+
EagerAlbum.many_to_one :al_band, :class=>'EagerBand', :key=>:band_id, :after_load=>proc{|o, a| a.id *=2}
|
501
|
+
EagerAlbum.one_to_many :al_tracks, :class=>'EagerTrack', :key=>:album_id, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
|
502
|
+
EagerAlbum.many_to_many :al_genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
|
503
|
+
a = EagerAlbum.eager(:al_band, :al_tracks, :al_genres).all.first
|
504
|
+
a.should == EagerAlbum.load(:id => 1, :band_id => 2)
|
505
|
+
a.al_band.should == EagerBand.load(:id=>4)
|
506
|
+
a.al_tracks.should == [EagerTrack.load(:id=>6, :album_id=>1)]
|
507
|
+
a.al_genres.should == [EagerGenre.load(:id=>8)]
|
508
|
+
end
|
458
509
|
end
|
459
510
|
|
460
511
|
describe Sequel::Model, "#eager_graph" do
|
@@ -902,6 +953,42 @@ describe Sequel::Model, "#eager_graph" do
|
|
902
953
|
a[3].album.band.members.last.values.should == {:id => 6}
|
903
954
|
end
|
904
955
|
|
956
|
+
it "should respect the association's :primary_key option" do
|
957
|
+
GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :primary_key=>:vocalist_id
|
958
|
+
ds = GraphAlbum.eager_graph(:inner_band)
|
959
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, inner_band.id AS inner_band_id, inner_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS inner_band ON (inner_band.vocalist_id = albums.band_id)'
|
960
|
+
def ds.fetch_rows(sql, &block)
|
961
|
+
yield({:id=>3, :band_id=>2, :inner_band_id=>5, :vocalist_id=>2})
|
962
|
+
end
|
963
|
+
as = ds.all
|
964
|
+
as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
|
965
|
+
as.first.inner_band.should == GraphBand.load(:id=>5, :vocalist_id=>2)
|
966
|
+
|
967
|
+
GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :primary_key=>:band_id
|
968
|
+
ds = GraphAlbum.eager_graph(:right_tracks)
|
969
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.band_id)'
|
970
|
+
def ds.fetch_rows(sql, &block)
|
971
|
+
yield({:id=>3, :band_id=>2, :right_tracks_id=>5, :album_id=>2})
|
972
|
+
yield({:id=>3, :band_id=>2, :right_tracks_id=>6, :album_id=>2})
|
973
|
+
end
|
974
|
+
as = ds.all
|
975
|
+
as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
|
976
|
+
as.first.right_tracks.should == [GraphTrack.load(:id=>5, :album_id=>2), GraphTrack.load(:id=>6, :album_id=>2)]
|
977
|
+
end
|
978
|
+
|
979
|
+
it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
|
980
|
+
GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :left_primary_key=>:band_id, :right_key=>:genre_id, :right_primary_key=>:xxx, :join_table=>:ag
|
981
|
+
ds = GraphAlbum.eager_graph(:inner_genres)
|
982
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.band_id) LEFT OUTER JOIN genres AS inner_genres ON (inner_genres.xxx = ag.genre_id)'
|
983
|
+
def ds.fetch_rows(sql, &block)
|
984
|
+
yield({:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12})
|
985
|
+
yield({:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22})
|
986
|
+
end
|
987
|
+
as = ds.all
|
988
|
+
as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
|
989
|
+
as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
|
990
|
+
end
|
991
|
+
|
905
992
|
it "should respect the association's :graph_select option" do
|
906
993
|
GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_select=>:vocalist_id
|
907
994
|
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)'
|
@@ -970,6 +1057,17 @@ describe Sequel::Model, "#eager_graph" do
|
|
970
1057
|
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))"
|
971
1058
|
end
|
972
1059
|
|
1060
|
+
it "should respect the association's :eager_grapher option" do
|
1061
|
+
GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :eager_grapher=>proc{|ds, aa, ta| ds.graph(GraphBand, {:active=>true}, :table_alias=>aa, :join_type=>:inner)}
|
1062
|
+
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 INNER JOIN bands AS active_band ON (active_band.active = 't')"
|
1063
|
+
|
1064
|
+
GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :eager_grapher=>proc{|ds, aa, ta| ds.graph(GraphTrack, nil, :join_type=>:natural, :table_alias=>aa)}
|
1065
|
+
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 NATURAL JOIN tracks AS right_tracks'
|
1066
|
+
|
1067
|
+
GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :eager_grapher=>proc{|ds, aa, ta| ds.graph(:ag, {:album_id=>:id}, :table_alias=>:a123, :implicit_qualifier=>ta).graph(GraphGenre, [:album_id], :table_alias=>aa)}
|
1068
|
+
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 AS a123 ON (a123.album_id = albums.id) LEFT OUTER JOIN genres AS active_genres USING (album_id)"
|
1069
|
+
end
|
1070
|
+
|
973
1071
|
it "should respect the association's :graph_only_conditions option" do
|
974
1072
|
GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_only_conditions=>{:active=>true}
|
975
1073
|
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.active = 't')"
|
@@ -1026,4 +1124,10 @@ describe Sequel::Model, "#eager_graph" do
|
|
1026
1124
|
GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
|
1027
1125
|
GraphAlbum.eager_graph(:a_genres, :b_tracks).sql.should == 'SELECT albums.id, albums.band_id, a_genres.id AS a_genres_id, b_tracks.id AS b_tracks_id, b_tracks.album_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS a_genres ON (a_genres.id = ag.genre_id) LEFT OUTER JOIN tracks AS b_tracks ON (b_tracks.album_id = albums.id) ORDER BY a_genres.id, b_tracks.id, b_tracks.album_id'
|
1028
1126
|
end
|
1127
|
+
|
1128
|
+
it "should use the correct qualifier when graphing multiple tables with extra conditions" do
|
1129
|
+
GraphAlbum.many_to_many :a_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
|
1130
|
+
GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_conditions=>{:a=>:b}
|
1131
|
+
GraphAlbum.eager_graph(:a_genres, :b_tracks).sql.should == 'SELECT albums.id, albums.band_id, a_genres.id AS a_genres_id, b_tracks.id AS b_tracks_id, b_tracks.album_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS a_genres ON (a_genres.id = ag.genre_id) LEFT OUTER JOIN tracks AS b_tracks ON ((b_tracks.album_id = albums.id) AND (b_tracks.a = albums.b))'
|
1132
|
+
end
|
1029
1133
|
end
|