activerecord 6.1.7 → 7.1.0

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 (307) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1516 -1019
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +50 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +35 -31
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency.rb +26 -16
  27. data/lib/active_record/associations/preloader/association.rb +207 -52
  28. data/lib/active_record/associations/preloader/batch.rb +48 -0
  29. data/lib/active_record/associations/preloader/branch.rb +147 -0
  30. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  31. data/lib/active_record/associations/preloader.rb +50 -121
  32. data/lib/active_record/associations/singular_association.rb +9 -3
  33. data/lib/active_record/associations/through_association.rb +25 -14
  34. data/lib/active_record/associations.rb +423 -289
  35. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  36. data/lib/active_record/attribute_assignment.rb +1 -3
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +61 -14
  39. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  40. data/lib/active_record/attribute_methods/query.rb +31 -19
  41. data/lib/active_record/attribute_methods/read.rb +25 -10
  42. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  44. data/lib/active_record/attribute_methods/write.rb +10 -13
  45. data/lib/active_record/attribute_methods.rb +121 -40
  46. data/lib/active_record/attributes.rb +27 -38
  47. data/lib/active_record/autosave_association.rb +61 -30
  48. data/lib/active_record/base.rb +25 -2
  49. data/lib/active_record/callbacks.rb +18 -34
  50. data/lib/active_record/coders/column_serializer.rb +61 -0
  51. data/lib/active_record/coders/json.rb +1 -1
  52. data/lib/active_record/coders/yaml_column.rb +70 -46
  53. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  54. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
  69. data/lib/active_record/connection_adapters/column.rb +13 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
  83. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
  98. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  99. data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
  100. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  101. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  102. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
  103. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  104. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  107. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  108. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  109. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  110. data/lib/active_record/connection_adapters.rb +9 -6
  111. data/lib/active_record/connection_handling.rb +107 -136
  112. data/lib/active_record/core.rb +194 -224
  113. data/lib/active_record/counter_cache.rb +46 -25
  114. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  115. data/lib/active_record/database_configurations/database_config.rb +21 -12
  116. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  117. data/lib/active_record/database_configurations/url_config.rb +18 -12
  118. data/lib/active_record/database_configurations.rb +95 -59
  119. data/lib/active_record/delegated_type.rb +61 -15
  120. data/lib/active_record/deprecator.rb +7 -0
  121. data/lib/active_record/destroy_association_async_job.rb +3 -1
  122. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  123. data/lib/active_record/dynamic_matchers.rb +1 -1
  124. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  125. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  126. data/lib/active_record/encryption/cipher.rb +53 -0
  127. data/lib/active_record/encryption/config.rb +68 -0
  128. data/lib/active_record/encryption/configurable.rb +60 -0
  129. data/lib/active_record/encryption/context.rb +42 -0
  130. data/lib/active_record/encryption/contexts.rb +76 -0
  131. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  132. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  133. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  134. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  135. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  136. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  137. data/lib/active_record/encryption/encryptor.rb +155 -0
  138. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  139. data/lib/active_record/encryption/errors.rb +15 -0
  140. data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
  141. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  142. data/lib/active_record/encryption/key.rb +28 -0
  143. data/lib/active_record/encryption/key_generator.rb +53 -0
  144. data/lib/active_record/encryption/key_provider.rb +46 -0
  145. data/lib/active_record/encryption/message.rb +33 -0
  146. data/lib/active_record/encryption/message_serializer.rb +92 -0
  147. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  148. data/lib/active_record/encryption/properties.rb +76 -0
  149. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  150. data/lib/active_record/encryption/scheme.rb +96 -0
  151. data/lib/active_record/encryption.rb +56 -0
  152. data/lib/active_record/enum.rb +156 -62
  153. data/lib/active_record/errors.rb +171 -15
  154. data/lib/active_record/explain.rb +23 -3
  155. data/lib/active_record/explain_registry.rb +11 -6
  156. data/lib/active_record/explain_subscriber.rb +1 -1
  157. data/lib/active_record/fixture_set/file.rb +15 -1
  158. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  159. data/lib/active_record/fixture_set/render_context.rb +2 -0
  160. data/lib/active_record/fixture_set/table_row.rb +70 -14
  161. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  162. data/lib/active_record/fixtures.rb +131 -86
  163. data/lib/active_record/future_result.rb +164 -0
  164. data/lib/active_record/gem_version.rb +3 -3
  165. data/lib/active_record/inheritance.rb +81 -29
  166. data/lib/active_record/insert_all.rb +133 -20
  167. data/lib/active_record/integration.rb +11 -10
  168. data/lib/active_record/internal_metadata.rb +117 -33
  169. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  170. data/lib/active_record/locking/optimistic.rb +36 -21
  171. data/lib/active_record/locking/pessimistic.rb +15 -6
  172. data/lib/active_record/log_subscriber.rb +52 -19
  173. data/lib/active_record/marshalling.rb +56 -0
  174. data/lib/active_record/message_pack.rb +124 -0
  175. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  176. data/lib/active_record/middleware/database_selector.rb +23 -13
  177. data/lib/active_record/middleware/shard_selector.rb +62 -0
  178. data/lib/active_record/migration/command_recorder.rb +108 -13
  179. data/lib/active_record/migration/compatibility.rb +221 -48
  180. data/lib/active_record/migration/default_strategy.rb +23 -0
  181. data/lib/active_record/migration/execution_strategy.rb +19 -0
  182. data/lib/active_record/migration/join_table.rb +1 -1
  183. data/lib/active_record/migration.rb +355 -171
  184. data/lib/active_record/model_schema.rb +116 -97
  185. data/lib/active_record/nested_attributes.rb +36 -15
  186. data/lib/active_record/no_touching.rb +3 -3
  187. data/lib/active_record/normalization.rb +159 -0
  188. data/lib/active_record/persistence.rb +405 -85
  189. data/lib/active_record/promise.rb +84 -0
  190. data/lib/active_record/query_cache.rb +3 -21
  191. data/lib/active_record/query_logs.rb +174 -0
  192. data/lib/active_record/query_logs_formatter.rb +41 -0
  193. data/lib/active_record/querying.rb +29 -6
  194. data/lib/active_record/railtie.rb +219 -43
  195. data/lib/active_record/railties/controller_runtime.rb +13 -9
  196. data/lib/active_record/railties/databases.rake +185 -249
  197. data/lib/active_record/railties/job_runtime.rb +23 -0
  198. data/lib/active_record/readonly_attributes.rb +41 -3
  199. data/lib/active_record/reflection.rb +229 -80
  200. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  201. data/lib/active_record/relation/batches.rb +192 -63
  202. data/lib/active_record/relation/calculations.rb +211 -90
  203. data/lib/active_record/relation/delegation.rb +27 -13
  204. data/lib/active_record/relation/finder_methods.rb +108 -51
  205. data/lib/active_record/relation/merger.rb +22 -13
  206. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  207. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  208. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  209. data/lib/active_record/relation/predicate_builder.rb +27 -20
  210. data/lib/active_record/relation/query_attribute.rb +30 -12
  211. data/lib/active_record/relation/query_methods.rb +654 -127
  212. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  213. data/lib/active_record/relation/spawn_methods.rb +20 -3
  214. data/lib/active_record/relation/where_clause.rb +10 -19
  215. data/lib/active_record/relation.rb +262 -120
  216. data/lib/active_record/result.rb +37 -11
  217. data/lib/active_record/runtime_registry.rb +18 -13
  218. data/lib/active_record/sanitization.rb +65 -20
  219. data/lib/active_record/schema.rb +36 -22
  220. data/lib/active_record/schema_dumper.rb +73 -24
  221. data/lib/active_record/schema_migration.rb +68 -33
  222. data/lib/active_record/scoping/default.rb +72 -15
  223. data/lib/active_record/scoping/named.rb +5 -13
  224. data/lib/active_record/scoping.rb +65 -34
  225. data/lib/active_record/secure_password.rb +60 -0
  226. data/lib/active_record/secure_token.rb +21 -3
  227. data/lib/active_record/serialization.rb +6 -1
  228. data/lib/active_record/signed_id.rb +10 -8
  229. data/lib/active_record/store.rb +10 -10
  230. data/lib/active_record/suppressor.rb +13 -15
  231. data/lib/active_record/table_metadata.rb +16 -3
  232. data/lib/active_record/tasks/database_tasks.rb +225 -136
  233. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  234. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  235. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  236. data/lib/active_record/test_databases.rb +1 -1
  237. data/lib/active_record/test_fixtures.rb +116 -96
  238. data/lib/active_record/timestamp.rb +28 -17
  239. data/lib/active_record/token_for.rb +113 -0
  240. data/lib/active_record/touch_later.rb +11 -6
  241. data/lib/active_record/transactions.rb +48 -27
  242. data/lib/active_record/translation.rb +3 -3
  243. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  244. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  245. data/lib/active_record/type/internal/timezone.rb +7 -2
  246. data/lib/active_record/type/serialized.rb +9 -5
  247. data/lib/active_record/type/time.rb +4 -0
  248. data/lib/active_record/type/type_map.rb +17 -20
  249. data/lib/active_record/type.rb +1 -2
  250. data/lib/active_record/validations/absence.rb +1 -1
  251. data/lib/active_record/validations/associated.rb +4 -4
  252. data/lib/active_record/validations/numericality.rb +5 -4
  253. data/lib/active_record/validations/presence.rb +5 -28
  254. data/lib/active_record/validations/uniqueness.rb +51 -6
  255. data/lib/active_record/validations.rb +8 -4
  256. data/lib/active_record/version.rb +1 -1
  257. data/lib/active_record.rb +335 -32
  258. data/lib/arel/attributes/attribute.rb +0 -8
  259. data/lib/arel/crud.rb +28 -22
  260. data/lib/arel/delete_manager.rb +18 -4
  261. data/lib/arel/errors.rb +10 -0
  262. data/lib/arel/factory_methods.rb +4 -0
  263. data/lib/arel/filter_predications.rb +9 -0
  264. data/lib/arel/insert_manager.rb +2 -3
  265. data/lib/arel/nodes/and.rb +4 -0
  266. data/lib/arel/nodes/binary.rb +6 -1
  267. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  268. data/lib/arel/nodes/casted.rb +1 -1
  269. data/lib/arel/nodes/cte.rb +36 -0
  270. data/lib/arel/nodes/delete_statement.rb +12 -13
  271. data/lib/arel/nodes/filter.rb +10 -0
  272. data/lib/arel/nodes/fragments.rb +35 -0
  273. data/lib/arel/nodes/function.rb +1 -0
  274. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  275. data/lib/arel/nodes/insert_statement.rb +2 -2
  276. data/lib/arel/nodes/leading_join.rb +8 -0
  277. data/lib/arel/nodes/node.rb +111 -2
  278. data/lib/arel/nodes/select_core.rb +2 -2
  279. data/lib/arel/nodes/select_statement.rb +2 -2
  280. data/lib/arel/nodes/sql_literal.rb +6 -0
  281. data/lib/arel/nodes/table_alias.rb +4 -0
  282. data/lib/arel/nodes/update_statement.rb +8 -3
  283. data/lib/arel/nodes.rb +5 -0
  284. data/lib/arel/predications.rb +13 -3
  285. data/lib/arel/select_manager.rb +10 -4
  286. data/lib/arel/table.rb +9 -6
  287. data/lib/arel/tree_manager.rb +0 -12
  288. data/lib/arel/update_manager.rb +18 -4
  289. data/lib/arel/visitors/dot.rb +80 -90
  290. data/lib/arel/visitors/mysql.rb +16 -3
  291. data/lib/arel/visitors/postgresql.rb +0 -10
  292. data/lib/arel/visitors/to_sql.rb +139 -19
  293. data/lib/arel/visitors/visitor.rb +2 -2
  294. data/lib/arel.rb +18 -3
  295. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  296. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  297. data/lib/rails/generators/active_record/migration.rb +3 -1
  298. data/lib/rails/generators/active_record/model/USAGE +113 -0
  299. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  300. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  302. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  303. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  304. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  305. metadata +92 -13
  306. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  307. data/lib/active_record/null_relation.rb +0 -67
