ibm_db 5.2.0 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (622) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +6 -0
  3. data/LICENSE +55 -18
  4. data/ext/Makefile +12 -12
  5. data/ext/ibm_db.c +62 -57
  6. data/ext/ibm_db.o +0 -0
  7. data/ext/ibm_db.so +0 -0
  8. data/ext/mkmf.log +11 -11
  9. data/ext/ruby_ibm_db_cli.c +1 -0
  10. data/ext/ruby_ibm_db_cli.o +0 -0
  11. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +1458 -1279
  12. data/test/active_record/connection_adapters/fake_adapter.rb +5 -2
  13. data/test/activejob/destroy_association_async_test.rb +305 -0
  14. data/test/activejob/destroy_async_job_not_present_test.rb +31 -0
  15. data/test/activejob/helper.rb +15 -0
  16. data/test/assets/schema_dump_5_1.yml +345 -0
  17. data/test/cases/adapter_prevent_writes_test.rb +334 -0
  18. data/test/cases/adapter_test.rb +432 -218
  19. data/test/cases/adapters/mysql2/active_schema_test.rb +85 -75
  20. data/test/cases/adapters/mysql2/auto_increment_test.rb +34 -0
  21. data/test/cases/adapters/mysql2/bind_parameter_test.rb +5 -3
  22. data/test/cases/adapters/mysql2/boolean_test.rb +6 -4
  23. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +26 -24
  24. data/test/cases/adapters/mysql2/charset_collation_test.rb +20 -17
  25. data/test/cases/adapters/mysql2/connection_test.rb +48 -50
  26. data/test/cases/adapters/mysql2/count_deleted_rows_with_lock_test.rb +28 -0
  27. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +23 -19
  28. data/test/cases/adapters/mysql2/enum_test.rb +32 -11
  29. data/test/cases/adapters/mysql2/explain_test.rb +13 -11
  30. data/test/cases/adapters/mysql2/json_test.rb +17 -188
  31. data/test/cases/adapters/mysql2/mysql2_adapter_prevent_writes_test.rb +208 -0
  32. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +183 -28
  33. data/test/cases/adapters/mysql2/nested_deadlock_test.rb +75 -0
  34. data/test/cases/adapters/mysql2/optimizer_hints_test.rb +69 -0
  35. data/test/cases/adapters/mysql2/schema_migrations_test.rb +26 -21
  36. data/test/cases/adapters/mysql2/schema_test.rb +24 -22
  37. data/test/cases/adapters/mysql2/set_test.rb +32 -0
  38. data/test/cases/adapters/mysql2/sp_test.rb +10 -8
  39. data/test/cases/adapters/mysql2/sql_types_test.rb +8 -6
  40. data/test/cases/adapters/mysql2/table_options_test.rb +93 -10
  41. data/test/cases/adapters/mysql2/transaction_test.rb +151 -0
  42. data/test/cases/adapters/mysql2/unsigned_type_test.rb +11 -9
  43. data/test/cases/adapters/mysql2/virtual_column_test.rb +66 -0
  44. data/test/cases/adapters/postgresql/active_schema_test.rb +40 -25
  45. data/test/cases/adapters/postgresql/array_test.rb +118 -63
  46. data/test/cases/adapters/postgresql/bit_string_test.rb +12 -10
  47. data/test/cases/adapters/postgresql/bytea_test.rb +26 -25
  48. data/test/cases/adapters/postgresql/case_insensitive_test.rb +10 -9
  49. data/test/cases/adapters/postgresql/change_schema_test.rb +7 -5
  50. data/test/cases/adapters/postgresql/cidr_test.rb +2 -0
  51. data/test/cases/adapters/postgresql/citext_test.rb +58 -58
  52. data/test/cases/adapters/postgresql/collation_test.rb +17 -15
  53. data/test/cases/adapters/postgresql/composite_test.rb +25 -23
  54. data/test/cases/adapters/postgresql/connection_test.rb +73 -85
  55. data/test/cases/adapters/postgresql/create_unlogged_tables_test.rb +74 -0
  56. data/test/cases/adapters/postgresql/datatype_test.rb +19 -22
  57. data/test/cases/adapters/postgresql/date_test.rb +42 -0
  58. data/test/cases/adapters/postgresql/domain_test.rb +9 -7
  59. data/test/cases/adapters/postgresql/enum_test.rb +12 -10
  60. data/test/cases/adapters/postgresql/explain_test.rb +10 -8
  61. data/test/cases/adapters/postgresql/extension_migration_test.rb +13 -12
  62. data/test/cases/adapters/postgresql/foreign_table_test.rb +109 -0
  63. data/test/cases/adapters/postgresql/full_text_test.rb +8 -6
  64. data/test/cases/adapters/postgresql/geometric_test.rb +57 -63
  65. data/test/cases/adapters/postgresql/hstore_test.rb +288 -280
  66. data/test/cases/adapters/postgresql/infinity_test.rb +54 -15
  67. data/test/cases/adapters/postgresql/integer_test.rb +2 -0
  68. data/test/cases/adapters/postgresql/interval_test.rb +99 -0
  69. data/test/cases/adapters/postgresql/json_test.rb +16 -201
  70. data/test/cases/adapters/postgresql/ltree_test.rb +14 -16
  71. data/test/cases/adapters/postgresql/money_test.rb +47 -16
  72. data/test/cases/adapters/postgresql/network_test.rb +36 -28
  73. data/test/cases/adapters/postgresql/numbers_test.rb +7 -5
  74. data/test/cases/adapters/postgresql/optimizer_hints_test.rb +71 -0
  75. data/test/cases/adapters/postgresql/partitions_test.rb +22 -0
  76. data/test/cases/adapters/postgresql/postgresql_adapter_prevent_writes_test.rb +205 -0
  77. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +178 -136
  78. data/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb +27 -0
  79. data/test/cases/adapters/postgresql/quoting_test.rb +12 -6
  80. data/test/cases/adapters/postgresql/range_test.rb +406 -292
  81. data/test/cases/adapters/postgresql/referential_integrity_test.rb +16 -15
  82. data/test/cases/adapters/postgresql/rename_table_test.rb +9 -8
  83. data/test/cases/adapters/postgresql/schema_authorization_test.rb +14 -23
  84. data/test/cases/adapters/postgresql/schema_test.rb +207 -91
  85. data/test/cases/adapters/postgresql/serial_test.rb +9 -7
  86. data/test/cases/adapters/postgresql/statement_pool_test.rb +26 -6
  87. data/test/cases/adapters/postgresql/timestamp_test.rb +17 -15
  88. data/test/cases/adapters/postgresql/transaction_nested_test.rb +114 -0
  89. data/test/cases/adapters/postgresql/transaction_test.rb +189 -0
  90. data/test/cases/adapters/postgresql/type_lookup_test.rb +12 -10
  91. data/test/cases/adapters/postgresql/utils_test.rb +11 -9
  92. data/test/cases/adapters/postgresql/uuid_test.rb +226 -109
  93. data/test/cases/adapters/postgresql/xml_test.rb +10 -14
  94. data/test/cases/adapters/sqlite3/collation_test.rb +26 -15
  95. data/test/cases/adapters/sqlite3/copy_table_test.rb +31 -28
  96. data/test/cases/adapters/sqlite3/explain_test.rb +13 -11
  97. data/test/cases/adapters/sqlite3/json_test.rb +29 -0
  98. data/test/cases/adapters/sqlite3/quoting_test.rb +35 -57
  99. data/test/cases/adapters/sqlite3/sqlite3_adapter_prevent_writes_test.rb +186 -0
  100. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +318 -131
  101. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +11 -11
  102. data/test/cases/adapters/sqlite3/statement_pool_test.rb +7 -6
  103. data/test/cases/adapters/sqlite3/transaction_test.rb +123 -0
  104. data/test/cases/aggregations_test.rb +14 -12
  105. data/test/cases/annotate_test.rb +46 -0
  106. data/test/cases/ar_schema_test.rb +153 -86
  107. data/test/cases/arel/attributes/attribute_test.rb +1145 -0
  108. data/test/cases/arel/attributes/math_test.rb +83 -0
  109. data/test/cases/arel/attributes_test.rb +27 -0
  110. data/test/cases/arel/collectors/bind_test.rb +40 -0
  111. data/test/cases/arel/collectors/composite_test.rb +47 -0
  112. data/test/cases/arel/collectors/sql_string_test.rb +41 -0
  113. data/test/cases/arel/collectors/substitute_bind_collector_test.rb +48 -0
  114. data/test/cases/arel/crud_test.rb +65 -0
  115. data/test/cases/arel/delete_manager_test.rb +53 -0
  116. data/test/cases/arel/factory_methods_test.rb +46 -0
  117. data/test/cases/arel/helper.rb +45 -0
  118. data/test/cases/arel/insert_manager_test.rb +241 -0
  119. data/test/cases/arel/nodes/and_test.rb +30 -0
  120. data/test/cases/arel/nodes/as_test.rb +36 -0
  121. data/test/cases/arel/nodes/ascending_test.rb +46 -0
  122. data/test/cases/arel/nodes/bin_test.rb +35 -0
  123. data/test/cases/arel/nodes/binary_test.rb +29 -0
  124. data/test/cases/arel/nodes/bind_param_test.rb +22 -0
  125. data/test/cases/arel/nodes/case_test.rb +96 -0
  126. data/test/cases/arel/nodes/casted_test.rb +18 -0
  127. data/test/cases/arel/nodes/comment_test.rb +22 -0
  128. data/test/cases/arel/nodes/count_test.rb +35 -0
  129. data/test/cases/arel/nodes/delete_statement_test.rb +36 -0
  130. data/test/cases/arel/nodes/descending_test.rb +46 -0
  131. data/test/cases/arel/nodes/distinct_test.rb +21 -0
  132. data/test/cases/arel/nodes/equality_test.rb +62 -0
  133. data/test/cases/arel/nodes/extract_test.rb +43 -0
  134. data/test/cases/arel/nodes/false_test.rb +21 -0
  135. data/test/cases/arel/nodes/grouping_test.rb +26 -0
  136. data/test/cases/arel/nodes/infix_operation_test.rb +42 -0
  137. data/test/cases/arel/nodes/insert_statement_test.rb +44 -0
  138. data/test/cases/arel/nodes/named_function_test.rb +48 -0
  139. data/test/cases/arel/nodes/node_test.rb +22 -0
  140. data/test/cases/arel/nodes/not_test.rb +31 -0
  141. data/test/cases/arel/nodes/or_test.rb +36 -0
  142. data/test/cases/arel/nodes/over_test.rb +69 -0
  143. data/test/cases/arel/nodes/select_core_test.rb +79 -0
  144. data/test/cases/arel/nodes/select_statement_test.rb +51 -0
  145. data/test/cases/arel/nodes/sql_literal_test.rb +75 -0
  146. data/test/cases/arel/nodes/sum_test.rb +35 -0
  147. data/test/cases/arel/nodes/table_alias_test.rb +29 -0
  148. data/test/cases/arel/nodes/true_test.rb +21 -0
  149. data/test/cases/arel/nodes/unary_operation_test.rb +41 -0
  150. data/test/cases/arel/nodes/update_statement_test.rb +60 -0
  151. data/test/cases/arel/nodes/window_test.rb +81 -0
  152. data/test/cases/arel/nodes_test.rb +34 -0
  153. data/test/cases/arel/select_manager_test.rb +1238 -0
  154. data/test/cases/arel/support/fake_record.rb +135 -0
  155. data/test/cases/arel/table_test.rb +216 -0
  156. data/test/cases/arel/update_manager_test.rb +126 -0
  157. data/test/cases/arel/visitors/dispatch_contamination_test.rb +78 -0
  158. data/test/cases/arel/visitors/dot_test.rb +90 -0
  159. data/test/cases/arel/visitors/mysql_test.rb +157 -0
  160. data/test/cases/arel/visitors/postgres_test.rb +366 -0
  161. data/test/cases/arel/visitors/sqlite_test.rb +75 -0
  162. data/test/cases/arel/visitors/to_sql_test.rb +750 -0
  163. data/test/cases/associations/belongs_to_associations_test.rb +510 -158
  164. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +4 -2
  165. data/test/cases/associations/callbacks_test.rb +56 -38
  166. data/test/cases/associations/cascaded_eager_loading_test.rb +118 -61
  167. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +138 -18
  168. data/test/cases/associations/eager_load_nested_include_test.rb +38 -37
  169. data/test/cases/associations/eager_singularization_test.rb +21 -21
  170. data/test/cases/associations/eager_test.rb +559 -415
  171. data/test/cases/associations/extension_test.rb +18 -12
  172. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +234 -213
  173. data/test/cases/associations/has_many_associations_test.rb +1038 -465
  174. data/test/cases/associations/has_many_through_associations_test.rb +558 -249
  175. data/test/cases/associations/has_one_associations_test.rb +294 -129
  176. data/test/cases/associations/has_one_through_associations_test.rb +121 -75
  177. data/test/cases/associations/inner_join_association_test.rb +114 -38
  178. data/test/cases/associations/inverse_associations_test.rb +606 -398
  179. data/test/cases/associations/join_model_test.rb +158 -148
  180. data/test/cases/associations/left_outer_join_association_test.rb +59 -24
  181. data/test/cases/associations/nested_through_associations_test.rb +166 -109
  182. data/test/cases/associations/required_test.rb +35 -10
  183. data/test/cases/associations_test.rb +241 -110
  184. data/test/cases/attribute_methods/read_test.rb +11 -11
  185. data/test/cases/attribute_methods_test.rb +413 -298
  186. data/test/cases/attributes_test.rb +145 -27
  187. data/test/cases/autosave_association_test.rb +681 -436
  188. data/test/cases/base_prevent_writes_test.rb +229 -0
  189. data/test/cases/base_test.rb +599 -542
  190. data/test/cases/batches_test.rb +288 -82
  191. data/test/cases/binary_test.rb +26 -31
  192. data/test/cases/bind_parameter_test.rb +194 -21
  193. data/test/cases/boolean_test.rb +52 -0
  194. data/test/cases/cache_key_test.rb +110 -5
  195. data/test/cases/calculations_test.rb +737 -174
  196. data/test/cases/callbacks_test.rb +74 -207
  197. data/test/cases/clone_test.rb +15 -10
  198. data/test/cases/coders/json_test.rb +2 -0
  199. data/test/cases/coders/yaml_column_test.rb +16 -13
  200. data/test/cases/collection_cache_key_test.rb +177 -20
  201. data/test/cases/column_alias_test.rb +9 -7
  202. data/test/cases/column_definition_test.rb +10 -68
  203. data/test/cases/comment_test.rb +166 -107
  204. data/test/cases/connection_adapters/adapter_leasing_test.rb +14 -10
  205. data/test/cases/connection_adapters/connection_handler_test.rb +358 -51
  206. data/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +400 -0
  207. data/test/cases/connection_adapters/connection_handlers_multi_pool_config_test.rb +103 -0
  208. data/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb +499 -0
  209. data/test/cases/connection_adapters/connection_swapping_nested_test.rb +457 -0
  210. data/test/cases/connection_adapters/legacy_connection_handlers_multi_db_test.rb +486 -0
  211. data/test/cases/connection_adapters/legacy_connection_handlers_sharding_db_test.rb +586 -0
  212. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +319 -138
  213. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +62 -50
  214. data/test/cases/connection_adapters/schema_cache_test.rb +259 -26
  215. data/test/cases/connection_adapters/type_lookup_test.rb +96 -95
  216. data/test/cases/connection_management_test.rb +13 -11
  217. data/test/cases/connection_pool_test.rb +316 -83
  218. data/test/cases/core_test.rb +82 -58
  219. data/test/cases/counter_cache_test.rb +204 -50
  220. data/test/cases/custom_locking_test.rb +5 -3
  221. data/test/cases/database_configurations/hash_config_test.rb +74 -0
  222. data/test/cases/database_configurations/resolver_test.rb +150 -0
  223. data/test/cases/database_configurations_test.rb +145 -0
  224. data/test/cases/database_selector_test.rb +296 -0
  225. data/test/cases/database_statements_test.rb +18 -16
  226. data/test/cases/date_test.rb +8 -16
  227. data/test/cases/date_time_precision_test.rb +100 -78
  228. data/test/cases/date_time_test.rb +23 -8
  229. data/test/cases/defaults_test.rb +106 -71
  230. data/test/cases/delegated_type_test.rb +57 -0
  231. data/test/cases/dirty_test.rb +419 -223
  232. data/test/cases/disconnected_test.rb +6 -6
  233. data/test/cases/dup_test.rb +54 -27
  234. data/test/cases/enum_test.rb +461 -82
  235. data/test/cases/errors_test.rb +7 -7
  236. data/test/cases/explain_subscriber_test.rb +17 -15
  237. data/test/cases/explain_test.rb +11 -19
  238. data/test/cases/filter_attributes_test.rb +153 -0
  239. data/test/cases/finder_respond_to_test.rb +14 -14
  240. data/test/cases/finder_test.rb +669 -287
  241. data/test/cases/fixture_set/file_test.rb +34 -38
  242. data/test/cases/fixtures_test.rb +833 -176
  243. data/test/cases/forbidden_attributes_protection_test.rb +32 -67
  244. data/test/cases/habtm_destroy_order_test.rb +25 -25
  245. data/test/cases/helper.rb +78 -49
  246. data/test/cases/hot_compatibility_test.rb +33 -32
  247. data/test/cases/i18n_test.rb +18 -17
  248. data/test/cases/inheritance_test.rb +180 -115
  249. data/test/cases/insert_all_test.rb +489 -0
  250. data/test/cases/instrumentation_test.rb +101 -0
  251. data/test/cases/integration_test.rb +119 -31
  252. data/test/cases/invalid_connection_test.rb +18 -16
  253. data/test/cases/invertible_migration_test.rb +183 -43
  254. data/test/cases/json_attribute_test.rb +35 -0
  255. data/test/cases/json_serialization_test.rb +57 -58
  256. data/test/cases/json_shared_test_cases.rb +290 -0
  257. data/test/cases/locking_test.rb +413 -119
  258. data/test/cases/log_subscriber_test.rb +68 -26
  259. data/test/cases/marshal_serialization_test.rb +39 -0
  260. data/test/cases/migration/change_schema_test.rb +118 -72
  261. data/test/cases/migration/change_table_test.rb +138 -30
  262. data/test/cases/migration/check_constraint_test.rb +162 -0
  263. data/test/cases/migration/column_attributes_test.rb +45 -35
  264. data/test/cases/migration/column_positioning_test.rb +18 -6
  265. data/test/cases/migration/columns_test.rb +93 -77
  266. data/test/cases/migration/command_recorder_test.rb +121 -34
  267. data/test/cases/migration/compatibility_test.rb +578 -23
  268. data/test/cases/migration/create_join_table_test.rb +35 -25
  269. data/test/cases/migration/foreign_key_test.rb +503 -284
  270. data/test/cases/migration/helper.rb +4 -3
  271. data/test/cases/migration/index_test.rb +119 -70
  272. data/test/cases/migration/logger_test.rb +9 -6
  273. data/test/cases/migration/pending_migrations_test.rb +88 -34
  274. data/test/cases/migration/references_foreign_key_test.rb +164 -150
  275. data/test/cases/migration/references_index_test.rb +38 -19
  276. data/test/cases/migration/references_statements_test.rb +15 -14
  277. data/test/cases/migration/rename_table_test.rb +53 -30
  278. data/test/cases/migration_test.rb +637 -269
  279. data/test/cases/migrator_test.rb +191 -135
  280. data/test/cases/mixin_test.rb +7 -11
  281. data/test/cases/modules_test.rb +36 -34
  282. data/test/cases/multi_db_migrator_test.rb +223 -0
  283. data/test/cases/multiparameter_attributes_test.rb +60 -33
  284. data/test/cases/multiple_db_test.rb +16 -22
  285. data/test/cases/nested_attributes_test.rb +341 -320
  286. data/test/cases/nested_attributes_with_callbacks_test.rb +26 -24
  287. data/test/cases/null_relation_test.rb +84 -0
  288. data/test/cases/numeric_data_test.rb +93 -0
  289. data/test/cases/persistence_test.rb +361 -269
  290. data/test/cases/pooled_connections_test.rb +18 -26
  291. data/test/cases/prepared_statement_status_test.rb +48 -0
  292. data/test/cases/primary_keys_test.rb +210 -104
  293. data/test/cases/query_cache_test.rb +610 -141
  294. data/test/cases/quoting_test.rb +132 -31
  295. data/test/cases/readonly_test.rb +49 -48
  296. data/test/cases/reaper_test.rb +146 -32
  297. data/test/cases/reflection_test.rb +167 -156
  298. data/test/cases/relation/delegation_test.rb +49 -36
  299. data/test/cases/relation/delete_all_test.rb +117 -0
  300. data/test/cases/relation/merging_test.rb +319 -42
  301. data/test/cases/relation/mutation_test.rb +55 -93
  302. data/test/cases/relation/or_test.rb +129 -29
  303. data/test/cases/relation/predicate_builder_test.rb +21 -6
  304. data/test/cases/relation/record_fetch_warning_test.rb +5 -3
  305. data/test/cases/relation/select_test.rb +67 -0
  306. data/test/cases/relation/update_all_test.rb +317 -0
  307. data/test/cases/relation/where_chain_test.rb +68 -32
  308. data/test/cases/relation/where_clause_test.rb +136 -61
  309. data/test/cases/relation/where_test.rb +155 -48
  310. data/test/cases/relation_test.rb +266 -112
  311. data/test/cases/relations_test.rb +969 -744
  312. data/test/cases/reload_models_test.rb +13 -9
  313. data/test/cases/reserved_word_test.rb +141 -0
  314. data/test/cases/result_test.rb +68 -17
  315. data/test/cases/sanitize_test.rb +87 -71
  316. data/test/cases/schema_dumper_test.rb +221 -128
  317. data/test/cases/schema_loading_test.rb +3 -2
  318. data/test/cases/scoping/default_scoping_test.rb +185 -144
  319. data/test/cases/scoping/named_scoping_test.rb +177 -89
  320. data/test/cases/scoping/relation_scoping_test.rb +197 -75
  321. data/test/cases/secure_token_test.rb +18 -3
  322. data/test/cases/serialization_test.rb +30 -28
  323. data/test/cases/serialized_attribute_test.rb +133 -42
  324. data/test/cases/signed_id_test.rb +168 -0
  325. data/test/cases/statement_cache_test.rb +41 -24
  326. data/test/cases/statement_invalid_test.rb +42 -0
  327. data/test/cases/store_test.rb +180 -55
  328. data/test/cases/strict_loading_test.rb +473 -0
  329. data/test/cases/suppressor_test.rb +26 -12
  330. data/test/cases/tasks/database_tasks_test.rb +1258 -194
  331. data/test/cases/tasks/mysql_rake_test.rb +370 -298
  332. data/test/cases/tasks/postgresql_rake_test.rb +481 -251
  333. data/test/cases/tasks/sqlite_rake_test.rb +225 -178
  334. data/test/cases/test_case.rb +51 -40
  335. data/test/cases/test_databases_test.rb +79 -0
  336. data/test/cases/test_fixtures_test.rb +79 -19
  337. data/test/cases/time_precision_test.rb +98 -76
  338. data/test/cases/timestamp_test.rb +102 -99
  339. data/test/cases/touch_later_test.rb +12 -10
  340. data/test/cases/transaction_callbacks_test.rb +344 -90
  341. data/test/cases/transaction_isolation_test.rb +12 -12
  342. data/test/cases/transactions_test.rb +612 -162
  343. data/test/cases/type/adapter_specific_registry_test.rb +14 -2
  344. data/test/cases/type/date_time_test.rb +4 -2
  345. data/test/cases/type/integer_test.rb +4 -2
  346. data/test/cases/type/string_test.rb +10 -8
  347. data/test/cases/type/time_test.rb +28 -0
  348. data/test/cases/type/type_map_test.rb +29 -28
  349. data/test/cases/type/unsigned_integer_test.rb +19 -0
  350. data/test/cases/type_test.rb +2 -0
  351. data/test/cases/types_test.rb +3 -1
  352. data/test/cases/unconnected_test.rb +14 -1
  353. data/test/cases/unsafe_raw_sql_test.rb +274 -0
  354. data/test/cases/validations/absence_validation_test.rb +19 -17
  355. data/test/cases/validations/association_validation_test.rb +30 -28
  356. data/test/cases/validations/i18n_generate_message_validation_test.rb +34 -16
  357. data/test/cases/validations/i18n_validation_test.rb +22 -21
  358. data/test/cases/validations/length_validation_test.rb +34 -33
  359. data/test/cases/validations/numericality_validation_test.rb +181 -0
  360. data/test/cases/validations/presence_validation_test.rb +21 -19
  361. data/test/cases/validations/uniqueness_validation_test.rb +156 -86
  362. data/test/cases/validations_repair_helper.rb +2 -0
  363. data/test/cases/validations_test.rb +61 -26
  364. data/test/cases/view_test.rb +122 -116
  365. data/test/cases/yaml_serialization_test.rb +79 -34
  366. data/test/config.example.yml +19 -19
  367. data/test/config.rb +3 -1
  368. data/test/config.yml +16 -6
  369. data/test/fixtures/admin/randomly_named_a9.yml +7 -7
  370. data/test/fixtures/admin/randomly_named_b0.yml +7 -7
  371. data/test/fixtures/all/namespaced/accounts.yml +2 -0
  372. data/test/fixtures/author_addresses.yml +1 -8
  373. data/test/fixtures/authors.yml +1 -7
  374. data/test/fixtures/binaries.yml +4 -0
  375. data/test/fixtures/books.yml +9 -2
  376. data/test/fixtures/categories_posts.yml +3 -0
  377. data/test/fixtures/citations.yml +5 -0
  378. data/test/fixtures/comments.yml +7 -0
  379. data/test/fixtures/companies.yml +5 -0
  380. data/test/fixtures/computers.yml +2 -0
  381. data/test/fixtures/customers.yml +10 -1
  382. data/test/fixtures/developers.yml +1 -1
  383. data/test/fixtures/essays.yml +10 -0
  384. data/test/fixtures/faces.yml +3 -3
  385. data/test/fixtures/humans.yml +5 -0
  386. data/test/fixtures/interests.yml +7 -7
  387. data/test/fixtures/memberships.yml +7 -0
  388. data/test/fixtures/minimalistics.yml +3 -0
  389. data/test/fixtures/mixed_case_monkeys.yml +2 -2
  390. data/test/fixtures/naked/yml/courses_with_invalid_key.yml +3 -0
  391. data/test/fixtures/naked/yml/parrots.yml +1 -0
  392. data/test/fixtures/other_books.yml +26 -0
  393. data/test/fixtures/other_posts.yml +1 -0
  394. data/test/fixtures/parrots.yml +7 -1
  395. data/test/fixtures/pirates.yml +3 -0
  396. data/test/fixtures/posts.yml +11 -3
  397. data/test/fixtures/randomly_named_a9.yml +7 -7
  398. data/test/fixtures/readers.yml +6 -0
  399. data/test/fixtures/reserved_words/values.yml +2 -2
  400. data/test/fixtures/sponsors.yml +3 -0
  401. data/test/fixtures/strict_zines.yml +2 -0
  402. data/test/fixtures/subscribers.yml +1 -1
  403. data/test/fixtures/tasks.yml +1 -1
  404. data/test/fixtures/warehouse-things.yml +3 -0
  405. data/test/migrations/10_urban/9_add_expressions.rb +2 -0
  406. data/test/migrations/decimal/1_give_me_big_numbers.rb +6 -4
  407. data/test/migrations/magic/1_currencies_have_symbols.rb +3 -2
  408. data/test/migrations/missing/1000_people_have_middle_names.rb +2 -0
  409. data/test/migrations/missing/1_people_have_last_names.rb +2 -0
  410. data/test/migrations/missing/3_we_need_reminders.rb +2 -0
  411. data/test/migrations/missing/4_innocent_jointable.rb +3 -1
  412. data/test/migrations/rename/1_we_need_things.rb +2 -0
  413. data/test/migrations/rename/2_rename_things.rb +2 -0
  414. data/test/migrations/to_copy/1_people_have_hobbies.rb +3 -1
  415. data/test/migrations/to_copy/2_people_have_descriptions.rb +3 -1
  416. data/test/migrations/to_copy2/1_create_articles.rb +2 -0
  417. data/test/migrations/to_copy2/2_create_comments.rb +3 -1
  418. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +3 -1
  419. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +3 -1
  420. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +3 -1
  421. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +2 -0
  422. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +2 -0
  423. data/test/migrations/valid/1_valid_people_have_last_names.rb +2 -0
  424. data/test/migrations/valid/2_we_need_reminders.rb +2 -0
  425. data/test/migrations/valid/3_innocent_jointable.rb +3 -1
  426. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +2 -0
  427. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +2 -0
  428. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +3 -1
  429. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +2 -0
  430. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +2 -0
  431. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +3 -1
  432. data/test/migrations/version_check/20131219224947_migration_version_check.rb +2 -0
  433. data/test/models/account.rb +46 -0
  434. data/test/models/admin/account.rb +3 -1
  435. data/test/models/admin/randomly_named_c1.rb +2 -0
  436. data/test/models/admin/user.rb +16 -8
  437. data/test/models/admin.rb +4 -2
  438. data/test/models/aircraft.rb +3 -1
  439. data/test/models/arunit2_model.rb +2 -0
  440. data/test/models/author.rb +153 -102
  441. data/test/models/auto_id.rb +2 -0
  442. data/test/models/autoloadable/extra_firm.rb +2 -0
  443. data/test/models/binary.rb +3 -1
  444. data/test/models/binary_field.rb +6 -0
  445. data/test/models/bird.rb +13 -1
  446. data/test/models/book.rb +14 -4
  447. data/test/models/book_destroy_async.rb +24 -0
  448. data/test/models/boolean.rb +5 -0
  449. data/test/models/bulb.rb +13 -4
  450. data/test/models/cake_designer.rb +2 -0
  451. data/test/models/car.rb +17 -10
  452. data/test/models/carrier.rb +2 -0
  453. data/test/models/cart.rb +5 -0
  454. data/test/models/cat.rb +2 -0
  455. data/test/models/categorization.rb +8 -6
  456. data/test/models/category.rb +28 -16
  457. data/test/models/chef.rb +2 -0
  458. data/test/models/citation.rb +5 -1
  459. data/test/models/club.rb +13 -10
  460. data/test/models/college.rb +4 -2
  461. data/test/models/column.rb +2 -0
  462. data/test/models/column_name.rb +2 -0
  463. data/test/models/comment.rb +32 -10
  464. data/test/models/company.rb +102 -106
  465. data/test/models/company_in_module.rb +27 -26
  466. data/test/models/computer.rb +3 -1
  467. data/test/models/contact.rb +15 -13
  468. data/test/models/content.rb +5 -3
  469. data/test/models/contract.rb +21 -3
  470. data/test/models/country.rb +2 -4
  471. data/test/models/course.rb +3 -1
  472. data/test/models/customer.rb +10 -8
  473. data/test/models/customer_carrier.rb +2 -0
  474. data/test/models/dashboard.rb +2 -0
  475. data/test/models/default.rb +2 -0
  476. data/test/models/department.rb +2 -0
  477. data/test/models/destroy_async_parent.rb +15 -0
  478. data/test/models/destroy_async_parent_soft_delete.rb +20 -0
  479. data/test/models/developer.rb +152 -85
  480. data/test/models/dl_keyed_belongs_to.rb +13 -0
  481. data/test/models/dl_keyed_belongs_to_soft_delete.rb +19 -0
  482. data/test/models/dl_keyed_has_many.rb +5 -0
  483. data/test/models/dl_keyed_has_many_through.rb +5 -0
  484. data/test/models/dl_keyed_has_one.rb +5 -0
  485. data/test/models/dl_keyed_join.rb +10 -0
  486. data/test/models/dog.rb +2 -0
  487. data/test/models/dog_lover.rb +2 -0
  488. data/test/models/doubloon.rb +3 -1
  489. data/test/models/drink_designer.rb +17 -0
  490. data/test/models/edge.rb +4 -2
  491. data/test/models/electron.rb +2 -0
  492. data/test/models/engine.rb +3 -2
  493. data/test/models/entrant.rb +2 -0
  494. data/test/models/entry.rb +5 -0
  495. data/test/models/essay.rb +6 -3
  496. data/test/models/essay_destroy_async.rb +12 -0
  497. data/test/models/event.rb +3 -1
  498. data/test/models/eye.rb +5 -3
  499. data/test/models/face.rb +14 -6
  500. data/test/models/family.rb +6 -0
  501. data/test/models/family_tree.rb +6 -0
  502. data/test/models/friendship.rb +5 -3
  503. data/test/models/frog.rb +8 -0
  504. data/test/models/guid.rb +3 -1
  505. data/test/models/guitar.rb +2 -0
  506. data/test/models/hotel.rb +5 -3
  507. data/test/models/human.rb +39 -0
  508. data/test/models/image.rb +3 -1
  509. data/test/models/interest.rb +14 -3
  510. data/test/models/invoice.rb +4 -2
  511. data/test/models/item.rb +3 -1
  512. data/test/models/job.rb +5 -3
  513. data/test/models/joke.rb +4 -2
  514. data/test/models/keyboard.rb +3 -1
  515. data/test/models/legacy_thing.rb +2 -0
  516. data/test/models/lesson.rb +2 -0
  517. data/test/models/line_item.rb +3 -1
  518. data/test/models/liquid.rb +2 -0
  519. data/test/models/matey.rb +3 -1
  520. data/test/models/measurement.rb +4 -0
  521. data/test/models/member.rb +23 -20
  522. data/test/models/member_detail.rb +3 -0
  523. data/test/models/member_type.rb +2 -0
  524. data/test/models/membership.rb +4 -1
  525. data/test/models/mentor.rb +3 -1
  526. data/test/models/message.rb +5 -0
  527. data/test/models/minimalistic.rb +2 -0
  528. data/test/models/minivan.rb +3 -2
  529. data/test/models/mixed_case_monkey.rb +3 -1
  530. data/test/models/molecule.rb +2 -0
  531. data/test/models/mouse.rb +6 -0
  532. data/test/models/movie.rb +2 -0
  533. data/test/models/node.rb +4 -2
  534. data/test/models/non_primary_key.rb +2 -0
  535. data/test/models/notification.rb +2 -0
  536. data/test/models/numeric_data.rb +12 -0
  537. data/test/models/order.rb +4 -2
  538. data/test/models/organization.rb +9 -7
  539. data/test/models/other_dog.rb +3 -1
  540. data/test/models/owner.rb +6 -4
  541. data/test/models/parrot.rb +12 -4
  542. data/test/models/person.rb +59 -54
  543. data/test/models/personal_legacy_thing.rb +3 -1
  544. data/test/models/pet.rb +4 -2
  545. data/test/models/pet_treasure.rb +2 -0
  546. data/test/models/pirate.rb +67 -43
  547. data/test/models/possession.rb +3 -1
  548. data/test/models/post.rb +184 -86
  549. data/test/models/price_estimate.rb +11 -1
  550. data/test/models/professor.rb +3 -1
  551. data/test/models/project.rb +14 -12
  552. data/test/models/publisher/article.rb +2 -0
  553. data/test/models/publisher/magazine.rb +2 -0
  554. data/test/models/publisher.rb +2 -0
  555. data/test/models/randomly_named_c1.rb +2 -0
  556. data/test/models/rating.rb +5 -1
  557. data/test/models/reader.rb +7 -5
  558. data/test/models/recipe.rb +2 -0
  559. data/test/models/record.rb +2 -0
  560. data/test/models/reference.rb +6 -3
  561. data/test/models/reply.rb +39 -21
  562. data/test/models/room.rb +6 -0
  563. data/test/models/section.rb +6 -0
  564. data/test/models/seminar.rb +6 -0
  565. data/test/models/session.rb +6 -0
  566. data/test/models/ship.rb +12 -9
  567. data/test/models/ship_part.rb +5 -3
  568. data/test/models/shop.rb +4 -2
  569. data/test/models/shop_account.rb +2 -0
  570. data/test/models/speedometer.rb +2 -0
  571. data/test/models/sponsor.rb +8 -5
  572. data/test/models/squeak.rb +6 -0
  573. data/test/models/strict_zine.rb +7 -0
  574. data/test/models/string_key_object.rb +2 -0
  575. data/test/models/student.rb +2 -0
  576. data/test/models/subscriber.rb +4 -2
  577. data/test/models/subscription.rb +5 -1
  578. data/test/models/tag.rb +6 -3
  579. data/test/models/tagging.rb +13 -6
  580. data/test/models/task.rb +2 -0
  581. data/test/models/topic.rb +54 -19
  582. data/test/models/toy.rb +4 -0
  583. data/test/models/traffic_light.rb +2 -0
  584. data/test/models/treasure.rb +5 -3
  585. data/test/models/treaty.rb +2 -4
  586. data/test/models/tree.rb +2 -0
  587. data/test/models/tuning_peg.rb +2 -0
  588. data/test/models/tyre.rb +2 -0
  589. data/test/models/user.rb +12 -4
  590. data/test/models/uuid_child.rb +2 -0
  591. data/test/models/uuid_item.rb +2 -0
  592. data/test/models/uuid_parent.rb +2 -0
  593. data/test/models/vegetables.rb +12 -3
  594. data/test/models/vertex.rb +6 -4
  595. data/test/models/warehouse_thing.rb +2 -0
  596. data/test/models/wheel.rb +3 -1
  597. data/test/models/without_table.rb +3 -1
  598. data/test/models/zine.rb +3 -1
  599. data/test/schema/mysql2_specific_schema.rb +49 -35
  600. data/test/schema/oracle_specific_schema.rb +13 -15
  601. data/test/schema/postgresql_specific_schema.rb +51 -40
  602. data/test/schema/schema.rb +334 -154
  603. data/test/schema/sqlite_specific_schema.rb +9 -16
  604. data/test/support/config.rb +26 -26
  605. data/test/support/connection.rb +14 -8
  606. data/test/support/connection_helper.rb +3 -1
  607. data/test/support/ddl_helper.rb +2 -0
  608. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic.dump +0 -0
  609. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_0_topic_associations.dump +0 -0
  610. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic.dump +0 -0
  611. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_0_topic_associations.dump +0 -0
  612. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic.dump +0 -0
  613. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_0_topic_associations.dump +0 -0
  614. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic.dump +0 -0
  615. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_0_topic_associations.dump +0 -0
  616. data/test/support/marshal_compatibility_fixtures/legacy_6_0_record_mysql.dump +0 -0
  617. data/test/support/marshal_compatibility_fixtures/legacy_relation.dump +0 -0
  618. data/test/support/schema_dumping_helper.rb +2 -0
  619. data/test/support/stubs/strong_parameters.rb +40 -0
  620. data/test/support/yaml_compatibility_fixtures/rails_v1_mysql.yml +206 -0
  621. data/test/support/yaml_compatibility_fixtures/rails_v2.yml +55 -0
  622. metadata +185 -4
