activerecord 6.1.7 → 7.2.2

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 (333) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +616 -1290
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +19 -8
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  30. data/lib/active_record/associations/join_dependency.rb +28 -20
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +429 -522
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +1 -5
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +57 -54
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +19 -35
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -143
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +310 -253
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +58 -0
  165. data/lib/active_record/enum.rb +170 -62
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +59 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +145 -158
  199. data/lib/active_record/nested_attributes.rb +61 -23
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +18 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +229 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +332 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +200 -65
  216. data/lib/active_record/relation/calculations.rb +301 -112
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +870 -163
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +6 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +288 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +65 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/sqlite.rb +25 -0
  317. data/lib/arel/visitors/to_sql.rb +170 -36
  318. data/lib/arel/visitors/visitor.rb +2 -2
  319. data/lib/arel.rb +23 -4
  320. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  321. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  322. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  323. data/lib/rails/generators/active_record/migration.rb +3 -1
  324. data/lib/rails/generators/active_record/model/USAGE +113 -0
  325. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  326. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  328. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  329. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  330. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  331. metadata +103 -17
  332. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  333. data/lib/active_record/null_relation.rb +0 -67
@@ -6,7 +6,7 @@ require "active_record/fixture_set/model_metadata"
6
6
  module ActiveRecord
7
7
  class FixtureSet
8
8
  class TableRows # :nodoc:
9
- def initialize(table_name, model_class:, fixtures:, config:)
9
+ def initialize(table_name, model_class:, fixtures:)
10
10
  @model_class = model_class
11
11
 
12
12
  # track any join tables we need to insert later
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  # ensure this table is loaded before any HABTM associations
16
16
  @tables[table_name] = nil
17
17
 
18
- build_table_rows_from(table_name, fixtures, config)
18
+ build_table_rows_from(table_name, fixtures)
19
19
  end
20
20
 
21
21
  attr_reader :tables, :model_class
@@ -29,8 +29,8 @@ module ActiveRecord
29
29
  end
30
30
 
31
31
  private
32
- def build_table_rows_from(table_name, fixtures, config)
33
- now = config.default_timezone == :utc ? Time.now.utc : Time.now
32
+ def build_table_rows_from(table_name, fixtures)
33
+ now = ActiveRecord.default_timezone == :utc ? Time.now.utc : Time.now
34
34
 
35
35
  @tables[table_name] = fixtures.map do |label, fixture|
