sequel 4.7.0 → 4.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +46 -0
  3. data/README.rdoc +25 -1
  4. data/doc/active_record.rdoc +1 -1
  5. data/doc/advanced_associations.rdoc +143 -17
  6. data/doc/association_basics.rdoc +80 -59
  7. data/doc/release_notes/4.8.0.txt +175 -0
  8. data/lib/sequel/adapters/odbc.rb +1 -1
  9. data/lib/sequel/adapters/odbc/mssql.rb +4 -2
  10. data/lib/sequel/adapters/shared/postgres.rb +19 -3
  11. data/lib/sequel/adapters/shared/sqlite.rb +3 -3
  12. data/lib/sequel/ast_transformer.rb +1 -1
  13. data/lib/sequel/dataset/actions.rb +1 -1
  14. data/lib/sequel/dataset/graph.rb +23 -9
  15. data/lib/sequel/dataset/misc.rb +2 -2
  16. data/lib/sequel/dataset/sql.rb +3 -3
  17. data/lib/sequel/extensions/columns_introspection.rb +1 -1
  18. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +1 -1
  19. data/lib/sequel/extensions/pg_array.rb +1 -1
  20. data/lib/sequel/extensions/pg_array_ops.rb +6 -0
  21. data/lib/sequel/extensions/pg_hstore_ops.rb +7 -0
  22. data/lib/sequel/extensions/pg_json_ops.rb +5 -0
  23. data/lib/sequel/extensions/query.rb +8 -2
  24. data/lib/sequel/extensions/to_dot.rb +1 -1
  25. data/lib/sequel/model/associations.rb +476 -152
  26. data/lib/sequel/plugins/class_table_inheritance.rb +11 -3
  27. data/lib/sequel/plugins/dataset_associations.rb +21 -18
  28. data/lib/sequel/plugins/many_through_many.rb +87 -20
  29. data/lib/sequel/plugins/nested_attributes.rb +12 -0
  30. data/lib/sequel/plugins/pg_array_associations.rb +31 -12
  31. data/lib/sequel/plugins/single_table_inheritance.rb +9 -1
  32. data/lib/sequel/sql.rb +1 -0
  33. data/lib/sequel/version.rb +1 -1
  34. data/spec/adapters/mssql_spec.rb +2 -2
  35. data/spec/adapters/postgres_spec.rb +7 -0
  36. data/spec/core/object_graph_spec.rb +250 -196
  37. data/spec/extensions/core_refinements_spec.rb +1 -1
  38. data/spec/extensions/dataset_associations_spec.rb +100 -6
  39. data/spec/extensions/many_through_many_spec.rb +1002 -19
  40. data/spec/extensions/nested_attributes_spec.rb +24 -0
  41. data/spec/extensions/pg_array_associations_spec.rb +17 -12
  42. data/spec/extensions/pg_array_spec.rb +4 -2
  43. data/spec/extensions/spec_helper.rb +1 -1
  44. data/spec/integration/associations_test.rb +1003 -48
  45. data/spec/integration/dataset_test.rb +12 -5
  46. data/spec/integration/prepared_statement_test.rb +1 -1
  47. data/spec/integration/type_test.rb +1 -1
  48. data/spec/model/associations_spec.rb +467 -130
  49. data/spec/model/eager_loading_spec.rb +332 -5
  50. metadata +5 -3
@@ -7,6 +7,7 @@ describe Sequel::Model, "#eager" do
7
7
  many_to_one :band, :class=>'EagerBand', :key=>:band_id
8
8
  one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id
9
9
  many_to_many :genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
10
+ one_through_one :genre, :clone=>:genres
10
11
  one_to_many :good_tracks, :class=>'EagerTrack', :reciprocal=>nil, :key=>:album_id do |ds|
11
12
  ds.filter(:name=>'Good')
12
13
  end
@@ -168,10 +169,10 @@ describe Sequel::Model, "#eager" do
168
169
 
169
170
  it "should eagerly load a single one_to_one association using the :distinct_on strategy" do
170
171
  def (EagerTrack.dataset).supports_distinct_on?() true end
171
- EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true
172
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :order=>:a
172
173
  a = EagerAlbum.eager(:track).all
173
174
  a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
174
- DB.sqls.should == ['SELECT * FROM albums', 'SELECT DISTINCT ON (tracks.album_id) * FROM tracks WHERE (tracks.album_id IN (1)) ORDER BY tracks.album_id']
175
+ DB.sqls.should == ['SELECT * FROM albums', 'SELECT DISTINCT ON (tracks.album_id) * FROM tracks WHERE (tracks.album_id IN (1)) ORDER BY tracks.album_id, a']
175
176
  a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
176
177
  DB.sqls.should == []
177
178
  end
@@ -215,6 +216,52 @@ describe Sequel::Model, "#eager" do
215
216
  DB.sqls.should == []
216
217
  end
217
218
 