@@ -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:
@@ -124,7 +134,7 @@ module ActiveRecord
124
134
  # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
125
135
  # large sets of fixtured data.
126
136
  #
127
- # = Dynamic fixtures with ERB
137
+ # == Dynamic fixtures with \ERB
128
138
  #
129
139
  # Sometimes you don't care about the content of the fixtures as much as you care about the volume.
130
140
  # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
@@ -152,7 +162,7 @@ module ActiveRecord
152
162
  # - define a helper method in <tt>test_helper.rb</tt>
153
163
  # module FixtureFileHelpers
154
164
  # def file_sha(path)
155
- # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
165
+ # OpenSSL::Digest::SHA256.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
156
166
  # end
157
167
  # end
158
168
  # ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
@@ -162,7 +172,7 @@ module ActiveRecord
162
172
  # name: kitten.png
163
173
  # sha: <%= file_sha 'files/kitten.png' %>
164
174
  #
165
- # = Transactional Tests
175
+ # == Transactional Tests
166
176
  #
167
177
  # Test cases can use begin+rollback to isolate their changes to the database instead of having to
168
178
  # delete+insert for every test case.
@@ -198,7 +208,7 @@ module ActiveRecord
198
208
  # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
199
209
  # Use InnoDB, MaxDB, or NDB instead.