36
36
  TableRow.new(
@@ -6,20 +6,23 @@ require "zlib"
6
6
  require "set"
7
7
  require "active_support/dependencies"
8
8
  require "active_support/core_ext/digest/uuid"
9
- require "active_record/fixture_set/file"
10
- require "active_record/fixture_set/render_context"
11
- require "active_record/fixture_set/table_rows"
12
9
  require "active_record/test_fixtures"
13
10
 
14
11
  module ActiveRecord
15
- class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
12
+ class FixtureClassNotFound < ActiveRecord::ActiveRecordError # :nodoc:
16
13
  end
17
14
 
15
+ # = Active Record \Fixtures
16
+ #
18
17
  # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
19
18
  #
20
- # They are stored in YAML files, one file per model, which are placed in the directory
21
- # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
22
- # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
19
+ # They are stored in YAML files, one file per model, which are by default placed in either
20
+ # <tt><your-rails-app>/test/fixtures/</tt> or in the <tt>test/fixtures</tt>
21
+ # folder under any of your application's engines.
22
+ #
23
+ # The location can also be changed with ActiveSupport::TestCase.fixture_paths=,
24
+ # once you have <tt>require "rails/test_help"</tt> in your +test_helper.rb+.
25
+ #
23
26
  # The fixture file ends with the +.yml+ file extension, for example:
24
27
  # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
25
28
  #
@@ -35,14 +38,21 @@ module ActiveRecord
35
38
  # name: Google
36
39
  # url: http://www.google.com
37
40
  #
38
- # This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
41
+ # This fixture file includes two fixtures. Each YAML fixture (i.e. record) is given a name and
39
42
  # is followed by an indented list of key/value pairs in the "key: value" format. Records are
40
43
  # separated by a blank line for your viewing pleasure.
41
44
  #
42
- # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
43
- # See https://yaml.org/type/omap.html
44
- # for the specification. You will need ordered fixtures when you have foreign key constraints
45
- # on keys in the same table. This is commonly needed for tree structures. Example:
45
+ # == Ordering
46
+ #
47
+ # Fixtures by default are unordered. This is because the maps in YAML are unordered.
48
+ #
49
+ # If you want ordered fixtures, use the omap YAML type.
50
+ # See https://yaml.org/type/omap.html for the specification.
51
+ #
52
+ # You will need ordered fixtures when you have foreign key constraints
53
+ # on keys in the same table. This is commonly needed for tree structures.
54
+ #
55
+ # For example:
46
56
  #
47
57
  # --- !omap
48
58
  # - parent:
@@ -54,7 +64,7 @@ module ActiveRecord
54
64
  # parent_id: 1
55
65
  # title: Child
56
66
  #
57
- # = Using Fixtures in Test Cases
67
+ # == Using Fixtures in Test Cases
58
68
  #
59
69
  # Since fixtures are a testing construct, we use them in our unit and functional tests. There
60
70
  # are two ways to use the fixtures, but first let's take a look at a sample unit test:
@@ -100,6 +110,12 @@ module ActiveRecord
100
110
  # assert_raise(StandardError) { web_sites(:reddit) }
101
111
  # end
102
112
  #
113
+ # If the model names conflicts with a +TestCase+ methods, you can use the generic +fixture+ accessor
114
+ #
115
+ # test "generic find" do
116
+ # assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).name
117
+ # end
118
+ #
103
119
  # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
104
120
  # following tests:
105
121
  #
@@ -124,7 +140,7 @@ module ActiveRecord
124
140
  # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
125
141
  # large sets of fixtured data.
126
142
  #
127
- # = Dynamic fixtures with ERB
143
+ # == Dynamic fixtures with \ERB
128
144
  #
129
145
  # Sometimes you don't care about the content of the fixtures as much as you care about the volume.
130
146
  # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
@@ -152,7 +168,7 @@ module ActiveRecord
152
168
  # - define a helper method in <tt>test_helper.rb</tt>
153
169
  # module FixtureFileHelpers
154
170
  # def file_sha(path)
155
- # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
171
+ # OpenSSL::Digest::SHA256.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
156
172
  # end
157
173
  # end
158
174
  # ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
@@ -162,7 +178,7 @@ module ActiveRecord
162
178
  # name: kitten.png
163
179
  # sha: <%= file_sha 'files/kitten.png' %>
164
180
  #
165
- # = Transactional Tests
181
+ # == Transactional Tests
166
182
  #
167
183
  # Test cases can use begin+rollback to isolate their changes to the database instead of having to
168
184
  # delete+insert for every test case.
@@ -198,7 +214,7 @@ module ActiveRecord
198
214
  # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
199
215
  # Use InnoDB, MaxDB, or NDB instead.
200
216
  #
201
- # = Advanced Fixtures
217
+ # == Advanced Fixtures
202
218
  #
203
219
  # Fixtures that don't specify an ID get some extra features:
204
220
  #
@@ -212,7 +228,7 @@ module ActiveRecord
212
228
  # * Fixture label interpolation
213
229
  # * Support for YAML defaults
214
230
  #
215
- # == Stable, Autogenerated IDs
231
+ # === Stable, Autogenerated IDs
216
232
  #
217
233
  # Here, have a monkey fixture:
218
234
  #
@@ -241,13 +257,13 @@ module ActiveRecord
241
257
  # The generated ID for a given label is constant, so we can discover
242
258
  # any fixture's ID without loading anything, as long as we know the label.
243
259
  #
244
- # == Label references for associations (belongs_to, has_one, has_many)
260
+ # === Label references for associations (+belongs_to+, +has_one+, +has_many+)
245
261
  #
246
262
  # Specifying foreign keys in fixtures can be very fragile, not to
247
263
  # mention difficult to read. Since Active Record can figure out the ID of
248
264
  # any fixture from its label, you can specify FK's by label instead of ID.
249
265
  #
250
- # === belongs_to
266
+ # ==== +belongs_to+
251
267
  #
252
268
  # Let's break out some more monkeys and pirates.
253
269
  #
@@ -258,6 +274,8 @@ module ActiveRecord
258
274
  # name: Reginald the Pirate
259
275
  # monkey_id: 1
260
276
  #
277
+ # <code></code>
278
+ #
261
279
  # ### in monkeys.yml
262
280
  #
263
281
  # george:
@@ -275,6 +293,8 @@ module ActiveRecord
275
293
  # name: Reginald the Pirate
276
294
  # monkey: george
277
295
  #
296
+ # <code></code>
297
+ #
278
298
  # ### in monkeys.yml
279
299
  #
280
300
  # george:
@@ -286,7 +306,7 @@ module ActiveRecord
286
306
  # a target *label* for the *association* (monkey: george) rather than
287
307
  # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
288
308
  #
289
- # ==== Polymorphic belongs_to
309
+ # ==== Polymorphic +belongs_to+
290
310
  #
291
311
  # Supporting polymorphic relationships is a little bit more complicated, since
292
312
  # Active Record needs to know what type your association is pointing at. Something
@@ -296,6 +316,8 @@ module ActiveRecord
296
316
  #
297
317
  # belongs_to :eater, polymorphic: true
298
318
  #
319
+ # <code></code>
320
+ #
299
321
  # ### in fruits.yml
300
322
  #
301
323
  # apple:
@@ -311,9 +333,9 @@ module ActiveRecord
311
333
  #
312
334
  # Just provide the polymorphic target type and Active Record will take care of the rest.
313
335
  #
314
- # === has_and_belongs_to_many
336
+ # ==== +has_and_belongs_to_many+ or <tt>has_many :through</tt>
315
337
  #
316
- # Time to give our monkey some fruit.
338
+ # \Time to give our monkey some fruit.
317
339
  #
318
340
  # ### in monkeys.yml
319
341
  #
@@ -321,6 +343,8 @@ module ActiveRecord
321
343
  # id: 1
322
344
  # name: George the Monkey
323
345
  #
346
+ # <code></code>
347
+ #
324
348
  # ### in fruits.yml
325
349
  #
326
350
  # apple:
@@ -335,6 +359,8 @@ module ActiveRecord
335
359
  # id: 3
336
360
  # name: grape
337
361
  #
362
+ # <code></code>
363
+ #
338
364
  # ### in fruits_monkeys.yml
339
365
  #
340
366
  # apple_george:
@@ -358,6 +384,8 @@ module ActiveRecord
358
384
  # name: George the Monkey
359
385
  # fruits: apple, orange, grape
360
386
  #
387
+ # <code></code>
388
+ #
361
389
  # ### in fruits.yml
362
390
  #
363
391
  # apple:
@@ -375,7 +403,7 @@ module ActiveRecord
375
403
  # the fixture's model class and discovers the +has_and_belongs_to_many+
376
404
  # associations.
377
405
  #
378
- # == Autofilled Timestamp Columns
406
+ # === Autofilled \Timestamp Columns
379
407
  #
380
408
  # If your table/model specifies any of Active Record's
381
409
  # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
@@ -383,7 +411,7 @@ module ActiveRecord
383
411
  #
384
412
  # If you've set specific values, they'll be left alone.
385
413
  #
386
- # == Fixture label interpolation
414
+ # === Fixture label interpolation
387
415
  #
388
416
  # The label of the current fixture is always available as a column value:
389
417
  #
@@ -400,14 +428,18 @@ module ActiveRecord
400
428
  # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
401
429
  # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
402
430
  #
403
- # == Support for YAML defaults
431
+ # If the model uses UUID values for identifiers, add the +:uuid+ argument:
432
+ #
433
+ # ActiveRecord::FixtureSet.identify(:boaty_mcboatface, :uuid)
434
+ #
435
+ # === Support for YAML defaults
404
436
  #
405
437
  # You can set and reuse defaults in your fixtures YAML file.
406
438
  # This is the same technique used in the +database.yml+ file to specify
407
439
  # defaults:
408
440
  #
409
441
  # DEFAULTS: &DEFAULTS
410
- # created_on: <%= 3.weeks.ago.to_s(:db) %>
442
+ # created_on: <%= 3.weeks.ago.to_fs(:db) %>
411
443
  #
412
444
  # first:
413
445
  # name: Smurf
@@ -426,7 +458,7 @@ module ActiveRecord
426
458
  # _fixture:
427
459
  # ignore:
428
460
  # - base
429
- # # or use "ignore: base" when there is only one fixture needs to be ignored.
461
+ # # or use "ignore: base" when there is only one fixture that needs to be ignored.
430
462
  #
431
463
  # base: &base
432
464
  # admin: false
@@ -442,6 +474,45 @@ module ActiveRecord
442
474
  # In the above example, 'base' will be ignored when creating fixtures.
443
475
  # This can be used for common attributes inheriting.
444
476
  #
477
+ # == Composite Primary Key Fixtures
478
+ #
479
+ # Fixtures for composite primary key tables are fairly similar to normal tables.
480
+ # When using an id column, the column may be omitted as usual:
481
+ #
482
+ # # app/models/book.rb
483
+ # class Book < ApplicationRecord
484
+ # self.primary_key = [:author_id, :id]
485
+ # belongs_to :author
486
+ # end
487
+ #
488
+ # <code></code>
489
+ #
490
+ # # books.yml
491
+ # alices_adventure_in_wonderland:
492
+ # author_id: <%= ActiveRecord::FixtureSet.identify(:lewis_carroll) %>
493
+ # title: "Alice's Adventures in Wonderland"
494
+ #
495
+ # However, in order to support composite primary key relationships,
496
+ # you must use the `composite_identify` method:
497
+ #
498
+ # # app/models/book_orders.rb
499
+ # class BookOrder < ApplicationRecord
500
+ # self.primary_key = [:shop_id, :id]
501
+ # belongs_to :order, foreign_key: [:shop_id, :order_id]
502
+ # belongs_to :book, foreign_key: [:author_id, :book_id]
503
+ # end
504
+ #
505
+ # <code></code>
506
+ #
507
+ # # book_orders.yml
508
+ # alices_adventure_in_wonderland_in_books:
509
+ # author: lewis_carroll
510
+ # book_id: <%= ActiveRecord::FixtureSet.composite_identify(
511
+ # :alices_adventure_in_wonderland, Book.primary_key)[:id] %>
512
+ # shop: book_store
513
+ # order_id: <%= ActiveRecord::FixtureSet.composite_identify(
514
+ # :books, Order.primary_key)[:id] %>
515
+ #
445
516
  # == Configure the fixture model class
446
517
  #
447
518
  # It's possible to set the fixture's model class directly in the YAML file.
@@ -456,6 +527,10 @@ module ActiveRecord
456
527
  #
457
528
  # Any fixtures labeled "_fixture" are safely ignored.
458
529
  class FixtureSet
530
+ require "active_record/fixture_set/file"
531
+ require "active_record/fixture_set/render_context"
532
+ require "active_record/fixture_set/table_rows"
533
+
459
534
  #--
460
535
  # An instance of FixtureSet is normally stored in a single YAML file and
461
536
  # possibly in a folder with the same name.
@@ -467,39 +542,6 @@ module ActiveRecord
467
542
 
468
543
  cattr_accessor :all_loaded_fixtures, default: {}
469
544
 
470
- class ClassCache
471
- def initialize(class_names, config)
472
- @class_names = class_names.stringify_keys
473
- @config = config
474
-
475
- # Remove string values that aren't constants or subclasses of AR
476
- @class_names.delete_if do |klass_name, klass|
477
- !insert_class(@class_names, klass_name, klass)
478
- end
479
- end
480
-
481
- def [](fs_name)
482
- @class_names.fetch(fs_name) do
483
- klass = default_fixture_model(fs_name, @config).safe_constantize
484
- insert_class(@class_names, fs_name, klass)
485
- end
486
- end
487
-
488
- private
489
- def insert_class(class_names, name, klass)
490
- # We only want to deal with AR objects.
491
- if klass && klass < ActiveRecord::Base
492
- class_names[name] = klass
493
- else
494
- class_names[name] = nil
495
- end
496
- end
497
-
498
- def default_fixture_model(fs_name, config)
499
- ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
500
- end
501
- end
502
-
503
545
  class << self
504
546
  def default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
505
547
  config.pluralize_table_names ?
@@ -517,24 +559,24 @@ module ActiveRecord
517
559
  @@all_cached_fixtures.clear
518
560
  end
519
561
 
520
- def cache_for_connection(connection)
521
- @@all_cached_fixtures[connection]
562
+ def cache_for_connection_pool(connection_pool)
563
+ @@all_cached_fixtures[connection_pool]
522
564
  end
523
565
 
524
- def fixture_is_cached?(connection, table_name)
525
- cache_for_connection(connection)[table_name]
566
+ def fixture_is_cached?(connection_pool, table_name)
567
+ cache_for_connection_pool(connection_pool)[table_name]
526
568
  end
527
569
 
528
- def cached_fixtures(connection, keys_to_fetch = nil)
570
+ def cached_fixtures(connection_pool, keys_to_fetch = nil)
529
571
  if keys_to_fetch
530
- cache_for_connection(connection).values_at(*keys_to_fetch)
572
+ cache_for_connection_pool(connection_pool).values_at(*keys_to_fetch)
531
573
  else
532
- cache_for_connection(connection).values
574
+ cache_for_connection_pool(connection_pool).values
533
575
  end
534
576
  end
535
577
 
536
- def cache_fixtures(connection, fixtures_map)
537
- cache_for_connection(connection).update(fixtures_map)
578
+ def cache_fixtures(connection_pool, fixtures_map)
579
+ cache_for_connection_pool(connection_pool).update(fixtures_map)
538
580
  end
539
581
 
540
582
  def instantiate_fixtures(object, fixture_set, load_instances = true)
@@ -552,31 +594,30 @@ module ActiveRecord
552
594
  end
553
595
  end
554
596
 
555
- def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
597
+ def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
556
598
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
557
- class_names = ClassCache.new class_names, config
558
-
559
- # FIXME: Apparently JK uses this.
560
- connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
599
+ class_names.stringify_keys!
561
600
 
601
+ connection_pool = config.connection_pool
562
602
  fixture_files_to_read = fixture_set_names.reject do |fs_name|
563
- fixture_is_cached?(connection.call, fs_name)
603
+ fixture_is_cached?(connection_pool, fs_name)
564
604
  end
565
605
 
566
606
  if fixture_files_to_read.any?
567
607
  fixtures_map = read_and_insert(
568
- fixtures_directory,
608
+ Array(fixtures_directories),
569
609
  fixture_files_to_read,
570
610
  class_names,
571
- connection,
611
+ connection_pool,
572
612
  )
573
- cache_fixtures(connection.call, fixtures_map)
613
+ cache_fixtures(connection_pool, fixtures_map)
574
614
  end
575
- cached_fixtures(connection.call, fixture_set_names)
615
+ cached_fixtures(connection_pool, fixture_set_names)
576
616
  end
577
617
 
578
618
  # Returns a consistent, platform-independent identifier for +label+.
579
- # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
619
+ #
620
+ # \Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
580
621
  def identify(label, column_type = :integer)
581
622
  if column_type == :uuid
582
623
  Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
@@ -585,48 +626,54 @@ module ActiveRecord
585
626
  end
586
627
  end
587
628
 
588
- def signed_global_id(fixture_set_name, label, column_type: :integer, **options)
589
- identifier = identify(label, column_type)
590
- model_name = default_fixture_model_name(fixture_set_name)
591
- uri = URI::GID.build([GlobalID.app, model_name, identifier, {}])
592
-
593
- SignedGlobalID.new(uri, **options)
629
+ # Returns a consistent, platform-independent hash representing a mapping
630
+ # between the label and the subcomponents of the provided composite key.
631
+ #
632
+ # Example:
633
+ #
634
+ # composite_identify("label", [:a, :b, :c]) # => { a: hash_1, b: hash_2, c: hash_3 }
635
+ def composite_identify(label, key)
636
+ key
637
+ .index_with
638
+ .with_index { |sub_key, index| (identify(label) << index) % MAX_ID }
639
+ .with_indifferent_access
594
640
  end
595
641
 
596
- # Superclass for the evaluation contexts used by ERB fixtures.
642
+ # Superclass for the evaluation contexts used by \ERB fixtures.
597
643
  def context_class
598
644
  @context_class ||= Class.new
599
645
  end
600
646
 
601
647
  private
602
- def read_and_insert(fixtures_directory, fixture_files, class_names, connection) # :nodoc:
648
+ def read_and_insert(fixtures_directories, fixture_files, class_names, connection_pool) # :nodoc:
603
649
  fixtures_map = {}
650
+ directory_glob = "{#{fixtures_directories.join(",")}}"
604
651
  fixture_sets = fixture_files.map do |fixture_set_name|
605
652
  klass = class_names[fixture_set_name]
606
653
  fixtures_map[fixture_set_name] = new( # ActiveRecord::FixtureSet.new
607
654
  nil,
608
655
  fixture_set_name,
609
656
  klass,
610
- ::File.join(fixtures_directory, fixture_set_name)
657
+ ::File.join(directory_glob, fixture_set_name)
611
658
  )
612
659
  end
613
660
  update_all_loaded_fixtures(fixtures_map)
614
661
 
615
- insert(fixture_sets, connection)
662
+ insert(fixture_sets, connection_pool)
616
663
 
617
664
  fixtures_map
618
665
  end
619
666
 
620
- def insert(fixture_sets, connection) # :nodoc:
621
- fixture_sets_by_connection = fixture_sets.group_by do |fixture_set|
667
+ def insert(fixture_sets, connection_pool) # :nodoc:
668
+ fixture_sets_by_pool = fixture_sets.group_by do |fixture_set|
622
669
  if fixture_set.model_class
623
- fixture_set.model_class.connection
670
+ fixture_set.model_class.connection_pool
624
671
  else
625
- connection.call
672
+ connection_pool
626
673
  end
627
674
  end
628
675
 
629
- fixture_sets_by_connection.each do |conn, set|
676
+ fixture_sets_by_pool.each do |pool, set|
630
677
  table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
631
678
 
632
679
  set.each do |fixture_set|
@@ -635,15 +682,29 @@ module ActiveRecord
635
682
  end
636
683
  end
637
684
 
638
- conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
685
+ pool.with_connection do |conn|
686
+ conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
687
+
688
+ check_all_foreign_keys_valid!(conn)
639
689
 
640
- # Cap primary key sequences to max(pk).
641
- if conn.respond_to?(:reset_pk_sequence!)
642
- set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
690
+ # Cap primary key sequences to max(pk).
691
+ if conn.respond_to?(:reset_pk_sequence!)
692
+ set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
693
+ end
643
694
  end
644
695
  end
645
696
  end
646
697
 
698
+ def check_all_foreign_keys_valid!(conn)
699
+ return unless ActiveRecord.verify_foreign_keys_for_fixtures
700
+
701
+ begin
702
+ conn.check_all_foreign_keys_valid!
703
+ rescue ActiveRecord::StatementInvalid => e
704
+ raise "Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations. Error from database:\n\n#{e.message}"
705
+ end
706
+ end
707
+
647
708
  def update_all_loaded_fixtures(fixtures_map) # :nodoc:
648
709
  all_loaded_fixtures.update(fixtures_map)
649
710
  end
@@ -657,7 +718,6 @@ module ActiveRecord
657
718
  @config = config
658
719
 
659
720
  self.model_class = class_name
660
-
661
721
  @fixtures = read_fixture_files(path)
662
722
 
663
723
  @table_name = model_class&.table_name || self.class.default_fixture_table_name(name, config)
@@ -689,7 +749,6 @@ module ActiveRecord
689
749
  table_name,
690
750
  model_class: model_class,
691
751
  fixtures: fixtures,
692
- config: config,
693
752
  ).to_hash
