activerecord 7.0.8 → 7.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (231) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1536 -1454
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +16 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +20 -4
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +15 -9
  15. data/lib/active_record/associations/collection_proxy.rb +15 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +31 -7
  22. data/lib/active_record/associations/preloader.rb +13 -10
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +313 -217
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +52 -34
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +74 -51
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -124
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  75. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  76. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  77. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  78. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
  80. data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
  81. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  82. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  83. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  84. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  85. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  86. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  87. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
  88. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  89. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  90. data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
  91. data/lib/active_record/connection_adapters.rb +3 -1
  92. data/lib/active_record/connection_handling.rb +72 -95
  93. data/lib/active_record/core.rb +175 -153
  94. data/lib/active_record/counter_cache.rb +46 -25
  95. data/lib/active_record/database_configurations/database_config.rb +9 -3
  96. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  97. data/lib/active_record/database_configurations/url_config.rb +17 -11
  98. data/lib/active_record/database_configurations.rb +86 -33
  99. data/lib/active_record/delegated_type.rb +9 -4
  100. data/lib/active_record/deprecator.rb +7 -0
  101. data/lib/active_record/destroy_association_async_job.rb +2 -0
  102. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  103. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  104. data/lib/active_record/encryption/config.rb +25 -1
  105. data/lib/active_record/encryption/configurable.rb +12 -19
  106. data/lib/active_record/encryption/context.rb +10 -3
  107. data/lib/active_record/encryption/contexts.rb +5 -1
  108. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  109. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
  111. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  112. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  113. data/lib/active_record/encryption/key_generator.rb +12 -1
  114. data/lib/active_record/encryption/message_serializer.rb +2 -0
  115. data/lib/active_record/encryption/properties.rb +3 -3
  116. data/lib/active_record/encryption/scheme.rb +19 -22
  117. data/lib/active_record/encryption.rb +1 -0
  118. data/lib/active_record/enum.rb +112 -28
  119. data/lib/active_record/errors.rb +112 -18
  120. data/lib/active_record/explain.rb +23 -3
  121. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  122. data/lib/active_record/fixture_set/render_context.rb +2 -0
  123. data/lib/active_record/fixture_set/table_row.rb +29 -8
  124. data/lib/active_record/fixtures.rb +135 -71
  125. data/lib/active_record/future_result.rb +31 -5
  126. data/lib/active_record/gem_version.rb +3 -3
  127. data/lib/active_record/inheritance.rb +30 -16
  128. data/lib/active_record/insert_all.rb +57 -10
  129. data/lib/active_record/integration.rb +8 -8
  130. data/lib/active_record/internal_metadata.rb +120 -30
  131. data/lib/active_record/locking/pessimistic.rb +5 -2
  132. data/lib/active_record/log_subscriber.rb +29 -12
  133. data/lib/active_record/marshalling.rb +56 -0
  134. data/lib/active_record/message_pack.rb +124 -0
  135. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  136. data/lib/active_record/middleware/database_selector.rb +6 -8
  137. data/lib/active_record/middleware/shard_selector.rb +3 -1
  138. data/lib/active_record/migration/command_recorder.rb +104 -5
  139. data/lib/active_record/migration/compatibility.rb +139 -5
  140. data/lib/active_record/migration/default_strategy.rb +23 -0
  141. data/lib/active_record/migration/execution_strategy.rb +19 -0
  142. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  143. data/lib/active_record/migration.rb +219 -111
  144. data/lib/active_record/model_schema.rb +64 -44
  145. data/lib/active_record/nested_attributes.rb +24 -6
  146. data/lib/active_record/normalization.rb +167 -0
  147. data/lib/active_record/persistence.rb +188 -37
  148. data/lib/active_record/promise.rb +84 -0
  149. data/lib/active_record/query_cache.rb +3 -21
  150. data/lib/active_record/query_logs.rb +77 -52
  151. data/lib/active_record/query_logs_formatter.rb +41 -0
  152. data/lib/active_record/querying.rb +15 -2
  153. data/lib/active_record/railtie.rb +109 -47
  154. data/lib/active_record/railties/controller_runtime.rb +12 -6
  155. data/lib/active_record/railties/databases.rake +142 -148
  156. data/lib/active_record/railties/job_runtime.rb +23 -0
  157. data/lib/active_record/readonly_attributes.rb +32 -5
  158. data/lib/active_record/reflection.rb +174 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  160. data/lib/active_record/relation/batches.rb +190 -61
  161. data/lib/active_record/relation/calculations.rb +187 -63
  162. data/lib/active_record/relation/delegation.rb +23 -9
  163. data/lib/active_record/relation/finder_methods.rb +77 -16
  164. data/lib/active_record/relation/merger.rb +2 -0
  165. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  167. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  168. data/lib/active_record/relation/predicate_builder.rb +26 -14
  169. data/lib/active_record/relation/query_attribute.rb +2 -1
  170. data/lib/active_record/relation/query_methods.rb +352 -63
  171. data/lib/active_record/relation/spawn_methods.rb +18 -1
  172. data/lib/active_record/relation.rb +91 -35
  173. data/lib/active_record/result.rb +19 -5
  174. data/lib/active_record/runtime_registry.rb +24 -1
  175. data/lib/active_record/sanitization.rb +51 -11
  176. data/lib/active_record/schema.rb +2 -3
  177. data/lib/active_record/schema_dumper.rb +46 -7
  178. data/lib/active_record/schema_migration.rb +68 -33
  179. data/lib/active_record/scoping/default.rb +15 -5
  180. data/lib/active_record/scoping/named.rb +2 -2
  181. data/lib/active_record/scoping.rb +2 -1
  182. data/lib/active_record/secure_password.rb +60 -0
  183. data/lib/active_record/secure_token.rb +21 -3
  184. data/lib/active_record/signed_id.rb +7 -5
  185. data/lib/active_record/store.rb +8 -8
  186. data/lib/active_record/suppressor.rb +3 -1
  187. data/lib/active_record/table_metadata.rb +10 -1
  188. data/lib/active_record/tasks/database_tasks.rb +127 -105
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  192. data/lib/active_record/test_fixtures.rb +113 -96
  193. data/lib/active_record/timestamp.rb +27 -15
  194. data/lib/active_record/token_for.rb +113 -0
  195. data/lib/active_record/touch_later.rb +11 -6
  196. data/lib/active_record/transactions.rb +36 -10
  197. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  198. data/lib/active_record/type/internal/timezone.rb +7 -2
  199. data/lib/active_record/type/time.rb +4 -0
  200. data/lib/active_record/validations/absence.rb +1 -1
  201. data/lib/active_record/validations/numericality.rb +5 -4
  202. data/lib/active_record/validations/presence.rb +5 -28
  203. data/lib/active_record/validations/uniqueness.rb +47 -2
  204. data/lib/active_record/validations.rb +8 -4
  205. data/lib/active_record/version.rb +1 -1
  206. data/lib/active_record.rb +121 -16
  207. data/lib/arel/errors.rb +10 -0
  208. data/lib/arel/factory_methods.rb +4 -0
  209. data/lib/arel/nodes/binary.rb +6 -1
  210. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  211. data/lib/arel/nodes/cte.rb +36 -0
  212. data/lib/arel/nodes/fragments.rb +35 -0
  213. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  214. data/lib/arel/nodes/leading_join.rb +8 -0
  215. data/lib/arel/nodes/node.rb +111 -2
  216. data/lib/arel/nodes/sql_literal.rb +6 -0
  217. data/lib/arel/nodes/table_alias.rb +4 -0
  218. data/lib/arel/nodes.rb +4 -0
  219. data/lib/arel/predications.rb +2 -0
  220. data/lib/arel/table.rb +9 -5
  221. data/lib/arel/visitors/mysql.rb +8 -1
  222. data/lib/arel/visitors/to_sql.rb +81 -17
  223. data/lib/arel/visitors/visitor.rb +2 -2
  224. data/lib/arel.rb +16 -2
  225. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  226. data/lib/rails/generators/active_record/migration.rb +3 -1
  227. data/lib/rails/generators/active_record/model/USAGE +113 -0
  228. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  229. metadata +47 -11
  230. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  231. data/lib/active_record/null_relation.rb +0 -63
