sequel 3.21.0 → 3.39.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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -1,9 +1,7 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe Sequel::Model, "#eager" do
4
- before(:each) do
5
- MODEL_DB.reset
6
-
4
+ before do
7
5
  class ::EagerAlbum < Sequel::Model(:albums)
8
6
  columns :id, :band_id
9
7
  many_to_one :band, :class=>'EagerBand', :key=>:band_id
@@ -15,6 +13,10 @@ describe Sequel::Model, "#eager" do
15
13
  many_to_one :band_name, :class=>'EagerBand', :key=>:band_id, :select=>[:id, :name]
16
14
  one_to_many :track_names, :class=>'EagerTrack', :key=>:album_id, :select=>[:id, :name]
17
15
  many_to_many :genre_names, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :select=>[:id]
16
+
17
+ def band_id3
18
+ band_id*3
19
+ end
18
20
  end
19
21
 
20
22
  class ::EagerBand < Sequel::Model(:bands)
@@ -31,6 +33,10 @@ describe Sequel::Model, "#eager" do
31
33
  end
32
34
  one_to_many :albums_by_name, :class=>'EagerAlbum', :key=>:band_id, :order=>:name, :allow_eager=>false
33
35
  one_to_many :top_10_albums, :class=>'EagerAlbum', :key=>:band_id, :limit=>10
36
+
37
+ def id3
38
+ id/3
39
+ end
34
40
  end
35
41
 
36
42
  class ::EagerTrack < Sequel::Model(:tracks)
@@ -48,80 +54,90 @@ describe Sequel::Model, "#eager" do
48
54
  many_to_many :bands, :class=>'EagerBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm, :order =>:id
49
55
  end
50
56
 
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
57
+ EagerAlbum.dataset.columns(:id, :band_id)
58
+ EagerAlbum.dataset._fetch = proc do |sql|
59
+ h = if sql =~ /101/
60
+ {:id => 101, :band_id=> 101}
61
+ else
62
+ {:id => 1, :band_id=> 2}
65
63
  end
66
- })
64
+ h[:x_foreign_key_x] = 4 if sql =~ /ag\.genre_id/
65
+ h
66
+ end
67
67
 
68
- EagerBand.dataset.extend(Module.new {
69
- def fetch_rows(sql)
68
+ EagerBand.dataset._fetch = proc do |sql|
69
+ case sql
70
+ when /id IN (101)/
71
+ # nothing
72
+ when /id > 100/
73
+ [{:id => 101}, {:id => 102}]
74
+ else
70
75
  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
76
+ h[:x_foreign_key_x] = 5 if sql =~ /bm\.member_id/
77
+ h
81
78
  end
82
- })
79
+ end
83
80
 
84
- EagerTrack.dataset.extend(Module.new {
85
- def fetch_rows(sql)
86
- @db << sql
87
- yield({:id => 3, :album_id => 1})
88
- end
89
- })
81
+ EagerTrack.dataset._fetch = {:id => 3, :album_id => 1}
90
82
 
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
- })
83
+ EagerGenre.dataset._fetch = proc do |sql|
84
+ h = {:id => 4}
85
+ h[:x_foreign_key_x] = 1 if sql =~ /ag\.album_id/
86
+ h
87
+ end
99
88
 
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
- })
89
+ EagerBandMember.dataset._fetch = proc do |sql|
90
+ h = {:id => 5}
91
+ h[:x_foreign_key_x] = 2 if sql =~ /bm\.band_id/
92
+ h
93
+ end
94
+
95
+ MODEL_DB.reset
96
+ end
97
+ after do
98
+ [:EagerAlbum, :EagerBand, :EagerTrack, :EagerGenre, :EagerBandMember].each{|x| Object.send(:remove_const, x)}
108
99
  end
109
100
 
101
+ it "should populate :key_hash and :id_map option correctly for custom eager loaders" do
102
+ khs = {}
103
+ pr = proc{|a, m| proc{|h| khs[a] = h[:key_hash][m]; h[:id_map].should == h[:key_hash][m]}}
104
+ EagerAlbum.many_to_one :sband, :clone=>:band, :eager_loader=>pr.call(:sband, :band_id)
105
+ EagerAlbum.one_to_many :stracks, :clone=>:tracks, :eager_loader=>pr.call(:stracks, :id)
106
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :eager_loader=>pr.call(:sgenres, :id)
107
+ EagerAlbum.eager(:sband, :stracks, :sgenres).all
108
+ khs.should == {:sband=>{2=>[EagerAlbum.load(:band_id=>2, :id=>1)]}, :stracks=>{1=>[EagerAlbum.load(:band_id=>2, :id=>1)]}, :sgenres=>{1=>[EagerAlbum.load(:band_id=>2, :id=>1)]}}
109
+ end
110
+
111
+ it "should populate :key_hash using the method symbol" do
112
+ khs = {}
113
+ pr = proc{|a, m| proc{|h| khs[a] = h[:key_hash][m]}}
114
+ EagerAlbum.many_to_one :sband, :clone=>:band, :eager_loader=>pr.call(:sband, :band_id), :key=>:band_id, :key_column=>:b_id
115
+ EagerAlbum.one_to_many :stracks, :clone=>:tracks, :eager_loader=>pr.call(:stracks, :id), :primary_key=>:id, :primary_key_column=>:i
116
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :eager_loader=>pr.call(:sgenres, :id), :left_primary_key=>:id, :left_primary_key_column=>:i
117
+ EagerAlbum.eager(:sband, :stracks, :sgenres).all
118
+ khs.should == {:sband=>{2=>[EagerAlbum.load(:band_id=>2, :id=>1)]}, :stracks=>{1=>[EagerAlbum.load(:band_id=>2, :id=>1)]}, :sgenres=>{1=>[EagerAlbum.load(:band_id=>2, :id=>1)]}}
119
+ end
120
+
110
121
  it "should raise an error if called without a symbol or hash" do
111
122
  proc{EagerAlbum.eager(Object.new)}.should raise_error(Sequel::Error)
112
123
  end
113
124
 
114
125
  it "should eagerly load a single many_to_one association" do
115
126
  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
127
  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
128
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
129
+ a.first.band.should == EagerBand.load(:id=>2)
130
+ MODEL_DB.sqls.should == []
131
+ end
132
+
133
+ it "should eagerly load a single many_to_one association with the same name as the column" do
134
+ EagerAlbum.def_column_alias(:band_id_id, :band_id)
135
+ EagerAlbum.many_to_one :band_id, :key_column=>:band_id, :class=>EagerBand
136
+ a = EagerAlbum.eager(:band_id).all
137
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM bands WHERE (bands.id IN (2))']
138
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
139
+ a.first.band_id.should == EagerBand.load(:id=>2)
140
+ MODEL_DB.sqls.should == []
125
141
  end
126
142
 
127
143
  it "should eagerly load a single one_to_one association" do
@@ -130,269 +146,277 @@ describe Sequel::Model, "#eager" do
130
146
  a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
131
147
  MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
132
148
  a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
133
- MODEL_DB.sqls.length.should == 2
149
+ MODEL_DB.sqls.should == []
150
+ end
151
+
152
+ it "should eagerly load a single one_to_one association using the :distinct_on strategy" do
153
+ EagerTrack.dataset.meta_def(:supports_distinct_on?){true}
154
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true
155
+ a = EagerAlbum.eager(:track).all
156
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
157
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT DISTINCT ON (tracks.album_id) * FROM tracks WHERE (tracks.album_id IN (1)) ORDER BY tracks.album_id']
158
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
159
+ MODEL_DB.sqls.should == []
160
+ end
161
+
162
+ it "should eagerly load a single one_to_one association using the :window_function strategy" do
163
+ EagerTrack.dataset.meta_def(:supports_window_functions?){true}
164
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name
165
+ a = EagerAlbum.eager(:track).all
166
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
167
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE (x_sequel_row_number_x = 1)']
168
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
169
+ MODEL_DB.sqls.should == []
170
+ end
171
+
172
+ it "should eagerly load a single one_to_one association using the :correlated_subquery strategy" do
173
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name
174
+ a = EagerAlbum.eager(:track).all
175
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
176
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 1))) ORDER BY name']
177
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
178
+ MODEL_DB.sqls.should == []
179
+ end
180
+
181
+ it "should handle qualified order clauses when eagerly loading a single one_to_one association using the :correlated_subquery strategy" do
182
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>[:tracks__name, Sequel.desc(:tracks__name), Sequel.qualify(:tracks, :name), Sequel.qualify(:t, :name), 1]
183
+ a = EagerAlbum.eager(:track).all
184
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
185
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY t1.name, t1.name DESC, t1.name, t.name, 1 LIMIT 1))) ORDER BY tracks.name, tracks.name DESC, tracks.name, t.name, 1']
186
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
187
+ MODEL_DB.sqls.should == []
188
+ end
189
+
190
+ it "should handle qualified composite keys when eagerly loading a single one_to_one association using the :correlated_subquery strategy" do
191
+ c1 = Class.new(EagerAlbum)
192
+ c2 = Class.new(EagerTrack)
193
+ c1.set_primary_key [:id, :band_id]
194
+ c2.set_primary_key [:id, :album_id]
195
+ c1.one_to_one :track, :class=>c2, :key=>[:album_id, :id], :eager_limit_strategy=>:correlated_subquery
196
+ c2.dataset._fetch = {:id => 2, :album_id=>1}
197
+ a = c1.eager(:track).all
198
+ a.should == [c1.load(:id => 1, :band_id => 2)]
199
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (((tracks.album_id, tracks.id) IN ((1, 2))) AND ((tracks.id, tracks.album_id) IN (SELECT t1.id, t1.album_id FROM tracks AS t1 WHERE ((t1.album_id = tracks.album_id) AND (t1.id = tracks.id)) LIMIT 1)))']
200
+ a.first.track.should == c2.load(:id => 2, :album_id=>1)
201
+ MODEL_DB.sqls.should == []
134
202
  end
135
203
 
136
204
  it "should eagerly load a single one_to_many association" do
137
205
  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}
206
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
142
207
  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
208
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
209
+ MODEL_DB.sqls.should == []
149
210
  end
150
211
 
151
212
  it "should eagerly load a single many_to_many association" do
152
213
  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}
214
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
157
215
  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