219
+ it "should eagerly load a single one_through_one association" do
220
+ a = EagerAlbum.eager(:genre).all
221
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
222
+ 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.id) AND (ag.album_id IN (1)))"]
223
+ a.first.genre.should == EagerGenre.load(:id=>4)
224
+ DB.sqls.should == []
225
+ end
226
+
227
+ it "should use first matching entry when eager loading one_through_one association" do
228
+ EagerGenre.dataset._fetch = [{:id => 3, :x_foreign_key_x=>1}, {:id => 4, :x_foreign_key_x=>1}]
229
+ a = EagerAlbum.eager(:genre).all
230
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
231
+ 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.id) AND (ag.album_id IN (1)))"]
232
+ a.first.genre.should == EagerGenre.load(:id=>3)
233
+ DB.sqls.should == []
234
+ end
235
+
236
+ it "should eagerly load a single one_through_one association using the :distinct_on strategy" do
237
+ def (EagerGenre.dataset).supports_distinct_on?() true end
238
+ EagerAlbum.one_through_one :genre, :clone=>:genre, :order=>:a
239
+ a = EagerAlbum.eager(:genre).all
240
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
241
+ DB.sqls.should == ['SELECT * FROM albums', "SELECT DISTINCT ON (ag.album_id) genres.*, 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))) ORDER BY ag.album_id, a"]
242
+ a.first.genre.should == EagerGenre.load(:id=>4)
243
+ DB.sqls.should == []
244
+ end
245
+
246
+ it "should eagerly load a single one_through_one association using the :window_function strategy" do
247
+ def (EagerGenre.dataset).supports_window_functions?() true end
248
+ EagerAlbum.one_through_one :genre, :clone=>:genre, :eager_limit_strategy=>true
249
+ a = EagerAlbum.eager(:genre).all
250
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
251
+ DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM (SELECT genres.*, ag.album_id AS x_foreign_key_x, row_number() OVER (PARTITION BY ag.album_id) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))) AS t1 WHERE (x_sequel_row_number_x = 1)"]
252
+ a.first.genre.should == EagerGenre.load(:id=>4)
253
+ DB.sqls.should == []
254
+ end
255
+
256
+ it "should automatically use an eager limit stategy if the association has an offset" do
257
+ EagerGenre.dataset._fetch = [{:id => 3, :x_foreign_key_x=>1}, {:id => 4, :x_foreign_key_x=>1}]
258
+ a = EagerAlbum.eager(:genre).all
259
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
260
+ 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.id) AND (ag.album_id IN (1)))"]
261
+ a.first.genre.should == EagerGenre.load(:id=>3)
262
+ DB.sqls.should == []
263
+ end
264
+
218
265
  it "should eagerly load a single many_to_many association" do
219
266
  a = EagerAlbum.eager(:genres).all
220
267
  a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
@@ -253,6 +300,16 @@ describe Sequel::Model, "#eager" do
253
300
  DB.sqls.should == []
254
301
  end
255
302
 
303
+ it "should support using a custom :left_primary_key option when eager loading one_through_one associations" do
304
+ EagerAlbum.one_through_one :sgenre, :clone=>:genre, :left_primary_key=>:band_id3
305
+ EagerGenre.dataset._fetch = {:id=>4, :x_foreign_key_x=>6}
306
+ a = EagerAlbum.eager(:sgenre).all
307
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
308
+ 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.id) AND (ag.album_id IN (6)))"]
309
+ a.first.sgenre.should == EagerGenre.load(:id=>4)
310
+ DB.sqls.should == []
311
+ end
312
+
256
313
  it "should handle a :eager_loading_predicate_key option to change the SQL used in the lookup, for many_to_one associations" do
257
314
  EagerAlbum.many_to_one :sband, :clone=>:band, :eager_loading_predicate_key=>Sequel./(:bands__id, 3), :primary_key_method=>:id3
258
315
  EagerBand.dataset._fetch = {:id=>6}
@@ -282,6 +339,15 @@ describe Sequel::Model, "#eager" do
282
339
  DB.sqls.should == []
283
340
  end
284
341
 
342
+ it "should handle a :eager_loading_predicate_key option to change the SQL used in the lookup, for one_through_one associations" do
343
+ EagerAlbum.one_through_one :sgenre, :clone=>:genre, :eager_loading_predicate_key=>Sequel.*(:ag__album_id, 1)
344
+ a = EagerAlbum.eager(:sgenre).all
345
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
346
+ DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, (ag.album_id * 1) AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND ((ag.album_id * 1) IN (1)))"]
347
+ a.first.sgenre.should == EagerGenre.load(:id=>4)
348
+ DB.sqls.should == []
349
+ end
350
+
285
351
  it "should raise an error for an unhandled :eager_loader_key option" do
286
352
  EagerAlbum.many_to_many :sgenres, :clone=>:genres, :eager_loader_key=>1
287
353
  ds = EagerAlbum.eager(:sgenres)
@@ -303,12 +369,24 @@ describe Sequel::Model, "#eager" do
303
369
  DB.sqls.should == ['SELECT * FROM albums', "SELECT *, 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)))"]
304
370
  end
305
371
 
372
+ it "should correctly handle a :select=>[] option to one_through_one" do
373
+ EagerAlbum.one_through_one :sgenre, :clone=>:genre, :select=>[]
374
+ EagerAlbum.eager(:sgenre).all
375
+ DB.sqls.should == ['SELECT * FROM albums', "SELECT *, 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)))"]
376
+ end
377
+
306
378
  it "should correctly handle an aliased join table in many_to_many" do
307
379
  EagerAlbum.many_to_many :sgenres, :clone=>:genres, :join_table=>:ag___ga
308
380
  EagerAlbum.eager(:sgenres).all
309
381
  DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ga.album_id AS x_foreign_key_x FROM genres INNER JOIN ag AS ga ON ((ga.genre_id = genres.id) AND (ga.album_id IN (1)))"]