@@ -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
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
  #
@@ -39,10 +42,17 @@ module ActiveRecord
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
@@ -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
  #
@@ -258,6 +268,8 @@ module ActiveRecord
258
268
  # name: Reginald the Pirate
259
269
  # monkey_id: 1
260
270
  #
271
+ # <code></code>
272
+ #
261
273
  # ### in monkeys.yml
262
274
  #
263
275
  # george:
@@ -275,6 +287,8 @@ module ActiveRecord
275
287
  # name: Reginald the Pirate
276
288
  # monkey: george
277
289
  #
290
+ # <code></code>
291
+ #
278
292
  # ### in monkeys.yml
279
293
  #
280
294
  # george:
@@ -296,6 +310,8 @@ module ActiveRecord
296
310
  #
297
311
  # belongs_to :eater, polymorphic: true
298
312
  #
313
+ # <code></code>
314
+ #
299
315
  # ### in fruits.yml
300
316
  #
301
317
  # apple:
@@ -311,9 +327,9 @@ module ActiveRecord
311
327
  #
312
328
  # Just provide the polymorphic target type and Active Record will take care of the rest.
313
329
  #
314
- # === +has_and_belongs_to_many+ or <tt>has_many :through</tt>
330
+ # ==== +has_and_belongs_to_many+ or <tt>has_many :through</tt>
315
331
  #