216
+ a.first.genres.should == [EagerGenre.load(:id=>4)]
217
+ MODEL_DB.sqls.should == []
218
+ end
219
+
220
+ it "should support using a custom :key option when eager loading many_to_one associations" do
221
+ EagerAlbum.many_to_one :sband, :clone=>:band, :key=>:band_id3
222
+ EagerBand.dataset._fetch = {:id=>6}
223
+ a = EagerAlbum.eager(:sband).all
224
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM bands WHERE (bands.id IN (6))']
225
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
226
+ a.first.sband.should == EagerBand.load(:id=>6)
227
+ MODEL_DB.sqls.should == []
228
+ end
229
+
230
+ it "should support using a custom :primary_key option when eager loading one_to_many associations" do
231
+ EagerBand.one_to_many :salbums, :clone=>:albums, :primary_key=>:id3, :eager=>nil
232
+ EagerBand.dataset._fetch = {:id=>6}
233
+ a = EagerBand.eager(:salbums).all
234
+ MODEL_DB.sqls.should == ['SELECT * FROM bands', 'SELECT * FROM albums WHERE (albums.band_id IN (2))']
235
+ a.should == [EagerBand.load(:id => 6)]
236
+ a.first.salbums.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
237
+ MODEL_DB.sqls.should == []
238
+ end
239
+
240
+ it "should support using a custom :left_primary_key option when eager loading many_to_many associations" do
241
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :left_primary_key=>:band_id3
242
+ EagerGenre.dataset._fetch = {:id=>4, :x_foreign_key_x=>6}
243
+ a = EagerAlbum.eager(:sgenres).all
244
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
245
+ 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 (6)))"]
246
+ a.first.sgenres.should == [EagerGenre.load(:id=>4)]
247
+ MODEL_DB.sqls.should == []
248
+ end
249
+
250
+ it "should handle a :eager_loading_predicate_key option to change the SQL used in the lookup, for many_to_one associations" do
251
+ EagerAlbum.many_to_one :sband, :clone=>:band, :eager_loading_predicate_key=>Sequel./(:bands__id, 3), :primary_key_method=>:id3
252
+ EagerBand.dataset._fetch = {:id=>6}
253
+ a = EagerAlbum.eager(:sband).all
254
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM bands WHERE ((bands.id / 3) IN (2))']
255
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
256
+ a.first.sband.should == EagerBand.load(:id=>6)
257
+ MODEL_DB.sqls.should == []
258
+ end
259
+
260
+ it "should handle a :eager_loading_predicate_key option to change the SQL used in the lookup, for one_to_many associations" do
261
+ EagerBand.one_to_many :salbums, :clone=>:albums, :eager_loading_predicate_key=>Sequel.*(:albums__band_id, 3), :key_method=>:band_id3, :eager=>nil
262
+ EagerBand.dataset._fetch = {:id=>6}
263
+ a = EagerBand.eager(:salbums).all
264
+ MODEL_DB.sqls.should == ['SELECT * FROM bands', 'SELECT * FROM albums WHERE ((albums.band_id * 3) IN (6))']
265
+ a.should == [EagerBand.load(:id => 6)]
266
+ a.first.salbums.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
267
+ MODEL_DB.sqls.should == []
268
+ end
269
+
270
+ it "should handle a :eager_loading_predicate_key option to change the SQL used in the lookup, for many_to_many associations" do
271
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :eager_loading_predicate_key=>Sequel.*(:ag__album_id, 1)
272
+ a = EagerAlbum.eager(:sgenres).all
273
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
274
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, (ag.album_id * 1) AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND ((ag.album_id * 1) IN (1)))"]
275
+ a.first.sgenres.should == [EagerGenre.load(:id=>4)]
276
+ MODEL_DB.sqls.should == []
277
+ end
278
+
279
+ it "should raise an error for an unhandled :eager_loader_key option" do
280
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :eager_loader_key=>1
281
+ ds = EagerAlbum.eager(:sgenres)
282
+ proc{ds.all}.should raise_error(Sequel::Error)
283
+ end
284
+
285
+ it "should not add entry to key_hash for :eager_loader_key=>nil option" do
286
+ eo = nil
287
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :eager_loader_key=>nil, :eager_loader=>proc{|o| eo = o}
288
+ ds = EagerAlbum.eager(:sgenres)
289
+ ds.all
290
+ eo[:key_hash].should == {}
291
+ eo[:id_map].should == nil
292
+ end
293
+
294
+ it "should correctly handle a :select=>[] option to many_to_many" do
295
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :select=>[]
296
+ a = EagerAlbum.eager(:sgenres).all
297
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT *, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
298
+ end
299
+
300
+ it "should correctly handle an aliased join table in many_to_many" do
301
+ EagerAlbum.many_to_many :sgenres, :clone=>:genres, :join_table=>:ag___ga
302
+ a = EagerAlbum.eager(:sgenres).all
303
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ga.album_id AS x_foreign_key_x FROM genres INNER JOIN ag AS ga ON ((ga.genre_id = genres.id) AND (ga.album_id IN (1)))"]
164
304
  end
165
305
 
166
306
  it "should eagerly load multiple associations in a single call" do
167
307
  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)))'))
308
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
309
+ sqls = MODEL_DB.sqls
310
+ sqls.shift.should == 'SELECT * FROM albums'
311
+ sqls.sort.should == ['SELECT * FROM bands WHERE (bands.id IN (2))',
312
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))',
313
+ '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
314
  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
315
+ a.band.should == EagerBand.load(:id=>2)
316
+ a.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
317
+ a.genres.should == [EagerGenre.load(:id => 4)]
318
+ MODEL_DB.sqls.should == []
189
319
  end
190
320
 
191
321
  it "should eagerly load multiple associations in separate calls" do
192
322
  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)))'))
323
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
324
+ sqls = MODEL_DB.sqls
325
+ sqls.shift.should == 'SELECT * FROM albums'
326
+ sqls.sort.should == ['SELECT * FROM bands WHERE (bands.id IN (2))',
327
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))',
328
+ '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
329
  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
330
+ a.band.should == EagerBand.load(:id=>2)
331
+ a.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
332
+ a.genres.should == [EagerGenre.load(:id => 4)]
333
+ MODEL_DB.sqls.should == []
214
334
  end
215
335
 
216
336
  it "should allow cascading of eager loading for associations of associated models" do
217
337
  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
338
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
223
339
  MODEL_DB.sqls.should == ['SELECT * FROM tracks',
224
340
  'SELECT * FROM albums WHERE (albums.id IN (1))',
225
341
  'SELECT * FROM bands WHERE (bands.id IN (2))',
226
342
  "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
343
  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
344
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
345
+ a.album.band.should == EagerBand.load(:id => 2)
346
+ a.album.band.members.should == [EagerBandMember.load(:id => 5)]
347
+ MODEL_DB.sqls.should == []
237
348
  end
238
349
 
239
350
  it "should cascade eagerly loading when the :eager association option is used" do
240
351
  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}
352
+ a.should == [EagerBand.load(:id=>2)]
245
353
  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
354
+ 'SELECT * FROM albums WHERE (albums.band_id IN (2))',
355
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
356
+ a.first.albums.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
357
+ a.first.albums.first.tracks.should == [EagerTrack.load(:id => 3, :album_id => 1)]
358
+ MODEL_DB.sqls.should == []
259
359
  end
260
360
 
261
361
  it "should respect :eager when lazily loading an association" do
262
362
  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}
363
+ a.should == [EagerBand.load(:id=>2)]
267
364
  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
365
+ a = a.first.albums
366
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (albums.band_id = 2)',
367
+ 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
368
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
369
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id => 1)]
370
+ MODEL_DB.sqls.should == []
283
371
  end
284
372
 
285
373
  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
- })
374
+ EagerAlbum.dataset._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
292
375
  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}
376
+ a.should == [EagerBand.load(:id=>2)]
297
377
  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
378
+ '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))']
379
+ a.first.graph_albums.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
380
+ a.first.graph_albums.first.tracks.should == [EagerTrack.load(:id => 3, :album_id => 1)]
381
+ MODEL_DB.sqls.should == []
310
382
  end
311
383
 
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
384
+ it "should raise an Error when eager loading a many_to_many association with the :eager_graph option" do
385
+ proc{EagerBand.eager(:graph_members).all}.should raise_error(Sequel::Error)
331
386
  end
332
387
 
333
388
  it "should respect :eager_graph when lazily loading an association" do
334
389
  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}
390
+ a.should == [EagerBand.load(:id=>2)]
339
391
  MODEL_DB.sqls.should == ['SELECT * FROM bands']
340
392
  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
- })
393
+ EagerAlbum.dataset._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
347
394
  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
395
+ MODEL_DB.sqls.should == ['SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) WHERE (albums.band_id = 2)']
396
+ a.graph_albums.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
397
+ a.graph_albums.first.tracks.should == [EagerTrack.load(:id => 3, :album_id => 1)]
398
+ MODEL_DB.sqls.should == []
360
399
  end
361
400
 
362
401
  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
- })
402
+ ds = EagerBandMember.dataset
403
+ def ds.columns() [:id] end
404
+ ds._fetch = [{:id=>5, :bands_id=>2, :p_k=>6}, {:id=>5, :bands_id=>3, :p_k=>6}]
373
405
  a = EagerBand.load(:id=>2)
374
406
  a.graph_members.should == [EagerBandMember.load(:id=>5)]
375
407
  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
408
  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
409
+ MODEL_DB.sqls.should == []
378
410
  end
379
411
 
380
412
  it "should respect :conditions when eagerly loading" do
381
413
  EagerBandMember.many_to_many :good_bands, :clone=>:bands, :conditions=>{:a=>32}
382
414
  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}
415
+ a.should == [EagerBandMember.load(:id => 5)]
387
416
  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
417
+ a.first.good_bands.should == [EagerBand.load(:id => 2)]
418
+ MODEL_DB.sqls.should == []
394
419
 
395
- MODEL_DB.sqls.clear
396
420
  EagerBandMember.many_to_many :good_bands, :clone=>:bands, :conditions=>"x = 1"
397
421
  a = EagerBandMember.eager(:good_bands).all
398
422
  MODEL_DB.sqls.should == ['SELECT * FROM members', 'SELECT bands.*, bm.member_id AS x_foreign_key_x FROM bands INNER JOIN bm ON ((bm.band_id = bands.id) AND (bm.member_id IN (5))) WHERE (x = 1) ORDER BY id']
@@ -400,63 +424,38 @@ describe Sequel::Model, "#eager" do
400
424
 
401
425
  it "should respect :order when eagerly loading" do
402
426
  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}
427
+ a.should == [EagerBandMember.load(:id => 5)]
407
428
  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
429
+ a.first.bands.should == [EagerBand.load(:id => 2)]
430
+ MODEL_DB.sqls.should == []
414
431
  end
415
432
 
416
433
  it "should populate the reciprocal many_to_one association when eagerly loading the one_to_many association" do
417
434
  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}
435
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
422
436
  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
437
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
438
+ a.first.tracks.first.album.should == a.first
439
+ MODEL_DB.sqls.should == []
431
440
  end
432
441
 
433
442
  it "should cache the negative lookup when eagerly loading a many_to_one association" do
434
443
  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}
444
+ a.should == [EagerAlbum.load(:id => 101, :band_id => 101)]
439
445
  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
446
+ a.first.associations.fetch(:band, 2).should be_nil
447
+ a.first.band.should be_nil
448
+ MODEL_DB.sqls.should == []
444
449
  end
445
450
 
446
451
  it "should cache the negative lookup when eagerly loading a *_to_many associations" do
447
452
  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
453
+ a.should == [EagerBand.load(:id => 101), EagerBand.load(:id =>102)]
454
+ sqls = MODEL_DB.sqls
455
+ ['SELECT * FROM albums WHERE (albums.band_id IN (101, 102))', 'SELECT * FROM albums WHERE (albums.band_id IN (102, 101))'].should include(sqls.delete_at(1))
456
+ sqls.should == ['SELECT * FROM bands WHERE (id > 100)', "SELECT * FROM tracks WHERE (tracks.album_id IN (101))"]
457
+ a.map{|b| b.associations[:albums]}.should == [[EagerAlbum.load({:band_id=>101, :id=>101})], []]
458
+ MODEL_DB.sqls.should == []
460
459
  end
461
460
 
462
461
  it "should use the association's block when eager loading by default" do
@@ -467,7 +466,6 @@ describe Sequel::Model, "#eager" do
467
466
  it "should use the eager_block option when eager loading if given" do
468
467
  EagerBand.eager(:good_albums).all
469
468
  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
469
  EagerBand.eager(:good_albums=>:good_tracks).all
472
470
  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
471
  end
@@ -480,34 +478,31 @@ describe Sequel::Model, "#eager" do
480
478
  it "should respect the association's :select option" do
481
479
  EagerAlbum.eager(:band_name).all
482
480
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM bands WHERE (bands.id IN (2))"]
483
- MODEL_DB.sqls.clear
484
481
  EagerAlbum.eager(:track_names).all
485
482
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT id, name FROM tracks WHERE (tracks.album_id IN (1))"]
486
- MODEL_DB.sqls.clear
487
483
  EagerAlbum.eager(:genre_names).all
488
484
  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
485
  end
490
486
 
487
+ it "should respect many_to_one association's :qualify option" do
488
+ EagerAlbum.many_to_one :special_band, :class=>:EagerBand, :qualify=>false, :key=>:band_id
489
+ EagerBand.dataset._fetch = {:id=>2}
490
+ as = EagerAlbum.eager(:special_band).all
491
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE (id IN (2))"]
492
+ as.map{|a| a.special_band}.should == [EagerBand.load(:id=>2)]
493
+ MODEL_DB.sqls.should == []
494
+ end
495
+
491
496
  it "should respect the association's :primary_key option" do
492
497
  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
- })
498
+ EagerBand.dataset._fetch = {:p_k=>2, :id=>1}
499
499
  as = EagerAlbum.eager(:special_band).all
500
500
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE (bands.p_k IN (2))"]
501
501
  as.length.should == 1
502
502
  as.first.special_band.should == EagerBand.load(:p_k=>2, :id=>1)
503
- MODEL_DB.sqls.clear
503
+
504
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
- })
505
+ EagerTrack.dataset._fetch = {:album_id=>2, :id=>1}
511
506
  as = EagerAlbum.eager(:special_tracks).all