310
382
  end
311
383
 
384
+ it "should correctly handle an aliased join table in one_through_one" do
385
+ EagerAlbum.one_through_one :sgenre, :clone=>:genre, :join_table=>:ag___ga
386
+ EagerAlbum.eager(:sgenre).all
387
+ DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ga.album_id AS x_foreign_key_x FROM genres INNER JOIN ag AS ga ON ((ga.genre_id = genres.id) AND (ga.album_id IN (1)))"]
388
+ end
389
+
312
390
  it "should eagerly load multiple associations in a single call" do
313
391
  a = EagerAlbum.eager(:genres, :tracks, :band).all
314
392
  a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
@@ -542,6 +620,15 @@ describe Sequel::Model, "#eager" do
542
620
  as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
543
621
  end
544
622
 
623
+ it "should respect one_through_one association's composite keys" do
624
+ EagerAlbum.one_through_one :special_genre, :class=>:EagerGenre, :left_primary_key=>[:band_id, :id], :left_key=>[:l1, :l2], :right_primary_key=>[:xxx, :id], :right_key=>[:r1, :r2], :join_table=>:ag
625
+ EagerGenre.dataset._fetch = [{:x_foreign_key_0_x=>2, :x_foreign_key_1_x=>1, :id=>5}]
626
+ as = EagerAlbum.eager(:special_genre).all
627
+ DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.l1 AS x_foreign_key_0_x, ag.l2 AS x_foreign_key_1_x FROM genres INNER JOIN ag ON ((ag.r1 = genres.xxx) AND (ag.r2 = genres.id) AND ((ag.l1, ag.l2) IN ((2, 1))))"]
628
+ as.length.should == 1
629
+ as.first.special_genre.should == EagerGenre.load(:id=>5)
630
+ end
631
+
545
632
  it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
546
633
  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
547
634
  EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
@@ -551,6 +638,15 @@ describe Sequel::Model, "#eager" do
551
638
  as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
552
639
  end
553
640
 
641
+ it "should respect one_through_one association's :left_primary_key and :right_primary_key options" do
642
+ EagerAlbum.one_through_one :special_genre, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_primary_key=>:xxx, :right_key=>:genre_id, :join_table=>:ag
643
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}]
644
+ as = EagerAlbum.eager(:special_genre).all
645
+ 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)))"]
646
+ as.length.should == 1
647
+ as.first.special_genre.should == EagerGenre.load(:id=>5)
648
+ end
649
+
554
650
  it "should respect the :limit option on a one_to_many association" do
555
651
  EagerAlbum.one_to_many :first_two_tracks, :class=>:EagerTrack, :key=>:album_id, :limit=>2
556
652
  EagerTrack.dataset._fetch = [{:album_id=>1, :id=>2}, {:album_id=>1, :id=>3}, {:album_id=>1, :id=>4}]
@@ -739,6 +835,14 @@ describe Sequel::Model, "#eager" do
739
835
  DB.sqls.should == []
740
836
  end
741
837
 
838
+ it "should eagerly load a one_through_one association with custom eager block" do
839
+ a = EagerAlbum.eager(:genre => proc {|ds| ds.select(:name)}).all
840
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
841
+ DB.sqls.should == ['SELECT * FROM albums', "SELECT name, 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)))"]
842
+ a.first.genre.should == EagerGenre.load(:id => 4)
843
+ DB.sqls.should == []
844
+ end
845
+
742
846
  it "should allow cascading of eager loading within a custom eager block" do
743
847
  a = EagerTrack.eager(:album => proc {|ds| ds.eager(:band => :members)}).all
744
848
  a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
@@ -807,7 +911,9 @@ describe Sequel::Model, "#eager_graph" do
807
911
  columns :id, :band_id
808
912
  many_to_one :band, :class=>'GraphBand', :key=>:band_id
809
913
  one_to_many :tracks, :class=>'GraphTrack', :key=>:album_id
914
+ one_to_one :track, :class=>'GraphTrack', :key=>:album_id
810
915
  many_to_many :genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
916
+ one_through_one :genre, :clone=>:genres
811
917
  many_to_one :previous_album, :class=>'GraphAlbum'
812
918
  end
813
919
 
@@ -909,6 +1015,21 @@ describe Sequel::Model, "#eager_graph" do
909
1015
  a.album.band.members.should == [GraphBandMember.load(:id => 5)]
910
1016
  end
911
1017
 
1018
+ it "should set up correct inner joins when using association_join" do
1019
+ GraphAlbum.association_join(:band).sql.should == 'SELECT * FROM albums INNER JOIN bands AS band ON (band.id = albums.band_id)'
1020
+ GraphAlbum.association_join(:track).sql.should == 'SELECT * FROM albums INNER JOIN tracks AS track ON (track.album_id = albums.id)'
1021
+ GraphAlbum.association_join(:tracks).sql.should == 'SELECT * FROM albums INNER JOIN tracks ON (tracks.album_id = albums.id)'
1022
+ GraphAlbum.association_join(:genres).sql.should == 'SELECT * FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres ON (genres.id = ag.genre_id)'
1023
+ GraphAlbum.association_join(:genre).sql.should == 'SELECT * FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres AS genre ON (genre.id = ag.genre_id)'
1024
+ end
1025
+
1026
+ it "should set up correct join types when using association_*_join" do
1027
+ GraphAlbum.association_inner_join(:band).sql.should == 'SELECT * FROM albums INNER JOIN bands AS band ON (band.id = albums.band_id)'
1028
+ GraphAlbum.association_left_join(:track).sql.should == 'SELECT * FROM albums LEFT JOIN tracks AS track ON (track.album_id = albums.id)'
1029
+ GraphAlbum.association_right_join(:tracks).sql.should == 'SELECT * FROM albums RIGHT JOIN tracks ON (tracks.album_id = albums.id)'
1030
+ GraphAlbum.association_full_join(:genres).sql.should == 'SELECT * FROM albums FULL JOIN ag ON (ag.album_id = albums.id) FULL JOIN genres ON (genres.id = ag.genre_id)'
1031
+ end
1032
+
912
1033
  it "should eagerly load a single many_to_one association" do
