sequel 5.8.0 → 5.38.0

Sign up to get free protection for your applications and to get access to all the features.
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