sequel 3.21.0 → 3.41.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 (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -1,10 +1,300 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
+ shared_examples_for "eager limit strategies" do
4
+ specify "eager loading one_to_one associations should work correctly" do
5
+ Artist.one_to_one :first_album, {:clone=>:first_album}.merge(@els) if @els
6
+ Artist.one_to_one :last_album, {:clone=>:last_album}.merge(@els) if @els
7
+ @album.update(:artist => @artist)
8
+ diff_album = @diff_album.call
9
+ al, ar, t = @pr.call
10
+
11
+ a = Artist.eager(:first_album, :last_album).order(:name).all
12
+ a.should == [@artist, ar]
13
+ a.first.first_album.should == @album
14
+ a.first.last_album.should == diff_album
15
+ a.last.first_album.should == nil
16
+ a.last.last_album.should == nil
17
+
18
+ # Check that no extra columns got added by the eager loading
19
+ a.first.first_album.values.should == @album.values
20
+ a.first.last_album.values.should == diff_album.values
21
+
22
+ same_album = @same_album.call
23
+ a = Artist.eager(:first_album).order(:name).all
24
+ a.should == [@artist, ar]
25
+ [@album, same_album].should include(a.first.first_album)
26
+ a.last.first_album.should == nil
27
+ end
28
+
29
+ specify "should correctly handle limits and offsets when eager loading one_to_many associations" do
30
+ Artist.one_to_many :first_two_albums, {:clone=>:first_two_albums}.merge(@els) if @els
31
+ Artist.one_to_many :second_two_albums, {:clone=>:second_two_albums}.merge(@els) if @els
32
+ Artist.one_to_many :last_two_albums, {:clone=>:last_two_albums}.merge(@els) if @els
33
+ @album.update(:artist => @artist)
34
+ middle_album = @middle_album.call
35
+ diff_album = @diff_album.call
36
+ al, ar, t = @pr.call
37
+
38
+ ars = Artist.eager(:first_two_albums, :second_two_albums, :last_two_albums).order(:name).all
39
+ ars.should == [@artist, ar]
40
+ ars.first.first_two_albums.should == [@album, middle_album]
41
+ ars.first.second_two_albums.should == [middle_album, diff_album]
42
+ ars.first.last_two_albums.should == [diff_album, middle_album]
43
+ ars.last.first_two_albums.should == []
44
+ ars.last.second_two_albums.should == []
45
+ ars.last.last_two_albums.should == []
46
+
47
+ # Check that no extra columns got added by the eager loading
48
+ ars.first.first_two_albums.map{|x| x.values}.should == [@album, middle_album].map{|x| x.values}
49
+ ars.first.second_two_albums.map{|x| x.values}.should == [middle_album, diff_album].map{|x| x.values}
50
+ ars.first.last_two_albums.map{|x| x.values}.should == [diff_album, middle_album].map{|x| x.values}
51
+ end
52
+
53
+ specify "should correctly handle limits and offsets when eager loading many_to_many associations" do
54
+ if @els == {:eager_limit_strategy=>:correlated_subquery} && Sequel.guarded?(:derby)
55
+ pending("Derby errors with correlated subqueries on many_to_many associations")
56
+ end
57
+ Album.many_to_many :first_two_tags, {:clone=>:first_two_tags}.merge(@els) if @els
58
+ Album.many_to_many :second_two_tags, {:clone=>:second_two_tags}.merge(@els) if @els
59
+ Album.many_to_many :last_two_tags, {:clone=>:last_two_tags}.merge(@els) if @els
60
+ tu, tv = @other_tags.call
61
+ al, ar, t = @pr.call
62
+
63
+ als = Album.eager(:first_two_tags, :second_two_tags, :last_two_tags).order(:name).all
64
+ als.should == [@album, al]
65
+ als.first.first_two_tags.should == [@tag, tu]
66
+ als.first.second_two_tags.should == [tu, tv]
67
+ als.first.last_two_tags.should == [tv, tu]
68
+ als.last.first_two_tags.should == []
69
+ als.last.second_two_tags.should == []
70
+ als.last.last_two_tags.should == []
71
+
72
+ # Check that no extra columns got added by the eager loading
73
+ als.first.first_two_tags.map{|x| x.values}.should == [@tag, tu].map{|x| x.values}
74
+ als.first.second_two_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
75
+ als.first.last_two_tags.map{|x| x.values}.should == [tv, tu].map{|x| x.values}
76
+ end
77
+
78
+ specify "should correctly handle limits and offsets when eager loading many_through_many associations" do
79
+ if @els == {:eager_limit_strategy=>:correlated_subquery} && Sequel.guarded?(:derby)
80
+ pending("Derby errors with correlated subqueries on many_through_many associations")
81
+ end
82
+ Artist.many_through_many :first_two_tags, {:clone=>:first_two_tags}.merge(@els) if @els
83
+ Artist.many_through_many :second_two_tags, {:clone=>:second_two_tags}.merge(@els) if @els
84
+ Artist.many_through_many :last_two_tags, {:clone=>:last_two_tags}.merge(@els) if @els
85
+ @album.update(:artist => @artist)
86
+ tu, tv = @other_tags.call
87
+ al, ar, t = @pr.call
88
+
89
+ ars = Artist.eager(:first_two_tags, :second_two_tags, :last_two_tags).order(:name).all
90
+ ars.should == [@artist, ar]
91
+ ars.first.first_two_tags.should == [@tag, tu]
92
+ ars.first.second_two_tags.should == [tu, tv]
93
+ ars.first.last_two_tags.should == [tv, tu]
94
+ ars.last.first_two_tags.should == []
95
+ ars.last.second_two_tags.should == []
96
+ ars.last.last_two_tags.should == []
97
+
98
+ # Check that no extra columns got added by the eager loading
99
+ ars.first.first_two_tags.map{|x| x.values}.should == [@tag, tu].map{|x| x.values}
100
+ ars.first.second_two_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
101
+ ars.first.last_two_tags.map{|x| x.values}.should == [tv, tu].map{|x| x.values}
102
+ end
103
+ end
104
+
105
+ shared_examples_for "filtering/excluding by associations" do
106
+ specify "should work correctly when filtering by associations" do
107
+ @album.update(:artist => @artist)
108
+ @album.add_tag(@tag)
109
+
110
+ @Artist.filter(:albums=>@album).all.should == [@artist]
111
+ @Artist.filter(:first_album=>@album).all.should == [@artist]
112
+ @Album.filter(:artist=>@artist).all.should == [@album]
113
+ @Album.filter(:tags=>@tag).all.should == [@album]
114
+ @Album.filter(:alias_tags=>@tag).all.should == [@album]
115
+ @Tag.filter(:albums=>@album).all.should == [@tag]
116
+ @Album.filter(:artist=>@artist, :tags=>@tag).all.should == [@album]
117
+ @artist.albums_dataset.filter(:tags=>@tag).all.should == [@album]
118
+ end
119
+
120
+ specify "should work correctly when excluding by associations" do
121
+ @album.update(:artist => @artist)
122
+ @album.add_tag(@tag)
123
+ album, artist, tag = @pr.call
124
+
125
+ @Artist.exclude(:albums=>@album).all.should == [artist]
126
+ @Artist.exclude(:first_album=>@album).all.should == [artist]
127
+ @Album.exclude(:artist=>@artist).all.should == [album]
128
+ @Album.exclude(:tags=>@tag).all.should == [album]
129
+ @Album.exclude(:alias_tags=>@tag).all.should == [album]
130
+ @Tag.exclude(:albums=>@album).all.should == [tag]
131
+ @Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [album]
132
+ end
133
+
134
+ specify "should work correctly when filtering by multiple associations" do
135
+ album, artist, tag = @pr.call
136
+ @album.update(:artist => @artist)
137
+ @album.add_tag(@tag)
138
+
139
+ @Artist.filter(:albums=>[@album, album]).all.should == [@artist]
140
+ @Artist.filter(:first_album=>[@album, album]).all.should == [@artist]
141
+ @Album.filter(:artist=>[@artist, artist]).all.should == [@album]
142
+ @Album.filter(:tags=>[@tag, tag]).all.should == [@album]
143
+ @Album.filter(:alias_tags=>[@tag, tag]).all.should == [@album]
144
+ @Tag.filter(:albums=>[@album, album]).all.should == [@tag]
145
+ @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
146
+ @artist.albums_dataset.filter(:tags=>[@tag, tag]).all.should == [@album]
147
+
148
+ album.add_tag(tag)
149
+
150
+ @Artist.filter(:albums=>[@album, album]).all.should == [@artist]
151
+ @Artist.filter(:first_album=>[@album, album]).all.should == [@artist]
152
+ @Album.filter(:artist=>[@artist, artist]).all.should == [@album]
153
+ @Album.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
154
+ @Album.filter(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
155
+ @Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
156
+ @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
157
+
158
+ album.update(:artist => artist)
159
+
160
+ @Artist.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
161
+ @Artist.filter(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
162
+ @Album.filter(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [@album, album]
163
+ @Album.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
164
+ @Album.filter(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
165
+ @Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
166
+ @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
167
+ end
168
+
169
+ specify "should work correctly when excluding by multiple associations" do
170
+ album, artist, tag = @pr.call
171
+
172
+ @Artist.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
173
+ @Artist.exclude(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
174
+ @Album.exclude(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [@album, album]
175
+ @Album.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
176
+ @Album.exclude(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
177
+ @Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
178
+ @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
179
+
180
+ @album.update(:artist => @artist)
181
+ @album.add_tag(@tag)
182
+
183
+ @Artist.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [artist]
184
+ @Artist.exclude(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [artist]
185
+ @Album.exclude(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [album]
186
+ @Album.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
187
+ @Album.exclude(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
188
+ @Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [tag]
189
+ @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
190
+
191
+ album.add_tag(tag)
192
+
193
+ @Artist.exclude(:albums=>[@album, album]).all.should == [artist]
194
+ @Artist.exclude(:first_album=>[@album, album]).all.should == [artist]
195
+ @Album.exclude(:artist=>[@artist, artist]).all.should == [album]
196
+ @Album.exclude(:tags=>[@tag, tag]).all.should == []
197
+ @Album.exclude(:alias_tags=>[@tag, tag]).all.should == []
198
+ @Tag.exclude(:albums=>[@album, album]).all.should == []
199
+ @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [album]
200
+
201
+ album.update(:artist => artist)
202
+
203
+ @Artist.exclude(:albums=>[@album, album]).all.should == []
204
+ @Artist.exclude(:first_album=>[@album, album]).all.should == []
205
+ @Album.exclude(:artist=>[@artist, artist]).all.should == []
206
+ @Album.exclude(:tags=>[@tag, tag]).all.should == []
207
+ @Album.exclude(:alias_tags=>[@tag, tag]).all.should == []
208
+ @Tag.exclude(:albums=>[@album, album]).all.should == []
209
+ @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == []
210
+ end
211
+
212
+ specify "should work correctly when excluding by associations in regards to NULL values" do
213
+ @Artist.exclude(:albums=>@album).all.should == [@artist]
214
+ @Artist.exclude(:first_album=>@album).all.should == [@artist]
215
+ @Album.exclude(:artist=>@artist).all.should == [@album]
216
+ @Album.exclude(:tags=>@tag).all.should == [@album]
217
+ @Album.exclude(:alias_tags=>@tag).all.should == [@album]
218
+ @Tag.exclude(:albums=>@album).all.should == [@tag]
219
+ @Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [@album]
220
+
221
+ @album.update(:artist => @artist)
222
+ @artist.albums_dataset.exclude(:tags=>@tag).all.should == [@album]
223
+ end
224
+
225
+ specify "should handle NULL values in join table correctly when filtering/excluding many_to_many associations" do
226
+ @ins.call
227
+ @Album.exclude(:tags=>@tag).all.should == [@album]
228
+ @Album.exclude(:alias_tags=>@tag).all.should == [@album]
229
+ @album.add_tag(@tag)
230
+ @Album.filter(:tags=>@tag).all.should == [@album]
231
+ @Album.filter(:alias_tags=>@tag).all.should == [@album]
232
+ album, artist, tag = @pr.call
233
+ @Album.exclude(:tags=>@tag).all.should == [album]
234
+ @Album.exclude(:alias_tags=>@tag).all.should == [album]
235
+ @Album.exclude(:tags=>tag).all.sort_by{|x| x.pk}.should == [@album, album]
236
+ @Album.exclude(:alias_tags=>tag).all.sort_by{|x| x.pk}.should == [@album, album]
237
+ end
238
+
239
+ specify "should work correctly when filtering by association datasets" do
240
+ album, artist, tag = @pr.call
241
+ @album.update(:artist => @artist)
242
+ @album.add_tag(@tag)
243
+ album.add_tag(tag)
244
+ album.update(:artist => artist)
245
+
246
+ @Artist.filter(:albums=>@Album).all.sort_by{|x| x.pk}.should == [@artist, artist]
247
+ @Artist.filter(:albums=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
248
+ @Artist.filter(:albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
249
+ @Artist.filter(:first_album=>@Album).all.sort_by{|x| x.pk}.should == [@artist, artist]
250
+ @Artist.filter(:first_album=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
251
+ @Artist.filter(:first_album=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
252
+ @Album.filter(:artist=>@Artist).all.sort_by{|x| x.pk}.should == [@album, album]
253
+ @Album.filter(:artist=>@Artist.filter(Array(Artist.primary_key).map{|k| Sequel.qualify(Artist.table_name, k)}.zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [album]
254
+ @Album.filter(:artist=>@Artist.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
255
+ @Album.filter(:tags=>@Tag).all.sort_by{|x| x.pk}.should == [@album, album]
256
+ @Album.filter(:tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
257
+ @Album.filter(:tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
258
+ @Album.filter(:alias_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@album, album]
259
+ @Album.filter(:alias_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
260
+ @Album.filter(:alias_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
261
+ @Tag.filter(:albums=>@Album).all.sort_by{|x| x.pk}.should == [@tag, tag]
262
+ @Tag.filter(:albums=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [tag]
263
+ @Tag.filter(:albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
264
+ end
265
+
266
+ specify "should work correctly when excluding by association datasets" do
267
+ album, artist, tag = @pr.call
268
+ @album.update(:artist => @artist)
269
+ @album.add_tag(@tag)
270
+ album.add_tag(tag)
271
+ album.update(:artist => artist)
272
+
273
+ @Artist.exclude(:albums=>@Album).all.sort_by{|x| x.pk}.should == []
274
+ @Artist.exclude(:albums=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
275
+ @Artist.exclude(:albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
276
+ @Album.exclude(:artist=>@Artist).all.sort_by{|x| x.pk}.should == []
277
+ @Album.exclude(:artist=>@Artist.filter(Array(Artist.primary_key).map{|k| Sequel.qualify(Artist.table_name, k)}.zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [@album]
278
+ @Album.exclude(:artist=>@Artist.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
279
+ @Album.exclude(:tags=>@Tag).all.sort_by{|x| x.pk}.should == []
280
+ @Album.exclude(:tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
281
+ @Album.exclude(:tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
282
+ @Album.exclude(:alias_tags=>@Tag).all.sort_by{|x| x.pk}.should == []
283
+ @Album.exclude(:alias_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
284
+ @Album.exclude(:alias_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
285
+ @Tag.exclude(:albums=>@Album).all.sort_by{|x| x.pk}.should == []
286
+ @Tag.exclude(:albums=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@tag]
287
+ @Tag.exclude(:albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@tag, tag]
288
+ end
289
+ end
290
+
3
291
  shared_examples_for "regular and composite key associations" do
4
292
  specify "should return no objects if none are associated" do
5
293
  @album.artist.should == nil
294
+ @artist.first_album.should == nil
6
295
  @artist.albums.should == []
7
296
  @album.tags.should == []
297
+ @album.alias_tags.should == []
8
298
  @tag.albums.should == []
9
299
  end
10
300
 
@@ -17,17 +307,86 @@ shared_examples_for "regular and composite key associations" do
17
307
  @tag.reload
18
308
 
19
309
  @album.artist.should == @artist
310
+ @artist.first_album.should == @album
20
311
  @artist.albums.should == [@album]
21
312
  @album.tags.should == [@tag]
313
+ @album.alias_tags.should == [@tag]
22
314
  @tag.albums.should == [@album]
23
315
  end
24
316
 
317
+ specify "should work correctly with prepared_statements_association plugin" do
318
+ @album.update(:artist => @artist)
319
+ @album.add_tag(@tag)
320
+
321
+ @album.reload
322
+ @artist.reload
323
+ @tag.reload
324
+
325
+ [Tag, Album, Artist].each{|x| x.plugin :prepared_statements_associations}
326
+ @album.artist.should == @artist
327
+ @artist.first_album.should == @album
328
+ @artist.albums.should == [@album]
329
+ @album.tags.should == [@tag]
330
+ @album.alias_tags.should == [@tag]
331
+ @tag.albums.should == [@album]
332
+ end
333
+
334
+ specify "should have working dataset associations" do
335
+ album, artist, tag = @pr.call
336
+
337
+ Tag.albums.all.should == []
338
+ Album.artists.all.should == []
339
+ Album.tags.all.should == []
340
+ Album.alias_tags.all.should == []
341
+ Artist.albums.all.should == []
342
+ Artist.tags.all.should == []
343
+ Artist.albums.tags.all.should == []
344
+
345
+ @album.update(:artist => @artist)
346
+ @album.add_tag(@tag)
347
+
348
+ Tag.albums.all.should == [@album]
349
+ Album.artists.all.should == [@artist]
350
+ Album.tags.all.should == [@tag]
351
+ Album.alias_tags.all.should == [@tag]
352
+ Artist.albums.all.should == [@album]
353
+ Artist.tags.all.should == [@tag]
354
+ Artist.albums.tags.all.should == [@tag]
355
+
356
+ album.add_tag(tag)
357
+ album.update(:artist => artist)
358
+
359
+ Tag.albums.order(:name).all.should == [@album, album]
360
+ Album.artists.order(:name).all.should == [@artist, artist]
361
+ Album.tags.order(:name).all.should == [@tag, tag]
362
+ Album.alias_tags.order(:name).all.should == [@tag, tag]
363
+ Artist.albums.order(:name).all.should == [@album, album]
364
+ Artist.tags.order(:name).all.should == [@tag, tag]
365
+ Artist.albums.tags.order(:name).all.should == [@tag, tag]
366
+
367
+ Tag.filter(Tag.qualified_primary_key_hash(tag.pk)).albums.all.should == [album]
368
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).artists.all.should == [artist]
369
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == [tag]
370
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).alias_tags.all.should == [tag]
371
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.all.should == [album]
372
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).tags.all.should == [tag]
373
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.tags.all.should == [tag]
374
+
375
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == [tag]
376
+ Artist.filter(Artist.qualified_primary_key_hash(@artist.pk)).albums.filter(Album.qualified_primary_key_hash(@album.pk)).tags.all.should == [@tag]
377
+ Artist.filter(Artist.qualified_primary_key_hash(@artist.pk)).albums.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == []
378
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.filter(Album.qualified_primary_key_hash(@album.pk)).tags.all.should == []
379
+ end
380
+
25
381
  specify "should have remove methods work" do
26
382
  @album.update(:artist => @artist)
27
383
  @album.add_tag(@tag)
28
384
 
29
385
  @album.update(:artist => nil)
30
386
  @album.remove_tag(@tag)
387
+
388
+ @album.add_alias_tag(@tag)
389
+ @album.remove_alias_tag(@tag)
31
390
 
32
391
  @album.reload
33
392
  @artist.reload
@@ -37,6 +396,12 @@ shared_examples_for "regular and composite key associations" do
37
396
  @artist.albums.should == []
38
397
  @album.tags.should == []
39
398
  @tag.albums.should == []
399
+
400
+ @album.add_alias_tag(@tag)
401
+ @album.remove_alias_tag(@tag)
402
+
403
+ @album.reload
404
+ @album.alias_tags.should == []
40
405
  end
41
406
 
42
407
  specify "should have remove_all methods work" do
@@ -54,16 +419,24 @@ shared_examples_for "regular and composite key associations" do
54
419
  @artist.albums.should == []
55
420
  @album.tags.should == []
56
421
  @tag.albums.should == []
422
+
423
+ @album.add_alias_tag(@tag)
424
+ @album.remove_all_alias_tags
425
+
426
+ @album.reload
427
+ @album.alias_tags.should == []
57
428
  end
58
429
 
59
430
  specify "should eager load via eager correctly" do
60
431
  @album.update(:artist => @artist)
61
432
  @album.add_tag(@tag)
62
433
 
63
- a = Artist.eager(:albums=>:tags).all
434
+ a = Artist.eager(:albums=>[:tags, :alias_tags]).eager(:first_album).all
64
435
  a.should == [@artist]
65
436
  a.first.albums.should == [@album]
437
+ a.first.first_album.should == @album
66
438
  a.first.albums.first.tags.should == [@tag]
439
+ a.first.albums.first.alias_tags.should == [@tag]
67
440
 
68
441
  a = Tag.eager(:albums=>:artist).all
69
442
  a.should == [@tag]
@@ -71,14 +444,58 @@ shared_examples_for "regular and composite key associations" do
71
444
  a.first.albums.first.artist.should == @artist
72
445
  end
73
446
 
447
+ describe "when filtering/excluding by associations" do
448
+ before do
449
+ @Artist = Artist.dataset
450
+ @Album = Album.dataset
451
+ @Tag = Tag.dataset
452
+ end
453
+
454
+ it_should_behave_like "filtering/excluding by associations"
455
+ end
456
+
457
+ describe "when filtering/excluding by associations when joining" do
458
+ def self_join(c)
459
+ c.join(Sequel.as(c.table_name, :b), Array(c.primary_key).zip(Array(c.primary_key))).select_all(c.table_name)
460
+ end
461
+
462
+ before do
463
+ @Artist = self_join(Artist)
464
+ @Album = self_join(Album)
465
+ @Tag = self_join(Tag)
466
+ end
467
+
468
+ it_should_behave_like "filtering/excluding by associations"
469
+ end
470
+
471
+ describe "with no :eager_limit_strategy" do
472
+ it_should_behave_like "eager limit strategies"
473
+ end
474
+
475
+ describe "with :eager_limit_strategy=>true" do
476
+ before do
477
+ @els = {:eager_limit_strategy=>true}
478
+ end
479
+ it_should_behave_like "eager limit strategies"
480
+ end
481
+
482
+ describe "with :eager_limit_strategy=>:window_function" do
483
+ before do
484
+ @els = {:eager_limit_strategy=>:window_function}
485
+ end
486
+ it_should_behave_like "eager limit strategies"
487
+ end if INTEGRATION_DB.dataset.supports_window_functions?
488
+
74
489
  specify "should eager load via eager_graph correctly" do
75
490
  @album.update(:artist => @artist)
76
491
  @album.add_tag(@tag)
77
492
 
78
- a = Artist.eager_graph(:albums=>:tags).all
493
+ a = Artist.eager_graph(:albums=>[:tags, :alias_tags]).eager_graph(:first_album).all
79
494
  a.should == [@artist]
80
495
  a.first.albums.should == [@album]
496
+ a.first.first_album.should == @album
81
497
  a.first.albums.first.tags.should == [@tag]
498
+ a.first.albums.first.alias_tags.should == [@tag]
82
499
 
83
500
  a = Tag.eager_graph(:albums=>:artist).all
84
501
  a.should == [@tag]
@@ -117,46 +534,97 @@ shared_examples_for "regular and composite key associations" do
117
534
  end
118
535
 
119
536
  describe "Sequel::Model Simple Associations" do
120
- before do
537
+ before(:all) do
121
538
  @db = INTEGRATION_DB
122
- @db.create_table!(:artists) do
539
+ @db.drop_table?(:albums_tags, :tags, :albums, :artists)
540
+ @db.create_table(:artists) do
123
541
  primary_key :id
124
542
  String :name
125
543
  end
126
- @db.create_table!(:albums) do
544
+ @db.create_table(:albums) do
127
545
  primary_key :id
128
546
  String :name
129
547
  foreign_key :artist_id, :artists
130
548
  end
131
- @db.create_table!(:tags) do
549
+ @db.create_table(:tags) do
132
550
  primary_key :id
133
551
  String :name
134
552
  end
135
- @db.create_table!(:albums_tags) do
553
+ @db.create_table(:albums_tags) do
136
554
  foreign_key :album_id, :albums
137
555
  foreign_key :tag_id, :tags
138
556
  end
557
+ end
558
+ before do
559
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db[t].delete}
139
560
  class ::Artist < Sequel::Model(@db)
140
- one_to_many :albums
561
+ plugin :dataset_associations
562
+ one_to_many :albums, :order=>:name
563
+ one_to_one :first_album, :class=>:Album, :order=>:name
564
+ one_to_one :last_album, :class=>:Album, :order=>Sequel.desc(:name)
565
+ one_to_many :first_two_albums, :class=>:Album, :order=>:name, :limit=>2
566
+ one_to_many :second_two_albums, :class=>:Album, :order=>:name, :limit=>[2, 1]
567
+ one_to_many :last_two_albums, :class=>:Album, :order=>Sequel.desc(:name), :limit=>2
141
568
  plugin :many_through_many
142
- Artist.many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]]
569
+ many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]]
570
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
571
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
572
+ many_through_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:tags__name), :limit=>2
143
573
  end
144
574
  class ::Album < Sequel::Model(@db)
575
+ plugin :dataset_associations
145
576
  many_to_one :artist
146
- many_to_many :tags
577
+ many_to_many :tags, :right_key=>:tag_id
578
+ many_to_many :alias_tags, :clone=>:tags, :join_table=>:albums_tags___at
579
+ many_to_many :first_two_tags, :clone=>:tags, :order=>:name, :limit=>2
580
+ many_to_many :second_two_tags, :clone=>:tags, :order=>:name, :limit=>[2, 1]
581
+ many_to_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:name), :limit=>2
147
582
  end
148
583
  class ::Tag < Sequel::Model(@db)
584
+ plugin :dataset_associations
149
585
  many_to_many :albums
150
586
  end
151
587
  @album = Album.create(:name=>'Al')
152
588
  @artist = Artist.create(:name=>'Ar')
153
589
  @tag = Tag.create(:name=>'T')
590
+ @same_album = lambda{Album.create(:name=>'Al', :artist_id=>@artist.id)}
591
+ @diff_album = lambda{Album.create(:name=>'lA', :artist_id=>@artist.id)}
592
+ @middle_album = lambda{Album.create(:name=>'Bl', :artist_id=>@artist.id)}
593
+ @other_tags = lambda{t = [Tag.create(:name=>'U'), Tag.create(:name=>'V')]; @db[:albums_tags].insert([:album_id, :tag_id], Tag.select(@album.id, :id)); t}
594
+ @pr = lambda{[Album.create(:name=>'Al2'),Artist.create(:name=>'Ar2'),Tag.create(:name=>'T2')]}
595
+ @ins = lambda{@db[:albums_tags].insert(:tag_id=>@tag.id)}
154
596
  end
155
597
  after do
156
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
157
598
  [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
158
599
  end
600
+ after(:all) do
601
+ @db.drop_table?(:albums_tags, :tags, :albums, :artists)
602
+ end
159
603
 
604
+ it_should_behave_like "regular and composite key associations"
605
+
606
+ describe "with :eager_limit_strategy=>:correlated_subquery" do
607
+ before do
608
+ @els = {:eager_limit_strategy=>:correlated_subquery}
609
+ end
610
+ it_should_behave_like "eager limit strategies"
611
+ end unless Sequel.guarded?(:mysql, :db2, :oracle, :h2, :cubrid)
612
+
613
+ specify "should handle many_to_one associations with same name as :key" do
614
+ Album.def_column_alias(:artist_id_id, :artist_id)
615
+ Album.many_to_one :artist_id, :key_column =>:artist_id, :class=>Artist
616
+ @album.update(:artist_id_id => @artist.id)
617
+ @album.artist_id.should == @artist
618
+
619
+ as = Album.eager(:artist_id).all
620
+ as.should == [@album]
621
+ as.map{|a| a.artist_id}.should == [@artist]
622
+
623
+ as = Album.eager_graph(:artist_id).all
624
+ as.should == [@album]
625
+ as.map{|a| a.artist_id}.should == [@artist]
626
+ end
627
+
160
628
  specify "should handle aliased tables when eager_graphing" do
161
629
  @album.update(:artist => @artist)
162
630
  @album.add_tag(@tag)
@@ -180,8 +648,6 @@ describe "Sequel::Model Simple Associations" do
180
648
  a.first.balbums.first.bartist.should == @artist
181
649
  end
182
650
 
183
- it_should_behave_like "regular and composite key associations"
184
-
185
651
  specify "should have add method accept hashes and create new records" do
186
652
  @artist.remove_all_albums
187
653
  Album.delete
@@ -216,6 +682,46 @@ describe "Sequel::Model Simple Associations" do
216
682
  @tag.reload.albums.should == []
217
683
  end
218
684
 
685
+ specify "should handle dynamic callbacks for regular loading" do
686
+ @artist.add_album(@album)
687
+
688
+ @artist.albums.should == [@album]
689
+ @artist.albums(proc{|ds| ds.exclude(:id=>@album.id)}).should == []
690
+ @artist.albums(proc{|ds| ds.filter(:id=>@album.id)}).should == [@album]
691
+
692
+ @album.artist.should == @artist
693
+ @album.artist(proc{|ds| ds.exclude(:id=>@artist.id)}).should == nil
694
+ @album.artist(proc{|ds| ds.filter(:id=>@artist.id)}).should == @artist
695
+
696
+ @artist.albums{|ds| ds.exclude(:id=>@album.id)}.should == []
697
+ @artist.albums{|ds| ds.filter(:id=>@album.id)}.should == [@album]
698
+ @album.artist{|ds| ds.exclude(:id=>@artist.id)}.should == nil
699
+ @album.artist{|ds| ds.filter(:id=>@artist.id)}.should == @artist
700
+ end
701
+
702
+ specify "should handle dynamic callbacks for eager loading via eager and eager_graph" do
703
+ @artist.add_album(@album)
704
+ @album.add_tag(@tag)
705
+ album2 = @artist.add_album(:name=>'Foo')
706
+ tag2 = album2.add_tag(:name=>'T2')
707
+
708
+ artist = Artist.eager(:albums=>:tags).all.first
709
+ artist.albums.should == [@album, album2]
710
+ artist.albums.map{|x| x.tags}.should == [[@tag], [tag2]]
711
+
712
+ artist = Artist.eager_graph(:albums=>:tags).all.first
713
+ artist.albums.should == [@album, album2]
714
+ artist.albums.map{|x| x.tags}.should == [[@tag], [tag2]]
715
+
716
+ artist = Artist.eager(:albums=>{proc{|ds| ds.where(:id=>album2.id)}=>:tags}).all.first
717
+ artist.albums.should == [album2]
718
+ artist.albums.first.tags.should == [tag2]
719
+
720
+ artist = Artist.eager_graph(:albums=>{proc{|ds| ds.where(:id=>album2.id)}=>:tags}).all.first
721
+ artist.albums.should == [album2]
722
+ artist.albums.first.tags.should == [tag2]
723
+ end
724
+
219
725
  specify "should have remove method raise an error for one_to_many records if the object isn't already associated" do
220
726
  proc{@artist.remove_album(@album.id)}.should raise_error(Sequel::Error)
221
727
  proc{@artist.remove_album(@album)}.should raise_error(Sequel::Error)
@@ -223,15 +729,16 @@ describe "Sequel::Model Simple Associations" do
223
729
  end
224
730
 
225
731
  describe "Sequel::Model Composite Key Associations" do
226
- before do
732
+ before(:all) do
227
733
  @db = INTEGRATION_DB
228
- @db.create_table!(:artists) do
734
+ @db.drop_table?(:albums_tags, :tags, :albums, :artists)
735
+ @db.create_table(:artists) do
229
736
  Integer :id1
230
737
  Integer :id2
231
738
  String :name
232
739
  primary_key [:id1, :id2]
233
740
  end
234
- @db.create_table!(:albums) do
741
+ @db.create_table(:albums) do
235
742
  Integer :id1
236
743
  Integer :id2
237
744
  String :name
@@ -240,13 +747,13 @@ describe "Sequel::Model Composite Key Associations" do
240
747
  foreign_key [:artist_id1, :artist_id2], :artists
241
748
  primary_key [:id1, :id2]
242
749
  end
243
- @db.create_table!(:tags) do
750
+ @db.create_table(:tags) do
244
751
  Integer :id1
245
752
  Integer :id2
246
753
  String :name
247
754
  primary_key [:id1, :id2]
248
755
  end
249
- @db.create_table!(:albums_tags) do
756
+ @db.create_table(:albums_tags) do
250
757
  Integer :album_id1
251
758
  Integer :album_id2
252
759
  Integer :tag_id1
@@ -254,20 +761,38 @@ describe "Sequel::Model Composite Key Associations" do
254
761
  foreign_key [:album_id1, :album_id2], :albums
255
762
  foreign_key [:tag_id1, :tag_id2], :tags
256
763
  end
764
+ end
765
+ before do
766
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db[t].delete}
257
767
  class ::Artist < Sequel::Model(@db)
768
+ plugin :dataset_associations
258
769
  set_primary_key :id1, :id2
259
770
  unrestrict_primary_key
260
- one_to_many :albums, :key=>[:artist_id1, :artist_id2]
771
+ one_to_many :albums, :key=>[:artist_id1, :artist_id2], :order=>:name
772
+ one_to_one :first_album, :clone=>:albums, :order=>:name
773
+ one_to_one :last_album, :clone=>:albums, :order=>Sequel.desc(:name)
774
+ one_to_many :first_two_albums, :clone=>:albums, :order=>:name, :limit=>2
775
+ one_to_many :second_two_albums, :clone=>:albums, :order=>:name, :limit=>[2, 1]
776
+ one_to_many :last_two_albums, :clone=>:albums, :order=>Sequel.desc(:name), :limit=>2
261
777
  plugin :many_through_many
262
- Artist.many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]]
778
+ many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]]
779
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
780
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
781
+ many_through_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:tags__name), :limit=>2
263
782
  end
264
783
  class ::Album < Sequel::Model(@db)
784
+ plugin :dataset_associations
265
785
  set_primary_key :id1, :id2
266
786
  unrestrict_primary_key
267
787
  many_to_one :artist, :key=>[:artist_id1, :artist_id2]
268
788
  many_to_many :tags, :left_key=>[:album_id1, :album_id2], :right_key=>[:tag_id1, :tag_id2]
789
+ many_to_many :alias_tags, :clone=>:tags, :join_table=>:albums_tags___at
790
+ many_to_many :first_two_tags, :clone=>:tags, :order=>:name, :limit=>2
791
+ many_to_many :second_two_tags, :clone=>:tags, :order=>:name, :limit=>[2, 1]
792
+ many_to_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:name), :limit=>2
269
793
  end
270
794
  class ::Tag < Sequel::Model(@db)
795
+ plugin :dataset_associations
271
796
  set_primary_key :id1, :id2
272
797
  unrestrict_primary_key
273
798
  many_to_many :albums, :right_key=>[:album_id1, :album_id2], :left_key=>[:tag_id1, :tag_id2]
@@ -275,14 +800,29 @@ describe "Sequel::Model Composite Key Associations" do
275
800
  @album = Album.create(:name=>'Al', :id1=>1, :id2=>2)
276
801
  @artist = Artist.create(:name=>'Ar', :id1=>3, :id2=>4)
277
802
  @tag = Tag.create(:name=>'T', :id1=>5, :id2=>6)
803
+ @same_album = lambda{Album.create(:name=>'Al', :id1=>7, :id2=>8, :artist_id1=>3, :artist_id2=>4)}
804
+ @diff_album = lambda{Album.create(:name=>'lA', :id1=>9, :id2=>10, :artist_id1=>3, :artist_id2=>4)}
805
+ @middle_album = lambda{Album.create(:name=>'Bl', :id1=>13, :id2=>14, :artist_id1=>3, :artist_id2=>4)}
806
+ @other_tags = lambda{t = [Tag.create(:name=>'U', :id1=>17, :id2=>18), Tag.create(:name=>'V', :id1=>19, :id2=>20)]; @db[:albums_tags].insert([:album_id1, :album_id2, :tag_id1, :tag_id2], Tag.select(1, 2, :id1, :id2)); t}
807
+ @pr = lambda{[Album.create(:name=>'Al2', :id1=>11, :id2=>12),Artist.create(:name=>'Ar2', :id1=>13, :id2=>14),Tag.create(:name=>'T2', :id1=>15, :id2=>16)]}
808
+ @ins = lambda{@db[:albums_tags].insert(:tag_id1=>@tag.id1, :tag_id2=>@tag.id2)}
278
809
  end
279
810
  after do
280
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
281
811
  [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
282
812
  end
813
+ after(:all) do
814
+ @db.drop_table?(:albums_tags, :tags, :albums, :artists)
815
+ end
283
816
 
284
817
  it_should_behave_like "regular and composite key associations"
285
818
 
819
+ describe "with :eager_limit_strategy=>:correlated_subquery" do
820
+ before do
821
+ @els = {:eager_limit_strategy=>:correlated_subquery}
822
+ end
823
+ it_should_behave_like "eager limit strategies"
824
+ end if INTEGRATION_DB.dataset.supports_multiple_column_in? && !Sequel.guarded?(:mysql, :db2, :oracle)
825
+
286
826
  specify "should have add method accept hashes and create new records" do
287
827
  @artist.remove_all_albums
288
828
  Album.delete
@@ -322,3 +862,101 @@ describe "Sequel::Model Composite Key Associations" do
322
862
  proc{@artist.remove_album(@album)}.should raise_error(Sequel::Error)
323
863
  end
324
864
  end
865
+
866
+ describe "Sequel::Model Associations with clashing column names" do
867
+ before(:all) do
868
+ @db = INTEGRATION_DB
869
+ @db.drop_table?(:bars_foos, :bars, :foos)
870
+ @db.create_table(:foos) do
871
+ primary_key :id
872
+ Integer :object_id
873
+ end
874
+ @db.create_table(:bars) do
875
+ primary_key :id
876
+ Integer :object_id
877
+ end
878
+ @db.create_table(:bars_foos) do
879
+ Integer :foo_id
880
+ Integer :object_id
881
+ primary_key [:foo_id, :object_id]
882
+ end
883
+ end
884
+ before do
885
+ [:bars_foos, :bars, :foos].each{|t| @db[t].delete}
886
+ @Foo = Class.new(Sequel::Model(:foos))
887
+ @Bar = Class.new(Sequel::Model(:bars))
888
+ @Foo.def_column_alias(:obj_id, :object_id)
889
+ @Bar.def_column_alias(:obj_id, :object_id)
890
+ @Foo.one_to_many :bars, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :class=>@Bar
891
+ @Foo.one_to_one :bar, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :class=>@Bar
892
+ @Bar.many_to_one :foo, :key=>:obj_id, :key_column=>:object_id, :primary_key=>:object_id, :primary_key_method=>:obj_id, :class=>@Foo
893
+ @Foo.many_to_many :mtmbars, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:foo_id, :right_key=>:object_id, :class=>@Bar
894
+ @Bar.many_to_many :mtmfoos, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:object_id, :right_key=>:foo_id, :class=>@Foo
895
+ @foo = @Foo.create(:obj_id=>2)
896
+ @bar = @Bar.create(:obj_id=>2)
897
+ @Foo.db[:bars_foos].insert(2, 2)
898
+ end
899
+ after(:all) do
900
+ @db.drop_table?(:bars_foos, :bars, :foos)
901
+ end
902
+
903
+ it "should have working regular association methods" do
904
+ @Bar.first.foo.should == @foo
905
+ @Foo.first.bars.should == [@bar]
906
+ @Foo.first.bar.should == @bar
907
+ @Foo.first.mtmbars.should == [@bar]
908
+ @Bar.first.mtmfoos.should == [@foo]
909
+ end
910
+
911
+ it "should have working eager loading methods" do
912
+ @Bar.eager(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
913
+ @Foo.eager(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
914
+ @Foo.eager(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
915
+ @Foo.eager(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
916
+ @Bar.eager(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
917
+ end
918
+
919
+ it "should have working eager graphing methods" do
920
+ @Bar.eager_graph(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
921
+ @Foo.eager_graph(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
922
+ @Foo.eager_graph(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
923
+ @Foo.eager_graph(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
924
+ @Bar.eager_graph(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
925
+ end
926
+
927
+ it "should have working modification methods" do
928
+ b = @Bar.create(:obj_id=>3)
929
+ f = @Foo.create(:obj_id=>3)
930
+
931
+ @bar.foo = f
932
+ @bar.obj_id.should == 3
933
+ @foo.bar = @bar
934
+ @bar.obj_id.should == 2
935
+
936
+ @foo.add_bar(b)
937
+ @foo.bars.sort_by{|x| x.obj_id}.should == [@bar, b]
938
+ @foo.remove_bar(b)
939
+ @foo.bars.should == [@bar]
940
+ @foo.remove_all_bars
941
+ @foo.bars.should == []
942
+
943
+ @bar.refresh.update(:obj_id=>2)
944
+ b.refresh.update(:obj_id=>3)
945
+ @foo.mtmbars.should == [@bar]
946
+ @foo.remove_all_mtmbars
947
+ @foo.mtmbars.should == []
948
+ @foo.add_mtmbar(b)
949
+ @foo.mtmbars.should == [b]
950
+ @foo.remove_mtmbar(b)
951
+ @foo.mtmbars.should == []
952
+
953
+ @bar.add_mtmfoo(f)
954
+ @bar.mtmfoos.should == [f]
955
+ @bar.remove_all_mtmfoos
956
+ @bar.mtmfoos.should == []
957
+ @bar.add_mtmfoo(f)
958
+ @bar.mtmfoos.should == [f]
959
+ @bar.remove_mtmfoo(f)
960
+ @bar.mtmfoos.should == []
961
+ end
962
+ end