512
507
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (2))"]
513
508
  as.length.should == 1
@@ -516,12 +511,7 @@ describe Sequel::Model, "#eager" do
516
511
 
517
512
  it "should respect the many_to_one association's composite keys" do
518
513
  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
- })
514
+ EagerBand.dataset._fetch = {:p_k=>1, :id=>2}
525
515
  as = EagerAlbum.eager(:special_band).all
526
516
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM bands WHERE ((bands.id, bands.p_k) IN ((2, 1)))"]
527
517
  as.length.should == 1
@@ -530,12 +520,7 @@ describe Sequel::Model, "#eager" do
530
520
 
531
521
  it "should respect the one_to_many association's composite keys" do
532
522
  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
- })
523
+ EagerTrack.dataset._fetch = {:album_id=>1, :id=>2}
539
524
  as = EagerAlbum.eager(:special_tracks).all
540
525
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE ((tracks.id, tracks.album_id) IN ((2, 1)))"]
541
526
  as.length.should == 1
@@ -544,13 +529,7 @@ describe Sequel::Model, "#eager" do
544
529
 
545
530
  it "should respect many_to_many association's composite keys" do
546
531
  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
- })
532
+ EagerGenre.dataset._fetch = [{:x_foreign_key_0_x=>2, :x_foreign_key_1_x=>1, :id=>5}, {:x_foreign_key_0_x=>2, :x_foreign_key_1_x=>1, :id=>6}]
554
533
  as = EagerAlbum.eager(:special_genres).all
555
534
  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
535
  as.length.should == 1
@@ -559,21 +538,118 @@ describe Sequel::Model, "#eager" do
559
538
 
560
539
  it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
561
540
  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
- })
541
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
569
542
  as = EagerAlbum.eager(:special_genres).all
570
543
  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
544
  as.length.should == 1
572
545
  as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
573
546
  end
574
547
 
548
+ it "should respect the :limit option on a one_to_many association" do
549
+ EagerAlbum.one_to_many :first_two_tracks, :class=>:EagerTrack, :key=>:album_id, :limit=>2
550
+ EagerTrack.dataset._fetch = [{:album_id=>1, :id=>2}, {:album_id=>1, :id=>3}, {:album_id=>1, :id=>4}]
551
+ as = EagerAlbum.eager(:first_two_tracks).all
552
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (1))"]
553
+ as.length.should == 1
554
+ as.first.first_two_tracks.should == [EagerTrack.load(:album_id=>1, :id=>2), EagerTrack.load(:album_id=>1, :id=>3)]
555
+
556
+ MODEL_DB.reset
557
+ EagerAlbum.one_to_many :first_two_tracks, :class=>:EagerTrack, :key=>:album_id, :limit=>[2,1]
558
+ as = EagerAlbum.eager(:first_two_tracks).all
559
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (1))"]
560
+ as.length.should == 1
561
+ as.first.first_two_tracks.should == [EagerTrack.load(:album_id=>1, :id=>3), EagerTrack.load(:album_id=>1, :id=>4)]
562
+ end
563
+
564
+ it "should respect the :limit option on a one_to_many association using the :window_function strategy" do
565
+ EagerTrack.dataset.meta_def(:supports_window_functions?){true}
566
+ EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name, :limit=>2
567
+ a = EagerAlbum.eager(:tracks).all
568
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
569
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE (x_sequel_row_number_x <= 2)']
570
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
571
+ MODEL_DB.sqls.should == []
572
+ end
573
+
574
+ it "should respect the :limit option with an offset on a one_to_many association using the :window_function strategy" do
575
+ EagerTrack.dataset.meta_def(:supports_window_functions?){true}
576
+ EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name, :limit=>[2, 1]
577
+ a = EagerAlbum.eager(:tracks).all
578
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
579
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))']
580
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
581
+ MODEL_DB.sqls.should == []
582
+ end
583
+
584
+ it "should respect the :limit option on a one_to_many association using the :correlated_subquery strategy" do
585
+ EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name, :limit=>2
586
+ a = EagerAlbum.eager(:tracks).all
587
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
588
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 2))) ORDER BY name']
589
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
590
+ MODEL_DB.sqls.should == []
591
+ end
592
+
593
+ it "should respect the :limit option with an offset on a one_to_many association using the :correlated_subquery strategy" do
594
+ EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name, :limit=>[2, 1]
595
+ a = EagerAlbum.eager(:tracks).all
596
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
597
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 2 OFFSET 1))) ORDER BY name']
598
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
599
+ MODEL_DB.sqls.should == []
600
+ end
601
+
602
+ it "should respect the limit option on a many_to_many association" do
603
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>2
604
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}, {:x_foreign_key_x=>2, :id=>7}]
605
+ as = EagerAlbum.eager(:first_two_genres).all
606
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))"]
607
+ as.length.should == 1
608
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
609
+
610
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}, {:x_foreign_key_x=>2, :id=>7}]
611
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>[2, 1]
612
+ as = EagerAlbum.eager(:first_two_genres).all
613
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))"]
614
+ as.length.should == 1
615
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>6), EagerGenre.load(:id=>7)]
616
+ end
617
+
618
+ it "should respect the limit option on a many_to_many association using the :window_function strategy" do
619
+ EagerGenre.dataset.meta_def(:supports_window_functions?){true}
620
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>true, :limit=>2, :order=>:name
621
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
622
+ as = EagerAlbum.eager(:first_two_genres).all
623
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM (SELECT genres.*, ag.album_id AS x_foreign_key_x, row_number() OVER (PARTITION BY ag.album_id ORDER BY name) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))) AS t1 WHERE (x_sequel_row_number_x <= 2)"]
624
+ as.length.should == 1
625
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
626
+
627
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
628
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>true, :limit=>[2, 1], :order=>:name
629
+ as = EagerAlbum.eager(:first_two_genres).all
630
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM (SELECT genres.*, ag.album_id AS x_foreign_key_x, row_number() OVER (PARTITION BY ag.album_id ORDER BY name) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))"]
631
+ as.length.should == 1
632
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
633
+ end
634
+
635
+ it "should respect the limit option on a many_to_many association using the :correlated_subquery strategy" do
636
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>:correlated_subquery, :limit=>2, :order=>:name
637
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
638
+ as = EagerAlbum.eager(:first_two_genres).all
639
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2))) WHERE (genres.id IN (SELECT t1.id FROM genres AS t1 INNER JOIN ag AS t2 ON ((t2.genre_id = t1.id) AND (t2.album_id = ag.album_id)) ORDER BY name LIMIT 2)) ORDER BY name"]
640
+ as.length.should == 1
641
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
642
+
643
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>2, :id=>5}, {:x_foreign_key_x=>2, :id=>6}]
644
+ EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>:correlated_subquery, :limit=>[2, 1], :order=>:name
645
+ as = EagerAlbum.eager(:first_two_genres).all
646
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2))) WHERE (genres.id IN (SELECT t1.id FROM genres AS t1 INNER JOIN ag AS t2 ON ((t2.genre_id = t1.id) AND (t2.album_id = ag.album_id)) ORDER BY name LIMIT 2 OFFSET 1)) ORDER BY name"]
647
+ as.length.should == 1
648
+ as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
649
+ end
650
+
575
651
  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|
652
+ EagerAlbum.many_to_one :special_band, :key=>:band_id, :eager_loader=>(proc do |key_hash, records, assocs|
577
653
  item = EagerBand.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).order(:name).first
578
654
  records.each{|r| r.associations[:special_band] = item}
579
655
  end)
@@ -586,27 +662,17 @@ describe Sequel::Model, "#eager" do
586
662
  records.each{|r| r.associations[:special_genres] = items}
587
663
  end)
588
664
  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))'))
665
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
666
+ sqls = MODEL_DB.sqls
667
+ sqls.shift.should == 'SELECT * FROM albums'
668
+ sqls.sort.should == ['SELECT * FROM bands WHERE (album_id IN (1, 2)) ORDER BY name LIMIT 1',
669
+ 'SELECT * FROM genres INNER JOIN ag USING (genre_id) WHERE (album_id IN (1))',
670
+ 'SELECT * FROM tracks WHERE (album_id IN (1, 2))']
598
671
  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
672
+ a.special_band.should == EagerBand.load(:id => 2)
673
+ a.special_tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
674
+ a.special_genres.should == [EagerGenre.load(:id => 4)]
675
+ MODEL_DB.sqls.should == []
610
676
  end
611
677
 
612
678
  it "should raise an error if you use an :eager_loader proc with the wrong arity" do
@@ -626,13 +692,7 @@ describe Sequel::Model, "#eager" do
626
692
 
627
693
  it "should respect :uniq option when eagerly loading many_to_many associations" do
628
694
  EagerAlbum.many_to_many :al_genres, :class=>'EagerGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :uniq=>true
629
- EagerGenre.dataset.extend(Module.new {
630
- def fetch_rows(sql)
631
- MODEL_DB.sqls << sql
632
- yield({:x_foreign_key_x=>1, :id=>8})
633
- yield({:x_foreign_key_x=>1, :id=>8})
634
- end
635
- })
695
+ EagerGenre.dataset._fetch = [{:x_foreign_key_x=>1, :id=>8}, {:x_foreign_key_x=>1, :id=>8}]
636
696
  a = EagerAlbum.eager(:al_genres).all.first
637
697
  MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
638
698
  a.should == EagerAlbum.load(:id => 1, :band_id => 2)
@@ -646,26 +706,103 @@ describe Sequel::Model, "#eager" do
646
706
  a.should == EagerAlbum.load(:id => 1, :band_id => 2)
647
707
  a.al_genres.should == [EagerGenre.load(:id=>4)]
648
708
  end
649
- end
650
709
 
651
- describe Sequel::Model, "#eager_graph" do
652
- after(:all) do
653
- class ::MockDataset
654
- alias clone orig_clone
655
- end
710
+ it "should eagerly load a many_to_one association with custom eager block" do
711
+ a = EagerAlbum.eager(:band => proc {|ds| ds.select(:id, :name)}).all
712
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
713
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id, name FROM bands WHERE (bands.id IN (2))']
714
+ a.first.band.should == EagerBand.load(:id => 2)
715
+ MODEL_DB.sqls.should == []
656
716
  end
657
717
 
658
- before(:all) do
659
- class ::MockDataset
660
- alias orig_clone clone
661
- def clone(opts = {})
662
- c = super()
663
- c.opts = @opts.merge(opts)
664
- c.instance_variable_set(:@columns, (@columns.dup if @columns))
665
- c
666
- end
667
- end
718
+ it "should eagerly load a one_to_one association with custom eager block" do
719
+ EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id
720
+ a = EagerAlbum.eager(:track => proc {|ds| ds.select(:id)}).all
721
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
722
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id FROM tracks WHERE (tracks.album_id IN (1))']
723
+ a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
724
+ MODEL_DB.sqls.should == []
725
+ end
668
726
 
