sequel 3.21.0 → 3.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -1,9 +1,7 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe Sequel::Model, "#eager" do
4
- before(:each) do
5
- MODEL_DB.reset
6
-
4
+ before do
7
5
  class ::EagerAlbum < Sequel::Model(:albums)
8
6
  columns :id, :band_id
9
7
  many_to_one :band, :class=>'EagerBand', :key=>:band_id
@@ -48,63 +46,48 @@ describe Sequel::Model, "#eager" do
48
46
  many_to_many :bands, :class=>'EagerBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm, :order =>:id
49
47
  end
50
48
 
51
- EagerAlbum.dataset.extend(Module.new {
52
- def columns
53
- [:id, :band_id]
54
- end
55
-
56
- def fetch_rows(sql)
57
- h = if sql =~ /101/
58
- {:id => 101, :band_id=> 101}
59
- else
60
- {:id => 1, :band_id=> 2}
61
- end
62
- h.merge!(:x_foreign_key_x=>4) if sql =~ /ag\.genre_id/
63
- @db << sql
64
- yield h
49
+ EagerAlbum.dataset.columns(:id, :band_id)
50
+ EagerAlbum.dataset._fetch = proc do |sql|
51
+ h = if sql =~ /101/
52
+ {:id => 101, :band_id=> 101}
53
+ else
54
+ {:id => 1, :band_id=> 2}
65
55
  end
66
- })
56
+ h[:x_foreign_key_x] = 4 if sql =~ /ag\.genre_id/
57
+ h
58
+ end
67
59
 
68
- EagerBand.dataset.extend(Module.new {
69
- def fetch_rows(sql)
60
+ EagerBand.dataset._fetch = proc do |sql|
61
+ case sql
62
+ when /id IN (101)/
63
+ # nothing
64
+ when /id > 100/
65
+ [{:id => 101}, {:id => 102}]
66
+ else
70
67
  h = {:id => 2}
71
- h.merge!(:x_foreign_key_x=>5) if sql =~ /bm\.member_id/
72
- @db << sql
73
- case sql
74
- when /id IN (101)/
75
- when /id > 100/
76
- yield({:id => 101})
77
- yield({:id => 102})
78
- else
79
- yield h
80
- end
68
+ h[:x_foreign_key_x] = 5 if sql =~ /bm\.member_id/
69
+ h
81
70
  end
82
- })
71
+ end
83
72
 
84
- EagerTrack.dataset.extend(Module.new {
85
- def fetch_rows(sql)
86
- @db << sql
87
- yield({:id => 3, :album_id => 1})
88
- end
89
- })
73
+ EagerTrack.dataset._fetch = {:id => 3, :album_id => 1}
90
74
 
91
- EagerGenre.dataset.extend(Module.new {
92
- def fetch_rows(sql)
93
- h = {:id => 4}
94
- h.merge!(:x_foreign_key_x=>1) if sql =~ /ag\.album_id/
95
- @db << sql
96
- yield h
97
- end
98
- })
75
+ EagerGenre.dataset._fetch = proc do |sql|
76
+ h = {:id => 4}
77
+ h[:x_foreign_key_x] = 1 if sql =~ /ag\.album_id/
78
+ h
79
+ end
99
80
 
100
- EagerBandMember.dataset.extend(Module.new {
101
- def fetch_rows(sql)
102
- h = {:id => 5}
103
- h.merge!(:x_foreign_key_x=>2) if sql =~ /bm\.band_id/
104
- @db << sql
105
- yield h
106
- end
107
- })
81
+ EagerBandMember.dataset._fetch = proc do |sql|
82
+ h = {:id => 5}
83
+ h[:x_foreign_key_x] = 2 if sql =~ /bm\.band_id/
84
+ h
85
+ end
86
+
87
+ MODEL_DB.reset
88
+ end
89
+ after do
90
+ [:EagerAlbum, :EagerBand, :EagerTrack, :EagerGenre, :EagerBandMember].each{|x| Object.send(:remove_const, x)}
108
91
  end
109
92
 
110
93
  it "should raise an error if called without a symbol or hash" do
@@ -113,15 +96,10 @@ describe Sequel::Model, "#eager" do
113
96
 
114
97
  it "should eagerly load a single many_to_one association" do
115
98
  a = EagerAlbum.eager(:band).all
116
- a.should be_a_kind_of(Array)
117
- a.size.should == 1
118
- a.first.should be_a_kind_of(EagerAlbum)
119
- a.first.values.should == {:id => 1, :band_id => 2}
120
99
  MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM bands WHERE (bands.id IN (2))']
121
- a = a.first
122
- a.band.should be_a_kind_of(EagerBand)
123
- a.band.values.should == {:id => 2}
124
- MODEL_DB.sqls.length.should == 2
100
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
101
+ a.first.band.should == EagerBand.load(:id=>2)
102
+ MODEL_DB.sqls.should == []
125
103
  end
126
104
 
127
105
  it "should eagerly load a single one_to_one association" do
@@ -130,269 +108,203 @@ describe Sequel::Model, "#eager" do
130
108
  a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
131
109
  MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
132
110
  a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
133
- MODEL_DB.sqls.length.should == 2
111
+ MODEL_DB.sqls.should == []
112
+ end
113
+
114
+ it "should eagerly load a single one_to_one association using the :distinct_on strategy" do
115
+ EagerTrack.dataset.meta_def(:supports_distinct_on?){true}
116
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true
117
+ a = EagerAlbum.eager(:track).all
118
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
119
+ MODEL_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']
120
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
121
+ MODEL_DB.sqls.should == []
122
+ end
123
+
124
+ it "should eagerly load a single one_to_one association using the :window_function strategy" do
125
+ EagerTrack.dataset.meta_def(:supports_window_functions?){true}
126
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name
127
+ a = EagerAlbum.eager(:track).all
128
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
129
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE (x_sequel_row_number_x = 1)']
130
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
131
+ MODEL_DB.sqls.should == []
132
+ end
133
+
134
+ it "should eagerly load a single one_to_one association using the :correlated_subquery strategy" do
135
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name
136
+ a = EagerAlbum.eager(:track).all
137
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
138
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 1))) ORDER BY name']
139
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
140
+ MODEL_DB.sqls.should == []
141
+ end
142
+
143
+ it "should handle qualified order clauses when eagerly loading a single one_to_one association using the :correlated_subquery strategy" do
144
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>[:tracks__name, :tracks__name.desc, :name.qualify(:tracks), :name.qualify(:t), 1]
145
+ a = EagerAlbum.eager(:track).all
146
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
147
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY t1.name, t1.name DESC, t1.name, t.name, 1 LIMIT 1))) ORDER BY tracks.name, tracks.name DESC, tracks.name, t.name, 1']
148
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
149
+ MODEL_DB.sqls.should == []
150
+ end
151
+
152
+ it "should handle qualified composite keys when eagerly loading a single one_to_one association using the :correlated_subquery strategy" do
153
+ c1 = Class.new(EagerAlbum)
154
+ c2 = Class.new(EagerTrack)
155
+ c1.set_primary_key [:id, :band_id]
156
+ c2.set_primary_key [:id, :album_id]
157
+ c1.one_to_one :track, :class=>c2, :key=>[:album_id, :id], :eager_limit_strategy=>:correlated_subquery
158
+ c2.dataset._fetch = {:id => 2, :album_id=>1}
159
+ a = c1.eager(:track).all
160
+ a.should == [c1.load(:id => 1, :band_id => 2)]
161
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (((tracks.album_id, tracks.id) IN ((1, 2))) AND ((tracks.id, tracks.album_id) IN (SELECT t1.id, t1.album_id FROM tracks AS t1 WHERE ((t1.album_id = tracks.album_id) AND (t1.id = tracks.id)) LIMIT 1)))']
162
+ a.first.track.should == c2.load(:id => 2, :album_id=>1)
163
+ MODEL_DB.sqls.should == []
134
164
  end
135
165
 
136
166
  it "should eagerly load a single one_to_many association" do
137
167
  a = EagerAlbum.eager(:tracks).all
138
- a.should be_a_kind_of(Array)
139
- a.size.should == 1
140
- a.first.should be_a_kind_of(EagerAlbum)
141
- a.first.values.should == {:id => 1, :band_id => 2}
168
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
142
169
  MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
143
- a = a.first
144
- a.tracks.should be_a_kind_of(Array)
145
- a.tracks.size.should == 1
146
- a.tracks.first.should be_a_kind_of(EagerTrack)
147
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
148
- MODEL_DB.sqls.length.should == 2
170
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
171
+ MODEL_DB.sqls.should == []
149
172
  end
150
173
 
151
174
  it "should eagerly load a single many_to_many association" do
152
175
  a = EagerAlbum.eager(:genres).all
153
- a.should be_a_kind_of(Array)
154
- a.size.should == 1
155
- a.first.should be_a_kind_of(EagerAlbum)
156
- a.first.values.should == {:id => 1, :band_id => 2}
176
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
157
177
  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.id) AND (ag.album_id IN (1)))"]
158
- a = a.first
159
- a.genres.should be_a_kind_of(Array)
160
- a.genres.size.should == 1
161
- a.genres.first.should be_a_kind_of(EagerGenre)
162
- a.genres.first.values.should == {:id => 4}
163
- MODEL_DB.sqls.length.should == 2
178
+ a.first.genres.should == [EagerGenre.load(:id=>4)]
179
+ MODEL_DB.sqls.should == []
180
+ end
181
+
182
+ it "should correctly handle a :select=>[] option to many_to_many" do
183
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :select=>[]
184
+ a = EagerAlbum.eager(:sgenres).all
185
+ MODEL_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)))"]
186
+ end
187
+
188
+ it "should correctly handle an aliased join table in many_to_many" do
189
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :join_table=>:ag___ga
190
+ a = EagerAlbum.eager(:sgenres).all
191
+ MODEL_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)))"]
164
192
  end
165
193
 
166
194
  it "should eagerly load multiple associations in a single call" do
167
195
  a = EagerAlbum.eager(:genres, :tracks, :band).all
168
- a.should be_a_kind_of(Array)
169
- a.size.should == 1
170
- a.first.should be_a_kind_of(EagerAlbum)
171
- a.first.values.should == {:id => 1, :band_id => 2}
172
- MODEL_DB.sqls.length.should == 4
173
- MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
174
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM bands WHERE (bands.id IN (2))'))
175
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM tracks WHERE (tracks.album_id IN (1))'))
176
- MODEL_DB.sqls[1..-1].should(include('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)))'))
196
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
197
+ sqls = MODEL_DB.sqls
198
+ sqls.shift.should == 'SELECT * FROM albums'
199
+ sqls.sort.should == ['SELECT * FROM bands WHERE (bands.id IN (2))',
200
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))',
201
+ '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)))']
177
202
  a = a.first
178
- a.band.should be_a_kind_of(EagerBand)
179
- a.band.values.should == {:id => 2}
180
- a.tracks.should be_a_kind_of(Array)
181
- a.tracks.size.should == 1
182
- a.tracks.first.should be_a_kind_of(EagerTrack)
183
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
184
- a.genres.should be_a_kind_of(Array)
185
- a.genres.size.should == 1
186
- a.genres.first.should be_a_kind_of(EagerGenre)
187
- a.genres.first.values.should == {:id => 4}
188
- MODEL_DB.sqls.length.should == 4
203
+ a.band.should == EagerBand.load(:id=>2)
204
+ a.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
205
+ a.genres.should == [EagerGenre.load(:id => 4)]
206
+ MODEL_DB.sqls.should == []
189
207
  end
190
208
 
191
209
  it "should eagerly load multiple associations in separate calls" do
192
210
  a = EagerAlbum.eager(:genres).eager(:tracks).eager(:band).all
