activerecord 7.1.5.1 → 8.0.2
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 +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- 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 +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- 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 +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +92 -295
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- 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/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- 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 +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- 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/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- 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 +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- 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 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- 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 +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -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 +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- 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 +72 -17
- 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 +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/nodes/binary.rb +1 -7
- 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 +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- 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/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- 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 +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -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
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
|
-
|
5
3
|
module ActiveRecord
|
6
4
|
module AttributeMethods
|
7
5
|
# = Active Record Attribute Methods Primary Key
|
@@ -18,74 +16,45 @@ module ActiveRecord
|
|
18
16
|
# Returns the primary key column's value. If the primary key is composite,
|
19
17
|
# returns an array of the primary key column values.
|
20
18
|
def id
|
21
|
-
|
22
|
-
|
23
|
-
@primary_key.map { |pk| _read_attribute(pk) }
|
19
|
+
_read_attribute(@primary_key)
|
24
20
|
end
|
25
21
|
|
26
22
|
def primary_key_values_present? # :nodoc:
|
27
|
-
return id.all? if self.class.composite_primary_key?
|
28
|
-
|
29
23
|
!!id
|
30
24
|
end
|
31
25
|
|
32
26
|
# Sets the primary key column's value. If the primary key is composite,
|
33
27
|
# raises TypeError when the set value not enumerable.
|
34
28
|
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
|
29
|
+
_write_attribute(@primary_key, value)
|
41
30
|
end
|
42
31
|
|
43
32
|
# Queries the primary key column's value. If the primary key is composite,
|
44
33
|
# all primary key column values must be queryable.
|
45
34
|
def id?
|
46
|
-
|
47
|
-
@primary_key.all? { |col| _query_attribute(col) }
|
48
|
-
else
|
49
|
-
_query_attribute(@primary_key)
|
50
|
-
end
|
35
|
+
_query_attribute(@primary_key)
|
51
36
|
end
|
52
37
|
|
53
38
|
# Returns the primary key column's value before type cast. If the primary key is composite,
|
54
39
|
# returns an array of primary key column values before type cast.
|
55
40
|
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
|
41
|
+
attribute_before_type_cast(@primary_key)
|
61
42
|
end
|
62
43
|
|
63
44
|
# Returns the primary key column's previous value. If the primary key is composite,
|
64
45
|
# returns an array of primary key column previous values.
|
65
46
|
def id_was
|
66
|
-
|
67
|
-
@primary_key.map { |col| attribute_was(col) }
|
68
|
-
else
|
69
|
-
attribute_was(@primary_key)
|
70
|
-
end
|
47
|
+
attribute_was(@primary_key)
|
71
48
|
end
|
72
49
|
|
73
50
|
# Returns the primary key column's value from the database. If the primary key is composite,
|
74
51
|
# returns an array of primary key column values from database.
|
75
52
|
def id_in_database
|
76
|
-
|
77
|
-
@primary_key.map { |col| attribute_in_database(col) }
|
78
|
-
else
|
79
|
-
attribute_in_database(@primary_key)
|
80
|
-
end
|
53
|
+
attribute_in_database(@primary_key)
|
81
54
|
end
|
82
55
|
|
83
56
|
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
|
57
|
+
@attributes[@primary_key].value_for_database
|
89
58
|
end
|
90
59
|
|
91
60
|
private
|
@@ -109,20 +78,18 @@ module ActiveRecord
|
|
109
78
|
# Overwriting will negate any effect of the +primary_key_prefix_type+
|
110
79
|
# setting, though.
|
111
80
|
def primary_key
|
112
|
-
if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
113
|
-
@primary_key = reset_primary_key
|
114
|
-
end
|
81
|
+
reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
115
82
|
@primary_key
|
116
83
|
end
|
117
84
|
|
118
85
|
def composite_primary_key? # :nodoc:
|
119
|
-
|
86
|
+
reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
87
|
+
@composite_primary_key
|
120
88
|
end
|
121
89
|
|
122
|
-
# Returns a quoted version of the primary key name
|
123
|
-
# SQL statements.
|
90
|
+
# Returns a quoted version of the primary key name.
|
124
91
|
def quoted_primary_key
|
125
|
-
|
92
|
+
adapter_class.quote_column_name(primary_key)
|
126
93
|
end
|
127
94
|
|
128
95
|
def reset_primary_key # :nodoc:
|
@@ -138,12 +105,10 @@ module ActiveRecord
|
|
138
105
|
base_name.foreign_key(false)
|
139
106
|
elsif base_name && primary_key_prefix_type == :table_name_with_underscore
|
140
107
|
base_name.foreign_key
|
108
|
+
elsif ActiveRecord::Base != self && table_exists?
|
109
|
+
schema_cache.primary_keys(table_name)
|
141
110
|
else
|
142
|
-
|
143
|
-
connection.schema_cache.primary_keys(table_name)
|
144
|
-
else
|
145
|
-
"id"
|
146
|
-
end
|
111
|
+
"id"
|
147
112
|
end
|
148
113
|
end
|
149
114
|
|
@@ -163,25 +128,24 @@ module ActiveRecord
|
|
163
128
|
#
|
164
129
|
# Project.primary_key # => "foo_id"
|
165
130
|
def primary_key=(value)
|
166
|
-
@primary_key
|
167
|
-
|
131
|
+
@primary_key = if value.is_a?(Array)
|
132
|
+
include CompositePrimaryKey
|
133
|
+
@primary_key = value.map { |v| -v.to_s }.freeze
|
134
|
+
elsif value
|
135
|
+
-value.to_s
|
136
|
+
end
|
137
|
+
|
138
|
+
@composite_primary_key = value.is_a?(Array)
|
168
139
|
@attributes_builder = nil
|
169
140
|
end
|
170
141
|
|
171
142
|
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
143
|
def inherited(base)
|
181
144
|
super
|
182
145
|
base.class_eval do
|
183
146
|
@primary_key = PRIMARY_KEY_NOT_SET
|
184
|
-
@
|
147
|
+
@composite_primary_key = false
|
148
|
+
@attributes_builder = nil
|
185
149
|
end
|
186
150
|
end
|
187
151
|
end
|
@@ -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
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
28
28
|
elsif value.respond_to?(:infinite?) && value.infinite?
|
29
29
|
value
|
30
30
|
else
|
31
|
-
|
31
|
+
map(super) { |v| cast(v) }
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -45,23 +45,13 @@ module ActiveRecord
|
|
45
45
|
elsif value.respond_to?(:infinite?) && value.infinite?
|
46
46
|
value
|
47
47
|
else
|
48
|
-
|
48
|
+
map(value) { |v| convert_time_to_time_zone(v) }
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
def set_time_zone_without_conversion(value)
|
53
53
|
::Time.zone.local_to_utc(value).try(:in_time_zone) if value
|
54
54
|
end
|
55
|
-
|
56
|
-
def map_avoiding_infinite_recursion(value)
|
57
|
-
map(value) do |v|
|
58
|
-
if value.equal?(v)
|
59
|
-
nil
|
60
|
-
else
|
61
|
-
yield(v)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
55
|
end
|
66
56
|
|
67
57
|
extend ActiveSupport::Concern
|
@@ -73,14 +63,15 @@ module ActiveRecord
|
|
73
63
|
end
|
74
64
|
|
75
65
|
module ClassMethods # :nodoc:
|
76
|
-
|
77
|
-
|
78
|
-
|
66
|
+
private
|
67
|
+
def hook_attribute_type(name, cast_type)
|
68
|
+
if create_time_zone_conversion_attribute?(name, cast_type)
|
69
|
+
cast_type = TimeZoneConverter.new(cast_type)
|
70
|
+
end
|
71
|
+
|
72
|
+
super
|
79
73
|
end
|
80
|
-
super
|
81
|
-
end
|
82
74
|
|
83
|
-
private
|
84
75
|
def create_time_zone_conversion_attribute?(name, cast_type)
|
85
76
|
enabled_for_column = time_zone_aware_attributes &&
|
86
77
|
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
|
@@ -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,25 +77,6 @@ module ActiveRecord
|
|
64
77
|
# alias attributes in Active Record are lazily generated
|
65
78
|
end
|
66
79
|
|
67
|
-
def generate_alias_attributes # :nodoc:
|
68
|
-
superclass.generate_alias_attributes unless superclass == Base
|
69
|
-
return false if @alias_attributes_mass_generated
|
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
|
82
|
-
end
|
83
|
-
true
|
84
|
-
end
|
85
|
-
|
86
80
|
def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
|
87
81
|
attribute_method_patterns.each do |pattern|
|
88
82
|
alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
@@ -91,63 +85,64 @@ module ActiveRecord
|
|
91
85
|
end
|
92
86
|
|
93
87
|
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name) # :nodoc:
|
94
|
-
method_name = pattern.method_name(new_name).to_s
|
95
|
-
target_name = pattern.method_name(old_name).to_s
|
96
88
|
old_name = old_name.to_s
|
97
89
|
|
98
|
-
method_defined = method_defined?(target_name) || private_method_defined?(target_name)
|
99
|
-
manually_defined = method_defined &&
|
100
|
-
!self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
|
101
|
-
reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)
|
102
|
-
|
103
90
|
if !abstract_class? && !has_attribute?(old_name)
|
104
|
-
|
105
|
-
|
106
|
-
if should_warn
|
107
|
-
ActiveRecord.deprecator.warn(
|
108
|
-
"#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
109
|
-
"Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
|
110
|
-
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
111
|
-
)
|
112
|
-
end
|
113
|
-
super
|
114
|
-
elsif manually_defined && !reserved_method_name
|
115
|
-
aliased_method_redefined_as_well = method_defined_within?(method_name, self)
|
116
|
-
return if aliased_method_redefined_as_well
|
117
|
-
|
118
|
-
ActiveRecord.deprecator.warn(
|
119
|
-
"#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
|
120
|
-
"Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
|
121
|
-
"You may want to additionally define `#{method_name}` to preserve the current behavior."
|
122
|
-
)
|
123
|
-
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."
|
124
93
|
else
|
125
94
|
define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
|
126
95
|
end
|
127
96
|
end
|
128
97
|
|
98
|
+
def attribute_methods_generated? # :nodoc:
|
99
|
+
@attribute_methods_generated
|
100
|
+
end
|
101
|
+
|
129
102
|
# Generates all the attribute related methods for columns in the database
|
130
103
|
# accessors, mutators and query methods.
|
131
104
|
def define_attribute_methods # :nodoc:
|
132
105
|
return false if @attribute_methods_generated
|
133
106
|
# Use a mutex; we don't want two threads simultaneously trying to define
|
134
107
|
# attribute methods.
|
135
|
-
|
108
|
+
GeneratedAttributeMethods::LOCK.synchronize do
|
136
109
|
return false if @attribute_methods_generated
|
110
|
+
|
137
111
|
superclass.define_attribute_methods unless base_class?
|
138
|
-
|
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
|
+
|
119
|
+
generate_alias_attributes
|
120
|
+
|
139
121
|
@attribute_methods_generated = true
|
140
122
|
end
|
123
|
+
|
141
124
|
true
|
142
125
|
end
|
143
126
|
|
144
|
-
def
|
145
|
-
|
127
|
+
def generate_alias_attributes # :nodoc:
|
128
|
+
superclass.generate_alias_attributes unless superclass == Base
|
129
|
+
|
130
|
+
return if @alias_attributes_mass_generated
|
131
|
+
|
132
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
133
|
+
aliases_by_attribute_name.each do |old_name, new_names|
|
134
|
+
new_names.each do |new_name|
|
135
|
+
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
@alias_attributes_mass_generated = true
|
146
141
|
end
|
147
142
|
|
148
143
|
def undefine_attribute_methods # :nodoc:
|
149
|
-
|
150
|
-
super if
|
144
|
+
GeneratedAttributeMethods::LOCK.synchronize do
|
145
|
+
super if @attribute_methods_generated
|
151
146
|
@attribute_methods_generated = false
|
152
147
|
@alias_attributes_mass_generated = false
|
153
148
|
end
|
@@ -298,9 +293,7 @@ module ActiveRecord
|
|
298
293
|
|
299
294
|
# If the result is true then check for the select case.
|
300
295
|
# For queries selecting a subset of columns, return false for unselected columns.
|
301
|
-
|
302
|
-
# have been allocated but not yet initialized.
|
303
|
-
if defined?(@attributes)
|
296
|
+
if @attributes
|
304
297
|
if name = self.class.symbol_column_to_string(name.to_sym)
|
305
298
|
return _has_attribute?(name)
|
306
299
|
end
|
@@ -480,28 +473,31 @@ module ActiveRecord
|
|
480
473
|
end
|
481
474
|
|
482
475
|
def method_missing(name, ...)
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
# Some attribute methods weren't generated yet, we retry the call
|
495
|
-
return public_send(name, ...)
|
496
|
-
end
|
476
|
+
# We can't know whether some method was defined or not because
|
477
|
+
# multiple thread might be concurrently be in this code path.
|
478
|
+
# So the first one would define the methods and the others would
|
479
|
+
# appear to already have them.
|
480
|
+
self.class.define_attribute_methods
|
481
|
+
|
482
|
+
# So in all cases we must behave as if the method was just defined.
|
483
|
+
method = begin
|
484
|
+
self.class.public_instance_method(name)
|
485
|
+
rescue NameError
|
486
|
+
nil
|
497
487
|
end
|
498
488
|
|
499
|
-
|
489
|
+
# The method might be explicitly defined in the model, but call a generated
|
490
|
+
# method with super. So we must resume the call chain at the right step.
|
491
|
+
method = method.super_method while method && !method.owner.is_a?(GeneratedAttributeMethods)
|
492
|
+
if method
|
493
|
+
method.bind_call(self, ...)
|
494
|
+
else
|
495
|
+
super
|
496
|
+
end
|
500
497
|
end
|
501
498
|
|
502
499
|
def attribute_method?(attr_name)
|
503
|
-
|
504
|
-
defined?(@attributes) && @attributes.key?(attr_name)
|
500
|
+
@attributes&.key?(attr_name)
|
505
501
|
end
|
506
502
|
|
507
503
|
def attributes_with_values(attribute_names)
|