sequel 5.28.0 → 5.33.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 (380) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +64 -1922
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/doc/advanced_associations.rdoc +4 -4
  6. data/doc/association_basics.rdoc +3 -3
  7. data/doc/code_order.rdoc +12 -2
  8. data/doc/model_dataset_method_design.rdoc +1 -1
  9. data/doc/postgresql.rdoc +71 -0
  10. data/doc/release_notes/5.29.0.txt +22 -0
  11. data/doc/release_notes/5.30.0.txt +20 -0
  12. data/doc/release_notes/5.31.0.txt +148 -0
  13. data/doc/release_notes/5.32.0.txt +46 -0
  14. data/doc/release_notes/5.33.0.txt +24 -0
  15. data/doc/testing.rdoc +1 -1
  16. data/lib/sequel/adapters/postgres.rb +5 -1
  17. data/lib/sequel/adapters/shared/access.rb +6 -6
  18. data/lib/sequel/adapters/shared/mssql.rb +5 -5
  19. data/lib/sequel/adapters/shared/mysql.rb +10 -10
  20. data/lib/sequel/adapters/shared/oracle.rb +16 -16
  21. data/lib/sequel/adapters/shared/postgres.rb +169 -14
  22. data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
  23. data/lib/sequel/adapters/shared/sqlite.rb +32 -5
  24. data/lib/sequel/adapters/tinytds.rb +1 -1
  25. data/lib/sequel/connection_pool/sharded_threaded.rb +2 -2
  26. data/lib/sequel/connection_pool/threaded.rb +1 -1
  27. data/lib/sequel/core.rb +318 -314
  28. data/lib/sequel/database/query.rb +1 -1
  29. data/lib/sequel/database/schema_generator.rb +1 -1
  30. data/lib/sequel/database/transactions.rb +8 -12
  31. data/lib/sequel/dataset/misc.rb +2 -2
  32. data/lib/sequel/extensions/connection_expiration.rb +2 -2
  33. data/lib/sequel/extensions/connection_validator.rb +2 -2
  34. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  35. data/lib/sequel/extensions/index_caching.rb +9 -7
  36. data/lib/sequel/extensions/integer64.rb +2 -0
  37. data/lib/sequel/extensions/migration.rb +1 -1
  38. data/lib/sequel/extensions/pg_enum.rb +5 -2
  39. data/lib/sequel/extensions/pg_hstore.rb +6 -0
  40. data/lib/sequel/extensions/pg_inet.rb +13 -5
  41. data/lib/sequel/extensions/pg_interval.rb +2 -0
  42. data/lib/sequel/extensions/pg_range.rb +2 -0
  43. data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
  44. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  45. data/lib/sequel/extensions/schema_dumper.rb +10 -4
  46. data/lib/sequel/extensions/server_block.rb +3 -3
  47. data/lib/sequel/model/associations.rb +18 -5
  48. data/lib/sequel/model/base.rb +60 -53
  49. data/lib/sequel/model/plugins.rb +1 -0
  50. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  51. data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
  52. data/lib/sequel/plugins/association_proxies.rb +2 -0
  53. data/lib/sequel/plugins/boolean_subsets.rb +4 -1
  54. data/lib/sequel/plugins/class_table_inheritance.rb +26 -26
  55. data/lib/sequel/plugins/dirty.rb +13 -13
  56. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  57. data/lib/sequel/plugins/forbid_lazy_load.rb +214 -0
  58. data/lib/sequel/plugins/json_serializer.rb +18 -11
  59. data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
  60. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  61. data/lib/sequel/plugins/subclasses.rb +2 -0
  62. data/lib/sequel/plugins/throw_failures.rb +1 -1
  63. data/lib/sequel/timezones.rb +6 -4
  64. data/lib/sequel/version.rb +1 -1
  65. metadata +18 -367
  66. data/Rakefile +0 -151
  67. data/doc/release_notes/4.0.0.txt +0 -262
  68. data/doc/release_notes/4.1.0.txt +0 -85
  69. data/doc/release_notes/4.10.0.txt +0 -226
  70. data/doc/release_notes/4.11.0.txt +0 -147
  71. data/doc/release_notes/4.12.0.txt +0 -105
  72. data/doc/release_notes/4.13.0.txt +0 -169
  73. data/doc/release_notes/4.14.0.txt +0 -68
  74. data/doc/release_notes/4.15.0.txt +0 -56
  75. data/doc/release_notes/4.16.0.txt +0 -36
  76. data/doc/release_notes/4.17.0.txt +0 -38
  77. data/doc/release_notes/4.18.0.txt +0 -36
  78. data/doc/release_notes/4.19.0.txt +0 -45
  79. data/doc/release_notes/4.2.0.txt +0 -129
  80. data/doc/release_notes/4.20.0.txt +0 -79
  81. data/doc/release_notes/4.21.0.txt +0 -94
  82. data/doc/release_notes/4.22.0.txt +0 -72
  83. data/doc/release_notes/4.23.0.txt +0 -65
  84. data/doc/release_notes/4.24.0.txt +0 -99
  85. data/doc/release_notes/4.25.0.txt +0 -181
  86. data/doc/release_notes/4.26.0.txt +0 -44
  87. data/doc/release_notes/4.27.0.txt +0 -78
  88. data/doc/release_notes/4.28.0.txt +0 -57
  89. data/doc/release_notes/4.29.0.txt +0 -41
  90. data/doc/release_notes/4.3.0.txt +0 -40
  91. data/doc/release_notes/4.30.0.txt +0 -37
  92. data/doc/release_notes/4.31.0.txt +0 -57
  93. data/doc/release_notes/4.32.0.txt +0 -132
  94. data/doc/release_notes/4.33.0.txt +0 -88
  95. data/doc/release_notes/4.34.0.txt +0 -86
  96. data/doc/release_notes/4.35.0.txt +0 -130
  97. data/doc/release_notes/4.36.0.txt +0 -116
  98. data/doc/release_notes/4.37.0.txt +0 -50
  99. data/doc/release_notes/4.38.0.txt +0 -67
  100. data/doc/release_notes/4.39.0.txt +0 -127
  101. data/doc/release_notes/4.4.0.txt +0 -92
  102. data/doc/release_notes/4.40.0.txt +0 -179
  103. data/doc/release_notes/4.41.0.txt +0 -77
  104. data/doc/release_notes/4.42.0.txt +0 -221
  105. data/doc/release_notes/4.43.0.txt +0 -87
  106. data/doc/release_notes/4.44.0.txt +0 -125
  107. data/doc/release_notes/4.45.0.txt +0 -370
  108. data/doc/release_notes/4.46.0.txt +0 -404
  109. data/doc/release_notes/4.47.0.txt +0 -56
  110. data/doc/release_notes/4.48.0.txt +0 -293
  111. data/doc/release_notes/4.49.0.txt +0 -222
  112. data/doc/release_notes/4.5.0.txt +0 -34
  113. data/doc/release_notes/4.6.0.txt +0 -30
  114. data/doc/release_notes/4.7.0.txt +0 -103
  115. data/doc/release_notes/4.8.0.txt +0 -175
  116. data/doc/release_notes/4.9.0.txt +0 -190
  117. data/spec/adapter_spec.rb +0 -4
  118. data/spec/adapters/db2_spec.rb +0 -170
  119. data/spec/adapters/mssql_spec.rb +0 -828
  120. data/spec/adapters/mysql_spec.rb +0 -1060
  121. data/spec/adapters/oracle_spec.rb +0 -371
  122. data/spec/adapters/postgres_spec.rb +0 -4476
  123. data/spec/adapters/spec_helper.rb +0 -44
  124. data/spec/adapters/sqlanywhere_spec.rb +0 -97
  125. data/spec/adapters/sqlite_spec.rb +0 -652
  126. data/spec/bin_spec.rb +0 -278
  127. data/spec/core/connection_pool_spec.rb +0 -1250
  128. data/spec/core/database_spec.rb +0 -2915
  129. data/spec/core/dataset_spec.rb +0 -5547
  130. data/spec/core/deprecated_spec.rb +0 -70
  131. data/spec/core/expression_filters_spec.rb +0 -1498
  132. data/spec/core/mock_adapter_spec.rb +0 -722
  133. data/spec/core/object_graph_spec.rb +0 -336
  134. data/spec/core/placeholder_literalizer_spec.rb +0 -166
  135. data/spec/core/schema_generator_spec.rb +0 -214
  136. data/spec/core/schema_spec.rb +0 -1844
  137. data/spec/core/spec_helper.rb +0 -24
  138. data/spec/core/version_spec.rb +0 -14
  139. data/spec/core_extensions_spec.rb +0 -763
  140. data/spec/core_model_spec.rb +0 -2
  141. data/spec/core_spec.rb +0 -1
  142. data/spec/deprecation_helper.rb +0 -30
  143. data/spec/extensions/accessed_columns_spec.rb +0 -51
  144. data/spec/extensions/active_model_spec.rb +0 -99
  145. data/spec/extensions/after_initialize_spec.rb +0 -28
  146. data/spec/extensions/any_not_empty_spec.rb +0 -23
  147. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  148. data/spec/extensions/association_dependencies_spec.rb +0 -125
  149. data/spec/extensions/association_multi_add_remove_spec.rb +0 -1041
  150. data/spec/extensions/association_pks_spec.rb +0 -423
  151. data/spec/extensions/association_proxies_spec.rb +0 -100
  152. data/spec/extensions/auto_literal_strings_spec.rb +0 -205
  153. data/spec/extensions/auto_validations_spec.rb +0 -229
  154. data/spec/extensions/blacklist_security_spec.rb +0 -95
  155. data/spec/extensions/blank_spec.rb +0 -69
  156. data/spec/extensions/boolean_readers_spec.rb +0 -93
  157. data/spec/extensions/boolean_subsets_spec.rb +0 -47
  158. data/spec/extensions/caching_spec.rb +0 -273
  159. data/spec/extensions/caller_logging_spec.rb +0 -52
  160. data/spec/extensions/class_table_inheritance_spec.rb +0 -750
  161. data/spec/extensions/column_conflicts_spec.rb +0 -75
  162. data/spec/extensions/column_select_spec.rb +0 -129
  163. data/spec/extensions/columns_introspection_spec.rb +0 -90
  164. data/spec/extensions/columns_updated_spec.rb +0 -35
  165. data/spec/extensions/composition_spec.rb +0 -248
  166. data/spec/extensions/connection_expiration_spec.rb +0 -151
  167. data/spec/extensions/connection_validator_spec.rb +0 -144
  168. data/spec/extensions/constant_sql_override_spec.rb +0 -24
  169. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -300
  170. data/spec/extensions/constraint_validations_spec.rb +0 -439
  171. data/spec/extensions/core_refinements_spec.rb +0 -528
  172. data/spec/extensions/csv_serializer_spec.rb +0 -183
  173. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  174. data/spec/extensions/dataset_associations_spec.rb +0 -365
  175. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  176. data/spec/extensions/date_arithmetic_spec.rb +0 -181
  177. data/spec/extensions/datetime_parse_to_time_spec.rb +0 -169
  178. data/spec/extensions/def_dataset_method_spec.rb +0 -100
  179. data/spec/extensions/defaults_setter_spec.rb +0 -150
  180. data/spec/extensions/delay_add_association_spec.rb +0 -73
  181. data/spec/extensions/dirty_spec.rb +0 -222
  182. data/spec/extensions/duplicate_columns_handler_spec.rb +0 -104
  183. data/spec/extensions/eager_each_spec.rb +0 -62
  184. data/spec/extensions/eager_graph_eager_spec.rb +0 -100
  185. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  186. data/spec/extensions/error_splitter_spec.rb +0 -18
  187. data/spec/extensions/error_sql_spec.rb +0 -20
  188. data/spec/extensions/escaped_like_spec.rb +0 -40
  189. data/spec/extensions/eval_inspect_spec.rb +0 -81
  190. data/spec/extensions/exclude_or_null_spec.rb +0 -15
  191. data/spec/extensions/finder_spec.rb +0 -260
  192. data/spec/extensions/force_encoding_spec.rb +0 -126
  193. data/spec/extensions/freeze_datasets_spec.rb +0 -31
  194. data/spec/extensions/graph_each_spec.rb +0 -113
  195. data/spec/extensions/hook_class_methods_spec.rb +0 -402
  196. data/spec/extensions/identifier_mangling_spec.rb +0 -201
  197. data/spec/extensions/implicit_subquery_spec.rb +0 -58
  198. data/spec/extensions/index_caching_spec.rb +0 -66
  199. data/spec/extensions/inflector_spec.rb +0 -183
  200. data/spec/extensions/input_transformer_spec.rb +0 -69
  201. data/spec/extensions/insert_conflict_spec.rb +0 -103
  202. data/spec/extensions/insert_returning_select_spec.rb +0 -72
  203. data/spec/extensions/instance_filters_spec.rb +0 -79
  204. data/spec/extensions/instance_hooks_spec.rb +0 -246
  205. data/spec/extensions/integer64_spec.rb +0 -22
  206. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  207. data/spec/extensions/json_serializer_spec.rb +0 -336
  208. data/spec/extensions/lazy_attributes_spec.rb +0 -183
  209. data/spec/extensions/list_spec.rb +0 -291
  210. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  211. data/spec/extensions/many_through_many_spec.rb +0 -2177
  212. data/spec/extensions/migration_spec.rb +0 -864
  213. data/spec/extensions/modification_detection_spec.rb +0 -93
  214. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -92
  215. data/spec/extensions/named_timezones_spec.rb +0 -218
  216. data/spec/extensions/nested_attributes_spec.rb +0 -815
  217. data/spec/extensions/null_dataset_spec.rb +0 -85
  218. data/spec/extensions/optimistic_locking_spec.rb +0 -127
  219. data/spec/extensions/pagination_spec.rb +0 -116
  220. data/spec/extensions/pg_array_associations_spec.rb +0 -802
  221. data/spec/extensions/pg_array_ops_spec.rb +0 -144
  222. data/spec/extensions/pg_array_spec.rb +0 -398
  223. data/spec/extensions/pg_auto_constraint_validations_spec.rb +0 -209
  224. data/spec/extensions/pg_enum_spec.rb +0 -118
  225. data/spec/extensions/pg_extended_date_support_spec.rb +0 -126
  226. data/spec/extensions/pg_hstore_ops_spec.rb +0 -238
  227. data/spec/extensions/pg_hstore_spec.rb +0 -219
  228. data/spec/extensions/pg_inet_ops_spec.rb +0 -102
  229. data/spec/extensions/pg_inet_spec.rb +0 -72
  230. data/spec/extensions/pg_interval_spec.rb +0 -103
  231. data/spec/extensions/pg_json_ops_spec.rb +0 -356
  232. data/spec/extensions/pg_json_spec.rb +0 -451
  233. data/spec/extensions/pg_loose_count_spec.rb +0 -23
  234. data/spec/extensions/pg_range_ops_spec.rb +0 -60
  235. data/spec/extensions/pg_range_spec.rb +0 -600
  236. data/spec/extensions/pg_row_ops_spec.rb +0 -61
  237. data/spec/extensions/pg_row_plugin_spec.rb +0 -60
  238. data/spec/extensions/pg_row_spec.rb +0 -363
  239. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -93
  240. data/spec/extensions/pg_timestamptz_spec.rb +0 -17
  241. data/spec/extensions/prepared_statements_safe_spec.rb +0 -66
  242. data/spec/extensions/prepared_statements_spec.rb +0 -177
  243. data/spec/extensions/pretty_table_spec.rb +0 -123
  244. data/spec/extensions/query_spec.rb +0 -94
  245. data/spec/extensions/rcte_tree_spec.rb +0 -387
  246. data/spec/extensions/round_timestamps_spec.rb +0 -39
  247. data/spec/extensions/s_spec.rb +0 -60
  248. data/spec/extensions/schema_caching_spec.rb +0 -64
  249. data/spec/extensions/schema_dumper_spec.rb +0 -870
  250. data/spec/extensions/select_remove_spec.rb +0 -38
  251. data/spec/extensions/sequel_4_dataset_methods_spec.rb +0 -121
  252. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  253. data/spec/extensions/serialization_spec.rb +0 -365
  254. data/spec/extensions/server_block_spec.rb +0 -135
  255. data/spec/extensions/server_logging_spec.rb +0 -45
  256. data/spec/extensions/sharding_spec.rb +0 -197
  257. data/spec/extensions/shared_caching_spec.rb +0 -151
  258. data/spec/extensions/single_table_inheritance_spec.rb +0 -347
  259. data/spec/extensions/singular_table_names_spec.rb +0 -22
  260. data/spec/extensions/skip_create_refresh_spec.rb +0 -18
  261. data/spec/extensions/spec_helper.rb +0 -70
  262. data/spec/extensions/split_array_nil_spec.rb +0 -24
  263. data/spec/extensions/split_values_spec.rb +0 -57
  264. data/spec/extensions/sql_comments_spec.rb +0 -33
  265. data/spec/extensions/sql_expr_spec.rb +0 -59
  266. data/spec/extensions/static_cache_cache_spec.rb +0 -35
  267. data/spec/extensions/static_cache_spec.rb +0 -471
  268. data/spec/extensions/string_agg_spec.rb +0 -90
  269. data/spec/extensions/string_date_time_spec.rb +0 -95
  270. data/spec/extensions/string_stripper_spec.rb +0 -68
  271. data/spec/extensions/subclasses_spec.rb +0 -79
  272. data/spec/extensions/subset_conditions_spec.rb +0 -38
  273. data/spec/extensions/symbol_aref_refinement_spec.rb +0 -28
  274. data/spec/extensions/symbol_as_refinement_spec.rb +0 -21
  275. data/spec/extensions/synchronize_sql_spec.rb +0 -124
  276. data/spec/extensions/table_select_spec.rb +0 -83
  277. data/spec/extensions/tactical_eager_loading_spec.rb +0 -402
  278. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  279. data/spec/extensions/throw_failures_spec.rb +0 -74
  280. data/spec/extensions/timestamps_spec.rb +0 -209
  281. data/spec/extensions/to_dot_spec.rb +0 -153
  282. data/spec/extensions/touch_spec.rb +0 -226
  283. data/spec/extensions/tree_spec.rb +0 -334
  284. data/spec/extensions/typecast_on_load_spec.rb +0 -86
  285. data/spec/extensions/unlimited_update_spec.rb +0 -21
  286. data/spec/extensions/update_or_create_spec.rb +0 -83
  287. data/spec/extensions/update_primary_key_spec.rb +0 -105
  288. data/spec/extensions/update_refresh_spec.rb +0 -59
  289. data/spec/extensions/uuid_spec.rb +0 -101
  290. data/spec/extensions/validate_associated_spec.rb +0 -52
  291. data/spec/extensions/validation_class_methods_spec.rb +0 -1040
  292. data/spec/extensions/validation_contexts_spec.rb +0 -31
  293. data/spec/extensions/validation_helpers_spec.rb +0 -525
  294. data/spec/extensions/whitelist_security_spec.rb +0 -157
  295. data/spec/extensions/xml_serializer_spec.rb +0 -213
  296. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  297. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  298. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  299. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  300. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  301. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  302. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  303. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  304. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  305. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  306. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  307. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  308. data/spec/files/double_migration/001_create_sessions.rb +0 -9
  309. data/spec/files/double_migration/002_create_nodes.rb +0 -19
  310. data/spec/files/double_migration/003_3_create_users.rb +0 -4
  311. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  312. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  313. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  314. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  315. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  316. data/spec/files/empty_migration/001_create_sessions.rb +0 -9
  317. data/spec/files/empty_migration/002_create_nodes.rb +0 -0
  318. data/spec/files/empty_migration/003_3_create_users.rb +0 -4
  319. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  320. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  321. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  322. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  323. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  324. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  325. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  326. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  327. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  328. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  329. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  330. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  331. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  332. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  333. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  334. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  335. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  336. data/spec/files/reversible_migrations/006_reversible.rb +0 -10
  337. data/spec/files/reversible_migrations/007_reversible.rb +0 -10
  338. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  339. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  340. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  341. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  342. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  343. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  344. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  345. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  346. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  347. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  348. data/spec/guards_helper.rb +0 -59
  349. data/spec/integration/associations_test.rb +0 -2597
  350. data/spec/integration/database_test.rb +0 -113
  351. data/spec/integration/dataset_test.rb +0 -2037
  352. data/spec/integration/eager_loader_test.rb +0 -687
  353. data/spec/integration/migrator_test.rb +0 -262
  354. data/spec/integration/model_test.rb +0 -203
  355. data/spec/integration/plugin_test.rb +0 -2423
  356. data/spec/integration/prepared_statement_test.rb +0 -405
  357. data/spec/integration/schema_test.rb +0 -903
  358. data/spec/integration/spec_helper.rb +0 -71
  359. data/spec/integration/timezone_test.rb +0 -86
  360. data/spec/integration/transaction_test.rb +0 -603
  361. data/spec/integration/type_test.rb +0 -127
  362. data/spec/model/association_reflection_spec.rb +0 -803
  363. data/spec/model/associations_spec.rb +0 -4738
  364. data/spec/model/base_spec.rb +0 -875
  365. data/spec/model/class_dataset_methods_spec.rb +0 -146
  366. data/spec/model/dataset_methods_spec.rb +0 -198
  367. data/spec/model/eager_loading_spec.rb +0 -2377
  368. data/spec/model/hooks_spec.rb +0 -370
  369. data/spec/model/inflector_spec.rb +0 -26
  370. data/spec/model/model_spec.rb +0 -956
  371. data/spec/model/plugins_spec.rb +0 -429
  372. data/spec/model/record_spec.rb +0 -2118
  373. data/spec/model/spec_helper.rb +0 -46
  374. data/spec/model/validations_spec.rb +0 -220
  375. data/spec/model_no_assoc_spec.rb +0 -1
  376. data/spec/model_spec.rb +0 -1
  377. data/spec/plugin_spec.rb +0 -1
  378. data/spec/sequel_coverage.rb +0 -15
  379. data/spec/sequel_warning.rb +0 -4
  380. data/spec/spec_config.rb +0 -12