193
- a.should be_a_kind_of(Array)
194
- a.size.should == 1
195
- a.first.should be_a_kind_of(EagerAlbum)
196
- a.first.values.should == {:id => 1, :band_id => 2}
197
- MODEL_DB.sqls.length.should == 4
198
- MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
199
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM bands WHERE (bands.id IN (2))'))
200
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM tracks WHERE (tracks.album_id IN (1))'))
201
- MODEL_DB.sqls[1..-1].should(include('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)))'))
211
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
212
+ sqls = MODEL_DB.sqls
213
+ sqls.shift.should == 'SELECT * FROM albums'
214
+ sqls.sort.should == ['SELECT * FROM bands WHERE (bands.id IN (2))',
215
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))',
216
+ '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)))']
202
217
  a = a.first
203
- a.band.should be_a_kind_of(EagerBand)
204
- a.band.values.should == {:id => 2}
205
- a.tracks.should be_a_kind_of(Array)
206
- a.tracks.size.should == 1
207
- a.tracks.first.should be_a_kind_of(EagerTrack)
208
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
209
- a.genres.should be_a_kind_of(Array)
210
- a.genres.size.should == 1
211
- a.genres.first.should be_a_kind_of(EagerGenre)
212
- a.genres.first.values.should == {:id => 4}
213
- MODEL_DB.sqls.length.should == 4
218
+ a.band.should == EagerBand.load(:id=>2)
219
+ a.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
220
+ a.genres.should == [EagerGenre.load(:id => 4)]
221
+ MODEL_DB.sqls.should == []
214
222
  end
215
223
 
216
224
  it "should allow cascading of eager loading for associations of associated models" do
217
225
  a = EagerTrack.eager(:album=>{:band=>:members}).all
218
- a.should be_a_kind_of(Array)
219
- a.size.should == 1
220
- a.first.should be_a_kind_of(EagerTrack)
221
- a.first.values.should == {:id => 3, :album_id => 1}
222
- MODEL_DB.sqls.length.should == 4
226
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
223
227
  MODEL_DB.sqls.should == ['SELECT * FROM tracks',
224
228
  'SELECT * FROM albums WHERE (albums.id IN (1))',
225
229
  'SELECT * FROM bands WHERE (bands.id IN (2))',
226
230
  "SELECT members.*, bm.band_id AS x_foreign_key_x FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id IN (2)))"]
227
231
  a = a.first
228
- a.album.should be_a_kind_of(EagerAlbum)
229
- a.album.values.should == {:id => 1, :band_id => 2}
230
- a.album.band.should be_a_kind_of(EagerBand)
231
- a.album.band.values.should == {:id => 2}
232
- a.album.band.members.should be_a_kind_of(Array)
233
- a.album.band.members.size.should == 1
234
- a.album.band.members.first.should be_a_kind_of(EagerBandMember)
235
- a.album.band.members.first.values.should == {:id => 5}
236
- MODEL_DB.sqls.length.should == 4
232
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
233
+ a.album.band.should == EagerBand.load(:id => 2)
234
+ a.album.band.members.should == [EagerBandMember.load(:id => 5)]
235
+ MODEL_DB.sqls.should == []
237
236
  end
238
237
 
239
238
  it "should cascade eagerly loading when the :eager association option is used" do
240
239
  a = EagerBand.eager(:albums).all
241
- a.should be_a_kind_of(Array)
242
- a.size.should == 1
243
- a.first.should be_a_kind_of(EagerBand)
244
- a.first.values.should == {:id => 2}
240
+ a.should == [EagerBand.load(:id=>2)]
245
241
  MODEL_DB.sqls.should == ['SELECT * FROM bands',
246
- 'SELECT * FROM albums WHERE (albums.band_id IN (2))',
247
- 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
248
- a = a.first
249
- a.albums.should be_a_kind_of(Array)
250
- a.albums.size.should == 1
251
- a.albums.first.should be_a_kind_of(EagerAlbum)
252
- a.albums.first.values.should == {:id => 1, :band_id => 2}
253
- a = a.albums.first
254
- a.tracks.should be_a_kind_of(Array)
255
- a.tracks.size.should == 1
256
- a.tracks.first.should be_a_kind_of(EagerTrack)
257
- a.tracks.first.values.should == {:id => 3, :album_id => 1}
258
- MODEL_DB.sqls.length.should == 3
242
+ 'SELECT * FROM albums WHERE (albums.band_id IN (2))',
243
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
244
+ a.first.albums.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
245
+ a.first.albums.first.tracks.should == [EagerTrack.load(:id => 3, :album_id => 1)]
246
+ MODEL_DB.sqls.should == []
259
247
  end
260
248
 
261
249
  it "should respect :eager when lazily loading an association" do
262
250
  a = EagerBand.all
263
- a.should be_a_kind_of(Array)
264
- a.size.should == 1
265
- a.first.should be_a_kind_of(EagerBand)
266
- a.first.values.should == {:id => 2}
251
+ a.should == [EagerBand.load(:id=>2)]
267
252
  MODEL_DB.sqls.should == ['SELECT * FROM bands']
268
- a = a.first
269
- a.albums
270
- MODEL_DB.sqls.should == ['SELECT * FROM bands',
271
- 'SELECT * FROM albums WHERE (albums.band_id = 2)',
272
- 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
273
- a.albums.should be_a_kind_of(Array)
274
- a.albums.size.should == 1
275
- a.albums.first.should be_a_kind_of(EagerAlbum)
276
- a.albums.first.values.should == {:id => 1, :band_id => 2}
277
- a = a.albums.first
278
- a.tracks.should be_a_kind_of(Array)
279
- a.tracks.size.should == 1
280
- a.tracks.first.should be_a_kind_of(EagerTrack)
281
- a.tracks.first.values.should == {:id => 3, :album_id => 1}
282
- MODEL_DB.sqls.length.should == 3
253
+ a = a.first.albums
254
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (albums.band_id = 2)',
255
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
256
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
257
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id => 1)]
258
+ MODEL_DB.sqls.should == []
283
259
  end
284
260
 
285
261
  it "should cascade eagerly loading when the :eager_graph association option is used" do
286
- EagerAlbum.dataset.extend(Module.new {
287
- def fetch_rows(sql)
288
- @db << sql
289
- yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
290
- end
291
- })
262
+ EagerAlbum.dataset._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
292
263
  a = EagerBand.eager(:graph_albums).all
293
- a.should be_a_kind_of(Array)
294
- a.size.should == 1
295
- a.first.should be_a_kind_of(EagerBand)
296
- a.first.values.should == {:id => 2}
264
+ a.should == [EagerBand.load(:id=>2)]
297
265
  MODEL_DB.sqls.should == ['SELECT * FROM bands',
298
- '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) WHERE (albums.band_id IN (2))']
299
- a = a.first
300
- a.graph_albums.should be_a_kind_of(Array)
301
- a.graph_albums.size.should == 1
302
- a.graph_albums.first.should be_a_kind_of(EagerAlbum)
303
- a.graph_albums.first.values.should == {:id => 1, :band_id => 2}
304
- a = a.graph_albums.first
305
- a.tracks.should be_a_kind_of(Array)
306
- a.tracks.size.should == 1
307
- a.tracks.first.should be_a_kind_of(EagerTrack)
308
- a.tracks.first.values.should == {:id => 3, :album_id => 1}
309
- MODEL_DB.sqls.length.should == 2
266
+ '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) WHERE (albums.band_id IN (2))']
267
+ a.first.graph_albums.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
268
+ a.first.graph_albums.first.tracks.should == [EagerTrack.load(:id => 3, :album_id => 1)]
269
+ MODEL_DB.sqls.should == []
310
270
  end
311
271
 
312
- it "should cascade eagerly loading when the :eager_graph association option is used with a many_to_many association" do
313
- EagerBandMember.dataset.extend(Module.new {
314
- def columns
315
- [:id]
316
- end
317
- def fetch_rows(sql)
318
- @db << sql
319
- yield({:id=>5, :bands_id=>2, :p_k=>6, :x_foreign_key_x=>2})
320
- yield({:id=>5, :bands_id=>3, :p_k=>6, :x_foreign_key_x=>2})
321
- end
322
- })
323
- a = EagerBand.eager(:graph_members).all
324
- a.should == [EagerBand.load(:id=>2)]
325
- MODEL_DB.sqls.should == ['SELECT * FROM bands',
326
- 'SELECT members.id, bands.id AS bands_id, bands.p_k, bm.band_id AS x_foreign_key_x FROM (SELECT members.* FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id IN (2)))) AS members LEFT OUTER JOIN bm AS bm_0 ON (bm_0.member_id = members.id) LEFT OUTER JOIN bands ON (bands.id = bm_0.band_id) ORDER BY bands.id']
327
- a = a.first
328
- a.graph_members.should == [EagerBandMember.load(:id=>5)]
329
- a.graph_members.first.bands.should == [EagerBand.load(:id=>2, :p_k=>6), EagerBand.load(:id=>3, :p_k=>6)]
330
- MODEL_DB.sqls.length.should == 2
272
+ it "should raise an Error when eager loading a many_to_many association with the :eager_graph option" do
273
+ proc{EagerBand.eager(:graph_members).all}.should raise_error(Sequel::Error)
331
274
  end
332
275
 
333
276
  it "should respect :eager_graph when lazily loading an association" do
334
277
  a = EagerBand.all
335
- a.should be_a_kind_of(Array)
336
- a.size.should == 1
337
- a.first.should be_a_kind_of(EagerBand)
338
- a.first.values.should == {:id => 2}
278
+ a.should == [EagerBand.load(:id=>2)]
339
279
  MODEL_DB.sqls.should == ['SELECT * FROM bands']
340
280
  a = a.first
341
- EagerAlbum.dataset.extend(Module.new {
342
- def fetch_rows(sql)
343
- @db << sql
344
- yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
345
- end
346
- })
281
+ EagerAlbum.dataset._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
347
282
  a.graph_albums
348
- MODEL_DB.sqls.should == ['SELECT * FROM bands',
349
- '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) WHERE (albums.band_id = 2)']
350
- a.graph_albums.should be_a_kind_of(Array)
351
- a.graph_albums.size.should == 1
352
- a.graph_albums.first.should be_a_kind_of(EagerAlbum)
353
- a.graph_albums.first.values.should == {:id => 1, :band_id => 2}
354
- a = a.graph_albums.first
355
- a.tracks.should be_a_kind_of(Array)
356
- a.tracks.size.should == 1
357
- a.tracks.first.should be_a_kind_of(EagerTrack)
358
- a.tracks.first.values.should == {:id => 3, :album_id => 1}
359
- MODEL_DB.sqls.length.should == 2
283
+ MODEL_DB.sqls.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) WHERE (albums.band_id = 2)']
284
+ a.graph_albums.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
285
+ a.graph_albums.first.tracks.should == [EagerTrack.load(:id => 3, :album_id => 1)]
286
+ MODEL_DB.sqls.should == []
360
287
  end
361
288
 
362
289
  it "should respect :eager_graph when lazily loading a many_to_many association" do
363
- EagerBandMember.dataset.extend(Module.new {
364
- def columns
365
- [:id]
366
- end
367
- def fetch_rows(sql)
368
- @db << sql
369
- yield({:id=>5, :bands_id=>2, :p_k=>6})
370
- yield({:id=>5, :bands_id=>3, :p_k=>6})
371
- end
372
- })
290
+ ds = EagerBandMember.dataset
291
+ def ds.columns() [:id] end
292
+ ds._fetch = [{:id=>5, :bands_id=>2, :p_k=>6}, {:id=>5, :bands_id=>3, :p_k=>6}]
373
293
  a = EagerBand.load(:id=>2)
374
294
  a.graph_members.should == [EagerBandMember.load(:id=>5)]
