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,1119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper"
4
+ require "models/pirate"
5
+ require "models/ship"
6
+ require "models/ship_part"
7
+ require "models/bird"
8
+ require "models/parrot"
9
+ require "models/treasure"
10
+ require "models/human"
11
+ require "models/interest"
12
+ require "models/owner"
13
+ require "models/pet"
14
+ require "active_support/hash_with_indifferent_access"
15
+
16
+ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
17
+ teardown do
18
+ Pirate.accepts_nested_attributes_for :ship, allow_destroy: true, reject_if: proc(&:empty?)
19
+ end
20
+
21
+ def test_base_should_have_an_empty_nested_attributes_options
22
+ assert_equal Hash.new, ActiveRecord::Base.nested_attributes_options
23
+ end
24
+
25
+ def test_should_add_a_proc_to_nested_attributes_options
26
+ assert_equal ActiveRecord::NestedAttributes::ClassMethods::REJECT_ALL_BLANK_PROC,
27
+ Pirate.nested_attributes_options[:birds_with_reject_all_blank][:reject_if]
28
+
29
+ [:parrots, :birds].each do |name|
30
+ assert_instance_of Proc, Pirate.nested_attributes_options[name][:reject_if]
31
+ end
32
+ end
33
+
34
+ def test_should_not_build_a_new_record_using_reject_all_even_if_destroy_is_given
35
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
36
+ pirate.birds_with_reject_all_blank_attributes = [{ name: "", color: "", _destroy: "0" }]
37
+ pirate.save!
38
+
39
+ assert_empty pirate.birds_with_reject_all_blank
40
+ end
41
+
42
+ def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
43
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
44
+ pirate.birds_with_reject_all_blank_attributes = [{ name: "", color: "" }]
45
+ pirate.save!
46
+
47
+ assert_empty pirate.birds_with_reject_all_blank
48
+ end
49
+
50
+ def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
51
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
52
+ pirate.birds_with_reject_all_blank_attributes = [{ name: "Tweetie", color: "" }]
53
+ pirate.save!
54
+
55
+ assert_equal 1, pirate.birds_with_reject_all_blank.count
56
+ assert_equal "Tweetie", pirate.birds_with_reject_all_blank.first.name
57
+ end
58
+
59
+ def test_should_raise_an_ArgumentError_for_non_existing_associations
60
+ exception = assert_raise ArgumentError do
61
+ Pirate.accepts_nested_attributes_for :honesty
62
+ end
63
+ assert_equal "No association found for name `honesty'. Has it been defined yet?", exception.message
64
+ end
65
+
66
+ def test_should_raise_an_UnknownAttributeError_for_non_existing_nested_attributes
67
+ exception = assert_raise ActiveModel::UnknownAttributeError do
68
+ Pirate.new(ship_attributes: { sail: true })
69
+ end
70
+ assert_match "unknown attribute 'sail' for Ship.", exception.message
71
+ end
72
+
73
+ def test_should_disable_allow_destroy_by_default
74
+ Pirate.accepts_nested_attributes_for :ship
75
+
76
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
77
+ ship = pirate.create_ship(name: "Nights Dirty Lightning")
78
+
79
+ pirate.update(ship_attributes: { "_destroy" => true, :id => ship.id })
80
+
81
+ assert_nothing_raised { pirate.ship.reload }
82
+ end
83
+
84
+ def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction
85
+ ship = Ship.create!(name: "Nights Dirty Lightning")
86
+ assert_not ship._destroy
87
+ ship.mark_for_destruction
88
+ assert ship._destroy
89
+ end
90
+
91
+ def test_reject_if_method_without_arguments
92
+ Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record?
93
+
94
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
95
+ pirate.ship_attributes = { name: "Black Pearl" }
96
+ assert_no_difference("Ship.count") { pirate.save! }
97
+ end
98
+
99
+ def test_reject_if_method_with_arguments
100
+ Pirate.accepts_nested_attributes_for :ship, reject_if: :reject_empty_ships_on_create
101
+
102
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
103
+ pirate.ship_attributes = { name: "Red Pearl", _reject_me_if_new: true }
104
+ assert_no_difference("Ship.count") { pirate.save! }
105
+
106
+ # pirate.reject_empty_ships_on_create returns false for saved pirate records
107
+ # in the previous step note that pirate gets saved but ship fails
108
+ pirate.ship_attributes = { name: "Red Pearl", _reject_me_if_new: true }
109
+ assert_difference("Ship.count") { pirate.save! }
110
+ end
111
+
112
+ def test_reject_if_with_indifferent_keys
113
+ Pirate.accepts_nested_attributes_for :ship, reject_if: proc { |attributes| attributes[:name].blank? }
114
+
115
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
116
+ pirate.ship_attributes = { name: "Hello Pearl" }
117
+ assert_difference("Ship.count") { pirate.save! }
118
+ end
119
+
120
+ def test_reject_if_with_a_proc_which_returns_true_always_for_has_one
121
+ Pirate.accepts_nested_attributes_for :ship, reject_if: proc { |attributes| true }
122
+ pirate = Pirate.create(catchphrase: "Stop wastin' me time")
123
+ ship = pirate.create_ship(name: "s1")
124
+ pirate.update(ship_attributes: { name: "s2", id: ship.id })
125
+ assert_equal "s1", ship.reload.name
126
+ end
127
+
128
+ def test_reuse_already_built_new_record
129
+ pirate = Pirate.new
130
+ ship_built_first = pirate.build_ship
131
+ pirate.ship_attributes = { name: "Ship 1" }
132
+ assert_same ship_built_first, pirate.ship
133
+ end
134
+
135
+ def test_do_not_allow_assigning_foreign_key_when_reusing_existing_new_record
136
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
137
+ pirate.build_ship
138
+ pirate.ship_attributes = { name: "Ship 1", pirate_id: pirate.id + 1 }
139
+ assert_equal pirate.id, pirate.ship.pirate_id
140
+ end
141
+
142
+ def test_reject_if_with_a_proc_which_returns_true_always_for_has_many
143
+ Human.accepts_nested_attributes_for :interests, reject_if: proc { |attributes| true }
144
+ human = Human.create(name: "John")
145
+ interest = human.interests.create(topic: "photography")
146
+ human.update(interests_attributes: { topic: "gardening", id: interest.id })
147
+ assert_equal "photography", interest.reload.topic
148
+ end
149
+
150
+ def test_destroy_works_independent_of_reject_if
151
+ Human.accepts_nested_attributes_for :interests, reject_if: proc { |attributes| true }, allow_destroy: true
152
+ human = Human.create(name: "Jon")
153
+ interest = human.interests.create(topic: "the ladies")
154
+ human.update(interests_attributes: { _destroy: "1", id: interest.id })
155
+ assert_empty human.reload.interests
156
+ end
157
+
158
+ def test_reject_if_is_not_short_circuited_if_allow_destroy_is_false
159
+ Pirate.accepts_nested_attributes_for :ship, reject_if: ->(a) { a[:name] == "The Golden Hind" }, allow_destroy: false
160
+
161
+ pirate = Pirate.create!(catchphrase: "Stop wastin' me time", ship_attributes: { name: "White Pearl", _destroy: "1" })
162
+ assert_equal "White Pearl", pirate.reload.ship.name
163
+
164
+ pirate.update!(ship_attributes: { id: pirate.ship.id, name: "The Golden Hind", _destroy: "1" })
165
+ assert_equal "White Pearl", pirate.reload.ship.name
166
+
167
+ pirate.update!(ship_attributes: { id: pirate.ship.id, name: "Black Pearl", _destroy: "1" })
168
+ assert_equal "Black Pearl", pirate.reload.ship.name
169
+ end
170
+
171
+ def test_has_many_association_updating_a_single_record
172
+ Human.accepts_nested_attributes_for(:interests)
173
+ human = Human.create(name: "John")
174
+ interest = human.interests.create(topic: "photography")
175
+ human.update(interests_attributes: { topic: "gardening", id: interest.id })
176
+ assert_equal "gardening", interest.reload.topic
177
+ end
178
+
179
+ def test_reject_if_with_blank_nested_attributes_id
180
+ # When using a select list to choose an existing 'ship' id, with include_blank: true
181
+ Pirate.accepts_nested_attributes_for :ship, reject_if: proc { |attributes| attributes[:id].blank? }
182
+
183
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
184
+ pirate.ship_attributes = { id: "" }
185
+ assert_nothing_raised { pirate.save! }
186
+ end
187
+
188
+ def test_first_and_array_index_zero_methods_return_the_same_value_when_nested_attributes_are_set_to_update_existing_record
189
+ Human.accepts_nested_attributes_for(:interests)
190
+ human = Human.create(name: "John")
191
+ interest = human.interests.create topic: "gardening"
192
+ human = Human.find human.id
193
+ human.interests_attributes = [{ id: interest.id, topic: "gardening" }]
194
+ assert_equal human.interests.first.topic, human.interests[0].topic
195
+ end
196
+
197
+ def test_allows_class_to_override_setter_and_call_super
198
+ mean_pirate_class = Class.new(Pirate) do
199
+ accepts_nested_attributes_for :parrot
200
+ def parrot_attributes=(attrs)
201
+ super(attrs.merge(color: "blue"))
202
+ end
203
+ end
204
+ mean_pirate = mean_pirate_class.new
205
+ mean_pirate.parrot_attributes = { name: "James" }
206
+ assert_equal "James", mean_pirate.parrot.name
207
+ assert_equal "blue", mean_pirate.parrot.color
208
+ end
209
+
210
+ def test_accepts_nested_attributes_for_can_be_overridden_in_subclasses
211
+ Pirate.accepts_nested_attributes_for(:parrot)
212
+
213
+ mean_pirate_class = Class.new(Pirate) do
214
+ accepts_nested_attributes_for :parrot
215
+ end
216
+ mean_pirate = mean_pirate_class.new
217
+ mean_pirate.parrot_attributes = { name: "James" }
218
+ assert_equal "James", mean_pirate.parrot.name
219
+ end
220
+
221
+ def test_should_not_create_duplicates_with_create_with
222
+ Human.accepts_nested_attributes_for(:interests)
223
+
224
+ assert_difference("Interest.count", 1) do
225
+ Human.create_with(
226
+ interests_attributes: [{ topic: "Pirate king" }]
227
+ ).find_or_create_by!(
228
+ name: "Monkey D. Luffy"
229
+ )
230
+ end
231
+ end
232
+ end
233
+
234
+ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
235
+ def setup
236
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
237
+ @ship = @pirate.create_ship(name: "Nights Dirty Lightning")
238
+ end
239
+
240
+ def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to
241
+ exception = assert_raise ArgumentError do
242
+ Treasure.new(name: "pearl", looter_attributes: { catchphrase: "Arrr" })
243
+ end
244
+ assert_equal "Cannot build association `looter'. Are you trying to build a polymorphic one-to-one association?", exception.message
245
+ end
246
+
247
+ def test_should_define_an_attribute_writer_method_for_the_association
248
+ assert_respond_to @pirate, :ship_attributes=
249
+ end
250
+
251
+ def test_should_build_a_new_record_if_there_is_no_id
252
+ @ship.destroy
253
+ @pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger" }
254
+
255
+ assert_not_predicate @pirate.ship, :persisted?
256
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
257
+ end
258
+
259
+ def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
260
+ @ship.destroy
261
+ @pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger", _destroy: "1" }
262
+
263
+ assert_nil @pirate.ship
264
+ end
265
+
266
+ def test_should_not_build_a_new_record_if_a_reject_if_proc_returns_false
267
+ @ship.destroy
268
+ @pirate.reload.ship_attributes = {}
269
+
270
+ assert_nil @pirate.ship
271
+ end
272
+
273
+ def test_should_replace_an_existing_record_if_there_is_no_id
274
+ @pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger" }
275
+
276
+ assert_not_predicate @pirate.ship, :persisted?
277
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
278
+ assert_equal "Nights Dirty Lightning", @ship.name
279
+ end
280
+
281
+ def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
282
+ @pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger", _destroy: "1" }
283
+
284
+ assert_equal @ship, @pirate.ship
285
+ assert_equal "Nights Dirty Lightning", @pirate.ship.name
286
+ end
287
+
288
+ def test_should_modify_an_existing_record_if_there_is_a_matching_id
289
+ @pirate.reload.ship_attributes = { id: @ship.id, name: "Davy Jones Gold Dagger" }
290
+
291
+ assert_equal @ship, @pirate.ship
292
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
293
+ end
294
+
295
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
296
+ exception = assert_raise ActiveRecord::RecordNotFound do
297
+ @pirate.ship_attributes = { id: 1234567890 }
298
+ end
299
+ assert_equal "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}", exception.message
300
+ end
301
+
302
+ def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
303
+ @pirate.reload.ship_attributes = { "id" => @ship.id, "name" => "Davy Jones Gold Dagger" }
304
+
305
+ assert_equal @ship, @pirate.ship
306
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
307
+ end
308
+
309
+ def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
310
+ @ship.stub(:id, "ABC1X") do
311
+ @pirate.ship_attributes = { id: @ship.id, name: "Davy Jones Gold Dagger" }
312
+
313
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
314
+ end
315
+ end
316
+
317
+ def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
318
+ @pirate.ship.destroy
319
+
320
+ [1, "1", true, "true"].each do |truth|
321
+ ship = @pirate.reload.create_ship(name: "Mister Pablo")
322
+ @pirate.update(ship_attributes: { id: ship.id, _destroy: truth })
323
+
324
+ assert_nil @pirate.reload.ship
325
+ assert_raise(ActiveRecord::RecordNotFound) { Ship.find(ship.id) }
326
+ end
327
+ end
328
+
329
+ def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
330
+ [nil, "0", 0, "false", false].each do |not_truth|
331
+ @pirate.update(ship_attributes: { id: @pirate.ship.id, _destroy: not_truth })
332
+
333
+ assert_equal @ship, @pirate.reload.ship
334
+ end
335
+ end
336
+
337
+ def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
338
+ Pirate.accepts_nested_attributes_for :ship, allow_destroy: false, reject_if: proc(&:empty?)
339
+
340
+ @pirate.update(ship_attributes: { id: @pirate.ship.id, _destroy: "1" })
341
+
342
+ assert_equal @ship, @pirate.reload.ship
343
+
344
+ Pirate.accepts_nested_attributes_for :ship, allow_destroy: true, reject_if: proc(&:empty?)
345
+ end
346
+
347
+ def test_should_also_work_with_a_HashWithIndifferentAccess
348
+ @pirate.ship_attributes = ActiveSupport::HashWithIndifferentAccess.new(id: @ship.id, name: "Davy Jones Gold Dagger")
349
+
350
+ assert_predicate @pirate.ship, :persisted?
351
+ assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
352
+ end
353
+
354
+ def test_should_work_with_update_as_well
355
+ @pirate.update(catchphrase: "Arr", ship_attributes: { id: @ship.id, name: "Mister Pablo" })
356
+ @pirate.reload
357
+
358
+ assert_equal "Arr", @pirate.catchphrase
359
+ assert_equal "Mister Pablo", @pirate.ship.name
360
+ end
361
+
362
+ def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
363
+ @pirate.attributes = { ship_attributes: { id: @ship.id, _destroy: "1" } }
364
+
365
+ assert_not_predicate @pirate.ship, :destroyed?
366
+ assert_predicate @pirate.ship, :marked_for_destruction?
367
+
368
+ @pirate.save
369
+
370
+ assert_predicate @pirate.ship, :destroyed?
371
+ assert_nil @pirate.reload.ship
372
+ end
373
+
374
+ def test_should_automatically_enable_autosave_on_the_association
375
+ assert Pirate.reflect_on_association(:ship).options[:autosave]
376
+ end
377
+
378
+ def test_should_accept_update_only_option
379
+ @pirate.update(update_only_ship_attributes: { id: @pirate.ship.id, name: "Mayflower" })
380
+ end
381
+
382
+ def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
383
+ @ship.delete
384
+
385
+ @pirate.reload.update(update_only_ship_attributes: { name: "Mayflower" })
386
+
387
+ assert_not_nil @pirate.ship
388
+ end
389
+
390
+ def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
391
+ @ship.delete
392
+ @ship = @pirate.create_update_only_ship(name: "Nights Dirty Lightning")
393
+
394
+ @pirate.update(update_only_ship_attributes: { name: "Mayflower" })
395
+
396
+ assert_equal "Mayflower", @ship.reload.name
397
+ assert_equal @ship, @pirate.reload.ship
398
+ end
399
+
400
+ def test_should_update_existing_when_update_only_is_true_and_id_is_given
401
+ @ship.delete
402
+ @ship = @pirate.create_update_only_ship(name: "Nights Dirty Lightning")
403
+
404
+ @pirate.update(update_only_ship_attributes: { name: "Mayflower", id: @ship.id })
405
+
406
+ assert_equal "Mayflower", @ship.reload.name
407
+ assert_equal @ship, @pirate.reload.ship
408
+ end
409
+
410
+ def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction
411
+ Pirate.accepts_nested_attributes_for :update_only_ship, update_only: true, allow_destroy: true
412
+ @ship.delete
413
+ @ship = @pirate.create_update_only_ship(name: "Nights Dirty Lightning")
414
+
415
+ @pirate.update(update_only_ship_attributes: { name: "Mayflower", id: @ship.id, _destroy: true })
416
+
417
+ assert_nil @pirate.reload.ship
418
+ assert_raise(ActiveRecord::RecordNotFound) { Ship.find(@ship.id) }
419
+
420
+ Pirate.accepts_nested_attributes_for :update_only_ship, update_only: true, allow_destroy: false
421
+ end
422
+ end
423
+
424
+ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
425
+ def setup
426
+ @ship = Ship.new(name: "Nights Dirty Lightning")
427
+ @pirate = @ship.build_pirate(catchphrase: "Aye")
428
+ @ship.save!
429
+ end
430
+
431
+ def test_should_define_an_attribute_writer_method_for_the_association
432
+ assert_respond_to @ship, :pirate_attributes=
433
+ end
434
+
435
+ def test_should_build_a_new_record_if_there_is_no_id
436
+ @pirate.destroy
437
+ @ship.reload.pirate_attributes = { catchphrase: "Arr" }
438
+
439
+ assert_not_predicate @ship.pirate, :persisted?
440
+ assert_equal "Arr", @ship.pirate.catchphrase
441
+ end
442
+
443
+ def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
444
+ @pirate.destroy
445
+ @ship.reload.pirate_attributes = { catchphrase: "Arr", _destroy: "1" }
446
+
447
+ assert_nil @ship.pirate
448
+ end
449
+
450
+ def test_should_not_build_a_new_record_if_a_reject_if_proc_returns_false
451
+ @pirate.destroy
452
+ @ship.reload.pirate_attributes = {}
453
+
454
+ assert_nil @ship.pirate
455
+ end
456
+
457
+ def test_should_replace_an_existing_record_if_there_is_no_id
458
+ @ship.reload.pirate_attributes = { catchphrase: "Arr" }
459
+
460
+ assert_not_predicate @ship.pirate, :persisted?
461
+ assert_equal "Arr", @ship.pirate.catchphrase
462
+ assert_equal "Aye", @pirate.catchphrase
463
+ end
464
+
465
+ def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
466
+ @ship.reload.pirate_attributes = { catchphrase: "Arr", _destroy: "1" }
467
+
468
+ assert_equal @pirate, @ship.pirate
469
+ assert_equal "Aye", @ship.pirate.catchphrase
470
+ end
471
+
472
+ def test_should_modify_an_existing_record_if_there_is_a_matching_id
473
+ @ship.reload.pirate_attributes = { id: @pirate.id, catchphrase: "Arr" }
474
+
475
+ assert_equal @pirate, @ship.pirate
476
+ assert_equal "Arr", @ship.pirate.catchphrase
477
+ end
478
+
479
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
480
+ exception = assert_raise ActiveRecord::RecordNotFound do
481
+ @ship.pirate_attributes = { id: 1234567890 }
482
+ end
483
+ assert_equal "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}", exception.message
484
+ end
485
+
486
+ def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
487
+ @ship.reload.pirate_attributes = { "id" => @pirate.id, "catchphrase" => "Arr" }
488
+
489
+ assert_equal @pirate, @ship.pirate
490
+ assert_equal "Arr", @ship.pirate.catchphrase
491
+ end
492
+
493
+ def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
494
+ @pirate.stub(:id, "ABC1X") do
495
+ @ship.pirate_attributes = { id: @pirate.id, catchphrase: "Arr" }
496
+
497
+ assert_equal "Arr", @ship.pirate.catchphrase
498
+ end
499
+ end
500
+
501
+ def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
502
+ @ship.pirate.destroy
503
+ [1, "1", true, "true"].each do |truth|
504
+ pirate = @ship.reload.create_pirate(catchphrase: "Arr")
505
+ @ship.update(pirate_attributes: { id: pirate.id, _destroy: truth })
506
+ assert_raise(ActiveRecord::RecordNotFound) { pirate.reload }
507
+ end
508
+ end
509
+
510
+ def test_should_unset_association_when_an_existing_record_is_destroyed
511
+ original_pirate_id = @ship.pirate.id
512
+ @ship.update! pirate_attributes: { id: @ship.pirate.id, _destroy: true }
513
+
514
+ assert_empty Pirate.where(id: original_pirate_id)
515
+ assert_nil @ship.pirate_id
516
+ assert_nil @ship.pirate
517
+
518
+ @ship.reload
519
+ assert_empty Pirate.where(id: original_pirate_id)
520
+ assert_nil @ship.pirate_id
521
+ assert_nil @ship.pirate
522
+ end
523
+
524
+ def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
525
+ [nil, "0", 0, "false", false].each do |not_truth|
526
+ @ship.update(pirate_attributes: { id: @ship.pirate.id, _destroy: not_truth })
527
+ assert_nothing_raised { @ship.pirate.reload }
528
+ end
529
+ end
530
+
531
+ def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
532
+ Ship.accepts_nested_attributes_for :pirate, allow_destroy: false, reject_if: proc(&:empty?)
533
+
534
+ @ship.update(pirate_attributes: { id: @ship.pirate.id, _destroy: "1" })
535
+ assert_nothing_raised { @ship.pirate.reload }
536
+ ensure
537
+ Ship.accepts_nested_attributes_for :pirate, allow_destroy: true, reject_if: proc(&:empty?)
538
+ end
539
+
540
+ def test_should_work_with_update_as_well
541
+ @ship.update(name: "Mister Pablo", pirate_attributes: { catchphrase: "Arr" })
542
+ @ship.reload
543
+
544
+ assert_equal "Mister Pablo", @ship.name
545
+ assert_equal "Arr", @ship.pirate.catchphrase
546
+ end
547
+
548
+ def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
549
+ pirate = @ship.pirate
550
+
551
+ @ship.attributes = { pirate_attributes: { :id => pirate.id, "_destroy" => true } }
552
+ assert_nothing_raised { Pirate.find(pirate.id) }
553
+ @ship.save
554
+ assert_raise(ActiveRecord::RecordNotFound) { Pirate.find(pirate.id) }
555
+ end
556
+
557
+ def test_should_automatically_enable_autosave_on_the_association
558
+ assert Ship.reflect_on_association(:pirate).options[:autosave]
559
+ end
560
+
561
+ def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
562
+ @pirate.delete
563
+ @ship.reload.attributes = { update_only_pirate_attributes: { catchphrase: "Arr" } }
564
+
565
+ assert_not_predicate @ship.update_only_pirate, :persisted?
566
+ end
567
+
568
+ def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
569
+ @pirate.delete
570
+ @pirate = @ship.create_update_only_pirate(catchphrase: "Aye")
571
+
572
+ @ship.update(update_only_pirate_attributes: { catchphrase: "Arr" })
573
+ assert_equal "Arr", @pirate.reload.catchphrase
574
+ assert_equal @pirate, @ship.reload.update_only_pirate
575
+ end
576
+
577
+ def test_should_update_existing_when_update_only_is_true_and_id_is_given
578
+ @pirate.delete
579
+ @pirate = @ship.create_update_only_pirate(catchphrase: "Aye")
580
+
581
+ @ship.update(update_only_pirate_attributes: { catchphrase: "Arr", id: @pirate.id })
582
+
583
+ assert_equal "Arr", @pirate.reload.catchphrase
584
+ assert_equal @pirate, @ship.reload.update_only_pirate
585
+ end
586
+
587
+ def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction
588
+ Ship.accepts_nested_attributes_for :update_only_pirate, update_only: true, allow_destroy: true
589
+ @pirate.delete
590
+ @pirate = @ship.create_update_only_pirate(catchphrase: "Aye")
591
+
592
+ @ship.update(update_only_pirate_attributes: { catchphrase: "Arr", id: @pirate.id, _destroy: true })
593
+
594
+ assert_raise(ActiveRecord::RecordNotFound) { @pirate.reload }
595
+
596
+ Ship.accepts_nested_attributes_for :update_only_pirate, update_only: true, allow_destroy: false
597
+ end
598
+ end
599
+
600
+ module NestedAttributesOnACollectionAssociationTests
601
+ def test_should_define_an_attribute_writer_method_for_the_association
602
+ assert_respond_to @pirate, association_setter
603
+ end
604
+
605
+ def test_should_raise_an_UnknownAttributeError_for_non_existing_nested_attributes_for_has_many
606
+ exception = assert_raise ActiveModel::UnknownAttributeError do
607
+ @pirate.parrots_attributes = [{ peg_leg: true }]
608
+ end
609
+ assert_match "unknown attribute 'peg_leg' for Parrot.", exception.message
610
+ end
611
+
612
+ def test_should_save_only_one_association_on_create
613
+ pirate = Pirate.create!(
614
+ :catchphrase => "Arr",
615
+ association_getter => { "foo" => { name: "Grace OMalley" } })
616
+
617
+ assert_equal 1, pirate.reload.public_send(@association_name).count
618
+ end
619
+
620
+ def test_should_take_a_hash_with_string_keys_and_assign_the_attributes_to_the_associated_models
621
+ @alternate_params[association_getter].stringify_keys!
622
+ @pirate.update @alternate_params
623
+ assert_equal ["Grace OMalley", "Privateers Greed"], [@child_1.reload.name, @child_2.reload.name]
624
+ end
625
+
626
+ def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
627
+ @pirate.public_send(association_setter, @alternate_params[association_getter].values)
628
+ @pirate.save
629
+ assert_equal ["Grace OMalley", "Privateers Greed"], [@child_1.reload.name, @child_2.reload.name]
630
+ end
631
+
632
+ def test_should_also_work_with_a_HashWithIndifferentAccess
633
+ @pirate.public_send(association_setter, ActiveSupport::HashWithIndifferentAccess.new("foo" => ActiveSupport::HashWithIndifferentAccess.new(id: @child_1.id, name: "Grace OMalley")))
634
+ @pirate.save
635
+ assert_equal "Grace OMalley", @child_1.reload.name
636
+ end
637
+
638
+ def test_should_take_a_hash_and_assign_the_attributes_to_the_associated_models
639
+ @pirate.attributes = @alternate_params
640
+ assert_equal "Grace OMalley", @pirate.public_send(@association_name).first.name
641
+ assert_equal "Privateers Greed", @pirate.public_send(@association_name).last.name
642
+ end
643
+
644
+ def test_should_not_load_association_when_updating_existing_records
645
+ @pirate.reload
646
+ @pirate.public_send(association_setter, [{ id: @child_1.id, name: "Grace OMalley" }])
647
+ assert_not_predicate @pirate.public_send(@association_name), :loaded?
648
+
649
+ @pirate.save
650
+ assert_not_predicate @pirate.public_send(@association_name), :loaded?
651
+ assert_equal "Grace OMalley", @child_1.reload.name
652
+ end
653
+
654
+ def test_should_not_overwrite_unsaved_updates_when_loading_association
655
+ @pirate.reload
656
+ @pirate.public_send(association_setter, [{ id: @child_1.id, name: "Grace OMalley" }])
657
+ assert_equal "Grace OMalley", @pirate.public_send(@association_name).load_target.find { |r| r.id == @child_1.id }.name
658
+ end
659
+
660
+ def test_should_preserve_order_when_not_overwriting_unsaved_updates
661
+ @pirate.reload
662
+ @pirate.public_send(association_setter, [{ id: @child_1.id, name: "Grace OMalley" }])
663
+ assert_equal @child_1.id, @pirate.public_send(@association_name).load_target.first.id
664
+ end
665
+
666
+ def test_should_refresh_saved_records_when_not_overwriting_unsaved_updates
667
+ @pirate.reload
668
+ record = @pirate.class.reflect_on_association(@association_name).klass.new(name: "Grace OMalley")
669
+ @pirate.public_send(@association_name) << record
670
+ record.save!
671
+ @pirate.public_send(@association_name).last.update!(name: "Polly")
672
+ assert_equal "Polly", @pirate.public_send(@association_name).load_target.last.name
673
+ end
674
+
675
+ def test_should_not_remove_scheduled_destroys_when_loading_association
676
+ @pirate.reload
677
+ @pirate.public_send(association_setter, [{ id: @child_1.id, _destroy: "1" }])
678
+ assert_predicate @pirate.public_send(@association_name).load_target.find { |r| r.id == @child_1.id }, :marked_for_destruction?
679
+ end
680
+
681
+ def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
682
+ @child_1.stub(:id, "ABC1X") do
683
+ @child_2.stub(:id, "ABC2X") do
684
+ @pirate.attributes = {
685
+ association_getter => [
686
+ { id: @child_1.id, name: "Grace OMalley" },
687
+ { id: @child_2.id, name: "Privateers Greed" }
688
+ ]
689
+ }
690
+
691
+ assert_equal ["Grace OMalley", "Privateers Greed"], [@child_1.name, @child_2.name]
692
+ end
693
+ end
694
+ end
695
+
696
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
697
+ exception = assert_raise ActiveRecord::RecordNotFound do
698
+ @pirate.attributes = { association_getter => [{ id: 1234567890 }] }
699
+ end
700
+ assert_equal "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}", exception.message
701
+ end
702
+
703
+ def test_should_raise_RecordNotFound_if_an_id_belonging_to_a_different_record_is_given
704
+ other_pirate = Pirate.create! catchphrase: "Ahoy!"
705
+ other_child = other_pirate.public_send(@association_name).create! name: "Buccaneers Servant"
706
+
707
+ exception = assert_raise ActiveRecord::RecordNotFound do
708
+ @pirate.attributes = { association_getter => [{ id: other_child.id }] }
709
+ end
710
+ assert_equal "Couldn't find #{@child_1.class.name} with ID=#{other_child.id} for Pirate with ID=#{@pirate.id}", exception.message
711
+ end
712
+
713
+ def test_should_automatically_build_new_associated_models_for_each_entry_in_a_hash_where_the_id_is_missing
714
+ @pirate.public_send(@association_name).destroy_all
715
+ @pirate.reload.attributes = {
716
+ association_getter => { "foo" => { name: "Grace OMalley" }, "bar" => { name: "Privateers Greed" } }
717
+ }
718
+
719
+ assert_not_predicate @pirate.public_send(@association_name).first, :persisted?
720
+ assert_equal "Grace OMalley", @pirate.public_send(@association_name).first.name
721
+
722
+ assert_not_predicate @pirate.public_send(@association_name).last, :persisted?
723
+ assert_equal "Privateers Greed", @pirate.public_send(@association_name).last.name
724
+ end
725
+
726
+ def test_should_not_assign_destroy_key_to_a_record
727
+ assert_nothing_raised do
728
+ @pirate.public_send(association_setter, "foo" => { "_destroy" => "0" })
729
+ end
730
+ end
731
+
732
+ def test_should_ignore_new_associated_records_with_truthy_destroy_attribute
733
+ @pirate.public_send(@association_name).destroy_all
734
+ @pirate.reload.attributes = {
735
+ association_getter => {
736
+ "foo" => { name: "Grace OMalley" },
737
+ "bar" => { :name => "Privateers Greed", "_destroy" => "1" }
738
+ }
739
+ }
740
+
741
+ assert_equal 1, @pirate.public_send(@association_name).length
742
+ assert_equal "Grace OMalley", @pirate.public_send(@association_name).first.name
743
+ end
744
+
745
+ def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false
746
+ @alternate_params[association_getter]["baz"] = {}
747
+ assert_no_difference("@pirate.public_send(@association_name).count") do
748
+ @pirate.attributes = @alternate_params
749
+ end
750
+ end
751
+
752
+ def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models
753
+ attributes = {}
754
+ attributes["123726353"] = { name: "Grace OMalley" }
755
+ attributes["2"] = { name: "Privateers Greed" } # 2 is lower then 123726353
756
+ @pirate.public_send(association_setter, attributes)
757
+
758
+ assert_equal ["Posideons Killer", "Killer bandita Dionne", "Privateers Greed", "Grace OMalley"].to_set, @pirate.public_send(@association_name).map(&:name).to_set
759
+ end
760
+
761
+ def test_should_raise_an_argument_error_if_something_else_than_a_hash_is_passed
762
+ assert_nothing_raised { @pirate.public_send(association_setter, {}) }
763
+ assert_nothing_raised { @pirate.public_send(association_setter, Hash.new) }
764
+
765
+ exception = assert_raise ArgumentError do
766
+ @pirate.public_send(association_setter, "foo")
767
+ end
768
+ assert_equal %{Hash or Array expected for attribute `#{@association_name}`, got String ("foo")}, exception.message
769
+ end
770
+
771
+ def test_should_work_with_update_as_well
772
+ @pirate.update(catchphrase: "Arr",
773
+ association_getter => { "foo" => { id: @child_1.id, name: "Grace OMalley" } })
774
+
775
+ assert_equal "Grace OMalley", @child_1.reload.name
776
+ end
777
+
778
+ def test_should_update_existing_records_and_add_new_ones_that_have_no_id
779
+ @alternate_params[association_getter]["baz"] = { name: "Buccaneers Servant" }
780
+ assert_difference("@pirate.public_send(@association_name).count", +1) do
781
+ @pirate.update @alternate_params
782
+ end
783
+ assert_equal ["Grace OMalley", "Privateers Greed", "Buccaneers Servant"].to_set, @pirate.reload.public_send(@association_name).map(&:name).to_set
784
+ end
785
+
786
+ def test_should_be_possible_to_destroy_a_record
787
+ ["1", 1, "true", true].each do |true_variable|
788
+ record = @pirate.reload.public_send(@association_name).create!(name: "Grace OMalley")
789
+ @pirate.public_send(association_setter,
790
+ @alternate_params[association_getter].merge("baz" => { :id => record.id, "_destroy" => true_variable })
791
+ )
792
+
793
+ assert_difference("@pirate.public_send(@association_name).count", -1) do
794
+ @pirate.save
795
+ end
796
+ end
797
+ end
798
+
799
+ def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument
800
+ [nil, "", "0", 0, "false", false].each do |false_variable|
801
+ @alternate_params[association_getter]["foo"]["_destroy"] = false_variable
802
+ assert_no_difference("@pirate.public_send(@association_name).count") do
803
+ @pirate.update(@alternate_params)
804
+ end
805
+ end
806
+ end
807
+
808
+ def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
809
+ assert_no_difference("@pirate.public_send(@association_name).count") do
810
+ @pirate.public_send(association_setter, @alternate_params[association_getter].merge("baz" => { :id => @child_1.id, "_destroy" => true }))
811
+ end
812
+ assert_difference("@pirate.public_send(@association_name).count", -1) { @pirate.save }
813
+ end
814
+
815
+ def test_should_automatically_enable_autosave_on_the_association
816
+ assert Pirate.reflect_on_association(@association_name).options[:autosave]
817
+ end
818
+
819
+ def test_validate_presence_of_parent_works_with_inverse_of
820
+ Human.accepts_nested_attributes_for(:interests)
821
+ assert_equal :human, Human.reflect_on_association(:interests).options[:inverse_of]
822
+ assert_equal :interests, Interest.reflect_on_association(:human).options[:inverse_of]
823
+
824
+ repair_validations(Interest) do
825
+ Interest.validates_presence_of(:human)
826
+ assert_difference "Human.count" do
827
+ assert_difference "Interest.count", 2 do
828
+ human = Human.create!(name: "John",
829
+ interests_attributes: [{ topic: "Cars" }, { topic: "Sports" }])
830
+ assert_equal 2, human.interests.count
831
+ end
832
+ end
833
+ end
834
+ end
835
+
836
+ def test_can_use_symbols_as_object_identifier
837
+ @pirate.attributes = { parrots_attributes: { foo: { name: "Lovely Day" }, bar: { name: "Blown Away" } } }
838
+ assert_nothing_raised { @pirate.save! }
839
+ end
840
+
841
+ def test_numeric_column_changes_from_zero_to_no_empty_string
842
+ Human.accepts_nested_attributes_for(:interests)
843
+
844
+ repair_validations(Interest) do
845
+ Interest.validates_numericality_of(:zine_id)
846
+ human = Human.create(name: "John")
847
+ interest = human.interests.create(topic: "bar", zine_id: 0)
848
+ assert interest.save
849
+ assert_not human.update(interests_attributes: { id: interest.id, zine_id: "foo" })
850
+ end
851
+ end
852
+
853
+ private
854
+ def association_setter
855
+ @association_setter ||= "#{@association_name}_attributes=".to_sym
856
+ end
857
+
858
+ def association_getter
859
+ @association_getter ||= "#{@association_name}_attributes".to_sym
860
+ end
861
+ end
862
+
863
+ class TestNestedAttributesOnAHasManyAssociation < ActiveRecord::TestCase
864
+ def setup
865
+ @association_type = :has_many
866
+ @association_name = :birds
867
+
868
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
869
+ @pirate.birds.create!(name: "Posideons Killer")
870
+ @pirate.birds.create!(name: "Killer bandita Dionne")
871
+
872
+ @child_1, @child_2 = @pirate.birds
873
+
874
+ @alternate_params = {
875
+ birds_attributes: {
876
+ "foo" => { id: @child_1.id, name: "Grace OMalley" },
877
+ "bar" => { id: @child_2.id, name: "Privateers Greed" }
878
+ }
879
+ }
880
+ end
881
+
882
+ include NestedAttributesOnACollectionAssociationTests
883
+ end
884
+
885
+ class TestNestedAttributesOnAHasAndBelongsToManyAssociation < ActiveRecord::TestCase
886
+ def setup
887
+ @association_type = :has_and_belongs_to_many
888
+ @association_name = :parrots
889
+
890
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
891
+ @pirate.parrots.create!(name: "Posideons Killer")
892
+ @pirate.parrots.create!(name: "Killer bandita Dionne")
893
+
894
+ @child_1, @child_2 = @pirate.parrots
895
+
896
+ @alternate_params = {
897
+ parrots_attributes: {
898
+ "foo" => { id: @child_1.id, name: "Grace OMalley" },
899
+ "bar" => { id: @child_2.id, name: "Privateers Greed" }
900
+ }
901
+ }
902
+ end
903
+
904
+ include NestedAttributesOnACollectionAssociationTests
905
+ end
906
+
907
+ module NestedAttributesLimitTests
908
+ def teardown
909
+ Pirate.accepts_nested_attributes_for :parrots, allow_destroy: true, reject_if: proc(&:empty?)
910
+ end
911
+
912
+ def test_limit_with_less_records
913
+ @pirate.attributes = { parrots_attributes: { "foo" => { name: "Big Big Love" } } }
914
+ assert_difference("Parrot.count") { @pirate.save! }
915
+ end
916
+
917
+ def test_limit_with_number_exact_records
918
+ @pirate.attributes = { parrots_attributes: { "foo" => { name: "Lovely Day" }, "bar" => { name: "Blown Away" } } }
919
+ assert_difference("Parrot.count", 2) { @pirate.save! }
920
+ end
921
+
922
+ def test_limit_with_exceeding_records
923
+ assert_raises(ActiveRecord::NestedAttributes::TooManyRecords) do
924
+ @pirate.attributes = { parrots_attributes: { "foo" => { name: "Lovely Day" },
925
+ "bar" => { name: "Blown Away" },
926
+ "car" => { name: "The Happening" } } }
927
+ end
928
+ end
929
+ end
930
+
931
+ class TestNestedAttributesLimitNumeric < ActiveRecord::TestCase
932
+ def setup
933
+ Pirate.accepts_nested_attributes_for :parrots, limit: 2
934
+
935
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
936
+ end
937
+
938
+ include NestedAttributesLimitTests
939
+ end
940
+
941
+ class TestNestedAttributesLimitSymbol < ActiveRecord::TestCase
942
+ def setup
943
+ Pirate.accepts_nested_attributes_for :parrots, limit: :parrots_limit
944
+
945
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?", parrots_limit: 2)
946
+ end
947
+
948
+ include NestedAttributesLimitTests
949
+ end
950
+
951
+ class TestNestedAttributesLimitProc < ActiveRecord::TestCase
952
+ def setup
953
+ Pirate.accepts_nested_attributes_for :parrots, limit: proc { 2 }
954
+
955
+ @pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
956
+ end
957
+
958
+ include NestedAttributesLimitTests
959
+ end
960
+
961
+ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase
962
+ fixtures :owners, :pets
963
+
964
+ def setup
965
+ Owner.accepts_nested_attributes_for :pets, allow_destroy: true
966
+
967
+ @owner = owners(:ashley)
968
+ @pet1, @pet2 = pets(:chew), pets(:mochi)
969
+
970
+ @params = {
971
+ pets_attributes: {
972
+ "0" => { id: @pet1.id, name: "Foo" },
973
+ "1" => { id: @pet2.id, name: "Bar" }
974
+ }
975
+ }
976
+ end
977
+
978
+ def test_should_update_existing_records_with_non_standard_primary_key
979
+ @owner.update(@params)
980
+ assert_equal ["Foo", "Bar"], @owner.pets.map(&:name)
981
+ end
982
+
983
+ def test_attr_accessor_of_child_should_be_value_provided_during_update
984
+ @owner = owners(:ashley)
985
+ @pet1 = pets(:chew)
986
+ attributes = { pets_attributes: { "1" => { id: @pet1.id,
987
+ name: "Foo2",
988
+ current_user: "John",
989
+ _destroy: true } } }
990
+ @owner.update(attributes)
991
+ assert_equal "John", Pet.after_destroy_output
992
+ end
993
+ end
994
+
995
+ class TestHasOneAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
996
+ self.use_transactional_tests = false unless supports_savepoints?
997
+
998
+ def setup
999
+ @pirate = Pirate.create!(catchphrase: "My baby takes tha mornin' train!")
1000
+ @ship = @pirate.create_ship(name: "The good ship Dollypop")
1001
+ @part = @ship.parts.create!(name: "Mast")
1002
+ @trinket = @part.trinkets.create!(name: "Necklace")
1003
+ end
1004
+
1005
+ test "when great-grandchild changed in memory, saving parent should save great-grandchild" do
1006
+ @trinket.name = "changed"
1007
+ @pirate.save
1008
+ assert_equal "changed", @trinket.reload.name
1009
+ end
1010
+
1011
+ test "when great-grandchild changed via attributes, saving parent should save great-grandchild" do
1012
+ @pirate.attributes = { ship_attributes: { id: @ship.id, parts_attributes: [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, name: "changed" }] }] } }
1013
+ @pirate.save
1014
+ assert_equal "changed", @trinket.reload.name
1015
+ end
1016
+
1017
+ test "when great-grandchild marked_for_destruction via attributes, saving parent should destroy great-grandchild" do
1018
+ @pirate.attributes = { ship_attributes: { id: @ship.id, parts_attributes: [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, _destroy: true }] }] } }
1019
+ assert_difference("@part.trinkets.count", -1) { @pirate.save }
1020
+ end
1021
+
1022
+ test "when great-grandchild added via attributes, saving parent should create great-grandchild" do
1023
+ @pirate.attributes = { ship_attributes: { id: @ship.id, parts_attributes: [{ id: @part.id, trinkets_attributes: [{ name: "created" }] }] } }
1024
+ assert_difference("@part.trinkets.count", 1) { @pirate.save }
1025
+ end
1026
+
1027
+ test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
1028
+ @trinket.name = "changed"
1029
+ Ship.create!(pirate: @pirate, name: "The Black Rock")
1030
+ ShipPart.create!(ship: @ship, name: "Stern")
1031
+ assert_no_queries { @pirate.valid? }
1032
+ end
1033
+ end
1034
+
1035
+ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
1036
+ self.use_transactional_tests = false unless supports_savepoints?
1037
+
1038
+ def setup
1039
+ @ship = Ship.create!(name: "The good ship Dollypop")
1040
+ @part = @ship.parts.create!(name: "Mast")
1041
+ @trinket = @part.trinkets.create!(name: "Necklace")
1042
+ end
1043
+
1044
+ test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do
1045
+ @ship.parts_attributes = [{ id: @part.id, name: "Deck" }]
1046
+ assert_equal 1, @ship.association(:parts).target.size
1047
+ assert_equal "Deck", @ship.parts[0].name
1048
+ end
1049
+
1050
+ test "if association is not loaded and child doesn't change and I am saving a grandchild then in memory record should be used" do
1051
+ @ship.parts_attributes = [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, name: "Ruby" }] }]
1052
+ assert_equal 1, @ship.association(:parts).target.size
1053
+ assert_equal "Mast", @ship.parts[0].name
1054
+ assert_no_difference("@ship.parts[0].association(:trinkets).target.size") do
1055
+ @ship.parts[0].association(:trinkets).target.size
1056
+ end
1057
+ assert_equal "Ruby", @ship.parts[0].trinkets[0].name
1058
+ @ship.save
1059
+ assert_equal "Ruby", @ship.parts[0].trinkets[0].name
1060
+ end
1061
+
1062
+ test "when grandchild changed in memory, saving parent should save grandchild" do
1063
+ @trinket.name = "changed"
1064
+ @ship.save
1065
+ assert_equal "changed", @trinket.reload.name
1066
+ end
1067
+
1068
+ test "when grandchild changed via attributes, saving parent should save grandchild" do
1069
+ @ship.attributes = { parts_attributes: [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, name: "changed" }] }] }
1070
+ @ship.save
1071
+ assert_equal "changed", @trinket.reload.name
1072
+ end
1073
+
1074
+ test "when grandchild marked_for_destruction via attributes, saving parent should destroy grandchild" do
1075
+ @ship.attributes = { parts_attributes: [{ id: @part.id, trinkets_attributes: [{ id: @trinket.id, _destroy: true }] }] }
1076
+ assert_difference("@part.trinkets.count", -1) { @ship.save }
1077
+ end
1078
+
1079
+ test "when grandchild added via attributes, saving parent should create grandchild" do
1080
+ @ship.attributes = { parts_attributes: [{ id: @part.id, trinkets_attributes: [{ name: "created" }] }] }
1081
+ assert_difference("@part.trinkets.count", 1) { @ship.save }
1082
+ end
1083
+
1084
+ test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
1085
+ @trinket.name = "changed"
1086
+ Ship.create!(name: "The Black Rock")
1087
+ ShipPart.create!(ship: @ship, name: "Stern")
1088
+ assert_no_queries { @ship.valid? }
1089
+ end
1090
+
1091
+ test "circular references do not perform unnecessary queries" do
1092
+ ship = Ship.new(name: "The Black Rock")
1093
+ part = ship.parts.build(name: "Stern")
1094
+ ship.treasures.build(looter: part)
1095
+
1096
+ assert_queries 3 do
1097
+ ship.save!
1098
+ end
1099
+ end
1100
+
1101
+ test "nested singular associations are validated" do
1102
+ part = ShipPart.new(name: "Stern", ship_attributes: { name: nil })
1103
+
1104
+ assert_not_predicate part, :valid?
1105
+ assert_equal ["Ship name can't be blank"], part.errors.full_messages
1106
+ end
1107
+ end
1108
+
1109
+ class TestNestedAttributesWithExtend < ActiveRecord::TestCase
1110
+ setup do
1111
+ Pirate.accepts_nested_attributes_for :treasures
1112
+ end
1113
+
1114
+ def test_extend_affects_nested_attributes
1115
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
1116
+ pirate.treasures_attributes = [{ id: nil }]
1117
+ assert_equal "from extension", pirate.treasures[0].name
1118
+ end
1119
+ end