@@ -1,44 +1,88 @@
1
+ # frozen_string_literal: true
2
+
1
3
  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'
4
+ require "models/post"
5
+ require "models/person"
6
+ require "models/reference"
7
+ require "models/job"
8
+ require "models/reader"
9
+ require "models/comment"
10
+ require "models/rating"
11
+ require "models/tag"
12
+ require "models/tagging"
13
+ require "models/author"
14
+ require "models/owner"
15
+ require "models/pet"
16
+ require "models/pet_treasure"
17
+ require "models/toy"
18
+ require "models/treasure"
19
+ require "models/contract"
20
+ require "models/company"
21
+ require "models/developer"
22
+ require "models/computer"
23
+ require "models/subscriber"
24
+ require "models/book"
25
+ require "models/subscription"
26
+ require "models/essay"
27
+ require "models/category"
28
+ require "models/categorization"
29
+ require "models/member"
30
+ require "models/membership"
31
+ require "models/club"
32
+ require "models/organization"
33
+ require "models/user"
34
+ require "models/family"
35
+ require "models/family_tree"
36
+ require "models/section"
37
+ require "models/seminar"
38
+ require "models/session"
31
39
 
32
40
  class HasManyThroughAssociationsTest < ActiveRecord::TestCase
33
41
  fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags,