375
295
  MODEL_DB.sqls.should == ['SELECT members.id, bands.id AS bands_id, bands.p_k FROM (SELECT members.* FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id = 2))) AS members LEFT OUTER JOIN bm AS bm_0 ON (bm_0.member_id = members.id) LEFT OUTER JOIN bands ON (bands.id = bm_0.band_id) ORDER BY bands.id']
376
296
  a.graph_members.first.bands.should == [EagerBand.load(:id=>2, :p_k=>6), EagerBand.load(:id=>3, :p_k=>6)]
377
- MODEL_DB.sqls.length.should == 1
297
+ MODEL_DB.sqls.should == []
378
298
  end
379
299
 
380
300
  it "should respect :conditions when eagerly loading" do
381
301
  EagerBandMember.many_to_many :good_bands, :clone=>:bands, :conditions=>{:a=>32}
382
302
  a = EagerBandMember.eager(:good_bands).all
383
- a.should be_a_kind_of(Array)
384
- a.size.should == 1
385
- a.first.should be_a_kind_of(EagerBandMember)
386
- a.first.values.should == {:id => 5}
303
+ a.should == [EagerBandMember.load(:id => 5)]
387
304
  MODEL_DB.sqls.should == ['SELECT * FROM members', 'SELECT bands.*, bm.member_id AS x_foreign_key_x FROM bands INNER JOIN bm ON ((bm.band_id = bands.id) AND (bm.member_id IN (5))) WHERE (a = 32) ORDER BY id']
388
- a = a.first
389
- a.good_bands.should be_a_kind_of(Array)
390
- a.good_bands.size.should == 1
391
- a.good_bands.first.should be_a_kind_of(EagerBand)
392
- a.good_bands.first.values.should == {:id => 2}
393
- MODEL_DB.sqls.length.should == 2
305
+ a.first.good_bands.should == [EagerBand.load(:id => 2)]
306
+ MODEL_DB.sqls.should == []
394
307
 
395
- MODEL_DB.sqls.clear
396
308
  EagerBandMember.many_to_many :good_bands, :clone=>:bands, :conditions=>"x = 1"
397
309
  a = EagerBandMember.eager(:good_bands).all
398
310
  MODEL_DB.sqls.should == ['SELECT * FROM members', 'SELECT bands.*, bm.member_id AS x_foreign_key_x FROM bands INNER JOIN bm ON ((bm.band_id = bands.id) AND (bm.member_id IN (5))) WHERE (x = 1) ORDER BY id']
@@ -400,63 +312,36 @@ describe Sequel::Model, "#eager" do
400
312
 
401
313
  it "should respect :order when eagerly loading" do
402
314
  a = EagerBandMember.eager(:bands).all
403
- a.should be_a_kind_of(Array)
404
- a.size.should == 1
405
- a.first.should be_a_kind_of(EagerBandMember)
406
- a.first.values.should == {:id => 5}
315
+ a.should == [EagerBandMember.load(:id => 5)]
407
316
  MODEL_DB.sqls.should == ['SELECT * FROM members', 'SELECT bands.*, bm.member_id AS x_foreign_key_x FROM bands INNER JOIN bm ON ((bm.band_id = bands.id) AND (bm.member_id IN (5))) ORDER BY id']
408
- a = a.first
409
- a.bands.should be_a_kind_of(Array)
410
- a.bands.size.should == 1
411
- a.bands.first.should be_a_kind_of(EagerBand)
412
- a.bands.first.values.should == {:id => 2}
413
- MODEL_DB.sqls.length.should == 2
317
+ a.first.bands.should == [EagerBand.load(:id => 2)]
318
+ MODEL_DB.sqls.should == []
414
319
  end
415
320
 
416
321
  it "should populate the reciprocal many_to_one association when eagerly loading the one_to_many association" do
417
322
  a = EagerAlbum.eager(:tracks).all
418
- a.should be_a_kind_of(Array)
419
- a.size.should == 1
420
- a.first.should be_a_kind_of(EagerAlbum)
421
- a.first.values.should == {:id => 1, :band_id => 2}
323
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
422
324
  MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
423
- a = a.first
424
- a.tracks.should be_a_kind_of(Array)
425
- a.tracks.size.should == 1
426
- a.tracks.first.should be_a_kind_of(EagerTrack)
427
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
428
- a.tracks.first.album.should be_a_kind_of(EagerAlbum)
429
- a.tracks.first.album.should == a
430
- MODEL_DB.sqls.length.should == 2
325
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
326
+ a.first.tracks.first.album.should == a.first
327
+ MODEL_DB.sqls.should == []
431
328
  end
432
329
 
433
330
  it "should cache the negative lookup when eagerly loading a many_to_one association" do
434
331
  a = EagerAlbum.eager(:band).filter(:id=>101).all
435
- a.should be_a_kind_of(Array)
436
- a.size.should == 1
437
- a.first.should be_a_kind_of(EagerAlbum)
438
- a.first.values.should == {:id => 101, :band_id => 101}
332
+ a.should == [EagerAlbum.load(:id => 101, :band_id => 101)]
439
333
  MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (id = 101)', 'SELECT * FROM bands WHERE (bands.id IN (101))']
440
- a = a.first
441
- a.associations.fetch(:band, 2).should == nil
442
- a.band.should == nil
443
- MODEL_DB.sqls.length.should == 2
334
+ a.first.associations.fetch(:band, 2).should be_nil
335
+ a.first.band.should be_nil
336
+ MODEL_DB.sqls.should == []
444
337
  end
445
338
 
446
339
  it "should cache the negative lookup when eagerly loading a *_to_many associations" do
447
340
  a = EagerBand.eager(:albums).filter('id > 100').all
448
- a.should be_a_kind_of(Array)
449
- a.size.should == 2
450
- a.first.should be_a_kind_of(EagerBand)
451
- a.first.values.should == {:id => 101}
452
- a.last.values.should == {:id => 102}
341
+ a.should == [EagerBand.load(:id => 101), EagerBand.load(:id =>102)]
453
342
  MODEL_DB.sqls.should == ['SELECT * FROM bands WHERE (id > 100)', 'SELECT * FROM albums WHERE (albums.band_id IN (101, 102))', "SELECT * FROM tracks WHERE (tracks.album_id IN (101))"]
454
- a.first.associations[:albums].should be_a_kind_of(Array)
455
- a.first.albums.length.should == 1
456
- a.first.albums.first.should be_a_kind_of(EagerAlbum)
457
- a.last.associations[:albums].should == []
458
- a.last.albums.should == []
459
- MODEL_DB.sqls.length.should == 3
343
+ a.map{|b| b.associations[:albums]}.should == [[EagerAlbum.load({:band_id=>101, :id=>101})], []]
344
+ MODEL_DB.sqls.should == []
460
345
  end
461
346
 
462
347
  it "should use the association's block when eager loading by default" do
@@ -467,7 +352,6 @@ describe Sequel::Model, "#eager" do
467
352
  it "should use the eager_block option when eager loading if given" do
468
353
  EagerBand.eager(:good_albums).all
469
354
  MODEL_DB.sqls.should == ['SELECT * FROM bands', "SELECT * FROM albums WHERE ((albums.band_id IN (2)) AND (name = 'good'))"]
470
- MODEL_DB.sqls.clear
471
355
  EagerBand.eager(:good_albums=>:good_tracks).all
472
356
  MODEL_DB.sqls.should == ['SELECT * FROM bands', "SELECT * FROM albums WHERE ((albums.band_id IN (2)) AND (name = 'good'))", "SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (name = 'Good'))"]
473
357
  end
@@ -480,34 +364,31 @@ describe Sequel::Model, "#eager" do
480
364
  it "should respect the association's :select option" do
481
365
  EagerAlbum.eager(:band_name).all
482
366
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM bands WHERE (bands.id IN (2))"]
483
- MODEL_DB.sqls.clear
484
367
  EagerAlbum.eager(:track_names).all
485
368
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM tracks WHERE (tracks.album_id IN (1))"]
486
- MODEL_DB.sqls.clear
487
369
  EagerAlbum.eager(:genre_names).all
488
370
  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)))"]
489
371
  end
490
372
 
373
+ it "should respect many_to_one association's :qualify option" do
374
+ EagerAlbum.many_to_one :special_band, :class=>:EagerBand, :qualify=>false, :key=>:band_id
375
+ EagerBand.dataset._fetch = {:id=>2}
376
+ as = EagerAlbum.eager(:special_band).all
377
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE (id IN (2))"]
378
+ as.map{|a| a.special_band}.should == [EagerBand.load(:id=>2)]
379
+ MODEL_DB.sqls.should == []
380
+ end
381
+
491
382
  it "should respect the association's :primary_key option" do
492
383
  EagerAlbum.many_to_one :special_band, :class=>:EagerBand, :primary_key=>:p_k, :key=>:band_id
493
- EagerBand.dataset.extend(Module.new {
494
- def fetch_rows(sql)
495
- MODEL_DB.sqls << sql
496
- yield({:p_k=>2, :id=>1})
497
- end
498
- })
384
+ EagerBand.dataset._fetch = {:p_k=>2, :id=>1}
499
385
  as = EagerAlbum.eager(:special_band).all
500
386
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE (bands.p_k IN (2))"]
501
387
  as.length.should == 1
502
388
  as.first.special_band.should == EagerBand.load(:p_k=>2, :id=>1)
503
- MODEL_DB.sqls.clear
389
+
504
390
  EagerAlbum.one_to_many :special_tracks, :class=>:EagerTrack, :primary_key=>:band_id, :key=>:album_id
505
- EagerTrack.dataset.extend(Module.new {
506
- def fetch_rows(sql)
507
- MODEL_DB.sqls << sql
508
- yield({:album_id=>2, :id=>1})
509
- end
510
- })
391
+ EagerTrack.dataset._fetch = {:album_id=>2, :id=>1}
511
392
  as = EagerAlbum.eager(:special_tracks).all
512
393
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (2))"]
513
394
  as.length.should == 1
@@ -516,12 +397,7 @@ describe Sequel::Model, "#eager" do
516
397
 
517
398
  it "should respect the many_to_one association's composite keys" do
518
399
  EagerAlbum.many_to_one :special_band, :class=>:EagerBand, :primary_key=>[:id, :p_k], :key=>[:band_id, :id]
519
- EagerBand.dataset.extend(Module.new {
520
- def fetch_rows(sql)
521
- MODEL_DB.sqls << sql
522
- yield({:p_k=>1, :id=>2})
523
- end
524
- })
400
+ EagerBand.dataset._fetch = {:p_k=>1, :id=>2}
525
401
  as = EagerAlbum.eager(:special_band).all
526
402
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE ((bands.id, bands.p_k) IN ((2, 1)))"]
527
403
  as.length.should == 1
@@ -530,12 +406,7 @@ describe Sequel::Model, "#eager" do
530
406
 
531
407
  it "should respect the one_to_many association's composite keys" do
532
408
  EagerAlbum.one_to_many :special_tracks, :class=>:EagerTrack, :primary_key=>[:band_id, :id], :key=>[:id, :album_id]
533
- EagerTrack.dataset.extend(Module.new {
534
- def fetch_rows(sql)
535
- MODEL_DB.sqls << sql
536
- yield({:album_id=>1, :id=>2})
537
- end
538
- })
409
+ EagerTrack.dataset._fetch = {:album_id=>1, :id=>2}
539
410
  as = EagerAlbum.eager(:special_tracks).all
540
411
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE ((tracks.id, tracks.album_id) IN ((2, 1)))"]
541
412
  as.length.should == 1
@@ -544,13 +415,7 @@ describe Sequel::Model, "#eager" do
544
415
 
545
416
  it "should respect many_to_many association's composite keys" do
546
417
  EagerAlbum.many_to_many :special_genres, :class=>:EagerGenre, :left_primary_key=>[:band_id, :id], :left_key=>[:l1, :l2], :right_primary_key=>[:xxx, :id], :right_key=>[:r1, :r2], :join_table=>:ag