316
- # Time to give our monkey some fruit.
332
+ # \Time to give our monkey some fruit.
317
333
  #
318
334
  # ### in monkeys.yml
319
335
  #
@@ -321,6 +337,8 @@ module ActiveRecord
321
337
  # id: 1
322
338
  # name: George the Monkey
323
339
  #
340
+ # <code></code>
341
+ #
324
342
  # ### in fruits.yml
325
343
  #
326
344
  # apple:
@@ -335,6 +353,8 @@ module ActiveRecord
335
353
  # id: 3
336
354
  # name: grape
337
355
  #
356
+ # <code></code>
357
+ #
338
358
  # ### in fruits_monkeys.yml
339
359
  #
340
360
  # apple_george:
@@ -358,6 +378,8 @@ module ActiveRecord
358
378
  # name: George the Monkey
359
379
  # fruits: apple, orange, grape
360
380
  #
381
+ # <code></code>
382
+ #
361
383
  # ### in fruits.yml
362
384
  #
363
385
  # apple:
@@ -375,7 +397,7 @@ module ActiveRecord
375
397
  # the fixture's model class and discovers the +has_and_belongs_to_many+
376
398
  # associations.
377
399
  #
378
- # == Autofilled Timestamp Columns
400
+ # === Autofilled \Timestamp Columns
379
401
  #
380
402
  # If your table/model specifies any of Active Record's
381
403
  # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
@@ -383,7 +405,7 @@ module ActiveRecord
383
405
  #
384
406
  # If you've set specific values, they'll be left alone.
385
407
  #
386
- # == Fixture label interpolation
408
+ # === Fixture label interpolation
387
409
  #
388
410
  # The label of the current fixture is always available as a column value:
389
411
  #
@@ -400,7 +422,11 @@ module ActiveRecord
400
422
  # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
401
423
  # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
402
424
  #
403
- # == Support for YAML defaults
425
+ # If the model uses UUID values for identifiers, add the +:uuid+ argument:
426
+ #
427
+ # ActiveRecord::FixtureSet.identify(:boaty_mcboatface, :uuid)
428
+ #
429
+ # === Support for YAML defaults
404
430
  #
405
431
  # You can set and reuse defaults in your fixtures YAML file.
406
432
  # This is the same technique used in the +database.yml+ file to specify
@@ -442,6 +468,45 @@ module ActiveRecord
442
468
  # In the above example, 'base' will be ignored when creating fixtures.
443
469
  # This can be used for common attributes inheriting.
444
470
  #
471
+ # == Composite Primary Key Fixtures
472
+ #
473
+ # Fixtures for composite primary key tables are fairly similar to normal tables.
474
+ # When using an id column, the column may be omitted as usual:
475
+ #
476
+ # # app/models/book.rb
477
+ # class Book < ApplicationRecord
478
+ # self.primary_key = [:author_id, :id]
479
+ # belongs_to :author
480
+ # end
481
+ #
482
+ # <code></code>
483
+ #
484
+ # # books.yml
485
+ # alices_adventure_in_wonderland:
486
+ # author_id: <%= ActiveRecord::FixtureSet.identify(:lewis_carroll) %>
487
+ # title: "Alice's Adventures in Wonderland"
488
+ #
489
+ # However, in order to support composite primary key relationships,
490
+ # you must use the `composite_identify` method:
491
+ #
492
+ # # app/models/book_orders.rb
493
+ # class BookOrder < ApplicationRecord
494
+ # self.primary_key = [:shop_id, :id]
495
+ # belongs_to :order, query_constraints: [:shop_id, :order_id]
496
+ # belongs_to :book, query_constraints: [:author_id, :book_id]
497
+ # end
498
+ #
499
+ # <code></code>
500
+ #
501
+ # # book_orders.yml
502
+ # alices_adventure_in_wonderland_in_books:
503
+ # author: lewis_carroll
504
+ # book_id: <%= ActiveRecord::FixtureSet.composite_identify(
505
+ # :alices_adventure_in_wonderland, Book.primary_key)[:id] %>
506
+ # shop: book_store
507
+ # order_id: <%= ActiveRecord::FixtureSet.composite_identify(
508
+ # :books, Order.primary_key)[:id] %>
509
+ #
445
510
  # == Configure the fixture model class
