ibm_db 5.6.1-arm64-darwin-24

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