547
- EagerGenre.dataset.extend(Module.new {
548
- def fetch_rows(sql)
549
- MODEL_DB.sqls << sql
550
- yield({:x_foreign_key_0_x=>2, :x_foreign_key_1_x=>1, :id=>5})
551
- yield({:x_foreign_key_0_x=>2, :x_foreign_key_1_x=>1, :id=>6})
552
- end
553
- })
418
+ EagerGenre.dataset._fetch = [{:x_foreign_key_0_x=>2, :x_foreign_key_1_x=>1, :id=>5}, {:x_foreign_key_0_x=>2, :x_foreign_key_1_x=>1, :id=>6}]
554
419
  as = EagerAlbum.eager(:special_genres).all
555
420
  MODEL_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))))"]
556
421
  as.length.should == 1
@@ -559,19 +424,116 @@ describe Sequel::Model, "#eager" do
559
424
 
560
425
  it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
561
426
  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
562
- EagerGenre.dataset.extend(Module.new {
563
- def fetch_rows(sql)
564
- MODEL_DB.sqls << sql
565
- yield({:x_foreign_key_x=>2, :id=>5})
566
- yield({:x_foreign_key_x=>2, :id=>6})
567
- end
568
- })
427
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
569
428
  as = EagerAlbum.eager(:special_genres).all
570
429
  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)))"]
571
430
  as.length.should == 1
572
431
  as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
573
432
  end
574
433
 
434
+ it "should respect the :limit option on a one_to_many association" do
435
+ EagerAlbum.one_to_many :first_two_tracks, :class=>:EagerTrack, :key=>:album_id, :limit=>2
436
+ EagerTrack.dataset._fetch = [{:album_id=>1, :id=>2}, {:album_id=>1, :id=>3}, {:album_id=>1, :id=>4}]
437
+ as = EagerAlbum.eager(:first_two_tracks).all
438
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (1))"]
439
+ as.length.should == 1
440
+ as.first.first_two_tracks.should == [EagerTrack.load(:album_id=>1, :id=>2), EagerTrack.load(:album_id=>1, :id=>3)]
441
+
442
+ MODEL_DB.reset
443
+ EagerAlbum.one_to_many :first_two_tracks, :class=>:EagerTrack, :key=>:album_id, :limit=>[2,1]
444
+ as = EagerAlbum.eager(:first_two_tracks).all
445
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (1))"]
446
+ as.length.should == 1
447
+ as.first.first_two_tracks.should == [EagerTrack.load(:album_id=>1, :id=>3), EagerTrack.load(:album_id=>1, :id=>4)]
448
+ end
449
+
450
+ it "should respect the :limit option on a one_to_many association using the :window_function strategy" do
451
+ EagerTrack.dataset.meta_def(:supports_window_functions?){true}
452
+ EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name, :limit=>2
453
+ a = EagerAlbum.eager(:tracks).all
454
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
455
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE (x_sequel_row_number_x <= 2)']
456
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
457
+ MODEL_DB.sqls.should == []
458
+ end
459
+
460
+ it "should respect the :limit option with an offset on a one_to_many association using the :window_function strategy" do
461
+ EagerTrack.dataset.meta_def(:supports_window_functions?){true}
462
+ EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name, :limit=>[2, 1]
463
+ a = EagerAlbum.eager(:tracks).all
464
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
465
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))']
466
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
467
+ MODEL_DB.sqls.should == []
468
+ end
469
+
470
+ it "should respect the :limit option on a one_to_many association using the :correlated_subquery strategy" do
471
+ EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name, :limit=>2
472
+ a = EagerAlbum.eager(:tracks).all
473
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
474
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 2))) ORDER BY name']
475
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
476
+ MODEL_DB.sqls.should == []
477
+ end
478
+
479
+ it "should respect the :limit option with an offset on a one_to_many association using the :correlated_subquery strategy" do
480
+ EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name, :limit=>[2, 1]
481
+ a = EagerAlbum.eager(:tracks).all
482
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
483
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 2 OFFSET 1))) ORDER BY name']
484
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
485
+ MODEL_DB.sqls.should == []
486
+ end
487
+
488
+ it "should respect the limit option on a many_to_many association" do
489
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>2
490
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}, {:x_foreign_key_x=>2, :id=>7}]
491
+ as = EagerAlbum.eager(:first_two_genres).all
492
+ 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.id) AND (ag.album_id IN (2)))"]
493
+ as.length.should == 1
494
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
495
+
496
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}, {:x_foreign_key_x=>2, :id=>7}]
497
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>[2, 1]
498
+ as = EagerAlbum.eager(:first_two_genres).all
499
+ 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.id) AND (ag.album_id IN (2)))"]
500
+ as.length.should == 1
501
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>6), EagerGenre.load(:id=>7)]
502
+ end
503
+
504
+ it "should respect the limit option on a many_to_many association using the :window_function strategy" do
505
+ EagerGenre.dataset.meta_def(:supports_window_functions?){true}
506
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>true, :limit=>2, :order=>:name
507
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
508
+ as = EagerAlbum.eager(:first_two_genres).all
509
+ MODEL_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 ORDER BY name) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))) AS t1 WHERE (x_sequel_row_number_x <= 2)"]
510
+ as.length.should == 1
511
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
512
+
513
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
514
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>true, :limit=>[2, 1], :order=>:name
515
+ as = EagerAlbum.eager(:first_two_genres).all
516
+ MODEL_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 ORDER BY name) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))"]
517
+ as.length.should == 1
518
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
519
+ end
520
+
521
+ it "should respect the limit option on a many_to_many association using the :correlated_subquery strategy" do
522
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>:correlated_subquery, :limit=>2, :order=>:name
523
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
524
+ as = EagerAlbum.eager(:first_two_genres).all
525
+ 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.id) AND (ag.album_id IN (2))) WHERE (genres.id IN (SELECT t1.id FROM genres AS t1 INNER JOIN ag AS t2 ON ((t2.genre_id = t1.id) AND (t2.album_id = ag.album_id)) ORDER BY name LIMIT 2)) ORDER BY name"]
526
+ as.length.should == 1
527
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
528
+
529
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
530
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>:correlated_subquery, :limit=>[2, 1], :order=>:name
531
+ as = EagerAlbum.eager(:first_two_genres).all
532
+ 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.id) AND (ag.album_id IN (2))) WHERE (genres.id IN (SELECT t1.id FROM genres AS t1 INNER JOIN ag AS t2 ON ((t2.genre_id = t1.id) AND (t2.album_id = ag.album_id)) ORDER BY name LIMIT 2 OFFSET 1)) ORDER BY name"]
533
+ as.length.should == 1
534
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
535
+ end
536
+
575
537
  it "should use the :eager_loader association option when eager loading" do
576
538
  EagerAlbum.many_to_one :special_band, :eager_loader=>(proc do |key_hash, records, assocs|
577
539
  item = EagerBand.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).order(:name).first
@@ -586,27 +548,17 @@ describe Sequel::Model, "#eager" do
586
548
  records.each{|r| r.associations[:special_genres] = items}
587
549
  end)
588
550
  a = EagerAlbum.eager(:special_genres, :special_tracks, :special_band).all
589
- a.should be_a_kind_of(Array)
590
- a.size.should == 1
591
- a.first.should be_a_kind_of(EagerAlbum)
592
- a.first.values.should == {:id => 1, :band_id => 2}
593
- MODEL_DB.sqls.length.should == 4
594
- MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
595
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM bands WHERE (album_id IN (1, 2)) ORDER BY name LIMIT 1'))
596
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM tracks WHERE (album_id IN (1, 2))'))
597
- MODEL_DB.sqls[1..-1].should(include('SELECT * FROM genres INNER JOIN ag USING (genre_id) WHERE (album_id IN (1))'))
551
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
552
+ sqls = MODEL_DB.sqls
553
+ sqls.shift.should == 'SELECT * FROM albums'
554
+ sqls.sort.should == ['SELECT * FROM bands WHERE (album_id IN (1, 2)) ORDER BY name LIMIT 1',
555
+ 'SELECT * FROM genres INNER JOIN ag USING (genre_id) WHERE (album_id IN (1))',
556
+ 'SELECT * FROM tracks WHERE (album_id IN (1, 2))']
598
557
  a = a.first
599
- a.special_band.should be_a_kind_of(EagerBand)
600
- a.special_band.values.should == {:id => 2}
601
- a.special_tracks.should be_a_kind_of(Array)
602
- a.special_tracks.size.should == 1
603
- a.special_tracks.first.should be_a_kind_of(EagerTrack)
604
- a.special_tracks.first.values.should == {:id => 3, :album_id=>1}
605
- a.special_genres.should be_a_kind_of(Array)
606
- a.special_genres.size.should == 1
607
- a.special_genres.first.should be_a_kind_of(EagerGenre)
608
- a.special_genres.first.values.should == {:id => 4}
609
- MODEL_DB.sqls.length.should == 4
558
+ a.special_band.should == EagerBand.load(:id => 2)
559
+ a.special_tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
560
+ a.special_genres.should == [EagerGenre.load(:id => 4)]
561
+ MODEL_DB.sqls.should == []
610
562
  end
611
563
 
612
564
  it "should raise an error if you use an :eager_loader proc with the wrong arity" do
@@ -626,13 +578,7 @@ describe Sequel::Model, "#eager" do
626
578
 
627
579
  it "should respect :uniq option when eagerly loading many_to_many associations" do
628
580
  EagerAlbum.many_to_many :al_genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :uniq=>true
629
- EagerGenre.dataset.extend(Module.new {
630
- def fetch_rows(sql)
631
- MODEL_DB.sqls << sql
632
- yield({:x_foreign_key_x=>1, :id=>8})
633
- yield({:x_foreign_key_x=>1, :id=>8})
634
- end
635
- })
581
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>1, :id=>8}, {:x_foreign_key_x=>1, :id=>8}]
636
582
  a = EagerAlbum.eager(:al_genres).all.first
637
583
  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.id) AND (ag.album_id IN (1)))"]
638
584
  a.should == EagerAlbum.load(:id => 1, :band_id => 2)
@@ -646,26 +592,103 @@ describe Sequel::Model, "#eager" do
646
592
  a.should == EagerAlbum.load(:id => 1, :band_id => 2)
647
593
  a.al_genres.should == [EagerGenre.load(:id=>4)]
648
594
  end
649
- end
650
595
 
651
- describe Sequel::Model, "#eager_graph" do
652
- after(:all) do
653
- class ::MockDataset
654
- alias clone orig_clone
655
- end
596
+ it "should eagerly load a many_to_one association with custom eager block" do
597
+ a = EagerAlbum.eager(:band => proc {|ds| ds.select(:id, :name)}).all
598
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
599
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id, name FROM bands WHERE (bands.id IN (2))']
600
+ a.first.band.should == EagerBand.load(:id => 2)
601
+ MODEL_DB.sqls.should == []
656
602
  end
657
603
 
658
- before(:all) do
659
- class ::MockDataset
660
- alias orig_clone clone
661
- def clone(opts = {})
662
- c = super()
663
- c.opts = @opts.merge(opts)
664
- c.instance_variable_set(:@columns, (@columns.dup if @columns))
665
- c
666
- end
667
- end
604
+ it "should eagerly load a one_to_one association with custom eager block" do
605
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id
606
+ a = EagerAlbum.eager(:track => proc {|ds| ds.select(:id)}).all
607
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
608
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id FROM tracks WHERE (tracks.album_id IN (1))']
609
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
610
+ MODEL_DB.sqls.should == []
611
+ end
668
612
 
