sequel 2.6.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/CHANGELOG +64 -0
  2. data/Rakefile +1 -1
  3. data/lib/sequel_core/adapters/jdbc.rb +6 -2
  4. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  5. data/lib/sequel_core/adapters/oracle.rb +4 -77
  6. data/lib/sequel_core/adapters/postgres.rb +39 -26
  7. data/lib/sequel_core/adapters/shared/mssql.rb +0 -1
  8. data/lib/sequel_core/adapters/shared/mysql.rb +1 -1
  9. data/lib/sequel_core/adapters/shared/oracle.rb +82 -0
  10. data/lib/sequel_core/adapters/shared/postgres.rb +65 -46
  11. data/lib/sequel_core/core_ext.rb +10 -0
  12. data/lib/sequel_core/core_sql.rb +7 -0
  13. data/lib/sequel_core/database.rb +22 -0
  14. data/lib/sequel_core/database/schema.rb +1 -1
  15. data/lib/sequel_core/dataset.rb +29 -11
  16. data/lib/sequel_core/dataset/sql.rb +27 -7
  17. data/lib/sequel_core/migration.rb +20 -2
  18. data/lib/sequel_core/object_graph.rb +24 -10
  19. data/lib/sequel_core/schema/generator.rb +22 -9
  20. data/lib/sequel_core/schema/sql.rb +13 -9
  21. data/lib/sequel_core/sql.rb +27 -2
  22. data/lib/sequel_model/association_reflection.rb +251 -141
  23. data/lib/sequel_model/associations.rb +114 -61
  24. data/lib/sequel_model/base.rb +25 -21
  25. data/lib/sequel_model/eager_loading.rb +17 -40
  26. data/lib/sequel_model/hooks.rb +25 -24
  27. data/lib/sequel_model/record.rb +29 -51
  28. data/lib/sequel_model/schema.rb +1 -1
  29. data/lib/sequel_model/validations.rb +13 -3
  30. data/spec/adapters/postgres_spec.rb +104 -18
  31. data/spec/adapters/spec_helper.rb +4 -1
  32. data/spec/integration/eager_loader_test.rb +5 -4
  33. data/spec/integration/spec_helper.rb +4 -1
  34. data/spec/sequel_core/connection_pool_spec.rb +24 -24
  35. data/spec/sequel_core/core_sql_spec.rb +12 -0
  36. data/spec/sequel_core/dataset_spec.rb +77 -2
  37. data/spec/sequel_core/expression_filters_spec.rb +6 -0
  38. data/spec/sequel_core/object_graph_spec.rb +40 -2
  39. data/spec/sequel_core/schema_spec.rb +13 -0
  40. data/spec/sequel_model/association_reflection_spec.rb +8 -8
  41. data/spec/sequel_model/associations_spec.rb +164 -3
  42. data/spec/sequel_model/caching_spec.rb +2 -1
  43. data/spec/sequel_model/eager_loading_spec.rb +107 -3
  44. data/spec/sequel_model/hooks_spec.rb +38 -22
  45. data/spec/sequel_model/model_spec.rb +11 -35
  46. data/spec/sequel_model/plugins_spec.rb +4 -2
  47. data/spec/sequel_model/record_spec.rb +8 -5
  48. data/spec/sequel_model/validations_spec.rb +25 -0
  49. data/spec/spec_config.rb +4 -3
  50. 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, "#associated_primary_key" do
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 :right_primary_key value if present" do
28
- @c.many_to_one :c, :class=>ParParent, :associated_primary_key=>:blah__blah
29
- @c.association_reflection(:c).should include(:associated_primary_key)
30
- @c.association_reflection(:c).associated_primary_key.should == :blah__blah
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 :associated_primary_key is not present" do
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(:associated_primary_key)
35
- @c.association_reflection(:c).associated_primary_key.should == :id
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 getter method if the :one_to_one option is true and :read_only option is true" do
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(false).collect{|x| x.to_s}
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
@@ -12,7 +12,8 @@ describe Sequel::Model, "caching" do
12
12
  cache = @cache_class.new
13
13
  @cache = cache
14
14
 
15
- @c = Class.new(Sequel::Model(:items)) do
15
+ @c = Class.new(Sequel::Model(:items))
16
+ @c.class_eval do
16
17
  set_cache cache
17
18
  def self.name; 'Item' end
18
19
 
@@ -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