ibm_db 5.6.1-arm64-darwin-24

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