913
1034
  ds = GraphAlbum.eager_graph(:band)
914
1035
  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)'
@@ -929,8 +1050,16 @@ describe Sequel::Model, "#eager_graph" do
929
1050
  a.first.band_id.should == GraphBand.load(:id => 2, :vocalist_id=>3)
930
1051
  end
931
1052
 
1053
+ it "should support :join_type eager_graph option one_to_one association" do
1054
+ ds = GraphAlbum.eager_graph_with_options(:track, :join_type=>:inner)
1055
+ ds.sql.should == 'SELECT albums.id, albums.band_id, track.id AS track_id, track.album_id FROM albums INNER JOIN tracks AS track ON (track.album_id = albums.id)'
1056
+ ds._fetch = {:id=>1, :band_id=>2, :track_id=>3, :album_id=>1}
1057
+ a = ds.all
1058
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1059
+ a.first.track.should == GraphTrack.load(:id => 3, :album_id=>1)
1060
+ end
1061
+
932
1062
  it "should eagerly load a single one_to_one association" do
933
- GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
934
1063
  ds = GraphAlbum.eager_graph(:track)
935
1064
  ds.sql.should == 'SELECT albums.id, albums.band_id, track.id AS track_id, track.album_id FROM albums LEFT OUTER JOIN tracks AS track ON (track.album_id = albums.id)'
936
1065
  ds._fetch = {:id=>1, :band_id=>2, :track_id=>3, :album_id=>1}
@@ -939,6 +1068,32 @@ describe Sequel::Model, "#eager_graph" do
939
1068
  a.first.track.should == GraphTrack.load(:id => 3, :album_id=>1)
940
1069
  end
941
1070
 
1071
+ it "should eagerly graph a single one_to_one association using the :distinct_on strategy" do
1072
+ sub = Class.new(GraphTrack)
1073
+ def (sub.dataset).supports_distinct_on?() true end
1074
+ def (sub.dataset).columns() [:id, :album_id] end
1075
+ GraphAlbum.one_to_one :ltrack, :clone=>:track, :class=>sub
1076
+ ds = GraphAlbum.eager_graph_with_options(:ltrack, :limit_strategy=>true)
1077
+ ds.sql.should == 'SELECT albums.id, albums.band_id, ltrack.id AS ltrack_id, ltrack.album_id FROM albums LEFT OUTER JOIN (SELECT DISTINCT ON (tracks.album_id) * FROM tracks ORDER BY tracks.album_id) AS ltrack ON (ltrack.album_id = albums.id)'
1078
+ ds._fetch = {:id=>1, :band_id=>2, :ltrack_id=>3, :album_id=>1}
1079
+ a = ds.all
1080
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1081
+ a.first.ltrack.should == sub.load(:id => 3, :album_id=>1)
1082
+ end
1083
+
1084
+ it "should eagerly graph a single one_to_one association using the :window_function strategy" do
1085
+ sub = Class.new(GraphTrack)
1086
+ def (sub.dataset).supports_window_functions?() true end
1087
+ def (sub.dataset).columns() [:id, :album_id] end
1088
+ GraphAlbum.one_to_one :ltrack, :clone=>:track, :class=>sub
1089
+ ds = GraphAlbum.eager_graph_with_options(:ltrack, :limit_strategy=>true)
1090
+ ds.sql.should == 'SELECT albums.id, albums.band_id, ltrack.id AS ltrack_id, ltrack.album_id FROM albums LEFT OUTER JOIN (SELECT id, album_id FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x = 1)) AS ltrack ON (ltrack.album_id = albums.id)'
1091
+ ds._fetch = {:id=>1, :band_id=>2, :ltrack_id=>3, :album_id=>1}
1092
+ a = ds.all
1093
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1094
+ a.first.ltrack.should == sub.load(:id => 3, :album_id=>1)
1095
+ end
1096
+
942
1097
  it "should eagerly load a single one_to_many association" do
943
1098
  ds = GraphAlbum.eager_graph(:tracks)
944
1099
  ds.sql.should == 'SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)'
@@ -948,6 +1103,19 @@ describe Sequel::Model, "#eager_graph" do
948
1103
  a.first.tracks.should == [GraphTrack.load(:id => 3, :album_id=>1)]
949
1104
  end
950
1105
 