200
210
  #
201
- # = Advanced Fixtures
211
+ # == Advanced Fixtures
202
212
  #
203
213
  # Fixtures that don't specify an ID get some extra features:
204
214
  #
@@ -212,7 +222,7 @@ module ActiveRecord
212
222
  # * Fixture label interpolation
213
223
  # * Support for YAML defaults
214
224
  #
215
- # == Stable, Autogenerated IDs
225
+ # === Stable, Autogenerated IDs
216
226
  #
217
227
  # Here, have a monkey fixture:
218
228
  #
@@ -241,13 +251,13 @@ module ActiveRecord
241
251
  # The generated ID for a given label is constant, so we can discover
242
252
  # any fixture's ID without loading anything, as long as we know the label.
243
253
  #
244
- # == Label references for associations (belongs_to, has_one, has_many)
254
+ # === Label references for associations (+belongs_to+, +has_one+, +has_many+)
245
255
  #
246
256
  # Specifying foreign keys in fixtures can be very fragile, not to
247
257
  # mention difficult to read. Since Active Record can figure out the ID of
248
258
  # any fixture from its label, you can specify FK's by label instead of ID.
249
259
  #
250
- # === belongs_to
260
+ # ==== +belongs_to+
251
261
  #
252
262
  # Let's break out some more monkeys and pirates.
