ibm_db 5.1.0-x86-mingw32 → 5.3.2-x86-mingw32

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 (624) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +9 -0
  3. data/LICENSE +55 -18
  4. data/ext/Makefile +14 -14
  5. data/ext/extconf.rb +4 -4
  6. data/ext/ibm_db.c +62 -57
  7. data/ext/ibm_db.o +0 -0
  8. data/ext/ibm_db.so +0 -0
  9. data/ext/mkmf.log +11 -11
  10. data/ext/ruby_ibm_db_cli.c +1 -0
  11. data/ext/ruby_ibm_db_cli.o +0 -0
  12. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +1463 -1279
  13. data/lib/ibm_db.so +1 -0
  14. data/lib/mswin32/ibm_db.rb +7 -3
  15. data/lib/mswin32/rb2x/i386/ruby25/ibm_db.so +0 -0
  16. data/lib/mswin32/rb3x/i386/ruby30/ibm_db.so +0 -0
  17. data/test/active_record/connection_adapters/fake_adapter.rb +5 -2
  18. data/test/activejob/destroy_association_async_test.rb +305 -0
  19. data/test/activejob/destroy_async_job_not_present_test.rb +31 -0
  20. data/test/activejob/helper.rb +15 -0
  21. data/test/assets/schema_dump_5_1.yml +345 -0
  22. data/test/cases/adapter_prevent_writes_test.rb +334 -0
  23. data/test/cases/adapter_test.rb +432 -218
  24. data/test/cases/adapters/mysql2/active_schema_test.rb +85 -75
  25. data/test/cases/adapters/mysql2/auto_increment_test.rb +34 -0
  26. data/test/cases/adapters/mysql2/bind_parameter_test.rb +5 -3
  27. data/test/cases/adapters/mysql2/boolean_test.rb +6 -4
  28. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +26 -24
  29. data/test/cases/adapters/mysql2/charset_collation_test.rb +20 -17
  30. data/test/cases/adapters/mysql2/connection_test.rb +48 -50
  31. data/test/cases/adapters/mysql2/count_deleted_rows_with_lock_test.rb +28 -0
  32. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +23 -19
  33. data/test/cases/adapters/mysql2/enum_test.rb +32 -11
  34. data/test/cases/adapters/mysql2/explain_test.rb +13 -11
  35. data/test/cases/adapters/mysql2/json_test.rb +17 -188
  36. data/test/cases/adapters/mysql2/mysql2_adapter_prevent_writes_test.rb +208 -0
  37. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +183 -28
  38. data/test/cases/adapters/mysql2/nested_deadlock_test.rb +75 -0
  39. data/test/cases/adapters/mysql2/optimizer_hints_test.rb +69 -0
  40. data/test/cases/adapters/mysql2/schema_migrations_test.rb +26 -21
  41. data/test/cases/adapters/mysql2/schema_test.rb +24 -22
  42. data/test/cases/adapters/mysql2/set_test.rb +32 -0
  43. data/test/cases/adapters/mysql2/sp_test.rb +10 -8
  44. data/test/cases/adapters/mysql2/sql_types_test.rb +8 -6
  45. data/test/cases/adapters/mysql2/table_options_test.rb +93 -10
  46. data/test/cases/adapters/mysql2/transaction_test.rb +151 -0
  47. data/test/cases/adapters/mysql2/unsigned_type_test.rb +11 -9
  48. data/test/cases/adapters/mysql2/virtual_column_test.rb +66 -0
  49. data/test/cases/adapters/postgresql/active_schema_test.rb +40 -25
  50. data/test/cases/adapters/postgresql/array_test.rb +118 -63
  51. data/test/cases/adapters/postgresql/bit_string_test.rb +12 -10
  52. data/test/cases/adapters/postgresql/bytea_test.rb +26 -25
  53. data/test/cases/adapters/postgresql/case_insensitive_test.rb +10 -9
  54. data/test/cases/adapters/postgresql/change_schema_test.rb +7 -5
  55. data/test/cases/adapters/postgresql/cidr_test.rb +2 -0
  56. data/test/cases/adapters/postgresql/citext_test.rb +58 -58
  57. data/test/cases/adapters/postgresql/collation_test.rb +17 -15
  58. data/test/cases/adapters/postgresql/composite_test.rb +25 -23
  59. data/test/cases/adapters/postgresql/connection_test.rb +73 -85
  60. data/test/cases/adapters/postgresql/create_unlogged_tables_test.rb +74 -0
  61. data/test/cases/adapters/postgresql/datatype_test.rb +19 -22
  62. data/test/cases/adapters/postgresql/date_test.rb +42 -0
  63. data/test/cases/adapters/postgresql/domain_test.rb +9 -7
  64. data/test/cases/adapters/postgresql/enum_test.rb +12 -10
  65. data/test/cases/adapters/postgresql/explain_test.rb +10 -8
  66. data/test/cases/adapters/postgresql/extension_migration_test.rb +13 -12
  67. data/test/cases/adapters/postgresql/foreign_table_test.rb +109 -0
  68. data/test/cases/adapters/postgresql/full_text_test.rb +8 -6
  69. data/test/cases/adapters/postgresql/geometric_test.rb +57 -63
  70. data/test/cases/adapters/postgresql/hstore_test.rb +288 -280
  71. data/test/cases/adapters/postgresql/infinity_test.rb +54 -15
  72. data/test/cases/adapters/postgresql/integer_test.rb +2 -0
  73. data/test/cases/adapters/postgresql/interval_test.rb +99 -0
  74. data/test/cases/adapters/postgresql/json_test.rb +16 -201
  75. data/test/cases/adapters/postgresql/ltree_test.rb +14 -16
  76. data/test/cases/adapters/postgresql/money_test.rb +47 -16
  77. data/test/cases/adapters/postgresql/network_test.rb +36 -28
  78. data/test/cases/adapters/postgresql/numbers_test.rb +7 -5
  79. data/test/cases/adapters/postgresql/optimizer_hints_test.rb +71 -0
  80. data/test/cases/adapters/postgresql/partitions_test.rb +22 -0
  81. data/test/cases/adapters/postgresql/postgresql_adapter_prevent_writes_test.rb +205 -0
  82. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +178 -136
  83. data/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb +27 -0
  84. data/test/cases/adapters/postgresql/quoting_test.rb +12 -6
  85. data/test/cases/adapters/postgresql/range_test.rb +406 -292
  86. data/test/cases/adapters/postgresql/referential_integrity_test.rb +16 -15
  87. data/test/cases/adapters/postgresql/rename_table_test.rb +9 -8
  88. data/test/cases/adapters/postgresql/schema_authorization_test.rb +14 -23
  89. data/test/cases/adapters/postgresql/schema_test.rb +207 -91
  90. data/test/cases/adapters/postgresql/serial_test.rb +9 -7
  91. data/test/cases/adapters/postgresql/statement_pool_test.rb +26 -6
  92. data/test/cases/adapters/postgresql/timestamp_test.rb +17 -15
  93. data/test/cases/adapters/postgresql/transaction_nested_test.rb +114 -0
  94. data/test/cases/adapters/postgresql/transaction_test.rb +189 -0
  95. data/test/cases/adapters/postgresql/type_lookup_test.rb +12 -10
  96. data/test/cases/adapters/postgresql/utils_test.rb +11 -9
  97. data/test/cases/adapters/postgresql/uuid_test.rb +226 -109
  98. data/test/cases/adapters/postgresql/xml_test.rb +10 -14
  99. data/test/cases/adapters/sqlite3/collation_test.rb +26 -15
  100. data/test/cases/adapters/sqlite3/copy_table_test.rb +31 -28
  101. data/test/cases/adapters/sqlite3/explain_test.rb +13 -11
  102. data/test/cases/adapters/sqlite3/json_test.rb +29 -0
  103. data/test/cases/adapters/sqlite3/quoting_test.rb +35 -57
  104. data/test/cases/adapters/sqlite3/sqlite3_adapter_prevent_writes_test.rb +186 -0
  105. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +318 -131
  106. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +11 -11
  107. data/test/cases/adapters/sqlite3/statement_pool_test.rb +7 -6
  108. data/test/cases/adapters/sqlite3/transaction_test.rb +123 -0
  109. data/test/cases/aggregations_test.rb +14 -12
  110. data/test/cases/annotate_test.rb +46 -0
  111. data/test/cases/ar_schema_test.rb +153 -86
  112. data/test/cases/arel/attributes/attribute_test.rb +1145 -0
  113. data/test/cases/arel/attributes/math_test.rb +83 -0
  114. data/test/cases/arel/attributes_test.rb +27 -0
  115. data/test/cases/arel/collectors/bind_test.rb +40 -0
  116. data/test/cases/arel/collectors/composite_test.rb +47 -0
  117. data/test/cases/arel/collectors/sql_string_test.rb +41 -0
  118. data/test/cases/arel/collectors/substitute_bind_collector_test.rb +48 -0
  119. data/test/cases/arel/crud_test.rb +65 -0
  120. data/test/cases/arel/delete_manager_test.rb +53 -0
  121. data/test/cases/arel/factory_methods_test.rb +46 -0
  122. data/test/cases/arel/helper.rb +45 -0
  123. data/test/cases/arel/insert_manager_test.rb +241 -0
  124. data/test/cases/arel/nodes/and_test.rb +30 -0
  125. data/test/cases/arel/nodes/as_test.rb +36 -0
  126. data/test/cases/arel/nodes/ascending_test.rb +46 -0
  127. data/test/cases/arel/nodes/bin_test.rb +35 -0
  128. data/test/cases/arel/nodes/binary_test.rb +29 -0
  129. data/test/cases/arel/nodes/bind_param_test.rb +22 -0
  130. data/test/cases/arel/nodes/case_test.rb +96 -0
  131. data/test/cases/arel/nodes/casted_test.rb +18 -0
  132. data/test/cases/arel/nodes/comment_test.rb +22 -0
  133. data/test/cases/arel/nodes/count_test.rb +35 -0
  134. data/test/cases/arel/nodes/delete_statement_test.rb +36 -0
  135. data/test/cases/arel/nodes/descending_test.rb +46 -0
  136. data/test/cases/arel/nodes/distinct_test.rb +21 -0
  137. data/test/cases/arel/nodes/equality_test.rb +62 -0
  138. data/test/cases/arel/nodes/extract_test.rb +43 -0
  139. data/test/cases/arel/nodes/false_test.rb +21 -0
  140. data/test/cases/arel/nodes/grouping_test.rb +26 -0
  141. data/test/cases/arel/nodes/infix_operation_test.rb +42 -0
  142. data/test/cases/arel/nodes/insert_statement_test.rb +44 -0
  143. data/test/cases/arel/nodes/named_function_test.rb +48 -0
  144. data/test/cases/arel/nodes/node_test.rb +22 -0
  145. data/test/cases/arel/nodes/not_test.rb +31 -0
  146. data/test/cases/arel/nodes/or_test.rb +36 -0
  147. data/test/cases/arel/nodes/over_test.rb +69 -0
  148. data/test/cases/arel/nodes/select_core_test.rb +79 -0
  149. data/test/cases/arel/nodes/select_statement_test.rb +51 -0
  150. data/test/cases/arel/nodes/sql_literal_test.rb +75 -0
  151. data/test/cases/arel/nodes/sum_test.rb +35 -0
  152. data/test/cases/arel/nodes/table_alias_test.rb +29 -0
  153. data/test/cases/arel/nodes/true_test.rb +21 -0
  154. data/test/cases/arel/nodes/unary_operation_test.rb +41 -0
  155. data/test/cases/arel/nodes/update_statement_test.rb +60 -0
  156. data/test/cases/arel/nodes/window_test.rb +81 -0
  157. data/test/cases/arel/nodes_test.rb +34 -0
  158. data/test/cases/arel/select_manager_test.rb +1238 -0
  159. data/test/cases/arel/support/fake_record.rb +135 -0
  160. data/test/cases/arel/table_test.rb +216 -0
  161. data/test/cases/arel/update_manager_test.rb +126 -0
  162. data/test/cases/arel/visitors/dispatch_contamination_test.rb +78 -0
  163. data/test/cases/arel/visitors/dot_test.rb +90 -0
  164. data/test/cases/arel/visitors/mysql_test.rb +157 -0
  165. data/test/cases/arel/visitors/postgres_test.rb +366 -0
  166. data/test/cases/arel/visitors/sqlite_test.rb +75 -0
  167. data/test/cases/arel/visitors/to_sql_test.rb +750 -0
  168. data/test/cases/associations/belongs_to_associations_test.rb +510 -158
  169. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +4 -2
  170. data/test/cases/associations/callbacks_test.rb +56 -38
  171. data/test/cases/associations/cascaded_eager_loading_test.rb +118 -61
  172. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +138 -18
  173. data/test/cases/associations/eager_load_nested_include_test.rb +38 -37
  174. data/test/cases/associations/eager_singularization_test.rb +21 -21
  175. data/test/cases/associations/eager_test.rb +559 -415
  176. data/test/cases/associations/extension_test.rb +18 -12
  177. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +234 -213
  178. data/test/cases/associations/has_many_associations_test.rb +1038 -465
  179. data/test/cases/associations/has_many_through_associations_test.rb +558 -249
  180. data/test/cases/associations/has_one_associations_test.rb +294 -129
  181. data/test/cases/associations/has_one_through_associations_test.rb +121 -75
  182. data/test/cases/associations/inner_join_association_test.rb +114 -38
  183. data/test/cases/associations/inverse_associations_test.rb +606 -398
  184. data/test/cases/associations/join_model_test.rb +158 -148
  185. data/test/cases/associations/left_outer_join_association_test.rb +59 -24
  186. data/test/cases/associations/nested_through_associations_test.rb +166 -109
  187. data/test/cases/associations/required_test.rb +35 -10
  188. data/test/cases/associations_test.rb +241 -110
  189. data/test/cases/attribute_methods/read_test.rb +11 -11
  190. data/test/cases/attribute_methods_test.rb +413 -298
  191. data/test/cases/attributes_test.rb +145 -27
  192. data/test/cases/autosave_association_test.rb +681 -436
  193. data/test/cases/base_prevent_writes_test.rb +229 -0
  194. data/test/cases/base_test.rb +599 -542
  195. data/test/cases/batches_test.rb +288 -82
  196. data/test/cases/binary_test.rb +26 -31
  197. data/test/cases/bind_parameter_test.rb +194 -21
  198. data/test/cases/boolean_test.rb +52 -0
  199. data/test/cases/cache_key_test.rb +110 -5
  200. data/test/cases/calculations_test.rb +740 -177
  201. data/test/cases/callbacks_test.rb +74 -207
  202. data/test/cases/clone_test.rb +15 -10
  203. data/test/cases/coders/json_test.rb +2 -0
  204. data/test/cases/coders/yaml_column_test.rb +16 -13
  205. data/test/cases/collection_cache_key_test.rb +177 -20
  206. data/test/cases/column_alias_test.rb +9 -7
  207. data/test/cases/column_definition_test.rb +10 -68
  208. data/test/cases/comment_test.rb +166 -107
  209. data/test/cases/connection_adapters/adapter_leasing_test.rb +14 -10
  210. data/test/cases/connection_adapters/connection_handler_test.rb +358 -51
  211. data/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +400 -0
  212. data/test/cases/connection_adapters/connection_handlers_multi_pool_config_test.rb +103 -0
  213. data/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb +499 -0
  214. data/test/cases/connection_adapters/connection_swapping_nested_test.rb +457 -0
  215. data/test/cases/connection_adapters/legacy_connection_handlers_multi_db_test.rb +486 -0
  216. data/test/cases/connection_adapters/legacy_connection_handlers_sharding_db_test.rb +586 -0
  217. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +319 -138
  218. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +62 -50
  219. data/test/cases/connection_adapters/schema_cache_test.rb +259 -26
  220. data/test/cases/connection_adapters/type_lookup_test.rb +96 -95
  221. data/test/cases/connection_management_test.rb +13 -11
  222. data/test/cases/connection_pool_test.rb +316 -83
  223. data/test/cases/core_test.rb +82 -58
  224. data/test/cases/counter_cache_test.rb +204 -50
  225. data/test/cases/custom_locking_test.rb +5 -3
  226. data/test/cases/database_configurations/hash_config_test.rb +74 -0
  227. data/test/cases/database_configurations/resolver_test.rb +150 -0
  228. data/test/cases/database_configurations_test.rb +145 -0
  229. data/test/cases/database_selector_test.rb +296 -0
  230. data/test/cases/database_statements_test.rb +18 -16
  231. data/test/cases/date_test.rb +8 -16
  232. data/test/cases/date_time_precision_test.rb +100 -78
  233. data/test/cases/date_time_test.rb +23 -8
  234. data/test/cases/defaults_test.rb +106 -71
  235. data/test/cases/delegated_type_test.rb +57 -0
  236. data/test/cases/dirty_test.rb +419 -223
  237. data/test/cases/disconnected_test.rb +6 -6
  238. data/test/cases/dup_test.rb +54 -27
  239. data/test/cases/enum_test.rb +461 -82
  240. data/test/cases/errors_test.rb +7 -7
  241. data/test/cases/explain_subscriber_test.rb +17 -15
  242. data/test/cases/explain_test.rb +11 -19
  243. data/test/cases/filter_attributes_test.rb +153 -0
  244. data/test/cases/finder_respond_to_test.rb +14 -14
  245. data/test/cases/finder_test.rb +669 -287
  246. data/test/cases/fixture_set/file_test.rb +34 -38
  247. data/test/cases/fixtures_test.rb +833 -176
  248. data/test/cases/forbidden_attributes_protection_test.rb +32 -67
  249. data/test/cases/habtm_destroy_order_test.rb +25 -25
  250. data/test/cases/helper.rb +78 -49
  251. data/test/cases/hot_compatibility_test.rb +33 -32
  252. data/test/cases/i18n_test.rb +18 -17
  253. data/test/cases/inheritance_test.rb +180 -115
  254. data/test/cases/insert_all_test.rb +489 -0
  255. data/test/cases/instrumentation_test.rb +101 -0
  256. data/test/cases/integration_test.rb +119 -31
  257. data/test/cases/invalid_connection_test.rb +18 -16
  258. data/test/cases/invertible_migration_test.rb +183 -43
  259. data/test/cases/json_attribute_test.rb +35 -0
  260. data/test/cases/json_serialization_test.rb +57 -58
  261. data/test/cases/json_shared_test_cases.rb +290 -0
  262. data/test/cases/locking_test.rb +413 -119
  263. data/test/cases/log_subscriber_test.rb +68 -26
  264. data/test/cases/marshal_serialization_test.rb +39 -0
  265. data/test/cases/migration/change_schema_test.rb +118 -72
  266. data/test/cases/migration/change_table_test.rb +138 -30
  267. data/test/cases/migration/check_constraint_test.rb +162 -0
  268. data/test/cases/migration/column_attributes_test.rb +45 -35
  269. data/test/cases/migration/column_positioning_test.rb +18 -6
  270. data/test/cases/migration/columns_test.rb +93 -77
  271. data/test/cases/migration/command_recorder_test.rb +121 -34
  272. data/test/cases/migration/compatibility_test.rb +578 -23
  273. data/test/cases/migration/create_join_table_test.rb +35 -25
  274. data/test/cases/migration/foreign_key_test.rb +503 -284
  275. data/test/cases/migration/helper.rb +4 -3
  276. data/test/cases/migration/index_test.rb +119 -70
  277. data/test/cases/migration/logger_test.rb +9 -6
  278. data/test/cases/migration/pending_migrations_test.rb +88 -34
  279. data/test/cases/migration/references_foreign_key_test.rb +164 -150
  280. data/test/cases/migration/references_index_test.rb +38 -19
  281. data/test/cases/migration/references_statements_test.rb +15 -14
  282. data/test/cases/migration/rename_table_test.rb +53 -30
  283. data/test/cases/migration_test.rb +637 -269
  284. data/test/cases/migrator_test.rb +191 -135
  285. data/test/cases/mixin_test.rb +7 -11
  286. data/test/cases/modules_test.rb +36 -34
  287. data/test/cases/multi_db_migrator_test.rb +223 -0
  288. data/test/cases/multiparameter_attributes_test.rb +60 -33
  289. data/test/cases/multiple_db_test.rb +16 -22
  290. data/test/cases/nested_attributes_test.rb +341 -320
  291. data/test/cases/nested_attributes_with_callbacks_test.rb +26 -24
  292. data/test/cases/null_relation_test.rb +84 -0
  293. data/test/cases/numeric_data_test.rb +93 -0
  294. data/test/cases/persistence_test.rb +361 -269
  295. data/test/cases/pooled_connections_test.rb +18 -26
  296. data/test/cases/prepared_statement_status_test.rb +48 -0
  297. data/test/cases/primary_keys_test.rb +210 -104
  298. data/test/cases/query_cache_test.rb +610 -141
  299. data/test/cases/quoting_test.rb +132 -31
  300. data/test/cases/readonly_test.rb +49 -48
  301. data/test/cases/reaper_test.rb +146 -32
  302. data/test/cases/reflection_test.rb +167 -156
  303. data/test/cases/relation/delegation_test.rb +49 -36
  304. data/test/cases/relation/delete_all_test.rb +117 -0
  305. data/test/cases/relation/merging_test.rb +319 -42
  306. data/test/cases/relation/mutation_test.rb +55 -93
  307. data/test/cases/relation/or_test.rb +129 -29
  308. data/test/cases/relation/predicate_builder_test.rb +21 -6
  309. data/test/cases/relation/record_fetch_warning_test.rb +5 -3
  310. data/test/cases/relation/select_test.rb +67 -0
  311. data/test/cases/relation/update_all_test.rb +317 -0
  312. data/test/cases/relation/where_chain_test.rb +68 -32
  313. data/test/cases/relation/where_clause_test.rb +136 -61
  314. data/test/cases/relation/where_test.rb +155 -48
  315. data/test/cases/relation_test.rb +266 -112
  316. data/test/cases/relations_test.rb +969 -744
  317. data/test/cases/reload_models_test.rb +13 -9
  318. data/test/cases/reserved_word_test.rb +141 -0
  319. data/test/cases/result_test.rb +68 -17
  320. data/test/cases/sanitize_test.rb +87 -71
  321. data/test/cases/schema_dumper_test.rb +221 -128
  322. data/test/cases/schema_loading_test.rb +3 -2
  323. data/test/cases/scoping/default_scoping_test.rb +185 -144
  324. data/test/cases/scoping/named_scoping_test.rb +177 -89
  325. data/test/cases/scoping/relation_scoping_test.rb +197 -75
  326. data/test/cases/secure_token_test.rb +18 -3
  327. data/test/cases/serialization_test.rb +30 -28
  328. data/test/cases/serialized_attribute_test.rb +133 -42
  329. data/test/cases/signed_id_test.rb +168 -0
  330. data/test/cases/statement_cache_test.rb +41 -24
  331. data/test/cases/statement_invalid_test.rb +42 -0
  332. data/test/cases/store_test.rb +180 -55
  333. data/test/cases/strict_loading_test.rb +473 -0
  334. data/test/cases/suppressor_test.rb +26 -12
  335. data/test/cases/tasks/database_tasks_test.rb +1258 -194
  336. data/test/cases/tasks/mysql_rake_test.rb +370 -298
  337. data/test/cases/tasks/postgresql_rake_test.rb +481 -251
  338. data/test/cases/tasks/sqlite_rake_test.rb +225 -178
  339. data/test/cases/test_case.rb +51 -40
  340. data/test/cases/test_databases_test.rb +79 -0
  341. data/test/cases/test_fixtures_test.rb +79 -19
  342. data/test/cases/time_precision_test.rb +98 -76
  343. data/test/cases/timestamp_test.rb +102 -99
  344. data/test/cases/touch_later_test.rb +12 -10
  345. data/test/cases/transaction_callbacks_test.rb +344 -90
  346. data/test/cases/transaction_isolation_test.rb +12 -12
  347. data/test/cases/transactions_test.rb +612 -162
  348. data/test/cases/type/adapter_specific_registry_test.rb +14 -2
  349. data/test/cases/type/date_time_test.rb +4 -2
  350. data/test/cases/type/integer_test.rb +4 -2
  351. data/test/cases/type/string_test.rb +10 -8
  352. data/test/cases/type/time_test.rb +28 -0
  353. data/test/cases/type/type_map_test.rb +29 -28
  354. data/test/cases/type/unsigned_integer_test.rb +19 -0
  355. data/test/cases/type_test.rb +2 -0
  356. data/test/cases/types_test.rb +3 -1
  357. data/test/cases/unconnected_test.rb +14 -1
  358. data/test/cases/unsafe_raw_sql_test.rb +274 -0
  359. data/test/cases/validations/absence_validation_test.rb +19 -17
  360. data/test/cases/validations/association_validation_test.rb +30 -28
  361. data/test/cases/validations/i18n_generate_message_validation_test.rb +34 -16
  362. data/test/cases/validations/i18n_validation_test.rb +22 -21
  363. data/test/cases/validations/length_validation_test.rb +34 -33
  364. data/test/cases/validations/numericality_validation_test.rb +181 -0
  365. data/test/cases/validations/presence_validation_test.rb +21 -19
  366. data/test/cases/validations/uniqueness_validation_test.rb +156 -86
  367. data/test/cases/validations_repair_helper.rb +2 -0
  368. data/test/cases/validations_test.rb +61 -26
  369. data/test/cases/view_test.rb +122 -116
  370. data/test/cases/yaml_serialization_test.rb +79 -34
  371. data/test/config.example.yml +19 -19
  372. data/test/config.rb +3 -1
  373. data/test/config.yml +16 -6
  374. data/test/fixtures/all/namespaced/accounts.yml +2 -0
  375. data/test/fixtures/author_addresses.yml +1 -8
  376. data/test/fixtures/authors.yml +1 -7
  377. data/test/fixtures/binaries.yml +4 -0
  378. data/test/fixtures/books.yml +9 -2
  379. data/test/fixtures/categories_posts.yml +3 -0
  380. data/test/fixtures/citations.yml +5 -0
  381. data/test/fixtures/comments.yml +7 -0
  382. data/test/fixtures/companies.yml +5 -0
  383. data/test/fixtures/computers.yml +2 -0
  384. data/test/fixtures/customers.yml +10 -1
  385. data/test/fixtures/developers.yml +1 -1
  386. data/test/fixtures/essays.yml +10 -0
  387. data/test/fixtures/faces.yml +3 -3
  388. data/test/fixtures/humans.yml +5 -0
  389. data/test/fixtures/interests.yml +7 -7
  390. data/test/fixtures/memberships.yml +7 -0
  391. data/test/fixtures/minimalistics.yml +3 -0
  392. data/test/fixtures/mixed_case_monkeys.yml +2 -2
  393. data/test/fixtures/naked/yml/courses_with_invalid_key.yml +3 -0
  394. data/test/fixtures/naked/yml/parrots.yml +1 -0
  395. data/test/fixtures/other_books.yml +26 -0
  396. data/test/fixtures/other_posts.yml +1 -0
  397. data/test/fixtures/parrots.yml +7 -1
  398. data/test/fixtures/pirates.yml +3 -0
  399. data/test/fixtures/posts.yml +11 -3
  400. data/test/fixtures/readers.yml +6 -0
  401. data/test/fixtures/reserved_words/values.yml +2 -2
  402. data/test/fixtures/sponsors.yml +3 -0
  403. data/test/fixtures/strict_zines.yml +2 -0
  404. data/test/fixtures/subscribers.yml +1 -1
  405. data/test/fixtures/tasks.yml +1 -1
  406. data/test/fixtures/warehouse-things.yml +3 -0
  407. data/test/migrations/10_urban/9_add_expressions.rb +2 -0
  408. data/test/migrations/decimal/1_give_me_big_numbers.rb +6 -4
  409. data/test/migrations/magic/1_currencies_have_symbols.rb +3 -2
  410. data/test/migrations/missing/1000_people_have_middle_names.rb +2 -0
  411. data/test/migrations/missing/1_people_have_last_names.rb +2 -0
  412. data/test/migrations/missing/3_we_need_reminders.rb +2 -0
  413. data/test/migrations/missing/4_innocent_jointable.rb +3 -1
  414. data/test/migrations/rename/1_we_need_things.rb +2 -0
  415. data/test/migrations/rename/2_rename_things.rb +2 -0
  416. data/test/migrations/to_copy/1_people_have_hobbies.rb +3 -1
  417. data/test/migrations/to_copy/2_people_have_descriptions.rb +3 -1
  418. data/test/migrations/to_copy2/1_create_articles.rb +2 -0
  419. data/test/migrations/to_copy2/2_create_comments.rb +3 -1
  420. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +3 -1
  421. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +3 -1
  422. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +3 -1
  423. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +2 -0
  424. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +2 -0
  425. data/test/migrations/valid/1_valid_people_have_last_names.rb +2 -0
  426. data/test/migrations/valid/2_we_need_reminders.rb +2 -0
  427. data/test/migrations/valid/3_innocent_jointable.rb +3 -1
  428. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +2 -0
  429. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +2 -0
  430. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +3 -1
  431. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +2 -0
  432. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +2 -0
  433. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +3 -1
  434. data/test/migrations/version_check/20131219224947_migration_version_check.rb +2 -0
  435. data/test/models/account.rb +46 -0
  436. data/test/models/admin/account.rb +3 -1
  437. data/test/models/admin/randomly_named_c1.rb +2 -0
  438. data/test/models/admin/user.rb +16 -8
  439. data/test/models/admin.rb +4 -2
  440. data/test/models/aircraft.rb +3 -1
  441. data/test/models/arunit2_model.rb +2 -0
  442. data/test/models/author.rb +153 -102
  443. data/test/models/auto_id.rb +2 -0
  444. data/test/models/autoloadable/extra_firm.rb +2 -0
  445. data/test/models/binary.rb +3 -1
  446. data/test/models/binary_field.rb +6 -0
  447. data/test/models/bird.rb +13 -1
  448. data/test/models/book.rb +14 -4
  449. data/test/models/book_destroy_async.rb +24 -0
  450. data/test/models/boolean.rb +5 -0
  451. data/test/models/bulb.rb +13 -4
  452. data/test/models/cake_designer.rb +2 -0
  453. data/test/models/car.rb +17 -10
  454. data/test/models/carrier.rb +2 -0
  455. data/test/models/cart.rb +5 -0
  456. data/test/models/cat.rb +2 -0
  457. data/test/models/categorization.rb +8 -6
  458. data/test/models/category.rb +28 -16
  459. data/test/models/chef.rb +2 -0
  460. data/test/models/citation.rb +5 -1
  461. data/test/models/club.rb +13 -10
  462. data/test/models/college.rb +4 -2
  463. data/test/models/column.rb +2 -0
  464. data/test/models/column_name.rb +2 -0
  465. data/test/models/comment.rb +32 -10
  466. data/test/models/company.rb +102 -106
  467. data/test/models/company_in_module.rb +27 -26
  468. data/test/models/computer.rb +3 -1
  469. data/test/models/contact.rb +15 -13
  470. data/test/models/content.rb +5 -3
  471. data/test/models/contract.rb +21 -3
  472. data/test/models/country.rb +2 -4
  473. data/test/models/course.rb +3 -1
  474. data/test/models/customer.rb +10 -8
  475. data/test/models/customer_carrier.rb +2 -0
  476. data/test/models/dashboard.rb +2 -0
  477. data/test/models/default.rb +2 -0
  478. data/test/models/department.rb +2 -0
  479. data/test/models/destroy_async_parent.rb +15 -0
  480. data/test/models/destroy_async_parent_soft_delete.rb +20 -0
  481. data/test/models/developer.rb +152 -85
  482. data/test/models/dl_keyed_belongs_to.rb +13 -0
  483. data/test/models/dl_keyed_belongs_to_soft_delete.rb +19 -0
  484. data/test/models/dl_keyed_has_many.rb +5 -0
  485. data/test/models/dl_keyed_has_many_through.rb +5 -0
  486. data/test/models/dl_keyed_has_one.rb +5 -0
  487. data/test/models/dl_keyed_join.rb +10 -0
  488. data/test/models/dog.rb +2 -0
  489. data/test/models/dog_lover.rb +2 -0
  490. data/test/models/doubloon.rb +3 -1
  491. data/test/models/drink_designer.rb +17 -0
  492. data/test/models/edge.rb +4 -2
  493. data/test/models/electron.rb +2 -0
  494. data/test/models/engine.rb +3 -2
  495. data/test/models/entrant.rb +2 -0
  496. data/test/models/entry.rb +5 -0
  497. data/test/models/essay.rb +6 -3
  498. data/test/models/essay_destroy_async.rb +12 -0
  499. data/test/models/event.rb +3 -1
  500. data/test/models/eye.rb +5 -3
  501. data/test/models/face.rb +14 -6
  502. data/test/models/family.rb +6 -0
  503. data/test/models/family_tree.rb +6 -0
  504. data/test/models/friendship.rb +5 -3
  505. data/test/models/frog.rb +8 -0
  506. data/test/models/guid.rb +3 -1
  507. data/test/models/guitar.rb +2 -0
  508. data/test/models/hotel.rb +5 -3
  509. data/test/models/human.rb +39 -0
  510. data/test/models/image.rb +3 -1
  511. data/test/models/interest.rb +14 -3
  512. data/test/models/invoice.rb +4 -2
  513. data/test/models/item.rb +3 -1
  514. data/test/models/job.rb +5 -3
  515. data/test/models/joke.rb +4 -2
  516. data/test/models/keyboard.rb +3 -1
  517. data/test/models/legacy_thing.rb +2 -0
  518. data/test/models/lesson.rb +2 -0
  519. data/test/models/line_item.rb +3 -1
  520. data/test/models/liquid.rb +2 -0
  521. data/test/models/matey.rb +3 -1
  522. data/test/models/measurement.rb +4 -0
  523. data/test/models/member.rb +23 -20
  524. data/test/models/member_detail.rb +3 -0
  525. data/test/models/member_type.rb +2 -0
  526. data/test/models/membership.rb +4 -1
  527. data/test/models/mentor.rb +3 -1
  528. data/test/models/message.rb +5 -0
  529. data/test/models/minimalistic.rb +2 -0
  530. data/test/models/minivan.rb +3 -2
  531. data/test/models/mixed_case_monkey.rb +3 -1
  532. data/test/models/molecule.rb +2 -0
  533. data/test/models/mouse.rb +6 -0
  534. data/test/models/movie.rb +2 -0
  535. data/test/models/node.rb +4 -2
  536. data/test/models/non_primary_key.rb +2 -0
  537. data/test/models/notification.rb +2 -0
  538. data/test/models/numeric_data.rb +12 -0
  539. data/test/models/order.rb +4 -2
  540. data/test/models/organization.rb +9 -7
  541. data/test/models/other_dog.rb +3 -1
  542. data/test/models/owner.rb +6 -4
  543. data/test/models/parrot.rb +12 -4
  544. data/test/models/person.rb +59 -54
  545. data/test/models/personal_legacy_thing.rb +3 -1
  546. data/test/models/pet.rb +4 -2
  547. data/test/models/pet_treasure.rb +2 -0
  548. data/test/models/pirate.rb +67 -43
  549. data/test/models/possession.rb +3 -1
  550. data/test/models/post.rb +184 -86
  551. data/test/models/price_estimate.rb +11 -1
  552. data/test/models/professor.rb +3 -1
  553. data/test/models/project.rb +14 -12
  554. data/test/models/publisher/article.rb +2 -0
  555. data/test/models/publisher/magazine.rb +2 -0
  556. data/test/models/publisher.rb +2 -0
  557. data/test/models/randomly_named_c1.rb +2 -0
  558. data/test/models/rating.rb +5 -1
  559. data/test/models/reader.rb +7 -5
  560. data/test/models/recipe.rb +2 -0
  561. data/test/models/record.rb +2 -0
  562. data/test/models/reference.rb +6 -3
  563. data/test/models/reply.rb +39 -21
  564. data/test/models/room.rb +6 -0
  565. data/test/models/section.rb +6 -0
  566. data/test/models/seminar.rb +6 -0
  567. data/test/models/session.rb +6 -0
  568. data/test/models/ship.rb +12 -9
  569. data/test/models/ship_part.rb +5 -3
  570. data/test/models/shop.rb +4 -2
  571. data/test/models/shop_account.rb +2 -0
  572. data/test/models/speedometer.rb +2 -0
  573. data/test/models/sponsor.rb +8 -5
  574. data/test/models/squeak.rb +6 -0
  575. data/test/models/strict_zine.rb +7 -0
  576. data/test/models/string_key_object.rb +2 -0
  577. data/test/models/student.rb +2 -0
  578. data/test/models/subscriber.rb +4 -2
  579. data/test/models/subscription.rb +5 -1
  580. data/test/models/tag.rb +6 -3
  581. data/test/models/tagging.rb +13 -6
  582. data/test/models/task.rb +2 -0
  583. data/test/models/topic.rb +54 -19
  584. data/test/models/toy.rb +4 -0
  585. data/test/models/traffic_light.rb +2 -0
  586. data/test/models/treasure.rb +5 -3
  587. data/test/models/treaty.rb +2 -4
  588. data/test/models/tree.rb +2 -0
  589. data/test/models/tuning_peg.rb +2 -0
  590. data/test/models/tyre.rb +2 -0
  591. data/test/models/user.rb +12 -4
  592. data/test/models/uuid_child.rb +2 -0
  593. data/test/models/uuid_item.rb +2 -0
  594. data/test/models/uuid_parent.rb +2 -0
  595. data/test/models/vegetables.rb +12 -3
  596. data/test/models/vertex.rb +6 -4
  597. data/test/models/warehouse_thing.rb +2 -0
  598. data/test/models/wheel.rb +3 -1
  599. data/test/models/without_table.rb +3 -1
  600. data/test/models/zine.rb +3 -1
  601. data/test/schema/mysql2_specific_schema.rb +49 -35
  602. data/test/schema/oracle_specific_schema.rb +13 -15
  603. data/test/schema/postgresql_specific_schema.rb +51 -40
  604. data/test/schema/schema.rb +334 -154
  605. data/test/schema/sqlite_specific_schema.rb +9 -16
  606. data/test/support/config.rb +26 -26
  607. data/test/support/connection.rb +14 -8
  608. data/test/support/connection_helper.rb +3 -1
  609. data/test/support/ddl_helper.rb +2 -0
  610. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic.dump +0 -0
  611. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic_associations.dump +0 -0
  612. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic.dump +0 -0
  613. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic_associations.dump +0 -0
  614. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic.dump +0 -0
  615. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic_associations.dump +0 -0
  616. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic.dump +0 -0
  617. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic_associations.dump +0 -0
  618. data/test/support/marshal_compatibility_fixtures/legacy_6_0_record_mysql.dump +0 -0
  619. data/test/support/marshal_compatibility_fixtures/legacy_relation.dump +0 -0
  620. data/test/support/schema_dumping_helper.rb +2 -0
  621. data/test/support/stubs/strong_parameters.rb +40 -0
  622. data/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml +206 -0
  623. data/test/support/yaml_compatibility_fixtures/rails_v2.yml +55 -0
  624. metadata +196 -11