1106
+ it "should eagerly graph a single one_to_many association using the :window_function strategy" do
1107
+ sub = Class.new(GraphTrack)
1108
+ def (sub.dataset).supports_window_functions?() true end
1109
+ def (sub.dataset).columns() [:id, :album_id] end
1110
+ GraphAlbum.one_to_many :ltracks, :clone=>:tracks, :limit=>2, :class=>sub
1111
+ ds = GraphAlbum.eager_graph_with_options(:ltracks, :limit_strategy=>true)
1112
+ ds.sql.should == 'SELECT albums.id, albums.band_id, ltracks.id AS ltracks_id, ltracks.album_id FROM albums LEFT OUTER JOIN (SELECT id, album_id FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x <= 2)) AS ltracks ON (ltracks.album_id = albums.id)'
1113
+ ds._fetch = {:id=>1, :band_id=>2, :ltracks_id=>3, :album_id=>1}
1114
+ a = ds.all
1115
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1116
+ a.first.ltracks.should == [sub.load(:id => 3, :album_id=>1)]
1117
+ end
1118
+
951
1119
  it "should eagerly load a single many_to_many association" do
952
1120
  ds = GraphAlbum.eager_graph(:genres)
953
1121
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id)'
@@ -957,11 +1125,62 @@ describe Sequel::Model, "#eager_graph" do
957
1125
  a.first.genres.should == [GraphGenre.load(:id => 4)]
958
1126
  end
959
1127
 
960
- it "should correctly handle an aliased join table in many_to_many" do
1128
+ it "should eagerly graph a single many_to_many association using the :window_function strategy" do
1129
+ sub = Class.new(GraphGenre)
1130
+ def (sub.dataset).supports_window_functions?() true end
1131
+ def (sub.dataset).columns() literal(opts[:select]) =~ /x_foreign_key_x/ ? [:id, :x_foreign_key_x] : [:id] end
1132
+ GraphAlbum.many_to_many :lgenres, :clone=>:genres, :class=>sub, :limit=>2
1133
+ ds = GraphAlbum.eager_graph_with_options(:lgenres, :limit_strategy=>true)
1134
+ ds.sql.should == 'SELECT albums.id, albums.band_id, lgenres.id AS lgenres_id FROM albums LEFT OUTER JOIN (SELECT id, x_foreign_key_x FROM (SELECT genres.*, ag.album_id AS x_foreign_key_x, row_number() OVER (PARTITION BY ag.album_id) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON (ag.genre_id = genres.id)) AS t1 WHERE (x_sequel_row_number_x <= 2)) AS lgenres ON (lgenres.x_foreign_key_x = albums.id)'
1135
+ ds._fetch = {:id=>1, :band_id=>2, :lgenres_id=>4}
1136
+ a = ds.all
1137
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1138
+ a.first.lgenres.should == [sub.load(:id => 4)]
1139
+ end
1140
+
1141
+ it "should eagerly load a single one_through_one association" do
1142
+ ds = GraphAlbum.eager_graph(:genre)
1143
+ ds.sql.should == 'SELECT albums.id, albums.band_id, genre.id AS genre_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS genre ON (genre.id = ag.genre_id)'
1144
+ ds._fetch = {:id=>1, :band_id=>2, :genre_id=>4}
1145
+ a = ds.all
1146
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1147
+ a.first.genre.should == GraphGenre.load(:id => 4)
1148
+ end
1149
+
1150
+ it "should eagerly graph a single one_through_one association using the :distinct_on strategy" do
1151
+ sub = Class.new(GraphGenre)
1152
+ def (sub.dataset).supports_distinct_on?() true end
1153
+ def (sub.dataset).columns() [:id] end
1154
+ GraphAlbum.one_through_one :lgenre, :clone=>:genre, :class=>sub
1155
+ ds = GraphAlbum.eager_graph_with_options(:lgenre, :limit_strategy=>true)
1156
+ ds.sql.should == 'SELECT albums.id, albums.band_id, lgenre.id AS lgenre_id FROM albums LEFT OUTER JOIN (SELECT DISTINCT ON (ag.album_id) genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON (ag.genre_id = genres.id) ORDER BY ag.album_id) AS lgenre ON (lgenre.x_foreign_key_x = albums.id)'
1157
+ ds._fetch = {:id=>1, :band_id=>2, :lgenre_id=>4}
1158
+ a = ds.all
1159
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1160
+ a.first.lgenre.should == sub.load(:id => 4)
1161
+ end
1162
+
1163
+ it "should eagerly graph a single one_through_one association using the :window_function strategy" do
1164
+ sub = Class.new(GraphGenre)
1165
+ def (sub.dataset).supports_window_functions?() true end
1166
+ def (sub.dataset).columns() literal(opts[:select]) =~ /x_foreign_key_x/ ? [:id, :x_foreign_key_x] : [:id] end
1167
+ GraphAlbum.one_through_one :lgenre, :clone=>:genre, :class=>sub
1168
+ ds = GraphAlbum.eager_graph_with_options(:lgenre, :limit_strategy=>true)
1169
+ ds.sql.should == 'SELECT albums.id, albums.band_id, lgenre.id AS lgenre_id FROM albums LEFT OUTER JOIN (SELECT id, x_foreign_key_x FROM (SELECT genres.*, ag.album_id AS x_foreign_key_x, row_number() OVER (PARTITION BY ag.album_id) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON (ag.genre_id = genres.id)) AS t1 WHERE (x_sequel_row_number_x = 1)) AS lgenre ON (lgenre.x_foreign_key_x = albums.id)'
1170
+ ds._fetch = {:id=>1, :band_id=>2, :lgenre_id=>4}
1171
+ a = ds.all
1172
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1173
+ a.first.lgenre.should == sub.load(:id => 4)
1174
+ end
1175
+
1176
+ it "should correctly handle an aliased join table in many_to_many and one_through_one" do
961
1177
  c = Class.new(GraphAlbum)
