colincasey-sequel 2.10.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +7 -1
  2. data/doc/advanced_associations.rdoc +614 -0
  3. data/doc/cheat_sheet.rdoc +223 -0
  4. data/doc/dataset_filtering.rdoc +158 -0
  5. data/doc/prepared_statements.rdoc +104 -0
  6. data/doc/release_notes/1.0.txt +38 -0
  7. data/doc/release_notes/1.1.txt +143 -0
  8. data/doc/release_notes/1.3.txt +101 -0
  9. data/doc/release_notes/1.4.0.txt +53 -0
  10. data/doc/release_notes/1.5.0.txt +155 -0
  11. data/doc/release_notes/2.0.0.txt +298 -0
  12. data/doc/release_notes/2.1.0.txt +271 -0
  13. data/doc/release_notes/2.10.0.txt +328 -0
  14. data/doc/release_notes/2.2.0.txt +253 -0
  15. data/doc/release_notes/2.3.0.txt +88 -0
  16. data/doc/release_notes/2.4.0.txt +106 -0
  17. data/doc/release_notes/2.5.0.txt +137 -0
  18. data/doc/release_notes/2.6.0.txt +157 -0
  19. data/doc/release_notes/2.7.0.txt +166 -0
  20. data/doc/release_notes/2.8.0.txt +171 -0
  21. data/doc/release_notes/2.9.0.txt +97 -0
  22. data/doc/schema.rdoc +29 -0
  23. data/doc/sharding.rdoc +113 -0
  24. data/lib/sequel.rb +1 -0
  25. data/lib/sequel_core/adapters/ado.rb +89 -0
  26. data/lib/sequel_core/adapters/db2.rb +143 -0
  27. data/lib/sequel_core/adapters/dbi.rb +112 -0
  28. data/lib/sequel_core/adapters/do/mysql.rb +38 -0
  29. data/lib/sequel_core/adapters/do/postgres.rb +92 -0
  30. data/lib/sequel_core/adapters/do/sqlite.rb +31 -0
  31. data/lib/sequel_core/adapters/do.rb +205 -0
  32. data/lib/sequel_core/adapters/firebird.rb +298 -0
  33. data/lib/sequel_core/adapters/informix.rb +85 -0
  34. data/lib/sequel_core/adapters/jdbc/h2.rb +69 -0
  35. data/lib/sequel_core/adapters/jdbc/mysql.rb +66 -0
  36. data/lib/sequel_core/adapters/jdbc/oracle.rb +23 -0
  37. data/lib/sequel_core/adapters/jdbc/postgresql.rb +113 -0
  38. data/lib/sequel_core/adapters/jdbc/sqlite.rb +43 -0
  39. data/lib/sequel_core/adapters/jdbc.rb +491 -0
  40. data/lib/sequel_core/adapters/mysql.rb +369 -0
  41. data/lib/sequel_core/adapters/odbc.rb +174 -0
  42. data/lib/sequel_core/adapters/openbase.rb +68 -0
  43. data/lib/sequel_core/adapters/oracle.rb +107 -0
  44. data/lib/sequel_core/adapters/postgres.rb +456 -0
  45. data/lib/sequel_core/adapters/shared/ms_access.rb +110 -0
  46. data/lib/sequel_core/adapters/shared/mssql.rb +102 -0
  47. data/lib/sequel_core/adapters/shared/mysql.rb +325 -0
  48. data/lib/sequel_core/adapters/shared/oracle.rb +61 -0
  49. data/lib/sequel_core/adapters/shared/postgres.rb +715 -0
  50. data/lib/sequel_core/adapters/shared/progress.rb +31 -0
  51. data/lib/sequel_core/adapters/shared/sqlite.rb +265 -0
  52. data/lib/sequel_core/adapters/sqlite.rb +248 -0
  53. data/lib/sequel_core/connection_pool.rb +258 -0
  54. data/lib/sequel_core/core_ext.rb +217 -0
  55. data/lib/sequel_core/core_sql.rb +202 -0
  56. data/lib/sequel_core/database/schema.rb +164 -0
  57. data/lib/sequel_core/database.rb +691 -0
  58. data/lib/sequel_core/dataset/callback.rb +13 -0
  59. data/lib/sequel_core/dataset/convenience.rb +237 -0
  60. data/lib/sequel_core/dataset/pagination.rb +96 -0
  61. data/lib/sequel_core/dataset/prepared_statements.rb +220 -0
  62. data/lib/sequel_core/dataset/query.rb +41 -0
  63. data/lib/sequel_core/dataset/schema.rb +15 -0
  64. data/lib/sequel_core/dataset/sql.rb +1010 -0
  65. data/lib/sequel_core/dataset/stored_procedures.rb +75 -0
  66. data/lib/sequel_core/dataset/unsupported.rb +43 -0
  67. data/lib/sequel_core/dataset.rb +511 -0
  68. data/lib/sequel_core/deprecated.rb +26 -0
  69. data/lib/sequel_core/exceptions.rb +44 -0
  70. data/lib/sequel_core/migration.rb +212 -0
  71. data/lib/sequel_core/object_graph.rb +230 -0
  72. data/lib/sequel_core/pretty_table.rb +71 -0
  73. data/lib/sequel_core/schema/generator.rb +320 -0
  74. data/lib/sequel_core/schema/sql.rb +325 -0
  75. data/lib/sequel_core/schema.rb +2 -0
  76. data/lib/sequel_core/sql.rb +887 -0
  77. data/lib/sequel_core/version.rb +11 -0
  78. data/lib/sequel_core.rb +172 -0
  79. data/lib/sequel_model/association_reflection.rb +267 -0
  80. data/lib/sequel_model/associations.rb +499 -0
  81. data/lib/sequel_model/base.rb +523 -0
  82. data/lib/sequel_model/caching.rb +82 -0
  83. data/lib/sequel_model/dataset_methods.rb +26 -0
  84. data/lib/sequel_model/eager_loading.rb +370 -0
  85. data/lib/sequel_model/exceptions.rb +7 -0
  86. data/lib/sequel_model/hooks.rb +101 -0
  87. data/lib/sequel_model/inflector.rb +281 -0
  88. data/lib/sequel_model/plugins.rb +62 -0
  89. data/lib/sequel_model/record.rb +568 -0
  90. data/lib/sequel_model/schema.rb +49 -0
  91. data/lib/sequel_model/validations.rb +429 -0
  92. data/lib/sequel_model.rb +91 -0
  93. data/spec/adapters/ado_spec.rb +46 -0
  94. data/spec/adapters/firebird_spec.rb +376 -0
  95. data/spec/adapters/informix_spec.rb +96 -0
  96. data/spec/adapters/mysql_spec.rb +881 -0
  97. data/spec/adapters/oracle_spec.rb +244 -0
  98. data/spec/adapters/postgres_spec.rb +687 -0
  99. data/spec/adapters/spec_helper.rb +10 -0
  100. data/spec/adapters/sqlite_spec.rb +555 -0
  101. data/spec/integration/dataset_test.rb +134 -0
  102. data/spec/integration/eager_loader_test.rb +696 -0
  103. data/spec/integration/prepared_statement_test.rb +130 -0
  104. data/spec/integration/schema_test.rb +180 -0
  105. data/spec/integration/spec_helper.rb +58 -0
  106. data/spec/integration/type_test.rb +96 -0
  107. data/spec/rcov.opts +6 -0
  108. data/spec/sequel_core/connection_pool_spec.rb +526 -0
  109. data/spec/sequel_core/core_ext_spec.rb +156 -0
  110. data/spec/sequel_core/core_sql_spec.rb +522 -0
  111. data/spec/sequel_core/database_spec.rb +1188 -0
  112. data/spec/sequel_core/dataset_spec.rb +3481 -0
  113. data/spec/sequel_core/expression_filters_spec.rb +363 -0
  114. data/spec/sequel_core/migration_spec.rb +261 -0
  115. data/spec/sequel_core/object_graph_spec.rb +272 -0
  116. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  117. data/spec/sequel_core/schema_generator_spec.rb +167 -0
  118. data/spec/sequel_core/schema_spec.rb +780 -0
  119. data/spec/sequel_core/spec_helper.rb +55 -0
  120. data/spec/sequel_core/version_spec.rb +7 -0
  121. data/spec/sequel_model/association_reflection_spec.rb +93 -0
  122. data/spec/sequel_model/associations_spec.rb +1767 -0
  123. data/spec/sequel_model/base_spec.rb +419 -0
  124. data/spec/sequel_model/caching_spec.rb +215 -0
  125. data/spec/sequel_model/dataset_methods_spec.rb +78 -0
  126. data/spec/sequel_model/eager_loading_spec.rb +1165 -0
  127. data/spec/sequel_model/hooks_spec.rb +485 -0
  128. data/spec/sequel_model/inflector_spec.rb +119 -0
  129. data/spec/sequel_model/model_spec.rb +588 -0
  130. data/spec/sequel_model/plugins_spec.rb +80 -0
  131. data/spec/sequel_model/record_spec.rb +1184 -0
  132. data/spec/sequel_model/schema_spec.rb +90 -0
  133. data/spec/sequel_model/spec_helper.rb +78 -0
  134. data/spec/sequel_model/validations_spec.rb +1067 -0
  135. data/spec/spec.opts +0 -0
  136. data/spec/spec_config.rb.example +10 -0
  137. metadata +177 -3