@@ -1,56 +1,90 @@
1
- require 'cases/helper'
2
- require 'models/bird'
3
- require 'models/comment'
4
- require 'models/company'
5
- require 'models/customer'
6
- require 'models/developer'
7
- require 'models/computer'
8
- require 'models/invoice'
9
- require 'models/line_item'
10
- require 'models/order'
11
- require 'models/parrot'
12
- require 'models/person'
13
- require 'models/pirate'
14
- require 'models/post'
15
- require 'models/reader'
16
- require 'models/ship'
17
- require 'models/ship_part'
18
- require 'models/tag'
19
- require 'models/tagging'
20
- require 'models/treasure'
21
- require 'models/eye'
22
- require 'models/electron'
23
- require 'models/molecule'
24
- require 'models/member'
25
- require 'models/member_detail'
26
- require 'models/organization'
27
- require 'models/guitar'
28
- require 'models/tuning_peg'
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper"
4
+ require "models/author"
5
+ require "models/book"
6
+ require "models/bird"
7
+ require "models/post"
8
+ require "models/comment"
9
+ require "models/category"
10
+ require "models/company"
11
+ require "models/contract"
12
+ require "models/customer"
13
+ require "models/developer"
14
+ require "models/computer"
15
+ require "models/invoice"
16
+ require "models/line_item"
17
+ require "models/mouse"
18
+ require "models/order"
19
+ require "models/parrot"
20
+ require "models/pirate"
21
+ require "models/project"
22
+ require "models/ship"
23
+ require "models/ship_part"
24
+ require "models/squeak"
25
+ require "models/tag"
26
+ require "models/tagging"
27
+ require "models/treasure"
28
+ require "models/eye"
29
+ require "models/electron"
30
+ require "models/molecule"
31
+ require "models/member"
32
+ require "models/member_detail"
33
+ require "models/organization"
34
+ require "models/guitar"
35
+ require "models/tuning_peg"
36
+ require "models/reply"
29
37
 
