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