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
@@ -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
|
@@ -8,11 +8,11 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
module ClassMethods # :nodoc:
|
10
10
|
private
|
11
|
-
def define_method_attribute(
|
11
|
+
def define_method_attribute(canonical_name, owner:, as: canonical_name)
|
12
12
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
13
|
-
owner,
|
13
|
+
owner, canonical_name
|
14
14
|
) do |temp_method_name, attr_name_expr|
|
15
|
-
owner.define_cached_method(
|
15
|
+
owner.define_cached_method(temp_method_name, as: as, namespace: :active_record) do |batch|
|
16
16
|
batch <<
|
17
17
|
"def #{temp_method_name}" <<
|
18
18
|
" _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
|
@@ -30,19 +30,7 @@ module ActiveRecord
|
|
30
30
|
name = attr_name.to_s
|
31
31
|
name = self.class.attribute_aliases[name] || name
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
if self.class.composite_primary_key?
|
36
|
-
@attributes.fetch_value("id", &block)
|
37
|
-
else
|
38
|
-
if @primary_key != "id"
|
39
|
-
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
40
|
-
Using read_attribute(:id) to read the primary key value is deprecated.
|
41
|
-
Use #id instead.
|
42
|
-
MSG
|
43
|
-
end
|
44
|
-
@attributes.fetch_value(@primary_key, &block)
|
45
|
-
end
|
33
|
+
@attributes.fetch_value(name, &block)
|
46
34
|
end
|
47
35
|
|
48
36
|
# This method exists to avoid the expensive primary_key check internally, without
|
@@ -180,29 +180,7 @@ module ActiveRecord
|
|
180
180
|
# serialize :preferences, coder: Rot13JSON
|
181
181
|
# end
|
182
182
|
#
|
183
|
-
def serialize(attr_name,
|
184
|
-
unless class_name_or_coder.nil?
|
185
|
-
if class_name_or_coder == ::JSON || [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
|
186
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
187
|
-
Passing the coder as positional argument is deprecated and will be removed in Rails 7.2.
|
188
|
-
|
189
|
-
Please pass the coder as a keyword argument:
|
190
|
-
|
191
|
-
serialize #{attr_name.inspect}, coder: #{class_name_or_coder}
|
192
|
-
MSG
|
193
|
-
coder = class_name_or_coder
|
194
|
-
else
|
195
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
196
|
-
Passing the class as positional argument is deprecated and will be removed in Rails 7.2.
|
197
|
-
|
198
|
-
Please pass the class as a keyword argument:
|
199
|
-
|
200
|
-
serialize #{attr_name.inspect}, type: #{class_name_or_coder.name}
|
201
|
-
MSG
|
202
|
-
type = class_name_or_coder
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
183
|
+
def serialize(attr_name, coder: nil, type: Object, yaml: {}, **options)
|
206
184
|
coder ||= default_column_serializer
|
207
185
|
unless coder
|
208
186
|
raise ArgumentError, <<~MSG.squish
|
@@ -214,7 +192,9 @@ module ActiveRecord
|
|
214
192
|
|
215
193
|
column_serializer = build_column_serializer(attr_name, coder, type, yaml)
|
216
194
|
|
217
|
-
attribute(attr_name, **options)
|
195
|
+
attribute(attr_name, **options)
|
196
|
+
|
197
|
+
decorate_attributes([attr_name]) do |attr_name, cast_type|
|
218
198
|
if type_incompatible_with_serialize?(cast_type, coder, type)
|
219
199
|
raise ColumnNotSerializableError.new(attr_name, cast_type)
|
220
200
|
end
|
@@ -32,6 +32,10 @@ module ActiveRecord
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
def ==(other)
|
36
|
+
other.is_a?(self.class) && __getobj__ == other.__getobj__
|
37
|
+
end
|
38
|
+
|
35
39
|
private
|
36
40
|
def convert_time_to_time_zone(value)
|
37
41
|
return if value.nil?
|
@@ -69,14 +73,15 @@ module ActiveRecord
|
|
69
73
|
end
|
70
74
|
|
71
75
|
module ClassMethods # :nodoc:
|
72
|
-
|
73
|
-
|
74
|
-
|
76
|
+
private
|
77
|
+
def hook_attribute_type(name, cast_type)
|
78
|
+
if create_time_zone_conversion_attribute?(name, cast_type)
|
79
|
+
cast_type = TimeZoneConverter.new(cast_type)
|
80
|
+
end
|
81
|
+
|
82
|
+
super
|
75
83
|
end
|
76
|
-
super
|
77
|
-
end
|
78
84
|
|
79
|
-
private
|
80
85
|
def create_time_zone_conversion_attribute?(name, cast_type)
|
81
86
|
enabled_for_column = time_zone_aware_attributes &&
|
82
87
|
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
|
@@ -12,11 +12,11 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
module ClassMethods # :nodoc:
|
14
14
|
private
|
15
|
-
def define_method_attribute=(
|
15
|
+
def define_method_attribute=(canonical_name, owner:, as: canonical_name)
|
16
16
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
17
|
-
owner,
|
17
|
+
owner, canonical_name, writer: true,
|
18
18
|
) do |temp_method_name, attr_name_expr|
|
19
|
-
owner.define_cached_method("#{
|
19
|
+
owner.define_cached_method(temp_method_name, as: "#{as}=", namespace: :active_record) do |batch|
|
20
20
|
batch <<
|
21
21
|
"def #{temp_method_name}(value)" <<
|
22
22
|
" _write_attribute(#{attr_name_expr}, value)" <<
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "mutex_m"
|
4
3
|
require "active_support/core_ext/enumerable"
|
5
4
|
|
6
5
|
module ActiveRecord
|
@@ -21,10 +20,10 @@ module ActiveRecord
|
|
21
20
|
include Serialization
|
22
21
|
end
|
23
22
|
|
24
|
-
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name
|
23
|
+
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name superclass)
|
25
24
|
|
26
25
|
class GeneratedAttributeMethods < Module # :nodoc:
|
27
|
-
|
26
|
+
LOCK = Monitor.new
|
28
27
|
end
|
29
28
|
|
30
29
|
class << self
|
@@ -50,6 +49,20 @@ module ActiveRecord
|
|
50
49
|
super
|
51
50
|
end
|
52
51
|
|
52
|
+
# Allows you to make aliases for attributes.
|
53
|
+
#
|
54
|
+
# class Person < ActiveRecord::Base
|
55
|
+
# alias_attribute :nickname, :name
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# person = Person.create(name: 'Bob')
|
59
|
+
# person.name # => "Bob"
|
60
|
+
# person.nickname # => "Bob"
|
61
|
+
#
|
62
|
+
# The alias can also be used for querying:
|
63
|
+
#
|
64
|
+
# Person.where(nickname: "Bob")
|
65
|
+
# # SELECT "people".* FROM "people" WHERE "people"."name" = "Bob"
|
53
66
|
def alias_attribute(new_name, old_name)
|
54
67
|
super
|
55
68
|
|
@@ -64,80 +77,71 @@ module ActiveRecord
|
|
64
77
|
# alias attributes in Active Record are lazily generated
|
65
78
|
end
|
66
79
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
generated_attribute_methods.synchronize do
|
72
|
-
return if @alias_attributes_mass_generated
|
73
|
-
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
74
|
-
aliases_by_attribute_name.each do |old_name, new_names|
|
75
|
-
new_names.each do |new_name|
|
76
|
-
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
@alias_attributes_mass_generated = true
|
80
|
+
def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
|
81
|
+
attribute_method_patterns.each do |pattern|
|
82
|
+
alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
82
83
|
end
|
84
|
+
attribute_method_patterns_cache.clear
|
83
85
|
end
|
84
86
|
|
85
87
|
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
86
|
-
method_name = pattern.method_name(new_name).to_s
|
87
|
-
target_name = pattern.method_name(old_name).to_s
|
88
|
-
parameters = pattern.parameters
|
89
88
|
old_name = old_name.to_s
|
90
89
|
|
91
|
-
method_defined = method_defined?(target_name) || private_method_defined?(target_name)
|
92
|
-
manually_defined = method_defined &&
|
93
|
-
!self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
|
94
|
-
reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)
|
95
|
-
|
96
90
|
if !abstract_class? && !has_attribute?(old_name)
|
97
|
-
|
98
|
-
|
99
|
-
if should_warn
|
100
|
-
ActiveRecord.deprecator.warn(
|
101
|
-
"#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
102
|
-
"Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
|
103
|
-
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
104
|
-
)
|
105
|
-
end
|
106
|
-
super
|
107
|
-
elsif manually_defined && !reserved_method_name
|
108
|
-
aliased_method_redefined_as_well = method_defined_within?(method_name, self)
|
109
|
-
return if aliased_method_redefined_as_well
|
110
|
-
|
111
|
-
ActiveRecord.deprecator.warn(
|
112
|
-
"#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
|
113
|
-
"Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
|
114
|
-
"You may want to additionally define `#{method_name}` to preserve the current behavior."
|
115
|
-
)
|
116
|
-
super
|
91
|
+
raise ArgumentError, "#{self.name} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
92
|
+
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
117
93
|
else
|
118
|
-
|
119
|
-
namespace: :proxy_alias_attribute
|
120
|
-
)
|
94
|
+
define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
|
121
95
|
end
|
122
96
|
end
|
123
97
|
|
98
|
+
def attribute_methods_generated? # :nodoc:
|
99
|
+
@attribute_methods_generated
|
100
|
+
end
|
101
|
+
|
124
102
|
# Generates all the attribute related methods for columns in the database
|
125
103
|
# accessors, mutators and query methods.
|
126
104
|
def define_attribute_methods # :nodoc:
|
127
105
|
return false if @attribute_methods_generated
|
128
106
|
# Use a mutex; we don't want two threads simultaneously trying to define
|
129
107
|
# attribute methods.
|
130
|
-
|
108
|
+
GeneratedAttributeMethods::LOCK.synchronize do
|
131
109
|
return false if @attribute_methods_generated
|
110
|
+
|
132
111
|
superclass.define_attribute_methods unless base_class?
|
133
|
-
|
112
|
+
|
113
|
+
unless abstract_class?
|
114
|
+
load_schema
|
115
|
+
super(attribute_names)
|
116
|
+
alias_attribute :id_value, :id if _has_attribute?("id")
|
117
|
+
end
|
118
|
+
|
134
119
|
@attribute_methods_generated = true
|
120
|
+
|
121
|
+
generate_alias_attributes
|
122
|
+
end
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
def generate_alias_attributes # :nodoc:
|
127
|
+
superclass.generate_alias_attributes unless superclass == Base
|
128
|
+
|
129
|
+
return if @alias_attributes_mass_generated
|
130
|
+
|
131
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
132
|
+
aliases_by_attribute_name.each do |old_name, new_names|
|
133
|
+
new_names.each do |new_name|
|
134
|
+
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
135
|
+
end
|
136
|
+
end
|
135
137
|
end
|
138
|
+
|
139
|
+
@alias_attributes_mass_generated = true
|
136
140
|
end
|
137
141
|
|
138
142
|
def undefine_attribute_methods # :nodoc:
|
139
|
-
|
140
|
-
super if
|
143
|
+
GeneratedAttributeMethods::LOCK.synchronize do
|
144
|
+
super if @attribute_methods_generated
|
141
145
|
@attribute_methods_generated = false
|
142
146
|
@alias_attributes_mass_generated = false
|
143
147
|
end
|
@@ -288,9 +292,7 @@ module ActiveRecord
|
|
288
292
|
|
289
293
|
# If the result is true then check for the select case.
|
290
294
|
# For queries selecting a subset of columns, return false for unselected columns.
|
291
|
-
|
292
|
-
# have been allocated but not yet initialized.
|
293
|
-
if defined?(@attributes)
|
295
|
+
if @attributes
|
294
296
|
if name = self.class.symbol_column_to_string(name.to_sym)
|
295
297
|
return _has_attribute?(name)
|
296
298
|
end
|
@@ -459,9 +461,38 @@ module ActiveRecord
|
|
459
461
|
end
|
460
462
|
|
461
463
|
private
|
464
|
+
def respond_to_missing?(name, include_private = false)
|
465
|
+
if self.class.define_attribute_methods
|
466
|
+
# Some methods weren't defined yet.
|
467
|
+
return true if self.class.method_defined?(name)
|
468
|
+
return true if include_private && self.class.private_method_defined?(name)
|
469
|
+
end
|
470
|
+
|
471
|
+
super
|
472
|
+
end
|
473
|
+
|
474
|
+
def method_missing(name, ...)
|
475
|
+
unless self.class.attribute_methods_generated?
|
476
|
+
if self.class.method_defined?(name)
|
477
|
+
# The method is explicitly defined in the model, but calls a generated
|
478
|
+
# method with super. So we must resume the call chain at the right step.
|
479
|
+
last_method = method(name)
|
480
|
+
last_method = last_method.super_method while last_method.super_method
|
481
|
+
self.class.define_attribute_methods
|
482
|
+
if last_method.super_method
|
483
|
+
return last_method.super_method.call(...)
|
484
|
+
end
|
485
|
+
elsif self.class.define_attribute_methods
|
486
|
+
# Some attribute methods weren't generated yet, we retry the call
|
487
|
+
return public_send(name, ...)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
super
|
492
|
+
end
|
493
|
+
|
462
494
|
def attribute_method?(attr_name)
|
463
|
-
|
464
|
-
defined?(@attributes) && @attributes.key?(attr_name)
|
495
|
+
@attributes&.key?(attr_name)
|
465
496
|
end
|
466
497
|
|
467
498
|
def attributes_with_values(attribute_names)
|