ibm_db 4.0.0-x86-mingw32 → 5.1.0-x86-mingw32

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