694
753
  end
695
754
 
@@ -720,14 +779,18 @@ module ActiveRecord
720
779
  # Loads the fixtures from the YAML file at +path+.
721
780
  # If the file sets the +model_class+ and current instance value is not set,
722
781
  # it uses the file value.
782
+
723
783
  def read_fixture_files(path)
724
- yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
784
+ yaml_files = Dir["#{path}{.yml,/{**,*}/*.yml}"].select { |f|
725
785
  ::File.file?(f)
726
- } + [yaml_file_path(path)]
786
+ }
787
+
788
+ raise ArgumentError, "No fixture files found for #{@name}" if yaml_files.empty?
727
789
 
728
790
  yaml_files.each_with_object({}) do |file, fixtures|
729
791
  FixtureSet::File.open(file) do |fh|
730
792
  self.model_class ||= fh.model_class if fh.model_class
793
+ self.model_class ||= default_fixture_model_class
731
794
  self.ignored_fixtures ||= fh.ignored_fixtures
732
795
  fh.each do |fixture_name, row|
733
796
  fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
@@ -736,18 +799,19 @@ module ActiveRecord
736
799
  end
737
800
  end
738
801
 
739
- def yaml_file_path(path)
740
- "#{path}.yml"
802
+ def default_fixture_model_class
803
+ klass = ActiveRecord::FixtureSet.default_fixture_model_name(@name, @config).safe_constantize
804
+ klass if klass && klass < ActiveRecord::Base
741
805
  end