@@ -0,0 +1,1165 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sequel::Model, "#eager" do
4
+ before(:each) do
5
+ MODEL_DB.reset
6
+
7
+ class ::EagerAlbum < Sequel::Model(:albums)
8
+ columns :id, :band_id
9
+ many_to_one :band, :class=>'EagerBand', :key=>:band_id
10
+ one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id
11
+ many_to_many :genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
12
+ one_to_many :good_tracks, :class=>'EagerTrack', :key=>:album_id do |ds|
13
+ ds.filter(:name=>'Good')
14
+ end
15
+ many_to_one :band_name, :class=>'EagerBand', :key=>:band_id, :select=>[:id, :name]
16
+ one_to_many :track_names, :class=>'EagerTrack', :key=>:album_id, :select=>[:id, :name]
17
+ many_to_many :genre_names, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :select=>[:id]
18
+ end
19
+
20
+ class ::EagerBand < Sequel::Model(:bands)
21
+ columns :id, :p_k
22
+ one_to_many :albums, :class=>'EagerAlbum', :key=>:band_id, :eager=>:tracks
23
+ one_to_many :graph_albums, :class=>'EagerAlbum', :key=>:band_id, :eager_graph=>:tracks
24
+ many_to_many :members, :class=>'EagerBandMember', :left_key=>:band_id, :right_key=>:member_id, :join_table=>:bm
25
+ one_to_many :good_albums, :class=>'EagerAlbum', :key=>:band_id, :eager_block=>proc{|ds| ds.filter(:name=>'good')} do |ds|
26
+ ds.filter(:name=>'Good')
27
+ end
28
+ one_to_many :self_titled_albums, :class=>'EagerAlbum', :key=>:band_id, :allow_eager=>false do |ds|
29
+ ds.filter(:name=>name)
30
+ end
31
+ one_to_many :albums_by_name, :class=>'EagerAlbum', :key=>:band_id, :order=>:name, :allow_eager=>false
32
+ one_to_many :top_10_albums, :class=>'EagerAlbum', :key=>:band_id, :limit=>10
33
+ end
34
+
35
+ class ::EagerTrack < Sequel::Model(:tracks)
36
+ columns :id, :album_id
37
+ many_to_one :album, :class=>'EagerAlbum', :key=>:album_id
38
+ end
39
+
40
+ class ::EagerGenre < Sequel::Model(:genres)
41
+ columns :id, :xxx
42
+ many_to_many :albums, :class=>'EagerAlbum', :left_key=>:genre_id, :right_key=>:album_id, :join_table=>:ag
43
+ end
44
+
45
+ class ::EagerBandMember < Sequel::Model(:members)
46
+ columns :id
47
+ many_to_many :bands, :class=>'EagerBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm, :order =>:id
48
+ end
49
+
50
+ EagerAlbum.dataset.extend(Module.new {
51
+ def columns
52
+ [:id, :band_id]
53
+ end
54
+
55
+ def fetch_rows(sql)
56
+ h = if sql =~ /101/
57
+ {:id => 101, :band_id=> 101}
58
+ else
59
+ {:id => 1, :band_id=> 2}
60
+ end
61
+ h.merge!(:x_foreign_key_x=>4) if sql =~ /ag\.genre_id/
62
+ @db << sql
63
+ yield h
64
+ end
65
+ })
66
+
67
+ EagerBand.dataset.extend(Module.new {
68
+ def fetch_rows(sql)
69
+ h = {:id => 2}
70
+ h.merge!(:x_foreign_key_x=>5) if sql =~ /bm\.member_id/
71
+ @db << sql
72
+ case sql
73
+ when /id IN (101)/
74
+ when /id > 100/
75
+ yield({:id => 101})
76
+ yield({:id => 102})
77
+ else
78
+ yield h
79
+ end
80
+ end
81
+ })
82
+
83
+ EagerTrack.dataset.extend(Module.new {
84
+ def fetch_rows(sql)
85
+ @db << sql
86
+ yield({:id => 3, :album_id => 1})
87
+ end
88
+ })
89
+
90
+ EagerGenre.dataset.extend(Module.new {
91
+ def fetch_rows(sql)
92
+ h = {:id => 4}
93
+ h.merge!(:x_foreign_key_x=>1) if sql =~ /ag\.album_id/
94
+ @db << sql
95
+ yield h
96
+ end
97
+ })
98
+
99
+ EagerBandMember.dataset.extend(Module.new {
100
+ def fetch_rows(sql)
101
+ h = {:id => 5}
102
+ h.merge!(:x_foreign_key_x=>2) if sql =~ /bm\.band_id/
103
+ @db << sql
104
+ yield h
105
+ end
106
+ })
107
+ end
108
+
109
+ it "should raise an error if called without a symbol or hash" do
110
+ proc{EagerAlbum.eager(Object.new)}.should raise_error(Sequel::Error)
111
+ end
112
+
113
+ it "should eagerly load a single many_to_one association" do
114
+ a = EagerAlbum.eager(:band).all
115
+ a.should be_a_kind_of(Array)
116
+ a.size.should == 1
117
+ a.first.should be_a_kind_of(EagerAlbum)
118
+ a.first.values.should == {:id => 1, :band_id => 2}
119
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM bands WHERE (bands.id IN (2))']
120
+ a = a.first
121
+ a.band.should be_a_kind_of(EagerBand)
122
+ a.band.values.should == {:id => 2}
123
+ MODEL_DB.sqls.length.should == 2
124
+ end
125
+
126
+ it "should eagerly load a single one_to_many association" do
127
+ a = EagerAlbum.eager(:tracks).all
128
+ a.should be_a_kind_of(Array)
129
+ a.size.should == 1
130
+ a.first.should be_a_kind_of(EagerAlbum)
131
+ a.first.values.should == {:id => 1, :band_id => 2}
132
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
133
+ a = a.first
134
+ a.tracks.should be_a_kind_of(Array)
135
+ a.tracks.size.should == 1
136
+ a.tracks.first.should be_a_kind_of(EagerTrack)
137
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
138
+ MODEL_DB.sqls.length.should == 2
139
+ end
140
+
141
+ it "should eagerly load a single many_to_many association" do
142
+ a = EagerAlbum.eager(:genres).all
143
+ a.should be_a_kind_of(Array)
144
+ a.size.should == 1
145
+ a.first.should be_a_kind_of(EagerAlbum)
146
+ a.first.values.should == {:id => 1, :band_id => 2}
147
+ 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)))"]
148
+ a = a.first
149
+ a.genres.should be_a_kind_of(Array)
150
+ a.genres.size.should == 1
151
+ a.genres.first.should be_a_kind_of(EagerGenre)
152
+ a.genres.first.values.should == {:id => 4}
153
+ MODEL_DB.sqls.length.should == 2
154
+ end
155
+
156
+ it "should eagerly load multiple associations in a single call" do
157
+ a = EagerAlbum.eager(:genres, :tracks, :band).all
158
+ a.should be_a_kind_of(Array)
159
+ a.size.should == 1
160
+ a.first.should be_a_kind_of(EagerAlbum)
161
+ a.first.values.should == {:id => 1, :band_id => 2}
162
+ MODEL_DB.sqls.length.should == 4
163
+ MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
164
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM bands WHERE (bands.id IN (2))'))
165
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM tracks WHERE (tracks.album_id IN (1))'))
166
+ 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)))'))
167
+ a = a.first
168
+ a.band.should be_a_kind_of(EagerBand)
169
+ a.band.values.should == {:id => 2}
170
+ a.tracks.should be_a_kind_of(Array)
171
+ a.tracks.size.should == 1
172
+ a.tracks.first.should be_a_kind_of(EagerTrack)
173
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
174
+ a.genres.should be_a_kind_of(Array)
175
+ a.genres.size.should == 1
176
+ a.genres.first.should be_a_kind_of(EagerGenre)
177
+ a.genres.first.values.should == {:id => 4}
178
+ MODEL_DB.sqls.length.should == 4
179
+ end
180
+
181
+ it "should eagerly load multiple associations in separate calls" do
182
+ a = EagerAlbum.eager(:genres).eager(:tracks).eager(:band).all
183
+ a.should be_a_kind_of(Array)
184
+ a.size.should == 1
185
+ a.first.should be_a_kind_of(EagerAlbum)
186
+ a.first.values.should == {:id => 1, :band_id => 2}
187
+ MODEL_DB.sqls.length.should == 4
188
+ MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
189
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM bands WHERE (bands.id IN (2))'))
190
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM tracks WHERE (tracks.album_id IN (1))'))
191
+ 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)))'))
192
+ a = a.first
193
+ a.band.should be_a_kind_of(EagerBand)
194
+ a.band.values.should == {:id => 2}
195
+ a.tracks.should be_a_kind_of(Array)
196
+ a.tracks.size.should == 1
197
+ a.tracks.first.should be_a_kind_of(EagerTrack)
198
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
199
+ a.genres.should be_a_kind_of(Array)
200
+ a.genres.size.should == 1
201
+ a.genres.first.should be_a_kind_of(EagerGenre)
202
+ a.genres.first.values.should == {:id => 4}
203
+ MODEL_DB.sqls.length.should == 4
204
+ end
205
+
206
+ it "should allow cascading of eager loading for associations of associated models" do
207
+ a = EagerTrack.eager(:album=>{:band=>:members}).all
208
+ a.should be_a_kind_of(Array)
209
+ a.size.should == 1
210
+ a.first.should be_a_kind_of(EagerTrack)
211
+ a.first.values.should == {:id => 3, :album_id => 1}
212
+ MODEL_DB.sqls.length.should == 4
213
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
214
+ 'SELECT * FROM albums WHERE (albums.id IN (1))',
215
+ 'SELECT * FROM bands WHERE (bands.id IN (2))',
216
+ "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)))"]
217
+ a = a.first
218
+ a.album.should be_a_kind_of(EagerAlbum)
219
+ a.album.values.should == {:id => 1, :band_id => 2}
220
+ a.album.band.should be_a_kind_of(EagerBand)
221
+ a.album.band.values.should == {:id => 2}
222
+ a.album.band.members.should be_a_kind_of(Array)
223
+ a.album.band.members.size.should == 1
224
+ a.album.band.members.first.should be_a_kind_of(EagerBandMember)
225
+ a.album.band.members.first.values.should == {:id => 5}
226
+ MODEL_DB.sqls.length.should == 4
227
+ end
228
+
229
+ it "should cascade eagerly loading when the :eager association option is used" do
230
+ a = EagerBand.eager(:albums).all
231
+ a.should be_a_kind_of(Array)
232
+ a.size.should == 1
233
+ a.first.should be_a_kind_of(EagerBand)
234
+ a.first.values.should == {:id => 2}
235
+ MODEL_DB.sqls.should == ['SELECT * FROM bands',
236
+ 'SELECT * FROM albums WHERE (albums.band_id IN (2))',
237
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
238
+ a = a.first
239
+ a.albums.should be_a_kind_of(Array)
240
+ a.albums.size.should == 1
241
+ a.albums.first.should be_a_kind_of(EagerAlbum)
242
+ a.albums.first.values.should == {:id => 1, :band_id => 2}
243
+ a = a.albums.first
244
+ a.tracks.should be_a_kind_of(Array)
245
+ a.tracks.size.should == 1
246
+ a.tracks.first.should be_a_kind_of(EagerTrack)
247
+ a.tracks.first.values.should == {:id => 3, :album_id => 1}
248
+ MODEL_DB.sqls.length.should == 3
249
+ end
250
+
251
+ it "should respect :eager when lazily loading an association" do
252
+ a = EagerBand.all
253
+ a.should be_a_kind_of(Array)
254
+ a.size.should == 1
255
+ a.first.should be_a_kind_of(EagerBand)
256
+ a.first.values.should == {:id => 2}
257
+ MODEL_DB.sqls.should == ['SELECT * FROM bands']
258
+ a = a.first
259
+ a.albums
260
+ MODEL_DB.sqls.should == ['SELECT * FROM bands',
261
+ 'SELECT * FROM albums WHERE (albums.band_id = 2)',
262
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
263
+ a.albums.should be_a_kind_of(Array)
264
+ a.albums.size.should == 1
265
+ a.albums.first.should be_a_kind_of(EagerAlbum)
266
+ a.albums.first.values.should == {:id => 1, :band_id => 2}
267
+ a = a.albums.first
268
+ a.tracks.should be_a_kind_of(Array)
269
+ a.tracks.size.should == 1
270
+ a.tracks.first.should be_a_kind_of(EagerTrack)
271
+ a.tracks.first.values.should == {:id => 3, :album_id => 1}
272
+ MODEL_DB.sqls.length.should == 3
273
+ end
274
+
275
+ it "should cascade eagerly loading when the :eager_graph association option is used" do
276
+ EagerAlbum.dataset.extend(Module.new {
277
+ def fetch_rows(sql)
278
+ @db << sql
279
+ yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
280
+ end
281
+ })
282
+ a = EagerBand.eager(:graph_albums).all
283
+ a.should be_a_kind_of(Array)
284
+ a.size.should == 1
285
+ a.first.should be_a_kind_of(EagerBand)
286
+ a.first.values.should == {:id => 2}
287
+ MODEL_DB.sqls.should == ['SELECT * FROM bands',
288
+ '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))']
289
+ a = a.first
290
+ a.graph_albums.should be_a_kind_of(Array)
291
+ a.graph_albums.size.should == 1
292
+ a.graph_albums.first.should be_a_kind_of(EagerAlbum)
293
+ a.graph_albums.first.values.should == {:id => 1, :band_id => 2}
294
+ a = a.graph_albums.first
295
+ a.tracks.should be_a_kind_of(Array)
296
+ a.tracks.size.should == 1
297
+ a.tracks.first.should be_a_kind_of(EagerTrack)
298
+ a.tracks.first.values.should == {:id => 3, :album_id => 1}
299
+ MODEL_DB.sqls.length.should == 2
300
+ end
301
+
302
+ it "should respect :eager_graph when lazily loading an association" do
303
+ a = EagerBand.all
304
+ a.should be_a_kind_of(Array)
305
+ a.size.should == 1
306
+ a.first.should be_a_kind_of(EagerBand)
307
+ a.first.values.should == {:id => 2}
308
+ MODEL_DB.sqls.should == ['SELECT * FROM bands']
309
+ a = a.first
310
+ EagerAlbum.dataset.extend(Module.new {
311
+ def fetch_rows(sql)
312
+ @db << sql
313
+ yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
314
+ end
315
+ })
316
+ a.graph_albums
317
+ MODEL_DB.sqls.should == ['SELECT * FROM bands',
318
+ '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)']
319
+ a.graph_albums.should be_a_kind_of(Array)
320
+ a.graph_albums.size.should == 1
321
+ a.graph_albums.first.should be_a_kind_of(EagerAlbum)
322
+ a.graph_albums.first.values.should == {:id => 1, :band_id => 2}
323
+ a = a.graph_albums.first
324
+ a.tracks.should be_a_kind_of(Array)
325
+ a.tracks.size.should == 1
326
+ a.tracks.first.should be_a_kind_of(EagerTrack)
327
+ a.tracks.first.values.should == {:id => 3, :album_id => 1}
328
+ MODEL_DB.sqls.length.should == 2
329
+ end
330
+
331
+ it "should respect :conditions when eagerly loading" do
332
+ EagerBandMember.many_to_many :good_bands, :clone=>:bands, :conditions=>{:a=>32}
333
+ a = EagerBandMember.eager(:good_bands).all
334
+ a.should be_a_kind_of(Array)
335
+ a.size.should == 1
336
+ a.first.should be_a_kind_of(EagerBandMember)
337
+ a.first.values.should == {:id => 5}
338
+ 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']
339
+ a = a.first
340
+ a.good_bands.should be_a_kind_of(Array)
341
+ a.good_bands.size.should == 1
342
+ a.good_bands.first.should be_a_kind_of(EagerBand)
343
+ a.good_bands.first.values.should == {:id => 2}
344
+ MODEL_DB.sqls.length.should == 2
345
+
346
+ MODEL_DB.sqls.clear
347
+ EagerBandMember.many_to_many :good_bands, :clone=>:bands, :conditions=>"x = 1"
348
+ a = EagerBandMember.eager(:good_bands).all
349
+ 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']
350
+ end
351
+
352
+ it "should respect :order when eagerly loading" do
353
+ a = EagerBandMember.eager(:bands).all
354
+ a.should be_a_kind_of(Array)
355
+ a.size.should == 1
356
+ a.first.should be_a_kind_of(EagerBandMember)
357
+ a.first.values.should == {:id => 5}
358
+ 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']
359
+ a = a.first
360
+ a.bands.should be_a_kind_of(Array)
361
+ a.bands.size.should == 1
362
+ a.bands.first.should be_a_kind_of(EagerBand)
363
+ a.bands.first.values.should == {:id => 2}
364
+ MODEL_DB.sqls.length.should == 2
365
+ end
366
+
367
+ it "should populate the reciprocal many_to_one association when eagerly loading the one_to_many association" do
368
+ a = EagerAlbum.eager(:tracks).all
369
+ a.should be_a_kind_of(Array)
370
+ a.size.should == 1
371
+ a.first.should be_a_kind_of(EagerAlbum)
372
+ a.first.values.should == {:id => 1, :band_id => 2}
373
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
374
+ a = a.first
375
+ a.tracks.should be_a_kind_of(Array)
376
+ a.tracks.size.should == 1
377
+ a.tracks.first.should be_a_kind_of(EagerTrack)
378
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
379
+ a.tracks.first.album.should be_a_kind_of(EagerAlbum)
380
+ a.tracks.first.album.should == a
381
+ MODEL_DB.sqls.length.should == 2
382
+ end
383
+
384
+ it "should cache the negative lookup when eagerly loading a many_to_one association" do
385
+ a = EagerAlbum.eager(:band).filter(:id=>101).all
386
+ a.should be_a_kind_of(Array)
387
+ a.size.should == 1
388
+ a.first.should be_a_kind_of(EagerAlbum)
389
+ a.first.values.should == {:id => 101, :band_id => 101}
390
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (id = 101)', 'SELECT * FROM bands WHERE (bands.id IN (101))']
391
+ a = a.first
392
+ a.associations.fetch(:band, 2).should == nil
393
+ a.band.should == nil
394
+ MODEL_DB.sqls.length.should == 2
395
+ end
396
+
397
+ it "should cache the negative lookup when eagerly loading a *_to_many associations" do
398
+ a = EagerBand.eager(:albums).filter('id > 100').all
399
+ a.should be_a_kind_of(Array)
400
+ a.size.should == 2
401
+ a.first.should be_a_kind_of(EagerBand)
402
+ a.first.values.should == {:id => 101}
403
+ a.last.values.should == {:id => 102}
404
+ 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))"]
405
+ a.first.associations[:albums].should be_a_kind_of(Array)
406
+ a.first.albums.length.should == 1
407
+ a.first.albums.first.should be_a_kind_of(EagerAlbum)
408
+ a.last.associations[:albums].should == []
409
+ a.last.albums.should == []
410
+ MODEL_DB.sqls.length.should == 3
411
+ end
412
+
413
+ it "should use the association's block when eager loading by default" do
414
+ EagerAlbum.eager(:good_tracks).all
415
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (name = 'Good'))"]
416
+ end
417
+
418
+ it "should use the eager_block option when eager loading if given" do
419
+ EagerBand.eager(:good_albums).all
420
+ MODEL_DB.sqls.should == ['SELECT * FROM bands', "SELECT * FROM albums WHERE ((albums.band_id IN (2)) AND (name = 'good'))"]
421
+ MODEL_DB.sqls.clear
422
+ EagerBand.eager(:good_albums=>:good_tracks).all
423
+ 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'))"]
424
+ end
425
+
426
+ it "should raise an error when attempting to eagerly load an association with the :allow_eager option set to false" do
427
+ proc{EagerBand.eager(:self_titled_albums).all}.should raise_error(Sequel::Error)
428
+ proc{EagerBand.eager(:albums_by_name).all}.should raise_error(Sequel::Error)
429
+ end
430
+
431
+ it "should respect the association's :select option" do
432
+ EagerAlbum.eager(:band_name).all
433
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM bands WHERE (bands.id IN (2))"]
434
+ MODEL_DB.sqls.clear
435
+ EagerAlbum.eager(:track_names).all
436
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM tracks WHERE (tracks.album_id IN (1))"]
437
+ MODEL_DB.sqls.clear
438
+ EagerAlbum.eager(:genre_names).all
439
+ 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)))"]
440
+ end
441
+
442
+ it "should respect the association's :primary_key option" do
443
+ EagerAlbum.many_to_one :special_band, :class=>:EagerBand, :primary_key=>:p_k, :key=>:band_id
444
+ EagerBand.dataset.extend(Module.new {
445
+ def fetch_rows(sql)
446
+ MODEL_DB.sqls << sql
447
+ yield({:p_k=>2, :id=>1})
448
+ end
449
+ })
450
+ as = EagerAlbum.eager(:special_band).all
451
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE (bands.p_k IN (2))"]
452
+ as.length.should == 1
453
+ as.first.special_band.should == EagerBand.load(:p_k=>2, :id=>1)
454
+ MODEL_DB.sqls.clear
455
+ EagerAlbum.one_to_many :special_tracks, :class=>:EagerTrack, :primary_key=>:band_id, :key=>:album_id
456
+ EagerTrack.dataset.extend(Module.new {
457
+ def fetch_rows(sql)
458
+ MODEL_DB.sqls << sql
459
+ yield({:album_id=>2, :id=>1})
460
+ end
461
+ })
462
+ as = EagerAlbum.eager(:special_tracks).all
463
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (2))"]
464
+ as.length.should == 1
465
+ as.first.special_tracks.should == [EagerTrack.load(:album_id=>2, :id=>1)]
466
+ end
467
+
468
+ it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
469
+ 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
470
+ EagerGenre.dataset.extend(Module.new {
471
+ def fetch_rows(sql)
472
+ MODEL_DB.sqls << sql
473
+ yield({:x_foreign_key_x=>2, :id=>5})
474
+ yield({:x_foreign_key_x=>2, :id=>6})
475
+ end
476
+ })
477
+ as = EagerAlbum.eager(:special_genres).all
478
+ 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)))"]
479
+ as.length.should == 1
480
+ as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
481
+ end
482
+
483
+ it "should use the :eager_loader association option when eager loading" do
484
+ EagerAlbum.many_to_one :special_band, :eager_loader=>(proc do |key_hash, records, assocs|
485
+ item = EagerBand.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).order(:name).first
486
+ records.each{|r| r.associations[:special_band] = item}
487
+ end)
488
+ EagerAlbum.one_to_many :special_tracks, :eager_loader=>(proc do |key_hash, records, assocs|
489
+ items = EagerTrack.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).all
490
+ records.each{|r| r.associations[:special_tracks] = items}
491
+ end)
492
+ EagerAlbum.many_to_many :special_genres, :class=>:EagerGenre, :eager_loader=>(proc do |key_hash, records, assocs|
493
+ items = EagerGenre.inner_join(:ag, [:genre_id]).filter(:album_id=>records.collect{|r| r.pk}).all
494
+ records.each{|r| r.associations[:special_genres] = items}
495
+ end)
496
+ a = EagerAlbum.eager(:special_genres, :special_tracks, :special_band).all
497
+ a.should be_a_kind_of(Array)
498
+ a.size.should == 1
499
+ a.first.should be_a_kind_of(EagerAlbum)
500
+ a.first.values.should == {:id => 1, :band_id => 2}
501
+ MODEL_DB.sqls.length.should == 4
502
+ MODEL_DB.sqls[0].should == 'SELECT * FROM albums'
503
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM bands WHERE (album_id IN (1, 2)) ORDER BY name LIMIT 1'))
504
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM tracks WHERE (album_id IN (1, 2))'))
505
+ MODEL_DB.sqls[1..-1].should(include('SELECT * FROM genres INNER JOIN ag USING (genre_id) WHERE (album_id IN (1))'))
506
+ a = a.first
507
+ a.special_band.should be_a_kind_of(EagerBand)
508
+ a.special_band.values.should == {:id => 2}
509
+ a.special_tracks.should be_a_kind_of(Array)
510
+ a.special_tracks.size.should == 1
511
+ a.special_tracks.first.should be_a_kind_of(EagerTrack)
512
+ a.special_tracks.first.values.should == {:id => 3, :album_id=>1}
513
+ a.special_genres.should be_a_kind_of(Array)
514
+ a.special_genres.size.should == 1
515
+ a.special_genres.first.should be_a_kind_of(EagerGenre)
516
+ a.special_genres.first.values.should == {:id => 4}
517
+ MODEL_DB.sqls.length.should == 4
518
+ end
519
+
520
+ it "should respect :after_load callbacks on associations when eager loading" do
521
+ EagerAlbum.many_to_one :al_band, :class=>'EagerBand', :key=>:band_id, :after_load=>proc{|o, a| a.id *=2}
522
+ EagerAlbum.one_to_many :al_tracks, :class=>'EagerTrack', :key=>:album_id, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
523
+ EagerAlbum.many_to_many :al_genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
524
+ a = EagerAlbum.eager(:al_band, :al_tracks, :al_genres).all.first
525
+ a.should == EagerAlbum.load(:id => 1, :band_id => 2)
526
+ a.al_band.should == EagerBand.load(:id=>4)
527
+ a.al_tracks.should == [EagerTrack.load(:id=>6, :album_id=>1)]
528
+ a.al_genres.should == [EagerGenre.load(:id=>8)]
529
+ end
530
+ end
531
+
532
+ describe Sequel::Model, "#eager_graph" do
533
+ after(:all) do
534
+ class ::MockDataset
535
+ alias clone orig_clone
536
+ end
537
+ end
538
+
539
+ before(:all) do
540
+ class ::MockDataset
541
+ alias orig_clone clone
542
+ def clone(opts = {})
543
+ c = super()
544
+ c.opts = @opts.merge(opts)
545
+ c.instance_variable_set(:@columns, (@columns.dup if @columns))
546
+ c
547
+ end
548
+ end
549
+
550
+ class ::GraphAlbum < Sequel::Model(:albums)
551
+ dataset.opts[:from] = [:albums]
552
+ columns :id, :band_id
553
+ many_to_one :band, :class=>'GraphBand', :key=>:band_id
554
+ one_to_many :tracks, :class=>'GraphTrack', :key=>:album_id
555
+ many_to_many :genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
556
+ many_to_one :previous_album, :class=>'GraphAlbum'
557
+ end
558
+
559
+ class ::GraphBand < Sequel::Model(:bands)
560
+ dataset.opts[:from] = [:bands]
561
+ columns :id, :vocalist_id
562
+ many_to_one :vocalist, :class=>'GraphBandMember', :key=>:vocalist_id
563
+ one_to_many :albums, :class=>'GraphAlbum', :key=>:band_id
564
+ many_to_many :members, :class=>'GraphBandMember', :left_key=>:band_id, :right_key=>:member_id, :join_table=>:bm
565
+ many_to_many :genres, :class=>'GraphGenre', :left_key=>:band_id, :right_key=>:genre_id, :join_table=>:bg
566
+ end
567
+
568
+ class ::GraphTrack < Sequel::Model(:tracks)
569
+ dataset.opts[:from] = [:tracks]
570
+ columns :id, :album_id
571
+ many_to_one :album, :class=>'GraphAlbum', :key=>:album_id
572
+ end
573
+
574
+ class ::GraphGenre < Sequel::Model(:genres)
575
+ dataset.opts[:from] = [:genres]
576
+ columns :id
577
+ many_to_many :albums, :class=>'GraphAlbum', :left_key=>:genre_id, :right_key=>:album_id, :join_table=>:ag
578
+ end
579
+
580
+ class ::GraphBandMember < Sequel::Model(:members)
581
+ dataset.opts[:from] = [:members]
582
+ columns :id
583
+ many_to_many :bands, :class=>'GraphBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm
584
+ end
585
+ end
586
+
587
+ it "should raise an error if called without a symbol or hash" do
588
+ proc{GraphAlbum.eager_graph(Object.new)}.should raise_error(Sequel::Error)
589
+ end
590
+
591
+ it "should eagerly load a single many_to_one association" do
592
+ ds = GraphAlbum.eager_graph(:band)
593
+ 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)'
594
+ def ds.fetch_rows(sql, &block)
595
+ yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
596
+ end
597
+ a = ds.all
598
+ a.should be_a_kind_of(Array)
599
+ a.size.should == 1
600
+ a.first.should be_a_kind_of(GraphAlbum)
601
+ a.first.values.should == {:id => 1, :band_id => 2}
602
+ a = a.first
603
+ a.band.should be_a_kind_of(GraphBand)
604
+ a.band.values.should == {:id => 2, :vocalist_id=>3}
605
+ end
606
+
607
+ it "should eagerly load a single one_to_many association" do
608
+ ds = GraphAlbum.eager_graph(:tracks)
609
+ 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)'
610
+ def ds.fetch_rows(sql, &block)
611
+ yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
612
+ end
613
+ a = ds.all
614
+ a.should be_a_kind_of(Array)
615
+ a.size.should == 1
616
+ a.first.should be_a_kind_of(GraphAlbum)
617
+ a.first.values.should == {:id => 1, :band_id => 2}
618
+ a = a.first
619
+ a.tracks.should be_a_kind_of(Array)
620
+ a.tracks.size.should == 1
621
+ a.tracks.first.should be_a_kind_of(GraphTrack)
622
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
623
+ end
624
+
625
+ it "should eagerly load a single many_to_many association" do
626
+ ds = GraphAlbum.eager_graph(:genres)
627
+ 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)'
628
+ def ds.fetch_rows(sql, &block)
629
+ yield({:id=>1, :band_id=>2, :genres_id=>4})
630
+ end
631
+ a = ds.all
632
+ a.should be_a_kind_of(Array)
633
+ a.size.should == 1
634
+ a.first.should be_a_kind_of(GraphAlbum)
635
+ a.first.values.should == {:id => 1, :band_id => 2}
636
+ a = a.first
637
+ a.genres.should be_a_kind_of(Array)
638
+ a.genres.size.should == 1
639
+ a.genres.first.should be_a_kind_of(GraphGenre)
640
+ a.genres.first.values.should == {:id => 4}
641
+ end
642
+
643
+ it "should eagerly load multiple associations in a single call" do
644
+ ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
645
+ 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)'
646
+ def ds.fetch_rows(sql, &block)
647
+ yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
648
+ end
649
+ a = ds.all
650
+ a.should be_a_kind_of(Array)
651
+ a.size.should == 1
652
+ a.first.should be_a_kind_of(GraphAlbum)
653
+ a.first.values.should == {:id => 1, :band_id => 2}
654
+ a = a.first
655
+ a.band.should be_a_kind_of(GraphBand)
656
+ a.band.values.should == {:id => 2, :vocalist_id=>6}
657
+ a.tracks.should be_a_kind_of(Array)
658
+ a.tracks.size.should == 1
659
+ a.tracks.first.should be_a_kind_of(GraphTrack)
660
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
661
+ a.genres.should be_a_kind_of(Array)
662
+ a.genres.size.should == 1
663
+ a.genres.first.should be_a_kind_of(GraphGenre)
664
+ a.genres.first.values.should == {:id => 4}
665
+ end
666
+
667
+ it "should eagerly load multiple associations in separate calls" do
668
+ ds = GraphAlbum.eager_graph(:genres).eager_graph(:tracks).eager_graph(:band)
669
+ 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)'
670
+ def ds.fetch_rows(sql, &block)
671
+ yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
672
+ end
673
+ a = ds.all
674
+ a.should be_a_kind_of(Array)
675
+ a.size.should == 1
676
+ a.first.should be_a_kind_of(GraphAlbum)
677
+ a.first.values.should == {:id => 1, :band_id => 2}
678
+ a = a.first
679
+ a.band.should be_a_kind_of(GraphBand)
680
+ a.band.values.should == {:id => 2, :vocalist_id=>6}
681
+ a.tracks.should be_a_kind_of(Array)
682
+ a.tracks.size.should == 1
683
+ a.tracks.first.should be_a_kind_of(GraphTrack)
684
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
685
+ a.genres.should be_a_kind_of(Array)
686
+ a.genres.size.should == 1
687
+ a.genres.first.should be_a_kind_of(GraphGenre)
688
+ a.genres.first.values.should == {:id => 4}
689
+ end
690
+
691
+ it "should allow cascading of eager loading for associations of associated models" do
692
+ ds = GraphTrack.eager_graph(:album=>{:band=>:members})
693
+ 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)'
694
+ def ds.fetch_rows(sql, &block)
695
+ yield({:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6})
696
+ end
697
+ a = ds.all
698
+ a.should be_a_kind_of(Array)
699
+ a.size.should == 1
700
+ a.first.should be_a_kind_of(GraphTrack)
701
+ a.first.values.should == {:id => 3, :album_id => 1}
702
+ a = a.first
703
+ a.album.should be_a_kind_of(GraphAlbum)
704
+ a.album.values.should == {:id => 1, :band_id => 2}
705
+ a.album.band.should be_a_kind_of(GraphBand)
706
+ a.album.band.values.should == {:id => 2, :vocalist_id=>6}
707
+ a.album.band.members.should be_a_kind_of(Array)
708
+ a.album.band.members.size.should == 1
709
+ a.album.band.members.first.should be_a_kind_of(GraphBandMember)
710
+ a.album.band.members.first.values.should == {:id => 5}
711
+ end
712
+
713
+ it "should allow cascading of eager loading for multiple *_to_many associations, eliminating duplicates caused by cartesian products" do
714
+ ds = GraphBand.eager_graph({:albums=>:tracks}, :members)
715
+ 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)'
716
+ def ds.fetch_rows(sql, &block)
717
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>5})
718
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>6})
719
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>5})
720
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>6})
721
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>5})
722
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>6})
723
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>5})
724
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>6})
725
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>5})
726
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>6})
727
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>5})
728
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>6})
729
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>5})
730
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>6})
731
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>5})
732
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>6})
733
+ end
734
+ a = ds.all
735
+ a.should == [GraphBand.load(:id=>1, :vocalist_id=>2), GraphBand.load(:id=>2, :vocalist_id=>2)]
736
+ members = a.map{|x| x.members}
737
+ members.should == [[GraphBandMember.load(:id=>5), GraphBandMember.load(:id=>6)], [GraphBandMember.load(:id=>5), GraphBandMember.load(:id=>6)]]
738
+ albums = a.map{|x| x.albums}
739
+ albums.should == [[GraphAlbum.load(:id=>3, :band_id=>1), GraphAlbum.load(:id=>4, :band_id=>1)], [GraphAlbum.load(:id=>5, :band_id=>2), GraphAlbum.load(:id=>6, :band_id=>2)]]
740
+ tracks = albums.map{|x| x.map{|y| y.tracks}}
741
+ tracks.should == [[[GraphTrack.load(:id=>4, :album_id=>3), GraphTrack.load(:id=>5, :album_id=>3)], [GraphTrack.load(:id=>6, :album_id=>4), GraphTrack.load(:id=>7, :album_id=>4)]], [[GraphTrack.load(:id=>8, :album_id=>5), GraphTrack.load(:id=>9, :album_id=>5)], [GraphTrack.load(:id=>1, :album_id=>6), GraphTrack.load(:id=>2, :album_id=>6)]]]
742
+ end
743
+
744
+ it "should populate the reciprocal many_to_one association when eagerly loading the one_to_many association" do
745
+ MODEL_DB.reset
746
+ ds = GraphAlbum.eager_graph(:tracks)
747
+ 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)'
748
+ def ds.fetch_rows(sql, &block)
749
+ @db << sql
750
+ yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
751
+ end
752
+ a = ds.all
753
+ a.should be_a_kind_of(Array)
754
+ a.size.should == 1
755
+ a.first.should be_a_kind_of(GraphAlbum)
756
+ a.first.values.should == {:id => 1, :band_id => 2}
757
+ a = a.first
758
+ a.tracks.should be_a_kind_of(Array)
759
+ a.tracks.size.should == 1
760
+ a.tracks.first.should be_a_kind_of(GraphTrack)
761
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
762
+ a.tracks.first.album.should be_a_kind_of(GraphAlbum)
763
+ a.tracks.first.album.should == a
764
+ MODEL_DB.sqls.length.should == 1
765
+ end
766
+
767
+ it "should eager load multiple associations from the same table" do
768
+ ds = GraphBand.eager_graph(:vocalist, :members)
769
+ 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)'
770
+ def ds.fetch_rows(sql, &block)
771
+ yield({:id=>2, :vocalist_id=>6, :vocalist_id_0=>6, :members_id=>5})
772
+ end
773
+ a = ds.all
774
+ a.should be_a_kind_of(Array)
775
+ a.size.should == 1
776
+ a.first.should be_a_kind_of(GraphBand)
777
+ a.first.values.should == {:id => 2, :vocalist_id => 6}
778
+ a = a.first
779
+ a.vocalist.should be_a_kind_of(GraphBandMember)
780
+ a.vocalist.values.should == {:id => 6}
781
+ a.members.should be_a_kind_of(Array)
782
+ a.members.size.should == 1
783
+ a.members.first.should be_a_kind_of(GraphBandMember)
784
+ a.members.first.values.should == {:id => 5}
785
+ end
786
+
787
+ it "should give you a graph of tables when called without .all" do
788
+ ds = GraphAlbum.eager_graph(:band)
789
+ 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)'
790
+ def ds.fetch_rows(sql, &block)
791
+ yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
792
+ end
793
+ ds.first.should == {:albums=>GraphAlbum.load(:id => 1, :band_id => 2), :band=>GraphBand.load(:id => 2, :vocalist_id=>3)}
794
+ end
795
+
796
+ it "should not drop any associated objects if the graph could not be a cartesian product" do
797
+ ds = GraphBand.eager_graph(:members, :vocalist)
798
+ 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)'
799
+ def ds.fetch_rows(sql, &block)
800
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
801
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
802
+ end
803
+ a = ds.all
804
+ a.should be_a_kind_of(Array)
805
+ a.size.should == 1
806
+ a.first.should be_a_kind_of(GraphBand)
807
+ a.first.values.should == {:id => 2, :vocalist_id => 6}
808
+ a = a.first
809
+ a.vocalist.should be_a_kind_of(GraphBandMember)
810
+ a.vocalist.values.should == {:id => 6}
811
+ a.members.should be_a_kind_of(Array)
812
+ a.members.size.should == 2
813
+ a.members.first.should be_a_kind_of(GraphBandMember)
814
+ a.members.first.values.should == {:id => 5}
815
+ a.members.last.should be_a_kind_of(GraphBandMember)
816
+ a.members.last.values.should == {:id => 5}
817
+ end
818
+
819
+ it "should drop duplicate items that occur in sequence if the graph could be a cartesian product" do
820
+ ds = GraphBand.eager_graph(:members, :genres)
821
+ 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)'
822
+ def ds.fetch_rows(sql, &block)
823
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>7})
824
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>8})
825
+ yield({:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>7})
826
+ yield({:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>8})
827
+ end
828
+ a = ds.all
829
+ a.should be_a_kind_of(Array)
830
+ a.size.should == 1
831
+ a.first.should be_a_kind_of(GraphBand)
832
+ a.first.values.should == {:id => 2, :vocalist_id => 6}
833
+ a = a.first
834
+ a.members.should be_a_kind_of(Array)
835
+ a.members.size.should == 2
836
+ a.members.first.should be_a_kind_of(GraphBandMember)
837
+ a.members.first.values.should == {:id => 5}
838
+ a.members.last.should be_a_kind_of(GraphBandMember)
839
+ a.members.last.values.should == {:id => 6}
840
+ a.genres.size.should == 2
841
+ a.genres.first.should be_a_kind_of(GraphGenre)
842
+ a.genres.first.values.should == {:id => 7}
843
+ a.genres.last.should be_a_kind_of(GraphGenre)
844
+ a.genres.last.values.should == {:id => 8}
845
+ end
846
+
847
+ it "should be able to be used in combination with #eager" do
848
+ MODEL_DB.reset
849
+ ds = GraphAlbum.eager_graph(:tracks).eager(:genres)
850
+ def ds.fetch_rows(sql, &block)
851
+ @db << sql
852
+ yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
853
+ end
854
+ ds2 = GraphGenre.dataset
855
+ def ds2.fetch_rows(sql, &block)
856
+ @db << sql
857
+ yield({:id=>6, :x_foreign_key_x=>1})
858
+ end
859
+ a = ds.all
860
+ a.should be_a_kind_of(Array)
861
+ a.size.should == 1
862
+ a.first.should be_a_kind_of(GraphAlbum)
863
+ a.first.values.should == {:id => 1, :band_id => 2}
864
+ a = a.first
865
+ a.tracks.should be_a_kind_of(Array)
866
+ a.tracks.size.should == 1
867
+ a.tracks.first.should be_a_kind_of(GraphTrack)
868
+ a.tracks.first.values.should == {:id=>3, :album_id=>1}
869
+ a.genres.should be_a_kind_of(Array)
870
+ a.genres.size.should == 1
871
+ a.genres.first.should be_a_kind_of(GraphGenre)
872
+ a.genres.first.values.should == {:id=>6}
873
+ 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)',
874
+ "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)))"]
875
+ end
876
+
877
+ it "should handle no associated records for a single many_to_one association" do
878
+ ds = GraphAlbum.eager_graph(:band)
879
+ 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)'
880
+ def ds.fetch_rows(sql, &block)
881
+ yield({:id=>1, :band_id=>2, :band_id_0=>nil, :vocalist_id=>nil})
882
+ end
883
+ a = ds.all
884
+ a.should be_a_kind_of(Array)
885
+ a.size.should == 1
886
+ a.first.should be_a_kind_of(GraphAlbum)
887
+ a.first.values.should == {:id => 1, :band_id => 2}
888
+ a.first.associations.fetch(:band, 2).should == nil
889
+ end
890
+
891
+ it "should handle no associated records for a single one_to_many association" do
892
+ ds = GraphAlbum.eager_graph(:tracks)
893
+ 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)'
894
+ def ds.fetch_rows(sql, &block)
895
+ yield({:id=>1, :band_id=>2, :tracks_id=>nil, :album_id=>nil})
896
+ end
897
+ a = ds.all
898
+ a.should be_a_kind_of(Array)
899
+ a.size.should == 1
900
+ a.first.should be_a_kind_of(GraphAlbum)
901
+ a.first.values.should == {:id => 1, :band_id => 2}
902
+ a.first.tracks.should == []
903
+ end
904
+
905
+ it "should handle no associated records for a single many_to_many association" do
906
+ ds = GraphAlbum.eager_graph(:genres)
907
+ 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)'
908
+ def ds.fetch_rows(sql, &block)
909
+ yield({:id=>1, :band_id=>2, :genres_id=>nil})
910
+ end
911
+ a = ds.all
912
+ a.should be_a_kind_of(Array)
913
+ a.size.should == 1
914
+ a.first.should be_a_kind_of(GraphAlbum)
915
+ a.first.values.should == {:id => 1, :band_id => 2}
916
+ a.first.genres.should == []
917
+ end
918
+
919
+ it "should handle missing associated records when loading multiple associations" do
920
+ ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
921
+ 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)'
922
+ def ds.fetch_rows(sql, &block)
923
+ yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>3, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
924
+ yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>4, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
925
+ yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>5, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
926
+ yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>6, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
927
+ end
928
+ a = ds.all
929
+ a.should be_a_kind_of(Array)
930
+ a.size.should == 1
931
+ a.first.should be_a_kind_of(GraphAlbum)
932
+ a.first.values.should == {:id => 1, :band_id => 2}
933
+ a = a.first
934
+ a.tracks.should be_a_kind_of(Array)
935
+ a.tracks.size.should == 4
936
+ a.tracks.first.should be_a_kind_of(GraphTrack)
937
+ a.tracks.collect{|x|x[:id]}.should == [3,4,5,6]
938
+ a.associations.fetch(:band, 2).should == nil
939
+ a.genres.should == []
940
+ end
941
+
942
+ it "should handle missing associated records when cascading eager loading for associations of associated models" do
943
+ ds = GraphTrack.eager_graph(:album=>{:band=>:members})
944
+ 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)'
945
+ def ds.fetch_rows(sql, &block)
946
+ yield({:id=>2, :album_id=>2, :album_id_0=>nil, :band_id=>nil, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
947
+ yield({:id=>3, :album_id=>3, :album_id_0=>3, :band_id=>3, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
948
+ yield({:id=>4, :album_id=>4, :album_id_0=>4, :band_id=>2, :members_id=>nil, :band_id_0=>2, :vocalist_id=>6})
949
+ yield({:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>5, :band_id_0=>4, :vocalist_id=>8})
950
+ yield({:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>6, :band_id_0=>4, :vocalist_id=>8})
951
+ end
952
+ a = ds.all
953
+ a.should be_a_kind_of(Array)
954
+ a.size.should == 4
955
+ a.first.should be_a_kind_of(GraphTrack)
956
+ a.collect{|x|x[:id]}.should == [2,3,4,5]
957
+ a[0].associations.fetch(:album, 2).should == nil
958
+ a[1].album.should be_a_kind_of(GraphAlbum)
959
+ a[1].album.values.should == {:id => 3, :band_id => 3}
960
+ a[1].album.associations.fetch(:band, 2).should == nil
961
+ a[2].album.should be_a_kind_of(GraphAlbum)
962
+ a[2].album.values.should == {:id => 4, :band_id => 2}
963
+ a[2].album.band.should be_a_kind_of(GraphBand)
964
+ a[2].album.band.values.should == {:id => 2, :vocalist_id=>6}
965
+ a[2].album.band.members.should == []
966
+ a[3].album.should be_a_kind_of(GraphAlbum)
967
+ a[3].album.values.should == {:id => 1, :band_id => 4}
968
+ a[3].album.band.should be_a_kind_of(GraphBand)
969
+ a[3].album.band.values.should == {:id => 4, :vocalist_id=>8}
970
+ a[3].album.band.members.size.should == 2
971
+ a[3].album.band.members.first.should be_a_kind_of(GraphBandMember)
972
+ a[3].album.band.members.first.values.should == {:id => 5}
973
+ a[3].album.band.members.last.should be_a_kind_of(GraphBandMember)
974
+ a[3].album.band.members.last.values.should == {:id => 6}
975
+ end
976
+
977
+ it "should respect the association's :primary_key option" do
978
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :primary_key=>:vocalist_id
979
+ ds = GraphAlbum.eager_graph(:inner_band)
980
+ 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)'
981
+ def ds.fetch_rows(sql, &block)
982
+ yield({:id=>3, :band_id=>2, :inner_band_id=>5, :vocalist_id=>2})
983
+ end
984
+ as = ds.all
985
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
986
+ as.first.inner_band.should == GraphBand.load(:id=>5, :vocalist_id=>2)
987
+
988
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :primary_key=>:band_id
989
+ ds = GraphAlbum.eager_graph(:right_tracks)
990
+ 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)'
991
+ def ds.fetch_rows(sql, &block)
992
+ yield({:id=>3, :band_id=>2, :right_tracks_id=>5, :album_id=>2})
993
+ yield({:id=>3, :band_id=>2, :right_tracks_id=>6, :album_id=>2})
994
+ end
995
+ as = ds.all
996
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
997
+ as.first.right_tracks.should == [GraphTrack.load(:id=>5, :album_id=>2), GraphTrack.load(:id=>6, :album_id=>2)]
998
+ end
999
+
1000
+ it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
1001
+ 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
1002
+ ds = GraphAlbum.eager_graph(:inner_genres)
1003
+ 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)'
1004
+ def ds.fetch_rows(sql, &block)
1005
+ yield({:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12})
1006
+ yield({:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22})
1007
+ end
1008
+ as = ds.all
1009
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1010
+ as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
1011
+ end
1012
+
1013
+ it "should respect the association's :graph_select option" do
1014
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_select=>:vocalist_id
1015
+ 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)'
1016
+
1017
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_select=>[:album_id]
1018
+ GraphAlbum.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.album_id FROM albums LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id)'
1019
+
1020
+ GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_select=>[]
1021
+ GraphAlbum.eager_graph(:inner_genres).sql.should == 'SELECT albums.id, albums.band_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS inner_genres ON (inner_genres.id = ag.genre_id)'
1022
+ end
1023
+
1024
+ it "should respect the association's :graph_join_type option" do
1025
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_join_type=>:inner
1026
+ GraphAlbum.eager_graph(:inner_band).sql.should == 'SELECT albums.id, albums.band_id, inner_band.id AS inner_band_id, inner_band.vocalist_id FROM albums INNER JOIN bands AS inner_band ON (inner_band.id = albums.band_id)'
1027
+
1028
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_join_type=>:right_outer
1029
+ GraphAlbum.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums RIGHT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id)'
1030
+
1031
+ GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_type=>:inner
1032
+ GraphAlbum.eager_graph(:inner_genres).sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums INNER JOIN ag ON (ag.album_id = albums.id) INNER JOIN genres AS inner_genres ON (inner_genres.id = ag.genre_id)'
1033
+ end
1034
+
1035
+ it "should respect the association's :graph_join_type option" do
1036
+ GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_table_join_type=>:inner
1037
+ GraphAlbum.eager_graph(:inner_genres).sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums INNER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS inner_genres ON (inner_genres.id = ag.genre_id)'
1038
+
1039
+ GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_table_join_type=>:inner, :graph_join_type=>:right_outer
1040
+ GraphAlbum.eager_graph(:inner_genres).sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums INNER JOIN ag ON (ag.album_id = albums.id) RIGHT OUTER JOIN genres AS inner_genres ON (inner_genres.id = ag.genre_id)'
1041
+ end
1042
+
1043
+ it "should respect the association's :conditions option" do
1044
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :conditions=>{:active=>true}
1045
+ GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS active_band ON ((active_band.id = albums.band_id) AND (active_band.active = 't'))"
1046
+
1047
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :conditions=>{:id=>(0..100)}
1048
+ 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 LEFT OUTER JOIN tracks AS right_tracks ON ((right_tracks.album_id = albums.id) AND ((right_tracks.id >= 0) AND (right_tracks.id <= 100)))'
1049
+
1050
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :conditions=>{true=>:active}
1051
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
1052
+ end
1053
+
1054
+ it "should respect the association's :graph_conditions option" do
1055
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_conditions=>{:active=>true}
1056
+ GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS active_band ON ((active_band.id = albums.band_id) AND (active_band.active = 't'))"
1057
+
1058
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_conditions=>{:id=>(0..100)}
1059
+ 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 LEFT OUTER JOIN tracks AS right_tracks ON ((right_tracks.album_id = albums.id) AND ((right_tracks.id >= 0) AND (right_tracks.id <= 100)))'
1060
+
1061
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_conditions=>{true=>:active}
1062
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
1063
+ end
1064
+
1065
+ it "should respect the association's :graph_join_table_conditions option" do
1066
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_table_conditions=>{:active=>true}
1067
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND (ag.active = 't')) LEFT OUTER JOIN genres AS active_genres ON (active_genres.id = ag.genre_id)"
1068
+
1069
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_conditions=>{true=>:active}, :graph_join_table_conditions=>{true=>:active}
1070
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND ('t' = albums.active)) LEFT OUTER JOIN genres AS active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
1071
+ end
1072
+
1073
+ it "should respect the association's :graph_block option" do
1074
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_block=>proc{|ja,lja,js| {:active.qualify(ja)=>true}}
1075
+ GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS active_band ON ((active_band.id = albums.band_id) AND (active_band.active = 't'))"
1076
+
1077
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_block=>proc{|ja,lja,js| {:id.qualify(ja)=>(0..100)}}
1078
+ 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 LEFT OUTER JOIN tracks AS right_tracks ON ((right_tracks.album_id = albums.id) AND ((right_tracks.id >= 0) AND (right_tracks.id <= 100)))'
1079
+
1080
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_block=>proc{|ja,lja,js| {true=>:active.qualify(lja)}}
1081
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
1082
+ end
1083
+
1084
+ it "should respect the association's :graph_join_block option" do
1085
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_table_block=>proc{|ja,lja,js| {:active.qualify(ja)=>true}}
1086
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND (ag.active = 't')) LEFT OUTER JOIN genres AS active_genres ON (active_genres.id = ag.genre_id)"
1087
+
1088
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_block=>proc{|ja,lja,js| {true=>:active.qualify(lja)}}, :graph_join_table_block=>proc{|ja,lja,js| {true=>:active.qualify(lja)}}
1089
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON ((ag.album_id = albums.id) AND ('t' = albums.active)) LEFT OUTER JOIN genres AS active_genres ON ((active_genres.id = ag.genre_id) AND ('t' = ag.active))"
1090
+ end
1091
+
1092
+ it "should respect the association's :eager_grapher option" do
1093
+ 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)}
1094
+ GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums INNER JOIN bands AS active_band ON (active_band.active = 't')"
1095
+
1096
+ 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)}
1097
+ 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'
1098
+
1099
+ 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)}
1100
+ 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)"
1101
+ end
1102
+
1103
+ it "should respect the association's :graph_only_conditions option" do
1104
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_only_conditions=>{:active=>true}
1105
+ GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS active_band ON (active_band.active = 't')"
1106
+
1107
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_only_conditions=>nil, :graph_join_type=>:natural
1108
+ 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'
1109
+
1110
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_only_conditions=>[:album_id]
1111
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS active_genres USING (album_id)"
1112
+ end
1113
+
1114
+ it "should respect the association's :graph_join_table_only_conditions option" do
1115
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_table_only_conditions=>{:active=>true}
1116
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.active = 't') LEFT OUTER JOIN genres AS active_genres ON (active_genres.id = ag.genre_id)"
1117
+
1118
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_only_conditions=>(:price + 2 > 100), :graph_join_table_only_conditions=>"active"
1119
+ GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag ON (active) LEFT OUTER JOIN genres AS active_genres ON ((price + 2) > 100)"
1120
+ end
1121
+
1122
+ it "should create unique table aliases for all associations" do
1123
+ GraphAlbum.eager_graph(:previous_album=>{:previous_album=>:previous_album}).sql.should == "SELECT albums.id, albums.band_id, previous_album.id AS previous_album_id, previous_album.band_id AS previous_album_band_id, previous_album_0.id AS previous_album_0_id, previous_album_0.band_id AS previous_album_0_band_id, previous_album_1.id AS previous_album_1_id, previous_album_1.band_id AS previous_album_1_band_id FROM albums LEFT OUTER JOIN albums AS previous_album ON (previous_album.id = albums.previous_album_id) LEFT OUTER JOIN albums AS previous_album_0 ON (previous_album_0.id = previous_album.previous_album_id) LEFT OUTER JOIN albums AS previous_album_1 ON (previous_album_1.id = previous_album_0.previous_album_id)"
1124
+ end
1125
+
1126
+ it "should respect the association's :order" do
1127
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
1128
+ 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 LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id) ORDER BY right_tracks.id, right_tracks.album_id'
1129
+ end
1130
+
1131
+ it "should only qualify unqualified symbols, identifiers, or ordered versions in association's :order" do
1132
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:blah__id.identifier, :blah__id.identifier.desc, :blah__id.desc, :blah__id, :album_id, :album_id.desc, 1, 'RANDOM()'.lit, :a.qualify(:b)]
1133
+ 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 LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id) ORDER BY right_tracks.blah__id, right_tracks.blah__id DESC, blah.id DESC, blah.id, right_tracks.album_id, right_tracks.album_id DESC, 1, RANDOM(), b.a'
1134
+ end
1135
+
1136
+ it "should not respect the association's :order if :order_eager_graph is false" do
1137
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id], :order_eager_graph=>false
1138
+ 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 LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id)'
1139
+ end
1140
+
1141
+ it "should add the association's :order to the existing order" do
1142
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
1143
+ GraphAlbum.order(:band_id).eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.id) ORDER BY band_id, right_tracks.id, right_tracks.album_id'
1144
+ end
1145
+
1146
+ it "should add the association's :order for cascading associations" do
1147
+ GraphBand.one_to_many :a_albums, :class=>'GraphAlbum', :key=>:band_id, :order=>:name
1148
+ GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
1149
+ GraphBand.eager_graph(:a_albums=>:b_tracks).sql.should == 'SELECT bands.id, bands.vocalist_id, a_albums.id AS a_albums_id, a_albums.band_id, b_tracks.id AS b_tracks_id, b_tracks.album_id FROM bands LEFT OUTER JOIN albums AS a_albums ON (a_albums.band_id = bands.id) LEFT OUTER JOIN tracks AS b_tracks ON (b_tracks.album_id = a_albums.id) ORDER BY a_albums.name, b_tracks.id, b_tracks.album_id'
1150
+ GraphAlbum.one_to_many :albums, :class=>'GraphAlbum', :key=>:band_id, :order=>[:band_id, :id]
1151
+ GraphAlbum.eager_graph(:albums=>{:albums=>:albums}).sql.should == 'SELECT albums.id, albums.band_id, albums_0.id AS albums_0_id, albums_0.band_id AS albums_0_band_id, albums_1.id AS albums_1_id, albums_1.band_id AS albums_1_band_id, albums_2.id AS albums_2_id, albums_2.band_id AS albums_2_band_id FROM albums LEFT OUTER JOIN albums AS albums_0 ON (albums_0.band_id = albums.id) LEFT OUTER JOIN albums AS albums_1 ON (albums_1.band_id = albums_0.id) LEFT OUTER JOIN albums AS albums_2 ON (albums_2.band_id = albums_1.id) ORDER BY albums_0.band_id, albums_0.id, albums_1.band_id, albums_1.id, albums_2.band_id, albums_2.id'
1152
+ end
1153
+
1154
+ it "should add the associations :order for multiple associations" do
1155
+ GraphAlbum.many_to_many :a_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :order=>:id
1156
+ GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
1157
+ GraphAlbum.eager_graph(:a_genres, :b_tracks).sql.should == 'SELECT albums.id, albums.band_id, a_genres.id AS a_genres_id, b_tracks.id AS b_tracks_id, b_tracks.album_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS a_genres ON (a_genres.id = ag.genre_id) LEFT OUTER JOIN tracks AS b_tracks ON (b_tracks.album_id = albums.id) ORDER BY a_genres.id, b_tracks.id, b_tracks.album_id'
1158
+ end
1159
+
1160
+ it "should use the correct qualifier when graphing multiple tables with extra conditions" do
1161
+ GraphAlbum.many_to_many :a_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
1162
+ GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_conditions=>{:a=>:b}
1163
+ GraphAlbum.eager_graph(:a_genres, :b_tracks).sql.should == 'SELECT albums.id, albums.band_id, a_genres.id AS a_genres_id, b_tracks.id AS b_tracks_id, b_tracks.album_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS a_genres ON (a_genres.id = ag.genre_id) LEFT OUTER JOIN tracks AS b_tracks ON ((b_tracks.album_id = albums.id) AND (b_tracks.a = albums.b))'
1164
+ end
1165
+ end