34
42
  :owners, :pets, :toys, :jobs, :references, :companies, :members, :author_addresses,
35
43
  :subscribers, :books, :subscriptions, :developers, :categorizations, :essays,
36
- :categories_posts, :clubs, :memberships, :organizations
44
+ :categories_posts, :clubs, :memberships, :organizations, :author_favorites
37
45
 
38
46
  # Dummies to force column loads so query counts are clean.
39
47
  def setup
40
- Person.create :first_name => 'gummy'
41
- Reader.create :person_id => 0, :post_id => 0
48
+ Person.create first_name: "gummy"
49
+ Reader.create person_id: 0, post_id: 0
50
+ end
51
+
52
+ def test_has_many_through_create_record
53
+ assert books(:awdr).subscribers.create!(nick: "bob")
54
+ end
55
+
56
+ def test_marshal_dump
57
+ preloaded = Post.includes(:first_blue_tags).first
58
+ assert_equal preloaded, Marshal.load(Marshal.dump(preloaded))
59
+ end
60
+
61
+ def test_through_association_with_joins
62
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).comments.merge(Post.joins(:comments))
63
+ end
64
+
65
+ def test_through_association_with_left_joins
66
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).comments.merge(Post.left_joins(:comments))
67
+ end
68
+
69
+ def test_through_association_with_through_scope_and_nested_where
70
+ company = Company.create!(name: "special")
71
+ developer = SpecialDeveloper.create!
72
+ SpecialContract.create!(company: company, special_developer: developer)
73
+
74
+ assert_equal [developer], company.special_developers.where.not("contracts.id": nil)
75
+ end
76
+
77
+ def test_preload_with_nested_association
78
+ posts = Post.where(id: [authors(:david).id, authors(:mary).id]).
79
+ preload(:author, :author_favorites_with_scope).order(:id).to_a
80
+
81
+ assert_no_queries do
82
+ posts.each(&:author)
83
+ posts.each(&:author_favorites_with_scope)
84
+ assert_equal 1, posts[0].author_favorites_with_scope.length
85
+ end
42
86
  end