962
1178
  c.many_to_many :genres, :clone=>:genres, :join_table=>:ag___ga
963
1179
  c.eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag AS ga ON (ga.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ga.genre_id)'
964
1180
 
1181
+ c.many_to_many :genre, :clone=>:genre, :join_table=>:ag___ga
1182
+ c.eager_graph(:genre).sql.should == 'SELECT albums.id, albums.band_id, genre.id AS genre_id FROM albums LEFT OUTER JOIN ag AS ga ON (ga.album_id = albums.id) LEFT OUTER JOIN genres AS genre ON (genre.id = ga.genre_id)'
1183
+
965
1184
  c.many_to_many :genres, :clone=>:genres, :join_table=>:ag___albums
966
1185
  c.eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag AS albums_0 ON (albums_0.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = albums_0.genre_id)'
967
1186
 
@@ -969,6 +1188,10 @@ describe Sequel::Model, "#eager_graph" do
969
1188
  c.eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag AS genres_0 ON (genres_0.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = genres_0.genre_id)'
970
1189
  end
971
1190
 
1191
+ it "should handle multiple associations in a single call to association_join" do
1192
+ GraphAlbum.association_join(:genres, :tracks, :band).sql.should == 'SELECT * FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres ON (genres.id = ag.genre_id) INNER JOIN tracks ON (tracks.album_id = albums.id) INNER JOIN bands AS band ON (band.id = albums.band_id)'
1193
+ end
1194
+
972
1195
  it "should eagerly load multiple associations in a single call" do
973
1196
  ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
974
1197
  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)'
@@ -981,6 +1204,28 @@ describe Sequel::Model, "#eager_graph" do
981
1204
  a.genres.should == [GraphGenre.load(:id => 4)]
982
1205
  end
983
1206
 
1207
+ it "should eagerly load multiple associations with different limit strategies in a single call" do
1208
+ subg = Class.new(GraphGenre)
1209
+ def (subg.dataset).supports_distinct_on?() true end
1210
+ def (subg.dataset).supports_window_functions?() true end
1211
+ def (subg.dataset).columns() literal(opts[:select]) =~ /x_foreign_key_x/ ? [:id, :x_foreign_key_x] : [:id] end
1212
+ GraphAlbum.one_through_one :lgenre, :clone=>:genre, :class=>subg
1213
+ GraphAlbum.many_to_many :lgenres, :clone=>:genres, :class=>subg, :limit=>2
1214
+
1215
+ ds = GraphAlbum.eager_graph_with_options([:lgenre, :lgenres], :limit_strategy=>{:lgenre=>:distinct_on, :lgenres=>:window_function})
1216
+ ds.sql.should == 'SELECT albums.id, albums.band_id, lgenre.id AS lgenre_id, lgenres.id AS lgenres_id FROM albums LEFT OUTER JOIN (SELECT DISTINCT ON (ag.album_id) genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON (ag.genre_id = genres.id) ORDER BY ag.album_id) AS lgenre ON (lgenre.x_foreign_key_x = albums.id) LEFT OUTER JOIN (SELECT id, x_foreign_key_x FROM (SELECT genres.*, ag.album_id AS x_foreign_key_x, row_number() OVER (PARTITION BY ag.album_id) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON (ag.genre_id = genres.id)) AS t1 WHERE (x_sequel_row_number_x <= 2)) AS lgenres ON (lgenres.x_foreign_key_x = albums.id)'
1217
+ ds._fetch = {:id=>1, :band_id=>2, :lgenres_id=>4, :lgenre_id=>3}
1218
+ a = ds.all
1219
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1220
+ a = a.first
1221
+ a.lgenre.should == subg.load(:id => 3)
1222
+ a.lgenres.should == [subg.load(:id => 4)]
1223
+ end
1224
+
1225
+ it "should handle multiple associations in separate calls to association_join" do
1226
+ GraphAlbum.association_join(:genres).association_join(:tracks).association_join(:band).sql.should == 'SELECT * FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres ON (genres.id = ag.genre_id) INNER JOIN tracks ON (tracks.album_id = albums.id) INNER JOIN bands AS band ON (band.id = albums.band_id)'
1227
+ end
1228
+
984
1229
  it "should eagerly load multiple associations in separate calls" do
985
1230
  ds = GraphAlbum.eager_graph(:genres).eager_graph(:tracks).eager_graph(:band)
986
1231
  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)'
@@ -993,6 +1238,15 @@ describe Sequel::Model, "#eager_graph" do
993
1238
  a.genres.should == [GraphGenre.load(:id => 4)]
994
1239
  end
995
1240
 
