ibm_db 3.0.0-x86-mingw32 → 3.0.1-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 (465) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +4 -0
  3. data/MANIFEST +14 -14
  4. data/README +225 -225
  5. data/ext/Makefile.nt32 +181 -181
  6. data/ext/Makefile.nt32.191 +212 -212
  7. data/ext/OLD/extconf.rb +264 -0
  8. data/ext/{extconf_MacOS.rb → OLD/extconf_MacOS.rb} +269 -269
  9. data/ext/extconf.rb +291 -264
  10. data/ext/ibm_db.c +2 -2
  11. data/ext/ruby_ibm_db.h +241 -241
  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 +4 -4
  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 +115 -115
  19. data/test/active_record/connection_adapters/fake_adapter.rb +46 -0
  20. data/test/assets/example.log +1 -0
  21. data/test/assets/flowers.jpg +0 -0
  22. data/test/assets/test.txt +1 -0
  23. data/test/cases/adapter_test.rb +261 -207
  24. data/test/cases/aggregations_test.rb +158 -0
  25. data/test/cases/ar_schema_test.rb +161 -0
  26. data/test/cases/associations/association_scope_test.rb +21 -0
  27. data/test/cases/associations/belongs_to_associations_test.rb +1029 -711
  28. data/test/cases/associations/callbacks_test.rb +192 -0
  29. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -181
  30. data/test/cases/associations/deprecated_counter_cache_on_has_many_through_test.rb +26 -0
  31. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  32. data/test/cases/associations/eager_load_nested_include_test.rb +128 -0
  33. data/test/cases/associations/eager_singularization_test.rb +148 -0
  34. data/test/cases/associations/eager_test.rb +1411 -0
  35. data/test/cases/associations/extension_test.rb +82 -0
  36. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +932 -851
  37. data/test/cases/associations/has_many_associations_test.rb +2162 -0
  38. data/test/cases/associations/has_many_through_associations_test.rb +1204 -0
  39. data/test/cases/associations/has_one_associations_test.rb +610 -0
  40. data/test/cases/associations/has_one_through_associations_test.rb +380 -0
  41. data/test/cases/associations/inner_join_association_test.rb +139 -0
  42. data/test/cases/associations/inverse_associations_test.rb +693 -0
  43. data/test/cases/associations/join_model_test.rb +754 -743
  44. data/test/cases/associations/nested_through_associations_test.rb +579 -0
  45. data/test/cases/associations/required_test.rb +82 -0
  46. data/test/cases/associations_test.rb +380 -0
  47. data/test/cases/attribute_decorators_test.rb +125 -0
  48. data/test/cases/attribute_methods/read_test.rb +60 -0
  49. data/test/cases/attribute_methods/serialization_test.rb +29 -0
  50. data/test/cases/attribute_methods_test.rb +952 -822
  51. data/test/cases/attribute_set_test.rb +200 -0
  52. data/test/cases/attribute_test.rb +180 -0
  53. data/test/cases/attributes_test.rb +136 -0
  54. data/test/cases/autosave_association_test.rb +1595 -0
  55. data/test/cases/base_test.rb +1638 -2133
  56. data/test/cases/batches_test.rb +212 -0
  57. data/test/cases/binary_test.rb +52 -0
  58. data/test/cases/bind_parameter_test.rb +100 -0
  59. data/test/cases/calculations_test.rb +646 -482
  60. data/test/cases/callbacks_test.rb +543 -0
  61. data/test/cases/clone_test.rb +40 -0
  62. data/test/cases/coders/yaml_column_test.rb +63 -0
  63. data/test/cases/column_alias_test.rb +17 -0
  64. data/test/cases/column_definition_test.rb +123 -0
  65. data/test/cases/connection_adapters/adapter_leasing_test.rb +54 -0
  66. data/test/cases/connection_adapters/connection_handler_test.rb +53 -0
  67. data/test/cases/connection_adapters/connection_specification_test.rb +12 -0
  68. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +293 -0
  69. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +65 -0
  70. data/test/cases/connection_adapters/quoting_test.rb +13 -0
  71. data/test/cases/connection_adapters/schema_cache_test.rb +56 -0
  72. data/test/cases/connection_adapters/type_lookup_test.rb +110 -0
  73. data/test/cases/connection_management_test.rb +122 -0
  74. data/test/cases/connection_pool_test.rb +346 -0
  75. data/test/cases/connection_specification/resolver_test.rb +116 -0
  76. data/test/cases/core_test.rb +112 -0
  77. data/test/cases/counter_cache_test.rb +209 -0
  78. data/test/cases/custom_locking_test.rb +17 -0
  79. data/test/cases/database_statements_test.rb +19 -0
  80. data/test/cases/date_time_test.rb +61 -0
  81. data/test/cases/defaults_test.rb +223 -0
  82. data/test/cases/dirty_test.rb +775 -0
  83. data/test/cases/disconnected_test.rb +28 -0
  84. data/test/cases/dup_test.rb +157 -0
  85. data/test/cases/enum_test.rb +290 -0
  86. data/test/cases/explain_subscriber_test.rb +64 -0
  87. data/test/cases/explain_test.rb +76 -0
  88. data/test/cases/finder_respond_to_test.rb +60 -0
  89. data/test/cases/finder_test.rb +1166 -0
  90. data/test/cases/fixture_set/file_test.rb +138 -0
  91. data/test/cases/fixtures_test.rb +897 -0
  92. data/test/cases/forbidden_attributes_protection_test.rb +99 -0
  93. data/test/cases/habtm_destroy_order_test.rb +61 -0
  94. data/test/cases/helper.rb +210 -0
  95. data/test/cases/hot_compatibility_test.rb +54 -0
  96. data/test/cases/i18n_test.rb +45 -0
  97. data/test/cases/inheritance_test.rb +375 -0
  98. data/test/cases/integration_test.rb +139 -0
  99. data/test/cases/invalid_connection_test.rb +22 -0
  100. data/test/cases/invalid_date_test.rb +32 -0
  101. data/test/cases/invertible_migration_test.rb +295 -0
  102. data/test/cases/json_serialization_test.rb +302 -0
  103. data/test/cases/locking_test.rb +477 -0
  104. data/test/cases/log_subscriber_test.rb +136 -0
  105. data/test/cases/migration/change_schema_test - Copy.rb +448 -0
  106. data/test/cases/migration/change_schema_test.rb +472 -0
  107. data/test/cases/migration/change_table_test.rb +224 -0
  108. data/test/cases/migration/column_attributes_test.rb +192 -0
  109. data/test/cases/migration/column_positioning_test.rb +56 -0
  110. data/test/cases/migration/columns_test.rb +304 -0
  111. data/test/cases/migration/command_recorder_test.rb +305 -0
  112. data/test/cases/migration/create_join_table_test.rb +148 -0
  113. data/test/cases/migration/foreign_key_test - Changed.rb +325 -0
  114. data/test/cases/migration/foreign_key_test.rb +360 -0
  115. data/test/cases/migration/helper.rb +39 -0
  116. data/test/cases/migration/index_test.rb +216 -0
  117. data/test/cases/migration/logger_test.rb +36 -0
  118. data/test/cases/migration/pending_migrations_test.rb +53 -0
  119. data/test/cases/migration/references_foreign_key_test.rb +214 -0
  120. data/test/cases/migration/references_index_test.rb +101 -0
  121. data/test/cases/migration/references_statements_test.rb +116 -0
  122. data/test/cases/migration/rename_table_test.rb +93 -0
  123. data/test/cases/migration/table_and_index_test.rb +24 -0
  124. data/test/cases/migration_test.rb +959 -2408
  125. data/test/cases/migrator_test.rb +388 -0
  126. data/test/cases/mixin_test.rb +70 -0
  127. data/test/cases/modules_test.rb +173 -0
  128. data/test/cases/multiparameter_attributes_test.rb +350 -0
  129. data/test/cases/multiple_db_test.rb +115 -0
  130. data/test/cases/nested_attributes_test.rb +1057 -0
  131. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -0
  132. data/test/cases/persistence_test.rb +909 -642
  133. data/test/cases/pooled_connections_test.rb +81 -0
  134. data/test/cases/primary_keys_test.rb +237 -0
  135. data/test/cases/query_cache_test.rb +326 -257
  136. data/test/cases/quoting_test.rb +156 -0
  137. data/test/cases/readonly_test.rb +118 -0
  138. data/test/cases/reaper_test.rb +85 -0
  139. data/test/cases/reflection_test.rb +454 -0
  140. data/test/cases/relation/delegation_test.rb +68 -0
  141. data/test/cases/relation/merging_test.rb +161 -0
  142. data/test/cases/relation/mutation_test.rb +165 -0
  143. data/test/cases/relation/predicate_builder_test.rb +14 -0
  144. data/test/cases/relation/where_chain_test.rb +181 -0
  145. data/test/cases/relation/where_test.rb +300 -0
  146. data/test/cases/relation/where_test2.rb +36 -0
  147. data/test/cases/relation_test.rb +297 -0
  148. data/test/cases/relations_test.rb +1815 -1182
  149. data/test/cases/reload_models_test.rb +22 -0
  150. data/test/cases/result_test.rb +80 -0
  151. data/test/cases/sanitize_test.rb +83 -0
  152. data/test/cases/schema_dumper_test.rb +463 -256
  153. data/test/cases/scoping/default_scoping_test.rb +454 -0
  154. data/test/cases/scoping/named_scoping_test.rb +524 -0
  155. data/test/cases/scoping/relation_scoping_test.rb +357 -0
  156. data/test/cases/serialization_test.rb +104 -0
  157. data/test/cases/serialized_attribute_test.rb +277 -0
  158. data/test/cases/statement_cache_test.rb +98 -0
  159. data/test/cases/store_test.rb +194 -0
  160. data/test/cases/tasks/database_tasks_test.rb +396 -0
  161. data/test/cases/tasks/mysql_rake_test.rb +311 -0
  162. data/test/cases/tasks/postgresql_rake_test.rb +245 -0
  163. data/test/cases/tasks/sqlite_rake_test.rb +193 -0
  164. data/test/cases/test_case.rb +123 -0
  165. data/test/cases/timestamp_test.rb +468 -0
  166. data/test/cases/transaction_callbacks_test.rb +452 -300
  167. data/test/cases/transaction_isolation_test.rb +106 -0
  168. data/test/cases/transactions_test.rb +817 -0
  169. data/test/cases/type/decimal_test.rb +51 -0
  170. data/test/cases/type/integer_test.rb +121 -0
  171. data/test/cases/type/string_test.rb +36 -0
  172. data/test/cases/type/type_map_test.rb +177 -0
  173. data/test/cases/type/unsigned_integer_test.rb +18 -0
  174. data/test/cases/types_test.rb +141 -0
  175. data/test/cases/unconnected_test.rb +33 -0
  176. data/test/cases/validations/association_validation_test.rb +86 -0
  177. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -0
  178. data/test/cases/validations/i18n_validation_test.rb +90 -0
  179. data/test/cases/validations/length_validation_test.rb +47 -0
  180. data/test/cases/validations/presence_validation_test.rb +68 -0
  181. data/test/cases/validations/uniqueness_validation_test.rb +434 -299
  182. data/test/cases/validations_repair_helper.rb +23 -0
  183. data/test/cases/validations_test.rb +165 -0
  184. data/test/cases/view_test.rb +113 -0
  185. data/test/cases/xml_serialization_test.rb +457 -408
  186. data/test/cases/yaml_serialization_test.rb +86 -0
  187. data/test/config.rb +5 -0
  188. data/test/config.yml +154 -154
  189. data/test/connections/native_ibm_db/connection.rb +43 -43
  190. data/test/fixtures/accounts.yml +29 -0
  191. data/test/fixtures/admin/accounts.yml +2 -0
  192. data/test/fixtures/admin/randomly_named_a9.yml +7 -0
  193. data/test/fixtures/admin/randomly_named_b0.yml +7 -0
  194. data/test/fixtures/admin/users.yml +10 -0
  195. data/test/fixtures/all/admin +1 -0
  196. data/test/fixtures/all/developers.yml +0 -0
  197. data/test/fixtures/all/people.yml +0 -0
  198. data/test/fixtures/all/tasks.yml +0 -0
  199. data/test/fixtures/author_addresses.yml +18 -0
  200. data/test/fixtures/author_favorites.yml +4 -0
  201. data/test/fixtures/authors.yml +23 -0
  202. data/test/fixtures/binaries.yml +133 -0
  203. data/test/fixtures/books.yml +11 -0
  204. data/test/fixtures/bulbs.yml +5 -0
  205. data/test/fixtures/cars.yml +9 -0
  206. data/test/fixtures/categories.yml +19 -0
  207. data/test/fixtures/categories/special_categories.yml +9 -0
  208. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  209. data/test/fixtures/categories_ordered.yml +7 -0
  210. data/test/fixtures/categories_posts.yml +31 -0
  211. data/test/fixtures/categorizations.yml +23 -0
  212. data/test/fixtures/clubs.yml +8 -0
  213. data/test/fixtures/collections.yml +3 -0
  214. data/test/fixtures/colleges.yml +3 -0
  215. data/test/fixtures/comments.yml +65 -0
  216. data/test/fixtures/companies.yml +67 -0
  217. data/test/fixtures/computers.yml +10 -0
  218. data/test/fixtures/courses.yml +8 -0
  219. data/test/fixtures/customers.yml +26 -0
  220. data/test/fixtures/dashboards.yml +6 -0
  221. data/test/fixtures/developers.yml +22 -0
  222. data/test/fixtures/developers_projects.yml +17 -0
  223. data/test/fixtures/dog_lovers.yml +7 -0
  224. data/test/fixtures/dogs.yml +4 -0
  225. data/test/fixtures/doubloons.yml +3 -0
  226. data/test/fixtures/edges.yml +5 -0
  227. data/test/fixtures/entrants.yml +14 -0
  228. data/test/fixtures/essays.yml +6 -0
  229. data/test/fixtures/faces.yml +11 -0
  230. data/test/fixtures/fk_test_has_fk.yml +3 -0
  231. data/test/fixtures/fk_test_has_pk.yml +2 -0
  232. data/test/fixtures/friendships.yml +4 -0
  233. data/test/fixtures/funny_jokes.yml +10 -0
  234. data/test/fixtures/interests.yml +33 -0
  235. data/test/fixtures/items.yml +3 -0
  236. data/test/fixtures/jobs.yml +7 -0
  237. data/test/fixtures/legacy_things.yml +3 -0
  238. data/test/fixtures/mateys.yml +4 -0
  239. data/test/fixtures/member_details.yml +8 -0
  240. data/test/fixtures/member_types.yml +6 -0
  241. data/test/fixtures/members.yml +11 -0
  242. data/test/fixtures/memberships.yml +34 -0
  243. data/test/fixtures/men.yml +5 -0
  244. data/test/fixtures/minimalistics.yml +2 -0
  245. data/test/fixtures/minivans.yml +5 -0
  246. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  247. data/test/fixtures/mixins.yml +29 -0
  248. data/test/fixtures/movies.yml +7 -0
  249. data/test/fixtures/naked/csv/accounts.csv +1 -0
  250. data/test/fixtures/naked/yml/accounts.yml +1 -0
  251. data/test/fixtures/naked/yml/companies.yml +1 -0
  252. data/test/fixtures/naked/yml/courses.yml +1 -0
  253. data/test/fixtures/organizations.yml +5 -0
  254. data/test/fixtures/other_topics.yml +42 -0
  255. data/test/fixtures/owners.yml +9 -0
  256. data/test/fixtures/parrots.yml +27 -0
  257. data/test/fixtures/parrots_pirates.yml +7 -0
  258. data/test/fixtures/people.yml +24 -0
  259. data/test/fixtures/peoples_treasures.yml +3 -0
  260. data/test/fixtures/pets.yml +19 -0
  261. data/test/fixtures/pirates.yml +12 -0
  262. data/test/fixtures/posts.yml +80 -0
  263. data/test/fixtures/price_estimates.yml +7 -0
  264. data/test/fixtures/products.yml +4 -0
  265. data/test/fixtures/projects.yml +7 -0
  266. data/test/fixtures/randomly_named_a9.yml +7 -0
  267. data/test/fixtures/ratings.yml +14 -0
  268. data/test/fixtures/readers.yml +11 -0
  269. data/test/fixtures/references.yml +17 -0
  270. data/test/fixtures/reserved_words/distinct.yml +5 -0
  271. data/test/fixtures/reserved_words/distinct_select.yml +11 -0
  272. data/test/fixtures/reserved_words/group.yml +14 -0
  273. data/test/fixtures/reserved_words/select.yml +8 -0
  274. data/test/fixtures/reserved_words/values.yml +7 -0
  275. data/test/fixtures/ships.yml +6 -0
  276. data/test/fixtures/speedometers.yml +8 -0
  277. data/test/fixtures/sponsors.yml +12 -0
  278. data/test/fixtures/string_key_objects.yml +7 -0
  279. data/test/fixtures/subscribers.yml +11 -0
  280. data/test/fixtures/subscriptions.yml +12 -0
  281. data/test/fixtures/taggings.yml +78 -0
  282. data/test/fixtures/tags.yml +11 -0
  283. data/test/fixtures/tasks.yml +7 -0
  284. data/test/fixtures/teapots.yml +3 -0
  285. data/test/fixtures/to_be_linked/accounts.yml +2 -0
  286. data/test/fixtures/to_be_linked/users.yml +10 -0
  287. data/test/fixtures/topics.yml +49 -0
  288. data/test/fixtures/toys.yml +14 -0
  289. data/test/fixtures/traffic_lights.yml +10 -0
  290. data/test/fixtures/treasures.yml +10 -0
  291. data/test/fixtures/uuid_children.yml +3 -0
  292. data/test/fixtures/uuid_parents.yml +2 -0
  293. data/test/fixtures/variants.yml +4 -0
  294. data/test/fixtures/vegetables.yml +20 -0
  295. data/test/fixtures/vertices.yml +4 -0
  296. data/test/fixtures/warehouse_things.yml +3 -0
  297. data/test/fixtures/zines.yml +5 -0
  298. data/test/ibm_db_test.rb +24 -24
  299. data/test/migrations/10_urban/9_add_expressions.rb +11 -0
  300. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  301. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -0
  302. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  303. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  304. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  305. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  306. data/test/migrations/rename/1_we_need_things.rb +11 -0
  307. data/test/migrations/rename/2_rename_things.rb +9 -0
  308. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -0
  309. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -0
  310. data/test/migrations/to_copy2/1_create_articles.rb +7 -0
  311. data/test/migrations/to_copy2/2_create_comments.rb +7 -0
  312. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -0
  313. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -0
  314. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -0
  315. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -0
  316. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -0
  317. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -0
  318. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  319. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  320. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -0
  321. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -0
  322. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -0
  323. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -0
  324. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -0
  325. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -0
  326. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -0
  327. data/test/models/admin.rb +5 -0
  328. data/test/models/admin/account.rb +3 -0
  329. data/test/models/admin/randomly_named_c1.rb +3 -0
  330. data/test/models/admin/user.rb +40 -0
  331. data/test/models/aircraft.rb +4 -0
  332. data/test/models/arunit2_model.rb +3 -0
  333. data/test/models/author.rb +212 -0
  334. data/test/models/auto_id.rb +4 -0
  335. data/test/models/autoloadable/extra_firm.rb +2 -0
  336. data/test/models/binary.rb +2 -0
  337. data/test/models/bird.rb +12 -0
  338. data/test/models/book.rb +18 -0
  339. data/test/models/boolean.rb +2 -0
  340. data/test/models/bulb.rb +51 -0
  341. data/test/models/cake_designer.rb +3 -0
  342. data/test/models/car.rb +26 -0
  343. data/test/models/carrier.rb +2 -0
  344. data/test/models/categorization.rb +19 -0
  345. data/test/models/category.rb +35 -0
  346. data/test/models/chef.rb +3 -0
  347. data/test/models/citation.rb +3 -0
  348. data/test/models/club.rb +23 -0
  349. data/test/models/college.rb +10 -0
  350. data/test/models/column.rb +3 -0
  351. data/test/models/column_name.rb +3 -0
  352. data/test/models/comment.rb +64 -0
  353. data/test/models/company.rb +225 -0
  354. data/test/models/company_in_module.rb +98 -0
  355. data/test/models/computer.rb +3 -0
  356. data/test/models/contact.rb +41 -0
  357. data/test/models/contract.rb +20 -0
  358. data/test/models/country.rb +7 -0
  359. data/test/models/course.rb +6 -0
  360. data/test/models/customer.rb +77 -0
  361. data/test/models/customer_carrier.rb +14 -0
  362. data/test/models/dashboard.rb +3 -0
  363. data/test/models/default.rb +2 -0
  364. data/test/models/department.rb +4 -0
  365. data/test/models/developer.rb +252 -0
  366. data/test/models/dog.rb +5 -0
  367. data/test/models/dog_lover.rb +5 -0
  368. data/test/models/doubloon.rb +12 -0
  369. data/test/models/drink_designer.rb +3 -0
  370. data/test/models/edge.rb +5 -0
  371. data/test/models/electron.rb +5 -0
  372. data/test/models/engine.rb +4 -0
  373. data/test/models/entrant.rb +3 -0
  374. data/test/models/essay.rb +5 -0
  375. data/test/models/event.rb +3 -0
  376. data/test/models/eye.rb +37 -0
  377. data/test/models/face.rb +9 -0
  378. data/test/models/friendship.rb +6 -0
  379. data/test/models/guid.rb +2 -0
  380. data/test/models/hotel.rb +6 -0
  381. data/test/models/image.rb +3 -0
  382. data/test/models/interest.rb +5 -0
  383. data/test/models/invoice.rb +4 -0
  384. data/test/models/item.rb +7 -0
  385. data/test/models/job.rb +7 -0
  386. data/test/models/joke.rb +7 -0
  387. data/test/models/keyboard.rb +3 -0
  388. data/test/models/legacy_thing.rb +3 -0
  389. data/test/models/lesson.rb +11 -0
  390. data/test/models/line_item.rb +3 -0
  391. data/test/models/liquid.rb +4 -0
  392. data/test/models/man.rb +11 -0
  393. data/test/models/matey.rb +4 -0
  394. data/test/models/member.rb +41 -0
  395. data/test/models/member_detail.rb +7 -0
  396. data/test/models/member_type.rb +3 -0
  397. data/test/models/membership.rb +35 -0
  398. data/test/models/minimalistic.rb +2 -0
  399. data/test/models/minivan.rb +9 -0
  400. data/test/models/mixed_case_monkey.rb +3 -0
  401. data/test/models/molecule.rb +6 -0
  402. data/test/models/movie.rb +5 -0
  403. data/test/models/order.rb +4 -0
  404. data/test/models/organization.rb +14 -0
  405. data/test/models/owner.rb +34 -0
  406. data/test/models/parrot.rb +29 -0
  407. data/test/models/person.rb +143 -0
  408. data/test/models/personal_legacy_thing.rb +4 -0
  409. data/test/models/pet.rb +15 -0
  410. data/test/models/pirate.rb +92 -0
  411. data/test/models/possession.rb +3 -0
  412. data/test/models/post.rb +264 -0
  413. data/test/models/price_estimate.rb +4 -0
  414. data/test/models/professor.rb +5 -0
  415. data/test/models/project.rb +29 -0
  416. data/test/models/publisher.rb +2 -0
  417. data/test/models/publisher/article.rb +4 -0
  418. data/test/models/publisher/magazine.rb +3 -0
  419. data/test/models/randomly_named_c1.rb +3 -0
  420. data/test/models/rating.rb +4 -0
  421. data/test/models/reader.rb +23 -0
  422. data/test/models/record.rb +2 -0
  423. data/test/models/reference.rb +22 -0
  424. data/test/models/reply.rb +61 -0
  425. data/test/models/ship.rb +33 -0
  426. data/test/models/ship_part.rb +8 -0
  427. data/test/models/shop.rb +17 -0
  428. data/test/models/shop_account.rb +6 -0
  429. data/test/models/speedometer.rb +6 -0
  430. data/test/models/sponsor.rb +7 -0
  431. data/test/models/string_key_object.rb +3 -0
  432. data/test/models/student.rb +4 -0
  433. data/test/models/subject.rb +16 -0
  434. data/test/models/subscriber.rb +8 -0
  435. data/test/models/subscription.rb +4 -0
  436. data/test/models/tag.rb +7 -0
  437. data/test/models/tagging.rb +13 -0
  438. data/test/models/task.rb +5 -0
  439. data/test/models/topic.rb +124 -0
  440. data/test/models/toy.rb +6 -0
  441. data/test/models/traffic_light.rb +4 -0
  442. data/test/models/treasure.rb +14 -0
  443. data/test/models/treaty.rb +7 -0
  444. data/test/models/tyre.rb +11 -0
  445. data/test/models/uuid_child.rb +3 -0
  446. data/test/models/uuid_parent.rb +3 -0
  447. data/test/models/vegetables.rb +24 -0
  448. data/test/models/vehicle.rb +7 -0
  449. data/test/models/vertex.rb +9 -0
  450. data/test/models/warehouse_thing.rb +5 -5
  451. data/test/models/wheel.rb +3 -0
  452. data/test/models/without_table.rb +3 -0
  453. data/test/models/zine.rb +3 -0
  454. data/test/schema/mysql2_specific_schema.rb +58 -0
  455. data/test/schema/mysql_specific_schema.rb +70 -0
  456. data/test/schema/oracle_specific_schema.rb +43 -0
  457. data/test/schema/postgresql_specific_schema.rb +202 -0
  458. data/test/schema/schema.rb +938 -751
  459. data/test/schema/sqlite_specific_schema.rb +22 -0
  460. data/test/support/config.rb +43 -0
  461. data/test/support/connection.rb +22 -0
  462. data/test/support/connection_helper.rb +14 -0
  463. data/test/support/ddl_helper.rb +8 -0
  464. data/test/support/schema_dumping_helper.rb +20 -0
  465. metadata +444 -18