43
87
 
44
88
  def test_preload_sti_rhs_class
@@ -49,9 +93,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
49
93
  end
50
94
 
51
95
  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')
96
+ club = Club.create!(name: "Aaron cool banana club")
97
+ member1 = Member.create!(name: "Aaron")
98
+ member2 = Member.create!(name: "Cat")
55
99
 
56
100
  SuperMembership.create! club: club, member: member1
57
101
  CurrentMembership.create! club: club, member: member2
@@ -61,21 +105,26 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
61
105
  club1.members.sort_by(&:id)
62
106
  end
63
107
 
64
- def make_model(name)
65
- Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
108
+ def test_preload_multiple_instances_of_the_same_record
109
+ club = Club.create!(name: "Aaron cool banana club")
110
+ Membership.create! club: club, member: Member.create!(name: "Aaron")
111
+ Membership.create! club: club, member: Member.create!(name: "Bob")
112
+
113
+ preloaded_clubs = Club.joins(:memberships).preload(:membership).to_a
114
+ assert_no_queries { preloaded_clubs.each(&:membership) }
66
115
  end
67
116
 
68
- def test_ordered_habtm
117
+ def test_ordered_has_many_through
69
118
  person_prime = Class.new(ActiveRecord::Base) do
70
- def self.name; 'Person'; end
119
+ def self.name; "Person"; end
71
120
 
72
121
  has_many :readers
73
- has_many :posts, -> { order('posts.id DESC') }, :through => :readers
122
+ has_many :posts, -> { order("posts.id DESC") }, through: :readers
74
123
  end
75
124
  posts = person_prime.includes(:posts).first.posts
76
125
 
77
126
  assert_operator posts.length, :>, 1
78
- posts.each_cons(2) do |left,right|
127
+ posts.each_cons(2) do |left, right|
79
128
  assert_operator left.id, :>, right.id
80
129
  end
81
130
  end
@@ -85,7 +134,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
85
134
  subscription = make_model "Subscription"
86
135
  subscriber = make_model "Subscriber"
87
136
 
88
- subscriber.primary_key = 'nick'
137
+ subscriber.primary_key = "nick"
89
138
  subscription.belongs_to :book, anonymous_class: book
90
139
  subscription.belongs_to :subscriber, anonymous_class: subscriber
91
140
 
@@ -106,8 +155,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
106
155
  def test_no_pk_join_table_append
107
156
  lesson, _, student = make_no_pk_hm_t
108
157
 
109
- sicp = lesson.new(:name => "SICP")
110
- ben = student.new(:name => "Ben Bitdiddle")
158
+ sicp = lesson.new(name: "SICP")
159
+ ben = student.new(name: "Ben Bitdiddle")
111
160
  sicp.students << ben
112
161
  assert sicp.save!
113
162
  end
@@ -115,17 +164,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
115
164
  def test_no_pk_join_table_delete
116
165
  lesson, lesson_student, student = make_no_pk_hm_t
117
166
 
118
- sicp = lesson.new(:name => "SICP")
119
- ben = student.new(:name => "Ben Bitdiddle")
120
- louis = student.new(:name => "Louis Reasoner")
167
+ sicp = lesson.new(name: "SICP")
168
+ ben = student.new(name: "Ben Bitdiddle")
169
+ louis = student.new(name: "Louis Reasoner")
121
170
  sicp.students << ben
122
171
  sicp.students << louis
123
172
  assert sicp.save!
124
173
 
125
174
  sicp.students.reload
126
175
  assert_operator lesson_student.count, :>=, 2
127
- assert_no_difference('student.count') do
128
- assert_difference('lesson_student.count', -2) do
176
+ assert_no_difference("student.count") do
177
+ assert_difference("lesson_student.count", -2) do
129
178
  sicp.students.destroy(*student.all.to_a)
130
179
  end
131
180
  end
@@ -139,8 +188,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
139
188
  after_destroy_called = true
140
189
  end
141
190
 
142
- sicp = lesson.new(:name => "SICP")
143
- ben = student.new(:name => "Ben Bitdiddle")
191
+ sicp = lesson.new(name: "SICP")
192
+ ben = student.new(name: "Ben Bitdiddle")
144
193
  sicp.students << ben
145
194
  assert sicp.save!
146
195
 
@@ -149,33 +198,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
149
198
  assert after_destroy_called, "after destroy should be called"
150
199
  end
151
200
 
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
201
  def test_pk_is_not_required_for_join
167
202
  post = Post.includes(:scategories).first
168
203
  post2 = Post.includes(:categories).first
169
204
 
170
205
  assert_operator post.categories.length, :>, 0
171
- assert_equal post2.categories, post.categories
206
+ assert_equal post2.categories.sort_by(&:id), post.categories.sort_by(&:id)
172
207
  end
173
208
 
174
209
  def test_include?
175
210
  person = Person.new
176
211
  post = Post.new
177
212
  person.posts << post
178
- assert person.posts.include?(post)
213
+ assert_includes person.posts, post
179
214
  end
180
215
 
181
216
  def test_associate_existing
@@ -187,19 +222,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
187
222
  end
188
223
 
189
224
  assert_queries(1) do
190
- assert post.people.include?(person)
225
+ assert_includes post.people, person
191
226
  end
192
227
 
193
- assert post.reload.people.reload.include?(person)
228
+ assert_includes post.reload.people.reload, person
194
229
  end
195
230
 
196
231
  def test_delete_all_for_with_dependent_option_destroy
197
232
  person = people(:david)
198
233
  assert_equal 1, person.jobs_with_dependent_destroy.count
199
234
 
200
- assert_no_difference 'Job.count' do
201
- assert_difference 'Reference.count', -1 do
202
- person.reload.jobs_with_dependent_destroy.delete_all
235
+ assert_no_difference "Job.count" do
236
+ assert_difference "Reference.count", -1 do
237
+ assert_equal 1, person.reload.jobs_with_dependent_destroy.delete_all
203
238
  end
204
239
  end
205
240
  end
@@ -208,9 +243,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
208
243
  person = people(:david)
209
244
  assert_equal 1, person.jobs_with_dependent_nullify.count
210
245
 
211
- assert_no_difference 'Job.count' do
212
- assert_no_difference 'Reference.count' do
213
- person.reload.jobs_with_dependent_nullify.delete_all
246
+ assert_no_difference "Job.count" do
247
+ assert_no_difference "Reference.count" do
248
+ assert_equal 1, person.reload.jobs_with_dependent_nullify.delete_all
214
249
  end
215
250
  end
216
251
  end
@@ -219,26 +254,35 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
219
254
  person = people(:david)
220
255
  assert_equal 1, person.jobs_with_dependent_delete_all.count
221
256
 
222
- assert_no_difference 'Job.count' do
223
- assert_difference 'Reference.count', -1 do
224
- person.reload.jobs_with_dependent_delete_all.delete_all
257
+ assert_no_difference "Job.count" do
258
+ assert_difference "Reference.count", -1 do
259
+ assert_equal 1, person.reload.jobs_with_dependent_delete_all.delete_all
225
260
  end
226
261
  end
227
262
  end
228
263
 
264
+ def test_delete_all_on_association_clears_scope
265
+ post = Post.create!(title: "Rails 6", body: "")
266
+ people = post.people
267
+ people.create!(first_name: "Jeb")
268
+ people.delete_all
269
+ assert_nil people.first
270
+ end
271
+
229
272
  def test_concat
230
273
  person = people(:david)
231
274
  post = posts(:thinking)
232
- post.people.concat [person]
275
+ result = post.people.concat [person]
233
276
  assert_equal 1, post.people.size
234
277
  assert_equal 1, post.people.reload.size
278
+ assert_equal post.people, result
235
279
  end
236
280
 
237
281
  def test_associate_existing_record_twice_should_add_to_target_twice
238
282
  post = posts(:thinking)
239
283
  person = people(:david)
240
284
 
241
- assert_difference 'post.people.to_a.count', 2 do
285
+ assert_difference "post.people.to_a.count", 2 do
242
286
  post.people << person
243
287
  post.people << person
244
288
  end
@@ -248,7 +292,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
248
292
  post = posts(:thinking)
249
293
  person = people(:david)
250
294
 
251
- assert_difference 'post.people.count', 2 do
295
+ assert_difference "post.people.count", 2 do
252
296
  post.people << person
253
297
  post.people << person
254
298
  end
@@ -261,12 +305,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
261
305
  post.people << person
262
306
  post.people << person
263
307
 