30
38
  class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
31
- def test_autosave_validation
39
+ def test_autosave_works_even_when_other_callbacks_update_the_parent_model
40
+ reference = Class.new(ActiveRecord::Base) do
41
+ self.table_name = "references"
42
+ def self.name; "Reference"; end
43
+ end
44
+
45
+ person = Class.new(ActiveRecord::Base) do
46
+ self.table_name = "people"
47
+ def self.name; "Person"; end
48
+
49
+ # It is necessary that the after_create is before the has_many _and_ that it updates the model.
50
+ # This replicates a bug found in https://github.com/rails/rails/issues/38120
51
+ after_create { update(first_name: "first name") }
52
+ has_many :references, autosave: true, anonymous_class: reference
53
+ end
54
+
55
+ reference_instance = reference.create!
56
+ person_instance = person.create!(first_name: "foo", references: [reference_instance])
57
+
58
+ reference_instance.reload
59
+ assert_equal person_instance.id, reference_instance.person_id
60
+ assert_equal "first name", person_instance.first_name # Make sure the after_create is actually called
61
+ end
62
+
63
+ def test_autosave_does_not_pass_through_non_custom_validation_contexts
32
64
  person = Class.new(ActiveRecord::Base) {
33
- self.table_name = 'people'
34
- validate :should_be_cool, :on => :create
35
- def self.name; 'Person'; end
65
+ self.table_name = "people"
66
+ validate :should_be_cool, on: :create
67
+ def self.name; "Person"; end
36
68
 
37
69
  private
38
-
39
- def should_be_cool
40
- unless self.first_name == 'cool'
41
- errors.add :first_name, "not cool"
70
+ def should_be_cool
71
+ unless first_name == "cool"
72
+ errors.add :first_name, "not cool"
73
+ end
42
74
  end
43
- end
44
75
  }
45
76
  reference = Class.new(ActiveRecord::Base) {
46
77
  self.table_name = "references"
47
- def self.name; 'Reference'; end
78
+ def self.name; "Reference"; end
48
79
  belongs_to :person, autosave: true, anonymous_class: person
49
80
  }
50
81
 
51
- u = person.create!(first_name: 'cool')
52
- u.update_attributes!(first_name: 'nah') # still valid because validation only applies on 'create'
53
- assert reference.create!(person: u).persisted?
82
+ u = person.create!(first_name: "cool")
83
+ u.first_name = "nah"
84
+
85
+ assert_predicate u, :valid?
86
+ r = reference.new(person: u)
87
+ assert_predicate r, :valid?
54
88
  end
55
89
 
56
90
  def test_should_not_add_the_same_callbacks_multiple_times_for_has_one
@@ -73,60 +107,59 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
73
107
  ship = ShipWithoutNestedAttributes.new
74
108
  ship.prisoners.build
75
109
 
76
- assert_not ship.valid?
110
+ assert_not_predicate ship, :valid?
77
111
  assert_equal 1, ship.errors[:name].length
78
112
  end
79
113
 
80
114
  private
81
-
82
- def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
83
- reflection = model.reflect_on_association(association_name)
84
- assert_no_difference "callbacks_for_model(#{model.name}).length" do
85
- model.send(:add_autosave_association_callbacks, reflection)
115
+ def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
116
+ reflection = model.reflect_on_association(association_name)
117
+ assert_no_difference "callbacks_for_model(#{model.name}).length" do
118
+ model.send(:add_autosave_association_callbacks, reflection)
119
+ end
86
120
  end
87
- end
88
121
 
89
- def callbacks_for_model(model)
90
- model.instance_variables.grep(/_callbacks$/).flat_map do |ivar|
91
- model.instance_variable_get(ivar)
122
+ def callbacks_for_model(model)
123
+ model.instance_variables.grep(/_callbacks$/).flat_map do |ivar|
124
+ model.instance_variable_get(ivar)
125
+ end
92
126
  end
93
- end
94
127
  end
95
128
 
96
129
  class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
97
130
  fixtures :companies, :accounts
98
131
 
99
132
  def test_should_save_parent_but_not_invalid_child
100
- firm = Firm.new(:name => 'GlobalMegaCorp')
101
- assert firm.valid?
133
+ firm = Firm.new(name: "GlobalMegaCorp")
134
+ assert_predicate firm, :valid?
102
135
 
103
136
  firm.build_account_using_primary_key
104
- assert !firm.build_account_using_primary_key.valid?
137
+ assert_not_predicate firm.build_account_using_primary_key, :valid?
105
138
 
106
139
  assert firm.save
107
- assert !firm.account_using_primary_key.persisted?
140
+ assert_not_predicate firm.account_using_primary_key, :persisted?
108
141
  end
109
142
 
110
143
  def test_save_fails_for_invalid_has_one
111
144
  firm = Firm.first
112
- assert firm.valid?
145
+ assert_predicate firm, :valid?
113
146
 
114
147
  firm.build_account
115
148
 
116
- assert !firm.account.valid?
117
- assert !firm.valid?
118
- assert !firm.save
149
+ assert_not_predicate firm.account, :valid?
150
+ assert_not_predicate firm, :valid?
151
+ assert_not firm.save
119
152
  assert_equal ["is invalid"], firm.errors["account"]
120
153
  end
121
154
 
122
155
  def test_save_succeeds_for_invalid_has_one_with_validate_false
123
156
  firm = Firm.first
124
- assert firm.valid?
157
+ assert_predicate firm, :valid?
125
158
 
126
159
  firm.build_unvalidated_account
127
160
 
128
- assert !firm.unvalidated_account.valid?
129
- assert firm.valid?
161
+ assert_not_predicate firm.unvalidated_account, :valid?
162
+ assert_predicate firm, :valid?
130
163
  assert firm.save
131
164
  end
132
165
 
@@ -135,10 +168,10 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
135
168
 
136
169
  account = firm.build_account("credit_limit" => 1000)
137
170
  assert_equal account, firm.account
138
- assert !account.persisted?
171
+ assert_not_predicate account, :persisted?
139
172
  assert firm.save
140
173
  assert_equal account, firm.account
141
- assert account.persisted?
174
+ assert_predicate account, :persisted?
142
175
  end
143
176
 
144
177
  def test_build_before_either_saved
@@ -146,16 +179,16 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
146
179
 
147
180
  firm.account = account = Account.new("credit_limit" => 1000)
148
181
  assert_equal account, firm.account
149
- assert !account.persisted?
182
+ assert_not_predicate account, :persisted?
150
183
  assert firm.save
151
184
  assert_equal account, firm.account
152
- assert account.persisted?
185
+ assert_predicate account, :persisted?
153
186
  end
154
187
 
155
188
  def test_assignment_before_parent_saved
156
189
  firm = Firm.new("name" => "GlobalMegaCorp")
157
190
  firm.account = a = Account.find(1)
158
- assert !firm.persisted?
191
+ assert_not_predicate firm, :persisted?
159
192
  assert_equal a, firm.account
160
193
  assert firm.save
161
194
  assert_equal a, firm.account
@@ -166,20 +199,20 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
166
199
  def test_assignment_before_either_saved
167
200
  firm = Firm.new("name" => "GlobalMegaCorp")
168
201
  firm.account = a = Account.new("credit_limit" => 1000)
169
- assert !firm.persisted?
170
- assert !a.persisted?
202
+ assert_not_predicate firm, :persisted?
203
+ assert_not_predicate a, :persisted?
171
204
  assert_equal a, firm.account
172
205
  assert firm.save
