sequel 4.36.0 → 5.61.0

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