activerecord 7.1.5.2 → 8.1.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 +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- 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 +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- 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/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -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/join_dependency.rb +4 -2
- 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/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -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 +250 -315
- 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/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- 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/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- 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 +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- 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 +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- 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 +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- 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 +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -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/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -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") && !_has_attribute?("id_value")
|
|
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)
|
|
@@ -6,12 +6,14 @@ module ActiveRecord
|
|
|
6
6
|
# See ActiveRecord::Attributes::ClassMethods for documentation
|
|
7
7
|
module Attributes
|
|
8
8
|
extend ActiveSupport::Concern
|
|
9
|
+
include ActiveModel::AttributeRegistration
|
|
10
|
+
include ActiveModel::Attributes::Normalization
|
|
9
11
|
|
|
10
|
-
included do
|
|
11
|
-
class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
|
|
12
|
-
end
|
|
13
12
|
# = Active Record \Attributes
|
|
14
13
|
module ClassMethods
|
|
14
|
+
# :method: attribute
|
|
15
|
+
# :call-seq: attribute(name, cast_type = nil, **options)
|
|
16
|
+
#
|
|
15
17
|
# Defines an attribute with a type on this model. It will override the
|
|
16
18
|
# type of existing attributes if needed. This allows control over how
|
|
17
19
|
# values are converted to and from SQL when assigned to a model. It also
|
|
@@ -20,26 +22,34 @@ module ActiveRecord
|
|
|
20
22
|
# your domain objects across much of Active Record, without having to
|
|
21
23
|
# rely on implementation details or monkey patching.
|
|
22
24
|
#
|
|
23
|
-
#
|
|
24
|
-
# column which this will persist to.
|
|
25
|
+
# ==== Parameters
|
|
25
26
|
#
|
|
26
|
-
# +
|
|
27
|
-
#
|
|
28
|
-
#
|
|
27
|
+
# [+name+]
|
|
28
|
+
# The name of the methods to define attribute methods for, and the
|
|
29
|
+
# column which this will persist to.
|
|
29
30
|
#
|
|
30
|
-
#
|
|
31
|
+
# [+cast_type+]
|
|
32
|
+
# A symbol such as +:string+ or +:integer+, or a type object to be used
|
|
33
|
+
# for this attribute. If this parameter is not passed, the previously
|
|
34
|
+
# defined type (if any) will be used. Otherwise, the type will be
|
|
35
|
+
# ActiveModel::Type::Value. See the examples below for more information
|
|
36
|
+
# about providing custom type objects.
|
|
31
37
|
#
|
|
32
|
-
#
|
|
38
|
+
# ==== Options
|
|
33
39
|
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
40
|
+
# [+:default+]
|
|
41
|
+
# The default value to use when no value is provided. If this option is
|
|
42
|
+
# not passed, the previously defined default value (if any) on the
|
|
43
|
+
# superclass or in the schema will be used. Otherwise, the default will
|
|
44
|
+
# be +nil+.
|
|
37
45
|
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
46
|
+
# [+:array+]
|
|
47
|
+
# (PostgreSQL only) Specifies that the type should be an array. See the
|
|
48
|
+
# examples below.
|
|
40
49
|
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
50
|
+
# [+:range+]
|
|
51
|
+
# (PostgreSQL only) Specifies that the type should be a range. See the
|
|
52
|
+
# examples below.
|
|
43
53
|
#
|
|
44
54
|
# When using a symbol for +cast_type+, extra options are forwarded to the
|
|
45
55
|
# constructor of the type object.
|
|
@@ -134,7 +144,7 @@ module ActiveRecord
|
|
|
134
144
|
# expected API. It is recommended that your type objects inherit from an
|
|
135
145
|
# existing type, or from ActiveRecord::Type::Value
|
|
136
146
|
#
|
|
137
|
-
# class
|
|
147
|
+
# class PriceType < ActiveRecord::Type::Integer
|
|
138
148
|
# def cast(value)
|
|
139
149
|
# if !value.kind_of?(Numeric) && value.include?('$')
|
|
140
150
|
# price_in_dollars = value.gsub(/\$/, '').to_f
|
|
@@ -146,11 +156,11 @@ module ActiveRecord
|
|
|
146
156
|
# end
|
|
147
157
|
#
|
|
148
158
|
# # config/initializers/types.rb
|
|
149
|
-
# ActiveRecord::Type.register(:
|
|
159
|
+
# ActiveRecord::Type.register(:price, PriceType)
|
|
150
160
|
#
|
|
151
161
|
# # app/models/store_listing.rb
|
|
152
162
|
# class StoreListing < ActiveRecord::Base
|
|
153
|
-
# attribute :price_in_cents, :
|
|
163
|
+
# attribute :price_in_cents, :price
|
|
154
164
|
# end
|
|
155
165
|
#
|
|
156
166
|
# store_listing = StoreListing.new(price_in_cents: '$10.00')
|
|
@@ -170,13 +180,13 @@ module ActiveRecord
|
|
|
170
180
|
# class Money < Struct.new(:amount, :currency)
|
|
171
181
|
# end
|
|
172
182
|
#
|
|
173
|
-
# class
|
|
183
|
+
# class PriceType < ActiveRecord::Type::Value
|
|
174
184
|
# def initialize(currency_converter:)
|
|
175
185
|
# @currency_converter = currency_converter
|
|
176
186
|
# end
|
|
177
187
|
#
|
|
178
|
-
# # value will be the result of
|
|
179
|
-
# #
|
|
188
|
+
# # value will be the result of #deserialize or
|
|
189
|
+
# # #cast. Assumed to be an instance of Money in
|
|
180
190
|
# # this case.
|
|
181
191
|
# def serialize(value)
|
|
182
192
|
# value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
|
|
@@ -185,12 +195,12 @@ module ActiveRecord
|
|
|
185
195
|
# end
|
|
186
196
|
#
|
|
187
197
|
# # config/initializers/types.rb
|
|
188
|
-
# ActiveRecord::Type.register(:
|
|
198
|
+
# ActiveRecord::Type.register(:price, PriceType)
|
|
189
199
|
#
|
|
190
200
|
# # app/models/product.rb
|
|
191
201
|
# class Product < ActiveRecord::Base
|
|
192
202
|
# currency_converter = ConversionRatesFromTheInternet.new
|
|
193
|
-
# attribute :price_in_bitcoins, :
|
|
203
|
+
# attribute :price_in_bitcoins, :price, currency_converter: currency_converter
|
|
194
204
|
# end
|
|
195
205
|
#
|
|
196
206
|
# Product.where(price_in_bitcoins: Money.new(5, "USD"))
|
|
@@ -205,51 +215,31 @@ module ActiveRecord
|
|
|
205
215
|
# tracking is performed. The methods +changed?+ and +changed_in_place?+
|
|
206
216
|
# will be called from ActiveModel::Dirty. See the documentation for those
|
|
207
217
|
# methods in ActiveModel::Type::Value for more details.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
reload_schema_from_cache
|
|
213
|
-
|
|
214
|
-
case cast_type
|
|
215
|
-
when Symbol
|
|
216
|
-
cast_type = Type.lookup(cast_type, **options, adapter: Type.adapter_name_from(self))
|
|
217
|
-
when nil
|
|
218
|
-
if (prev_cast_type, prev_default = attributes_to_define_after_schema_loads[name])
|
|
219
|
-
default = prev_default if default == NO_DEFAULT_PROVIDED
|
|
220
|
-
else
|
|
221
|
-
prev_cast_type = -> subtype { subtype }
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
cast_type = if block_given?
|
|
225
|
-
-> subtype { yield Proc === prev_cast_type ? prev_cast_type[subtype] : prev_cast_type }
|
|
226
|
-
else
|
|
227
|
-
prev_cast_type
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
self.attributes_to_define_after_schema_loads =
|
|
232
|
-
attributes_to_define_after_schema_loads.merge(name => [cast_type, default])
|
|
233
|
-
end
|
|
218
|
+
#
|
|
219
|
+
#--
|
|
220
|
+
# Implemented by ActiveModel::AttributeRegistration#attribute.
|
|
234
221
|
|
|
235
|
-
# This
|
|
236
|
-
#
|
|
237
|
-
# waiting for the schema to load. Automatic schema detection and
|
|
238
|
-
# ClassMethods#attribute both call this under the hood. While this method
|
|
222
|
+
# This API only accepts type objects, and will do its work immediately instead of
|
|
223
|
+
# waiting for the schema to load. While this method
|
|
239
224
|
# is provided so it can be used by plugin authors, application code
|
|
240
225
|
# should probably use ClassMethods#attribute.
|
|
241
226
|
#
|
|
242
|
-
#
|
|
227
|
+
# ==== Parameters
|
|
243
228
|
#
|
|
244
|
-
# +
|
|
229
|
+
# [+name+]
|
|
230
|
+
# The name of the attribute being defined. Expected to be a +String+.
|
|
245
231
|
#
|
|
246
|
-
# +
|
|
247
|
-
#
|
|
248
|
-
# Otherwise, the default will be +nil+. A proc can also be passed, and
|
|
249
|
-
# will be called once each time a new value is needed.
|
|
232
|
+
# [+cast_type+]
|
|
233
|
+
# The type object to use for this attribute.
|
|
250
234
|
#
|
|
251
|
-
# +
|
|
252
|
-
#
|
|
235
|
+
# [+default+]
|
|
236
|
+
# The default value to use when no value is provided. If this option
|
|
237
|
+
# is not passed, the previous default value (if any) will be used.
|
|
238
|
+
# Otherwise, the default will be +nil+. A proc can also be passed, and
|
|
239
|
+
# will be called once each time a new value is needed.
|
|
240
|
+
#
|
|
241
|
+
# [+user_provided_default+]
|
|
242
|
+
# Whether the default value should be cast using +cast+ or +deserialize+.
|
|
253
243
|
def define_attribute(
|
|
254
244
|
name,
|
|
255
245
|
cast_type,
|
|
@@ -260,14 +250,39 @@ module ActiveRecord
|
|
|
260
250
|
define_default_attribute(name, default, cast_type, from_user: user_provided_default)
|
|
261
251
|
end
|
|
262
252
|
|
|
263
|
-
def
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
253
|
+
def _default_attributes # :nodoc:
|
|
254
|
+
@default_attributes ||= begin
|
|
255
|
+
# TODO: Remove the need for a connection after we release 8.1.
|
|
256
|
+
attributes_hash = with_connection do |connection|
|
|
257
|
+
columns_hash.transform_values do |column|
|
|
258
|
+
ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
attribute_set = ActiveModel::AttributeSet.new(attributes_hash)
|
|
263
|
+
apply_pending_attribute_modifications(attribute_set)
|
|
264
|
+
attribute_set
|
|
268
265
|
end
|
|
269
266
|
end
|
|
270
267
|
|
|
268
|
+
##
|
|
269
|
+
# :method: type_for_attribute
|
|
270
|
+
# :call-seq: type_for_attribute(attribute_name, &block)
|
|
271
|
+
#
|
|
272
|
+
# See ActiveModel::Attributes::ClassMethods#type_for_attribute.
|
|
273
|
+
#
|
|
274
|
+
# This method will access the database and load the model's schema if
|
|
275
|
+
# necessary.
|
|
276
|
+
#--
|
|
277
|
+
# Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
|
|
278
|
+
|
|
279
|
+
##
|
|
280
|
+
protected
|
|
281
|
+
def reload_schema_from_cache(*)
|
|
282
|
+
reset_default_attributes!
|
|
283
|
+
super
|
|
284
|
+
end
|
|
285
|
+
|
|
271
286
|
private
|
|
272
287
|
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
|
|
273
288
|
private_constant :NO_DEFAULT_PROVIDED
|
|
@@ -287,6 +302,19 @@ module ActiveRecord
|
|
|
287
302
|
end
|
|
288
303
|
_default_attributes[name] = default_attribute
|
|
289
304
|
end
|
|
305
|
+
|
|
306
|
+
def reset_default_attributes
|
|
307
|
+
reload_schema_from_cache
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def resolve_type_name(name, **options)
|
|
311
|
+
Type.lookup(name, **options, adapter: Type.adapter_name_from(self))
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def type_for_column(connection, column)
|
|
315
|
+
# TODO: Remove the need for a connection after we release 8.1.
|
|
316
|
+
hook_attribute_type(column.name, super)
|
|
317
|
+
end
|
|
290
318
|
end
|
|
291
319
|
end
|
|
292
320
|
end
|