727
+ it "should eagerly load a one_to_many association with custom eager block" do
728
+ a = EagerAlbum.eager(:tracks => proc {|ds| ds.select(:id)}).all
729
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
730
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id FROM tracks WHERE (tracks.album_id IN (1))']
731
+ a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
732
+ MODEL_DB.sqls.should == []
733
+ end
734
+
735
+ it "should eagerly load a many_to_many association with custom eager block" do
736
+ a = EagerAlbum.eager(:genres => proc {|ds| ds.select(:name)}).all
737
+ a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
738
+ MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT name, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
739
+ a.first.genres.should == [EagerGenre.load(:id => 4)]
740
+ MODEL_DB.sqls.should == []
741
+ end
742
+
743
+ it "should allow cascading of eager loading within a custom eager block" do
744
+ a = EagerTrack.eager(:album => proc {|ds| ds.eager(:band => :members)}).all
745
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
746
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
747
+ 'SELECT * FROM albums WHERE (albums.id IN (1))',
748
+ 'SELECT * FROM bands WHERE (bands.id IN (2))',
749
+ "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)))"]
750
+ a = a.first
751
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
752
+ a.album.band.should == EagerBand.load(:id => 2)
753
+ a.album.band.members.should == [EagerBandMember.load(:id => 5)]
754
+ MODEL_DB.sqls.should == []
755
+ end
756
+
757
+ it "should allow cascading of eager loading with custom callback with hash value" do
758
+ a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>{:band => :members}}).all
759
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
760
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
761
+ 'SELECT id, band_id FROM albums WHERE (albums.id IN (1))',
762
+ 'SELECT * FROM bands WHERE (bands.id IN (2))',
763
+ "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)))"]
764
+ a = a.first
765
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
766
+ a.album.band.should == EagerBand.load(:id => 2)
767
+ a.album.band.members.should == [EagerBandMember.load(:id => 5)]
768
+ MODEL_DB.sqls.should == []
769
+ end
770
+
771
+ it "should allow cascading of eager loading with custom callback with symbol value" do
772
+ a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>:band}).all
773
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
774
+ MODEL_DB.sqls.should == ['SELECT * FROM tracks',
775
+ 'SELECT id, band_id FROM albums WHERE (albums.id IN (1))',
776
+ 'SELECT * FROM bands WHERE (bands.id IN (2))']
777
+ a = a.first
778
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
779
+ a.album.band.should == EagerBand.load(:id => 2)
780
+ MODEL_DB.sqls.should == []
781
+ end
782
+
783
+ it "should allow cascading of eager loading with custom callback with array value" do
784
+ a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>[:band, :band_name]}).all
785
+ a.should == [EagerTrack.load(:id => 3, :album_id => 1)]
786
+ sqls = MODEL_DB.sqls
787
+ sqls.slice!(0..1).should == ['SELECT * FROM tracks',
788
+ 'SELECT id, band_id FROM albums WHERE (albums.id IN (1))']
789
+ sqls.sort.should == ['SELECT * FROM bands WHERE (bands.id IN (2))',
790
+ 'SELECT id, name FROM bands WHERE (bands.id IN (2))']
791
+ a = a.first
792
+ a.album.should == EagerAlbum.load(:id => 1, :band_id => 2)
793
+ a.album.band.should == EagerBand.load(:id => 2)
794
+ a.album.band_name.should == EagerBand.load(:id => 2)
795
+ MODEL_DB.sqls.should == []
796
+ end
797
+
798
+ it "should call both association and custom eager blocks" do
799
+ EagerBand.eager(:good_albums => proc {|ds| ds.select(:name)}).all
800
+ MODEL_DB.sqls.should == ['SELECT * FROM bands', "SELECT name FROM albums WHERE ((albums.band_id IN (2)) AND (name = 'good'))"]
801
+ end
802
+ end
803
+
804
+ describe Sequel::Model, "#eager_graph" do
805
+ before(:all) do
669
806
  class ::GraphAlbum < Sequel::Model(:albums)
670
807
  dataset.opts[:from] = [:albums]
671
808
  columns :id, :band_id
@@ -702,6 +839,9 @@ describe Sequel::Model, "#eager_graph" do
702
839
  many_to_many :bands, :class=>'GraphBand', :left_key=>:member_id, :right_key=>:band_id, :join_table=>:bm
703
840
  end
704
841
  end
842
+ after(:all) do
843
+ [:GraphAlbum, :GraphBand, :GraphTrack, :GraphGenre, :GraphBandMember].each{|x| Object.send(:remove_const, x)}
844
+ end
705
845
 
706
846
  it "should raise an error if called without a symbol or hash" do
707
847
  proc{GraphAlbum.eager_graph(Object.new)}.should raise_error(Sequel::Error)
@@ -710,35 +850,74 @@ describe Sequel::Model, "#eager_graph" do
710
850
  it "should not split results and assign associations if ungraphed is called" do
711
851
  ds = GraphAlbum.eager_graph(:band).ungraphed
712
852
  ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
713
- def ds.fetch_rows(sql, &block)
714
- yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
715
- end
853
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
716
854
  ds.all.should == [GraphAlbum.load(:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3)]
717
855
  end
718
856
 
857
+ it "should not modify existing dataset" do
858
+ ds1 = GraphAlbum.dataset
859
+ ds2 = ds1.eager_graph(:band)
860
+ proc{ds1.eager_graph(:band)}.should_not raise_error
861
+ proc{ds2.eager_graph(:tracks)}.should_not raise_error
862
+ proc{ds2.eager_graph(:tracks)}.should_not raise_error
863
+ end
864
+
865
+ it "should allow manually selecting the alias base per call via an AliasedExpression" do
866
+ ds = GraphAlbum.eager_graph(Sequel.as(:band, :b))
867
+ ds.sql.should == 'SELECT albums.id, albums.band_id, b.id AS b_id, b.vocalist_id FROM albums LEFT OUTER JOIN bands AS b ON (b.id = albums.band_id)'
868
+ ds._fetch = {:id=>1, :band_id=>2, :b_id=>2, :vocalist_id=>3}
869
+ a = ds.all
870
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
871
+ a.first.band.should == GraphBand.load(:id => 2, :vocalist_id=>3)
872
+ end
873
+
874
+ it "should handle multiple associations using the same alias base" do
875
+ ds = GraphAlbum.eager_graph(Sequel.as(:genres, :b), Sequel.as(:tracks, :b), Sequel.as(:band, :b))
876
+ ds.sql.should == 'SELECT albums.id, albums.band_id, b.id AS b_id, b_0.id AS b_0_id, b_0.album_id, b_1.id AS b_1_id, b_1.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS b ON (b.id = ag.genre_id) LEFT OUTER JOIN tracks AS b_0 ON (b_0.album_id = albums.id) LEFT OUTER JOIN bands AS b_1 ON (b_1.id = albums.band_id)'
877
+ ds._fetch = {:id=>1, :band_id=>2, :b_id=>4, :b_0_id=>3, :album_id=>1, :b_1_id=>2, :vocalist_id=>6}
878
+ a = ds.all
879
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
880
+ a = a.first
881
+ a.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
882
+ a.tracks.should == [GraphTrack.load({:id => 3, :album_id=>1})]
883
+ a.genres.should == [GraphGenre.load(:id => 4)]
884
+
885
+ ds = GraphTrack.eager_graph(Sequel.as(:album, :b)=>{Sequel.as(:band, :b)=>Sequel.as(:members, :b)})
886
+ ds.sql.should == 'SELECT tracks.id, tracks.album_id, b.id AS b_id, b.band_id, b_0.id AS b_0_id, b_0.vocalist_id, b_1.id AS b_1_id FROM tracks LEFT OUTER JOIN albums AS b ON (b.id = tracks.album_id) LEFT OUTER JOIN bands AS b_0 ON (b_0.id = b.band_id) LEFT OUTER JOIN bm ON (bm.band_id = b_0.id) LEFT OUTER JOIN members AS b_1 ON (b_1.id = bm.member_id)'
887
+ ds._fetch = {:id=>3, :album_id=>1, :b_id=>1, :band_id=>2, :b_1_id=>5, :b_0_id=>2, :vocalist_id=>6}
888
+ a = ds.all
889
+ a.should == [GraphTrack.load(:id => 3, :album_id => 1)]
890
+ a = a.first
891
+ a.album.should == GraphAlbum.load(:id => 1, :band_id => 2)
892
+ a.album.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
893
+ a.album.band.members.should == [GraphBandMember.load(:id => 5)]
894
+ end
895
+
719
896
  it "should eagerly load a single many_to_one association" do
720
897
  ds = GraphAlbum.eager_graph(:band)
721
898
  ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
722
- def ds.fetch_rows(sql, &block)
723
- yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
724
- end
899
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
725
900
  a = ds.all
726
- a.should be_a_kind_of(Array)
727
- a.size.should == 1
728
- a.first.should be_a_kind_of(GraphAlbum)
729
- a.first.values.should == {:id => 1, :band_id => 2}
730
- a = a.first
731
- a.band.should be_a_kind_of(GraphBand)
732
- a.band.values.should == {:id => 2, :vocalist_id=>3}
901
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
902
+ a.first.band.should == GraphBand.load(:id => 2, :vocalist_id=>3)
903
+ end
904
+
905
+ it "should eagerly load a single many_to_one association with the same name as a column" do
906
+ GraphAlbum.def_column_alias(:band_id_id, :band_id)
907
+ GraphAlbum.many_to_one :band_id, :key_column=>:band_id, :class=>GraphBand
908
+ ds = GraphAlbum.eager_graph(:band_id)
909
+ ds.sql.should == 'SELECT albums.id, albums.band_id, band_id.id AS band_id_id, band_id.vocalist_id FROM albums LEFT OUTER JOIN bands AS band_id ON (band_id.id = albums.band_id)'
910
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_id=>2, :vocalist_id=>3}
911
+ a = ds.all
912
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
913
+ a.first.band_id.should == GraphBand.load(:id => 2, :vocalist_id=>3)
733
914
  end
734
915
 
735
916
  it "should eagerly load a single one_to_one association" do
736
917
  GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
737
918
  ds = GraphAlbum.eager_graph(:track)
738
919
  ds.sql.should == 'SELECT albums.id, albums.band_id, track.id AS track_id, track.album_id FROM albums LEFT OUTER JOIN tracks AS track ON (track.album_id = albums.id)'
739
- def ds.fetch_rows(sql, &block)
740
- yield({:id=>1, :band_id=>2, :track_id=>3, :album_id=>1})
741
- end
920
+ ds._fetch = {:id=>1, :band_id=>2, :track_id=>3, :album_id=>1}
742
921
  a = ds.all
743
922
  a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
744
923
  a.first.track.should == GraphTrack.load(:id => 3, :album_id=>1)
@@ -747,130 +926,88 @@ describe Sequel::Model, "#eager_graph" do
747
926
  it "should eagerly load a single one_to_many association" do
748
927
  ds = GraphAlbum.eager_graph(:tracks)
749
928
  ds.sql.should == 'SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)'
750
- def ds.fetch_rows(sql, &block)
751
- yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
752
- end
929
+ ds._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
753
930
  a = ds.all
754
- a.should be_a_kind_of(Array)
755
- a.size.should == 1
756
- a.first.should be_a_kind_of(GraphAlbum)
757
- a.first.values.should == {:id => 1, :band_id => 2}
758
- a = a.first
759
- a.tracks.should be_a_kind_of(Array)
760
- a.tracks.size.should == 1
761
- a.tracks.first.should be_a_kind_of(GraphTrack)
762
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
931
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
932
+ a.first.tracks.should == [GraphTrack.load(:id => 3, :album_id=>1)]
763
933
  end
764
934
 
765
935
  it "should eagerly load a single many_to_many association" do
766
936
  ds = GraphAlbum.eager_graph(:genres)
767
937
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id)'
768
- def ds.fetch_rows(sql, &block)
769
- yield({:id=>1, :band_id=>2, :genres_id=>4})
770
- end
938
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>4}
771
939
  a = ds.all
772
- a.should be_a_kind_of(Array)
773
- a.size.should == 1
774
- a.first.should be_a_kind_of(GraphAlbum)
775
- a.first.values.should == {:id => 1, :band_id => 2}
776
- a = a.first
777
- a.genres.should be_a_kind_of(Array)
778
- a.genres.size.should == 1
779
- a.genres.first.should be_a_kind_of(GraphGenre)
780
- a.genres.first.values.should == {:id => 4}
940
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
941
+ a.first.genres.should == [GraphGenre.load(:id => 4)]
781
942
  end
782
943
 
944
+ it "should correctly handle an aliased join table in many_to_many" do
945
+ c = Class.new(GraphAlbum)
946
+ c.many_to_many :genres, :clone=>:genres, :join_table=>:ag___ga
947
+ c.eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag AS ga ON (ga.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ga.genre_id)'
948
+
949
+ c.many_to_many :genres, :clone=>:genres, :join_table=>:ag___albums
950
+ c.eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag AS albums_0 ON (albums_0.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = albums_0.genre_id)'
951
+
952
+ c.many_to_many :genres, :clone=>:genres, :join_table=>:ag___genres
953
+ c.eager_graph(:genres).sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag AS genres_0 ON (genres_0.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = genres_0.genre_id)'
954
+ end
955
+
783
956
  it "should eagerly load multiple associations in a single call" do
784
957
  ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
785
958
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
786
- def ds.fetch_rows(sql, &block)
787
- yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
788
- end
959
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6}
789
960
  a = ds.all
790
- a.should be_a_kind_of(Array)
791
- a.size.should == 1
792
- a.first.should be_a_kind_of(GraphAlbum)
793
- a.first.values.should == {:id => 1, :band_id => 2}
961
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
794
962
  a = a.first