253
263
  #
@@ -286,7 +296,7 @@ module ActiveRecord
286
296
  # a target *label* for the *association* (monkey: george) rather than
287
297
  # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
288
298
  #
289
- # ==== Polymorphic belongs_to
299
+ # ==== Polymorphic +belongs_to+
290
300
  #
291
301
  # Supporting polymorphic relationships is a little bit more complicated, since
292
302
  # Active Record needs to know what type your association is pointing at. Something
@@ -311,9 +321,9 @@ module ActiveRecord
311
321
  #
312
322
  # Just provide the polymorphic target type and Active Record will take care of the rest.
313
323
  #
314
- # === has_and_belongs_to_many
324
+ # ==== +has_and_belongs_to_many+ or <tt>has_many :through</tt>
315
325
  #
316
- # Time to give our monkey some fruit.
326
+ # \Time to give our monkey some fruit.
317
327
  #
318
328
  # ### in monkeys.yml
319
329
  #
@@ -375,7 +385,7 @@ module ActiveRecord
375
385
  # the fixture's model class and discovers the +has_and_belongs_to_many+
376
386
  # associations.
377
387
  #
378
- # == Autofilled Timestamp Columns
388
+ # === Autofilled \Timestamp Columns
379
389
  #
380
390
  # If your table/model specifies any of Active Record's