173
- assert firm.persisted?
174
- assert a.persisted?
206
+ assert_predicate firm, :persisted?
207
+ assert_predicate a, :persisted?
175
208
  assert_equal a, firm.account
176
209
  firm.association(:account).reload
177
210
  assert_equal a, firm.account
178
211
  end
179
212
 
180
213
  def test_not_resaved_when_unchanged
181
- firm = Firm.all.merge!(:includes => :account).first
182
- firm.name += '-changed'
214
+ firm = Firm.all.merge!(includes: :account).first
215
+ firm.name += "-changed"
183
216
  assert_queries(1) { firm.save! }
184
217
 
185
218
  firm = Firm.first
@@ -196,21 +229,21 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
196
229
  end
197
230
 
198
231
  def test_callbacks_firing_order_on_create
199
- eye = Eye.create(:iris_attributes => {:color => 'honey'})
232
+ eye = Eye.create(iris_attributes: { color: "honey" })
200
233
  assert_equal [true, false], eye.after_create_callbacks_stack
201
234
  end
202
235
 
203
236
  def test_callbacks_firing_order_on_update
204
- eye = Eye.create(iris_attributes: {color: 'honey'})
205
- eye.update(iris_attributes: {color: 'green'})
237
+ eye = Eye.create(iris_attributes: { color: "honey" })
238
+ eye.update(iris_attributes: { color: "green" })
206
239
  assert_equal [true, false], eye.after_update_callbacks_stack
207
240
  end
208
241
 
209
242
  def test_callbacks_firing_order_on_save
210
- eye = Eye.create(iris_attributes: {color: 'honey'})
243
+ eye = Eye.create(iris_attributes: { color: "honey" })
211
244
  assert_equal [false, false], eye.after_save_callbacks_stack
212
245
 
213
- eye.update(iris_attributes: {color: 'blue'})
246
+ eye.update(iris_attributes: { color: "blue" })
214
247
  assert_equal [false, false, false, false], eye.after_save_callbacks_stack
215
248
  end
216
249
  end
@@ -219,34 +252,34 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
219
252
  fixtures :companies, :posts, :tags, :taggings
220
253
 
221
254
  def test_should_save_parent_but_not_invalid_child
222
- client = Client.new(:name => 'Joe (the Plumber)')
223
- assert client.valid?
255
+ client = Client.new(name: "Joe (the Plumber)")
256
+ assert_predicate client, :valid?
224
257
 
225
258
  client.build_firm
226
- assert !client.firm.valid?
259
+ assert_not_predicate client.firm, :valid?
227
260
 
228
261
  assert client.save
229
- assert !client.firm.persisted?
262
+ assert_not_predicate client.firm, :persisted?
230
263
  end
231
264
 
232
265
  def test_save_fails_for_invalid_belongs_to
233
266
  # Oracle saves empty string as NULL therefore :message changed to one space
234
- assert log = AuditLog.create(:developer_id => 0, :message => " ")
267
+ assert log = AuditLog.create(developer_id: 0, message: " ")
235
268
 
236
269
  log.developer = Developer.new
237
- assert !log.developer.valid?
238
- assert !log.valid?
239
- assert !log.save
270
+ assert_not_predicate log.developer, :valid?
271
+ assert_not_predicate log, :valid?
272
+ assert_not log.save
240
273
  assert_equal ["is invalid"], log.errors["developer"]
241
274
  end
242
275
 
243
276
  def test_save_succeeds_for_invalid_belongs_to_with_validate_false
244
277
  # Oracle saves empty string as NULL therefore :message changed to one space
245
- assert log = AuditLog.create(:developer_id => 0, :message=> " ")
278
+ assert log = AuditLog.create(developer_id: 0, message: " ")
246
279
 
247
280
  log.unvalidated_developer = Developer.new
248
- assert !log.unvalidated_developer.valid?
249
- assert log.valid?
281
+ assert_not_predicate log.unvalidated_developer, :valid?
282
+ assert_predicate log, :valid?
250
283
  assert log.save
251
284
  end
252
285
 
@@ -255,10 +288,10 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
255
288
  apple = Firm.new("name" => "Apple")
256
289
  client.firm = apple
257
290
  assert_equal apple, client.firm
258
- assert !apple.persisted?
291
+ assert_not_predicate apple, :persisted?
259
292
  assert client.save
260
293
  assert apple.save
261
- assert apple.persisted?
294
+ assert_predicate apple, :persisted?
262
295
  assert_equal apple, client.firm
263
296
  client.association(:firm).reload
264
297
  assert_equal apple, client.firm
@@ -268,11 +301,11 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
268
301
  final_cut = Client.new("name" => "Final Cut")
269
302
  apple = Firm.new("name" => "Apple")
270
303
  final_cut.firm = apple
271
- assert !final_cut.persisted?
272
- assert !apple.persisted?
304
+ assert_not_predicate final_cut, :persisted?
305
+ assert_not_predicate apple, :persisted?
273
306
  assert final_cut.save
274
- assert final_cut.persisted?
275
- assert apple.persisted?
307
+ assert_predicate final_cut, :persisted?
308
+ assert_predicate apple, :persisted?
276
309
  assert_equal apple, final_cut.firm
277
310
  final_cut.association(:firm).reload
278
311
  assert_equal apple, final_cut.firm
@@ -362,41 +395,55 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
362
395
 
363
396
  def test_store_association_with_a_polymorphic_relationship
364
397
  num_tagging = Tagging.count
365
- tags(:misc).create_tagging(:taggable => posts(:thinking))
398
+ tags(:misc).create_tagging(taggable: posts(:thinking))
366
399
  assert_equal num_tagging + 1, Tagging.count
367
400
  end
368
401
 
369
402
  def test_build_and_then_save_parent_should_not_reload_target
370
403
  client = Client.first
371
- apple = client.build_firm(:name => "Apple")
404
+ apple = client.build_firm(name: "Apple")
372
405
  client.save!
373
406
  assert_no_queries { assert_equal apple, client.firm }
374
407
  end
375
408
 
376
409
  def test_validation_does_not_validate_stale_association_target
377
- valid_developer = Developer.create!(:name => "Dude", :salary => 50_000)
410
+ valid_developer = Developer.create!(name: "Dude", salary: 50_000)
378
411
  invalid_developer = Developer.new()
379
412
 
380
- auditlog = AuditLog.new(:message => "foo")
413
+ auditlog = AuditLog.new(message: "foo")
381
414
  auditlog.developer = invalid_developer
382
415
  auditlog.developer_id = valid_developer.id
383
416
 
384
- assert auditlog.valid?
417
+ assert_predicate auditlog, :valid?
418
+ end
419
+
420
+ def test_validation_does_not_validate_non_dirty_association_target
421
+ mouse = Mouse.create!(name: "Will")
422
+ Squeak.create!(mouse: mouse)
423
+
424
+ mouse.name = nil
425
+ mouse.save! validate: false
426
+
427
+ squeak = Squeak.last
428
+
429
+ assert_equal true, squeak.valid?
430
+ assert_equal true, squeak.mouse.present?
431
+ assert_equal true, squeak.valid?
385
432
  end
386
433
  end
387
434
 
388
435
  class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttributes < ActiveRecord::TestCase
389
436
  def test_invalid_adding_with_nested_attributes
390
437
  molecule = Molecule.new
391
- valid_electron = Electron.new(name: 'electron')
438
+ valid_electron = Electron.new(name: "electron")
392
439
  invalid_electron = Electron.new
393
440
 
394
441
  molecule.electrons = [valid_electron, invalid_electron]
395
442
  molecule.save
396
443
 
397
- assert_not invalid_electron.valid?
398
- assert valid_electron.valid?
399
- assert_not molecule.persisted?, 'Molecule should not be persisted when its electrons are invalid'
444
+ assert_not_predicate invalid_electron, :valid?
445
+ assert_predicate valid_electron, :valid?
446
+ assert_not molecule.persisted?, "Molecule should not be persisted when its electrons are invalid"
400
447
  end
401
448
 
402
449
  def test_errors_should_be_indexed_when_passed_as_array
@@ -407,9 +454,9 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
407
454
 
408
455
  guitar.tuning_pegs = [tuning_peg_valid, tuning_peg_invalid]
409
456
 
410
- assert_not tuning_peg_invalid.valid?
411
- assert tuning_peg_valid.valid?
412
- assert_not guitar.valid?
457
+ assert_not_predicate tuning_peg_invalid, :valid?
458
+ assert_predicate tuning_peg_valid, :valid?
459
+ assert_not_predicate guitar, :valid?
413
460
  assert_equal ["is not a number"], guitar.errors["tuning_pegs[1].pitch"]
414
461
  assert_not_equal ["is not a number"], guitar.errors["tuning_pegs.pitch"]
415
462
  end
@@ -419,14 +466,14 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
419
466
  ActiveRecord::Base.index_nested_attribute_errors = true
420
467
 
421
468
  molecule = Molecule.new
422
- valid_electron = Electron.new(name: 'electron')
469
+ valid_electron = Electron.new(name: "electron")
423
470
  invalid_electron = Electron.new
424
471
 
425
472
  molecule.electrons = [valid_electron, invalid_electron]
426
473
 
427
- assert_not invalid_electron.valid?
428
- assert valid_electron.valid?
429
- assert_not molecule.valid?
474
+ assert_not_predicate invalid_electron, :valid?
475
+ assert_predicate valid_electron, :valid?
476
+ assert_not_predicate molecule, :valid?
430
477
  assert_equal ["can't be blank"], molecule.errors["electrons[1].name"]
431
478
  assert_not_equal ["can't be blank"], molecule.errors["electrons.name"]
432
479
  ensure
@@ -435,14 +482,14 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
435
482
 
436
483
  def test_errors_details_should_be_set
437
484
  molecule = Molecule.new
438
- valid_electron = Electron.new(name: 'electron')
485
+ valid_electron = Electron.new(name: "electron")
439
486
  invalid_electron = Electron.new
440
487
 
441
488
  molecule.electrons = [valid_electron, invalid_electron]
442
489
 
443
- assert_not invalid_electron.valid?
444
- assert valid_electron.valid?
445
- assert_not molecule.valid?
490
+ assert_not_predicate invalid_electron, :valid?
491
+ assert_predicate valid_electron, :valid?
492
+ assert_not_predicate molecule, :valid?
446
493
  assert_equal [{ error: :blank }], molecule.errors.details[:"electrons.name"]
447
494
  end
448
495
 
@@ -454,9 +501,9 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
454
501
 
455
502
  guitar.tuning_pegs = [tuning_peg_valid, tuning_peg_invalid]
456
503
 
457
- assert_not tuning_peg_invalid.valid?
458
- assert tuning_peg_valid.valid?
459
- assert_not guitar.valid?
504
+ assert_not_predicate tuning_peg_invalid, :valid?
505
+ assert_predicate tuning_peg_valid, :valid?
506
+ assert_not_predicate guitar, :valid?
460
507
  assert_equal [{ error: :not_a_number, value: nil }], guitar.errors.details[:"tuning_pegs[1].pitch"]
461
508
  assert_equal [], guitar.errors.details[:"tuning_pegs.pitch"]
462
509
  end
@@ -466,14 +513,14 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
466
513
  ActiveRecord::Base.index_nested_attribute_errors = true
467
514
 
468
515
  molecule = Molecule.new
469
- valid_electron = Electron.new(name: 'electron')
516
+ valid_electron = Electron.new(name: "electron")
470
517
  invalid_electron = Electron.new
471
518
 
472
519
  molecule.electrons = [valid_electron, invalid_electron]
473
520
 
474
- assert_not invalid_electron.valid?
475
- assert valid_electron.valid?
476
- assert_not molecule.valid?
521
+ assert_not_predicate invalid_electron, :valid?
522
+ assert_predicate valid_electron, :valid?
523
+ assert_not_predicate molecule, :valid?
477
524
  assert_equal [{ error: :blank }], molecule.errors.details[:"electrons[1].name"]
478
525
  assert_equal [], molecule.errors.details[:"electrons.name"]
479
526
  ensure
@@ -482,38 +529,50 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
482
529
 
483
530
  def test_valid_adding_with_nested_attributes
484
531
  molecule = Molecule.new
485
- valid_electron = Electron.new(name: 'electron')
532
+ valid_electron = Electron.new(name: "electron")
486
533
 
487
534
  molecule.electrons = [valid_electron]
488
535
  molecule.save
489
536
 
490
- assert valid_electron.valid?
491
- assert molecule.persisted?
537
+ assert_predicate valid_electron, :valid?
538
+ assert_predicate molecule, :persisted?
492
539
  assert_equal 1, molecule.electrons.count
493
540
  end
494
541
  end
495
542
 
496
543
  class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
497
- fixtures :companies, :people
544
+ fixtures :companies, :developers
498
545
 
499
546
  def test_invalid_adding
500
547
  firm = Firm.find(1)
501
- assert !(firm.clients_of_firm << c = Client.new)
502
- assert !c.persisted?
503
- assert !firm.valid?
504
- assert !firm.save
505
- assert !c.persisted?
548
+ assert_not (firm.clients_of_firm << c = Client.new)
549
+ assert_not_predicate c, :persisted?
550
+ assert_not_predicate firm, :valid?
551
+ assert_not firm.save
552
+ assert_not_predicate c, :persisted?
506
553
  end
507
554
 
508
555
  def test_invalid_adding_before_save
509
556
  new_firm = Firm.new("name" => "A New Firm, Inc")
510
557
  new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
511
- assert !c.persisted?
512
- assert !c.valid?
513
- assert !new_firm.valid?
514
- assert !new_firm.save
515
- assert !c.persisted?
516
- assert !new_firm.persisted?
558
+ assert_not_predicate c, :persisted?
559
+ assert_not_predicate c, :valid?
560
+ assert_not_predicate new_firm, :valid?
561
+ assert_not new_firm.save
562
+ assert_not_predicate c, :persisted?
563
+ assert_not_predicate new_firm, :persisted?
564
+ end
565
+
566
+ def test_adding_unsavable_association
567
+ new_firm = Firm.new("name" => "A New Firm, Inc")
568
+ client = new_firm.clients.new("name" => "Apple")
569
+ client.throw_on_save = true
570
+
571
+ assert_predicate client, :valid?
572
+ assert_predicate new_firm, :valid?
573
+ assert_not new_firm.save
574
+ assert_not_predicate new_firm, :persisted?
575
+ assert_not_predicate client, :persisted?
517
576
  end
518
577
 
519
578
  def test_invalid_adding_with_validate_false
@@ -521,10 +580,10 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
521
580
  client = Client.new
522
581
  firm.unvalidated_clients_of_firm << client
523
582
 
524
- assert firm.valid?
525
- assert !client.valid?
583
+ assert_predicate firm, :valid?
584
+ assert_not_predicate client, :valid?
526
585
  assert firm.save
527
- assert !client.persisted?
586
+ assert_not_predicate client, :persisted?
528
587
  end
529
588
 
530
589
  def test_valid_adding_with_validate_false
@@ -533,24 +592,52 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
533
592
  firm = Firm.first
534
593
  client = Client.new("name" => "Apple")
535
594
 
536
- assert firm.valid?
537
- assert client.valid?
538
- assert !client.persisted?
595
+ assert_predicate firm, :valid?
596
+ assert_predicate client, :valid?
597
+ assert_not_predicate client, :persisted?
539
598
 
540
599
  firm.unvalidated_clients_of_firm << client
541
600
 
542
601
  assert firm.save
543
- assert client.persisted?
602
+ assert_predicate client, :persisted?
544
603
  assert_equal no_of_clients + 1, Client.count
545
604
  end
546
605
 
606
+ def test_parent_should_save_children_record_with_foreign_key_validation_set_in_before_save_callback
607
+ company = NewlyContractedCompany.new(name: "test")
608
+
609
+ assert company.save
610
+ assert_not_empty company.reload.new_contracts
611
+ end
612
+
613
+ def test_parent_should_not_get_saved_with_duplicate_children_records
614
+ assert_no_difference "Reply.count" do
615
+ assert_no_difference "SillyUniqueReply.count" do
616
+ reply = Reply.new
617
+ reply.silly_unique_replies.build([
618
+ { content: "Best content" },
619
+ { content: "Best content" }
620
+ ])
621
+
622
+ assert_not reply.save
623
+ assert_equal ["is invalid"], reply.errors[:silly_unique_replies]
624
+ assert_empty reply.silly_unique_replies.first.errors
625
+
626
+ assert_equal(
627
+ ["has already been taken"],
628
+ reply.silly_unique_replies.last.errors[:content]
629
+ )
630
+ end
631
+ end
632
+ end
633
+
547
634
  def test_invalid_build
