ibm_db 4.0.0-x86-mingw32 → 5.0.2-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 (570) hide show
  1. checksums.yaml +5 -5
  2. data/MANIFEST +14 -14
  3. data/README +208 -208
  4. data/ext/Makefile +269 -0
  5. data/ext/Makefile.nt32 +181 -181
  6. data/ext/Makefile.nt32.191 +212 -212
  7. data/ext/extconf.rb +322 -291
  8. data/ext/gil_release_version +3 -0
  9. data/ext/ibm_db.c +11879 -11887
  10. data/ext/mkmf.log +110 -0
  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/ext/unicode_support_version +3 -0
  15. data/init.rb +41 -41
  16. data/lib/IBM_DB.rb +27 -27
  17. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3533 -3452
  18. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +5 -5
  19. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  20. data/lib/mswin32/ibm_db.rb +90 -90
  21. data/lib/mswin32/rb2x/i386/ibm_db.so +0 -0
  22. data/test/active_record/connection_adapters/fake_adapter.rb +49 -49
  23. data/test/assets/example.log +1 -1
  24. data/test/assets/test.txt +1 -1
  25. data/test/cases/adapter_test.rb +351 -351
  26. data/test/cases/adapters/mysql2/active_schema_test.rb +193 -193
  27. data/test/cases/adapters/mysql2/bind_parameter_test.rb +50 -50
  28. data/test/cases/adapters/mysql2/boolean_test.rb +100 -100
  29. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +63 -63
  30. data/test/cases/adapters/mysql2/charset_collation_test.rb +54 -54
  31. data/test/cases/adapters/mysql2/connection_test.rb +210 -210
  32. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +45 -45
  33. data/test/cases/adapters/mysql2/enum_test.rb +26 -26
  34. data/test/cases/adapters/mysql2/explain_test.rb +21 -21
  35. data/test/cases/adapters/mysql2/json_test.rb +195 -195
  36. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +83 -83
  37. data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -152
  38. data/test/cases/adapters/mysql2/schema_migrations_test.rb +59 -59
  39. data/test/cases/adapters/mysql2/schema_test.rb +126 -126
  40. data/test/cases/adapters/mysql2/sp_test.rb +36 -36
  41. data/test/cases/adapters/mysql2/sql_types_test.rb +14 -14
  42. data/test/cases/adapters/mysql2/table_options_test.rb +42 -42
  43. data/test/cases/adapters/mysql2/unsigned_type_test.rb +66 -66
  44. data/test/cases/adapters/postgresql/active_schema_test.rb +98 -98
  45. data/test/cases/adapters/postgresql/array_test.rb +339 -339
  46. data/test/cases/adapters/postgresql/bit_string_test.rb +82 -82
  47. data/test/cases/adapters/postgresql/bytea_test.rb +134 -134
  48. data/test/cases/adapters/postgresql/case_insensitive_test.rb +26 -26
  49. data/test/cases/adapters/postgresql/change_schema_test.rb +38 -38
  50. data/test/cases/adapters/postgresql/cidr_test.rb +25 -25
  51. data/test/cases/adapters/postgresql/citext_test.rb +78 -78
  52. data/test/cases/adapters/postgresql/collation_test.rb +53 -53
  53. data/test/cases/adapters/postgresql/composite_test.rb +132 -132
  54. data/test/cases/adapters/postgresql/connection_test.rb +257 -257
  55. data/test/cases/adapters/postgresql/datatype_test.rb +92 -92
  56. data/test/cases/adapters/postgresql/domain_test.rb +47 -47
  57. data/test/cases/adapters/postgresql/enum_test.rb +91 -91
  58. data/test/cases/adapters/postgresql/explain_test.rb +20 -20
  59. data/test/cases/adapters/postgresql/extension_migration_test.rb +63 -63
  60. data/test/cases/adapters/postgresql/full_text_test.rb +44 -44
  61. data/test/cases/adapters/postgresql/geometric_test.rb +378 -378
  62. data/test/cases/adapters/postgresql/hstore_test.rb +382 -382
  63. data/test/cases/adapters/postgresql/infinity_test.rb +69 -69
  64. data/test/cases/adapters/postgresql/integer_test.rb +25 -25
  65. data/test/cases/adapters/postgresql/json_test.rb +237 -237
  66. data/test/cases/adapters/postgresql/ltree_test.rb +53 -53
  67. data/test/cases/adapters/postgresql/money_test.rb +96 -96
  68. data/test/cases/adapters/postgresql/network_test.rb +94 -94
  69. data/test/cases/adapters/postgresql/numbers_test.rb +49 -49
  70. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +405 -405
  71. data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -22
  72. data/test/cases/adapters/postgresql/quoting_test.rb +44 -44
  73. data/test/cases/adapters/postgresql/range_test.rb +343 -343
  74. data/test/cases/adapters/postgresql/referential_integrity_test.rb +111 -111
  75. data/test/cases/adapters/postgresql/rename_table_test.rb +34 -34
  76. data/test/cases/adapters/postgresql/schema_authorization_test.rb +119 -119
  77. data/test/cases/adapters/postgresql/schema_test.rb +597 -597
  78. data/test/cases/adapters/postgresql/serial_test.rb +154 -154
  79. data/test/cases/adapters/postgresql/statement_pool_test.rb +41 -41
  80. data/test/cases/adapters/postgresql/timestamp_test.rb +90 -90
  81. data/test/cases/adapters/postgresql/type_lookup_test.rb +33 -33
  82. data/test/cases/adapters/postgresql/utils_test.rb +62 -62
  83. data/test/cases/adapters/postgresql/uuid_test.rb +294 -294
  84. data/test/cases/adapters/postgresql/xml_test.rb +54 -54
  85. data/test/cases/adapters/sqlite3/collation_test.rb +53 -53
  86. data/test/cases/adapters/sqlite3/copy_table_test.rb +98 -98
  87. data/test/cases/adapters/sqlite3/explain_test.rb +21 -21
  88. data/test/cases/adapters/sqlite3/quoting_test.rb +101 -101
  89. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +441 -441
  90. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -24
  91. data/test/cases/adapters/sqlite3/statement_pool_test.rb +20 -20
  92. data/test/cases/aggregations_test.rb +168 -168
  93. data/test/cases/ar_schema_test.rb +146 -146
  94. data/test/cases/associations/association_scope_test.rb +16 -16
  95. data/test/cases/associations/belongs_to_associations_test.rb +1141 -1141
  96. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +41 -41
  97. data/test/cases/associations/callbacks_test.rb +190 -190
  98. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -188
  99. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -36
  100. data/test/cases/associations/eager_load_nested_include_test.rb +126 -126
  101. data/test/cases/associations/eager_singularization_test.rb +148 -148
  102. data/test/cases/associations/eager_test.rb +1514 -1514
  103. data/test/cases/associations/extension_test.rb +87 -87
  104. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1004 -1004
  105. data/test/cases/associations/has_many_associations_test.rb +2501 -2501
  106. data/test/cases/associations/has_many_through_associations_test.rb +1271 -1271
  107. data/test/cases/associations/has_one_associations_test.rb +707 -707
  108. data/test/cases/associations/has_one_through_associations_test.rb +383 -383
  109. data/test/cases/associations/inner_join_association_test.rb +139 -139
  110. data/test/cases/associations/inverse_associations_test.rb +733 -733
  111. data/test/cases/associations/join_model_test.rb +777 -777
  112. data/test/cases/associations/left_outer_join_association_test.rb +88 -88
  113. data/test/cases/associations/nested_through_associations_test.rb +579 -579
  114. data/test/cases/associations/required_test.rb +102 -102
  115. data/test/cases/associations_test.rb +385 -385
  116. data/test/cases/attribute_decorators_test.rb +126 -125
  117. data/test/cases/attribute_methods/read_test.rb +60 -60
  118. data/test/cases/attribute_methods_test.rb +1009 -1009
  119. data/test/cases/attribute_set_test.rb +270 -270
  120. data/test/cases/attribute_test.rb +246 -246
  121. data/test/cases/attributes_test.rb +253 -253
  122. data/test/cases/autosave_association_test.rb +1708 -1708
  123. data/test/cases/base_test.rb +1713 -1713
  124. data/test/cases/batches_test.rb +489 -489
  125. data/test/cases/binary_test.rb +44 -44
  126. data/test/cases/bind_parameter_test.rb +110 -110
  127. data/test/cases/cache_key_test.rb +26 -25
  128. data/test/cases/calculations_test.rb +798 -798
  129. data/test/cases/callbacks_test.rb +636 -636
  130. data/test/cases/clone_test.rb +40 -40
  131. data/test/cases/coders/json_test.rb +15 -15
  132. data/test/cases/coders/yaml_column_test.rb +63 -63
  133. data/test/cases/collection_cache_key_test.rb +115 -115
  134. data/test/cases/column_alias_test.rb +17 -17
  135. data/test/cases/column_definition_test.rb +92 -92
  136. data/test/cases/comment_test.rb +145 -143
  137. data/test/cases/connection_adapters/adapter_leasing_test.rb +56 -56
  138. data/test/cases/connection_adapters/connection_handler_test.rb +160 -160
  139. data/test/cases/connection_adapters/connection_specification_test.rb +12 -12
  140. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +255 -255
  141. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +69 -69
  142. data/test/cases/connection_adapters/quoting_test.rb +13 -13
  143. data/test/cases/connection_adapters/schema_cache_test.rb +61 -61
  144. data/test/cases/connection_adapters/type_lookup_test.rb +118 -118
  145. data/test/cases/connection_management_test.rb +112 -112
  146. data/test/cases/connection_pool_test.rb +521 -521
  147. data/test/cases/connection_specification/resolver_test.rb +131 -131
  148. data/test/cases/core_test.rb +112 -112
  149. data/test/cases/counter_cache_test.rb +214 -214
  150. data/test/cases/custom_locking_test.rb +17 -17
  151. data/test/cases/database_statements_test.rb +34 -34
  152. data/test/cases/date_test.rb +44 -44
  153. data/test/cases/date_time_precision_test.rb +107 -106
  154. data/test/cases/date_time_test.rb +61 -61
  155. data/test/cases/defaults_test.rb +219 -218
  156. data/test/cases/dirty_test.rb +763 -763
  157. data/test/cases/disconnected_test.rb +30 -30
  158. data/test/cases/dup_test.rb +157 -157
  159. data/test/cases/enum_test.rb +444 -444
  160. data/test/cases/errors_test.rb +16 -16
  161. data/test/cases/explain_subscriber_test.rb +64 -64
  162. data/test/cases/explain_test.rb +87 -87
  163. data/test/cases/finder_respond_to_test.rb +60 -60
  164. data/test/cases/finder_test.rb +1294 -1294
  165. data/test/cases/fixture_set/file_test.rb +156 -156
  166. data/test/cases/fixtures_test.rb +988 -988
  167. data/test/cases/forbidden_attributes_protection_test.rb +165 -165
  168. data/test/cases/habtm_destroy_order_test.rb +61 -61
  169. data/test/cases/helper.rb +204 -204
  170. data/test/cases/hot_compatibility_test.rb +142 -142
  171. data/test/cases/i18n_test.rb +45 -45
  172. data/test/cases/inheritance_test.rb +606 -606
  173. data/test/cases/integration_test.rb +155 -155
  174. data/test/cases/invalid_connection_test.rb +24 -24
  175. data/test/cases/invertible_migration_test.rb +387 -387
  176. data/test/cases/json_serialization_test.rb +311 -311
  177. data/test/cases/locking_test.rb +493 -493
  178. data/test/cases/log_subscriber_test.rb +225 -225
  179. data/test/cases/migration/change_schema_test.rb +458 -458
  180. data/test/cases/migration/change_table_test.rb +256 -256
  181. data/test/cases/migration/column_attributes_test.rb +176 -176
  182. data/test/cases/migration/column_positioning_test.rb +56 -56
  183. data/test/cases/migration/columns_test.rb +310 -310
  184. data/test/cases/migration/command_recorder_test.rb +350 -350
  185. data/test/cases/migration/compatibility_test.rb +118 -118
  186. data/test/cases/migration/create_join_table_test.rb +157 -157
  187. data/test/cases/migration/foreign_key_test.rb +362 -360
  188. data/test/cases/migration/helper.rb +39 -39
  189. data/test/cases/migration/index_test.rb +218 -218
  190. data/test/cases/migration/logger_test.rb +36 -36
  191. data/test/cases/migration/pending_migrations_test.rb +52 -52
  192. data/test/cases/migration/references_foreign_key_test.rb +221 -216
  193. data/test/cases/migration/references_index_test.rb +101 -101
  194. data/test/cases/migration/references_statements_test.rb +136 -136
  195. data/test/cases/migration/rename_table_test.rb +93 -93
  196. data/test/cases/migration_test.rb +1157 -1157
  197. data/test/cases/migrator_test.rb +471 -470
  198. data/test/cases/mixin_test.rb +68 -68
  199. data/test/cases/modules_test.rb +172 -172
  200. data/test/cases/multiparameter_attributes_test.rb +372 -372
  201. data/test/cases/multiple_db_test.rb +122 -122
  202. data/test/cases/nested_attributes_test.rb +1098 -1098
  203. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -144
  204. data/test/cases/persistence_test.rb +1001 -1001
  205. data/test/cases/pooled_connections_test.rb +81 -81
  206. data/test/cases/primary_keys_test.rb +376 -376
  207. data/test/cases/query_cache_test.rb +446 -446
  208. data/test/cases/quoting_test.rb +202 -202
  209. data/test/cases/readonly_test.rb +119 -119
  210. data/test/cases/reaper_test.rb +85 -85
  211. data/test/cases/reflection_test.rb +509 -509
  212. data/test/cases/relation/delegation_test.rb +63 -63
  213. data/test/cases/relation/merging_test.rb +157 -157
  214. data/test/cases/relation/mutation_test.rb +183 -183
  215. data/test/cases/relation/or_test.rb +92 -92
  216. data/test/cases/relation/predicate_builder_test.rb +16 -16
  217. data/test/cases/relation/record_fetch_warning_test.rb +40 -40
  218. data/test/cases/relation/where_chain_test.rb +105 -105
  219. data/test/cases/relation/where_clause_test.rb +182 -182
  220. data/test/cases/relation/where_test.rb +322 -322
  221. data/test/cases/relation_test.rb +328 -328
  222. data/test/cases/relations_test.rb +2026 -2026
  223. data/test/cases/reload_models_test.rb +22 -22
  224. data/test/cases/result_test.rb +90 -90
  225. data/test/cases/sanitize_test.rb +176 -176
  226. data/test/cases/schema_dumper_test.rb +457 -457
  227. data/test/cases/schema_loading_test.rb +52 -52
  228. data/test/cases/scoping/default_scoping_test.rb +528 -528
  229. data/test/cases/scoping/named_scoping_test.rb +561 -561
  230. data/test/cases/scoping/relation_scoping_test.rb +400 -400
  231. data/test/cases/secure_token_test.rb +32 -32
  232. data/test/cases/serialization_test.rb +104 -104
  233. data/test/cases/serialized_attribute_test.rb +364 -364
  234. data/test/cases/statement_cache_test.rb +136 -136
  235. data/test/cases/store_test.rb +195 -195
  236. data/test/cases/suppressor_test.rb +63 -63
  237. data/test/cases/tasks/database_tasks_test.rb +462 -462
  238. data/test/cases/tasks/mysql_rake_test.rb +345 -345
  239. data/test/cases/tasks/postgresql_rake_test.rb +304 -304
  240. data/test/cases/tasks/sqlite_rake_test.rb +220 -220
  241. data/test/cases/test_case.rb +131 -131
  242. data/test/cases/test_fixtures_test.rb +36 -36
  243. data/test/cases/time_precision_test.rb +103 -102
  244. data/test/cases/timestamp_test.rb +501 -501
  245. data/test/cases/touch_later_test.rb +121 -121
  246. data/test/cases/transaction_callbacks_test.rb +518 -518
  247. data/test/cases/transaction_isolation_test.rb +106 -106
  248. data/test/cases/transactions_test.rb +835 -834
  249. data/test/cases/type/adapter_specific_registry_test.rb +133 -133
  250. data/test/cases/type/date_time_test.rb +14 -14
  251. data/test/cases/type/integer_test.rb +27 -27
  252. data/test/cases/type/string_test.rb +22 -22
  253. data/test/cases/type/type_map_test.rb +177 -177
  254. data/test/cases/type_test.rb +39 -39
  255. data/test/cases/types_test.rb +24 -24
  256. data/test/cases/unconnected_test.rb +33 -33
  257. data/test/cases/validations/absence_validation_test.rb +73 -73
  258. data/test/cases/validations/association_validation_test.rb +97 -97
  259. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -84
  260. data/test/cases/validations/i18n_validation_test.rb +86 -86
  261. data/test/cases/validations/length_validation_test.rb +79 -79
  262. data/test/cases/validations/presence_validation_test.rb +103 -103
  263. data/test/cases/validations/uniqueness_validation_test.rb +548 -548
  264. data/test/cases/validations_repair_helper.rb +19 -19
  265. data/test/cases/validations_test.rb +194 -194
  266. data/test/cases/view_test.rb +216 -216
  267. data/test/cases/yaml_serialization_test.rb +121 -121
  268. data/test/config.example.yml +97 -97
  269. data/test/config.rb +5 -5
  270. data/test/connections/native_ibm_db/connection.rb +44 -0
  271. data/test/fixtures/accounts.yml +29 -29
  272. data/test/fixtures/admin/accounts.yml +2 -2
  273. data/test/fixtures/admin/users.yml +10 -10
  274. data/test/fixtures/author_addresses.yml +17 -17
  275. data/test/fixtures/author_favorites.yml +3 -3
  276. data/test/fixtures/authors.yml +23 -23
  277. data/test/fixtures/bad_posts.yml +9 -9
  278. data/test/fixtures/binaries.yml +133 -133
  279. data/test/fixtures/books.yml +31 -31
  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 -3
  295. data/test/fixtures/content_positions.yml +3 -3
  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 -5
  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 -4
  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 -2
  333. data/test/fixtures/naked/yml/trees.yml +3 -3
  334. data/test/fixtures/nodes.yml +29 -29
  335. data/test/fixtures/organizations.yml +5 -5
  336. data/test/fixtures/other_comments.yml +6 -6
  337. data/test/fixtures/other_dogs.yml +2 -2
  338. data/test/fixtures/other_posts.yml +7 -7
  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 +12 -15
  347. data/test/fixtures/posts.yml +80 -80
  348. data/test/fixtures/price_estimates.yml +16 -16
  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 -3
  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/user.rb +40 -40
  414. data/test/models/aircraft.rb +5 -5
  415. data/test/models/arunit2_model.rb +3 -3
  416. data/test/models/author.rb +209 -209
  417. data/test/models/auto_id.rb +4 -4
  418. data/test/models/autoloadable/extra_firm.rb +2 -2
  419. data/test/models/binary.rb +2 -2
  420. data/test/models/bird.rb +12 -12
  421. data/test/models/book.rb +23 -23
  422. data/test/models/boolean.rb +2 -2
  423. data/test/models/bulb.rb +52 -52
  424. data/test/models/cake_designer.rb +3 -3
  425. data/test/models/car.rb +29 -29
  426. data/test/models/carrier.rb +2 -2
  427. data/test/models/cat.rb +10 -10
  428. data/test/models/categorization.rb +19 -19
  429. data/test/models/category.rb +35 -35
  430. data/test/models/chef.rb +8 -8
  431. data/test/models/citation.rb +3 -3
  432. data/test/models/club.rb +25 -25
  433. data/test/models/college.rb +10 -10
  434. data/test/models/column.rb +3 -3
  435. data/test/models/column_name.rb +3 -3
  436. data/test/models/comment.rb +76 -76
  437. data/test/models/company.rb +230 -230
  438. data/test/models/company_in_module.rb +98 -98
  439. data/test/models/computer.rb +3 -3
  440. data/test/models/contact.rb +41 -41
  441. data/test/models/content.rb +40 -40
  442. data/test/models/contract.rb +20 -20
  443. data/test/models/country.rb +7 -7
  444. data/test/models/course.rb +6 -6
  445. data/test/models/customer.rb +83 -83
  446. data/test/models/customer_carrier.rb +14 -14
  447. data/test/models/dashboard.rb +3 -3
  448. data/test/models/default.rb +2 -2
  449. data/test/models/department.rb +4 -4
  450. data/test/models/developer.rb +274 -274
  451. data/test/models/dog.rb +5 -5
  452. data/test/models/dog_lover.rb +5 -5
  453. data/test/models/doubloon.rb +12 -12
  454. data/test/models/drink_designer.rb +3 -3
  455. data/test/models/edge.rb +5 -5
  456. data/test/models/electron.rb +5 -5
  457. data/test/models/engine.rb +4 -4
  458. data/test/models/entrant.rb +3 -3
  459. data/test/models/essay.rb +5 -5
  460. data/test/models/event.rb +3 -3
  461. data/test/models/eye.rb +37 -37
  462. data/test/models/face.rb +9 -9
  463. data/test/models/friendship.rb +6 -6
  464. data/test/models/guid.rb +2 -2
  465. data/test/models/guitar.rb +4 -4
  466. data/test/models/hotel.rb +11 -11
  467. data/test/models/image.rb +3 -3
  468. data/test/models/interest.rb +5 -5
  469. data/test/models/invoice.rb +4 -4
  470. data/test/models/item.rb +7 -7
  471. data/test/models/job.rb +7 -7
  472. data/test/models/joke.rb +7 -7
  473. data/test/models/keyboard.rb +3 -3
  474. data/test/models/legacy_thing.rb +3 -3
  475. data/test/models/lesson.rb +11 -11
  476. data/test/models/line_item.rb +3 -3
  477. data/test/models/liquid.rb +4 -4
  478. data/test/models/man.rb +11 -11
  479. data/test/models/matey.rb +4 -4
  480. data/test/models/member.rb +42 -42
  481. data/test/models/member_detail.rb +8 -8
  482. data/test/models/member_type.rb +3 -3
  483. data/test/models/membership.rb +35 -35
  484. data/test/models/mentor.rb +2 -2
  485. data/test/models/minimalistic.rb +2 -2
  486. data/test/models/minivan.rb +9 -9
  487. data/test/models/mixed_case_monkey.rb +3 -3
  488. data/test/models/mocktail_designer.rb +2 -2
  489. data/test/models/molecule.rb +6 -6
  490. data/test/models/movie.rb +5 -5
  491. data/test/models/node.rb +5 -5
  492. data/test/models/non_primary_key.rb +2 -2
  493. data/test/models/notification.rb +3 -3
  494. data/test/models/order.rb +4 -4
  495. data/test/models/organization.rb +14 -14
  496. data/test/models/other_dog.rb +5 -5
  497. data/test/models/owner.rb +37 -37
  498. data/test/models/parrot.rb +28 -28
  499. data/test/models/person.rb +142 -142
  500. data/test/models/personal_legacy_thing.rb +4 -4
  501. data/test/models/pet.rb +18 -18
  502. data/test/models/pet_treasure.rb +6 -6
  503. data/test/models/pirate.rb +92 -92
  504. data/test/models/possession.rb +3 -3
  505. data/test/models/post.rb +273 -273
  506. data/test/models/price_estimate.rb +4 -4
  507. data/test/models/professor.rb +5 -5
  508. data/test/models/project.rb +40 -40
  509. data/test/models/publisher.rb +2 -2
  510. data/test/models/publisher/article.rb +4 -4
  511. data/test/models/publisher/magazine.rb +3 -3
  512. data/test/models/rating.rb +4 -4
  513. data/test/models/reader.rb +23 -23
  514. data/test/models/recipe.rb +3 -3
  515. data/test/models/record.rb +2 -2
  516. data/test/models/reference.rb +22 -22
  517. data/test/models/reply.rb +61 -61
  518. data/test/models/ship.rb +39 -39
  519. data/test/models/ship_part.rb +8 -8
  520. data/test/models/shop.rb +17 -17
  521. data/test/models/shop_account.rb +6 -6
  522. data/test/models/speedometer.rb +6 -6
  523. data/test/models/sponsor.rb +7 -7
  524. data/test/models/string_key_object.rb +3 -3
  525. data/test/models/student.rb +4 -4
  526. data/test/models/subject.rb +16 -16
  527. data/test/models/subscriber.rb +8 -8
  528. data/test/models/subscription.rb +4 -4
  529. data/test/models/tag.rb +13 -13
  530. data/test/models/tagging.rb +13 -13
  531. data/test/models/task.rb +5 -5
  532. data/test/models/topic.rb +118 -118
  533. data/test/models/toy.rb +6 -6
  534. data/test/models/traffic_light.rb +4 -4
  535. data/test/models/treasure.rb +14 -14
  536. data/test/models/treaty.rb +7 -7
  537. data/test/models/tree.rb +3 -3
  538. data/test/models/tuning_peg.rb +4 -4
  539. data/test/models/tyre.rb +11 -11
  540. data/test/models/user.rb +14 -14
  541. data/test/models/uuid_child.rb +3 -3
  542. data/test/models/uuid_item.rb +6 -6
  543. data/test/models/uuid_parent.rb +3 -3
  544. data/test/models/vegetables.rb +24 -24
  545. data/test/models/vehicle.rb +6 -6
  546. data/test/models/vertex.rb +9 -9
  547. data/test/models/warehouse_thing.rb +5 -5
  548. data/test/models/wheel.rb +3 -3
  549. data/test/models/without_table.rb +3 -3
  550. data/test/models/zine.rb +3 -3
  551. data/test/schema/i5/ibm_db_specific_schema.rb +137 -0
  552. data/test/schema/ids/ibm_db_specific_schema.rb +140 -0
  553. data/test/schema/luw/ibm_db_specific_schema.rb +137 -0
  554. data/test/schema/mysql2_specific_schema.rb +68 -68
  555. data/test/schema/oracle_specific_schema.rb +40 -40
  556. data/test/schema/postgresql_specific_schema.rb +114 -114
  557. data/test/schema/schema.rb +1057 -1057
  558. data/test/schema/schema.rb.original +1057 -1057
  559. data/test/schema/sqlite_specific_schema.rb +18 -18
  560. data/test/schema/zOS/ibm_db_specific_schema.rb +208 -0
  561. data/test/support/config.rb +43 -43
  562. data/test/support/connection.rb +23 -23
  563. data/test/support/connection_helper.rb +14 -14
  564. data/test/support/ddl_helper.rb +8 -8
  565. data/test/support/schema_dumping_helper.rb +20 -20
  566. data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -22
  567. data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -182
  568. metadata +24 -13
  569. data/test/fixtures/author_addresses.original +0 -11
  570. data/test/fixtures/authors.original +0 -17
@@ -1,1271 +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/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
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