613
+ it "should eagerly load a one_to_many association with custom eager block" do
614
+ a = EagerAlbum.eager(:tracks => proc {|ds| ds.select(:id)}).all
615
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
616
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id FROM tracks WHERE (tracks.album_id IN (1))']
617
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
618
+ MODEL_DB.sqls.should == []
619
+ end
620
+
621
+ it "should eagerly load a many_to_many association with custom eager block" do
622
+ a = EagerAlbum.eager(:genres => proc {|ds| ds.select(:name)}).all
623
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
624
+ MODEL_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)))"]
625
+ a.first.genres.should == [EagerGenre.load(:id => 4)]
626
+ MODEL_DB.sqls.should == []
627
+ end
628
+
629
+ it "should allow cascading of eager loading within a custom eager block" do
630
+ a = EagerTrack.eager(:album => proc {|ds| ds.eager(:band => :members)}).all
631
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
632
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
633
+ 'SELECT * FROM albums WHERE (albums.id IN (1))',
634
+ 'SELECT * FROM bands WHERE (bands.id IN (2))',
635
+ "SELECT members.*, bm.band_id AS x_foreign_key_x FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id IN (2)))"]
636
+ a = a.first
637
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
638
+ a.album.band.should == EagerBand.load(:id => 2)
639
+ a.album.band.members.should == [EagerBandMember.load(:id => 5)]
640
+ MODEL_DB.sqls.should == []
641
+ end
642
+
643
+ it "should allow cascading of eager loading with custom callback with hash value" do
644
+ a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>{:band => :members}}).all
645
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
646
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
647
+ 'SELECT id, band_id FROM albums WHERE (albums.id IN (1))',
648
+ 'SELECT * FROM bands WHERE (bands.id IN (2))',
649
+ "SELECT members.*, bm.band_id AS x_foreign_key_x FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id IN (2)))"]
650
+ a = a.first
651
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
652
+ a.album.band.should == EagerBand.load(:id => 2)
653
+ a.album.band.members.should == [EagerBandMember.load(:id => 5)]
654
+ MODEL_DB.sqls.should == []
655
+ end
656
+
657
+ it "should allow cascading of eager loading with custom callback with symbol value" do
658
+ a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>:band}).all
659
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
660
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
661
+ 'SELECT id, band_id FROM albums WHERE (albums.id IN (1))',
662
+ 'SELECT * FROM bands WHERE (bands.id IN (2))']
663
+ a = a.first
664
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
665
+ a.album.band.should == EagerBand.load(:id => 2)
666
+ MODEL_DB.sqls.should == []
667
+ end
668
+
669
+ it "should allow cascading of eager loading with custom callback with array value" do
670
+ a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>[:band, :band_name]}).all
671
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
672
+ sqls = MODEL_DB.sqls
673
+ sqls.slice!(0..1).should == ['SELECT * FROM tracks',
674
+ 'SELECT id, band_id FROM albums WHERE (albums.id IN (1))']
675
+ sqls.sort.should == ['SELECT * FROM bands WHERE (bands.id IN (2))',
676
+ 'SELECT id, name FROM bands WHERE (bands.id IN (2))']
677
+ a = a.first
678
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
679
+ a.album.band.should == EagerBand.load(:id => 2)
680
+ a.album.band_name.should == EagerBand.load(:id => 2)
681
+ MODEL_DB.sqls.should == []
682
+ end
683
+
684
+ it "should call both association and custom eager blocks" do
685
+ EagerBand.eager(:good_albums => proc {|ds| ds.select(:name)}).all
686
+ MODEL_DB.sqls.should == ['SELECT * FROM bands', "SELECT name FROM albums WHERE ((albums.band_id IN (2)) AND (name = 'good'))"]
687
+ end
688
+ end
689
+
690
+ describe Sequel::Model, "#eager_graph" do
691
+ before(:all) do
669
692
  class ::GraphAlbum < Sequel::Model(:albums)
670
693
  dataset.opts[:from] = [:albums]
671
694
  columns :id, :band_id
@@ -702,6 +725,9 @@ describe Sequel::Model, "#eager_graph" do
702
725
  many_to_many :bands, :class=>'GraphBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm
703
726
  end
704
727
  end
728
+ after(:all) do
729
+ [:GraphAlbum, :GraphBand, :GraphTrack, :GraphGenre, :GraphBandMember].each{|x| Object.send(:remove_const, x)}
730
+ end
705
731
 
706
732
  it "should raise an error if called without a symbol or hash" do
707
733
  proc{GraphAlbum.eager_graph(Object.new)}.should raise_error(Sequel::Error)
@@ -710,35 +736,24 @@ describe Sequel::Model, "#eager_graph" do
710
736
  it "should not split results and assign associations if ungraphed is called" do
711
737
  ds = GraphAlbum.eager_graph(:band).ungraphed
712
738
  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)'
713
- def ds.fetch_rows(sql, &block)
714
- yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
715
- end
739
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
716
740
  ds.all.should == [GraphAlbum.load(:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3)]
717
741
  end
718
742
 
719
743
  it "should eagerly load a single many_to_one association" do
720
744
  ds = GraphAlbum.eager_graph(:band)
721
745
  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)'
722
- def ds.fetch_rows(sql, &block)
723
- yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
724
- end
746
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
725
747
  a = ds.all
726
- a.should be_a_kind_of(Array)
727
- a.size.should == 1
728
- a.first.should be_a_kind_of(GraphAlbum)
729
- a.first.values.should == {:id => 1, :band_id => 2}
730
- a = a.first
731
- a.band.should be_a_kind_of(GraphBand)
732
- a.band.values.should == {:id => 2, :vocalist_id=>3}
748
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
749
+ a.first.band.should == GraphBand.load(:id => 2, :vocalist_id=>3)
733
750
  end
734
751
 
735
752
  it "should eagerly load a single one_to_one association" do
736
753
  GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
737
754
  ds = GraphAlbum.eager_graph(:track)
738
755
  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)'
739
- def ds.fetch_rows(sql, &block)
740
- yield({:id=>1, :band_id=>2, :track_id=>3, :album_id=>1})
741
- end
756
+ ds._fetch = {:id=>1, :band_id=>2, :track_id=>3, :album_id=>1}
742
757
  a = ds.all
743
758
  a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
744
759
  a.first.track.should == GraphTrack.load(:id => 3, :album_id=>1)
@@ -747,130 +762,88 @@ describe Sequel::Model, "#eager_graph" do
747
762
  it "should eagerly load a single one_to_many association" do
748
763
  ds = GraphAlbum.eager_graph(:tracks)
749
764
  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)'
750
- def ds.fetch_rows(sql, &block)
751
- yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
752
- end
765
+ ds._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
753
766
  a = ds.all
754
- a.should be_a_kind_of(Array)
755
- a.size.should == 1
756
- a.first.should be_a_kind_of(GraphAlbum)
757
- a.first.values.should == {:id => 1, :band_id => 2}
758
- a = a.first
759
- a.tracks.should be_a_kind_of(Array)
760
- a.tracks.size.should == 1
761
- a.tracks.first.should be_a_kind_of(GraphTrack)
762
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
767
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
768
+ a.first.tracks.should == [GraphTrack.load(:id => 3, :album_id=>1)]
763
769
  end
764
770
 
765
771
  it "should eagerly load a single many_to_many association" do
766
772
  ds = GraphAlbum.eager_graph(:genres)
767
773
  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)'
768
- def ds.fetch_rows(sql, &block)
769
- yield({:id=>1, :band_id=>2, :genres_id=>4})
770
- end
774
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>4}
771
775
  a = ds.all
772
- a.should be_a_kind_of(Array)
773
- a.size.should == 1
774
- a.first.should be_a_kind_of(GraphAlbum)
775
- a.first.values.should == {:id => 1, :band_id => 2}
776
- a = a.first
777
- a.genres.should be_a_kind_of(Array)
778
- a.genres.size.should == 1
779
- a.genres.first.should be_a_kind_of(GraphGenre)
780
- a.genres.first.values.should == {:id => 4}
776
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
777
+ a.first.genres.should == [GraphGenre.load(:id => 4)]
781
778
  end
782
779
 
780
+ it "should correctly handle an aliased join table in many_to_many" do
781
+ c = Class.new(GraphAlbum)
782
+ c.many_to_many :genres, :clone=>:genres, :join_table=>:ag___ga
783
+ 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)'
784
+
785
+ c.many_to_many :genres, :clone=>:genres, :join_table=>:ag___albums
786
+ 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)'
787
+
788
+ c.many_to_many :genres, :clone=>:genres, :join_table=>:ag___genres
789
+ 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)'
790
+ end
791
+
783
792
  it "should eagerly load multiple associations in a single call" do
784
793
  ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
785
794
  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)'
786
- def ds.fetch_rows(sql, &block)
787
- yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
788
- end
795
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6}
789
796
  a = ds.all
790
- a.should be_a_kind_of(Array)
791
- a.size.should == 1
792
- a.first.should be_a_kind_of(GraphAlbum)
793
- a.first.values.should == {:id => 1, :band_id => 2}
797
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
794
798
  a = a.first
795
- a.band.should be_a_kind_of(GraphBand)
796
- a.band.values.should == {:id => 2, :vocalist_id=>6}
797
- a.tracks.should be_a_kind_of(Array)
798
- a.tracks.size.should == 1
799
- a.tracks.first.should be_a_kind_of(GraphTrack)
800
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
801
- a.genres.should be_a_kind_of(Array)
802
- a.genres.size.should == 1
803
- a.genres.first.should be_a_kind_of(GraphGenre)
804
- a.genres.first.values.should == {:id => 4}
799
+ a.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
800
+ a.tracks.should == [GraphTrack.load({:id => 3, :album_id=>1})]
801
+ a.genres.should == [GraphGenre.load(:id => 4)]
805
802
  end
806
803
 
807
804
  it "should eagerly load multiple associations in separate calls" do
808
805
  ds = GraphAlbum.eager_graph(:genres).eager_graph(:tracks).eager_graph(:band)
809
806
  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)'
810
- def ds.fetch_rows(sql, &block)
811
- yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
812
- end
807
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6}
813
808
  a = ds.all
814
- a.should be_a_kind_of(Array)
815
- a.size.should == 1
816
- a.first.should be_a_kind_of(GraphAlbum)
817
- a.first.values.should == {:id => 1, :band_id => 2}
809
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
818
810
  a = a.first
819
- a.band.should be_a_kind_of(GraphBand)
820
- a.band.values.should == {:id => 2, :vocalist_id=>6}
821
- a.tracks.should be_a_kind_of(Array)
822
- a.tracks.size.should == 1
823
- a.tracks.first.should be_a_kind_of(GraphTrack)
824
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
825
- a.genres.should be_a_kind_of(Array)
826
- a.genres.size.should == 1
827
- a.genres.first.should be_a_kind_of(GraphGenre)
828
- a.genres.first.values.should == {:id => 4}
811
+ a.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
812
+ a.tracks.should == [GraphTrack.load({:id => 3, :album_id=>1})]
813
+ a.genres.should == [GraphGenre.load(:id => 4)]
829
814
  end
830
815
 
831
816
  it "should allow cascading of eager loading for associations of associated models" do
832
817
  ds = GraphTrack.eager_graph(:album=>{:band=>:members})
833
818
  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)'
834
- def ds.fetch_rows(sql, &block)
835
- yield({:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6})
836
- end
819
+ ds._fetch = {:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6}
837
820
  a = ds.all
838
- a.should be_a_kind_of(Array)
839
- a.size.should == 1
840
- a.first.should be_a_kind_of(GraphTrack)
841
- a.first.values.should == {:id => 3, :album_id => 1}
821
+ a.should == [GraphTrack.load(:id => 3, :album_id => 1)]
842
822
  a = a.first