381
391
  # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
@@ -383,7 +393,7 @@ module ActiveRecord
383
393
  #
384
394
  # If you've set specific values, they'll be left alone.
385
395
  #
386
- # == Fixture label interpolation
396
+ # === Fixture label interpolation
387
397
  #
388
398
  # The label of the current fixture is always available as a column value:
389
399
  #
@@ -400,14 +410,18 @@ module ActiveRecord
400
410
  # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
401
411
  # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
402
412
  #
403
- # == Support for YAML defaults
413
+ # If the model uses UUID values for identifiers, add the +:uuid+ argument:
414
+ #
415
+ # ActiveRecord::FixtureSet.identify(:boaty_mcboatface, :uuid)
416
+ #
417
+ # === Support for YAML defaults
404
418
  #
405
419
  # You can set and reuse defaults in your fixtures YAML file.
406
420
  # This is the same technique used in the +database.yml+ file to specify
407
421
  # defaults:
408
422
  #
409
423
  # DEFAULTS: &DEFAULTS
410
- # created_on: <%= 3.weeks.ago.to_s(:db) %>
424
+ # created_on: <%= 3.weeks.ago.to_fs(:db) %>
411
425
  #
412
426
  # first:
413
427
  # name: Smurf
@@ -426,7 +440,7 @@ module ActiveRecord
426
440
  # _fixture:
427
441
  # ignore:
428
442
  # - base
429
- # # or use "ignore: base" when there is only one fixture needs to be ignored.
443
+ # # or use "ignore: base" when there is only one fixture that needs to be ignored.
430
444
  #
431
445
  # base: &base
432
446
  # admin: false
@@ -442,6 +456,41 @@ module ActiveRecord
442
456
  # In the above example, 'base' will be ignored when creating fixtures.
443
457
  # This can be used for common attributes inheriting.
444
458
  #
459
+ # == Composite Primary Key Fixtures
460
+ #
461
+ # Fixtures for composite primary key tables are fairly similar to normal tables.
462
+ # When using an id column, the column may be omitted as usual:
463
+ #
464
+ # # app/models/book.rb
465
+ # class Book < ApplicationRecord
466
+ # self.primary_key = [:author_id, :id]
467
+ # belongs_to :author
468
+ # end
469
+ #
470
+ # # books.yml
471
+ # alices_adventure_in_wonderland:
472
+ # author_id: <%= ActiveRecord::FixtureSet.identify(:lewis_carroll) %>
473
+ # title: "Alice's Adventures in Wonderland"
474
+ #
475
+ # However, in order to support composite primary key relationships,
476
+ # you must use the `composite_identify` method:
477
+ #
478
+ # # app/models/book_orders.rb
479
+ # class BookOrder < ApplicationRecord
480
+ # self.primary_key = [:shop_id, :id]
481
+ # belongs_to :order, query_constraints: [:shop_id, :order_id]
482
+ # belongs_to :book, query_constraints: [:author_id, :book_id]
483
+ # end
484
+ #
485
+ # # book_orders.yml
486
+ # alices_adventure_in_wonderland_in_books:
487
+ # author: lewis_carroll
488
+ # book_id: <%= ActiveRecord::FixtureSet.composite_identify(
489
+ # :alices_adventure_in_wonderland, Book.primary_key)[:id] %>
490
+ # shop: book_store
491
+ # order_id: <%= ActiveRecord::FixtureSet.composite_identify(
492
+ # :books, Order.primary_key)[:id] %>
493
+ #
445
494
  # == Configure the fixture model class
446
495
  #
447
496
  # It's possible to set the fixture's model class directly in the YAML file.
@@ -456,6 +505,10 @@ module ActiveRecord
456
505
  #
457
506
  # Any fixtures labeled "_fixture" are safely ignored.
458
507
  class FixtureSet
508
+ require "active_record/fixture_set/file"
509
+ require "active_record/fixture_set/render_context"
510
+ require "active_record/fixture_set/table_rows"
511
+
459
512
  #--