1241
+ it "should handle cascading associations in a single call to association_join" do
1242
+ GraphTrack.association_join(:album=>{:band=>:members}).sql.should == 'SELECT * FROM tracks INNER JOIN albums AS album ON (album.id = tracks.album_id) INNER JOIN bands AS band ON (band.id = album.band_id) INNER JOIN bm ON (bm.band_id = band.id) INNER JOIN members ON (members.id = bm.member_id)'
1243
+ GraphBand.association_join({:albums=>:tracks}, :members).sql.should == 'SELECT * FROM bands INNER JOIN albums ON (albums.band_id = bands.id) INNER JOIN tracks ON (tracks.album_id = albums.id) INNER JOIN bm ON (bm.band_id = bands.id) INNER JOIN members ON (members.id = bm.member_id)'
1244
+ end
1245
+
1246
+ it "should handle matching association names for different models when using association_join" do
1247
+ GraphAlbum.association_join(:genres).association_join(:band=>:genres).sql.should == 'SELECT * FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres ON (genres.id = ag.genre_id) INNER JOIN bands AS band ON (band.id = albums.band_id) INNER JOIN bg ON (bg.band_id = band.id) INNER JOIN genres AS genres_0 ON (genres_0.id = bg.genre_id)'
1248
+ end
1249
+
996
1250
  it "should allow cascading of eager loading for associations of associated models" do
997
1251
  ds = GraphTrack.eager_graph(:album=>{:band=>:members})
998
1252
  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)'
@@ -1125,6 +1379,15 @@ describe Sequel::Model, "#eager_graph" do
1125
1379
  a.first.band.should == nil
1126
1380
  end
1127
1381
 
1382
+ it "should handle no associated records for a single one_to_one association" do
1383
+ ds = GraphAlbum.eager_graph(:track)
1384
+ ds.sql.should == 'SELECT albums.id, albums.band_id, track.id AS track_id, track.album_id FROM albums LEFT OUTER JOIN tracks AS track ON (track.album_id = albums.id)'
1385
+ ds._fetch = {:id=>1, :band_id=>2, :track_id=>nil, :album_id=>nil}
1386
+ a = ds.all
1387
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1388
+ a.first.track.should == nil
1389
+ end
1390
+
1128
1391
  it "should handle no associated records for a single one_to_many association" do
1129
1392
  ds = GraphAlbum.eager_graph(:tracks)
1130
1393
  ds.sql.should == 'SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)'
@@ -1134,6 +1397,15 @@ describe Sequel::Model, "#eager_graph" do
1134
1397
  a.first.tracks.should == []
1135
1398
  end
1136
1399
 
1400
+ it "should handle no associated records for a single one_through_one association" do
1401
+ ds = GraphAlbum.eager_graph(:genre)
1402
+ ds.sql.should == 'SELECT albums.id, albums.band_id, genre.id AS genre_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS genre ON (genre.id = ag.genre_id)'
1403
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>nil}
1404
+ a = ds.all
1405
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1406
+ a.first.genre.should == nil
1407
+ end
1408
+
1137
1409
  it "should handle no associated records for a single many_to_many association" do
1138
1410
  ds = GraphAlbum.eager_graph(:genres)
1139
1411
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id)'
@@ -1391,6 +1663,11 @@ describe Sequel::Model, "#eager_graph" do
1391
1663
  GraphAlbum.order(:band_id).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 LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id) ORDER BY band_id, right_tracks.id, right_tracks.album_id'
1392
1664
  end
1393
1665
 
1666
+ it "should use the association's :graph_order in preference or order" do
1667
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:tracks__id, :tracks__album_id], :graph_order=>[:id, :album_id]
1668
+ GraphAlbum.order(:band_id).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 LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id) ORDER BY band_id, right_tracks.id, right_tracks.album_id'
1669
+ end
1670
+
1394
1671
  it "should add the association's :order for cascading associations" do
1395
1672
  GraphBand.one_to_many :a_albums, :class=>'GraphAlbum', :key=>:band_id, :order=>:name, :reciprocal=>nil
1396
1673
  GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
@@ -1480,6 +1757,48 @@ describe Sequel::Model, "#eager_graph" do
1480
1757
  a.al_genres.should == [GraphGenre.load(:id=>7), GraphGenre.load(:id=>12)]
1481
1758
  end
1482
1759
 