843
- a.album.should be_a_kind_of(GraphAlbum)
844
- a.album.values.should == {:id => 1, :band_id => 2}
845
- a.album.band.should be_a_kind_of(GraphBand)
846
- a.album.band.values.should == {:id => 2, :vocalist_id=>6}
847
- a.album.band.members.should be_a_kind_of(Array)
848
- a.album.band.members.size.should == 1
849
- a.album.band.members.first.should be_a_kind_of(GraphBandMember)
850
- a.album.band.members.first.values.should == {:id => 5}
823
+ a.album.should == GraphAlbum.load(:id => 1, :band_id => 2)
824
+ a.album.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
825
+ a.album.band.members.should == [GraphBandMember.load(:id => 5)]
851
826
  end
852
827
 
853
828
  it "should allow cascading of eager loading for multiple *_to_many associations, eliminating duplicates caused by cartesian products" do
854
829
  ds = GraphBand.eager_graph({:albums=>:tracks}, :members)
855
830
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, albums.id AS albums_id, albums.band_id, tracks.id AS tracks_id, tracks.album_id, members.id AS members_id FROM bands LEFT OUTER JOIN albums ON (albums.band_id = bands.id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
856
- def ds.fetch_rows(sql, &block)
857
- yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>5})
858
- yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>6})
859
- yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>5})
860
- yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>6})
861
- yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>5})
862
- yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>6})
863
- yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>5})
864
- yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>6})
865
- yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>5})
866
- yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>6})
867
- yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>5})
868
- yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>6})
869
- yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>5})
870
- yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>6})
871
- yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>5})
872
- yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>6})
873
- end
831
+ ds._fetch = [{:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>5},
832
+ {:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>6},
833
+ {:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>5},
834
+ {:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>6},
835
+ {:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>5},
836
+ {:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>6},
837
+ {:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>5},
838
+ {:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>6},
839
+ {:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>5},
840
+ {:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>6},
841
+ {:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>5},
842
+ {:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>6},
843
+ {:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>5},
844
+ {:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>6},
845
+ {:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>5},
846
+ {:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>6}]
874
847
  a = ds.all
875
848
  a.should == [GraphBand.load(:id=>1, :vocalist_id=>2), GraphBand.load(:id=>2, :vocalist_id=>2)]
876
849
  members = a.map{|x| x.members}
@@ -885,85 +858,49 @@ describe Sequel::Model, "#eager_graph" do
885
858
  MODEL_DB.reset
886
859
  ds = GraphAlbum.eager_graph(:tracks)
887
860
  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)'
888
- def ds.fetch_rows(sql, &block)
889
- @db << sql
890
- yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
891
- end
861
+ ds._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
892
862
  a = ds.all
893
- a.should be_a_kind_of(Array)
894
- a.size.should == 1
895
- a.first.should be_a_kind_of(GraphAlbum)
896
- a.first.values.should == {:id => 1, :band_id => 2}
863
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
897
864
  a = a.first
898
- a.tracks.should be_a_kind_of(Array)
899
- a.tracks.size.should == 1
900
- a.tracks.first.should be_a_kind_of(GraphTrack)
901
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
902
- a.tracks.first.album.should be_a_kind_of(GraphAlbum)
865
+ a.tracks.should == [GraphTrack.load(:id => 3, :album_id=>1)]
903
866
  a.tracks.first.album.should == a
904
- MODEL_DB.sqls.length.should == 1
867
+ MODEL_DB.sqls.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)']
905
868
  end
906
869
 
907
870
  it "should eager load multiple associations from the same table" do
908
871
  ds = GraphBand.eager_graph(:vocalist, :members)
909
872
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, vocalist.id AS vocalist_id_0, members.id AS members_id FROM bands LEFT OUTER JOIN members AS vocalist ON (vocalist.id = bands.vocalist_id) LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
910
- def ds.fetch_rows(sql, &block)
911
- yield({:id=>2, :vocalist_id=>6, :vocalist_id_0=>6, :members_id=>5})
912
- end
873
+ ds._fetch = {:id=>2, :vocalist_id=>6, :vocalist_id_0=>6, :members_id=>5}
913
874
  a = ds.all
914
- a.should be_a_kind_of(Array)
915
- a.size.should == 1
916
- a.first.should be_a_kind_of(GraphBand)
917
- a.first.values.should == {:id => 2, :vocalist_id => 6}
875
+ a.should == [GraphBand.load(:id => 2, :vocalist_id => 6)]
918
876
  a = a.first
919
- a.vocalist.should be_a_kind_of(GraphBandMember)
920
- a.vocalist.values.should == {:id => 6}
921
- a.members.should be_a_kind_of(Array)
922
- a.members.size.should == 1
923
- a.members.first.should be_a_kind_of(GraphBandMember)
924
- a.members.first.values.should == {:id => 5}
877
+ a.vocalist.should == GraphBandMember.load(:id => 6)
878
+ a.members.should == [GraphBandMember.load(:id => 5)]
925
879
  end
926
880
 
927
- it "should give you a graph of tables when called without .all" do
881
+ it "should give you a plain hash when called without .all" do
928
882
  ds = GraphAlbum.eager_graph(:band)
929
883
  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)'
930
- def ds.fetch_rows(sql, &block)
931
- yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
932
- end
933
- ds.first.should == {:albums=>GraphAlbum.load(:id => 1, :band_id => 2), :band=>GraphBand.load(:id => 2, :vocalist_id=>3)}
884
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
885
+ ds.first.should == {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
934
886
  end
935
887
 
936
888
  it "should not drop any associated objects if the graph could not be a cartesian product" do
937
889
  ds = GraphBand.eager_graph(:members, :vocalist)
938
890
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, members.id AS members_id, vocalist.id AS vocalist_id_0 FROM bands LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id) LEFT OUTER JOIN members AS vocalist ON (vocalist.id = bands.vocalist_id)'
939
- def ds.fetch_rows(sql, &block)
940
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
941
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
942
- end
891
+ ds._fetch = [{:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6}, {:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6}]
943
892
  a = ds.all
944
- a.should be_a_kind_of(Array)
945
- a.size.should == 1
946
- a.first.should be_a_kind_of(GraphBand)
947
- a.first.values.should == {:id => 2, :vocalist_id => 6}
893
+ a.should == [GraphBand.load(:id => 2, :vocalist_id => 6)]
948
894
  a = a.first
949
- a.vocalist.should be_a_kind_of(GraphBandMember)
950
- a.vocalist.values.should == {:id => 6}
951
- a.members.should be_a_kind_of(Array)
952
- a.members.size.should == 2
953
- a.members.first.should be_a_kind_of(GraphBandMember)
954
- a.members.first.values.should == {:id => 5}
955
- a.members.last.should be_a_kind_of(GraphBandMember)
956
- a.members.last.values.should == {:id => 5}
895
+ a.vocalist.should == GraphBandMember.load(:id => 6)
896
+ a.members.should == [GraphBandMember.load(:id => 5), GraphBandMember.load(:id => 5)]
957
897
  end
958
898
 
959
899
  it "should respect the :cartesian_product_number option" do
960
900
  GraphBand.many_to_one :other_vocalist, :class=>'GraphBandMember', :key=>:vocalist_id, :cartesian_product_number=>1
961
901
  ds = GraphBand.eager_graph(:members, :other_vocalist)
962
902
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, members.id AS members_id, other_vocalist.id AS other_vocalist_id FROM bands LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id) LEFT OUTER JOIN members AS other_vocalist ON (other_vocalist.id = bands.vocalist_id)'
963
- def ds.fetch_rows(sql, &block)
964
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6})
965
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6})
966
- end
903
+ ds._fetch = [{:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6}, {:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6}]
967
904
  a = ds.all
968
905
  a.should == [GraphBand.load(:id=>2, :vocalist_id => 6)]
969
906
  a.first.other_vocalist.should == GraphBandMember.load(:id=>6)
@@ -973,57 +910,28 @@ describe Sequel::Model, "#eager_graph" do
973
910
  it "should drop duplicate items that occur in sequence if the graph could be a cartesian product" do
974
911
  ds = GraphBand.eager_graph(:members, :genres)
975
912
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, members.id AS members_id, genres.id AS genres_id FROM bands LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id) LEFT OUTER JOIN bg ON (bg.band_id = bands.id) LEFT OUTER JOIN genres ON (genres.id = bg.genre_id)'
976
- def ds.fetch_rows(sql, &block)
977
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>7})
978
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>8})
979
- yield({:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>7})
980
- yield({:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>8})
981
- end
913
+ ds._fetch = [{:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>7},
914
+ {:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>8},
915
+ {:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>7},
916
+ {:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>8}]
982
917
  a = ds.all
983
- a.should be_a_kind_of(Array)
984
- a.size.should == 1
985
- a.first.should be_a_kind_of(GraphBand)
986
- a.first.values.should == {:id => 2, :vocalist_id => 6}
918
+ a.should == [GraphBand.load(:id => 2, :vocalist_id => 6)]
987
919
  a = a.first
988
- a.members.should be_a_kind_of(Array)
989
- a.members.size.should == 2
990
- a.members.first.should be_a_kind_of(GraphBandMember)
991
- a.members.first.values.should == {:id => 5}
992
- a.members.last.should be_a_kind_of(GraphBandMember)
993
- a.members.last.values.should == {:id => 6}
994
- a.genres.size.should == 2
995
- a.genres.first.should be_a_kind_of(GraphGenre)
996
- a.genres.first.values.should == {:id => 7}
997
- a.genres.last.should be_a_kind_of(GraphGenre)
998
- a.genres.last.values.should == {:id => 8}
920
+ a.members.should == [GraphBandMember.load(:id => 5), GraphBandMember.load(:id => 6)]
921
+ a.genres.should == [GraphGenre.load(:id => 7), GraphGenre.load(:id => 8)]
999
922
  end
1000
923
 
1001
924
  it "should be able to be used in combination with #eager" do
1002
925
  MODEL_DB.reset
1003
926
  ds = GraphAlbum.eager_graph(:tracks).eager(:genres)
1004
- def ds.fetch_rows(sql, &block)
1005
- @db << sql
1006
- yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
1007
- end
927
+ ds._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
1008
928
  ds2 = GraphGenre.dataset
1009
- def ds2.fetch_rows(sql, &block)
1010
- @db << sql
1011
- yield({:id=>6, :x_foreign_key_x=>1})
1012
- end
929
+ ds2._fetch = {:id=>6, :x_foreign_key_x=>1}
1013
930
  a = ds.all
1014
- a.should be_a_kind_of(Array)
1015
- a.size.should == 1
1016
- a.first.should be_a_kind_of(GraphAlbum)
1017
- a.first.values.should == {:id => 1, :band_id => 2}
931
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1018
932
  a = a.first
1019
- a.tracks.should be_a_kind_of(Array)
1020
- a.tracks.size.should == 1
1021
- a.tracks.first.should be_a_kind_of(GraphTrack)
1022
- a.tracks.first.values.should == {:id=>3, :album_id=>1}
1023
- a.genres.should be_a_kind_of(Array)
1024
- a.genres.size.should == 1
1025
- a.genres.first.should be_a_kind_of(GraphGenre)
1026
- a.genres.first.values.should == {:id=>6}
933
+ a.tracks.should == [GraphTrack.load(:id=>3, :album_id=>1)]
934
+ a.genres.should == [GraphGenre.load(:id => 6)]
1027
935
  MODEL_DB.sqls.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)',
1028
936
  "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)))"]
1029
937
  end
@@ -1031,110 +939,65 @@ describe Sequel::Model, "#eager_graph" do
1031
939
  it "should handle no associated records for a single many_to_one association" do
1032
940
  ds = GraphAlbum.eager_graph(:band)
1033
941
  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)'
1034
- def ds.fetch_rows(sql, &block)
1035
- yield({:id=>1, :band_id=>2, :band_id_0=>nil, :vocalist_id=>nil})
1036
- end
942
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>nil, :vocalist_id=>nil}
1037
943
  a = ds.all