@@ -1,93 +0,0 @@
1
- require_relative "spec_helper"
2
- require 'yaml'
3
-
4
- describe "serialization_modification_detection plugin" do
5
- before do
6
- @ds = Sequel.mock(:fetch=>{:id=>1, :a=>'a'.dup, :b=>1, :c=>['a'.dup], :d=>{'b'=>'c'.dup}}, :numrows=>1, :autoid=>1)[:items]
7
- @c = Class.new(Sequel::Model(@ds))
8
- @c.plugin :modification_detection
9
- @c.columns :a, :b, :c, :d
10
- @o = @c.first
11
- @ds.db.sqls
12
- end
13
-
14
- it "should detect setting new column values on new objects" do
15
- @o = @c.new
16
- @o.changed_columns.must_equal []
17
- @o.a = 'c'
18
- @o.changed_columns.must_equal [:a]
19
- end
20
-
21
- it "should only detect columns that have been changed" do
22
- @o.changed_columns.must_equal []
23
- @o.a << 'b'
24
- @o.changed_columns.must_equal [:a]
25
- @o.a.replace('a')
26
- @o.changed_columns.must_equal []
27
-
28
- @o.values[:b] = 2
29
- @o.changed_columns.must_equal [:b]
30
- @o.values[:b] = 1
31
- @o.changed_columns.must_equal []
32
-
33
- @o.c[0] << 'b'
34
- @o.d['b'] << 'b'
35
- @o.changed_columns.sort_by{|c| c.to_s}.must_equal [:c, :d]
36
- @o.c[0] = 'a'
37
- @o.changed_columns.must_equal [:d]
38
- @o.d['b'] = 'c'
39
- @o.changed_columns.must_equal []
40
- end
41
-
42
- it "should detect columns that have been changed on frozen objects" do
43
- @o.freeze
44
- @o.a << 'b'
45
- @o.changed_columns.must_equal [:a]
46
- end
47
-
48
- it "should not list a column twice" do
49
- @o.a = 'b'.dup
50
- @o.a << 'a'
51
- @o.changed_columns.must_equal [:a]
52
- end
53
-
54
- it "should report correct changed_columns after updating" do
55
- @o.a << 'a'
56
- @o.save_changes
57
- @o.changed_columns.must_equal []
58
-
59
- @o.values[:b] = 2
60
- @o.save_changes
61
- @o.changed_columns.must_equal []
62
-
63
- @o.c[0] << 'b'
64
- @o.save_changes
65
- @o.changed_columns.must_equal []
66
-
67
- @o.d['b'] << 'a'
68
- @o.save_changes
69
- @o.changed_columns.must_equal []
70
-
71
- @ds.db.sqls.must_equal ["UPDATE items SET a = 'aa' WHERE (id = 1)",
72
- "UPDATE items SET b = 2 WHERE (id = 1)",
73
- "UPDATE items SET c = ('ab') WHERE (id = 1)",
74
- "UPDATE items SET d = ('b' = 'ca') WHERE (id = 1)"]
75
- end
76
-
77
- it "should report correct changed_columns after creating new object" do
78
- o = @c.create
79
- o.changed_columns.must_equal []
80
- o.a << 'a'
81
- o.changed_columns.must_equal [:a]
82
- @ds.db.sqls.must_equal ["INSERT INTO items DEFAULT VALUES", "SELECT * FROM items WHERE (id = 1) LIMIT 1"]
83
- end
84
-
85
- it "should report correct changed_columns after refreshing existing object" do
86
- @o.a << 'a'
87
- @o.changed_columns.must_equal [:a]
88
- @o.refresh
89
- @o.changed_columns.must_equal []
90
- @o.a << 'a'
91
- @o.changed_columns.must_equal [:a]
92
- end
93
- end
@@ -1,92 +0,0 @@
1
- require_relative "spec_helper"
2
-
3
- describe "MSSSQL optimistic locking plugin" do
4
- before do
5
- @db = Sequel.mock(:host=>'mssql')
6
- @ds = @db[:items].with_quote_identifiers(false).with_extend{def input_identifier(v); v.to_s end}
7
- @c = Class.new(Sequel::Model(@ds))
8
- @c.columns :id, :name, :timestamp
9
- @c.plugin :mssql_optimistic_locking
10
- @o = @c.load(:id=>1, :name=>'a', :timestamp=>'1234')
11
- @db.sqls
12
- end
13
-
14
- it "should not include the lock column when updating" do
15
- @db.fetch = [[{:timestamp=>'2345'}]]
16
- @o.save
17
- @db.sqls.must_equal ["UPDATE TOP (1) items SET name = 'a' OUTPUT inserted.timestamp WHERE ((id = 1) AND (timestamp = 0x31323334))"]
18
- end
19
-
20
- it "should automatically update lock column using new value from database" do
21
- @db.fetch = [[{:timestamp=>'2345'}]]
22
- @o.save
23
- @o.timestamp.must_equal '2345'
24
- end
25
-
26
- it "should raise error when updating stale object" do
27
- @db.fetch = []
28
- @o.timestamp = '2345'
29
- proc{@o.save}.must_raise(Sequel::NoExistingObject)
30
- @o.timestamp.must_equal '2345'
31
- @db.sqls.must_equal ["UPDATE TOP (1) items SET name = 'a' OUTPUT inserted.timestamp WHERE ((id = 1) AND (timestamp = 0x32333435))"]
32
- end
33
-
34
- it "should raise error when destroying stale object" do
35
- @db.numrows = 0
36
- @o.timestamp = '2345'
37
- proc{@o.destroy}.must_raise(Sequel::NoExistingObject)
38
- @db.sqls.must_equal ["DELETE TOP (1) FROM items WHERE ((id = 1) AND (timestamp = 0x32333435))"]
39
- end
40
-
41
- it "should allow refresh after failed save" do
42
- @db.fetch = []
43
- @o.timestamp = '2345'
44
- proc{@o.save}.must_raise(Sequel::NoExistingObject)
45
- @db.fetch = {:id=>1, :name=>'a', :timestamp=>'2345'}
46
- @o.refresh
47
- @db.sqls
48
- @o.save
49
- @db.sqls.must_equal ["UPDATE TOP (1) items SET name = 'a' OUTPUT inserted.timestamp WHERE ((id = 1) AND (timestamp = 0x32333435))"]
50
- end
51
-
52
- it "should allow changing the lock column via model.lock_column=" do
53
- @c = Class.new(Sequel::Model(@ds))
54
- @c.columns :id, :name, :lv
55
- @c.plugin :mssql_optimistic_locking
56
- @c.lock_column = :lv
57
- @o = @c.load(:id=>1, :name=>'a', :lv=>'1234')
58
- @db.sqls
59
- @db.fetch = []
60
- proc{@o.save}.must_raise(Sequel::NoExistingObject)
61
- @o.lv.must_equal '1234'
62
- @db.sqls.must_equal ["UPDATE TOP (1) items SET name = 'a' OUTPUT inserted.lv WHERE ((id = 1) AND (lv = 0x31323334))"]
63
- @o = @c.load(:id=>1, :name=>'a', :lv=>'1234')
64
- @db.fetch = {:lv=>'2345'}
65
- @o.save
66
- @o.lv.must_equal '2345'
67
- end
68
-
69
- it "should allow changing the lock column via plugin option" do
70
- @c = Class.new(Sequel::Model(@ds))
71
- @c.columns :id, :name, :lv
72
- @c.plugin :mssql_optimistic_locking, :lock_column=>:lv
73
- @o = @c.load(:id=>1, :name=>'a', :lv=>'1234')
74
- @db.sqls
75
- @db.fetch = []
76
- proc{@o.save}.must_raise(Sequel::NoExistingObject)
77
- @o.lv.must_equal '1234'
78
- @db.sqls.must_equal ["UPDATE TOP (1) items SET name = 'a' OUTPUT inserted.lv WHERE ((id = 1) AND (lv = 0x31323334))"]
79
- @o = @c.load(:id=>1, :name=>'a', :lv=>'1234')
80
- @db.fetch = {:lv=>'2345'}
81
- @o.save
82
- @o.lv.must_equal '2345'
83
- end
84
-
85
- it "should work when subclassing" do
86
- c = Class.new(@c)
87
- o = c.load(:id=>1, :name=>'a', :timestamp=>'1234')
88
- @db.fetch = [[{:timestamp=>'2345'}]]
89
- o.save
90
- @db.sqls.must_equal ["UPDATE TOP (1) items SET name = 'a' OUTPUT inserted.timestamp WHERE ((id = 1) AND (timestamp = 0x31323334))"]
91
- end
92
- end
@@ -1,218 +0,0 @@
1
- require_relative "spec_helper"
2
-
3
- begin
4
- require 'tzinfo'
5
- rescue LoadError
6
- warn "Skipping test of named_timezones extension: can't load tzinfo"
7
- else
8
- Sequel.extension :thread_local_timezones
9
- Sequel.extension :named_timezones
10
- Sequel.datetime_class = Time
11
-
12
- describe "Sequel named_timezones extension with DateTime class" do
13
- before do
14
- @tz_in = TZInfo::Timezone.get('America/Los_Angeles')
15
- @tz_out = TZInfo::Timezone.get('America/New_York')
16
- @db = Sequel.mock
17
- @dt = DateTime.civil(2009,6,1,10,20,30,0)
18
- Sequel.application_timezone = 'America/Los_Angeles'
19
- Sequel.database_timezone = 'America/New_York'
20
- Sequel.datetime_class = DateTime
21
- end
22
- after do
23
- Sequel.tzinfo_disambiguator = nil
24
- Sequel.default_timezone = nil
25
- Sequel.datetime_class = Time
26
- end
27
-
28
- it "should convert string arguments to *_timezone= to TZInfo::Timezone instances" do
29
- Sequel.application_timezone.must_equal @tz_in
30
- Sequel.database_timezone.must_equal @tz_out
31
- end
32
-
33
- it "should convert string arguments for Database#timezone= to TZInfo::Timezone instances for database-specific timezones" do
34
- @db.extension :named_timezones
35
- @db.timezone = 'America/Los_Angeles'
36
- @db.timezone.must_equal @tz_in
37
- end
38
-
39
- it "should accept TZInfo::Timezone instances in *_timezone=" do
40
- Sequel.application_timezone = @tz_in
41
- Sequel.database_timezone = @tz_out
42
- Sequel.application_timezone.must_equal @tz_in
43
- Sequel.database_timezone.must_equal @tz_out
44
- end
45
-
46
- it "should convert datetimes going into the database to named database_timezone" do
47
- ds = @db[:a].with_extend do
48
- def supports_timestamp_timezones?; true; end
49
- def supports_timestamp_usecs?; false; end
50
- end
51
- ds.insert([@dt, DateTime.civil(2009,6,1,3,20,30,-7/24.0), DateTime.civil(2009,6,1,6,20,30,-1/6.0)])
52
- @db.sqls.must_equal ["INSERT INTO a VALUES ('2009-06-01 06:20:30-0400', '2009-06-01 06:20:30-0400', '2009-06-01 06:20:30-0400')"]
53
- end
54
-
55
- it "should convert datetimes coming out of the database from database_timezone to application_timezone" do
56
- dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30-0400')
57
- dt.must_be_instance_of DateTime
58
- dt.must_equal @dt
59
- dt.offset.must_equal(-7/24.0)
60
-
61
- dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30+0000')
62
- dt.must_be_instance_of DateTime
63
- dt.must_equal @dt
64
- dt.offset.must_equal(-7/24.0)
65
- end
66
-
67
- it "should raise an error for ambiguous timezones by default" do
68
- proc{Sequel.database_to_application_timestamp('2004-10-31T01:30:00')}.must_raise(Sequel::InvalidValue)
69
- end
70
-
71
- it "should support tzinfo_disambiguator= to handle ambiguous timezones automatically" do
72
- Sequel.tzinfo_disambiguator = proc{|datetime, periods| periods.first}
73
- dt = Sequel.database_to_application_timestamp('2004-10-31T01:30:00')
74
- dt.must_equal DateTime.parse('2004-10-30T22:30:00-07:00')
75
- dt.offset.must_equal(-7/24.0)
76
- end
77
-
78
- it "should assume datetimes coming out of the database that don't have an offset as coming from database_timezone" do
79
- dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30')
80
- dt.must_be_instance_of DateTime
81
- dt.must_equal @dt
82
- dt.offset.must_equal(-7/24.0)
83
-
84
- dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30')
85
- dt.must_be_instance_of DateTime
86
- dt.must_equal(@dt + 1/6.0)
87
- dt.offset.must_equal(-7/24.0)
88
- end
89
-
90
- it "should work with the thread_local_timezones extension" do
91
- q, q1, q2 = Queue.new, Queue.new, Queue.new
92
- tz1, tz2 = nil, nil
93
- t1 = Thread.new do
94
- Sequel.thread_application_timezone = 'America/New_York'
95
- q2.push nil
96
- q.pop
97
- tz1 = Sequel.application_timezone
98
- end
99
- t2 = Thread.new do
100
- Sequel.thread_application_timezone = 'America/Los_Angeles'
101
- q2.push nil
102
- q1.pop
103
- tz2 = Sequel.application_timezone
104
- end
105
- q2.pop
106
- q2.pop
107
- q.push nil
108
- q1.push nil
109
- t1.join
110
- t2.join
111
- tz1.must_equal @tz_out
112
- tz2.must_equal @tz_in
113
- end
114
- end
115
-
116
- describe "Sequel named_timezones extension with Time class" do
117
- before do
118
- @tz_in = TZInfo::Timezone.get('America/Los_Angeles')
119
- @tz_out = TZInfo::Timezone.get('America/New_York')
120
- @db = Sequel.mock
121
- Sequel.application_timezone = 'America/Los_Angeles'
122
- Sequel.database_timezone = 'America/New_York'
123
- end
124
- after do
125
- Sequel.tzinfo_disambiguator = nil
126
- Sequel.default_timezone = nil
127
- Sequel.datetime_class = Time
128
- end
129
-
130
- it "should convert string arguments to *_timezone= to TZInfo::Timezone instances" do
131
- Sequel.application_timezone.must_equal @tz_in
132
- Sequel.database_timezone.must_equal @tz_out
133
- end
134
-
135
- it "should convert string arguments for Database#timezone= to TZInfo::Timezone instances for database-specific timezones" do
136
- @db.extension :named_timezones
137
- @db.timezone = 'America/Los_Angeles'
138
- @db.timezone.must_equal @tz_in
139
- end
140
-
141
- it "should accept TZInfo::Timezone instances in *_timezone=" do
142
- Sequel.application_timezone = @tz_in
143
- Sequel.database_timezone = @tz_out
144
- Sequel.application_timezone.must_equal @tz_in
145
- Sequel.database_timezone.must_equal @tz_out
146
- end
147
-
148
- it "should convert datetimes going into the database to named database_timezone" do
149
- ds = @db[:a].with_extend do
150
- def supports_timestamp_timezones?; true; end
151
- def supports_timestamp_usecs?; false; end
152
- end
153
- ds.insert([Time.new(2009,6,1,3,20,30, RUBY_VERSION >= '2.6' ? @tz_in : -25200), Time.new(2009,6,1,3,20,30,-25200), Time.new(2009,6,1,6,20,30,-14400)])
154
- @db.sqls.must_equal ["INSERT INTO a VALUES ('2009-06-01 06:20:30-0400', '2009-06-01 06:20:30-0400', '2009-06-01 06:20:30-0400')"]
155
- end
156
-
157
- it "should convert datetimes coming out of the database from database_timezone to application_timezone" do
158
- dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30-0400')
159
- dt.must_be_instance_of Time
160
- dt.must_equal Time.new(2009,6,1,3,20,30,-25200)
161
- dt.utc_offset.must_equal -25200
162
-
163
- dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30+0000')
164
- dt.must_be_instance_of Time
165
- dt.must_equal Time.new(2009,6,1,3,20,30,-25200)
166
- dt.utc_offset.must_equal -25200
167
- end
168
-
169
- it "should raise an error for ambiguous timezones by default" do
170
- proc{Sequel.database_to_application_timestamp('2004-10-31T01:30:00')}.must_raise(Sequel::InvalidValue)
171
- end
172
-
173
- it "should support tzinfo_disambiguator= to handle ambiguous timezones automatically" do
174
- Sequel.tzinfo_disambiguator = proc{|datetime, periods| periods.first}
175
- Sequel.database_to_application_timestamp('2004-10-31T01:30:00').must_equal Time.new(2004, 10, 30, 22, 30, 0, -25200)
176
- dt = Sequel.database_to_application_timestamp('2004-10-31T01:30:00')
177
- dt.must_equal Time.new(2004, 10, 30, 22, 30, 0, -25200)
178
- dt.utc_offset.must_equal -25200
179
- end
180
-
181
- it "should assume datetimes coming out of the database that don't have an offset as coming from database_timezone" do
182
- dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30')
183
- dt.must_be_instance_of Time
184
- dt.must_equal Time.new(2009,6,1,3,20,30, -25200)
185
- dt.utc_offset.must_equal -25200
186
-
187
- dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30')
188
- dt.must_be_instance_of Time
189
- dt.must_equal Time.new(2009,6,1,7,20,30, -25200)
190
- dt.utc_offset.must_equal -25200
191
- end
192
-
193
- it "should work with the thread_local_timezones extension" do
194
- q, q1, q2 = Queue.new, Queue.new, Queue.new
195
- tz1, tz2 = nil, nil
196
- t1 = Thread.new do
197
- Sequel.thread_application_timezone = 'America/New_York'
198
- q2.push nil
199
- q.pop
200
- tz1 = Sequel.application_timezone
201
- end
202
- t2 = Thread.new do
203
- Sequel.thread_application_timezone = 'America/Los_Angeles'
204
- q2.push nil
205
- q1.pop
206
- tz2 = Sequel.application_timezone
207
- end
208
- q2.pop
209
- q2.pop
210
- q.push nil
211
- q1.push nil
212
- t1.join
213
- t2.join
214
- tz1.must_equal @tz_out
215
- tz2.must_equal @tz_in
216
- end
217
- end
218
- end
@@ -1,815 +0,0 @@
1
- require_relative "spec_helper"
2
-
3
- describe "NestedAttributes plugin" do
4
- def check_sqls(should, is)
5
- if should.is_a?(Array)
6
- should.must_include(is)
7
- else
8
- is.must_equal should
9
- end
10
- end
11
-
12
- def check_sql_array(*shoulds)
13
- sqls = @db.sqls
14
- sqls.length.must_equal shoulds.length
15
- shoulds.zip(sqls){|s, i| check_sqls(s, i)}
16
- end
17
-
18
- before do
19
- @db = Sequel.mock(:autoid=>1, :numrows=>1)
20
- @c = Class.new(Sequel::Model(@db))
21
- @c.plugin :nested_attributes
22
- @Artist = Class.new(@c).set_dataset(:artists)
23
- @Album = Class.new(@c).set_dataset(:albums)
24
- @Tag = Class.new(@c).set_dataset(:tags)
25
- @Concert = Class.new(@c).set_dataset(:concerts)
26
- @Artist.plugin :skip_create_refresh
27
- @Album.plugin :skip_create_refresh
28
- @Tag.plugin :skip_create_refresh
29
- @Concert.plugin :skip_create_refresh
30
- @Artist.columns :id, :name
31
- @Album.columns :id, :name, :artist_id
32
- @Tag.columns :id, :name
33
- @Concert.columns :tour, :date, :artist_id, :playlist
34
- @Concert.set_primary_key([:tour, :date])
35
- @Concert.unrestrict_primary_key
36
- @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
37
- @Artist.one_to_many :concerts, :class=>@Concert, :key=>:artist_id
38
- @Artist.one_to_one :first_album, :class=>@Album, :key=>:artist_id
39
- @Artist.one_to_one :first_concert, :class=>@Concert, :key=>:artist_id
40
- @Concert.one_to_many :albums, :class=>@Album, :key=>:artist_id, :primary_key=>:artist_id
41
- @Album.many_to_one :artist, :class=>@Artist, :reciprocal=>:albums
42
- @Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
43
- @Tag.many_to_many :albums, :class=>@Album, :left_key=>:tag_id, :right_key=>:album_id, :join_table=>:at
44
- @Artist.nested_attributes :albums, :first_album, :destroy=>true, :remove=>true
45
- @Artist.nested_attributes :concerts, :destroy=>true, :remove=>true
46
- @Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
47
- @Artist.nested_attributes :first_concert
48
- @Concert.nested_attributes :albums
49
- @db.sqls
50
- end
51
-
52
- it "should not modify options hash when loading plugin" do
53
- h = {}
54
- @Concert.nested_attributes :albums, h
55
- h.must_equal({})
56
- end
57
-
58
- it "should support creating new many_to_one objects" do
59
- a = @Album.new({:name=>'Al', :artist_attributes=>{:name=>'Ar'}})
60
- @db.sqls.must_equal []
61
- a.save
62
- check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
63
- ["INSERT INTO albums (name, artist_id) VALUES ('Al', 1)", "INSERT INTO albums (artist_id, name) VALUES (1, 'Al')"])
64
- end
65
-
66
- it "should support creating new one_to_one objects" do
67
- a = @Artist.new(:name=>'Ar')
68
- a.id = 1
69
- a.first_album_attributes = {:name=>'Al'}
70
- @db.sqls.must_equal []
71
- a.save
72
- check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
73
- "UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
74
- ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
75
- end
76
-
77
- it "should support creating new one_to_many objects" do
78
- a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:name=>'Al'}]})
79
- @db.sqls.must_equal []
80
- a.save
81
- check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
82
- ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
83
- end
84
-
85
- it "should support creating new one_to_many and one_to_one objects with presence validations on the foreign key" do
86
- @Album.class_eval do
87
- plugin :validation_helpers
88
- def validate
89
- validates_integer :artist_id
90
- super
91
- end
92
- end
93
- a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:name=>'Al'}]})
94
- @db.sqls.must_equal []
95
- a.save
96
- check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
97
- ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
98
-
99
- a = @Artist.new(:name=>'Ar')
100
- a.id = 1
101
- a.first_album_attributes = {:name=>'Al'}
102
- @db.sqls.must_equal []
103
- a.save
104
- check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
105
- "UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
106
- ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
107
- end
108
-
109
- it "should support creating new one_to_many and one_to_one objects with composite keys with presence validations on the foreign key" do
110
- insert = nil
111
- @Album.class_eval do
112
- plugin :validation_helpers
113
- def validate
114
- validates_integer :artist_id
115
- super
116
- end
117
- end
118
- @Concert.class_eval do
119
- define_method :_insert do
120
- insert = values.dup
121
- end
122
- def before_create # Have to define the CPK somehow.
123
- self.tour = 'To'
124
- self.date = '2004-04-05'
125
- super
126
- end
127
- def after_create
128
- super
129
- self.artist_id = 3
130
- end
131
- end
132
-
133
- c = @Concert.new(:playlist=>'Pl')
134
- @db.sqls.must_equal []
135
- c.albums_attributes = [{:name=>'Al'}]
136
- c.save
137
- insert.must_equal(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
138
- check_sql_array(["INSERT INTO albums (name, artist_id) VALUES ('Al', 3)", "INSERT INTO albums (artist_id, name) VALUES (3, 'Al')"])
139
-
140
- @Concert.class_eval do
141
- plugin :validation_helpers
142
- def validate
143
- validates_integer :artist_id
144
- super
145
- end
146
- end
147
-
148
- a = @Artist.new(:name=>'Ar')
149
- a.id = 1
150
- a.first_concert_attributes = {:playlist=>'Pl'}
151
- @db.sqls.must_equal []
152
- a.save
153
- check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
154
- "UPDATE concerts SET artist_id = NULL WHERE (artist_id = 1)")
155
- insert.must_equal(:tour=>'To', :date=>'2004-04-05', :artist_id=>1, :playlist=>'Pl')
156
- end
157
-
158
- it "should should not remove existing values from object when validating" do
159
- @Artist.one_to_one :first_album, :class=>@Album, :key=>:id
160
- @Artist.nested_attributes :first_album
161
- @db.fetch = {:id=>1}
162
- a = @Artist.load(:id=>1)
163
- a.set(:first_album_attributes=>{:id=>1, :name=>'Ar'})
164
- a.first_album.values.must_equal(:id=>1, :name=>'Ar')
165
- @db.sqls.must_equal ["SELECT * FROM albums WHERE (albums.id = 1) LIMIT 1"]
166
- a.save_changes
167
- check_sql_array("UPDATE albums SET name = 'Ar' WHERE (id = 1)")
168
- end
169
-
170
- it "should support creating new many_to_many objects" do
171
- a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}]})
172
- @db.sqls.must_equal []
173
- a.save
174
- check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
175
- "INSERT INTO tags (name) VALUES ('T')",
176
- ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"])
177
- end
178
-
179
- it "should add new objects to the cached association array as soon as the *_attributes= method is called" do
180
- a = @Artist.new({:name=>'Ar', :first_album_attributes=>{:name=>'B'}, :albums_attributes=>[{:name=>'Al', :tags_attributes=>[{:name=>'T'}]}]})
181
- a.albums.must_equal [@Album.new(:name=>'Al')]
182
- a.albums.first.artist.must_equal a
183
- a.albums.first.tags.must_equal [@Tag.new(:name=>'T')]
184
- a.first_album.must_equal @Album.new(:name=>'B')
185
- a.first_album.artist.must_equal a
186
- end
187
-
188
- it "should support creating new objects with composite primary keys" do
189
- insert = nil
190
- @Concert.class_eval do
191
- define_method :_insert do
192
- insert = values.dup
193
- end
194
- def before_create # Have to define the CPK somehow.
195
- self.tour = 'To'
196
- self.date = '2004-04-05'
197
- super
198
- end
199
- end
200
- a = @Artist.new({:name=>'Ar', :concerts_attributes=>[{:playlist=>'Pl'}]})
201
- @db.sqls.must_equal []
202
- a.save
203
- @db.sqls.must_equal ["INSERT INTO artists (name) VALUES ('Ar')"]
204
- insert.must_equal(:tour=>'To', :date=>'2004-04-05', :artist_id=>1, :playlist=>'Pl')
205
- end
206
-
207
- it "should support creating new objects with specific primary keys if :unmatched_pk => :create is set" do
208
- @Artist.nested_attributes :albums, :unmatched_pk=>:create
209
- insert = nil
210
- @Album.class_eval do
211
- unrestrict_primary_key
212
- define_method :_insert do
213
- insert = values.dup
214
- end
215
- end
216
- a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:id=>7, :name=>'Al'}]})
217
- @db.sqls.must_equal []
218
- a.save
219
- @db.sqls.must_equal ["INSERT INTO artists (name) VALUES ('Ar')"]
220
- insert.must_equal(:artist_id=>1, :name=>'Al', :id=>7)
221
- end
222
-
223
- it "should support creating new objects with specific composite primary keys if :unmatched_pk => :create is set" do
224
- insert = nil
225
- @Artist.nested_attributes :concerts, :unmatched_pk=>:create
226
- @Concert.class_eval do
227
- define_method :_insert do
228
- insert = values.dup
229
- end
230
- end
231
- a = @Artist.new({:name=>'Ar', :concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl'}]})
232
- @db.sqls.must_equal []
233
- a.save
234
- @db.sqls.must_equal ["INSERT INTO artists (name) VALUES ('Ar')"]
235
- insert.must_equal(:tour=>'To', :date=>'2004-04-05', :artist_id=>1, :playlist=>'Pl')
236
- end
237
-
238
- it "should support updating many_to_one objects" do
239
- al = @Album.load(:id=>10, :name=>'Al')
240
- ar = @Artist.load(:id=>20, :name=>'Ar')
241
- al.associations[:artist] = ar
242
- al.set(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
243
- @db.sqls.must_equal []
244
- al.save
245
- @db.sqls.must_equal ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE artists SET name = 'Ar2' WHERE (id = 20)"]
246
- end
247
-
248
- it "should support updating one_to_one objects" do
249
- al = @Album.load(:id=>10, :name=>'Al')
250
- ar = @Artist.load(:id=>20, :name=>'Ar')
251
- ar.associations[:first_album] = al
252
- ar.set(:first_album_attributes=>{:id=>10, :name=>'Al2'})
253
- @db.sqls.must_equal []
254
- ar.save
255
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
256
- end
257
-
258
- it "should support updating one_to_many objects" do
259
- al = @Album.load(:id=>10, :name=>'Al')
260
- ar = @Artist.load(:id=>20, :name=>'Ar')
261
- ar.associations[:albums] = [al]
262
- ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2'}])
263
- @db.sqls.must_equal []
264
- ar.save
265
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
266
- end
267
-
268
- it "should support updating one_to_many objects with _delete/_remove flags set to false" do
269
- al = @Album.load(:id=>10, :name=>'Al')
270
- ar = @Artist.load(:id=>20, :name=>'Ar')
271
- ar.associations[:albums] = [al]
272
- ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2', :_delete => 'f', :_remove => '0'}])
273
- @db.sqls.must_equal []
274
- ar.save
275
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
276
- end
277
-
278
- it "should support updating many_to_many objects" do
279
- a = @Album.load(:id=>10, :name=>'Al')
280
- t = @Tag.load(:id=>20, :name=>'T')
281
- a.associations[:tags] = [t]
282
- a.set(:tags_attributes=>[{:id=>20, :name=>'T2'}])
283
- @db.sqls.must_equal []
284
- a.save
285
- @db.sqls.must_equal ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
286
- end
287
-
288
- it "should support updating many_to_many objects with _delete/_remove flags set to false" do
289
- a = @Album.load(:id=>10, :name=>'Al')
290
- t = @Tag.load(:id=>20, :name=>'T')
291
- a.associations[:tags] = [t]
292
- a.set(:tags_attributes=>[{:id=>20, :name=>'T2', '_delete' => false, '_remove' => 'F'}])
293
- @db.sqls.must_equal []
294
- a.save
295
- @db.sqls.must_equal ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
296
- end
297
-
298
- it "should support updating objects with composite primary keys" do
299
- ar = @Artist.load(:id=>10, :name=>'Ar')
300
- co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
301
- ar.associations[:concerts] = [co]
302
- ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl2'}])
303
- @db.sqls.must_equal []
304
- ar.save
305
- check_sql_array("UPDATE artists SET name = 'Ar' WHERE (id = 10)", ["UPDATE concerts SET playlist = 'Pl2' WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET playlist = 'Pl2' WHERE ((date = '2004-04-05') AND (tour = 'To'))"])
306
- end
307
-
308
- it "should support removing many_to_one objects" do
309
- al = @Album.load(:id=>10, :name=>'Al')
310
- ar = @Artist.load(:id=>20, :name=>'Ar')
311
- al.associations[:artist] = ar
312
- al.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})
313
- @db.sqls.must_equal []
314
- al.save
315
- check_sql_array(["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"])
316
- end
317
-
318
- it "should support removing one_to_one objects" do
319
- al = @Album.load(:id=>10, :name=>'Al')
320
- ar = @Artist.load(:id=>20, :name=>'Ar')
321
- ar.associations[:first_album] = al
322
- ar.set(:first_album_attributes=>{:id=>10, :_remove=>'t'})
323
- @db.sqls.must_equal []
324
- ar.save
325
- @db.sqls.must_equal ["UPDATE albums SET artist_id = NULL WHERE (artist_id = 20)", "UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
326
- end
327
-
328
- it "should support removing one_to_many objects" do
329
- al = @Album.load(:id=>10, :name=>'Al')
330
- ar = @Artist.load(:id=>20, :name=>'Ar')
331
- ar.associations[:albums] = [al]
332
- ar.set(:albums_attributes=>[{:id=>10, :_remove=>'t'}])
333
- ar.associations[:albums].must_equal []
334
- @db.sqls.must_equal []
335
- @Album.dataset = @Album.dataset.with_fetch(:id=>1)
336
- ar.save
337
- check_sql_array("SELECT 1 AS one FROM albums WHERE ((albums.artist_id = 20) AND (id = 10)) LIMIT 1",
338
- ["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"],
339
- "UPDATE artists SET name = 'Ar' WHERE (id = 20)")
340
- end
341
-
342
- it "should support removing many_to_many objects" do
343
- a = @Album.load(:id=>10, :name=>'Al')
344
- t = @Tag.load(:id=>20, :name=>'T')
345
- a.associations[:tags] = [t]
346
- a.set(:tags_attributes=>[{:id=>20, :_remove=>true}])
347
- a.associations[:tags].must_equal []
348
- @db.sqls.must_equal []
349
- a.save
350
- @db.sqls.must_equal ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)"]
351
- end
352
-
353
- it "should support removing objects with composite primary keys" do
354
- ar = @Artist.load(:id=>10, :name=>'Ar')
355
- co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
356
- ar.associations[:concerts] = [co]
357
- ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_remove=>'t'}])
358
- @db.sqls.must_equal []
359
- @Concert.dataset = @Concert.dataset.with_fetch(:id=>1)
360
- ar.save
361
- check_sql_array(["SELECT 1 AS one FROM concerts WHERE ((concerts.artist_id = 10) AND (tour = 'To') AND (date = '2004-04-05')) LIMIT 1", "SELECT 1 AS one FROM concerts WHERE ((concerts.artist_id = 10) AND (date = '2004-04-05') AND (tour = 'To')) LIMIT 1"],
362
- ["UPDATE concerts SET artist_id = NULL, playlist = 'Pl' WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET playlist = 'Pl', artist_id = NULL WHERE ((tour = 'To') AND (date = '2004-04-05'))", "UPDATE concerts SET artist_id = NULL, playlist = 'Pl' WHERE ((date = '2004-04-05') AND (tour = 'To'))", "UPDATE concerts SET playlist = 'Pl', artist_id = NULL WHERE ((date = '2004-04-05') AND (tour = 'To'))"],
363
- "UPDATE artists SET name = 'Ar' WHERE (id = 10)")
364
- end
365
-
366
- it "should support destroying many_to_one objects" do
367
- al = @Album.load(:id=>10, :name=>'Al')
368
- ar = @Artist.load(:id=>20, :name=>'Ar')
369
- al.associations[:artist] = ar
370
- al.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})
371
- @db.sqls.must_equal []
372
- al.save
373
- check_sql_array(["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"],
374
- "DELETE FROM artists WHERE (id = 20)")
375
- end
376
-
377
- it "should support destroying one_to_one objects" do
378
- al = @Album.load(:id=>10, :name=>'Al')
379
- ar = @Artist.load(:id=>20, :name=>'Ar')
380
- ar.associations[:first_album] = al
381
- ar.set(:first_album_attributes=>{:id=>10, :_delete=>'t'})
382
- @db.sqls.must_equal []
383
- ar.save
384
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
385
- end
386
-
387
- it "should support destroying one_to_many objects" do
388
- al = @Album.load(:id=>10, :name=>'Al')
389
- ar = @Artist.load(:id=>20, :name=>'Ar')
390
- ar.associations[:albums] = [al]
391
- ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
392
- @db.sqls.must_equal []
393
- ar.save
394
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
395
- end
396
-
397
- it "should support destroying many_to_many objects" do
398
- a = @Album.load(:id=>10, :name=>'Al')
399
- t = @Tag.load(:id=>20, :name=>'T')
400
- a.associations[:tags] = [t]
401
- a.set(:tags_attributes=>[{:id=>20, :_delete=>true}])
402
- @db.sqls.must_equal []
403
- a.save
404
- @db.sqls.must_equal ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
405
- end
406
-
407
- it "should support destroying objects with composite primary keys" do
408
- ar = @Artist.load(:id=>10, :name=>'Ar')
409
- co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
410
- ar.associations[:concerts] = [co]
411
- ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])
412
- @db.sqls.must_equal []
413
- ar.save
414
- check_sql_array("UPDATE artists SET name = 'Ar' WHERE (id = 10)", ["DELETE FROM concerts WHERE ((tour = 'To') AND (date = '2004-04-05'))", "DELETE FROM concerts WHERE ((date = '2004-04-05') AND (tour = 'To'))"])
415
- end
416
-
417
- it "should support both string and symbol keys in nested attribute hashes" do
418
- a = @Album.load(:id=>10, :name=>'Al')
419
- t = @Tag.load(:id=>20, :name=>'T')
420
- a.associations[:tags] = [t]
421
- a.set('tags_attributes'=>[{'id'=>20, '_delete'=>true}])
422
- @db.sqls.must_equal []
423
- a.save
424
- @db.sqls.must_equal ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
425
- end
426
-
427
- it "should support using a hash instead of an array for to_many nested attributes" do
428
- a = @Album.load(:id=>10, :name=>'Al')
429
- t = @Tag.load(:id=>20, :name=>'T')
430
- a.associations[:tags] = [t]
431
- a.set('tags_attributes'=>{'1'=>{'id'=>20, '_delete'=>true}})
432
- @db.sqls.must_equal []
433
- a.save
434
- @db.sqls.must_equal ["DELETE FROM at WHERE ((album_id = 10) AND (tag_id = 20))", "UPDATE albums SET name = 'Al' WHERE (id = 10)", "DELETE FROM tags WHERE (id = 20)"]
435
- end
436
-
437
- it "should only allow destroying associated objects if :destroy option is used in the nested_attributes call" do
438
- a = @Album.load(:id=>10, :name=>'Al')
439
- ar = @Artist.load(:id=>20, :name=>'Ar')
440
- a.associations[:artist] = ar
441
- @Album.nested_attributes :artist
442
- proc{a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})}.must_raise(Sequel::MassAssignmentRestriction)
443
- @Album.nested_attributes :artist, :destroy=>true
444
- a.set(:artist_attributes=>{:id=>'20', :_delete=>'1'})
445
- end
446
-
447
- it "should only allow removing associated objects if :remove option is used in the nested_attributes call" do
448
- a = @Album.load(:id=>10, :name=>'Al')
449
- ar = @Artist.load(:id=>20, :name=>'Ar')
450
- a.associations[:artist] = ar
451
- @Album.nested_attributes :artist
452
- proc{a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})}.must_raise(Sequel::MassAssignmentRestriction)
453
- @Album.nested_attributes :artist, :remove=>true
454
- a.set(:artist_attributes=>{:id=>'20', :_remove=>'1'})
455
- end
456
-
457
- it "should raise an Error if a primary key is given in a nested attribute hash, but no matching associated object exists" do
458
- al = @Album.load(:id=>10, :name=>'Al')
459
- ar = @Artist.load(:id=>20, :name=>'Ar')
460
- ar.associations[:albums] = [al]
461
- proc{ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])}.must_raise(Sequel::Error)
462
- ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
463
- end
464
-
465
- it "should not raise an Error if an unmatched primary key is given, if the :unmatched_pk=>:ignore option is used" do
466
- @Artist.nested_attributes :albums, :unmatched_pk=>:ignore
467
- al = @Album.load(:id=>10, :name=>'Al')
468
- ar = @Artist.load(:id=>20, :name=>'Ar')
469
- ar.associations[:albums] = [al]
470
- ar.set(:albums_attributes=>[{:id=>30, :_delete=>'t'}])
471
- @db.sqls.must_equal []
472
- ar.save
473
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)"]
474
- end
475
-
476
- it "should raise an Error if a composite primary key is given in a nested attribute hash, but no matching associated object exists" do
477
- ar = @Artist.load(:id=>10, :name=>'Ar')
478
- co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
479
- ar.associations[:concerts] = [co]
480
- proc{ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-04', :_delete=>'t'}])}.must_raise(Sequel::Error)
481
- ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-05', :_delete=>'t'}])
482
- end
483
-
484
- it "should not raise an Error if an unmatched composite primary key is given, if the :unmatched_pk=>:ignore option is used" do
485
- @Artist.nested_attributes :concerts, :unmatched_pk=>:ignore
486
- ar = @Artist.load(:id=>10, :name=>'Ar')
487
- co = @Concert.load(:tour=>'To', :date=>'2004-04-05', :playlist=>'Pl')
488
- ar.associations[:concerts] = [co]
489
- ar.set(:concerts_attributes=>[{:tour=>'To', :date=>'2004-04-06', :_delete=>'t'}])
490
- @db.sqls.must_equal []
491
- ar.save
492
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 10)"]
493
- end
494
-
495
- it "should raise a NoExistingObject error if object to be updated no longer exists, if the :require_modification=>true option is used" do
496
- @Artist.nested_attributes :albums, :require_modification=>true, :destroy=>true
497
- al = @Album.load(:id=>10, :name=>'Al')
498
- ar = @Artist.load(:id=>20, :name=>'Ar')
499
- ar.associations[:albums] = [al]
500
- ar.set(:albums_attributes=>[{:id=>10, :name=>'L'}])
501
- @db.sqls.must_equal []
502
- @db.numrows = [1, 0]
503
- proc{ar.save}.must_raise Sequel::NoExistingObject
504
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'L' WHERE (id = 10)"]
505
- end
506
-
507
- it "should not raise an Error if object to be updated no longer exists, if the :require_modification=>false option is used" do
508
- @Artist.nested_attributes :albums, :require_modification=>false, :destroy=>true
509
- al = @Album.load(:id=>10, :name=>'Al')
510
- ar = @Artist.load(:id=>20, :name=>'Ar')
511
- ar.associations[:albums] = [al]
512
- ar.set(:albums_attributes=>[{:id=>10, :name=>'L'}])
513
- @db.sqls.must_equal []
514
- @db.numrows = [1, 0]
515
- ar.save
516
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'L' WHERE (id = 10)"]
517
- end
518
-
519
- it "should raise a NoExistingObject error if object to be deleted no longer exists, if the :require_modification=>true option is used" do
520
- @Artist.nested_attributes :albums, :require_modification=>true, :destroy=>true
521
- al = @Album.load(:id=>10, :name=>'Al')
522
- ar = @Artist.load(:id=>20, :name=>'Ar')
523
- ar.associations[:albums] = [al]
524
- ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
525
- @db.sqls.must_equal []
526
- @db.numrows = [1, 0]
527
- proc{ar.save}.must_raise Sequel::NoExistingObject
528
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
529
- end
530
-
531
- it "should not raise an Error if object to be deleted no longer exists, if the :require_modification=>false option is used" do
532
- @Artist.nested_attributes :albums, :require_modification=>false, :destroy=>true
533
- al = @Album.load(:id=>10, :name=>'Al')
534
- ar = @Artist.load(:id=>20, :name=>'Ar')
535
- ar.associations[:albums] = [al]
536
- ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
537
- @db.sqls.must_equal []
538
- @db.numrows = [1, 0]
539
- ar.save
540
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
541
- end
542
-
543
- it "should not attempt to validate nested attributes twice for one_to_many associations when creating them" do
544
- @Artist.nested_attributes :albums
545
- validated = []
546
- @Album.class_eval do
547
- define_method(:validate) do
548
- super()
549
- validated << self
550
- end
551
- end
552
- a = @Artist.new(:name=>'Ar', :albums_attributes=>[{:name=>'Al'}])
553
- @db.sqls.must_equal []
554
- validated.length.must_equal 0
555
- a.save
556
- validated.length.must_equal 1
557
- check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
558
- ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
559
- end
560
-
561
- it "should not attempt to validate nested attributes twice for one_to_one associations when creating them" do
562
- @Artist.nested_attributes :first_album
563
- validated = []
564
- @Album.class_eval do
565
- define_method(:validate) do
566
- super()
567
- validated << self
568
- end
569
- end
570
- a = @Artist.new(:name=>'Ar', :first_album_attributes=>{:name=>'Al'})
571
- @db.sqls.must_equal []
572
- validated.length.must_equal 0
573
- a.save
574
- validated.length.must_equal 1
575
- check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
576
- "UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
577
- "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)")
578
- end
579
-
580
- it "should not clear reciprocal association before saving new one_to_one associated object" do
581
- @Artist.one_to_one :first_album, :clone=>:first_album, :reciprocal=>:artist
582
- @Artist.nested_attributes :first_album
583
- assoc = []
584
- @Album.class_eval do
585
- define_method(:after_save) do
586
- super()
587
- assoc << associations[:artist]
588
- end
589
- end
590
- a = @Artist.new(:name=>'Ar', :first_album_attributes=>{:name=>'Al'})
591
- @db.sqls.must_equal []
592
- assoc.must_be_empty
593
- a.save
594
- assoc.length.must_equal 1
595
- assoc.first.must_be_kind_of(@Artist)
596
- check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
597
- "UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
598
- "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)")
599
- end
600
-
601
- it "should not save if nested attribute is not valid and should include nested attribute validation errors in the main object's validation errors" do
602
- @Artist.class_eval do
603
- def validate
604
- super
605
- errors.add(:name, 'cannot be Ar') if name == 'Ar'
606
- end
607
- end
608
- a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
609
- @db.sqls.must_equal []
610
- proc{a.save}.must_raise(Sequel::ValidationFailed)
611
- a.errors.full_messages.must_equal ['artist name cannot be Ar']
612
- @db.sqls.must_equal []
613
- # Should preserve attributes
614
- a.artist.name.must_equal 'Ar'
615
- end
616
-
617
- it "should not attempt to validate nested attributes if the :validate=>false association option is used" do
618
- @Album.many_to_one :artist, :class=>@Artist, :validate=>false, :reciprocal=>nil
619
- @Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
620
- @Artist.class_eval do
621
- def validate
622
- super
623
- errors.add(:name, 'cannot be Ar') if name == 'Ar'
624
- end
625
- end
626
- a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
627
- @db.sqls.must_equal []
628
- a.save
629
- check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
630
- ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
631
- end
632
-
633
- it "should not attempt to validate nested attributes if the :validate=>false option is passed to save" do
634
- @Artist.class_eval do
635
- def validate
636
- super
637
- errors.add(:name, 'cannot be Ar') if name == 'Ar'
638
- end
639
- end
640
- a = @Album.new(:name=>'Al', :artist_attributes=>{:name=>'Ar'})
641
- @db.sqls.must_equal []
642
- a.save(:validate=>false)
643
- check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
644
- ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
645
- end
646
-
647
- it "should not accept nested attributes unless explicitly specified" do
648
- @Artist.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at
649
- proc{@Artist.create({:name=>'Ar', :tags_attributes=>[{:name=>'T'}]})}.must_raise(Sequel::MassAssignmentRestriction)
650
- @db.sqls.must_equal []
651
- end
652
-
653
- it "should save when save_changes or update is called if nested attribute associated objects changed but there are no changes to the main object" do
654
- al = @Album.load(:id=>10, :name=>'Al')
655
- ar = @Artist.load(:id=>20, :name=>'Ar')
656
- al.associations[:artist] = ar
657
- @db.sqls.must_equal []
658
- al.update(:artist_attributes=>{:id=>'20', :name=>'Ar2'})
659
- @db.sqls.must_equal ["UPDATE artists SET name = 'Ar2' WHERE (id = 20)"]
660
- end
661
-
662
- it "should have a :limit option limiting the amount of entries" do
663
- @Album.nested_attributes :tags, :limit=>2
664
- arr = [{:name=>'T'}]
665
- proc{@Album.new({:name=>'Al', :tags_attributes=>arr*3})}.must_raise(Sequel::Error)
666
- a = @Album.new({:name=>'Al', :tags_attributes=>arr*2})
667
- @db.sqls.must_equal []
668
- a.save
669
- check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
670
- "INSERT INTO tags (name) VALUES ('T')",
671
- ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
672
- "INSERT INTO tags (name) VALUES ('T')",
673
- ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
674
- end
675
-
676
- it "should accept a block that each hash gets passed to determine if it should be processed" do
677
- @Album.nested_attributes(:tags){|h| h[:name].empty?}
678
- a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}, {:name=>''}, {:name=>'T2'}]})
679
- @db.sqls.must_equal []
680
- a.save
681
- check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
682
- "INSERT INTO tags (name) VALUES ('T')",
683
- ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
684
- "INSERT INTO tags (name) VALUES ('T2')",
685
- ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
686
- end
687
-
688
- it "should accept a :transform block that returns a changed attributes hash" do
689
- @Album.nested_attributes :tags, :transform=>proc{|parent, hash| hash[:name] << parent.name; hash }
690
- a = @Album.new(:name => 'Al')
691
- a.set(:tags_attributes=>[{:name=>'T'.dup}, {:name=>'T2'.dup}])
692
- @db.sqls.must_equal []
693
- a.save
694
- check_sql_array("INSERT INTO albums (name) VALUES ('Al')",
695
- "INSERT INTO tags (name) VALUES ('TAl')",
696
- ["INSERT INTO at (album_id, tag_id) VALUES (1, 2)", "INSERT INTO at (tag_id, album_id) VALUES (2, 1)"],
697
- "INSERT INTO tags (name) VALUES ('T2Al')",
698
- ["INSERT INTO at (album_id, tag_id) VALUES (1, 4)", "INSERT INTO at (tag_id, album_id) VALUES (4, 1)"])
699
- end
700
-
701
- it "should return objects created/modified in the internal methods" do
702
- @Album.nested_attributes :tags, :remove=>true, :unmatched_pk=>:ignore
703
- objs = []
704
- @Album.class_eval do
705
- define_method(:nested_attributes_create){|*a| objs << [super(*a), :create]}
706
- define_method(:nested_attributes_remove){|*a| objs << [super(*a), :remove]}
707
- define_method(:nested_attributes_update){|*a| objs << [super(*a), :update]}
708
- end
709
- a = @Album.new(:name=>'Al')
710
- a.associations[:tags] = [@Tag.load(:id=>6, :name=>'A'), @Tag.load(:id=>7, :name=>'A2')]
711
- a.tags_attributes = [{:id=>6, :name=>'T'}, {:id=>7, :name=>'T2', :_remove=>true}, {:name=>'T3'}, {:id=>8, :name=>'T4'}, {:id=>9, :name=>'T5', :_remove=>true}]
712
- objs.must_equal [[@Tag.load(:id=>6, :name=>'T'), :update], [@Tag.load(:id=>7, :name=>'A2'), :remove], [@Tag.new(:name=>'T3'), :create]]
713
- end
714
-
715
- it "should raise an error if updating modifies the associated objects keys" do
716
- @Artist.columns :id, :name, :artist_id
717
- @Album.columns :id, :name, :artist_id
718
- @Tag.columns :id, :name, :tag_id
719
- @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id, :primary_key=>:artist_id
720
- @Album.many_to_one :artist, :class=>@Artist, :primary_key=>:artist_id
721
- @Album.many_to_many :tags, :class=>@Tag, :left_key=>:album_id, :right_key=>:tag_id, :join_table=>:at, :right_primary_key=>:tag_id
722
- @Artist.nested_attributes :albums, :destroy=>true, :remove=>true
723
- @Album.nested_attributes :artist, :tags, :destroy=>true, :remove=>true
724
-
725
- al = @Album.load(:id=>10, :name=>'Al', :artist_id=>25)
726
- ar = @Artist.load(:id=>20, :name=>'Ar', :artist_id=>25)
727
- t = @Tag.load(:id=>30, :name=>'T', :tag_id=>15)
728
- al.associations[:artist] = ar
729
- al.associations[:tags] = [t]
730
- ar.associations[:albums] = [al]
731
- proc{ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2', :artist_id=>'3'}])}.must_raise(Sequel::Error)
732
- proc{al.set(:artist_attributes=>{:id=>20, :name=>'Ar2', :artist_id=>'3'})}.must_raise(Sequel::Error)
733
- proc{al.set(:tags_attributes=>[{:id=>30, :name=>'T2', :tag_id=>'3'}])}.must_raise(Sequel::Error)
734
- end
735
-
736
- it "should accept a :fields option and only allow modification of those fields" do
737
- @Tag.columns :id, :name, :number
738
- @Album.nested_attributes :tags, :destroy=>true, :remove=>true, :fields=>[:name]
739
-
740
- al = @Album.load(:id=>10, :name=>'Al')
741
- t = @Tag.load(:id=>30, :name=>'T', :number=>10)
742
- al.associations[:tags] = [t]
743
- al.set(:tags_attributes=>[{:id=>30, :name=>'T2'}, {:name=>'T3'}])
744
- @db.sqls.must_equal []
745
- al.save
746
- check_sql_array("UPDATE albums SET name = 'Al' WHERE (id = 10)",
747
- "UPDATE tags SET name = 'T2' WHERE (id = 30)",
748
- "INSERT INTO tags (name) VALUES ('T3')",
749
- ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
750
- al.set(:tags_attributes=>[{:id=>30, :name=>'T3', :number=>3}])
751
- al.tags.first.name.must_equal 'T3'
752
- al.tags.first.number.must_equal 10
753
- al.set(:tags_attributes=>[{:name=>'T4', :number=>3}])
754
- al.tags.last.name.must_equal 'T4'
755
- al.tags.last.number.must_be_nil
756
- end
757
-
758
- it "should accept a proc for the :fields option that accepts the associated object and returns an array of fields" do
759
- @Tag.columns :id, :name, :number
760
- @Album.nested_attributes :tags, :destroy=>true, :remove=>true, :fields=>proc{|object| object.is_a?(@Tag) ? [:name] : []}
761
-
762
- al = @Album.load(:id=>10, :name=>'Al')
763
- t = @Tag.load(:id=>30, :name=>'T', :number=>10)
764
- al.associations[:tags] = [t]
765
- al.set(:tags_attributes=>[{:id=>30, :name=>'T2'}, {:name=>'T3'}])
766
- @db.sqls.must_equal []
767
- al.save
768
- check_sql_array("UPDATE albums SET name = 'Al' WHERE (id = 10)",
769
- "UPDATE tags SET name = 'T2' WHERE (id = 30)",
770
- "INSERT INTO tags (name) VALUES ('T3')",
771
- ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
772
- al.set_nested_attributes(:tags, [{:id=>30, :name=>'T3', :number=>3}], :fields=>[:name])
773
- al.tags.first.name.must_equal 'T3'
774
- al.tags.first.number.must_equal 10
775
- al.set_nested_attributes(:tags, [{:name=>'T4', :number=>3}], :fields=>[:name])
776
- al.tags.last.name.must_equal 'T4'
777
- al.tags.last.number.must_be_nil
778
- end
779
-
780
- it "should allow per-call options via the set_nested_attributes method" do
781
- @Tag.columns :id, :name, :number
782
- @Album.nested_attributes :tags
783
-
784
- al = @Album.load(:id=>10, :name=>'Al')
785
- t = @Tag.load(:id=>30, :name=>'T', :number=>10)
786
- al.associations[:tags] = [t]
787
- al.set_nested_attributes(:tags, [{:id=>30, :name=>'T2'}, {:name=>'T3'}], :fields=>[:name])
788
- @db.sqls.must_equal []
789
- al.save
790
- check_sql_array("UPDATE albums SET name = 'Al' WHERE (id = 10)",
791
- "UPDATE tags SET name = 'T2' WHERE (id = 30)",
792
- "INSERT INTO tags (name) VALUES ('T3')",
793
- ["INSERT INTO at (album_id, tag_id) VALUES (10, 1)", "INSERT INTO at (tag_id, album_id) VALUES (1, 10)"])
794
- al.set_nested_attributes(:tags, [{:id=>30, :name=>'T3', :number=>3}], :fields=>[:name])
795
- al.tags.first.name.must_equal 'T3'
796
- al.tags.first.number.must_equal 10
797
- al.set_nested_attributes(:tags, [{:name=>'T4', :number=>3}], :fields=>[:name])
798
- al.tags.last.name.must_equal 'T4'
799
- al.tags.last.number.must_be_nil
800
- end
801
-
802
- it "should have set_nested_attributes method raise error if called with a bad association" do
803
- proc{@Album.load(:id=>10, :name=>'Al').set_nested_attributes(:tags2, [{:id=>30, :name=>'T2', :number=>3}], :fields=>[:name])}.must_raise(Sequel::Error)
804
- end
805
-
806
- it "should have set_nested_attributes method raise error if called with an association that doesn't support nested attributes" do
807
- @Tag.columns :id, :name, :number
808
- proc{@Album.load(:id=>10, :name=>'Al').set_nested_attributes(:tags, [{:id=>30, :name=>'T2', :number=>3}], :fields=>[:name])}.must_raise(Sequel::Error)
809
- end
810
-
811
- it "should not allow modifying ensted attributes after freezing" do
812
- @Artist.freeze
813
- proc{@Artist.nested_attributes :albums}.must_raise RuntimeError, TypeError
814
- end
815
- end