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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (576) hide show
  1. checksums.yaml +5 -5
  2. data/MANIFEST +14 -14
  3. data/README +208 -208
  4. data/ext/Makefile +269 -0
  5. data/ext/Makefile.nt32 +181 -181
  6. data/ext/Makefile.nt32.191 +212 -212
  7. data/ext/extconf.rb +322 -291
  8. data/ext/gil_release_version.h +3 -0
  9. data/ext/ibm_db-i386-mingw32.def +2 -0
  10. data/ext/ibm_db.c +11879 -11887
  11. data/ext/ibm_db.o +0 -0
  12. data/ext/ibm_db.so +0 -0
  13. data/ext/mkmf.log +110 -0
  14. data/ext/ruby_ibm_db.h +241 -241
  15. data/ext/ruby_ibm_db_cli.c +866 -866
  16. data/ext/ruby_ibm_db_cli.h +500 -500
  17. data/ext/ruby_ibm_db_cli.o +0 -0
  18. data/ext/unicode_support_version.h +3 -0
  19. data/init.rb +41 -41
  20. data/lib/IBM_DB.rb +27 -27
  21. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3593 -3452
  22. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +5 -5
  23. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  24. data/lib/mswin32/ibm_db.rb +94 -90
  25. data/lib/mswin32/rb2x/i386/ruby26/ibm_db.so +0 -0
  26. data/lib/mswin32/rb2x/i386/ruby27/ibm_db.so +0 -0
  27. data/test/active_record/connection_adapters/fake_adapter.rb +49 -49
  28. data/test/assets/example.log +1 -1
  29. data/test/assets/test.txt +1 -1
  30. data/test/cases/adapter_test.rb +351 -351
  31. data/test/cases/adapters/mysql2/active_schema_test.rb +193 -193
  32. data/test/cases/adapters/mysql2/bind_parameter_test.rb +50 -50
  33. data/test/cases/adapters/mysql2/boolean_test.rb +100 -100
  34. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +63 -63
  35. data/test/cases/adapters/mysql2/charset_collation_test.rb +54 -54
  36. data/test/cases/adapters/mysql2/connection_test.rb +210 -210
  37. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +45 -45
  38. data/test/cases/adapters/mysql2/enum_test.rb +26 -26
  39. data/test/cases/adapters/mysql2/explain_test.rb +21 -21
  40. data/test/cases/adapters/mysql2/json_test.rb +195 -195
  41. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +83 -83
  42. data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -152
  43. data/test/cases/adapters/mysql2/schema_migrations_test.rb +59 -59
  44. data/test/cases/adapters/mysql2/schema_test.rb +126 -126
  45. data/test/cases/adapters/mysql2/sp_test.rb +36 -36
  46. data/test/cases/adapters/mysql2/sql_types_test.rb +14 -14
  47. data/test/cases/adapters/mysql2/table_options_test.rb +42 -42
  48. data/test/cases/adapters/mysql2/unsigned_type_test.rb +66 -66
  49. data/test/cases/adapters/postgresql/active_schema_test.rb +98 -98
  50. data/test/cases/adapters/postgresql/array_test.rb +339 -339
  51. data/test/cases/adapters/postgresql/bit_string_test.rb +82 -82
  52. data/test/cases/adapters/postgresql/bytea_test.rb +134 -134
  53. data/test/cases/adapters/postgresql/case_insensitive_test.rb +26 -26
  54. data/test/cases/adapters/postgresql/change_schema_test.rb +38 -38
  55. data/test/cases/adapters/postgresql/cidr_test.rb +25 -25
  56. data/test/cases/adapters/postgresql/citext_test.rb +78 -78
  57. data/test/cases/adapters/postgresql/collation_test.rb +53 -53
  58. data/test/cases/adapters/postgresql/composite_test.rb +132 -132
  59. data/test/cases/adapters/postgresql/connection_test.rb +257 -257
  60. data/test/cases/adapters/postgresql/datatype_test.rb +92 -92
  61. data/test/cases/adapters/postgresql/domain_test.rb +47 -47
  62. data/test/cases/adapters/postgresql/enum_test.rb +91 -91
  63. data/test/cases/adapters/postgresql/explain_test.rb +20 -20
  64. data/test/cases/adapters/postgresql/extension_migration_test.rb +63 -63
  65. data/test/cases/adapters/postgresql/full_text_test.rb +44 -44
  66. data/test/cases/adapters/postgresql/geometric_test.rb +378 -378
  67. data/test/cases/adapters/postgresql/hstore_test.rb +382 -382
  68. data/test/cases/adapters/postgresql/infinity_test.rb +69 -69
  69. data/test/cases/adapters/postgresql/integer_test.rb +25 -25
  70. data/test/cases/adapters/postgresql/json_test.rb +237 -237
  71. data/test/cases/adapters/postgresql/ltree_test.rb +53 -53
  72. data/test/cases/adapters/postgresql/money_test.rb +96 -96
  73. data/test/cases/adapters/postgresql/network_test.rb +94 -94
  74. data/test/cases/adapters/postgresql/numbers_test.rb +49 -49
  75. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +405 -405
  76. data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -22
  77. data/test/cases/adapters/postgresql/quoting_test.rb +44 -44
  78. data/test/cases/adapters/postgresql/range_test.rb +343 -343
  79. data/test/cases/adapters/postgresql/referential_integrity_test.rb +111 -111
  80. data/test/cases/adapters/postgresql/rename_table_test.rb +34 -34
  81. data/test/cases/adapters/postgresql/schema_authorization_test.rb +119 -119
  82. data/test/cases/adapters/postgresql/schema_test.rb +597 -597
  83. data/test/cases/adapters/postgresql/serial_test.rb +154 -154
  84. data/test/cases/adapters/postgresql/statement_pool_test.rb +41 -41
  85. data/test/cases/adapters/postgresql/timestamp_test.rb +90 -90
  86. data/test/cases/adapters/postgresql/type_lookup_test.rb +33 -33
  87. data/test/cases/adapters/postgresql/utils_test.rb +62 -62
  88. data/test/cases/adapters/postgresql/uuid_test.rb +294 -294
  89. data/test/cases/adapters/postgresql/xml_test.rb +54 -54
  90. data/test/cases/adapters/sqlite3/collation_test.rb +53 -53
  91. data/test/cases/adapters/sqlite3/copy_table_test.rb +98 -98
  92. data/test/cases/adapters/sqlite3/explain_test.rb +21 -21
  93. data/test/cases/adapters/sqlite3/quoting_test.rb +101 -101
  94. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +441 -441
  95. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -24
  96. data/test/cases/adapters/sqlite3/statement_pool_test.rb +20 -20
  97. data/test/cases/aggregations_test.rb +168 -168
  98. data/test/cases/ar_schema_test.rb +146 -146
  99. data/test/cases/associations/association_scope_test.rb +16 -16
  100. data/test/cases/associations/belongs_to_associations_test.rb +1141 -1141
  101. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +41 -41
  102. data/test/cases/associations/callbacks_test.rb +190 -190
  103. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -188
  104. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -36
  105. data/test/cases/associations/eager_load_nested_include_test.rb +126 -126
  106. data/test/cases/associations/eager_singularization_test.rb +148 -148
  107. data/test/cases/associations/eager_test.rb +1514 -1514
  108. data/test/cases/associations/extension_test.rb +87 -87
  109. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1004 -1004
  110. data/test/cases/associations/has_many_associations_test.rb +2501 -2501
  111. data/test/cases/associations/has_many_through_associations_test.rb +1271 -1271
  112. data/test/cases/associations/has_one_associations_test.rb +707 -707
  113. data/test/cases/associations/has_one_through_associations_test.rb +383 -383
  114. data/test/cases/associations/inner_join_association_test.rb +139 -139
  115. data/test/cases/associations/inverse_associations_test.rb +733 -733
  116. data/test/cases/associations/join_model_test.rb +777 -777
  117. data/test/cases/associations/left_outer_join_association_test.rb +88 -88
  118. data/test/cases/associations/nested_through_associations_test.rb +579 -579
  119. data/test/cases/associations/required_test.rb +102 -102
  120. data/test/cases/associations_test.rb +385 -385
  121. data/test/cases/attribute_decorators_test.rb +126 -125
  122. data/test/cases/attribute_methods/read_test.rb +60 -60
  123. data/test/cases/attribute_methods_test.rb +1009 -1009
  124. data/test/cases/attribute_set_test.rb +270 -270
  125. data/test/cases/attribute_test.rb +246 -246
  126. data/test/cases/attributes_test.rb +253 -253
  127. data/test/cases/autosave_association_test.rb +1708 -1708
  128. data/test/cases/base_test.rb +1713 -1713
  129. data/test/cases/batches_test.rb +489 -489
  130. data/test/cases/binary_test.rb +44 -44
  131. data/test/cases/bind_parameter_test.rb +110 -110
  132. data/test/cases/cache_key_test.rb +26 -25
  133. data/test/cases/calculations_test.rb +798 -798
  134. data/test/cases/callbacks_test.rb +636 -636
  135. data/test/cases/clone_test.rb +40 -40
  136. data/test/cases/coders/json_test.rb +15 -15
  137. data/test/cases/coders/yaml_column_test.rb +63 -63
  138. data/test/cases/collection_cache_key_test.rb +115 -115
  139. data/test/cases/column_alias_test.rb +17 -17
  140. data/test/cases/column_definition_test.rb +92 -92
  141. data/test/cases/comment_test.rb +145 -143
  142. data/test/cases/connection_adapters/adapter_leasing_test.rb +56 -56
  143. data/test/cases/connection_adapters/connection_handler_test.rb +160 -160
  144. data/test/cases/connection_adapters/connection_specification_test.rb +12 -12
  145. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +255 -255
  146. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +69 -69
  147. data/test/cases/connection_adapters/quoting_test.rb +13 -13
  148. data/test/cases/connection_adapters/schema_cache_test.rb +61 -61
  149. data/test/cases/connection_adapters/type_lookup_test.rb +118 -118
  150. data/test/cases/connection_management_test.rb +112 -112
  151. data/test/cases/connection_pool_test.rb +521 -521
  152. data/test/cases/connection_specification/resolver_test.rb +131 -131
  153. data/test/cases/core_test.rb +112 -112
  154. data/test/cases/counter_cache_test.rb +214 -214
  155. data/test/cases/custom_locking_test.rb +17 -17
  156. data/test/cases/database_statements_test.rb +34 -34
  157. data/test/cases/date_test.rb +44 -44
  158. data/test/cases/date_time_precision_test.rb +107 -106
  159. data/test/cases/date_time_test.rb +61 -61
  160. data/test/cases/defaults_test.rb +219 -218
  161. data/test/cases/dirty_test.rb +763 -763
  162. data/test/cases/disconnected_test.rb +30 -30
  163. data/test/cases/dup_test.rb +157 -157
  164. data/test/cases/enum_test.rb +444 -444
  165. data/test/cases/errors_test.rb +16 -16
  166. data/test/cases/explain_subscriber_test.rb +64 -64
  167. data/test/cases/explain_test.rb +87 -87
  168. data/test/cases/finder_respond_to_test.rb +60 -60
  169. data/test/cases/finder_test.rb +1294 -1294
  170. data/test/cases/fixture_set/file_test.rb +156 -156
  171. data/test/cases/fixtures_test.rb +988 -988
  172. data/test/cases/forbidden_attributes_protection_test.rb +165 -165
  173. data/test/cases/habtm_destroy_order_test.rb +61 -61
  174. data/test/cases/helper.rb +204 -204
  175. data/test/cases/hot_compatibility_test.rb +142 -142
  176. data/test/cases/i18n_test.rb +45 -45
  177. data/test/cases/inheritance_test.rb +606 -606
  178. data/test/cases/integration_test.rb +155 -155
  179. data/test/cases/invalid_connection_test.rb +24 -24
  180. data/test/cases/invertible_migration_test.rb +387 -387
  181. data/test/cases/json_serialization_test.rb +311 -311
  182. data/test/cases/locking_test.rb +493 -493
  183. data/test/cases/log_subscriber_test.rb +225 -225
  184. data/test/cases/migration/change_schema_test.rb +458 -458
  185. data/test/cases/migration/change_table_test.rb +256 -256
  186. data/test/cases/migration/column_attributes_test.rb +176 -176
  187. data/test/cases/migration/column_positioning_test.rb +56 -56
  188. data/test/cases/migration/columns_test.rb +310 -310
  189. data/test/cases/migration/command_recorder_test.rb +350 -350
  190. data/test/cases/migration/compatibility_test.rb +118 -118
  191. data/test/cases/migration/create_join_table_test.rb +157 -157
  192. data/test/cases/migration/foreign_key_test.rb +362 -360
  193. data/test/cases/migration/helper.rb +39 -39
  194. data/test/cases/migration/index_test.rb +218 -218
  195. data/test/cases/migration/logger_test.rb +36 -36
  196. data/test/cases/migration/pending_migrations_test.rb +52 -52
  197. data/test/cases/migration/references_foreign_key_test.rb +221 -216
  198. data/test/cases/migration/references_index_test.rb +101 -101
  199. data/test/cases/migration/references_statements_test.rb +136 -136
  200. data/test/cases/migration/rename_table_test.rb +93 -93
  201. data/test/cases/migration_test.rb +1157 -1157
  202. data/test/cases/migrator_test.rb +471 -470
  203. data/test/cases/mixin_test.rb +68 -68
  204. data/test/cases/modules_test.rb +172 -172
  205. data/test/cases/multiparameter_attributes_test.rb +372 -372
  206. data/test/cases/multiple_db_test.rb +122 -122
  207. data/test/cases/nested_attributes_test.rb +1098 -1098
  208. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -144
  209. data/test/cases/persistence_test.rb +1001 -1001
  210. data/test/cases/pooled_connections_test.rb +81 -81
  211. data/test/cases/primary_keys_test.rb +376 -376
  212. data/test/cases/query_cache_test.rb +446 -446
  213. data/test/cases/quoting_test.rb +202 -202
  214. data/test/cases/readonly_test.rb +119 -119
  215. data/test/cases/reaper_test.rb +85 -85
  216. data/test/cases/reflection_test.rb +509 -509
  217. data/test/cases/relation/delegation_test.rb +63 -63
  218. data/test/cases/relation/merging_test.rb +157 -157
  219. data/test/cases/relation/mutation_test.rb +183 -183
  220. data/test/cases/relation/or_test.rb +92 -92
  221. data/test/cases/relation/predicate_builder_test.rb +16 -16
  222. data/test/cases/relation/record_fetch_warning_test.rb +40 -40
  223. data/test/cases/relation/where_chain_test.rb +105 -105
  224. data/test/cases/relation/where_clause_test.rb +182 -182
  225. data/test/cases/relation/where_test.rb +322 -322
  226. data/test/cases/relation_test.rb +328 -328
  227. data/test/cases/relations_test.rb +2026 -2026
  228. data/test/cases/reload_models_test.rb +22 -22
  229. data/test/cases/result_test.rb +90 -90
  230. data/test/cases/sanitize_test.rb +176 -176
  231. data/test/cases/schema_dumper_test.rb +457 -457
  232. data/test/cases/schema_loading_test.rb +52 -52
  233. data/test/cases/scoping/default_scoping_test.rb +528 -528
  234. data/test/cases/scoping/named_scoping_test.rb +561 -561
  235. data/test/cases/scoping/relation_scoping_test.rb +400 -400
  236. data/test/cases/secure_token_test.rb +32 -32
  237. data/test/cases/serialization_test.rb +104 -104
  238. data/test/cases/serialized_attribute_test.rb +364 -364
  239. data/test/cases/statement_cache_test.rb +136 -136
  240. data/test/cases/store_test.rb +195 -195
  241. data/test/cases/suppressor_test.rb +63 -63
  242. data/test/cases/tasks/database_tasks_test.rb +462 -462
  243. data/test/cases/tasks/mysql_rake_test.rb +345 -345
  244. data/test/cases/tasks/postgresql_rake_test.rb +304 -304
  245. data/test/cases/tasks/sqlite_rake_test.rb +220 -220
  246. data/test/cases/test_case.rb +131 -131
  247. data/test/cases/test_fixtures_test.rb +36 -36
  248. data/test/cases/time_precision_test.rb +103 -102
  249. data/test/cases/timestamp_test.rb +501 -501
  250. data/test/cases/touch_later_test.rb +121 -121
  251. data/test/cases/transaction_callbacks_test.rb +518 -518
  252. data/test/cases/transaction_isolation_test.rb +106 -106
  253. data/test/cases/transactions_test.rb +835 -834
  254. data/test/cases/type/adapter_specific_registry_test.rb +133 -133
  255. data/test/cases/type/date_time_test.rb +14 -14
  256. data/test/cases/type/integer_test.rb +27 -27
  257. data/test/cases/type/string_test.rb +22 -22
  258. data/test/cases/type/type_map_test.rb +177 -177
  259. data/test/cases/type_test.rb +39 -39
  260. data/test/cases/types_test.rb +24 -24
  261. data/test/cases/unconnected_test.rb +33 -33
  262. data/test/cases/validations/absence_validation_test.rb +73 -73
  263. data/test/cases/validations/association_validation_test.rb +97 -97
  264. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -84
  265. data/test/cases/validations/i18n_validation_test.rb +86 -86
  266. data/test/cases/validations/length_validation_test.rb +79 -79
  267. data/test/cases/validations/presence_validation_test.rb +103 -103
  268. data/test/cases/validations/uniqueness_validation_test.rb +548 -548
  269. data/test/cases/validations_repair_helper.rb +19 -19
  270. data/test/cases/validations_test.rb +194 -194
  271. data/test/cases/view_test.rb +216 -216
  272. data/test/cases/yaml_serialization_test.rb +121 -121
  273. data/test/config.example.yml +97 -97
  274. data/test/config.rb +5 -5
  275. data/test/connections/native_ibm_db/connection.rb +44 -0
  276. data/test/fixtures/accounts.yml +29 -29
  277. data/test/fixtures/admin/accounts.yml +2 -2
  278. data/test/fixtures/admin/users.yml +10 -10
  279. data/test/fixtures/author_addresses.yml +17 -17
  280. data/test/fixtures/author_favorites.yml +3 -3
  281. data/test/fixtures/authors.yml +23 -23
  282. data/test/fixtures/bad_posts.yml +9 -9
  283. data/test/fixtures/binaries.yml +133 -133
  284. data/test/fixtures/books.yml +31 -31
  285. data/test/fixtures/bulbs.yml +5 -5
  286. data/test/fixtures/cars.yml +9 -9
  287. data/test/fixtures/categories.yml +19 -19
  288. data/test/fixtures/categories/special_categories.yml +9 -9
  289. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -4
  290. data/test/fixtures/categories_ordered.yml +7 -7
  291. data/test/fixtures/categories_posts.yml +31 -31
  292. data/test/fixtures/categorizations.yml +23 -23
  293. data/test/fixtures/clubs.yml +8 -8
  294. data/test/fixtures/collections.yml +3 -3
  295. data/test/fixtures/colleges.yml +3 -3
  296. data/test/fixtures/comments.yml +65 -65
  297. data/test/fixtures/companies.yml +67 -67
  298. data/test/fixtures/computers.yml +10 -10
  299. data/test/fixtures/content.yml +3 -3
  300. data/test/fixtures/content_positions.yml +3 -3
  301. data/test/fixtures/courses.yml +8 -8
  302. data/test/fixtures/customers.yml +25 -25
  303. data/test/fixtures/dashboards.yml +6 -6
  304. data/test/fixtures/dead_parrots.yml +5 -5
  305. data/test/fixtures/developers.yml +22 -22
  306. data/test/fixtures/developers_projects.yml +16 -16
  307. data/test/fixtures/dog_lovers.yml +7 -7
  308. data/test/fixtures/dogs.yml +4 -4
  309. data/test/fixtures/doubloons.yml +3 -3
  310. data/test/fixtures/edges.yml +5 -5
  311. data/test/fixtures/entrants.yml +14 -14
  312. data/test/fixtures/essays.yml +6 -6
  313. data/test/fixtures/faces.yml +11 -11
  314. data/test/fixtures/fk_test_has_fk.yml +3 -3
  315. data/test/fixtures/fk_test_has_pk.yml +1 -1
  316. data/test/fixtures/friendships.yml +4 -4
  317. data/test/fixtures/funny_jokes.yml +10 -10
  318. data/test/fixtures/interests.yml +33 -33
  319. data/test/fixtures/items.yml +3 -3
  320. data/test/fixtures/jobs.yml +7 -7
  321. data/test/fixtures/legacy_things.yml +3 -3
  322. data/test/fixtures/live_parrots.yml +4 -4
  323. data/test/fixtures/mateys.yml +4 -4
  324. data/test/fixtures/member_details.yml +8 -8
  325. data/test/fixtures/member_types.yml +6 -6
  326. data/test/fixtures/members.yml +11 -11
  327. data/test/fixtures/memberships.yml +34 -34
  328. data/test/fixtures/men.yml +5 -5
  329. data/test/fixtures/minimalistics.yml +2 -2
  330. data/test/fixtures/minivans.yml +5 -5
  331. data/test/fixtures/mixed_case_monkeys.yml +6 -6
  332. data/test/fixtures/mixins.yml +29 -29
  333. data/test/fixtures/movies.yml +7 -7
  334. data/test/fixtures/naked/yml/accounts.yml +1 -1
  335. data/test/fixtures/naked/yml/companies.yml +1 -1
  336. data/test/fixtures/naked/yml/courses.yml +1 -1
  337. data/test/fixtures/naked/yml/parrots.yml +2 -2
  338. data/test/fixtures/naked/yml/trees.yml +3 -3
  339. data/test/fixtures/nodes.yml +29 -29
  340. data/test/fixtures/organizations.yml +5 -5
  341. data/test/fixtures/other_comments.yml +6 -6
  342. data/test/fixtures/other_dogs.yml +2 -2
  343. data/test/fixtures/other_posts.yml +7 -7
  344. data/test/fixtures/other_topics.yml +42 -42
  345. data/test/fixtures/owners.yml +9 -9
  346. data/test/fixtures/parrots.yml +27 -27
  347. data/test/fixtures/parrots_pirates.yml +7 -7
  348. data/test/fixtures/people.yml +24 -24
  349. data/test/fixtures/peoples_treasures.yml +3 -3
  350. data/test/fixtures/pets.yml +19 -19
  351. data/test/fixtures/pirates.yml +12 -15
  352. data/test/fixtures/posts.yml +80 -80
  353. data/test/fixtures/price_estimates.yml +16 -16
  354. data/test/fixtures/products.yml +4 -4
  355. data/test/fixtures/projects.yml +7 -7
  356. data/test/fixtures/ratings.yml +14 -14
  357. data/test/fixtures/readers.yml +11 -11
  358. data/test/fixtures/references.yml +17 -17
  359. data/test/fixtures/reserved_words/distinct.yml +5 -5
  360. data/test/fixtures/reserved_words/distinct_select.yml +11 -11
  361. data/test/fixtures/reserved_words/group.yml +14 -14
  362. data/test/fixtures/reserved_words/select.yml +8 -8
  363. data/test/fixtures/reserved_words/values.yml +7 -7
  364. data/test/fixtures/ships.yml +6 -6
  365. data/test/fixtures/speedometers.yml +8 -8
  366. data/test/fixtures/sponsors.yml +12 -12
  367. data/test/fixtures/string_key_objects.yml +7 -7
  368. data/test/fixtures/subscribers.yml +10 -10
  369. data/test/fixtures/subscriptions.yml +12 -12
  370. data/test/fixtures/taggings.yml +78 -78
  371. data/test/fixtures/tags.yml +11 -11
  372. data/test/fixtures/tasks.yml +7 -7
  373. data/test/fixtures/teapots.yml +3 -3
  374. data/test/fixtures/to_be_linked/accounts.yml +2 -2
  375. data/test/fixtures/to_be_linked/users.yml +10 -10
  376. data/test/fixtures/topics.yml +49 -49
  377. data/test/fixtures/toys.yml +14 -14
  378. data/test/fixtures/traffic_lights.yml +9 -9
  379. data/test/fixtures/treasures.yml +10 -10
  380. data/test/fixtures/trees.yml +3 -3
  381. data/test/fixtures/uuid_children.yml +3 -3
  382. data/test/fixtures/uuid_parents.yml +2 -2
  383. data/test/fixtures/variants.yml +4 -4
  384. data/test/fixtures/vegetables.yml +19 -19
  385. data/test/fixtures/vertices.yml +3 -3
  386. data/test/fixtures/warehouse_things.yml +2 -2
  387. data/test/fixtures/zines.yml +5 -5
  388. data/test/migrations/10_urban/9_add_expressions.rb +11 -11
  389. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -15
  390. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -12
  391. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -9
  392. data/test/migrations/missing/1_people_have_last_names.rb +9 -9
  393. data/test/migrations/missing/3_we_need_reminders.rb +12 -12
  394. data/test/migrations/missing/4_innocent_jointable.rb +12 -12
  395. data/test/migrations/rename/1_we_need_things.rb +11 -11
  396. data/test/migrations/rename/2_rename_things.rb +9 -9
  397. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -9
  398. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -9
  399. data/test/migrations/to_copy2/1_create_articles.rb +7 -7
  400. data/test/migrations/to_copy2/2_create_comments.rb +7 -7
  401. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -9
  402. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -9
  403. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -9
  404. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -7
  405. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -7
  406. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -9
  407. data/test/migrations/valid/2_we_need_reminders.rb +12 -12
  408. data/test/migrations/valid/3_innocent_jointable.rb +12 -12
  409. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -9
  410. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -12
  411. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -12
  412. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -9
  413. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -12
  414. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -12
  415. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -8
  416. data/test/models/admin.rb +5 -5
  417. data/test/models/admin/account.rb +3 -3
  418. data/test/models/admin/user.rb +40 -40
  419. data/test/models/aircraft.rb +5 -5
  420. data/test/models/arunit2_model.rb +3 -3
  421. data/test/models/author.rb +209 -209
  422. data/test/models/auto_id.rb +4 -4
  423. data/test/models/autoloadable/extra_firm.rb +2 -2
  424. data/test/models/binary.rb +2 -2
  425. data/test/models/bird.rb +12 -12
  426. data/test/models/book.rb +23 -23
  427. data/test/models/boolean.rb +2 -2
  428. data/test/models/bulb.rb +52 -52
  429. data/test/models/cake_designer.rb +3 -3
  430. data/test/models/car.rb +29 -29
  431. data/test/models/carrier.rb +2 -2
  432. data/test/models/cat.rb +10 -10
  433. data/test/models/categorization.rb +19 -19
  434. data/test/models/category.rb +35 -35
  435. data/test/models/chef.rb +8 -8
  436. data/test/models/citation.rb +3 -3
  437. data/test/models/club.rb +25 -25
  438. data/test/models/college.rb +10 -10
  439. data/test/models/column.rb +3 -3
  440. data/test/models/column_name.rb +3 -3
  441. data/test/models/comment.rb +76 -76
  442. data/test/models/company.rb +230 -230
  443. data/test/models/company_in_module.rb +98 -98
  444. data/test/models/computer.rb +3 -3
  445. data/test/models/contact.rb +41 -41
  446. data/test/models/content.rb +40 -40
  447. data/test/models/contract.rb +20 -20
  448. data/test/models/country.rb +7 -7
  449. data/test/models/course.rb +6 -6
  450. data/test/models/customer.rb +83 -83
  451. data/test/models/customer_carrier.rb +14 -14
  452. data/test/models/dashboard.rb +3 -3
  453. data/test/models/default.rb +2 -2
  454. data/test/models/department.rb +4 -4
  455. data/test/models/developer.rb +274 -274
  456. data/test/models/dog.rb +5 -5
  457. data/test/models/dog_lover.rb +5 -5
  458. data/test/models/doubloon.rb +12 -12
  459. data/test/models/drink_designer.rb +3 -3
  460. data/test/models/edge.rb +5 -5
  461. data/test/models/electron.rb +5 -5
  462. data/test/models/engine.rb +4 -4
  463. data/test/models/entrant.rb +3 -3
  464. data/test/models/essay.rb +5 -5
  465. data/test/models/event.rb +3 -3
  466. data/test/models/eye.rb +37 -37
  467. data/test/models/face.rb +9 -9
  468. data/test/models/friendship.rb +6 -6
  469. data/test/models/guid.rb +2 -2
  470. data/test/models/guitar.rb +4 -4
  471. data/test/models/hotel.rb +11 -11
  472. data/test/models/image.rb +3 -3
  473. data/test/models/interest.rb +5 -5
  474. data/test/models/invoice.rb +4 -4
  475. data/test/models/item.rb +7 -7
  476. data/test/models/job.rb +7 -7
  477. data/test/models/joke.rb +7 -7
  478. data/test/models/keyboard.rb +3 -3
  479. data/test/models/legacy_thing.rb +3 -3
  480. data/test/models/lesson.rb +11 -11
  481. data/test/models/line_item.rb +3 -3
  482. data/test/models/liquid.rb +4 -4
  483. data/test/models/man.rb +11 -11
  484. data/test/models/matey.rb +4 -4
  485. data/test/models/member.rb +42 -42
  486. data/test/models/member_detail.rb +8 -8
  487. data/test/models/member_type.rb +3 -3
  488. data/test/models/membership.rb +35 -35
  489. data/test/models/mentor.rb +2 -2
  490. data/test/models/minimalistic.rb +2 -2
  491. data/test/models/minivan.rb +9 -9
  492. data/test/models/mixed_case_monkey.rb +3 -3
  493. data/test/models/mocktail_designer.rb +2 -2
  494. data/test/models/molecule.rb +6 -6
  495. data/test/models/movie.rb +5 -5
  496. data/test/models/node.rb +5 -5
  497. data/test/models/non_primary_key.rb +2 -2
  498. data/test/models/notification.rb +3 -3
  499. data/test/models/order.rb +4 -4
  500. data/test/models/organization.rb +14 -14
  501. data/test/models/other_dog.rb +5 -5
  502. data/test/models/owner.rb +37 -37
  503. data/test/models/parrot.rb +28 -28
  504. data/test/models/person.rb +142 -142
  505. data/test/models/personal_legacy_thing.rb +4 -4
  506. data/test/models/pet.rb +18 -18
  507. data/test/models/pet_treasure.rb +6 -6
  508. data/test/models/pirate.rb +92 -92
  509. data/test/models/possession.rb +3 -3
  510. data/test/models/post.rb +273 -273
  511. data/test/models/price_estimate.rb +4 -4
  512. data/test/models/professor.rb +5 -5
  513. data/test/models/project.rb +40 -40
  514. data/test/models/publisher.rb +2 -2
  515. data/test/models/publisher/article.rb +4 -4
  516. data/test/models/publisher/magazine.rb +3 -3
  517. data/test/models/rating.rb +4 -4
  518. data/test/models/reader.rb +23 -23
  519. data/test/models/recipe.rb +3 -3
  520. data/test/models/record.rb +2 -2
  521. data/test/models/reference.rb +22 -22
  522. data/test/models/reply.rb +61 -61
  523. data/test/models/ship.rb +39 -39
  524. data/test/models/ship_part.rb +8 -8
  525. data/test/models/shop.rb +17 -17
  526. data/test/models/shop_account.rb +6 -6
  527. data/test/models/speedometer.rb +6 -6
  528. data/test/models/sponsor.rb +7 -7
  529. data/test/models/string_key_object.rb +3 -3
  530. data/test/models/student.rb +4 -4
  531. data/test/models/subject.rb +16 -16
  532. data/test/models/subscriber.rb +8 -8
  533. data/test/models/subscription.rb +4 -4
  534. data/test/models/tag.rb +13 -13
  535. data/test/models/tagging.rb +13 -13
  536. data/test/models/task.rb +5 -5
  537. data/test/models/topic.rb +118 -118
  538. data/test/models/toy.rb +6 -6
  539. data/test/models/traffic_light.rb +4 -4
  540. data/test/models/treasure.rb +14 -14
  541. data/test/models/treaty.rb +7 -7
  542. data/test/models/tree.rb +3 -3
  543. data/test/models/tuning_peg.rb +4 -4
  544. data/test/models/tyre.rb +11 -11
  545. data/test/models/user.rb +14 -14
  546. data/test/models/uuid_child.rb +3 -3
  547. data/test/models/uuid_item.rb +6 -6
  548. data/test/models/uuid_parent.rb +3 -3
  549. data/test/models/vegetables.rb +24 -24
  550. data/test/models/vehicle.rb +6 -6
  551. data/test/models/vertex.rb +9 -9
  552. data/test/models/warehouse_thing.rb +5 -5
  553. data/test/models/wheel.rb +3 -3
  554. data/test/models/without_table.rb +3 -3
  555. data/test/models/zine.rb +3 -3
  556. data/test/schema/i5/ibm_db_specific_schema.rb +137 -0
  557. data/test/schema/ids/ibm_db_specific_schema.rb +140 -0
  558. data/test/schema/luw/ibm_db_specific_schema.rb +137 -0
  559. data/test/schema/mysql2_specific_schema.rb +68 -68
  560. data/test/schema/oracle_specific_schema.rb +40 -40
  561. data/test/schema/postgresql_specific_schema.rb +114 -114
  562. data/test/schema/schema.rb +1057 -1057
  563. data/test/schema/schema.rb.original +1057 -1057
  564. data/test/schema/sqlite_specific_schema.rb +18 -18
  565. data/test/schema/zOS/ibm_db_specific_schema.rb +208 -0
  566. data/test/support/config.rb +43 -43
  567. data/test/support/connection.rb +23 -23
  568. data/test/support/connection_helper.rb +14 -14
  569. data/test/support/ddl_helper.rb +8 -8
  570. data/test/support/schema_dumping_helper.rb +20 -20
  571. data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -22
  572. data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -182
  573. metadata +30 -14
  574. data/lib/mswin32/rb2x/i386/ibm_db.so +0 -0
  575. data/test/fixtures/author_addresses.original +0 -11
  576. data/test/fixtures/authors.original +0 -17
@@ -1,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