activerecord 7.1.3.4 → 7.2.2.1
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 +652 -2032
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +15 -8
- data/lib/active_record/associations/belongs_to_association.rb +18 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +11 -5
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_many_through_association.rb +7 -1
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -12
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +62 -289
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +2 -2
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +4 -16
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +89 -58
- data/lib/active_record/attributes.rb +61 -47
- data/lib/active_record/autosave_association.rb +17 -31
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +270 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +190 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +23 -10
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
- data/lib/active_record/connection_adapters/abstract_adapter.rb +38 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +73 -19
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +18 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +93 -40
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +19 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +3 -3
- data/lib/active_record/encryption/encrypted_attribute_type.rb +26 -6
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +19 -2
- data/lib/active_record/errors.rb +46 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +38 -70
- data/lib/active_record/nested_attributes.rb +24 -5
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +50 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +42 -45
- data/lib/active_record/reflection.rb +106 -38
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +14 -8
- data/lib/active_record/relation/calculations.rb +96 -63
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +245 -65
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +500 -66
- data/lib/active_record/result.rb +32 -45
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/signed_id.rb +20 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +98 -48
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +87 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +5 -3
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +70 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +15 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +150 -41
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +21 -15
@@ -0,0 +1,265 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class AssociationNotFoundError < ConfigurationError # :nodoc:
|
5
|
+
attr_reader :record, :association_name
|
6
|
+
|
7
|
+
def initialize(record = nil, association_name = nil)
|
8
|
+
@record = record
|
9
|
+
@association_name = association_name
|
10
|
+
if record && association_name
|
11
|
+
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
|
12
|
+
else
|
13
|
+
super("Association was not found.")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
18
|
+
include DidYouMean::Correctable
|
19
|
+
|
20
|
+
def corrections
|
21
|
+
if record && association_name
|
22
|
+
@corrections ||= begin
|
23
|
+
maybe_these = record.class.reflections.keys
|
24
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(association_name)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class InverseOfAssociationNotFoundError < ActiveRecordError # :nodoc:
|
34
|
+
attr_reader :reflection, :associated_class
|
35
|
+
|
36
|
+
def initialize(reflection = nil, associated_class = nil)
|
37
|
+
if reflection
|
38
|
+
@reflection = reflection
|
39
|
+
@associated_class = associated_class.nil? ? reflection.klass : associated_class
|
40
|
+
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
|
41
|
+
else
|
42
|
+
super("Could not find the inverse association.")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
47
|
+
include DidYouMean::Correctable
|
48
|
+
|
49
|
+
def corrections
|
50
|
+
if reflection && associated_class
|
51
|
+
@corrections ||= begin
|
52
|
+
maybe_these = associated_class.reflections.keys
|
53
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:inverse_of].to_s)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
[]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class InverseOfAssociationRecursiveError < ActiveRecordError # :nodoc:
|
63
|
+
attr_reader :reflection
|
64
|
+
def initialize(reflection = nil)
|
65
|
+
if reflection
|
66
|
+
@reflection = reflection
|
67
|
+
super("Inverse association #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name}) is recursive.")
|
68
|
+
else
|
69
|
+
super("Inverse association is recursive.")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class HasManyThroughAssociationNotFoundError < ActiveRecordError # :nodoc:
|
75
|
+
attr_reader :owner_class, :reflection
|
76
|
+
|
77
|
+
def initialize(owner_class = nil, reflection = nil)
|
78
|
+
if owner_class && reflection
|
79
|
+
@owner_class = owner_class
|
80
|
+
@reflection = reflection
|
81
|
+
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
|
82
|
+
else
|
83
|
+
super("Could not find the association.")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
88
|
+
include DidYouMean::Correctable
|
89
|
+
|
90
|
+
def corrections
|
91
|
+
if owner_class && reflection
|
92
|
+
@corrections ||= begin
|
93
|
+
maybe_these = owner_class.reflections.keys
|
94
|
+
maybe_these -= [reflection.name.to_s] # remove failing reflection
|
95
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:through].to_s)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
[]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError # :nodoc:
|
105
|
+
def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
|
106
|
+
if owner_class_name && reflection && source_reflection
|
107
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
|
108
|
+
else
|
109
|
+
super("Cannot have a has_many :through association.")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
|
115
|
+
def initialize(owner_class_name = nil, reflection = nil)
|
116
|
+
if owner_class_name && reflection
|
117
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
|
118
|
+
else
|
119
|
+
super("Cannot have a has_many :through association.")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError # :nodoc:
|
125
|
+
def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
|
126
|
+
if owner_class_name && reflection && source_reflection
|
127
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
|
128
|
+
else
|
129
|
+
super("Cannot have a has_many :through association.")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class HasOneThroughCantAssociateThroughCollection < ActiveRecordError # :nodoc:
|
135
|
+
def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
|
136
|
+
if owner_class_name && reflection && through_reflection
|
137
|
+
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
|
138
|
+
else
|
139
|
+
super("Cannot have a has_one :through association.")
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class HasOneAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
|
145
|
+
def initialize(owner_class_name = nil, reflection = nil)
|
146
|
+
if owner_class_name && reflection
|
147
|
+
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
|
148
|
+
else
|
149
|
+
super("Cannot have a has_one :through association.")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError # :nodoc:
|
155
|
+
def initialize(reflection = nil)
|
156
|
+
if reflection
|
157
|
+
through_reflection = reflection.through_reflection
|
158
|
+
source_reflection_names = reflection.source_reflection_names
|
159
|
+
source_associations = reflection.through_reflection.klass._reflections.keys
|
160
|
+
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
|
161
|
+
else
|
162
|
+
super("Could not find the source association(s).")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class HasManyThroughOrderError < ActiveRecordError # :nodoc:
|
168
|
+
def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
|
169
|
+
if owner_class_name && reflection && through_reflection
|
170
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through '#{owner_class_name}##{through_reflection.name}' before the through association is defined.")
|
171
|
+
else
|
172
|
+
super("Cannot have a has_many :through association before the through association is defined.")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError # :nodoc:
|
178
|
+
def initialize(owner = nil, reflection = nil)
|
179
|
+
if owner && reflection
|
180
|
+
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
|
181
|
+
else
|
182
|
+
super("Cannot modify association.")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class CompositePrimaryKeyMismatchError < ActiveRecordError # :nodoc:
|
188
|
+
attr_reader :reflection
|
189
|
+
|
190
|
+
def initialize(reflection = nil)
|
191
|
+
if reflection
|
192
|
+
if reflection.has_one? || reflection.collection?
|
193
|
+
super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.active_record_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
|
194
|
+
else
|
195
|
+
super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.association_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
|
196
|
+
end
|
197
|
+
else
|
198
|
+
super("Association primary key doesn't match with foreign key.")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class AmbiguousSourceReflectionForThroughAssociation < ActiveRecordError # :nodoc:
|
204
|
+
def initialize(klass, macro, association_name, options, possible_sources)
|
205
|
+
example_options = options.dup
|
206
|
+
example_options[:source] = possible_sources.first
|
207
|
+
|
208
|
+
super("Ambiguous source reflection for through association. Please " \
|
209
|
+
"specify a :source directive on your declaration like:\n" \
|
210
|
+
"\n" \
|
211
|
+
" class #{klass} < ActiveRecord::Base\n" \
|
212
|
+
" #{macro} :#{association_name}, #{example_options}\n" \
|
213
|
+
" end"
|
214
|
+
)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
|
219
|
+
end
|
220
|
+
|
221
|
+
class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
|
222
|
+
end
|
223
|
+
|
224
|
+
class ThroughNestedAssociationsAreReadonly < ActiveRecordError # :nodoc:
|
225
|
+
def initialize(owner = nil, reflection = nil)
|
226
|
+
if owner && reflection
|
227
|
+
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
|
228
|
+
else
|
229
|
+
super("Through nested associations are read-only.")
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
class HasManyThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
|
235
|
+
end
|
236
|
+
|
237
|
+
class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
|
238
|
+
end
|
239
|
+
|
240
|
+
# This error is raised when trying to eager load a polymorphic association using a JOIN.
|
241
|
+
# Eager loading polymorphic associations is only possible with
|
242
|
+
# {ActiveRecord::Relation#preload}[rdoc-ref:QueryMethods#preload].
|
243
|
+
class EagerLoadPolymorphicError < ActiveRecordError
|
244
|
+
def initialize(reflection = nil)
|
245
|
+
if reflection
|
246
|
+
super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
|
247
|
+
else
|
248
|
+
super("Eager load polymorphic error.")
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
|
254
|
+
# (has_many, has_one) when there is at least 1 child associated instance.
|
255
|
+
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
|
256
|
+
class DeleteRestrictionError < ActiveRecordError # :nodoc:
|
257
|
+
def initialize(name = nil)
|
258
|
+
if name
|
259
|
+
super("Cannot delete record because of dependent #{name}")
|
260
|
+
else
|
261
|
+
super("Delete restriction error.")
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -35,10 +35,10 @@ module ActiveRecord
|
|
35
35
|
unless target.empty?
|
36
36
|
association_class = target.first.class
|
37
37
|
if association_class.query_constraints_list
|
38
|
-
primary_key_column = association_class.query_constraints_list
|
38
|
+
primary_key_column = association_class.query_constraints_list
|
39
39
|
ids = target.collect { |assoc| primary_key_column.map { |col| assoc.public_send(col) } }
|
40
40
|
else
|
41
|
-
primary_key_column = association_class.primary_key
|
41
|
+
primary_key_column = association_class.primary_key
|
42
42
|
ids = target.collect { |assoc| assoc.public_send(primary_key_column) }
|
43
43
|
end
|
44
44
|
|
@@ -78,7 +78,7 @@ module ActiveRecord
|
|
78
78
|
# If the collection is empty the target is set to an empty array and
|
79
79
|
# the loaded flag is set to true as well.
|
80
80
|
def count_records
|
81
|
-
count = if reflection.
|
81
|
+
count = if reflection.has_active_cached_counter?
|
82
82
|
owner.read_attribute(reflection.counter_cache_column).to_i
|
83
83
|
else
|
84
84
|
scope.count(:all)
|
@@ -93,7 +93,13 @@ module ActiveRecord
|
|
93
93
|
@through_scope = scope
|
94
94
|
record = super
|
95
95
|
|
96
|
-
inverse =
|
96
|
+
inverse =
|
97
|
+
if source_reflection.polymorphic?
|
98
|
+
source_reflection.polymorphic_inverse_of(record.class)
|
99
|
+
else
|
100
|
+
source_reflection.inverse_of
|
101
|
+
end
|
102
|
+
|
97
103
|
if inverse
|
98
104
|
if inverse.collection?
|
99
105
|
record.send(inverse.name) << build_through_record(record)
|
@@ -34,10 +34,10 @@ module ActiveRecord
|
|
34
34
|
throw(:abort) unless target.destroyed?
|
35
35
|
when :destroy_async
|
36
36
|
if target.class.query_constraints_list
|
37
|
-
primary_key_column = target.class.query_constraints_list
|
37
|
+
primary_key_column = target.class.query_constraints_list
|
38
38
|
id = primary_key_column.map { |col| target.public_send(col) }
|
39
39
|
else
|
40
|
-
primary_key_column = target.class.primary_key
|
40
|
+
primary_key_column = target.class.primary_key
|
41
41
|
id = target.public_send(primary_key_column)
|
42
42
|
end
|
43
43
|
|
@@ -25,8 +25,9 @@ module ActiveRecord
|
|
25
25
|
joins = []
|
26
26
|
chain = []
|
27
27
|
|
28
|
-
reflection.chain
|
29
|
-
|
28
|
+
reflection_chain = reflection.chain
|
29
|
+
reflection_chain.each_with_index do |reflection, index|
|
30
|
+
table, terminated = yield reflection, reflection_chain[index..]
|
30
31
|
@table ||= table
|
31
32
|
|
32
33
|
if terminated
|
@@ -37,39 +38,41 @@ module ActiveRecord
|
|
37
38
|
chain << [reflection, table]
|
38
39
|
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
base_klass.with_connection do |connection|
|
42
|
+
# The chain starts with the target table, but we want to end with it here (makes
|
43
|
+
# more sense in this context), so we reverse
|
44
|
+
chain.reverse_each do |reflection, table|
|
45
|
+
klass = reflection.klass
|
44
46
|
|
45
|
-
|
47
|
+
scope = reflection.join_scope(table, foreign_table, foreign_klass)
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
+
unless scope.references_values.empty?
|
50
|
+
associations = scope.eager_load_values | scope.includes_values
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
+
unless associations.empty?
|
53
|
+
scope.joins! scope.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
|
54
|
+
end
|
52
55
|
end
|
53
|
-
end
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
+
arel = scope.arel(alias_tracker.aliases)
|
58
|
+
nodes = arel.constraints.first
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
if nodes.is_a?(Arel::Nodes::And)
|
61
|
+
others = nodes.children.extract! do |node|
|
62
|
+
!Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
|
63
|
+
end
|
61
64
|
end
|
62
|
-
end
|
63
65
|
|
64
|
-
|
66
|
+
joins << join_type.new(table, Arel::Nodes::On.new(nodes))
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
if others && !others.empty?
|
69
|
+
joins.concat arel.join_sources
|
70
|
+
append_constraints(connection, joins.last, others)
|
71
|
+
end
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
+
# The current table in this iteration becomes the foreign table in the next
|
74
|
+
foreign_table, foreign_klass = table, klass
|
75
|
+
end
|
73
76
|
end
|
74
77
|
|
75
78
|
joins
|
@@ -88,10 +91,10 @@ module ActiveRecord
|
|
88
91
|
end
|
89
92
|
|
90
93
|
private
|
91
|
-
def append_constraints(join, constraints)
|
94
|
+
def append_constraints(connection, join, constraints)
|
92
95
|
if join.is_a?(Arel::Nodes::StringJoin)
|
93
96
|
join_string = Arel::Nodes::And.new(constraints.unshift join.left)
|
94
|
-
join.left = Arel.sql(
|
97
|
+
join.left = Arel.sql(connection.visitor.compile(join_string))
|
95
98
|
else
|
96
99
|
right = join.right
|
97
100
|
right.expr = Arel::Nodes::And.new(constraints.unshift right.expr)
|
@@ -61,7 +61,7 @@ module ActiveRecord
|
|
61
61
|
when Hash
|
62
62
|
associations.each do |k, v|
|
63
63
|
cache = hash[k] ||= {}
|
64
|
-
walk_tree v, cache
|
64
|
+
walk_tree v, cache if v
|
65
65
|
end
|
66
66
|
else
|
67
67
|
raise ConfigurationError, associations.inspect
|
@@ -190,12 +190,12 @@ module ActiveRecord
|
|
190
190
|
def make_constraints(parent, child, join_type)
|
191
191
|
foreign_table = parent.table
|
192
192
|
foreign_klass = parent.base_klass
|
193
|
-
child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection|
|
194
|
-
table, terminated = @joined_tables[
|
193
|
+
child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection, remaining_reflection_chain|
|
194
|
+
table, terminated = @joined_tables[remaining_reflection_chain]
|
195
195
|
root = reflection == child.reflection
|
196
196
|
|
197
197
|
if table && (!root || !terminated)
|
198
|
-
@joined_tables[
|
198
|
+
@joined_tables[remaining_reflection_chain] = [table, root] if root
|
199
199
|
next table, true
|
200
200
|
end
|
201
201
|
|
@@ -206,7 +206,7 @@ module ActiveRecord
|
|
206
206
|
root ? name : "#{name}_join"
|
207
207
|
end
|
208
208
|
|
209
|
-
@joined_tables[
|
209
|
+
@joined_tables[remaining_reflection_chain] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
|
210
210
|
table
|
211
211
|
end.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
|
212
212
|
end
|
@@ -254,10 +254,10 @@ module ActiveRecord
|
|
254
254
|
|
255
255
|
if node.primary_key
|
256
256
|
keys = Array(node.primary_key).map { |column| aliases.column_alias(node, column) }
|
257
|
-
|
257
|
+
id = keys.map { |key| row[key] }
|
258
258
|
else
|
259
259
|
keys = Array(node.reflection.join_primary_key).map { |column| aliases.column_alias(node, column.to_s) }
|
260
|
-
|
260
|
+
id = keys.map { nil } # Avoid id-based model caching.
|
261
261
|
end
|
262
262
|
|
263
263
|
if keys.any? { |key| row[key].nil? }
|
@@ -266,11 +266,9 @@ module ActiveRecord
|
|
266
266
|
next
|
267
267
|
end
|
268
268
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
seen[ar_parent][node][id] = model if id
|
273
|
-
end
|
269
|
+
unless model = seen[ar_parent][node][id]
|
270
|
+
model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
|
271
|
+
seen[ar_parent][node][id] = model if id
|
274
272
|
end
|
275
273
|
|
276
274
|
construct(model, node, row, seen, model_cache, strict_loading_value)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Validation error class to wrap association records' errors,
|
4
|
+
# with index_errors support.
|
5
|
+
module ActiveRecord
|
6
|
+
module Associations
|
7
|
+
class NestedError < ::ActiveModel::NestedError
|
8
|
+
def initialize(association, inner_error)
|
9
|
+
@base = association.owner
|
10
|
+
@association = association
|
11
|
+
@inner_error = inner_error
|
12
|
+
super(@base, inner_error, { attribute: compute_attribute(inner_error) })
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
attr_reader :association
|
17
|
+
|
18
|
+
def compute_attribute(inner_error)
|
19
|
+
association_name = association.reflection.name
|
20
|
+
|
21
|
+
if association.collection? && index_errors_setting && index
|
22
|
+
"#{association_name}[#{index}].#{inner_error.attribute}".to_sym
|
23
|
+
else
|
24
|
+
"#{association_name}.#{inner_error.attribute}".to_sym
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def index_errors_setting
|
29
|
+
@index_errors_setting ||=
|
30
|
+
association.options.fetch(:index_errors, ActiveRecord.index_nested_attribute_errors)
|
31
|
+
end
|
32
|
+
|
33
|
+
def index
|
34
|
+
@index ||= ordered_records&.find_index(inner_error.base)
|
35
|
+
end
|
36
|
+
|
37
|
+
def ordered_records
|
38
|
+
case index_errors_setting
|
39
|
+
when true # default is association order
|
40
|
+
association.target
|
41
|
+
when :nested_attributes_order
|
42
|
+
association.nested_attributes_target
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -248,7 +248,8 @@ module ActiveRecord
|
|
248
248
|
association = owner.association(reflection.name)
|
249
249
|
|
250
250
|
if reflection.collection?
|
251
|
-
association.target
|
251
|
+
not_persisted_records = association.target.reject(&:persisted?)
|
252
|
+
association.target = records + not_persisted_records
|
252
253
|
else
|
253
254
|
association.target = records.first
|
254
255
|
end
|
@@ -9,7 +9,13 @@ module ActiveRecord
|
|
9
9
|
attr_writer :preloaded_records
|
10
10
|
|
11
11
|
def initialize(association:, children:, parent:, associate_by_default:, scope:)
|
12
|
-
@association = association
|
12
|
+
@association = if association
|
13
|
+
begin
|
14
|
+
@association = association.to_sym
|
15
|
+
rescue NoMethodError
|
16
|
+
raise ArgumentError, "Association names must be Symbol or String, got: #{association.class.name}"
|
17
|
+
end
|
18
|
+
end
|
13
19
|
@parent = parent
|
14
20
|
@scope = scope
|
15
21
|
@associate_by_default = associate_by_default
|
@@ -9,9 +9,7 @@ module ActiveRecord
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def records_by_owner
|
12
|
-
|
13
|
-
|
14
|
-
@records_by_owner = owners.each_with_object({}) do |owner, result|
|
12
|
+
@records_by_owner ||= owners.each_with_object({}) do |owner, result|
|
15
13
|
if loaded?(owner)
|
16
14
|
result[owner] = target_for(owner)
|
17
15
|
next
|
@@ -14,6 +14,12 @@ module ActiveRecord
|
|
14
14
|
target
|
15
15
|
end
|
16
16
|
|
17
|
+
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
18
|
+
def reset
|
19
|
+
super
|
20
|
+
@target = nil
|
21
|
+
end
|
22
|
+
|
17
23
|
# Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
|
18
24
|
def writer(record)
|
19
25
|
replace(record)
|
@@ -82,7 +82,7 @@ module ActiveRecord
|
|
82
82
|
def stale_state
|
83
83
|
if through_reflection.belongs_to?
|
84
84
|
Array(through_reflection.foreign_key).filter_map do |foreign_key_column|
|
85
|
-
owner[foreign_key_column]
|
85
|
+
owner[foreign_key_column]
|
86
86
|
end.presence
|
87
87
|
end
|
88
88
|
end
|