742
806
  end
743
807
 
744
- class Fixture #:nodoc:
808
+ class Fixture # :nodoc:
745
809
  include Enumerable
746
810
 
747
- class FixtureError < StandardError #:nodoc:
811
+ class FixtureError < StandardError # :nodoc:
748
812
  end
749
813
 
750
- class FormatError < FixtureError #:nodoc:
814
+ class FormatError < FixtureError # :nodoc:
751
815
  end
752
816
 
753
817
  attr_reader :model_class, :fixture
@@ -761,8 +825,8 @@ module ActiveRecord
761
825
  model_class.name if model_class
762
826
  end
763
827
 
764
- def each
765
- fixture.each { |item| yield item }
828
+ def each(&block)
829
+ fixture.each(&block)
766
830
  end
767
831
 
768
832
  def [](key)
@@ -774,7 +838,8 @@ module ActiveRecord
774
838
  def find
775
839
  raise FixtureClassNotFound, "No class attached to find." unless model_class
776
840
  object = model_class.unscoped do
777
- model_class.find(fixture[model_class.primary_key])
841
+ pk_clauses = fixture.slice(*Array(model_class.primary_key))
842
+ model_class.find_by!(pk_clauses)
778
843
  end
779
844
  # Fixtures can't be eagerly loaded
780
845
  object.instance_variable_set(:@strict_loading, false)
@@ -782,3 +847,5 @@ module ActiveRecord
782
847
  end
783
848
  end
784
849
  end
850
+
851
+ ActiveSupport.run_load_hooks :active_record_fixture_set, ActiveRecord::FixtureSet