Package not found. Please check the package name and try again.

sequel 5.29.0 → 5.30.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 (323) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +8 -1922
  3. data/doc/release_notes/5.30.0.txt +20 -0
  4. data/lib/sequel/adapters/shared/sqlite.rb +7 -1
  5. data/lib/sequel/database/transactions.rb +5 -9
  6. data/lib/sequel/version.rb +1 -1
  7. metadata +4 -368
  8. data/Rakefile +0 -151
  9. data/doc/release_notes/4.0.0.txt +0 -262
  10. data/doc/release_notes/4.1.0.txt +0 -85
  11. data/doc/release_notes/4.10.0.txt +0 -226
  12. data/doc/release_notes/4.11.0.txt +0 -147
  13. data/doc/release_notes/4.12.0.txt +0 -105
  14. data/doc/release_notes/4.13.0.txt +0 -169
  15. data/doc/release_notes/4.14.0.txt +0 -68
  16. data/doc/release_notes/4.15.0.txt +0 -56
  17. data/doc/release_notes/4.16.0.txt +0 -36
  18. data/doc/release_notes/4.17.0.txt +0 -38
  19. data/doc/release_notes/4.18.0.txt +0 -36
  20. data/doc/release_notes/4.19.0.txt +0 -45
  21. data/doc/release_notes/4.2.0.txt +0 -129
  22. data/doc/release_notes/4.20.0.txt +0 -79
  23. data/doc/release_notes/4.21.0.txt +0 -94
  24. data/doc/release_notes/4.22.0.txt +0 -72
  25. data/doc/release_notes/4.23.0.txt +0 -65
  26. data/doc/release_notes/4.24.0.txt +0 -99
  27. data/doc/release_notes/4.25.0.txt +0 -181
  28. data/doc/release_notes/4.26.0.txt +0 -44
  29. data/doc/release_notes/4.27.0.txt +0 -78
  30. data/doc/release_notes/4.28.0.txt +0 -57
  31. data/doc/release_notes/4.29.0.txt +0 -41
  32. data/doc/release_notes/4.3.0.txt +0 -40
  33. data/doc/release_notes/4.30.0.txt +0 -37
  34. data/doc/release_notes/4.31.0.txt +0 -57
  35. data/doc/release_notes/4.32.0.txt +0 -132
  36. data/doc/release_notes/4.33.0.txt +0 -88
  37. data/doc/release_notes/4.34.0.txt +0 -86
  38. data/doc/release_notes/4.35.0.txt +0 -130
  39. data/doc/release_notes/4.36.0.txt +0 -116
  40. data/doc/release_notes/4.37.0.txt +0 -50
  41. data/doc/release_notes/4.38.0.txt +0 -67
  42. data/doc/release_notes/4.39.0.txt +0 -127
  43. data/doc/release_notes/4.4.0.txt +0 -92
  44. data/doc/release_notes/4.40.0.txt +0 -179
  45. data/doc/release_notes/4.41.0.txt +0 -77
  46. data/doc/release_notes/4.42.0.txt +0 -221
  47. data/doc/release_notes/4.43.0.txt +0 -87
  48. data/doc/release_notes/4.44.0.txt +0 -125
  49. data/doc/release_notes/4.45.0.txt +0 -370
  50. data/doc/release_notes/4.46.0.txt +0 -404
  51. data/doc/release_notes/4.47.0.txt +0 -56
  52. data/doc/release_notes/4.48.0.txt +0 -293
  53. data/doc/release_notes/4.49.0.txt +0 -222
  54. data/doc/release_notes/4.5.0.txt +0 -34
  55. data/doc/release_notes/4.6.0.txt +0 -30
  56. data/doc/release_notes/4.7.0.txt +0 -103
  57. data/doc/release_notes/4.8.0.txt +0 -175
  58. data/doc/release_notes/4.9.0.txt +0 -190
  59. data/spec/adapter_spec.rb +0 -4
  60. data/spec/adapters/db2_spec.rb +0 -170
  61. data/spec/adapters/mssql_spec.rb +0 -828
  62. data/spec/adapters/mysql_spec.rb +0 -1060
  63. data/spec/adapters/oracle_spec.rb +0 -371
  64. data/spec/adapters/postgres_spec.rb +0 -4476
  65. data/spec/adapters/spec_helper.rb +0 -44
  66. data/spec/adapters/sqlanywhere_spec.rb +0 -97
  67. data/spec/adapters/sqlite_spec.rb +0 -652
  68. data/spec/bin_spec.rb +0 -278
  69. data/spec/core/connection_pool_spec.rb +0 -1250
  70. data/spec/core/database_spec.rb +0 -2915
  71. data/spec/core/dataset_spec.rb +0 -5544
  72. data/spec/core/deprecated_spec.rb +0 -70
  73. data/spec/core/expression_filters_spec.rb +0 -1498
  74. data/spec/core/mock_adapter_spec.rb +0 -722
  75. data/spec/core/object_graph_spec.rb +0 -336
  76. data/spec/core/placeholder_literalizer_spec.rb +0 -166
  77. data/spec/core/schema_generator_spec.rb +0 -214
  78. data/spec/core/schema_spec.rb +0 -1844
  79. data/spec/core/spec_helper.rb +0 -24
  80. data/spec/core/version_spec.rb +0 -14
  81. data/spec/core_extensions_spec.rb +0 -763
  82. data/spec/core_model_spec.rb +0 -2
  83. data/spec/core_spec.rb +0 -1
  84. data/spec/deprecation_helper.rb +0 -30
  85. data/spec/extensions/accessed_columns_spec.rb +0 -51
  86. data/spec/extensions/active_model_spec.rb +0 -99
  87. data/spec/extensions/after_initialize_spec.rb +0 -28
  88. data/spec/extensions/any_not_empty_spec.rb +0 -23
  89. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  90. data/spec/extensions/association_dependencies_spec.rb +0 -125
  91. data/spec/extensions/association_multi_add_remove_spec.rb +0 -1041
  92. data/spec/extensions/association_pks_spec.rb +0 -423
  93. data/spec/extensions/association_proxies_spec.rb +0 -100
  94. data/spec/extensions/auto_literal_strings_spec.rb +0 -205
  95. data/spec/extensions/auto_validations_spec.rb +0 -229
  96. data/spec/extensions/blacklist_security_spec.rb +0 -95
  97. data/spec/extensions/blank_spec.rb +0 -69
  98. data/spec/extensions/boolean_readers_spec.rb +0 -93
  99. data/spec/extensions/boolean_subsets_spec.rb +0 -47
  100. data/spec/extensions/caching_spec.rb +0 -273
  101. data/spec/extensions/caller_logging_spec.rb +0 -52
  102. data/spec/extensions/class_table_inheritance_spec.rb +0 -750
  103. data/spec/extensions/column_conflicts_spec.rb +0 -75
  104. data/spec/extensions/column_select_spec.rb +0 -129
  105. data/spec/extensions/columns_introspection_spec.rb +0 -90
  106. data/spec/extensions/columns_updated_spec.rb +0 -35
  107. data/spec/extensions/composition_spec.rb +0 -248
  108. data/spec/extensions/connection_expiration_spec.rb +0 -151
  109. data/spec/extensions/connection_validator_spec.rb +0 -144
  110. data/spec/extensions/constant_sql_override_spec.rb +0 -24
  111. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -300
  112. data/spec/extensions/constraint_validations_spec.rb +0 -439
  113. data/spec/extensions/core_refinements_spec.rb +0 -528
  114. data/spec/extensions/csv_serializer_spec.rb +0 -183
  115. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  116. data/spec/extensions/dataset_associations_spec.rb +0 -365
  117. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  118. data/spec/extensions/date_arithmetic_spec.rb +0 -181
  119. data/spec/extensions/datetime_parse_to_time_spec.rb +0 -169
  120. data/spec/extensions/def_dataset_method_spec.rb +0 -100
  121. data/spec/extensions/defaults_setter_spec.rb +0 -150
  122. data/spec/extensions/delay_add_association_spec.rb +0 -73
  123. data/spec/extensions/dirty_spec.rb +0 -222
  124. data/spec/extensions/duplicate_columns_handler_spec.rb +0 -104
  125. data/spec/extensions/eager_each_spec.rb +0 -62
  126. data/spec/extensions/eager_graph_eager_spec.rb +0 -100
  127. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  128. data/spec/extensions/empty_failure_backtraces_spec.rb +0 -60
  129. data/spec/extensions/error_splitter_spec.rb +0 -18
  130. data/spec/extensions/error_sql_spec.rb +0 -20
  131. data/spec/extensions/escaped_like_spec.rb +0 -40
  132. data/spec/extensions/eval_inspect_spec.rb +0 -81
  133. data/spec/extensions/exclude_or_null_spec.rb +0 -15
  134. data/spec/extensions/finder_spec.rb +0 -260
  135. data/spec/extensions/force_encoding_spec.rb +0 -126
  136. data/spec/extensions/freeze_datasets_spec.rb +0 -31
  137. data/spec/extensions/graph_each_spec.rb +0 -113
  138. data/spec/extensions/hook_class_methods_spec.rb +0 -402
  139. data/spec/extensions/identifier_mangling_spec.rb +0 -201
  140. data/spec/extensions/implicit_subquery_spec.rb +0 -58
  141. data/spec/extensions/index_caching_spec.rb +0 -66
  142. data/spec/extensions/inflector_spec.rb +0 -183
  143. data/spec/extensions/input_transformer_spec.rb +0 -69
  144. data/spec/extensions/insert_conflict_spec.rb +0 -103
  145. data/spec/extensions/insert_returning_select_spec.rb +0 -72
  146. data/spec/extensions/instance_filters_spec.rb +0 -79
  147. data/spec/extensions/instance_hooks_spec.rb +0 -246
  148. data/spec/extensions/integer64_spec.rb +0 -22
  149. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  150. data/spec/extensions/json_serializer_spec.rb +0 -346
  151. data/spec/extensions/lazy_attributes_spec.rb +0 -183
  152. data/spec/extensions/list_spec.rb +0 -291
  153. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  154. data/spec/extensions/many_through_many_spec.rb +0 -2177
  155. data/spec/extensions/migration_spec.rb +0 -864
  156. data/spec/extensions/modification_detection_spec.rb +0 -93
  157. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -92
  158. data/spec/extensions/named_timezones_spec.rb +0 -218
  159. data/spec/extensions/nested_attributes_spec.rb +0 -815
  160. data/spec/extensions/null_dataset_spec.rb +0 -85
  161. data/spec/extensions/optimistic_locking_spec.rb +0 -127
  162. data/spec/extensions/pagination_spec.rb +0 -116
  163. data/spec/extensions/pg_array_associations_spec.rb +0 -802
  164. data/spec/extensions/pg_array_ops_spec.rb +0 -144
  165. data/spec/extensions/pg_array_spec.rb +0 -398
  166. data/spec/extensions/pg_auto_constraint_validations_spec.rb +0 -209
  167. data/spec/extensions/pg_enum_spec.rb +0 -118
  168. data/spec/extensions/pg_extended_date_support_spec.rb +0 -126
  169. data/spec/extensions/pg_hstore_ops_spec.rb +0 -238
  170. data/spec/extensions/pg_hstore_spec.rb +0 -219
  171. data/spec/extensions/pg_inet_ops_spec.rb +0 -102
  172. data/spec/extensions/pg_inet_spec.rb +0 -72
  173. data/spec/extensions/pg_interval_spec.rb +0 -103
  174. data/spec/extensions/pg_json_ops_spec.rb +0 -356
  175. data/spec/extensions/pg_json_spec.rb +0 -451
  176. data/spec/extensions/pg_loose_count_spec.rb +0 -23
  177. data/spec/extensions/pg_range_ops_spec.rb +0 -60
  178. data/spec/extensions/pg_range_spec.rb +0 -600
  179. data/spec/extensions/pg_row_ops_spec.rb +0 -61
  180. data/spec/extensions/pg_row_plugin_spec.rb +0 -60
  181. data/spec/extensions/pg_row_spec.rb +0 -363
  182. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -93
  183. data/spec/extensions/pg_timestamptz_spec.rb +0 -17
  184. data/spec/extensions/prepared_statements_safe_spec.rb +0 -66
  185. data/spec/extensions/prepared_statements_spec.rb +0 -177
  186. data/spec/extensions/pretty_table_spec.rb +0 -123
  187. data/spec/extensions/query_spec.rb +0 -94
  188. data/spec/extensions/rcte_tree_spec.rb +0 -387
  189. data/spec/extensions/round_timestamps_spec.rb +0 -39
  190. data/spec/extensions/s_spec.rb +0 -60
  191. data/spec/extensions/schema_caching_spec.rb +0 -64
  192. data/spec/extensions/schema_dumper_spec.rb +0 -870
  193. data/spec/extensions/select_remove_spec.rb +0 -38
  194. data/spec/extensions/sequel_4_dataset_methods_spec.rb +0 -121
  195. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  196. data/spec/extensions/serialization_spec.rb +0 -365
  197. data/spec/extensions/server_block_spec.rb +0 -135
  198. data/spec/extensions/server_logging_spec.rb +0 -45
  199. data/spec/extensions/sharding_spec.rb +0 -197
  200. data/spec/extensions/shared_caching_spec.rb +0 -151
  201. data/spec/extensions/single_table_inheritance_spec.rb +0 -347
  202. data/spec/extensions/singular_table_names_spec.rb +0 -22
  203. data/spec/extensions/skip_create_refresh_spec.rb +0 -18
  204. data/spec/extensions/spec_helper.rb +0 -70
  205. data/spec/extensions/split_array_nil_spec.rb +0 -24
  206. data/spec/extensions/split_values_spec.rb +0 -57
  207. data/spec/extensions/sql_comments_spec.rb +0 -33
  208. data/spec/extensions/sql_expr_spec.rb +0 -59
  209. data/spec/extensions/static_cache_cache_spec.rb +0 -35
  210. data/spec/extensions/static_cache_spec.rb +0 -471
  211. data/spec/extensions/string_agg_spec.rb +0 -90
  212. data/spec/extensions/string_date_time_spec.rb +0 -95
  213. data/spec/extensions/string_stripper_spec.rb +0 -68
  214. data/spec/extensions/subclasses_spec.rb +0 -79
  215. data/spec/extensions/subset_conditions_spec.rb +0 -38
  216. data/spec/extensions/symbol_aref_refinement_spec.rb +0 -28
  217. data/spec/extensions/symbol_as_refinement_spec.rb +0 -21
  218. data/spec/extensions/synchronize_sql_spec.rb +0 -124
  219. data/spec/extensions/table_select_spec.rb +0 -83
  220. data/spec/extensions/tactical_eager_loading_spec.rb +0 -402
  221. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  222. data/spec/extensions/throw_failures_spec.rb +0 -74
  223. data/spec/extensions/timestamps_spec.rb +0 -209
  224. data/spec/extensions/to_dot_spec.rb +0 -153
  225. data/spec/extensions/touch_spec.rb +0 -226
  226. data/spec/extensions/tree_spec.rb +0 -334
  227. data/spec/extensions/typecast_on_load_spec.rb +0 -86
  228. data/spec/extensions/unlimited_update_spec.rb +0 -21
  229. data/spec/extensions/update_or_create_spec.rb +0 -83
  230. data/spec/extensions/update_primary_key_spec.rb +0 -105
  231. data/spec/extensions/update_refresh_spec.rb +0 -59
  232. data/spec/extensions/uuid_spec.rb +0 -101
  233. data/spec/extensions/validate_associated_spec.rb +0 -52
  234. data/spec/extensions/validation_class_methods_spec.rb +0 -1040
  235. data/spec/extensions/validation_contexts_spec.rb +0 -31
  236. data/spec/extensions/validation_helpers_spec.rb +0 -525
  237. data/spec/extensions/whitelist_security_spec.rb +0 -157
  238. data/spec/extensions/xml_serializer_spec.rb +0 -213
  239. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  240. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  241. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  242. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  243. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  244. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  245. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  246. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  247. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  248. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  249. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  250. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  251. data/spec/files/double_migration/001_create_sessions.rb +0 -9
  252. data/spec/files/double_migration/002_create_nodes.rb +0 -19
  253. data/spec/files/double_migration/003_3_create_users.rb +0 -4
  254. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  255. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  256. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  257. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  258. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  259. data/spec/files/empty_migration/001_create_sessions.rb +0 -9
  260. data/spec/files/empty_migration/002_create_nodes.rb +0 -0
  261. data/spec/files/empty_migration/003_3_create_users.rb +0 -4
  262. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  263. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  264. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  265. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  266. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  267. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  268. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  269. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  270. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  271. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  272. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  273. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  274. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  275. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  276. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  277. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  278. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  279. data/spec/files/reversible_migrations/006_reversible.rb +0 -10
  280. data/spec/files/reversible_migrations/007_reversible.rb +0 -10
  281. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  282. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  283. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  284. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  285. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  286. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  287. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  288. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  289. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  290. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  291. data/spec/guards_helper.rb +0 -59
  292. data/spec/integration/associations_test.rb +0 -2597
  293. data/spec/integration/database_test.rb +0 -113
  294. data/spec/integration/dataset_test.rb +0 -2037
  295. data/spec/integration/eager_loader_test.rb +0 -687
  296. data/spec/integration/migrator_test.rb +0 -262
  297. data/spec/integration/model_test.rb +0 -203
  298. data/spec/integration/plugin_test.rb +0 -2423
  299. data/spec/integration/prepared_statement_test.rb +0 -405
  300. data/spec/integration/schema_test.rb +0 -903
  301. data/spec/integration/spec_helper.rb +0 -71
  302. data/spec/integration/timezone_test.rb +0 -86
  303. data/spec/integration/transaction_test.rb +0 -603
  304. data/spec/integration/type_test.rb +0 -127
  305. data/spec/model/association_reflection_spec.rb +0 -803
  306. data/spec/model/associations_spec.rb +0 -4738
  307. data/spec/model/base_spec.rb +0 -875
  308. data/spec/model/class_dataset_methods_spec.rb +0 -146
  309. data/spec/model/dataset_methods_spec.rb +0 -198
  310. data/spec/model/eager_loading_spec.rb +0 -2377
  311. data/spec/model/hooks_spec.rb +0 -370
  312. data/spec/model/inflector_spec.rb +0 -26
  313. data/spec/model/model_spec.rb +0 -956
  314. data/spec/model/plugins_spec.rb +0 -429
  315. data/spec/model/record_spec.rb +0 -2118
  316. data/spec/model/spec_helper.rb +0 -46
  317. data/spec/model/validations_spec.rb +0 -220
  318. data/spec/model_no_assoc_spec.rb +0 -1
  319. data/spec/model_spec.rb +0 -1
  320. data/spec/plugin_spec.rb +0 -1
  321. data/spec/sequel_coverage.rb +0 -15
  322. data/spec/sequel_warning.rb +0 -5
  323. 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