548
635
  new_client = companies(:first_firm).clients_of_firm.build
549
- assert !new_client.persisted?
550
- assert !new_client.valid?
636
+ assert_not_predicate new_client, :persisted?
637
+ assert_not_predicate new_client, :valid?
551
638
  assert_equal new_client, companies(:first_firm).clients_of_firm.last
552
- assert !companies(:first_firm).save
553
- assert !new_client.persisted?
639
+ assert_not companies(:first_firm).save
640
+ assert_not_predicate new_client, :persisted?
554
641
  assert_equal 2, companies(:first_firm).clients_of_firm.reload.size
555
642
  end
556
643
 
@@ -569,8 +656,8 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
569
656
  assert_equal no_of_firms, Firm.count # Firm was not saved to database.
570
657
  assert_equal no_of_clients, Client.count # Clients were not saved to database.
571
658
  assert new_firm.save
572
- assert new_firm.persisted?
573
- assert c.persisted?
659
+ assert_predicate new_firm, :persisted?
660
+ assert_predicate c, :persisted?
574
661
  assert_equal new_firm, c.firm
575
662
  assert_equal no_of_firms + 1, Firm.count # Firm was saved to database.
576
663
  assert_equal no_of_clients + 2, Client.count # Clients were saved to database.
@@ -585,58 +672,62 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
585
672
  firm.save
586
673
  firm.reload
587
674
  assert_equal 2, firm.clients.length
588
- assert firm.clients.include?(companies(:second_client))
675
+ assert_includes firm.clients, companies(:second_client)
589
676
  end
590
677
 
591
678
  def test_assign_ids_for_through_a_belongs_to
592
- post = Post.new(:title => "Assigning IDs works!", :body => "You heard it here first, folks!")
593
- post.person_ids = [people(:david).id, people(:michael).id]
594
- post.save
595
- post.reload
596
- assert_equal 2, post.people.length
597
- assert post.people.include?(people(:david))
679
+ firm = Firm.new("name" => "Apple")
680
+ firm.developer_ids = [developers(:david).id, developers(:jamis).id]
681
+ firm.save
682
+ firm.reload
683
+ assert_equal 2, firm.developers.length
684
+ assert_includes firm.developers, developers(:david)
598
685
  end
599
686
 
600
687
  def test_build_before_save
601
688
  company = companies(:first_firm)
602
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") }
603
- assert !company.clients_of_firm.loaded?
604
689
 
605
- company.name += '-changed'
690
+ new_client = assert_queries(0) { company.clients_of_firm.build("name" => "Another Client") }
691
+ assert_not_predicate company.clients_of_firm, :loaded?
692
+
693
+ company.name += "-changed"
606
694
  assert_queries(2) { assert company.save }
607
- assert new_client.persisted?
695
+ assert_predicate new_client, :persisted?
608
696
  assert_equal 3, company.clients_of_firm.reload.size
609
697
  end
610
698
 
611
699
  def test_build_many_before_save
612
700
  company = companies(:first_firm)
613
- assert_no_queries(ignore_none: false) { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
614
701
 
615
- company.name += '-changed'
702
+ assert_queries(0) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
703
+
704
+ company.name += "-changed"
616
705
  assert_queries(3) { assert company.save }
617
706
  assert_equal 4, company.clients_of_firm.reload.size
618
707
  end
619
708
 
620
709
  def test_build_via_block_before_save
621
710
  company = companies(:first_firm)
622
- new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build {|client| client.name = "Another Client" } }
623
- assert !company.clients_of_firm.loaded?
624
711
 
625
- company.name += '-changed'
712
+ new_client = assert_queries(0) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
713
+ assert_not_predicate company.clients_of_firm, :loaded?
714
+
715
+ company.name += "-changed"
626
716
  assert_queries(2) { assert company.save }
627
- assert new_client.persisted?
717
+ assert_predicate new_client, :persisted?
628
718
  assert_equal 3, company.clients_of_firm.reload.size
629
719
  end
630
720
 
631
721
  def test_build_many_via_block_before_save
632
722
  company = companies(:first_firm)
633
- assert_no_queries(ignore_none: false) do
634
- company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
723
+
724
+ assert_queries(0) do
725
+ company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
635
726
  client.name = "changed"
636
727
  end
637
728
  end
638
729
 
639
- company.name += '-changed'
730
+ company.name += "-changed"
640
731
  assert_queries(3) { assert company.save }
641
732
  assert_equal 4, company.clients_of_firm.reload.size
642
733
  end
@@ -647,7 +738,16 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
647
738
  assert firm.save
648
739
  firm.reload
649
740
  assert_equal 2, firm.clients.length
650
- assert firm.clients.include?(Client.find_by_name("New Client"))
741
+ assert_includes firm.clients, Client.find_by_name("New Client")
742
+ end
743
+
744
+ def test_replace_on_duplicated_object
745
+ firm = Firm.create!("name" => "New Firm").dup
746
+ firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
747
+ assert firm.save
748
+ firm.reload
749
+ assert_equal 2, firm.clients.length
750
+ assert_includes firm.clients, Client.find_by_name("New Client")
651
751
  end
652
752
  end
653
753
 
@@ -656,79 +756,88 @@ class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
656
756
  new_account = Account.new("credit_limit" => 1000)
657
757
  new_firm = Firm.new("name" => "some firm")
658
758
 
659
- assert !new_firm.persisted?
759
+ assert_not_predicate new_firm, :persisted?
660
760
  new_account.firm = new_firm
661
761
  new_account.save!
662
762
 
663
- assert new_firm.persisted?
763
+ assert_predicate new_firm, :persisted?
664
764
 
665
765
  new_account = Account.new("credit_limit" => 1000)
666
766
  new_autosaved_firm = Firm.new("name" => "some firm")
667
767
 
668
- assert !new_autosaved_firm.persisted?
768
+ assert_not_predicate new_autosaved_firm, :persisted?
669
769
  new_account.unautosaved_firm = new_autosaved_firm
670
770
  new_account.save!
671
771
 
672
- assert !new_autosaved_firm.persisted?
772
+ assert_not_predicate new_autosaved_firm, :persisted?
673
773
  end
674
774
 
675
775
  def test_autosave_new_record_on_has_one_can_be_disabled_per_relationship
676
776
  firm = Firm.new("name" => "some firm")
677
777
  account = Account.new("credit_limit" => 1000)
678
778
 
679
- assert !account.persisted?
779
+ assert_not_predicate account, :persisted?
680
780
  firm.account = account
681
781
  firm.save!
682
782
 
683
- assert account.persisted?
783
+ assert_predicate account, :persisted?
684
784
 
685
785
  firm = Firm.new("name" => "some firm")
686
786
  account = Account.new("credit_limit" => 1000)
687
787
 
688
788
  firm.unautosaved_account = account
689
789
 
690
- assert !account.persisted?
790
+ assert_not_predicate account, :persisted?
691
791
  firm.unautosaved_account = account
692
792
  firm.save!
693
793
 
694
- assert !account.persisted?
794
+ assert_not_predicate account, :persisted?
695
795
  end
696
796
 
697
797
  def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship
698
798
  firm = Firm.new("name" => "some firm")
699
799
  account = Account.new("credit_limit" => 1000)
700
800
 
701
- assert !account.persisted?
801
+ assert_not_predicate account, :persisted?
702
802
  firm.accounts << account
703
803
 
704
804
  firm.save!
705
- assert account.persisted?
805
+ assert_predicate account, :persisted?
706
806
 
707
807
  firm = Firm.new("name" => "some firm")
708
808
  account = Account.new("credit_limit" => 1000)
709
809
 
710
- assert !account.persisted?
810
+ assert_not_predicate account, :persisted?
711
811
  firm.unautosaved_accounts << account
712
812
 
713
813
  firm.save!
714
- assert !account.persisted?
814
+ assert_not_predicate account, :persisted?
715
815
  end
716
816
 
717
817
  def test_autosave_new_record_with_after_create_callback
718
- post = PostWithAfterCreateCallback.new(title: 'Captain Murphy', body: 'is back')
719
- post.comments.build(body: 'foo')
818
+ post = PostWithAfterCreateCallback.new(title: "Captain Murphy", body: "is back")
819
+ post.comments.build(body: "foo")
720
820
  post.save!
721
821
 
722
822
  assert_not_nil post.author_id
723
823
  end
824
+
825
+ def test_autosave_new_record_with_after_create_callback_and_habtm_association
826
+ post = PostWithAfterCreateCallback.new(title: "Captain Murphy", body: "is back")
827
+ post.comments.build(body: "foo")
828
+ post.categories.build(name: "bar")
829
+ post.save!
830
+
831
+ assert_equal 1, post.categories.reload.length
832
+ end
724
833
  end
725
834
 
726
835
  class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
727
836
  self.use_transactional_tests = false
728
837
 
729
838
  setup do
730
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
731
- @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
839
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
840
+ @ship = @pirate.create_ship(name: "Nights Dirty Lightning")
732
841
  end
733
842
 
734
843
  teardown do
@@ -744,18 +853,18 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
744
853
  @pirate.mark_for_destruction
745
854
  @pirate.ship.mark_for_destruction
746
855
 
747
- assert !@pirate.reload.marked_for_destruction?
748
- assert !@pirate.ship.reload.marked_for_destruction?
856
+ assert_not_predicate @pirate.reload, :marked_for_destruction?
857
+ assert_not_predicate @pirate.ship.reload, :marked_for_destruction?
749
858
  end
750
859
 
751
860
  # has_one
752
861
  def test_should_destroy_a_child_association_as_part_of_the_save_transaction_if_it_was_marked_for_destruction
753
- assert !@pirate.ship.marked_for_destruction?
862
+ assert_not_predicate @pirate.ship, :marked_for_destruction?
754
863
 
755
864
  @pirate.ship.mark_for_destruction
756
865
  id = @pirate.ship.id
757
866
 
758
- assert @pirate.ship.marked_for_destruction?
867
+ assert_predicate @pirate.ship, :marked_for_destruction?
759
868
  assert Ship.find_by_id(id)
760
869
 
761
870
  @pirate.save
@@ -764,12 +873,13 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
764
873
  end
765
874
 
766
875
  def test_should_skip_validation_on_a_child_association_if_marked_for_destruction
767
- @pirate.ship.name = ''
768
- assert !@pirate.valid?
876
+ @pirate.ship.name = ""
877
+ assert_not_predicate @pirate, :valid?
769
878
 
770
879
  @pirate.ship.mark_for_destruction
771
- @pirate.ship.expects(:valid?).never
772
- assert_difference('Ship.count', -1) { @pirate.save! }
880
+ assert_not_called(@pirate.ship, :valid?) do
881
+ assert_difference("Ship.count", -1) { @pirate.save! }
882
+ end
773
883
  end
774
884
 
775
885
  def test_a_child_marked_for_destruction_should_not_be_destroyed_twice
@@ -784,16 +894,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
784
894
  def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child
785
895
  # Stub the save method of the @pirate.ship instance to destroy and then raise an exception
786
896
  class << @pirate.ship
787
- def save(*args)
897
+ def save(**)
788
898
  super
789
899
  destroy
790
- raise 'Oh noes!'
900
+ raise "Oh noes!"
791
901
  end
792
902
  end
793
903
 
794
904
  @ship.pirate.catchphrase = "Changed Catchphrase"
905
+ @ship.name_will_change!
795
906
 
796
- assert_raise(RuntimeError) { assert !@pirate.save }
907
+ assert_raise(RuntimeError) { assert_not @pirate.save }
797
908
  assert_not_nil @pirate.reload.ship
798
909
  end
799
910
 
@@ -804,18 +915,19 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
804
915
  end
805
916
 
806
917
  def test_should_not_save_changed_has_one_unchanged_object_if_child_is_saved
807
- @pirate.ship.expects(:save).never
808
- assert @pirate.save
918
+ assert_not_called(@pirate.ship, :save) do
919
+ assert @pirate.save
920
+ end
809
921
  end
810
922
 
811
923
  # belongs_to
812
924
  def test_should_destroy_a_parent_association_as_part_of_the_save_transaction_if_it_was_marked_for_destruction
813
- assert !@ship.pirate.marked_for_destruction?
925
+ assert_not_predicate @ship.pirate, :marked_for_destruction?
814
926
 
815
927
  @ship.pirate.mark_for_destruction
816
928
  id = @ship.pirate.id
817
929
 
818
- assert @ship.pirate.marked_for_destruction?
930
+ assert_predicate @ship.pirate, :marked_for_destruction?
819
931
  assert Pirate.find_by_id(id)
820
932
 
821
933
  @ship.save
@@ -824,12 +936,13 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
824
936
  end
825
937
 
826
938
  def test_should_skip_validation_on_a_parent_association_if_marked_for_destruction
827
- @ship.pirate.catchphrase = ''
828
- assert !@ship.valid?
939
+ @ship.pirate.catchphrase = ""
940
+ assert_not_predicate @ship, :valid?
829
941
 
830
942
  @ship.pirate.mark_for_destruction
831
- @ship.pirate.expects(:valid?).never
832
- assert_difference('Pirate.count', -1) { @ship.save! }
943
+ assert_not_called(@ship.pirate, :valid?) do
944
+ assert_difference("Pirate.count", -1) { @ship.save! }
945
+ end
833
946
  end
834
947
 
835
948
  def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice
@@ -844,32 +957,32 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
844
957
  def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent
845
958
  # Stub the save method of the @ship.pirate instance to destroy and then raise an exception
846
959
  class << @ship.pirate
847
- def save(*args)
960
+ def save(**)
848
961
  super
849
962
  destroy
850
- raise 'Oh noes!'
963
+ raise "Oh noes!"
851
964
  end
852
965
  end
853
966
 
854
967
  @ship.pirate.catchphrase = "Changed Catchphrase"
855
968
 
856
- assert_raise(RuntimeError) { assert !@ship.save }
969
+ assert_raise(RuntimeError) { assert_not @ship.save }
857
970
  assert_not_nil @ship.reload.pirate
858
971
  end
859
972
 
860
973
  def test_should_save_changed_child_objects_if_parent_is_saved
861
- @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?")
862
- @parrot = @pirate.parrots.create!(:name => 'Posideons Killer')
974
+ @pirate = @ship.create_pirate(catchphrase: "Don' botharrr talkin' like one, savvy?")
975
+ @parrot = @pirate.parrots.create!(name: "Posideons Killer")
863
976
  @parrot.name = "NewName"
864
977
  @ship.save
865
978
 
866
- assert_equal 'NewName', @parrot.reload.name
979
+ assert_equal "NewName", @parrot.reload.name
867
980
  end
868
981
 
869
982
  def test_should_destroy_has_many_as_part_of_the_save_transaction_if_they_were_marked_for_destruction
870
- 2.times { |i| @pirate.birds.create!(:name => "birds_#{i}") }
983
+ 2.times { |i| @pirate.birds.create!(name: "birds_#{i}") }
871
984
 
872
- assert !@pirate.birds.any?(&:marked_for_destruction?)
985
+ assert_not @pirate.birds.any?(&:marked_for_destruction?)
873
986
 
874
987
  @pirate.birds.each(&:mark_for_destruction)
875
988
  klass = @pirate.birds.first.class
@@ -879,7 +992,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
879
992
  ids.each { |id| assert klass.find_by_id(id) }
880
993
 
881
994
  @pirate.save
882
- assert @pirate.reload.birds.empty?
995
+ assert_empty @pirate.reload.birds
883
996
  ids.each { |id| assert_nil klass.find_by_id(id) }
884
997
  end
885
998
 
@@ -887,62 +1000,67 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
887
1000
  @pirate.birds.create!(name: :parrot)
888
1001
  @pirate.birds.first.destroy
889
1002
  @pirate.save!
890
- assert @pirate.reload.birds.empty?
1003
+ assert_empty @pirate.reload.birds
891
1004
  end
892
1005
 
893
1006
  def test_should_skip_validation_on_has_many_if_marked_for_destruction