460
513
  # An instance of FixtureSet is normally stored in a single YAML file and
461
514
  # possibly in a folder with the same name.
@@ -467,39 +520,6 @@ module ActiveRecord
467
520
 
468
521
  cattr_accessor :all_loaded_fixtures, default: {}
469
522
 
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
523
  class << self
504
524
  def default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
505
525
  config.pluralize_table_names ?
@@ -552,9 +572,9 @@ module ActiveRecord
552
572
  end
553
573
  end
554
574
 
555
- def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
575
+ def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
556
576
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
557
- class_names = ClassCache.new class_names, config
577
+ class_names.stringify_keys!
558
578
 
559
579
  # FIXME: Apparently JK uses this.
560
580
  connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
@@ -565,7 +585,7 @@ module ActiveRecord
565
585
 
566
586
  if fixture_files_to_read.any?
567
587
  fixtures_map = read_and_insert(
568
- fixtures_directory,
588
+ Array(fixtures_directories),
569
589
  fixture_files_to_read,
570
590
  class_names,
571
591
  connection,
@@ -576,7 +596,8 @@ module ActiveRecord
576
596
  end
577
597
 
578
598
  # 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.
599
+ #
600
+ # \Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
580
601
  def identify(label, column_type = :integer)
581
602
  if column_type == :uuid
582
603
  Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
@@ -585,29 +606,35 @@ module ActiveRecord
585
606
  end
586
607
  end
587
608
 
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)
609
+ # Returns a consistent, platform-independent hash representing a mapping
610
+ # between the label and the subcomponents of the provided composite key.
611
+ #
612
+ # Example:
613
+ #
614
+ # composite_identify("label", [:a, :b, :c]) # => { a: hash_1, b: hash_2, c: hash_3 }
615
+ def composite_identify(label, key)
616
+ key
617
+ .index_with
618
+ .with_index { |sub_key, index| (identify(label) << index) % MAX_ID }
619
+ .with_indifferent_access
594
620
  end
595
621
 
596
- # Superclass for the evaluation contexts used by ERB fixtures.
622
+ # Superclass for the evaluation contexts used by \ERB fixtures.
597
623
  def context_class
598
624
  @context_class ||= Class.new
599
625
  end
600
626
 
601
627
  private
602
- def read_and_insert(fixtures_directory, fixture_files, class_names, connection) # :nodoc:
628
+ def read_and_insert(fixtures_directories, fixture_files, class_names, connection) # :nodoc:
603
629
  fixtures_map = {}
630
+ directory_glob = "{#{fixtures_directories.join(",")}}"
604
631
  fixture_sets = fixture_files.map do |fixture_set_name|
605
632
  klass = class_names[fixture_set_name]
606
633
  fixtures_map[fixture_set_name] = new( # ActiveRecord::FixtureSet.new
607
634
  nil,
608
635
  fixture_set_name,
609
636
  klass,
610
- ::File.join(fixtures_directory, fixture_set_name)
637
+ ::File.join(directory_glob, fixture_set_name)
611
638
  )
612
639
  end
613
640
  update_all_loaded_fixtures(fixtures_map)
@@ -637,6 +664,8 @@ module ActiveRecord
637
664
 
638
665
  conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
639
666
 
667
+ check_all_foreign_keys_valid!(conn)
668
+
640
669
  # Cap primary key sequences to max(pk).
641
670
  if conn.respond_to?(:reset_pk_sequence!)
642
671
  set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
@@ -644,6 +673,16 @@ module ActiveRecord
644
673
  end
645
674
  end
646
675
 
676
+ def check_all_foreign_keys_valid!(conn)
677
+ return unless ActiveRecord.verify_foreign_keys_for_fixtures
678
+
679
+ begin
680
+ conn.check_all_foreign_keys_valid!
681
+ rescue ActiveRecord::StatementInvalid => e
682
+ 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}"
683
+ end
684
+ end
685
+
647
686
  def update_all_loaded_fixtures(fixtures_map) # :nodoc:
648
687
  all_loaded_fixtures.update(fixtures_map)
649
688
  end