795
- a.band.should be_a_kind_of(GraphBand)
796
- a.band.values.should == {:id => 2, :vocalist_id=>6}
797
- a.tracks.should be_a_kind_of(Array)
798
- a.tracks.size.should == 1
799
- a.tracks.first.should be_a_kind_of(GraphTrack)
800
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
801
- a.genres.should be_a_kind_of(Array)
802
- a.genres.size.should == 1
803
- a.genres.first.should be_a_kind_of(GraphGenre)
804
- a.genres.first.values.should == {:id => 4}
963
+ a.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
964
+ a.tracks.should == [GraphTrack.load({:id => 3, :album_id=>1})]
965
+ a.genres.should == [GraphGenre.load(:id => 4)]
805
966
  end
806
967
 
807
968
  it "should eagerly load multiple associations in separate calls" do
808
969
  ds = GraphAlbum.eager_graph(:genres).eager_graph(:tracks).eager_graph(:band)
809
970
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
810
- def ds.fetch_rows(sql, &block)
811
- yield({:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6})
812
- end
971
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>4, :tracks_id=>3, :album_id=>1, :band_id_0=>2, :vocalist_id=>6}
813
972
  a = ds.all
814
- a.should be_a_kind_of(Array)
815
- a.size.should == 1
816
- a.first.should be_a_kind_of(GraphAlbum)
817
- a.first.values.should == {:id => 1, :band_id => 2}
973
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
818
974
  a = a.first
819
- a.band.should be_a_kind_of(GraphBand)
820
- a.band.values.should == {:id => 2, :vocalist_id=>6}
821
- a.tracks.should be_a_kind_of(Array)
822
- a.tracks.size.should == 1
823
- a.tracks.first.should be_a_kind_of(GraphTrack)
824
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
825
- a.genres.should be_a_kind_of(Array)
826
- a.genres.size.should == 1
827
- a.genres.first.should be_a_kind_of(GraphGenre)
828
- a.genres.first.values.should == {:id => 4}
975
+ a.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
976
+ a.tracks.should == [GraphTrack.load({:id => 3, :album_id=>1})]
977
+ a.genres.should == [GraphGenre.load(:id => 4)]
829
978
  end
830
979
 
831
980
  it "should allow cascading of eager loading for associations of associated models" do
832
981
  ds = GraphTrack.eager_graph(:album=>{:band=>:members})
833
982
  ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN albums AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
834
- def ds.fetch_rows(sql, &block)
835
- yield({:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6})
836
- end
983
+ ds._fetch = {:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6}
837
984
  a = ds.all
838
- a.should be_a_kind_of(Array)
839
- a.size.should == 1
840
- a.first.should be_a_kind_of(GraphTrack)
841
- a.first.values.should == {:id => 3, :album_id => 1}
985
+ a.should == [GraphTrack.load(:id => 3, :album_id => 1)]
842
986
  a = a.first
843
- a.album.should be_a_kind_of(GraphAlbum)
844
- a.album.values.should == {:id => 1, :band_id => 2}
845
- a.album.band.should be_a_kind_of(GraphBand)
846
- a.album.band.values.should == {:id => 2, :vocalist_id=>6}
847
- a.album.band.members.should be_a_kind_of(Array)
848
- a.album.band.members.size.should == 1
849
- a.album.band.members.first.should be_a_kind_of(GraphBandMember)
850
- a.album.band.members.first.values.should == {:id => 5}
987
+ a.album.should == GraphAlbum.load(:id => 1, :band_id => 2)
988
+ a.album.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
989
+ a.album.band.members.should == [GraphBandMember.load(:id => 5)]
851
990
  end
852
991
 
853
992
  it "should allow cascading of eager loading for multiple *_to_many associations, eliminating duplicates caused by cartesian products" do
854
993
  ds = GraphBand.eager_graph({:albums=>:tracks}, :members)
855
994
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, albums.id AS albums_id, albums.band_id, tracks.id AS tracks_id, tracks.album_id, members.id AS members_id FROM bands LEFT OUTER JOIN albums ON (albums.band_id = bands.id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
856
- def ds.fetch_rows(sql, &block)
857
- yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>5})
858
- yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>6})
859
- yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>5})
860
- yield({:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>6})
861
- yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>5})
862
- yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>6})
863
- yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>5})
864
- yield({:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>6})
865
- yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>5})
866
- yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>6})
867
- yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>5})
868
- yield({:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>6})
869
- yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>5})
870
- yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>6})
871
- yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>5})
872
- yield({:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>6})
873
- end
995
+ ds._fetch = [{:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>5},
996
+ {:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>4, :album_id=>3, :members_id=>6},
997
+ {:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>5},
998
+ {:id=>1, :vocalist_id=>2, :albums_id=>3, :band_id=>1, :tracks_id=>5, :album_id=>3, :members_id=>6},
999
+ {:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>5},
1000
+ {:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>6, :album_id=>4, :members_id=>6},
1001
+ {:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>5},
1002
+ {:id=>1, :vocalist_id=>2, :albums_id=>4, :band_id=>1, :tracks_id=>7, :album_id=>4, :members_id=>6},
1003
+ {:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>5},
1004
+ {:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>8, :album_id=>5, :members_id=>6},
1005
+ {:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>5},
1006
+ {:id=>2, :vocalist_id=>2, :albums_id=>5, :band_id=>2, :tracks_id=>9, :album_id=>5, :members_id=>6},
1007
+ {:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>5},
1008
+ {:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>1, :album_id=>6, :members_id=>6},
1009
+ {:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>5},
1010
+ {:id=>2, :vocalist_id=>2, :albums_id=>6, :band_id=>2, :tracks_id=>2, :album_id=>6, :members_id=>6}]
874
1011
  a = ds.all
875
1012
  a.should == [GraphBand.load(:id=>1, :vocalist_id=>2), GraphBand.load(:id=>2, :vocalist_id=>2)]
876
1013
  members = a.map{|x| x.members}
@@ -885,85 +1022,49 @@ describe Sequel::Model, "#eager_graph" do
885
1022
  MODEL_DB.reset
886
1023
  ds = GraphAlbum.eager_graph(:tracks)
887
1024
  ds.sql.should == 'SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)'
888
- def ds.fetch_rows(sql, &block)
889
- @db << sql
890
- yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
891
- end
1025
+ ds._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
892
1026
  a = ds.all
893
- a.should be_a_kind_of(Array)
894
- a.size.should == 1
895
- a.first.should be_a_kind_of(GraphAlbum)
896
- a.first.values.should == {:id => 1, :band_id => 2}
1027
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
897
1028
  a = a.first
898
- a.tracks.should be_a_kind_of(Array)
899
- a.tracks.size.should == 1
900
- a.tracks.first.should be_a_kind_of(GraphTrack)
901
- a.tracks.first.values.should == {:id => 3, :album_id=>1}
902
- a.tracks.first.album.should be_a_kind_of(GraphAlbum)
1029
+ a.tracks.should == [GraphTrack.load(:id => 3, :album_id=>1)]
903
1030
  a.tracks.first.album.should == a
904
- MODEL_DB.sqls.length.should == 1
1031
+ MODEL_DB.sqls.should == ['SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)']
905
1032
  end
906
1033
 
907
1034
  it "should eager load multiple associations from the same table" do
908
1035
  ds = GraphBand.eager_graph(:vocalist, :members)
909
1036
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, vocalist.id AS vocalist_id_0, members.id AS members_id FROM bands LEFT OUTER JOIN members AS vocalist ON (vocalist.id = bands.vocalist_id) LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
910
- def ds.fetch_rows(sql, &block)
911
- yield({:id=>2, :vocalist_id=>6, :vocalist_id_0=>6, :members_id=>5})
912
- end
1037
+ ds._fetch = {:id=>2, :vocalist_id=>6, :vocalist_id_0=>6, :members_id=>5}
913
1038
  a = ds.all
914
- a.should be_a_kind_of(Array)
915
- a.size.should == 1
916
- a.first.should be_a_kind_of(GraphBand)
917
- a.first.values.should == {:id => 2, :vocalist_id => 6}
1039
+ a.should == [GraphBand.load(:id => 2, :vocalist_id => 6)]
918
1040
  a = a.first
919
- a.vocalist.should be_a_kind_of(GraphBandMember)
920
- a.vocalist.values.should == {:id => 6}
921
- a.members.should be_a_kind_of(Array)
922
- a.members.size.should == 1
923
- a.members.first.should be_a_kind_of(GraphBandMember)
924
- a.members.first.values.should == {:id => 5}
1041
+ a.vocalist.should == GraphBandMember.load(:id => 6)
1042
+ a.members.should == [GraphBandMember.load(:id => 5)]
925
1043
  end
926
1044
 
927
- it "should give you a graph of tables when called without .all" do
1045
+ it "should give you a plain hash when called without .all" do
928
1046
  ds = GraphAlbum.eager_graph(:band)
929
1047
  ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
930
- def ds.fetch_rows(sql, &block)
931
- yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
932
- end
933
- ds.first.should == {:albums=>GraphAlbum.load(:id => 1, :band_id => 2), :band=>GraphBand.load(:id => 2, :vocalist_id=>3)}
1048
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
1049
+ ds.first.should == {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
934
1050
  end
935
1051
 
936
1052
  it "should not drop any associated objects if the graph could not be a cartesian product" do
937
1053
  ds = GraphBand.eager_graph(:members, :vocalist)
938
1054
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, members.id AS members_id, vocalist.id AS vocalist_id_0 FROM bands LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id) LEFT OUTER JOIN members AS vocalist ON (vocalist.id = bands.vocalist_id)'
939
- def ds.fetch_rows(sql, &block)
940
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
941
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6})
942
- end
1055
+ ds._fetch = [{:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6}, {:id=>2, :vocalist_id=>6, :members_id=>5, :vocalist_id_0=>6}]
943
1056
  a = ds.all
944
- a.should be_a_kind_of(Array)
945
- a.size.should == 1
946
- a.first.should be_a_kind_of(GraphBand)
947
- a.first.values.should == {:id => 2, :vocalist_id => 6}
1057
+ a.should == [GraphBand.load(:id => 2, :vocalist_id => 6)]
948
1058
  a = a.first
949
- a.vocalist.should be_a_kind_of(GraphBandMember)
950
- a.vocalist.values.should == {:id => 6}
951
- a.members.should be_a_kind_of(Array)
952
- a.members.size.should == 2
953
- a.members.first.should be_a_kind_of(GraphBandMember)
954
- a.members.first.values.should == {:id => 5}
955
- a.members.last.should be_a_kind_of(GraphBandMember)
956
- a.members.last.values.should == {:id => 5}
1059
+ a.vocalist.should == GraphBandMember.load(:id => 6)
1060
+ a.members.should == [GraphBandMember.load(:id => 5), GraphBandMember.load(:id => 5)]
957
1061
  end
958
1062
 
959
1063
  it "should respect the :cartesian_product_number option" do
960
1064
  GraphBand.many_to_one :other_vocalist, :class=>'GraphBandMember', :key=>:vocalist_id, :cartesian_product_number=>1
961
1065
  ds = GraphBand.eager_graph(:members, :other_vocalist)
962
1066
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, members.id AS members_id, other_vocalist.id AS other_vocalist_id FROM bands LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id) LEFT OUTER JOIN members AS other_vocalist ON (other_vocalist.id = bands.vocalist_id)'
963
- def ds.fetch_rows(sql, &block)
964
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6})
965
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6})
966
- end
1067
+ ds._fetch = [{:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6}, {:id=>2, :vocalist_id=>6, :members_id=>5, :other_vocalist_id=>6}]
967
1068
  a = ds.all
968
1069
  a.should == [GraphBand.load(:id=>2, :vocalist_id => 6)]
969
1070
  a.first.other_vocalist.should == GraphBandMember.load(:id=>6)
@@ -973,57 +1074,28 @@ describe Sequel::Model, "#eager_graph" do
973
1074
  it "should drop duplicate items that occur in sequence if the graph could be a cartesian product" do
974
1075
  ds = GraphBand.eager_graph(:members, :genres)
975
1076
  ds.sql.should == 'SELECT bands.id, bands.vocalist_id, members.id AS members_id, genres.id AS genres_id FROM bands LEFT OUTER JOIN bm ON (bm.band_id = bands.id) LEFT OUTER JOIN members ON (members.id = bm.member_id) LEFT OUTER JOIN bg ON (bg.band_id = bands.id) LEFT OUTER JOIN genres ON (genres.id = bg.genre_id)'