@@ -0,0 +1,115 @@
1
+ require "cases/helper"
2
+ require 'models/entrant'
3
+ require 'models/bird'
4
+ require 'models/course'
5
+
6
+ class MultipleDbTest < ActiveRecord::TestCase
7
+ self.use_transactional_fixtures = false
8
+
9
+ def setup
10
+ @courses = create_fixtures("courses") { Course.retrieve_connection }
11
+ @colleges = create_fixtures("colleges") { College.retrieve_connection }
12
+ @entrants = create_fixtures("entrants")
13
+ end
14
+
15
+ def test_connected
16
+ assert_not_nil Entrant.connection
17
+ assert_not_nil Course.connection
18
+ end
19
+
20
+ def test_proper_connection
21
+ assert_not_equal(Entrant.connection, Course.connection)
22
+ assert_equal(Entrant.connection, Entrant.retrieve_connection)
23
+ assert_equal(Course.connection, Course.retrieve_connection)
24
+ assert_equal(ActiveRecord::Base.connection, Entrant.connection)
25
+ end
26
+
27
+ def test_find
28
+ c1 = Course.find(1)
29
+ assert_equal "Ruby Development", c1.name
30
+ c2 = Course.find(2)
31
+ assert_equal "Java Development", c2.name
32
+ e1 = Entrant.find(1)
33
+ assert_equal "Ruby Developer", e1.name
34
+ e2 = Entrant.find(2)
35
+ assert_equal "Ruby Guru", e2.name
36
+ e3 = Entrant.find(3)
37
+ assert_equal "Java Lover", e3.name
38
+ end
39
+
40
+ def test_associations
41
+ c1 = Course.find(1)
42
+ assert_equal 2, c1.entrants.count
43
+ e1 = Entrant.find(1)
44
+ assert_equal e1.course.id, c1.id
45
+ c2 = Course.find(2)
46
+ assert_equal 1, c2.entrants.count
47
+ e3 = Entrant.find(3)
48
+ assert_equal e3.course.id, c2.id
49
+ end
50
+
51
+ def test_course_connection_should_survive_dependency_reload
52
+ assert Course.connection
53
+
54
+ ActiveSupport::Dependencies.clear
55
+ Object.send(:remove_const, :Course)
56
+ require_dependency 'models/course'
57
+
58
+ assert Course.connection
59
+ end
60
+
61
+ def test_transactions_across_databases
62
+ c1 = Course.find(1)
63
+ e1 = Entrant.find(1)
64
+
65
+ begin
66
+ Course.transaction do
67
+ Entrant.transaction do
68
+ c1.name = "Typo"
69
+ e1.name = "Typo"
70
+ c1.save
71
+ e1.save
72
+ raise "No I messed up."
73
+ end
74
+ end
75
+ rescue
76
+ # Yup caught it
77
+ end
78
+
79
+ assert_equal "Typo", c1.name
80
+ assert_equal "Typo", e1.name
81
+
82
+ assert_equal "Ruby Development", Course.find(1).name
83
+ assert_equal "Ruby Developer", Entrant.find(1).name
84
+ end
85
+
86
+ def test_arel_table_engines
87
+ assert_not_equal Entrant.arel_engine, Bird.arel_engine
88
+ assert_not_equal Entrant.arel_engine, Course.arel_engine
89
+ end
90
+
91
+ def test_connection
92
+ assert_equal Entrant.arel_engine.connection, Bird.arel_engine.connection
93
+ assert_not_equal Entrant.arel_engine.connection, Course.arel_engine.connection
94
+ end
95
+
96
+ def test_count_on_custom_connection
97
+ ActiveRecord::Base.remove_connection
98
+ assert_equal 1, College.count
99
+ ensure
100
+ ActiveRecord::Base.establish_connection :arunit
101
+ end
102
+
103
+ unless in_memory_db?
104
+ def test_associations_should_work_when_model_has_no_connection
105
+ begin
106
+ ActiveRecord::Base.remove_connection
107
+ assert_nothing_raised ActiveRecord::ConnectionNotEstablished do
108
+ College.first.courses.first
109
+ end
110
+ ensure
111
+ ActiveRecord::Base.establish_connection :arunit
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,1057 @@
1
+ require "cases/helper"
2
+ require "models/pirate"
3
+ require "models/ship"
4
+ require "models/ship_part"
5
+ require "models/bird"
6
+ require "models/parrot"
7
+ require "models/treasure"
8
+ require "models/man"
9
+ require "models/interest"
10
+ require "models/owner"
11
+ require "models/pet"
12
+ require 'active_support/hash_with_indifferent_access'
13
+
14
+ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
15
+ teardown do
16
+ Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
17
+ end
18
+
19
+ def test_base_should_have_an_empty_nested_attributes_options
20
+ assert_equal Hash.new, ActiveRecord::Base.nested_attributes_options
21
+ end
22
+
23
+ def test_should_add_a_proc_to_nested_attributes_options
24
+ assert_equal ActiveRecord::NestedAttributes::ClassMethods::REJECT_ALL_BLANK_PROC,
25
+ Pirate.nested_attributes_options[:birds_with_reject_all_blank][:reject_if]
26
+
27
+ [:parrots, :birds].each do |name|
28
+ assert_instance_of Proc, Pirate.nested_attributes_options[name][:reject_if]
29
+ end
30
+ end
31
+
32
+ def test_should_not_build_a_new_record_using_reject_all_even_if_destroy_is_given
33
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
34
+ pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => '', :_destroy => '0'}]
35
+ pirate.save!
36
+
37
+ assert pirate.birds_with_reject_all_blank.empty?
38
+ end
39
+
40
+ def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
41
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
42
+ pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => ''}]
43
+ pirate.save!
44
+
45
+ assert pirate.birds_with_reject_all_blank.empty?
46
+ end
47
+
48
+ def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
49
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
50
+ pirate.birds_with_reject_all_blank_attributes = [{:name => 'Tweetie', :color => ''}]
51
+ pirate.save!
52
+
53
+ assert_equal 1, pirate.birds_with_reject_all_blank.count
54
+ assert_equal 'Tweetie', pirate.birds_with_reject_all_blank.first.name
55
+ end
56
+
57
+ def test_should_raise_an_ArgumentError_for_non_existing_associations
58
+ exception = assert_raise ArgumentError do
59
+ Pirate.accepts_nested_attributes_for :honesty
60
+ end
61
+ assert_equal "No association found for name `honesty'. Has it been defined yet?", exception.message
62
+ end
63
+
64
+ def test_should_disable_allow_destroy_by_default
65
+ Pirate.accepts_nested_attributes_for :ship
66
+
67
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
68
+ ship = pirate.create_ship(name: 'Nights Dirty Lightning')
69
+
70
+ pirate.update(ship_attributes: { '_destroy' => true, :id => ship.id })
71
+
72
+ assert_nothing_raised(ActiveRecord::RecordNotFound) { pirate.ship.reload }
73
+ end
74
+
75
+ def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction
76
+ ship = Ship.create!(:name => 'Nights Dirty Lightning')
77
+ assert !ship._destroy
78
+ ship.mark_for_destruction
79
+ assert ship._destroy
80
+ end
81
+
82
+ def test_reject_if_method_without_arguments
83
+ Pirate.accepts_nested_attributes_for :ship, :reject_if => :new_record?
84
+
85
+ pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
86
+ pirate.ship_attributes = { :name => 'Black Pearl' }
87
+ assert_no_difference('Ship.count') { pirate.save! }
88
+ end
89
+
90
+ def test_reject_if_method_with_arguments
91
+ Pirate.accepts_nested_attributes_for :ship, :reject_if => :reject_empty_ships_on_create
92
+
93
+ pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
94
+ pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
95
+ assert_no_difference('Ship.count') { pirate.save! }
96
+
97
+ # pirate.reject_empty_ships_on_create returns false for saved pirate records
98
+ # in the previous step note that pirate gets saved but ship fails
99
+ pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
100
+ assert_difference('Ship.count') { pirate.save! }
101
+ end
102
+
103
+ def test_reject_if_with_indifferent_keys
104
+ Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:name].blank? }
105
+
106
+ pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
107
+ pirate.ship_attributes = { :name => 'Hello Pearl' }
108
+ assert_difference('Ship.count') { pirate.save! }
109
+ end
110
+
111
+ def test_reject_if_with_a_proc_which_returns_true_always_for_has_one
112
+ Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| true }
113
+ pirate = Pirate.new(catchphrase: "Stop wastin' me time")
114
+ ship = pirate.create_ship(name: 's1')
115
+ pirate.update({ship_attributes: { name: 's2', id: ship.id } })
116
+ assert_equal 's1', ship.reload.name
117
+ end
118
+
119
+ def test_reuse_already_built_new_record
120
+ pirate = Pirate.new
121
+ ship_built_first = pirate.build_ship
122
+ pirate.ship_attributes = { name: 'Ship 1' }
123
+ assert_equal ship_built_first.object_id, pirate.ship.object_id
124
+ end
125
+
126
+ def test_do_not_allow_assigning_foreign_key_when_reusing_existing_new_record
127
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
128
+ pirate.build_ship
129
+ pirate.ship_attributes = { name: 'Ship 1', pirate_id: pirate.id + 1 }
130
+ assert_equal pirate.id, pirate.ship.pirate_id
131
+ end
132
+
133
+ def test_reject_if_with_a_proc_which_returns_true_always_for_has_many
134
+ Man.accepts_nested_attributes_for :interests, :reject_if => proc {|attributes| true }
135
+ man = Man.create(name: "John")
136
+ interest = man.interests.create(topic: 'photography')
137
+ man.update({interests_attributes: { topic: 'gardening', id: interest.id } })
138
+ assert_equal 'photography', interest.reload.topic
139
+ end
140
+
141
+ def test_destroy_works_independent_of_reject_if
142
+ Man.accepts_nested_attributes_for :interests, :reject_if => proc {|attributes| true }, :allow_destroy => true
143
+ man = Man.create(name: "Jon")
144
+ interest = man.interests.create(topic: 'the ladies')
145
+ man.update({interests_attributes: { _destroy: "1", id: interest.id } })
146
+ assert man.reload.interests.empty?
147
+ end
148
+
149
+ def test_has_many_association_updating_a_single_record
150
+ Man.accepts_nested_attributes_for(:interests)
151
+ man = Man.create(name: 'John')
152
+ interest = man.interests.create(topic: 'photography')
153
+ man.update({interests_attributes: {topic: 'gardening', id: interest.id}})
154
+ assert_equal 'gardening', interest.reload.topic
155
+ end
156
+
157
+ def test_reject_if_with_blank_nested_attributes_id
158
+ # When using a select list to choose an existing 'ship' id, with include_blank: true
159
+ Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:id].blank? }
160
+
161
+ pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
162
+ pirate.ship_attributes = { :id => "" }
163
+ assert_nothing_raised(ActiveRecord::RecordNotFound) { pirate.save! }
164
+ end
165
+
166
+ def test_first_and_array_index_zero_methods_return_the_same_value_when_nested_attributes_are_set_to_update_existing_record
167
+ Man.accepts_nested_attributes_for(:interests)
168
+ man = Man.create(:name => "John")
169
+ interest = man.interests.create :topic => 'gardening'
170
+ man = Man.find man.id
171
+ man.interests_attributes = [{:id => interest.id, :topic => 'gardening'}]
172
+ assert_equal man.interests.first.topic, man.interests[0].topic
173
+ end
174
+
175
+ def test_allows_class_to_override_setter_and_call_super
176
+ mean_pirate_class = Class.new(Pirate) do
177
+ accepts_nested_attributes_for :parrot
178
+ def parrot_attributes=(attrs)
179
+ super(attrs.merge(:color => "blue"))
180
+ end
181
+ end
182
+ mean_pirate = mean_pirate_class.new
183
+ mean_pirate.parrot_attributes = { :name => "James" }
184
+ assert_equal "James", mean_pirate.parrot.name
185
+ assert_equal "blue", mean_pirate.parrot.color
186
+ end
187
+
188
+ def test_accepts_nested_attributes_for_can_be_overridden_in_subclasses
189
+ Pirate.accepts_nested_attributes_for(:parrot)
190
+
191
+ mean_pirate_class = Class.new(Pirate) do
192
+ accepts_nested_attributes_for :parrot
193
+ end
194
+ mean_pirate = mean_pirate_class.new
195
+ mean_pirate.parrot_attributes = { :name => "James" }
196
+ assert_equal "James", mean_pirate.parrot.name
197
+ end
198
+ end
199
+
200
+ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
201
+ def setup
202
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
203
+ @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
204
+ end
205
+
206
+ def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to
207
+ exception = assert_raise ArgumentError do
208
+ Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"})
209
+ end
210
+ assert_equal "Cannot build association `looter'. Are you trying to build a polymorphic one-to-one association?", exception.message
211
+ end
212
+
213
+ def test_should_define_an_attribute_writer_method_for_the_association
214
+ assert_respond_to @pirate, :ship_attributes=
215
+ end
216
+
217
+ def test_should_build_a_new_record_if_there_is_no_id
218
+ @ship.destroy
219
+ @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger' }
220
+
221
+ assert !@pirate.ship.persisted?
222
+ assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
223
+ end
224
+
225
+ def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
226
+ @ship.destroy
227
+ @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
228
+
229
+ assert_nil @pirate.ship
230
+ end
231
+
232
+ def test_should_not_build_a_new_record_if_a_reject_if_proc_returns_false
233
+ @ship.destroy
234
+ @pirate.reload.ship_attributes = {}
235
+
236
+ assert_nil @pirate.ship
237
+ end
238
+
239
+ def test_should_replace_an_existing_record_if_there_is_no_id
240
+ @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger' }
241
+
242
+ assert !@pirate.ship.persisted?
243
+ assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
244
+ assert_equal 'Nights Dirty Lightning', @ship.name
245
+ end
246
+
247
+ def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
248
+ @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
249
+
250
+ assert_equal @ship, @pirate.ship
251
+ assert_equal 'Nights Dirty Lightning', @pirate.ship.name
252
+ end
253
+
254
+ def test_should_modify_an_existing_record_if_there_is_a_matching_id
255
+ @pirate.reload.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
256
+
257
+ assert_equal @ship, @pirate.ship
258
+ assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
259
+ end
260
+
261
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
262
+ exception = assert_raise ActiveRecord::RecordNotFound do
263
+ @pirate.ship_attributes = { :id => 1234567890 }
264
+ end
265
+ assert_equal "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}", exception.message
266
+ end
267
+
268
+ def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
269
+ @pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
270
+
271
+ assert_equal @ship, @pirate.ship
272
+ assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
273
+ end
274
+
275
+ def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
276
+ @ship.stubs(:id).returns('ABC1X')
277
+ @pirate.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
278
+
279
+ assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
280
+ end
281
+
282
+ def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
283
+ @pirate.ship.destroy
284
+
285
+ [1, '1', true, 'true'].each do |truth|
286
+ ship = @pirate.reload.create_ship(name: 'Mister Pablo')
287
+ @pirate.update(ship_attributes: { id: ship.id, _destroy: truth })
288
+
289
+ assert_nil @pirate.reload.ship
290
+ assert_raise(ActiveRecord::RecordNotFound) { Ship.find(ship.id) }
291
+ end
292
+ end
293
+
294
+ def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
295
+ [nil, '0', 0, 'false', false].each do |not_truth|
296
+ @pirate.update(ship_attributes: { id: @pirate.ship.id, _destroy: not_truth })
297
+
298
+ assert_equal @ship, @pirate.reload.ship
299
+ end
300
+ end
301
+
302
+ def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
303
+ Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
304
+
305
+ @pirate.update(ship_attributes: { id: @pirate.ship.id, _destroy: '1' })
306
+
307
+ assert_equal @ship, @pirate.reload.ship
308
+
309
+ Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
310
+ end
311
+
312
+ def test_should_also_work_with_a_HashWithIndifferentAccess
313
+ @pirate.ship_attributes = ActiveSupport::HashWithIndifferentAccess.new(:id => @ship.id, :name => 'Davy Jones Gold Dagger')
314
+
315
+ assert @pirate.ship.persisted?
316
+ assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
317
+ end
318
+
319
+ def test_should_work_with_update_as_well
320
+ @pirate.update({ catchphrase: 'Arr', ship_attributes: { id: @ship.id, name: 'Mister Pablo' } })
321
+ @pirate.reload
322
+
323
+ assert_equal 'Arr', @pirate.catchphrase
324
+ assert_equal 'Mister Pablo', @pirate.ship.name
325
+ end
326
+
327
+ def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
328
+ @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_destroy => '1' } }
329
+
330
+ assert !@pirate.ship.destroyed?
331
+ assert @pirate.ship.marked_for_destruction?
332
+
333
+ @pirate.save
334
+
335
+ assert @pirate.ship.destroyed?
336
+ assert_nil @pirate.reload.ship
337
+ end
338
+
339
+ def test_should_automatically_enable_autosave_on_the_association
340
+ assert Pirate.reflect_on_association(:ship).options[:autosave]
341
+ end
342
+
343
+ def test_should_accept_update_only_option
344
+ @pirate.update(update_only_ship_attributes: { id: @pirate.ship.id, name: 'Mayflower' })
345
+ end
346
+
347
+ def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
348
+ @ship.delete
349
+
350
+ @pirate.reload.update(update_only_ship_attributes: { name: 'Mayflower' })
351
+
352
+ assert_not_nil @pirate.ship
353
+ end
354
+
355
+ def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
356
+ @ship.delete
357
+ @ship = @pirate.create_update_only_ship(name: 'Nights Dirty Lightning')
358
+
359
+ @pirate.update(update_only_ship_attributes: { name: 'Mayflower' })
360
+
361
+ assert_equal 'Mayflower', @ship.reload.name
362
+ assert_equal @ship, @pirate.reload.ship
363
+ end
364
+
365
+ def test_should_update_existing_when_update_only_is_true_and_id_is_given
366
+ @ship.delete
367
+ @ship = @pirate.create_update_only_ship(name: 'Nights Dirty Lightning')
368
+
369
+ @pirate.update(update_only_ship_attributes: { name: 'Mayflower', id: @ship.id })
370
+
371
+ assert_equal 'Mayflower', @ship.reload.name
372
+ assert_equal @ship, @pirate.reload.ship
373
+ end
374
+
375
+ def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction
376
+ Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => true
377
+ @ship.delete
378
+ @ship = @pirate.create_update_only_ship(name: 'Nights Dirty Lightning')
379
+
380
+ @pirate.update(update_only_ship_attributes: { name: 'Mayflower', id: @ship.id, _destroy: true })
381
+
382
+ assert_nil @pirate.reload.ship
383
+ assert_raise(ActiveRecord::RecordNotFound) { Ship.find(@ship.id) }
384
+
385
+ Pirate.accepts_nested_attributes_for :update_only_ship, :update_only => true, :allow_destroy => false
386
+ end
387
+
388
+ end
389
+
390
+ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
391
+ def setup
392
+ @ship = Ship.new(:name => 'Nights Dirty Lightning')
393
+ @pirate = @ship.build_pirate(:catchphrase => 'Aye')
394
+ @ship.save!
395
+ end
396
+
397
+ def test_should_define_an_attribute_writer_method_for_the_association
398
+ assert_respond_to @ship, :pirate_attributes=
399
+ end
400
+
401
+ def test_should_build_a_new_record_if_there_is_no_id
402
+ @pirate.destroy
403
+ @ship.reload.pirate_attributes = { :catchphrase => 'Arr' }
404
+
405
+ assert !@ship.pirate.persisted?
406
+ assert_equal 'Arr', @ship.pirate.catchphrase
407
+ end
408
+
409
+ def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
410
+ @pirate.destroy
411
+ @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
412
+
413
+ assert_nil @ship.pirate
414
+ end
415
+
416
+ def test_should_not_build_a_new_record_if_a_reject_if_proc_returns_false
417
+ @pirate.destroy
418
+ @ship.reload.pirate_attributes = {}
419
+
420
+ assert_nil @ship.pirate
421
+ end
422
+
423
+ def test_should_replace_an_existing_record_if_there_is_no_id
424
+ @ship.reload.pirate_attributes = { :catchphrase => 'Arr' }
425
+
426
+ assert !@ship.pirate.persisted?
427
+ assert_equal 'Arr', @ship.pirate.catchphrase
428
+ assert_equal 'Aye', @pirate.catchphrase
429
+ end
430
+
431
+ def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
432
+ @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
433
+
434
+ assert_equal @pirate, @ship.pirate
435
+ assert_equal 'Aye', @ship.pirate.catchphrase
436
+ end
437
+
438
+ def test_should_modify_an_existing_record_if_there_is_a_matching_id
439
+ @ship.reload.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
440
+
441
+ assert_equal @pirate, @ship.pirate
442
+ assert_equal 'Arr', @ship.pirate.catchphrase
443
+ end
444
+
445
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
446
+ exception = assert_raise ActiveRecord::RecordNotFound do
447
+ @ship.pirate_attributes = { :id => 1234567890 }
448
+ end
449
+ assert_equal "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}", exception.message
450
+ end
451
+
452
+ def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
453
+ @ship.reload.pirate_attributes = { 'id' => @pirate.id, 'catchphrase' => 'Arr' }
454
+
455
+ assert_equal @pirate, @ship.pirate
456
+ assert_equal 'Arr', @ship.pirate.catchphrase
457
+ end
458
+
459
+ def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
460
+ @pirate.stubs(:id).returns('ABC1X')
461
+ @ship.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
462
+
463
+ assert_equal 'Arr', @ship.pirate.catchphrase
464
+ end
465
+
466
+ def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
467
+ @ship.pirate.destroy
468
+ [1, '1', true, 'true'].each do |truth|
469
+ pirate = @ship.reload.create_pirate(catchphrase: 'Arr')
470
+ @ship.update(pirate_attributes: { id: pirate.id, _destroy: truth })
471
+ assert_raise(ActiveRecord::RecordNotFound) { pirate.reload }
472
+ end
473
+ end
474
+
475
+ def test_should_unset_association_when_an_existing_record_is_destroyed
476
+ original_pirate_id = @ship.pirate.id
477
+ @ship.update! pirate_attributes: { id: @ship.pirate.id, _destroy: true }
478
+
479
+ assert_empty Pirate.where(id: original_pirate_id)
480
+ assert_nil @ship.pirate_id
481
+ assert_nil @ship.pirate
482
+
483
+ @ship.reload
484
+ assert_empty Pirate.where(id: original_pirate_id)
485
+ assert_nil @ship.pirate_id
486
+ assert_nil @ship.pirate
487
+ end
488
+
489
+ def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
490
+ [nil, '0', 0, 'false', false].each do |not_truth|
491
+ @ship.update(pirate_attributes: { id: @ship.pirate.id, _destroy: not_truth })
492
+ assert_nothing_raised(ActiveRecord::RecordNotFound) { @ship.pirate.reload }
493
+ end
494
+ end
495
+
496
+ def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
497
+ Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
498
+
499
+ @ship.update(pirate_attributes: { id: @ship.pirate.id, _destroy: '1' })
500
+ assert_nothing_raised(ActiveRecord::RecordNotFound) { @ship.pirate.reload }
501
+ ensure
502
+ Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
503
+ end
504
+
505
+ def test_should_work_with_update_as_well
506
+ @ship.update({ name: 'Mister Pablo', pirate_attributes: { catchphrase: 'Arr' } })
507
+ @ship.reload
508
+
509
+ assert_equal 'Mister Pablo', @ship.name
510
+ assert_equal 'Arr', @ship.pirate.catchphrase
511
+ end
512
+
513
+ def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
514
+ pirate = @ship.pirate
515
+
516
+ @ship.attributes = { :pirate_attributes => { :id => pirate.id, '_destroy' => true } }
517
+ assert_nothing_raised(ActiveRecord::RecordNotFound) { Pirate.find(pirate.id) }
518
+ @ship.save
519
+ assert_raise(ActiveRecord::RecordNotFound) { Pirate.find(pirate.id) }
520
+ end
521
+
522
+ def test_should_automatically_enable_autosave_on_the_association
523
+ assert Ship.reflect_on_association(:pirate).options[:autosave]
524
+ end
525
+
526
+ def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
527
+ @pirate.delete
528
+ @ship.reload.attributes = { :update_only_pirate_attributes => { :catchphrase => 'Arr' } }
529
+
530
+ assert !@ship.update_only_pirate.persisted?
531
+ end
532
+
533
+ def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
534
+ @pirate.delete
535
+ @pirate = @ship.create_update_only_pirate(catchphrase: 'Aye')
536
+
537
+ @ship.update(update_only_pirate_attributes: { catchphrase: 'Arr' })
538
+ assert_equal 'Arr', @pirate.reload.catchphrase
539
+ assert_equal @pirate, @ship.reload.update_only_pirate
540
+ end
541
+
542
+ def test_should_update_existing_when_update_only_is_true_and_id_is_given
543
+ @pirate.delete
544
+ @pirate = @ship.create_update_only_pirate(catchphrase: 'Aye')
545
+
546
+ @ship.update(update_only_pirate_attributes: { catchphrase: 'Arr', id: @pirate.id })
547
+
548
+ assert_equal 'Arr', @pirate.reload.catchphrase
549
+ assert_equal @pirate, @ship.reload.update_only_pirate
550
+ end
551
+
552
+ def test_should_destroy_existing_when_update_only_is_true_and_id_is_given_and_is_marked_for_destruction
553
+ Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => true
554
+ @pirate.delete
555
+ @pirate = @ship.create_update_only_pirate(catchphrase: 'Aye')
556
+
557
+ @ship.update(update_only_pirate_attributes: { catchphrase: 'Arr', id: @pirate.id, _destroy: true })
558
+
559
+ assert_raise(ActiveRecord::RecordNotFound) { @pirate.reload }
560
+
561
+ Ship.accepts_nested_attributes_for :update_only_pirate, :update_only => true, :allow_destroy => false
562
+ end
563
+ end
564
+
565
+ module NestedAttributesOnACollectionAssociationTests
566
+ def test_should_define_an_attribute_writer_method_for_the_association
567
+ assert_respond_to @pirate, association_setter
568
+ end
569
+
570
+ def test_should_save_only_one_association_on_create
571
+ pirate = Pirate.create!({
572
+ :catchphrase => 'Arr',
573
+ association_getter => { 'foo' => { :name => 'Grace OMalley' } }
574
+ })
575
+
576
+ assert_equal 1, pirate.reload.send(@association_name).count
577
+ end
578
+
579
+ def test_should_take_a_hash_with_string_keys_and_assign_the_attributes_to_the_associated_models
580
+ @alternate_params[association_getter].stringify_keys!
581
+ @pirate.update @alternate_params
582
+ assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
583
+ end
584
+
585
+ def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
586
+ @pirate.send(association_setter, @alternate_params[association_getter].values)
587
+ @pirate.save
588
+ assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
589
+ end
590
+
591
+ def test_should_also_work_with_a_HashWithIndifferentAccess
592
+ @pirate.send(association_setter, ActiveSupport::HashWithIndifferentAccess.new('foo' => ActiveSupport::HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley')))
593
+ @pirate.save
594
+ assert_equal 'Grace OMalley', @child_1.reload.name
595
+ end
596
+
597
+ def test_should_take_a_hash_and_assign_the_attributes_to_the_associated_models
598
+ @pirate.attributes = @alternate_params
599
+ assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
600
+ assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
601
+ end
602
+
603
+ def test_should_not_load_association_when_updating_existing_records
604
+ @pirate.reload
605
+ @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
606
+ assert ! @pirate.send(@association_name).loaded?
607
+
608
+ @pirate.save
609
+ assert ! @pirate.send(@association_name).loaded?
610
+ assert_equal 'Grace OMalley', @child_1.reload.name
611
+ end
612
+
613
+ def test_should_not_overwrite_unsaved_updates_when_loading_association
614
+ @pirate.reload
615
+ @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
616
+ assert_equal 'Grace OMalley', @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.name
617
+ end
618
+
619
+ def test_should_preserve_order_when_not_overwriting_unsaved_updates
620
+ @pirate.reload
621
+ @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
622
+ assert_equal @child_1.id, @pirate.send(@association_name).send(:load_target).first.id
623
+ end
624
+
625
+ def test_should_refresh_saved_records_when_not_overwriting_unsaved_updates
626
+ @pirate.reload
627
+ record = @pirate.class.reflect_on_association(@association_name).klass.new(name: 'Grace OMalley')
628
+ @pirate.send(@association_name) << record
629
+ record.save!
630
+ @pirate.send(@association_name).last.update!(name: 'Polly')
631
+ assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name
632
+ end
633
+
634
+ def test_should_not_remove_scheduled_destroys_when_loading_association
635
+ @pirate.reload
636
+ @pirate.send(association_setter, [{ :id => @child_1.id, :_destroy => '1' }])
637
+ assert @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.marked_for_destruction?
638
+ end
639
+
640
+ def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
641
+ @child_1.stubs(:id).returns('ABC1X')
642
+ @child_2.stubs(:id).returns('ABC2X')
643
+
644
+ @pirate.attributes = {
645
+ association_getter => [
646
+ { :id => @child_1.id, :name => 'Grace OMalley' },
647
+ { :id => @child_2.id, :name => 'Privateers Greed' }
648
+ ]
649
+ }
650
+
651
+ assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
652
+ end
653
+
654
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
655
+ exception = assert_raise ActiveRecord::RecordNotFound do
656
+ @pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
657
+ end
658
+ assert_equal "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}", exception.message
659
+ end
660
+
661
+ def test_should_automatically_build_new_associated_models_for_each_entry_in_a_hash_where_the_id_is_missing
662
+ @pirate.send(@association_name).destroy_all
663
+ @pirate.reload.attributes = {
664
+ association_getter => { 'foo' => { :name => 'Grace OMalley' }, 'bar' => { :name => 'Privateers Greed' }}
665
+ }
666
+
667
+ assert !@pirate.send(@association_name).first.persisted?
668
+ assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
669
+
670
+ assert !@pirate.send(@association_name).last.persisted?
671
+ assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
672
+ end
673
+
674
+ def test_should_not_assign_destroy_key_to_a_record
675
+ assert_nothing_raised ActiveRecord::UnknownAttributeError do
676
+ @pirate.send(association_setter, { 'foo' => { '_destroy' => '0' }})
677
+ end
678
+ end
679
+
680
+ def test_should_ignore_new_associated_records_with_truthy_destroy_attribute
681
+ @pirate.send(@association_name).destroy_all
682
+ @pirate.reload.attributes = {
683
+ association_getter => {
684
+ 'foo' => { :name => 'Grace OMalley' },
685
+ 'bar' => { :name => 'Privateers Greed', '_destroy' => '1' }
686
+ }
687
+ }
688
+
689
+ assert_equal 1, @pirate.send(@association_name).length
690
+ assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
691
+ end
692
+
693
+ def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false
694
+ @alternate_params[association_getter]['baz'] = {}
695
+ assert_no_difference("@pirate.send(@association_name).count") do
696
+ @pirate.attributes = @alternate_params
697
+ end
698
+ end
699
+
700
+ def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models
701
+ attributes = {}
702
+ attributes['123726353'] = { :name => 'Grace OMalley' }
703
+ attributes['2'] = { :name => 'Privateers Greed' } # 2 is lower then 123726353
704
+ @pirate.send(association_setter, attributes)
705
+
706
+ assert_equal ['Posideons Killer', 'Killer bandita Dionne', 'Privateers Greed', 'Grace OMalley'].to_set, @pirate.send(@association_name).map(&:name).to_set
707
+ end
708
+
709
+ def test_should_raise_an_argument_error_if_something_else_than_a_hash_is_passed
710
+ assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, {}) }
711
+ assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, Hash.new) }
712
+
713
+ exception = assert_raise ArgumentError do
714
+ @pirate.send(association_setter, "foo")
715
+ end
716
+ assert_equal 'Hash or Array expected, got String ("foo")', exception.message
717
+ end
718
+
719
+ def test_should_work_with_update_as_well
720
+ @pirate.update(catchphrase: 'Arr',
721
+ association_getter => { 'foo' => { :id => @child_1.id, :name => 'Grace OMalley' }})
722
+
723
+ assert_equal 'Grace OMalley', @child_1.reload.name
724
+ end
725
+
726
+ def test_should_update_existing_records_and_add_new_ones_that_have_no_id
727
+ @alternate_params[association_getter]['baz'] = { name: 'Buccaneers Servant' }
728
+ assert_difference('@pirate.send(@association_name).count', +1) do
729
+ @pirate.update @alternate_params
730
+ end
731
+ assert_equal ['Grace OMalley', 'Privateers Greed', 'Buccaneers Servant'].to_set, @pirate.reload.send(@association_name).map(&:name).to_set
732
+ end
733
+
734
+ def test_should_be_possible_to_destroy_a_record
735
+ ['1', 1, 'true', true].each do |true_variable|
736
+ record = @pirate.reload.send(@association_name).create!(:name => 'Grace OMalley')
737
+ @pirate.send(association_setter,
738
+ @alternate_params[association_getter].merge('baz' => { :id => record.id, '_destroy' => true_variable })
739
+ )
740
+
741
+ assert_difference('@pirate.send(@association_name).count', -1) do
742
+ @pirate.save
743
+ end
744
+ end
745
+ end
746
+
747
+ def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument
748
+ [nil, '', '0', 0, 'false', false].each do |false_variable|
749
+ @alternate_params[association_getter]['foo']['_destroy'] = false_variable
750
+ assert_no_difference('@pirate.send(@association_name).count') do
751
+ @pirate.update(@alternate_params)
752
+ end
753
+ end
754
+ end
755
+
756
+ def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
757
+ assert_no_difference('@pirate.send(@association_name).count') do
758
+ @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_destroy' => true }))
759
+ end
760
+ assert_difference('@pirate.send(@association_name).count', -1) { @pirate.save }
761
+ end
762
+
763
+ def test_should_automatically_enable_autosave_on_the_association
764
+ assert Pirate.reflect_on_association(@association_name).options[:autosave]
765
+ end
766
+
767
+ def test_validate_presence_of_parent_works_with_inverse_of
768
+ Man.accepts_nested_attributes_for(:interests)
769
+ assert_equal :man, Man.reflect_on_association(:interests).options[:inverse_of]
770
+ assert_equal :interests, Interest.reflect_on_association(:man).options[:inverse_of]
771
+
772
+ repair_validations(Interest) do
773
+ Interest.validates_presence_of(:man)
774
+ assert_difference 'Man.count' do
775
+ assert_difference 'Interest.count', 2 do
776
+ man = Man.create!(:name => 'John',
777
+ :interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}])
778
+ assert_equal 2, man.interests.count
779
+ end
780
+ end
781
+ end
782
+ end
783
+
784
+ def test_can_use_symbols_as_object_identifier
785
+ @pirate.attributes = { :parrots_attributes => { :foo => { :name => 'Lovely Day' }, :bar => { :name => 'Blown Away' } } }
786
+ assert_nothing_raised(NoMethodError) { @pirate.save! }
787
+ end
788
+
789
+ def test_numeric_column_changes_from_zero_to_no_empty_string
790
+ Man.accepts_nested_attributes_for(:interests)
791
+
792
+ repair_validations(Interest) do
793
+ Interest.validates_numericality_of(:zine_id)
794
+ man = Man.create(name: 'John')
795
+ interest = man.interests.create(topic: 'bar', zine_id: 0)
796
+ assert interest.save
797
+ assert !man.update({interests_attributes: { id: interest.id, zine_id: 'foo' }})
798
+ end
799
+ end
800
+
801
+ private
802
+
803
+ def association_setter
804
+ @association_setter ||= "#{@association_name}_attributes=".to_sym
805
+ end
806
+
807
+ def association_getter
808
+ @association_getter ||= "#{@association_name}_attributes".to_sym
809
+ end
810
+ end
811
+
812
+ class TestNestedAttributesOnAHasManyAssociation < ActiveRecord::TestCase
813
+ def setup
814
+ @association_type = :has_many
815
+ @association_name = :birds
816
+
817
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
818
+ @pirate.birds.create!(:name => 'Posideons Killer')
819
+ @pirate.birds.create!(:name => 'Killer bandita Dionne')
820
+
821
+ @child_1, @child_2 = @pirate.birds
822
+
823
+ @alternate_params = {
824
+ :birds_attributes => {
825
+ 'foo' => { :id => @child_1.id, :name => 'Grace OMalley' },
826
+ 'bar' => { :id => @child_2.id, :name => 'Privateers Greed' }
827
+ }
828
+ }
829
+ end
830
+
831
+ include NestedAttributesOnACollectionAssociationTests
832
+ end
833
+
834
+ class TestNestedAttributesOnAHasAndBelongsToManyAssociation < ActiveRecord::TestCase
835
+ def setup
836
+ @association_type = :has_and_belongs_to_many
837
+ @association_name = :parrots
838
+
839
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
840
+ @pirate.parrots.create!(:name => 'Posideons Killer')
841
+ @pirate.parrots.create!(:name => 'Killer bandita Dionne')
842
+
843
+ @child_1, @child_2 = @pirate.parrots
844
+
845
+ @alternate_params = {
846
+ :parrots_attributes => {
847
+ 'foo' => { :id => @child_1.id, :name => 'Grace OMalley' },
848
+ 'bar' => { :id => @child_2.id, :name => 'Privateers Greed' }
849
+ }
850
+ }
851
+ end
852
+
853
+ include NestedAttributesOnACollectionAssociationTests
854
+ end
855
+
856
+ module NestedAttributesLimitTests
857
+ def teardown
858
+ Pirate.accepts_nested_attributes_for :parrots, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
859
+ end
860
+
861
+ def test_limit_with_less_records
862
+ @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Big Big Love' } } }
863
+ assert_difference('Parrot.count') { @pirate.save! }
864
+ end
865
+
866
+ def test_limit_with_number_exact_records
867
+ @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' }, 'bar' => { :name => 'Blown Away' } } }
868
+ assert_difference('Parrot.count', 2) { @pirate.save! }
869
+ end
870
+
871
+ def test_limit_with_exceeding_records
872
+ assert_raises(ActiveRecord::NestedAttributes::TooManyRecords) do
873
+ @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' },
874
+ 'bar' => { :name => 'Blown Away' },
875
+ 'car' => { :name => 'The Happening' }} }
876
+ end
877
+ end
878
+ end
879
+
880
+ class TestNestedAttributesLimitNumeric < ActiveRecord::TestCase
881
+ def setup
882
+ Pirate.accepts_nested_attributes_for :parrots, :limit => 2
883
+
884
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
885
+ end
886
+
887
+ include NestedAttributesLimitTests
888
+ end
889
+
890
+ class TestNestedAttributesLimitSymbol < ActiveRecord::TestCase
891
+ def setup
892
+ Pirate.accepts_nested_attributes_for :parrots, :limit => :parrots_limit
893
+
894
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?", :parrots_limit => 2)
895
+ end
896
+
897
+ include NestedAttributesLimitTests
898
+ end
899
+
900
+ class TestNestedAttributesLimitProc < ActiveRecord::TestCase
901
+ def setup
902
+ Pirate.accepts_nested_attributes_for :parrots, :limit => proc { 2 }
903
+
904
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
905
+ end
906
+
907
+ include NestedAttributesLimitTests
908
+ end
909
+
910
+ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase
911
+ fixtures :owners, :pets
912
+
913
+ def setup
914
+ Owner.accepts_nested_attributes_for :pets, :allow_destroy => true
915
+
916
+ @owner = owners(:ashley)
917
+ @pet1, @pet2 = pets(:chew), pets(:mochi)
918
+
919
+ @params = {
920
+ :pets_attributes => {
921
+ '0' => { :id => @pet1.id, :name => 'Foo' },
922
+ '1' => { :id => @pet2.id, :name => 'Bar' }
923
+ }
924
+ }
925
+ end
926
+
927
+ def test_should_update_existing_records_with_non_standard_primary_key
928
+ @owner.update(@params)
929
+ assert_equal ['Foo', 'Bar'], @owner.pets.map(&:name)
930
+ end
931
+
932
+ def test_attr_accessor_of_child_should_be_value_provided_during_update
933
+ @owner = owners(:ashley)
934
+ @pet1 = pets(:chew)
935
+ attributes = {:pets_attributes => { "1"=> { :id => @pet1.id,
936
+ :name => "Foo2",
937
+ :current_user => "John",
938
+ :_destroy=>true }}}
939
+ @owner.update(attributes)
940
+ assert_equal 'John', Pet.after_destroy_output
941
+ end
942
+
943
+ end
944
+
945
+ class TestHasOneAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
946
+ self.use_transactional_fixtures = false unless supports_savepoints?
947
+
948
+ def setup
949
+ @pirate = Pirate.create!(:catchphrase => "My baby takes tha mornin' train!")
950
+ @ship = @pirate.create_ship(:name => "The good ship Dollypop")
951
+ @part = @ship.parts.create!(:name => "Mast")
952
+ @trinket = @part.trinkets.create!(:name => "Necklace")
953
+ end
954
+
955
+ test "when great-grandchild changed in memory, saving parent should save great-grandchild" do
956
+ @trinket.name = "changed"
957
+ @pirate.save
958
+ assert_equal "changed", @trinket.reload.name
959
+ end
960
+
961
+ test "when great-grandchild changed via attributes, saving parent should save great-grandchild" do
962
+ @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}}
963
+ @pirate.save
964
+ assert_equal "changed", @trinket.reload.name
965
+ end
966
+
967
+ test "when great-grandchild marked_for_destruction via attributes, saving parent should destroy great-grandchild" do
968
+ @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}}
969
+ assert_difference('@part.trinkets.count', -1) { @pirate.save }
970
+ end
971
+
972
+ test "when great-grandchild added via attributes, saving parent should create great-grandchild" do
973
+ @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}}
974
+ assert_difference('@part.trinkets.count', 1) { @pirate.save }
975
+ end
976
+
977
+ test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
978
+ @trinket.name = "changed"
979
+ Ship.create!(:pirate => @pirate, :name => "The Black Rock")
980
+ ShipPart.create!(:ship => @ship, :name => "Stern")
981
+ assert_no_queries { @pirate.valid? }
982
+ end
983
+ end
984
+
985
+ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
986
+ self.use_transactional_fixtures = false unless supports_savepoints?
987
+
988
+ def setup
989
+ @ship = Ship.create!(:name => "The good ship Dollypop")
990
+ @part = @ship.parts.create!(:name => "Mast")
991
+ @trinket = @part.trinkets.create!(:name => "Necklace")
992
+ end
993
+
994
+ test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do
995
+ @ship.parts_attributes=[{:id => @part.id,:name =>'Deck'}]
996
+ assert_equal 1, @ship.association(:parts).target.size
997
+ assert_equal 'Deck', @ship.parts[0].name
998
+ end
999
+
1000
+ test "if association is not loaded and child doesn't change and I am saving a grandchild then in memory record should be used" do
1001
+ @ship.parts_attributes=[{:id => @part.id,:trinkets_attributes =>[{:id => @trinket.id, :name => 'Ruby'}]}]
1002
+ assert_equal 1, @ship.association(:parts).target.size
1003
+ assert_equal 'Mast', @ship.parts[0].name
1004
+ assert_no_difference("@ship.parts[0].association(:trinkets).target.size") do
1005
+ @ship.parts[0].association(:trinkets).target.size
1006
+ end
1007
+ assert_equal 'Ruby', @ship.parts[0].trinkets[0].name
1008
+ @ship.save
1009
+ assert_equal 'Ruby', @ship.parts[0].trinkets[0].name
1010
+ end
1011
+
1012
+ test "when grandchild changed in memory, saving parent should save grandchild" do
1013
+ @trinket.name = "changed"
1014
+ @ship.save
1015
+ assert_equal "changed", @trinket.reload.name
1016
+ end
1017
+
1018
+ test "when grandchild changed via attributes, saving parent should save grandchild" do
1019
+ @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}
1020
+ @ship.save
1021
+ assert_equal "changed", @trinket.reload.name
1022
+ end
1023
+
1024
+ test "when grandchild marked_for_destruction via attributes, saving parent should destroy grandchild" do
1025
+ @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}
1026
+ assert_difference('@part.trinkets.count', -1) { @ship.save }
1027
+ end
1028
+
1029
+ test "when grandchild added via attributes, saving parent should create grandchild" do
1030
+ @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}
1031
+ assert_difference('@part.trinkets.count', 1) { @ship.save }
1032
+ end
1033
+
1034
+ test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
1035
+ @trinket.name = "changed"
1036
+ Ship.create!(:name => "The Black Rock")
1037
+ ShipPart.create!(:ship => @ship, :name => "Stern")
1038
+ assert_no_queries { @ship.valid? }
1039
+ end
1040
+
1041
+ test "circular references do not perform unnecessary queries" do
1042
+ ship = Ship.new(name: "The Black Rock")
1043
+ part = ship.parts.build(name: "Stern")
1044
+ ship.treasures.build(looter: part)
1045
+
1046
+ assert_queries 3 do
1047
+ ship.save!
1048
+ end
1049
+ end
1050
+
1051
+ test "nested singular associations are validated" do
1052
+ part = ShipPart.new(name: "Stern", ship_attributes: { name: nil })
1053
+
1054
+ assert_not part.valid?
1055
+ assert_equal ["Ship name can't be blank"], part.errors.full_messages
1056
+ end
1057
+ end