446
511
  #
447
512
  # It's possible to set the fixture's model class directly in the YAML file.
@@ -456,6 +521,10 @@ module ActiveRecord
456
521
  #
457
522
  # Any fixtures labeled "_fixture" are safely ignored.
458
523
  class FixtureSet
524
+ require "active_record/fixture_set/file"
525
+ require "active_record/fixture_set/render_context"
526
+ require "active_record/fixture_set/table_rows"
527
+
459
528
  #--
460
529
  # An instance of FixtureSet is normally stored in a single YAML file and
461
530
  # possibly in a folder with the same name.
@@ -467,39 +536,6 @@ module ActiveRecord
467
536
 
468
537
  cattr_accessor :all_loaded_fixtures, default: {}
469
538
 
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
539
  class << self
504
540
  def default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
505
541
  config.pluralize_table_names ?
@@ -552,9 +588,9 @@ module ActiveRecord
552
588
  end
553
589
  end
554
590
 
555
- def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
591
+ def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
556
592
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
557
- class_names = ClassCache.new class_names, config
593
+ class_names.stringify_keys!
558
594
 
559
595
  # FIXME: Apparently JK uses this.
560
596
  connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
@@ -565,7 +601,7 @@ module ActiveRecord
565
601
 
566
602
  if fixture_files_to_read.any?
567
603
  fixtures_map = read_and_insert(
568
- fixtures_directory,
604
+ Array(fixtures_directories),
569
605
  fixture_files_to_read,
570
606
  class_names,
571
607
  connection,
@@ -576,7 +612,8 @@ module ActiveRecord
576
612
  end
577
613
 
578
614
  # 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.
615
+ #
616
+ # \Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
580
617
  def identify(label, column_type = :integer)
581
618
  if column_type == :uuid
582
619
  Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
@@ -585,21 +622,35 @@ module ActiveRecord
585
622
  end
586
623
  end
587
624
 
588
- # Superclass for the evaluation contexts used by ERB fixtures.
625
+ # Returns a consistent, platform-independent hash representing a mapping
626
+ # between the label and the subcomponents of the provided composite key.
627
+ #
628
+ # Example:
629
+ #
630
+ # composite_identify("label", [:a, :b, :c]) # => { a: hash_1, b: hash_2, c: hash_3 }
631
+ def composite_identify(label, key)
632
+ key
633
+ .index_with
634
+ .with_index { |sub_key, index| (identify(label) << index) % MAX_ID }
635
+ .with_indifferent_access
636
+ end
637
+
638
+ # Superclass for the evaluation contexts used by \ERB fixtures.
589
639
  def context_class
590
640
  @context_class ||= Class.new
591
641
  end
592
642
 
593
643
  private
594
- def read_and_insert(fixtures_directory, fixture_files, class_names, connection) # :nodoc:
644
+ def read_and_insert(fixtures_directories, fixture_files, class_names, connection) # :nodoc:
595
645
  fixtures_map = {}
646
+ directory_glob = "{#{fixtures_directories.join(",")}}"
596
647
  fixture_sets = fixture_files.map do |fixture_set_name|
597
648
  klass = class_names[fixture_set_name]
598
649
  fixtures_map[fixture_set_name] = new( # ActiveRecord::FixtureSet.new
599
650
  nil,
600
651
  fixture_set_name,
601
652
  klass,
602
- ::File.join(fixtures_directory, fixture_set_name)
653
+ ::File.join(directory_glob, fixture_set_name)
603
654
  )
604
655
  end
605
656
  update_all_loaded_fixtures(fixtures_map)
@@ -629,9 +680,7 @@ module ActiveRecord
629
680
 
630
681
  conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
631
682
 
632
- if ActiveRecord.verify_foreign_keys_for_fixtures && !conn.all_foreign_keys_valid?
633
- raise "Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations."
634
- end
683
+ check_all_foreign_keys_valid!(conn)
635
684
 
636
685
  # Cap primary key sequences to max(pk).
637
686
  if conn.respond_to?(:reset_pk_sequence!)
@@ -640,6 +689,16 @@ module ActiveRecord
640
689
  end
641
690
  end
642
691
 
692
+ def check_all_foreign_keys_valid!(conn)
693
+ return unless ActiveRecord.verify_foreign_keys_for_fixtures
694
+
695
+ begin
696
+ conn.check_all_foreign_keys_valid!
697
+ rescue ActiveRecord::StatementInvalid => e
698
+ 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}"
699
+ end
700
+ end
701
+
643
702
  def update_all_loaded_fixtures(fixtures_map) # :nodoc:
644
703
  all_loaded_fixtures.update(fixtures_map)
645
704
  end
@@ -653,7 +712,6 @@ module ActiveRecord
653
712
  @config = config
654
713
 
655
714
  self.model_class = class_name
656
-
657
715
  @fixtures = read_fixture_files(path)
658
716
 
659
717
  @table_name = model_class&.table_name || self.class.default_fixture_table_name(name, config)
@@ -715,14 +773,18 @@ module ActiveRecord
715
773
  # Loads the fixtures from the YAML file at +path+.
716
774
  # If the file sets the +model_class+ and current instance value is not set,
717
775
  # it uses the file value.
776
+
718
777
  def read_fixture_files(path)
719
- yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
778
+ yaml_files = Dir["#{path}{.yml,/{**,*}/*.yml}"].select { |f|
720
779
  ::File.file?(f)
721
- } + [yaml_file_path(path)]
780
+ }
781
+
782
+ raise ArgumentError, "No fixture files found for #{@name}" if yaml_files.empty?
722
783
 
723
784
  yaml_files.each_with_object({}) do |file, fixtures|
724
785
  FixtureSet::File.open(file) do |fh|
725
786
  self.model_class ||= fh.model_class if fh.model_class
787
+ self.model_class ||= default_fixture_model_class
726
788
  self.ignored_fixtures ||= fh.ignored_fixtures
727
789
  fh.each do |fixture_name, row|
728
790
  fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
@@ -731,8 +793,9 @@ module ActiveRecord
731
793
  end
732
794
  end
733
795
 
734
- def yaml_file_path(path)
735
- "#{path}.yml"
796
+ def default_fixture_model_class
797
+ klass = ActiveRecord::FixtureSet.default_fixture_model_name(@name, @config).safe_constantize
798
+ klass if klass && klass < ActiveRecord::Base
736
799
  end
737
800
  end
738
801
 
@@ -769,7 +832,8 @@ module ActiveRecord
769
832
  def find
770
833
  raise FixtureClassNotFound, "No class attached to find." unless model_class
771
834
  object = model_class.unscoped do
772
- model_class.find(fixture[model_class.primary_key])
835
+ pk_clauses = fixture.slice(*Array(model_class.primary_key))
836
+ model_class.find_by!(pk_clauses)
773
837
  end
774
838
  # Fixtures can't be eagerly loaded
775
839
  object.instance_variable_set(:@strict_loading, false)
@@ -2,6 +2,27 @@
2
2
 
3
3
  module ActiveRecord
4
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
+
5
26
  class EventBuffer
6
27
  def initialize(future_result, instrumenter)
7
28
  @future_result = future_result
@@ -27,6 +48,7 @@ module ActiveRecord
27
48
  Canceled = Class.new(ActiveRecordError)
28
49
 
29
50
  delegate :empty?, :to_a, to: :result
51
+ delegate_missing_to :result
30
52
 
31
53
  attr_reader :lock_wait
32
54
 
@@ -45,6 +67,10 @@ module ActiveRecord
45
67
  @event_buffer = nil
46
68
  end
47
69
 
70
+ def then(&block)
71
+ Promise.new(self, block)
72
+ end
73
+
48
74
  def schedule!(session)
49
75
  @session = session
50
76
  @pool.schedule_query(self)
@@ -95,11 +121,11 @@ module ActiveRecord
95
121
  @pending && (!@session || @session.active?)
96
122
  end
97
123
 