976
- def ds.fetch_rows(sql, &block)
977
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>7})
978
- yield({:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>8})
979
- yield({:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>7})
980
- yield({:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>8})
981
- end
1077
+ ds._fetch = [{:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>7},
1078
+ {:id=>2, :vocalist_id=>6, :members_id=>5, :genres_id=>8},
1079
+ {:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>7},
1080
+ {:id=>2, :vocalist_id=>6, :members_id=>6, :genres_id=>8}]
982
1081
  a = ds.all
983
- a.should be_a_kind_of(Array)
984
- a.size.should == 1
985
- a.first.should be_a_kind_of(GraphBand)
986
- a.first.values.should == {:id => 2, :vocalist_id => 6}
1082
+ a.should == [GraphBand.load(:id => 2, :vocalist_id => 6)]
987
1083
  a = a.first
988
- a.members.should be_a_kind_of(Array)
989
- a.members.size.should == 2
990
- a.members.first.should be_a_kind_of(GraphBandMember)
991
- a.members.first.values.should == {:id => 5}
992
- a.members.last.should be_a_kind_of(GraphBandMember)
993
- a.members.last.values.should == {:id => 6}
994
- a.genres.size.should == 2
995
- a.genres.first.should be_a_kind_of(GraphGenre)
996
- a.genres.first.values.should == {:id => 7}
997
- a.genres.last.should be_a_kind_of(GraphGenre)
998
- a.genres.last.values.should == {:id => 8}
1084
+ a.members.should == [GraphBandMember.load(:id => 5), GraphBandMember.load(:id => 6)]
1085
+ a.genres.should == [GraphGenre.load(:id => 7), GraphGenre.load(:id => 8)]
999
1086
  end
1000
1087
 
1001
1088
  it "should be able to be used in combination with #eager" do
1002
1089
  MODEL_DB.reset
1003
1090
  ds = GraphAlbum.eager_graph(:tracks).eager(:genres)
1004
- def ds.fetch_rows(sql, &block)
1005
- @db << sql
1006
- yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1})
1007
- end
1091
+ ds._fetch = {:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}
1008
1092
  ds2 = GraphGenre.dataset
1009
- def ds2.fetch_rows(sql, &block)
1010
- @db << sql
1011
- yield({:id=>6, :x_foreign_key_x=>1})
1012
- end
1093
+ ds2._fetch = {:id=>6, :x_foreign_key_x=>1}
1013
1094
  a = ds.all
1014
- a.should be_a_kind_of(Array)
1015
- a.size.should == 1
1016
- a.first.should be_a_kind_of(GraphAlbum)
1017
- a.first.values.should == {:id => 1, :band_id => 2}
1095
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1018
1096
  a = a.first
1019
- a.tracks.should be_a_kind_of(Array)
1020
- a.tracks.size.should == 1
1021
- a.tracks.first.should be_a_kind_of(GraphTrack)
1022
- a.tracks.first.values.should == {:id=>3, :album_id=>1}
1023
- a.genres.should be_a_kind_of(Array)
1024
- a.genres.size.should == 1
1025
- a.genres.first.should be_a_kind_of(GraphGenre)
1026
- a.genres.first.values.should == {:id=>6}
1097
+ a.tracks.should == [GraphTrack.load(:id=>3, :album_id=>1)]
1098
+ a.genres.should == [GraphGenre.load(:id => 6)]
1027
1099
  MODEL_DB.sqls.should == ['SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)',
1028
1100
  "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
1029
1101
  end
@@ -1031,110 +1103,65 @@ describe Sequel::Model, "#eager_graph" do
1031
1103
  it "should handle no associated records for a single many_to_one association" do
1032
1104
  ds = GraphAlbum.eager_graph(:band)
1033
1105
  ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
1034
- def ds.fetch_rows(sql, &block)
1035
- yield({:id=>1, :band_id=>2, :band_id_0=>nil, :vocalist_id=>nil})
1036
- end
1106
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>nil, :vocalist_id=>nil}
1037
1107
  a = ds.all
1038
- a.should be_a_kind_of(Array)
1039
- a.size.should == 1
1040
- a.first.should be_a_kind_of(GraphAlbum)
1041
- a.first.values.should == {:id => 1, :band_id => 2}
1042
- a.first.associations.fetch(:band, 2).should == nil
1108
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1109
+ a.first.band.should == nil
1043
1110
  end
1044
1111
 
1045
1112
  it "should handle no associated records for a single one_to_many association" do
1046
1113
  ds = GraphAlbum.eager_graph(:tracks)
1047
1114
  ds.sql.should == 'SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)'
1048
- def ds.fetch_rows(sql, &block)
1049
- yield({:id=>1, :band_id=>2, :tracks_id=>nil, :album_id=>nil})
1050
- end
1115
+ ds._fetch = {:id=>1, :band_id=>2, :tracks_id=>nil, :album_id=>nil}
1051
1116
  a = ds.all
1052
- a.should be_a_kind_of(Array)
1053
- a.size.should == 1
1054
- a.first.should be_a_kind_of(GraphAlbum)
1055
- a.first.values.should == {:id => 1, :band_id => 2}
1117
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1056
1118
  a.first.tracks.should == []
1057
1119
  end
1058
1120
 
1059
1121
  it "should handle no associated records for a single many_to_many association" do
1060
1122
  ds = GraphAlbum.eager_graph(:genres)
1061
1123
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id)'
1062
- def ds.fetch_rows(sql, &block)
1063
- yield({:id=>1, :band_id=>2, :genres_id=>nil})
1064
- end
1124
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>nil}
1065
1125
  a = ds.all
1066
- a.should be_a_kind_of(Array)
1067
- a.size.should == 1
1068
- a.first.should be_a_kind_of(GraphAlbum)
1069
- a.first.values.should == {:id => 1, :band_id => 2}
1126
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1070
1127
  a.first.genres.should == []
1071
1128
  end
1072
1129
 
1073
1130
  it "should handle missing associated records when loading multiple associations" do
1074
1131
  ds = GraphAlbum.eager_graph(:genres, :tracks, :band)