264
- counts = ['post.people.count', 'post.people.to_a.count', 'post.readers.count', 'post.readers.to_a.count']
308
+ counts = ["post.people.count", "post.people.to_a.count", "post.readers.count", "post.readers.to_a.count"]
265
309
  assert_difference counts, -2 do
266
310
  post.people.delete(person)
267
311
  end
268
312
 
269
- assert !post.people.reload.include?(person)
313
+ assert_not_includes post.people.reload, person
270
314
  end
271
315
 
272
316
  def test_associating_new
@@ -274,7 +318,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
274
318
  new_person = nil # so block binding catches it
275
319
 
276
320
  assert_queries(0) do
277
- new_person = Person.new :first_name => 'bob'
321
+ new_person = Person.new first_name: "bob"
278
322
  end
279
323
 
280
324
  # Associating new records always saves them
@@ -284,59 +328,70 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
284
328
  end
285
329
 
286
330
  assert_queries(1) do
287
- assert posts(:thinking).people.include?(new_person)
331
+ assert_includes posts(:thinking).people, new_person
288
332
  end
289
333
 
290
- assert posts(:thinking).reload.people.reload.include?(new_person)
334
+ assert_includes posts(:thinking).reload.people.reload, new_person
291
335
  end
292
336
 
293
337
  def test_associate_new_by_building
294
338
  assert_queries(1) { posts(:thinking) }
295
339
 
296
340
  assert_queries(0) do
297
- posts(:thinking).people.build(:first_name => "Bob")
298
- posts(:thinking).people.new(:first_name => "Ted")
341
+ posts(:thinking).people.build(first_name: "Bob")
342
+ posts(:thinking).people.new(first_name: "Ted")
299
343
  end
300
344
 
301
345
  # Should only need to load the association once
302
346
  assert_queries(1) do
303
- assert posts(:thinking).people.collect(&:first_name).include?("Bob")
304
- assert posts(:thinking).people.collect(&:first_name).include?("Ted")
347
+ assert_includes posts(:thinking).people.collect(&:first_name), "Bob"
348
+ assert_includes posts(:thinking).people.collect(&:first_name), "Ted"
305
349
  end
306
350
 
307
351
  # 2 queries for each new record (1 to save the record itself, 1 for the join model)
308
352
  # * 2 new records = 4
309
353
  # + 1 query to save the actual post = 5
310
354
  assert_queries(5) do
311
- posts(:thinking).body += '-changed'
355
+ posts(:thinking).body += "-changed"
312
356
  posts(:thinking).save
313
357
  end
314
358
 
315
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Bob")
316
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Ted")
359
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Bob"
360
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Ted"
317
361
  end
318
362
 
319
363
  def test_build_then_save_with_has_many_inverse
320
364
  post = posts(:thinking)
321
- person = post.people.build(:first_name => "Bob")
365
+ person = post.people.build(first_name: "Bob")
322
366
  person.save
323
367
  post.reload
324
368
 
325
- assert post.people.include?(person)
369
+ assert_includes post.people, person
326
370
  end
327
371
 
328
372
  def test_build_then_save_with_has_one_inverse
329
373
  post = posts(:thinking)
330
- person = post.single_people.build(:first_name => "Bob")
374
+ person = post.single_people.build(first_name: "Bob")
331
375
  person.save
332
376
  post.reload
333
377
 
334
- assert post.single_people.include?(person)
378
+ assert_includes post.single_people, person
379
+ end
380
+
381
+ def test_build_then_remove_then_save
382
+ post = posts(:thinking)
383
+ post.people.build(first_name: "Bob")
384
+ ted = post.people.build(first_name: "Ted")
385
+ post.people.delete(ted)
386
+ post.save!
387
+ post.reload
388
+
389
+ assert_equal ["Bob"], post.people.collect(&:first_name)
335
390
  end
336
391
 
337
392
  def test_both_parent_ids_set_when_saving_new
338
- post = Post.new(title: 'Hello', body: 'world')
339
- person = Person.new(first_name: 'Sean')
393
+ post = Post.new(title: "Hello", body: "world")
394
+ person = Person.new(first_name: "Sean")
340
395
 
341
396
  post.people = [person]
342
397
  post.save
@@ -348,17 +403,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
348
403
  end
349
404
 
350
405
  def test_delete_association
351
- assert_queries(2){posts(:welcome);people(:michael); }
406
+ assert_queries(2) { posts(:welcome); people(:michael); }
352
407
 
353
408
  assert_queries(1) do
354
409
  posts(:welcome).people.delete(people(:michael))
355
410
  end
356
411
 
357
412
  assert_queries(1) do
358
- assert posts(:welcome).people.empty?
413
+ assert_empty posts(:welcome).people
359
414
  end
360
415
 
361
- assert posts(:welcome).reload.people.reload.empty?
416
+ assert_empty posts(:welcome).reload.people.reload
362
417
  end
363
418
 
364
419
  def test_destroy_association
@@ -368,8 +423,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
368
423
  end
369
424
  end
370
425
 
371
- assert posts(:welcome).reload.people.empty?
372
- assert posts(:welcome).people.reload.empty?
426
+ assert_empty posts(:welcome).reload.people
427
+ assert_empty posts(:welcome).people.reload
373
428
  end
374
429
 
375
430
  def test_destroy_all
@@ -379,8 +434,32 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
379
434
  end
380
435
  end
381
436
 
382
- assert posts(:welcome).reload.people.empty?
383
- assert posts(:welcome).people.reload.empty?
437
+ assert_empty posts(:welcome).reload.people
438
+ assert_empty posts(:welcome).people.reload
439
+ end
440
+
441
+ def test_destroy_all_on_association_clears_scope
442
+ post = Post.create!(title: "Rails 6", body: "")
443
+ people = post.people
444
+ people.create!(first_name: "Jeb")
445
+ people.destroy_all
446
+ assert_nil people.first
447
+ end
448
+
449
+ def test_destroy_on_association_clears_scope
450
+ post = Post.create!(title: "Rails 6", body: "")
451
+ people = post.people
452
+ person = people.create!(first_name: "Jeb")
453
+ people.destroy(person)
454
+ assert_nil people.first
455
+ end
456
+
457
+ def test_delete_on_association_clears_scope
458
+ post = Post.create!(title: "Rails 6", body: "")
459
+ people = post.people
460
+ person = people.create!(first_name: "Jeb")
461
+ people.delete(person)
462
+ assert_nil people.first
384
463
  end
385
464
 
386
465
  def test_should_raise_exception_for_destroying_mismatching_records
@@ -394,15 +473,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
394
473
 
395
474
  person = people(:michael)
396
475
  job = jobs(:magician)
397
- reference = Reference.where(:job_id => job.id, :person_id => person.id).first
476
+ reference = Reference.where(job_id: job.id, person_id: person.id).first
398
477
 
399
- assert_no_difference ['Job.count', 'Reference.count'] do
400
- assert_difference 'person.jobs.count', -1 do
478
+ assert_no_difference ["Job.count", "Reference.count"] do
479
+ assert_difference "person.jobs.count", -1 do
401
480
  person.jobs_with_dependent_nullify.delete(job)
402
481
  end
403
482
  end
404
483
 
405
- assert_equal nil, reference.reload.job_id
484
+ assert_nil reference.reload.job_id
406
485
  ensure
407
486
  Reference.make_comments = false
408
487
  end
@@ -416,14 +495,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
416
495
  # Make sure we're not deleting everything
417
496
  assert person.jobs.count >= 2
418
497
 
419
- assert_no_difference 'Job.count' do
420
- assert_difference ['person.jobs.count', 'Reference.count'], -1 do
498
+ assert_no_difference "Job.count" do
499
+ assert_difference ["person.jobs.count", "Reference.count"], -1 do
421
500
  person.jobs_with_dependent_delete_all.delete(job)
422
501
  end
423
502
  end
424
503
 
425
504
  # Check that the destroy callback on Reference did not run
426
- assert_equal nil, person.reload.comments
505
+ assert_nil person.reload.comments
427
506
  ensure
428
507
  Reference.make_comments = false
429
508
  end
@@ -437,8 +516,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
437
516
  # Make sure we're not deleting everything
438
517
  assert person.jobs.count >= 2
439
518
 
440
- assert_no_difference 'Job.count' do
441
- assert_difference ['person.jobs.count', 'Reference.count'], -1 do
519
+ assert_no_difference "Job.count" do
520
+ assert_difference ["person.jobs.count", "Reference.count"], -1 do
442
521
  person.jobs_with_dependent_destroy.delete(job)
443
522
  end
444
523
  end
@@ -455,8 +534,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
455
534
  # Create a reference which is not linked to a job. This should not be destroyed.
456
535
  person.references.create!
457
536
 
458
- assert_no_difference 'Job.count' do
459
- assert_difference 'Reference.count', -person.jobs.count do
537
+ assert_no_difference "Job.count" do
538
+ assert_difference "Reference.count", -person.jobs.count do
460
539
  person.destroy
461
540
  end
462
541
  end
@@ -468,8 +547,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
468
547
  # Create a reference which is not linked to a job. This should not be destroyed.
469
548
  person.references.create!
470
549
 
471
- assert_no_difference 'Job.count' do
472
- assert_difference 'Reference.count', -person.jobs.count do
550
+ assert_no_difference "Job.count" do
551
+ assert_difference "Reference.count", -person.jobs.count do
473
552
  person.destroy
474
553
  end
475
554
  end
@@ -480,41 +559,41 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
480
559
 
481
560
  references = person.references.to_a
482
561
 
483
- assert_no_difference ['Reference.count', 'Job.count'] do
562
+ assert_no_difference ["Reference.count", "Job.count"] do
484
563
  person.destroy
485
564
  end
486
565
 
487
566
  references.each do |reference|
488
- assert_equal nil, reference.reload.job_id
567
+ assert_nil reference.reload.job_id
489
568
  end
490
569
  end
491
570
 
492
571
  def test_update_counter_caches_on_delete
493
572
  post = posts(:welcome)
494
- tag = post.tags.create!(:name => 'doomed')
573
+ tag = post.tags.create!(name: "doomed")
495
574
 
496
- assert_difference ['post.reload.tags_count'], -1 do
575
+ assert_difference ["post.reload.tags_count"], -1 do
497
576
  posts(:welcome).tags.delete(tag)
498
577
  end
499
578
  end
500
579
 
501
580
  def test_update_counter_caches_on_delete_with_dependent_destroy
502
581
  post = posts(:welcome)
503
- tag = post.tags.create!(:name => 'doomed')
582
+ tag = post.tags.create!(name: "doomed")
504
583
  post.update_columns(tags_with_destroy_count: post.tags.count)
505
584
 
506
- assert_difference ['post.reload.tags_with_destroy_count'], -1 do
585
+ assert_difference ["post.reload.tags_with_destroy_count"], -1 do
507
586
  posts(:welcome).tags_with_destroy.delete(tag)
508
587
  end
509
588
  end
510
589
 
511
590
  def test_update_counter_caches_on_delete_with_dependent_nullify
512
591
  post = posts(:welcome)
513
- tag = post.tags.create!(:name => 'doomed')
592
+ tag = post.tags.create!(name: "doomed")
514
593
  post.update_columns(tags_with_nullify_count: post.tags.count)
515
594
 
516
- assert_no_difference 'post.reload.tags_count' do
517
- assert_difference 'post.reload.tags_with_nullify_count', -1 do
595
+ assert_no_difference "post.reload.tags_count" do
596
+ assert_difference "post.reload.tags_with_nullify_count", -1 do
518
597
  posts(:welcome).tags_with_nullify.delete(tag)
519
598
  end
520
599
  end
@@ -522,7 +601,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
522
601
 
523
602
  def test_update_counter_caches_on_replace_association
524
603
  post = posts(:welcome)
525
- tag = post.tags.create!(:name => 'doomed')
604
+ tag = post.tags.create!(name: "doomed")
526
605
  tag.tagged_posts << posts(:thinking)
527
606
 
528
607
  tag.tagged_posts = []
@@ -533,15 +612,25 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
533
612
 
534
613
  def test_update_counter_caches_on_destroy
535
614
  post = posts(:welcome)
536
- tag = post.tags.create!(name: 'doomed')
615
+ tag = post.tags.create!(name: "doomed")
537
616
 
538
- assert_difference 'post.reload.tags_count', -1 do
617
+ assert_difference "post.reload.tags_count", -1 do
539
618
  tag.tagged_posts.destroy(post)
540
619
  end
541
620
  end
542
621
 
622
+ def test_update_counter_caches_on_destroy_with_indestructible_through_record
623
+ post = posts(:welcome)
624
+ tag = post.indestructible_tags.create!(name: "doomed")
625
+ post.update_columns(indestructible_tags_count: post.indestructible_tags.count)
626
+
627
+ assert_no_difference "post.reload.indestructible_tags_count" do
628
+ posts(:welcome).indestructible_tags.destroy(tag)
629
+ end
630
+ end
631
+
543
632
  def test_replace_association
