activerecord 7.1.3.2 → 7.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 +570 -2094
- 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_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +6 -8
- 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 +34 -273
- data/lib/active_record/attribute_assignment.rb +1 -11
- 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 +7 -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 +60 -45
- data/lib/active_record/autosave_association.rb +17 -31
- data/lib/active_record/base.rb +2 -3
- 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 +244 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +188 -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 +22 -9
- 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 +69 -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 +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- 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 +60 -39
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- 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/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/enum.rb +26 -6
- 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 +1 -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 +39 -70
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- 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 +54 -67
- 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 +102 -37
- 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 +95 -62
- 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 +212 -47
- 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 +87 -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 +14 -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/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 +18 -12
@@ -1,268 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
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
|
-
|
266
4
|
# See ActiveRecord::Associations::ClassMethods for documentation.
|
267
5
|
module Associations # :nodoc:
|
268
6
|
extend ActiveSupport::Autoload
|
@@ -1506,6 +1244,13 @@ module ActiveRecord
|
|
1506
1244
|
# Serves as a composite foreign key. Defines the list of columns to be used to query the associated object.
|
1507
1245
|
# This is an optional option. By default Rails will attempt to derive the value automatically.
|
1508
1246
|
# When the value is set the Array size must match associated model's primary key or +query_constraints+ size.
|
1247
|
+
# [+:index_errors+]
|
1248
|
+
# Allows differentiation of multiple validation errors from the association records, by including
|
1249
|
+
# an index in the error attribute name, e.g. +roles[2].level+.
|
1250
|
+
# When set to +true+, the index is based on association order, i.e. database order, with yet to be
|
1251
|
+
# persisted new records placed at the end.
|
1252
|
+
# When set to +:nested_attributes_order+, the index is based on the record order received by
|
1253
|
+
# nested attributes setter, when accepts_nested_attributes_for is used.
|
1509
1254
|
#
|
1510
1255
|
# Option examples:
|
1511
1256
|
# has_many :comments, -> { order("posted_on") }
|
@@ -1519,6 +1264,7 @@ module ActiveRecord
|
|
1519
1264
|
# has_many :subscribers, through: :subscriptions, disable_joins: true
|
1520
1265
|
# has_many :comments, strict_loading: true
|
1521
1266
|
# has_many :comments, query_constraints: [:blog_id, :post_id]
|
1267
|
+
# has_many :comments, index_errors: :nested_attributes_order
|
1522
1268
|
def has_many(name, scope = nil, **options, &extension)
|
1523
1269
|
reflection = Builder::HasMany.build(self, name, scope, options, &extension)
|
1524
1270
|
Reflection.add_reflection self, name, reflection
|
@@ -1661,9 +1407,14 @@ module ActiveRecord
|
|
1661
1407
|
# When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
|
1662
1408
|
# If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
|
1663
1409
|
# [+:autosave+]
|
1664
|
-
# If true
|
1665
|
-
# when saving the parent object.
|
1666
|
-
#
|
1410
|
+
# If +true+, always saves the associated object or destroys it if marked for destruction,
|
1411
|
+
# when saving the parent object.
|
1412
|
+
# If +false+, never save or destroy the associated object.
|
1413
|
+
#
|
1414
|
+
# By default, only saves the associated object if it's a new record. Setting this option
|
1415
|
+
# to +true+ also enables validations on the associated object unless explicitly disabled
|
1416
|
+
# with <tt>validate: false</tt>. This is because saving an object with invalid associated
|
1417
|
+
# objects would fail, so any associated objects will go through validation checks.
|
1667
1418
|
#
|
1668
1419
|
# Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
|
1669
1420
|
# <tt>:autosave</tt> to <tt>true</tt>.
|
@@ -1816,15 +1567,25 @@ module ActiveRecord
|
|
1816
1567
|
# named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
|
1817
1568
|
# is used on the associate class (such as a Post class) - that is the migration for
|
1818
1569
|
# <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
|
1819
|
-
# return the count cached
|
1570
|
+
# return the count cached). You can also specify a custom counter
|
1820
1571
|
# cache column by providing a column name instead of a +true+/+false+ value to this
|
1821
1572
|
# option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
|
1822
|
-
#
|
1823
|
-
#
|
1824
|
-
#
|
1825
|
-
#
|
1573
|
+
#
|
1574
|
+
# Starting to use counter caches on existing large tables can be troublesome, because the column
|
1575
|
+
# values must be backfilled separately of the column addition (to not lock the table for too long)
|
1576
|
+
# and before the use of +:counter_cache+ (otherwise methods like +size+/+any?+/etc, which use
|
1577
|
+
# counter caches internally, can produce incorrect results). To safely backfill the values while keeping
|
1578
|
+
# counter cache columns updated with the child records creation/removal and to avoid the mentioned methods
|
1579
|
+
# use the possibly incorrect counter cache column values and always get the results from the database,
|
1580
|
+
# use <tt>counter_cache: { active: false }</tt>.
|
1581
|
+
# If you also need to specify a custom column name, use <tt>counter_cache: { active: false, column: :my_custom_counter }</tt>.
|
1582
|
+
#
|
1826
1583
|
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
|
1827
1584
|
# to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
|
1585
|
+
# [+:polymorphic+]
|
1586
|
+
# Specify this association is a polymorphic association by passing +true+.
|
1587
|
+
# Note: Since polymorphic associations rely on storing class names in the database, make sure to update the class names in the
|
1588
|
+
# <tt>*_type</tt> polymorphic type column of the corresponding rows.
|
1828
1589
|
# [+:validate+]
|
1829
1590
|
# When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
|
1830
1591
|
# If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
|
@@ -1882,7 +1643,7 @@ module ActiveRecord
|
|
1882
1643
|
# belongs_to :user, optional: true
|
1883
1644
|
# belongs_to :account, default: -> { company.account }
|
1884
1645
|
# belongs_to :account, strict_loading: true
|
1885
|
-
#
|
1646
|
+
# belongs_to :note, query_constraints: [:organization_id, :note_id]
|
1886
1647
|
def belongs_to(name, scope = nil, **options)
|
1887
1648
|
reflection = Builder::BelongsTo.build(self, name, scope, options)
|
1888
1649
|
Reflection.add_reflection self, name, reflection
|
@@ -1905,7 +1666,7 @@ module ActiveRecord
|
|
1905
1666
|
# The join table should not have a primary key or a model associated with it. You must manually generate the
|
1906
1667
|
# join table with a migration such as this:
|
1907
1668
|
#
|
1908
|
-
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.
|
1669
|
+
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.2]
|
1909
1670
|
# def change
|
1910
1671
|
# create_join_table :developers, :projects
|
1911
1672
|
# end
|
@@ -2099,7 +1860,7 @@ module ActiveRecord
|
|
2099
1860
|
end
|
2100
1861
|
|
2101
1862
|
has_many name, scope, **hm_options, &extension
|
2102
|
-
_reflections[name
|
1863
|
+
_reflections[name].parent_reflection = habtm_reflection
|
2103
1864
|
end
|
2104
1865
|
end
|
2105
1866
|
end
|
@@ -2,33 +2,23 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module AttributeAssignment
|
5
|
-
include ActiveModel::AttributeAssignment
|
6
|
-
|
7
5
|
private
|
8
6
|
def _assign_attributes(attributes)
|
9
|
-
multi_parameter_attributes =
|
7
|
+
multi_parameter_attributes = nil
|
10
8
|
|
11
9
|
attributes.each do |k, v|
|
12
10
|
key = k.to_s
|
13
11
|
|
14
12
|
if key.include?("(")
|
15
13
|
(multi_parameter_attributes ||= {})[key] = v
|
16
|
-
elsif v.is_a?(Hash)
|
17
|
-
(nested_parameter_attributes ||= {})[key] = v
|
18
14
|
else
|
19
15
|
_assign_attribute(key, v)
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
23
|
-
assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
|
24
19
|
assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
|
25
20
|
end
|
26
21
|
|
27
|
-
# Assign any deferred nested attributes after the base attributes have been set.
|
28
|
-
def assign_nested_parameter_attributes(pairs)
|
29
|
-
pairs.each { |k, v| _assign_attribute(k, v) }
|
30
|
-
end
|
31
|
-
|
32
22
|
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
|
33
23
|
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
|
34
24
|
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module AttributeMethods
|
5
|
+
module CompositePrimaryKey # :nodoc:
|
6
|
+
# Returns the primary key column's value. If the primary key is composite,
|
7
|
+
# returns an array of the primary key column values.
|
8
|
+
def id
|
9
|
+
if self.class.composite_primary_key?
|
10
|
+
@primary_key.map { |pk| _read_attribute(pk) }
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def primary_key_values_present? # :nodoc:
|
17
|
+
if self.class.composite_primary_key?
|
18
|
+
id.all?
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sets the primary key column's value. If the primary key is composite,
|
25
|
+
# raises TypeError when the set value not enumerable.
|
26
|
+
def id=(value)
|
27
|
+
if self.class.composite_primary_key?
|
28
|
+
raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
|
29
|
+
@primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Queries the primary key column's value. If the primary key is composite,
|
36
|
+
# all primary key column values must be queryable.
|
37
|
+
def id?
|
38
|
+
if self.class.composite_primary_key?
|
39
|
+
@primary_key.all? { |col| _query_attribute(col) }
|
40
|
+
else
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the primary key column's value before type cast. If the primary key is composite,
|
46
|
+
# returns an array of primary key column values before type cast.
|
47
|
+
def id_before_type_cast
|
48
|
+
if self.class.composite_primary_key?
|
49
|
+
@primary_key.map { |col| attribute_before_type_cast(col) }
|
50
|
+
else
|
51
|
+
super
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the primary key column's previous value. If the primary key is composite,
|
56
|
+
# returns an array of primary key column previous values.
|
57
|
+
def id_was
|
58
|
+
if self.class.composite_primary_key?
|
59
|
+
@primary_key.map { |col| attribute_was(col) }
|
60
|
+
else
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the primary key column's value from the database. If the primary key is composite,
|
66
|
+
# returns an array of primary key column values from database.
|
67
|
+
def id_in_database
|
68
|
+
if self.class.composite_primary_key?
|
69
|
+
@primary_key.map { |col| attribute_in_database(col) }
|
70
|
+
else
|
71
|
+
super
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def id_for_database # :nodoc:
|
76
|
+
if self.class.composite_primary_key?
|
77
|
+
@primary_key.map { |col| @attributes[col].value_for_database }
|
78
|
+
else
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# person.name_in_database # => "Alice"
|
32
32
|
# person.saved_change_to_name? # => true
|
33
33
|
# person.saved_change_to_name # => ["Allison", "Alice"]
|
34
|
-
# person.
|
34
|
+
# person.name_before_last_save # => "Allison"
|
35
35
|
#
|
36
36
|
# Similar to ActiveModel::Dirty, methods can be invoked as
|
37
37
|
# +saved_change_to_name?+ or by passing an argument to the generic method
|
@@ -251,7 +251,7 @@ module ActiveRecord
|
|
251
251
|
changed_attribute_names_to_save
|
252
252
|
else
|
253
253
|
attribute_names.reject do |attr_name|
|
254
|
-
if column_for_attribute(attr_name).
|
254
|
+
if column_for_attribute(attr_name).auto_populated?
|
255
255
|
!attribute_changed?(attr_name)
|
256
256
|
end
|
257
257
|
end
|
@@ -18,74 +18,45 @@ module ActiveRecord
|
|
18
18
|
# Returns the primary key column's value. If the primary key is composite,
|
19
19
|
# returns an array of the primary key column values.
|
20
20
|
def id
|
21
|
-
|
22
|
-
|
23
|
-
@primary_key.map { |pk| _read_attribute(pk) }
|
21
|
+
_read_attribute(@primary_key)
|
24
22
|
end
|
25
23
|
|
26
24
|
def primary_key_values_present? # :nodoc:
|
27
|
-
return id.all? if self.class.composite_primary_key?
|
28
|
-
|
29
25
|
!!id
|
30
26
|
end
|
31
27
|
|
32
28
|
# Sets the primary key column's value. If the primary key is composite,
|
33
29
|
# raises TypeError when the set value not enumerable.
|
34
30
|
def id=(value)
|
35
|
-
|
36
|
-
raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
|
37
|
-
@primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
|
38
|
-
else
|
39
|
-
_write_attribute(@primary_key, value)
|
40
|
-
end
|
31
|
+
_write_attribute(@primary_key, value)
|
41
32
|
end
|
42
33
|
|
43
34
|
# Queries the primary key column's value. If the primary key is composite,
|
44
35
|
# all primary key column values must be queryable.
|
45
36
|
def id?
|
46
|
-
|
47
|
-
@primary_key.all? { |col| _query_attribute(col) }
|
48
|
-
else
|
49
|
-
_query_attribute(@primary_key)
|
50
|
-
end
|
37
|
+
_query_attribute(@primary_key)
|
51
38
|
end
|
52
39
|
|
53
40
|
# Returns the primary key column's value before type cast. If the primary key is composite,
|
54
41
|
# returns an array of primary key column values before type cast.
|
55
42
|
def id_before_type_cast
|
56
|
-
|
57
|
-
@primary_key.map { |col| attribute_before_type_cast(col) }
|
58
|
-
else
|
59
|
-
attribute_before_type_cast(@primary_key)
|
60
|
-
end
|
43
|
+
attribute_before_type_cast(@primary_key)
|
61
44
|
end
|
62
45
|
|
63
46
|
# Returns the primary key column's previous value. If the primary key is composite,
|
64
47
|
# returns an array of primary key column previous values.
|
65
48
|
def id_was
|
66
|
-
|
67
|
-
@primary_key.map { |col| attribute_was(col) }
|
68
|
-
else
|
69
|
-
attribute_was(@primary_key)
|
70
|
-
end
|
49
|
+
attribute_was(@primary_key)
|
71
50
|
end
|
72
51
|
|
73
52
|
# Returns the primary key column's value from the database. If the primary key is composite,
|
74
53
|
# returns an array of primary key column values from database.
|
75
54
|
def id_in_database
|
76
|
-
|
77
|
-
@primary_key.map { |col| attribute_in_database(col) }
|
78
|
-
else
|
79
|
-
attribute_in_database(@primary_key)
|
80
|
-
end
|
55
|
+
attribute_in_database(@primary_key)
|
81
56
|
end
|
82
57
|
|
83
58
|
def id_for_database # :nodoc:
|
84
|
-
|
85
|
-
@primary_key.map { |col| @attributes[col].value_for_database }
|
86
|
-
else
|
87
|
-
@attributes[@primary_key].value_for_database
|
88
|
-
end
|
59
|
+
@attributes[@primary_key].value_for_database
|
89
60
|
end
|
90
61
|
|
91
62
|
private
|
@@ -109,20 +80,19 @@ module ActiveRecord
|
|
109
80
|
# Overwriting will negate any effect of the +primary_key_prefix_type+
|
110
81
|
# setting, though.
|
111
82
|
def primary_key
|
112
|
-
if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
113
|
-
@primary_key = reset_primary_key
|
114
|
-
end
|
83
|
+
reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
115
84
|
@primary_key
|
116
85
|
end
|
117
86
|
|
118
87
|
def composite_primary_key? # :nodoc:
|
119
|
-
|
88
|
+
reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
89
|
+
@composite_primary_key
|
120
90
|
end
|
121
91
|
|
122
92
|
# Returns a quoted version of the primary key name, used to construct
|
123
93
|
# SQL statements.
|
124
94
|
def quoted_primary_key
|
125
|
-
@quoted_primary_key ||=
|
95
|
+
@quoted_primary_key ||= adapter_class.quote_column_name(primary_key)
|
126
96
|
end
|
127
97
|
|
128
98
|
def reset_primary_key # :nodoc:
|
@@ -138,12 +108,10 @@ module ActiveRecord
|
|
138
108
|
base_name.foreign_key(false)
|
139
109
|
elsif base_name && primary_key_prefix_type == :table_name_with_underscore
|
140
110
|
base_name.foreign_key
|
111
|
+
elsif ActiveRecord::Base != self && table_exists?
|
112
|
+
schema_cache.primary_keys(table_name)
|
141
113
|
else
|
142
|
-
|
143
|
-
connection.schema_cache.primary_keys(table_name)
|
144
|
-
else
|
145
|
-
"id"
|
146
|
-
end
|
114
|
+
"id"
|
147
115
|
end
|
148
116
|
end
|
149
117
|
|
@@ -163,25 +131,25 @@ module ActiveRecord
|
|
163
131
|
#
|
164
132
|
# Project.primary_key # => "foo_id"
|
165
133
|
def primary_key=(value)
|
166
|
-
@primary_key
|
134
|
+
@primary_key = if value.is_a?(Array)
|
135
|
+
@composite_primary_key = true
|
136
|
+
include CompositePrimaryKey
|
137
|
+
@primary_key = value.map { |v| -v.to_s }.freeze
|
138
|
+
elsif value
|
139
|
+
-value.to_s
|
140
|
+
end
|
167
141
|
@quoted_primary_key = nil
|
168
142
|
@attributes_builder = nil
|
169
143
|
end
|
170
144
|
|
171
145
|
private
|
172
|
-
def derive_primary_key(value)
|
173
|
-
return unless value
|
174
|
-
|
175
|
-
return -value.to_s unless value.is_a?(Array)
|
176
|
-
|
177
|
-
value.map { |v| -v.to_s }.freeze
|
178
|
-
end
|
179
|
-
|
180
146
|
def inherited(base)
|
181
147
|
super
|
182
148
|
base.class_eval do
|
183
149
|
@primary_key = PRIMARY_KEY_NOT_SET
|
150
|
+
@composite_primary_key = false
|
184
151
|
@quoted_primary_key = nil
|
152
|
+
@attributes_builder = nil
|
185
153
|
end
|
186
154
|
end
|
187
155
|
end
|