1760
+ it "should handle offsets on associations with no results when eager graphing" do
1761
+ GraphAlbum.one_to_many :al_tracks, :class=>GraphTrack, :key=>:album_id, :limit=>[2, 1]
1762
+ ds = GraphAlbum.eager_graph(:al_tracks)
1763
+ ds.sql.should == "SELECT albums.id, albums.band_id, al_tracks.id AS al_tracks_id, al_tracks.album_id FROM albums LEFT OUTER JOIN tracks AS al_tracks ON (al_tracks.album_id = albums.id)"
1764
+ ds._fetch = [{:id=>1, :band_id=>2, :al_tracks_id=>nil, :album_id=>nil}]
1765
+ a = ds.all.first
1766
+ a.should == GraphAlbum.load(:id => 1, :band_id => 2)
1767
+ a.al_tracks.should == []
1768
+ end
1769
+
1770
+ it "should respect offsets on associations when eager graphing" do
1771
+ GraphAlbum.many_to_one :al_band, :class=>GraphBand, :key=>:band_id
1772
+ GraphAlbum.one_to_many :al_tracks, :class=>GraphTrack, :key=>:album_id, :limit=>[1, 1]
1773
+ GraphAlbum.many_to_many :al_genres, :class=>GraphGenre, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>[1,1]
1774
+ ds = GraphAlbum.eager_graph(:al_band, :al_tracks, :al_genres)
1775
+ ds.sql.should == "SELECT albums.id, albums.band_id, al_band.id AS al_band_id, al_band.vocalist_id, al_tracks.id AS al_tracks_id, al_tracks.album_id, al_genres.id AS al_genres_id FROM albums LEFT OUTER JOIN bands AS al_band ON (al_band.id = albums.band_id) LEFT OUTER JOIN tracks AS al_tracks ON (al_tracks.album_id = albums.id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS al_genres ON (al_genres.id = ag.genre_id)"
1776
+ ds._fetch = [{:id=>1, :band_id=>2, :al_band_id=>3, :vocalist_id=>4, :al_tracks_id=>5, :album_id=>6, :al_genres_id=>7},
1777
+ {:id=>1, :band_id=>2, :al_band_id=>8, :vocalist_id=>9, :al_tracks_id=>10, :album_id=>11, :al_genres_id=>12},
1778
+ {:id=>1, :band_id=>2, :al_band_id=>13, :vocalist_id=>14, :al_tracks_id=>15, :album_id=>16, :al_genres_id=>17}]
1779
+ a = ds.all.first
1780
+ a.should == GraphAlbum.load(:id => 1, :band_id => 2)
1781
+ a.al_band.should == GraphBand.load(:id=>3, :vocalist_id=>4)
1782
+ a.al_tracks.should == [GraphTrack.load(:id=>10, :album_id=>11)]
1783
+ a.al_genres.should == [GraphGenre.load(:id=>12)]
1784
+ end
1785
+
1786
+ it "should respect offsets on associations when eager graphing one_to_one and one_through_one associations" do
1787
+ GraphAlbum.many_to_one :al_band, :class=>GraphBand, :key=>:band_id
1788
+ GraphAlbum.one_to_one :al_track, :class=>GraphTrack, :key=>:album_id, :limit=>[nil, 1]
1789
+ GraphAlbum.one_through_one :al_genre, :class=>GraphGenre, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>[nil,1]
1790
+ ds = GraphAlbum.eager_graph(:al_band, :al_track, :al_genre)
1791
+ ds.sql.should == "SELECT albums.id, albums.band_id, al_band.id AS al_band_id, al_band.vocalist_id, al_track.id AS al_track_id, al_track.album_id, al_genre.id AS al_genre_id FROM albums LEFT OUTER JOIN bands AS al_band ON (al_band.id = albums.band_id) LEFT OUTER JOIN tracks AS al_track ON (al_track.album_id = albums.id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS al_genre ON (al_genre.id = ag.genre_id)"
1792
+ ds._fetch = [{:id=>1, :band_id=>2, :al_band_id=>3, :vocalist_id=>4, :al_track_id=>5, :album_id=>6, :al_genre_id=>7},
1793
+ {:id=>1, :band_id=>2, :al_band_id=>8, :vocalist_id=>9, :al_track_id=>10, :album_id=>11, :al_genre_id=>12},
1794
+ {:id=>1, :band_id=>2, :al_band_id=>13, :vocalist_id=>14, :al_track_id=>15, :album_id=>16, :al_genre_id=>17}]
1795
+ a = ds.all.first
1796
+ a.should == GraphAlbum.load(:id => 1, :band_id => 2)
1797
+ a.al_band.should == GraphBand.load(:id=>3, :vocalist_id=>4)
1798
+ a.al_track.should == GraphTrack.load(:id=>10, :album_id=>11)
1799
+ a.al_genre.should == GraphGenre.load(:id=>12)
1800
+ end
1801
+
1483
1802
  it "should eagerly load a many_to_one association with a custom callback" do
1484
1803
  ds = GraphAlbum.eager_graph(:band => proc {|ds1| ds1.select(:id).columns(:id)})
1485
1804
  ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0 FROM albums LEFT OUTER JOIN (SELECT id FROM bands) AS band ON (band.id = albums.band_id)'
@@ -1490,7 +1809,6 @@ describe Sequel::Model, "#eager_graph" do
1490
1809
  end
1491
1810
 
1492
1811
  it "should eagerly load a one_to_one association with a custom callback" do
1493
- GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
1494
1812
  ds = GraphAlbum.eager_graph(:track => proc {|ds1| ds1.select(:album_id).columns(:album_id)})
1495
1813
  ds.sql.should == 'SELECT albums.id, albums.band_id, track.album_id FROM albums LEFT OUTER JOIN (SELECT album_id FROM tracks) AS track ON (track.album_id = albums.id)'
1496
1814
  ds._fetch = {:id=>1, :band_id=>2, :album_id=>1}
@@ -1508,6 +1826,15 @@ describe Sequel::Model, "#eager_graph" do
1508
1826
  a.first.tracks.should == [GraphTrack.load(:album_id=>1)]
1509
1827
  end
1510
1828
 
1829
+ it "should eagerly load a one_through_one association with a custom callback" do
1830
+ ds = GraphAlbum.eager_graph(:genre => proc {|ds1| ds1.select(:id).columns(:id)})
1831
+ ds.sql.should == 'SELECT albums.id, albums.band_id, genre.id AS genre_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN (SELECT id FROM genres) AS genre ON (genre.id = ag.genre_id)'
1832
+ ds._fetch = {:id=>1, :band_id=>2, :genre_id=>4}
1833
+ a = ds.all
1834
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1835
+ a.first.genre.should == GraphGenre.load(:id => 4)
1836
+ end
1837
+
1511
1838
  it "should eagerly load a many_to_many association with a custom callback" do
1512
1839
  ds = GraphAlbum.eager_graph(:genres => proc {|ds1| ds1.select(:id).columns(:id)})
1513
1840
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN (SELECT id FROM genres) AS genres ON (genres.id = ag.genre_id)'