1075
1132
  ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id, tracks.id AS tracks_id, tracks.album_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres ON (genres.id = ag.genre_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
1076
- def ds.fetch_rows(sql, &block)
1077
- yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>3, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1078
- yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>4, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1079
- yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>5, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1080
- yield({:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>6, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil})
1081
- end
1133
+ ds._fetch = [{:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>3, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil},
1134
+ {:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>4, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil},
1135
+ {:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>5, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil},
1136
+ {:id=>1, :band_id=>2, :genres_id=>nil, :tracks_id=>6, :album_id=>1, :band_id_0=>nil, :vocalist_id=>nil}]
1082
1137
  a = ds.all
1083
- a.should be_a_kind_of(Array)
1084
- a.size.should == 1
1085
- a.first.should be_a_kind_of(GraphAlbum)
1086
- a.first.values.should == {:id => 1, :band_id => 2}
1138
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1087
1139
  a = a.first
1088
- a.tracks.should be_a_kind_of(Array)
1089
- a.tracks.size.should == 4
1090
- a.tracks.first.should be_a_kind_of(GraphTrack)
1091
- a.tracks.collect{|x|x[:id]}.should == [3,4,5,6]
1092
- a.associations.fetch(:band, 2).should == nil
1140
+ a.tracks.should == [GraphTrack.load(:id => 3, :album_id => 1), GraphTrack.load(:id => 4, :album_id => 1), GraphTrack.load(:id => 5, :album_id => 1), GraphTrack.load(:id => 6, :album_id => 1)]
1141
+ a.band.should == nil
1093
1142
  a.genres.should == []
1094
1143
  end
1095
1144
 
1096
1145
  it "should handle missing associated records when cascading eager loading for associations of associated models" do
1097
1146
  ds = GraphTrack.eager_graph(:album=>{:band=>:members})
1098
1147
  ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN albums AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
1099
- def ds.fetch_rows(sql, &block)
1100
- yield({:id=>2, :album_id=>2, :album_id_0=>nil, :band_id=>nil, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
1101
- yield({:id=>3, :album_id=>3, :album_id_0=>3, :band_id=>3, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil})
1102
- yield({:id=>4, :album_id=>4, :album_id_0=>4, :band_id=>2, :members_id=>nil, :band_id_0=>2, :vocalist_id=>6})
1103
- yield({:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>5, :band_id_0=>4, :vocalist_id=>8})
1104
- yield({:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>6, :band_id_0=>4, :vocalist_id=>8})
1105
- end
1148
+ ds._fetch = [{:id=>2, :album_id=>2, :album_id_0=>nil, :band_id=>nil, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil},
1149
+ {:id=>3, :album_id=>3, :album_id_0=>3, :band_id=>3, :members_id=>nil, :band_id_0=>nil, :vocalist_id=>nil},
1150
+ {:id=>4, :album_id=>4, :album_id_0=>4, :band_id=>2, :members_id=>nil, :band_id_0=>2, :vocalist_id=>6},
1151
+ {:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>5, :band_id_0=>4, :vocalist_id=>8},
1152
+ {:id=>5, :album_id=>1, :album_id_0=>1, :band_id=>4, :members_id=>6, :band_id_0=>4, :vocalist_id=>8}]
1106
1153
  a = ds.all
1107
- a.should be_a_kind_of(Array)
1108
- a.size.should == 4
1109
- a.first.should be_a_kind_of(GraphTrack)
1110
- a.collect{|x|x[:id]}.should == [2,3,4,5]
1111
- a[0].associations.fetch(:album, 2).should == nil
1112
- a[1].album.should be_a_kind_of(GraphAlbum)
1113
- a[1].album.values.should == {:id => 3, :band_id => 3}
1114
- a[1].album.associations.fetch(:band, 2).should == nil
1115
- a[2].album.should be_a_kind_of(GraphAlbum)
1116
- a[2].album.values.should == {:id => 4, :band_id => 2}
1117
- a[2].album.band.should be_a_kind_of(GraphBand)
1118
- a[2].album.band.values.should == {:id => 2, :vocalist_id=>6}
1119
- a[2].album.band.members.should == []
1120
- a[3].album.should be_a_kind_of(GraphAlbum)
1121
- a[3].album.values.should == {:id => 1, :band_id => 4}
1122
- a[3].album.band.should be_a_kind_of(GraphBand)
1123
- a[3].album.band.values.should == {:id => 4, :vocalist_id=>8}
1124
- a[3].album.band.members.size.should == 2
1125
- a[3].album.band.members.first.should be_a_kind_of(GraphBandMember)
1126
- a[3].album.band.members.first.values.should == {:id => 5}
1127
- a[3].album.band.members.last.should be_a_kind_of(GraphBandMember)
1128
- a[3].album.band.members.last.values.should == {:id => 6}
1154
+ a.should == [GraphTrack.load(:id => 2, :album_id => 2), GraphTrack.load(:id => 3, :album_id => 3), GraphTrack.load(:id => 4, :album_id => 4), GraphTrack.load(:id => 5, :album_id => 1)]
1155
+ a.map{|x| x.album}.should == [nil, GraphAlbum.load(:id => 3, :band_id => 3), GraphAlbum.load(:id => 4, :band_id => 2), GraphAlbum.load(:id => 1, :band_id => 4)]
1156
+ a.map{|x| x.album.band if x.album}.should == [nil, nil, GraphBand.load(:id => 2, :vocalist_id=>6), GraphBand.load(:id => 4, :vocalist_id=>8)]
1157
+ a.map{|x| x.album.band.members if x.album && x.album.band}.should == [nil, nil, [], [GraphBandMember.load(:id => 5), GraphBandMember.load(:id => 6)]]
1129
1158
  end
1130
1159
 
1131
1160
  it "should respect the association's :primary_key option" do
1132
1161
  GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :primary_key=>:vocalist_id
1133
1162
  ds = GraphAlbum.eager_graph(:inner_band)
1134
1163
  ds.sql.should == 'SELECT albums.id, albums.band_id, inner_band.id AS inner_band_id, inner_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS inner_band ON (inner_band.vocalist_id = albums.band_id)'
1135
- def ds.fetch_rows(sql, &block)
1136
- yield({:id=>3, :band_id=>2, :inner_band_id=>5, :vocalist_id=>2})
1137
- end
1164
+ ds._fetch = {:id=>3, :band_id=>2, :inner_band_id=>5, :vocalist_id=>2}
1138
1165
  as = ds.all
1139
1166
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1140
1167
  as.first.inner_band.should == GraphBand.load(:id=>5, :vocalist_id=>2)
@@ -1142,10 +1169,7 @@ describe Sequel::Model, "#eager_graph" do
1142
1169
  GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :primary_key=>:band_id
1143
1170
  ds = GraphAlbum.eager_graph(:right_tracks)
1144
1171
  ds.sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums LEFT OUTER JOIN tracks AS right_tracks ON (right_tracks.album_id = albums.band_id)'
1145
- def ds.fetch_rows(sql, &block)
1146
- yield({:id=>3, :band_id=>2, :right_tracks_id=>5, :album_id=>2})
1147
- yield({:id=>3, :band_id=>2, :right_tracks_id=>6, :album_id=>2})
1148
- end
1172
+ ds._fetch = [{:id=>3, :band_id=>2, :right_tracks_id=>5, :album_id=>2}, {:id=>3, :band_id=>2, :right_tracks_id=>6, :album_id=>2}]
1149
1173
  as = ds.all
1150
1174
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1151
1175
  as.first.right_tracks.should == [GraphTrack.load(:id=>5, :album_id=>2), GraphTrack.load(:id=>6, :album_id=>2)]
@@ -1155,9 +1179,7 @@ describe Sequel::Model, "#eager_graph" do
1155
1179
  GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>[:band_id, :id], :primary_key=>[:vocalist_id, :id]
1156
1180
  ds = GraphAlbum.eager_graph(:inner_band)
1157
1181
  ds.sql.should == 'SELECT albums.id, albums.band_id, inner_band.id AS inner_band_id, inner_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS inner_band ON ((inner_band.vocalist_id = albums.band_id) AND (inner_band.id = albums.id))'
1158
- def ds.fetch_rows(sql, &block)
1159
- yield({:id=>3, :band_id=>2, :inner_band_id=>3, :vocalist_id=>2})
1160
- end
1182
+ ds._fetch = {:id=>3, :band_id=>2, :inner_band_id=>3, :vocalist_id=>2}
1161
1183
  as = ds.all
1162
1184
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1163
1185
  as.first.inner_band.should == GraphBand.load(:id=>3, :vocalist_id=>2)
@@ -1167,9 +1189,7 @@ describe Sequel::Model, "#eager_graph" do
1167
1189
  GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>[:album_id, :id], :primary_key=>[:band_id, :id]
1168
1190
  ds = GraphAlbum.eager_graph(:right_tracks)
1169
1191
  ds.sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums LEFT OUTER JOIN tracks AS right_tracks ON ((right_tracks.album_id = albums.band_id) AND (right_tracks.id = albums.id))'
1170
- def ds.fetch_rows(sql, &block)
1171
- yield({:id=>3, :band_id=>2, :right_tracks_id=>3, :album_id=>2})
1172
- end
1192
+ ds._fetch = {:id=>3, :band_id=>2, :right_tracks_id=>3, :album_id=>2}
1173
1193
  as = ds.all
1174
1194
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1175
1195
  as.first.right_tracks.should == [GraphTrack.load(:id=>3, :album_id=>2)]
@@ -1179,10 +1199,7 @@ describe Sequel::Model, "#eager_graph" do
1179
1199
  GraphAlbum.many_to_many :sbands, :class=>'GraphBand', :left_key=>[:l1, :l2], :left_primary_key=>[:band_id, :id], :right_key=>[:r1, :r2], :right_primary_key=>[:vocalist_id, :id], :join_table=>:b
1180
1200
  ds = GraphAlbum.eager_graph(:sbands)
1181
1201
  ds.sql.should == 'SELECT albums.id, albums.band_id, sbands.id AS sbands_id, sbands.vocalist_id FROM albums LEFT OUTER JOIN b ON ((b.l1 = albums.band_id) AND (b.l2 = albums.id)) LEFT OUTER JOIN bands AS sbands ON ((sbands.vocalist_id = b.r1) AND (sbands.id = b.r2))'
1182
- def ds.fetch_rows(sql, &block)
1183
- yield({:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6})
1184
- yield({:id=>3, :band_id=>2, :sbands_id=>6, :vocalist_id=>22})
1185
- end
1202
+ ds._fetch = [{:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6}, {:id=>3, :band_id=>2, :sbands_id=>6, :vocalist_id=>22}]
1186
1203
  as = ds.all
1187
1204
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1188
1205
  as.first.sbands.should == [GraphBand.load(:id=>5, :vocalist_id=>6), GraphBand.load(:id=>6, :vocalist_id=>22)]
@@ -1192,15 +1209,31 @@ describe Sequel::Model, "#eager_graph" do
1192
1209
  GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :left_primary_key=>:band_id, :right_key=>:genre_id, :right_primary_key=>:xxx, :join_table=>:ag
1193
1210
  ds = GraphAlbum.eager_graph(:inner_genres)
1194
1211
  ds.sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.band_id) LEFT OUTER JOIN genres AS inner_genres ON (inner_genres.xxx = ag.genre_id)'
1195
- def ds.fetch_rows(sql, &block)
1196
- yield({:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12})
1197
- yield({:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22})
1198
- end
1212
+ ds._fetch = [{:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12}, {:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22}]
1199
1213
  as = ds.all
1200
1214
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1201
1215
  as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
1202
1216
  end
1203
1217
 
1218
+ it "should respect composite primary keys for classes when eager loading" do
1219
+ c1 = Class.new(GraphAlbum)
1220
+ c2 = Class.new(GraphBand)
1221
+ c1.set_primary_key [:band_id, :id]
1222
+ c2.set_primary_key [:vocalist_id, :id]
1223
+ c1.many_to_many :sbands, :class=>c2, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :join_table=>:b
1224
+ c2.one_to_many :salbums, :class=>c1, :key=>[:band_id, :id]
1225
+ ds = c1.eager_graph(:sbands=>:salbums)
1226
+ ds.sql.should == 'SELECT albums.id, albums.band_id, sbands.id AS sbands_id, sbands.vocalist_id, salbums.id AS salbums_id, salbums.band_id AS salbums_band_id FROM albums LEFT OUTER JOIN b ON ((b.l1 = albums.band_id) AND (b.l2 = albums.id)) LEFT OUTER JOIN bands AS sbands ON ((sbands.vocalist_id = b.r1) AND (sbands.id = b.r2)) LEFT OUTER JOIN albums AS salbums ON ((salbums.band_id = sbands.vocalist_id) AND (salbums.id = sbands.id))'
1227
+ ds._fetch = [{:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6, :salbums_id=>7, :salbums_band_id=>8},
1228
+ {:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6, :salbums_id=>9, :salbums_band_id=>10},
1229
+ {:id=>3, :band_id=>2, :sbands_id=>6, :vocalist_id=>22, :salbums_id=>nil, :salbums_band_id=>nil},
1230
+ {:id=>7, :band_id=>8, :sbands_id=>nil, :vocalist_id=>nil, :salbums_id=>nil, :salbums_band_id=>nil}]
1231
+ as = ds.all
1232
+ as.should == [c1.load(:id=>3, :band_id=>2), c1.load(:id=>7, :band_id=>8)]
1233
+ as.map{|x| x.sbands}.should == [[c2.load(:id=>5, :vocalist_id=>6), c2.load(:id=>6, :vocalist_id=>22)], []]
1234
+ as.map{|x| x.sbands.map{|y| y.salbums}}.should == [[[c1.load(:id=>7, :band_id=>8), c1.load(:id=>9, :band_id=>10)], []], []]
1235
+ end
1236
+
1204
1237
  it "should respect the association's :graph_select option" do
1205
1238
  GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_select=>:vocalist_id
1206
1239
  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)'
@@ -1212,6 +1245,14 @@ describe Sequel::Model, "#eager_graph" do
1212
1245
  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)'
1213
1246
  end
1214
1247
 
1248
+ it "should respect the association's :graph_alias_base option" do
1249
+ GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_alias_base=>:foo
1250
+ ds = GraphAlbum.eager_graph(:inner_band)
1251
+ ds.sql.should == 'SELECT albums.id, albums.band_id, foo.id AS foo_id, foo.vocalist_id FROM albums LEFT OUTER JOIN bands AS foo ON (foo.id = albums.band_id)'
1252
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_alias_base=>:foo
1253
+ ds.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, foo.id AS foo_id, foo.vocalist_id, foo_0.id AS foo_0_id, foo_0.album_id FROM albums LEFT OUTER JOIN bands AS foo ON (foo.id = albums.band_id) LEFT OUTER JOIN tracks AS foo_0 ON (foo_0.album_id = albums.id)'
1254
+ end
1255
+
1215
1256
  it "should respect the association's :graph_join_type option" do
1216
1257
  GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_join_type=>:inner
1217
1258
  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)'
@@ -1262,21 +1303,21 @@ describe Sequel::Model, "#eager_graph" do
1262
1303
  end
1263
1304
 
1264
1305
  it "should respect the association's :graph_block option" do
1265
- GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_block=>proc{|ja,lja,js| {:active.qualify(ja)=>true}}
1306
+ GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_block=>proc{|ja,lja,js| {Sequel.qualify(ja, :active)=>true}}
1266
1307
  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))"
1267
1308
 
1268
- GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_block=>proc{|ja,lja,js| {:id.qualify(ja)=>(0..100)}}
1309
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :graph_block=>proc{|ja,lja,js| {Sequel.qualify(ja, :id)=>(0..100)}}
1269
1310
  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))'
1270
1311
 
1271
- 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)}}
1312
+ 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=>Sequel.qualify(lja, :active)}}
1272
1313
  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))"
1273
1314
  end
1274
1315
 
1275
- it "should respect the association's :graph_join_block option" do
1276
- 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}}
1316
+ it "should respect the association's :graph_join_table_block option" do
1317
+ 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| {Sequel.qualify(ja, :active)=>true}}
1277
1318
  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)"
1278
1319
 
1279
- 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)}}
1320
+ 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=>Sequel.qualify(lja, :active)}}, :graph_join_table_block=>proc{|ja,lja,js| {true=>Sequel.qualify(lja, :active)}}
1280
1321
  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))"
1281
1322
  end
1282
1323
 
@@ -1284,13 +1325,17 @@ describe Sequel::Model, "#eager_graph" do
1284
1325
  GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :eager_grapher=>proc{|ds, aa, ta| ds.graph(GraphBand, {:active=>true}, :table_alias=>aa, :join_type=>:inner)}
1285
1326
  GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums INNER JOIN bands AS active_band ON (active_band.active IS TRUE)"
1286
1327
 
1287
- GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :eager_grapher=>proc{|ds, aa, ta| ds.graph(GraphTrack, nil, :join_type=>:natural, :table_alias=>aa)}
1328
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :eager_grapher=>proc{|eo| eo[:self].graph(GraphTrack, nil, :join_type=>:natural, :table_alias=>eo[:table_alias])}
1288
1329
  GraphAlbum.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums NATURAL JOIN tracks AS right_tracks'
1289
1330
 
1290
1331
  GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :eager_grapher=>proc{|ds, aa, ta| ds.graph(:ag, {:album_id=>:id}, :table_alias=>:a123, :implicit_qualifier=>ta).graph(GraphGenre, [:album_id], :table_alias=>aa)}
1291
1332
  GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag AS a123 ON (a123.album_id = albums.id) LEFT OUTER JOIN genres AS active_genres USING (album_id)"
1292
1333
  end
1293
1334
 
1335
+ it "should raise an error if you use an :eager_grapher proc with the wrong arity" do
1336
+ proc{GraphAlbum.many_to_one :special_band, :eager_grapher=>proc{|a, b|}}.should raise_error(Sequel::Error)
1337
+ end
1338
+
1294
1339
  it "should respect the association's :graph_only_conditions option" do
1295
1340
  GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_only_conditions=>{:active=>true}
1296
1341
  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)"
@@ -1306,7 +1351,7 @@ describe Sequel::Model, "#eager_graph" do
1306
1351
  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}
1307
1352
  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)"
1308
1353
 
1309
- 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"
1354
+ GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :graph_only_conditions=>(Sequel.expr(:price) + 2 > 100), :graph_join_table_only_conditions=>"active"
1310
1355
  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)"