@@ -657,7 +696,6 @@ module ActiveRecord
657
696
  @config = config
658
697
 
659
698
  self.model_class = class_name
660
-
661
699
  @fixtures = read_fixture_files(path)
662
700
 
663
701
  @table_name = model_class&.table_name || self.class.default_fixture_table_name(name, config)
@@ -689,7 +727,6 @@ module ActiveRecord
689
727
  table_name,
690
728
  model_class: model_class,
691
729
  fixtures: fixtures,
692
- config: config,
693
730
  ).to_hash
694
731
  end
695
732
 
@@ -720,14 +757,18 @@ module ActiveRecord
720
757
  # Loads the fixtures from the YAML file at +path+.
721
758
  # If the file sets the +model_class+ and current instance value is not set,
722
759
  # it uses the file value.
760
+
723
761
  def read_fixture_files(path)
724
- yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
762
+ yaml_files = Dir["#{path}{.yml,/{**,*}/*.yml}"].select { |f|
725
763
  ::File.file?(f)
726
- } + [yaml_file_path(path)]
764
+ }
765
+
766
+ raise ArgumentError, "No fixture files found for #{@name}" if yaml_files.empty?
727
767
 
728
768
  yaml_files.each_with_object({}) do |file, fixtures|
729
769
  FixtureSet::File.open(file) do |fh|
730
770
  self.model_class ||= fh.model_class if fh.model_class
771
+ self.model_class ||= default_fixture_model_class
731
772
  self.ignored_fixtures ||= fh.ignored_fixtures
732
773
  fh.each do |fixture_name, row|
733
774
  fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
@@ -736,18 +777,19 @@ module ActiveRecord
736
777
  end
737
778
  end
738
779
 
739
- def yaml_file_path(path)
740
- "#{path}.yml"
780
+ def default_fixture_model_class
781
+ klass = ActiveRecord::FixtureSet.default_fixture_model_name(@name, @config).safe_constantize
782
+ klass if klass && klass < ActiveRecord::Base
741
783
  end
742
784
  end
743
785
 
744
- class Fixture #:nodoc:
786
+ class Fixture # :nodoc:
745
787
  include Enumerable
746
788
 
747
- class FixtureError < StandardError #:nodoc:
789
+ class FixtureError < StandardError # :nodoc:
748
790
  end
749
791
 
750
- class FormatError < FixtureError #:nodoc:
792
+ class FormatError < FixtureError # :nodoc:
751
793
  end
752
794
 
753
795
  attr_reader :model_class, :fixture
@@ -761,8 +803,8 @@ module ActiveRecord
761
803
  model_class.name if model_class
762
804
  end
763
805
 
764
- def each
765
- fixture.each { |item| yield item }
806
+ def each(&block)
807
+ fixture.each(&block)
766
808
  end
767
809
 
768
810
  def [](key)
@@ -774,7 +816,8 @@ module ActiveRecord
774
816
  def find
775
817
  raise FixtureClassNotFound, "No class attached to find." unless model_class
776
818
  object = model_class.unscoped do
777
- model_class.find(fixture[model_class.primary_key])
819
+ pk_clauses = fixture.slice(*Array(model_class.primary_key))
820
+ model_class.find_by!(pk_clauses)
778
821
  end
779
822
  # Fixtures can't be eagerly loaded
780
823
  object.instance_variable_set(:@strict_loading, false)
@@ -782,3 +825,5 @@ module ActiveRecord
782
825
  end
783
826
  end
784
827
  end
