ibm_db 3.0.5-x86-mingw32 → 4.0.0-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (586) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +4 -0
  3. data/LICENSE +1 -1
  4. data/MANIFEST +14 -14
  5. data/ParameterizedQueries README +6 -6
  6. data/README +208 -225
  7. data/ext/Makefile.nt32 +181 -181
  8. data/ext/Makefile.nt32.191 +212 -212
  9. data/ext/extconf.rb +291 -291
  10. data/ext/ibm_db.c +11887 -11887
  11. data/ext/ruby_ibm_db.h +241 -241
  12. data/ext/ruby_ibm_db_cli.c +866 -866
  13. data/ext/ruby_ibm_db_cli.h +500 -500
  14. data/init.rb +41 -41
  15. data/lib/IBM_DB.rb +27 -27
  16. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3452 -3177
  17. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +5 -2
  18. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  19. data/lib/mswin32/ibm_db.rb +91 -123
  20. data/lib/mswin32/rb2x/i386/ibm_db.so +0 -0
  21. data/test/active_record/connection_adapters/fake_adapter.rb +49 -46
  22. data/test/assets/example.log +1 -1
  23. data/test/assets/test.txt +1 -1
  24. data/test/cases/adapter_test.rb +351 -276
  25. data/test/cases/adapters/mysql2/active_schema_test.rb +193 -0
  26. data/test/cases/adapters/mysql2/bind_parameter_test.rb +50 -0
  27. data/test/cases/adapters/mysql2/boolean_test.rb +100 -0
  28. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +63 -0
  29. data/test/cases/adapters/mysql2/charset_collation_test.rb +54 -0
  30. data/test/cases/adapters/mysql2/connection_test.rb +210 -0
  31. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +45 -0
  32. data/test/cases/adapters/mysql2/enum_test.rb +26 -0
  33. data/test/cases/adapters/mysql2/explain_test.rb +21 -0
  34. data/test/cases/adapters/mysql2/json_test.rb +195 -0
  35. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +83 -0
  36. data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -0
  37. data/test/cases/adapters/mysql2/schema_migrations_test.rb +59 -0
  38. data/test/cases/adapters/mysql2/schema_test.rb +126 -0
  39. data/test/cases/adapters/mysql2/sp_test.rb +36 -0
  40. data/test/cases/adapters/mysql2/sql_types_test.rb +14 -0
  41. data/test/cases/adapters/mysql2/table_options_test.rb +42 -0
  42. data/test/cases/adapters/mysql2/unsigned_type_test.rb +66 -0
  43. data/test/cases/adapters/postgresql/active_schema_test.rb +98 -0
  44. data/test/cases/adapters/postgresql/array_test.rb +339 -0
  45. data/test/cases/adapters/postgresql/bit_string_test.rb +82 -0
  46. data/test/cases/adapters/postgresql/bytea_test.rb +134 -0
  47. data/test/cases/adapters/postgresql/case_insensitive_test.rb +26 -0
  48. data/test/cases/adapters/postgresql/change_schema_test.rb +38 -0
  49. data/test/cases/adapters/postgresql/cidr_test.rb +25 -0
  50. data/test/cases/adapters/postgresql/citext_test.rb +78 -0
  51. data/test/cases/adapters/postgresql/collation_test.rb +53 -0
  52. data/test/cases/adapters/postgresql/composite_test.rb +132 -0
  53. data/test/cases/adapters/postgresql/connection_test.rb +257 -0
  54. data/test/cases/adapters/postgresql/datatype_test.rb +92 -0
  55. data/test/cases/adapters/postgresql/domain_test.rb +47 -0
  56. data/test/cases/adapters/postgresql/enum_test.rb +91 -0
  57. data/test/cases/adapters/postgresql/explain_test.rb +20 -0
  58. data/test/cases/adapters/postgresql/extension_migration_test.rb +63 -0
  59. data/test/cases/adapters/postgresql/full_text_test.rb +44 -0
  60. data/test/cases/adapters/postgresql/geometric_test.rb +378 -0
  61. data/test/cases/adapters/postgresql/hstore_test.rb +382 -0
  62. data/test/cases/adapters/postgresql/infinity_test.rb +69 -0
  63. data/test/cases/adapters/postgresql/integer_test.rb +25 -0
  64. data/test/cases/adapters/postgresql/json_test.rb +237 -0
  65. data/test/cases/adapters/postgresql/ltree_test.rb +53 -0
  66. data/test/cases/adapters/postgresql/money_test.rb +96 -0
  67. data/test/cases/adapters/postgresql/network_test.rb +94 -0
  68. data/test/cases/adapters/postgresql/numbers_test.rb +49 -0
  69. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +405 -0
  70. data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -0
  71. data/test/cases/adapters/postgresql/quoting_test.rb +44 -0
  72. data/test/cases/adapters/postgresql/range_test.rb +343 -0
  73. data/test/cases/adapters/postgresql/referential_integrity_test.rb +111 -0
  74. data/test/cases/adapters/postgresql/rename_table_test.rb +34 -0
  75. data/test/cases/adapters/postgresql/schema_authorization_test.rb +119 -0
  76. data/test/cases/adapters/postgresql/schema_test.rb +597 -0
  77. data/test/cases/adapters/postgresql/serial_test.rb +154 -0
  78. data/test/cases/adapters/postgresql/statement_pool_test.rb +41 -0
  79. data/test/cases/adapters/postgresql/timestamp_test.rb +90 -0
  80. data/test/cases/adapters/postgresql/type_lookup_test.rb +33 -0
  81. data/test/cases/adapters/postgresql/utils_test.rb +62 -0
  82. data/test/cases/adapters/postgresql/uuid_test.rb +294 -0
  83. data/test/cases/adapters/postgresql/xml_test.rb +54 -0
  84. data/test/cases/adapters/sqlite3/collation_test.rb +53 -0
  85. data/test/cases/adapters/sqlite3/copy_table_test.rb +98 -0
  86. data/test/cases/adapters/sqlite3/explain_test.rb +21 -0
  87. data/test/cases/adapters/sqlite3/quoting_test.rb +101 -0
  88. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +441 -0
  89. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -0
  90. data/test/cases/adapters/sqlite3/statement_pool_test.rb +20 -0
  91. data/test/cases/aggregations_test.rb +168 -158
  92. data/test/cases/ar_schema_test.rb +146 -161
  93. data/test/cases/associations/association_scope_test.rb +16 -21
  94. data/test/cases/associations/belongs_to_associations_test.rb +1141 -1029
  95. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +41 -0
  96. data/test/cases/associations/callbacks_test.rb +190 -192
  97. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -188
  98. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -36
  99. data/test/cases/associations/eager_load_nested_include_test.rb +126 -128
  100. data/test/cases/associations/eager_singularization_test.rb +148 -148
  101. data/test/cases/associations/eager_test.rb +1514 -1429
  102. data/test/cases/associations/extension_test.rb +87 -82
  103. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1004 -972
  104. data/test/cases/associations/has_many_associations_test.rb +2501 -2182
  105. data/test/cases/associations/has_many_through_associations_test.rb +1271 -1204
  106. data/test/cases/associations/has_one_associations_test.rb +707 -610
  107. data/test/cases/associations/has_one_through_associations_test.rb +383 -380
  108. data/test/cases/associations/inner_join_association_test.rb +139 -139
  109. data/test/cases/associations/inverse_associations_test.rb +733 -706
  110. data/test/cases/associations/join_model_test.rb +777 -754
  111. data/test/cases/associations/left_outer_join_association_test.rb +88 -0
  112. data/test/cases/associations/nested_through_associations_test.rb +579 -579
  113. data/test/cases/associations/required_test.rb +102 -82
  114. data/test/cases/associations_test.rb +385 -380
  115. data/test/cases/attribute_decorators_test.rb +125 -125
  116. data/test/cases/attribute_methods/read_test.rb +60 -60
  117. data/test/cases/attribute_methods_test.rb +1009 -952
  118. data/test/cases/attribute_set_test.rb +270 -210
  119. data/test/cases/attribute_test.rb +246 -180
  120. data/test/cases/attributes_test.rb +253 -136
  121. data/test/cases/autosave_association_test.rb +1708 -1595
  122. data/test/cases/base_test.rb +1713 -1664
  123. data/test/cases/batches_test.rb +489 -212
  124. data/test/cases/binary_test.rb +44 -52
  125. data/test/cases/bind_parameter_test.rb +110 -100
  126. data/test/cases/cache_key_test.rb +25 -0
  127. data/test/cases/calculations_test.rb +798 -646
  128. data/test/cases/callbacks_test.rb +636 -543
  129. data/test/cases/clone_test.rb +40 -40
  130. data/test/cases/coders/json_test.rb +15 -0
  131. data/test/cases/coders/yaml_column_test.rb +63 -63
  132. data/test/cases/collection_cache_key_test.rb +115 -0
  133. data/test/cases/column_alias_test.rb +17 -17
  134. data/test/cases/column_definition_test.rb +92 -123
  135. data/test/cases/comment_test.rb +143 -0
  136. data/test/cases/connection_adapters/adapter_leasing_test.rb +56 -54
  137. data/test/cases/connection_adapters/connection_handler_test.rb +160 -53
  138. data/test/cases/connection_adapters/connection_specification_test.rb +12 -12
  139. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +255 -293
  140. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +69 -65
  141. data/test/cases/connection_adapters/quoting_test.rb +13 -13
  142. data/test/cases/connection_adapters/schema_cache_test.rb +61 -56
  143. data/test/cases/connection_adapters/type_lookup_test.rb +118 -110
  144. data/test/cases/connection_management_test.rb +112 -122
  145. data/test/cases/connection_pool_test.rb +521 -346
  146. data/test/cases/connection_specification/resolver_test.rb +131 -116
  147. data/test/cases/core_test.rb +112 -112
  148. data/test/cases/counter_cache_test.rb +214 -209
  149. data/test/cases/custom_locking_test.rb +17 -17
  150. data/test/cases/database_statements_test.rb +34 -19
  151. data/test/cases/{invalid_date_test.rb → date_test.rb} +44 -32
  152. data/test/cases/date_time_precision_test.rb +106 -0
  153. data/test/cases/date_time_test.rb +61 -61
  154. data/test/cases/defaults_test.rb +218 -223
  155. data/test/cases/dirty_test.rb +763 -785
  156. data/test/cases/disconnected_test.rb +30 -28
  157. data/test/cases/dup_test.rb +157 -157
  158. data/test/cases/enum_test.rb +444 -290
  159. data/test/cases/errors_test.rb +16 -0
  160. data/test/cases/explain_subscriber_test.rb +64 -64
  161. data/test/cases/explain_test.rb +87 -76
  162. data/test/cases/finder_respond_to_test.rb +60 -60
  163. data/test/cases/finder_test.rb +1294 -1169
  164. data/test/cases/fixture_set/file_test.rb +156 -138
  165. data/test/cases/fixtures_test.rb +988 -908
  166. data/test/cases/forbidden_attributes_protection_test.rb +165 -99
  167. data/test/cases/habtm_destroy_order_test.rb +61 -61
  168. data/test/cases/helper.rb +204 -210
  169. data/test/cases/hot_compatibility_test.rb +142 -54
  170. data/test/cases/i18n_test.rb +45 -45
  171. data/test/cases/inheritance_test.rb +606 -375
  172. data/test/cases/integration_test.rb +155 -139
  173. data/test/cases/invalid_connection_test.rb +24 -22
  174. data/test/cases/invertible_migration_test.rb +387 -295
  175. data/test/cases/json_serialization_test.rb +311 -302
  176. data/test/cases/locking_test.rb +493 -477
  177. data/test/cases/log_subscriber_test.rb +225 -136
  178. data/test/cases/migration/change_schema_test.rb +458 -512
  179. data/test/cases/migration/change_table_test.rb +256 -224
  180. data/test/cases/migration/column_attributes_test.rb +176 -192
  181. data/test/cases/migration/column_positioning_test.rb +56 -56
  182. data/test/cases/migration/columns_test.rb +310 -304
  183. data/test/cases/migration/command_recorder_test.rb +350 -305
  184. data/test/cases/migration/compatibility_test.rb +118 -0
  185. data/test/cases/migration/create_join_table_test.rb +157 -148
  186. data/test/cases/migration/foreign_key_test.rb +360 -328
  187. data/test/cases/migration/helper.rb +39 -39
  188. data/test/cases/migration/index_test.rb +218 -216
  189. data/test/cases/migration/logger_test.rb +36 -36
  190. data/test/cases/migration/pending_migrations_test.rb +52 -53
  191. data/test/cases/migration/references_foreign_key_test.rb +216 -169
  192. data/test/cases/migration/references_index_test.rb +101 -101
  193. data/test/cases/migration/references_statements_test.rb +136 -116
  194. data/test/cases/migration/rename_table_test.rb +93 -93
  195. data/test/cases/migration_test.rb +1157 -959
  196. data/test/cases/migrator_test.rb +470 -388
  197. data/test/cases/mixin_test.rb +68 -70
  198. data/test/cases/modules_test.rb +172 -173
  199. data/test/cases/multiparameter_attributes_test.rb +372 -350
  200. data/test/cases/multiple_db_test.rb +122 -115
  201. data/test/cases/nested_attributes_test.rb +1098 -1070
  202. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -144
  203. data/test/cases/persistence_test.rb +1001 -909
  204. data/test/cases/pooled_connections_test.rb +81 -81
  205. data/test/cases/primary_keys_test.rb +376 -237
  206. data/test/cases/query_cache_test.rb +446 -326
  207. data/test/cases/quoting_test.rb +202 -156
  208. data/test/cases/readonly_test.rb +119 -118
  209. data/test/cases/reaper_test.rb +85 -85
  210. data/test/cases/reflection_test.rb +509 -463
  211. data/test/cases/relation/delegation_test.rb +63 -68
  212. data/test/cases/relation/merging_test.rb +157 -161
  213. data/test/cases/relation/mutation_test.rb +183 -165
  214. data/test/cases/relation/or_test.rb +92 -0
  215. data/test/cases/relation/predicate_builder_test.rb +16 -14
  216. data/test/cases/relation/record_fetch_warning_test.rb +40 -0
  217. data/test/cases/relation/where_chain_test.rb +105 -181
  218. data/test/cases/relation/where_clause_test.rb +182 -0
  219. data/test/cases/relation/where_test.rb +322 -300
  220. data/test/cases/relation_test.rb +328 -319
  221. data/test/cases/relations_test.rb +2026 -1815
  222. data/test/cases/reload_models_test.rb +22 -22
  223. data/test/cases/result_test.rb +90 -80
  224. data/test/cases/sanitize_test.rb +176 -83
  225. data/test/cases/schema_dumper_test.rb +457 -463
  226. data/test/cases/schema_loading_test.rb +52 -0
  227. data/test/cases/scoping/default_scoping_test.rb +528 -454
  228. data/test/cases/scoping/named_scoping_test.rb +561 -524
  229. data/test/cases/scoping/relation_scoping_test.rb +400 -357
  230. data/test/cases/secure_token_test.rb +32 -0
  231. data/test/cases/serialization_test.rb +104 -104
  232. data/test/cases/serialized_attribute_test.rb +364 -277
  233. data/test/cases/statement_cache_test.rb +136 -98
  234. data/test/cases/store_test.rb +195 -194
  235. data/test/cases/suppressor_test.rb +63 -0
  236. data/test/cases/tasks/database_tasks_test.rb +462 -398
  237. data/test/cases/tasks/mysql_rake_test.rb +345 -324
  238. data/test/cases/tasks/postgresql_rake_test.rb +304 -250
  239. data/test/cases/tasks/sqlite_rake_test.rb +220 -193
  240. data/test/cases/test_case.rb +131 -123
  241. data/test/cases/test_fixtures_test.rb +36 -0
  242. data/test/cases/time_precision_test.rb +102 -0
  243. data/test/cases/timestamp_test.rb +501 -467
  244. data/test/cases/touch_later_test.rb +121 -0
  245. data/test/cases/transaction_callbacks_test.rb +518 -452
  246. data/test/cases/transaction_isolation_test.rb +106 -106
  247. data/test/cases/transactions_test.rb +834 -817
  248. data/test/cases/type/adapter_specific_registry_test.rb +133 -0
  249. data/test/cases/type/date_time_test.rb +14 -0
  250. data/test/cases/type/integer_test.rb +27 -121
  251. data/test/cases/type/string_test.rb +22 -36
  252. data/test/cases/type/type_map_test.rb +177 -177
  253. data/test/cases/type_test.rb +39 -0
  254. data/test/cases/types_test.rb +24 -141
  255. data/test/cases/unconnected_test.rb +33 -33
  256. data/test/cases/validations/absence_validation_test.rb +73 -0
  257. data/test/cases/validations/association_validation_test.rb +97 -86
  258. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -84
  259. data/test/cases/validations/i18n_validation_test.rb +86 -90
  260. data/test/cases/validations/length_validation_test.rb +79 -47
  261. data/test/cases/validations/presence_validation_test.rb +103 -68
  262. data/test/cases/validations/uniqueness_validation_test.rb +548 -457
  263. data/test/cases/validations_repair_helper.rb +19 -23
  264. data/test/cases/validations_test.rb +194 -165
  265. data/test/cases/view_test.rb +216 -119
  266. data/test/cases/yaml_serialization_test.rb +121 -126
  267. data/test/config.example.yml +97 -0
  268. data/test/config.rb +5 -5
  269. data/test/fixtures/accounts.yml +29 -29
  270. data/test/fixtures/admin/accounts.yml +2 -2
  271. data/test/fixtures/admin/users.yml +10 -10
  272. data/test/fixtures/author_addresses.original +11 -0
  273. data/test/fixtures/author_addresses.yml +17 -17
  274. data/test/fixtures/author_favorites.yml +3 -3
  275. data/test/fixtures/authors.original +17 -0
  276. data/test/fixtures/authors.yml +23 -23
  277. data/test/fixtures/bad_posts.yml +9 -0
  278. data/test/fixtures/binaries.yml +133 -133
  279. data/test/fixtures/books.yml +31 -11
  280. data/test/fixtures/bulbs.yml +5 -5
  281. data/test/fixtures/cars.yml +9 -9
  282. data/test/fixtures/categories.yml +19 -19
  283. data/test/fixtures/categories/special_categories.yml +9 -9
  284. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -4
  285. data/test/fixtures/categories_ordered.yml +7 -7
  286. data/test/fixtures/categories_posts.yml +31 -31
  287. data/test/fixtures/categorizations.yml +23 -23
  288. data/test/fixtures/clubs.yml +8 -8
  289. data/test/fixtures/collections.yml +3 -3
  290. data/test/fixtures/colleges.yml +3 -3
  291. data/test/fixtures/comments.yml +65 -65
  292. data/test/fixtures/companies.yml +67 -67
  293. data/test/fixtures/computers.yml +10 -10
  294. data/test/fixtures/content.yml +3 -0
  295. data/test/fixtures/content_positions.yml +3 -0
  296. data/test/fixtures/courses.yml +8 -8
  297. data/test/fixtures/customers.yml +25 -25
  298. data/test/fixtures/dashboards.yml +6 -6
  299. data/test/fixtures/dead_parrots.yml +5 -0
  300. data/test/fixtures/developers.yml +22 -22
  301. data/test/fixtures/developers_projects.yml +16 -16
  302. data/test/fixtures/dog_lovers.yml +7 -7
  303. data/test/fixtures/dogs.yml +4 -4
  304. data/test/fixtures/doubloons.yml +3 -3
  305. data/test/fixtures/edges.yml +5 -5
  306. data/test/fixtures/entrants.yml +14 -14
  307. data/test/fixtures/essays.yml +6 -6
  308. data/test/fixtures/faces.yml +11 -11
  309. data/test/fixtures/fk_test_has_fk.yml +3 -3
  310. data/test/fixtures/fk_test_has_pk.yml +1 -1
  311. data/test/fixtures/friendships.yml +4 -4
  312. data/test/fixtures/funny_jokes.yml +10 -10
  313. data/test/fixtures/interests.yml +33 -33
  314. data/test/fixtures/items.yml +3 -3
  315. data/test/fixtures/jobs.yml +7 -7
  316. data/test/fixtures/legacy_things.yml +3 -3
  317. data/test/fixtures/live_parrots.yml +4 -0
  318. data/test/fixtures/mateys.yml +4 -4
  319. data/test/fixtures/member_details.yml +8 -8
  320. data/test/fixtures/member_types.yml +6 -6
  321. data/test/fixtures/members.yml +11 -11
  322. data/test/fixtures/memberships.yml +34 -34
  323. data/test/fixtures/men.yml +5 -5
  324. data/test/fixtures/minimalistics.yml +2 -2
  325. data/test/fixtures/minivans.yml +5 -5
  326. data/test/fixtures/mixed_case_monkeys.yml +6 -6
  327. data/test/fixtures/mixins.yml +29 -29
  328. data/test/fixtures/movies.yml +7 -7
  329. data/test/fixtures/naked/yml/accounts.yml +1 -1
  330. data/test/fixtures/naked/yml/companies.yml +1 -1
  331. data/test/fixtures/naked/yml/courses.yml +1 -1
  332. data/test/fixtures/naked/yml/parrots.yml +2 -0
  333. data/test/fixtures/naked/yml/trees.yml +3 -0
  334. data/test/fixtures/nodes.yml +29 -0
  335. data/test/fixtures/organizations.yml +5 -5
  336. data/test/fixtures/other_comments.yml +6 -0
  337. data/test/fixtures/other_dogs.yml +2 -0
  338. data/test/fixtures/other_posts.yml +7 -0
  339. data/test/fixtures/other_topics.yml +42 -42
  340. data/test/fixtures/owners.yml +9 -9
  341. data/test/fixtures/parrots.yml +27 -27
  342. data/test/fixtures/parrots_pirates.yml +7 -7
  343. data/test/fixtures/people.yml +24 -24
  344. data/test/fixtures/peoples_treasures.yml +3 -3
  345. data/test/fixtures/pets.yml +19 -19
  346. data/test/fixtures/pirates.yml +15 -12
  347. data/test/fixtures/posts.yml +80 -80
  348. data/test/fixtures/price_estimates.yml +16 -7
  349. data/test/fixtures/products.yml +4 -4
  350. data/test/fixtures/projects.yml +7 -7
  351. data/test/fixtures/ratings.yml +14 -14
  352. data/test/fixtures/readers.yml +11 -11
  353. data/test/fixtures/references.yml +17 -17
  354. data/test/fixtures/reserved_words/distinct.yml +5 -5
  355. data/test/fixtures/reserved_words/distinct_select.yml +11 -11
  356. data/test/fixtures/reserved_words/group.yml +14 -14
  357. data/test/fixtures/reserved_words/select.yml +8 -8
  358. data/test/fixtures/reserved_words/values.yml +7 -7
  359. data/test/fixtures/ships.yml +6 -6
  360. data/test/fixtures/speedometers.yml +8 -8
  361. data/test/fixtures/sponsors.yml +12 -12
  362. data/test/fixtures/string_key_objects.yml +7 -7
  363. data/test/fixtures/subscribers.yml +10 -10
  364. data/test/fixtures/subscriptions.yml +12 -12
  365. data/test/fixtures/taggings.yml +78 -78
  366. data/test/fixtures/tags.yml +11 -11
  367. data/test/fixtures/tasks.yml +7 -7
  368. data/test/fixtures/teapots.yml +3 -3
  369. data/test/fixtures/to_be_linked/accounts.yml +2 -2
  370. data/test/fixtures/to_be_linked/users.yml +10 -10
  371. data/test/fixtures/topics.yml +49 -49
  372. data/test/fixtures/toys.yml +14 -14
  373. data/test/fixtures/traffic_lights.yml +9 -9
  374. data/test/fixtures/treasures.yml +10 -10
  375. data/test/fixtures/trees.yml +3 -0
  376. data/test/fixtures/uuid_children.yml +3 -3
  377. data/test/fixtures/uuid_parents.yml +2 -2
  378. data/test/fixtures/variants.yml +4 -4
  379. data/test/fixtures/vegetables.yml +19 -19
  380. data/test/fixtures/vertices.yml +3 -3
  381. data/test/fixtures/warehouse_things.yml +2 -2
  382. data/test/fixtures/zines.yml +5 -5
  383. data/test/migrations/10_urban/9_add_expressions.rb +11 -11
  384. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -15
  385. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -12
  386. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -9
  387. data/test/migrations/missing/1_people_have_last_names.rb +9 -9
  388. data/test/migrations/missing/3_we_need_reminders.rb +12 -12
  389. data/test/migrations/missing/4_innocent_jointable.rb +12 -12
  390. data/test/migrations/rename/1_we_need_things.rb +11 -11
  391. data/test/migrations/rename/2_rename_things.rb +9 -9
  392. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -9
  393. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -9
  394. data/test/migrations/to_copy2/1_create_articles.rb +7 -7
  395. data/test/migrations/to_copy2/2_create_comments.rb +7 -7
  396. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -9
  397. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -9
  398. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -9
  399. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -7
  400. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -7
  401. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -9
  402. data/test/migrations/valid/2_we_need_reminders.rb +12 -12
  403. data/test/migrations/valid/3_innocent_jointable.rb +12 -12
  404. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -9
  405. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -12
  406. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -12
  407. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -9
  408. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -12
  409. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -12
  410. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -8
  411. data/test/models/admin.rb +5 -5
  412. data/test/models/admin/account.rb +3 -3
  413. data/test/models/admin/randomly_named_c1.rb +6 -2
  414. data/test/models/admin/user.rb +40 -40
  415. data/test/models/aircraft.rb +5 -4
  416. data/test/models/arunit2_model.rb +3 -3
  417. data/test/models/author.rb +209 -212
  418. data/test/models/auto_id.rb +4 -4
  419. data/test/models/autoloadable/extra_firm.rb +2 -2
  420. data/test/models/binary.rb +2 -2
  421. data/test/models/bird.rb +12 -12
  422. data/test/models/book.rb +23 -18
  423. data/test/models/boolean.rb +2 -2
  424. data/test/models/bulb.rb +52 -51
  425. data/test/models/cake_designer.rb +3 -3
  426. data/test/models/car.rb +29 -26
  427. data/test/models/carrier.rb +2 -2
  428. data/test/models/cat.rb +10 -0
  429. data/test/models/categorization.rb +19 -19
  430. data/test/models/category.rb +35 -35
  431. data/test/models/chef.rb +8 -7
  432. data/test/models/citation.rb +3 -3
  433. data/test/models/club.rb +25 -23
  434. data/test/models/college.rb +10 -10
  435. data/test/models/column.rb +3 -3
  436. data/test/models/column_name.rb +3 -3
  437. data/test/models/comment.rb +76 -64
  438. data/test/models/company.rb +230 -228
  439. data/test/models/company_in_module.rb +98 -98
  440. data/test/models/computer.rb +3 -3
  441. data/test/models/contact.rb +41 -41
  442. data/test/models/content.rb +40 -0
  443. data/test/models/contract.rb +20 -20
  444. data/test/models/country.rb +7 -7
  445. data/test/models/course.rb +6 -6
  446. data/test/models/customer.rb +83 -77
  447. data/test/models/customer_carrier.rb +14 -14
  448. data/test/models/dashboard.rb +3 -3
  449. data/test/models/default.rb +2 -2
  450. data/test/models/department.rb +4 -4
  451. data/test/models/developer.rb +274 -255
  452. data/test/models/dog.rb +5 -5
  453. data/test/models/dog_lover.rb +5 -5
  454. data/test/models/doubloon.rb +12 -12
  455. data/test/models/drink_designer.rb +3 -3
  456. data/test/models/edge.rb +5 -5
  457. data/test/models/electron.rb +5 -5
  458. data/test/models/engine.rb +4 -4
  459. data/test/models/entrant.rb +3 -3
  460. data/test/models/essay.rb +5 -5
  461. data/test/models/event.rb +3 -3
  462. data/test/models/eye.rb +37 -37
  463. data/test/models/face.rb +9 -9
  464. data/test/models/friendship.rb +6 -6
  465. data/test/models/guid.rb +2 -2
  466. data/test/models/guitar.rb +4 -0
  467. data/test/models/hotel.rb +11 -9
  468. data/test/models/image.rb +3 -3
  469. data/test/models/interest.rb +5 -5
  470. data/test/models/invoice.rb +4 -4
  471. data/test/models/item.rb +7 -7
  472. data/test/models/job.rb +7 -7
  473. data/test/models/joke.rb +7 -7
  474. data/test/models/keyboard.rb +3 -3
  475. data/test/models/legacy_thing.rb +3 -3
  476. data/test/models/lesson.rb +11 -11
  477. data/test/models/line_item.rb +3 -3
  478. data/test/models/liquid.rb +4 -4
  479. data/test/models/man.rb +11 -11
  480. data/test/models/matey.rb +4 -4
  481. data/test/models/member.rb +42 -41
  482. data/test/models/member_detail.rb +8 -7
  483. data/test/models/member_type.rb +3 -3
  484. data/test/models/membership.rb +35 -35
  485. data/test/models/mentor.rb +3 -0
  486. data/test/models/minimalistic.rb +2 -2
  487. data/test/models/minivan.rb +9 -9
  488. data/test/models/mixed_case_monkey.rb +3 -3
  489. data/test/models/mocktail_designer.rb +2 -0
  490. data/test/models/molecule.rb +6 -6
  491. data/test/models/movie.rb +5 -5
  492. data/test/models/node.rb +5 -0
  493. data/test/models/non_primary_key.rb +2 -0
  494. data/test/models/notification.rb +3 -0
  495. data/test/models/order.rb +4 -4
  496. data/test/models/organization.rb +14 -14
  497. data/test/models/other_dog.rb +5 -0
  498. data/test/models/owner.rb +37 -34
  499. data/test/models/parrot.rb +28 -29
  500. data/test/models/person.rb +142 -143
  501. data/test/models/personal_legacy_thing.rb +4 -4
  502. data/test/models/pet.rb +18 -15
  503. data/test/models/pet_treasure.rb +6 -0
  504. data/test/models/pirate.rb +92 -92
  505. data/test/models/possession.rb +3 -3
  506. data/test/models/post.rb +273 -264
  507. data/test/models/price_estimate.rb +4 -4
  508. data/test/models/professor.rb +5 -5
  509. data/test/models/project.rb +40 -31
  510. data/test/models/publisher.rb +2 -2
  511. data/test/models/publisher/article.rb +4 -4
  512. data/test/models/publisher/magazine.rb +3 -3
  513. data/test/models/randomly_named_c1.rb +1 -1
  514. data/test/models/rating.rb +4 -4
  515. data/test/models/reader.rb +23 -23
  516. data/test/models/recipe.rb +3 -0
  517. data/test/models/record.rb +2 -2
  518. data/test/models/reference.rb +22 -22
  519. data/test/models/reply.rb +61 -61
  520. data/test/models/ship.rb +39 -33
  521. data/test/models/ship_part.rb +8 -8
  522. data/test/models/shop.rb +17 -17
  523. data/test/models/shop_account.rb +6 -6
  524. data/test/models/speedometer.rb +6 -6
  525. data/test/models/sponsor.rb +7 -7
  526. data/test/models/string_key_object.rb +3 -3
  527. data/test/models/student.rb +4 -4
  528. data/test/models/subject.rb +16 -16
  529. data/test/models/subscriber.rb +8 -8
  530. data/test/models/subscription.rb +4 -4
  531. data/test/models/tag.rb +13 -7
  532. data/test/models/tagging.rb +13 -13
  533. data/test/models/task.rb +5 -5
  534. data/test/models/topic.rb +118 -124
  535. data/test/models/toy.rb +6 -6
  536. data/test/models/traffic_light.rb +4 -4
  537. data/test/models/treasure.rb +14 -14
  538. data/test/models/treaty.rb +7 -7
  539. data/test/models/tree.rb +3 -0
  540. data/test/models/tuning_peg.rb +4 -0
  541. data/test/models/tyre.rb +11 -11
  542. data/test/models/user.rb +14 -0
  543. data/test/models/uuid_child.rb +3 -3
  544. data/test/models/uuid_item.rb +6 -0
  545. data/test/models/uuid_parent.rb +3 -3
  546. data/test/models/vegetables.rb +24 -24
  547. data/test/models/vehicle.rb +6 -6
  548. data/test/models/vertex.rb +9 -9
  549. data/test/models/warehouse_thing.rb +5 -5
  550. data/test/models/wheel.rb +3 -3
  551. data/test/models/without_table.rb +3 -3
  552. data/test/models/zine.rb +3 -3
  553. data/test/schema/mysql2_specific_schema.rb +68 -58
  554. data/test/schema/oracle_specific_schema.rb +40 -43
  555. data/test/schema/postgresql_specific_schema.rb +114 -202
  556. data/test/schema/schema.rb +1057 -952
  557. data/test/schema/schema.rb.original +1057 -0
  558. data/test/schema/sqlite_specific_schema.rb +18 -22
  559. data/test/support/config.rb +43 -43
  560. data/test/support/connection.rb +23 -22
  561. data/test/support/connection_helper.rb +14 -14
  562. data/test/support/ddl_helper.rb +8 -8
  563. data/test/support/schema_dumping_helper.rb +20 -20
  564. data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -0
  565. data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -0
  566. metadata +129 -28
  567. data/lib/mswin32/rb19x/ibm_db.so +0 -0
  568. data/lib/mswin32/rb21x/i386/ibm_db.so +0 -0
  569. data/lib/mswin32/rb22x/i386/ibm_db.so +0 -0
  570. data/lib/mswin32/rb23x/i386/ibm_db.so +0 -0
  571. data/test/cases/associations/deprecated_counter_cache_on_has_many_through_test.rb +0 -26
  572. data/test/cases/attribute_methods/serialization_test.rb +0 -29
  573. data/test/cases/migration/change_schema_test - Copy.rb +0 -448
  574. data/test/cases/migration/foreign_key_test - Changed.rb +0 -325
  575. data/test/cases/migration/table_and_index_test.rb +0 -24
  576. data/test/cases/relation/where_test2.rb +0 -36
  577. data/test/cases/type/decimal_test.rb +0 -56
  578. data/test/cases/type/unsigned_integer_test.rb +0 -18
  579. data/test/cases/xml_serialization_test.rb +0 -457
  580. data/test/connections/native_ibm_db/connection.rb +0 -44
  581. data/test/fixtures/naked/csv/accounts.csv +0 -1
  582. data/test/schema/i5/ibm_db_specific_schema.rb +0 -137
  583. data/test/schema/ids/ibm_db_specific_schema.rb +0 -140
  584. data/test/schema/luw/ibm_db_specific_schema.rb +0 -137
  585. data/test/schema/mysql_specific_schema.rb +0 -70
  586. data/test/schema/zOS/ibm_db_specific_schema.rb +0 -208
