viking-sequel 3.10.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 (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,1391 @@
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
+ many_to_many :graph_members, :clone=>:members, :eager_graph=>:bands
26
+ one_to_many :good_albums, :class=>'EagerAlbum', :key=>:band_id, :eager_block=>proc{|ds| ds.filter(:name=>'good')} do |ds|
27
+ ds.filter(:name=>'Good')
28
+ end
29
+ one_to_many :self_titled_albums, :class=>'EagerAlbum', :key=>:band_id, :allow_eager=>false do |ds|
30
+ ds.filter(:name=>name)
31
+ end
32
+ one_to_many :albums_by_name, :class=>'EagerAlbum', :key=>:band_id, :order=>:name, :allow_eager=>false
33
+ one_to_many :top_10_albums, :class=>'EagerAlbum', :key=>:band_id, :limit=>10
34
+ end
35
+
36
+ class ::EagerTrack < Sequel::Model(:tracks)
37
+ columns :id, :album_id
38
+ many_to_one :album, :class=>'EagerAlbum', :key=>:album_id
39
+ end
40
+
41
+ class ::EagerGenre < Sequel::Model(:genres)
42
+ columns :id, :xxx
43
+ many_to_many :albums, :class=>'EagerAlbum', :left_key=>:genre_id, :right_key=>:album_id, :join_table=>:ag
44
+ end
45
+
46
+ class ::EagerBandMember < Sequel::Model(:members)
47
+ columns :id
48
+ many_to_many :bands, :class=>'EagerBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm, :order =>:id
49
+ end
50
+
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
65
+ end
66
+ })
67
+
68
+ EagerBand.dataset.extend(Module.new {
69
+ def fetch_rows(sql)
70
+ 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
81
+ end
82
+ })
83
+
84
+ EagerTrack.dataset.extend(Module.new {
85
+ def fetch_rows(sql)
86
+ @db << sql
87
+ yield({:id => 3, :album_id => 1})
88
+ end
89
+ })
90
+
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
+ })
99
+
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
+ })
108
+ end
109
+
110
+ it "should raise an error if called without a symbol or hash" do
111
+ proc{EagerAlbum.eager(Object.new)}.should raise_error(Sequel::Error)
112
+ end
113
+
114
+ it "should eagerly load a single many_to_one association" do
115
+ 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
+ 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
125
+ end
126
+
127
+ it "should eagerly load a single one_to_one association" do
128
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id
129
+ a = EagerAlbum.eager(:track).all
130
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
131
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
132
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
133
+ MODEL_DB.sqls.length.should == 2
134
+ end
135
+
136
+ it "should eagerly load a single one_to_many association" do
137
+ 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}
142
+ 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
149
+ end
150
+
151
+ it "should eagerly load a single many_to_many association" do
152
+ 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}
157
+ 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
164
+ end
165
+
166
+ it "should eagerly load multiple associations in a single call" do
167
+ 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)))'))
177
+ 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
189
+ end
190
+
191
+ it "should eagerly load multiple associations in separate calls" do
192
+ 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)))'))
202
+ 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
214
+ end
215
+
216
+ it "should allow cascading of eager loading for associations of associated models" do
217
+ 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
223
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
224
+ 'SELECT * FROM albums WHERE (albums.id IN (1))',
225
+ 'SELECT * FROM bands WHERE (bands.id IN (2))',
226
+ "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
+ 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
237
+ end
238
+
239
+ it "should cascade eagerly loading when the :eager association option is used" do
240
+ 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}
245
+ 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
259
+ end
260
+
261
+ it "should respect :eager when lazily loading an association" do
262
+ 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}
267
+ 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
283
+ end
284
+
285
+ 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
+ })
292
+ 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}
297
+ 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
310
+ end
311
+
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
331
+ end
332
+
333
+ it "should respect :eager_graph when lazily loading an association" do
334
+ 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}
339
+ MODEL_DB.sqls.should == ['SELECT * FROM bands']
340
+ 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
+ })
347
+ 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
360
+ end
361
+
362
+ 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
+ })
373
+ a = EagerBand.load(:id=>2)
374
+ a.graph_members.should == [EagerBandMember.load(:id=>5)]
375
+ 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
+ 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
378
+ end
379
+
380
+ it "should respect :conditions when eagerly loading" do
381
+ EagerBandMember.many_to_many :good_bands, :clone=>:bands, :conditions=>{:a=>32}
382
+ 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}
387
+ 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
394
+
395
+ MODEL_DB.sqls.clear
396
+ EagerBandMember.many_to_many :good_bands, :clone=>:bands, :conditions=>"x = 1"
397
+ a = EagerBandMember.eager(:good_bands).all
398
+ 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']
399
+ end
400
+
401
+ it "should respect :order when eagerly loading" do
402
+ 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}
407
+ 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
414
+ end
415
+
416
+ it "should populate the reciprocal many_to_one association when eagerly loading the one_to_many association" do
417
+ 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}
422
+ 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
431
+ end
432
+
433
+ it "should cache the negative lookup when eagerly loading a many_to_one association" do
434
+ 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}
439
+ 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
444
+ end
445
+
446
+ it "should cache the negative lookup when eagerly loading a *_to_many associations" do
447
+ 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}
453
+ 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
460
+ end
461
+
462
+ it "should use the association's block when eager loading by default" do
463
+ EagerAlbum.eager(:good_tracks).all
464
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (name = 'Good'))"]
465
+ end
466
+
467
+ it "should use the eager_block option when eager loading if given" do
468
+ EagerBand.eager(:good_albums).all
469
+ 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
+ EagerBand.eager(:good_albums=>:good_tracks).all
472
+ 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
+ end
474
+
475
+ it "should raise an error when attempting to eagerly load an association with the :allow_eager option set to false" do
476
+ proc{EagerBand.eager(:self_titled_albums).all}.should raise_error(Sequel::Error)
477
+ proc{EagerBand.eager(:albums_by_name).all}.should raise_error(Sequel::Error)
478
+ end
479
+
480
+ it "should respect the association's :select option" do
481
+ EagerAlbum.eager(:band_name).all
482
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM bands WHERE (bands.id IN (2))"]
483
+ MODEL_DB.sqls.clear
484
+ EagerAlbum.eager(:track_names).all
485
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM tracks WHERE (tracks.album_id IN (1))"]
486
+ MODEL_DB.sqls.clear
487
+ EagerAlbum.eager(:genre_names).all
488
+ 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
+ end
490
+
491
+ it "should respect the association's :primary_key option" do
492
+ 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
+ })
499
+ as = EagerAlbum.eager(:special_band).all
500
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE (bands.p_k IN (2))"]
501
+ as.length.should == 1
502
+ as.first.special_band.should == EagerBand.load(:p_k=>2, :id=>1)
503
+ MODEL_DB.sqls.clear
504
+ 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
+ })
511
+ as = EagerAlbum.eager(:special_tracks).all
512
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (2))"]
513
+ as.length.should == 1
514
+ as.first.special_tracks.should == [EagerTrack.load(:album_id=>2, :id=>1)]
515
+ end
516
+
517
+ it "should respect the many_to_one association's composite keys" do
518
+ 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
+ })
525
+ as = EagerAlbum.eager(:special_band).all
526
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE ((bands.id, bands.p_k) IN ((2, 1)))"]
527
+ as.length.should == 1
528
+ as.first.special_band.should == EagerBand.load(:p_k=>1, :id=>2)
529
+ end
530
+
531
+ it "should respect the one_to_many association's composite keys" do
532
+ 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
+ })
539
+ as = EagerAlbum.eager(:special_tracks).all
540
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE ((tracks.id, tracks.album_id) IN ((2, 1)))"]
541
+ as.length.should == 1
542
+ as.first.special_tracks.should == [EagerTrack.load(:album_id=>1, :id=>2)]
543
+ end
544
+
545
+ it "should respect many_to_many association's composite keys" do
546
+ 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
+ })
554
+ as = EagerAlbum.eager(:special_genres).all
555
+ 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
+ as.length.should == 1
557
+ as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
558
+ end
559
+
560
+ it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
561
+ 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
+ })
569
+ as = EagerAlbum.eager(:special_genres).all
570
+ 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
+ as.length.should == 1
572
+ as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
573
+ end
574
+
575
+ it "should use the :eager_loader association option when eager loading" do
576
+ EagerAlbum.many_to_one :special_band, :eager_loader=>(proc do |key_hash, records, assocs|
577
+ item = EagerBand.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).order(:name).first
578
+ records.each{|r| r.associations[:special_band] = item}
579
+ end)
580
+ EagerAlbum.one_to_many :special_tracks, :eager_loader=>(proc do |key_hash, records, assocs|
581
+ items = EagerTrack.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).all
582
+ records.each{|r| r.associations[:special_tracks] = items}
583
+ end)
584
+ EagerAlbum.many_to_many :special_genres, :class=>:EagerGenre, :eager_loader=>(proc do |key_hash, records, assocs|
585
+ items = EagerGenre.inner_join(:ag, [:genre_id]).filter(:album_id=>records.collect{|r| r.pk}).all
586
+ records.each{|r| r.associations[:special_genres] = items}
587
+ end)
588
+ 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))'))
598
+ 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
610
+ end
611
+
612
+ it "should respect :after_load callbacks on associations when eager loading" do
613
+ EagerAlbum.many_to_one :al_band, :class=>'EagerBand', :key=>:band_id, :after_load=>proc{|o, a| a.id *=2}
614
+ EagerAlbum.one_to_many :al_tracks, :class=>'EagerTrack', :key=>:album_id, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
615
+ 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}}
616
+ a = EagerAlbum.eager(:al_band, :al_tracks, :al_genres).all.first
617
+ a.should == EagerAlbum.load(:id => 1, :band_id => 2)
618
+ a.al_band.should == EagerBand.load(:id=>4)
619
+ a.al_tracks.should == [EagerTrack.load(:id=>6, :album_id=>1)]
620
+ a.al_genres.should == [EagerGenre.load(:id=>8)]
621
+ end
622
+
623
+ it "should respect :uniq option when eagerly loading many_to_many associations" do
624
+ EagerAlbum.many_to_many :al_genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :uniq=>true
625
+ EagerGenre.dataset.extend(Module.new {
626
+ def fetch_rows(sql)
627
+ MODEL_DB.sqls << sql
628
+ yield({:x_foreign_key_x=>1, :id=>8})
629
+ yield({:x_foreign_key_x=>1, :id=>8})
630
+ end
631
+ })
632
+ a = EagerAlbum.eager(:al_genres).all.first
633
+ 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)))"]
634
+ a.should == EagerAlbum.load(:id => 1, :band_id => 2)
635
+ a.al_genres.should == [EagerGenre.load(:id=>8)]
636
+ end
637
+
638
+ it "should respect :distinct option when eagerly loading many_to_many associations" do
639
+ EagerAlbum.many_to_many :al_genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :distinct=>true
640
+ a = EagerAlbum.eager(:al_genres).all.first
641
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT DISTINCT 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)))"]
642
+ a.should == EagerAlbum.load(:id => 1, :band_id => 2)
643
+ a.al_genres.should == [EagerGenre.load(:id=>4)]
644
+ end
645
+ end
646
+
647
+ describe Sequel::Model, "#eager_graph" do
648
+ after(:all) do
649
+ class ::MockDataset
650
+ alias clone orig_clone
651
+ end
652
+ end
653
+
654
+ before(:all) do
655
+ class ::MockDataset
656
+ alias orig_clone clone
657
+ def clone(opts = {})
658
+ c = super()
659
+ c.opts = @opts.merge(opts)
660
+ c.instance_variable_set(:@columns, (@columns.dup if @columns))
661
+ c
662
+ end
663
+ end
664
+
665
+ class ::GraphAlbum < Sequel::Model(:albums)
666
+ dataset.opts[:from] = [:albums]
667
+ columns :id, :band_id
668
+ many_to_one :band, :class=>'GraphBand', :key=>:band_id
669
+ one_to_many :tracks, :class=>'GraphTrack', :key=>:album_id
670
+ many_to_many :genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
671
+ many_to_one :previous_album, :class=>'GraphAlbum'
672
+ end
673
+
674
+ class ::GraphBand < Sequel::Model(:bands)
675
+ dataset.opts[:from] = [:bands]
676
+ columns :id, :vocalist_id
677
+ many_to_one :vocalist, :class=>'GraphBandMember', :key=>:vocalist_id
678
+ one_to_many :albums, :class=>'GraphAlbum', :key=>:band_id
679
+ many_to_many :members, :class=>'GraphBandMember', :left_key=>:band_id, :right_key=>:member_id, :join_table=>:bm
680
+ many_to_many :genres, :class=>'GraphGenre', :left_key=>:band_id, :right_key=>:genre_id, :join_table=>:bg
681
+ end
682
+
683
+ class ::GraphTrack < Sequel::Model(:tracks)
684
+ dataset.opts[:from] = [:tracks]
685
+ columns :id, :album_id
686
+ many_to_one :album, :class=>'GraphAlbum', :key=>:album_id
687
+ end
688
+
689
+ class ::GraphGenre < Sequel::Model(:genres)
690
+ dataset.opts[:from] = [:genres]
691
+ columns :id
692
+ many_to_many :albums, :class=>'GraphAlbum', :left_key=>:genre_id, :right_key=>:album_id, :join_table=>:ag
693
+ end
694
+
695
+ class ::GraphBandMember < Sequel::Model(:members)
696
+ dataset.opts[:from] = [:members]
697
+ columns :id
698
+ many_to_many :bands, :class=>'GraphBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm
699
+ end
700
+ end
701
+
702
+ it "should raise an error if called without a symbol or hash" do
703
+ proc{GraphAlbum.eager_graph(Object.new)}.should raise_error(Sequel::Error)
704
+ end
705
+
706
+ it "should not split results and assign associations if ungraphed is called" do
707
+ ds = GraphAlbum.eager_graph(:band).ungraphed
708
+ 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)'
709
+ def ds.fetch_rows(sql, &block)
710
+ yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
711
+ end
712
+ ds.all.should == [GraphAlbum.load(:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3)]
713
+ end
714
+
715
+ it "should eagerly load a single many_to_one association" do
716
+ ds = GraphAlbum.eager_graph(:band)
717
+ 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)'
718
+ def ds.fetch_rows(sql, &block)
719
+ yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
720
+ end
721
+ a = ds.all
722
+ a.should be_a_kind_of(Array)
723
+ a.size.should == 1
724
+ a.first.should be_a_kind_of(GraphAlbum)
725
+ a.first.values.should == {:id => 1, :band_id => 2}
726
+ a = a.first
727
+ a.band.should be_a_kind_of(GraphBand)
728
+ a.band.values.should == {:id => 2, :vocalist_id=>3}
729
+ end
730
+
731
+ it "should eagerly load a single one_to_one association" do
732
+ GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
733
+ ds = GraphAlbum.eager_graph(:track)
734
+ 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)'
735
+ def ds.fetch_rows(sql, &block)
736
+ yield({:id=>1, :band_id=>2, :track_id=>3, :album_id=>1})
737
+ end
738
+ a = ds.all
739
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
740
+ a.first.track.should == GraphTrack.load(:id => 3, :album_id=>1)
741
+ end
742
+
743
+ it "should eagerly load a single one_to_many association" do
744
+ ds = GraphAlbum.eager_graph(:tracks)
745
+ 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)'
746
+ def ds.fetch_rows(sql, &block)
747
+ yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
748
+ end
749
+ a = ds.all
750
+ a.should be_a_kind_of(Array)
751
+ a.size.should == 1
752
+ a.first.should be_a_kind_of(GraphAlbum)
753
+ a.first.values.should == {:id => 1, :band_id => 2}
754
+ a = a.first
755
+ a.tracks.should be_a_kind_of(Array)
756
+ a.tracks.size.should == 1
757
+ a.tracks.first.should be_a_kind_of(GraphTrack)
758
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
759
+ end
760
+
761
+ it "should eagerly load a single many_to_many association" do
762
+ ds = GraphAlbum.eager_graph(:genres)
763
+ 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)'
764
+ def ds.fetch_rows(sql, &block)
765
+ yield({:id=>1, :band_id=>2, :genres_id=>4})
766
+ end
767
+ a = ds.all
768
+ a.should be_a_kind_of(Array)
769
+ a.size.should == 1
770
+ a.first.should be_a_kind_of(GraphAlbum)
771
+ a.first.values.should == {:id => 1, :band_id => 2}
772
+ a = a.first
773
+ a.genres.should be_a_kind_of(Array)
774
+ a.genres.size.should == 1
775
+ a.genres.first.should be_a_kind_of(GraphGenre)
776
+ a.genres.first.values.should == {:id => 4}
777
+ end
778
+
779
+ it "should eagerly load multiple associations in a single call" do
780
+ ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
781
+ 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)'
782
+ def ds.fetch_rows(sql, &block)
783
+ yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
784
+ end
785
+ a = ds.all
786
+ a.should be_a_kind_of(Array)
787
+ a.size.should == 1
788
+ a.first.should be_a_kind_of(GraphAlbum)
789
+ a.first.values.should == {:id => 1, :band_id => 2}
790
+ a = a.first
791
+ a.band.should be_a_kind_of(GraphBand)
792
+ a.band.values.should == {:id => 2, :vocalist_id=>6}
793
+ a.tracks.should be_a_kind_of(Array)
794
+ a.tracks.size.should == 1
795
+ a.tracks.first.should be_a_kind_of(GraphTrack)
796
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
797
+ a.genres.should be_a_kind_of(Array)
798
+ a.genres.size.should == 1
799
+ a.genres.first.should be_a_kind_of(GraphGenre)
800
+ a.genres.first.values.should == {:id => 4}
801
+ end
802
+
803
+ it "should eagerly load multiple associations in separate calls" do
804
+ ds = GraphAlbum.eager_graph(:genres).eager_graph(:tracks).eager_graph(:band)
805
+ 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)'
806
+ def ds.fetch_rows(sql, &block)
807
+ yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
808
+ end
809
+ a = ds.all
810
+ a.should be_a_kind_of(Array)
811
+ a.size.should == 1
812
+ a.first.should be_a_kind_of(GraphAlbum)
813
+ a.first.values.should == {:id => 1, :band_id => 2}
814
+ a = a.first
815
+ a.band.should be_a_kind_of(GraphBand)
816
+ a.band.values.should == {:id => 2, :vocalist_id=>6}
817
+ a.tracks.should be_a_kind_of(Array)
818
+ a.tracks.size.should == 1
819
+ a.tracks.first.should be_a_kind_of(GraphTrack)
820
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
821
+ a.genres.should be_a_kind_of(Array)
822
+ a.genres.size.should == 1
823
+ a.genres.first.should be_a_kind_of(GraphGenre)
824
+ a.genres.first.values.should == {:id => 4}
825
+ end
826
+
827
+ it "should allow cascading of eager loading for associations of associated models" do
828
+ ds = GraphTrack.eager_graph(:album=>{:band=>:members})
829
+ 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)'
830
+ def ds.fetch_rows(sql, &block)
831
+ yield({:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6})
832
+ end
833
+ a = ds.all
834
+ a.should be_a_kind_of(Array)
835
+ a.size.should == 1
836
+ a.first.should be_a_kind_of(GraphTrack)
837
+ a.first.values.should == {:id => 3, :album_id => 1}
838
+ a = a.first
839
+ a.album.should be_a_kind_of(GraphAlbum)
840
+ a.album.values.should == {:id => 1, :band_id => 2}
841
+ a.album.band.should be_a_kind_of(GraphBand)
842
+ a.album.band.values.should == {:id => 2, :vocalist_id=>6}
843
+ a.album.band.members.should be_a_kind_of(Array)
844
+ a.album.band.members.size.should == 1
845
+ a.album.band.members.first.should be_a_kind_of(GraphBandMember)
846
+ a.album.band.members.first.values.should == {:id => 5}
847
+ end
848
+
849
+ it "should allow cascading of eager loading for multiple *_to_many associations, eliminating duplicates caused by cartesian products" do
850
+ ds = GraphBand.eager_graph({:albums=>:tracks}, :members)
851
+ 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)'
852
+ def ds.fetch_rows(sql, &block)
853
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>5})
854
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>6})
855
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>5})
856
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>6})
857
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>5})
858
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>6})
859
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>5})
860
+ yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>6})
861
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>5})
862
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>6})
863
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>5})
864
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>6})
865
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>5})
866
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>6})
867
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>5})
868
+ yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>6})
869
+ end
870
+ a = ds.all
871
+ a.should == [GraphBand.load(:id=>1, :vocalist_id=>2), GraphBand.load(:id=>2, :vocalist_id=>2)]
872
+ members = a.map{|x| x.members}
873
+ members.should == [[GraphBandMember.load(:id=>5), GraphBandMember.load(:id=>6)], [GraphBandMember.load(:id=>5), GraphBandMember.load(:id=>6)]]
874
+ albums = a.map{|x| x.albums}
875
+ 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)]]
876
+ tracks = albums.map{|x| x.map{|y| y.tracks}}
877
+ 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)]]]
878
+ end
879
+
880
+ it "should populate the reciprocal many_to_one association when eagerly loading the one_to_many association" do
881
+ MODEL_DB.reset
882
+ ds = GraphAlbum.eager_graph(:tracks)
883
+ 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)'
884
+ def ds.fetch_rows(sql, &block)
885
+ @db << sql
886
+ yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
887
+ end
888
+ a = ds.all
889
+ a.should be_a_kind_of(Array)
890
+ a.size.should == 1
891
+ a.first.should be_a_kind_of(GraphAlbum)
892
+ a.first.values.should == {:id => 1, :band_id => 2}
893
+ a = a.first
894
+ a.tracks.should be_a_kind_of(Array)
895
+ a.tracks.size.should == 1
896
+ a.tracks.first.should be_a_kind_of(GraphTrack)
897
+ a.tracks.first.values.should == {:id => 3, :album_id=>1}
898
+ a.tracks.first.album.should be_a_kind_of(GraphAlbum)
899
+ a.tracks.first.album.should == a
900
+ MODEL_DB.sqls.length.should == 1
901
+ end
902
+
903
+ it "should eager load multiple associations from the same table" do
904
+ ds = GraphBand.eager_graph(:vocalist, :members)
905
+ 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)'
906
+ def ds.fetch_rows(sql, &block)
907
+ yield({:id=>2, :vocalist_id=>6, :vocalist_id_0=>6, :members_id=>5})
908
+ end
909
+ a = ds.all
910
+ a.should be_a_kind_of(Array)
911
+ a.size.should == 1
912
+ a.first.should be_a_kind_of(GraphBand)
913
+ a.first.values.should == {:id => 2, :vocalist_id => 6}
914
+ a = a.first
915
+ a.vocalist.should be_a_kind_of(GraphBandMember)
916
+ a.vocalist.values.should == {:id => 6}
917
+ a.members.should be_a_kind_of(Array)
918
+ a.members.size.should == 1
919
+ a.members.first.should be_a_kind_of(GraphBandMember)
920
+ a.members.first.values.should == {:id => 5}
921
+ end
922
+
923
+ it "should give you a graph of tables when called without .all" do
924
+ ds = GraphAlbum.eager_graph(:band)
925
+ 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)'
926
+ def ds.fetch_rows(sql, &block)
927
+ yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
928
+ end
929
+ ds.first.should == {:albums=>GraphAlbum.load(:id => 1, :band_id => 2), :band=>GraphBand.load(:id => 2, :vocalist_id=>3)}
930
+ end
931
+
932
+ it "should not drop any associated objects if the graph could not be a cartesian product" do
933
+ ds = GraphBand.eager_graph(:members, :vocalist)
934
+ 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)'
935
+ def ds.fetch_rows(sql, &block)
936
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
937
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
938
+ end
939
+ a = ds.all
940
+ a.should be_a_kind_of(Array)
941
+ a.size.should == 1
942
+ a.first.should be_a_kind_of(GraphBand)
943
+ a.first.values.should == {:id => 2, :vocalist_id => 6}
944
+ a = a.first
945
+ a.vocalist.should be_a_kind_of(GraphBandMember)
946
+ a.vocalist.values.should == {:id => 6}
947
+ a.members.should be_a_kind_of(Array)
948
+ a.members.size.should == 2
949
+ a.members.first.should be_a_kind_of(GraphBandMember)
950
+ a.members.first.values.should == {:id => 5}
951
+ a.members.last.should be_a_kind_of(GraphBandMember)
952
+ a.members.last.values.should == {:id => 5}
953
+ end
954
+
955
+ it "should respect the :cartesian_product_number option" do
956
+ GraphBand.many_to_one :other_vocalist, :class=>'GraphBandMember', :key=>:vocalist_id, :cartesian_product_number=>1
957
+ ds = GraphBand.eager_graph(:members, :other_vocalist)
958
+ 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)'
959
+ def ds.fetch_rows(sql, &block)
960
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6})
961
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6})
962
+ end
963
+ a = ds.all
964
+ a.should == [GraphBand.load(:id=>2, :vocalist_id => 6)]
965
+ a.first.other_vocalist.should == GraphBandMember.load(:id=>6)
966
+ a.first.members.should == [GraphBandMember.load(:id=>5)]
967
+ end
968
+
969
+ it "should drop duplicate items that occur in sequence if the graph could be a cartesian product" do
970
+ ds = GraphBand.eager_graph(:members, :genres)
971
+ 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)'
972
+ def ds.fetch_rows(sql, &block)
973
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>7})
974
+ yield({:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>8})
975
+ yield({:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>7})
976
+ yield({:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>8})
977
+ end
978
+ a = ds.all
979
+ a.should be_a_kind_of(Array)
980
+ a.size.should == 1
981
+ a.first.should be_a_kind_of(GraphBand)
982
+ a.first.values.should == {:id => 2, :vocalist_id => 6}
983
+ a = a.first
984
+ a.members.should be_a_kind_of(Array)
985
+ a.members.size.should == 2
986
+ a.members.first.should be_a_kind_of(GraphBandMember)
987
+ a.members.first.values.should == {:id => 5}
988
+ a.members.last.should be_a_kind_of(GraphBandMember)
989
+ a.members.last.values.should == {:id => 6}
990
+ a.genres.size.should == 2
991
+ a.genres.first.should be_a_kind_of(GraphGenre)
992
+ a.genres.first.values.should == {:id => 7}
993
+ a.genres.last.should be_a_kind_of(GraphGenre)
994
+ a.genres.last.values.should == {:id => 8}
995
+ end
996
+
997
+ it "should be able to be used in combination with #eager" do
998
+ MODEL_DB.reset
999
+ ds = GraphAlbum.eager_graph(:tracks).eager(:genres)
1000
+ def ds.fetch_rows(sql, &block)
1001
+ @db << sql
1002
+ yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
1003
+ end
1004
+ ds2 = GraphGenre.dataset
1005
+ def ds2.fetch_rows(sql, &block)
1006
+ @db << sql
1007
+ yield({:id=>6, :x_foreign_key_x=>1})
1008
+ end
1009
+ a = ds.all
1010
+ a.should be_a_kind_of(Array)
1011
+ a.size.should == 1
1012
+ a.first.should be_a_kind_of(GraphAlbum)
1013
+ a.first.values.should == {:id => 1, :band_id => 2}
1014
+ a = a.first
1015
+ a.tracks.should be_a_kind_of(Array)
1016
+ a.tracks.size.should == 1
1017
+ a.tracks.first.should be_a_kind_of(GraphTrack)
1018
+ a.tracks.first.values.should == {:id=>3, :album_id=>1}
1019
+ a.genres.should be_a_kind_of(Array)
1020
+ a.genres.size.should == 1
1021
+ a.genres.first.should be_a_kind_of(GraphGenre)
1022
+ a.genres.first.values.should == {:id=>6}
1023
+ 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)',
1024
+ "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)))"]
1025
+ end
1026
+
1027
+ it "should handle no associated records for a single many_to_one association" do
1028
+ ds = GraphAlbum.eager_graph(:band)
1029
+ 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)'
1030
+ def ds.fetch_rows(sql, &block)
1031
+ yield({:id=>1, :band_id=>2, :band_id_0=>nil, :vocalist_id=>nil})
1032
+ end
1033
+ a = ds.all
1034
+ a.should be_a_kind_of(Array)
1035
+ a.size.should == 1
1036
+ a.first.should be_a_kind_of(GraphAlbum)
1037
+ a.first.values.should == {:id => 1, :band_id => 2}
1038
+ a.first.associations.fetch(:band, 2).should == nil
1039
+ end
1040
+
1041
+ it "should handle no associated records for a single one_to_many association" do
1042
+ ds = GraphAlbum.eager_graph(:tracks)
1043
+ 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)'
1044
+ def ds.fetch_rows(sql, &block)
1045
+ yield({:id=>1, :band_id=>2, :tracks_id=>nil, :album_id=>nil})
1046
+ end
1047
+ a = ds.all
1048
+ a.should be_a_kind_of(Array)
1049
+ a.size.should == 1
1050
+ a.first.should be_a_kind_of(GraphAlbum)
1051
+ a.first.values.should == {:id => 1, :band_id => 2}
1052
+ a.first.tracks.should == []
1053
+ end
1054
+
1055
+ it "should handle no associated records for a single many_to_many association" do
1056
+ ds = GraphAlbum.eager_graph(:genres)
1057
+ 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)'
1058
+ def ds.fetch_rows(sql, &block)
1059
+ yield({:id=>1, :band_id=>2, :genres_id=>nil})
1060
+ end
1061
+ a = ds.all
1062
+ a.should be_a_kind_of(Array)
1063
+ a.size.should == 1
1064
+ a.first.should be_a_kind_of(GraphAlbum)
1065
+ a.first.values.should == {:id => 1, :band_id => 2}
1066
+ a.first.genres.should == []
1067
+ end
1068
+
1069
+ it "should handle missing associated records when loading multiple associations" do
1070
+ ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
1071
+ 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)'
1072
+ def ds.fetch_rows(sql, &block)
1073
+ yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>3, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1074
+ yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>4, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1075
+ yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>5, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1076
+ yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>6, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1077
+ end
1078
+ a = ds.all
1079
+ a.should be_a_kind_of(Array)
1080
+ a.size.should == 1
1081
+ a.first.should be_a_kind_of(GraphAlbum)
1082
+ a.first.values.should == {:id => 1, :band_id => 2}
1083
+ a = a.first
1084
+ a.tracks.should be_a_kind_of(Array)
1085
+ a.tracks.size.should == 4
1086
+ a.tracks.first.should be_a_kind_of(GraphTrack)
1087
+ a.tracks.collect{|x|x[:id]}.should == [3,4,5,6]
1088
+ a.associations.fetch(:band, 2).should == nil
1089
+ a.genres.should == []
1090
+ end
1091
+
1092
+ it "should handle missing associated records when cascading eager loading for associations of associated models" do
1093
+ ds = GraphTrack.eager_graph(:album=>{:band=>:members})
1094
+ 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)'
1095
+ def ds.fetch_rows(sql, &block)
1096
+ yield({:id=>2, :album_id=>2, :album_id_0=>nil, :band_id=>nil, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
1097
+ yield({:id=>3, :album_id=>3, :album_id_0=>3, :band_id=>3, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
1098
+ yield({:id=>4, :album_id=>4, :album_id_0=>4, :band_id=>2, :members_id=>nil, :band_id_0=>2, :vocalist_id=>6})
1099
+ yield({:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>5, :band_id_0=>4, :vocalist_id=>8})
1100
+ yield({:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>6, :band_id_0=>4, :vocalist_id=>8})
1101
+ end
1102
+ a = ds.all
1103
+ a.should be_a_kind_of(Array)
1104
+ a.size.should == 4
1105
+ a.first.should be_a_kind_of(GraphTrack)
1106
+ a.collect{|x|x[:id]}.should == [2,3,4,5]
1107
+ a[0].associations.fetch(:album, 2).should == nil
1108
+ a[1].album.should be_a_kind_of(GraphAlbum)
1109
+ a[1].album.values.should == {:id => 3, :band_id => 3}
1110
+ a[1].album.associations.fetch(:band, 2).should == nil
1111
+ a[2].album.should be_a_kind_of(GraphAlbum)
1112
+ a[2].album.values.should == {:id => 4, :band_id => 2}
1113
+ a[2].album.band.should be_a_kind_of(GraphBand)
1114
+ a[2].album.band.values.should == {:id => 2, :vocalist_id=>6}
1115
+ a[2].album.band.members.should == []
1116
+ a[3].album.should be_a_kind_of(GraphAlbum)
1117
+ a[3].album.values.should == {:id => 1, :band_id => 4}
1118
+ a[3].album.band.should be_a_kind_of(GraphBand)
1119
+ a[3].album.band.values.should == {:id => 4, :vocalist_id=>8}
1120
+ a[3].album.band.members.size.should == 2
1121
+ a[3].album.band.members.first.should be_a_kind_of(GraphBandMember)
1122
+ a[3].album.band.members.first.values.should == {:id => 5}
1123
+ a[3].album.band.members.last.should be_a_kind_of(GraphBandMember)
1124
+ a[3].album.band.members.last.values.should == {:id => 6}
1125
+ end
1126
+
1127
+ it "should respect the association's :primary_key option" do
1128
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :primary_key=>:vocalist_id
1129
+ ds = GraphAlbum.eager_graph(:inner_band)
1130
+ 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)'
1131
+ def ds.fetch_rows(sql, &block)
1132
+ yield({:id=>3, :band_id=>2, :inner_band_id=>5, :vocalist_id=>2})
1133
+ end
1134
+ as = ds.all
1135
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1136
+ as.first.inner_band.should == GraphBand.load(:id=>5, :vocalist_id=>2)
1137
+
1138
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :primary_key=>:band_id
1139
+ ds = GraphAlbum.eager_graph(:right_tracks)
1140
+ 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)'
1141
+ def ds.fetch_rows(sql, &block)
1142
+ yield({:id=>3, :band_id=>2, :right_tracks_id=>5, :album_id=>2})
1143
+ yield({:id=>3, :band_id=>2, :right_tracks_id=>6, :album_id=>2})
1144
+ end
1145
+ as = ds.all
1146
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1147
+ as.first.right_tracks.should == [GraphTrack.load(:id=>5, :album_id=>2), GraphTrack.load(:id=>6, :album_id=>2)]
1148
+ end
1149
+
1150
+ it "should respect many_to_one association's composite keys" do
1151
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>[:band_id, :id], :primary_key=>[:vocalist_id, :id]
1152
+ ds = GraphAlbum.eager_graph(:inner_band)
1153
+ 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))'
1154
+ def ds.fetch_rows(sql, &block)
1155
+ yield({:id=>3, :band_id=>2, :inner_band_id=>3, :vocalist_id=>2})
1156
+ end
1157
+ as = ds.all
1158
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1159
+ as.first.inner_band.should == GraphBand.load(:id=>3, :vocalist_id=>2)
1160
+ end
1161
+
1162
+ it "should respect one_to_many association's composite keys" do
1163
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>[:album_id, :id], :primary_key=>[:band_id, :id]
1164
+ ds = GraphAlbum.eager_graph(:right_tracks)
1165
+ 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))'
1166
+ def ds.fetch_rows(sql, &block)
1167
+ yield({:id=>3, :band_id=>2, :right_tracks_id=>3, :album_id=>2})
1168
+ end
1169
+ as = ds.all
1170
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1171
+ as.first.right_tracks.should == [GraphTrack.load(:id=>3, :album_id=>2)]
1172
+ end
1173
+
1174
+ it "should respect many_to_many association's composite keys" do
1175
+ 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
1176
+ ds = GraphAlbum.eager_graph(:sbands)
1177
+ 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))'
1178
+ def ds.fetch_rows(sql, &block)
1179
+ yield({:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6})
1180
+ yield({:id=>3, :band_id=>2, :sbands_id=>6, :vocalist_id=>22})
1181
+ end
1182
+ as = ds.all
1183
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1184
+ as.first.sbands.should == [GraphBand.load(:id=>5, :vocalist_id=>6), GraphBand.load(:id=>6, :vocalist_id=>22)]
1185
+ end
1186
+
1187
+ it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
1188
+ 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
1189
+ ds = GraphAlbum.eager_graph(:inner_genres)
1190
+ 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)'
1191
+ def ds.fetch_rows(sql, &block)
1192
+ yield({:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12})
1193
+ yield({:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22})
1194
+ end
1195
+ as = ds.all
1196
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1197
+ as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
1198
+ end
1199
+
1200
+ it "should respect the association's :graph_select option" do
1201
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_select=>:vocalist_id
1202
+ 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)'
1203
+
1204
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_select=>[:album_id]
1205
+ 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)'
1206
+
1207
+ GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_select=>[]
1208
+ 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)'
1209
+ end
1210
+
1211
+ it "should respect the association's :graph_join_type option" do
1212
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_join_type=>:inner
1213
+ 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)'
1214
+
1215
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_join_type=>:right_outer
1216
+ 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)'
1217
+
1218
+ GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_join_type=>:inner
1219
+ 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)'
1220
+ end
1221
+
1222
+ it "should respect the association's :graph_join_table_join_type option" do
1223
+ 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
1224
+ 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)'
1225
+
1226
+ 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
1227
+ 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)'
1228
+ end
1229
+
1230
+ it "should respect the association's :conditions option" do
1231
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :conditions=>{:active=>true}
1232
+ 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 IS TRUE))"
1233
+
1234
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :conditions=>{:id=>(0..100)}
1235
+ 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))'
1236
+
1237
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :conditions=>{true=>:active}
1238
+ 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))"
1239
+ end
1240
+
1241
+ it "should respect the association's :graph_conditions option" do
1242
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_conditions=>{:active=>true}
1243
+ 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 IS TRUE))"
1244
+
1245
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_conditions=>{:id=>(0..100)}
1246
+ 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))'
1247
+
1248
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_conditions=>{true=>:active}
1249
+ 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))"
1250
+ end
1251
+
1252
+ it "should respect the association's :graph_join_table_conditions option" do
1253
+ 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}
1254
+ 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 IS TRUE)) LEFT OUTER JOIN genres AS active_genres ON (active_genres.id = ag.genre_id)"
1255
+
1256
+ 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}
1257
+ 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))"
1258
+ end
1259
+
1260
+ it "should respect the association's :graph_block option" do
1261
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_block=>proc{|ja,lja,js| {:active.qualify(ja)=>true}}
1262
+ 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 IS TRUE))"
1263
+
1264
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_block=>proc{|ja,lja,js| {:id.qualify(ja)=>(0..100)}}
1265
+ 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))'
1266
+
1267
+ 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)}}
1268
+ 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))"
1269
+ end
1270
+
1271
+ it "should respect the association's :graph_join_block option" do
1272
+ 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}}
1273
+ 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 IS TRUE)) LEFT OUTER JOIN genres AS active_genres ON (active_genres.id = ag.genre_id)"
1274
+
1275
+ 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)}}
1276
+ 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))"
1277
+ end
1278
+
1279
+ it "should respect the association's :eager_grapher option" do
1280
+ 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)}
1281
+ 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)"
1282
+
1283
+ 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)}
1284
+ 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'
1285
+
1286
+ 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)}
1287
+ 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)"
1288
+ end
1289
+
1290
+ it "should respect the association's :graph_only_conditions option" do
1291
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_only_conditions=>{:active=>true}
1292
+ 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)"
1293
+
1294
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_only_conditions=>nil, :graph_join_type=>:natural
1295
+ 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'
1296
+
1297
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_only_conditions=>[:album_id]
1298
+ 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)"
1299
+ end
1300
+
1301
+ it "should respect the association's :graph_join_table_only_conditions option" do
1302
+ 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}
1303
+ 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 IS TRUE) LEFT OUTER JOIN genres AS active_genres ON (active_genres.id = ag.genre_id)"
1304
+
1305
+ 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"
1306
+ 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)"
1307
+ end
1308
+
1309
+ it "should create unique table aliases for all associations" do
1310
+ 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)"
1311
+ end
1312
+
1313
+ it "should respect the association's :order" do
1314
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
1315
+ 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'
1316
+ end
1317
+
1318
+ it "should only qualify unqualified symbols, identifiers, or ordered versions in association's :order" do
1319
+ 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)]
1320
+ 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'
1321
+ end
1322
+
1323
+ it "should not respect the association's :order if :order_eager_graph is false" do
1324
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id], :order_eager_graph=>false
1325
+ 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)'
1326
+ end
1327
+
1328
+ it "should add the association's :order to the existing order" do
1329
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
1330
+ 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'
1331
+ end
1332
+
1333
+ it "should add the association's :order for cascading associations" do
1334
+ GraphBand.one_to_many :a_albums, :class=>'GraphAlbum', :key=>:band_id, :order=>:name
1335
+ GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
1336
+ 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'
1337
+ GraphAlbum.one_to_many :albums, :class=>'GraphAlbum', :key=>:band_id, :order=>[:band_id, :id]
1338
+ 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'
1339
+ end
1340
+
1341
+ it "should add the associations :order for multiple associations" do
1342
+ GraphAlbum.many_to_many :a_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :order=>:id
1343
+ GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[:id, :album_id]
1344
+ 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'
1345
+ end
1346
+
1347
+ it "should use the correct qualifier when graphing multiple tables with extra conditions" do
1348
+ GraphAlbum.many_to_many :a_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag
1349
+ GraphAlbum.one_to_many :b_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_conditions=>{:a=>:b}
1350
+ 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))'
1351
+ end
1352
+
1353
+ it "should eagerly load associated records for classes that do not have a primary key" do
1354
+ GraphAlbum.no_primary_key
1355
+ GraphGenre.no_primary_key
1356
+ 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
1357
+ ds = GraphAlbum.eager_graph(:inner_genres)
1358
+ 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)'
1359
+ def ds.fetch_rows(sql, &block)
1360
+ yield({:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12})
1361
+ yield({:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22})
1362
+ end
1363
+ as = ds.all
1364
+ as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1365
+ as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
1366
+ end
1367
+
1368
+ it "should handle eager loading with schemas and aliases of different types" do
1369
+ GraphAlbum.eager_graph(:band).join(:s__genres, [:b_id]).eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id, genres_0.id AS genres_0_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id) INNER JOIN s.genres USING (b_id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS genres_0 ON (genres_0.id = ag.genre_id)'
1370
+ GraphAlbum.eager_graph(:band).join(:genres.qualify(:s), [:b_id]).eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id, genres_0.id AS genres_0_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id) INNER JOIN s.genres USING (b_id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS genres_0 ON (genres_0.id = ag.genre_id)'
1371
+ GraphAlbum.eager_graph(:band).join(:s__b.as('genres'), [:b_id]).eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id, genres_0.id AS genres_0_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id) INNER JOIN s.b AS genres USING (b_id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS genres_0 ON (genres_0.id = ag.genre_id)'
1372
+ GraphAlbum.eager_graph(:band).join(:s__b, [:b_id], :genres.identifier).eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id, genres_0.id AS genres_0_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id) INNER JOIN s.b AS genres USING (b_id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS genres_0 ON (genres_0.id = ag.genre_id)'
1373
+ GraphAlbum.eager_graph(:band).join(:genres.identifier, [:b_id]).eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id, genres_0.id AS genres_0_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id) INNER JOIN genres USING (b_id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS genres_0 ON (genres_0.id = ag.genre_id)'
1374
+ GraphAlbum.eager_graph(:band).join('genres', [:b_id]).eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id, genres_0.id AS genres_0_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id) INNER JOIN genres USING (b_id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS genres_0 ON (genres_0.id = ag.genre_id)'
1375
+ end
1376
+
1377
+ it "should raise errors if invalid aliases or table styles are used" do
1378
+ proc{GraphAlbum.from_self(:alias=>:bands.qualify(:s)).eager_graph(:band)}.should raise_error(Sequel::Error)
1379
+ proc{GraphAlbum.from('?'.lit(:bands)).eager_graph(:band)}.should raise_error(Sequel::Error)
1380
+ end
1381
+
1382
+ it "should eagerly load schema qualified tables correctly with joins" do
1383
+ c1 = Class.new(GraphAlbum)
1384
+ c2 = Class.new(GraphGenre)
1385
+ c1.dataset = c1.dataset.from(:s__a)
1386
+ c2.dataset = c2.dataset.from(:s__g)
1387
+ c1.many_to_many :a_genres, :class=>c2, :left_primary_key=>:id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:s__ag
1388
+ ds = c1.join(:s__t, [:b_id]).eager_graph(:a_genres)
1389
+ 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)'
1390
+ end
1391
+ end