828
+
829
+ ActiveSupport.run_load_hooks :active_record_fixture_set, ActiveRecord::FixtureSet
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class FutureResult # :nodoc:
5
+ class Complete
6
+ attr_reader :result
7
+ delegate :empty?, :to_a, to: :result
8
+
9
+ def initialize(result)
10
+ @result = result
11
+ end
12
+
13
+ def pending?
14
+ false
15
+ end
16
+
17
+ def canceled?
18
+ false
19
+ end
20
+
21
+ def then(&block)
22
+ Promise::Complete.new(@result.then(&block))
23
+ end
24
+ end
25
+
26
+ class EventBuffer
27
+ def initialize(future_result, instrumenter)
28
+ @future_result = future_result
29
+ @instrumenter = instrumenter
30
+ @events = []
31
+ end
32
+
33
+ def instrument(name, payload = {}, &block)
34
+ event = @instrumenter.new_event(name, payload)
35
+ @events << event
36
+ event.record(&block)
37
+ end
38
+
39
+ def flush
40
+ events, @events = @events, []
41
+ events.each do |event|
42
+ event.payload[:lock_wait] = @future_result.lock_wait
43
+ ActiveSupport::Notifications.publish_event(event)
44
+ end
45
+ end
46
+ end
47
+
48
+ Canceled = Class.new(ActiveRecordError)
49
+
50
+ delegate :empty?, :to_a, to: :result
51
+
52
+ attr_reader :lock_wait
53
+
54
+ def initialize(pool, *args, **kwargs)
55
+ @mutex = Mutex.new
56
+
57
+ @session = nil
58
+ @pool = pool
59
+ @args = args
60
+ @kwargs = kwargs
61
+
62
+ @pending = true
63
+ @error = nil
64
+ @result = nil
65
+ @instrumenter = ActiveSupport::Notifications.instrumenter
66
+ @event_buffer = nil
67
+ end
68
+
69
+ def then(&block)
70
+ Promise.new(self, block)
71
+ end
72
+
73
+ def schedule!(session)
74
+ @session = session
75
+ @pool.schedule_query(self)
76
+ end
77
+
78
+ def execute!(connection)
79
+ execute_query(connection)
80
+ end
81
+
82
+ def cancel
83
+ @pending = false
84
+ @error = Canceled
85
+ self
86
+ end
87
+
88
+ def execute_or_skip
89
+ return unless pending?
90
+
91
+ @pool.with_connection do |connection|
92
+ return unless @mutex.try_lock
93
+ begin
94
+ if pending?
95
+ @event_buffer = EventBuffer.new(self, @instrumenter)
96
+ connection.with_instrumenter(@event_buffer) do
97
+ execute_query(connection, async: true)
98
+ end
99
+ end
100
+ ensure
101
+ @mutex.unlock
102
+ end
103
+ end
104
+ end
105
+
106
+ def result
107
+ execute_or_wait
108
+ @event_buffer&.flush
109
+
110
+ if canceled?
111
+ raise Canceled
112
+ elsif @error
113
+ raise @error
114
+ else
115
+ @result
116
+ end
117
+ end
118
+
119
+ def pending?
120
+ @pending && (!@session || @session.active?)
121
+ end
122
+
123
+ def canceled?
124
+ @session && !@session.active?
125
+ end
126
+
127
+ private
128
+ def execute_or_wait
129
+ if pending?
130
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
131
+ @mutex.synchronize do
132
+ if pending?
133
+ execute_query(@pool.connection)
134
+ else
135
+ @lock_wait = (Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start)
136
+ end
137
+ end
138
+ else
139
+ @lock_wait = 0.0
140
+ end
141
+ end
142
+
143
+ def execute_query(connection, async: false)
144
+ @result = exec_query(connection, *@args, **@kwargs, async: async)
145
+ rescue => error
146
+ @error = error
147
+ ensure
148
+ @pending = false
149
+ end
150
+
151
+ def exec_query(connection, *args, **kwargs)
152
+ connection.internal_exec_query(*args, **kwargs)
153
+ end
154
+
155
+ class SelectAll < FutureResult # :nodoc:
156
+ private
157
+ def exec_query(*, **)
158
+ super
159
+ rescue ::RangeError
160
+ ActiveRecord::Result.empty
161
+ end
162
+ end
163
+ end
164
+ end
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- # Returns the version of the currently loaded Active Record as a <tt>Gem::Version</tt>
4
+ # Returns the currently loaded version of Active Record as a +Gem::Version+.
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION::STRING
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 6
10
+ MAJOR = 7
11
11
  MINOR = 1
12
- TINY = 7
12
+ TINY = 0
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")