544
- assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people.reload}
633
+ assert_queries(4) { posts(:welcome); people(:david); people(:michael); posts(:welcome).people.reload }
545
634
 
546
635
  # 1 query to delete the existing reader (michael)
547
636
  # 1 query to associate the new reader (david)
@@ -549,35 +638,45 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
549
638
  posts(:welcome).people = [people(:david)]
550
639
  end
551
640
 
552
- assert_queries(0){
553
- assert posts(:welcome).people.include?(people(:david))
554
- assert !posts(:welcome).people.include?(people(:michael))
555
- }
641
+ assert_no_queries do
642
+ assert_includes posts(:welcome).people, people(:david)
643
+ assert_not_includes posts(:welcome).people, people(:michael)
644
+ end
645
+
646
+ assert_includes posts(:welcome).reload.people.reload, people(:david)
647
+ assert_not_includes posts(:welcome).reload.people.reload, people(:michael)
648
+ end
556
649
 
557
- assert posts(:welcome).reload.people.reload.include?(people(:david))
558
- assert !posts(:welcome).reload.people.reload.include?(people(:michael))
650
+ def test_replace_association_with_duplicates
651
+ post = posts(:thinking)
652
+ person = people(:david)
653
+
654
+ assert_difference "post.people.count", 2 do
655
+ post.people = [person]
656
+ post.people = [person, person]
657
+ end
559
658
  end
560
659
 
561
660
  def test_replace_order_is_preserved
562
661
  posts(:welcome).people.clear
563
662
  posts(:welcome).people = [people(:david), people(:michael)]
564
- assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order('id').map(&:person_id)
663
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order("id").map(&:person_id)
565
664
 
566
665
  # Test the inverse order in case the first success was a coincidence
567
666
  posts(:welcome).people.clear
568
667
  posts(:welcome).people = [people(:michael), people(:david)]
569
- assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order('id').map(&:person_id)
668
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order("id").map(&:person_id)
570
669
  end
571
670
 
572
671
  def test_replace_by_id_order_is_preserved
573
672
  posts(:welcome).people.clear
574
673
  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)
674
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.order("id").map(&:person_id)
576
675
 
577
676
  # Test the inverse order in case the first success was a coincidence
578
677
  posts(:welcome).people.clear
579
678
  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)
679
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.order("id").map(&:person_id)
581
680
  end
582
681
 
583
682
  def test_associate_with_create
@@ -586,101 +685,107 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
586
685
  # 1 query for the new record, 1 for the join table record
587
686
  # No need to update the actual collection yet!
588
687
  assert_queries(2) do
589
- posts(:thinking).people.create(:first_name=>"Jeb")
688
+ posts(:thinking).people.create(first_name: "Jeb")
590
689
  end
591
690
 
592
691
  # *Now* we actually need the collection so it's loaded
593
692
  assert_queries(1) do
594
- assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
693
+ assert_includes posts(:thinking).people.collect(&:first_name), "Jeb"
595
694
  end
596
695
 
597
- assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Jeb")
696
+ assert_includes posts(:thinking).reload.people.reload.collect(&:first_name), "Jeb"
598
697
  end
599
698
 
600
699
  def test_through_record_is_built_when_created_with_where
601
700
  assert_difference("posts(:thinking).readers.count", 1) do
602
- posts(:thinking).people.where(first_name: "Jeb").create
701
+ posts(:thinking).people.where(readers: { skimmer: true }).create(first_name: "Jeb")
603
702
  end
703
+ reader = posts(:thinking).readers.last
704
+ assert_equal true, reader.skimmer
604
705
  end
605
706
 
606
707
  def test_associate_with_create_and_no_options
607
708
  peeps = posts(:thinking).people.count
608
- posts(:thinking).people.create(:first_name => 'foo')
709
+ posts(:thinking).people.create(first_name: "foo")
609
710
  assert_equal peeps + 1, posts(:thinking).people.count
610
711
  end
611
712
 
612
713
  def test_associate_with_create_with_through_having_conditions
613
714
  impatient_people = posts(:thinking).impatient_people.count
614
- posts(:thinking).impatient_people.create!(:first_name => 'foo')
715
+ posts(:thinking).impatient_people.create!(first_name: "foo")
615
716
  assert_equal impatient_people + 1, posts(:thinking).impatient_people.count
616
717
  end
617
718
 
618
719
  def test_associate_with_create_exclamation_and_no_options
619
720
  peeps = posts(:thinking).people.count
620
- posts(:thinking).people.create!(:first_name => 'foo')
721
+ posts(:thinking).people.create!(first_name: "foo")
621
722
  assert_equal peeps + 1, posts(:thinking).people.count
622
723
  end
623
724
 
624
725
  def test_create_on_new_record
625
726
  p = Post.new
626
727
 
627
- error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(:first_name => "mew") }
728
+ error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(first_name: "mew") }
628
729
  assert_equal "You cannot call create unless the parent is saved", error.message
629
730
 
630
- error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(:first_name => "snow") }
731
+ error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(first_name: "snow") }
631
732
  assert_equal "You cannot call create unless the parent is saved", error.message
632
733
  end
633
734
 
634
735
  def test_associate_with_create_and_invalid_options
635
736
  firm = companies(:first_firm)
636
- assert_no_difference('firm.developers.count') { assert_nothing_raised { firm.developers.create(:name => '0') } }
737
+ assert_no_difference("firm.developers.count") { assert_nothing_raised { firm.developers.create(name: "0") } }
637
738
  end
638
739
 
639
740
  def test_associate_with_create_and_valid_options
640
741
  firm = companies(:first_firm)
641
- assert_difference('firm.developers.count', 1) { firm.developers.create(:name => 'developer') }
742
+ assert_difference("firm.developers.count", 1) { firm.developers.create(name: "developer") }
642
743
  end
643
744
 
644
745
  def test_associate_with_create_bang_and_invalid_options
645
746
  firm = companies(:first_firm)
646
- assert_no_difference('firm.developers.count') { assert_raises(ActiveRecord::RecordInvalid) { firm.developers.create!(:name => '0') } }
747
+ assert_no_difference("firm.developers.count") { assert_raises(ActiveRecord::RecordInvalid) { firm.developers.create!(name: "0") } }
647
748
  end
648
749
 
649
750
  def test_associate_with_create_bang_and_valid_options
650
751
  firm = companies(:first_firm)
651
- assert_difference('firm.developers.count', 1) { firm.developers.create!(:name => 'developer') }
752
+ assert_difference("firm.developers.count", 1) { firm.developers.create!(name: "developer") }
652
753
  end
653
754
 
654
755
  def test_push_with_invalid_record
655
756
  firm = companies(:first_firm)
656
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << Developer.new(:name => '0') }
757
+ assert_raises(ActiveRecord::RecordInvalid) { firm.developers << Developer.new(name: "0") }
657
758
  end
658
759
 
659
760
  def test_push_with_invalid_join_record
660
761
  repair_validations(Contract) do
661
- Contract.validate {|r| r.errors[:base] << 'Invalid Contract' }
762
+ Contract.validate { |r| r.errors[:base] << "Invalid Contract" }
662
763
 
663
764
  firm = companies(:first_firm)
664
- lifo = Developer.new(:name => 'lifo')
665
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
765
+ lifo = Developer.new(name: "lifo")
766
+ assert_raises(ActiveRecord::RecordInvalid) do
767
+ assert_deprecated { firm.developers << lifo }
768
+ end
666
769
 
667
- lifo = Developer.create!(:name => 'lifo')
668
- assert_raises(ActiveRecord::RecordInvalid) { firm.developers << lifo }
770
+ lifo = Developer.create!(name: "lifo")
771
+ assert_raises(ActiveRecord::RecordInvalid) do
772
+ assert_deprecated { firm.developers << lifo }
773
+ end
669
774
  end
670
775
  end
671
776
 
672
777
  def test_clear_associations
673
- assert_queries(2) { posts(:welcome);posts(:welcome).people.reload }
778
+ assert_queries(2) { posts(:welcome); posts(:welcome).people.reload }
674
779
 
675
780
  assert_queries(1) do
676
781
  posts(:welcome).people.clear
677
782
  end
678
783
 
679
- assert_queries(0) do
680
- assert posts(:welcome).people.empty?
784
+ assert_no_queries do
785
+ assert_empty posts(:welcome).people
681
786
  end
682
787
 
683
- assert posts(:welcome).reload.people.reload.empty?
788
+ assert_empty posts(:welcome).reload.people.reload
684
789
  end
685
790
 
686
791
  def test_association_callback_ordering
@@ -694,7 +799,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
694
799
  [:added, :after, "Michael"]
695
800
  ], log.last(2)
696
801
 
697
- post.people_with_callbacks.push(people(:david), Person.create!(:first_name => "Bob"), Person.new(:first_name => "Lary"))
802
+ post.people_with_callbacks.push(people(:david), Person.create!(first_name: "Bob"), Person.new(first_name: "Lary"))
698
803
  assert_equal [
699
804
  [:added, :before, "David"],
700
805
  [:added, :after, "David"],
@@ -702,21 +807,21 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
702
807
  [:added, :after, "Bob"],
703
808
  [:added, :before, "Lary"],
704
809
  [:added, :after, "Lary"]
705
- ],log.last(6)
810
+ ], log.last(6)
706
811
 
707
- post.people_with_callbacks.build(:first_name => "Ted")
812
+ post.people_with_callbacks.build(first_name: "Ted")
708
813
  assert_equal [
709
814
  [:added, :before, "Ted"],
710
815
  [:added, :after, "Ted"]
711
816
  ], log.last(2)
712
817
 
713
- post.people_with_callbacks.create(:first_name => "Sam")
818
+ post.people_with_callbacks.create(first_name: "Sam")
714
819
  assert_equal [
715
820
  [:added, :before, "Sam"],
716
821
  [:added, :after, "Sam"]
717
822
  ], log.last(2)
718
823
 
719
- post.people_with_callbacks = [people(:michael),people(:david), Person.new(:first_name => "Julian"), Person.create!(:first_name => "Roger")]
824
+ post.people_with_callbacks = [people(:michael), people(:david), Person.new(first_name: "Julian"), Person.create!(first_name: "Roger")]
720
825
  assert_equal((%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort)
721
826
  assert_equal [
722
827
  [:added, :before, "Julian"],
@@ -724,12 +829,24 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
724
829
  [:added, :before, "Roger"],
725
830
  [:added, :after, "Roger"]
726
831
  ], log.last(4)
832
+
833
+ post.people_with_callbacks.build { |person| person.first_name = "Ted" }
834
+ assert_equal [
835
+ [:added, :before, "Ted"],
836
+ [:added, :after, "Ted"]
837
+ ], log.last(2)
838
+
839
+ post.people_with_callbacks.create { |person| person.first_name = "Sam" }
840
+ assert_equal [
841
+ [:added, :before, "Sam"],
842
+ [:added, :after, "Sam"]
843
+ ], log.last(2)
727
844
  end
728
845
 
729
846
  def test_dynamic_find_should_respect_association_include
730
847
  # SQL error in sort clause if :include is not included
731
848
  # 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')
849
+ assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title("Welcome to the weblog")
733
850
  end
734
851
 
735
852
  def test_count_with_include_should_alias_join_table
@@ -745,7 +862,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
745
862
  end
746
863
 
747
864
  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))
865
+ Tagging.create!(taggable_type: "Post", taggable_id: posts(:welcome).id, tag: tags(:misc))
749
866
  assert_not_called(ActiveRecord::Associations::Preloader, :new) do
750
867
  posts(:welcome).misc_tag_ids
751
868
  end
@@ -754,7 +871,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
754
871
  def test_get_ids_for_loaded_associations
755
872
  person = people(:michael)
756
873
  person.posts.reload
757
- assert_queries(0) do
874
+ assert_no_queries do
758
875
  person.post_ids
759
876
  person.post_ids
760
877
  end
@@ -762,9 +879,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
762
879
 
763
880
  def test_get_ids_for_unloaded_associations_does_not_load_them
764
881
  person = people(:michael)
765
- assert !person.posts.loaded?
882
+ assert_not_predicate person.posts, :loaded?
766
883
  assert_equal [posts(:welcome).id, posts(:authorless).id].sort, person.post_ids.sort
767
- assert !person.posts.loaded?
884
+ assert_not_predicate person.posts, :loaded?
768
885
  end
769
886
 
770
887
  def test_association_proxy_transaction_method_starts_transaction_in_association_class
@@ -776,16 +893,16 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
776
893
  end
777
894
 
778
895
  def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
779
- post = Post.create!(:title => "TITLE", :body => "BODY")
896
+ post = Post.create!(title: "TITLE", body: "BODY")
780
897
  assert_equal [], post.author_favorites
