activerecord 7.1.5.1 → 7.2.0.beta1
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 +515 -2445
- 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 +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +14 -7
- 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 +6 -4
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
- data/lib/active_record/associations/join_dependency.rb +5 -5
- 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 +33 -16
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +4 -16
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +60 -71
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +13 -32
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
- 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 +14 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
- 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 +7 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
- 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 +1 -1
- 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 +15 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +53 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -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 +24 -0
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
- data/lib/active_record/encryption/encryptor.rb +17 -2
- 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.rb +0 -2
- data/lib/active_record/enum.rb +10 -1
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -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 +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -4
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -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 +28 -68
- data/lib/active_record/nested_attributes.rb +13 -16
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +50 -62
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +90 -35
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- 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.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -57
- 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 +496 -72
- data/lib/active_record/result.rb +31 -44
- 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/signed_id.rb +11 -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 +76 -70
- 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 +81 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +68 -0
- data/lib/active_record/transactions.rb +43 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- 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 +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/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- metadata +20 -15
@@ -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,90 +77,69 @@ module ActiveRecord
|
|
64
77
|
# alias attributes in Active Record are lazily generated
|
65
78
|
end
|
66
79
|
|
67
|
-
def
|
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
|
-
def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
|
87
|
-
attribute_method_patterns.each do |pattern|
|
88
|
-
alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
89
|
-
end
|
90
|
-
attribute_method_patterns_cache.clear
|
91
|
-
end
|
92
|
-
|
93
|
-
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
|
80
|
+
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
96
81
|
old_name = old_name.to_s
|
97
82
|
|
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
83
|
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
|
84
|
+
raise ArgumentError, "#{self.name} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
85
|
+
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
124
86
|
else
|
125
|
-
|
87
|
+
method_name = pattern.method_name(new_name).to_s
|
88
|
+
parameters = pattern.parameters
|
89
|
+
|
90
|
+
define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
|
91
|
+
namespace: :proxy_alias_attribute
|
92
|
+
)
|
126
93
|
end
|
127
94
|
end
|
128
95
|
|
96
|
+
def attribute_methods_generated? # :nodoc:
|
97
|
+
@attribute_methods_generated
|
98
|
+
end
|
99
|
+
|
129
100
|
# Generates all the attribute related methods for columns in the database
|
130
101
|
# accessors, mutators and query methods.
|
131
102
|
def define_attribute_methods # :nodoc:
|
132
103
|
return false if @attribute_methods_generated
|
133
104
|
# Use a mutex; we don't want two threads simultaneously trying to define
|
134
105
|
# attribute methods.
|
135
|
-
|
106
|
+
GeneratedAttributeMethods::LOCK.synchronize do
|
136
107
|
return false if @attribute_methods_generated
|
108
|
+
|
137
109
|
superclass.define_attribute_methods unless base_class?
|
138
|
-
|
110
|
+
|
111
|
+
unless abstract_class?
|
112
|
+
load_schema
|
113
|
+
super(attribute_names)
|
114
|
+
alias_attribute :id_value, :id if _has_attribute?("id")
|
115
|
+
end
|
116
|
+
|
139
117
|
@attribute_methods_generated = true
|
118
|
+
|
119
|
+
generate_alias_attributes
|
140
120
|
end
|
141
121
|
true
|
142
122
|
end
|
143
123
|
|
144
|
-
def
|
145
|
-
|
124
|
+
def generate_alias_attributes # :nodoc:
|
125
|
+
superclass.generate_alias_attributes unless superclass == Base
|
126
|
+
|
127
|
+
return if @alias_attributes_mass_generated
|
128
|
+
|
129
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
130
|
+
aliases_by_attribute_name.each do |old_name, new_names|
|
131
|
+
new_names.each do |new_name|
|
132
|
+
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
@alias_attributes_mass_generated = true
|
146
138
|
end
|
147
139
|
|
148
140
|
def undefine_attribute_methods # :nodoc:
|
149
|
-
|
150
|
-
super if
|
141
|
+
GeneratedAttributeMethods::LOCK.synchronize do
|
142
|
+
super if @attribute_methods_generated
|
151
143
|
@attribute_methods_generated = false
|
152
144
|
@alias_attributes_mass_generated = false
|
153
145
|
end
|
@@ -298,9 +290,7 @@ module ActiveRecord
|
|
298
290
|
|
299
291
|
# If the result is true then check for the select case.
|
300
292
|
# 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)
|
293
|
+
if @attributes
|
304
294
|
if name = self.class.symbol_column_to_string(name.to_sym)
|
305
295
|
return _has_attribute?(name)
|
306
296
|
end
|
@@ -483,14 +473,14 @@ module ActiveRecord
|
|
483
473
|
unless self.class.attribute_methods_generated?
|
484
474
|
if self.class.method_defined?(name)
|
485
475
|
# The method is explicitly defined in the model, but calls a generated
|
486
|
-
# method with super. So we must resume the call chain at the right
|
476
|
+
# method with super. So we must resume the call chain at the right step.
|
487
477
|
last_method = method(name)
|
488
478
|
last_method = last_method.super_method while last_method.super_method
|
489
479
|
self.class.define_attribute_methods
|
490
480
|
if last_method.super_method
|
491
481
|
return last_method.super_method.call(...)
|
492
482
|
end
|
493
|
-
elsif self.class.define_attribute_methods
|
483
|
+
elsif self.class.define_attribute_methods
|
494
484
|
# Some attribute methods weren't generated yet, we retry the call
|
495
485
|
return public_send(name, ...)
|
496
486
|
end
|
@@ -500,8 +490,7 @@ module ActiveRecord
|
|
500
490
|
end
|
501
491
|
|
502
492
|
def attribute_method?(attr_name)
|
503
|
-
|
504
|
-
defined?(@attributes) && @attributes.key?(attr_name)
|
493
|
+
@attributes&.key?(attr_name)
|
505
494
|
end
|
506
495
|
|
507
496
|
def attributes_with_values(attribute_names)
|
@@ -6,12 +6,13 @@ module ActiveRecord
|
|
6
6
|
# See ActiveRecord::Attributes::ClassMethods for documentation
|
7
7
|
module Attributes
|
8
8
|
extend ActiveSupport::Concern
|
9
|
+
include ActiveModel::AttributeRegistration
|
9
10
|
|
10
|
-
included do
|
11
|
-
class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
|
12
|
-
end
|
13
11
|
# = Active Record \Attributes
|
14
12
|
module ClassMethods
|
13
|
+
# :method: attribute
|
14
|
+
# :call-seq: attribute(name, cast_type = nil, **options)
|
15
|
+
#
|
15
16
|
# Defines an attribute with a type on this model. It will override the
|
16
17
|
# type of existing attributes if needed. This allows control over how
|
17
18
|
# values are converted to and from SQL when assigned to a model. It also
|
@@ -134,7 +135,7 @@ module ActiveRecord
|
|
134
135
|
# expected API. It is recommended that your type objects inherit from an
|
135
136
|
# existing type, or from ActiveRecord::Type::Value
|
136
137
|
#
|
137
|
-
# class
|
138
|
+
# class PriceType < ActiveRecord::Type::Integer
|
138
139
|
# def cast(value)
|
139
140
|
# if !value.kind_of?(Numeric) && value.include?('$')
|
140
141
|
# price_in_dollars = value.gsub(/\$/, '').to_f
|
@@ -146,11 +147,11 @@ module ActiveRecord
|
|
146
147
|
# end
|
147
148
|
#
|
148
149
|
# # config/initializers/types.rb
|
149
|
-
# ActiveRecord::Type.register(:
|
150
|
+
# ActiveRecord::Type.register(:price, PriceType)
|
150
151
|
#
|
151
152
|
# # app/models/store_listing.rb
|
152
153
|
# class StoreListing < ActiveRecord::Base
|
153
|
-
# attribute :price_in_cents, :
|
154
|
+
# attribute :price_in_cents, :price
|
154
155
|
# end
|
155
156
|
#
|
156
157
|
# store_listing = StoreListing.new(price_in_cents: '$10.00')
|
@@ -170,7 +171,7 @@ module ActiveRecord
|
|
170
171
|
# class Money < Struct.new(:amount, :currency)
|
171
172
|
# end
|
172
173
|
#
|
173
|
-
# class
|
174
|
+
# class PriceType < ActiveRecord::Type::Value
|
174
175
|
# def initialize(currency_converter:)
|
175
176
|
# @currency_converter = currency_converter
|
176
177
|
# end
|
@@ -185,12 +186,12 @@ module ActiveRecord
|
|
185
186
|
# end
|
186
187
|
#
|
187
188
|
# # config/initializers/types.rb
|
188
|
-
# ActiveRecord::Type.register(:
|
189
|
+
# ActiveRecord::Type.register(:price, PriceType)
|
189
190
|
#
|
190
191
|
# # app/models/product.rb
|
191
192
|
# class Product < ActiveRecord::Base
|
192
193
|
# currency_converter = ConversionRatesFromTheInternet.new
|
193
|
-
# attribute :price_in_bitcoins, :
|
194
|
+
# attribute :price_in_bitcoins, :price, currency_converter: currency_converter
|
194
195
|
# end
|
195
196
|
#
|
196
197
|
# Product.where(price_in_bitcoins: Money.new(5, "USD"))
|
@@ -205,37 +206,13 @@ module ActiveRecord
|
|
205
206
|
# tracking is performed. The methods +changed?+ and +changed_in_place?+
|
206
207
|
# will be called from ActiveModel::Dirty. See the documentation for those
|
207
208
|
# 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
|
209
|
+
#
|
210
|
+
#--
|
211
|
+
# Implemented by ActiveModel::AttributeRegistration#attribute.
|
234
212
|
|
235
213
|
# This is the low level API which sits beneath +attribute+. It only
|
236
214
|
# accepts type objects, and will do its work immediately instead of
|
237
|
-
# waiting for the schema to load.
|
238
|
-
# ClassMethods#attribute both call this under the hood. While this method
|
215
|
+
# waiting for the schema to load. While this method
|
239
216
|
# is provided so it can be used by plugin authors, application code
|
240
217
|
# should probably use ClassMethods#attribute.
|
241
218
|
#
|
@@ -260,14 +237,38 @@ module ActiveRecord
|
|
260
237
|
define_default_attribute(name, default, cast_type, from_user: user_provided_default)
|
261
238
|
end
|
262
239
|
|
263
|
-
def
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
240
|
+
def _default_attributes # :nodoc:
|
241
|
+
@default_attributes ||= begin
|
242
|
+
attributes_hash = with_connection do |connection|
|
243
|
+
columns_hash.transform_values do |column|
|
244
|
+
ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
attribute_set = ActiveModel::AttributeSet.new(attributes_hash)
|
249
|
+
apply_pending_attribute_modifications(attribute_set)
|
250
|
+
attribute_set
|
268
251
|
end
|
269
252
|
end
|
270
253
|
|
254
|
+
##
|
255
|
+
# :method: type_for_attribute
|
256
|
+
# :call-seq: type_for_attribute(attribute_name, &block)
|
257
|
+
#
|
258
|
+
# See ActiveModel::Attributes::ClassMethods#type_for_attribute.
|
259
|
+
#
|
260
|
+
# This method will access the database and load the model's schema if
|
261
|
+
# necessary.
|
262
|
+
#--
|
263
|
+
# Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
|
264
|
+
|
265
|
+
##
|
266
|
+
protected
|
267
|
+
def reload_schema_from_cache(*)
|
268
|
+
reset_default_attributes!
|
269
|
+
super
|
270
|
+
end
|
271
|
+
|
271
272
|
private
|
272
273
|
NO_DEFAULT_PROVIDED = Object.new # :nodoc:
|
273
274
|
private_constant :NO_DEFAULT_PROVIDED
|
@@ -287,6 +288,18 @@ module ActiveRecord
|
|
287
288
|
end
|
288
289
|
_default_attributes[name] = default_attribute
|
289
290
|
end
|
291
|
+
|
292
|
+
def reset_default_attributes
|
293
|
+
reload_schema_from_cache
|
294
|
+
end
|
295
|
+
|
296
|
+
def resolve_type_name(name, **options)
|
297
|
+
Type.lookup(name, **options, adapter: Type.adapter_name_from(self))
|
298
|
+
end
|
299
|
+
|
300
|
+
def type_for_column(connection, column)
|
301
|
+
hook_attribute_type(column.name, super)
|
302
|
+
end
|
290
303
|
end
|
291
304
|
end
|
292
305
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_record/associations/nested_error"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
# = Active Record Autosave Association
|
5
7
|
#
|
@@ -315,7 +317,7 @@ module ActiveRecord
|
|
315
317
|
def validate_single_association(reflection)
|
316
318
|
association = association_instance_get(reflection.name)
|
317
319
|
record = association && association.reader
|
318
|
-
association_valid?(
|
320
|
+
association_valid?(association, record) if record && (record.changed_for_autosave? || custom_validation_context?)
|
319
321
|
end
|
320
322
|
|
321
323
|
# Validate the associated records if <tt>:validate</tt> or
|
@@ -324,7 +326,7 @@ module ActiveRecord
|
|
324
326
|
def validate_collection_association(reflection)
|
325
327
|
if association = association_instance_get(reflection.name)
|
326
328
|
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
|
327
|
-
records.
|
329
|
+
records.each { |record| association_valid?(association, record) }
|
328
330
|
end
|
329
331
|
end
|
330
332
|
end
|
@@ -332,40 +334,25 @@ module ActiveRecord
|
|
332
334
|
# Returns whether or not the association is valid and applies any errors to
|
333
335
|
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
|
334
336
|
# enabled records if they're marked_for_destruction? or destroyed.
|
335
|
-
def association_valid?(
|
336
|
-
return true if record.destroyed? || (
|
337
|
+
def association_valid?(association, record)
|
338
|
+
return true if record.destroyed? || (association.options[:autosave] && record.marked_for_destruction?)
|
337
339
|
|
338
340
|
context = validation_context if custom_validation_context?
|
339
341
|
|
340
342
|
unless valid = record.valid?(context)
|
341
|
-
if
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
errors.each { |error|
|
348
|
-
self.errors.import(
|
349
|
-
error,
|
350
|
-
attribute: attribute
|
351
|
-
)
|
352
|
-
}
|
343
|
+
if association.options[:autosave]
|
344
|
+
record.errors.each { |error|
|
345
|
+
self.errors.objects.append(
|
346
|
+
Associations::NestedError.new(association, error)
|
347
|
+
)
|
353
348
|
}
|
354
349
|
else
|
355
|
-
errors.add(reflection.name)
|
350
|
+
errors.add(association.reflection.name)
|
356
351
|
end
|
357
352
|
end
|
358
353
|
valid
|
359
354
|
end
|
360
355
|
|
361
|
-
def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
|
362
|
-
if indexed_attribute
|
363
|
-
"#{reflection.name}[#{index}].#{attribute}"
|
364
|
-
else
|
365
|
-
"#{reflection.name}.#{attribute}"
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
356
|
# Is used as an around_save callback to check while saving a collection
|
370
357
|
# association whether or not the parent was a new record before saving.
|
371
358
|
def around_save_collection_association
|
@@ -441,9 +428,7 @@ module ActiveRecord
|
|
441
428
|
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
442
429
|
def save_has_one_association(reflection)
|
443
430
|
association = association_instance_get(reflection.name)
|
444
|
-
|
445
|
-
|
446
|
-
record = association.load_target
|
431
|
+
record = association && association.load_target
|
447
432
|
|
448
433
|
if record && !record.destroyed?
|
449
434
|
autosave = reflection.options[:autosave]
|
@@ -550,10 +535,6 @@ module ActiveRecord
|
|
550
535
|
end
|
551
536
|
end
|
552
537
|
|
553
|
-
def custom_validation_context?
|
554
|
-
validation_context && [:create, :update].exclude?(validation_context)
|
555
|
-
end
|
556
|
-
|
557
538
|
def _ensure_no_duplicate_errors
|
558
539
|
errors.uniq!
|
559
540
|
end
|
data/lib/active_record/base.rb
CHANGED
@@ -233,7 +233,7 @@ module ActiveRecord # :nodoc:
|
|
233
233
|
#
|
234
234
|
# Connections are usually created through
|
235
235
|
# {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] and retrieved
|
236
|
-
# by ActiveRecord::Base.
|
236
|
+
# by ActiveRecord::Base.lease_connection. All classes inheriting from ActiveRecord::Base will use this
|
237
237
|
# connection. But you can also set a class-specific connection. For example, if Course is an
|
238
238
|
# ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
|
239
239
|
# and Course and all of its subclasses will use this connection instead.
|
@@ -280,7 +280,7 @@ module ActiveRecord # :nodoc:
|
|
280
280
|
# So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
|
281
281
|
# instances in the current object space.
|
282
282
|
class Base
|
283
|
-
|
283
|
+
include ActiveModel::API
|
284
284
|
|
285
285
|
extend ActiveSupport::Benchmarkable
|
286
286
|
extend ActiveSupport::DescendantsTracker
|
@@ -304,7 +304,6 @@ module ActiveRecord # :nodoc:
|
|
304
304
|
include Scoping
|
305
305
|
include Sanitization
|
306
306
|
include AttributeAssignment
|
307
|
-
include ActiveModel::Conversion
|
308
307
|
include Integration
|
309
308
|
include Validations
|
310
309
|
include CounterCache
|