1311
1356
  end
1312
1357
 
@@ -1320,7 +1365,7 @@ describe Sequel::Model, "#eager_graph" do
1320
1365
  end
1321
1366
 
1322
1367
  it "should only qualify unqualified symbols, identifiers, or ordered versions in association's :order" do
1323
- 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)]
1368
+ GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :order=>[Sequel.identifier(:blah__id), Sequel.identifier(:blah__id).desc, Sequel.desc(:blah__id), :blah__id, :album_id, Sequel.desc(:album_id), 1, Sequel.lit('RANDOM()'), Sequel.qualify(:b, :a)]
1324
1369
  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'
1325
1370
  end
1326
1371
 
@@ -1360,36 +1405,127 @@ describe Sequel::Model, "#eager_graph" do
1360
1405
  GraphAlbum.many_to_many :inner_genres, :class=>'GraphGenre', :left_key=>:album_id, :left_primary_key=>:band_id, :right_key=>:genre_id, :right_primary_key=>:xxx, :join_table=>:ag
1361
1406
  ds = GraphAlbum.eager_graph(:inner_genres)
1362
1407
  ds.sql.should == 'SELECT albums.id, albums.band_id, inner_genres.id AS inner_genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.band_id) LEFT OUTER JOIN genres AS inner_genres ON (inner_genres.xxx = ag.genre_id)'
1363
- def ds.fetch_rows(sql, &block)
1364
- yield({:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12})
1365
- yield({:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22})
1366
- end
1408
+ ds._fetch = [{:id=>3, :band_id=>2, :inner_genres_id=>5, :xxx=>12}, {:id=>3, :band_id=>2, :inner_genres_id=>6, :xxx=>22}]
1367
1409
  as = ds.all
1368
1410
  as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
1369
1411
  as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
1412
+ GraphAlbum.set_primary_key :id
1413
+ GraphGenre.set_primary_key :id
1370
1414
  end
1371
1415
 
1372
1416
  it "should handle eager loading with schemas and aliases of different types" do
1373
1417
  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)'
1374
- 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)'
1375
- 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)'
1376
- 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)'
1377
- 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)'
1418
+ GraphAlbum.eager_graph(:band).join(Sequel.qualify(: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)'
1419
+ GraphAlbum.eager_graph(:band).join(Sequel.expr(: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)'
1420
+ GraphAlbum.eager_graph(:band).join(:s__b, [:b_id], Sequel.identifier(:genres)).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)'
1421
+ GraphAlbum.eager_graph(:band).join(Sequel.identifier(: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)'
1378
1422
  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)'
1379
1423
  end
1380
1424
 
1381
1425
  it "should raise errors if invalid aliases or table styles are used" do
1382
- proc{GraphAlbum.from_self(:alias=>:bands.qualify(:s)).eager_graph(:band)}.should raise_error(Sequel::Error)
1383
- proc{GraphAlbum.from('?'.lit(:bands)).eager_graph(:band)}.should raise_error(Sequel::Error)
1426
+ proc{GraphAlbum.from_self(:alias=>Sequel.qualify(:s, :bands)).eager_graph(:band)}.should raise_error(Sequel::Error)
1427
+ proc{GraphAlbum.from(Sequel.lit('?', :bands)).eager_graph(:band)}.should raise_error(Sequel::Error)
1384
1428
  end
1385
1429
 
1386
1430
  it "should eagerly load schema qualified tables correctly with joins" do
1387
1431
  c1 = Class.new(GraphAlbum)
1388
1432
  c2 = Class.new(GraphGenre)
1389
- c1.dataset = c1.dataset.from(:s__a)
1433
+ ds = c1.dataset = c1.dataset.from(:s__a)
1434
+ def ds.columns() [:id] end
1390
1435
  c2.dataset = c2.dataset.from(:s__g)
1391
1436
  c1.many_to_many :a_genres, :class=>c2, :left_primary_key=>:id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:s__ag
1392
1437
  ds = c1.join(:s__t, [:b_id]).eager_graph(:a_genres)
1393
1438
  ds.sql.should == 'SELECT a.id, a_genres.id AS a_genres_id FROM (SELECT * FROM s.a INNER JOIN s.t USING (b_id)) AS a LEFT OUTER JOIN s.ag AS ag ON (ag.album_id = a.id) LEFT OUTER JOIN s.g AS a_genres ON (a_genres.id = ag.genre_id)'
1394
1439
  end
1440
+
1441
+ it "should respect :after_load callbacks on associations when eager graphing" do
1442
+ GraphAlbum.many_to_one :al_band, :class=>GraphBand, :key=>:band_id, :after_load=>proc{|o, a| a.id *=2}
1443
+ GraphAlbum.one_to_many :al_tracks, :class=>GraphTrack, :key=>:album_id, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
1444
+ GraphAlbum.many_to_many :al_genres, :class=>GraphGenre, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
1445
+ ds = GraphAlbum.eager_graph(:al_band, :al_tracks, :al_genres)
1446
+ ds.sql.should == "SELECT albums.id, albums.band_id, al_band.id AS al_band_id, al_band.vocalist_id, al_tracks.id AS al_tracks_id, al_tracks.album_id, al_genres.id AS al_genres_id FROM albums LEFT OUTER JOIN bands AS al_band ON (al_band.id = albums.band_id) LEFT OUTER JOIN tracks AS al_tracks ON (al_tracks.album_id = albums.id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS al_genres ON (al_genres.id = ag.genre_id)"
1447
+ ds._fetch = {:id=>1, :band_id=>2, :al_band_id=>3, :vocalist_id=>4, :al_tracks_id=>5, :album_id=>6, :al_genres_id=>7}
1448
+ a = ds.all.first
1449
+ a.should == GraphAlbum.load(:id => 1, :band_id => 2)
1450
+ a.al_band.should == GraphBand.load(:id=>6, :vocalist_id=>4)
1451
+ a.al_tracks.should == [GraphTrack.load(:id=>10, :album_id=>6)]
1452
+ a.al_genres.should == [GraphGenre.load(:id=>14)]
1453
+ end
1454
+
1455
+ it "should respect limits on associations when eager graphing" do
1456
+ GraphAlbum.many_to_one :al_band, :class=>GraphBand, :key=>:band_id
1457
+ GraphAlbum.one_to_many :al_tracks, :class=>GraphTrack, :key=>:album_id, :limit=>2
1458
+ GraphAlbum.many_to_many :al_genres, :class=>GraphGenre, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>2
1459
+ ds = GraphAlbum.eager_graph(:al_band, :al_tracks, :al_genres)
1460
+ ds.sql.should == "SELECT albums.id, albums.band_id, al_band.id AS al_band_id, al_band.vocalist_id, al_tracks.id AS al_tracks_id, al_tracks.album_id, al_genres.id AS al_genres_id FROM albums LEFT OUTER JOIN bands AS al_band ON (al_band.id = albums.band_id) LEFT OUTER JOIN tracks AS al_tracks ON (al_tracks.album_id = albums.id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS al_genres ON (al_genres.id = ag.genre_id)"
1461
+ ds._fetch = [{:id=>1, :band_id=>2, :al_band_id=>3, :vocalist_id=>4, :al_tracks_id=>5, :album_id=>6, :al_genres_id=>7},
1462
+ {:id=>1, :band_id=>2, :al_band_id=>8, :vocalist_id=>9, :al_tracks_id=>10, :album_id=>11, :al_genres_id=>12},
1463
+ {:id=>1, :band_id=>2, :al_band_id=>13, :vocalist_id=>14, :al_tracks_id=>15, :album_id=>16, :al_genres_id=>17}]
1464
+ a = ds.all.first
1465
+ a.should == GraphAlbum.load(:id => 1, :band_id => 2)
1466
+ a.al_band.should == GraphBand.load(:id=>3, :vocalist_id=>4)
1467
+ a.al_tracks.should == [GraphTrack.load(:id=>5, :album_id=>6), GraphTrack.load(:id=>10, :album_id=>11)]
1468
+ a.al_genres.should == [GraphGenre.load(:id=>7), GraphGenre.load(:id=>12)]
1469
+ end
1470
+
1471
+ it "should eagerly load a many_to_one association with a custom callback" do
1472
+ ds = GraphAlbum.eager_graph(:band => proc {|ds| ds.select(:id).columns(:id)})
1473
+ ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0 FROM albums LEFT OUTER JOIN (SELECT id FROM bands) AS band ON (band.id = albums.band_id)'
1474
+ ds._fetch = {:id=>1, :band_id=>2, :band_id_0=>2}
1475
+ a = ds.all
1476
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1477
+ a.first.band.should == GraphBand.load(:id => 2)
1478
+ end
1479
+
1480
+ it "should eagerly load a one_to_one association with a custom callback" do
1481
+ GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
1482
+ ds = GraphAlbum.eager_graph(:track => proc {|ds| ds.select(:album_id).columns(:album_id)})
1483
+ ds.sql.should == 'SELECT albums.id, albums.band_id, track.album_id FROM albums LEFT OUTER JOIN (SELECT album_id FROM tracks) AS track ON (track.album_id = albums.id)'
1484
+ ds._fetch = {:id=>1, :band_id=>2, :album_id=>1}
1485
+ a = ds.all
1486
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1487
+ a.first.track.should == GraphTrack.load(:album_id=>1)
1488
+ end
1489
+
1490
+ it "should eagerly load a one_to_many association with a custom callback" do
1491
+ ds = GraphAlbum.eager_graph(:tracks => proc {|ds| ds.select(:album_id).columns(:album_id)})
1492
+ ds.sql.should == 'SELECT albums.id, albums.band_id, tracks.album_id FROM albums LEFT OUTER JOIN (SELECT album_id FROM tracks) AS tracks ON (tracks.album_id = albums.id)'
1493
+ ds._fetch = {:id=>1, :band_id=>2, :album_id=>1}
1494
+ a = ds.all
1495
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1496
+ a.first.tracks.should == [GraphTrack.load(:album_id=>1)]
1497
+ end
1498
+
1499
+ it "should eagerly load a many_to_many association with a custom callback" do
1500
+ ds = GraphAlbum.eager_graph(:genres => proc {|ds| ds.select(:id).columns(:id)})
1501
+ ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN (SELECT id FROM genres) AS genres ON (genres.id = ag.genre_id)'
1502
+ ds._fetch = {:id=>1, :band_id=>2, :genres_id=>4}
1503
+ a = ds.all
1504
+ a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
1505
+ a.first.genres.should == [GraphGenre.load(:id => 4)]
1506
+ end
1507
+
1508
+ it "should allow cascading of eager loading with a custom callback with hash value" do
1509
+ ds = GraphTrack.eager_graph(:album=>{proc{|ds| ds.select(:id, :band_id).columns(:id, :band_id)}=>{:band=>:members}})
1510
+ ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN (SELECT id, band_id FROM albums) AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
1511
+ ds._fetch = {:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6}
1512
+ a = ds.all
1513
+ a.should == [GraphTrack.load(:id => 3, :album_id => 1)]
1514
+ a = a.first
1515
+ a.album.should == GraphAlbum.load(:id => 1, :band_id => 2)
1516
+ a.album.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
1517
+ a.album.band.members.should == [GraphBandMember.load(:id => 5)]
1518
+ end
1519
+
1520
+ it "should allow cascading of eager loading with a custom callback with array value" do
1521
+ ds = GraphTrack.eager_graph(:album=>{proc{|ds| ds.select(:id, :band_id).columns(:id, :band_id)}=>[:band, :tracks]})
1522
+ ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, tracks_0.id AS tracks_0_id, tracks_0.album_id AS tracks_0_album_id FROM tracks LEFT OUTER JOIN (SELECT id, band_id FROM albums) AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN tracks AS tracks_0 ON (tracks_0.album_id = album.id)'
1523
+ ds._fetch = {:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>6, :tracks_0_id=>3, :tracks_0_album_id=>1}
1524
+ a = ds.all
1525
+ a.should == [GraphTrack.load(:id => 3, :album_id => 1)]
1526
+ a = a.first
1527
+ a.album.should == GraphAlbum.load(:id => 1, :band_id => 2)
1528
+ a.album.band.should == GraphBand.load(:id => 2, :vocalist_id=>6)
1529
+ a.album.tracks.should == [GraphTrack.load(:id => 3, :album_id => 1)]
1530
+ end
1395
1531
  end