sequel 5.8.0 → 5.38.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 (510) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +409 -1795
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/bin/sequel +4 -0
  6. data/doc/advanced_associations.rdoc +136 -18
  7. data/doc/association_basics.rdoc +10 -5
  8. data/doc/cheat_sheet.rdoc +1 -0
  9. data/doc/code_order.rdoc +12 -2
  10. data/doc/dataset_filtering.rdoc +17 -2
  11. data/doc/mass_assignment.rdoc +3 -3
  12. data/doc/model_dataset_method_design.rdoc +1 -1
  13. data/doc/model_plugins.rdoc +1 -1
  14. data/doc/opening_databases.rdoc +30 -8
  15. data/doc/postgresql.rdoc +107 -2
  16. data/doc/release_notes/5.10.0.txt +84 -0
  17. data/doc/release_notes/5.11.0.txt +83 -0
  18. data/doc/release_notes/5.12.0.txt +141 -0
  19. data/doc/release_notes/5.13.0.txt +27 -0
  20. data/doc/release_notes/5.14.0.txt +63 -0
  21. data/doc/release_notes/5.15.0.txt +39 -0
  22. data/doc/release_notes/5.16.0.txt +110 -0
  23. data/doc/release_notes/5.17.0.txt +31 -0
  24. data/doc/release_notes/5.18.0.txt +69 -0
  25. data/doc/release_notes/5.19.0.txt +28 -0
  26. data/doc/release_notes/5.20.0.txt +89 -0
  27. data/doc/release_notes/5.21.0.txt +87 -0
  28. data/doc/release_notes/5.22.0.txt +48 -0
  29. data/doc/release_notes/5.23.0.txt +56 -0
  30. data/doc/release_notes/5.24.0.txt +56 -0
  31. data/doc/release_notes/5.25.0.txt +32 -0
  32. data/doc/release_notes/5.26.0.txt +35 -0
  33. data/doc/release_notes/5.27.0.txt +21 -0
  34. data/doc/release_notes/5.28.0.txt +16 -0
  35. data/doc/release_notes/5.29.0.txt +22 -0
  36. data/doc/release_notes/5.30.0.txt +20 -0
  37. data/doc/release_notes/5.31.0.txt +148 -0
  38. data/doc/release_notes/5.32.0.txt +46 -0
  39. data/doc/release_notes/5.33.0.txt +24 -0
  40. data/doc/release_notes/5.34.0.txt +40 -0
  41. data/doc/release_notes/5.35.0.txt +56 -0
  42. data/doc/release_notes/5.36.0.txt +60 -0
  43. data/doc/release_notes/5.37.0.txt +30 -0
  44. data/doc/release_notes/5.38.0.txt +28 -0
  45. data/doc/release_notes/5.9.0.txt +99 -0
  46. data/doc/security.rdoc +10 -0
  47. data/doc/sharding.rdoc +42 -28
  48. data/doc/sql.rdoc +12 -0
  49. data/doc/testing.rdoc +24 -17
  50. data/doc/transactions.rdoc +78 -0
  51. data/doc/validations.rdoc +2 -2
  52. data/lib/sequel/adapters/ado.rb +26 -18
  53. data/lib/sequel/adapters/ado/access.rb +2 -2
  54. data/lib/sequel/adapters/ado/mssql.rb +5 -8
  55. data/lib/sequel/adapters/amalgalite.rb +1 -1
  56. data/lib/sequel/adapters/jdbc.rb +71 -27
  57. data/lib/sequel/adapters/jdbc/mysql.rb +6 -6
  58. data/lib/sequel/adapters/jdbc/oracle.rb +7 -6
  59. data/lib/sequel/adapters/jdbc/postgresql.rb +17 -28
  60. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +5 -6
  61. data/lib/sequel/adapters/jdbc/sqlite.rb +33 -2
  62. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -3
  63. data/lib/sequel/adapters/jdbc/transactions.rb +14 -28
  64. data/lib/sequel/adapters/mysql.rb +14 -15
  65. data/lib/sequel/adapters/mysql2.rb +5 -3
  66. data/lib/sequel/adapters/odbc.rb +4 -6
  67. data/lib/sequel/adapters/oracle.rb +7 -7
  68. data/lib/sequel/adapters/postgres.rb +52 -16
  69. data/lib/sequel/adapters/shared/access.rb +16 -12
  70. data/lib/sequel/adapters/shared/db2.rb +5 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +41 -18
  72. data/lib/sequel/adapters/shared/mysql.rb +66 -19
  73. data/lib/sequel/adapters/shared/oracle.rb +29 -23
  74. data/lib/sequel/adapters/shared/postgres.rb +341 -95
  75. data/lib/sequel/adapters/shared/sqlanywhere.rb +23 -10
  76. data/lib/sequel/adapters/shared/sqlite.rb +174 -21
  77. data/lib/sequel/adapters/sqlanywhere.rb +33 -17
  78. data/lib/sequel/adapters/sqlite.rb +78 -68
  79. data/lib/sequel/adapters/tinytds.rb +14 -6
  80. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +2 -5
  81. data/lib/sequel/adapters/utils/mysql_mysql2.rb +5 -1
  82. data/lib/sequel/connection_pool.rb +2 -6
  83. data/lib/sequel/connection_pool/sharded_single.rb +7 -4
  84. data/lib/sequel/connection_pool/sharded_threaded.rb +32 -21
  85. data/lib/sequel/connection_pool/single.rb +1 -1
  86. data/lib/sequel/connection_pool/threaded.rb +26 -11
  87. data/lib/sequel/core.rb +327 -319
  88. data/lib/sequel/database/connecting.rb +7 -8
  89. data/lib/sequel/database/logging.rb +7 -1
  90. data/lib/sequel/database/misc.rb +68 -34
  91. data/lib/sequel/database/query.rb +6 -4
  92. data/lib/sequel/database/schema_generator.rb +31 -11
  93. data/lib/sequel/database/schema_methods.rb +32 -22
  94. data/lib/sequel/database/transactions.rb +129 -25
  95. data/lib/sequel/dataset.rb +4 -2
  96. data/lib/sequel/dataset/actions.rb +34 -23
  97. data/lib/sequel/dataset/features.rb +34 -0
  98. data/lib/sequel/dataset/graph.rb +27 -11
  99. data/lib/sequel/dataset/misc.rb +17 -3
  100. data/lib/sequel/dataset/placeholder_literalizer.rb +50 -21
  101. data/lib/sequel/dataset/prepared_statements.rb +96 -26
  102. data/lib/sequel/dataset/query.rb +43 -8
  103. data/lib/sequel/dataset/sql.rb +189 -41
  104. data/lib/sequel/deprecated.rb +3 -1
  105. data/lib/sequel/exceptions.rb +2 -0
  106. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  107. data/lib/sequel/extensions/any_not_empty.rb +45 -0
  108. data/lib/sequel/extensions/caller_logging.rb +79 -0
  109. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  110. data/lib/sequel/extensions/connection_expiration.rb +6 -6
  111. data/lib/sequel/extensions/connection_validator.rb +7 -6
  112. data/lib/sequel/extensions/constant_sql_override.rb +65 -0
  113. data/lib/sequel/extensions/constraint_validations.rb +53 -28
  114. data/lib/sequel/extensions/core_refinements.rb +2 -0
  115. data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
  116. data/lib/sequel/extensions/escaped_like.rb +100 -0
  117. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  118. data/lib/sequel/extensions/exclude_or_null.rb +68 -0
  119. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  120. data/lib/sequel/extensions/index_caching.rb +9 -7
  121. data/lib/sequel/extensions/integer64.rb +3 -1
  122. data/lib/sequel/extensions/looser_typecasting.rb +3 -3
  123. data/lib/sequel/extensions/migration.rb +13 -6
  124. data/lib/sequel/extensions/named_timezones.rb +84 -23
  125. data/lib/sequel/extensions/pg_array.rb +87 -79
  126. data/lib/sequel/extensions/pg_array_ops.rb +14 -6
  127. data/lib/sequel/extensions/pg_enum.rb +34 -18
  128. data/lib/sequel/extensions/pg_extended_date_support.rb +34 -14
  129. data/lib/sequel/extensions/pg_hstore.rb +6 -0
  130. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  131. data/lib/sequel/extensions/pg_inet.rb +15 -5
  132. data/lib/sequel/extensions/pg_interval.rb +2 -0
  133. data/lib/sequel/extensions/pg_json.rb +387 -123
  134. data/lib/sequel/extensions/pg_json_ops.rb +168 -0
  135. data/lib/sequel/extensions/pg_range.rb +20 -10
  136. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  137. data/lib/sequel/extensions/pg_row.rb +3 -2
  138. data/lib/sequel/extensions/pg_row_ops.rb +24 -0
  139. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
  140. data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
  141. data/lib/sequel/extensions/query.rb +1 -0
  142. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  143. data/lib/sequel/extensions/s.rb +2 -0
  144. data/lib/sequel/extensions/schema_dumper.rb +13 -7
  145. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +4 -2
  146. data/lib/sequel/extensions/server_block.rb +18 -7
  147. data/lib/sequel/extensions/sql_comments.rb +2 -2
  148. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  149. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  150. data/lib/sequel/extensions/to_dot.rb +9 -3
  151. data/lib/sequel/model.rb +3 -1
  152. data/lib/sequel/model/associations.rb +403 -69
  153. data/lib/sequel/model/base.rb +170 -90
  154. data/lib/sequel/model/plugins.rb +105 -0
  155. data/lib/sequel/plugins/after_initialize.rb +1 -1
  156. data/lib/sequel/plugins/association_dependencies.rb +3 -3
  157. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  158. data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
  159. data/lib/sequel/plugins/association_pks.rb +74 -22
  160. data/lib/sequel/plugins/association_proxies.rb +6 -2
  161. data/lib/sequel/plugins/auto_validations.rb +36 -17
  162. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  163. data/lib/sequel/plugins/boolean_subsets.rb +4 -1
  164. data/lib/sequel/plugins/caching.rb +3 -0
  165. data/lib/sequel/plugins/class_table_inheritance.rb +62 -34
  166. data/lib/sequel/plugins/composition.rb +13 -9
  167. data/lib/sequel/plugins/csv_serializer.rb +28 -9
  168. data/lib/sequel/plugins/defaults_setter.rb +2 -2
  169. data/lib/sequel/plugins/dirty.rb +60 -22
  170. data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
  171. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  172. data/lib/sequel/plugins/finder.rb +2 -2
  173. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  174. data/lib/sequel/plugins/hook_class_methods.rb +17 -5
  175. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  176. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  177. data/lib/sequel/plugins/inverted_subsets.rb +2 -2
  178. data/lib/sequel/plugins/json_serializer.rb +21 -14
  179. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  180. data/lib/sequel/plugins/list.rb +22 -10
  181. data/lib/sequel/plugins/many_through_many.rb +1 -1
  182. data/lib/sequel/plugins/nested_attributes.rb +27 -5
  183. data/lib/sequel/plugins/pg_array_associations.rb +12 -9
  184. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +149 -61
  185. data/lib/sequel/plugins/prepared_statements.rb +6 -12
  186. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  187. data/lib/sequel/plugins/rcte_tree.rb +20 -22
  188. data/lib/sequel/plugins/sharding.rb +13 -7
  189. data/lib/sequel/plugins/single_table_inheritance.rb +20 -15
  190. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  191. data/lib/sequel/plugins/static_cache.rb +36 -17
  192. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  193. data/lib/sequel/plugins/string_stripper.rb +1 -1
  194. data/lib/sequel/plugins/subclasses.rb +2 -0
  195. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  196. data/lib/sequel/plugins/tactical_eager_loading.rb +73 -2
  197. data/lib/sequel/plugins/throw_failures.rb +110 -0
  198. data/lib/sequel/plugins/tree.rb +49 -31
  199. data/lib/sequel/plugins/typecast_on_load.rb +3 -2
  200. data/lib/sequel/plugins/validation_class_methods.rb +11 -5
  201. data/lib/sequel/plugins/validation_helpers.rb +2 -2
  202. data/lib/sequel/sql.rb +120 -30
  203. data/lib/sequel/timezones.rb +55 -14
  204. data/lib/sequel/version.rb +6 -1
  205. metadata +101 -361
  206. data/Rakefile +0 -151
  207. data/doc/release_notes/4.0.0.txt +0 -262
  208. data/doc/release_notes/4.1.0.txt +0 -85
  209. data/doc/release_notes/4.10.0.txt +0 -226
  210. data/doc/release_notes/4.11.0.txt +0 -147
  211. data/doc/release_notes/4.12.0.txt +0 -105
  212. data/doc/release_notes/4.13.0.txt +0 -169
  213. data/doc/release_notes/4.14.0.txt +0 -68
  214. data/doc/release_notes/4.15.0.txt +0 -56
  215. data/doc/release_notes/4.16.0.txt +0 -36
  216. data/doc/release_notes/4.17.0.txt +0 -38
  217. data/doc/release_notes/4.18.0.txt +0 -36
  218. data/doc/release_notes/4.19.0.txt +0 -45
  219. data/doc/release_notes/4.2.0.txt +0 -129
  220. data/doc/release_notes/4.20.0.txt +0 -79
  221. data/doc/release_notes/4.21.0.txt +0 -94
  222. data/doc/release_notes/4.22.0.txt +0 -72
  223. data/doc/release_notes/4.23.0.txt +0 -65
  224. data/doc/release_notes/4.24.0.txt +0 -99
  225. data/doc/release_notes/4.25.0.txt +0 -181
  226. data/doc/release_notes/4.26.0.txt +0 -44
  227. data/doc/release_notes/4.27.0.txt +0 -78
  228. data/doc/release_notes/4.28.0.txt +0 -57
  229. data/doc/release_notes/4.29.0.txt +0 -41
  230. data/doc/release_notes/4.3.0.txt +0 -40
  231. data/doc/release_notes/4.30.0.txt +0 -37
  232. data/doc/release_notes/4.31.0.txt +0 -57
  233. data/doc/release_notes/4.32.0.txt +0 -132
  234. data/doc/release_notes/4.33.0.txt +0 -88
  235. data/doc/release_notes/4.34.0.txt +0 -86
  236. data/doc/release_notes/4.35.0.txt +0 -130
  237. data/doc/release_notes/4.36.0.txt +0 -116
  238. data/doc/release_notes/4.37.0.txt +0 -50
  239. data/doc/release_notes/4.38.0.txt +0 -67
  240. data/doc/release_notes/4.39.0.txt +0 -127
  241. data/doc/release_notes/4.4.0.txt +0 -92
  242. data/doc/release_notes/4.40.0.txt +0 -179
  243. data/doc/release_notes/4.41.0.txt +0 -77
  244. data/doc/release_notes/4.42.0.txt +0 -221
  245. data/doc/release_notes/4.43.0.txt +0 -87
  246. data/doc/release_notes/4.44.0.txt +0 -125
  247. data/doc/release_notes/4.45.0.txt +0 -370
  248. data/doc/release_notes/4.46.0.txt +0 -404
  249. data/doc/release_notes/4.47.0.txt +0 -56
  250. data/doc/release_notes/4.48.0.txt +0 -293
  251. data/doc/release_notes/4.49.0.txt +0 -222
  252. data/doc/release_notes/4.5.0.txt +0 -34
  253. data/doc/release_notes/4.6.0.txt +0 -30
  254. data/doc/release_notes/4.7.0.txt +0 -103
  255. data/doc/release_notes/4.8.0.txt +0 -175
  256. data/doc/release_notes/4.9.0.txt +0 -190
  257. data/spec/adapter_spec.rb +0 -4
  258. data/spec/adapters/db2_spec.rb +0 -170
  259. data/spec/adapters/mssql_spec.rb +0 -804
  260. data/spec/adapters/mysql_spec.rb +0 -1041
  261. data/spec/adapters/oracle_spec.rb +0 -327
  262. data/spec/adapters/postgres_spec.rb +0 -4000
  263. data/spec/adapters/spec_helper.rb +0 -43
  264. data/spec/adapters/sqlanywhere_spec.rb +0 -97
  265. data/spec/adapters/sqlite_spec.rb +0 -600
  266. data/spec/bin_spec.rb +0 -269
  267. data/spec/core/connection_pool_spec.rb +0 -1228
  268. data/spec/core/database_spec.rb +0 -2673
  269. data/spec/core/dataset_spec.rb +0 -5419
  270. data/spec/core/deprecated_spec.rb +0 -70
  271. data/spec/core/expression_filters_spec.rb +0 -1344
  272. data/spec/core/mock_adapter_spec.rb +0 -722
  273. data/spec/core/object_graph_spec.rb +0 -306
  274. data/spec/core/placeholder_literalizer_spec.rb +0 -166
  275. data/spec/core/schema_generator_spec.rb +0 -214
  276. data/spec/core/schema_spec.rb +0 -1820
  277. data/spec/core/spec_helper.rb +0 -23
  278. data/spec/core/version_spec.rb +0 -7
  279. data/spec/core_extensions_spec.rb +0 -762
  280. data/spec/core_model_spec.rb +0 -2
  281. data/spec/core_spec.rb +0 -1
  282. data/spec/deprecation_helper.rb +0 -30
  283. data/spec/extensions/accessed_columns_spec.rb +0 -51
  284. data/spec/extensions/active_model_spec.rb +0 -99
  285. data/spec/extensions/after_initialize_spec.rb +0 -24
  286. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  287. data/spec/extensions/association_dependencies_spec.rb +0 -125
  288. data/spec/extensions/association_pks_spec.rb +0 -423
  289. data/spec/extensions/association_proxies_spec.rb +0 -100
  290. data/spec/extensions/auto_literal_strings_spec.rb +0 -205
  291. data/spec/extensions/auto_validations_spec.rb +0 -202
  292. data/spec/extensions/blacklist_security_spec.rb +0 -95
  293. data/spec/extensions/blank_spec.rb +0 -69
  294. data/spec/extensions/boolean_readers_spec.rb +0 -93
  295. data/spec/extensions/boolean_subsets_spec.rb +0 -47
  296. data/spec/extensions/caching_spec.rb +0 -273
  297. data/spec/extensions/class_table_inheritance_spec.rb +0 -568
  298. data/spec/extensions/column_conflicts_spec.rb +0 -75
  299. data/spec/extensions/column_select_spec.rb +0 -129
  300. data/spec/extensions/columns_introspection_spec.rb +0 -90
  301. data/spec/extensions/columns_updated_spec.rb +0 -35
  302. data/spec/extensions/composition_spec.rb +0 -248
  303. data/spec/extensions/connection_expiration_spec.rb +0 -133
  304. data/spec/extensions/connection_validator_spec.rb +0 -127
  305. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -300
  306. data/spec/extensions/constraint_validations_spec.rb +0 -395
  307. data/spec/extensions/core_refinements_spec.rb +0 -528
  308. data/spec/extensions/csv_serializer_spec.rb +0 -183
  309. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  310. data/spec/extensions/dataset_associations_spec.rb +0 -365
  311. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  312. data/spec/extensions/date_arithmetic_spec.rb +0 -181
  313. data/spec/extensions/datetime_parse_to_time_spec.rb +0 -169
  314. data/spec/extensions/def_dataset_method_spec.rb +0 -100
  315. data/spec/extensions/defaults_setter_spec.rb +0 -141
  316. data/spec/extensions/delay_add_association_spec.rb +0 -73
  317. data/spec/extensions/dirty_spec.rb +0 -189
  318. data/spec/extensions/duplicate_columns_handler_spec.rb +0 -104
  319. data/spec/extensions/eager_each_spec.rb +0 -62
  320. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  321. data/spec/extensions/error_splitter_spec.rb +0 -18
  322. data/spec/extensions/error_sql_spec.rb +0 -20
  323. data/spec/extensions/eval_inspect_spec.rb +0 -74
  324. data/spec/extensions/finder_spec.rb +0 -260
  325. data/spec/extensions/force_encoding_spec.rb +0 -126
  326. data/spec/extensions/freeze_datasets_spec.rb +0 -31
  327. data/spec/extensions/graph_each_spec.rb +0 -113
  328. data/spec/extensions/hook_class_methods_spec.rb +0 -380
  329. data/spec/extensions/identifier_mangling_spec.rb +0 -201
  330. data/spec/extensions/implicit_subquery_spec.rb +0 -58
  331. data/spec/extensions/index_caching_spec.rb +0 -66
  332. data/spec/extensions/inflector_spec.rb +0 -183
  333. data/spec/extensions/input_transformer_spec.rb +0 -69
  334. data/spec/extensions/insert_returning_select_spec.rb +0 -72
  335. data/spec/extensions/instance_filters_spec.rb +0 -79
  336. data/spec/extensions/instance_hooks_spec.rb +0 -246
  337. data/spec/extensions/integer64_spec.rb +0 -22
  338. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  339. data/spec/extensions/json_serializer_spec.rb +0 -336
  340. data/spec/extensions/lazy_attributes_spec.rb +0 -183
  341. data/spec/extensions/list_spec.rb +0 -275
  342. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  343. data/spec/extensions/many_through_many_spec.rb +0 -2177
  344. data/spec/extensions/migration_spec.rb +0 -840
  345. data/spec/extensions/modification_detection_spec.rb +0 -93
  346. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -92
  347. data/spec/extensions/named_timezones_spec.rb +0 -109
  348. data/spec/extensions/nested_attributes_spec.rb +0 -703
  349. data/spec/extensions/null_dataset_spec.rb +0 -85
  350. data/spec/extensions/optimistic_locking_spec.rb +0 -127
  351. data/spec/extensions/pagination_spec.rb +0 -116
  352. data/spec/extensions/pg_array_associations_spec.rb +0 -802
  353. data/spec/extensions/pg_array_ops_spec.rb +0 -144
  354. data/spec/extensions/pg_array_spec.rb +0 -398
  355. data/spec/extensions/pg_auto_constraint_validations_spec.rb +0 -165
  356. data/spec/extensions/pg_enum_spec.rb +0 -113
  357. data/spec/extensions/pg_extended_date_support_spec.rb +0 -126
  358. data/spec/extensions/pg_hstore_ops_spec.rb +0 -238
  359. data/spec/extensions/pg_hstore_spec.rb +0 -219
  360. data/spec/extensions/pg_inet_ops_spec.rb +0 -102
  361. data/spec/extensions/pg_inet_spec.rb +0 -72
  362. data/spec/extensions/pg_interval_spec.rb +0 -103
  363. data/spec/extensions/pg_json_ops_spec.rb +0 -289
  364. data/spec/extensions/pg_json_spec.rb +0 -262
  365. data/spec/extensions/pg_loose_count_spec.rb +0 -23
  366. data/spec/extensions/pg_range_ops_spec.rb +0 -60
  367. data/spec/extensions/pg_range_spec.rb +0 -487
  368. data/spec/extensions/pg_row_ops_spec.rb +0 -61
  369. data/spec/extensions/pg_row_plugin_spec.rb +0 -60
  370. data/spec/extensions/pg_row_spec.rb +0 -363
  371. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -93
  372. data/spec/extensions/pg_timestamptz_spec.rb +0 -17
  373. data/spec/extensions/prepared_statements_safe_spec.rb +0 -66
  374. data/spec/extensions/prepared_statements_spec.rb +0 -182
  375. data/spec/extensions/pretty_table_spec.rb +0 -123
  376. data/spec/extensions/query_spec.rb +0 -94
  377. data/spec/extensions/rcte_tree_spec.rb +0 -381
  378. data/spec/extensions/round_timestamps_spec.rb +0 -39
  379. data/spec/extensions/s_spec.rb +0 -60
  380. data/spec/extensions/schema_caching_spec.rb +0 -64
  381. data/spec/extensions/schema_dumper_spec.rb +0 -868
  382. data/spec/extensions/select_remove_spec.rb +0 -38
  383. data/spec/extensions/sequel_4_dataset_methods_spec.rb +0 -121
  384. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  385. data/spec/extensions/serialization_spec.rb +0 -365
  386. data/spec/extensions/server_block_spec.rb +0 -97
  387. data/spec/extensions/server_logging_spec.rb +0 -45
  388. data/spec/extensions/sharding_spec.rb +0 -189
  389. data/spec/extensions/shared_caching_spec.rb +0 -151
  390. data/spec/extensions/single_table_inheritance_spec.rb +0 -347
  391. data/spec/extensions/singular_table_names_spec.rb +0 -22
  392. data/spec/extensions/skip_create_refresh_spec.rb +0 -18
  393. data/spec/extensions/spec_helper.rb +0 -61
  394. data/spec/extensions/split_array_nil_spec.rb +0 -24
  395. data/spec/extensions/split_values_spec.rb +0 -57
  396. data/spec/extensions/sql_comments_spec.rb +0 -33
  397. data/spec/extensions/sql_expr_spec.rb +0 -59
  398. data/spec/extensions/static_cache_spec.rb +0 -410
  399. data/spec/extensions/string_agg_spec.rb +0 -90
  400. data/spec/extensions/string_date_time_spec.rb +0 -95
  401. data/spec/extensions/string_stripper_spec.rb +0 -68
  402. data/spec/extensions/subclasses_spec.rb +0 -79
  403. data/spec/extensions/subset_conditions_spec.rb +0 -38
  404. data/spec/extensions/symbol_aref_refinement_spec.rb +0 -28
  405. data/spec/extensions/symbol_as_refinement_spec.rb +0 -21
  406. data/spec/extensions/synchronize_sql_spec.rb +0 -124
  407. data/spec/extensions/table_select_spec.rb +0 -83
  408. data/spec/extensions/tactical_eager_loading_spec.rb +0 -141
  409. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  410. data/spec/extensions/timestamps_spec.rb +0 -209
  411. data/spec/extensions/to_dot_spec.rb +0 -153
  412. data/spec/extensions/touch_spec.rb +0 -226
  413. data/spec/extensions/tree_spec.rb +0 -284
  414. data/spec/extensions/typecast_on_load_spec.rb +0 -86
  415. data/spec/extensions/unlimited_update_spec.rb +0 -21
  416. data/spec/extensions/update_or_create_spec.rb +0 -83
  417. data/spec/extensions/update_primary_key_spec.rb +0 -105
  418. data/spec/extensions/update_refresh_spec.rb +0 -59
  419. data/spec/extensions/uuid_spec.rb +0 -101
  420. data/spec/extensions/validate_associated_spec.rb +0 -52
  421. data/spec/extensions/validation_class_methods_spec.rb +0 -1040
  422. data/spec/extensions/validation_contexts_spec.rb +0 -31
  423. data/spec/extensions/validation_helpers_spec.rb +0 -525
  424. data/spec/extensions/whitelist_security_spec.rb +0 -157
  425. data/spec/extensions/xml_serializer_spec.rb +0 -213
  426. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  427. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  428. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  429. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  430. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  431. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  432. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  433. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  434. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  435. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  436. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  437. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  438. data/spec/files/double_migration/001_create_sessions.rb +0 -9
  439. data/spec/files/double_migration/002_create_nodes.rb +0 -19
  440. data/spec/files/double_migration/003_3_create_users.rb +0 -4
  441. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  442. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  443. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  444. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  445. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  446. data/spec/files/empty_migration/001_create_sessions.rb +0 -9
  447. data/spec/files/empty_migration/002_create_nodes.rb +0 -0
  448. data/spec/files/empty_migration/003_3_create_users.rb +0 -4
  449. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  450. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  451. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  452. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  453. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  454. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  455. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  456. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  457. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  458. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  459. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  460. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  461. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  462. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  463. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  464. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  465. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  466. data/spec/files/reversible_migrations/006_reversible.rb +0 -10
  467. data/spec/files/reversible_migrations/007_reversible.rb +0 -10
  468. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  469. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  470. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  471. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  472. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  473. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  474. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  475. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  476. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  477. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  478. data/spec/guards_helper.rb +0 -58
  479. data/spec/integration/associations_test.rb +0 -2513
  480. data/spec/integration/database_test.rb +0 -113
  481. data/spec/integration/dataset_test.rb +0 -1880
  482. data/spec/integration/eager_loader_test.rb +0 -687
  483. data/spec/integration/migrator_test.rb +0 -262
  484. data/spec/integration/model_test.rb +0 -203
  485. data/spec/integration/plugin_test.rb +0 -2302
  486. data/spec/integration/prepared_statement_test.rb +0 -398
  487. data/spec/integration/schema_test.rb +0 -869
  488. data/spec/integration/spec_helper.rb +0 -64
  489. data/spec/integration/timezone_test.rb +0 -86
  490. data/spec/integration/transaction_test.rb +0 -354
  491. data/spec/integration/type_test.rb +0 -127
  492. data/spec/model/association_reflection_spec.rb +0 -803
  493. data/spec/model/associations_spec.rb +0 -4538
  494. data/spec/model/base_spec.rb +0 -817
  495. data/spec/model/class_dataset_methods_spec.rb +0 -146
  496. data/spec/model/dataset_methods_spec.rb +0 -198
  497. data/spec/model/eager_loading_spec.rb +0 -2262
  498. data/spec/model/hooks_spec.rb +0 -370
  499. data/spec/model/inflector_spec.rb +0 -26
  500. data/spec/model/model_spec.rb +0 -953
  501. data/spec/model/plugins_spec.rb +0 -318
  502. data/spec/model/record_spec.rb +0 -2107
  503. data/spec/model/spec_helper.rb +0 -45
  504. data/spec/model/validations_spec.rb +0 -193
  505. data/spec/model_no_assoc_spec.rb +0 -1
  506. data/spec/model_spec.rb +0 -1
  507. data/spec/plugin_spec.rb +0 -1
  508. data/spec/sequel_coverage.rb +0 -15
  509. data/spec/sequel_warning.rb +0 -4
  510. data/spec/spec_config.rb +0 -12
