activerecord 7.1.3.3 → 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 +507 -2128
- 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 +18 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +4 -2
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +5 -7
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +34 -11
- 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 +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +87 -58
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +14 -30
- 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 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +59 -38
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- 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/scheme.rb +8 -4
- data/lib/active_record/enum.rb +11 -2
- 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 +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +34 -69
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +52 -64
- 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 +98 -37
- 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/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -43
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +500 -66
- data/lib/active_record/result.rb +32 -45
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/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 +70 -42
- 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 +82 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +4 -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 +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 +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- metadata +16 -11
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "mutex_m"
|
4
3
|
require "active_support/core_ext/enumerable"
|
5
4
|
|
6
5
|
module ActiveRecord
|
@@ -21,10 +20,10 @@ module ActiveRecord
|
|
21
20
|
include Serialization
|
22
21
|
end
|
23
22
|
|
24
|
-
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name
|
23
|
+
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name superclass)
|
25
24
|
|
26
25
|
class GeneratedAttributeMethods < Module # :nodoc:
|
27
|
-
|
26
|
+
LOCK = Monitor.new
|
28
27
|
end
|
29
28
|
|
30
29
|
class << self
|
@@ -50,6 +49,20 @@ module ActiveRecord
|
|
50
49
|
super
|
51
50
|
end
|
52
51
|
|
52
|
+
# Allows you to make aliases for attributes.
|
53
|
+
#
|
54
|
+
# class Person < ActiveRecord::Base
|
55
|
+
# alias_attribute :nickname, :name
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# person = Person.create(name: 'Bob')
|
59
|
+
# person.name # => "Bob"
|
60
|
+
# person.nickname # => "Bob"
|
61
|
+
#
|
62
|
+
# The alias can also be used for querying:
|
63
|
+
#
|
64
|
+
# Person.where(nickname: "Bob")
|
65
|
+
# # SELECT "people".* FROM "people" WHERE "people"."name" = "Bob"
|
53
66
|
def alias_attribute(new_name, old_name)
|
54
67
|
super
|
55
68
|
|
@@ -64,80 +77,69 @@ 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 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
|
-
end
|
84
|
-
|
85
80
|
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
86
|
-
method_name = pattern.method_name(new_name).to_s
|
87
|
-
target_name = pattern.method_name(old_name).to_s
|
88
|
-
parameters = pattern.parameters
|
89
81
|
old_name = old_name.to_s
|
90
82
|
|
91
|
-
method_defined = method_defined?(target_name) || private_method_defined?(target_name)
|
92
|
-
manually_defined = method_defined &&
|
93
|
-
!self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
|
94
|
-
reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)
|
95
|
-
|
96
83
|
if !abstract_class? && !has_attribute?(old_name)
|
97
|
-
|
98
|
-
|
99
|
-
if should_warn
|
100
|
-
ActiveRecord.deprecator.warn(
|
101
|
-
"#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
102
|
-
"Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
|
103
|
-
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
104
|
-
)
|
105
|
-
end
|
106
|
-
super
|
107
|
-
elsif manually_defined && !reserved_method_name
|
108
|
-
aliased_method_redefined_as_well = method_defined_within?(method_name, self)
|
109
|
-
return if aliased_method_redefined_as_well
|
110
|
-
|
111
|
-
ActiveRecord.deprecator.warn(
|
112
|
-
"#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
|
113
|
-
"Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
|
114
|
-
"You may want to additionally define `#{method_name}` to preserve the current behavior."
|
115
|
-
)
|
116
|
-
super
|
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."
|
117
86
|
else
|
87
|
+
method_name = pattern.method_name(new_name).to_s
|
88
|
+
parameters = pattern.parameters
|
89
|
+
|
118
90
|
define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
|
119
91
|
namespace: :proxy_alias_attribute
|
120
92
|
)
|
121
93
|
end
|
122
94
|
end
|
123
95
|
|
96
|
+
def attribute_methods_generated? # :nodoc:
|
97
|
+
@attribute_methods_generated
|
98
|
+
end
|
99
|
+
|
124
100
|
# Generates all the attribute related methods for columns in the database
|
125
101
|
# accessors, mutators and query methods.
|
126
102
|
def define_attribute_methods # :nodoc:
|
127
103
|
return false if @attribute_methods_generated
|
128
104
|
# Use a mutex; we don't want two threads simultaneously trying to define
|
129
105
|
# attribute methods.
|
130
|
-
|
106
|
+
GeneratedAttributeMethods::LOCK.synchronize do
|
131
107
|
return false if @attribute_methods_generated
|
108
|
+
|
132
109
|
superclass.define_attribute_methods unless base_class?
|
133
|
-
|
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
|
+
|
134
117
|
@attribute_methods_generated = true
|
118
|
+
|
119
|
+
generate_alias_attributes
|
120
|
+
end
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
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
135
|
end
|
136
|
+
|
137
|
+
@alias_attributes_mass_generated = true
|
136
138
|
end
|
137
139
|
|
138
140
|
def undefine_attribute_methods # :nodoc:
|
139
|
-
|
140
|
-
super if
|
141
|
+
GeneratedAttributeMethods::LOCK.synchronize do
|
142
|
+
super if @attribute_methods_generated
|
141
143
|
@attribute_methods_generated = false
|
142
144
|
@alias_attributes_mass_generated = false
|
143
145
|
end
|
@@ -288,9 +290,7 @@ module ActiveRecord
|
|
288
290
|
|
289
291
|
# If the result is true then check for the select case.
|
290
292
|
# For queries selecting a subset of columns, return false for unselected columns.
|
291
|
-
|
292
|
-
# have been allocated but not yet initialized.
|
293
|
-
if defined?(@attributes)
|
293
|
+
if @attributes
|
294
294
|
if name = self.class.symbol_column_to_string(name.to_sym)
|
295
295
|
return _has_attribute?(name)
|
296
296
|
end
|
@@ -459,9 +459,38 @@ module ActiveRecord
|
|
459
459
|
end
|
460
460
|
|
461
461
|
private
|
462
|
+
def respond_to_missing?(name, include_private = false)
|
463
|
+
if self.class.define_attribute_methods
|
464
|
+
# Some methods weren't defined yet.
|
465
|
+
return true if self.class.method_defined?(name)
|
466
|
+
return true if include_private && self.class.private_method_defined?(name)
|
467
|
+
end
|
468
|
+
|
469
|
+
super
|
470
|
+
end
|
471
|
+
|
472
|
+
def method_missing(name, ...)
|
473
|
+
unless self.class.attribute_methods_generated?
|
474
|
+
if self.class.method_defined?(name)
|
475
|
+
# The method is explicitly defined in the model, but calls a generated
|
476
|
+
# method with super. So we must resume the call chain at the right step.
|
477
|
+
last_method = method(name)
|
478
|
+
last_method = last_method.super_method while last_method.super_method
|
479
|
+
self.class.define_attribute_methods
|
480
|
+
if last_method.super_method
|
481
|
+
return last_method.super_method.call(...)
|
482
|
+
end
|
483
|
+
elsif self.class.define_attribute_methods
|
484
|
+
# Some attribute methods weren't generated yet, we retry the call
|
485
|
+
return public_send(name, ...)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
super
|
490
|
+
end
|
491
|
+
|
462
492
|
def attribute_method?(attr_name)
|
463
|
-
|
464
|
-
defined?(@attributes) && @attributes.key?(attr_name)
|
493
|
+
@attributes&.key?(attr_name)
|
465
494
|
end
|
466
495
|
|
467
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
|
@@ -458,7 +445,8 @@ module ActiveRecord
|
|
458
445
|
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
459
446
|
|
460
447
|
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
461
|
-
|
448
|
+
association_id = _read_attribute(primary_key)
|
449
|
+
record[foreign_key] = association_id unless record[foreign_key] == association_id
|
462
450
|
end
|
463
451
|
association.set_inverse_instance(record)
|
464
452
|
end
|
@@ -547,10 +535,6 @@ module ActiveRecord
|
|
547
535
|
end
|
548
536
|
end
|
549
537
|
|
550
|
-
def custom_validation_context?
|
551
|
-
validation_context && [:create, :update].exclude?(validation_context)
|
552
|
-
end
|
553
|
-
|
554
538
|
def _ensure_no_duplicate_errors
|
555
539
|
errors.uniq!
|
556
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
|