781
898
  end
782
899
 
783
900
  def test_has_many_association_through_a_belongs_to_association
784
901
  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)
902
+ post = Post.create!(author: author, title: "TITLE", body: "BODY")
903
+ author.author_favorites.create(favorite_author_id: 1)
904
+ author.author_favorites.create(favorite_author_id: 2)
905
+ author.author_favorites.create(favorite_author_id: 3)
789
906
  assert_equal post.author.author_favorites, post.author_favorites
790
907
  end
791
908
 
@@ -809,37 +926,37 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
809
926
 
810
927
  def test_modifying_has_many_through_has_one_reflection_should_raise
811
928
  [
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) },
929
+ lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(body: "Gorp!", post_id: 1011), VerySpecialComment.create!(body: "Eep!", post_id: 1012)] },
930
+ lambda { authors(:david).very_special_comments << VerySpecialComment.create!(body: "Hoohah!", post_id: 1013) },
814
931
  lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
815
- ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
932
+ ].each { |block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
816
933
  end
817
934
 
818
935
  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)
936
+ sarah = Person.create!(first_name: "Sarah", primary_contact_id: people(:susan).id, gender: "F", number1_fan_id: 1)
937
+ john = Person.create!(first_name: "John", primary_contact_id: sarah.id, gender: "M", number1_fan_id: 1)
821
938
  assert_equal sarah.agents, [john]
822
- assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
939
+ assert_equal people(:susan).agents.flat_map(&:agents).sort, people(:susan).agents_of_agents.sort
823
940
  end
824
941
 
825
942
  def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
826
- Categorization.create(:author => authors(:mary), :named_category_name => categories(:general).name)
943
+ Categorization.create(author: authors(:mary), named_category_name: categories(:general).name)
827
944
  assert_equal categories(:general), authors(:mary).named_categories.first
828
945
  end
829
946
 
830
947
  def test_collection_build_with_nonstandard_primary_key_on_belongs_to
831
948
  author = authors(:mary)
832
- category = author.named_categories.build(:name => "Primary")
949
+ category = author.named_categories.build(name: "Primary")
833
950
  author.save
834
- assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
835
- assert author.named_categories.reload.include?(category)
951
+ assert Categorization.exists?(author_id: author.id, named_category_name: category.name)
952
+ assert_includes author.named_categories.reload, category
836
953
  end
837
954
 
838
955
  def test_collection_create_with_nonstandard_primary_key_on_belongs_to
839
956
  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)
957
+ category = author.named_categories.create(name: "Primary")
958
+ assert Categorization.exists?(author_id: author.id, named_category_name: category.name)
959
+ assert_includes author.named_categories.reload, category
843
960
  end
844
961
 
845
962
  def test_collection_exists
@@ -851,10 +968,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
851
968
 
852
969
  def test_collection_delete_with_nonstandard_primary_key_on_belongs_to
853
970
  author = authors(:mary)
854
- category = author.named_categories.create(:name => "Primary")
971
+ category = author.named_categories.create(name: "Primary")
855
972
  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?
973
+ assert_not Categorization.exists?(author_id: author.id, named_category_name: category.name)
974
+ assert_empty author.named_categories.reload
858
975
  end
859
976
 
860
977
  def test_collection_singular_ids_getter_with_string_primary_keys
@@ -871,6 +988,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
871
988
  assert_equal [dev], company.developers
872
989
  end
873
990
 
991
+ def test_collection_singular_ids_setter_with_required_type_cast
992
+ company = companies(:rails_core)
993
+ dev = Developer.first
994
+
995
+ company.developer_ids = [dev.id.to_s]
996
+ assert_equal [dev], company.developers
997
+ end
998
+
874
999
  def test_collection_singular_ids_setter_with_string_primary_keys
875
1000
  assert_nothing_raised do
876
1001
  book = books(:awdr)
@@ -880,34 +1005,35 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
880
1005
  book.subscriber_ids = []
881
1006
  assert_equal [], book.subscribers.reload
882
1007
  end
883
-
884
1008
  end
885
1009
 
886
1010
  def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
887
1011
  company = companies(:rails_core)
888
1012
  ids = [Developer.first.id, -9999]
889
1013
  e = assert_raises(ActiveRecord::RecordNotFound) { company.developer_ids = ids }
890
- assert_match(/Couldn't find all Developers with 'id'/, e.message)
1014
+ msg = "Couldn't find all Developers with 'id': (1, -9999) (found 1 results, but was looking for 2). Couldn't find Developer with id -9999."
1015
+ assert_equal(msg, e.message)
891
1016
  end
892
1017
 
893
1018
  def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set
894
1019
  author = authors(:david)
895
1020
  ids = [categories(:general).name, "Unknown"]
896
1021
  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
1022
+ msg = "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2). Couldn't find Category with name Unknown."
1023
+ assert_equal msg, e.message
898
1024
  end
899
1025
 
900
1026
  def test_build_a_model_from_hm_through_association_with_where_clause
901
- assert_nothing_raised { books(:awdr).subscribers.where(:nick => "marklazz").build }
1027
+ assert_nothing_raised { books(:awdr).subscribers.where(nick: "marklazz").build }
902
1028
  end
903
1029
 
904
1030
  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
1031
+ new_subscriber = books(:awdr).subscribers.where(nick: "marklazz").build
906
1032
  assert_equal new_subscriber.nick, "marklazz"
907
1033
  end
908
1034
 
909
1035
  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
1036
+ new_subscriber = books(:awdr).subscribers.where(nick: "marklazz").where(name: "Marcelo Giorgi").build
911
1037
  assert_equal new_subscriber.nick, "marklazz"
912
1038
  assert_equal new_subscriber.name, "Marcelo Giorgi"
913
1039
  end
@@ -916,19 +1042,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
916
1042
  person = Person.new
917
1043
  reference = person.references.build
918
1044
  job = reference.build_job
919
- assert person.jobs.include?(job)
1045
+ assert_includes person.jobs, job
920
1046
  end
921
1047
 
922
1048
  def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
923
1049
  author = Author.new
924
1050
  post = author.posts.build
925
1051
  comment = post.comments.build
926
- assert author.comments.include?(comment)
1052
+ assert_includes author.comments, comment
927
1053
  end
928
1054
 
929
1055
  def test_through_association_readonly_should_be_false
930
- assert !people(:michael).posts.first.readonly?
931
- assert !people(:michael).posts.to_a.first.readonly?
1056
+ assert_not_predicate people(:michael).posts.first, :readonly?
1057
+ assert_not_predicate people(:michael).posts.to_a.first, :readonly?
932
1058
  end
933
1059
 
934
1060
  def test_can_update_through_association
@@ -937,10 +1063,50 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
937
1063
  end
938
1064
  end
939
1065
 
1066
+ def test_has_many_through_with_source_scope
1067
+ expected = [readers(:michael_welcome).becomes(LazyReader)]
1068
+ assert_equal expected, Author.first.lazy_readers_skimmers_or_not
1069
+ assert_equal expected, Author.preload(:lazy_readers_skimmers_or_not).first.lazy_readers_skimmers_or_not
1070
+ assert_equal expected, Author.eager_load(:lazy_readers_skimmers_or_not).first.lazy_readers_skimmers_or_not
1071
+ end
1072
+
1073
+ def test_has_many_through_with_through_scope_with_includes
1074
+ expected = [readers(:bob_welcome).becomes(LazyReader)]
1075
+ assert_equal expected, Author.last.lazy_readers_skimmers_or_not_2
1076
+ assert_equal expected, Author.preload(:lazy_readers_skimmers_or_not_2).last.lazy_readers_skimmers_or_not_2
1077
+ assert_equal expected, Author.eager_load(:lazy_readers_skimmers_or_not_2).last.lazy_readers_skimmers_or_not_2
1078
+ end
1079
+
1080
+ def test_has_many_through_with_through_scope_with_joins
1081
+ expected = [readers(:bob_welcome).becomes(LazyReader)]
1082
+ assert_equal expected, Author.last.lazy_readers_skimmers_or_not_3
1083
+ assert_equal expected, Author.preload(:lazy_readers_skimmers_or_not_3).last.lazy_readers_skimmers_or_not_3
1084
+ assert_equal expected, Author.eager_load(:lazy_readers_skimmers_or_not_3).last.lazy_readers_skimmers_or_not_3
1085
+ end
1086
+
1087
+ def test_duplicated_has_many_through_with_through_scope_with_joins
1088
+ Categorization.create!(author: authors(:david), post: posts(:thinking), category: categories(:technology))
1089
+
1090
+ expected = [categorizations(:david_welcome_general)]
1091
+ assert_equal expected, Author.preload(:general_posts, :general_categorizations).first.general_categorizations
1092
+ assert_equal expected, Author.eager_load(:general_posts, :general_categorizations).first.general_categorizations
1093
+
1094
+ expected = [posts(:welcome)]
1095
+ assert_equal expected, Author.preload(:general_categorizations, :general_posts).first.general_posts
1096
+ assert_equal expected, Author.eager_load(:general_categorizations, :general_posts).first.general_posts
1097
+ end
1098
+
1099
+ def test_has_many_through_polymorphic_with_rewhere
1100
+ post = TaggedPost.create!(title: "Tagged", body: "Post")
1101
+ tag = post.tags.create!(name: "Tag")
1102
+ assert_equal [tag], TaggedPost.preload(:tags).last.tags
1103
+ assert_equal [tag], TaggedPost.eager_load(:tags).last.tags
1104
+ end
1105
+
940
1106
  def test_has_many_through_polymorphic_with_primary_key_option
941
1107
  assert_equal [categories(:general)], authors(:david).essay_categories
942
1108
 
943
- authors = Author.joins(:essay_categories).where('categories.id' => categories(:general).id)
1109
+ authors = Author.joins(:essay_categories).where("categories.id" => categories(:general).id)
944
1110
  assert_equal authors(:david), authors.first
945
1111
 
946
1112
  assert_equal [owners(:blackbeard)], authors(:david).essay_owners
@@ -952,7 +1118,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
952
1118
  def test_has_many_through_with_primary_key_option
953
1119
  assert_equal [categories(:general)], authors(:david).essay_categories_2
954
1120
 
955
- authors = Author.joins(:essay_categories_2).where('categories.id' => categories(:general).id)
1121
+ authors = Author.joins(:essay_categories_2).where("categories.id" => categories(:general).id)
956
1122
  assert_equal authors(:david), authors.first
957
1123
  end
958
1124
 
@@ -964,30 +1130,30 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
964
1130
  end
965
1131
 
966
1132
  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
1133
+ assert_equal posts(:welcome).comments.order("id").to_a, authors(:david).comments_on_first_posts
968
1134
  end
969
1135
 
970
1136
  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
1137
+ category = authors(:david).special_categories.create(name: "Foo")
1138
+ assert_equal 1, category.categorizations.where(special: true).count
973
1139
  end
974
1140
 
975
1141
  def test_joining_has_many_through_with_distinct
976
- mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first
1142
+ mary = Author.joins(:unique_categorized_posts).where(id: authors(:mary).id).first
977
1143
  assert_equal 1, mary.unique_categorized_posts.length
978
1144
  assert_equal 1, mary.unique_categorized_post_ids.length
979
1145
  end
980
1146
 
981
1147
  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)
1148
+ posts = Post.joins(:author_categorizations).order("posts.id").
1149
+ where("categorizations.id" => categorizations(:mary_thinking_sti).id)
984
1150
 
985
1151
  assert_equal [posts(:eager_other), posts(:misc_by_mary), posts(:other_by_mary)], posts
986
1152
  end
987
1153
 
988
1154
  def test_select_chosen_fields_only
989
1155
  author = authors(:david)
990
- assert_equal ['body', 'id'].sort, author.comments.select('comments.body').first.attributes.keys.sort
1156
+ assert_equal ["body", "id"].sort, author.comments.select("comments.body").first.attributes.keys.sort
991
1157
  end
992
1158
 
993
1159
  def test_get_has_many_through_belongs_to_ids_with_conditions
@@ -1010,12 +1176,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1010
1176
  post.author_categorizations
1011
1177
  proxy = post.send(:association_instance_get, :author_categorizations)
1012
1178
 
1013
- assert !proxy.stale_target?
1179
+ assert_not_predicate proxy, :stale_target?
1014
1180
  assert_equal authors(:mary).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
1015
1181
 
1016
1182
  post.author_id = authors(:david).id
1017
1183
 
1018
- assert proxy.stale_target?
1184
+ assert_predicate proxy, :stale_target?
1019
1185
  assert_equal authors(:david).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
1020
1186
  end
1021
1187
 
@@ -1030,15 +1196,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1030
1196
  post = posts(:welcome)
1031
1197
  address = author_addresses(:david_address)
1032
1198
 