@@ -1,1204 +1,1271 @@
1
- require "cases/helper"
2
- require 'models/post'
3
- require 'models/person'
4
- require 'models/reference'
5
- require 'models/job'
6
- require 'models/reader'
7
- require 'models/comment'
8
- require 'models/rating'
9
- require 'models/tag'
10
- require 'models/tagging'
11
- require 'models/author'
12
- require 'models/owner'
13
- require 'models/pet'
14
- require 'models/toy'
15
- require 'models/contract'
16
- require 'models/company'
17
- require 'models/developer'
18
- require 'models/computer'
19
- require 'models/subscriber'
20
- require 'models/book'
21
- require 'models/subscription'
22
- require 'models/essay'
23
- require 'models/category'
24
- require 'models/categorization'
25
- require 'models/member'
26
- require 'models/membership'
27
- require 'models/club'
28
- require 'models/organization'
29
-
30
- class HasManyThroughAssociationsTest < ActiveRecord::TestCase
31
- fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags,
32
- :owners, :pets, :toys, :jobs, :references, :companies, :members, :author_addresses,
33
- :subscribers, :books, :subscriptions, :developers, :categorizations, :essays,
34
- :categories_posts, :clubs, :memberships, :organizations
35
-
36
- # Dummies to force column loads so query counts are clean.
37
- def setup
38
- Person.create :first_name => 'gummy'
39
- Reader.create :person_id => 0, :post_id => 0
40
- end
41
-
42
- def test_preload_sti_rhs_class
43
- developers = Developer.includes(:firms).all.to_a
44
- assert_no_queries do
45
- developers.each { |d| d.firms }
46
- end
47
- end
48
-
49
- def test_preload_sti_middle_relation
50
- club = Club.create!(name: 'Aaron cool banana club')
51
- member1 = Member.create!(name: 'Aaron')
52
- member2 = Member.create!(name: 'Cat')
53
-
54
- SuperMembership.create! club: club, member: member1
55
- CurrentMembership.create! club: club, member: member2
56
-
57
- club1 = Club.includes(:members).find_by_id club.id
58
- assert_equal [member1, member2].sort_by(&:id),
59
- club1.members.sort_by(&:id)
60
- end
61
-
62
- def make_model(name)
63
- Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
64
- end
65
-
66
- def test_ordered_habtm
67
- person_prime = Class.new(ActiveRecord::Base) do
68
- def self.name; 'Person'; end
69
-
70
- has_many :readers
71
- has_many :posts, -> { order('posts.id DESC') }, :through => :readers
72
- end
73
- posts = person_prime.includes(:posts).first.posts
74
-
75
- assert_operator posts.length, :>, 1
76
- posts.each_cons(2) do |left,right|
77
- assert_operator left.id, :>, right.id
78
- end
79
- end
80
-
81
- def test_singleton_has_many_through
82
- book = make_model "Book"
83
- subscription = make_model "Subscription"
84
- subscriber = make_model "Subscriber"
85
-
86
- subscriber.primary_key = 'nick'
87
- subscription.belongs_to :book, anonymous_class: book
88
- subscription.belongs_to :subscriber, anonymous_class: subscriber
89
-
90
- book.has_many :subscriptions, anonymous_class: subscription
91
- book.has_many :subscribers, through: :subscriptions, anonymous_class: subscriber
92
-
93
- anonbook = book.first
94
- namebook = Book.find anonbook.id
95
-
96
- assert_operator anonbook.subscribers.count, :>, 0
97
- anonbook.subscribers.each do |s|
98
- assert_instance_of subscriber, s
99
- end
100
- assert_equal namebook.subscribers.map(&:id).sort,
101
- anonbook.subscribers.map(&:id).sort
102
- end
103
-
104
- def test_no_pk_join_table_append
105
- lesson, _, student = make_no_pk_hm_t
106
-
107
- sicp = lesson.new(:name => "SICP")
108
- ben = student.new(:name => "Ben Bitdiddle")
109
- sicp.students << ben
110
- assert sicp.save!
111
- end
112
-
113
- def test_no_pk_join_table_delete
114
- lesson, lesson_student, student = make_no_pk_hm_t
115
-
116
- sicp = lesson.new(:name => "SICP")
117
- ben = student.new(:name => "Ben Bitdiddle")
118
- louis = student.new(:name => "Louis Reasoner")
119
- sicp.students << ben
120
- sicp.students << louis
121
- assert sicp.save!
122
-
123
- sicp.students.reload
124
- assert_operator lesson_student.count, :>=, 2
125
- assert_no_difference('student.count') do
126
- assert_difference('lesson_student.count', -2) do
127
- sicp.students.destroy(*student.all.to_a)
128
- end
129
- end
130
- end
131
-
132
- def test_no_pk_join_model_callbacks
133
- lesson, lesson_student, student = make_no_pk_hm_t
134
-
135
- after_destroy_called = false
136
- lesson_student.after_destroy do
137
- after_destroy_called = true
138
- end
139
-
140
- sicp = lesson.new(:name => "SICP")
141
- ben = student.new(:name => "Ben Bitdiddle")
142
- sicp.students << ben
143
- assert sicp.save!
144
-
145
- sicp.students.reload
146
- sicp.students.destroy(*student.all.to_a)
147
- assert after_destroy_called, "after destroy should be called"
148
- end
149
-
150
- def make_no_pk_hm_t
151
- lesson = make_model 'Lesson'
152
- student = make_model 'Student'
153
-
154
- lesson_student = make_model 'LessonStudent'
155
- lesson_student.table_name = 'lessons_students'
156
-
157
- lesson_student.belongs_to :lesson, :anonymous_class => lesson
158
- lesson_student.belongs_to :student, :anonymous_class => student
159
- lesson.has_many :lesson_students, :anonymous_class => lesson_student
160
- lesson.has_many :students, :through => :lesson_students, :anonymous_class => student
161
- [lesson, lesson_student, student]
162
- end
163
-
164
- def test_pk_is_not_required_for_join
165
- post = Post.includes(:scategories).first
166
- post2 = Post.includes(:categories).first
167
-
168
- assert_operator post.categories.length, :>, 0
169
- assert_equal post2.categories, post.categories
170
- end
171
-
172
- def test_include?
173
- person = Person.new
174
- post = Post.new
175
- person.posts << post
176
- assert person.posts.include?(post)
177
- end
178
-
179
- def test_associate_existing
180
- post = posts(:thinking)
181
- person = people(:david)
182
-
183
- assert_queries(1) do
184
- post.people << person
185
- end
186
-
187
- assert_queries(1) do
188
- assert post.people.include?(person)
189
- end
190
-
191
- assert post.reload.people(true).include?(person)
192
- end
193
-
194
- def test_delete_all_for_with_dependent_option_destroy
195
- person = people(:david)
196
- assert_equal 1, person.jobs_with_dependent_destroy.count
197
-
198
- assert_no_difference 'Job.count' do
199
- assert_difference 'Reference.count', -1 do
200
- person.reload.jobs_with_dependent_destroy.delete_all
201
- end
202
- end
203
- end
204
-
205
- def test_delete_all_for_with_dependent_option_nullify
206
- person = people(:david)
207
- assert_equal 1, person.jobs_with_dependent_nullify.count
208
-
209
- assert_no_difference 'Job.count' do
210
- assert_no_difference 'Reference.count' do
211
- person.reload.jobs_with_dependent_nullify.delete_all
212
- end
213
- end
214
- end
215
-
216
- def test_delete_all_for_with_dependent_option_delete_all
217
- person = people(:david)
218
- assert_equal 1, person.jobs_with_dependent_delete_all.count
219
-
220
- assert_no_difference 'Job.count' do
221
- assert_difference 'Reference.count', -1 do
222
- person.reload.jobs_with_dependent_delete_all.delete_all
223
- end
224
- end
225
- end
226
-
227
- def test_concat
228
- person = people(:david)
229
- post = posts(:thinking)
230
- post.people.concat [person]
231
- assert_equal 1, post.people.size
232
- assert_equal 1, post.people(true).size
233
- end
234
-
235
- def test_associate_existing_record_twice_should_add_to_target_twice
236
- post = posts(:thinking)
237
- person = people(:david)
238
-
239
- assert_difference 'post.people.to_a.count', 2 do
240
- post.people << person
241
- post.people << person
242
- end
243
- end
244
-
245
- def test_associate_existing_record_twice_should_add_records_twice
246
- post = posts(:thinking)
247
- person = people(:david)
248
-
249
- assert_difference 'post.people.count', 2 do
250
- post.people << person
251
- post.people << person
252
- end
253
- end
254
-
255
- def test_add_two_instance_and_then_deleting
256
- post = posts(:thinking)
257
- person = people(:david)
258
-
259
- post.people << person
260
- post.people << person
261
-
262
- counts = ['post.people.count', 'post.people.to_a.count', 'post.readers.count', 'post.readers.to_a.count']
263
- assert_difference counts, -2 do
264
- post.people.delete(person)
265
- end
266
-
267
- assert !post.people.reload.include?(person)
268
- end
269
-
270
- def test_associating_new
271
- assert_queries(1) { posts(:thinking) }
272
- new_person = nil # so block binding catches it
273
-
274
- assert_queries(0) do
275
- new_person = Person.new :first_name => 'bob'
276
- end
277
-
278
- # Associating new records always saves them
279
- # Thus, 1 query for the new person record, 1 query for the new join table record
280
- assert_queries(2) do
281
- posts(:thinking).people << new_person
282
- end
283
-
284
- assert_queries(1) do
285
- assert posts(:thinking).people.include?(new_person)
286
- end
287
-
288
- assert posts(:thinking).reload.people(true).include?(new_person)
289
- end
290
-
291
- def test_associate_new_by_building
292
- assert_queries(1) { posts(:thinking) }
293
-
294
- assert_queries(0) do
295
- posts(:thinking).people.build(:first_name => "Bob")
296
- posts(:thinking).people.new(:first_name => "Ted")
297
- end
298
-
299
- # Should only need to load the association once
300
- assert_queries(1) do
301
- assert posts(:thinking).people.collect(&:first_name).include?("Bob")
302
- assert posts(:thinking).people.collect(&:first_name).include?("Ted")
303
- end
304
-
305
- # 2 queries for each new record (1 to save the record itself, 1 for the join model)
306
- # * 2 new records = 4
307
- # + 1 query to save the actual post = 5
308
- assert_queries(5) do
309
- posts(:thinking).body += '-changed'
310
- posts(:thinking).save
311
- end
312
-
313
- assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Bob")
314
- assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Ted")
315
- end
316
-
317
- def test_build_then_save_with_has_many_inverse
318
- post = posts(:thinking)
319
- person = post.people.build(:first_name => "Bob")
320
- person.save
321
- post.reload
322
-
323
- assert post.people.include?(person)
324
- end
325
-
326
- def test_build_then_save_with_has_one_inverse
327
- post = posts(:thinking)
328
- person = post.single_people.build(:first_name => "Bob")
329
- person.save
330
- post.reload
331
-
332
- assert post.single_people.include?(person)
333
- end
334
-
335
- def test_both_parent_ids_set_when_saving_new
336
- post = Post.new(title: 'Hello', body: 'world')
337
- person = Person.new(first_name: 'Sean')
338
-
339
- post.people = [person]
340
- post.save
341
-
342
- assert post.id
343
- assert person.id
344
- assert_equal post.id, post.readers.first.post_id
345
- assert_equal person.id, post.readers.first.person_id
346
- end
347
-
348
- def test_delete_association
349
- assert_queries(2){posts(:welcome);people(:michael); }
350
-
351
- assert_queries(1) do
352
- posts(:welcome).people.delete(people(:michael))
353
- end
354
-
355
- assert_queries(1) do
356
- assert posts(:welcome).people.empty?
357
- end
358
-
359
- assert posts(:welcome).reload.people(true).empty?
360
- end
361
-
362
- def test_destroy_association
363
- assert_no_difference "Person.count" do
364
- assert_difference "Reader.count", -1 do
365
- posts(:welcome).people.destroy(people(:michael))
366
- end
367
- end
368
-
369
- assert posts(:welcome).reload.people.empty?
370
- assert posts(:welcome).people(true).empty?
371
- end
372
-
373
- def test_destroy_all
374
- assert_no_difference "Person.count" do
375
- assert_difference "Reader.count", -1 do
376
- posts(:welcome).people.destroy_all
377
- end
378
- end
379
-
380
- assert posts(:welcome).reload.people.empty?
381
- assert posts(:welcome).people(true).empty?
382
- end
383
-
384
- def test_should_raise_exception_for_destroying_mismatching_records
385
- assert_no_difference ["Person.count", "Reader.count"] do
386
- assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:welcome).people.destroy(posts(:thinking)) }
387
- end
388
- end
389
-
390
- def test_delete_through_belongs_to_with_dependent_nullify
391
- Reference.make_comments = true
392
-
393
- person = people(:michael)
394
- job = jobs(:magician)
395
- reference = Reference.where(:job_id => job.id, :person_id => person.id).first
396
-
397
- assert_no_difference ['Job.count', 'Reference.count'] do
398
- assert_difference 'person.jobs.count', -1 do
399
- person.jobs_with_dependent_nullify.delete(job)
400
- end
401
- end
402
-
403
- assert_equal nil, reference.reload.job_id
404
- ensure
405
- Reference.make_comments = false
406
- end
407
-
408
- def test_delete_through_belongs_to_with_dependent_delete_all
409
- Reference.make_comments = true
410
-
411
- person = people(:michael)
412
- job = jobs(:magician)
413
-
414
- # Make sure we're not deleting everything
415
- assert person.jobs.count >= 2
416
-
417
- assert_no_difference 'Job.count' do
418
- assert_difference ['person.jobs.count', 'Reference.count'], -1 do
419
- person.jobs_with_dependent_delete_all.delete(job)
420
- end
421
- end
422
-
423
- # Check that the destroy callback on Reference did not run
424
- assert_equal nil, person.reload.comments
425
- ensure
426
- Reference.make_comments = false
427
- end
428
-
429
- def test_delete_through_belongs_to_with_dependent_destroy
430
- Reference.make_comments = true
431
-
432
- person = people(:michael)
433
- job = jobs(:magician)
434
-
435
- # Make sure we're not deleting everything
436
- assert person.jobs.count >= 2
437
-
438
- assert_no_difference 'Job.count' do
439
- assert_difference ['person.jobs.count', 'Reference.count'], -1 do
440
- person.jobs_with_dependent_destroy.delete(job)
441
- end
442
- end
443
-
444
- # Check that the destroy callback on Reference ran
445
- assert_equal "Reference destroyed", person.reload.comments
446
- ensure
447
- Reference.make_comments = false
448
- end
449
-
450
- def test_belongs_to_with_dependent_destroy
451
- person = PersonWithDependentDestroyJobs.find(1)
452
-
453
- # Create a reference which is not linked to a job. This should not be destroyed.
454
- person.references.create!
455
-
456
- assert_no_difference 'Job.count' do
457
- assert_difference 'Reference.count', -person.jobs.count do
458
- person.destroy
459
- end
460
- end
461
- end
462
-
463
- def test_belongs_to_with_dependent_delete_all
464
- person = PersonWithDependentDeleteAllJobs.find(1)
465
-
466
- # Create a reference which is not linked to a job. This should not be destroyed.
467
- person.references.create!
468
-
469
- assert_no_difference 'Job.count' do
470
- assert_difference 'Reference.count', -person.jobs.count do
471
- person.destroy
472
- end
473
- end
474
- end
475
-
476
- def test_belongs_to_with_dependent_nullify
477
- person = PersonWithDependentNullifyJobs.find(1)
478
-
479
- references = person.references.to_a
480
-
481
- assert_no_difference ['Reference.count', 'Job.count'] do
482
- person.destroy
483
- end
484
-
485
- references.each do |reference|
486
- assert_equal nil, reference.reload.job_id
487
- end
488
- end
489
-
490
- def test_update_counter_caches_on_delete
491
- post = posts(:welcome)
492
- tag = post.tags.create!(:name => 'doomed')
493
-
494
- assert_difference ['post.reload.tags_count'], -1 do
495
- posts(:welcome).tags.delete(tag)
496
- end
497
- end
498
-
499
- def test_update_counter_caches_on_delete_with_dependent_destroy
500
- post = posts(:welcome)
501
- tag = post.tags.create!(:name => 'doomed')
502
- post.update_columns(tags_with_destroy_count: post.tags.count)
503
-
504
- assert_difference ['post.reload.tags_with_destroy_count'], -1 do
505
- posts(:welcome).tags_with_destroy.delete(tag)
506
- end
507
- end
508
-
509
- def test_update_counter_caches_on_delete_with_dependent_nullify
510
- post = posts(:welcome)
511
- tag = post.tags.create!(:name => 'doomed')
512
- post.update_columns(tags_with_nullify_count: post.tags.count)
513
-
514
- assert_no_difference 'post.reload.tags_count' do
515
- assert_difference 'post.reload.tags_with_nullify_count', -1 do
516
- posts(:welcome).tags_with_nullify.delete(tag)
517
- end
518
- end
519
- end
520
-
521
- def test_update_counter_caches_on_replace_association
522
- post = posts(:welcome)
523
- tag = post.tags.create!(:name => 'doomed')
524
- tag.tagged_posts << posts(:thinking)
525
-
526
- tag.tagged_posts = []
527
- post.reload
528
-
529
- assert_equal(post.taggings.count, post.tags_count)
530
- end
531
-
532
- def test_update_counter_caches_on_destroy
533
- post = posts(:welcome)
534
- tag = post.tags.create!(name: 'doomed')
535
-
536
- assert_difference 'post.reload.tags_count', -1 do
537
- tag.tagged_posts.destroy(post)
538
- end
539
- end
540
-
541
- def test_replace_association
542
- assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
543
-
544
- # 1 query to delete the existing reader (michael)
545
- # 1 query to associate the new reader (david)
546
- assert_queries(2) do
547
- posts(:welcome).people = [people(:david)]
548
- end
549
-
550
- assert_queries(0){
551
- assert posts(:welcome).people.include?(people(:david))
552
- assert !posts(:welcome).people.include?(people(:michael))
553
- }
554
-
555
- assert posts(:welcome).reload.people(true).include?(people(:david))
556
- assert !posts(:welcome).reload.people(true).include?(people(:michael))
557
- end
558
-
559
- def test_replace_order_is_preserved
560
- posts(:welcome).people.clear
561
- posts(:welcome).people = [people(:david), people(:michael)]
562
- assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
563
-
564
- # Test the inverse order in case the first success was a coincidence
565
- posts(:welcome).people.clear
566
- posts(:welcome).people = [people(:michael), people(:david)]
567
- assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
568
- end
569
-
570
- def test_replace_by_id_order_is_preserved
571
- posts(:welcome).people.clear
572
- posts(:welcome).person_ids = [people(:david).id, people(:michael).id]
573
- assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
574
-
575
- # Test the inverse order in case the first success was a coincidence
576
- posts(:welcome).people.clear
577
- posts(:welcome).person_ids = [people(:michael).id, people(:david).id]
578
- assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
579
- end
580
-
581
- def test_associate_with_create
582
- assert_queries(1) { posts(:thinking) }
583
-
584
- # 1 query for the new record, 1 for the join table record
585
- # No need to update the actual collection yet!
586
- assert_queries(2) do
587
- posts(:thinking).people.create(:first_name=>"Jeb")
588
- end
589
-
590
- # *Now* we actually need the collection so it's loaded
591
- assert_queries(1) do
592
- assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
593
- end
594
-
595
- assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Jeb")
596
- end
597
-
598
- def test_through_record_is_built_when_created_with_where
599
- assert_difference("posts(:thinking).readers.count", 1) do
600
- posts(:thinking).people.where(first_name: "Jeb").create
601
- end
602
- end
603
-
604
- def test_associate_with_create_and_no_options
605
- peeps = posts(:thinking).people.count
606
- posts(:thinking).people.create(:first_name => 'foo')
607
- assert_equal peeps + 1, posts(:thinking).people.count
608
- end
609
-
610
- def test_associate_with_create_with_through_having_conditions
611
- impatient_people = posts(:thinking).impatient_people.count
612
- posts(:thinking).impatient_people.create!(:first_name => 'foo')
613
- assert_equal impatient_people + 1, posts(:thinking).impatient_people.count
614
- end
615
-
616
- def test_associate_with_create_exclamation_and_no_options
617
- peeps = posts(:thinking).people.count
618
- posts(:thinking).people.create!(:first_name => 'foo')
619
- assert_equal peeps + 1, posts(:thinking).people.count
620
- end
621
-
622
- def test_create_on_new_record
623
- p = Post.new
624
-
625
- error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(:first_name => "mew") }
626
- assert_equal "You cannot call create unless the parent is saved", error.message
627
-
628
- error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(:first_name => "snow") }
629
- assert_equal "You cannot call create unless the parent is saved", error.message
630
- end
631
-
632
- def test_associate_with_create_and_invalid_options
633
- firm = companies(:first_firm)
634
- assert_no_difference('firm.developers.count') { assert_nothing_raised { firm.developers.create(:name => '0') } }
635
- end
636
-
637
- def test_associate_with_create_and_valid_options
638
- firm = companies(:first_firm)
639
- assert_difference('firm.developers.count', 1) { firm.developers.create(:name => 'developer') }
640
- end
641
-
642
- def test_associate_with_create_bang_and_invalid_options
643
- firm = companies(:first_firm)
644
- assert_no_difference('firm.developers.count') { assert_raises(ActiveRecord::RecordInvalid) { firm.developers.create!(:name => '0') } }
645
- end
646
-
647
- def test_associate_with_create_bang_and_valid_options
648
- firm = companies(:first_firm)
649
- assert_difference('firm.developers.count', 1) { firm.developers.create!(:name => 'developer') }
650
- end
651
-
652
- def test_push_with_invalid_record
653
- firm = companies(:first_firm)
654
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << Developer.new(:name => '0') }
655
- end
656
-
657
- def test_push_with_invalid_join_record
658
- repair_validations(Contract) do
659
- Contract.validate {|r| r.errors[:base] << 'Invalid Contract' }
660
-
661
- firm = companies(:first_firm)
662
- lifo = Developer.new(:name => 'lifo')
663
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
664
-
665
- lifo = Developer.create!(:name => 'lifo')
666
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
667
- end
668
- end
669
-
670
- def test_clear_associations
671
- assert_queries(2) { posts(:welcome);posts(:welcome).people(true) }
672
-
673
- assert_queries(1) do
674
- posts(:welcome).people.clear
675
- end
676
-
677
- assert_queries(0) do
678
- assert posts(:welcome).people.empty?
679
- end
680
-
681
- assert posts(:welcome).reload.people(true).empty?
682
- end
683
-
684
- def test_association_callback_ordering
685
- Post.reset_log
686
- log = Post.log
687
- post = posts(:thinking)
688
-
689
- post.people_with_callbacks << people(:michael)
690
- assert_equal [
691
- [:added, :before, "Michael"],
692
- [:added, :after, "Michael"]
693
- ], log.last(2)
694
-
695
- post.people_with_callbacks.push(people(:david), Person.create!(:first_name => "Bob"), Person.new(:first_name => "Lary"))
696
- assert_equal [
697
- [:added, :before, "David"],
698
- [:added, :after, "David"],
699
- [:added, :before, "Bob"],
700
- [:added, :after, "Bob"],
701
- [:added, :before, "Lary"],
702
- [:added, :after, "Lary"]
703
- ],log.last(6)
704
-
705
- post.people_with_callbacks.build(:first_name => "Ted")
706
- assert_equal [
707
- [:added, :before, "Ted"],
708
- [:added, :after, "Ted"]
709
- ], log.last(2)
710
-
711
- post.people_with_callbacks.create(:first_name => "Sam")
712
- assert_equal [
713
- [:added, :before, "Sam"],
714
- [:added, :after, "Sam"]
715
- ], log.last(2)
716
-
717
- post.people_with_callbacks = [people(:michael),people(:david), Person.new(:first_name => "Julian"), Person.create!(:first_name => "Roger")]
718
- assert_equal((%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort)
719
- assert_equal [
720
- [:added, :before, "Julian"],
721
- [:added, :after, "Julian"],
722
- [:added, :before, "Roger"],
723
- [:added, :after, "Roger"]
724
- ], log.last(4)
725
- end
726
-
727
- def test_dynamic_find_should_respect_association_include
728
- # SQL error in sort clause if :include is not included
729
- # due to Unknown column 'comments.id'
730
- assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog')
731
- end
732
-
733
- def test_count_with_include_should_alias_join_table
734
- assert_equal 2, people(:michael).posts.includes(:readers).count
735
- end
736
-
737
- def test_inner_join_with_quoted_table_name
738
- assert_equal 2, people(:michael).jobs.size
739
- end
740
-
741
- def test_get_ids
742
- assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
743
- end
744
-
745
- def test_get_ids_for_has_many_through_with_conditions_should_not_preload
746
- Tagging.create!(:taggable_type => 'Post', :taggable_id => posts(:welcome).id, :tag => tags(:misc))
747
- ActiveRecord::Associations::Preloader.expects(:new).never
748
- posts(:welcome).misc_tag_ids
749
- end
750
-
751
- def test_get_ids_for_loaded_associations
752
- person = people(:michael)
753
- person.posts(true)
754
- assert_queries(0) do
755
- person.post_ids
756
- person.post_ids
757
- end
758
- end
759
-
760
- def test_get_ids_for_unloaded_associations_does_not_load_them
761
- person = people(:michael)
762
- assert !person.posts.loaded?
763
- assert_equal [posts(:welcome).id, posts(:authorless).id].sort, person.post_ids.sort
764
- assert !person.posts.loaded?
765
- end
766
-
767
- def test_association_proxy_transaction_method_starts_transaction_in_association_class
768
- Tag.expects(:transaction)
769
- Post.first.tags.transaction do
770
- # nothing
771
- end
772
- end
773
-
774
- def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
775
- post = Post.create!(:title => "TITLE", :body => "BODY")
776
- assert_equal [], post.author_favorites
777
- end
778
-
779
- def test_has_many_association_through_a_belongs_to_association
780
- author = authors(:mary)
781
- post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
782
- author.author_favorites.create(:favorite_author_id => 1)
783
- author.author_favorites.create(:favorite_author_id => 2)
784
- author.author_favorites.create(:favorite_author_id => 3)
785
- assert_equal post.author.author_favorites, post.author_favorites
786
- end
787
-
788
- def test_merge_join_association_with_has_many_through_association_proxy
789
- author = authors(:mary)
790
- assert_nothing_raised { author.comments.ratings.to_sql }
791
- end
792
-
793
- def test_has_many_association_through_a_has_many_association_with_nonstandard_primary_keys
794
- assert_equal 2, owners(:blackbeard).toys.count
795
- end
796
-
797
- def test_find_on_has_many_association_collection_with_include_and_conditions
798
- post_with_no_comments = people(:michael).posts_with_no_comments.first
799
- assert_equal post_with_no_comments, posts(:authorless)
800
- end
801
-
802
- def test_has_many_through_has_one_reflection
803
- assert_equal [comments(:eager_sti_on_associations_vs_comment)], authors(:david).very_special_comments
804
- end
805
-
806
- def test_modifying_has_many_through_has_one_reflection_should_raise
807
- [
808
- lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(:body => "Gorp!", :post_id => 1011), VerySpecialComment.create!(:body => "Eep!", :post_id => 1012)] },
809
- lambda { authors(:david).very_special_comments << VerySpecialComment.create!(:body => "Hoohah!", :post_id => 1013) },
810
- lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
811
- ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
812
- end
813
-
814
- def test_has_many_association_through_a_has_many_association_to_self
815
- sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1)
816
- john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1)
817
- assert_equal sarah.agents, [john]
818
- assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
819
- end
820
-
821
- def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
822
- Categorization.create(:author => authors(:mary), :named_category_name => categories(:general).name)
823
- assert_equal categories(:general), authors(:mary).named_categories.first
824
- end
825
-
826
- def test_collection_build_with_nonstandard_primary_key_on_belongs_to
827
- author = authors(:mary)
828
- category = author.named_categories.build(:name => "Primary")
829
- author.save
830
- assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
831
- assert author.named_categories(true).include?(category)
832
- end
833
-
834
- def test_collection_create_with_nonstandard_primary_key_on_belongs_to
835
- author = authors(:mary)
836
- category = author.named_categories.create(:name => "Primary")
837
- assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
838
- assert author.named_categories(true).include?(category)
839
- end
840
-
841
- def test_collection_exists
842
- author = authors(:mary)
843
- category = Category.create!(author_ids: [author.id], name: "Primary")
844
- assert category.authors.exists?(id: author.id)
845
- assert category.reload.authors.exists?(id: author.id)
846
- end
847
-
848
- def test_collection_delete_with_nonstandard_primary_key_on_belongs_to
849
- author = authors(:mary)
850
- category = author.named_categories.create(:name => "Primary")
851
- author.named_categories.delete(category)
852
- assert !Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
853
- assert author.named_categories(true).empty?
854
- end
855
-
856
- def test_collection_singular_ids_getter_with_string_primary_keys
857
- book = books(:awdr)
858
- assert_equal 2, book.subscriber_ids.size
859
- assert_equal [subscribers(:first).nick, subscribers(:second).nick].sort, book.subscriber_ids.sort
860
- end
861
-
862
- def test_collection_singular_ids_setter
863
- company = companies(:rails_core)
864
- dev = Developer.first
865
-
866
- company.developer_ids = [dev.id]
867
- assert_equal [dev], company.developers
868
- end
869
-
870
- def test_collection_singular_ids_setter_with_string_primary_keys
871
- assert_nothing_raised do
872
- book = books(:awdr)
873
- book.subscriber_ids = [subscribers(:second).nick]
874
- assert_equal [subscribers(:second)], book.subscribers(true)
875
-
876
- book.subscriber_ids = []
877
- assert_equal [], book.subscribers(true)
878
- end
879
-
880
- end
881
-
882
- def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
883
- company = companies(:rails_core)
884
- ids = [Developer.first.id, -9999]
885
- assert_raises(ActiveRecord::RecordNotFound) {company.developer_ids= ids}
886
- end
887
-
888
- def test_build_a_model_from_hm_through_association_with_where_clause
889
- assert_nothing_raised { books(:awdr).subscribers.where(:nick => "marklazz").build }
890
- end
891
-
892
- def test_attributes_are_being_set_when_initialized_from_hm_through_association_with_where_clause
893
- new_subscriber = books(:awdr).subscribers.where(:nick => "marklazz").build
894
- assert_equal new_subscriber.nick, "marklazz"
895
- end
896
-
897
- def test_attributes_are_being_set_when_initialized_from_hm_through_association_with_multiple_where_clauses
898
- new_subscriber = books(:awdr).subscribers.where(:nick => "marklazz").where(:name => 'Marcelo Giorgi').build
899
- assert_equal new_subscriber.nick, "marklazz"
900
- assert_equal new_subscriber.name, "Marcelo Giorgi"
901
- end
902
-
903
- def test_include_method_in_association_through_should_return_true_for_instance_added_with_build
904
- person = Person.new
905
- reference = person.references.build
906
- job = reference.build_job
907
- assert person.jobs.include?(job)
908
- end
909
-
910
- def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
911
- author = Author.new
912
- post = author.posts.build
913
- comment = post.comments.build
914
- assert author.comments.include?(comment)
915
- end
916
-
917
- def test_through_association_readonly_should_be_false
918
- assert !people(:michael).posts.first.readonly?
919
- assert !people(:michael).posts.to_a.first.readonly?
920
- end
921
-
922
- def test_can_update_through_association
923
- assert_nothing_raised do
924
- people(:michael).posts.first.update!(title: "Can write")
925
- end
926
- end
927
-
928
- def test_has_many_through_polymorphic_with_primary_key_option
929
- assert_equal [categories(:general)], authors(:david).essay_categories
930
-
931
- authors = Author.joins(:essay_categories).where('categories.id' => categories(:general).id)
932
- assert_equal authors(:david), authors.first
933
-
934
- assert_equal [owners(:blackbeard)], authors(:david).essay_owners
935
-
936
- authors = Author.joins(:essay_owners).where("owners.name = 'blackbeard'")
937
- assert_equal authors(:david), authors.first
938
- end
939
-
940
- def test_has_many_through_with_primary_key_option
941
- assert_equal [categories(:general)], authors(:david).essay_categories_2
942
-
943
- authors = Author.joins(:essay_categories_2).where('categories.id' => categories(:general).id)
944
- assert_equal authors(:david), authors.first
945
- end
946
-
947
- def test_size_of_through_association_should_increase_correctly_when_has_many_association_is_added
948
- post = posts(:thinking)
949
- readers = post.readers.size
950
- post.people << people(:michael)
951
- assert_equal readers + 1, post.readers.size
952
- end
953
-
954
- def test_has_many_through_with_default_scope_on_join_model
955
- assert_equal posts(:welcome).comments.order('id').to_a, authors(:david).comments_on_first_posts
956
- end
957
-
958
- def test_create_has_many_through_with_default_scope_on_join_model
959
- category = authors(:david).special_categories.create(:name => "Foo")
960
- assert_equal 1, category.categorizations.where(:special => true).count
961
- end
962
-
963
- def test_joining_has_many_through_with_uniq
964
- mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first
965
- assert_equal 1, mary.unique_categorized_posts.length
966
- assert_equal 1, mary.unique_categorized_post_ids.length
967
- end
968
-
969
- def test_joining_has_many_through_belongs_to
970
- posts = Post.joins(:author_categorizations).order('posts.id').
971
- where('categorizations.id' => categorizations(:mary_thinking_sti).id)
972
-
973
- assert_equal [posts(:eager_other), posts(:misc_by_mary), posts(:other_by_mary)], posts
974
- end
975
-
976
- def test_select_chosen_fields_only
977
- author = authors(:david)
978
- assert_equal ['body', 'id'].sort, author.comments.select('comments.body').first.attributes.keys.sort
979
- end
980
-
981
- def test_get_has_many_through_belongs_to_ids_with_conditions
982
- assert_equal [categories(:general).id], authors(:mary).categories_like_general_ids
983
- end
984
-
985
- def test_get_collection_singular_ids_on_has_many_through_with_conditions_and_include
986
- person = Person.first
987
- assert_equal person.posts_with_no_comment_ids, person.posts_with_no_comments.map(&:id)
988
- end
989
-
990
- def test_count_has_many_through_with_named_scope
991
- assert_equal 2, authors(:mary).categories.count
992
- assert_equal 1, authors(:mary).categories.general.count
993
- end
994
-
995
- def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes
996
- post = posts(:eager_other)
997
-
998
- post.author_categorizations
999
- proxy = post.send(:association_instance_get, :author_categorizations)
1000
-
1001
- assert !proxy.stale_target?
1002
- assert_equal authors(:mary).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
1003
-
1004
- post.author_id = authors(:david).id
1005
-
1006
- assert proxy.stale_target?
1007
- assert_equal authors(:david).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
1008
- end
1009
-
1010
- def test_create_with_conditions_hash_on_through_association
1011
- member = members(:groucho)
1012
- club = member.clubs.create!
1013
-
1014
- assert_equal true, club.reload.membership.favourite
1015
- end
1016
-
1017
- def test_deleting_from_has_many_through_a_belongs_to_should_not_try_to_update_counter
1018
- post = posts(:welcome)
1019
- address = author_addresses(:david_address)
1020
-
1021
- assert post.author_addresses.include?(address)
1022
- post.author_addresses.delete(address)
1023
- assert post[:author_count].nil?
1024
- end
1025
-
1026
- def test_primary_key_option_on_source
1027
- post = posts(:welcome)
1028
- category = categories(:general)
1029
- Categorization.create!(:post_id => post.id, :named_category_name => category.name)
1030
-
1031
- assert_equal [category], post.named_categories
1032
- assert_equal [category.name], post.named_category_ids # checks when target loaded
1033
- assert_equal [category.name], post.reload.named_category_ids # checks when target no loaded
1034
- end
1035
-
1036
- def test_create_should_not_raise_exception_when_join_record_has_errors
1037
- repair_validations(Categorization) do
1038
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1039
- Category.create(:name => 'Fishing', :authors => [Author.first])
1040
- end
1041
- end
1042
-
1043
- def test_save_should_not_raise_exception_when_join_record_has_errors
1044
- repair_validations(Categorization) do
1045
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1046
- c = Category.create(:name => 'Fishing', :authors => [Author.first])
1047
- c.save
1048
- end
1049
- end
1050
-
1051
- def test_assign_array_to_new_record_builds_join_records
1052
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
1053
- assert_equal 1, c.categorizations.size
1054
- end
1055
-
1056
- def test_create_bang_should_raise_exception_when_join_record_has_errors
1057
- repair_validations(Categorization) do
1058
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1059
- assert_raises(ActiveRecord::RecordInvalid) do
1060
- Category.create!(:name => 'Fishing', :authors => [Author.first])
1061
- end
1062
- end
1063
- end
1064
-
1065
- def test_save_bang_should_raise_exception_when_join_record_has_errors
1066
- repair_validations(Categorization) do
1067
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1068
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
1069
- assert_raises(ActiveRecord::RecordInvalid) do
1070
- c.save!
1071
- end
1072
- end
1073
- end
1074
-
1075
- def test_create_bang_returns_falsy_when_join_record_has_errors
1076
- repair_validations(Categorization) do
1077
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1078
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
1079
- assert !c.save
1080
- end
1081
- end
1082
-
1083
- def test_preloading_empty_through_association_via_joins
1084
- person = Person.create!(:first_name => "Gaga")
1085
- person = Person.where(:id => person.id).where('readers.id = 1 or 1=1').references(:readers).includes(:posts).to_a.first
1086
-
1087
- assert person.posts.loaded?, 'person.posts should be loaded'
1088
- assert_equal [], person.posts
1089
- end
1090
-
1091
- def test_explicitly_joining_join_table
1092
- assert_equal owners(:blackbeard).toys, owners(:blackbeard).toys.with_pet
1093
- end
1094
-
1095
- def test_has_many_through_with_polymorphic_source
1096
- post = tags(:general).tagged_posts.create! :title => "foo", :body => "bar"
1097
- assert_equal [tags(:general)], post.reload.tags
1098
- end
1099
-
1100
- def test_has_many_through_obeys_order_on_through_association
1101
- owner = owners(:blackbeard)
1102
- assert owner.toys.to_sql.include?("pets.name desc")
1103
- assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name }
1104
- end
1105
-
1106
- def test_has_many_through_associations_on_new_records_use_null_relations
1107
- person = Person.new
1108
-
1109
- assert_no_queries(ignore_none: false) do
1110
- assert_equal [], person.posts
1111
- assert_equal [], person.posts.where(body: 'omg')
1112
- assert_equal [], person.posts.pluck(:body)
1113
- assert_equal 0, person.posts.sum(:tags_count)
1114
- assert_equal 0, person.posts.count
1115
- end
1116
- end
1117
-
1118
- def test_has_many_through_with_default_scope_on_the_target
1119
- person = people(:michael)
1120
- assert_equal [posts(:thinking)], person.first_posts
1121
-
1122
- readers(:michael_authorless).update(first_post_id: 1)
1123
- assert_equal [posts(:thinking)], person.reload.first_posts
1124
- end
1125
-
1126
- def test_has_many_through_with_includes_in_through_association_scope
1127
- assert_not_empty posts(:welcome).author_address_extra_with_address
1128
- end
1129
-
1130
- def test_insert_records_via_has_many_through_association_with_scope
1131
- club = Club.create!
1132
- member = Member.create!
1133
- Membership.create!(club: club, member: member)
1134
-
1135
- club.favourites << member
1136
- assert_equal [member], club.favourites
1137
-
1138
- club.reload
1139
- assert_equal [member], club.favourites
1140
- end
1141
-
1142
- def test_has_many_through_unscope_default_scope
1143
- post = Post.create!(:title => 'Beaches', :body => "I like beaches!")
1144
- Reader.create! :person => people(:david), :post => post
1145
- LazyReader.create! :person => people(:susan), :post => post
1146
-
1147
- assert_equal 2, post.people.to_a.size
1148
- assert_equal 1, post.lazy_people.to_a.size
1149
-
1150
- assert_equal 2, post.lazy_readers_unscope_skimmers.to_a.size
1151
- assert_equal 2, post.lazy_people_unscope_skimmers.to_a.size
1152
- end
1153
-
1154
- def test_has_many_through_add_with_sti_middle_relation
1155
- club = SuperClub.create!(name: 'Fight Club')
1156
- member = Member.create!(name: 'Tyler Durden')
1157
-
1158
- club.members << member
1159
- assert_equal 1, SuperMembership.where(member_id: member.id, club_id: club.id).count
1160
- end
1161
-
1162
- def test_build_for_has_many_through_association
1163
- organization = organizations(:nsa)
1164
- author = organization.author
1165
- post_direct = author.posts.build
1166
- post_through = organization.posts.build
1167
- assert_equal post_direct.author_id, post_through.author_id
1168
- end
1169
-
1170
- def test_has_many_through_with_scope_that_should_not_be_fully_merged
1171
- Club.has_many :distinct_memberships, -> { distinct }, class_name: "Membership"
1172
- Club.has_many :special_favourites, through: :distinct_memberships, source: :member
1173
-
1174
- assert_nil Club.new.special_favourites.distinct_value
1175
- end
1176
-
1177
- def test_has_many_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
1178
- member = Member.create!
1179
- club = Club.create!
1180
- TenantMembership.create!(
1181
- member: member,
1182
- club: club
1183
- )
1184
-
1185
- TenantMembership.current_member = member
1186
-
1187
- tenant_clubs = member.tenant_clubs
1188
- assert_equal [club], tenant_clubs
1189
-
1190
- TenantMembership.current_member = nil
1191
-
1192
- other_member = Member.create!
1193
- other_club = Club.create!
1194
- TenantMembership.create!(
1195
- member: other_member,
1196
- club: other_club
1197
- )
1198
-
1199
- tenant_clubs = other_member.tenant_clubs
1200
- assert_equal [other_club], tenant_clubs
1201
- ensure
1202
- TenantMembership.current_member = nil
1203
- end
1204
- end
1
+ require "cases/helper"
2
+ require 'models/post'
3
+ require 'models/person'
4
+ require 'models/reference'
5
+ require 'models/job'
6
+ require 'models/reader'
7
+ require 'models/comment'
8
+ require 'models/rating'
9
+ require 'models/tag'
10
+ require 'models/tagging'
11
+ require 'models/author'
12
+ require 'models/owner'
13
+ require 'models/pet'
14
+ require 'models/pet_treasure'
15
+ require 'models/toy'
16
+ require 'models/treasure'
17
+ require 'models/contract'
18
+ require 'models/company'
19
+ require 'models/developer'
20
+ require 'models/computer'
21
+ require 'models/subscriber'
22
+ require 'models/book'
23
+ require 'models/subscription'
24
+ require 'models/essay'
25
+ require 'models/category'
26
+ require 'models/categorization'
27
+ require 'models/member'
28
+ require 'models/membership'
29
+ require 'models/club'
30
+ require 'models/organization'
31
+
32
+ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
33
+ fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags,
34
+ :owners, :pets, :toys, :jobs, :references, :companies, :members, :author_addresses,
35
+ :subscribers, :books, :subscriptions, :developers, :categorizations, :essays,
36
+ :categories_posts, :clubs, :memberships, :organizations
37
+
38
+ # Dummies to force column loads so query counts are clean.
39
+ def setup
40
+ Person.create :first_name => 'gummy'
41
+ Reader.create :person_id => 0, :post_id => 0
42
+ end
43
+
44
+ def test_preload_sti_rhs_class
45
+ developers = Developer.includes(:firms).all.to_a
46
+ assert_no_queries do
47
+ developers.each(&:firms)
48
+ end
49
+ end
50
+
51
+ def test_preload_sti_middle_relation
52
+ club = Club.create!(name: 'Aaron cool banana club')
53
+ member1 = Member.create!(name: 'Aaron')
54
+ member2 = Member.create!(name: 'Cat')
55
+
56
+ SuperMembership.create! club: club, member: member1
57
+ CurrentMembership.create! club: club, member: member2
58
+
59
+ club1 = Club.includes(:members).find_by_id club.id
60
+ assert_equal [member1, member2].sort_by(&:id),
61
+ club1.members.sort_by(&:id)
62
+ end
63
+
64
+ def make_model(name)
65
+ Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
66
+ end
67
+
68
+ def test_ordered_habtm
69
+ person_prime = Class.new(ActiveRecord::Base) do
70
+ def self.name; 'Person'; end
71
+
72
+ has_many :readers
73
+ has_many :posts, -> { order('posts.id DESC') }, :through => :readers
74
+ end
75
+ posts = person_prime.includes(:posts).first.posts
76
+
77
+ assert_operator posts.length, :>, 1
78
+ posts.each_cons(2) do |left,right|
79
+ assert_operator left.id, :>, right.id
80
+ end
81
+ end
82
+
83
+ def test_singleton_has_many_through
84
+ book = make_model "Book"
85
+ subscription = make_model "Subscription"
86
+ subscriber = make_model "Subscriber"
87
+
88
+ subscriber.primary_key = 'nick'
89
+ subscription.belongs_to :book, anonymous_class: book
90
+ subscription.belongs_to :subscriber, anonymous_class: subscriber
91
+
92
+ book.has_many :subscriptions, anonymous_class: subscription
93
+ book.has_many :subscribers, through: :subscriptions, anonymous_class: subscriber
94
+
95
+ anonbook = book.first
96
+ namebook = Book.find anonbook.id
97
+
98
+ assert_operator anonbook.subscribers.count, :>, 0
99
+ anonbook.subscribers.each do |s|
100
+ assert_instance_of subscriber, s
101
+ end
102
+ assert_equal namebook.subscribers.map(&:id).sort,
103
+ anonbook.subscribers.map(&:id).sort
104
+ end
105
+
106
+ def test_no_pk_join_table_append
107
+ lesson, _, student = make_no_pk_hm_t
108
+
109
+ sicp = lesson.new(:name => "SICP")
110
+ ben = student.new(:name => "Ben Bitdiddle")
111
+ sicp.students << ben
112
+ assert sicp.save!
113
+ end
114
+
115
+ def test_no_pk_join_table_delete
116
+ lesson, lesson_student, student = make_no_pk_hm_t
117
+
118
+ sicp = lesson.new(:name => "SICP")
119
+ ben = student.new(:name => "Ben Bitdiddle")
120
+ louis = student.new(:name => "Louis Reasoner")
121
+ sicp.students << ben
122
+ sicp.students << louis
123
+ assert sicp.save!
124
+
125
+ sicp.students.reload
126
+ assert_operator lesson_student.count, :>=, 2
127
+ assert_no_difference('student.count') do
128
+ assert_difference('lesson_student.count', -2) do
129
+ sicp.students.destroy(*student.all.to_a)
130
+ end
131
+ end
132
+ end
133
+
134
+ def test_no_pk_join_model_callbacks
135
+ lesson, lesson_student, student = make_no_pk_hm_t
136
+
137
+ after_destroy_called = false
138
+ lesson_student.after_destroy do
139
+ after_destroy_called = true
140
+ end
141
+
142
+ sicp = lesson.new(:name => "SICP")
143
+ ben = student.new(:name => "Ben Bitdiddle")
144
+ sicp.students << ben
145
+ assert sicp.save!
146
+
147
+ sicp.students.reload
148
+ sicp.students.destroy(*student.all.to_a)
149
+ assert after_destroy_called, "after destroy should be called"
150
+ end
151
+
152
+ def make_no_pk_hm_t
153
+ lesson = make_model 'Lesson'
154
+ student = make_model 'Student'
155
+
156
+ lesson_student = make_model 'LessonStudent'
157
+ lesson_student.table_name = 'lessons_students'
158
+
159
+ lesson_student.belongs_to :lesson, :anonymous_class => lesson
160
+ lesson_student.belongs_to :student, :anonymous_class => student
161
+ lesson.has_many :lesson_students, :anonymous_class => lesson_student
162
+ lesson.has_many :students, :through => :lesson_students, :anonymous_class => student
163
+ [lesson, lesson_student, student]
164
+ end
165
+
166
+ def test_pk_is_not_required_for_join
167
+ post = Post.includes(:scategories).first
168
+ post2 = Post.includes(:categories).first
169
+
170
+ assert_operator post.categories.length, :>, 0
171
+ assert_equal post2.categories, post.categories
172
+ end
173
+
174
+ def test_include?
175
+ person = Person.new
176
+ post = Post.new
177
+ person.posts << post
178
+ assert person.posts.include?(post)
179
+ end
180
+
181
+ def test_associate_existing
182
+ post = posts(:thinking)
183
+ person = people(:david)
184
+
185
+ assert_queries(1) do
186
+ post.people << person
187
+ end
188
+
189
+ assert_queries(1) do
190
+ assert post.people.include?(person)
191
+ end
192
+
193
+ assert post.reload.people.reload.include?(person)
194
+ end
195
+
196
+ def test_delete_all_for_with_dependent_option_destroy
197
+ person = people(:david)
198
+ assert_equal 1, person.jobs_with_dependent_destroy.count
199
+
200
+ assert_no_difference 'Job.count' do
201
+ assert_difference 'Reference.count', -1 do
202
+ person.reload.jobs_with_dependent_destroy.delete_all
203
+ end
204
+ end
205
+ end
206
+
207
+ def test_delete_all_for_with_dependent_option_nullify
208
+ person = people(:david)
209
+ assert_equal 1, person.jobs_with_dependent_nullify.count
210
+
211
+ assert_no_difference 'Job.count' do
212
+ assert_no_difference 'Reference.count' do
213
+ person.reload.jobs_with_dependent_nullify.delete_all
214
+ end
215
+ end
216
+ end
217
+
218
+ def test_delete_all_for_with_dependent_option_delete_all
219
+ person = people(:david)
220
+ assert_equal 1, person.jobs_with_dependent_delete_all.count
221
+
222
+ assert_no_difference 'Job.count' do
223
+ assert_difference 'Reference.count', -1 do
224
+ person.reload.jobs_with_dependent_delete_all.delete_all
225
+ end
226
+ end
227
+ end
228
+
229
+ def test_concat
230
+ person = people(:david)
231
+ post = posts(:thinking)
232
+ post.people.concat [person]
233
+ assert_equal 1, post.people.size
234
+ assert_equal 1, post.people.reload.size
235
+ end
236
+
237
+ def test_associate_existing_record_twice_should_add_to_target_twice
238
+ post = posts(:thinking)
239
+ person = people(:david)
240
+
241
+ assert_difference 'post.people.to_a.count', 2 do
242
+ post.people << person
243
+ post.people << person
244
+ end
245
+ end
246
+
247
+ def test_associate_existing_record_twice_should_add_records_twice
248
+ post = posts(:thinking)
249
+ person = people(:david)
250
+
251
+ assert_difference 'post.people.count', 2 do
252
+ post.people << person
253
+ post.people << person
254
+ end
255
+ end
256
+
257
+ def test_add_two_instance_and_then_deleting
258
+ post = posts(:thinking)
259
+ person = people(:david)
260
+
261
+ post.people << person
262
+ post.people << person
263
+
264
+ counts = ['post.people.count', 'post.people.to_a.count', 'post.readers.count', 'post.readers.to_a.count']
265
+ assert_difference counts, -2 do
266
+ post.people.delete(person)
267
+ end
268
+
269
+ assert !post.people.reload.include?(person)
270
+ end
271
+
272
+ def test_associating_new
273
+ assert_queries(1) { posts(:thinking) }
274
+ new_person = nil # so block binding catches it
275
+
276
+ assert_queries(0) do
277
+ new_person = Person.new :first_name => 'bob'
278
+ end
279
+
280
+ # Associating new records always saves them
281
+ # Thus, 1 query for the new person record, 1 query for the new join table record
282
+ assert_queries(2) do
283
+ posts(:thinking).people << new_person
284
+ end
285
+
286
+ assert_queries(1) do
287
+ assert posts(:thinking).people.include?(new_person)
288
+ end
289
+
290
+ assert posts(:thinking).reload.people.reload.include?(new_person)
291
+ end
292
+
293
+ def test_associate_new_by_building
294
+ assert_queries(1) { posts(:thinking) }
295
+
296
+ assert_queries(0) do
297
+ posts(:thinking).people.build(:first_name => "Bob")
298
+ posts(:thinking).people.new(:first_name => "Ted")
299
+ end
300
+
301
+ # Should only need to load the association once
302
+ assert_queries(1) do
303
+ assert posts(:thinking).people.collect(&:first_name).include?("Bob")
304
+ assert posts(:thinking).people.collect(&:first_name).include?("Ted")
305
+ end
306
+
307
+ # 2 queries for each new record (1 to save the record itself, 1 for the join model)
308
+ # * 2 new records = 4
309
+ # + 1 query to save the actual post = 5
310
+ assert_queries(5) do
311
+ posts(:thinking).body += '-changed'
312
+ posts(:thinking).save
313
+ end
314
+
315
+ assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Bob")
316
+ assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Ted")
317
+ end
318
+
319
+ def test_build_then_save_with_has_many_inverse
320
+ post = posts(:thinking)
321
+ person = post.people.build(:first_name => "Bob")
322
+ person.save
323
+ post.reload
324
+
325
+ assert post.people.include?(person)
326
+ end
327
+
328
+ def test_build_then_save_with_has_one_inverse
329
+ post = posts(:thinking)
330
+ person = post.single_people.build(:first_name => "Bob")
331
+ person.save
332
+ post.reload
333
+
334
+ assert post.single_people.include?(person)
335
+ end
336
+
337
+ def test_both_parent_ids_set_when_saving_new
338
+ post = Post.new(title: 'Hello', body: 'world')
339
+ person = Person.new(first_name: 'Sean')
340
+
341
+ post.people = [person]
342
+ post.save
343
+
344
+ assert post.id
345
+ assert person.id
346
+ assert_equal post.id, post.readers.first.post_id
347
+ assert_equal person.id, post.readers.first.person_id
348
+ end
349
+
350
+ def test_delete_association
351
+ assert_queries(2){posts(:welcome);people(:michael); }
352
+
353
+ assert_queries(1) do
354
+ posts(:welcome).people.delete(people(:michael))
355
+ end
356
+
357
+ assert_queries(1) do
358
+ assert posts(:welcome).people.empty?
359
+ end
360
+
361
+ assert posts(:welcome).reload.people.reload.empty?
362
+ end
363
+
364
+ def test_destroy_association
365
+ assert_no_difference "Person.count" do
366
+ assert_difference "Reader.count", -1 do
367
+ posts(:welcome).people.destroy(people(:michael))
368
+ end
369
+ end
370
+
371
+ assert posts(:welcome).reload.people.empty?
372
+ assert posts(:welcome).people.reload.empty?
373
+ end
374
+
375
+ def test_destroy_all
376
+ assert_no_difference "Person.count" do
377
+ assert_difference "Reader.count", -1 do
378
+ posts(:welcome).people.destroy_all
379
+ end
380
+ end
381
+
382
+ assert posts(:welcome).reload.people.empty?
383
+ assert posts(:welcome).people.reload.empty?
384
+ end
385
+
386
+ def test_should_raise_exception_for_destroying_mismatching_records
387
+ assert_no_difference ["Person.count", "Reader.count"] do
388
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:welcome).people.destroy(posts(:thinking)) }
389
+ end
390
+ end
391
+
392
+ def test_delete_through_belongs_to_with_dependent_nullify
393
+ Reference.make_comments = true
394
+
395
+ person = people(:michael)
396
+ job = jobs(:magician)
397
+ reference = Reference.where(:job_id => job.id, :person_id => person.id).first
398
+
399
+ assert_no_difference ['Job.count', 'Reference.count'] do
400
+ assert_difference 'person.jobs.count', -1 do
401
+ person.jobs_with_dependent_nullify.delete(job)
402
+ end
403
+ end
404
+
405
+ assert_equal nil, reference.reload.job_id
406
+ ensure
407
+ Reference.make_comments = false
408
+ end
409
+
410
+ def test_delete_through_belongs_to_with_dependent_delete_all
411
+ Reference.make_comments = true
412
+
413
+ person = people(:michael)
414
+ job = jobs(:magician)
415
+
416
+ # Make sure we're not deleting everything
417
+ assert person.jobs.count >= 2
418
+
419
+ assert_no_difference 'Job.count' do
420
+ assert_difference ['person.jobs.count', 'Reference.count'], -1 do
421
+ person.jobs_with_dependent_delete_all.delete(job)
422
+ end
423
+ end
424
+
425
+ # Check that the destroy callback on Reference did not run
426
+ assert_equal nil, person.reload.comments
427
+ ensure
428
+ Reference.make_comments = false
429
+ end
430
+
431
+ def test_delete_through_belongs_to_with_dependent_destroy
432
+ Reference.make_comments = true
433
+
434
+ person = people(:michael)
435
+ job = jobs(:magician)
436
+
437
+ # Make sure we're not deleting everything
438
+ assert person.jobs.count >= 2
439
+
440
+ assert_no_difference 'Job.count' do
441
+ assert_difference ['person.jobs.count', 'Reference.count'], -1 do
442
+ person.jobs_with_dependent_destroy.delete(job)
443
+ end
444
+ end
445
+
446
+ # Check that the destroy callback on Reference ran
447
+ assert_equal "Reference destroyed", person.reload.comments
448
+ ensure
449
+ Reference.make_comments = false
450
+ end
451
+
452
+ def test_belongs_to_with_dependent_destroy
453
+ person = PersonWithDependentDestroyJobs.find(1)
454
+
455
+ # Create a reference which is not linked to a job. This should not be destroyed.
456
+ person.references.create!
457
+
458
+ assert_no_difference 'Job.count' do
459
+ assert_difference 'Reference.count', -person.jobs.count do
460
+ person.destroy
461
+ end
462
+ end
463
+ end
464
+
465
+ def test_belongs_to_with_dependent_delete_all
466
+ person = PersonWithDependentDeleteAllJobs.find(1)
467
+
468
+ # Create a reference which is not linked to a job. This should not be destroyed.
469
+ person.references.create!
470
+
471
+ assert_no_difference 'Job.count' do
472
+ assert_difference 'Reference.count', -person.jobs.count do
473
+ person.destroy
474
+ end
475
+ end
476
+ end
477
+
478
+ def test_belongs_to_with_dependent_nullify
479
+ person = PersonWithDependentNullifyJobs.find(1)
480
+
481
+ references = person.references.to_a
482
+
483
+ assert_no_difference ['Reference.count', 'Job.count'] do
484
+ person.destroy
485
+ end
486
+
487
+ references.each do |reference|
488
+ assert_equal nil, reference.reload.job_id
489
+ end
490
+ end
491
+
492
+ def test_update_counter_caches_on_delete
493
+ post = posts(:welcome)
494
+ tag = post.tags.create!(:name => 'doomed')
495
+
496
+ assert_difference ['post.reload.tags_count'], -1 do
497
+ posts(:welcome).tags.delete(tag)
498
+ end
499
+ end
500
+
501
+ def test_update_counter_caches_on_delete_with_dependent_destroy
502
+ post = posts(:welcome)
503
+ tag = post.tags.create!(:name => 'doomed')
504
+ post.update_columns(tags_with_destroy_count: post.tags.count)
505
+
506
+ assert_difference ['post.reload.tags_with_destroy_count'], -1 do
507
+ posts(:welcome).tags_with_destroy.delete(tag)
508
+ end
509
+ end
510
+
511
+ def test_update_counter_caches_on_delete_with_dependent_nullify
512
+ post = posts(:welcome)
513
+ tag = post.tags.create!(:name => 'doomed')
514
+ post.update_columns(tags_with_nullify_count: post.tags.count)
515
+
516
+ assert_no_difference 'post.reload.tags_count' do
517
+ assert_difference 'post.reload.tags_with_nullify_count', -1 do
518
+ posts(:welcome).tags_with_nullify.delete(tag)
519
+ end
520
+ end
521
+ end
522
+
523
+ def test_update_counter_caches_on_replace_association
524
+ post = posts(:welcome)
525
+ tag = post.tags.create!(:name => 'doomed')
526
+ tag.tagged_posts << posts(:thinking)
527
+
528
+ tag.tagged_posts = []
529
+ post.reload
530
+
531
+ assert_equal(post.taggings.count, post.tags_count)
532
+ end
533
+
534
+ def test_update_counter_caches_on_destroy
535
+ post = posts(:welcome)
536
+ tag = post.tags.create!(name: 'doomed')
537
+
538
+ assert_difference 'post.reload.tags_count', -1 do
539
+ tag.tagged_posts.destroy(post)
540
+ end
541
+ end
542
+
543
+ def test_replace_association
544
+ assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people.reload}
545
+
546
+ # 1 query to delete the existing reader (michael)
547
+ # 1 query to associate the new reader (david)
548
+ assert_queries(2) do
549
+ posts(:welcome).people = [people(:david)]
550
+ end
551
+
552
+ assert_queries(0){
553
+ assert posts(:welcome).people.include?(people(:david))
554
+ assert !posts(:welcome).people.include?(people(:michael))
555
+ }
556
+
557
+ assert posts(:welcome).reload.people.reload.include?(people(:david))
558
+ assert !posts(:welcome).reload.people.reload.include?(people(:michael))
559
+ end
560
+
561
+ def test_replace_order_is_preserved
562
+ posts(:welcome).people.clear
563
+ posts(:welcome).people = [people(:david), people(:michael)]
564
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
565
+
566
+ # Test the inverse order in case the first success was a coincidence
567
+ posts(:welcome).people.clear
568
+ posts(:welcome).people = [people(:michael), people(:david)]
569
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
570
+ end
571
+
572
+ def test_replace_by_id_order_is_preserved
573
+ posts(:welcome).people.clear
574
+ posts(:welcome).person_ids = [people(:david).id, people(:michael).id]
575
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
576
+
577
+ # Test the inverse order in case the first success was a coincidence
578
+ posts(:welcome).people.clear
579
+ posts(:welcome).person_ids = [people(:michael).id, people(:david).id]
580
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
581
+ end
582
+
583
+ def test_associate_with_create
584
+ assert_queries(1) { posts(:thinking) }
585
+
586
+ # 1 query for the new record, 1 for the join table record
587
+ # No need to update the actual collection yet!
588
+ assert_queries(2) do
589
+ posts(:thinking).people.create(:first_name=>"Jeb")
590
+ end
591
+
592
+ # *Now* we actually need the collection so it's loaded
593
+ assert_queries(1) do
594
+ assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
595
+ end
596
+
597
+ assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Jeb")
598
+ end
599
+
600
+ def test_through_record_is_built_when_created_with_where
601
+ assert_difference("posts(:thinking).readers.count", 1) do
602
+ posts(:thinking).people.where(first_name: "Jeb").create
603
+ end
604
+ end
605
+
606
+ def test_associate_with_create_and_no_options
607
+ peeps = posts(:thinking).people.count
608
+ posts(:thinking).people.create(:first_name => 'foo')
609
+ assert_equal peeps + 1, posts(:thinking).people.count
610
+ end
611
+
612
+ def test_associate_with_create_with_through_having_conditions
613
+ impatient_people = posts(:thinking).impatient_people.count
614
+ posts(:thinking).impatient_people.create!(:first_name => 'foo')
615
+ assert_equal impatient_people + 1, posts(:thinking).impatient_people.count
616
+ end
617
+
618
+ def test_associate_with_create_exclamation_and_no_options
619
+ peeps = posts(:thinking).people.count
620
+ posts(:thinking).people.create!(:first_name => 'foo')
621
+ assert_equal peeps + 1, posts(:thinking).people.count
622
+ end
623
+
624
+ def test_create_on_new_record
625
+ p = Post.new
626
+
627
+ error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(:first_name => "mew") }
628
+ assert_equal "You cannot call create unless the parent is saved", error.message
629
+
630
+ error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(:first_name => "snow") }
631
+ assert_equal "You cannot call create unless the parent is saved", error.message
632
+ end
633
+
634
+ def test_associate_with_create_and_invalid_options
635
+ firm = companies(:first_firm)
636
+ assert_no_difference('firm.developers.count') { assert_nothing_raised { firm.developers.create(:name => '0') } }
637
+ end
638
+
639
+ def test_associate_with_create_and_valid_options
640
+ firm = companies(:first_firm)
641
+ assert_difference('firm.developers.count', 1) { firm.developers.create(:name => 'developer') }
642
+ end
643
+
644
+ def test_associate_with_create_bang_and_invalid_options
645
+ firm = companies(:first_firm)
646
+ assert_no_difference('firm.developers.count') { assert_raises(ActiveRecord::RecordInvalid) { firm.developers.create!(:name => '0') } }
647
+ end
648
+
649
+ def test_associate_with_create_bang_and_valid_options
650
+ firm = companies(:first_firm)
651
+ assert_difference('firm.developers.count', 1) { firm.developers.create!(:name => 'developer') }
652
+ end
653
+
654
+ def test_push_with_invalid_record
655
+ firm = companies(:first_firm)
656
+ assert_raises(ActiveRecord::RecordInvalid) { firm.developers << Developer.new(:name => '0') }
657
+ end
658
+
659
+ def test_push_with_invalid_join_record
660
+ repair_validations(Contract) do
661
+ Contract.validate {|r| r.errors[:base] << 'Invalid Contract' }
662
+
663
+ firm = companies(:first_firm)
664
+ lifo = Developer.new(:name => 'lifo')
665
+ assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
666
+
667
+ lifo = Developer.create!(:name => 'lifo')
668
+ assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
669
+ end
670
+ end
671
+
672
+ def test_clear_associations
673
+ assert_queries(2) { posts(:welcome);posts(:welcome).people.reload }
674
+
675
+ assert_queries(1) do
676
+ posts(:welcome).people.clear
677
+ end
678
+
679
+ assert_queries(0) do
680
+ assert posts(:welcome).people.empty?
681
+ end
682
+
683
+ assert posts(:welcome).reload.people.reload.empty?
684
+ end
685
+
686
+ def test_association_callback_ordering
687
+ Post.reset_log
688
+ log = Post.log
689
+ post = posts(:thinking)
690
+
691
+ post.people_with_callbacks << people(:michael)
692
+ assert_equal [
693
+ [:added, :before, "Michael"],
694
+ [:added, :after, "Michael"]
695
+ ], log.last(2)
696
+
697
+ post.people_with_callbacks.push(people(:david), Person.create!(:first_name => "Bob"), Person.new(:first_name => "Lary"))
698
+ assert_equal [
699
+ [:added, :before, "David"],
700
+ [:added, :after, "David"],
701
+ [:added, :before, "Bob"],
702
+ [:added, :after, "Bob"],
703
+ [:added, :before, "Lary"],
704
+ [:added, :after, "Lary"]
705
+ ],log.last(6)
706
+
707
+ post.people_with_callbacks.build(:first_name => "Ted")
708
+ assert_equal [
709
+ [:added, :before, "Ted"],
710
+ [:added, :after, "Ted"]
711
+ ], log.last(2)
712
+
713
+ post.people_with_callbacks.create(:first_name => "Sam")
714
+ assert_equal [
715
+ [:added, :before, "Sam"],
716
+ [:added, :after, "Sam"]
717
+ ], log.last(2)
718
+
719
+ post.people_with_callbacks = [people(:michael),people(:david), Person.new(:first_name => "Julian"), Person.create!(:first_name => "Roger")]
720
+ assert_equal((%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort)
721
+ assert_equal [
722
+ [:added, :before, "Julian"],
723
+ [:added, :after, "Julian"],
724
+ [:added, :before, "Roger"],
725
+ [:added, :after, "Roger"]
726
+ ], log.last(4)
727
+ end
728
+
729
+ def test_dynamic_find_should_respect_association_include
730
+ # SQL error in sort clause if :include is not included
731
+ # due to Unknown column 'comments.id'
732
+ assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog')
733
+ end
734
+
735
+ def test_count_with_include_should_alias_join_table
736
+ assert_equal 2, people(:michael).posts.includes(:readers).count
737
+ end
738
+
739
+ def test_inner_join_with_quoted_table_name
740
+ assert_equal 2, people(:michael).jobs.size
741
+ end
742
+
743
+ def test_get_ids
744
+ assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
745
+ end
746
+
747
+ def test_get_ids_for_has_many_through_with_conditions_should_not_preload
748
+ Tagging.create!(:taggable_type => 'Post', :taggable_id => posts(:welcome).id, :tag => tags(:misc))
749
+ assert_not_called(ActiveRecord::Associations::Preloader, :new) do
750
+ posts(:welcome).misc_tag_ids
751
+ end
752
+ end
753
+
754
+ def test_get_ids_for_loaded_associations
755
+ person = people(:michael)
756
+ person.posts.reload
757
+ assert_queries(0) do
758
+ person.post_ids
759
+ person.post_ids
760
+ end
761
+ end
762
+
763
+ def test_get_ids_for_unloaded_associations_does_not_load_them
764
+ person = people(:michael)
765
+ assert !person.posts.loaded?
766
+ assert_equal [posts(:welcome).id, posts(:authorless).id].sort, person.post_ids.sort
767
+ assert !person.posts.loaded?
768
+ end
769
+
770
+ def test_association_proxy_transaction_method_starts_transaction_in_association_class
771
+ assert_called(Tag, :transaction) do
772
+ Post.first.tags.transaction do
773
+ # nothing
774
+ end
775
+ end
776
+ end
777
+
778
+ def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
779
+ post = Post.create!(:title => "TITLE", :body => "BODY")
780
+ assert_equal [], post.author_favorites
781
+ end
782
+
783
+ def test_has_many_association_through_a_belongs_to_association
784
+ author = authors(:mary)
785
+ post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
786
+ author.author_favorites.create(:favorite_author_id => 1)
787
+ author.author_favorites.create(:favorite_author_id => 2)
788
+ author.author_favorites.create(:favorite_author_id => 3)
789
+ assert_equal post.author.author_favorites, post.author_favorites
790
+ end
791
+
792
+ def test_merge_join_association_with_has_many_through_association_proxy
793
+ author = authors(:mary)
794
+ assert_nothing_raised { author.comments.ratings.to_sql }
795
+ end
796
+
797
+ def test_has_many_association_through_a_has_many_association_with_nonstandard_primary_keys
798
+ assert_equal 2, owners(:blackbeard).toys.count
799
+ end
800
+
801
+ def test_find_on_has_many_association_collection_with_include_and_conditions
802
+ post_with_no_comments = people(:michael).posts_with_no_comments.first
803
+ assert_equal post_with_no_comments, posts(:authorless)
804
+ end
805
+
806
+ def test_has_many_through_has_one_reflection
807
+ assert_equal [comments(:eager_sti_on_associations_vs_comment)], authors(:david).very_special_comments
808
+ end
809
+
810
+ def test_modifying_has_many_through_has_one_reflection_should_raise
811
+ [
812
+ lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(:body => "Gorp!", :post_id => 1011), VerySpecialComment.create!(:body => "Eep!", :post_id => 1012)] },
813
+ lambda { authors(:david).very_special_comments << VerySpecialComment.create!(:body => "Hoohah!", :post_id => 1013) },
814
+ lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
815
+ ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
816
+ end
817
+
818
+ def test_has_many_association_through_a_has_many_association_to_self
819
+ sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1)
820
+ john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1)
821
+ assert_equal sarah.agents, [john]
822
+ assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
823
+ end
824
+
825
+ def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
826
+ Categorization.create(:author => authors(:mary), :named_category_name => categories(:general).name)
827
+ assert_equal categories(:general), authors(:mary).named_categories.first
828
+ end
829
+
830
+ def test_collection_build_with_nonstandard_primary_key_on_belongs_to
831
+ author = authors(:mary)
832
+ category = author.named_categories.build(:name => "Primary")
833
+ author.save
834
+ assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
835
+ assert author.named_categories.reload.include?(category)
836
+ end
837
+
838
+ def test_collection_create_with_nonstandard_primary_key_on_belongs_to
839
+ author = authors(:mary)
840
+ category = author.named_categories.create(:name => "Primary")
841
+ assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
842
+ assert author.named_categories.reload.include?(category)
843
+ end
844
+
845
+ def test_collection_exists
846
+ author = authors(:mary)
847
+ category = Category.create!(author_ids: [author.id], name: "Primary")
848
+ assert category.authors.exists?(id: author.id)
849
+ assert category.reload.authors.exists?(id: author.id)
850
+ end
851
+
852
+ def test_collection_delete_with_nonstandard_primary_key_on_belongs_to
853
+ author = authors(:mary)
854
+ category = author.named_categories.create(:name => "Primary")
855
+ author.named_categories.delete(category)
856
+ assert !Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
857
+ assert author.named_categories.reload.empty?
858
+ end
859
+
860
+ def test_collection_singular_ids_getter_with_string_primary_keys
861
+ book = books(:awdr)
862
+ assert_equal 2, book.subscriber_ids.size
863
+ assert_equal [subscribers(:first).nick, subscribers(:second).nick].sort, book.subscriber_ids.sort
864
+ end
865
+
866
+ def test_collection_singular_ids_setter
867
+ company = companies(:rails_core)
868
+ dev = Developer.first
869
+
870
+ company.developer_ids = [dev.id]
871
+ assert_equal [dev], company.developers
872
+ end
873
+
874
+ def test_collection_singular_ids_setter_with_string_primary_keys
875
+ assert_nothing_raised do
876
+ book = books(:awdr)
877
+ book.subscriber_ids = [subscribers(:second).nick]
878
+ assert_equal [subscribers(:second)], book.subscribers.reload
879
+
880
+ book.subscriber_ids = []
881
+ assert_equal [], book.subscribers.reload
882
+ end
883
+
884
+ end
885
+
886
+ def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
887
+ company = companies(:rails_core)
888
+ ids = [Developer.first.id, -9999]
889
+ e = assert_raises(ActiveRecord::RecordNotFound) { company.developer_ids = ids }
890
+ assert_match(/Couldn't find all Developers with 'id'/, e.message)
891
+ end
892
+
893
+ def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set
894
+ author = authors(:david)
895
+ ids = [categories(:general).name, "Unknown"]
896
+ e = assert_raises(ActiveRecord::RecordNotFound) { author.essay_category_ids = ids }
897
+ assert_equal "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2)", e.message
898
+ end
899
+
900
+ def test_build_a_model_from_hm_through_association_with_where_clause
901
+ assert_nothing_raised { books(:awdr).subscribers.where(:nick => "marklazz").build }
902
+ end
903
+
904
+ def test_attributes_are_being_set_when_initialized_from_hm_through_association_with_where_clause
905
+ new_subscriber = books(:awdr).subscribers.where(:nick => "marklazz").build
906
+ assert_equal new_subscriber.nick, "marklazz"
907
+ end
908
+
909
+ def test_attributes_are_being_set_when_initialized_from_hm_through_association_with_multiple_where_clauses
910
+ new_subscriber = books(:awdr).subscribers.where(:nick => "marklazz").where(:name => 'Marcelo Giorgi').build
911
+ assert_equal new_subscriber.nick, "marklazz"
912
+ assert_equal new_subscriber.name, "Marcelo Giorgi"
913
+ end
914
+
915
+ def test_include_method_in_association_through_should_return_true_for_instance_added_with_build
916
+ person = Person.new
917
+ reference = person.references.build
918
+ job = reference.build_job
919
+ assert person.jobs.include?(job)
920
+ end
921
+
922
+ def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
923
+ author = Author.new
924
+ post = author.posts.build
925
+ comment = post.comments.build
926
+ assert author.comments.include?(comment)
927
+ end
928
+
929
+ def test_through_association_readonly_should_be_false
930
+ assert !people(:michael).posts.first.readonly?
931
+ assert !people(:michael).posts.to_a.first.readonly?
932
+ end
933
+
934
+ def test_can_update_through_association
935
+ assert_nothing_raised do
936
+ people(:michael).posts.first.update!(title: "Can write")
937
+ end
938
+ end
939
+
940
+ def test_has_many_through_polymorphic_with_primary_key_option
941
+ assert_equal [categories(:general)], authors(:david).essay_categories
942
+
943
+ authors = Author.joins(:essay_categories).where('categories.id' => categories(:general).id)
944
+ assert_equal authors(:david), authors.first
945
+
946
+ assert_equal [owners(:blackbeard)], authors(:david).essay_owners
947
+
948
+ authors = Author.joins(:essay_owners).where("owners.name = 'blackbeard'")
949
+ assert_equal authors(:david), authors.first
950
+ end
951
+
952
+ def test_has_many_through_with_primary_key_option
953
+ assert_equal [categories(:general)], authors(:david).essay_categories_2
954
+
955
+ authors = Author.joins(:essay_categories_2).where('categories.id' => categories(:general).id)
956
+ assert_equal authors(:david), authors.first
957
+ end
958
+
959
+ def test_size_of_through_association_should_increase_correctly_when_has_many_association_is_added
960
+ post = posts(:thinking)
961
+ readers = post.readers.size
962
+ post.people << people(:michael)
963
+ assert_equal readers + 1, post.readers.size
964
+ end
965
+
966
+ def test_has_many_through_with_default_scope_on_join_model
967
+ assert_equal posts(:welcome).comments.order('id').to_a, authors(:david).comments_on_first_posts
968
+ end
969
+
970
+ def test_create_has_many_through_with_default_scope_on_join_model
971
+ category = authors(:david).special_categories.create(:name => "Foo")
972
+ assert_equal 1, category.categorizations.where(:special => true).count
973
+ end
974
+
975
+ def test_joining_has_many_through_with_distinct
976
+ mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first
977
+ assert_equal 1, mary.unique_categorized_posts.length
978
+ assert_equal 1, mary.unique_categorized_post_ids.length
979
+ end
980
+
981
+ def test_joining_has_many_through_belongs_to
982
+ posts = Post.joins(:author_categorizations).order('posts.id').
983
+ where('categorizations.id' => categorizations(:mary_thinking_sti).id)
984
+
985
+ assert_equal [posts(:eager_other), posts(:misc_by_mary), posts(:other_by_mary)], posts
986
+ end
987
+
988
+ def test_select_chosen_fields_only
989
+ author = authors(:david)
990
+ assert_equal ['body', 'id'].sort, author.comments.select('comments.body').first.attributes.keys.sort
991
+ end
992
+
993
+ def test_get_has_many_through_belongs_to_ids_with_conditions
994
+ assert_equal [categories(:general).id], authors(:mary).categories_like_general_ids
995
+ end
996
+
997
+ def test_get_collection_singular_ids_on_has_many_through_with_conditions_and_include
998
+ person = Person.first
999
+ assert_equal person.posts_with_no_comment_ids, person.posts_with_no_comments.map(&:id)
1000
+ end
1001
+
1002
+ def test_count_has_many_through_with_named_scope
1003
+ assert_equal 2, authors(:mary).categories.count
1004
+ assert_equal 1, authors(:mary).categories.general.count
1005
+ end
1006
+
1007
+ def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes
1008
+ post = posts(:eager_other)
1009
+
1010
+ post.author_categorizations
1011
+ proxy = post.send(:association_instance_get, :author_categorizations)
1012
+
1013
+ assert !proxy.stale_target?
1014
+ assert_equal authors(:mary).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
1015
+
1016
+ post.author_id = authors(:david).id
1017
+
1018
+ assert proxy.stale_target?
1019
+ assert_equal authors(:david).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
1020
+ end
1021
+
1022
+ def test_create_with_conditions_hash_on_through_association
1023
+ member = members(:groucho)
1024
+ club = member.clubs.create!
1025
+
1026
+ assert_equal true, club.reload.membership.favourite
1027
+ end
1028
+
1029
+ def test_deleting_from_has_many_through_a_belongs_to_should_not_try_to_update_counter
1030
+ post = posts(:welcome)
1031
+ address = author_addresses(:david_address)
1032
+
1033
+ assert post.author_addresses.include?(address)
1034
+ post.author_addresses.delete(address)
1035
+ assert post[:author_count].nil?
1036
+ end
1037
+
1038
+ def test_primary_key_option_on_source
1039
+ post = posts(:welcome)
1040
+ category = categories(:general)
1041
+ Categorization.create!(:post_id => post.id, :named_category_name => category.name)
1042
+
1043
+ assert_equal [category], post.named_categories
1044
+ assert_equal [category.name], post.named_category_ids # checks when target loaded
1045
+ assert_equal [category.name], post.reload.named_category_ids # checks when target no loaded
1046
+ end
1047
+
1048
+ def test_create_should_not_raise_exception_when_join_record_has_errors
1049
+ repair_validations(Categorization) do
1050
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1051
+ Category.create(:name => 'Fishing', :authors => [Author.first])
1052
+ end
1053
+ end
1054
+
1055
+ def test_assign_array_to_new_record_builds_join_records
1056
+ c = Category.new(:name => 'Fishing', :authors => [Author.first])
1057
+ assert_equal 1, c.categorizations.size
1058
+ end
1059
+
1060
+ def test_create_bang_should_raise_exception_when_join_record_has_errors
1061
+ repair_validations(Categorization) do
1062
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1063
+ assert_raises(ActiveRecord::RecordInvalid) do
1064
+ Category.create!(:name => 'Fishing', :authors => [Author.first])
1065
+ end
1066
+ end
1067
+ end
1068
+
1069
+ def test_save_bang_should_raise_exception_when_join_record_has_errors
1070
+ repair_validations(Categorization) do
1071
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1072
+ c = Category.new(:name => 'Fishing', :authors => [Author.first])
1073
+ assert_raises(ActiveRecord::RecordInvalid) do
1074
+ c.save!
1075
+ end
1076
+ end
1077
+ end
1078
+
1079
+ def test_save_returns_falsy_when_join_record_has_errors
1080
+ repair_validations(Categorization) do
1081
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1082
+ c = Category.new(:name => 'Fishing', :authors => [Author.first])
1083
+ assert_not c.save
1084
+ end
1085
+ end
1086
+
1087
+ def test_preloading_empty_through_association_via_joins
1088
+ person = Person.create!(:first_name => "Gaga")
1089
+ person = Person.where(:id => person.id).where('readers.id = 1 or 1=1').references(:readers).includes(:posts).to_a.first
1090
+
1091
+ assert person.posts.loaded?, 'person.posts should be loaded'
1092
+ assert_equal [], person.posts
1093
+ end
1094
+
1095
+ def test_preloading_empty_through_with_polymorphic_source_association
1096
+ owner = Owner.create!(name: "Rainbow Unicat")
1097
+ pet = Pet.create!(owner: owner)
1098
+ person = Person.create!(first_name: "Gaga")
1099
+ treasure = Treasure.create!(looter: person)
1100
+ non_looted_treasure = Treasure.create!()
1101
+ PetTreasure.create!(pet: pet, treasure: treasure, rainbow_color: "Ultra violet indigo")
1102
+ PetTreasure.create!(pet: pet, treasure: non_looted_treasure, rainbow_color: "Ultra violet indigo")
1103
+
1104
+ assert_equal [person], Owner.where(name: "Rainbow Unicat").includes(pets: :persons).first.persons.to_a
1105
+ end
1106
+
1107
+ def test_explicitly_joining_join_table
1108
+ assert_equal owners(:blackbeard).toys, owners(:blackbeard).toys.with_pet
1109
+ end
1110
+
1111
+ def test_has_many_through_with_polymorphic_source
1112
+ post = tags(:general).tagged_posts.create! :title => "foo", :body => "bar"
1113
+ assert_equal [tags(:general)], post.reload.tags
1114
+ end
1115
+
1116
+ def test_has_many_through_obeys_order_on_through_association
1117
+ owner = owners(:blackbeard)
1118
+ assert owner.toys.to_sql.include?("pets.name desc")
1119
+ assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name }
1120
+ end
1121
+
1122
+ def test_has_many_through_associations_sum_on_columns
1123
+ post1 = Post.create(title: "active", body: "sample")
1124
+ post2 = Post.create(title: "inactive", body: "sample")
1125
+
1126
+ person1 = Person.create(first_name: "aaron", followers_count: 1)
1127
+ person2 = Person.create(first_name: "schmit", followers_count: 2)
1128
+ person3 = Person.create(first_name: "bill", followers_count: 3)
1129
+ person4 = Person.create(first_name: "cal", followers_count: 4)
1130
+
1131
+ Reader.create(post_id: post1.id, person_id: person1.id)
1132
+ Reader.create(post_id: post1.id, person_id: person2.id)
1133
+ Reader.create(post_id: post1.id, person_id: person3.id)
1134
+ Reader.create(post_id: post1.id, person_id: person4.id)
1135
+
1136
+ Reader.create(post_id: post2.id, person_id: person1.id)
1137
+ Reader.create(post_id: post2.id, person_id: person2.id)
1138
+ Reader.create(post_id: post2.id, person_id: person3.id)
1139
+ Reader.create(post_id: post2.id, person_id: person4.id)
1140
+
1141
+ active_persons = Person.joins(:readers).joins(:posts).distinct(true).where("posts.title" => "active")
1142
+
1143
+ assert_equal active_persons.map(&:followers_count).reduce(:+), 10
1144
+ assert_equal active_persons.sum(:followers_count), 10
1145
+ assert_equal active_persons.sum(:followers_count), active_persons.map(&:followers_count).reduce(:+)
1146
+ end
1147
+
1148
+ def test_has_many_through_associations_on_new_records_use_null_relations
1149
+ person = Person.new
1150
+
1151
+ assert_no_queries(ignore_none: false) do
1152
+ assert_equal [], person.posts
1153
+ assert_equal [], person.posts.where(body: 'omg')
1154
+ assert_equal [], person.posts.pluck(:body)
1155
+ assert_equal 0, person.posts.sum(:tags_count)
1156
+ assert_equal 0, person.posts.count
1157
+ end
1158
+ end
1159
+
1160
+ def test_has_many_through_with_default_scope_on_the_target
1161
+ person = people(:michael)
1162
+ assert_equal [posts(:thinking).id], person.first_posts.map(&:id)
1163
+
1164
+ readers(:michael_authorless).update(first_post_id: 1)
1165
+ assert_equal [posts(:thinking).id], person.reload.first_posts.map(&:id)
1166
+ end
1167
+
1168
+ def test_has_many_through_with_includes_in_through_association_scope
1169
+ assert_not_empty posts(:welcome).author_address_extra_with_address
1170
+ end
1171
+
1172
+ def test_insert_records_via_has_many_through_association_with_scope
1173
+ club = Club.create!
1174
+ member = Member.create!
1175
+ Membership.create!(club: club, member: member)
1176
+
1177
+ club.favourites << member
1178
+ assert_equal [member], club.favourites
1179
+
1180
+ club.reload
1181
+ assert_equal [member], club.favourites
1182
+ end
1183
+
1184
+ def test_has_many_through_unscope_default_scope
1185
+ post = Post.create!(:title => 'Beaches', :body => "I like beaches!")
1186
+ Reader.create! :person => people(:david), :post => post
1187
+ LazyReader.create! :person => people(:susan), :post => post
1188
+
1189
+ assert_equal 2, post.people.to_a.size
1190
+ assert_equal 1, post.lazy_people.to_a.size
1191
+
1192
+ assert_equal 2, post.lazy_readers_unscope_skimmers.to_a.size
1193
+ assert_equal 2, post.lazy_people_unscope_skimmers.to_a.size
1194
+ end
1195
+
1196
+ def test_has_many_through_add_with_sti_middle_relation
1197
+ club = SuperClub.create!(name: 'Fight Club')
1198
+ member = Member.create!(name: 'Tyler Durden')
1199
+
1200
+ club.members << member
1201
+ assert_equal 1, SuperMembership.where(member_id: member.id, club_id: club.id).count
1202
+ end
1203
+
1204
+ def test_build_for_has_many_through_association
1205
+ organization = organizations(:nsa)
1206
+ author = organization.author
1207
+ post_direct = author.posts.build
1208
+ post_through = organization.posts.build
1209
+ assert_equal post_direct.author_id, post_through.author_id
1210
+ end
1211
+
1212
+ def test_has_many_through_with_scope_that_should_not_be_fully_merged
1213
+ Club.has_many :distinct_memberships, -> { distinct }, class_name: "Membership"
1214
+ Club.has_many :special_favourites, through: :distinct_memberships, source: :member
1215
+
1216
+ assert_nil Club.new.special_favourites.distinct_value
1217
+ end
1218
+
1219
+ def test_association_force_reload_with_only_true_is_deprecated
1220
+ post = Post.find(1)
1221
+
1222
+ assert_deprecated { post.people(true) }
1223
+ end
1224
+
1225
+ def test_has_many_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
1226
+ member = Member.create!
1227
+ club = Club.create!
1228
+ TenantMembership.create!(
1229
+ member: member,
1230
+ club: club
1231
+ )
1232
+
1233
+ TenantMembership.current_member = member
1234
+
1235
+ tenant_clubs = member.tenant_clubs
1236
+ assert_equal [club], tenant_clubs
1237
+
1238
+ TenantMembership.current_member = nil
1239
+
1240
+ other_member = Member.create!
1241
+ other_club = Club.create!
1242
+ TenantMembership.create!(
1243
+ member: other_member,
1244
+ club: other_club
1245
+ )
1246
+
1247
+ tenant_clubs = other_member.tenant_clubs
1248
+ assert_equal [other_club], tenant_clubs
1249
+ ensure
1250
+ TenantMembership.current_member = nil
1251
+ end
1252
+
1253
+ def test_through_scope_is_affected_by_unscoping
1254
+ author = authors(:david)
1255
+
1256
+ expected = author.comments.to_a
1257
+ FirstPost.unscoped do
1258
+ assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
1259
+ end
1260
+ end
1261
+
1262
+ def test_through_scope_isnt_affected_by_scoping
1263
+ author = authors(:david)
1264
+
1265
+ expected = author.comments_on_first_posts.to_a
1266
+ FirstPost.where(id: 2).scoping do
1267
+ author.comments_on_first_posts.reset
1268
+ assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
1269
+ end
1270
+ end
1271
+ end