activerecord 7.0.8 → 7.1.3.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1554 -1452
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +313 -217
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +52 -34
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +135 -71
- data/lib/active_record/future_result.rb +31 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +104 -5
- data/lib/active_record/migration/compatibility.rb +139 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +219 -111
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +142 -148
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +174 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +352 -63
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +48 -12
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
+
class ReadonlyAttributeError < ActiveRecordError
|
5
|
+
end
|
6
|
+
|
4
7
|
module ReadonlyAttributes
|
5
8
|
extend ActiveSupport::Concern
|
6
9
|
|
@@ -9,10 +12,11 @@ module ActiveRecord
|
|
9
12
|
end
|
10
13
|
|
11
14
|
module ClassMethods
|
12
|
-
# Attributes listed as readonly will be used to create a new record
|
13
|
-
#
|
15
|
+
# Attributes listed as readonly will be used to create a new record.
|
16
|
+
# Assigning a new value to a readonly attribute on a persisted record raises an error.
|
14
17
|
#
|
15
|
-
#
|
18
|
+
# By setting +config.active_record.raise_on_assign_to_attr_readonly+ to +false+, it will
|
19
|
+
# not raise. The value will change in memory, but will not be persisted on +save+.
|
16
20
|
#
|
17
21
|
# ==== Examples
|
18
22
|
#
|
@@ -21,9 +25,14 @@ module ActiveRecord
|
|
21
25
|
# end
|
22
26
|
#
|
23
27
|
# post = Post.create!(title: "Introducing Ruby on Rails!")
|
24
|
-
# post.
|
28
|
+
# post.title = "a different title" # raises ActiveRecord::ReadonlyAttributeError
|
29
|
+
# post.update(title: "a different title") # raises ActiveRecord::ReadonlyAttributeError
|
25
30
|
def attr_readonly(*attributes)
|
26
|
-
self._attr_readonly
|
31
|
+
self._attr_readonly |= attributes.map(&:to_s)
|
32
|
+
|
33
|
+
if ActiveRecord.raise_on_assign_to_attr_readonly
|
34
|
+
include(HasReadonlyAttributes)
|
35
|
+
end
|
27
36
|
end
|
28
37
|
|
29
38
|
# Returns an array of all the attributes that have been specified as readonly.
|
@@ -35,5 +44,23 @@ module ActiveRecord
|
|
35
44
|
_attr_readonly.include?(name)
|
36
45
|
end
|
37
46
|
end
|
47
|
+
|
48
|
+
module HasReadonlyAttributes # :nodoc:
|
49
|
+
def write_attribute(attr_name, value)
|
50
|
+
if !new_record? && self.class.readonly_attribute?(attr_name.to_s)
|
51
|
+
raise ReadonlyAttributeError.new(attr_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
def _write_attribute(attr_name, value)
|
58
|
+
if !new_record? && self.class.readonly_attribute?(attr_name.to_s)
|
59
|
+
raise ReadonlyAttributeError.new(attr_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
38
65
|
end
|
39
66
|
end
|
@@ -46,6 +46,8 @@ module ActiveRecord
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
# = Active Record Reflection
|
50
|
+
#
|
49
51
|
# \Reflection enables the ability to examine the associations and aggregations of
|
50
52
|
# Active Record classes and objects. This information, for example,
|
51
53
|
# can be used in a form builder that takes an Active Record object
|
@@ -128,6 +130,14 @@ module ActiveRecord
|
|
128
130
|
def clear_reflections_cache # :nodoc:
|
129
131
|
@__reflections = nil
|
130
132
|
end
|
133
|
+
|
134
|
+
private
|
135
|
+
def inherited(subclass)
|
136
|
+
super
|
137
|
+
subclass.class_eval do
|
138
|
+
@__reflections = nil
|
139
|
+
end
|
140
|
+
end
|
131
141
|
end
|
132
142
|
|
133
143
|
# Holds all the methods that are shared between MacroReflection and ThroughReflection.
|
@@ -144,6 +154,14 @@ module ActiveRecord
|
|
144
154
|
# PolymorphicReflection
|
145
155
|
# RuntimeReflection
|
146
156
|
class AbstractReflection # :nodoc:
|
157
|
+
def initialize
|
158
|
+
@class_name = nil
|
159
|
+
@counter_cache_column = nil
|
160
|
+
@inverse_of = nil
|
161
|
+
@inverse_which_updates_counter_cache_defined = false
|
162
|
+
@inverse_which_updates_counter_cache = nil
|
163
|
+
end
|
164
|
+
|
147
165
|
def through_reflection?
|
148
166
|
false
|
149
167
|
end
|
@@ -183,10 +201,14 @@ module ActiveRecord
|
|
183
201
|
|
184
202
|
scope_chain_items.inject(klass_scope, &:merge!)
|
185
203
|
|
186
|
-
|
187
|
-
|
204
|
+
primary_key_column_names = Array(join_primary_key)
|
205
|
+
foreign_key_column_names = Array(join_foreign_key)
|
206
|
+
|
207
|
+
primary_foreign_key_pairs = primary_key_column_names.zip(foreign_key_column_names)
|
188
208
|
|
189
|
-
|
209
|
+
primary_foreign_key_pairs.each do |primary_key_column_name, foreign_key_column_name|
|
210
|
+
klass_scope.where!(table[primary_key_column_name].eq(foreign_table[foreign_key_column_name]))
|
211
|
+
end
|
190
212
|
|
191
213
|
if klass.finder_needs_type_condition?
|
192
214
|
klass_scope.where!(klass.send(:type_condition, table))
|
@@ -231,11 +253,11 @@ module ActiveRecord
|
|
231
253
|
end
|
232
254
|
|
233
255
|
def check_validity_of_inverse!
|
234
|
-
|
235
|
-
if
|
256
|
+
if !polymorphic? && has_inverse?
|
257
|
+
if inverse_of.nil?
|
236
258
|
raise InverseOfAssociationNotFoundError.new(self)
|
237
259
|
end
|
238
|
-
if
|
260
|
+
if inverse_of == self
|
239
261
|
raise InverseOfAssociationRecursiveError.new(self)
|
240
262
|
end
|
241
263
|
end
|
@@ -252,10 +274,16 @@ module ActiveRecord
|
|
252
274
|
#
|
253
275
|
# Hence this method.
|
254
276
|
def inverse_which_updates_counter_cache
|
255
|
-
|
256
|
-
|
257
|
-
|
277
|
+
unless @inverse_which_updates_counter_cache_defined
|
278
|
+
if counter_cache_column
|
279
|
+
inverse_candidates = inverse_of ? [inverse_of] : klass.reflect_on_all_associations(:belongs_to)
|
280
|
+
@inverse_which_updates_counter_cache = inverse_candidates.find do |inverse|
|
281
|
+
inverse.counter_cache_column == counter_cache_column && (inverse.polymorphic? || inverse.klass == active_record)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
@inverse_which_updates_counter_cache_defined = true
|
258
285
|
end
|
286
|
+
@inverse_which_updates_counter_cache
|
259
287
|
end
|
260
288
|
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
261
289
|
|
@@ -346,6 +374,7 @@ module ActiveRecord
|
|
346
374
|
attr_reader :plural_name # :nodoc:
|
347
375
|
|
348
376
|
def initialize(name, scope, options, active_record)
|
377
|
+
super()
|
349
378
|
@name = name
|
350
379
|
@scope = scope
|
351
380
|
@options = options
|
@@ -353,6 +382,7 @@ module ActiveRecord
|
|
353
382
|
@klass = options[:anonymous_class]
|
354
383
|
@plural_name = active_record.pluralize_table_names ?
|
355
384
|
name.to_s.pluralize : name.to_s
|
385
|
+
validate_reflection!
|
356
386
|
end
|
357
387
|
|
358
388
|
def autosave=(autosave)
|
@@ -404,6 +434,17 @@ module ActiveRecord
|
|
404
434
|
def derive_class_name
|
405
435
|
name.to_s.camelize
|
406
436
|
end
|
437
|
+
|
438
|
+
def validate_reflection!
|
439
|
+
return unless options[:foreign_key].is_a?(Array)
|
440
|
+
|
441
|
+
message = <<~MSG.squish
|
442
|
+
Passing #{options[:foreign_key]} array to :foreign_key option
|
443
|
+
on the #{active_record}##{name} association is not supported.
|
444
|
+
Use the query_constraints: #{options[:foreign_key]} option instead to represent a composite foreign key.
|
445
|
+
MSG
|
446
|
+
raise ArgumentError, message
|
447
|
+
end
|
407
448
|
end
|
408
449
|
|
409
450
|
# Holds all the metadata about an aggregation as it was specified in the
|
@@ -423,23 +464,23 @@ module ActiveRecord
|
|
423
464
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
424
465
|
end
|
425
466
|
|
426
|
-
msg = <<-MSG.squish
|
427
|
-
Rails couldn't find a valid model for #{name} association.
|
428
|
-
Please provide the :class_name option on the association declaration.
|
429
|
-
If :class_name is already provided, make sure it's an ActiveRecord::Base subclass.
|
430
|
-
MSG
|
431
|
-
|
432
467
|
begin
|
433
468
|
klass = active_record.send(:compute_type, name)
|
434
|
-
|
435
|
-
|
436
|
-
|
469
|
+
rescue NameError => error
|
470
|
+
if error.name.match?(/(?:\A|::)#{name}\z/)
|
471
|
+
message = "Missing model class #{name} for the #{active_record}##{self.name} association."
|
472
|
+
message += " You can specify a different model class with the :class_name option." unless options[:class_name]
|
473
|
+
raise NameError.new(message, name)
|
474
|
+
else
|
475
|
+
raise
|
437
476
|
end
|
477
|
+
end
|
438
478
|
|
439
|
-
|
440
|
-
|
441
|
-
raise NameError, msg
|
479
|
+
unless klass < ActiveRecord::Base
|
480
|
+
raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass."
|
442
481
|
end
|
482
|
+
|
483
|
+
klass
|
443
484
|
end
|
444
485
|
|
445
486
|
attr_reader :type, :foreign_type
|
@@ -449,6 +490,10 @@ module ActiveRecord
|
|
449
490
|
super
|
450
491
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
451
492
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
493
|
+
@join_table = nil
|
494
|
+
@foreign_key = nil
|
495
|
+
@association_foreign_key = nil
|
496
|
+
@association_primary_key = nil
|
452
497
|
|
453
498
|
ensure_option_not_given_as_class!(:class_name)
|
454
499
|
end
|
@@ -465,8 +510,20 @@ module ActiveRecord
|
|
465
510
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
466
511
|
end
|
467
512
|
|
468
|
-
def foreign_key
|
469
|
-
@foreign_key ||=
|
513
|
+
def foreign_key(infer_from_inverse_of: true)
|
514
|
+
@foreign_key ||= if options[:query_constraints]
|
515
|
+
options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
|
516
|
+
elsif options[:foreign_key]
|
517
|
+
options[:foreign_key].to_s
|
518
|
+
else
|
519
|
+
derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
|
520
|
+
|
521
|
+
if active_record.has_query_constraints?
|
522
|
+
derived_fk = derive_fk_query_constraints(derived_fk)
|
523
|
+
end
|
524
|
+
|
525
|
+
derived_fk
|
526
|
+
end
|
470
527
|
end
|
471
528
|
|
472
529
|
def association_foreign_key
|
@@ -478,7 +535,22 @@ module ActiveRecord
|
|
478
535
|
end
|
479
536
|
|
480
537
|
def active_record_primary_key
|
481
|
-
|
538
|
+
custom_primary_key = options[:primary_key]
|
539
|
+
@active_record_primary_key ||= if custom_primary_key
|
540
|
+
if custom_primary_key.is_a?(Array)
|
541
|
+
custom_primary_key.map { |pk| pk.to_s.freeze }.freeze
|
542
|
+
else
|
543
|
+
custom_primary_key.to_s.freeze
|
544
|
+
end
|
545
|
+
elsif active_record.has_query_constraints? || options[:query_constraints]
|
546
|
+
active_record.query_constraints_list
|
547
|
+
elsif active_record.composite_primary_key?
|
548
|
+
# If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
549
|
+
primary_key = primary_key(active_record)
|
550
|
+
primary_key.include?("id") ? "id" : primary_key.freeze
|
551
|
+
else
|
552
|
+
primary_key(active_record).freeze
|
553
|
+
end
|
482
554
|
end
|
483
555
|
|
484
556
|
def join_primary_key(klass = nil)
|
@@ -495,6 +567,14 @@ module ActiveRecord
|
|
495
567
|
|
496
568
|
def check_validity!
|
497
569
|
check_validity_of_inverse!
|
570
|
+
|
571
|
+
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
572
|
+
if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length
|
573
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
574
|
+
elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length
|
575
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
576
|
+
end
|
577
|
+
end
|
498
578
|
end
|
499
579
|
|
500
580
|
def check_eager_loadable!
|
@@ -510,7 +590,7 @@ module ActiveRecord
|
|
510
590
|
end
|
511
591
|
|
512
592
|
def join_id_for(owner) # :nodoc:
|
513
|
-
owner
|
593
|
+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
|
514
594
|
end
|
515
595
|
|
516
596
|
def through_reflection
|
@@ -631,7 +711,9 @@ module ActiveRecord
|
|
631
711
|
|
632
712
|
begin
|
633
713
|
reflection = klass._reflect_on_association(inverse_name)
|
634
|
-
rescue NameError
|
714
|
+
rescue NameError => error
|
715
|
+
raise unless error.name.to_s == class_name
|
716
|
+
|
635
717
|
# Give up: we couldn't compute the klass type so we won't be able
|
636
718
|
# to find any associations either.
|
637
719
|
reflection = false
|
@@ -688,16 +770,56 @@ module ActiveRecord
|
|
688
770
|
class_name.camelize
|
689
771
|
end
|
690
772
|
|
691
|
-
def derive_foreign_key
|
773
|
+
def derive_foreign_key(infer_from_inverse_of: true)
|
692
774
|
if belongs_to?
|
693
775
|
"#{name}_id"
|
694
776
|
elsif options[:as]
|
695
777
|
"#{options[:as]}_id"
|
778
|
+
elsif options[:inverse_of] && infer_from_inverse_of
|
779
|
+
inverse_of.foreign_key(infer_from_inverse_of: false)
|
696
780
|
else
|
697
781
|
active_record.model_name.to_s.foreign_key
|
698
782
|
end
|
699
783
|
end
|
700
784
|
|
785
|
+
def derive_fk_query_constraints(foreign_key)
|
786
|
+
primary_query_constraints = active_record.query_constraints_list
|
787
|
+
owner_pk = active_record.primary_key
|
788
|
+
|
789
|
+
if primary_query_constraints.size != 2
|
790
|
+
raise ArgumentError, <<~MSG.squish
|
791
|
+
The query constraints list on the `#{active_record}` model has more than 2
|
792
|
+
attributes. Active Record is unable to derive the query constraints
|
793
|
+
for the association. You need to explicitly define the query constraints
|
794
|
+
for this association.
|
795
|
+
MSG
|
796
|
+
end
|
797
|
+
|
798
|
+
if !primary_query_constraints.include?(owner_pk)
|
799
|
+
raise ArgumentError, <<~MSG.squish
|
800
|
+
The query constraints on the `#{active_record}` model does not include the primary
|
801
|
+
key so Active Record is unable to derive the foreign key constraints for
|
802
|
+
the association. You need to explicitly define the query constraints for this
|
803
|
+
association.
|
804
|
+
MSG
|
805
|
+
end
|
806
|
+
|
807
|
+
first_key, last_key = primary_query_constraints
|
808
|
+
|
809
|
+
if first_key == owner_pk
|
810
|
+
[foreign_key, last_key.to_s]
|
811
|
+
elsif last_key == owner_pk
|
812
|
+
[first_key.to_s, foreign_key]
|
813
|
+
else
|
814
|
+
raise ArgumentError, <<~MSG.squish
|
815
|
+
Active Record couldn't correctly interpret the query constraints
|
816
|
+
for the `#{active_record}` model. The query constraints on `#{active_record}` are
|
817
|
+
`#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
|
818
|
+
You need to explicitly set the query constraints for this association.
|
819
|
+
MSG
|
820
|
+
end
|
821
|
+
end
|
822
|
+
|
701
823
|
def derive_join_table
|
702
824
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
703
825
|
end
|
@@ -748,6 +870,12 @@ module ActiveRecord
|
|
748
870
|
def association_primary_key(klass = nil)
|
749
871
|
if primary_key = options[:primary_key]
|
750
872
|
@association_primary_key ||= -primary_key.to_s
|
873
|
+
elsif (klass || self.klass).has_query_constraints? || options[:query_constraints]
|
874
|
+
(klass || self.klass).composite_query_constraints_list
|
875
|
+
elsif (klass || self.klass).composite_primary_key?
|
876
|
+
# If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
877
|
+
primary_key = (klass || self.klass).primary_key
|
878
|
+
primary_key.include?("id") ? "id" : primary_key
|
751
879
|
else
|
752
880
|
primary_key(klass || self.klass)
|
753
881
|
end
|
@@ -786,6 +914,7 @@ module ActiveRecord
|
|
786
914
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
787
915
|
|
788
916
|
def initialize(delegate_reflection)
|
917
|
+
super()
|
789
918
|
@delegate_reflection = delegate_reflection
|
790
919
|
@klass = delegate_reflection.options[:anonymous_class]
|
791
920
|
@source_reflection_name = delegate_reflection.options[:source]
|
@@ -919,24 +1048,23 @@ module ActiveRecord
|
|
919
1048
|
end
|
920
1049
|
|
921
1050
|
def source_reflection_name # :nodoc:
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
1051
|
+
@source_reflection_name ||= begin
|
1052
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
1053
|
+
names = names.find_all { |n|
|
1054
|
+
through_reflection.klass._reflect_on_association(n)
|
1055
|
+
}
|
1056
|
+
|
1057
|
+
if names.length > 1
|
1058
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
1059
|
+
active_record.name,
|
1060
|
+
macro,
|
1061
|
+
name,
|
1062
|
+
options,
|
1063
|
+
source_reflection_names
|
1064
|
+
)
|
1065
|
+
end
|
1066
|
+
names.first
|
937
1067
|
end
|
938
|
-
|
939
|
-
@source_reflection_name = names.first
|
940
1068
|
end
|
941
1069
|
|
942
1070
|
def source_options
|
@@ -1040,6 +1168,7 @@ module ActiveRecord
|
|
1040
1168
|
:name, :scope_for, to: :@reflection
|
1041
1169
|
|
1042
1170
|
def initialize(reflection, previous_reflection)
|
1171
|
+
super()
|
1043
1172
|
@reflection = reflection
|
1044
1173
|
@previous_reflection = previous_reflection
|
1045
1174
|
end
|
@@ -1065,6 +1194,7 @@ module ActiveRecord
|
|
1065
1194
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1066
1195
|
|
1067
1196
|
def initialize(reflection, association)
|
1197
|
+
super()
|
1068
1198
|
@reflection = reflection
|
1069
1199
|
@association = association
|
1070
1200
|
end
|
@@ -5,11 +5,13 @@ module ActiveRecord
|
|
5
5
|
class BatchEnumerator
|
6
6
|
include Enumerable
|
7
7
|
|
8
|
-
def initialize(of: 1000, start: nil, finish: nil, relation:) # :nodoc:
|
8
|
+
def initialize(of: 1000, start: nil, finish: nil, relation:, order: :asc, use_ranges: nil) # :nodoc:
|
9
9
|
@of = of
|
10
10
|
@relation = relation
|
11
11
|
@start = start
|
12
12
|
@finish = finish
|
13
|
+
@order = order
|
14
|
+
@use_ranges = use_ranges
|
13
15
|
end
|
14
16
|
|
15
17
|
# The primary key value from which the BatchEnumerator starts, inclusive of the value.
|
@@ -50,7 +52,7 @@ module ActiveRecord
|
|
50
52
|
def each_record(&block)
|
51
53
|
return to_enum(:each_record) unless block_given?
|
52
54
|
|
53
|
-
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
|
55
|
+
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true, order: @order).each do |relation|
|
54
56
|
relation.records.each(&block)
|
55
57
|
end
|
56
58
|
end
|
@@ -90,7 +92,7 @@ module ActiveRecord
|
|
90
92
|
# relation.update_all(awesome: true)
|
91
93
|
# end
|
92
94
|
def each(&block)
|
93
|
-
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
|
95
|
+
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false, order: @order, use_ranges: @use_ranges)
|
94
96
|
return enum.each(&block) if block_given?
|
95
97
|
enum
|
96
98
|
end
|