1033
- assert post.author_addresses.include?(address)
1199
+ assert_includes post.author_addresses, address
1034
1200
  post.author_addresses.delete(address)
1035
- assert post[:author_count].nil?
1201
+ assert_predicate post[:author_count], :nil?
1036
1202
  end
1037
1203
 
1038
1204
  def test_primary_key_option_on_source
1039
1205
  post = posts(:welcome)
1040
1206
  category = categories(:general)
1041
- Categorization.create!(:post_id => post.id, :named_category_name => category.name)
1207
+ Categorization.create!(post_id: post.id, named_category_name: category.name)
1042
1208
 
1043
1209
  assert_equal [category], post.named_categories
1044
1210
  assert_equal [category.name], post.named_category_ids # checks when target loaded
@@ -1047,48 +1213,48 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1047
1213
 
1048
1214
  def test_create_should_not_raise_exception_when_join_record_has_errors
1049
1215
  repair_validations(Categorization) do
1050
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1051
- Category.create(:name => 'Fishing', :authors => [Author.first])
1216
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
1217
+ assert_deprecated { Category.create(name: "Fishing", authors: [Author.first]) }
1052
1218
  end
1053
1219
  end
1054
1220
 
1055
1221
  def test_assign_array_to_new_record_builds_join_records
1056
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
1222
+ c = Category.new(name: "Fishing", authors: [Author.first])
1057
1223
  assert_equal 1, c.categorizations.size
1058
1224
  end
1059
1225
 
1060
1226
  def test_create_bang_should_raise_exception_when_join_record_has_errors
1061
1227
  repair_validations(Categorization) do
1062
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1228
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
1063
1229
  assert_raises(ActiveRecord::RecordInvalid) do
1064
- Category.create!(:name => 'Fishing', :authors => [Author.first])
1230
+ assert_deprecated { Category.create!(name: "Fishing", authors: [Author.first]) }
1065
1231
  end
1066
1232
  end
1067
1233
  end
1068
1234
 
1069
1235
  def test_save_bang_should_raise_exception_when_join_record_has_errors
1070
1236
  repair_validations(Categorization) do
1071
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
1072
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
1237
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
1238
+ c = Category.new(name: "Fishing", authors: [Author.first])
1073
1239
  assert_raises(ActiveRecord::RecordInvalid) do
1074
- c.save!
1240
+ assert_deprecated { c.save! }
1075
1241
  end
1076
1242
  end
1077
1243
  end
1078
1244
 
1079
1245
  def test_save_returns_falsy_when_join_record_has_errors
1080
1246
  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
1247
+ Categorization.validate { |r| r.errors[:base] << "Invalid Categorization" }
1248
+ c = Category.new(name: "Fishing", authors: [Author.first])
1249
+ assert_deprecated { assert_not c.save }
1084
1250
  end
1085
1251
  end
1086
1252
 
1087
1253
  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
1254
+ person = Person.create!(first_name: "Gaga")
1255
+ person = Person.where(id: person.id).where("readers.id = 1 or 1=1").references(:readers).includes(:posts).to_a.first
1090
1256
 
1091
- assert person.posts.loaded?, 'person.posts should be loaded'
1257
+ assert person.posts.loaded?, "person.posts should be loaded"
1092
1258
  assert_equal [], person.posts
1093
1259
  end
1094
1260
 
@@ -1109,13 +1275,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1109
1275
  end
1110
1276
 
1111
1277
  def test_has_many_through_with_polymorphic_source
1112
- post = tags(:general).tagged_posts.create! :title => "foo", :body => "bar"
1278
+ post = tags(:general).tagged_posts.create! title: "foo", body: "bar"
1113
1279
  assert_equal [tags(:general)], post.reload.tags
1114
1280
  end
1115
1281
 
1116
1282
  def test_has_many_through_obeys_order_on_through_association
1117
1283
  owner = owners(:blackbeard)
1118
- assert owner.toys.to_sql.include?("pets.name desc")
1284
+ assert_includes owner.toys.to_sql, "pets.name desc"
1119
1285
  assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name }
1120
1286
  end
1121
1287
 
@@ -1148,9 +1314,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1148
1314
  def test_has_many_through_associations_on_new_records_use_null_relations
1149
1315
  person = Person.new
1150
1316
 
1151
- assert_no_queries(ignore_none: false) do
1317
+ assert_no_queries do
1152
1318
  assert_equal [], person.posts
1153
- assert_equal [], person.posts.where(body: 'omg')
1319
+ assert_equal [], person.posts.where(body: "omg")
1154
1320
  assert_equal [], person.posts.pluck(:body)
1155
1321
  assert_equal 0, person.posts.sum(:tags_count)
1156
1322
  assert_equal 0, person.posts.count
@@ -1182,9 +1348,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1182
1348
  end
1183
1349
 
1184
1350
  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
1351
+ post = Post.create!(title: "Beaches", body: "I like beaches!")
1352
+ Reader.create! person: people(:david), post: post
1353
+ LazyReader.create! person: people(:susan), post: post
1188
1354
 
1189
1355
  assert_equal 2, post.people.to_a.size
1190
1356
  assert_equal 1, post.lazy_people.to_a.size
@@ -1194,8 +1360,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1194
1360
  end
1195
1361
 
1196
1362
  def test_has_many_through_add_with_sti_middle_relation
1197
- club = SuperClub.create!(name: 'Fight Club')
1198
- member = Member.create!(name: 'Tyler Durden')
1363
+ club = SuperClub.create!(name: "Fight Club")
1364
+ member = Member.create!(name: "Tyler Durden")
1199
1365
 
1200
1366
  club.members << member
1201
1367
  assert_equal 1, SuperMembership.where(member_id: member.id, club_id: club.id).count
@@ -1216,12 +1382,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1216
1382
  assert_nil Club.new.special_favourites.distinct_value
1217
1383
  end
1218
1384
 
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
1385
  def test_has_many_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
1226
1386
  member = Member.create!
1227
1387
  club = Club.create!
@@ -1250,6 +1410,33 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1250
1410
  TenantMembership.current_member = nil
1251
1411
  end
1252
1412
 
1413
+ def test_has_many_through_with_scope_that_has_joined_same_table_with_parent_relation
1414
+ assert_equal authors(:david), Author.joins(:comments_for_first_author).take
1415
+ end
1416
+
1417
+ def test_has_many_through_with_left_joined_same_table_with_through_table
1418
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).comments.left_joins(:post)
1419
+ end
1420
+
1421
+ def test_has_many_through_with_unscope_should_affect_to_through_scope
1422
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments
1423
+ end
1424
+
1425
+ def test_has_many_through_with_scope_should_accept_string_and_hash_join
1426
+ assert_equal authors(:david), Author.joins({ comments_for_first_author: :post }, "inner join posts posts_alias on authors.id = posts_alias.author_id").eager_load(:categories).take
1427
+ end
1428
+
1429
+ def test_has_many_through_with_scope_should_respect_table_alias
1430
+ family = Family.create!
1431
+ users = 3.times.map { User.create! }
1432
+ FamilyTree.create!(member: users[0], family: family)
1433
+ FamilyTree.create!(member: users[1], family: family)
1434
+ FamilyTree.create!(member: users[2], family: family, token: "wat")
1435
+
1436
+ assert_equal 2, users[0].family_members.to_a.size
1437
+ assert_equal 0, users[2].family_members.to_a.size
1438
+ end
1439
+
1253
1440
  def test_through_scope_is_affected_by_unscoping
1254
1441
  author = authors(:david)
1255
1442
 
@@ -1268,4 +1455,126 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
1268
1455
  assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
1269
1456
  end
1270
1457
  end
1458
+
1459
+ def test_incorrectly_ordered_through_associations
1460
+ assert_raises(ActiveRecord::HasManyThroughOrderError) do
1461
+ DeveloperWithIncorrectlyOrderedHasManyThrough.create(
1462
+ companies: [Company.create]
1463
+ )
1464
+ end
1465
+ end
1466
+
1467
+ def test_has_many_through_update_ids_with_conditions
1468
+ author = Author.create!(name: "Bill")
1469
+ category = categories(:general)
1470
+
1471
+ author.update(
1472
+ special_categories_with_condition_ids: [category.id],
1473
+ nonspecial_categories_with_condition_ids: [category.id]
1474
+ )
1475
+
1476
+ assert_equal [category.id], author.special_categories_with_condition_ids
1477
+ assert_equal [category.id], author.nonspecial_categories_with_condition_ids
1478
+
1479
+ author.update(nonspecial_categories_with_condition_ids: [])
1480
+ author.reload
1481
+
1482
+ assert_equal [category.id], author.special_categories_with_condition_ids
1483
+ assert_equal [], author.nonspecial_categories_with_condition_ids
1484
+ end
1485
+
1486
+ def test_single_has_many_through_association_with_unpersisted_parent_instance
1487
+ post_with_single_has_many_through = Class.new(Post) do
1488
+ def self.name; "PostWithSingleHasManyThrough"; end
1489
+ has_many :subscriptions, through: :author
1490
+ end
1491
+ post = post_with_single_has_many_through.new
1492
+
1493
+ post.author = authors(:mary)
1494
+ book1 = Book.create!(name: "essays on single has many through associations 1")
1495
+ post.author.books << book1
1496
+ subscription1 = Subscription.first
1497
+ book1.subscriptions << subscription1
1498
+ assert_equal [subscription1], post.subscriptions.to_a
1499
+
1500
+ post.author = authors(:bob)
1501
+ book2 = Book.create!(name: "essays on single has many through associations 2")
1502
+ post.author.books << book2
1503
+ subscription2 = Subscription.second
1504
+ book2.subscriptions << subscription2
1505
+ assert_equal [subscription2], post.subscriptions.to_a
1506
+ end
1507
+
1508
+ def test_nested_has_many_through_association_with_unpersisted_parent_instance
1509
+ post_with_nested_has_many_through = Class.new(Post) do
1510
+ def self.name; "PostWithNestedHasManyThrough"; end
1511
+ has_many :books, through: :author
1512
+ has_many :subscriptions, through: :books
1513
+ end
1514
+ post = post_with_nested_has_many_through.new
1515
+
1516
+ post.author = authors(:mary)
1517
+ book1 = Book.create!(name: "essays on nested has many through associations 1")
1518
+ post.author.books << book1
1519
+ subscription1 = Subscription.first
1520
+ book1.subscriptions << subscription1
1521
+ assert_equal [subscription1], post.subscriptions.to_a
1522
+
1523
+ post.author = authors(:bob)
1524
+ book2 = Book.create!(name: "essays on nested has many through associations 2")
1525
+ post.author.books << book2
1526
+ subscription2 = Subscription.second
1527
+ book2.subscriptions << subscription2
1528
+ assert_equal [subscription2], post.subscriptions.to_a
1529
+ end
1530
+
1531
+ def test_child_is_visible_to_join_model_in_add_association_callbacks
1532
+ [:before_add, :after_add].each do |callback_name|
1533
+ sentient_treasure = Class.new(Treasure) do
1534
+ def self.name; "SentientTreasure"; end
1535
+
1536
+ has_many :pet_treasures, foreign_key: :treasure_id, callback_name => :check_pet!
1537
+ has_many :pets, through: :pet_treasures
1538
+
1539
+ def check_pet!(added)
1540
+ raise "No pet!" if added.pet.nil?
1541
+ end
1542
+ end
1543
+
1544
+ treasure = sentient_treasure.new
1545
+ assert_nothing_raised { treasure.pets << pets(:mochi) }
1546
+ end
1547
+ end
1548
+
1549
+ def test_circular_autosave_association_correctly_saves_multiple_records
1550
+ cs180 = Seminar.new(name: "CS180")
1551
+ fall = Session.new(name: "Fall")
1552
+ sections = [
1553
+ cs180.sections.build(short_name: "A"),
1554
+ cs180.sections.build(short_name: "B"),
1555
+ ]
1556
+ fall.sections << sections
1557
+ fall.save!
1558
+ fall.reload
1559
+ assert_equal sections, fall.sections.sort_by(&:id)
1560
+ end
1561
+
1562
+ private
1563
+ def make_model(name)
1564
+ Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
1565
+ end
1566
+
1567
+ def make_no_pk_hm_t
1568
+ lesson = make_model "Lesson"
1569
+ student = make_model "Student"
1570
+
1571
+ lesson_student = make_model "LessonStudent"
1572
+ lesson_student.table_name = "lessons_students"
1573
+
1574
+ lesson_student.belongs_to :lesson, anonymous_class: lesson
1575
+ lesson_student.belongs_to :student, anonymous_class: student
1576
+ lesson.has_many :lesson_students, anonymous_class: lesson_student
1577
+ lesson.has_many :students, through: :lesson_students, anonymous_class: student
1578
+ [lesson, lesson_student, student]
1579
+ end
1271
1580
  end