98
- private
99
- def canceled?
100
- @session && !@session.active?
101
- end
124
+ def canceled?
125
+ @session && !@session.active?
126
+ end
102
127
 
128
+ private
103
129
  def execute_or_wait
104
130
  if pending?
105
131
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
@@ -124,7 +150,7 @@ module ActiveRecord
124
150
  end
125
151
 
126
152
  def exec_query(connection, *args, **kwargs)
127
- connection.exec_query(*args, **kwargs)
153
+ connection.internal_exec_query(*args, **kwargs)
128
154
  end
129
155
 
130
156
  class SelectAll < FutureResult # :nodoc:
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- # Returns the currently loaded version of 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
10
  MAJOR = 7
11
- MINOR = 0
12
- TINY = 8
11
+ MINOR = 1
12
+ TINY = 3
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -4,7 +4,7 @@ require "active_support/inflector"
4
4
  require "active_support/core_ext/hash/indifferent_access"
5
5
 
6
6
  module ActiveRecord
7
- # == Single table inheritance
7
+ # = Single table inheritance
8
8
  #
9
9
  # Active Record allows inheritance by storing the name of the class in a column that by
10
10
  # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
@@ -32,8 +32,9 @@ module ActiveRecord
32
32
  # be triggered. In that case, it'll work just like normal subclasses with no special magic
33
33
  # for differentiating between them or reloading the right type with find.
34
34
  #
35
- # Note, all the attributes for all the cases are kept in the same table. Read more:
36
- # https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
35
+ # Note, all the attributes for all the cases are kept in the same table.
36
+ # Read more:
37
+ # * https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
37
38
  #
38
39
  module Inheritance
39
40
  extend ActiveSupport::Concern
@@ -93,14 +94,24 @@ module ActiveRecord
93
94
  :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
94
95
  end
95
96
 
96
- # Returns the class descending directly from ActiveRecord::Base, or
97
- # an abstract class, if any, in the inheritance hierarchy.
97
+ # Returns the first class in the inheritance hierarchy that descends from either an
98
+ # abstract class or from <tt>ActiveRecord::Base</tt>.
98
99
  #
99
- # If A extends ActiveRecord::Base, A.base_class will return A. If B descends from A
100
- # through some arbitrarily deep hierarchy, B.base_class will return A.
100
+ # Consider the following behaviour:
101
101
  #
102
- # If B < A and C < B and if A is an abstract_class then both B.base_class
103
- # and C.base_class would return B as the answer since A is an abstract_class.
102
+ # class ApplicationRecord < ActiveRecord::Base
103
+ # self.abstract_class = true
104
+ # end
105
+ # class Shape < ApplicationRecord
106
+ # self.abstract_class = true
107
+ # end
108
+ # Polygon = Class.new(Shape)
109
+ # Square = Class.new(Polygon)
110
+ #
111
+ # ApplicationRecord.base_class # => ApplicationRecord
112
+ # Shape.base_class # => Shape
113
+ # Polygon.base_class # => Polygon
114
+ # Square.base_class # => Polygon
104
115
  attr_reader :base_class
105
116
 
106
117
  # Returns whether the class is a base class.
@@ -116,7 +127,7 @@ module ActiveRecord
116
127
  # true.
117
128
  # +ApplicationRecord+, for example, is generated as an abstract class.
118
129
  #
119
- # Consider the following default behaviour:
130
+ # Consider the following default behavior:
120
131
  #
121
132
  # Shape = Class.new(ActiveRecord::Base)
122
133
  # Polygon = Class.new(Shape)
@@ -210,12 +221,6 @@ module ActiveRecord
210
221
  end
211
222
  end
212
223
 
213
- def inherited(subclass)
214
- subclass.set_base_class
215
- subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
216
- super
217
- end
218
-
219
224
  def dup # :nodoc:
220
225
  # `initialize_dup` / `initialize_copy` don't work when defined
221
226
  # in the `singleton_class`.
@@ -277,6 +282,15 @@ module ActiveRecord
277
282
  end
278
283
 
279
284
  private
285
+ def inherited(subclass)
286
+ super
287
+ subclass.set_base_class
288
+ subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
289
+ subclass.class_eval do
290
+ @finder_needs_type_condition = nil
291
+ end
292
+ end
293
+
280
294
  # Called by +instantiate+ to decide which class to use for a new
281
295
  # record instance. For single-table inheritance, we check the record
282
296
  # for a +type+ column and return the corresponding class.