activerecord 6.1.6 → 7.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (309) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1627 -983
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -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 +439 -305
  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 +73 -22
  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 -34
  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 +172 -50
  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 -138
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -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 +151 -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 +30 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -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/money.rb +3 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  95. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +394 -74
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +509 -247
  101. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  102. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  105. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  106. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  107. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  108. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  109. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  110. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  111. data/lib/active_record/connection_adapters.rb +9 -6
  112. data/lib/active_record/connection_handling.rb +107 -136
  113. data/lib/active_record/core.rb +202 -223
  114. data/lib/active_record/counter_cache.rb +46 -25
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  116. data/lib/active_record/database_configurations/database_config.rb +21 -12
  117. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  118. data/lib/active_record/database_configurations/url_config.rb +18 -12
  119. data/lib/active_record/database_configurations.rb +95 -59
  120. data/lib/active_record/delegated_type.rb +61 -15
  121. data/lib/active_record/deprecator.rb +7 -0
  122. data/lib/active_record/destroy_association_async_job.rb +3 -1
  123. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  124. data/lib/active_record/dynamic_matchers.rb +1 -1
  125. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  126. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  127. data/lib/active_record/encryption/cipher.rb +53 -0
  128. data/lib/active_record/encryption/config.rb +68 -0
  129. data/lib/active_record/encryption/configurable.rb +60 -0
  130. data/lib/active_record/encryption/context.rb +42 -0
  131. data/lib/active_record/encryption/contexts.rb +76 -0
  132. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  133. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  134. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  135. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  136. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  137. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  138. data/lib/active_record/encryption/encryptor.rb +155 -0
  139. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  140. data/lib/active_record/encryption/errors.rb +15 -0
  141. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  142. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  143. data/lib/active_record/encryption/key.rb +28 -0
  144. data/lib/active_record/encryption/key_generator.rb +53 -0
  145. data/lib/active_record/encryption/key_provider.rb +46 -0
  146. data/lib/active_record/encryption/message.rb +33 -0
  147. data/lib/active_record/encryption/message_serializer.rb +92 -0
  148. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  149. data/lib/active_record/encryption/properties.rb +76 -0
  150. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  151. data/lib/active_record/encryption/scheme.rb +96 -0
  152. data/lib/active_record/encryption.rb +56 -0
  153. data/lib/active_record/enum.rb +154 -63
  154. data/lib/active_record/errors.rb +171 -15
  155. data/lib/active_record/explain.rb +23 -3
  156. data/lib/active_record/explain_registry.rb +11 -6
  157. data/lib/active_record/explain_subscriber.rb +1 -1
  158. data/lib/active_record/fixture_set/file.rb +15 -1
  159. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  160. data/lib/active_record/fixture_set/render_context.rb +2 -0
  161. data/lib/active_record/fixture_set/table_row.rb +70 -14
  162. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  163. data/lib/active_record/fixtures.rb +131 -86
  164. data/lib/active_record/future_result.rb +164 -0
  165. data/lib/active_record/gem_version.rb +3 -3
  166. data/lib/active_record/inheritance.rb +81 -29
  167. data/lib/active_record/insert_all.rb +135 -22
  168. data/lib/active_record/integration.rb +11 -10
  169. data/lib/active_record/internal_metadata.rb +119 -33
  170. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  171. data/lib/active_record/locking/optimistic.rb +36 -21
  172. data/lib/active_record/locking/pessimistic.rb +15 -6
  173. data/lib/active_record/log_subscriber.rb +52 -19
  174. data/lib/active_record/marshalling.rb +56 -0
  175. data/lib/active_record/message_pack.rb +124 -0
  176. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  177. data/lib/active_record/middleware/database_selector.rb +23 -13
  178. data/lib/active_record/middleware/shard_selector.rb +62 -0
  179. data/lib/active_record/migration/command_recorder.rb +112 -14
  180. data/lib/active_record/migration/compatibility.rb +221 -48
  181. data/lib/active_record/migration/default_strategy.rb +23 -0
  182. data/lib/active_record/migration/execution_strategy.rb +19 -0
  183. data/lib/active_record/migration/join_table.rb +1 -1
  184. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  185. data/lib/active_record/migration.rb +358 -171
  186. data/lib/active_record/model_schema.rb +120 -101
  187. data/lib/active_record/nested_attributes.rb +37 -18
  188. data/lib/active_record/no_touching.rb +3 -3
  189. data/lib/active_record/normalization.rb +167 -0
  190. data/lib/active_record/persistence.rb +405 -85
  191. data/lib/active_record/promise.rb +84 -0
  192. data/lib/active_record/query_cache.rb +3 -21
  193. data/lib/active_record/query_logs.rb +174 -0
  194. data/lib/active_record/query_logs_formatter.rb +41 -0
  195. data/lib/active_record/querying.rb +29 -6
  196. data/lib/active_record/railtie.rb +219 -43
  197. data/lib/active_record/railties/controller_runtime.rb +13 -9
  198. data/lib/active_record/railties/databases.rake +188 -252
  199. data/lib/active_record/railties/job_runtime.rb +23 -0
  200. data/lib/active_record/readonly_attributes.rb +41 -3
  201. data/lib/active_record/reflection.rb +241 -80
  202. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  203. data/lib/active_record/relation/batches.rb +192 -63
  204. data/lib/active_record/relation/calculations.rb +219 -90
  205. data/lib/active_record/relation/delegation.rb +27 -13
  206. data/lib/active_record/relation/finder_methods.rb +108 -51
  207. data/lib/active_record/relation/merger.rb +22 -13
  208. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  209. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  210. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  211. data/lib/active_record/relation/predicate_builder.rb +27 -20
  212. data/lib/active_record/relation/query_attribute.rb +30 -12
  213. data/lib/active_record/relation/query_methods.rb +654 -127
  214. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  215. data/lib/active_record/relation/spawn_methods.rb +20 -3
  216. data/lib/active_record/relation/where_clause.rb +10 -19
  217. data/lib/active_record/relation.rb +262 -120
  218. data/lib/active_record/result.rb +37 -11
  219. data/lib/active_record/runtime_registry.rb +18 -13
  220. data/lib/active_record/sanitization.rb +65 -20
  221. data/lib/active_record/schema.rb +36 -22
  222. data/lib/active_record/schema_dumper.rb +73 -24
  223. data/lib/active_record/schema_migration.rb +68 -33
  224. data/lib/active_record/scoping/default.rb +72 -15
  225. data/lib/active_record/scoping/named.rb +5 -13
  226. data/lib/active_record/scoping.rb +65 -34
  227. data/lib/active_record/secure_password.rb +60 -0
  228. data/lib/active_record/secure_token.rb +21 -3
  229. data/lib/active_record/serialization.rb +6 -1
  230. data/lib/active_record/signed_id.rb +10 -8
  231. data/lib/active_record/store.rb +16 -11
  232. data/lib/active_record/suppressor.rb +13 -15
  233. data/lib/active_record/table_metadata.rb +16 -3
  234. data/lib/active_record/tasks/database_tasks.rb +225 -136
  235. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  236. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  237. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  238. data/lib/active_record/test_databases.rb +1 -1
  239. data/lib/active_record/test_fixtures.rb +123 -99
  240. data/lib/active_record/timestamp.rb +29 -18
  241. data/lib/active_record/token_for.rb +113 -0
  242. data/lib/active_record/touch_later.rb +11 -6
  243. data/lib/active_record/transactions.rb +48 -27
  244. data/lib/active_record/translation.rb +3 -3
  245. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  246. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  247. data/lib/active_record/type/internal/timezone.rb +7 -2
  248. data/lib/active_record/type/serialized.rb +9 -5
  249. data/lib/active_record/type/time.rb +4 -0
  250. data/lib/active_record/type/type_map.rb +17 -20
  251. data/lib/active_record/type.rb +1 -2
  252. data/lib/active_record/validations/absence.rb +1 -1
  253. data/lib/active_record/validations/associated.rb +4 -4
  254. data/lib/active_record/validations/numericality.rb +5 -4
  255. data/lib/active_record/validations/presence.rb +5 -28
  256. data/lib/active_record/validations/uniqueness.rb +51 -6
  257. data/lib/active_record/validations.rb +8 -4
  258. data/lib/active_record/version.rb +1 -1
  259. data/lib/active_record.rb +335 -32
  260. data/lib/arel/attributes/attribute.rb +0 -8
  261. data/lib/arel/crud.rb +28 -22
  262. data/lib/arel/delete_manager.rb +18 -4
  263. data/lib/arel/errors.rb +10 -0
  264. data/lib/arel/factory_methods.rb +4 -0
  265. data/lib/arel/filter_predications.rb +9 -0
  266. data/lib/arel/insert_manager.rb +2 -3
  267. data/lib/arel/nodes/and.rb +4 -0
  268. data/lib/arel/nodes/binary.rb +6 -1
  269. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  270. data/lib/arel/nodes/casted.rb +1 -1
  271. data/lib/arel/nodes/cte.rb +36 -0
  272. data/lib/arel/nodes/delete_statement.rb +12 -13
  273. data/lib/arel/nodes/filter.rb +10 -0
  274. data/lib/arel/nodes/fragments.rb +35 -0
  275. data/lib/arel/nodes/function.rb +1 -0
  276. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  277. data/lib/arel/nodes/insert_statement.rb +2 -2
  278. data/lib/arel/nodes/leading_join.rb +8 -0
  279. data/lib/arel/nodes/node.rb +111 -2
  280. data/lib/arel/nodes/select_core.rb +2 -2
  281. data/lib/arel/nodes/select_statement.rb +2 -2
  282. data/lib/arel/nodes/sql_literal.rb +6 -0
  283. data/lib/arel/nodes/table_alias.rb +4 -0
  284. data/lib/arel/nodes/update_statement.rb +8 -3
  285. data/lib/arel/nodes.rb +5 -0
  286. data/lib/arel/predications.rb +13 -3
  287. data/lib/arel/select_manager.rb +10 -4
  288. data/lib/arel/table.rb +9 -6
  289. data/lib/arel/tree_manager.rb +0 -12
  290. data/lib/arel/update_manager.rb +18 -4
  291. data/lib/arel/visitors/dot.rb +80 -90
  292. data/lib/arel/visitors/mysql.rb +16 -3
  293. data/lib/arel/visitors/postgresql.rb +0 -10
  294. data/lib/arel/visitors/to_sql.rb +139 -19
  295. data/lib/arel/visitors/visitor.rb +2 -2
  296. data/lib/arel.rb +18 -3
  297. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  298. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  299. data/lib/rails/generators/active_record/migration.rb +3 -1
  300. data/lib/rails/generators/active_record/model/USAGE +113 -0
  301. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  302. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  303. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  304. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  305. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  306. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  307. metadata +93 -13
  308. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  309. 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 = 6
12
+ TINY = 2
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")