@@ -1,4538 +0,0 @@
1
- require_relative "spec_helper"
2
-
3
- describe Sequel::Model, "associate" do
4
- it "should use explicit class if given a class, symbol, or string" do
5
- begin
6
- klass = Class.new(Sequel::Model(:nodes))
7
- class ::ParParent < Sequel::Model; end
8
-
9
- klass.associate :many_to_one, :par_parent0, :class=>ParParent
10
- klass.associate :one_to_many, :par_parent1s, :class=>'ParParent'
11
- klass.associate :many_to_many, :par_parent2s, :class=>:ParParent
12
-
13
- klass.association_reflection(:"par_parent0").associated_class.must_equal ParParent
14
- klass.association_reflection(:"par_parent1s").associated_class.must_equal ParParent
15
- klass.association_reflection(:"par_parent2s").associated_class.must_equal ParParent
16
- ensure
17
- Object.send(:remove_const, :ParParent)
18
- end
19
- end
20
-
21
- it "should default to associating to other models in the same scope" do
22
- begin
23
- class ::AssociationModuleTest
24
- class Album < Sequel::Model
25
- many_to_one :artist
26
- many_to_many :tags
27
- end
28
- class Artist< Sequel::Model
29
- one_to_many :albums
30
- end
31
- class Tag < Sequel::Model
32
- many_to_many :albums
33
- end
34
- end
35
-
36
- ::AssociationModuleTest::Album.association_reflection(:artist).associated_class.must_equal ::AssociationModuleTest::Artist
37
- ::AssociationModuleTest::Album.association_reflection(:tags).associated_class.must_equal ::AssociationModuleTest::Tag
38
- ::AssociationModuleTest::Artist.association_reflection(:albums).associated_class.must_equal ::AssociationModuleTest::Album
39
- ::AssociationModuleTest::Tag.association_reflection(:albums).associated_class.must_equal ::AssociationModuleTest::Album
40
- ensure
41
- Object.send(:remove_const, :AssociationModuleTest)
42
- end
43
- end
44
-
45
- it "should add a model_object and association_reflection accessors to the dataset, and return it with the current model object" do
46
- klass = Class.new(Sequel::Model(:nodes)) do
47
- columns :id, :a_id
48
- end
49
- mod = Module.new do
50
- def blah
51
- filter{|o| o.__send__(association_reflection[:key]) > model_object.id*2}
52
- end
53
- end
54
-
55
- klass.associate :many_to_one, :a, :class=>klass
56
- klass.associate :one_to_many, :bs, :key=>:b_id, :class=>klass, :extend=>mod
57
- klass.associate :many_to_many, :cs, :class=>klass
58
-
59
- node = klass.load(:id=>1)
60
- node.a_dataset.model_object.must_equal node
61
- node.bs_dataset.model_object.must_equal node
62
- node.cs_dataset.model_object.must_equal node
63
-
64
- node.a_dataset.association_reflection.must_equal klass.association_reflection(:a)
65
- node.bs_dataset.association_reflection.must_equal klass.association_reflection(:bs)
66
- node.cs_dataset.association_reflection.must_equal klass.association_reflection(:cs)
67
-
68
- node.bs_dataset.blah.sql.must_equal 'SELECT * FROM nodes WHERE ((nodes.b_id = 1) AND (b_id > 2))'
69
- end
70
-
71
- it "should allow extending the dataset with :extend option" do
72
- klass = Class.new(Sequel::Model(:nodes)) do
73
- columns :id, :a_id
74
- end
75
- mod = Module.new do
76
- def blah
77
- 1
78
- end
79
- end
80
- mod2 = Module.new do
81
- def blar
82
- 2
83
- end
84
- end
85
-
86
- klass.associate :many_to_one, :a, :class=>klass, :extend=>mod
87
- klass.associate :one_to_many, :bs, :class=>klass, :extend=>[mod]
88
- klass.associate :many_to_many, :cs, :class=>klass, :extend=>[mod, mod2]
89
-
90
- node = klass.load(:id=>1)
91
- node.a_dataset.blah.must_equal 1
92
- node.bs_dataset.blah.must_equal 1
93
- node.cs_dataset.blah.must_equal 1
94
- node.cs_dataset.blar.must_equal 2
95
- end
96
-
97
- it "should clone an existing association with the :clone option" do
98
- begin
99
- class ::ParParent < Sequel::Model; end
100
- klass = Class.new(Sequel::Model(:nodes))
101
-
102
- klass.many_to_one(:par_parent, :order=>:a){1}
103
- klass.one_to_many(:par_parent1s, :class=>'ParParent', :limit=>12){4}
104
- klass.many_to_many(:par_parent2s, :class=>:ParParent, :uniq=>true){2}
105
-
106
- klass.many_to_one :par, :clone=>:par_parent, :select=>:b
107
- klass.one_to_many :par1s, :clone=>:par_parent1s, :order=>:b, :limit=>10, :block=>nil
108
- klass.many_to_many(:par2s, :clone=>:par_parent2s, :order=>:c){3}
109
- klass.many_to_one :par3, :clone=>:par
110
-
111
- klass.association_reflection(:par).associated_class.must_equal ParParent
112
- klass.association_reflection(:par1s).associated_class.must_equal ParParent
113
- klass.association_reflection(:par2s).associated_class.must_equal ParParent
114
-
115
- klass.association_reflection(:par)[:order].must_equal :a
116
- klass.association_reflection(:par).select.must_equal :b
117
- klass.association_reflection(:par)[:block].call.must_equal 1
118
- klass.association_reflection(:par)[:eager_block].call.must_equal 1
119
- klass.association_reflection(:par1s)[:limit].must_equal 10
120
- klass.association_reflection(:par1s)[:order].must_equal :b
121
- klass.association_reflection(:par1s)[:block].must_be_nil
122
- klass.association_reflection(:par2s)[:after_load].length.must_equal 1
123
- klass.association_reflection(:par2s)[:order].must_equal :c
124
- klass.association_reflection(:par2s)[:block].call.must_equal 3
125
-
126
- klass.association_reflection(:par3)[:block].call.must_equal 1
127
- klass.association_reflection(:par3)[:eager_block].call.must_equal 1
128
- ensure
129
- Object.send(:remove_const, :ParParent)
130
- end
131
- end
132
-
133
- it "should raise an error if attempting to clone an association of differing type" do
134
- c = Class.new(Sequel::Model(:c))
135
- c.many_to_one :c
136
- proc{c.one_to_many :cs, :clone=>:c}.must_raise(Sequel::Error)
137
- end
138
-
139
- it "should allow overriding the :instance_specific option" do
140
- c = Class.new(Sequel::Model(:c))
141
- c.many_to_one :c, :instance_specific=>true
142
- c.association_reflection(:c)[:instance_specific].must_equal true
143
- c.many_to_one :c, :instance_specific=>false do |ds| ds end
144
- c.association_reflection(:c)[:instance_specific].must_equal false
145
- end
146
-
147
- it "should allow cloning of one_to_many to one_to_one associations and vice-versa" do
148
- c = Class.new(Sequel::Model(:c))
149
- c.one_to_one :c
150
- c.one_to_many :cs, :clone=>:c
151
- c.one_to_one :c2, :clone=>:cs
152
- end
153
-
154
- it "should allow cloning of many_to_many to one_through_one associations and vice-versa" do
155
- c = Class.new(Sequel::Model(:c))
156
- c.many_to_many :c
157
- c.one_through_one :cs, :clone=>:c
158
- c.many_to_many :c2, :clone=>:cs
159
- end
160
-
161
- it "should clear associations cache when refreshing object manually" do
162
- c = Class.new(Sequel::Model(:c))
163
- c.many_to_one :c
164
- o = c.new
165
- o.associations[:c] = 1
166
- o.refresh
167
- o.associations.must_equal({})
168
- end
169
-
170
- it "should not clear associations cache when refreshing object after save" do
171
- c = Class.new(Sequel::Model(:c))
172
- c.many_to_one :c
173
- o = c.new
174
- o.associations[:c] = 1
175
- o.save
176
- o.associations.must_equal(:c=>1)
177
- end
178
-
179
- it "should not clear associations cache when saving with insert_select" do
180
- ds = Sequel::Model.db[:c].with_extend do
181
- def supports_insert_select?; true end
182
- def insert_select(*) {:id=>1} end
183
- end
184
- c = Class.new(Sequel::Model(ds))
185
- c.many_to_one :c
186
- o = c.new
187
- o.associations[:c] = 1
188
- o.save
189
- o.associations.must_equal(:c=>1)
190
- end
191
-
192
- end
193
-
194
- describe Sequel::Model, "many_to_one" do
195
- before do
196
- @c2 = Class.new(Sequel::Model(:nodes)) do
197
- unrestrict_primary_key
198
- columns :id, :parent_id, :par_parent_id, :blah
199
- end
200
- @dataset = @c2.dataset
201
- DB.reset
202
- end
203
-
204
- it "should raise an error if associated class does not have a primary key, and :primary_key is not specified" do
205
- @c2.no_primary_key
206
- @c2.many_to_one :parent, :class => @c2
207
- d = @c2.new(:id => 1, :parent_id => 234)
208
- proc{d.parent}.must_raise(Sequel::Error)
209
- DB.sqls.must_equal []
210
- end
211
-
212
- it "should raise an error if associated class does not have a primary key, and :primary_key is not specified, with an association block" do
213
- @c2.no_primary_key
214
- @c2.many_to_one :parent, :class => @c2 do |ds| ds end
215
- d = @c2.new(:id => 1, :parent_id => 234)
216
- proc{d.parent}.must_raise(Sequel::Error)
217
- DB.sqls.must_equal []
218
- end
219
-
220
- it "should use implicit key if omitted" do
221
- @c2.many_to_one :parent, :class => @c2
222
-
223
- d = @c2.new(:id => 1, :parent_id => 234)
224
- p = d.parent
225
- p.class.must_equal @c2
226
- p.values.must_equal(:x => 1, :id => 1)
227
-
228
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
229
- end
230
-
231
- it "should allow association with the same name as the key if :key_column is given" do
232
- @c2.def_column_alias(:parent_id_id, :parent_id)
233
- @c2.many_to_one :parent_id, :key_column=>:parent_id, :class => @c2
234
- d = @c2.load(:id => 1, :parent_id => 234)
235
- d.parent_id_dataset.sql.must_equal "SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"
236
- d.parent_id.must_equal @c2.load(:x => 1, :id => 1)
237
- d.parent_id_id.must_equal 234
238
- d[:parent_id].must_equal 234
239
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
240
-
241
- d.parent_id_id = 3
242
- d.parent_id_id.must_equal 3
243
- d[:parent_id].must_equal 3
244
- end
245
-
246
- it "should use implicit class if omitted" do
247
- begin
248
- class ::ParParent < Sequel::Model; end
249
- @c2.many_to_one :par_parent
250
- @c2.new(:id => 1, :par_parent_id => 234).par_parent.class.must_equal ParParent
251
- DB.sqls.must_equal ["SELECT * FROM par_parents WHERE id = 234"]
252
- ensure
253
- Object.send(:remove_const, :ParParent)
254
- end
255
- end
256
-
257
- it "should use class inside module if given as a string" do
258
- begin
259
- module ::Par
260
- class Parent < Sequel::Model; end
261
- end
262
- @c2.many_to_one :par_parent, :class=>"Par::Parent"
263
- @c2.new(:id => 1, :par_parent_id => 234).par_parent.class.must_equal Par::Parent
264
- DB.sqls.must_equal ["SELECT * FROM parents WHERE id = 234"]
265
- ensure
266
- Object.send(:remove_const, :Par)
267
- end
268
- end
269
-
270
- it "should use explicit key if given" do
271
- @c2.many_to_one :parent, :class => @c2, :key => :blah
272
-
273
- d = @c2.new(:id => 1, :blah => 567)
274
- p = d.parent
275
- p.class.must_equal @c2
276
- p.values.must_equal(:x => 1, :id => 1)
277
-
278
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 567"]
279
- end
280
-
281
- it "should respect :qualify => false option" do
282
- @c2.many_to_one :parent, :class => @c2, :key => :blah, :qualify=>false
283
- @c2.new(:id => 1, :blah => 567).parent
284
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 567"]
285
- end
286
-
287
- it "should use :primary_key option if given" do
288
- @c2.many_to_one :parent, :class => @c2, :key => :blah, :primary_key => :pk
289
- @c2.new(:id => 1, :blah => 567).parent
290
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (nodes.pk = 567) LIMIT 1"]
291
- end
292
-
293
- it "should support composite keys" do
294
- @c2.many_to_one :parent, :class => @c2, :key=>[:id, :parent_id], :primary_key=>[:parent_id, :id]
295
- @c2.new(:id => 1, :parent_id => 234).parent
296
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE ((nodes.parent_id = 1) AND (nodes.id = 234)) LIMIT 1"]
297
- end
298
-
299
- it "should not issue query if not all keys have values" do
300
- @c2.many_to_one :parent, :class => @c2, :key=>[:id, :parent_id], :primary_key=>[:parent_id, :id]
301
- @c2.new(:id => 1, :parent_id => nil).parent.must_be_nil
302
- DB.sqls.must_equal []
303
- end
304
-
305
- it "should raise an Error unless same number of composite keys used" do
306
- proc{@c2.many_to_one :parent, :class => @c2, :primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
307
- proc{@c2.many_to_one :parent, :class => @c2, :key=>[:id, :parent_id], :primary_key=>:id}.must_raise(Sequel::Error)
308
- proc{@c2.many_to_one :parent, :class => @c2, :key=>:id, :primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
309
- proc{@c2.many_to_one :parent, :class => @c2, :key=>[:id, :parent_id, :blah], :primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
310
- end
311
-
312
- it "should use :select option if given" do
313
- @c2.many_to_one :parent, :class => @c2, :key => :blah, :select=>[:id, :name]
314
- @c2.new(:id => 1, :blah => 567).parent
315
- DB.sqls.must_equal ["SELECT id, name FROM nodes WHERE (nodes.id = 567) LIMIT 1"]
316
- end
317
-
318
- it "should use :conditions option if given" do
319
- @c2.many_to_one :parent, :class => @c2, :key => :blah, :conditions=>{:a=>32}
320
- @c2.new(:id => 1, :blah => 567).parent
321
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE ((a = 32) AND (nodes.id = 567)) LIMIT 1"]
322
-
323
- @c2.many_to_one :parent, :class => @c2, :key => :blah, :conditions=>:a
324
- @c2.new(:id => 1, :blah => 567).parent
325
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (a AND (nodes.id = 567)) LIMIT 1"]
326
- end
327
-
328
- it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
329
- @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{model.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
330
- ds.filter{x > 1}
331
- end
332
- @c2.load(:id => 100).child_20
333
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE ((parent_id = 100) AND (x > 1)) ORDER BY name LIMIT 1 OFFSET 20"]
334
- end
335
-
336
- it "should return nil if key value is nil" do
337
- @c2.many_to_one :parent, :class => @c2
338
- @c2.new(:id => 1).parent.must_be_nil
339
- DB.sqls.must_equal []
340
- end
341
-
342
- it "should cache negative lookup" do
343
- @c2.many_to_one :parent, :class => @c2
344
- @c2.dataset = @c2.dataset.with_fetch([])
345
- d = @c2.new(:id => 1, :parent_id=>555)
346
- DB.sqls.must_equal []
347
- d.parent.must_be_nil
348
- DB.sqls.must_equal ['SELECT * FROM nodes WHERE id = 555']
349
- d.parent.must_be_nil
350
- DB.sqls.must_equal []
351
- end
352
-
353
- it "should define a setter method" do
354
- @c2.many_to_one :parent, :class => @c2
355
-
356
- d = @c2.new(:id => 1)
357
- d.parent = @c2.new(:id => 4321)
358
- d.values.must_equal(:id => 1, :parent_id => 4321)
359
-
360
- d.parent = nil
361
- d.values.must_equal(:id => 1, :parent_id => nil)
362
-
363
- e = @c2.new(:id => 6677)
364
- d.parent = e
365
- d.values.must_equal(:id => 1, :parent_id => 6677)
366
- end
367
-
368
- it "should have the setter method respect the :primary_key option" do
369
- @c2.many_to_one :parent, :class => @c2, :primary_key=>:blah
370
-
371
- d = @c2.new(:id => 1)
372
- d.parent = @c2.new(:id => 4321, :blah=>444)
373
- d.values.must_equal(:id => 1, :parent_id => 444)
374
-
375
- d.parent = nil
376
- d.values.must_equal(:id => 1, :parent_id => nil)
377
-
378
- e = @c2.new(:id => 6677, :blah=>8)
379
- d.parent = e
380
- d.values.must_equal(:id => 1, :parent_id => 8)
381
- end
382
-
383
- it "should have the setter method respect composite keys" do
384
- @c2.many_to_one :parent, :class => @c2, :key=>[:id, :parent_id], :primary_key=>[:parent_id, :id]
385
-
386
- d = @c2.new(:id => 1, :parent_id=> 234)
387
- d.parent = @c2.new(:id => 4, :parent_id=>52)
388
- d.values.must_equal(:id => 52, :parent_id => 4)
389
-
390
- d.parent = nil
391
- d.values.must_equal(:id => nil, :parent_id => nil)
392
-
393
- e = @c2.new(:id => 6677, :parent_id=>8)
394
- d.parent = e
395
- d.values.must_equal(:id => 8, :parent_id => 6677)
396
- end
397
-
398
- it "should not persist changes until saved" do
399
- @c2.many_to_one :parent, :class => @c2
400
-
401
- d = @c2.load(:id => 1)
402
- DB.reset
403
- d.parent = @c2.new(:id => 345)
404
- DB.sqls.must_equal []
405
- d.save_changes
406
- DB.sqls.must_equal ['UPDATE nodes SET parent_id = 345 WHERE (id = 1)']
407
- end
408
-
409
- it "should populate cache when accessed" do
410
- @c2.many_to_one :parent, :class => @c2
411
-
412
- d = @c2.load(:id => 1)
413
- d.parent_id = 234
414
- d.associations[:parent].must_be_nil
415
- @c2.dataset = @c2.dataset.with_fetch(:id=>234)
416
- e = d.parent
417
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
418
- d.associations[:parent].must_equal e
419
- end
420
-
421
- it "should populate cache when assigned" do
422
- @c2.many_to_one :parent, :class => @c2
423
-
424
- d = @c2.create(:id => 1)
425
- DB.reset
426
- d.associations[:parent].must_be_nil
427
- d.parent = @c2.new(:id => 234)
428
- e = d.parent
429
- d.associations[:parent].must_equal e
430
- DB.sqls.must_equal []
431
- end
432
-
433
- it "should use cache if available" do
434
- @c2.many_to_one :parent, :class => @c2
435
-
436
- d = @c2.create(:id => 1, :parent_id => 234)
437
- DB.reset
438
- d.associations[:parent] = 42
439
- d.parent.must_equal 42
440
- DB.sqls.must_equal []
441
- end
442
-
443
- it "should not use cache if asked to reload" do
444
- @c2.many_to_one :parent, :class => @c2
445
-
446
- d = @c2.create(:id => 1)
447
- DB.reset
448
- d.parent_id = 234
449
- d.associations[:parent] = 42
450
- d.parent(:reload=>true).wont_equal 42
451
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE id = 234"]
452
- end
453
-
454
- it "should use a callback if given one as the argument" do
455
- @c2.many_to_one :parent, :class => @c2
456
-
457
- d = @c2.create(:id => 1)
458
- DB.reset
459
- d.parent_id = 234
460
- d.associations[:parent] = 42
461
- d.parent{|ds| ds.where{name > 'M'}}.wont_equal 42
462
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
463
- end
464
-
465
- it "should use a block given to the association method as a callback" do
466
- @c2.many_to_one :parent, :class => @c2
467
-
468
- d = @c2.create(:id => 1)
469
- DB.reset
470
- d.parent_id = 234
471
- d.associations[:parent] = 42
472
- d.parent{|ds| ds.filter{name > 'M'}}.wont_equal 42
473
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
474
- end
475
-
476
- it "should have the setter add to the reciprocal one_to_many cached association array if it exists" do
477
- @c2.many_to_one :parent, :class => @c2
478
- @c2.one_to_many :children, :class => @c2, :key=>:parent_id
479
- @c2.dataset = @c2.dataset.with_fetch([])
480
-
481
- d = @c2.new(:id => 1)
482
- e = @c2.new(:id => 2)
483
- DB.sqls.must_equal []
484
- d.parent = e
485
- e.children.wont_include(d)
486
- DB.sqls.must_equal ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
487
-
488
- d = @c2.new(:id => 1)
489
- e = @c2.new(:id => 2)
490
- e.children.wont_include(d)
491
- DB.sqls.must_equal ['SELECT * FROM nodes WHERE (nodes.parent_id = 2)']
492
- d.parent = e
493
- e.children.must_include(d)
494
- DB.sqls.must_equal []
495
- end
496
-
497
- it "should have setter deal with a one_to_one reciprocal" do
498
- @c2.many_to_one :parent, :class => @c2, :key=>:parent_id
499
- @c2.one_to_one :child, :class => @c2, :key=>:parent_id
500
-
501
- d = @c2.new(:id => 1)
502
- e = @c2.new(:id => 2)
503
- e.associations[:child] = nil
504
- d.parent = e
505
- e.child.must_equal d
506
- d.parent = nil
507
- e.child.must_be_nil
508
- d.parent = e
509
- e.child.must_equal d
510
-
511
- f = @c2.new(:id => 3)
512
- d.parent = nil
513
- e.child.must_be_nil
514
- e.associations[:child] = f
515
- d.parent = e
516
- e.child.must_equal d
517
- end
518
-
519
- it "should have the setter remove the object from the previous associated object's reciprocal one_to_many cached association array if it exists" do
520
- @c2.many_to_one :parent, :class => @c2
521
- @c2.one_to_many :children, :class => @c2, :key=>:parent_id
522
- @c2.dataset = @c2.dataset.with_fetch([])
523
-
524
- d = @c2.new(:id => 1)
525
- e = @c2.new(:id => 2)
526
- f = @c2.new(:id => 3)
527
- e.children.wont_include(d)
528
- f.children.wont_include(d)
529
- DB.reset
530
- d.parent = e
531
- e.children.must_include(d)
532
- d.parent = f
533
- f.children.must_include(d)
534
- e.children.wont_include(d)
535
- d.parent = nil
536
- f.children.wont_include(d)
537
- DB.sqls.must_equal []
538
- end
539
-
540
- it "should have the setter not modify the reciprocal if set to same value as current" do
541
- @c2.many_to_one :parent, :class => @c2
542
- @c2.one_to_many :children, :class => @c2, :key=>:parent_id
543
-
544
- c1 = @c2.load(:id => 1, :parent_id=>nil)
545
- c2 = @c2.load(:id => 2, :parent_id=>1)
546
- c3 = @c2.load(:id => 3, :parent_id=>1)
547
- c1.associations[:children] = [c2, c3]
548
- c2.associations[:parent] = c1
549
- c2.parent = c1
550
- c1.children.must_equal [c2, c3]
551
- DB.sqls.must_equal []
552
- end
553
-
554
- it "should get all matching records and only return the first if :key option is set to nil" do
555
- @c2.dataset = @c2.dataset.with_fetch([{:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4, :children_id=>2, :children_parent_id=>1, :children_par_parent_id=>5, :children_blah=>6}, {}])
556
- @c2.dataset.columns(:id, :parent_id, :par_parent_id, :blah)
557
- @c2.one_to_many :children, :class => @c2, :key=>:parent_id
558
- @c2.many_to_one :first_grand_parent, :class => @c2, :key=>nil, :eager_graph=>:children, :dataset=>proc{model.filter(:children_id=>parent_id)}
559
- p = @c2.new(:parent_id=>2)
560
- fgp = p.first_grand_parent
561
- DB.sqls.must_equal ["SELECT nodes.id, nodes.parent_id, nodes.par_parent_id, nodes.blah, children.id AS children_id, children.parent_id AS children_parent_id, children.par_parent_id AS children_par_parent_id, children.blah AS children_blah FROM nodes LEFT OUTER JOIN nodes AS children ON (children.parent_id = nodes.id) WHERE (children_id = 2)"]
562
- fgp.values.must_equal(:id=>1, :parent_id=>0, :par_parent_id=>3, :blah=>4)
563
- fgp.children.first.values.must_equal(:id=>2, :parent_id=>1, :par_parent_id=>5, :blah=>6)
564
- end
565
-
566
- it "should not create the setter method if :read_only option is used" do
567
- @c2.many_to_one :parent, :class => @c2, :read_only=>true
568
- @c2.instance_methods.must_include(:parent)
569
- @c2.instance_methods.wont_include(:parent=)
570
- end
571
-
572
- it "should not add associations methods directly to class" do
573
- @c2.many_to_one :parent, :class => @c2
574
- @c2.instance_methods.must_include(:parent)
575
- @c2.instance_methods.must_include(:parent=)
576
- @c2.instance_methods(false).wont_include(:parent)
577
- @c2.instance_methods(false).wont_include(:parent=)
578
- end
579
-
580
- it "should add associations methods to the :methods_module option" do
581
- m = Module.new
582
- @c2.many_to_one :parent, :class => @c2, :methods_module=>m
583
- m.instance_methods.must_include(:parent)
584
- m.instance_methods.must_include(:parent=)
585
- @c2.instance_methods.wont_include(:parent)
586
- @c2.instance_methods.wont_include(:parent=)
587
- end
588
-
589
- it "should add associations methods directly to class if :methods_module is the class itself" do
590
- @c2.many_to_one :parent, :class => @c2, :methods_module=>@c2
591
- @c2.instance_methods(false).must_include(:parent)
592
- @c2.instance_methods(false).must_include(:parent=)
593
- end
594
-
595
- it "should raise an error if trying to set a model object that doesn't have a valid primary key" do
596
- @c2.many_to_one :parent, :class => @c2
597
- p = @c2.new
598
- c = @c2.load(:id=>123)
599
- proc{c.parent = p}.must_raise(Sequel::Error)
600
- end
601
-
602
- it "should make the change to the foreign_key value inside a _association= method" do
603
- @c2.many_to_one :parent, :class => @c2
604
- @c2.private_instance_methods.must_include(:_parent=)
605
- p = @c2.new
606
- c = @c2.load(:id=>123)
607
- def p._parent=(x)
608
- @x = x
609
- end
610
- def p.parent_id=; raise; end
611
- p.parent = c
612
- p.instance_variable_get(:@x).must_equal c
613
- end
614
-
615
- it "should have the :setter option define the _association= method" do
616
- @c2.many_to_one :parent, :class => @c2, :setter=>proc{|x| @x = x}
617
- p = @c2.new
618
- c = @c2.load(:id=>123)
619
- def p.parent_id=; raise; end
620
- p.parent = c
621
- p.instance_variable_get(:@x).must_equal c
622
- end
623
-
624
- it "should support (before|after)_set callbacks" do
625
- h = []
626
- @c2.many_to_one :parent, :class => @c2, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << 3}
627
- @c2.class_eval do
628
- self::Foo = h
629
- def []=(a, v)
630
- a == :parent_id ? (model::Foo << (v ? 4 : 5)) : super
631
- end
632
- def blah(x)
633
- model::Foo << (x ? x.pk : :x)
634
- end
635
- def blahr(x)
636
- model::Foo << 6
637
- end
638
- end
639
- p = @c2.load(:id=>10)
640
- c = @c2.load(:id=>123)
641
- h.must_equal []
642
- p.parent = c
643
- h.must_equal [10, -123, 123, 4, 3]
644
- p.parent = nil
645
- h.must_equal [10, -123, 123, 4, 3, 10, :y, :x, 5, 3]
646
- end
647
-
648
- it "should support after_load association callback" do
649
- h = []
650
- @c2.many_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
651
- @c2.class_eval do
652
- self::Foo = h
653
- def al(v)
654
- model::Foo << v.pk
655
- end
656
- set_dataset dataset.with_fetch(:id=>20)
657
- end
658
- p = @c2.load(:id=>10, :parent_id=>20)
659
- parent = p.parent
660
- h.must_equal [[10, 20], 20]
661
- parent.pk.must_equal 20
662
- end
663
-
664
- it "should support after_load association callback that changes the cached object" do
665
- @c2.many_to_one :parent, :class => @c2, :after_load=>:al
666
- @c2.class_eval do
667
- def al(v)
668
- associations[:parent] = :foo
669
- end
670
- end
671
- p = @c2.load(:id=>10, :parent_id=>20)
672
- p.parent.must_equal :foo
673
- p.associations[:parent].must_equal :foo
674
- end
675
-
676
- it "should raise error and not call internal add or remove method if before callback calls cancel_action, even if raise_on_save_failure is false" do
677
- p = @c2.new
678
- c = @c2.load(:id=>123)
679
- p.raise_on_save_failure = false
680
- @c2.many_to_one :parent, :class => @c2, :before_set=>:bs
681
- def p.bs(x) cancel_action end
682
- def p._parent=; raise; end
683
- proc{p.parent = c}.must_raise(Sequel::HookFailed)
684
-
685
- p.parent.must_be_nil
686
- p.associations[:parent] = c
687
- p.parent.must_equal c
688
- proc{p.parent = nil}.must_raise(Sequel::HookFailed)
689
- end
690
-
691
- it "should raise an error if a callback is not a proc or symbol" do
692
- @c2.many_to_one :parent, :class => @c2, :before_set=>Object.new
693
- proc{@c2.new.parent = @c2.load(:id=>1)}.must_raise(Sequel::Error)
694
- end
695
-
696
- it "should have association dataset use false condition if any key is nil" do
697
- @c2.many_to_one :parent, :class => @c2
698
- @c2.load({}).parent_dataset.sql.must_equal "SELECT * FROM nodes WHERE 'f' LIMIT 1"
699
- end
700
- end
701
-
702
- describe Sequel::Model, "one_to_one" do
703
- before do
704
- @c1 = Class.new(Sequel::Model(:attributes)) do
705
- unrestrict_primary_key
706
- columns :id, :node_id, :y
707
- end
708
-
709
- @c2 = Class.new(Sequel::Model(:nodes)) do
710
- unrestrict_primary_key
711
- attr_accessor :xxx
712
-
713
- def self.name; 'Node'; end
714
- def self.to_s; 'Node'; end
715
- columns :id, :x, :parent_id, :par_parent_id, :blah, :node_id
716
- end
717
- @dataset = @c2.dataset
718
- @dataset = @dataset.with_fetch({})
719
- @c1.dataset = @c1.dataset.with_fetch({})
720
- DB.reset
721
- end
722
-
723
- it "should have the getter method return a single object" do
724
- @c2.one_to_one :attribute, :class => @c1
725
- att = @c2.new(:id => 1234).attribute
726
- DB.sqls.must_equal ['SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 1']
727
- att.must_be_kind_of(@c1)
728
- att.values.must_equal({})
729
- end
730
-
731
- it "should not add a setter method if the :read_only option is true" do
732
- @c2.one_to_one :attribute, :class => @c1, :read_only=>true
733
- im = @c2.instance_methods
734
- im.must_include(:attribute)
735
- im.wont_include(:attribute=)
736
- end
737
-
738
- it "should add a setter method" do
739
- @c2.one_to_one :attribute, :class => @c1
740
- attrib = @c1.new(:id=>3)
741
- @c1.dataset = @c1.dataset.with_fetch(:id=>3)
742
- @c2.new(:id => 1234).attribute = attrib
743
- DB.sqls.must_equal ['UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)',
744
- 'INSERT INTO attributes (id, node_id) VALUES (3, 1234)',
745
- "SELECT * FROM attributes WHERE id = 3"]
746
-
747
- @c2.new(:id => 1234).attribute.must_equal attrib
748
- attrib = @c1.load(:id=>3)
749
- @c2.new(:id => 1234).attribute = attrib
750
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 1",
751
- 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))',
752
- "UPDATE attributes SET node_id = 1234 WHERE (id = 3)"]
753
- end
754
-
755
- it "should use a transaction in the setter method" do
756
- @c2.one_to_one :attribute, :class => @c1
757
- @c2.use_transactions = true
758
- attrib = @c1.load(:id=>3)
759
- @c2.new(:id => 1234).attribute = attrib
760
- DB.sqls.must_equal ['BEGIN',
761
- 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 1234) AND (id != 3))',
762
- "UPDATE attributes SET node_id = 1234 WHERE (id = 3)",
763
- 'COMMIT']
764
- end
765
-
766
- it "should have setter method respect association filters" do
767
- @c2.one_to_one :attribute, :class => @c1, :conditions=>{:a=>1} do |ds|
768
- ds.filter(:b=>2)
769
- end
770
- attrib = @c1.load(:id=>3)
771
- @c2.new(:id => 1234).attribute = attrib
772
- DB.sqls.must_equal ['UPDATE attributes SET node_id = NULL WHERE ((a = 1) AND (node_id = 1234) AND (b = 2) AND (id != 3))',
773
- "UPDATE attributes SET node_id = 1234 WHERE (id = 3)"]
774
- end
775
-
776
- it "should have the setter method respect the :primary_key option" do
777
- @c2.one_to_one :attribute, :class => @c1, :primary_key=>:xxx
778
- attrib = @c1.new(:id=>3)
779
- @c1.dataset = @c1.dataset.with_fetch(:id=>3)
780
- @c2.new(:id => 1234, :xxx=>5).attribute = attrib
781
- DB.sqls.must_equal ['UPDATE attributes SET node_id = NULL WHERE (node_id = 5)',
782
- 'INSERT INTO attributes (id, node_id) VALUES (3, 5)',
783
- "SELECT * FROM attributes WHERE id = 3"]
784
-
785
- @c2.new(:id => 321, :xxx=>5).attribute.must_equal attrib
786
- attrib = @c1.load(:id=>3)
787
- @c2.new(:id => 621, :xxx=>5).attribute = attrib
788
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE (attributes.node_id = 5) LIMIT 1",
789
- 'UPDATE attributes SET node_id = NULL WHERE ((node_id = 5) AND (id != 3))',
790
- 'UPDATE attributes SET node_id = 5 WHERE (id = 3)']
791
- end
792
-
793
- it "should have the setter method respect composite keys" do
794
- @c2.one_to_one :attribute, :class => @c1, :key=>[:node_id, :y], :primary_key=>[:id, :x]
795
- attrib = @c1.load(:id=>3, :y=>6)
796
- @c1.dataset = @c1.dataset.with_fetch(:id=>3, :y=>6)
797
- @c2.load(:id => 1234, :x=>5).attribute = attrib
798
- DB.sqls.must_equal ["UPDATE attributes SET node_id = NULL, y = NULL WHERE ((node_id = 1234) AND (y = 5) AND (id != 3))",
799
- "UPDATE attributes SET y = 5, node_id = 1234 WHERE (id = 3)"]
800
- end
801
-
802
- it "should use implicit key if omitted" do
803
- @c2.dataset = @c2.dataset.with_fetch({})
804
- @c2.one_to_one :parent, :class => @c2
805
-
806
- d = @c2.new(:id => 234)
807
- p = d.parent
808
- p.class.must_equal @c2
809
- p.values.must_equal({})
810
-
811
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (nodes.node_id = 234) LIMIT 1"]
812
- end
813
-
814
- it "should use implicit class if omitted" do
815
- begin
816
- class ::ParParent < Sequel::Model; end
817
- @c2.one_to_one :par_parent
818
- @c2.new(:id => 234).par_parent.class.must_equal ParParent
819
- DB.sqls.must_equal ["SELECT * FROM par_parents WHERE (par_parents.node_id = 234) LIMIT 1"]
820
- ensure
821
- Object.send(:remove_const, :ParParent)
822
- end
823
- end
824
-
825
- it "should use class inside module if given as a string" do
826
- begin
827
- module ::Par
828
- class Parent < Sequel::Model; end
829
- end
830
- @c2.one_to_one :par_parent, :class=>"Par::Parent"
831
- @c2.new(:id => 234).par_parent.class.must_equal Par::Parent
832
- DB.sqls.must_equal ["SELECT * FROM parents WHERE (parents.node_id = 234) LIMIT 1"]
833
- ensure
834
- Object.send(:remove_const, :Par)
835
- end
836
- end
837
-
838
- it "should use explicit key if given" do
839
- @c2.dataset = @c2.dataset.with_fetch({})
840
- @c2.one_to_one :parent, :class => @c2, :key => :blah
841
-
842
- d = @c2.new(:id => 234)
843
- p = d.parent
844
- p.class.must_equal @c2
845
- p.values.must_equal({})
846
-
847
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (nodes.blah = 234) LIMIT 1"]
848
- end
849
-
850
- it "should use :primary_key option if given" do
851
- @c2.one_to_one :parent, :class => @c2, :key => :pk, :primary_key => :blah
852
- @c2.new(:id => 1, :blah => 567).parent
853
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (nodes.pk = 567) LIMIT 1"]
854
- end
855
-
856
- it "should support composite keys" do
857
- @c2.one_to_one :parent, :class => @c2, :primary_key=>[:id, :parent_id], :key=>[:parent_id, :id]
858
- @c2.new(:id => 1, :parent_id => 234).parent
859
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE ((nodes.parent_id = 1) AND (nodes.id = 234)) LIMIT 1"]
860
- end
861
-
862
- it "should not issue query if not all keys have values" do
863
- @c2.one_to_one :parent, :class => @c2, :key=>[:id, :parent_id], :primary_key=>[:parent_id, :id]
864
- @c2.new(:id => 1, :parent_id => nil).parent.must_be_nil
865
- DB.sqls.must_equal []
866
- end
867
-
868
- it "should raise an Error unless same number of composite keys used" do
869
- proc{@c2.one_to_one :parent, :class => @c2, :primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
870
- proc{@c2.one_to_one :parent, :class => @c2, :key=>[:id, :parent_id], :primary_key=>:id}.must_raise(Sequel::Error)
871
- proc{@c2.one_to_one :parent, :class => @c2, :key=>:id, :primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
872
- proc{@c2.one_to_one :parent, :class => @c2, :key=>[:id, :parent_id, :blah], :primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
873
- end
874
-
875
- it "should use :select option if given" do
876
- @c2.one_to_one :parent, :class => @c2, :select=>[:id, :name]
877
- @c2.new(:id => 567).parent
878
- DB.sqls.must_equal ["SELECT id, name FROM nodes WHERE (nodes.node_id = 567) LIMIT 1"]
879
- end
880
-
881
- it "should use :conditions option if given" do
882
- @c2.one_to_one :parent, :class => @c2, :conditions=>{:a=>32}
883
- @c2.new(:id => 567).parent
884
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE ((a = 32) AND (nodes.node_id = 567)) LIMIT 1"]
885
-
886
- @c2.one_to_one :parent, :class => @c2, :conditions=>:a
887
- @c2.new(:id => 567).parent
888
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (a AND (nodes.node_id = 567)) LIMIT 1"]
889
- end
890
-
891
- it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
892
- @c2.one_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{model.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
893
- ds.filter{x > 1}
894
- end
895
- @c2.load(:id => 100).child_20
896
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE ((parent_id = 100) AND (x > 1)) ORDER BY name LIMIT 1 OFFSET 20"]
897
- end
898
-
899
- it "should return nil if primary_key value is nil" do
900
- @c2.one_to_one :parent, :class => @c2, :primary_key=>:node_id
901
-
902
- @c2.new(:id => 1).parent.must_be_nil
903
- DB.sqls.must_equal []
904
- end
905
-
906
- it "should cache negative lookup" do
907
- @c2.one_to_one :parent, :class => @c2
908
- @c2.dataset = @c2.dataset.with_fetch([])
909
- d = @c2.new(:id => 555)
910
- DB.sqls.must_equal []
911
- d.parent.must_be_nil
912
- DB.sqls.must_equal ['SELECT * FROM nodes WHERE (nodes.node_id = 555) LIMIT 1']
913
- d.parent.must_be_nil
914
- DB.sqls.must_equal []
915
- end
916
-
917
- it "should have the setter method respect the :key option" do
918
- @c2.one_to_one :parent, :class => @c2, :key=>:blah
919
- d = @c2.new(:id => 3)
920
- e = @c2.new(:id => 4321, :blah=>444)
921
- @c2.dataset = @c2.dataset.with_fetch(:id => 4321, :blah => 3)
922
- d.parent = e
923
- e.values.must_equal(:id => 4321, :blah => 3)
924
- DB.sqls.must_equal ["UPDATE nodes SET blah = NULL WHERE (blah = 3)",
925
- "INSERT INTO nodes (id, blah) VALUES (4321, 3)",
926
- "SELECT * FROM nodes WHERE id = 4321"]
927
- end
928
-
929
- it "should persist changes to associated object when the setter is called" do
930
- @c2.one_to_one :parent, :class => @c2
931
- d = @c2.load(:id => 1)
932
- d.parent = @c2.load(:id => 3, :node_id=>345)
933
- DB.sqls.must_equal ["UPDATE nodes SET node_id = NULL WHERE ((node_id = 1) AND (id != 3))",
934
- "UPDATE nodes SET node_id = 1 WHERE (id = 3)"]
935
- end
936
-
937
- it "should populate cache when accessed" do
938
- @c2.one_to_one :parent, :class => @c2
939
-
940
- d = @c2.load(:id => 1)
941
- d.associations[:parent].must_be_nil
942
- @c2.dataset = @c2.dataset.with_fetch(:id=>234)
943
- e = d.parent
944
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (nodes.node_id = 1) LIMIT 1"]
945
- d.parent
946
- DB.sqls.must_equal []
947
- d.associations[:parent].must_equal e
948
- end
949
-
950
- it "should populate cache when assigned" do
951
- @c2.one_to_one :parent, :class => @c2
952
-
953
- d = @c2.load(:id => 1)
954
- d.associations[:parent].must_be_nil
955
- e = @c2.load(:id => 234)
956
- d.parent = e
957
- f = d.parent
958
- d.associations[:parent].must_equal e
959
- e.must_equal f
960
- end
961
-
962
- it "should use cache if available" do
963
- @c2.one_to_one :parent, :class => @c2
964
- d = @c2.load(:id => 1, :parent_id => 234)
965
- d.associations[:parent] = 42
966
- d.parent.must_equal 42
967
- DB.sqls.must_equal []
968
- end
969
-
970
- it "should not use cache if asked to reload" do
971
- @c2.one_to_one :parent, :class => @c2
972
- d = @c2.load(:id => 1)
973
- d.associations[:parent] = [42]
974
- d.parent(:reload=>true).wont_equal 42
975
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (nodes.node_id = 1) LIMIT 1"]
976
- end
977
-
978
- it "should have the setter set the reciprocal many_to_one cached association" do
979
- @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
980
- @c2.many_to_one :child, :class => @c2, :key=>:parent_id
981
-
982
- d = @c2.load(:id => 1)
983
- e = @c2.load(:id => 2)
984
- d.parent = e
985
- e.child.must_equal d
986
- DB.sqls.must_equal ["UPDATE nodes SET parent_id = NULL WHERE ((parent_id = 1) AND (id != 2))",
987
- "UPDATE nodes SET parent_id = 1 WHERE (id = 2)"]
988
- d.parent = nil
989
- e.child.must_be_nil
990
- DB.sqls.must_equal ["UPDATE nodes SET parent_id = NULL WHERE (parent_id = 1)"]
991
- end
992
-
993
- it "should have the setter remove the object from the previous associated object's reciprocal many_to_one cached association array if it exists" do
994
- @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
995
- @c2.many_to_one :child, :class => @c2, :key=>:parent_id
996
- @c2.dataset = @c2.dataset.with_fetch([])
997
-
998
- d = @c2.load(:id => 1)
999
- e = @c2.load(:id => 2)
1000
- f = @c2.load(:id => 3)
1001
- e.child.must_be_nil
1002
- f.child.must_be_nil
1003
- d.parent = e
1004
- e.child.must_equal d
1005
- d.parent = f
1006
- f.child.must_equal d
1007
- e.child.must_be_nil
1008
- d.parent = nil
1009
- f.child.must_be_nil
1010
- end
1011
-
1012
- it "should have the setter not modify the reciprocal if set to same value as current" do
1013
- @c2.one_to_one :parent, :class => @c2, :key=>:parent_id
1014
- @c2.many_to_one :child, :class => @c2, :key=>:parent_id
1015
-
1016
- c1 = @c2.load(:id => 1, :parent_id=>nil)
1017
- c2 = @c2.load(:id => 2, :parent_id=>1)
1018
- c1.associations[:child] = c2
1019
- c2.associations[:parent] = c1
1020
- c2.parent = c1
1021
- c1.child.must_equal c2
1022
- DB.sqls.must_equal []
1023
- end
1024
-
1025
- it "should not add associations methods directly to class" do
1026
- @c2.one_to_one :parent, :class => @c2
1027
- @c2.instance_methods.must_include(:parent)
1028
- @c2.instance_methods.must_include(:parent=)
1029
- @c2.instance_methods(false).wont_include(:parent)
1030
- @c2.instance_methods(false).wont_include(:parent=)
1031
- end
1032
-
1033
- it "should raise an error if the current model object that doesn't have a valid primary key" do
1034
- @c2.one_to_one :parent, :class => @c2
1035
- p = @c2.new
1036
- c = @c2.load(:id=>123)
1037
- proc{p.parent = c}.must_raise(Sequel::Error)
1038
- end
1039
-
1040
- it "should make the change to the foreign_key value inside a _association= method" do
1041
- @c2.one_to_one :parent, :class => @c2
1042
- @c2.private_instance_methods.must_include(:_parent=)
1043
- c = @c2.new
1044
- p = @c2.load(:id=>123)
1045
- def p._parent=(x)
1046
- @x = x
1047
- end
1048
- def p.parent_id=; raise; end
1049
- p.parent = c
1050
- p.instance_variable_get(:@x).must_equal c
1051
- end
1052
-
1053
- it "should have a :setter option define the _association= method" do
1054
- @c2.one_to_one :parent, :class => @c2, :setter=>proc{|x| @x = x}
1055
- c = @c2.new
1056
- p = @c2.load(:id=>123)
1057
- def p.parent_id=; raise; end
1058
- p.parent = c
1059
- p.instance_variable_get(:@x).must_equal c
1060
- end
1061
-
1062
- it "should support (before|after)_set callbacks" do
1063
- h = []
1064
- @c2.one_to_one :parent, :class => @c2, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << 3}
1065
- @c2.class_eval do
1066
- self::Foo = h
1067
- def blah(x)
1068
- model::Foo << (x ? x.pk : :x)
1069
- end
1070
- def blahr(x)
1071
- model::Foo << 6
1072
- end
1073
- end
1074
- p = @c2.load(:id=>10)
1075
- c = @c2.load(:id=>123)
1076
- h.must_equal []
1077
- p.parent = c
1078
- h.must_equal [10, -123, 123, 3]
1079
- p.parent = nil
1080
- h.must_equal [10, -123, 123, 3, 10, :y, :x, 3]
1081
- end
1082
-
1083
- it "should support after_load association callback" do
1084
- h = []
1085
- @c2.one_to_one :parent, :class => @c2, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
1086
- @c2.class_eval do
1087
- self::Foo = h
1088
- def al(v)
1089
- model::Foo << v.pk
1090
- end
1091
- @dataset = @dataset.with_fetch(:id=>20)
1092
- end
1093
- p = @c2.load(:id=>10)
1094
- parent = p.parent
1095
- h.must_equal [[10, 20], 20]
1096
- parent.pk.must_equal 20
1097
- end
1098
-
1099
- it "should raise error and not call internal add or remove method if before callback calls cancel_action, even if raise_on_save_failure is false" do
1100
- p = @c2.load(:id=>321)
1101
- c = @c2.load(:id=>123)
1102
- p.raise_on_save_failure = false
1103
- @c2.one_to_one :parent, :class => @c2, :before_set=>:bs
1104
- def p.bs(x) cancel_action end
1105
- def p._parent=; raise; end
1106
- proc{p.parent = c}.must_raise(Sequel::HookFailed)
1107
-
1108
- p.associations[:parent].must_be_nil
1109
- p.associations[:parent] = c
1110
- p.parent.must_equal c
1111
- proc{p.parent = nil}.must_raise(Sequel::HookFailed)
1112
- end
1113
-
1114
- it "should not validate the associated object in setter if the :validate=>false option is used" do
1115
- @c2.one_to_one :parent, :class => @c2, :validate=>false
1116
- n = @c2.new(:id => 1234)
1117
- a = @c2.new(:id => 2345)
1118
- def a.validate() errors.add(:id, 'foo') end
1119
- (n.parent = a).must_equal a
1120
- end
1121
-
1122
- it "should raise an error if a callback is not a proc or symbol" do
1123
- @c2.one_to_one :parent, :class => @c2, :before_set=>Object.new
1124
- proc{@c2.new.parent = @c2.load(:id=>1)}.must_raise(Sequel::Error)
1125
- end
1126
-
1127
- it "should work_correctly when used with associate" do
1128
- @c2.dataset = @c2.dataset.with_fetch({})
1129
- @c2.associate :one_to_one, :parent, :class => @c2
1130
- @c2.load(:id => 567).parent.must_equal @c2.load({})
1131
- DB.sqls.must_equal ["SELECT * FROM nodes WHERE (nodes.node_id = 567) LIMIT 1"]
1132
- end
1133
-
1134
- it "should have association dataset use false condition if any key is nil" do
1135
- @c2.one_to_one :parent, :class => @c2, :primary_key=>:parent_id
1136
- @c2.load(:id=>1).parent_dataset.sql.must_equal "SELECT * FROM nodes WHERE 'f' LIMIT 1"
1137
- end
1138
- end
1139
-
1140
- describe Sequel::Model, "one_to_many" do
1141
- before do
1142
- @c1 = Class.new(Sequel::Model(:attributes)) do
1143
- unrestrict_primary_key
1144
- columns :id, :node_id, :y, :z
1145
- end
1146
-
1147
- @c2 = Class.new(Sequel::Model(:nodes)) do
1148
- def _refresh(ds); end
1149
- unrestrict_primary_key
1150
- attr_accessor :xxx
1151
-
1152
- def self.name; 'Node'; end
1153
- def self.to_s; 'Node'; end
1154
- columns :id, :x
1155
- end
1156
- @dataset = @c2.dataset = @c2.dataset.with_fetch({})
1157
- @c1.dataset = @c1.dataset.with_fetch(proc{|sql| sql =~ /SELECT 1/ ? {:a=>1} : {}})
1158
- DB.reset
1159
- end
1160
-
1161
- it "should raise an error if current class does not have a primary key, and :primary_key is not specified" do
1162
- @c2.no_primary_key
1163
- proc{@c2.one_to_many :attributes, :class => @c1}.must_raise(Sequel::Error)
1164
- DB.sqls.must_equal []
1165
- end
1166
-
1167
- it "should use implicit key if omitted" do
1168
- @c2.one_to_many :attributes, :class => @c1
1169
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1170
- end
1171
-
1172
- it "should use implicit class if omitted" do
1173
- begin
1174
- class ::HistoricalValue < Sequel::Model; end
1175
- @c2.one_to_many :historical_values
1176
-
1177
- v = @c2.new(:id => 1234).historical_values_dataset
1178
- v.must_be_kind_of(Sequel::Dataset)
1179
- v.sql.must_equal 'SELECT * FROM historical_values WHERE (historical_values.node_id = 1234)'
1180
- v.model.must_equal HistoricalValue
1181
- ensure
1182
- Object.send(:remove_const, :HistoricalValue)
1183
- end
1184
- end
1185
-
1186
- it "should use class inside a module if given as a string" do
1187
- begin
1188
- module ::Historical
1189
- class Value < Sequel::Model; end
1190
- end
1191
- @c2.one_to_many :historical_values, :class=>'Historical::Value'
1192
-
1193
- v = @c2.new(:id => 1234).historical_values_dataset
1194
- v.must_be_kind_of(Sequel::Dataset)
1195
- v.sql.must_equal 'SELECT * FROM values WHERE (values.node_id = 1234)'
1196
- v.model.must_equal Historical::Value
1197
- ensure
1198
- Object.send(:remove_const, :Historical)
1199
- end
1200
- end
1201
-
1202
- it "should use a callback if given one as a block" do
1203
- @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1204
-
1205
- d = @c2.load(:id => 1234)
1206
- d.associations[:attributes] = []
1207
- d.attributes{|ds| ds.where{name > 'M'}}.wont_equal []
1208
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE ((attributes.nodeid = 1234) AND (name > 'M'))"]
1209
- end
1210
-
1211
- it "should use explicit key if given" do
1212
- @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1213
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE (attributes.nodeid = 1234)'
1214
- end
1215
-
1216
- it "should support_composite keys" do
1217
- @c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :id], :primary_key=>[:id, :x]
1218
- @c2.load(:id => 1234, :x=>234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.id = 234))'
1219
- end
1220
-
1221
- it "should not issue query if not all keys have values" do
1222
- @c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :id], :primary_key=>[:id, :x]
1223
- @c2.load(:id => 1234, :x=>nil).attributes.must_equal []
1224
- DB.sqls.must_equal []
1225
- end
1226
-
1227
- it "should raise an Error unless same number of composite keys used" do
1228
- proc{@c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :id]}.must_raise(Sequel::Error)
1229
- proc{@c2.one_to_many :attributes, :class => @c1, :primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
1230
- proc{@c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :id], :primary_key=>:id}.must_raise(Sequel::Error)
1231
- proc{@c2.one_to_many :attributes, :class => @c1, :key=>:id, :primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
1232
- proc{@c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :id, :x], :primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
1233
- end
1234
-
1235
- it "should define an add_ method that works on existing records" do
1236
- @c2.one_to_many :attributes, :class => @c1
1237
-
1238
- n = @c2.load(:id => 1234)
1239
- a = @c1.load(:id => 2345)
1240
- a.must_equal n.add_attribute(a)
1241
- a.values.must_equal(:node_id => 1234, :id => 2345)
1242
- DB.sqls.must_equal ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)']
1243
- end
1244
-
1245
- it "should define an add_ method that works on new records" do
1246
- @c2.one_to_many :attributes, :class => @c1
1247
-
1248
- n = @c2.load(:id => 1234)
1249
- a = @c1.new(:id => 234)
1250
- @c1.dataset = @c1.dataset.with_fetch(:id=>234, :node_id=>1234)
1251
- a.must_equal n.add_attribute(a)
1252
- DB.sqls.must_equal ["INSERT INTO attributes (id, node_id) VALUES (234, 1234)",
1253
- "SELECT * FROM attributes WHERE id = 234"]
1254
- a.values.must_equal(:node_id => 1234, :id => 234)
1255
- end
1256
-
1257
- it "should define a remove_ method that works on existing records" do
1258
- @c2.one_to_many :attributes, :class => @c1
1259
-
1260
- n = @c2.load(:id => 1234)
1261
- a = @c1.load(:id => 2345, :node_id => 1234)
1262
- a.must_equal n.remove_attribute(a)
1263
- a.values.must_equal(:node_id => nil, :id => 2345)
1264
- DB.sqls.must_equal ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1", 'UPDATE attributes SET node_id = NULL WHERE (id = 2345)']
1265
- end
1266
-
1267
- it "should have the remove_ method raise an error if the passed object is not already associated" do
1268
- @c2.one_to_many :attributes, :class => @c1
1269
-
1270
- n = @c2.new(:id => 1234)
1271
- a = @c1.load(:id => 2345, :node_id => 1234)
1272
- @c1.dataset = @c1.dataset.with_fetch([])
1273
- proc{n.remove_attribute(a)}.must_raise(Sequel::Error)
1274
- DB.sqls.must_equal ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (id = 2345)) LIMIT 1"]
1275
- end
1276
-
1277
- it "should accept a hash for the add_ method and create a new record" do
1278
- @c2.one_to_many :attributes, :class => @c1
1279
- n = @c2.new(:id => 1234)
1280
- DB.reset
1281
- @c1.dataset = @c1.dataset.with_fetch(:node_id => 1234, :id => 234)
1282
- n.add_attribute(:id => 234).must_equal @c1.load(:node_id => 1234, :id => 234)
1283
- DB.sqls.must_equal ["INSERT INTO attributes (id, node_id) VALUES (234, 1234)",
1284
- "SELECT * FROM attributes WHERE id = 234"]
1285
- end
1286
-
1287
- it "should accept a primary key for the add_ method" do
1288
- @c2.one_to_many :attributes, :class => @c1
1289
- n = @c2.new(:id => 1234)
1290
- @c1.dataset = @c1.dataset.with_fetch(:node_id => nil, :id => 234)
1291
- n.add_attribute(234).must_equal @c1.load(:node_id => 1234, :id => 234)
1292
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE id = 234", "UPDATE attributes SET node_id = 1234 WHERE (id = 234)"]
1293
- end
1294
-
1295
- it "should raise an error if the primary key passed to the add_ method does not match an existing record" do
1296
- @c2.one_to_many :attributes, :class => @c1
1297
- n = @c2.new(:id => 1234)
1298
- @c1.dataset = @c1.dataset.with_fetch([])
1299
- proc{n.add_attribute(234)}.must_raise(Sequel::NoMatchingRow)
1300
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE id = 234"]
1301
- end
1302
-
1303
- it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
1304
- @c2.one_to_many :attributes, :class => @c1
1305
- proc{@c2.new(:id => 1234).add_attribute(@c2.new)}.must_raise(Sequel::Error)
1306
- end
1307
-
1308
- it "should accept a primary key for the remove_ method and remove an existing record" do
1309
- @c2.one_to_many :attributes, :class => @c1
1310
- n = @c2.new(:id => 1234)
1311
- @c1.dataset = @c1.dataset.with_fetch(:id=>234, :node_id=>1234)
1312
- n.remove_attribute(234).must_equal @c1.load(:node_id => nil, :id => 234)
1313
- DB.sqls.must_equal ['SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.id = 234)) LIMIT 1',
1314
- 'UPDATE attributes SET node_id = NULL WHERE (id = 234)']
1315
- end
1316
-
1317
- it "should raise an error in the remove_ method if the passed associated object is not of the correct type" do
1318
- @c2.one_to_many :attributes, :class => @c1
1319
- proc{@c2.new(:id => 1234).remove_attribute(@c2.new)}.must_raise(Sequel::Error)
1320
- end
1321
-
1322
- it "should have add_ method respect the :primary_key option" do
1323
- @c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
1324
-
1325
- n = @c2.new(:id => 1234, :xxx=>5)
1326
- a = @c1.load(:id => 2345)
1327
- n.add_attribute(a).must_equal a
1328
- DB.sqls.must_equal ['UPDATE attributes SET node_id = 5 WHERE (id = 2345)']
1329
- end
1330
-
1331
- it "should have add_ method not add the same object to the cached association array if the object is already in the array" do
1332
- @c2.one_to_many :attributes, :class => @c1
1333
-
1334
- n = @c2.new(:id => 1234)
1335
- a = @c1.load(:id => 2345)
1336
- n.associations[:attributes] = []
1337
- a.must_equal n.add_attribute(a)
1338
- a.must_equal n.add_attribute(a)
1339
- a.values.must_equal(:node_id => 1234, :id => 2345)
1340
- n.attributes.must_equal [a]
1341
- DB.sqls.must_equal ['UPDATE attributes SET node_id = 1234 WHERE (id = 2345)'] * 2
1342
- end
1343
-
1344
- it "should have add_ method respect composite keys" do
1345
- @c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
1346
-
1347
- n = @c2.load(:id => 1234, :x=>5)
1348
- a = @c1.load(:id => 2345)
1349
- n.add_attribute(a).must_equal a
1350
- DB.sqls.must_equal ["UPDATE attributes SET node_id = 1234, y = 5 WHERE (id = 2345)"]
1351
- end
1352
-
1353
- it "should have add_ method accept a composite key" do
1354
- @c1.dataset = @c1.dataset.with_fetch(:id=>2345, :node_id=>1234, :z=>8, :y=>5)
1355
- @c1.set_primary_key [:id, :z]
1356
- @c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
1357
-
1358
- n = @c2.load(:id => 1234, :x=>5)
1359
- a = @c1.load(:id => 2345, :z => 8, :node_id => 1234, :y=>5)
1360
- n.add_attribute([2345, 8]).must_equal a
1361
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE ((id = 2345) AND (z = 8)) LIMIT 1",
1362
- "UPDATE attributes SET node_id = 1234, y = 5 WHERE ((id = 2345) AND (z = 8))"]
1363
- end
1364
-
1365
- it "should have remove_ method respect composite keys" do
1366
- @c2.one_to_many :attributes, :class => @c1, :key =>[:node_id, :y], :primary_key=>[:id, :x]
1367
-
1368
- n = @c2.load(:id => 1234, :x=>5)
1369
- a = @c1.load(:id => 2345, :node_id=>1234, :y=>5)
1370
- n.remove_attribute(a).must_equal a
1371
- DB.sqls.must_equal ["SELECT 1 AS one FROM attributes WHERE ((attributes.node_id = 1234) AND (attributes.y = 5) AND (id = 2345)) LIMIT 1",
1372
- "UPDATE attributes SET node_id = NULL, y = NULL WHERE (id = 2345)"]
1373
- end
1374
-
1375
- it "should accept a array of composite primary key values for the remove_ method and remove an existing record" do
1376
- @c1.dataset = @c1.dataset.with_fetch(:id=>234, :node_id=>123, :y=>5)
1377
- @c1.set_primary_key [:id, :y]
1378
- @c2.one_to_many :attributes, :class => @c1, :key=>:node_id, :primary_key=>:id
1379
- n = @c2.new(:id => 123)
1380
- n.remove_attribute([234, 5]).must_equal @c1.load(:node_id => nil, :y => 5, :id => 234)
1381
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE ((attributes.node_id = 123) AND (attributes.id = 234) AND (attributes.y = 5)) LIMIT 1",
1382
- "UPDATE attributes SET node_id = NULL WHERE ((id = 234) AND (y = 5))"]
1383
- end
1384
-
1385
- it "should raise an error in add_ and remove_ if the passed object returns false to save (is not valid)" do
1386
- @c2.one_to_many :attributes, :class => @c1
1387
- n = @c2.new(:id => 1234)
1388
- a = @c1.new(:id => 2345)
1389
- def a.validate() errors.add(:id, 'foo') end
1390
- proc{n.add_attribute(a)}.must_raise(Sequel::ValidationFailed)
1391
- proc{n.remove_attribute(a)}.must_raise(Sequel::ValidationFailed)
1392
- end
1393
-
1394
- it "should not validate the associated object in add_ and remove_ if the :validate=>false option is used" do
1395
- @c2.one_to_many :attributes, :class => @c1, :validate=>false
1396
- n = @c2.new(:id => 1234)
1397
- a = @c1.new(:id => 2345)
1398
- def a.validate() errors.add(:id, 'foo') end
1399
- n.add_attribute(a).must_equal a
1400
- n.remove_attribute(a).must_equal a
1401
- end
1402
-
1403
- it "should not raise exception in add_ and remove_ if the :raise_on_save_failure=>false option is used" do
1404
- @c2.one_to_many :attributes, :class => @c1, :raise_on_save_failure=>false
1405
- n = @c2.new(:id => 1234)
1406
- a = @c1.new(:id => 2345)
1407
- def a.validate() errors.add(:id, 'foo') end
1408
- n.associations[:attributes] = []
1409
- n.add_attribute(a).must_be_nil
1410
- n.associations[:attributes].must_equal []
1411
- n.remove_attribute(a).must_be_nil
1412
- n.associations[:attributes].must_equal []
1413
- end
1414
-
1415
- it "should raise an error if the model object doesn't have a valid primary key" do
1416
- @c2.one_to_many :attributes, :class => @c1
1417
- a = @c2.new
1418
- n = @c1.load(:id=>123)
1419
- proc{a.attributes_dataset}.must_raise(Sequel::Error)
1420
- proc{a.add_attribute(n)}.must_raise(Sequel::Error)
1421
- proc{a.remove_attribute(n)}.must_raise(Sequel::Error)
1422
- proc{a.remove_all_attributes}.must_raise(Sequel::Error)
1423
- end
1424
-
1425
- it "should use :primary_key option if given" do
1426
- @c1.one_to_many :nodes, :class => @c2, :primary_key => :node_id, :key=>:id
1427
- @c1.load(:id => 1234, :node_id=>4321).nodes_dataset.sql.must_equal "SELECT * FROM nodes WHERE (nodes.id = 4321)"
1428
- end
1429
-
1430
- it "should support a select option" do
1431
- @c2.one_to_many :attributes, :class => @c1, :select => [:id, :name]
1432
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal "SELECT id, name FROM attributes WHERE (attributes.node_id = 1234)"
1433
- end
1434
-
1435
- it "should support a conditions option" do
1436
- @c2.one_to_many :attributes, :class => @c1, :conditions => {:a=>32}
1437
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal "SELECT * FROM attributes WHERE ((a = 32) AND (attributes.node_id = 1234))"
1438
- @c2.one_to_many :attributes, :class => @c1, :conditions => Sequel.~(:a)
1439
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal "SELECT * FROM attributes WHERE (NOT a AND (attributes.node_id = 1234))"
1440
- end
1441
-
1442
- it "should support an order option" do
1443
- @c2.one_to_many :attributes, :class => @c1, :order => :kind
1444
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind"
1445
- end
1446
-
1447
- it "should support an array for the order option" do
1448
- @c2.one_to_many :attributes, :class => @c1, :order => [:kind1, :kind2]
1449
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal "SELECT * FROM attributes WHERE (attributes.node_id = 1234) ORDER BY kind1, kind2"
1450
- end
1451
-
1452
- it "should have a dataset method for the associated object dataset" do
1453
- @c2.one_to_many :attributes, :class => @c1
1454
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE (attributes.node_id = 1234)'
1455
- end
1456
-
1457
- it "should accept a block" do
1458
- @c2.one_to_many :attributes, :class => @c1 do |ds|
1459
- ds.filter(:xxx => nil)
1460
- end
1461
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL))'
1462
- end
1463
-
1464
- it "should support :order option with block" do
1465
- @c2.one_to_many :attributes, :class => @c1, :order => :kind do |ds|
1466
- ds.filter(:xxx => nil)
1467
- end
1468
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx IS NULL)) ORDER BY kind'
1469
- end
1470
-
1471
- it "should have the block argument affect the _dataset method" do
1472
- @c2.one_to_many :attributes, :class => @c1 do |ds|
1473
- ds.filter(:xxx => 456)
1474
- end
1475
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE ((attributes.node_id = 1234) AND (xxx = 456))'
1476
- end
1477
-
1478
- it "should support a :dataset option that is used instead of the default" do
1479
- c1 = @c1
1480
- @c1.dataset = @c1.dataset.with_fetch({})
1481
- @c2.one_to_many :all_other_attributes, :class => @c1, :dataset=>proc{c1.exclude(:nodeid=>pk)}, :order=>:a, :limit=>10 do |ds|
1482
- ds.filter(:xxx => 5)
1483
- end
1484
- @c2.new(:id => 1234).all_other_attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10'
1485
- @c2.new(:id => 1234).all_other_attributes.must_equal [@c1.load({})]
1486
- DB.sqls.must_equal ['SELECT * FROM attributes WHERE ((nodeid != 1234) AND (xxx = 5)) ORDER BY a LIMIT 10']
1487
- end
1488
-
1489
- it "should support a :limit option" do
1490
- @c2.one_to_many :attributes, :class => @c1 , :limit=>10
1491
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 10'
1492
- @c2.one_to_many :attributes, :class => @c1 , :limit=>[10,10]
1493
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT * FROM attributes WHERE (attributes.node_id = 1234) LIMIT 10 OFFSET 10'
1494
- end
1495
-
1496
- it "should have the :eager option affect the _dataset method" do
1497
- @c2.one_to_many :attributes, :class => @c2 , :eager=>:attributes
1498
- @c2.new(:id => 1234).attributes_dataset.opts[:eager].must_equal(:attributes=>nil)
1499
- end
1500
-
1501
- it "should populate cache when accessed" do
1502
- @c2.one_to_many :attributes, :class => @c1
1503
- n = @c2.new(:id => 1234)
1504
- n.associations.include?(:attributes).must_equal false
1505
- atts = n.attributes
1506
- atts.must_equal n.associations[:attributes]
1507
- DB.sqls.must_equal ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1508
- end
1509
-
1510
- it "should use cache if available" do
1511
- @c2.one_to_many :attributes, :class => @c1
1512
- n = @c2.new(:id => 1234)
1513
- n.associations[:attributes] = 42
1514
- n.attributes.must_equal 42
1515
- DB.sqls.must_equal []
1516
- end
1517
-
1518
- it "should not use cache if asked to reload" do
1519
- @c2.one_to_many :attributes, :class => @c1
1520
- n = @c2.new(:id => 1234)
1521
- n.associations[:attributes] = 42
1522
- n.attributes(:reload=>true).wont_equal 42
1523
- DB.sqls.must_equal ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1524
- end
1525
-
1526
- it "should add item to cache if it exists when calling add_" do
1527
- @c2.one_to_many :attributes, :class => @c1
1528
- n = @c2.new(:id => 1234)
1529
- att = @c1.load(:id => 345)
1530
- a = []
1531
- n.associations[:attributes] = a
1532
- n.add_attribute(att)
1533
- a.must_equal [att]
1534
- end
1535
-
1536
- it "should set object to item's reciprocal cache when calling add_" do
1537
- @c2.one_to_many :attributes, :class => @c1
1538
- @c1.many_to_one :node, :class => @c2
1539
-
1540
- n = @c2.new(:id => 1234)
1541
- att = @c1.new(:id => 345)
1542
- n.add_attribute(att)
1543
- att.node.must_equal n
1544
- end
1545
-
1546
- it "should remove item from cache if it exists when calling remove_" do
1547
- @c2.one_to_many :attributes, :class => @c1
1548
-
1549
- n = @c2.load(:id => 1234)
1550
- att = @c1.load(:id => 345)
1551
- a = [att]
1552
- n.associations[:attributes] = a
1553
- n.remove_attribute(att)
1554
- a.must_equal []
1555
- end
1556
-
1557
- it "should remove item's reciprocal cache calling remove_" do
1558
- @c2.one_to_many :attributes, :class => @c1
1559
- @c1.many_to_one :node, :class => @c2
1560
-
1561
- n = @c2.new(:id => 1234)
1562
- att = @c1.new(:id => 345)
1563
- att.associations[:node] = n
1564
- att.node.must_equal n
1565
- n.remove_attribute(att)
1566
- att.node.must_be_nil
1567
- end
1568
-
1569
- it "should not create the add_, remove_, or remove_all_ methods if :read_only option is used" do
1570
- @c2.one_to_many :attributes, :class => @c1, :read_only=>true
1571
- im = @c2.instance_methods
1572
- im.must_include(:attributes)
1573
- im.must_include(:attributes_dataset)
1574
- im.wont_include(:add_attribute)
1575
- im.wont_include(:remove_attribute)
1576
- im.wont_include(:remove_all_attributes)
1577
- end
1578
-
1579
- it "should not add associations methods directly to class" do
1580
- @c2.one_to_many :attributes, :class => @c1
1581
- im = @c2.instance_methods
1582
- im.must_include(:attributes)
1583
- im.must_include(:attributes_dataset)
1584
- im.must_include(:add_attribute)
1585
- im.must_include(:remove_attribute)
1586
- im.must_include(:remove_all_attributes)
1587
- im2 = @c2.instance_methods(false)
1588
- im2.wont_include(:attributes)
1589
- im2.wont_include(:attributes_dataset)
1590
- im2.wont_include(:add_attribute)
1591
- im2.wont_include(:remove_attribute)
1592
- im2.wont_include(:remove_all_attributes)
1593
- end
1594
-
1595
- it "should populate the reciprocal many_to_one cache when loading the one_to_many association" do
1596
- @c2.one_to_many :attributes, :class => @c1, :key => :node_id
1597
- @c1.many_to_one :node, :class => @c2, :key => :node_id
1598
-
1599
- n = @c2.new(:id => 1234)
1600
- atts = n.attributes
1601
- DB.sqls.must_equal ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1602
- atts.must_equal [@c1.load({})]
1603
- atts.map{|a| a.node}.must_equal [n]
1604
- DB.sqls.must_equal []
1605
- end
1606
-
1607
- it "should use an explicit :reciprocal option if given" do
1608
- @c2.one_to_many :attributes, :class => @c1, :key => :node_id, :reciprocal=>:wxyz
1609
-
1610
- n = @c2.new(:id => 1234)
1611
- atts = n.attributes
1612
- DB.sqls.must_equal ['SELECT * FROM attributes WHERE (attributes.node_id = 1234)']
1613
- atts.must_equal [@c1.load({})]
1614
- atts.map{|a| a.associations[:wxyz]}.must_equal [n]
1615
- DB.sqls.must_equal []
1616
- end
1617
-
1618
- it "should have an remove_all_ method that removes all associated objects" do
1619
- @c2.one_to_many :attributes, :class => @c1
1620
- @c2.new(:id => 1234).remove_all_attributes
1621
- DB.sqls.must_equal ['UPDATE attributes SET node_id = NULL WHERE (node_id = 1234)']
1622
- end
1623
-
1624
- it "should have remove_all method respect association filters" do
1625
- @c2.one_to_many :attributes, :class => @c1, :conditions=>{:a=>1} do |ds|
1626
- ds.filter(:b=>2)
1627
- end
1628
- @c2.new(:id => 1234).remove_all_attributes
1629
- DB.sqls.must_equal ['UPDATE attributes SET node_id = NULL WHERE ((a = 1) AND (node_id = 1234) AND (b = 2))']
1630
- end
1631
-
1632
- it "should have the remove_all_ method respect the :primary_key option" do
1633
- @c2.one_to_many :attributes, :class => @c1, :primary_key=>:xxx
1634
- @c2.new(:id => 1234, :xxx=>5).remove_all_attributes
1635
- DB.sqls.must_equal ['UPDATE attributes SET node_id = NULL WHERE (node_id = 5)']
1636
- end
1637
-
1638
- it "should have the remove_all_ method respect composite keys" do
1639
- @c2.one_to_many :attributes, :class => @c1, :key=>[:node_id, :y], :primary_key=>[:id, :x]
1640
- @c2.new(:id => 1234, :x=>5).remove_all_attributes
1641
- DB.sqls.must_equal ["UPDATE attributes SET node_id = NULL, y = NULL WHERE ((node_id = 1234) AND (y = 5))"]
1642
- end
1643
-
1644
- it "remove_all should set the cache to []" do
1645
- @c2.one_to_many :attributes, :class => @c1
1646
- node = @c2.new(:id => 1234)
1647
- node.remove_all_attributes
1648
- node.associations[:attributes].must_equal []
1649
- end
1650
-
1651
- it "remove_all should return the array of previously associated items if the cache is populated" do
1652
- @c2.one_to_many :attributes, :class => @c1
1653
- attrib = @c1.new(:id=>3)
1654
- node = @c2.new(:id => 1234)
1655
- @c1.dataset = @c1.dataset.with_fetch([[], [{:id=>3, :node_id=>1234}]])
1656
- node.attributes.must_equal []
1657
- node.add_attribute(attrib)
1658
- node.associations[:attributes].must_equal [attrib]
1659
- node.remove_all_attributes.must_equal [attrib]
1660
- end
1661
-
1662
- it "remove_all should return nil if the cache is not populated" do
1663
- @c2.one_to_many :attributes, :class => @c1
1664
- @c2.new(:id => 1234).remove_all_attributes.must_be_nil
1665
- end
1666
-
1667
- it "remove_all should remove the current item from all reciprocal association caches if they are populated" do
1668
- @c2.one_to_many :attributes, :class => @c1
1669
- @c1.many_to_one :node, :class => @c2
1670
- @c2.dataset = @c2.dataset.with_fetch([])
1671
- @c1.dataset = @c1.dataset.with_fetch([[], [{:id=>3, :node_id=>1234}]])
1672
- attrib = @c1.new(:id=>3)
1673
- node = @c2.load(:id => 1234)
1674
- node.attributes.must_equal []
1675
- attrib.node.must_be_nil
1676
- node.add_attribute(attrib)
1677
- attrib.associations[:node].must_equal node
1678
- node.remove_all_attributes
1679
- attrib.associations.fetch(:node, 2).must_be_nil
1680
- end
1681
-
1682
- it "should call an _add_ method internally to add attributes" do
1683
- @c2.one_to_many :attributes, :class => @c1
1684
- @c2.private_instance_methods.must_include(:_add_attribute)
1685
- p = @c2.load(:id=>10)
1686
- c = @c1.load(:id=>123)
1687
- def p._add_attribute(x)
1688
- @x = x
1689
- end
1690
- def c._node_id=; raise; end
1691
- p.add_attribute(c)
1692
- p.instance_variable_get(:@x).must_equal c
1693
- end
1694
-
1695
- it "should support an :adder option for defining the _add_ method" do
1696
- @c2.one_to_many :attributes, :class => @c1, :adder=>proc{|x| @x = x}
1697
- p = @c2.load(:id=>10)
1698
- c = @c1.load(:id=>123)
1699
- def c._node_id=; raise; end
1700
- p.add_attribute(c)
1701
- p.instance_variable_get(:@x).must_equal c
1702
- end
1703
-
1704
- it "should allow additional arguments given to the add_ method and pass them onwards to the _add_ method" do
1705
- @c2.one_to_many :attributes, :class => @c1
1706
- p = @c2.load(:id=>10)
1707
- c = @c1.load(:id=>123)
1708
- def p._add_attribute(x,*y)
1709
- @x = x
1710
- @y = y
1711
- end
1712
- def c._node_id=; raise; end
1713
- p.add_attribute(c,:foo,:bar=>:baz)
1714
- p.instance_variable_get(:@x).must_equal c
1715
- p.instance_variable_get(:@y).must_equal [:foo,{:bar=>:baz}]
1716
- end
1717
-
1718
- it "should call a _remove_ method internally to remove attributes" do
1719
- @c2.one_to_many :attributes, :class => @c1
1720
- @c2.private_instance_methods.must_include(:_remove_attribute)
1721
- p = @c2.load(:id=>10)
1722
- c = @c1.load(:id=>123)
1723
- def p._remove_attribute(x)
1724
- @x = x
1725
- end
1726
- def c._node_id=; raise; end
1727
- p.remove_attribute(c)
1728
- p.instance_variable_get(:@x).must_equal c
1729
- end
1730
-
1731
- it "should support a :remover option for defining the _remove_ method" do
1732
- @c2.one_to_many :attributes, :class => @c1, :remover=>proc{|x| @x = x}
1733
- p = @c2.load(:id=>10)
1734
- c = @c1.load(:id=>123)
1735
- def c._node_id=; raise; end
1736
- p.remove_attribute(c)
1737
- p.instance_variable_get(:@x).must_equal c
1738
- end
1739
-
1740
- it "should allow additional arguments given to the remove_ method and pass them onwards to the _remove_ method" do
1741
- @c2.one_to_many :attributes, :class => @c1, :reciprocal=>nil
1742
- p = @c2.load(:id=>10)
1743
- c = @c1.load(:id=>123)
1744
- def p._remove_attribute(x,*y)
1745
- @x = x
1746
- @y = y
1747
- end
1748
- def c._node_id=; raise; end
1749
- p.remove_attribute(c,:foo,:bar=>:baz)
1750
- p.instance_variable_get(:@x).must_equal c
1751
- p.instance_variable_get(:@y).must_equal [:foo,{:bar=>:baz}]
1752
- end
1753
-
1754
- it "should allow additional arguments given to the remove_all_ method and pass them onwards to the _remove_all_ method" do
1755
- @c2.one_to_many :attributes, :class => @c1
1756
- p = @c2.load(:id=>10)
1757
- c = @c1.load(:id=>123)
1758
- def p._remove_all_attributes(*y)
1759
- @y = y
1760
- end
1761
- def c._node_id=; raise; end
1762
- p.remove_all_attributes(:foo,:bar=>:baz)
1763
- p.instance_variable_get(:@y).must_equal [:foo,{:bar=>:baz}]
1764
- end
1765
-
1766
- it "should call a _remove_all_ method internally to remove attributes" do
1767
- @c2.one_to_many :attributes, :class => @c1
1768
- @c2.private_instance_methods.must_include(:_remove_all_attributes)
1769
- p = @c2.load(:id=>10)
1770
- def p._remove_all_attributes
1771
- @x = :foo
1772
- end
1773
- p.remove_all_attributes
1774
- p.instance_variable_get(:@x).must_equal :foo
1775
- end
1776
-
1777
- it "should support a :clearer option for defining the _remove_all_ method" do
1778
- @c2.one_to_many :attributes, :class => @c1, :clearer=>proc{@x = :foo}
1779
- p = @c2.load(:id=>10)
1780
- p.remove_all_attributes
1781
- p.instance_variable_get(:@x).must_equal :foo
1782
- end
1783
-
1784
- it "should support (before|after)_(add|remove) callbacks" do
1785
- h = []
1786
- @c2.one_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
1787
- @c2.class_eval do
1788
- self::Foo = h
1789
- def _add_attribute(v)
1790
- model::Foo << 4
1791
- end
1792
- def _remove_attribute(v)
1793
- model::Foo << 5
1794
- end
1795
- def blah(x)
1796
- model::Foo << x.pk
1797
- end
1798
- def blahr(x)
1799
- model::Foo << 6
1800
- end
1801
- end
1802
- p = @c2.load(:id=>10)
1803
- c = @c1.load(:id=>123)
1804
- h.must_equal []
1805
- p.add_attribute(c)
1806
- h.must_equal [10, -123, 123, 4, 3]
1807
- p.remove_attribute(c)
1808
- h.must_equal [10, -123, 123, 4, 3, 123, 5, 6]
1809
- end
1810
-
1811
- it "should support after_load association callback" do
1812
- h = []
1813
- @c2.one_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
1814
- @c2.class_eval do
1815
- self::Foo = h
1816
- def al(v)
1817
- v.each{|x| model::Foo << x.pk}
1818
- end
1819
- end
1820
- @c1.dataset = @c1.dataset.with_fetch([{:id=>20}, {:id=>30}])
1821
- p = @c2.load(:id=>10, :parent_id=>20)
1822
- attributes = p.attributes
1823
- h.must_equal [[10, [20, 30]], 20, 30]
1824
- attributes.collect{|a| a.pk}.must_equal [20, 30]
1825
- end
1826
-
1827
- it "should raise error and not call internal add or remove method if before callback calls cancel_action if raise_on_save_failure is true" do
1828
- p = @c2.load(:id=>10)
1829
- c = @c1.load(:id=>123)
1830
- @c2.one_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
1831
- def p.ba(o); cancel_action; end
1832
- def p._add_attribute; raise; end
1833
- def p._remove_attribute; raise; end
1834
- p.associations[:attributes] = []
1835
- proc{p.add_attribute(c)}.must_raise(Sequel::HookFailed)
1836
- p.attributes.must_equal []
1837
- p.associations[:attributes] = [c]
1838
- def p.br(o); cancel_action; end
1839
- proc{p.remove_attribute(c)}.must_raise(Sequel::HookFailed)
1840
- p.attributes.must_equal [c]
1841
- end
1842
-
1843
- it "should return nil and not call internal add or remove method if before callback calls cancel_action if raise_on_save_failure is false" do
1844
- p = @c2.load(:id=>10)
1845
- c = @c1.load(:id=>123)
1846
- p.raise_on_save_failure = false
1847
- @c2.one_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
1848
- def p.ba(o); cancel_action; end
1849
- def p._add_attribute; raise; end
1850
- def p._remove_attribute; raise; end
1851
- p.associations[:attributes] = []
1852
- p.add_attribute(c).must_be_nil
1853
- p.attributes.must_equal []
1854
- p.associations[:attributes] = [c]
1855
- def p.br(o); cancel_action; end
1856
- p.remove_attribute(c).must_be_nil
1857
- p.attributes.must_equal [c]
1858
- end
1859
-
1860
- it "should have association dataset use false condition if any key is nil" do
1861
- @c1.one_to_many :children, :class => @c1, :primary_key=>:node_id
1862
- @c1.load(:id=>1).children_dataset.sql.must_equal "SELECT * FROM attributes WHERE 'f'"
1863
- end
1864
- end
1865
-
1866
- describe Sequel::Model, "many_to_many" do
1867
- before do
1868
- @c1 = Class.new(Sequel::Model(:attributes)) do
1869
- unrestrict_primary_key
1870
- attr_accessor :yyy
1871
- def self.name; 'Attribute'; end
1872
- def self.to_s; 'Attribute'; end
1873
- columns :id, :y, :z
1874
- end
1875
-
1876
- @c2 = Class.new(Sequel::Model(:nodes)) do
1877
- unrestrict_primary_key
1878
- attr_accessor :xxx
1879
-
1880
- def self.name; 'Node'; end
1881
- def self.to_s; 'Node'; end
1882
- columns :id, :x
1883
- end
1884
- @dataset = @c2.dataset
1885
- @c1.dataset = @c1.dataset.with_autoid(1)
1886
-
1887
- [@c1, @c2].each{|c| c.dataset = c.dataset.with_fetch({})}
1888
- DB.reset
1889
- end
1890
-
1891
- it "should raise an error if current class does not have a primary key, and :left_primary_key is not specified" do
1892
- @c2.no_primary_key
1893
- proc{@c2.many_to_many :attributes, :class => @c1}.must_raise(Sequel::Error)
1894
- DB.sqls.must_equal []
1895
- end
1896
-
1897
- it "should raise an error if associated class does not have a primary key, and :right_primary_key is not specified" do
1898
- @c1.no_primary_key
1899
- @c2.many_to_many :attributes, :class => @c1
1900
- d = @c2.new(:id => 1234)
1901
- proc{d.attributes}.must_raise(Sequel::Error)
1902
- DB.sqls.must_equal []
1903
- end
1904
-
1905
- it "should use implicit key values and join table if omitted" do
1906
- @c2.many_to_many :attributes, :class => @c1
1907
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)'
1908
- end
1909
-
1910
- it "should use implicit key values and join table if omitted" do
1911
- @c2.one_through_one :attribute, :class => @c1
1912
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1'
1913
- end
1914
-
1915
- it "should use implicit class if omitted" do
1916
- begin
1917
- class ::Tag < Sequel::Model; end
1918
- @c2.many_to_many :tags
1919
- @c2.new(:id => 1234).tags_dataset.sql.must_equal 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON (nodes_tags.tag_id = tags.id) WHERE (nodes_tags.node_id = 1234)'
1920
- ensure
1921
- Object.send(:remove_const, :Tag)
1922
- end
1923
- end
1924
-
1925
- it "should use class inside module if given as a string" do
1926
- begin
1927
- module ::Historical
1928
- class Tag < Sequel::Model; end
1929
- end
1930
- @c2.many_to_many :tags, :class=>'::Historical::Tag'
1931
- @c2.new(:id => 1234).tags_dataset.sql.must_equal 'SELECT tags.* FROM tags INNER JOIN nodes_tags ON (nodes_tags.tag_id = tags.id) WHERE (nodes_tags.node_id = 1234)'
1932
- ensure
1933
- Object.send(:remove_const, :Historical)
1934
- end
1935
- end
1936
-
1937
- it "should not override a selection consisting completely of qualified columns using Sequel::SQL::QualifiedIdentifier" do
1938
- @c1.dataset = @c1.dataset.select(Sequel.qualify(:attributes, :id), Sequel.qualify(:attributes, :b))
1939
- @c2.many_to_many :attributes, :class => @c1
1940
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.id, attributes.b FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)'
1941
- end
1942
-
1943
- with_symbol_splitting "should not override a selection consisting completely of qualified columns using symbols" do
1944
- @c1.dataset = @c1.dataset.select(:attributes__id, :attributes__b)
1945
- @c2.many_to_many :attributes, :class => @c1
1946
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.id, attributes.b FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)'
1947
- end
1948
-
1949
- it "should not override a selection consisting completely of qualified columns using Sequel::SQL::AliasedExpression" do
1950
- @c1.dataset = @c1.dataset.select(Sequel.qualify(:attributes, :id).as(:a), Sequel[:attributes][:b].as(:c))
1951
- @c2.many_to_many :attributes, :class => @c1
1952
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.id AS a, attributes.b AS c FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)'
1953
- end
1954
-
1955
- with_symbol_splitting "should not override a selection consisting completely of qualified columns using Sequel::SQL::AliasedExpression with qualified symbol" do
1956
- @c1.dataset = @c1.dataset.select(Sequel.qualify(:attributes, :id).as(:a), Sequel.as(:attributes__b, :c))
1957
- @c2.many_to_many :attributes, :class => @c1
1958
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.id AS a, attributes.b AS c FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)'
1959
- end
1960
-
1961
- it "should override a selection consisting of non qualified columns" do
1962
- @c1.dataset = @c1.dataset.select{foo(:bar)}
1963
- @c2.many_to_many :attributes, :class => @c1
1964
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)'
1965
- end
1966
-
1967
- it "should respect :predicate_key when lazily loading" do
1968
- @c2.many_to_many :attributes, :class => @c1, :predicate_key=>Sequel.subscript(Sequel[:attributes_nodes][:node_id], 0)
1969
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id[0] = 1234)'
1970
- end
1971
-
1972
- it "should use explicit key values and join table if given" do
1973
- @c2.many_to_many :attributes, :class => @c1, :left_key => :nodeid, :right_key => :attributeid, :join_table => :attribute2node
1974
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON (attribute2node.attributeid = attributes.id) WHERE (attribute2node.nodeid = 1234)'
1975
- end
1976
-
1977
- it "should support a conditions option" do
1978
- @c2.many_to_many :attributes, :class => @c1, :conditions => {:a=>32}
1979
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((a = 32) AND (attributes_nodes.node_id = 1234))'
1980
-
1981
- @c2.many_to_many :attributes, :class => @c1, :conditions => Sequel.lit('a = ?', 32)
1982
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((a = 32) AND (attributes_nodes.node_id = 1234))'
1983
- @c2.new(:id => 1234).attributes.must_equal [@c1.load({})]
1984
- end
1985
-
1986
- it "should support an order option" do
1987
- @c2.many_to_many :attributes, :class => @c1, :order => :blah
1988
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) ORDER BY blah'
1989
- end
1990
-
1991
- it "should support an array for the order option" do
1992
- @c2.many_to_many :attributes, :class => @c1, :order => [:blah1, :blah2]
1993
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) ORDER BY blah1, blah2'
1994
- end
1995
-
1996
- it "should support :left_primary_key and :right_primary_key options" do
1997
- @c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
1998
- @c2.new(:id => 1234, :xxx=>5).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.yyy) WHERE (attributes_nodes.node_id = 5)'
1999
- end
2000
-
2001
- it "should support composite keys" do
2002
- @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2003
- @c2.load(:id => 1234, :x=>5).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.r1 = attributes.id) AND (attributes_nodes.r2 = attributes.y)) WHERE ((attributes_nodes.l1 = 1234) AND (attributes_nodes.l2 = 5))'
2004
- end
2005
-
2006
- it "should not issue query if not all keys have values" do
2007
- @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2008
- @c2.load(:id => 1234, :x=>nil).attributes.must_equal []
2009
- DB.sqls.must_equal []
2010
- end
2011
-
2012
- it "should raise an Error unless same number of composite keys used" do
2013
- proc{@c2.many_to_many :attributes, :class => @c1, :left_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2014
- proc{@c2.many_to_many :attributes, :class => @c1, :left_primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2015
- proc{@c2.many_to_many :attributes, :class => @c1, :left_key=>[:node_id, :id], :left_primary_key=>:id}.must_raise(Sequel::Error)
2016
- proc{@c2.many_to_many :attributes, :class => @c1, :left_key=>:id, :left_primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2017
- proc{@c2.many_to_many :attributes, :class => @c1, :left_key=>[:node_id, :id, :x], :left_primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
2018
-
2019
- proc{@c2.many_to_many :attributes, :class => @c1, :right_primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2020
- proc{@c2.many_to_many :attributes, :class => @c1, :right_key=>[:node_id, :id], :right_primary_key=>:id}.must_raise(Sequel::Error)
2021
- proc{@c2.many_to_many :attributes, :class => @c1, :right_key=>:id, :left_primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2022
- proc{@c2.many_to_many :attributes, :class => @c1, :right_key=>[:node_id, :id, :x], :right_primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
2023
- end
2024
-
2025
- it "should support a select option" do
2026
- @c2.many_to_many :attributes, :class => @c1, :select => :blah
2027
-
2028
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)'
2029
- end
2030
-
2031
- it "should support an array for the select option" do
2032
- @c2.many_to_many :attributes, :class => @c1, :select => [Sequel::SQL::ColumnAll.new(:attributes), Sequel[:attribute_nodes][:blah2]]
2033
-
2034
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)'
2035
- end
2036
-
2037
- it "should accept a block" do
2038
- @c2.many_to_many :attributes, :class => @c1 do |ds|
2039
- ds.filter(:xxx => @xxx)
2040
- end
2041
-
2042
- n = @c2.new(:id => 1234)
2043
- n.xxx = 555
2044
- n.attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((attributes_nodes.node_id = 1234) AND (xxx = 555))'
2045
- end
2046
-
2047
- it "should allow the :order option while accepting a block" do
2048
- @c2.many_to_many :attributes, :class => @c1, :order=>[:blah1, :blah2] do |ds|
2049
- ds.filter(:xxx => @xxx)
2050
- end
2051
-
2052
- n = @c2.new(:id => 1234)
2053
- n.xxx = 555
2054
- n.attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((attributes_nodes.node_id = 1234) AND (xxx = 555)) ORDER BY blah1, blah2'
2055
- end
2056
-
2057
- it "should support a :dataset option that is used instead of the default" do
2058
- c1 = @c1
2059
- @c2.many_to_many :attributes, :class => @c1, :dataset=>proc{c1.join_table(:natural, :an).filter(Sequel[:an][:nodeid]=>pk)}, :order=> :a, :limit=>10, :select=>nil do |ds|
2060
- ds.filter(:xxx => @xxx)
2061
- end
2062
-
2063
- n = @c2.new(:id => 1234)
2064
- n.xxx = 555
2065
- n.attributes_dataset.sql.must_equal 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
2066
- n.attributes.must_equal [@c1.load({})]
2067
- DB.sqls.must_equal ['SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10']
2068
- end
2069
-
2070
- it "should support a :dataset option that accepts the reflection as an argument" do
2071
- @c2.many_to_many :attributes, :class => @c1, :dataset=>lambda{|opts| opts.associated_class.natural_join(:an).filter(Sequel[:an][:nodeid]=>pk)}, :order=> :a, :limit=>10, :select=>nil do |ds|
2072
- ds.filter(:xxx => @xxx)
2073
- end
2074
-
2075
- n = @c2.new(:id => 1234)
2076
- n.xxx = 555
2077
- n.attributes_dataset.sql.must_equal 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10'
2078
- n.attributes.must_equal [@c1.load({})]
2079
- DB.sqls.must_equal ['SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 10']
2080
- end
2081
-
2082
- it "should support a :limit option" do
2083
- @c2.many_to_many :attributes, :class => @c1 , :limit=>10
2084
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 10'
2085
- @c2.many_to_many :attributes, :class => @c1 , :limit=>[10, 10]
2086
- @c2.new(:id => 1234).attributes_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 10 OFFSET 10'
2087
- end
2088
-
2089
- it "should have the :eager option affect the _dataset method" do
2090
- @c2.many_to_many :attributes, :class => @c2 , :eager=>:attributes
2091
- @c2.new(:id => 1234).attributes_dataset.opts[:eager].must_equal(:attributes=>nil)
2092
- end
2093
-
2094
- it "should handle an aliased join table" do
2095
- @c2.many_to_many :attributes, :class => @c1, :join_table => Sequel[:attribute2node].as(:attributes_nodes)
2096
- n = @c2.load(:id => 1234)
2097
- a = @c1.load(:id => 2345)
2098
- n.attributes_dataset.sql.must_equal "SELECT attributes.* FROM attributes INNER JOIN attribute2node AS attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)"
2099
- a.must_equal n.add_attribute(a)
2100
- a.must_equal n.remove_attribute(a)
2101
- n.remove_all_attributes
2102
- DB.sqls.must_equal ["INSERT INTO attribute2node (node_id, attribute_id) VALUES (1234, 2345)",
2103
- "DELETE FROM attribute2node WHERE ((node_id = 1234) AND (attribute_id = 2345))",
2104
- "DELETE FROM attribute2node WHERE (node_id = 1234)"]
2105
- end
2106
-
2107
- with_symbol_splitting "should handle an aliased symbol join table" do
2108
- @c2.many_to_many :attributes, :class => @c1, :join_table => :attribute2node___attributes_nodes
2109
- n = @c2.load(:id => 1234)
2110
- a = @c1.load(:id => 2345)
2111
- n.attributes_dataset.sql.must_equal "SELECT attributes.* FROM attributes INNER JOIN attribute2node AS attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)"
2112
- a.must_equal n.add_attribute(a)
2113
- a.must_equal n.remove_attribute(a)
2114
- n.remove_all_attributes
2115
- DB.sqls.must_equal ["INSERT INTO attribute2node (node_id, attribute_id) VALUES (1234, 2345)",
2116
- "DELETE FROM attribute2node WHERE ((node_id = 1234) AND (attribute_id = 2345))",
2117
- "DELETE FROM attribute2node WHERE (node_id = 1234)"]
2118
- end
2119
-
2120
- it "should define an add_ method that works on existing records" do
2121
- @c2.many_to_many :attributes, :class => @c1
2122
-
2123
- n = @c2.load(:id => 1234)
2124
- a = @c1.load(:id => 2345)
2125
- n.add_attribute(a).must_equal a
2126
- DB.sqls.must_equal ["INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)"]
2127
- end
2128
-
2129
- it "should define an add_ method that works with a primary key" do
2130
- @c2.many_to_many :attributes, :class => @c1
2131
-
2132
- n = @c2.load(:id => 1234)
2133
- a = @c1.load(:id => 2345)
2134
- @c1.dataset = @c1.dataset.with_fetch(:id=>2345)
2135
- n.add_attribute(2345).must_equal a
2136
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE id = 2345",
2137
- "INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 2345)"]
2138
- end
2139
-
2140
- it "should raise an error if the primary key passed to the add_ method does not match an existing record" do
2141
- @c2.many_to_many :attributes, :class => @c1
2142
-
2143
- n = @c2.load(:id => 1234)
2144
- @c1.dataset = @c1.dataset.with_fetch([])
2145
- proc{n.add_attribute(2345)}.must_raise(Sequel::NoMatchingRow)
2146
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE id = 2345"]
2147
- end
2148
-
2149
- it "should allow passing a hash to the add_ method which creates a new record" do
2150
- @c2.many_to_many :attributes, :class => @c1
2151
-
2152
- n = @c2.load(:id => 1234)
2153
- @c1.dataset = @c1.dataset.with_fetch(:id=>1)
2154
- n.add_attribute(:id => 1).must_equal @c1.load(:id => 1)
2155
- DB.sqls.must_equal ['INSERT INTO attributes (id) VALUES (1)',
2156
- "SELECT * FROM attributes WHERE id = 1",
2157
- "INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (1234, 1)"]
2158
- end
2159
-
2160
- it "should define a remove_ method that works on existing records" do
2161
- @c2.many_to_many :attributes, :class => @c1
2162
-
2163
- n = @c2.new(:id => 1234)
2164
- a = @c1.new(:id => 2345)
2165
- n.remove_attribute(a).must_equal a
2166
- DB.sqls.must_equal ['DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 2345))']
2167
- end
2168
-
2169
- it "should raise an error in the add_ method if the passed associated object is not of the correct type" do
2170
- @c2.many_to_many :attributes, :class => @c1
2171
- proc{@c2.new(:id => 1234).add_attribute(@c2.new)}.must_raise(Sequel::Error)
2172
- end
2173
-
2174
- it "should accept a primary key for the remove_ method and remove an existing record" do
2175
- @c2.many_to_many :attributes, :class => @c1
2176
- n = @c2.new(:id => 1234)
2177
- @c1.dataset = @c1.dataset.with_fetch(:id=>234)
2178
- n.remove_attribute(234).must_equal @c1.load(:id => 234)
2179
- DB.sqls.must_equal ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((attributes_nodes.node_id = 1234) AND (attributes.id = 234)) LIMIT 1",
2180
- "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"]
2181
- end
2182
-
2183
- it "should raise an error in the remove_ method if the passed associated object is not of the correct type" do
2184
- @c2.many_to_many :attributes, :class => @c1
2185
- proc{@c2.new(:id => 1234).remove_attribute(@c2.new)}.must_raise(Sequel::Error)
2186
- end
2187
-
2188
- it "should have the add_ method respect the :left_primary_key and :right_primary_key options" do
2189
- @c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
2190
-
2191
- n = @c2.load(:id => 1234).set(:xxx=>5)
2192
- a = @c1.load(:id => 2345).set(:yyy=>8)
2193
- n.add_attribute(a).must_equal a
2194
- DB.sqls.must_equal ["INSERT INTO attributes_nodes (node_id, attribute_id) VALUES (5, 8)"]
2195
- end
2196
-
2197
- it "should have add_ method not add the same object to the cached association array if the object is already in the array" do
2198
- @c2.many_to_many :attributes, :class => @c1
2199
-
2200
- n = @c2.load(:id => 1234).set(:xxx=>5)
2201
- a = @c1.load(:id => 2345).set(:yyy=>8)
2202
- n.associations[:attributes] = []
2203
- a.must_equal n.add_attribute(a)
2204
- a.must_equal n.add_attribute(a)
2205
- n.attributes.must_equal [a]
2206
- end
2207
-
2208
- it "should have the add_ method respect composite keys" do
2209
- @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :z]
2210
- n = @c2.load(:id => 1234, :x=>5)
2211
- a = @c1.load(:id => 2345, :z=>8)
2212
- a.must_equal n.add_attribute(a)
2213
- sqls = DB.sqls
2214
- m = /INSERT INTO attributes_nodes \((\w+), (\w+), (\w+), (\w+)\) VALUES \((\d+), (\d+), (\d+), (\d+)\)/.match(sqls.pop)
2215
- sqls.must_equal []
2216
- m.wont_equal nil
2217
- map = {'l1'=>1234, 'l2'=>5, 'r1'=>2345, 'r2'=>8}
2218
- %w[l1 l2 r1 r2].each do |x|
2219
- v = false
2220
- 4.times do |i| i += 1
2221
- if m[i] == x
2222
- m[i+4].must_equal map[x].to_s
2223
- v = true
2224
- end
2225
- end
2226
- v.must_equal true
2227
- end
2228
- end
2229
-
2230
- it "should have the add_ method respect composite keys" do
2231
- @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :z]
2232
- @c1.dataset = @c1.dataset.with_fetch(:id=>2345, :z=>8)
2233
- @c1.set_primary_key [:id, :z]
2234
- n = @c2.load(:id => 1234, :x=>5)
2235
- a = @c1.load(:id => 2345, :z=>8)
2236
- n.add_attribute([2345, 8]).must_equal a
2237
- DB.sqls.must_equal ["SELECT * FROM attributes WHERE ((id = 2345) AND (z = 8)) LIMIT 1",
2238
- "INSERT INTO attributes_nodes (l1, l2, r1, r2) VALUES (1234, 5, 2345, 8)"]
2239
- end
2240
-
2241
- it "should have the remove_ method respect the :left_primary_key and :right_primary_key options" do
2242
- @c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
2243
-
2244
- n = @c2.new(:id => 1234, :xxx=>5)
2245
- a = @c1.new(:id => 2345, :yyy=>8)
2246
- n.remove_attribute(a).must_equal a
2247
- DB.sqls.must_equal ['DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 8))']
2248
- end
2249
-
2250
- it "should have the remove_ method respect composite keys" do
2251
- @c2.many_to_many :attributes, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :z]
2252
- n = @c2.load(:id => 1234, :x=>5)
2253
- a = @c1.load(:id => 2345, :z=>8)
2254
- a.must_equal n.remove_attribute(a)
2255
- DB.sqls.must_equal ["DELETE FROM attributes_nodes WHERE ((l1 = 1234) AND (l2 = 5) AND (r1 = 2345) AND (r2 = 8))"]
2256
- end
2257
-
2258
- it "should accept a array of composite primary key values for the remove_ method and remove an existing record" do
2259
- @c1.dataset = @c1.dataset.with_fetch(:id=>234, :y=>8)
2260
- @c1.set_primary_key [:id, :y]
2261
- @c2.many_to_many :attributes, :class => @c1
2262
- n = @c2.new(:id => 1234)
2263
- @c1.load(:id => 234, :y=>8).must_equal n.remove_attribute([234, 8])
2264
- DB.sqls.must_equal ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((attributes_nodes.node_id = 1234) AND (attributes.id = 234) AND (attributes.y = 8)) LIMIT 1",
2265
- "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 234))"]
2266
- end
2267
-
2268
- it "should raise an error if the model object doesn't have a valid primary key" do
2269
- @c2.many_to_many :attributes, :class => @c1
2270
- a = @c2.new
2271
- n = @c1.load(:id=>123)
2272
- proc{a.attributes_dataset}.must_raise(Sequel::Error)
2273
- proc{a.add_attribute(n)}.must_raise(Sequel::Error)
2274
- proc{a.remove_attribute(n)}.must_raise(Sequel::Error)
2275
- proc{a.remove_all_attributes}.must_raise(Sequel::Error)
2276
- end
2277
-
2278
- it "should save the associated object first in add_ if passed a new model object" do
2279
- @c2.many_to_many :attributes, :class => @c1
2280
- n = @c1.new
2281
- a = @c2.load(:id=>123)
2282
- n.new?.must_equal true
2283
- @c1.dataset = @c1.dataset.with_fetch(:id=>1)
2284
- a.add_attribute(n)
2285
- n.new?.must_equal false
2286
- end
2287
-
2288
- it "should raise a ValidationFailed in add_ if the associated object is new and invalid" do
2289
- @c2.many_to_many :attributes, :class => @c1
2290
- n = @c1.new
2291
- a = @c2.load(:id=>123)
2292
- def n.validate() errors.add(:id, 'foo') end
2293
- proc{a.add_attribute(n)}.must_raise(Sequel::ValidationFailed)
2294
- end
2295
-
2296
- it "should raise an Error in add_ if the associated object is new and invalid and raise_on_save_failure is false" do
2297
- @c2.many_to_many :attributes, :class => @c1
2298
- n = @c1.new
2299
- n.raise_on_save_failure = false
2300
- a = @c2.load(:id=>123)
2301
- def n.validate() errors.add(:id, 'foo') end
2302
- proc{a.add_attribute(n)}.must_raise(Sequel::Error)
2303
- end
2304
-
2305
- it "should not attempt to validate the associated object in add_ if the :validate=>false option is used" do
2306
- @c2.many_to_many :attributes, :class => @c1, :validate=>false
2307
- n = @c1.new
2308
- a = @c2.load(:id=>123)
2309
- def n.validate() errors.add(:id, 'foo') end
2310
- @c1.dataset = @c1.dataset.with_fetch(:id=>1)
2311
- a.add_attribute(n)
2312
- n.new?.must_equal false
2313
- end
2314
-
2315
- it "should raise an error if trying to remove a model object that doesn't have a valid primary key" do
2316
- @c2.many_to_many :attributes, :class => @c1
2317
- n = @c1.new
2318
- a = @c2.load(:id=>123)
2319
- proc{a.remove_attribute(n)}.must_raise(Sequel::Error)
2320
- end
2321
-
2322
- it "should provide an array with all members of the association" do
2323
- @c2.many_to_many :attributes, :class => @c1
2324
-
2325
- @c2.new(:id => 1234).attributes.must_equal [@c1.load({})]
2326
- DB.sqls.must_equal ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)']
2327
- end
2328
-
2329
- it "should populate cache when accessed" do
2330
- @c2.many_to_many :attributes, :class => @c1
2331
-
2332
- n = @c2.new(:id => 1234)
2333
- n.associations.include?(:attributes).must_equal false
2334
- atts = n.attributes
2335
- atts.must_equal n.associations[:attributes]
2336
- end
2337
-
2338
- it "should use cache if available" do
2339
- @c2.many_to_many :attributes, :class => @c1
2340
-
2341
- n = @c2.new(:id => 1234)
2342
- n.associations[:attributes] = 42
2343
- n.attributes.must_equal 42
2344
- DB.sqls.must_equal []
2345
- end
2346
-
2347
- it "should not use cache if asked to reload" do
2348
- @c2.many_to_many :attributes, :class => @c1
2349
-
2350
- n = @c2.new(:id => 1234)
2351
- n.associations[:attributes] = 42
2352
- n.attributes(:reload=>true).wont_equal 42
2353
- DB.sqls.must_equal ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234)"]
2354
- end
2355
-
2356
- it "should add item to cache if it exists when calling add_" do
2357
- @c2.many_to_many :attributes, :class => @c1
2358
-
2359
- n = @c2.new(:id => 1234)
2360
- att = @c1.load(:id => 345)
2361
- a = []
2362
- n.associations[:attributes] = a
2363
- n.add_attribute(att)
2364
- a.must_equal [att]
2365
- end
2366
-
2367
- it "should add item to reciprocal's cache if it exists when calling add_" do
2368
- @c2.many_to_many :attributes, :class => @c1
2369
- @c1.many_to_many :nodes, :class => @c2
2370
-
2371
- n = @c2.new(:id => 1234)
2372
- att = @c1.load(:id => 345)
2373
- att.associations[:nodes] = []
2374
- n.add_attribute(att)
2375
- att.nodes.must_equal [n]
2376
- end
2377
-
2378
- it "should remove item from cache if it exists when calling remove_" do
2379
- @c2.many_to_many :attributes, :class => @c1
2380
-
2381
- n = @c2.new(:id => 1234)
2382
- att = @c1.load(:id => 345)
2383
- a = [att]
2384
- n.associations[:attributes] = a
2385
- n.remove_attribute(att)
2386
- a.must_equal []
2387
- end
2388
-
2389
- it "should remove item from reciprocal's if it exists when calling remove_" do
2390
- @c2.many_to_many :attributes, :class => @c1
2391
- @c1.many_to_many :nodes, :class => @c2
2392
-
2393
- n = @c2.new(:id => 1234)
2394
- att = @c1.new(:id => 345)
2395
- att.associations[:nodes] = [n]
2396
- n.remove_attribute(att)
2397
- att.nodes.must_equal []
2398
- end
2399
-
2400
- it "should not create the add_, remove_, or remove_all_ methods if :read_only option is used" do
2401
- @c2.many_to_many :attributes, :class => @c1, :read_only=>true
2402
- im = @c2.instance_methods
2403
- im.must_include(:attributes)
2404
- im.must_include(:attributes_dataset)
2405
- im.wont_include(:add_attribute)
2406
- im.wont_include(:remove_attribute)
2407
- im.wont_include(:remove_all_attributes)
2408
- end
2409
-
2410
- it "should not add associations methods directly to class" do
2411
- @c2.many_to_many :attributes, :class => @c1
2412
- im = @c2.instance_methods
2413
- im.must_include(:attributes)
2414
- im.must_include(:attributes_dataset)
2415
- im.must_include(:add_attribute)
2416
- im.must_include(:remove_attribute)
2417
- im.must_include(:remove_all_attributes)
2418
- im2 = @c2.instance_methods(false)
2419
- im2.wont_include(:attributes)
2420
- im2.wont_include(:attributes_dataset)
2421
- im2.wont_include(:add_attribute)
2422
- im2.wont_include(:remove_attribute)
2423
- im2.wont_include(:remove_all_attributes)
2424
- end
2425
-
2426
- it "should have an remove_all_ method that removes all associations" do
2427
- @c2.many_to_many :attributes, :class => @c1
2428
- @c2.new(:id => 1234).remove_all_attributes
2429
- DB.sqls.must_equal ['DELETE FROM attributes_nodes WHERE (node_id = 1234)']
2430
- end
2431
-
2432
- it "should have the remove_all_ method respect the :left_primary_key option" do
2433
- @c2.many_to_many :attributes, :class => @c1, :left_primary_key=>:xxx
2434
- @c2.new(:id => 1234, :xxx=>5).remove_all_attributes
2435
- DB.sqls.must_equal ['DELETE FROM attributes_nodes WHERE (node_id = 5)']
2436
- end
2437
-
2438
- it "should have the remove_all_ method respect composite keys" do
2439
- @c2.many_to_many :attributes, :class => @c1, :left_primary_key=>[:id, :x], :left_key=>[:l1, :l2]
2440
- @c2.load(:id => 1234, :x=>5).remove_all_attributes
2441
- DB.sqls.must_equal ['DELETE FROM attributes_nodes WHERE ((l1 = 1234) AND (l2 = 5))']
2442
- end
2443
-
2444
- it "remove_all should set the cached instance variable to []" do
2445
- @c2.many_to_many :attributes, :class => @c1
2446
- node = @c2.new(:id => 1234)
2447
- node.remove_all_attributes
2448
- node.associations[:attributes].must_equal []
2449
- end
2450
-
2451
- it "remove_all should return the array of previously associated items if the cached instance variable exists" do
2452
- @c2.many_to_many :attributes, :class => @c1
2453
- attrib = @c1.load(:id=>3)
2454
- node = @c2.load(:id => 1234)
2455
- @c1.dataset = @c1.dataset.with_fetch([])
2456
- node.attributes.must_equal []
2457
- node.add_attribute(attrib)
2458
- node.associations[:attributes].must_equal [attrib]
2459
- node.remove_all_attributes.must_equal [attrib]
2460
- end
2461
-
2462
- it "remove_all should return nil if the cached instance variable does not exist" do
2463
- @c2.many_to_many :attributes, :class => @c1
2464
- @c2.new(:id => 1234).remove_all_attributes.must_be_nil
2465
- end
2466
-
2467
- it "remove_all should remove the current item from all reciprocal instance varaibles if it cached instance variable exists" do
2468
- @c2.many_to_many :attributes, :class => @c1
2469
- @c1.many_to_many :nodes, :class => @c2
2470
- @c1.dataset = @c1.dataset.with_fetch([])
2471
- @c2.dataset = @c2.dataset.with_fetch([])
2472
- attrib = @c1.load(:id=>3)
2473
- node = @c2.new(:id => 1234)
2474
- node.attributes.must_equal []
2475
- attrib.nodes.must_equal []
2476
- node.add_attribute(attrib)
2477
- attrib.associations[:nodes].must_equal [node]
2478
- node.remove_all_attributes
2479
- attrib.associations[:nodes].must_equal []
2480
- end
2481
-
2482
- it "add, remove, and remove_all methods should respect :join_table_block option" do
2483
- @c2.many_to_many :attributes, :class => @c1, :join_table_block=>proc{|ds| ds.filter(:x=>123)}
2484
- o = @c2.load(:id => 1234)
2485
- o.add_attribute(@c1.load(:id=>44))
2486
- o.remove_attribute(@c1.load(:id=>45))
2487
- o.remove_all_attributes
2488
- sqls = DB.sqls
2489
- sqls.shift =~ /INSERT INTO attributes_nodes \((node_id|attribute_id), (node_id|attribute_id)\) VALUES \((1234|44), (1234|44)\)/
2490
- sqls.must_equal ["DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234) AND (attribute_id = 45))",
2491
- "DELETE FROM attributes_nodes WHERE ((x = 123) AND (node_id = 1234))"]
2492
- end
2493
-
2494
- it "should call an _add_ method internally to add attributes" do
2495
- @c2.many_to_many :attributes, :class => @c1
2496
- @c2.private_instance_methods.must_include(:_add_attribute)
2497
- p = @c2.load(:id=>10)
2498
- c = @c1.load(:id=>123)
2499
- def p._add_attribute(x)
2500
- @x = x
2501
- end
2502
- p.add_attribute(c)
2503
- p.instance_variable_get(:@x).must_equal c
2504
- DB.sqls.must_equal []
2505
- end
2506
-
2507
- it "should support an :adder option for defining the _add_ method" do
2508
- @c2.many_to_many :attributes, :class => @c1, :adder=>proc{|x| @x = x}
2509
- p = @c2.load(:id=>10)
2510
- c = @c1.load(:id=>123)
2511
- p.add_attribute(c)
2512
- p.instance_variable_get(:@x).must_equal c
2513
- DB.sqls.must_equal []
2514
- end
2515
-
2516
- it "should allow additional arguments given to the add_ method and pass them onwards to the _add_ method" do
2517
- @c2.many_to_many :attributes, :class => @c1
2518
- p = @c2.load(:id=>10)
2519
- c = @c1.load(:id=>123)
2520
- def p._add_attribute(x,*y)
2521
- @x = x
2522
- @y = y
2523
- end
2524
- p.add_attribute(c,:foo,:bar=>:baz)
2525
- p.instance_variable_get(:@x).must_equal c
2526
- p.instance_variable_get(:@y).must_equal [:foo,{:bar=>:baz}]
2527
- end
2528
-
2529
- it "should call a _remove_ method internally to remove attributes" do
2530
- @c2.many_to_many :attributes, :class => @c1
2531
- @c2.private_instance_methods.must_include(:_remove_attribute)
2532
- p = @c2.load(:id=>10)
2533
- c = @c1.load(:id=>123)
2534
- def p._remove_attribute(x)
2535
- @x = x
2536
- end
2537
- p.remove_attribute(c)
2538
- p.instance_variable_get(:@x).must_equal c
2539
- DB.sqls.must_equal []
2540
- end
2541
-
2542
- it "should support a :remover option for defining the _remove_ method" do
2543
- @c2.many_to_many :attributes, :class => @c1, :remover=>proc{|x| @x = x}
2544
- p = @c2.load(:id=>10)
2545
- c = @c1.load(:id=>123)
2546
- p.remove_attribute(c)
2547
- p.instance_variable_get(:@x).must_equal c
2548
- DB.sqls.must_equal []
2549
- end
2550
-
2551
- it "should allow additional arguments given to the remove_ method and pass them onwards to the _remove_ method" do
2552
- @c2.many_to_many :attributes, :class => @c1
2553
- p = @c2.load(:id=>10)
2554
- c = @c1.load(:id=>123)
2555
- def p._remove_attribute(x,*y)
2556
- @x = x
2557
- @y = y
2558
- end
2559
- p.remove_attribute(c,:foo,:bar=>:baz)
2560
- p.instance_variable_get(:@x).must_equal c
2561
- p.instance_variable_get(:@y).must_equal [:foo,{:bar=>:baz}]
2562
- end
2563
-
2564
- it "should allow additional arguments given to the remove_all_ method and pass them onwards to the _remove_all_ method" do
2565
- @c2.many_to_many :attributes, :class => @c1
2566
- p = @c2.load(:id=>10)
2567
- def p._remove_all_attributes(*y)
2568
- @y = y
2569
- end
2570
- p.remove_all_attributes(:foo,:bar=>:baz)
2571
- p.instance_variable_get(:@y).must_equal [:foo,{:bar=>:baz}]
2572
- end
2573
-
2574
- it "should call a _remove_all_ method internally to remove attributes" do
2575
- @c2.many_to_many :attributes, :class => @c1
2576
- @c2.private_instance_methods.must_include(:_remove_all_attributes)
2577
- p = @c2.load(:id=>10)
2578
- def p._remove_all_attributes
2579
- @x = :foo
2580
- end
2581
- p.remove_all_attributes
2582
- p.instance_variable_get(:@x).must_equal :foo
2583
- DB.sqls.must_equal []
2584
- end
2585
-
2586
- it "should support a :clearer option for defining the _remove_all_ method" do
2587
- @c2.many_to_many :attributes, :class => @c1, :clearer=>proc{@x = :foo}
2588
- p = @c2.load(:id=>10)
2589
- p.remove_all_attributes
2590
- p.instance_variable_get(:@x).must_equal :foo
2591
- DB.sqls.must_equal []
2592
- end
2593
-
2594
- it "should support (before|after)_(add|remove) callbacks" do
2595
- h = []
2596
- @c2.many_to_many :attributes, :class => @c1, :before_add=>[proc{|x,y| h << x.pk; h << -y.pk}, :blah], :after_add=>proc{h << 3}, :before_remove=>:blah, :after_remove=>[:blahr]
2597
- @c2.class_eval do
2598
- self::Foo = h
2599
- def _add_attribute(v)
2600
- model::Foo << 4
2601
- end
2602
- def _remove_attribute(v)
2603
- model::Foo << 5
2604
- end
2605
- def blah(x)
2606
- model::Foo << x.pk
2607
- end
2608
- def blahr(x)
2609
- model::Foo << 6
2610
- end
2611
- end
2612
- p = @c2.load(:id=>10)
2613
- c = @c1.load(:id=>123)
2614
- h.must_equal []
2615
- p.add_attribute(c)
2616
- h.must_equal [10, -123, 123, 4, 3]
2617
- p.remove_attribute(c)
2618
- h.must_equal [10, -123, 123, 4, 3, 123, 5, 6]
2619
- end
2620
-
2621
- it "should support after_load association callback" do
2622
- h = []
2623
- @c2.many_to_many :attributes, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.collect{|z|z.pk}]}, :al]
2624
- @c2.class_eval do
2625
- self::Foo = h
2626
- def al(v)
2627
- v.each{|x| model::Foo << x.pk}
2628
- end
2629
- end
2630
- @c1.dataset = @c1.dataset.with_fetch([{:id=>20}, {:id=>30}])
2631
- p = @c2.load(:id=>10, :parent_id=>20)
2632
- attributes = p.attributes
2633
- h.must_equal [[10, [20, 30]], 20, 30]
2634
- attributes.collect{|a| a.pk}.must_equal [20, 30]
2635
- end
2636
-
2637
- it "should raise error and not call internal add or remove method if before callback calls cancel_action if raise_on_save_failure is true" do
2638
- p = @c2.load(:id=>10)
2639
- c = @c1.load(:id=>123)
2640
- @c2.many_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
2641
- def p.ba(o) cancel_action end
2642
- def p._add_attribute; raise; end
2643
- def p._remove_attribute; raise; end
2644
- p.associations[:attributes] = []
2645
- p.raise_on_save_failure = true
2646
- proc{p.add_attribute(c)}.must_raise(Sequel::HookFailed)
2647
- p.attributes.must_equal []
2648
- p.associations[:attributes] = [c]
2649
- def p.br(o) cancel_action end
2650
- proc{p.remove_attribute(c)}.must_raise(Sequel::HookFailed)
2651
- p.attributes.must_equal [c]
2652
- end
2653
-
2654
- it "should return nil and not call internal add or remove method if before callback calls cancel_action if raise_on_save_failure is false" do
2655
- p = @c2.load(:id=>10)
2656
- c = @c1.load(:id=>123)
2657
- p.raise_on_save_failure = false
2658
- @c2.many_to_many :attributes, :class => @c1, :before_add=>:ba, :before_remove=>:br
2659
- def p.ba(o) cancel_action end
2660
- def p._add_attribute; raise; end
2661
- def p._remove_attribute; raise; end
2662
- p.associations[:attributes] = []
2663
- p.add_attribute(c).must_be_nil
2664
- p.attributes.must_equal []
2665
- p.associations[:attributes] = [c]
2666
- def p.br(o) cancel_action end
2667
- p.remove_attribute(c).must_be_nil
2668
- p.attributes.must_equal [c]
2669
- end
2670
-
2671
- it "should support a :uniq option that removes duplicates from the association" do
2672
- @c2.many_to_many :attributes, :class => @c1, :uniq=>true
2673
- @c1.dataset = @c1.dataset.with_fetch([{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}])
2674
- @c2.load(:id=>10, :parent_id=>20).attributes.must_equal [@c1.load(:id=>20), @c1.load(:id=>30)]
2675
- end
2676
-
2677
- it "should support a :distinct option that uses the DISTINCT clause" do
2678
- @c2.many_to_many :attributes, :class => @c1, :distinct=>true
2679
- @c2.load(:id=>10).attributes_dataset.sql.must_equal "SELECT DISTINCT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 10)"
2680
- end
2681
-
2682
- it "should not apply association options when removing all associated records" do
2683
- @c2.many_to_many :attributes, :class => @c1 do |ds|
2684
- ds.filter(:name=>'John')
2685
- end
2686
- @c2.load(:id=>1).remove_all_attributes
2687
- DB.sqls.must_equal ["DELETE FROM attributes_nodes WHERE (node_id = 1)"]
2688
- end
2689
-
2690
- it "should use assocation's dataset when grabbing a record to remove from the assocation by primary key" do
2691
- @c2.many_to_many :attributes, :class => @c1 do |ds|
2692
- ds.filter(:join_table_att=>3)
2693
- end
2694
- @c1.dataset = @c1.dataset.with_fetch(:id=>2)
2695
- @c2.load(:id=>1).remove_attribute(2)
2696
- DB.sqls.must_equal ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((attributes_nodes.node_id = 1) AND (join_table_att = 3) AND (attributes.id = 2)) LIMIT 1",
2697
- "DELETE FROM attributes_nodes WHERE ((node_id = 1) AND (attribute_id = 2))"]
2698
- end
2699
-
2700
- it "should have association dataset use false condition if any key is nil" do
2701
- @c1.many_to_many :attributes, :class => @c1, :left_primary_key=>:y
2702
- @c1.load(:id=>1).attributes_dataset.sql.must_equal "SELECT attributes.* FROM attributes INNER JOIN attributes_attributes ON (attributes_attributes.attribute_id = attributes.id) WHERE 'f'"
2703
- end
2704
- end
2705
-
2706
- describe Sequel::Model, "one_through_one" do
2707
- before do
2708
- @c1 = Class.new(Sequel::Model(:attributes)) do
2709
- unrestrict_primary_key
2710
- attr_accessor :yyy
2711
- def self.name; 'Attribute'; end
2712
- def self.to_s; 'Attribute'; end
2713
- columns :id, :y, :z
2714
- end
2715
-
2716
- @c2 = Class.new(Sequel::Model(:nodes)) do
2717
- unrestrict_primary_key
2718
- attr_accessor :xxx
2719
-
2720
- def self.name; 'Node'; end
2721
- def self.to_s; 'Node'; end
2722
- columns :id, :x
2723
- end
2724
- @dataset = @c2.dataset
2725
- @c1.dataset = @c1.dataset.with_autoid(1)
2726
-
2727
- [@c1, @c2].each{|c| c.dataset = c.dataset.with_fetch({})}
2728
- DB.reset
2729
- end
2730
- after do
2731
- DB.fetch = {:id => 1, :x => 1}
2732
- end
2733
-
2734
- it "should use implicit key values and join table if omitted" do
2735
- @c2.one_through_one :attribute, :class => @c1
2736
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1'
2737
- end
2738
-
2739
- it "should respect :predicate_key when lazily loading" do
2740
- @c2.one_through_one :attribute, :class => @c1, :predicate_key=>Sequel.subscript(Sequel[:attributes_nodes][:node_id], 0)
2741
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id[0] = 1234) LIMIT 1'
2742
- end
2743
-
2744
- it "should use explicit key values and join table if given" do
2745
- @c2.one_through_one :attribute, :class => @c1, :left_key => :nodeid, :right_key => :attributeid, :join_table => :attribute2node
2746
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attribute2node ON (attribute2node.attributeid = attributes.id) WHERE (attribute2node.nodeid = 1234) LIMIT 1'
2747
- end
2748
-
2749
- it "should support a conditions option" do
2750
- @c2.one_through_one :attribute, :class => @c1, :conditions => {:a=>32}
2751
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((a = 32) AND (attributes_nodes.node_id = 1234)) LIMIT 1'
2752
-
2753
- @c2.one_through_one :attribute, :class => @c1, :conditions => Sequel.lit('a = ?', 32)
2754
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((a = 32) AND (attributes_nodes.node_id = 1234)) LIMIT 1'
2755
- @c2.new(:id => 1234).attribute.must_equal @c1.load({})
2756
- end
2757
-
2758
- it "should support an order option" do
2759
- @c2.one_through_one :attribute, :class => @c1, :order => :blah
2760
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) ORDER BY blah LIMIT 1'
2761
- end
2762
-
2763
- it "should support an array for the order option" do
2764
- @c2.one_through_one :attribute, :class => @c1, :order => [:blah1, :blah2]
2765
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) ORDER BY blah1, blah2 LIMIT 1'
2766
- end
2767
-
2768
- it "should support :left_primary_key and :right_primary_key options" do
2769
- @c2.one_through_one :attribute, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
2770
- @c2.new(:id => 1234, :xxx=>5).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.yyy) WHERE (attributes_nodes.node_id = 5) LIMIT 1'
2771
- end
2772
-
2773
- it "should support composite keys" do
2774
- @c2.one_through_one :attribute, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2775
- @c2.load(:id => 1234, :x=>5).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON ((attributes_nodes.r1 = attributes.id) AND (attributes_nodes.r2 = attributes.y)) WHERE ((attributes_nodes.l1 = 1234) AND (attributes_nodes.l2 = 5)) LIMIT 1'
2776
- end
2777
-
2778
- it "should not issue query if not all keys have values" do
2779
- @c2.one_through_one :attribute, :class => @c1, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :left_primary_key=>[:id, :x], :right_primary_key=>[:id, :y]
2780
- @c2.load(:id => 1234, :x=>nil).attribute.must_be_nil
2781
- DB.sqls.must_equal []
2782
- end
2783
-
2784
- it "should raise an Error unless same number of composite keys used" do
2785
- proc{@c2.one_through_one :attribute, :class => @c1, :left_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2786
- proc{@c2.one_through_one :attribute, :class => @c1, :left_primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2787
- proc{@c2.one_through_one :attribute, :class => @c1, :left_key=>[:node_id, :id], :left_primary_key=>:id}.must_raise(Sequel::Error)
2788
- proc{@c2.one_through_one :attribute, :class => @c1, :left_key=>:id, :left_primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2789
- proc{@c2.one_through_one :attribute, :class => @c1, :left_key=>[:node_id, :id, :x], :left_primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
2790
-
2791
- proc{@c2.one_through_one :attribute, :class => @c1, :right_primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2792
- proc{@c2.one_through_one :attribute, :class => @c1, :right_key=>[:node_id, :id], :right_primary_key=>:id}.must_raise(Sequel::Error)
2793
- proc{@c2.one_through_one :attribute, :class => @c1, :right_key=>:id, :left_primary_key=>[:node_id, :id]}.must_raise(Sequel::Error)
2794
- proc{@c2.one_through_one :attribute, :class => @c1, :right_key=>[:node_id, :id, :x], :right_primary_key=>[:parent_id, :id]}.must_raise(Sequel::Error)
2795
- end
2796
-
2797
- it "should support a select option" do
2798
- @c2.one_through_one :attribute, :class => @c1, :select => :blah
2799
-
2800
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT blah FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1'
2801
- end
2802
-
2803
- it "should support an array for the select option" do
2804
- @c2.one_through_one :attribute, :class => @c1, :select => [Sequel::SQL::ColumnAll.new(:attributes), Sequel[:attribute_nodes][:blah2]]
2805
-
2806
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.*, attribute_nodes.blah2 FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1'
2807
- end
2808
-
2809
- it "should accept a block" do
2810
- @c2.one_through_one :attribute, :class => @c1 do |ds|
2811
- ds.filter(:xxx => @xxx)
2812
- end
2813
-
2814
- n = @c2.new(:id => 1234)
2815
- n.xxx = 555
2816
- n.attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((attributes_nodes.node_id = 1234) AND (xxx = 555)) LIMIT 1'
2817
- end
2818
-
2819
- it "should allow the :order option while accepting a block" do
2820
- @c2.one_through_one :attribute, :class => @c1, :order=>[:blah1, :blah2] do |ds|
2821
- ds.filter(:xxx => @xxx)
2822
- end
2823
-
2824
- n = @c2.new(:id => 1234)
2825
- n.xxx = 555
2826
- n.attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE ((attributes_nodes.node_id = 1234) AND (xxx = 555)) ORDER BY blah1, blah2 LIMIT 1'
2827
- end
2828
-
2829
- it "should support a :dataset option that is used instead of the default" do
2830
- c1 = @c1
2831
- @c2.one_through_one :attribute, :class => @c1, :dataset=>proc{c1.join_table(:natural, :an).filter(Sequel[:an][:nodeid]=>pk)}, :order=> :a, :select=>nil do |ds|
2832
- ds.filter(:xxx => @xxx)
2833
- end
2834
-
2835
- n = @c2.new(:id => 1234)
2836
- n.xxx = 555
2837
- n.attribute_dataset.sql.must_equal 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 1'
2838
- n.attribute.must_equal @c1.load({})
2839
- DB.sqls.must_equal ['SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 1']
2840
- end
2841
-
2842
- it "should support a :dataset option that accepts the reflection as an argument" do
2843
- @c2.one_through_one :attribute, :class => @c1, :dataset=>lambda{|opts| opts.associated_class.natural_join(:an).filter(Sequel[:an][:nodeid]=>pk)}, :order=> :a, :select=>nil do |ds|
2844
- ds.filter(:xxx => @xxx)
2845
- end
2846
-
2847
- n = @c2.new(:id => 1234)
2848
- n.xxx = 555
2849
- n.attribute_dataset.sql.must_equal 'SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 1'
2850
- n.attribute.must_equal @c1.load({})
2851
- DB.sqls.must_equal ['SELECT * FROM attributes NATURAL JOIN an WHERE ((an.nodeid = 1234) AND (xxx = 555)) ORDER BY a LIMIT 1']
2852
- end
2853
-
2854
- it "should support a :limit option to specify an offset" do
2855
- @c2.one_through_one :attribute, :class => @c1 , :limit=>[nil, 10]
2856
- @c2.new(:id => 1234).attribute_dataset.sql.must_equal 'SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1 OFFSET 10'
2857
- end
2858
-
2859
- it "should have the :eager option affect the _dataset method" do
2860
- @c2.one_through_one :attribute, :class => @c2 , :eager=>:attribute
2861
- @c2.new(:id => 1234).attribute_dataset.opts[:eager].must_equal(:attribute=>nil)
2862
- end
2863
-
2864
- it "should handle an aliased join table" do
2865
- @c2.one_through_one :attribute, :class => @c1, :join_table => Sequel[:attribute2node].as(:attributes_nodes)
2866
- n = @c2.load(:id => 1234)
2867
- n.attribute_dataset.sql.must_equal "SELECT attributes.* FROM attributes INNER JOIN attribute2node AS attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1"
2868
- end
2869
-
2870
- with_symbol_splitting "should handle an aliased join table with splittable symbol" do
2871
- @c2.one_through_one :attribute, :class => @c1, :join_table => :attribute2node___attributes_nodes
2872
- n = @c2.load(:id => 1234)
2873
- n.attribute_dataset.sql.must_equal "SELECT attributes.* FROM attributes INNER JOIN attribute2node AS attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1"
2874
- end
2875
-
2876
- it "should raise an error if the model object doesn't have a valid primary key" do
2877
- @c2.one_through_one :attribute, :class => @c1
2878
- a = @c2.new
2879
- proc{a.attribute_dataset}.must_raise(Sequel::Error)
2880
- end
2881
-
2882
- it "should provide an array with all members of the association" do
2883
- @c2.one_through_one :attribute, :class => @c1
2884
-
2885
- @c2.new(:id => 1234).attribute.must_equal @c1.load({})
2886
- DB.sqls.must_equal ['SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1']
2887
- end
2888
-
2889
- it "should populate cache when accessed" do
2890
- @c2.one_through_one :attribute, :class => @c1
2891
-
2892
- n = @c2.new(:id => 1234)
2893
- n.associations.include?(:attribute).must_equal false
2894
- atts = n.attribute
2895
- atts.must_equal n.associations[:attribute]
2896
- end
2897
-
2898
- it "should use cache if available" do
2899
- @c2.one_through_one :attribute, :class => @c1
2900
-
2901
- n = @c2.new(:id => 1234)
2902
- n.associations[:attribute] = 42
2903
- n.attribute.must_equal 42
2904
- DB.sqls.must_equal []
2905
- end
2906
-
2907
- it "should not use cache if asked to reload" do
2908
- @c2.one_through_one :attribute, :class => @c1
2909
-
2910
- n = @c2.new(:id => 1234)
2911
- n.associations[:attribute] = 42
2912
- n.attribute(:reload=>true).wont_equal 42
2913
- DB.sqls.must_equal ["SELECT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 1234) LIMIT 1"]
2914
- end
2915
-
2916
- it "should not add associations methods directly to class" do
2917
- @c2.one_through_one :attribute, :class => @c1
2918
- im = @c2.instance_methods
2919
- im.must_include(:attribute)
2920
- im.must_include(:attribute_dataset)
2921
- im2 = @c2.instance_methods(false)
2922
- im2.wont_include(:attribute)
2923
- im2.wont_include(:attribute_dataset)
2924
- end
2925
-
2926
- it "should support after_load association callback" do
2927
- h = []
2928
- @c2.one_through_one :attribute, :class => @c1, :after_load=>[proc{|x,y| h << [x.pk, y.pk]}, :al]
2929
- @c2.class_eval do
2930
- self::Foo = h
2931
- def al(v)
2932
- model::Foo << v.pk
2933
- end
2934
- end
2935
- @c1.dataset = @c1.dataset.with_fetch([{:id=>20}])
2936
- p = @c2.load(:id=>10, :parent_id=>20)
2937
- attribute = p.attribute
2938
- h.must_equal [[10, 20], 20]
2939
- attribute.pk.must_equal 20
2940
- end
2941
-
2942
- it "should support a :distinct option that uses the DISTINCT clause" do
2943
- @c2.one_through_one :attribute, :class => @c1, :distinct=>true
2944
- @c2.load(:id=>10).attribute_dataset.sql.must_equal "SELECT DISTINCT attributes.* FROM attributes INNER JOIN attributes_nodes ON (attributes_nodes.attribute_id = attributes.id) WHERE (attributes_nodes.node_id = 10) LIMIT 1"
2945
- end
2946
-
2947
- it "should not add a setter method if the :read_only option is true" do
2948
- @c2.one_through_one :attribute, :class => @c1, :read_only=>true
2949
- im = @c2.instance_methods
2950
- im.must_include(:attribute)
2951
- im.wont_include(:attribute=)
2952
- end
2953
-
2954
- it "should add a setter method" do
2955
- @c2.one_through_one :attribute, :class => @c1
2956
- attrib = @c1.new(:id=>3)
2957
- DB.fetch = []
2958
- o = @c2.load(:id => 1234)
2959
- o.attribute = nil
2960
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1"]
2961
-
2962
- o.attribute = attrib
2963
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1",
2964
- "INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (3, 1234)"]
2965
-
2966
- DB.fetch = {:node_id=>1234, :attribute_id=>5}
2967
- o.attribute = nil
2968
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1",
2969
- "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (attribute_id = 5))"]
2970
-
2971
- o.attribute = attrib
2972
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1",
2973
- "UPDATE attributes_nodes SET attribute_id = 3 WHERE ((node_id = 1234) AND (attribute_id = 5))"]
2974
-
2975
- @c2.load(:id => 1234).attribute = @c1.new(:id=>5)
2976
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1"]
2977
- end
2978
-
2979
- it "should use a transaction in the setter method" do
2980
- @c2.one_through_one :attribute, :class => @c1
2981
- @c2.use_transactions = true
2982
- @c1.load(:id=>3)
2983
- DB.fetch = []
2984
- @c2.new(:id => 1234).attribute = nil
2985
- DB.sqls.must_equal ['BEGIN',
2986
- "SELECT * FROM attributes_nodes WHERE (node_id = 1234) LIMIT 1",
2987
- 'COMMIT']
2988
- end
2989
-
2990
- it "should have setter method respect :join_table_block option" do
2991
- @c2.one_through_one :attribute, :class => @c1, :join_table_block=>proc{|ds| ds.where(:a)}
2992
- attrib = @c1.new(:id=>3)
2993
- DB.fetch = []
2994
- o = @c2.new(:id => 1234)
2995
- o.attribute = nil
2996
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (a AND (node_id = 1234)) LIMIT 1"]
2997
-
2998
- o.attribute = attrib
2999
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (a AND (node_id = 1234)) LIMIT 1",
3000
- "INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (3, 1234)"]
3001
-
3002
- DB.fetch = {:node_id=>1234, :attribute_id=>5}
3003
- o.attribute = nil
3004
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (a AND (node_id = 1234)) LIMIT 1",
3005
- "DELETE FROM attributes_nodes WHERE (a AND (node_id = 1234) AND (attribute_id = 5))"]
3006
-
3007
- o.attribute = attrib
3008
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (a AND (node_id = 1234)) LIMIT 1",
3009
- "UPDATE attributes_nodes SET attribute_id = 3 WHERE (a AND (node_id = 1234) AND (attribute_id = 5))"]
3010
- end
3011
-
3012
- it "should have the setter method respect the :left_primary_key and :right_primary_key option" do
3013
- @c2.one_through_one :attribute, :class => @c1, :left_primary_key=>:xxx, :right_primary_key=>:yyy
3014
- attrib = @c1.new(:id=>3, :yyy=>7)
3015
- DB.fetch = []
3016
- o = @c2.new(:id => 1234, :xxx=>5)
3017
- o.attribute = nil
3018
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 5) LIMIT 1"]
3019
-
3020
- o.attribute = attrib
3021
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 5) LIMIT 1",
3022
- "INSERT INTO attributes_nodes (attribute_id, node_id) VALUES (7, 5)"]
3023
-
3024
- DB.fetch = {:node_id=>1234, :attribute_id=>9}
3025
- o.attribute = nil
3026
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 5) LIMIT 1",
3027
- "DELETE FROM attributes_nodes WHERE ((node_id = 5) AND (attribute_id = 9))"]
3028
-
3029
- o.attribute = attrib
3030
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE (node_id = 5) LIMIT 1",
3031
- "UPDATE attributes_nodes SET attribute_id = 7 WHERE ((node_id = 5) AND (attribute_id = 9))"]
3032
- end
3033
-
3034
- it "should have the setter method respect composite keys" do
3035
- @c2.one_through_one :attribute, :class => @c1, :left_key=>[:node_id, :y], :left_primary_key=>[:id, :x], :right_key=>[:attribute_id, :z], :right_primary_key=>[:id, :w]
3036
- attrib = @c1.load(:id=>3, :w=>7)
3037
- @c1.def_column_alias :w, :w
3038
- DB.fetch = []
3039
- o = @c2.new(:id => 1234, :x=>5)
3040
- o.attribute = nil
3041
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5)) LIMIT 1"]
3042
-
3043
- o.attribute = attrib
3044
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5)) LIMIT 1",
3045
- "INSERT INTO attributes_nodes (attribute_id, z, node_id, y) VALUES (3, 7, 1234, 5)"]
3046
-
3047
- DB.fetch = {:node_id=>1234, :attribute_id=>10, :y=>6, :z=>8}
3048
- o.attribute = nil
3049
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5)) LIMIT 1",
3050
- "DELETE FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5) AND (attribute_id = 10) AND (z = 8))"]
3051
-
3052
- o.attribute = attrib
3053
- DB.sqls.must_equal ["SELECT * FROM attributes_nodes WHERE ((node_id = 1234) AND (y = 5)) LIMIT 1",
3054
- "UPDATE attributes_nodes SET attribute_id = 3, z = 7 WHERE ((node_id = 1234) AND (y = 5) AND (attribute_id = 10) AND (z = 8))"]
3055
- end
3056
-
3057
- it "should raise an error if the current model object that doesn't have a valid primary key" do
3058
- @c2.one_through_one :attribute, :class => @c1
3059
- p = @c2.new
3060
- c = @c2.load(:id=>123)
3061
- proc{c.attribute = p}.must_raise(Sequel::Error)
3062
- end
3063
-
3064
- it "should raise an error if the associated object that doesn't have a valid primary key" do
3065
- @c2.one_through_one :attribute, :class => @c1
3066
- p = @c2.new
3067
- c = @c2.load(:id=>123)
3068
- proc{p.attribute = c}.must_raise(Sequel::Error)
3069
- end
3070
-
3071
- it "should make the change to the foreign_key value inside a _association= method" do
3072
- @c2.one_through_one :attribute, :class => @c1
3073
- @c2.private_instance_methods.must_include(:_attribute=)
3074
- attrib = @c1.new(:id=>3)
3075
- o = @c2.new(:id => 1234)
3076
- def o._attribute=(x)
3077
- @x = x
3078
- end
3079
- o.attribute = attrib
3080
- o.instance_variable_get(:@x).must_equal attrib
3081
- end
3082
-
3083
- it "should have a :setter option define the _association= method" do
3084
- @c2.one_through_one :attribute, :class => @c1, :setter=>proc{|x| @x = x}
3085
- attrib = @c1.new(:id=>3)
3086
- o = @c2.new(:id => 1234)
3087
- o.attribute = attrib
3088
- o.instance_variable_get(:@x).must_equal attrib
3089
- end
3090
-
3091
- it "should support (before|after)_set callbacks" do
3092
- h = []
3093
- @c2.one_through_one :attribute, :class => @c1, :before_set=>[proc{|x,y| h << x.pk; h << (y ? -y.pk : :y)}, :blah], :after_set=>proc{h << :l}
3094
- @c2.class_eval do
3095
- self::Foo = h
3096
- def blah(x)
3097
- model::Foo << (x ? x.pk : :x)
3098
- end
3099
- end
3100
- attrib = @c1.new(:id=>3)
3101
- o = @c2.new(:id => 1234)
3102
- h.must_equal []
3103
- o.attribute = attrib
3104
- h.must_equal [1234, -3, 3, :l]
3105
- o.attribute = nil
3106
- h.must_equal [1234, -3, 3, :l, 1234, :y, :x, :l]
3107
- end
3108
-
3109
- it "should have association dataset use false condition if any key is nil" do
3110
- @c1.one_through_one :attribute, :class => @c1, :left_primary_key=>:y
3111
- @c1.load(:id=>1).attribute_dataset.sql.must_equal "SELECT attributes.* FROM attributes INNER JOIN attributes_attributes ON (attributes_attributes.attribute_id = attributes.id) WHERE 'f' LIMIT 1"
3112
- end
3113
- end
3114
-
3115
- describe "Filtering by associations" do
3116
- before(:all) do
3117
- db = Sequel.mock
3118
- db.extend_datasets do
3119
- def supports_window_functions?; true; end
3120
- def supports_distinct_on?; true; end
3121
- end
3122
- @Album = Class.new(Sequel::Model(db[:albums]))
3123
- artist = @Artist = Class.new(Sequel::Model(db[:artists]))
3124
- tag = @Tag = Class.new(Sequel::Model(db[:tags]))
3125
- track = @Track = Class.new(Sequel::Model(db[:tracks]))
3126
- album_info = @AlbumInfo = Class.new(Sequel::Model(db[:album_infos]))
3127
- @Artist.columns :id, :id1, :id2
3128
- @Tag.columns :id, :tid1, :tid2
3129
- @Track.columns :id, :album_id, :album_id1, :album_id2
3130
- @AlbumInfo.columns :id, :album_id, :album_id1, :album_id2
3131
- @Album.class_eval do
3132
- columns :id, :id1, :id2, :artist_id, :artist_id1, :artist_id2
3133
- b = lambda{|ds| ds.where(:name=>'B')}
3134
- c = {:name=>'A'}
3135
-
3136
- many_to_one :artist, :class=>artist, :key=>:artist_id
3137
- one_to_many :tracks, :class=>track, :key=>:album_id
3138
- one_to_one :track, :class=>track, :key=>:album_id
3139
- one_to_one :album_info, :class=>album_info, :key=>:album_id
3140
- many_to_many :tags, :class=>tag, :left_key=>:album_id, :join_table=>:albums_tags, :right_key=>:tag_id
3141
-
3142
- many_to_one :a_artist, :clone=>:artist, :conditions=>c
3143
- one_to_many :a_tracks, :clone=>:tracks, :conditions=>c
3144
- one_to_one :a_album_info, :clone=>:album_info, :conditions=>c
3145
- many_to_many :a_tags, :clone=>:tags, :conditions=>c
3146
-
3147
- many_to_one :b_artist, :clone=>:artist, &b
3148
- one_to_many :b_tracks, :clone=>:tracks, &b
3149
- one_to_one :b_album_info, :clone=>:album_info, &b
3150
- many_to_many :b_tags, :clone=>:tags, &b
3151
-
3152
- one_to_many :l_tracks, :clone=>:tracks, :limit=>10
3153
- one_to_one :l_track, :clone=>:tracks, :order=>:name
3154
- many_to_many :l_tags, :clone=>:tags, :limit=>10
3155
- one_through_one :l_tag, :clone=>:tags, :order=>:name
3156
-
3157
- one_to_many :al_tracks, :clone=>:l_tracks, :conditions=>c
3158
- one_to_one :al_track, :clone=>:l_track, :conditions=>c
3159
- many_to_many :al_tags, :clone=>:l_tags, :conditions=>c
3160
- one_through_one :al_tag, :clone=>:l_tag, :conditions=>c
3161
-
3162
- many_to_one :cartist, :class=>artist, :key=>[:artist_id1, :artist_id2], :primary_key=>[:id1, :id2]
3163
- one_to_many :ctracks, :class=>track, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
3164
- one_to_one :calbum_info, :class=>album_info, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
3165
- many_to_many :ctags, :class=>tag, :left_key=>[:album_id1, :album_id2], :left_primary_key=>[:id1, :id2], :right_key=>[:tag_id1, :tag_id2], :right_primary_key=>[:tid1, :tid2], :join_table=>:albums_tags
3166
-
3167
- many_to_one :a_cartist, :clone=>:cartist, :conditions=>c
3168
- one_to_many :a_ctracks, :clone=>:ctracks, :conditions=>c
3169
- one_to_one :a_calbum_info, :clone=>:calbum_info, :conditions=>c
3170
- many_to_many :a_ctags, :clone=>:ctags, :conditions=>c
3171
-
3172
- many_to_one :b_cartist, :clone=>:cartist, &b
3173
- one_to_many :b_ctracks, :clone=>:ctracks, &b
3174
- one_to_one :b_calbum_info, :clone=>:calbum_info, &b
3175
- many_to_many :b_ctags, :clone=>:ctags, &b
3176
-
3177
- one_to_many :l_ctracks, :clone=>:ctracks, :limit=>10
3178
- one_to_one :l_ctrack, :clone=>:ctracks, :order=>:name
3179
- many_to_many :l_ctags, :clone=>:ctags, :limit=>10
3180
- one_through_one :l_ctag, :clone=>:ctags, :order=>:name
3181
-
3182
- one_to_many :al_ctracks, :clone=>:l_ctracks, :conditions=>c
3183
- one_to_one :al_ctrack, :clone=>:l_ctrack, :conditions=>c
3184
- many_to_many :al_ctags, :clone=>:l_ctags, :conditions=>c
3185
- one_through_one :al_ctag, :clone=>:l_ctag, :conditions=>c
3186
- end
3187
- end
3188
- after do
3189
- @Album.default_eager_limit_strategy = true
3190
- end
3191
-
3192
- it "should be able to filter on many_to_one associations" do
3193
- @Album.filter(:artist=>@Artist.load(:id=>3)).sql.must_equal 'SELECT * FROM albums WHERE (albums.artist_id = 3)'
3194
- end
3195
-
3196
- it "should be able to filter on one_to_many associations" do
3197
- @Album.filter(:tracks=>@Track.load(:album_id=>3)).sql.must_equal 'SELECT * FROM albums WHERE (albums.id = 3)'
3198
- end
3199
-
3200
- it "should be able to filter on one_to_one associations" do
3201
- @Album.filter(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.must_equal 'SELECT * FROM albums WHERE (albums.id = 3)'
3202
- end
3203
-
3204
- it "should be able to filter on many_to_many associations" do
3205
- @Album.filter(:tags=>@Tag.load(:id=>3)).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id = 3) AND (albums_tags.album_id IS NOT NULL))))'
3206
- end
3207
-
3208
- it "should be able to filter on many_to_one associations with :conditions" do
3209
- @Album.filter(:a_artist=>@Artist.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((name = 'A') AND (artists.id IS NOT NULL) AND (artists.id = 3))))"
3210
- end
3211
-
3212
- it "should be able to filter on one_to_many associations with :conditions" do
3213
- @Album.filter(:a_tracks=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'A') AND (tracks.album_id IS NOT NULL) AND (tracks.id = 5))))"
3214
- end
3215
-
3216
- it "should be able to filter on one_to_one associations with :conditions" do
3217
- @Album.filter(:a_album_info=>@AlbumInfo.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id = 5))))"
3218
- end
3219
-
3220
- it "should be able to filter on many_to_many associations with :conditions" do
3221
- @Album.filter(:a_tags=>@Tag.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'A') AND (albums_tags.album_id IS NOT NULL) AND (tags.id = 3))))"
3222
- end
3223
-
3224
- it "should be able to filter on many_to_one associations with block" do
3225
- @Album.filter(:b_artist=>@Artist.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((name = 'B') AND (artists.id IS NOT NULL) AND (artists.id = 3))))"
3226
- end
3227
-
3228
- it "should be able to filter on one_to_many associations with block" do
3229
- @Album.filter(:b_tracks=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'B') AND (tracks.album_id IS NOT NULL) AND (tracks.id = 5))))"
3230
- end
3231
-
3232
- it "should be able to filter on one_to_one associations with block" do
3233
- @Album.filter(:b_album_info=>@AlbumInfo.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id = 5))))"
3234
- end
3235
-
3236
- it "should be able to filter on many_to_many associations with block" do
3237
- @Album.filter(:b_tags=>@Tag.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'B') AND (albums_tags.album_id IS NOT NULL) AND (tags.id = 3))))"
3238
- end
3239
-
3240
- it "should be able to filter on one_to_many associations with :limit" do
3241
- @Album.filter(:l_tracks=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x <= 10))) AND (tracks.id = 5))))"
3242
- end
3243
-
3244
- it "should be able to filter on one_to_one associations with :order" do
3245
- @Album.filter(:l_track=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT DISTINCT ON (tracks.album_id) tracks.id FROM tracks ORDER BY tracks.album_id, name)) AND (tracks.id = 5))))"
3246
- end
3247
-
3248
- it "should be able to filter on one_to_one associations with :filter_limit_strategy" do
3249
- @Album.one_to_one :l_track2, :clone=>:track, :filter_limit_strategy=>:window_function
3250
- @Album.filter(:l_track2=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x = 1))) AND (tracks.id = 5))))"
3251
- end
3252
-
3253
- it "should be able to filter on one_to_one associations with :eager_limit_strategy" do
3254
- @Album.one_to_one :l_track2, :clone=>:track, :eager_limit_strategy=>:window_function
3255
- @Album.filter(:l_track2=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x = 1))) AND (tracks.id = 5))))"
3256
- end
3257
-
3258
- it "should be able to filter on one_to_one associations with :order and :filter_limit_strategy" do
3259
- @Album.one_to_one :l_track2, :clone=>:l_track, :filter_limit_strategy=>:window_function
3260
- @Album.filter(:l_track2=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x = 1))) AND (tracks.id = 5))))"
3261
- end
3262
-
3263
- it "should be able to filter on one_to_one associations with :order and :eager_limit_strategy" do
3264
- @Album.one_to_one :l_track2, :clone=>:l_track, :eager_limit_strategy=>:window_function
3265
- @Album.filter(:l_track2=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x = 1))) AND (tracks.id = 5))))"
3266
- end
3267
-
3268
- it "should be able to filter on one_to_one associations with :order and Model.default_eager_limit_strategy" do
3269
- @Album.default_eager_limit_strategy = :window_function
3270
- @Album.one_to_one :l_track2, :clone=>:l_track
3271
- @Album.filter(:l_track2=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x = 1))) AND (tracks.id = 5))))"
3272
- end
3273
-
3274
- it "should be able to filter on one_to_one associations with :order and :eager_limit_strategy=>:union" do
3275
- @Album.one_to_one :l_track2, :clone=>:l_track, :eager_limit_strategy=>:union
3276
- @Album.filter(:l_track2=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT DISTINCT ON (tracks.album_id) tracks.id FROM tracks ORDER BY tracks.album_id, name)) AND (tracks.id = 5))))"
3277
- end
3278
-
3279
- it "should be able to filter on one_to_one associations with :order and :eager_limit_strategy=>:ruby" do
3280
- @Album.one_to_one :l_track2, :clone=>:l_track, :eager_limit_strategy=>:ruby
3281
- @Album.filter(:l_track2=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT DISTINCT ON (tracks.album_id) tracks.id FROM tracks ORDER BY tracks.album_id, name)) AND (tracks.id = 5))))"
3282
- end
3283
-
3284
- it "should be able to filter on one_to_one associations with :filter_limit_strategy :correlated_subquery" do
3285
- @Album.one_to_one :l_track2, :clone=>:track, :filter_limit_strategy=>:correlated_subquery
3286
- @Album.filter(:l_track2=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) LIMIT 1)) AND (tracks.id = 5))))"
3287
- end
3288
-
3289
- it "should be able to filter on many_to_many associations with :limit" do
3290
- @Album.filter(:l_tags=>@Tag.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((albums_tags.album_id IS NOT NULL) AND ((albums_tags.album_id, tags.id) IN (SELECT b, c FROM (SELECT albums_tags.album_id AS b, tags.id AS c, row_number() OVER (PARTITION BY albums_tags.album_id) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id)) AS t1 WHERE (x_sequel_row_number_x <= 10))) AND (tags.id = 3))))"
3291
- end
3292
-
3293
- it "should be able to filter on one_through_one associations with :order" do
3294
- @Album.filter(:l_tag=>@Tag.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((albums_tags.album_id IS NOT NULL) AND ((albums_tags.album_id, tags.id) IN (SELECT DISTINCT ON (albums_tags.album_id) albums_tags.album_id, tags.id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) ORDER BY albums_tags.album_id, name)) AND (tags.id = 3))))"
3295
- end
3296
-
3297
- it "should be able to filter on one_to_many associations with :limit and :conditions" do
3298
- @Album.filter(:al_tracks=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'A') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id) AS x_sequel_row_number_x FROM tracks WHERE (name = 'A')) AS t1 WHERE (x_sequel_row_number_x <= 10))) AND (tracks.id = 5))))"
3299
- end
3300
-
3301
- it "should be able to filter on one_to_one associations with :order and :conditions" do
3302
- @Album.filter(:al_track=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'A') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT DISTINCT ON (tracks.album_id) tracks.id FROM tracks WHERE (name = 'A') ORDER BY tracks.album_id, name)) AND (tracks.id = 5))))"
3303
- end
3304
-
3305
- it "should be able to filter on many_to_many associations with :limit and :conditions" do
3306
- @Album.filter(:al_tags=>@Tag.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'A') AND (albums_tags.album_id IS NOT NULL) AND ((albums_tags.album_id, tags.id) IN (SELECT b, c FROM (SELECT albums_tags.album_id AS b, tags.id AS c, row_number() OVER (PARTITION BY albums_tags.album_id) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE (name = 'A')) AS t1 WHERE (x_sequel_row_number_x <= 10))) AND (tags.id = 3))))"
3307
- end
3308
-
3309
- it "should be able to filter on one_through_one associations with :order and :conditions" do
3310
- @Album.filter(:al_tag=>@Tag.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'A') AND (albums_tags.album_id IS NOT NULL) AND ((albums_tags.album_id, tags.id) IN (SELECT DISTINCT ON (albums_tags.album_id) albums_tags.album_id, tags.id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE (name = 'A') ORDER BY albums_tags.album_id, name)) AND (tags.id = 3))))"
3311
- end
3312
-
3313
- it "should be able to filter on many_to_one associations with composite keys" do
3314
- @Album.filter(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id1 = 3) AND (albums.artist_id2 = 4))'
3315
- end
3316
-
3317
- it "should be able to filter on one_to_many associations with composite keys" do
3318
- @Album.filter(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1 = 3) AND (albums.id2 = 4))'
3319
- end
3320
-
3321
- it "should be able to filter on one_to_one associations with composite keys" do
3322
- @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1 = 3) AND (albums.id2 = 4))'
3323
- end
3324
-
3325
- it "should be able to filter on many_to_many associations with composite keys" do
3326
- @Album.filter(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE ((albums_tags.tag_id1 = 3) AND (albums_tags.tag_id2 = 4) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
3327
- end
3328
-
3329
- it "should be able to filter on many_to_one associations with :conditions and composite keys" do
3330
- @Album.filter(:a_cartist=>@Artist.load(:id=>5, :id1=>3, :id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'A') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id = 5))))"
3331
- end
3332
-
3333
- it "should be able to filter on one_to_many associations with :conditions and composite keys" do
3334
- @Album.filter(:a_ctracks=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'A') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id = 5))))"
3335
- end
3336
-
3337
- it "should be able to filter on one_to_one associations with :conditions and composite keys" do
3338
- @Album.filter(:a_calbum_info=>@AlbumInfo.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id = 5))))"
3339
- end
3340
-
3341
- it "should be able to filter on many_to_many associations with block and composite keys" do
3342
- @Album.filter(:a_ctags=>@Tag.load(:id=>5, :tid1=>3, :tid2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'A') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id = 5))))"
3343
- end
3344
-
3345
- it "should be able to filter on many_to_one associations with block and composite keys" do
3346
- @Album.filter(:b_cartist=>@Artist.load(:id=>5, :id1=>3, :id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'B') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id = 5))))"
3347
- end
3348
-
3349
- it "should be able to filter on one_to_many associations with block and composite keys" do
3350
- @Album.filter(:b_ctracks=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'B') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id = 5))))"
3351
- end
3352
-
3353
- it "should be able to filter on one_to_one associations with block and composite keys" do
3354
- @Album.filter(:b_calbum_info=>@AlbumInfo.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id = 5))))"
3355
- end
3356
-
3357
- it "should be able to filter on many_to_many associations with block and composite keys" do
3358
- @Album.filter(:b_ctags=>@Tag.load(:id=>5, :tid1=>3, :tid2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'B') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id = 5))))"
3359
- end
3360
-
3361
- it "should be able to filter on one_to_many associations with :limit and composite keys" do
3362
- @Album.filter(:l_ctracks=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id1, tracks.album_id2) AS x_sequel_row_number_x FROM tracks) AS t1 WHERE (x_sequel_row_number_x <= 10))) AND (tracks.id = 5))))"
3363
- end
3364
-
3365
- it "should be able to filter on one_to_many associations with composite keys and :filter_limit_strategy :correlated_subquery" do
3366
- @Album.one_to_one :l_ctracks2, :clone=>:l_ctracks, :filter_limit_strategy=>:correlated_subquery
3367
- @Album.filter(:l_ctracks2=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE ((t1.album_id1 = tracks.album_id1) AND (t1.album_id2 = tracks.album_id2)) LIMIT 1)) AND (tracks.id = 5))))"
3368
- end
3369
-
3370
- it "should be able to filter on one_to_one associations with :order and composite keys" do
3371
- @Album.filter(:l_ctrack=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT DISTINCT ON (tracks.album_id1, tracks.album_id2) tracks.id FROM tracks ORDER BY tracks.album_id1, tracks.album_id2, name)) AND (tracks.id = 5))))"
3372
- end
3373
-
3374
- it "should be able to filter on many_to_many associations with :limit and composite keys" do
3375
- @Album.filter(:l_ctags=>@Tag.load(:id=>5, :tid1=>3, :tid2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND ((albums_tags.album_id1, albums_tags.album_id2, tags.id) IN (SELECT b, c, d FROM (SELECT albums_tags.album_id1 AS b, albums_tags.album_id2 AS c, tags.id AS d, row_number() OVER (PARTITION BY albums_tags.album_id1, albums_tags.album_id2) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2))) AS t1 WHERE (x_sequel_row_number_x <= 10))) AND (tags.id = 5))))"
3376
- end
3377
-
3378
- it "should be able to filter on one_through_one associations with :order and composite keys" do
3379
- @Album.filter(:l_ctag=>@Tag.load(:id=>5, :tid1=>3, :tid2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND ((albums_tags.album_id1, albums_tags.album_id2, tags.id) IN (SELECT DISTINCT ON (albums_tags.album_id1, albums_tags.album_id2) albums_tags.album_id1, albums_tags.album_id2, tags.id FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) ORDER BY albums_tags.album_id1, albums_tags.album_id2, name)) AND (tags.id = 5))))"
3380
- end
3381
-
3382
- it "should be able to filter on one_to_many associations with :limit and :conditions and composite keys" do
3383
- @Album.filter(:al_ctracks=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'A') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT id FROM (SELECT tracks.id, row_number() OVER (PARTITION BY tracks.album_id1, tracks.album_id2) AS x_sequel_row_number_x FROM tracks WHERE (name = 'A')) AS t1 WHERE (x_sequel_row_number_x <= 10))) AND (tracks.id = 5))))"
3384
- end
3385
-
3386
- it "should be able to filter on one_to_one associations with :order and :conditions and composite keys" do
3387
- @Album.filter(:al_ctrack=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'A') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT DISTINCT ON (tracks.album_id1, tracks.album_id2) tracks.id FROM tracks WHERE (name = 'A') ORDER BY tracks.album_id1, tracks.album_id2, name)) AND (tracks.id = 5))))"
3388
- end
3389
-
3390
- it "should be able to filter on many_to_many associations with :limit and :conditions and composite keys" do
3391
- @Album.filter(:al_ctags=>@Tag.load(:id=>5, :tid1=>3, :tid2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'A') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND ((albums_tags.album_id1, albums_tags.album_id2, tags.id) IN (SELECT b, c, d FROM (SELECT albums_tags.album_id1 AS b, albums_tags.album_id2 AS c, tags.id AS d, row_number() OVER (PARTITION BY albums_tags.album_id1, albums_tags.album_id2) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE (name = 'A')) AS t1 WHERE (x_sequel_row_number_x <= 10))) AND (tags.id = 5))))"
3392
- end
3393
-
3394
- it "should be able to filter on one_through_one associations with :order and :conditions and composite keys" do
3395
- @Album.filter(:al_ctag=>@Tag.load(:id=>5, :tid1=>3, :tid2=>4)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'A') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND ((albums_tags.album_id1, albums_tags.album_id2, tags.id) IN (SELECT DISTINCT ON (albums_tags.album_id1, albums_tags.album_id2) albums_tags.album_id1, albums_tags.album_id2, tags.id FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE (name = 'A') ORDER BY albums_tags.album_id1, albums_tags.album_id2, name)) AND (tags.id = 5))))"
3396
- end
3397
-
3398
- it "should work inside a complex filter" do
3399
- artist = @Artist.load(:id=>3)
3400
- @Album.filter{foo & {:artist=>artist}}.sql.must_equal 'SELECT * FROM albums WHERE (foo AND (albums.artist_id = 3))'
3401
- track = @Track.load(:album_id=>4)
3402
- @Album.filter{foo & [[:artist, artist], [:tracks, track]]}.sql.must_equal 'SELECT * FROM albums WHERE (foo AND (albums.artist_id = 3) AND (albums.id = 4))'
3403
- end
3404
-
3405
- it "should raise for an invalid association name" do
3406
- proc{@Album.filter(:foo=>@Artist.load(:id=>3)).sql}.must_raise(Sequel::Error)
3407
- end
3408
-
3409
- it "should raise for an invalid association type" do
3410
- @Album.many_to_many :iatags, :clone=>:tags
3411
- @Album.association_reflection(:iatags)[:type] = :foo
3412
- proc{@Album.filter(:iatags=>@Tag.load(:id=>3)).sql}.must_raise(Sequel::Error)
3413
- end
3414
-
3415
- it "should raise for an invalid associated object class " do
3416
- proc{@Album.filter(:tags=>@Artist.load(:id=>3)).sql}.must_raise(Sequel::Error)
3417
- end
3418
-
3419
- it "should raise for an invalid associated object class when multiple objects are used" do
3420
- proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.must_raise(Sequel::Error)
3421
- end
3422
-
3423
- it "should correctly handle case when a multiple value association is used" do
3424
- proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.must_raise(Sequel::Error)
3425
- end
3426
-
3427
- it "should not affect non-association IN/NOT IN filtering with an empty array" do
3428
- @Album.filter(:tag_id=>[]).sql.must_equal 'SELECT * FROM albums WHERE (1 = 0)'
3429
- @Album.exclude(:tag_id=>[]).sql.must_equal 'SELECT * FROM albums WHERE (1 = 1)'
3430
- end
3431
-
3432
- it "should work correctly in subclasses" do
3433
- c = Class.new(@Album)
3434
- c.many_to_one :sartist, :class=>@Artist
3435
- c.filter(:sartist=>@Artist.load(:id=>3)).sql.must_equal 'SELECT * FROM albums WHERE (albums.sartist_id = 3)'
3436
- end
3437
-
3438
- it "should be able to exclude on many_to_one associations" do
3439
- @Album.exclude(:artist=>@Artist.load(:id=>3)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id != 3) OR (albums.artist_id IS NULL))'
3440
- end
3441
-
3442
- it "should be able to exclude on one_to_many associations" do
3443
- @Album.exclude(:tracks=>@Track.load(:album_id=>3)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id != 3) OR (albums.id IS NULL))'
3444
- end
3445
-
3446
- it "should be able to exclude on one_to_one associations" do
3447
- @Album.exclude(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id != 3) OR (albums.id IS NULL))'
3448
- end
3449
-
3450
- it "should be able to exclude on many_to_many associations" do
3451
- @Album.exclude(:tags=>@Tag.load(:id=>3)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id = 3) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
3452
- end
3453
-
3454
- it "should be able to exclude on many_to_one associations with :conditions" do
3455
- @Album.exclude(:a_artist=>@Artist.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((name = 'A') AND (artists.id IS NOT NULL) AND (artists.id = 3)))) OR (albums.artist_id IS NULL))"
3456
- end
3457
-
3458
- it "should be able to exclude on one_to_many associations with :conditions" do
3459
- @Album.exclude(:a_tracks=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'A') AND (tracks.album_id IS NOT NULL) AND (tracks.id = 5)))) OR (albums.id IS NULL))"
3460
- end
3461
-
3462
- it "should be able to exclude on one_to_one associations with :conditions" do
3463
- @Album.exclude(:a_album_info=>@AlbumInfo.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id = 5)))) OR (albums.id IS NULL))"
3464
- end
3465
-
3466
- it "should be able to exclude on many_to_many associations with :conditions" do
3467
- @Album.exclude(:a_tags=>@Tag.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'A') AND (albums_tags.album_id IS NOT NULL) AND (tags.id = 3)))) OR (albums.id IS NULL))"
3468
- end
3469
-
3470
- it "should be able to exclude on many_to_one associations with block" do
3471
- @Album.exclude(:b_artist=>@Artist.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((name = 'B') AND (artists.id IS NOT NULL) AND (artists.id = 3)))) OR (albums.artist_id IS NULL))"
3472
- end
3473
-
3474
- it "should be able to exclude on one_to_many associations with block" do
3475
- @Album.exclude(:b_tracks=>@Track.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'B') AND (tracks.album_id IS NOT NULL) AND (tracks.id = 5)))) OR (albums.id IS NULL))"
3476
- end
3477
-
3478
- it "should be able to exclude on one_to_one associations with block" do
3479
- @Album.exclude(:b_album_info=>@AlbumInfo.load(:id=>5, :album_id=>3)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id = 5)))) OR (albums.id IS NULL))"
3480
- end
3481
-
3482
- it "should be able to exclude on many_to_many associations with block" do
3483
- @Album.exclude(:b_tags=>@Tag.load(:id=>3)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'B') AND (albums_tags.album_id IS NOT NULL) AND (tags.id = 3)))) OR (albums.id IS NULL))"
3484
- end
3485
-
3486
- it "should be able to exclude on many_to_one associations with composite keys" do
3487
- @Album.exclude(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id1 != 3) OR (albums.artist_id2 != 4) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
3488
- end
3489
-
3490
- it "should be able to exclude on one_to_many associations with composite keys" do
3491
- @Album.exclude(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1 != 3) OR (albums.id2 != 4) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3492
- end
3493
-
3494
- it "should be able to exclude on one_to_one associations with composite keys" do
3495
- @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1 != 3) OR (albums.id2 != 4) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3496
- end
3497
-
3498
- it "should be able to exclude on many_to_many associations with composite keys" do
3499
- @Album.exclude(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE ((albums_tags.tag_id1 = 3) AND (albums_tags.tag_id2 = 4) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3500
- end
3501
-
3502
- it "should be able to exclude on many_to_one associations with :conditions and composite keys" do
3503
- @Album.exclude(:a_cartist=>@Artist.load(:id=>5, :id1=>3, :id2=>4)).sql.must_equal "SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'A') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id = 5)))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))"
3504
- end
3505
-
3506
- it "should be able to exclude on one_to_many associations with :conditions and composite keys" do
3507
- @Album.exclude(:a_ctracks=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'A') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id = 5)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3508
- end
3509
-
3510
- it "should be able to exclude on one_to_one associations with :conditions and composite keys" do
3511
- @Album.exclude(:a_calbum_info=>@AlbumInfo.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id = 5)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3512
- end
3513
-
3514
- it "should be able to exclude on many_to_many associations with block and composite keys" do
3515
- @Album.exclude(:a_ctags=>@Tag.load(:id=>5, :tid1=>3, :tid2=>4)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'A') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id = 5)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3516
- end
3517
-
3518
- it "should be able to exclude on many_to_one associations with block and composite keys" do
3519
- @Album.exclude(:b_cartist=>@Artist.load(:id=>5, :id1=>3, :id2=>4)).sql.must_equal "SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'B') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id = 5)))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))"
3520
- end
3521
-
3522
- it "should be able to exclude on one_to_many associations with block and composite keys" do
3523
- @Album.exclude(:b_ctracks=>@Track.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'B') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id = 5)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3524
- end
3525
-
3526
- it "should be able to exclude on one_to_one associations with block and composite keys" do
3527
- @Album.exclude(:b_calbum_info=>@AlbumInfo.load(:id=>5, :album_id1=>3, :album_id2=>4)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id = 5)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3528
- end
3529
-
3530
- it "should be able to exclude on many_to_many associations with block and composite keys" do
3531
- @Album.exclude(:b_ctags=>@Tag.load(:id=>5, :tid1=>3, :tid2=>4)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'B') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id = 5)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3532
- end
3533
-
3534
- it "should be able to filter on multiple many_to_one associations" do
3535
- @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.must_equal 'SELECT * FROM albums WHERE (albums.artist_id IN (3, 4))'
3536
- end
3537
-
3538
- it "should be able to filter on multiple one_to_many associations" do
3539
- @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (3, 4))'
3540
- end
3541
-
3542
- it "should be able to filter on multiple one_to_one associations" do
3543
- @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (3, 4))'
3544
- end
3545
-
3546
- it "should be able to filter on multiple many_to_many associations" do
3547
- @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3, 4)) AND (albums_tags.album_id IS NOT NULL))))'
3548
- end
3549
-
3550
- it "should be able to filter on multiple many_to_one associations with :conditions" do
3551
- @Album.filter(:a_artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.must_equal "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((name = 'A') AND (artists.id IS NOT NULL) AND (artists.id IN (3, 4)))))"
3552
- end
3553
-
3554
- it "should be able to filter on multiple one_to_many associations with :conditions" do
3555
- @Album.filter(:a_tracks=>[@Track.load(:id=>5, :album_id=>3), @Track.load(:id=>6, :album_id=>4)]).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'A') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (5, 6)))))"
3556
- end
3557
-
3558
- it "should be able to filter on multiple one_to_one associations with :conditions" do
3559
- @Album.filter(:a_album_info=>[@AlbumInfo.load(:id=>5, :album_id=>3), @AlbumInfo.load(:id=>6, :album_id=>4)]).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id IN (5, 6)))))"
3560
- end
3561
-
3562
- it "should be able to filter on multiple many_to_many associations with :conditions" do
3563
- @Album.filter(:a_tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'A') AND (albums_tags.album_id IS NOT NULL) AND (tags.id IN (3, 4)))))"
3564
- end
3565
-
3566
- it "should be able to filter on multiple many_to_one associations with block" do
3567
- @Album.filter(:b_artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.must_equal "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((name = 'B') AND (artists.id IS NOT NULL) AND (artists.id IN (3, 4)))))"
3568
- end
3569
-
3570
- it "should be able to filter on multiple one_to_many associations with block" do
3571
- @Album.filter(:b_tracks=>[@Track.load(:id=>5, :album_id=>3), @Track.load(:id=>6, :album_id=>4)]).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'B') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (5, 6)))))"
3572
- end
3573
-
3574
- it "should be able to filter on multiple one_to_one associations with block" do
3575
- @Album.filter(:b_album_info=>[@AlbumInfo.load(:id=>5, :album_id=>3), @AlbumInfo.load(:id=>6, :album_id=>4)]).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id IN (5, 6)))))"
3576
- end
3577
-
3578
- it "should be able to filter on multiple many_to_many associations with block" do
3579
- @Album.filter(:b_tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'B') AND (albums_tags.album_id IS NOT NULL) AND (tags.id IN (3, 4)))))"
3580
- end
3581
-
3582
- it "should be able to filter on multiple many_to_one associations with composite keys" do
3583
- @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4), (5, 6)))'
3584
- end
3585
-
3586
- it "should be able to filter on multiple one_to_many associations with composite keys" do
3587
- @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4), (5, 6)))'
3588
- end
3589
-
3590
- it "should be able to filter on multiple one_to_one associations with composite keys" do
3591
- @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4), (5, 6)))'
3592
- end
3593
-
3594
- it "should be able to filter on multiple many_to_many associations with composite keys" do
3595
- @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4), (5, 6))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
3596
- end
3597
-
3598
- it "should be able to filter on multiple many_to_one associations with :conditions and composite keys" do
3599
- @Album.filter(:a_cartist=>[@Artist.load(:id=>7, :id1=>3, :id2=>4), @Artist.load(:id=>8, :id1=>5, :id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'A') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id IN (7, 8)))))"
3600
- end
3601
-
3602
- it "should be able to filter on multiple one_to_many associations with :conditions and composite keys" do
3603
- @Album.filter(:a_ctracks=>[@Track.load(:id=>7, :album_id1=>3, :album_id2=>4), @Track.load(:id=>8, :album_id1=>5, :album_id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'A') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (7, 8)))))"
3604
- end
3605
-
3606
- it "should be able to filter on multiple one_to_one associations with :conditions and composite keys" do
3607
- @Album.filter(:a_calbum_info=>[@AlbumInfo.load(:id=>7, :album_id1=>3, :album_id2=>4), @AlbumInfo.load(:id=>8, :album_id1=>5, :album_id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id IN (7, 8)))))"
3608
- end
3609
-
3610
- it "should be able to filter on multiple many_to_many associations with block and composite keys" do
3611
- @Album.filter(:a_ctags=>[@Tag.load(:id=>7, :tid1=>3, :tid2=>4), @Tag.load(:id=>8, :tid1=>5, :tid2=>6)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'A') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id IN (7, 8)))))"
3612
- end
3613
-
3614
- it "should be able to filter on multiple many_to_one associations with block and composite keys" do
3615
- @Album.filter(:b_cartist=>[@Artist.load(:id=>7, :id1=>3, :id2=>4), @Artist.load(:id=>8, :id1=>5, :id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'B') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id IN (7, 8)))))"
3616
- end
3617
-
3618
- it "should be able to filter on multiple one_to_many associations with block and composite keys" do
3619
- @Album.filter(:b_ctracks=>[@Track.load(:id=>7, :album_id1=>3, :album_id2=>4), @Track.load(:id=>8, :album_id1=>5, :album_id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'B') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (7, 8)))))"
3620
- end
3621
-
3622
- it "should be able to filter on multiple one_to_one associations with block and composite keys" do
3623
- @Album.filter(:b_calbum_info=>[@AlbumInfo.load(:id=>7, :album_id1=>3, :album_id2=>4), @AlbumInfo.load(:id=>8, :album_id1=>5, :album_id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id IN (7, 8)))))"
3624
- end
3625
-
3626
- it "should be able to filter on multiple many_to_many associations with block and composite keys" do
3627
- @Album.filter(:b_ctags=>[@Tag.load(:id=>7, :tid1=>3, :tid2=>4), @Tag.load(:id=>8, :tid1=>5, :tid2=>6)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'B') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id IN (7, 8)))))"
3628
- end
3629
-
3630
- it "should be able to exclude on multiple many_to_one associations" do
3631
- @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (3, 4)) OR (albums.artist_id IS NULL))'
3632
- end
3633
-
3634
- it "should be able to exclude on multiple one_to_many associations" do
3635
- @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (3, 4)) OR (albums.id IS NULL))'
3636
- end
3637
-
3638
- it "should be able to exclude on multiple one_to_one associations" do
3639
- @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (3, 4)) OR (albums.id IS NULL))'
3640
- end
3641
-
3642
- it "should be able to exclude on multiple many_to_many associations" do
3643
- @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3, 4)) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
3644
- end
3645
-
3646
- it "should be able to exclude on multiple many_to_one associations with :conditions" do
3647
- @Album.exclude(:a_artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((name = 'A') AND (artists.id IS NOT NULL) AND (artists.id IN (3, 4))))) OR (albums.artist_id IS NULL))"
3648
- end
3649
-
3650
- it "should be able to exclude on multiple one_to_many associations with :conditions" do
3651
- @Album.exclude(:a_tracks=>[@Track.load(:id=>5, :album_id=>3), @Track.load(:id=>6, :album_id=>4)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'A') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (5, 6))))) OR (albums.id IS NULL))"
3652
- end
3653
-
3654
- it "should be able to exclude on multiple one_to_one associations with :conditions" do
3655
- @Album.exclude(:a_album_info=>[@AlbumInfo.load(:id=>5, :album_id=>3), @AlbumInfo.load(:id=>6, :album_id=>4)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id IN (5, 6))))) OR (albums.id IS NULL))"
3656
- end
3657
-
3658
- it "should be able to exclude on multiple many_to_many associations with :conditions" do
3659
- @Album.exclude(:a_tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'A') AND (albums_tags.album_id IS NOT NULL) AND (tags.id IN (3, 4))))) OR (albums.id IS NULL))"
3660
- end
3661
-
3662
- it "should be able to exclude on multiple many_to_one associations with block" do
3663
- @Album.exclude(:b_artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((name = 'B') AND (artists.id IS NOT NULL) AND (artists.id IN (3, 4))))) OR (albums.artist_id IS NULL))"
3664
- end
3665
-
3666
- it "should be able to exclude on multiple one_to_many associations with block" do
3667
- @Album.exclude(:b_tracks=>[@Track.load(:id=>5, :album_id=>3), @Track.load(:id=>6, :album_id=>4)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'B') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (5, 6))))) OR (albums.id IS NULL))"
3668
- end
3669
-
3670
- it "should be able to exclude on multiple one_to_one associations with block" do
3671
- @Album.exclude(:b_album_info=>[@AlbumInfo.load(:id=>5, :album_id=>3), @AlbumInfo.load(:id=>6, :album_id=>4)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id IN (5, 6))))) OR (albums.id IS NULL))"
3672
- end
3673
-
3674
- it "should be able to exclude on multiple many_to_many associations with block" do
3675
- @Album.exclude(:b_tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'B') AND (albums_tags.album_id IS NOT NULL) AND (tags.id IN (3, 4))))) OR (albums.id IS NULL))"
3676
- end
3677
-
3678
- it "should be able to exclude on multiple many_to_one associations with composite keys" do
3679
- @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4), (5, 6))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
3680
- end
3681
-
3682
- it "should be able to exclude on multiple one_to_many associations with composite keys" do
3683
- @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4), (5, 6))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3684
- end
3685
-
3686
- it "should be able to exclude on multiple one_to_one associations with composite keys" do
3687
- @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4), (5, 6))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3688
- end
3689
-
3690
- it "should be able to exclude on multiple many_to_many associations with composite keys" do
3691
- @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4), (5, 6))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3692
- end
3693
-
3694
- it "should be able to exclude on multiple many_to_one associations with :conditions and composite keys" do
3695
- @Album.exclude(:a_cartist=>[@Artist.load(:id=>7, :id1=>3, :id2=>4), @Artist.load(:id=>8, :id1=>5, :id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'A') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id IN (7, 8))))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))"
3696
- end
3697
-
3698
- it "should be able to exclude on multiple one_to_many associations with :conditions and composite keys" do
3699
- @Album.exclude(:a_ctracks=>[@Track.load(:id=>7, :album_id1=>3, :album_id2=>4), @Track.load(:id=>8, :album_id1=>5, :album_id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'A') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (7, 8))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3700
- end
3701
-
3702
- it "should be able to exclude on multiple one_to_one associations with :conditions and composite keys" do
3703
- @Album.exclude(:a_calbum_info=>[@AlbumInfo.load(:id=>7, :album_id1=>3, :album_id2=>4), @AlbumInfo.load(:id=>8, :album_id1=>5, :album_id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id IN (7, 8))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3704
- end
3705
-
3706
- it "should be able to exclude on multiple many_to_many associations with :conditions and composite keys" do
3707
- @Album.exclude(:a_ctags=>[@Tag.load(:id=>7, :tid1=>3, :tid2=>4), @Tag.load(:id=>8, :tid1=>5, :tid2=>6)]).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'A') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id IN (7, 8))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3708
- end
3709
-
3710
- it "should be able to exclude on multiple many_to_one associations with block and composite keys" do
3711
- @Album.exclude(:b_cartist=>[@Artist.load(:id=>7, :id1=>3, :id2=>4), @Artist.load(:id=>8, :id1=>5, :id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'B') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id IN (7, 8))))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))"
3712
- end
3713
-
3714
- it "should be able to exclude on multiple one_to_many associations with block and composite keys" do
3715
- @Album.exclude(:b_ctracks=>[@Track.load(:id=>7, :album_id1=>3, :album_id2=>4), @Track.load(:id=>8, :album_id1=>5, :album_id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'B') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (7, 8))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3716
- end
3717
-
3718
- it "should be able to exclude on multiple one_to_one associations with block and composite keys" do
3719
- @Album.exclude(:b_calbum_info=>[@AlbumInfo.load(:id=>7, :album_id1=>3, :album_id2=>4), @AlbumInfo.load(:id=>8, :album_id1=>5, :album_id2=>6)]).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id IN (7, 8))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3720
- end
3721
-
3722
- it "should be able to exclude on multiple many_to_many associations with block and composite keys" do
3723
- @Album.exclude(:b_ctags=>[@Tag.load(:id=>7, :tid1=>3, :tid2=>4), @Tag.load(:id=>8, :tid1=>5, :tid2=>6)]).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'B') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id IN (7, 8))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
3724
- end
3725
-
3726
- it "should be able to handle NULL values when filtering many_to_one associations" do
3727
- @Album.filter(:artist=>@Artist.new).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3728
- end
3729
-
3730
- it "should be able to handle NULL values when filtering one_to_many associations" do
3731
- @Album.filter(:tracks=>@Track.new).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3732
- end
3733
-
3734
- it "should be able to handle NULL values when filtering one_to_one associations" do
3735
- @Album.filter(:album_info=>@AlbumInfo.new).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3736
- end
3737
-
3738
- it "should be able to handle NULL values when filtering many_to_many associations" do
3739
- @Album.filter(:tags=>@Tag.new).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3740
- end
3741
-
3742
- it "should be able to handle filtering with NULL values for many_to_one associations with composite keys" do
3743
- @Album.filter(:cartist=>@Artist.load(:id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3744
- @Album.filter(:cartist=>@Artist.load(:id1=>3)).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3745
- @Album.filter(:cartist=>@Artist.new).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3746
- end
3747
-
3748
- it "should be able to filter with NULL values for one_to_many associations with composite keys" do
3749
- @Album.filter(:ctracks=>@Track.load(:album_id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3750
- @Album.filter(:ctracks=>@Track.load(:album_id1=>3)).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3751
- @Album.filter(:ctracks=>@Track.new).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3752
- end
3753
-
3754
- it "should be able to filter with NULL values for one_to_one associations with composite keys" do
3755
- @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3756
- @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3757
- @Album.filter(:calbum_info=>@AlbumInfo.new).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3758
- end
3759
-
3760
- it "should be able to filter with NULL values for many_to_many associations with composite keys" do
3761
- @Album.filter(:ctags=>@Tag.load(:tid1=>3)).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3762
- @Album.filter(:ctags=>@Tag.load(:tid2=>4)).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3763
- @Album.filter(:ctags=>@Tag.new).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3764
- end
3765
-
3766
- it "should be able to handle NULL values when excluding many_to_one associations" do
3767
- @Album.exclude(:artist=>@Artist.new).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3768
- end
3769
-
3770
- it "should be able to handle NULL values when excluding one_to_many associations" do
3771
- @Album.exclude(:tracks=>@Track.new).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3772
- end
3773
-
3774
- it "should be able to handle NULL values when excluding one_to_one associations" do
3775
- @Album.exclude(:album_info=>@AlbumInfo.new).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3776
- end
3777
-
3778
- it "should be able to handle NULL values when excluding many_to_many associations" do
3779
- @Album.exclude(:tags=>@Tag.new).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3780
- end
3781
-
3782
- it "should be able to handle excluding with NULL values for many_to_one associations with composite keys" do
3783
- @Album.exclude(:cartist=>@Artist.load(:id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3784
- @Album.exclude(:cartist=>@Artist.load(:id1=>3)).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3785
- @Album.exclude(:cartist=>@Artist.new).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3786
- end
3787
-
3788
- it "should be able to excluding with NULL values for one_to_many associations with composite keys" do
3789
- @Album.exclude(:ctracks=>@Track.load(:album_id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3790
- @Album.exclude(:ctracks=>@Track.load(:album_id1=>3)).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3791
- @Album.exclude(:ctracks=>@Track.new).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3792
- end
3793
-
3794
- it "should be able to excluding with NULL values for one_to_one associations with composite keys" do
3795
- @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3796
- @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3797
- @Album.exclude(:calbum_info=>@AlbumInfo.new).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3798
- end
3799
-
3800
- it "should be able to excluding with NULL values for many_to_many associations with composite keys" do
3801
- @Album.exclude(:ctags=>@Tag.load(:tid1=>3)).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3802
- @Album.exclude(:ctags=>@Tag.load(:tid2=>4)).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3803
- @Album.exclude(:ctags=>@Tag.new).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3804
- end
3805
-
3806
- it "should be able to handle NULL values when filtering multiple many_to_one associations" do
3807
- @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.must_equal 'SELECT * FROM albums WHERE (albums.artist_id IN (3))'
3808
- @Album.filter(:artist=>[@Artist.new, @Artist.new]).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3809
- end
3810
-
3811
- it "should be able to handle NULL values when filtering multiple one_to_many associations" do
3812
- @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (3))'
3813
- @Album.filter(:tracks=>[@Track.new, @Track.new]).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3814
- end
3815
-
3816
- it "should be able to handle NULL values when filtering multiple one_to_one associations" do
3817
- @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (3))'
3818
- @Album.filter(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3819
- end
3820
-
3821
- it "should be able to handle NULL values when filtering multiple many_to_many associations" do
3822
- @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3)) AND (albums_tags.album_id IS NOT NULL))))'
3823
- @Album.filter(:tags=>[@Tag.new, @Tag.new]).sql.must_equal 'SELECT * FROM albums WHERE \'f\''
3824
- end
3825
-
3826
- it "should be able to handle NULL values when filtering multiple many_to_one associations with composite keys" do
3827
- @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4)))'
3828
- @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN ((3, 4)))'
3829
- end
3830
-
3831
- it "should be able handle NULL values when filtering multiple one_to_many associations with composite keys" do
3832
- @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
3833
- @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
3834
- end
3835
-
3836
- it "should be able to handle NULL values when filtering multiple one_to_one associations with composite keys" do
3837
- @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
3838
- @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN ((3, 4)))'
3839
- end
3840
-
3841
- it "should be able to handle NULL values when filtering multiple many_to_many associations with composite keys" do
3842
- @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
3843
- @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
3844
- end
3845
-
3846
- it "should be able to handle NULL values when excluding multiple many_to_one associations" do
3847
- @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (3)) OR (albums.artist_id IS NULL))'
3848
- @Album.exclude(:artist=>[@Artist.new, @Artist.new]).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3849
- end
3850
-
3851
- it "should be able to handle NULL values when excluding multiple one_to_many associations" do
3852
- @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (3)) OR (albums.id IS NULL))'
3853
- @Album.exclude(:tracks=>[@Track.new, @Track.new]).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3854
- end
3855
-
3856
- it "should be able to handle NULL values when excluding multiple one_to_one associations" do
3857
- @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (3)) OR (albums.id IS NULL))'
3858
- @Album.exclude(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3859
- end
3860
-
3861
- it "should be able to handle NULL values when excluding multiple many_to_many associations" do
3862
- @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (3)) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
3863
- @Album.exclude(:tags=>[@Tag.new, @Tag.new]).sql.must_equal 'SELECT * FROM albums WHERE \'t\''
3864
- end
3865
-
3866
- it "should be able to handle NULL values when excluding multiple many_to_one associations with composite keys" do
3867
- @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
3868
- @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN ((3, 4))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
3869
- end
3870
-
3871
- it "should be able handle NULL values when excluding multiple one_to_many associations with composite keys" do
3872
- @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3873
- @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3874
- end
3875
-
3876
- it "should be able to handle NULL values when excluding multiple one_to_one associations with composite keys" do
3877
- @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3878
- @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN ((3, 4))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3879
- end
3880
-
3881
- it "should be able to handle NULL values when excluding multiple many_to_many associations with composite keys" do
3882
- @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3883
- @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN ((3, 4))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
3884
- end
3885
-
3886
- it "should be able to filter on many_to_one association datasets" do
3887
- @Album.filter(:artist=>@Artist.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((x = 1) AND (artists.id IS NOT NULL))))'
3888
- end
3889
-
3890
- it "should be able to filter on one_to_many association datasets" do
3891
- @Album.filter(:tracks=>@Track.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((x = 1) AND (tracks.album_id IS NOT NULL))))'
3892
- end
3893
-
3894
- it "should be able to filter on one_to_one association datasets" do
3895
- @Album.filter(:album_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((x = 1) AND (album_infos.album_id IS NOT NULL))))'
3896
- end
3897
-
3898
- it "should be able to filter on many_to_many association datasets" do
3899
- @Album.filter(:tags=>@Tag.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_tags.album_id IS NOT NULL))))'
3900
- end
3901
-
3902
- it "should be able to filter on many_to_one association datasets with :conditions" do
3903
- @Album.filter(:a_artist=>@Artist.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((name = 'A') AND (artists.id IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (x = 1))))))"
3904
- end
3905
-
3906
- it "should be able to filter on one_to_many association datasets with :conditions" do
3907
- @Album.filter(:a_tracks=>@Track.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'A') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT tracks.id FROM tracks WHERE (x = 1))))))"
3908
- end
3909
-
3910
- it "should be able to filter on one_to_one association datasets with :conditions" do
3911
- @Album.filter(:a_album_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id IN (SELECT album_infos.id FROM album_infos WHERE (x = 1))))))"
3912
- end
3913
-
3914
- it "should be able to filter on many_to_many association datasets with :conditions" do
3915
- @Album.filter(:a_tags=>@Tag.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'A') AND (albums_tags.album_id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1))))))"
3916
- end
3917
-
3918
- it "should be able to filter on many_to_one association datasets with block" do
3919
- @Album.filter(:b_artist=>@Artist.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (albums.artist_id IN (SELECT artists.id FROM artists WHERE ((name = 'B') AND (artists.id IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (x = 1))))))"
3920
- end
3921
-
3922
- it "should be able to filter on one_to_many association datasets with block" do
3923
- @Album.filter(:b_tracks=>@Track.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'B') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT tracks.id FROM tracks WHERE (x = 1))))))"
3924
- end
3925
-
3926
- it "should be able to filter on one_to_one association datasets with block" do
3927
- @Album.filter(:b_album_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id IN (SELECT album_infos.id FROM album_infos WHERE (x = 1))))))"
3928
- end
3929
-
3930
- it "should be able to filter on many_to_many association datasets with block" do
3931
- @Album.filter(:b_tags=>@Tag.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (albums.id IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'B') AND (albums_tags.album_id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1))))))"
3932
- end
3933
-
3934
- it "should be able to filter on many_to_one association datasets with composite keys" do
3935
- @Album.filter(:cartist=>@Artist.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((x = 1) AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL))))'
3936
- end
3937
-
3938
- it "should be able to filter on one_to_many association datasets with composite keys" do
3939
- @Album.filter(:ctracks=>@Track.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((x = 1) AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL))))'
3940
- end
3941
-
3942
- it "should be able to filter on one_to_one association datasets with composite keys" do
3943
- @Album.filter(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((x = 1) AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL))))'
3944
- end
3945
-
3946
- it "should be able to filter on many_to_many association datasets with composite keys" do
3947
- @Album.filter(:ctags=>@Tag.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN (SELECT tags.tid1, tags.tid2 FROM tags WHERE ((x = 1) AND (tags.tid1 IS NOT NULL) AND (tags.tid2 IS NOT NULL)))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL))))'
3948
- end
3949
-
3950
- it "should be able to filter on many_to_one association datasets with :conditions and composite keys" do
3951
- @Album.filter(:a_cartist=>@Artist.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'A') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (x = 1))))))"
3952
- end
3953
-
3954
- it "should be able to filter on one_to_many association datasets with :conditions and composite keys" do
3955
- @Album.filter(:a_ctracks=>@Track.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'A') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT tracks.id FROM tracks WHERE (x = 1))))))"
3956
- end
3957
-
3958
- it "should be able to filter on one_to_one association datasets with :conditions and composite keys" do
3959
- @Album.filter(:a_calbum_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id IN (SELECT album_infos.id FROM album_infos WHERE (x = 1))))))"
3960
- end
3961
-
3962
- it "should be able to filter on many_to_many association datasets with :conditions and composite keys" do
3963
- @Album.filter(:a_ctags=>@Tag.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'A') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1))))))"
3964
- end
3965
-
3966
- it "should be able to filter on many_to_one association datasets with block and composite keys" do
3967
- @Album.filter(:b_cartist=>@Artist.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id1, albums.artist_id2) IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'B') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (x = 1))))))"
3968
- end
3969
-
3970
- it "should be able to filter on one_to_many association datasets with block and composite keys" do
3971
- @Album.filter(:b_ctracks=>@Track.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'B') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT tracks.id FROM tracks WHERE (x = 1))))))"
3972
- end
3973
-
3974
- it "should be able to filter on one_to_one association datasets with block and composite keys" do
3975
- @Album.filter(:b_calbum_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id IN (SELECT album_infos.id FROM album_infos WHERE (x = 1))))))"
3976
- end
3977
-
3978
- it "should be able to filter on many_to_many association datasets with block and composite keys" do
3979
- @Album.filter(:b_ctags=>@Tag.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id1, albums.id2) IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'B') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1))))))"
3980
- end
3981
-
3982
- it "should be able to exclude on many_to_one association datasets" do
3983
- @Album.exclude(:artist=>@Artist.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((x = 1) AND (artists.id IS NOT NULL)))) OR (albums.artist_id IS NULL))'
3984
- end
3985
-
3986
- it "should be able to exclude on one_to_many association datasets" do
3987
- @Album.exclude(:tracks=>@Track.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((x = 1) AND (tracks.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
3988
- end
3989
-
3990
- it "should be able to exclude on one_to_one association datasets" do
3991
- @Album.exclude(:album_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((x = 1) AND (album_infos.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
3992
- end
3993
-
3994
- it "should be able to exclude on many_to_many association datasets" do
3995
- @Album.exclude(:tags=>@Tag.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM albums_tags WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_tags.album_id IS NOT NULL)))) OR (albums.id IS NULL))'
3996
- end
3997
-
3998
- it "should be able to exclude on many_to_one association datasets with :conditions" do
3999
- @Album.exclude(:a_artist=>@Artist.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((name = 'A') AND (artists.id IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (x = 1)))))) OR (albums.artist_id IS NULL))"
4000
- end
4001
-
4002
- it "should be able to exclude on one_to_many association datasets with :conditions" do
4003
- @Album.exclude(:a_tracks=>@Track.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'A') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT tracks.id FROM tracks WHERE (x = 1)))))) OR (albums.id IS NULL))"
4004
- end
4005
-
4006
- it "should be able to exclude on one_to_one association datasets with :conditions" do
4007
- @Album.exclude(:a_album_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id IN (SELECT album_infos.id FROM album_infos WHERE (x = 1)))))) OR (albums.id IS NULL))"
4008
- end
4009
-
4010
- it "should be able to exclude on many_to_many association datasets with :conditions" do
4011
- @Album.exclude(:a_tags=>@Tag.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'A') AND (albums_tags.album_id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1)))))) OR (albums.id IS NULL))"
4012
- end
4013
-
4014
- it "should be able to exclude on many_to_one association datasets with block" do
4015
- @Album.exclude(:b_artist=>@Artist.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.artist_id NOT IN (SELECT artists.id FROM artists WHERE ((name = 'B') AND (artists.id IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (x = 1)))))) OR (albums.artist_id IS NULL))"
4016
- end
4017
-
4018
- it "should be able to exclude on one_to_many association datasets with block" do
4019
- @Album.exclude(:b_tracks=>@Track.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT tracks.album_id FROM tracks WHERE ((name = 'B') AND (tracks.album_id IS NOT NULL) AND (tracks.id IN (SELECT tracks.id FROM tracks WHERE (x = 1)))))) OR (albums.id IS NULL))"
4020
- end
4021
-
4022
- it "should be able to exclude on one_to_one association datasets with block" do
4023
- @Album.exclude(:b_album_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT album_infos.album_id FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id IS NOT NULL) AND (album_infos.id IN (SELECT album_infos.id FROM album_infos WHERE (x = 1)))))) OR (albums.id IS NULL))"
4024
- end
4025
-
4026
- it "should be able to exclude on many_to_many association datasets with block" do
4027
- @Album.exclude(:b_tags=>@Tag.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE ((albums.id NOT IN (SELECT albums_tags.album_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) WHERE ((name = 'B') AND (albums_tags.album_id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1)))))) OR (albums.id IS NULL))"
4028
- end
4029
-
4030
- it "should be able to exclude on many_to_one association datasets with composite keys" do
4031
- @Album.exclude(:cartist=>@Artist.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((x = 1) AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL)))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))'
4032
- end
4033
-
4034
- it "should be able to exclude on one_to_many association datasets with composite keys" do
4035
- @Album.exclude(:ctracks=>@Track.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((x = 1) AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
4036
- end
4037
-
4038
- it "should be able to exclude on one_to_one association datasets with composite keys" do
4039
- @Album.exclude(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((x = 1) AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
4040
- end
4041
-
4042
- it "should be able to exclude on many_to_many association datasets with composite keys" do
4043
- @Album.exclude(:ctags=>@Tag.filter(:x=>1)).sql.must_equal 'SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM albums_tags WHERE (((albums_tags.tag_id1, albums_tags.tag_id2) IN (SELECT tags.tid1, tags.tid2 FROM tags WHERE ((x = 1) AND (tags.tid1 IS NOT NULL) AND (tags.tid2 IS NOT NULL)))) AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL)))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))'
4044
- end
4045
-
4046
- it "should be able to exclude on many_to_one association datasets with :conditions and composite keys" do
4047
- @Album.exclude(:a_cartist=>@Artist.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'A') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (x = 1)))))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))"
4048
- end
4049
-
4050
- it "should be able to exclude on one_to_many association datasets with :conditions and composite keys" do
4051
- @Album.exclude(:a_ctracks=>@Track.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'A') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT tracks.id FROM tracks WHERE (x = 1)))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
4052
- end
4053
-
4054
- it "should be able to exclude on one_to_one association datasets with :conditions and composite keys" do
4055
- @Album.exclude(:a_calbum_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'A') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id IN (SELECT album_infos.id FROM album_infos WHERE (x = 1)))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
4056
- end
4057
-
4058
- it "should be able to exclude on many_to_many association datasets with :conditions and composite keys" do
4059
- @Album.exclude(:a_ctags=>@Tag.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'A') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1)))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
4060
- end
4061
-
4062
- it "should be able to exclude on many_to_one association datasets with block and composite keys" do
4063
- @Album.exclude(:b_cartist=>@Artist.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (((albums.artist_id1, albums.artist_id2) NOT IN (SELECT artists.id1, artists.id2 FROM artists WHERE ((name = 'B') AND (artists.id1 IS NOT NULL) AND (artists.id2 IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (x = 1)))))) OR (albums.artist_id1 IS NULL) OR (albums.artist_id2 IS NULL))"
4064
- end
4065
-
4066
- it "should be able to exclude on one_to_many association datasets with block and composite keys" do
4067
- @Album.exclude(:b_ctracks=>@Track.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT tracks.album_id1, tracks.album_id2 FROM tracks WHERE ((name = 'B') AND (tracks.album_id1 IS NOT NULL) AND (tracks.album_id2 IS NOT NULL) AND (tracks.id IN (SELECT tracks.id FROM tracks WHERE (x = 1)))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
4068
- end
4069
-
4070
- it "should be able to exclude on one_to_one association datasets with block and composite keys" do
4071
- @Album.exclude(:b_calbum_info=>@AlbumInfo.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT album_infos.album_id1, album_infos.album_id2 FROM album_infos WHERE ((name = 'B') AND (album_infos.album_id1 IS NOT NULL) AND (album_infos.album_id2 IS NOT NULL) AND (album_infos.id IN (SELECT album_infos.id FROM album_infos WHERE (x = 1)))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
4072
- end
4073
-
4074
- it "should be able to exclude on many_to_many association datasets with block and composite keys" do
4075
- @Album.exclude(:b_ctags=>@Tag.filter(:x=>1)).sql.must_equal "SELECT * FROM albums WHERE (((albums.id1, albums.id2) NOT IN (SELECT albums_tags.album_id1, albums_tags.album_id2 FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id1 = tags.tid1) AND (albums_tags.tag_id2 = tags.tid2)) WHERE ((name = 'B') AND (albums_tags.album_id1 IS NOT NULL) AND (albums_tags.album_id2 IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1)))))) OR (albums.id1 IS NULL) OR (albums.id2 IS NULL))"
4076
- end
4077
-
4078
- it "should do a regular IN query if the dataset for a different model is used" do
4079
- @Album.filter(:artist=>@Album.select(:x)).sql.must_equal 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
4080
- end
4081
-
4082
- it "should do a regular IN query if a non-model dataset is used" do
4083
- @Album.filter(:artist=>@Album.db.from(:albums).select(:x)).sql.must_equal 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
4084
- end
4085
- end
4086
-
4087
- describe "Sequel::Model Associations with clashing column names" do
4088
- before do
4089
- @db = Sequel.mock(:fetch=>{:id=>1, :object_id=>2})
4090
- @Foo = Class.new(Sequel::Model(@db[:foos]))
4091
- @Bar = Class.new(Sequel::Model(@db[:bars]))
4092
- @Foo.columns :id, :object_id
4093
- @Bar.columns :id, :object_id
4094
- @Foo.def_column_alias(:obj_id, :object_id)
4095
- @Bar.def_column_alias(:obj_id, :object_id)
4096
- @Foo.one_to_many :bars, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :class=>@Bar
4097
- @Foo.one_to_one :bar, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :class=>@Bar
4098
- @Bar.many_to_one :foo, :key=>:obj_id, :key_column=>:object_id, :primary_key=>:object_id, :primary_key_method=>:obj_id, :class=>@Foo
4099
- @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
4100
- @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
4101
- @foo = @Foo.load(:id=>1, :object_id=>2)
4102
- @bar = @Bar.load(:id=>1, :object_id=>2)
4103
- @db.sqls
4104
- end
4105
-
4106
- it "should have working regular association methods" do
4107
- @Bar.first.foo.must_equal @foo
4108
- @db.sqls.must_equal ["SELECT * FROM bars LIMIT 1", "SELECT * FROM foos WHERE (foos.object_id = 2) LIMIT 1"]
4109
- @Foo.first.bars.must_equal [@bar]
4110
- @db.sqls.must_equal ["SELECT * FROM foos LIMIT 1", "SELECT * FROM bars WHERE (bars.object_id = 2)"]
4111
- @Foo.first.bar.must_equal @bar
4112
- @db.sqls.must_equal ["SELECT * FROM foos LIMIT 1", "SELECT * FROM bars WHERE (bars.object_id = 2) LIMIT 1"]
4113
- @Foo.first.mtmbars.must_equal [@bar]
4114
- @db.sqls.must_equal ["SELECT * FROM foos LIMIT 1", "SELECT bars.* FROM bars INNER JOIN bars_foos ON (bars_foos.object_id = bars.object_id) WHERE (bars_foos.foo_id = 2)"]
4115
- @Bar.first.mtmfoos.must_equal [@foo]
4116
- @db.sqls.must_equal ["SELECT * FROM bars LIMIT 1", "SELECT foos.* FROM foos INNER JOIN bars_foos ON (bars_foos.foo_id = foos.object_id) WHERE (bars_foos.object_id = 2)"]
4117
- end
4118
-
4119
- it "should have working eager loading methods" do
4120
- @Bar.eager(:foo).all.map{|o| [o, o.foo]}.must_equal [[@bar, @foo]]
4121
- @db.sqls.must_equal ["SELECT * FROM bars", "SELECT * FROM foos WHERE (foos.object_id IN (2))"]
4122
- @Foo.eager(:bars).all.map{|o| [o, o.bars]}.must_equal [[@foo, [@bar]]]
4123
- @db.sqls.must_equal ["SELECT * FROM foos", "SELECT * FROM bars WHERE (bars.object_id IN (2))"]
4124
- @Foo.eager(:bar).all.map{|o| [o, o.bar]}.must_equal [[@foo, @bar]]
4125
- @db.sqls.must_equal ["SELECT * FROM foos", "SELECT * FROM bars WHERE (bars.object_id IN (2))"]
4126
- @db.fetch = [[{:id=>1, :object_id=>2}], [{:id=>1, :object_id=>2, :x_foreign_key_x=>2}]]
4127
- @Foo.eager(:mtmbars).all.map{|o| [o, o.mtmbars]}.must_equal [[@foo, [@bar]]]
4128
- @db.sqls.must_equal ["SELECT * FROM foos", "SELECT bars.*, bars_foos.foo_id AS x_foreign_key_x FROM bars INNER JOIN bars_foos ON (bars_foos.object_id = bars.object_id) WHERE (bars_foos.foo_id IN (2))"]
4129
- @db.fetch = [[{:id=>1, :object_id=>2}], [{:id=>1, :object_id=>2, :x_foreign_key_x=>2}]]
4130
- @Bar.eager(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.must_equal [[@bar, [@foo]]]
4131
- @db.sqls.must_equal ["SELECT * FROM bars", "SELECT foos.*, bars_foos.object_id AS x_foreign_key_x FROM foos INNER JOIN bars_foos ON (bars_foos.foo_id = foos.object_id) WHERE (bars_foos.object_id IN (2))"]
4132
- end
4133
-
4134
- it "should have working eager graphing methods" do
4135
- @db.fetch = {:id=>1, :object_id=>2, :foo_id=>1, :foo_object_id=>2}
4136
- @Bar.eager_graph(:foo).all.map{|o| [o, o.foo]}.must_equal [[@bar, @foo]]
4137
- @db.sqls.must_equal ["SELECT bars.id, bars.object_id, foo.id AS foo_id, foo.object_id AS foo_object_id FROM bars LEFT OUTER JOIN foos AS foo ON (foo.object_id = bars.object_id)"]
4138
- @db.fetch = {:id=>1, :object_id=>2, :bars_id=>1, :bars_object_id=>2}
4139
- @Foo.eager_graph(:bars).all.map{|o| [o, o.bars]}.must_equal [[@foo, [@bar]]]
4140
- @db.sqls.must_equal ["SELECT foos.id, foos.object_id, bars.id AS bars_id, bars.object_id AS bars_object_id FROM foos LEFT OUTER JOIN bars ON (bars.object_id = foos.object_id)"]
4141
- @db.fetch = {:id=>1, :object_id=>2, :bar_id=>1, :bar_object_id=>2}
4142
- @Foo.eager_graph(:bar).all.map{|o| [o, o.bar]}.must_equal [[@foo, @bar]]
4143
- @db.sqls.must_equal ["SELECT foos.id, foos.object_id, bar.id AS bar_id, bar.object_id AS bar_object_id FROM foos LEFT OUTER JOIN bars AS bar ON (bar.object_id = foos.object_id)"]
4144
- @db.fetch = {:id=>1, :object_id=>2, :mtmfoos_id=>1, :mtmfoos_object_id=>2}
4145
- @Bar.eager_graph(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.must_equal [[@bar, [@foo]]]
4146
- @db.sqls.must_equal ["SELECT bars.id, bars.object_id, mtmfoos.id AS mtmfoos_id, mtmfoos.object_id AS mtmfoos_object_id FROM bars LEFT OUTER JOIN bars_foos ON (bars_foos.object_id = bars.object_id) LEFT OUTER JOIN foos AS mtmfoos ON (mtmfoos.object_id = bars_foos.foo_id)"]
4147
- @db.fetch = {:id=>1, :object_id=>2, :mtmbars_id=>1, :mtmbars_object_id=>2}
4148
- @Foo.eager_graph(:mtmbars).all.map{|o| [o, o.mtmbars]}.must_equal [[@foo, [@bar]]]
4149
- @db.sqls.must_equal ["SELECT foos.id, foos.object_id, mtmbars.id AS mtmbars_id, mtmbars.object_id AS mtmbars_object_id FROM foos LEFT OUTER JOIN bars_foos ON (bars_foos.foo_id = foos.object_id) LEFT OUTER JOIN bars AS mtmbars ON (mtmbars.object_id = bars_foos.object_id)"]
4150
- end
4151
-
4152
- it "should have working filter by associations with model instances" do
4153
- @Bar.first(:foo=>@foo).must_equal @bar
4154
- @db.sqls.must_equal ["SELECT * FROM bars WHERE (bars.object_id = 2) LIMIT 1"]
4155
- @Foo.first(:bars=>@bar).must_equal @foo
4156
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_id = 2) LIMIT 1"]
4157
- @Foo.first(:bar=>@bar).must_equal @foo
4158
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_id = 2) LIMIT 1"]
4159
- @Foo.first(:mtmbars=>@bar).must_equal @foo
4160
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_id IN (SELECT bars_foos.foo_id FROM bars_foos WHERE ((bars_foos.object_id = 2) AND (bars_foos.foo_id IS NOT NULL)))) LIMIT 1"]
4161
- @Bar.first(:mtmfoos=>@foo).must_equal @bar
4162
- @db.sqls.must_equal ["SELECT * FROM bars WHERE (bars.object_id IN (SELECT bars_foos.object_id FROM bars_foos WHERE ((bars_foos.foo_id = 2) AND (bars_foos.object_id IS NOT NULL)))) LIMIT 1"]
4163
- end
4164
-
4165
- it "should have working filter by associations for associations with :conditions with model instances" do
4166
- @Bar.many_to_one :foo, :clone=>:foo, :conditions=>{:name=>'A'}
4167
- @Foo.one_to_many :bars, :clone=>:bars, :conditions=>{:name=>'A'}
4168
- @Foo.one_to_one :bar, :clone=>:bars
4169
- @Foo.many_to_many :mtmbars, :clone=>:mtmbars, :conditions=>{:name=>'A'}
4170
- @Bar.many_to_many :mtmfoos, :clone=>:mtmfoos, :conditions=>{:name=>'A'}
4171
-
4172
- @Bar.where(:foo=>@foo).sql.must_equal "SELECT * FROM bars WHERE (bars.object_id IN (SELECT foos.object_id FROM foos WHERE ((name = 'A') AND (foos.object_id IS NOT NULL) AND (foos.id = 1))))"
4173
- @Foo.where(:bars=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_id IN (SELECT bars.object_id FROM bars WHERE ((name = 'A') AND (bars.object_id IS NOT NULL) AND (bars.id = 1))))"
4174
- @Foo.where(:bar=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_id IN (SELECT bars.object_id FROM bars WHERE ((name = 'A') AND (bars.object_id IS NOT NULL) AND (bars.id = 1))))"
4175
- @Foo.where(:mtmbars=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_id IN (SELECT bars_foos.foo_id FROM bars INNER JOIN bars_foos ON (bars_foos.object_id = bars.object_id) WHERE ((name = 'A') AND (bars_foos.foo_id IS NOT NULL) AND (bars.id = 1))))"
4176
- @Bar.where(:mtmfoos=>@foo).sql.must_equal "SELECT * FROM bars WHERE (bars.object_id IN (SELECT bars_foos.object_id FROM foos INNER JOIN bars_foos ON (bars_foos.foo_id = foos.object_id) WHERE ((name = 'A') AND (bars_foos.object_id IS NOT NULL) AND (foos.id = 1))))"
4177
- end
4178
-
4179
- it "should have working filter by associations for associations with block with model instances" do
4180
- b = lambda{|ds| ds.where(:name=>'A')}
4181
- @Bar.many_to_one :foo, :clone=>:foo, &b
4182
- @Foo.one_to_many :bars, :clone=>:bars, &b
4183
- @Foo.one_to_one :bar, :clone=>:bars
4184
- @Foo.many_to_many :mtmbars, :clone=>:mtmbars, &b
4185
- @Bar.many_to_many :mtmfoos, :clone=>:mtmfoos, &b
4186
-
4187
- @Bar.where(:foo=>@foo).sql.must_equal "SELECT * FROM bars WHERE (bars.object_id IN (SELECT foos.object_id FROM foos WHERE ((name = 'A') AND (foos.object_id IS NOT NULL) AND (foos.id = 1))))"
4188
- @Foo.where(:bars=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_id IN (SELECT bars.object_id FROM bars WHERE ((name = 'A') AND (bars.object_id IS NOT NULL) AND (bars.id = 1))))"
4189
- @Foo.where(:bar=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_id IN (SELECT bars.object_id FROM bars WHERE ((name = 'A') AND (bars.object_id IS NOT NULL) AND (bars.id = 1))))"
4190
- @Foo.where(:mtmbars=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_id IN (SELECT bars_foos.foo_id FROM bars INNER JOIN bars_foos ON (bars_foos.object_id = bars.object_id) WHERE ((name = 'A') AND (bars_foos.foo_id IS NOT NULL) AND (bars.id = 1))))"
4191
- @Bar.where(:mtmfoos=>@foo).sql.must_equal "SELECT * FROM bars WHERE (bars.object_id IN (SELECT bars_foos.object_id FROM foos INNER JOIN bars_foos ON (bars_foos.foo_id = foos.object_id) WHERE ((name = 'A') AND (bars_foos.object_id IS NOT NULL) AND (foos.id = 1))))"
4192
- end
4193
-
4194
- it "should have working modification methods" do
4195
- b = @Bar.load(:id=>2, :object_id=>3)
4196
- f = @Foo.load(:id=>2, :object_id=>3)
4197
- @db.numrows = 1
4198
-
4199
- @bar.foo = f
4200
- @bar.obj_id.must_equal 3
4201
- @foo.bar = @bar
4202
- @bar.obj_id.must_equal 2
4203
-
4204
- @foo.add_bar(b)
4205
- @db.fetch = [[{:id=>1, :object_id=>2}, {:id=>2, :object_id=>2}], [{:id=>1, :object_id=>2}]]
4206
- @foo.bars.must_equal [@bar, b]
4207
- @foo.remove_bar(b)
4208
- @foo.bars.must_equal [@bar]
4209
- @foo.remove_all_bars
4210
- @foo.bars.must_equal []
4211
-
4212
- @db.fetch = [[{:id=>1, :object_id=>2}], [], [{:id=>2, :object_id=>2}]]
4213
- @bar = @Bar.load(:id=>1, :object_id=>2)
4214
- @foo.mtmbars.must_equal [@bar]
4215
- @foo.remove_all_mtmbars
4216
- @foo.mtmbars.must_equal []
4217
- @foo.add_mtmbar(b)
4218
- @foo.mtmbars.must_equal [b]
4219
- @foo.remove_mtmbar(b)
4220
- @foo.mtmbars.must_equal []
4221
-
4222
- @db.fetch = [[{:id=>2, :object_id=>3}], [], [{:id=>2, :object_id=>3}]]
4223
- @bar.add_mtmfoo(f)
4224
- @bar.mtmfoos.must_equal [f]
4225
- @bar.remove_all_mtmfoos
4226
- @bar.mtmfoos.must_equal []
4227
- @bar.add_mtmfoo(f)
4228
- @bar.mtmfoos.must_equal [f]
4229
- @bar.remove_mtmfoo(f)
4230
- @bar.mtmfoos.must_equal []
4231
- end
4232
- end
4233
-
4234
- describe "Sequel::Model Associations with non-column expression keys" do
4235
- before do
4236
- @db = Sequel.mock(:fetch=>{:id=>1, :object_ids=>[2]})
4237
- @Foo = Class.new(Sequel::Model(@db[:foos]))
4238
- @Bar = Class.new(Sequel::Model(@db[:bars]))
4239
- @Foo.columns :id, :object_ids
4240
- @Bar.columns :id, :object_ids
4241
- m = Module.new{def obj_id; object_ids[0]; end}
4242
- @Foo.include m
4243
- @Bar.include m
4244
-
4245
- @Foo.one_to_many :bars, :primary_key=>:obj_id, :primary_key_column=>Sequel.subscript(:object_ids, 0), :key=>Sequel.subscript(:object_ids, 0), :key_method=>:obj_id, :class=>@Bar
4246
- @Foo.one_to_one :bar, :primary_key=>:obj_id, :primary_key_column=>Sequel.subscript(:object_ids, 0), :key=>Sequel.subscript(:object_ids, 0), :key_method=>:obj_id, :class=>@Bar
4247
- @Bar.many_to_one :foo, :key=>:obj_id, :key_column=>Sequel.subscript(:object_ids, 0), :primary_key=>Sequel.subscript(:object_ids, 0), :primary_key_method=>:obj_id, :class=>@Foo
4248
- @Foo.many_to_many :mtmbars, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>Sequel.subscript(:object_ids, 0), :right_primary_key=>Sequel.subscript(:object_ids, 0), :right_primary_key_method=>:obj_id, :left_key=>Sequel.subscript(:foo_ids, 0), :right_key=>Sequel.subscript(:bar_ids, 0), :class=>@Bar
4249
- @Bar.many_to_many :mtmfoos, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>Sequel.subscript(:object_ids, 0), :right_primary_key=>Sequel.subscript(:object_ids, 0), :right_primary_key_method=>:obj_id, :left_key=>Sequel.subscript(:bar_ids, 0), :right_key=>Sequel.subscript(:foo_ids, 0), :class=>@Foo, :reciprocal=>nil
4250
- @foo = @Foo.load(:id=>1, :object_ids=>[2])
4251
- @bar = @Bar.load(:id=>1, :object_ids=>[2])
4252
- @db.sqls
4253
- end
4254
-
4255
- it "should have working regular association methods" do
4256
- @Bar.first.foo.must_equal @foo
4257
- @db.sqls.must_equal ["SELECT * FROM bars LIMIT 1", "SELECT * FROM foos WHERE (foos.object_ids[0] = 2) LIMIT 1"]
4258
- @Foo.first.bars.must_equal [@bar]
4259
- @db.sqls.must_equal ["SELECT * FROM foos LIMIT 1", "SELECT * FROM bars WHERE (bars.object_ids[0] = 2)"]
4260
- @Foo.first.bar.must_equal @bar
4261
- @db.sqls.must_equal ["SELECT * FROM foos LIMIT 1", "SELECT * FROM bars WHERE (bars.object_ids[0] = 2) LIMIT 1"]
4262
- @Foo.first.mtmbars.must_equal [@bar]
4263
- @db.sqls.must_equal ["SELECT * FROM foos LIMIT 1", "SELECT bars.* FROM bars INNER JOIN bars_foos ON (bars_foos.bar_ids[0] = bars.object_ids[0]) WHERE (bars_foos.foo_ids[0] = 2)"]
4264
- @Bar.first.mtmfoos.must_equal [@foo]
4265
- @db.sqls.must_equal ["SELECT * FROM bars LIMIT 1", "SELECT foos.* FROM foos INNER JOIN bars_foos ON (bars_foos.foo_ids[0] = foos.object_ids[0]) WHERE (bars_foos.bar_ids[0] = 2)"]
4266
- end
4267
-
4268
- it "should have working eager loading methods" do
4269
- @Bar.eager(:foo).all.map{|o| [o, o.foo]}.must_equal [[@bar, @foo]]
4270
- @db.sqls.must_equal ["SELECT * FROM bars", "SELECT * FROM foos WHERE (foos.object_ids[0] IN (2))"]
4271
- @Foo.eager(:bars).all.map{|o| [o, o.bars]}.must_equal [[@foo, [@bar]]]
4272
- @db.sqls.must_equal ["SELECT * FROM foos", "SELECT * FROM bars WHERE (bars.object_ids[0] IN (2))"]
4273
- @Foo.eager(:bar).all.map{|o| [o, o.bar]}.must_equal [[@foo, @bar]]
4274
- @db.sqls.must_equal ["SELECT * FROM foos", "SELECT * FROM bars WHERE (bars.object_ids[0] IN (2))"]
4275
- @db.fetch = [[{:id=>1, :object_ids=>[2]}], [{:id=>1, :object_ids=>[2], :x_foreign_key_x=>2}]]
4276
- @Foo.eager(:mtmbars).all.map{|o| [o, o.mtmbars]}.must_equal [[@foo, [@bar]]]
4277
- @db.sqls.must_equal ["SELECT * FROM foos", "SELECT bars.*, bars_foos.foo_ids[0] AS x_foreign_key_x FROM bars INNER JOIN bars_foos ON (bars_foos.bar_ids[0] = bars.object_ids[0]) WHERE (bars_foos.foo_ids[0] IN (2))"]
4278
- @db.fetch = [[{:id=>1, :object_ids=>[2]}], [{:id=>1, :object_ids=>[2], :x_foreign_key_x=>2}]]
4279
- @Bar.eager(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.must_equal [[@bar, [@foo]]]
4280
- @db.sqls.must_equal ["SELECT * FROM bars", "SELECT foos.*, bars_foos.bar_ids[0] AS x_foreign_key_x FROM foos INNER JOIN bars_foos ON (bars_foos.foo_ids[0] = foos.object_ids[0]) WHERE (bars_foos.bar_ids[0] IN (2))"]
4281
- end
4282
-
4283
- it "should have working eager graphing methods" do
4284
- @db.fetch = {:id=>1, :object_ids=>[2], :foo_id=>1, :foo_object_ids=>[2]}
4285
- @Bar.eager_graph(:foo).all.map{|o| [o, o.foo]}.must_equal [[@bar, @foo]]
4286
- @db.sqls.must_equal ["SELECT bars.id, bars.object_ids, foo.id AS foo_id, foo.object_ids AS foo_object_ids FROM bars LEFT OUTER JOIN foos AS foo ON (foo.object_ids[0] = bars.object_ids[0])"]
4287
- @db.fetch = {:id=>1, :object_ids=>[2], :bars_id=>1, :bars_object_ids=>[2]}
4288
- @Foo.eager_graph(:bars).all.map{|o| [o, o.bars]}.must_equal [[@foo, [@bar]]]
4289
- @db.sqls.must_equal ["SELECT foos.id, foos.object_ids, bars.id AS bars_id, bars.object_ids AS bars_object_ids FROM foos LEFT OUTER JOIN bars ON (bars.object_ids[0] = foos.object_ids[0])"]
4290
- @db.fetch = {:id=>1, :object_ids=>[2], :bar_id=>1, :bar_object_ids=>[2]}
4291
- @Foo.eager_graph(:bar).all.map{|o| [o, o.bar]}.must_equal [[@foo, @bar]]
4292
- @db.sqls.must_equal ["SELECT foos.id, foos.object_ids, bar.id AS bar_id, bar.object_ids AS bar_object_ids FROM foos LEFT OUTER JOIN bars AS bar ON (bar.object_ids[0] = foos.object_ids[0])"]
4293
- @db.fetch = {:id=>1, :object_ids=>[2], :mtmfoos_id=>1, :mtmfoos_object_ids=>[2]}
4294
- @Bar.eager_graph(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.must_equal [[@bar, [@foo]]]
4295
- @db.sqls.must_equal ["SELECT bars.id, bars.object_ids, mtmfoos.id AS mtmfoos_id, mtmfoos.object_ids AS mtmfoos_object_ids FROM bars LEFT OUTER JOIN bars_foos ON (bars_foos.bar_ids[0] = bars.object_ids[0]) LEFT OUTER JOIN foos AS mtmfoos ON (mtmfoos.object_ids[0] = bars_foos.foo_ids[0])"]
4296
- @db.fetch = {:id=>1, :object_ids=>[2], :mtmbars_id=>1, :mtmbars_object_ids=>[2]}
4297
- @Foo.eager_graph(:mtmbars).all.map{|o| [o, o.mtmbars]}.must_equal [[@foo, [@bar]]]
4298
- @db.sqls.must_equal ["SELECT foos.id, foos.object_ids, mtmbars.id AS mtmbars_id, mtmbars.object_ids AS mtmbars_object_ids FROM foos LEFT OUTER JOIN bars_foos ON (bars_foos.foo_ids[0] = foos.object_ids[0]) LEFT OUTER JOIN bars AS mtmbars ON (mtmbars.object_ids[0] = bars_foos.bar_ids[0])"]
4299
- end
4300
-
4301
- it "should have working filter by associations with model instances" do
4302
- @Bar.first(:foo=>@foo).must_equal @bar
4303
- @db.sqls.must_equal ["SELECT * FROM bars WHERE (bars.object_ids[0] = 2) LIMIT 1"]
4304
- @Foo.first(:bars=>@bar).must_equal @foo
4305
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_ids[0] = 2) LIMIT 1"]
4306
- @Foo.first(:bar=>@bar).must_equal @foo
4307
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_ids[0] = 2) LIMIT 1"]
4308
- @Foo.first(:mtmbars=>@bar).must_equal @foo
4309
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars_foos.foo_ids[0] FROM bars_foos WHERE ((bars_foos.bar_ids[0] = 2) AND (bars_foos.foo_ids[0] IS NOT NULL)))) LIMIT 1"]
4310
- @Bar.first(:mtmfoos=>@foo).must_equal @bar
4311
- @db.sqls.must_equal ["SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT bars_foos.bar_ids[0] FROM bars_foos WHERE ((bars_foos.foo_ids[0] = 2) AND (bars_foos.bar_ids[0] IS NOT NULL)))) LIMIT 1"]
4312
- end
4313
-
4314
- it "should have working filter by associations for associations with :conditions with model instances" do
4315
- @Bar.many_to_one :foo, :clone=>:foo, :conditions=>{:name=>'A'}
4316
- @Foo.one_to_many :bars, :clone=>:bars, :conditions=>{:name=>'A'}
4317
- @Foo.one_to_one :bar, :clone=>:bars
4318
- @Foo.many_to_many :mtmbars, :clone=>:mtmbars, :conditions=>{:name=>'A'}
4319
- @Bar.many_to_many :mtmfoos, :clone=>:mtmfoos, :conditions=>{:name=>'A'}
4320
-
4321
- @Bar.where(:foo=>@foo).sql.must_equal "SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT foos.object_ids[0] FROM foos WHERE ((name = 'A') AND (foos.object_ids[0] IS NOT NULL) AND (foos.id = 1))))"
4322
- @Foo.where(:bars=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((name = 'A') AND (bars.object_ids[0] IS NOT NULL) AND (bars.id = 1))))"
4323
- @Foo.where(:bar=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((name = 'A') AND (bars.object_ids[0] IS NOT NULL) AND (bars.id = 1))))"
4324
- @Foo.where(:mtmbars=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars_foos.foo_ids[0] FROM bars INNER JOIN bars_foos ON (bars_foos.bar_ids[0] = bars.object_ids[0]) WHERE ((name = 'A') AND (bars_foos.foo_ids[0] IS NOT NULL) AND (bars.id = 1))))"
4325
- @Bar.where(:mtmfoos=>@foo).sql.must_equal "SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT bars_foos.bar_ids[0] FROM foos INNER JOIN bars_foos ON (bars_foos.foo_ids[0] = foos.object_ids[0]) WHERE ((name = 'A') AND (bars_foos.bar_ids[0] IS NOT NULL) AND (foos.id = 1))))"
4326
- end
4327
-
4328
- it "should have working filter by associations for associations with block with model instances" do
4329
- b = lambda{|ds| ds.where(:name=>'A')}
4330
- @Bar.many_to_one :foo, :clone=>:foo, &b
4331
- @Foo.one_to_many :bars, :clone=>:bars, &b
4332
- @Foo.one_to_one :bar, :clone=>:bars
4333
- @Foo.many_to_many :mtmbars, :clone=>:mtmbars, &b
4334
- @Bar.many_to_many :mtmfoos, :clone=>:mtmfoos, &b
4335
-
4336
- @Bar.where(:foo=>@foo).sql.must_equal "SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT foos.object_ids[0] FROM foos WHERE ((name = 'A') AND (foos.object_ids[0] IS NOT NULL) AND (foos.id = 1))))"
4337
- @Foo.where(:bars=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((name = 'A') AND (bars.object_ids[0] IS NOT NULL) AND (bars.id = 1))))"
4338
- @Foo.where(:bar=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((name = 'A') AND (bars.object_ids[0] IS NOT NULL) AND (bars.id = 1))))"
4339
- @Foo.where(:mtmbars=>@bar).sql.must_equal "SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars_foos.foo_ids[0] FROM bars INNER JOIN bars_foos ON (bars_foos.bar_ids[0] = bars.object_ids[0]) WHERE ((name = 'A') AND (bars_foos.foo_ids[0] IS NOT NULL) AND (bars.id = 1))))"
4340
- @Bar.where(:mtmfoos=>@foo).sql.must_equal "SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT bars_foos.bar_ids[0] FROM foos INNER JOIN bars_foos ON (bars_foos.foo_ids[0] = foos.object_ids[0]) WHERE ((name = 'A') AND (bars_foos.bar_ids[0] IS NOT NULL) AND (foos.id = 1))))"
4341
- end
4342
-
4343
- it "should have working filter by associations with model datasets" do
4344
- @Bar.first(:foo=>@Foo.where(:id=>@foo.id)).must_equal @bar
4345
- @db.sqls.must_equal ["SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT foos.object_ids[0] FROM foos WHERE ((id = 1) AND (foos.object_ids[0] IS NOT NULL)))) LIMIT 1"]
4346
- @Foo.first(:bars=>@Bar.where(:id=>@bar.id)).must_equal @foo
4347
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((id = 1) AND (bars.object_ids[0] IS NOT NULL)))) LIMIT 1"]
4348
- @Foo.first(:bar=>@Bar.where(:id=>@bar.id)).must_equal @foo
4349
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((id = 1) AND (bars.object_ids[0] IS NOT NULL)))) LIMIT 1"]
4350
- @Foo.first(:mtmbars=>@Bar.where(:id=>@bar.id)).must_equal @foo
4351
- @db.sqls.must_equal ["SELECT * FROM foos WHERE (foos.object_ids[0] IN (SELECT bars_foos.foo_ids[0] FROM bars_foos WHERE ((bars_foos.bar_ids[0] IN (SELECT bars.object_ids[0] FROM bars WHERE ((id = 1) AND (bars.object_ids[0] IS NOT NULL)))) AND (bars_foos.foo_ids[0] IS NOT NULL)))) LIMIT 1"]
4352
- @Bar.first(:mtmfoos=>@Foo.where(:id=>@foo.id)).must_equal @bar
4353
- @db.sqls.must_equal ["SELECT * FROM bars WHERE (bars.object_ids[0] IN (SELECT bars_foos.bar_ids[0] FROM bars_foos WHERE ((bars_foos.foo_ids[0] IN (SELECT foos.object_ids[0] FROM foos WHERE ((id = 1) AND (foos.object_ids[0] IS NOT NULL)))) AND (bars_foos.bar_ids[0] IS NOT NULL)))) LIMIT 1"]
4354
- end
4355
- end
4356
-
4357
- describe Sequel::Model, "#refresh" do
4358
- before do
4359
- @c = Class.new(Sequel::Model(:items)) do
4360
- unrestrict_primary_key
4361
- columns :id, :x
4362
- end
4363
- DB.reset
4364
- end
4365
-
4366
- it "should remove cached associations" do
4367
- @c.many_to_one :node, :class=>@c
4368
- @m = @c.new(:id => 555)
4369
- @m.associations[:node] = 15
4370
- @m.reload
4371
- @m.associations.must_equal({})
4372
- end
4373
- end
4374
-
4375
- describe "Model#freeze" do
4376
- before do
4377
- class ::Album < Sequel::Model
4378
- columns :id
4379
- class B < Sequel::Model
4380
- columns :id, :album_id
4381
- many_to_one :album, :class=>Album
4382
- end
4383
- one_to_one :b, :key=>:album_id, :class=>B
4384
- end
4385
- @o = Album.load(:id=>1).freeze
4386
- DB.sqls
4387
- end
4388
- after do
4389
- Object.send(:remove_const, :Album)
4390
- end
4391
-
4392
- it "should freeze the object's associations" do
4393
- @o.associations.frozen?.must_equal true
4394
- end
4395
-
4396
- it "should freeze associations after validating" do
4397
- Album.send(:define_method, :validate){super(); b}
4398
- @o = Album.load(:id=>1)
4399
- @o.freeze
4400
- @o.associations.fetch(:b).id.must_equal 1
4401
- end
4402
-
4403
- it "should not break associations getters" do
4404
- Album::B.dataset = Album::B.dataset.with_fetch(:album_id=>1, :id=>2)
4405
- @o.b.must_equal Album::B.load(:id=>2, :album_id=>1)
4406
- @o.associations[:b].must_be_nil
4407
-
4408
- @o = @o.dup
4409
- @o.b.must_equal Album::B.load(:id=>2, :album_id=>1)
4410
- @o.associations[:b].must_equal Album::B.load(:id=>2, :album_id=>1)
4411
- end
4412
-
4413
- it "should not break reciprocal associations" do
4414
- b = Album::B.load(:id=>2, :album_id=>nil)
4415
- b.album = @o
4416
- @o.associations[:b].must_be_nil
4417
-
4418
- @o = @o.dup
4419
- b = Album::B.load(:id=>2, :album_id=>nil)
4420
- b.album = @o
4421
- @o.associations[:b].must_equal Album::B.load(:id=>2, :album_id=>1)
4422
- end
4423
- end
4424
-
4425
- describe "association autoreloading" do
4426
- before do
4427
- @c = Class.new(Sequel::Model)
4428
- @Artist = Class.new(@c).set_dataset(:artists)
4429
- @Artist.dataset = @Artist.dataset.with_fetch(:id=>2, :name=>'Ar')
4430
- @Album = Class.new(@c).set_dataset(:albums)
4431
- @Artist.columns :id, :name
4432
- @Album.columns :id, :name, :artist_id
4433
- @Album.db_schema[:artist_id][:type] = :integer
4434
- @Album.many_to_one :artist, :class=>@Artist
4435
- DB.reset
4436
- end
4437
-
4438
- it "should reload many_to_one association when foreign key is modified" do
4439
- album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
4440
- album.artist
4441
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 2']
4442
-
4443
- album.artist_id = 1
4444
- album.artist
4445
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 1']
4446
- end
4447
-
4448
- it "should handle multiple many_to_one association with the same foreign key" do
4449
- @Album.many_to_one :artist2, :key=>:artist_id, :class=>@Artist
4450
- album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
4451
- album.artist
4452
- album.artist2
4453
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 2'] * 2
4454
-
4455
- album.artist
4456
- album.artist2
4457
- DB.sqls.must_equal []
4458
-
4459
- album.artist_id = 1
4460
- album.artist
4461
- album.artist2
4462
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 1'] * 2
4463
- end
4464
-
4465
- it "should not reload when value has not changed" do
4466
- album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
4467
- album.artist
4468
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 2']
4469
-
4470
- album.artist_id = 2
4471
- album.artist
4472
- DB.sqls.must_equal []
4473
-
4474
- album.artist_id = "2"
4475
- album.artist
4476
- DB.sqls.must_equal []
4477
- end
4478
-
4479
- it "should reload all associations which use the foreign key" do
4480
- @Album.many_to_one :other_artist, :key => :artist_id, :foreign_key => :id, :class => @Artist
4481
- album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
4482
- album.artist
4483
- album.other_artist
4484
- DB.reset
4485
-
4486
- album.artist_id = 1
4487
- album.artist
4488
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 1']
4489
-
4490
- album.other_artist
4491
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 1']
4492
- end
4493
-
4494
- it "should work with composite keys" do
4495
- @Album.many_to_one :composite_artist, :key => [:artist_id, :name], :primary_key => [:id, :name], :class => @Artist
4496
- album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
4497
- album.composite_artist
4498
- DB.reset
4499
-
4500
- album.artist_id = 1
4501
- album.composite_artist
4502
- DB.sqls.must_equal ["SELECT * FROM artists WHERE ((artists.id = 1) AND (artists.name = 'Al')) LIMIT 1"]
4503
-
4504
- album.name = 'Al2'
4505
- album.composite_artist
4506
- DB.sqls.must_equal ["SELECT * FROM artists WHERE ((artists.id = 1) AND (artists.name = 'Al2')) LIMIT 1"]
4507
- end
4508
-
4509
- it "should work with subclasses" do
4510
- salbum = Class.new(@Album)
4511
- oartist = Class.new(@c).set_dataset(:oartist)
4512
- oartist.columns :id, :name
4513
- salbum.many_to_one :artist2, :class=>oartist, :key=>:artist_id
4514
- album = salbum.load(:id => 1, :name=>'Al', :artist_id=>2)
4515
- album.artist
4516
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 2']
4517
-
4518
- album.artist_id = 1
4519
- album.artist
4520
- DB.sqls.must_equal ['SELECT * FROM artists WHERE id = 1']
4521
- end
4522
- end
4523
-
4524
- describe Sequel::Model, ".dataset_module" do
4525
- before do
4526
- @c = Class.new(Sequel::Model(:items))
4527
- end
4528
-
4529
- it "should have dataset_module support an eager method" do
4530
- @c.many_to_one :foo, :class=>@c
4531
- @c.many_to_one :bar, :class=>@c
4532
- @c.many_to_one :baz, :class=>@c
4533
- @c.many_to_one :quux, :class=>@c
4534
- @c.dataset_module{eager(:foo, {:foo=>{:bar=>:baz}}, :quux)}
4535
- @c.foo.opts[:eager].must_equal(:foo=>{:bar=>:baz}, :quux=>nil)
4536
- @c.where(:bar).foo.opts[:eager].must_equal(:foo=>{:bar=>:baz}, :quux=>nil)
4537
- end
4538
- end