894
- 2.times { |i| @pirate.birds.create!(:name => "birds_#{i}") }
1007
+ 2.times { |i| @pirate.birds.create!(name: "birds_#{i}") }
895
1008
 
896
- @pirate.birds.each { |bird| bird.name = '' }
897
- assert !@pirate.valid?
1009
+ @pirate.birds.each { |bird| bird.name = "" }
1010
+ assert_not_predicate @pirate, :valid?
898
1011
 
899
- @pirate.birds.each do |bird|
900
- bird.mark_for_destruction
901
- bird.expects(:valid?).never
1012
+ @pirate.birds.each(&:mark_for_destruction)
1013
+
1014
+ assert_not_called(@pirate.birds.first, :valid?) do
1015
+ assert_not_called(@pirate.birds.last, :valid?) do
1016
+ assert_difference("Bird.count", -2) { @pirate.save! }
1017
+ end
902
1018
  end
903
- assert_difference("Bird.count", -2) { @pirate.save! }
904
1019
  end
905
1020
 
906
1021
  def test_should_skip_validation_on_has_many_if_destroyed
907
- @pirate.birds.create!(:name => "birds_1")
1022
+ @pirate.birds.create!(name: "birds_1")
908
1023
 
909
- @pirate.birds.each { |bird| bird.name = '' }
910
- assert !@pirate.valid?
1024
+ @pirate.birds.each { |bird| bird.name = "" }
1025
+ assert_not_predicate @pirate, :valid?
911
1026
 
912
1027
  @pirate.birds.each(&:destroy)
913
- assert @pirate.valid?
1028
+ assert_predicate @pirate, :valid?
914
1029
  end
915
1030
 
916
1031
  def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_has_many
917
- @pirate.birds.create!(:name => "birds_1")
1032
+ @pirate.birds.create!(name: "birds_1")
918
1033
 
919
1034
  @pirate.birds.each(&:mark_for_destruction)
920
1035
  assert @pirate.save
921
1036
 
922
- @pirate.birds.each { |bird| bird.expects(:destroy).never }
923
- assert @pirate.save
1037
+ @pirate.birds.each do |bird|
1038
+ assert_not_called(bird, :destroy) do
1039
+ assert @pirate.save
1040
+ end
1041
+ end
924
1042
  end
925
1043
 
926
1044
  def test_should_rollback_destructions_if_an_exception_occurred_while_saving_has_many
927
- 2.times { |i| @pirate.birds.create!(:name => "birds_#{i}") }
1045
+ 2.times { |i| @pirate.birds.create!(name: "birds_#{i}") }
928
1046
  before = @pirate.birds.map { |c| c.mark_for_destruction ; c }
929
1047
 
930
1048
  # Stub the destroy method of the second child to raise an exception
931
1049
  class << before.last
932
1050
  def destroy(*args)
933
1051
  super
934
- raise 'Oh noes!'
1052
+ raise "Oh noes!"
935
1053
  end
936
1054
  end
937
1055
 
938
- assert_raise(RuntimeError) { assert !@pirate.save }
1056
+ assert_raise(RuntimeError) { assert_not @pirate.save }
939
1057
  assert_equal before, @pirate.reload.birds
940
1058
  end
941
1059
 
942
1060
  def test_when_new_record_a_child_marked_for_destruction_should_not_affect_other_records_from_saving
943
- @pirate = @ship.build_pirate(:catchphrase => "Arr' now I shall keep me eye on you matey!") # new record
1061
+ @pirate = @ship.build_pirate(catchphrase: "Arr' now I shall keep me eye on you matey!") # new record
944
1062
 
945
- 3.times { |i| @pirate.birds.build(:name => "birds_#{i}") }
1063
+ 3.times { |i| @pirate.birds.build(name: "birds_#{i}") }
946
1064
  @pirate.birds[1].mark_for_destruction
947
1065
  @pirate.save!
948
1066
 
@@ -968,8 +1086,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
968
1086
  define_method("test_should_run_add_callback_#{callback_type}s_for_has_many") do
969
1087
  association_name_with_callbacks = "birds_with_#{callback_type}_callbacks"
970
1088
 
971
- pirate = Pirate.new(:catchphrase => "Arr")
972
- pirate.send(association_name_with_callbacks).build(:name => "Crowe the One-Eyed")
1089
+ pirate = Pirate.new(catchphrase: "Arr")
1090
+ pirate.public_send(association_name_with_callbacks).build(name: "Crowe the One-Eyed")
973
1091
 