1038
- a.should be_a_kind_of(Array)
1039
- a.size.should == 1
1040
- a.first.should be_a_kind_of(GraphAlbum)
1041
- a.first.values.should == {:id => 1, :band_id => 2}
1042
- a.first.associations.fetch(:band, 2).should == nil
944
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
945
+ a.first.band.should == nil
1043
946
  end
1044
947
 
1045
948
  it "should handle no associated records for a single one_to_many association" do
1046
949
  ds = GraphAlbum.eager_graph(:tracks)
1047
950
  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)'
1048
- def ds.fetch_rows(sql, &block)
1049
- yield({:id=>1, :band_id=>2, :tracks_id=>nil, :album_id=>nil})
1050
- end
951
+ ds._fetch = {:id=>1, :band_id=>2, :tracks_id=>nil, :album_id=>nil}
1051
952
  a = ds.all
1052
- a.should be_a_kind_of(Array)
1053
- a.size.should == 1
1054
- a.first.should be_a_kind_of(GraphAlbum)
1055
- a.first.values.should == {:id => 1, :band_id => 2}
953
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1056
954
  a.first.tracks.should == []
1057
955
  end
1058
956
 
1059
957
  it "should handle no associated records for a single many_to_many association" do
1060
958
  ds = GraphAlbum.eager_graph(:genres)
1061
959
  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)'
1062
- def ds.fetch_rows(sql, &block)
1063
- yield({:id=>1, :band_id=>2, :genres_id=>nil})
1064
- end
960
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>nil}
1065
961
  a = ds.all
1066
- a.should be_a_kind_of(Array)
1067
- a.size.should == 1
1068
- a.first.should be_a_kind_of(GraphAlbum)
1069
- a.first.values.should == {:id => 1, :band_id => 2}
962
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1070
963
  a.first.genres.should == []
1071
964
  end
1072
965
 
1073
966
  it "should handle missing associated records when loading multiple associations" do
1074
967
  ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
1075
968
  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)'
1076
- def ds.fetch_rows(sql, &block)
1077
- yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>3, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1078
- yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>4, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1079
- yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>5, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1080
- yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>6, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1081
- end
969
+ ds._fetch = [{:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>3, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil},
970
+ {:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>4, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil},
971
+ {:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>5, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil},
972
+ {:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>6, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil}]
1082
973
  a = ds.all
1083
- a.should be_a_kind_of(Array)
1084
- a.size.should == 1
1085
- a.first.should be_a_kind_of(GraphAlbum)
1086
- a.first.values.should == {:id => 1, :band_id => 2}
974
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1087
975
  a = a.first
1088
- a.tracks.should be_a_kind_of(Array)
1089
- a.tracks.size.should == 4
1090
- a.tracks.first.should be_a_kind_of(GraphTrack)
1091
- a.tracks.collect{|x|x[:id]}.should == [3,4,5,6]
1092
- a.associations.fetch(:band, 2).should == nil
976
+ a.tracks.should == [GraphTrack.load(:id => 3, :album_id => 1), GraphTrack.load(:id => 4, :album_id => 1), GraphTrack.load(:id => 5, :album_id => 1), GraphTrack.load(:id => 6, :album_id => 1)]
977
+ a.band.should == nil
1093
978
  a.genres.should == []
1094
979
  end
1095
980
 
1096
981
  it "should handle missing associated records when cascading eager loading for associations of associated models" do
1097
982
  ds = GraphTrack.eager_graph(:album=>{:band=>:members})
1098
983
  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)'
1099
- def ds.fetch_rows(sql, &block)
1100
- yield({:id=>2, :album_id=>2, :album_id_0=>nil, :band_id=>nil, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
1101
- yield({:id=>3, :album_id=>3, :album_id_0=>3, :band_id=>3, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
1102
- yield({:id=>4, :album_id=>4, :album_id_0=>4, :band_id=>2, :members_id=>nil, :band_id_0=>2, :vocalist_id=>6})
1103
- yield({:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>5, :band_id_0=>4, :vocalist_id=>8})
1104
- yield({:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>6, :band_id_0=>4, :vocalist_id=>8})
1105
- end
984
+ ds._fetch = [{:id=>2, :album_id=>2, :album_id_0=>nil, :band_id=>nil, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil},
985
+ {:id=>3, :album_id=>3, :album_id_0=>3, :band_id=>3, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil},
986
+ {:id=>4, :album_id=>4, :album_id_0=>4, :band_id=>2, :members_id=>nil, :band_id_0=>2, :vocalist_id=>6},
987
+ {:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>5, :band_id_0=>4, :vocalist_id=>8},
988
+ {:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>6, :band_id_0=>4, :vocalist_id=>8}]
1106
989
  a = ds.all
1107
- a.should be_a_kind_of(Array)
1108
- a.size.should == 4
1109
- a.first.should be_a_kind_of(GraphTrack)
1110
- a.collect{|x|x[:id]}.should == [2,3,4,5]
1111
- a[0].associations.fetch(:album, 2).should == nil
1112
- a[1].album.should be_a_kind_of(GraphAlbum)
1113
- a[1].album.values.should == {:id => 3, :band_id => 3}
1114
- a[1].album.associations.fetch(:band, 2).should == nil
1115
- a[2].album.should be_a_kind_of(GraphAlbum)
1116
- a[2].album.values.should == {:id => 4, :band_id => 2}
1117
- a[2].album.band.should be_a_kind_of(GraphBand)
1118
- a[2].album.band.values.should == {:id => 2, :vocalist_id=>6}
1119
- a[2].album.band.members.should == []
1120
- a[3].album.should be_a_kind_of(GraphAlbum)
1121
- a[3].album.values.should == {:id => 1, :band_id => 4}
1122
- a[3].album.band.should be_a_kind_of(GraphBand)
1123
- a[3].album.band.values.should == {:id => 4, :vocalist_id=>8}
1124
- a[3].album.band.members.size.should == 2
1125
- a[3].album.band.members.first.should be_a_kind_of(GraphBandMember)
1126
- a[3].album.band.members.first.values.should == {:id => 5}
1127
- a[3].album.band.members.last.should be_a_kind_of(GraphBandMember)
1128
- a[3].album.band.members.last.values.should == {:id => 6}
990
+ a.should == [GraphTrack.load(:id => 2, :album_id => 2), GraphTrack.load(:id => 3, :album_id => 3), GraphTrack.load(:id => 4, :album_id => 4), GraphTrack.load(:id => 5, :album_id => 1)]
991
+ a.map{|x| x.album}.should == [nil, GraphAlbum.load(:id => 3, :band_id => 3), GraphAlbum.load(:id => 4, :band_id => 2), GraphAlbum.load(:id => 1, :band_id => 4)]
992
+ a.map{|x| x.album.band if x.album}.should == [nil, nil, GraphBand.load(:id => 2, :vocalist_id=>6), GraphBand.load(:id => 4, :vocalist_id=>8)]
993
+ a.map{|x| x.album.band.members if x.album && x.album.band}.should == [nil, nil, [], [GraphBandMember.load(:id => 5), GraphBandMember.load(:id => 6)]]
1129
994
  end
1130
995
 
1131
996
  it "should respect the association's :primary_key option" do
1132
997
  GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :primary_key=>:vocalist_id
1133
998
  ds = GraphAlbum.eager_graph(:inner_band)
1134
999
  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)'
1135
- def ds.fetch_rows(sql, &block)
1136
- yield({:id=>3, :band_id=>2, :inner_band_id=>5, :vocalist_id=>2})
1137
- end
1000
+ ds._fetch = {:id=>3, :band_id=>2, :inner_band_id=>5, :vocalist_id=>2}
1138
1001
  as = ds.all
1139
1002
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1140
1003
  as.first.inner_band.should == GraphBand.load(:id=>5, :vocalist_id=>2)
@@ -1142,10 +1005,7 @@ describe Sequel::Model, "#eager_graph" do
1142
1005
  GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :primary_key=>:band_id
1143
1006
  ds = GraphAlbum.eager_graph(:right_tracks)
1144
1007
  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)'
1145
- def ds.fetch_rows(sql, &block)
1146
- yield({:id=>3, :band_id=>2, :right_tracks_id=>5, :album_id=>2})
1147
- yield({:id=>3, :band_id=>2, :right_tracks_id=>6, :album_id=>2})
1148
- end
1008
+ ds._fetch = [{:id=>3, :band_id=>2, :right_tracks_id=>5, :album_id=>2}, {:id=>3, :band_id=>2, :right_tracks_id=>6, :album_id=>2}]
1149
1009
  as = ds.all
1150
1010
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1151
1011
  as.first.right_tracks.should == [GraphTrack.load(:id=>5, :album_id=>2), GraphTrack.load(:id=>6, :album_id=>2)]
@@ -1155,9 +1015,7 @@ describe Sequel::Model, "#eager_graph" do
1155
1015
  GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>[:band_id, :id], :primary_key=>[:vocalist_id, :id]
1156
1016
  ds = GraphAlbum.eager_graph(:inner_band)
1157
1017
  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) AND (inner_band.id = albums.id))'
1158
- def ds.fetch_rows(sql, &block)
1159
- yield({:id=>3, :band_id=>2, :inner_band_id=>3, :vocalist_id=>2})
1160
- end
1018
+ ds._fetch = {:id=>3, :band_id=>2, :inner_band_id=>3, :vocalist_id=>2}
1161
1019
  as = ds.all
1162
1020
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1163
1021
  as.first.inner_band.should == GraphBand.load(:id=>3, :vocalist_id=>2)
@@ -1167,9 +1025,7 @@ describe Sequel::Model, "#eager_graph" do
1167
1025
  GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>[:album_id, :id], :primary_key=>[:band_id, :id]
1168
1026
  ds = GraphAlbum.eager_graph(:right_tracks)
1169
1027
  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) AND (right_tracks.id = albums.id))'
1170
- def ds.fetch_rows(sql, &block)
1171
- yield({:id=>3, :band_id=>2, :right_tracks_id=>3, :album_id=>2})
1172
- end
1028
+ ds._fetch = {:id=>3, :band_id=>2, :right_tracks_id=>3, :album_id=>2}
1173
1029
  as = ds.all
1174
1030
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1175
1031
  as.first.right_tracks.should == [GraphTrack.load(:id=>3, :album_id=>2)]
@@ -1179,10 +1035,7 @@ describe Sequel::Model, "#eager_graph" do
1179
1035
  GraphAlbum.many_to_many :sbands, :class=>'GraphBand', :left_key=>[:l1, :l2], :left_primary_key=>[:band_id, :id], :right_key=>[:r1, :r2], :right_primary_key=>[:vocalist_id, :id], :join_table=>:b
1180
1036
  ds = GraphAlbum.eager_graph(:sbands)
1181
1037
  ds.sql.should == 'SELECT albums.id, albums.band_id, sbands.id AS sbands_id, sbands.vocalist_id FROM albums LEFT OUTER JOIN b ON ((b.l1 = albums.band_id) AND (b.l2 = albums.id)) LEFT OUTER JOIN bands AS sbands ON ((sbands.vocalist_id = b.r1) AND (sbands.id = b.r2))'
1182
- def ds.fetch_rows(sql, &block)
1183
- yield({:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6})
1184
- yield({:id=>3, :band_id=>2, :sbands_id=>6, :vocalist_id=>22})
1185
- end
1038
+ ds._fetch = [{:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6}, {:id=>3, :band_id=>2, :sbands_id=>6, :vocalist_id=>22}]
1186
1039
  as = ds.all
1187
1040
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1188
1041
  as.first.sbands.should == [GraphBand.load(:id=>5, :vocalist_id=>6), GraphBand.load(:id=>6, :vocalist_id=>22)]