974
1092
  expected = [
975
1093
  "before_adding_#{callback_type}_bird_<new>",
@@ -982,9 +1100,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
982
1100
  define_method("test_should_run_remove_callback_#{callback_type}s_for_has_many") do
983
1101
  association_name_with_callbacks = "birds_with_#{callback_type}_callbacks"
984
1102
 
985
- @pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed")
986
- @pirate.send(association_name_with_callbacks).each(&:mark_for_destruction)
987
- child_id = @pirate.send(association_name_with_callbacks).first.id
1103
+ @pirate.public_send(association_name_with_callbacks).create!(name: "Crowe the One-Eyed")
1104
+ @pirate.public_send(association_name_with_callbacks).each(&:mark_for_destruction)
1105
+ child_id = @pirate.public_send(association_name_with_callbacks).first.id
988
1106
 
989
1107
  @pirate.ship_log.clear
990
1108
  @pirate.save
@@ -999,71 +1117,73 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
999
1117
  end
1000
1118
 
1001
1119
  def test_should_destroy_habtm_as_part_of_the_save_transaction_if_they_were_marked_for_destruction
1002
- 2.times { |i| @pirate.parrots.create!(:name => "parrots_#{i}") }
1120
+ 2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") }
1003
1121
 
1004
- assert !@pirate.parrots.any?(&:marked_for_destruction?)
1122
+ assert_not @pirate.parrots.any?(&:marked_for_destruction?)
1005
1123
  @pirate.parrots.each(&:mark_for_destruction)
1006
1124
 
1007
1125
  assert_no_difference "Parrot.count" do
1008
1126
  @pirate.save
1009
1127
  end
1010
1128
 
1011
- assert @pirate.reload.parrots.empty?
1129
+ assert_empty @pirate.reload.parrots
1012
1130
 
1013
1131
  join_records = Pirate.connection.select_all("SELECT * FROM parrots_pirates WHERE pirate_id = #{@pirate.id}")
1014
- assert join_records.empty?
1132
+ assert_empty join_records
1015
1133
  end
1016
1134
 
1017
1135
  def test_should_skip_validation_on_habtm_if_marked_for_destruction
1018
- 2.times { |i| @pirate.parrots.create!(:name => "parrots_#{i}") }
1136
+ 2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") }
1019
1137
 
1020
- @pirate.parrots.each { |parrot| parrot.name = '' }
1021
- assert !@pirate.valid?
1138
+ @pirate.parrots.each { |parrot| parrot.name = "" }
1139
+ assert_not_predicate @pirate, :valid?
1022
1140
 
1023
- @pirate.parrots.each do |parrot|
1024
- parrot.mark_for_destruction
1025
- parrot.expects(:valid?).never
1141
+ @pirate.parrots.each { |parrot| parrot.mark_for_destruction }
1142
+
1143
+ assert_not_called(@pirate.parrots.first, :valid?) do
1144
+ assert_not_called(@pirate.parrots.last, :valid?) do
1145
+ @pirate.save!
1146
+ end
1026
1147
  end
1027
1148
 
1028
- @pirate.save!
1029
- assert @pirate.reload.parrots.empty?
1149
+ assert_empty @pirate.reload.parrots
1030
1150
  end
1031
1151
 
1032
1152
  def test_should_skip_validation_on_habtm_if_destroyed
1033
- @pirate.parrots.create!(:name => "parrots_1")
1153
+ @pirate.parrots.create!(name: "parrots_1")
1034
1154
 
1035
- @pirate.parrots.each { |parrot| parrot.name = '' }
1036
- assert !@pirate.valid?
1155
+ @pirate.parrots.each { |parrot| parrot.name = "" }
1156
+ assert_not_predicate @pirate, :valid?
1037
1157
 
1038
1158
  @pirate.parrots.each(&:destroy)
1039
- assert @pirate.valid?
1159
+ assert_predicate @pirate, :valid?
1040
1160
  end
1041
1161
 
1042
1162
  def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_habtm
1043
- @pirate.parrots.create!(:name => "parrots_1")
1163
+ @pirate.parrots.create!(name: "parrots_1")
1044
1164
 
1045
1165
  @pirate.parrots.each(&:mark_for_destruction)
1046
1166
  assert @pirate.save
1047
1167
 
1048
1168
  Pirate.transaction do
1049
- assert_queries(0) do
1169
+ assert_no_queries do
1050
1170
  assert @pirate.save
1051
1171
  end
1052
1172
  end
1053
1173
  end
1054
1174
 
1055
1175
  def test_should_rollback_destructions_if_an_exception_occurred_while_saving_habtm
1056
- 2.times { |i| @pirate.parrots.create!(:name => "parrots_#{i}") }
1176
+ 2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") }
1057
1177
  before = @pirate.parrots.map { |c| c.mark_for_destruction ; c }
1058
1178
 
1059
1179
  class << @pirate.association(:parrots)
1060
1180
  def destroy(*args)
1061
1181
  super
1062
- raise 'Oh noes!'
1182
+ raise "Oh noes!"
1063
1183
  end
1064
1184
  end
1065
1185
 
1066
- assert_raise(RuntimeError) { assert !@pirate.save }
1186
+ assert_raise(RuntimeError) { assert_not @pirate.save }
1067
1187
  assert_equal before, @pirate.reload.parrots
1068
1188
  end
1069
1189
 
@@ -1072,8 +1192,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
1072
1192
  define_method("test_should_run_add_callback_#{callback_type}s_for_habtm") do
1073
1193
  association_name_with_callbacks = "parrots_with_#{callback_type}_callbacks"
1074
1194
 
1075
- pirate = Pirate.new(:catchphrase => "Arr")
1076
- pirate.send(association_name_with_callbacks).build(:name => "Crowe the One-Eyed")
1195
+ pirate = Pirate.new(catchphrase: "Arr")
1196
+ pirate.public_send(association_name_with_callbacks).build(name: "Crowe the One-Eyed")
1077
1197
 
1078
1198
  expected = [
1079
1199
  "before_adding_#{callback_type}_parrot_<new>",
@@ -1086,9 +1206,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
1086
1206
  define_method("test_should_run_remove_callback_#{callback_type}s_for_habtm") do
1087
1207
  association_name_with_callbacks = "parrots_with_#{callback_type}_callbacks"
1088
1208
 
1089
- @pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed")
1090
- @pirate.send(association_name_with_callbacks).each(&:mark_for_destruction)
1091
- child_id = @pirate.send(association_name_with_callbacks).first.id
1209
+ @pirate.public_send(association_name_with_callbacks).create!(name: "Crowe the One-Eyed")
1210
+ @pirate.public_send(association_name_with_callbacks).each(&:mark_for_destruction)
1211
+ child_id = @pirate.public_send(association_name_with_callbacks).first.id
1092
1212
 
1093
1213
  @pirate.ship_log.clear
1094
1214
  @pirate.save
@@ -1108,60 +1228,60 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1108
1228
 
1109
1229
  def setup
1110
1230
  super
1111
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1112
- @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
1231
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1232
+ @ship = @pirate.create_ship(name: "Nights Dirty Lightning")
1113
1233
  end
1114
1234
 
1115
1235
  def test_should_still_work_without_an_associated_model
1116
1236
  @ship.destroy
1117
1237
  @pirate.reload.catchphrase = "Arr"
1118
1238
  @pirate.save
1119
- assert_equal 'Arr', @pirate.reload.catchphrase
1239
+ assert_equal "Arr", @pirate.reload.catchphrase
1120
1240
  end
1121
1241
 
1122
1242
  def test_should_automatically_save_the_associated_model
1123
- @pirate.ship.name = 'The Vile Insanity'
1243
+ @pirate.ship.name = "The Vile Insanity"
1124
1244
  @pirate.save
1125
- assert_equal 'The Vile Insanity', @pirate.reload.ship.name
1245
+ assert_equal "The Vile Insanity", @pirate.reload.ship.name
1126
1246
  end
1127
1247
 
1128
1248
  def test_changed_for_autosave_should_handle_cycles
1129
1249
  @ship.pirate = @pirate
1130
- assert_queries(0) { @ship.save! }
1250
+ assert_no_queries { @ship.save! }
1131
1251
 
1132
1252
  @parrot = @pirate.parrots.create(name: "some_name")
1133
- @parrot.name="changed_name"
1253
+ @parrot.name = "changed_name"
1134
1254
  assert_queries(1) { @ship.save! }
1135
- assert_queries(0) { @ship.save! }
1255
+ assert_no_queries { @ship.save! }
1136
1256
  end
1137
1257
 
1138
1258
  def test_should_automatically_save_bang_the_associated_model
1139
- @pirate.ship.name = 'The Vile Insanity'
1259
+ @pirate.ship.name = "The Vile Insanity"
1140
1260
  @pirate.save!
1141
- assert_equal 'The Vile Insanity', @pirate.reload.ship.name
1261
+ assert_equal "The Vile Insanity", @pirate.reload.ship.name
1142
1262
  end
1143
1263
 
1144
1264
  def test_should_automatically_validate_the_associated_model
1145
- @pirate.ship.name = ''
1146
- assert @pirate.invalid?
1147
- assert @pirate.errors[:"ship.name"].any?
1265
+ @pirate.ship.name = ""
1266
+ assert_predicate @pirate, :invalid?
1267
+ assert_predicate @pirate.errors[:"ship.name"], :any?
1148
1268
  end
1149
1269
 
1150
1270
  def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
1151
1271
  @pirate.ship.name = nil
1152
1272
  @pirate.catchphrase = nil
1153
- assert @pirate.invalid?
1154
- assert @pirate.errors[:"ship.name"].any?
1155
- assert @pirate.errors[:catchphrase].any?
1273
+ assert_predicate @pirate, :invalid?
1274
+ assert_predicate @pirate.errors[:"ship.name"], :any?
1275
+ assert_predicate @pirate.errors[:catchphrase], :any?
1156
1276
  end
1157
1277
 
1158
1278
  def test_should_not_ignore_different_error_messages_on_the_same_attribute
1159
1279
  old_validators = Ship._validators.deep_dup
1160
1280
  old_callbacks = Ship._validate_callbacks.deep_dup
1161
- Ship.validates_format_of :name, :with => /\w/
1281
+ Ship.validates_format_of :name, with: /\w/
1162
1282
  @pirate.ship.name = ""
1163
1283
  @pirate.catchphrase = nil
1164
- assert @pirate.invalid?
1284
+ assert_predicate @pirate, :invalid?
1165
1285
  assert_equal ["can't be blank", "is invalid"], @pirate.errors[:"ship.name"]
1166
1286
  ensure
1167
1287
  Ship._validators = old_validators if old_validators
@@ -1169,49 +1289,49 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1169
1289
  end
1170
1290
 
1171
1291
  def test_should_still_allow_to_bypass_validations_on_the_associated_model
1172
- @pirate.catchphrase = ''
1173
- @pirate.ship.name = ''
1174
- @pirate.save(:validate => false)
1292
+ @pirate.catchphrase = ""
1293
+ @pirate.ship.name = ""
1294
+ @pirate.save(validate: false)
1175
1295
  # Oracle saves empty string as NULL
1176
1296
  if current_adapter?(:OracleAdapter)
1177
1297
  assert_equal [nil, nil], [@pirate.reload.catchphrase, @pirate.ship.name]
1178
1298
  else
1179
- assert_equal ['', ''], [@pirate.reload.catchphrase, @pirate.ship.name]
1299
+ assert_equal ["", ""], [@pirate.reload.catchphrase, @pirate.ship.name]
1180
1300
  end
1181
1301
  end
1182
1302
 
1183
1303
  def test_should_allow_to_bypass_validations_on_associated_models_at_any_depth
1184
- 2.times { |i| @pirate.ship.parts.create!(:name => "part #{i}") }
1304
+ 2.times { |i| @pirate.ship.parts.create!(name: "part #{i}") }
1185
1305
 
1186
- @pirate.catchphrase = ''
1187
- @pirate.ship.name = ''
1188
- @pirate.ship.parts.each { |part| part.name = '' }
1189
- @pirate.save(:validate => false)
1306
+ @pirate.catchphrase = ""
1307
+ @pirate.ship.name = ""
1308
+ @pirate.ship.parts.each { |part| part.name = "" }
1309
+ @pirate.save(validate: false)
1190
1310
 
1191
1311
  values = [@pirate.reload.catchphrase, @pirate.ship.name, *@pirate.ship.parts.map(&:name)]
1192
1312
  # Oracle saves empty string as NULL
1193
1313
  if current_adapter?(:OracleAdapter)
1194
1314
  assert_equal [nil, nil, nil, nil], values
1195
1315
  else
1196
- assert_equal ['', '', '', ''], values
1316
+ assert_equal ["", "", "", ""], values
1197
1317
  end
1198
1318
  end
1199
1319
 
1200
1320
  def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
1201
- @pirate.ship.name = ''
1321
+ @pirate.ship.name = ""
1202
1322
  assert_raise(ActiveRecord::RecordInvalid) do
1203
1323
  @pirate.save!
1204
1324
  end
1205
1325
  end
1206
1326
 
1207
1327
  def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
1208
- pirate = Pirate.new(:catchphrase => 'Arr')
1209
- ship = pirate.build_ship(:name => 'The Vile Insanity')
1328
+ pirate = Pirate.new(catchphrase: "Arr")
1329
+ ship = pirate.build_ship(name: "The Vile Insanity")
1210
1330
  ship.cancel_save_from_callback = true
1211
1331
 
1212
- assert_no_difference 'Pirate.count' do
1213
- assert_no_difference 'Ship.count' do
1214
- assert !pirate.save
1332
+ assert_no_difference "Pirate.count" do
1333
+ assert_no_difference "Ship.count" do
1334
+ assert_not pirate.save
1215
1335
  end
1216
1336
  end
1217
1337
  end
@@ -1219,51 +1339,75 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1219
1339
  def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
1220
1340
  before = [@pirate.catchphrase, @pirate.ship.name]
1221
1341
 
1222
- @pirate.catchphrase = 'Arr'
1223
- @pirate.ship.name = 'The Vile Insanity'
1342
+ @pirate.catchphrase = "Arr"
1343
+ @pirate.ship.name = "The Vile Insanity"
1224
1344
 
1225
1345
  # Stub the save method of the @pirate.ship instance to raise an exception
1226
1346
  class << @pirate.ship
1227
- def save(*args)
1347
+ def save(**)
1228
1348
  super
1229
- raise 'Oh noes!'
1349
+ raise "Oh noes!"
1230
1350
  end
1231
1351
  end
1232
1352
 
1233
- assert_raise(RuntimeError) { assert !@pirate.save }
1353
+ assert_raise(RuntimeError) { assert_not @pirate.save }
1234
1354
  assert_equal before, [@pirate.reload.catchphrase, @pirate.ship.name]
1235
1355
  end
1236
1356
 
1237
1357
  def test_should_not_load_the_associated_model
1238
- assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
1358
+ assert_queries(1) { @pirate.catchphrase = "Arr"; @pirate.save! }
1239
1359
  end
1240
1360
 
1241
1361
  def test_mark_for_destruction_is_ignored_without_autosave_true
1242
1362
  ship = ShipWithoutNestedAttributes.new(name: "The Black Flag")
1243
1363
  ship.parts.build.mark_for_destruction
1244
1364
 
1245
- assert_not ship.valid?
1365
+ assert_not_predicate ship, :valid?
1246
1366
  end
1247
1367
  end
1248
1368
 
1249
1369
  class TestAutosaveAssociationOnAHasOneThroughAssociation < ActiveRecord::TestCase
1250
1370
  self.use_transactional_tests = false unless supports_savepoints?
1251
1371
 
1252
- def setup
1253
- super
1372
+ def create_member_with_organization
1254
1373
  organization = Organization.create
1255
- @member = Member.create
1256
- MemberDetail.create(organization: organization, member: @member)
1374
+ member = Member.create
1375
+ MemberDetail.create(organization: organization, member: member)
1376
+
1377
+ member
1257
1378
  end
1258
1379
 
1259
1380
  def test_should_not_has_one_through_model
1260
- class << @member.organization
1261
- def save(*args)
1381
+ member = create_member_with_organization
1382
+
1383
+ class << member.organization
1384
+ def save(**)
1385
+ super
1386
+ raise "Oh noes!"
1387
+ end
1388
+ end
1389
+ assert_nothing_raised { member.save }
1390
+ end
1391
+
1392
+ def create_author_with_post_with_comment
1393
+ Author.create! name: "David" # make comment_id not match author_id
1394
+ author = Author.create! name: "Sergiy"
1395
+ post = Post.create! author: author, title: "foo", body: "bar"
1396
+ Comment.create! post: post, body: "cool comment"
1397
+
1398
+ author
1399
+ end
1400
+
1401
+ def test_should_not_reversed_has_one_through_model
1402
+ author = create_author_with_post_with_comment
1403
+
1404
+ class << author.comment_on_first_post
1405
+ def save(**)
1262
1406
  super
1263
- raise 'Oh noes!'
1407
+ raise "Oh noes!"
1264
1408
  end
1265
1409
  end
1266
- assert_nothing_raised { @member.save }
1410
+ assert_nothing_raised { author.save }
1267
1411
  end
1268
1412
  end
1269
1413
 
@@ -1272,70 +1416,70 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
1272
1416
 
1273
1417
  def setup
1274
1418
  super
1275
- @ship = Ship.create(:name => 'Nights Dirty Lightning')
1276
- @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1419
+ @ship = Ship.create(name: "Nights Dirty Lightning")
1420
+ @pirate = @ship.create_pirate(catchphrase: "Don' botharrr talkin' like one, savvy?")
1277
1421
  end
1278
1422
 
1279
1423
  def test_should_still_work_without_an_associated_model
1280
1424
  @pirate.destroy
1281
1425
  @ship.reload.name = "The Vile Insanity"
1282
1426
  @ship.save
1283
- assert_equal 'The Vile Insanity', @ship.reload.name
1427
+ assert_equal "The Vile Insanity", @ship.reload.name
1284
1428
  end
1285
1429
 
1286
1430
  def test_should_automatically_save_the_associated_model
1287
- @ship.pirate.catchphrase = 'Arr'
1431
+ @ship.pirate.catchphrase = "Arr"
1288
1432
  @ship.save
1289
- assert_equal 'Arr', @ship.reload.pirate.catchphrase
1433
+ assert_equal "Arr", @ship.reload.pirate.catchphrase
1290
1434
  end
1291
1435
 
1292
1436
  def test_should_automatically_save_bang_the_associated_model
1293
- @ship.pirate.catchphrase = 'Arr'
1437
+ @ship.pirate.catchphrase = "Arr"
1294
1438
  @ship.save!
1295
- assert_equal 'Arr', @ship.reload.pirate.catchphrase
1439
+ assert_equal "Arr", @ship.reload.pirate.catchphrase
1296
1440
  end
1297
1441
 
1298
1442
  def test_should_automatically_validate_the_associated_model
1299
- @ship.pirate.catchphrase = ''
1300
- assert @ship.invalid?
1301
- assert @ship.errors[:"pirate.catchphrase"].any?
1443
+ @ship.pirate.catchphrase = ""
1444
+ assert_predicate @ship, :invalid?
1445
+ assert_predicate @ship.errors[:"pirate.catchphrase"], :any?
1302
1446
  end
1303
1447
 
1304
1448
  def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
1305
1449
  @ship.name = nil
1306
1450
  @ship.pirate.catchphrase = nil
1307
- assert @ship.invalid?
1308
- assert @ship.errors[:name].any?
1309
- assert @ship.errors[:"pirate.catchphrase"].any?
1451
+ assert_predicate @ship, :invalid?
1452
+ assert_predicate @ship.errors[:name], :any?
1453
+ assert_predicate @ship.errors[:"pirate.catchphrase"], :any?
1310
1454
  end
1311
1455
 
1312
1456
  def test_should_still_allow_to_bypass_validations_on_the_associated_model
1313
- @ship.pirate.catchphrase = ''
1314
- @ship.name = ''
1315
- @ship.save(:validate => false)
1457
+ @ship.pirate.catchphrase = ""
1458
+ @ship.name = ""
1459
+ @ship.save(validate: false)
1316
1460
  # Oracle saves empty string as NULL
1317
1461
  if current_adapter?(:OracleAdapter)
1318
1462
  assert_equal [nil, nil], [@ship.reload.name, @ship.pirate.catchphrase]
1319
1463
  else
1320
- assert_equal ['', ''], [@ship.reload.name, @ship.pirate.catchphrase]
1464
+ assert_equal ["", ""], [@ship.reload.name, @ship.pirate.catchphrase]
1321
1465
  end
1322
1466
  end
1323
1467
 
1324
1468
  def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
1325
- @ship.pirate.catchphrase = ''
1469
+ @ship.pirate.catchphrase = ""
1326
1470
  assert_raise(ActiveRecord::RecordInvalid) do
1327
1471
  @ship.save!
1328
1472
  end
1329
1473
  end
1330
1474
 
1331
1475
  def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
1332
- ship = Ship.new(:name => 'The Vile Insanity')
1333
- pirate = ship.build_pirate(:catchphrase => 'Arr')
1476
+ ship = Ship.new(name: "The Vile Insanity")
1477
+ pirate = ship.build_pirate(catchphrase: "Arr")
1334
1478
  pirate.cancel_save_from_callback = true
1335
1479
 
1336
- assert_no_difference 'Ship.count' do
1337
- assert_no_difference 'Pirate.count' do
1338
- assert !ship.save
1480
+ assert_no_difference "Ship.count" do
1481
+ assert_no_difference "Pirate.count" do
1482
+ assert_not ship.save
1339
1483
  end
1340
1484
  end
1341
1485
  end
@@ -1343,41 +1487,41 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
1343
1487
  def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
1344
1488
  before = [@ship.pirate.catchphrase, @ship.name]
1345
1489
 
1346
- @ship.pirate.catchphrase = 'Arr'
1347
- @ship.name = 'The Vile Insanity'
1490
+ @ship.pirate.catchphrase = "Arr"
1491
+ @ship.name = "The Vile Insanity"
1348
1492
 
1349
1493
  # Stub the save method of the @ship.pirate instance to raise an exception
1350
1494
  class << @ship.pirate
1351
- def save(*args)
1495
+ def save(**)
1352
1496
  super
1353
- raise 'Oh noes!'
1497
+ raise "Oh noes!"
1354
1498
  end
1355
1499
  end
1356
1500
 
1357
- assert_raise(RuntimeError) { assert !@ship.save }
1501
+ assert_raise(RuntimeError) { assert_not @ship.save }
1358
1502
  assert_equal before, [@ship.pirate.reload.catchphrase, @ship.reload.name]
1359
1503
  end
1360
1504
 
1361
1505
  def test_should_not_load_the_associated_model
1362
- assert_queries(1) { @ship.name = 'The Vile Insanity'; @ship.save! }
1506
+ assert_queries(1) { @ship.name = "The Vile Insanity"; @ship.save! }
1363
1507
  end
1364
1508
  end
1365
1509
 
1366
1510
  module AutosaveAssociationOnACollectionAssociationTests
1367
1511
  def test_should_automatically_save_the_associated_models
1368
- new_names = ['Grace OMalley', 'Privateers Greed']
1369
- @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1512
+ new_names = ["Grace OMalley", "Privateers Greed"]
1513
+ @pirate.public_send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1370
1514
 
1371
1515
  @pirate.save
1372
- assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
1516
+ assert_equal new_names.sort, @pirate.reload.public_send(@association_name).map(&:name).sort
1373
1517
  end
1374
1518
 
1375
1519
  def test_should_automatically_save_bang_the_associated_models
1376
- new_names = ['Grace OMalley', 'Privateers Greed']
1377
- @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1520
+ new_names = ["Grace OMalley", "Privateers Greed"]
1521
+ @pirate.public_send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1378
1522
 
1379
1523
  @pirate.save!
1380
- assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
1524
+ assert_equal new_names.sort, @pirate.reload.public_send(@association_name).map(&:name).sort
1381
1525
  end
1382
1526
 
1383
1527
  def test_should_update_children_when_autosave_is_true_and_parent_is_new_but_child_is_not
@@ -1399,135 +1543,135 @@ module AutosaveAssociationOnACollectionAssociationTests
1399
1543
  end
1400
1544
 
1401
1545
  def test_should_automatically_validate_the_associated_models
1402
- @pirate.send(@association_name).each { |child| child.name = '' }
1546
+ @pirate.public_send(@association_name).each { |child| child.name = "" }
1403
1547
 
1404
- assert !@pirate.valid?
1548
+ assert_not_predicate @pirate, :valid?
1405
1549
  assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
1406
- assert @pirate.errors[@association_name].empty?
1550
+ assert_empty @pirate.errors[@association_name]
1407
1551
  end
1408
1552
 
1409
1553
  def test_should_not_use_default_invalid_error_on_associated_models
1410
- @pirate.send(@association_name).build(:name => '')
1554
+ @pirate.public_send(@association_name).build(name: "")
1411
1555
 
1412
- assert !@pirate.valid?
1556
+ assert_not_predicate @pirate, :valid?
1413
1557
  assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
1414
- assert @pirate.errors[@association_name].empty?
1558
+ assert_empty @pirate.errors[@association_name]
1415
1559
  end
1416
1560
 
1417
1561
  def test_should_default_invalid_error_from_i18n
1418
- I18n.backend.store_translations(:en, activerecord: {errors: { models:
1562
+ I18n.backend.store_translations(:en, activerecord: { errors: { models:
1419
1563
  { @associated_model_name.to_s.to_sym => { blank: "cannot be blank" } }
1420
- }})
1564
+ } })
1421
1565
 
1422
- @pirate.send(@association_name).build(name: '')
1566
+ @pirate.public_send(@association_name).build(name: "")
1423
1567
 
1424
- assert !@pirate.valid?
1568
+ assert_not_predicate @pirate, :valid?
1425
1569
  assert_equal ["cannot be blank"], @pirate.errors["#{@association_name}.name"]
1426
1570
  assert_equal ["#{@association_name.to_s.humanize} name cannot be blank"], @pirate.errors.full_messages
1427
- assert @pirate.errors[@association_name].empty?
1571
+ assert_empty @pirate.errors[@association_name]
1428
1572
  ensure
1429
1573
  I18n.backend = I18n::Backend::Simple.new
1430
1574
  end
1431
1575
 
1432
1576
  def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
1433
- @pirate.send(@association_name).each { |child| child.name = '' }
1577
+ @pirate.public_send(@association_name).each { |child| child.name = "" }
1434
1578
  @pirate.catchphrase = nil
1435
1579
 
1436
- assert !@pirate.valid?
1580
+ assert_not_predicate @pirate, :valid?
1437
1581
  assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
1438
- assert @pirate.errors[:catchphrase].any?
1582
+ assert_predicate @pirate.errors[:catchphrase], :any?
1439
1583
  end
1440
1584
 
1441
1585
  def test_should_allow_to_bypass_validations_on_the_associated_models_on_update
1442
- @pirate.catchphrase = ''
1443
- @pirate.send(@association_name).each { |child| child.name = '' }
1586
+ @pirate.catchphrase = ""
1587
+ @pirate.public_send(@association_name).each { |child| child.name = "" }
1444
1588
 
1445
- assert @pirate.save(:validate => false)
1589
+ assert @pirate.save(validate: false)
1446
1590
  # Oracle saves empty string as NULL
1447
1591
  if current_adapter?(:OracleAdapter)
1448
1592
  assert_equal [nil, nil, nil], [
1449
1593
  @pirate.reload.catchphrase,
1450
- @pirate.send(@association_name).first.name,
1451
- @pirate.send(@association_name).last.name
1594
+ @pirate.public_send(@association_name).first.name,
1595
+ @pirate.public_send(@association_name).last.name
1452
1596
  ]
1453
1597
  else
1454
- assert_equal ['', '', ''], [
1598
+ assert_equal ["", "", ""], [
1455
1599
  @pirate.reload.catchphrase,
1456
- @pirate.send(@association_name).first.name,
1457
- @pirate.send(@association_name).last.name
1600
+ @pirate.public_send(@association_name).first.name,
1601
+ @pirate.public_send(@association_name).last.name
1458
1602
  ]
1459
1603
  end
1460
1604
  end
1461
1605
 
1462
1606
  def test_should_validation_the_associated_models_on_create
1463
1607
  assert_no_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count") do
1464
- 2.times { @pirate.send(@association_name).build }
1608
+ 2.times { @pirate.public_send(@association_name).build }
1465
1609
  @pirate.save
1466
1610
  end
1467
1611
  end
1468
1612
 
1469
1613
  def test_should_allow_to_bypass_validations_on_the_associated_models_on_create
1470
1614
  assert_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count", 2) do
1471
- 2.times { @pirate.send(@association_name).build }
1472
- @pirate.save(:validate => false)
1615
+ 2.times { @pirate.public_send(@association_name).build }
1616
+ @pirate.save(validate: false)
1473
1617
  end
1474
1618
  end
1475
1619
 
1476
1620
  def test_should_not_save_and_return_false_if_a_callback_cancelled_saving_in_either_create_or_update
1477
- @pirate.catchphrase = 'Changed'
1478
- @child_1.name = 'Changed'
1621
+ @pirate.catchphrase = "Changed"
1622
+ @child_1.name = "Changed"
1479
1623
  @child_1.cancel_save_from_callback = true
1480
1624
 
1481
- assert !@pirate.save
1625
+ assert_not @pirate.save
1482
1626
  assert_equal "Don' botharrr talkin' like one, savvy?", @pirate.reload.catchphrase
1483
1627
  assert_equal "Posideons Killer", @child_1.reload.name
1484
1628
 
1485
- new_pirate = Pirate.new(:catchphrase => 'Arr')
1486
- new_child = new_pirate.send(@association_name).build(:name => 'Grace OMalley')
1629
+ new_pirate = Pirate.new(catchphrase: "Arr")
1630
+ new_child = new_pirate.public_send(@association_name).build(name: "Grace OMalley")
1487
1631
  new_child.cancel_save_from_callback = true
1488
1632
 
1489
- assert_no_difference 'Pirate.count' do
1633
+ assert_no_difference "Pirate.count" do
1490
1634
  assert_no_difference "#{new_child.class.name}.count" do
1491
- assert !new_pirate.save
1635
+ assert_not new_pirate.save
1492
1636
  end
1493
1637
  end
1494
1638
  end
1495
1639
 
1496
1640
  def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
1497
- before = [@pirate.catchphrase, *@pirate.send(@association_name).map(&:name)]
1498
- new_names = ['Grace OMalley', 'Privateers Greed']
1641
+ before = [@pirate.catchphrase, *@pirate.public_send(@association_name).map(&:name)]
1642
+ new_names = ["Grace OMalley", "Privateers Greed"]
1499
1643
 
1500
- @pirate.catchphrase = 'Arr'
1501
- @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1644
+ @pirate.catchphrase = "Arr"
1645
+ @pirate.public_send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1502
1646
 
1503
1647
  # Stub the save method of the first child instance to raise an exception
1504
- class << @pirate.send(@association_name).first
1505
- def save(*args)
1648
+ class << @pirate.public_send(@association_name).first
1649
+ def save(**)
1506
1650
  super
1507
- raise 'Oh noes!'
1651
+ raise "Oh noes!"
1508
1652
  end
1509
1653
  end
1510
1654
 
1511
- assert_raise(RuntimeError) { assert !@pirate.save }
1655
+ assert_raise(RuntimeError) { assert_not @pirate.save }
1512
1656
  assert_equal before, [@pirate.reload.catchphrase, *@pirate.send(@association_name).map(&:name)]
1513
1657
  end
1514
1658
 
1515
1659
  def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
1516
- @pirate.send(@association_name).each { |child| child.name = '' }
1660
+ @pirate.public_send(@association_name).each { |child| child.name = "" }
1517
1661
  assert_raise(ActiveRecord::RecordInvalid) do
1518
1662
  @pirate.save!
1519
1663
  end
1520
1664
  end
1521
1665
 
1522
1666
  def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet
1523
- assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
1667
+ assert_queries(1) { @pirate.catchphrase = "Arr"; @pirate.save! }
1524
1668
 
1525
- @pirate.send(@association_name).load_target
1669
+ @pirate.public_send(@association_name).load_target
1526
1670
 
1527
1671
  assert_queries(3) do
1528
- @pirate.catchphrase = 'Yarr'
1529
- new_names = ['Grace OMalley', 'Privateers Greed']
1530
- @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1672
+ @pirate.catchphrase = "Yarr"
1673
+ new_names = ["Grace OMalley", "Privateers Greed"]
1674
+ @pirate.public_send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1531
1675
  @pirate.save!
1532
1676
  end
1533
1677
  end
@@ -1541,9 +1685,9 @@ class TestAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
1541
1685
  @association_name = :birds
1542
1686
  @associated_model_name = :bird
1543
1687
 
1544
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1545
- @child_1 = @pirate.birds.create(:name => 'Posideons Killer')
1546
- @child_2 = @pirate.birds.create(:name => 'Killer bandita Dionne')
1688
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1689
+ @child_1 = @pirate.birds.create(name: "Posideons Killer")
1690
+ @child_2 = @pirate.birds.create(name: "Killer bandita Dionne")
1547
1691
  end
1548
1692
 
1549
1693
  include AutosaveAssociationOnACollectionAssociationTests
@@ -1559,8 +1703,8 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
1559
1703
  @habtm = true
1560
1704
 
1561
1705
  @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1562
- @child_1 = @pirate.parrots.create(name: 'Posideons Killer')
1563
- @child_2 = @pirate.parrots.create(name: 'Killer bandita Dionne')
1706
+ @child_1 = @pirate.parrots.create(name: "Posideons Killer")
1707
+ @child_2 = @pirate.parrots.create(name: "Killer bandita Dionne")
1564
1708
  end
1565
1709
 
1566
1710
  include AutosaveAssociationOnACollectionAssociationTests
@@ -1576,8 +1720,8 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociationWithAcceptsNestedA
1576
1720
  @habtm = true
1577
1721
 
1578
1722
  @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1579
- @child_1 = @pirate.parrots.create(name: 'Posideons Killer')
1580
- @child_2 = @pirate.parrots.create(name: 'Killer bandita Dionne')
1723
+ @child_1 = @pirate.parrots.create(name: "Posideons Killer")
1724
+ @child_2 = @pirate.parrots.create(name: "Killer bandita Dionne")
1581
1725
  end
1582
1726
 
1583
1727
  include AutosaveAssociationOnACollectionAssociationTests
@@ -1588,15 +1732,63 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
1588
1732
 
1589
1733
  def setup
1590
1734
  super
1591
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1592
- @pirate.birds.create(:name => 'cookoo')
1735
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1736
+ @pirate.birds.create(name: "cookoo")
1737
+
1738
+ @author = Author.new(name: "DHH")
1739
+ @author.published_books.build(name: "Rework", isbn: "1234")
1740
+ @author.published_books.build(name: "Remote", isbn: "1234")
1593
1741
  end
1594
1742
 
1595
1743
  test "should automatically validate associations" do
1596
- assert @pirate.valid?
1597
- @pirate.birds.each { |bird| bird.name = '' }
1744
+ assert_predicate @pirate, :valid?
1745
+ @pirate.birds.each { |bird| bird.name = "" }
1746
+
1747
+ assert_not_predicate @pirate, :valid?
1748
+ end
1749
+
1750
+ test "rollbacks whole transaction and raises ActiveRecord::RecordInvalid when associations fail to #save! due to uniqueness validation failure" do
1751
+ author_count_before_save = Author.count
1752
+ book_count_before_save = Book.count
1753
+
1754
+ assert_no_difference "Author.count" do
1755
+ assert_no_difference "Book.count" do
1756
+ exception = assert_raises(ActiveRecord::RecordInvalid) do
1757
+ @author.save!
1758
+ end
1759
+
1760
+ assert_equal("Validation failed: Published books is invalid", exception.message)
1761
+ end
1762
+ end
1763
+
1764
+ assert_equal(author_count_before_save, Author.count)
1765
+ assert_equal(book_count_before_save, Book.count)
1766
+ end
1598
1767
 
1599
- assert !@pirate.valid?
1768
+ test "rollbacks whole transaction when associations fail to #save due to uniqueness validation failure" do
1769
+ author_count_before_save = Author.count
1770
+ book_count_before_save = Book.count
1771
+
1772
+ assert_no_difference "Author.count" do
1773
+ assert_no_difference "Book.count" do
1774
+ assert_nothing_raised do
1775
+ result = @author.save
1776
+
1777
+ assert_not(result)
1778
+ end
1779
+ end
1780
+ end
1781
+
1782
+ assert_equal(author_count_before_save, Author.count)
1783
+ assert_equal(book_count_before_save, Book.count)
1784
+ end
1785
+
1786
+ def test_validations_still_fire_on_unchanged_association_with_custom_validation_context
1787
+ pirate = FamousPirate.create!(catchphrase: "Avast Ye!")
1788
+ pirate.famous_ships.create!
1789
+
1790
+ assert pirate.valid?
1791
+ assert_not pirate.valid?(:conference)
1600
1792
  end
1601
1793
  end
1602
1794
 
@@ -1605,21 +1797,21 @@ class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::Tes
1605
1797
 
1606
1798
  def setup
1607
1799
  super
1608
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1609
- @pirate.create_ship(:name => 'titanic')
1800
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1801
+ @pirate.create_ship(name: "titanic")
1610
1802
  super
1611
1803
  end
1612
1804
 
1613
1805
  test "should automatically validate associations with :validate => true" do
1614
- assert @pirate.valid?
1615
- @pirate.ship.name = ''
1616
- assert !@pirate.valid?
1806
+ assert_predicate @pirate, :valid?
1807
+ @pirate.ship.name = ""
1808
+ assert_not_predicate @pirate, :valid?
1617
1809
  end
1618
1810
 
1619
1811
  test "should not automatically add validate associations without :validate => true" do
1620
- assert @pirate.valid?
1621
- @pirate.non_validated_ship.name = ''
1622
- assert @pirate.valid?
1812
+ assert_predicate @pirate, :valid?
1813
+ @pirate.non_validated_ship.name = ""
1814
+ assert_predicate @pirate, :valid?
1623
1815
  end
1624
1816
  end
1625
1817
 
@@ -1628,19 +1820,26 @@ class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::
1628
1820
 
1629
1821
  def setup
1630
1822
  super
1631
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1823
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1632
1824
  end
1633
1825
 
1634
1826
  test "should automatically validate associations with :validate => true" do
1635
- assert @pirate.valid?
1636
- @pirate.parrot = Parrot.new(:name => '')
1637
- assert !@pirate.valid?
1827
+ assert_predicate @pirate, :valid?
1828
+ @pirate.parrot = Parrot.new(name: "")
1829
+ assert_not_predicate @pirate, :valid?
1638
1830
  end
1639
1831
 
1640
1832
  test "should not automatically validate associations without :validate => true" do
1641
- assert @pirate.valid?
1642
- @pirate.non_validated_parrot = Parrot.new(:name => '')
1643
- assert @pirate.valid?
1833
+ assert_predicate @pirate, :valid?
1834
+ @pirate.non_validated_parrot = Parrot.new(name: "")
1835
+ assert_predicate @pirate, :valid?
1836
+ end
1837
+
1838
+ def test_validations_still_fire_on_unchanged_association_with_custom_validation_context
1839
+ firm_with_low_credit = Firm.create!(name: "Something", account: Account.new(credit_limit: 50))
1840
+
1841
+ assert firm_with_low_credit.valid?
1842
+ assert_not firm_with_low_credit.valid?(:bank_loan)
1644
1843
  end
1645
1844
  end
1646
1845
 
@@ -1649,21 +1848,21 @@ class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::Test
1649
1848
 
1650
1849
  def setup
1651
1850
  super
1652
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1851
+ @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1653
1852
  end
1654
1853
 
1655
1854
  test "should automatically validate associations with :validate => true" do
1656
- assert @pirate.valid?
1657
- @pirate.parrots = [ Parrot.new(:name => 'popuga') ]
1658
- @pirate.parrots.each { |parrot| parrot.name = '' }
1659
- assert !@pirate.valid?
1855
+ assert_predicate @pirate, :valid?
1856
+ @pirate.parrots = [ Parrot.new(name: "popuga") ]
1857
+ @pirate.parrots.each { |parrot| parrot.name = "" }
1858
+ assert_not_predicate @pirate, :valid?
1660
1859
  end
1661
1860
 
1662
1861
  test "should not automatically validate associations without :validate => true" do
1663
- assert @pirate.valid?
1664
- @pirate.non_validated_parrots = [ Parrot.new(:name => 'popuga') ]
1665
- @pirate.non_validated_parrots.each { |parrot| parrot.name = '' }
1666
- assert @pirate.valid?
1862
+ assert_predicate @pirate, :valid?
1863
+ @pirate.non_validated_parrots = [ Parrot.new(name: "popuga") ]
1864
+ @pirate.non_validated_parrots.each { |parrot| parrot.name = "" }
1865
+ assert_predicate @pirate, :valid?
1667
1866
  end
1668
1867
  end
1669
1868
 
@@ -1684,7 +1883,7 @@ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCas
1684
1883
  end
1685
1884
 
1686
1885
  test "should not generate validation methods for has_one associations without :validate => true" do
1687
- assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_ship)
1886
+ assert_not_respond_to @pirate, :validate_associated_records_for_non_validated_ship
1688
1887
  end
1689
1888
 
1690
1889
  test "should generate validation methods for belongs_to associations with :validate => true" do
@@ -1692,7 +1891,7 @@ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCas
1692
1891
  end
1693
1892
 
1694
1893
  test "should not generate validation methods for belongs_to associations without :validate => true" do
1695
- assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrot)
1894
+ assert_not_respond_to @pirate, :validate_associated_records_for_non_validated_parrot
1696
1895
  end
1697
1896
 
1698
1897
  test "should generate validation methods for HABTM associations with :validate => true" do
@@ -1703,6 +1902,52 @@ end
1703
1902
  class TestAutosaveAssociationWithTouch < ActiveRecord::TestCase
1704
1903
  def test_autosave_with_touch_should_not_raise_system_stack_error
1705
1904
  invoice = Invoice.create
1706
- assert_nothing_raised { invoice.line_items.create(:amount => 10) }
1905
+ assert_nothing_raised { invoice.line_items.create(amount: 10) }
1906
+ end
1907
+ end
1908
+
1909
+ class TestAutosaveAssociationOnAHasManyAssociationWithInverse < ActiveRecord::TestCase
1910
+ class Post < ActiveRecord::Base
1911
+ has_many :comments, inverse_of: :post
1912
+ end
1913
+
1914
+ class Comment < ActiveRecord::Base
1915
+ belongs_to :post, inverse_of: :comments
1916
+
1917
+ attr_accessor :post_comments_count
1918
+ after_save do
1919
+ self.post_comments_count = post.comments.count
1920
+ end
1921
+ end
1922
+
1923
+ def setup
1924
+ Comment.delete_all
1925
+ end
1926
+
1927
+ def test_after_save_callback_with_autosave
1928
+ post = Post.new(title: "Test", body: "...")
1929
+ comment = post.comments.build(body: "...")
1930
+ post.save!
1931
+
1932
+ assert_equal 1, post.comments.count
1933
+ assert_equal 1, comment.post_comments_count
1934
+ end
1935
+ end
1936
+
1937
+ class TestAutosaveAssociationOnAHasManyAssociationDefinedInSubclassWithAcceptsNestedAttributes < ActiveRecord::TestCase
1938
+ def test_should_update_children_when_association_redefined_in_subclass
1939
+ agency = Agency.create!(name: "Agency")
1940
+ valid_project = Project.create!(firm: agency, name: "Initial")
1941
+ agency.update!(
1942
+ "projects_attributes" => {
1943
+ "0" => {
1944
+ "name" => "Updated",
1945
+ "id" => valid_project.id
1946
+ }
1947
+ }
1948
+ )
1949
+ valid_project.reload
1950
+
1951
+ assert_equal "Updated", valid_project.name
1707
1952
  end
1708
1953
  end