@@ -1192,15 +1045,31 @@ describe Sequel::Model, "#eager_graph" do
1192
1045
  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
1193
1046
  ds = GraphAlbum.eager_graph(:inner_genres)
1194
1047
  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)'
1195
- def ds.fetch_rows(sql, &block)
1196
- yield({:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12})
1197
- yield({:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22})
1198
- end
1048
+ ds._fetch = [{:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12}, {:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22}]
1199
1049
  as = ds.all
1200
1050
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1201
1051
  as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
1202
1052
  end
1203
1053
 
1054
+ it "should respect composite primary keys for classes when eager loading" do
1055
+ c1 = Class.new(GraphAlbum)
1056
+ c2 = Class.new(GraphBand)
1057
+ c1.set_primary_key [:band_id, :id]
1058
+ c2.set_primary_key [:vocalist_id, :id]
1059
+ c1.many_to_many :sbands, :class=>c2, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :join_table=>:b
1060
+ c2.one_to_many :salbums, :class=>c1, :key=>[:band_id, :id]
1061
+ ds = c1.eager_graph(:sbands=>:salbums)
1062
+ ds.sql.should == 'SELECT albums.id, albums.band_id, sbands.id AS sbands_id, sbands.vocalist_id, salbums.id AS salbums_id, salbums.band_id AS salbums_band_id FROM albums LEFT OUTER JOIN b ON ((b.l1 = albums.band_id) AND (b.l2 = albums.id)) LEFT OUTER JOIN bands AS sbands ON ((sbands.vocalist_id = b.r1) AND (sbands.id = b.r2)) LEFT OUTER JOIN albums AS salbums ON ((salbums.band_id = sbands.vocalist_id) AND (salbums.id = sbands.id))'
1063
+ ds._fetch = [{:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6, :salbums_id=>7, :salbums_band_id=>8},
1064
+ {:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6, :salbums_id=>9, :salbums_band_id=>10},
1065
+ {:id=>3, :band_id=>2, :sbands_id=>6, :vocalist_id=>22, :salbums_id=>nil, :salbums_band_id=>nil},
1066
+ {:id=>7, :band_id=>8, :sbands_id=>nil, :vocalist_id=>nil, :salbums_id=>nil, :salbums_band_id=>nil}]
1067
+ as = ds.all
1068
+ as.should == [c1.load(:id=>3, :band_id=>2), c1.load(:id=>7, :band_id=>8)]
1069
+ as.map{|x| x.sbands}.should == [[c2.load(:id=>5, :vocalist_id=>6), c2.load(:id=>6, :vocalist_id=>22)], []]
1070
+ as.map{|x| x.sbands.map{|y| y.salbums}}.should == [[[c1.load(:id=>7, :band_id=>8), c1.load(:id=>9, :band_id=>10)], []], []]
1071
+ end
1072
+
1204
1073
  it "should respect the association's :graph_select option" do
1205
1074
  GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_select=>:vocalist_id
1206
1075
  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)'
@@ -1284,13 +1153,17 @@ describe Sequel::Model, "#eager_graph" do
1284
1153
  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)}
1285
1154
  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 IS TRUE)"
1286
1155
 
1287
- 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)}
1156
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :eager_grapher=>proc{|eo| eo[:self].graph(GraphTrack, nil, :join_type=>:natural, :table_alias=>eo[:table_alias])}
1288
1157
  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'
1289
1158
 
1290
1159
  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)}
1291
1160
  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)"
1292
1161
  end
1293
1162
 
1163
+ it "should raise an error if you use an :eager_grapher proc with the wrong arity" do
1164
+ proc{GraphAlbum.many_to_one :special_band, :eager_grapher=>proc{|a, b|}}.should raise_error(Sequel::Error)
1165
+ end
1166
+
1294
1167
  it "should respect the association's :graph_only_conditions option" do
1295
1168
  GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_only_conditions=>{:active=>true}
1296
1169
  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 IS TRUE)"
@@ -1360,13 +1233,12 @@ describe Sequel::Model, "#eager_graph" do
1360
1233
  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
1361
1234
  ds = GraphAlbum.eager_graph(:inner_genres)
1362
1235
  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)'
1363
- def ds.fetch_rows(sql, &block)
1364
- yield({:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12})
1365
- yield({:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22})
1366
- end
1236
+ ds._fetch = [{:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12}, {:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22}]
1367
1237
  as = ds.all
1368
1238
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1369
1239
  as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
1240
+ GraphAlbum.set_primary_key :id
1241
+ GraphGenre.set_primary_key :id
1370
1242
  end
1371
1243
 
1372
1244
  it "should handle eager loading with schemas and aliases of different types" do
@@ -1386,10 +1258,102 @@ describe Sequel::Model, "#eager_graph" do
1386
1258
  it "should eagerly load schema qualified tables correctly with joins" do
1387
1259
  c1 = Class.new(GraphAlbum)
1388
1260
  c2 = Class.new(GraphGenre)
1389
- c1.dataset = c1.dataset.from(:s__a)
1261
+ ds = c1.dataset = c1.dataset.from(:s__a)
1262
+ def ds.columns() [:id] end
1390
1263
  c2.dataset = c2.dataset.from(:s__g)
1391
1264
  c1.many_to_many :a_genres, :class=>c2, :left_primary_key=>:id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:s__ag
1392
1265
  ds = c1.join(:s__t, [:b_id]).eager_graph(:a_genres)
1393
1266
  ds.sql.should == 'SELECT a.id, a_genres.id AS a_genres_id FROM (SELECT * FROM s.a INNER JOIN s.t USING (b_id)) AS a LEFT OUTER JOIN s.ag AS ag ON (ag.album_id = a.id) LEFT OUTER JOIN s.g AS a_genres ON (a_genres.id = ag.genre_id)'
1394
1267
  end
1268
+
1269
+ it "should respect :after_load callbacks on associations when eager graphing" do
1270
+ GraphAlbum.many_to_one :al_band, :class=>GraphBand, :key=>:band_id, :after_load=>proc{|o, a| a.id *=2}
1271
+ GraphAlbum.one_to_many :al_tracks, :class=>GraphTrack, :key=>:album_id, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
1272
+ GraphAlbum.many_to_many :al_genres, :class=>GraphGenre, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
1273
+ ds = GraphAlbum.eager_graph(:al_band, :al_tracks, :al_genres)
1274
+ 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)"
1275
+ 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}
1276
+ a = ds.all.first
1277
+ a.should == GraphAlbum.load(:id => 1, :band_id => 2)
1278
+ a.al_band.should == GraphBand.load(:id=>6, :vocalist_id=>4)
1279
+ a.al_tracks.should == [GraphTrack.load(:id=>10, :album_id=>6)]
1280
+ a.al_genres.should == [GraphGenre.load(:id=>14)]
1281
+ end
1282
+
1283
+ it "should respect limits on associations when eager graphing" do
1284
+ GraphAlbum.many_to_one :al_band, :class=>GraphBand, :key=>:band_id
1285
+ GraphAlbum.one_to_many :al_tracks, :class=>GraphTrack, :key=>:album_id, :limit=>2
1286
+ GraphAlbum.many_to_many :al_genres, :class=>GraphGenre, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>2
1287
+ ds = GraphAlbum.eager_graph(:al_band, :al_tracks, :al_genres)
1288
+ 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)"
1289
+ 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},
1290
+ {:id=>1, :band_id=>2, :al_band_id=>8, :vocalist_id=>9, :al_tracks_id=>10, :album_id=>11, :al_genres_id=>12},
1291
+ {:id=>1, :band_id=>2, :al_band_id=>13, :vocalist_id=>14, :al_tracks_id=>15, :album_id=>16, :al_genres_id=>17}]
1292
+ a = ds.all.first
1293
+ a.should == GraphAlbum.load(:id => 1, :band_id => 2)
1294
+ a.al_band.should == GraphBand.load(:id=>3, :vocalist_id=>4)
1295
+ a.al_tracks.should == [GraphTrack.load(:id=>5, :album_id=>6), GraphTrack.load(:id=>10, :album_id=>11)]
1296
+ a.al_genres.should == [GraphGenre.load(:id=>7), GraphGenre.load(:id=>12)]
1297
+ end
1298
+
1299
+ it "should eagerly load a many_to_one association with a custom callback" do
1300
+ ds = GraphAlbum.eager_graph(:band => proc {|ds| ds.select(:id).columns(:id)})
1301
+ 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)'
1302
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>2}
1303
+ a = ds.all
1304
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1305
+ a.first.band.should == GraphBand.load(:id => 2)
1306
+ end
1307
+
1308
+ it "should eagerly load a one_to_one association with a custom callback" do
1309
+ GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
1310
+ ds = GraphAlbum.eager_graph(:track => proc {|ds| ds.select(:album_id).columns(:album_id)})
1311
+ 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)'
1312
+ ds._fetch = {:id=>1, :band_id=>2, :album_id=>1}
1313
+ a = ds.all
1314
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1315
+ a.first.track.should == GraphTrack.load(:album_id=>1)
1316
+ end
1317
+
1318
+ it "should eagerly load a one_to_many association with a custom callback" do
1319
+ ds = GraphAlbum.eager_graph(:tracks => proc {|ds| ds.select(:album_id).columns(:album_id)})
1320
+ ds.sql.should == 'SELECT albums.id, albums.band_id, tracks.album_id FROM albums LEFT OUTER JOIN (SELECT album_id FROM tracks) AS tracks ON (tracks.album_id = albums.id)'
1321
+ ds._fetch = {:id=>1, :band_id=>2, :album_id=>1}
1322
+ a = ds.all
1323
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1324
+ a.first.tracks.should == [GraphTrack.load(:album_id=>1)]
1325
+ end
1326
+
1327
+ it "should eagerly load a many_to_many association with a custom callback" do
1328
+ ds = GraphAlbum.eager_graph(:genres => proc {|ds| ds.select(:id).columns(:id)})
1329
+ 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)'
1330
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>4}
1331
+ a = ds.all
1332
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1333
+ a.first.genres.should == [GraphGenre.load(:id => 4)]
1334
+ end
1335
+
1336
+ it "should allow cascading of eager loading with a custom callback with hash value" do
1337
+ ds = GraphTrack.eager_graph(:album=>{proc{|ds| ds.select(:id, :band_id).columns(:id, :band_id)}=>{:band=>:members}})
1338
+ 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 (SELECT id, band_id FROM 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)'
1339
+ ds._fetch = {:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6}
1340
+ a = ds.all
1341
+ a.should == [GraphTrack.load(:id => 3, :album_id => 1)]
1342
+ a = a.first
1343
+ a.album.should == GraphAlbum.load(:id => 1, :band_id => 2)
1344
+ a.album.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
1345
+ a.album.band.members.should == [GraphBandMember.load(:id => 5)]
1346
+ end
1347
+
1348
+ it "should allow cascading of eager loading with a custom callback with array value" do
1349
+ ds = GraphTrack.eager_graph(:album=>{proc{|ds| ds.select(:id, :band_id).columns(:id, :band_id)}=>[:band, :tracks]})
1350
+ 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, tracks_0.id AS tracks_0_id, tracks_0.album_id AS tracks_0_album_id FROM tracks LEFT OUTER JOIN (SELECT id, band_id FROM albums) AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN tracks AS tracks_0 ON (tracks_0.album_id = album.id)'
1351
+ ds._fetch = {:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>6, :tracks_0_id=>3, :tracks_0_album_id=>1}
1352
+ a = ds.all
1353
+ a.should == [GraphTrack.load(:id => 3, :album_id => 1)]
1354
+ a = a.first
1355
+ a.album.should == GraphAlbum.load(:id => 1, :band_id => 2)
1356
+ a.album.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
1357
+ a.album.tracks.should == [GraphTrack.load(:id => 3, :album_id => 1)]
1358
+ end
1395
1359
  end