activerecord 7.0.8.7 → 7.1.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 +1339 -1572
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +193 -97
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +128 -138
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +89 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +169 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +52 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -33,26 +33,94 @@ module ActiveRecord
|
|
33
33
|
Base.instance_methods +
|
34
34
|
Base.private_instance_methods -
|
35
35
|
Base.superclass.instance_methods -
|
36
|
-
Base.superclass.private_instance_methods
|
36
|
+
Base.superclass.private_instance_methods +
|
37
|
+
%i[__id__ dup freeze frozen? hash object_id class clone]
|
37
38
|
).map { |m| -m.to_s }.to_set.freeze
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
module ClassMethods
|
42
|
-
def inherited(child_class) # :nodoc:
|
43
|
-
child_class.initialize_generated_modules
|
44
|
-
super
|
45
|
-
end
|
46
|
-
|
47
43
|
def initialize_generated_modules # :nodoc:
|
48
44
|
@generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
|
49
45
|
private_constant :GeneratedAttributeMethods
|
50
46
|
@attribute_methods_generated = false
|
47
|
+
@alias_attributes_mass_generated = false
|
51
48
|
include @generated_attribute_methods
|
52
49
|
|
53
50
|
super
|
54
51
|
end
|
55
52
|
|
53
|
+
def alias_attribute(new_name, old_name)
|
54
|
+
super
|
55
|
+
|
56
|
+
if @alias_attributes_mass_generated
|
57
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
58
|
+
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def eagerly_generate_alias_attribute_methods(_new_name, _old_name) # :nodoc:
|
64
|
+
# alias attributes in Active Record are lazily generated
|
65
|
+
end
|
66
|
+
|
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
|
+
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
|
+
old_name = old_name.to_s
|
90
|
+
|
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
|
+
if !abstract_class? && !has_attribute?(old_name)
|
97
|
+
# We only need to issue this deprecation warning once, so we issue it when defining the original reader method.
|
98
|
+
should_warn = target_name == old_name
|
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
|
117
|
+
else
|
118
|
+
define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
|
119
|
+
namespace: :proxy_alias_attribute
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
56
124
|
# Generates all the attribute related methods for columns in the database
|
57
125
|
# accessors, mutators and query methods.
|
58
126
|
def define_attribute_methods # :nodoc:
|
@@ -71,6 +139,7 @@ module ActiveRecord
|
|
71
139
|
generated_attribute_methods.synchronize do
|
72
140
|
super if defined?(@attribute_methods_generated) && @attribute_methods_generated
|
73
141
|
@attribute_methods_generated = false
|
142
|
+
@alias_attributes_mass_generated = false
|
74
143
|
end
|
75
144
|
end
|
76
145
|
|
@@ -186,6 +255,16 @@ module ActiveRecord
|
|
186
255
|
def _has_attribute?(attr_name) # :nodoc:
|
187
256
|
attribute_types.key?(attr_name)
|
188
257
|
end
|
258
|
+
|
259
|
+
private
|
260
|
+
def inherited(child_class)
|
261
|
+
super
|
262
|
+
child_class.initialize_generated_modules
|
263
|
+
child_class.class_eval do
|
264
|
+
@alias_attributes_mass_generated = false
|
265
|
+
@attribute_names = nil
|
266
|
+
end
|
267
|
+
end
|
189
268
|
end
|
190
269
|
|
191
270
|
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
|
@@ -309,36 +388,40 @@ module ActiveRecord
|
|
309
388
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
310
389
|
end
|
311
390
|
|
312
|
-
# Returns the value of the attribute identified by
|
313
|
-
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
# Note: +:id+ is always present.
|
391
|
+
# Returns the value of the attribute identified by +attr_name+ after it has
|
392
|
+
# been type cast. (For information about specific type casting behavior, see
|
393
|
+
# the types under ActiveModel::Type.)
|
317
394
|
#
|
318
395
|
# class Person < ActiveRecord::Base
|
319
396
|
# belongs_to :organization
|
320
397
|
# end
|
321
398
|
#
|
322
|
-
# person = Person.new(name:
|
323
|
-
# person[:name]
|
324
|
-
# person[:
|
399
|
+
# person = Person.new(name: "Francesco", date_of_birth: "2004-12-12")
|
400
|
+
# person[:name] # => "Francesco"
|
401
|
+
# person[:date_of_birth] # => Date.new(2004, 12, 12)
|
402
|
+
# person[:organization_id] # => nil
|
403
|
+
#
|
404
|
+
# Raises ActiveModel::MissingAttributeError if the attribute is missing.
|
405
|
+
# Note, however, that the +id+ attribute will never be considered missing.
|
325
406
|
#
|
326
|
-
# person = Person.select(
|
327
|
-
# person[:name] # =>
|
328
|
-
# person[:
|
407
|
+
# person = Person.select(:name).first
|
408
|
+
# person[:name] # => "Francesco"
|
409
|
+
# person[:date_of_birth] # => ActiveModel::MissingAttributeError: missing attribute 'date_of_birth' for Person
|
410
|
+
# person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute 'organization_id' for Person
|
411
|
+
# person[:id] # => nil
|
329
412
|
def [](attr_name)
|
330
413
|
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
|
331
414
|
end
|
332
415
|
|
333
|
-
# Updates the attribute identified by
|
416
|
+
# Updates the attribute identified by +attr_name+ using the specified
|
417
|
+
# +value+. The attribute value will be type cast upon being read.
|
334
418
|
#
|
335
419
|
# class Person < ActiveRecord::Base
|
336
420
|
# end
|
337
421
|
#
|
338
422
|
# person = Person.new
|
339
|
-
# person[:
|
340
|
-
# person[:
|
341
|
-
# person[:age].class # => Integer
|
423
|
+
# person[:date_of_birth] = "2004-12-12"
|
424
|
+
# person[:date_of_birth] # => Date.new(2004, 12, 12)
|
342
425
|
def []=(attr_name, value)
|
343
426
|
write_attribute(attr_name, value)
|
344
427
|
end
|
@@ -390,6 +473,7 @@ module ActiveRecord
|
|
390
473
|
attribute_names &= self.class.column_names
|
391
474
|
attribute_names.delete_if do |name|
|
392
475
|
self.class.readonly_attribute?(name) ||
|
476
|
+
self.class.counter_cache_column?(name) ||
|
393
477
|
column_for_attribute(name).virtual?
|
394
478
|
end
|
395
479
|
end
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
included do
|
11
11
|
class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
|
12
12
|
end
|
13
|
-
|
13
|
+
# = Active Record \Attributes
|
14
14
|
module ClassMethods
|
15
15
|
# Defines an attribute with a type on this model. It will override the
|
16
16
|
# type of existing attributes if needed. This allows control over how
|
@@ -194,10 +194,10 @@ module ActiveRecord
|
|
194
194
|
# end
|
195
195
|
#
|
196
196
|
# Product.where(price_in_bitcoins: Money.new(5, "USD"))
|
197
|
-
# #
|
197
|
+
# # SELECT * FROM products WHERE price_in_bitcoins = 0.02230
|
198
198
|
#
|
199
199
|
# Product.where(price_in_bitcoins: Money.new(5, "GBP"))
|
200
|
-
# #
|
200
|
+
# # SELECT * FROM products WHERE price_in_bitcoins = 0.03412
|
201
201
|
#
|
202
202
|
# ==== Dirty Tracking
|
203
203
|
#
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
#
|
27
27
|
# Child records are validated unless <tt>:validate</tt> is +false+.
|
28
28
|
#
|
29
|
-
# == Callbacks
|
29
|
+
# == \Callbacks
|
30
30
|
#
|
31
31
|
# Association with autosave option defines several callbacks on your
|
32
32
|
# model (around_save, before_save, after_create, after_update). Please note that
|
@@ -273,6 +273,11 @@ module ActiveRecord
|
|
273
273
|
end
|
274
274
|
|
275
275
|
private
|
276
|
+
def init_internals
|
277
|
+
super
|
278
|
+
@_already_called = nil
|
279
|
+
end
|
280
|
+
|
276
281
|
# Returns the record for an association collection that should be validated
|
277
282
|
# or saved. If +autosave+ is +false+ only new records will be returned,
|
278
283
|
# unless the parent is/was a new record itself.
|
@@ -444,11 +449,17 @@ module ActiveRecord
|
|
444
449
|
if autosave && record.marked_for_destruction?
|
445
450
|
record.destroy
|
446
451
|
elsif autosave != false
|
447
|
-
|
452
|
+
primary_key = Array(compute_primary_key(reflection, self)).map(&:to_s)
|
453
|
+
primary_key_value = primary_key.map { |key| _read_attribute(key) }
|
448
454
|
|
449
|
-
if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record,
|
455
|
+
if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, primary_key_value)
|
450
456
|
unless reflection.through_reflection
|
451
|
-
|
457
|
+
foreign_key = Array(reflection.foreign_key)
|
458
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
459
|
+
|
460
|
+
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
461
|
+
record[foreign_key] = _read_attribute(primary_key)
|
462
|
+
end
|
452
463
|
association.set_inverse_instance(record)
|
453
464
|
end
|
454
465
|
|
@@ -463,14 +474,26 @@ module ActiveRecord
|
|
463
474
|
# If the record is new or it has changed, returns true.
|
464
475
|
def _record_changed?(reflection, record, key)
|
465
476
|
record.new_record? ||
|
466
|
-
association_foreign_key_changed?(reflection, record, key) ||
|
477
|
+
(association_foreign_key_changed?(reflection, record, key) ||
|
478
|
+
inverse_polymorphic_association_changed?(reflection, record)) ||
|
467
479
|
record.will_save_change_to_attribute?(reflection.foreign_key)
|
468
480
|
end
|
469
481
|
|
470
482
|
def association_foreign_key_changed?(reflection, record, key)
|
471
483
|
return false if reflection.through_reflection?
|
472
484
|
|
473
|
-
|
485
|
+
foreign_key = Array(reflection.foreign_key)
|
486
|
+
return false unless foreign_key.all? { |key| record._has_attribute?(key) }
|
487
|
+
|
488
|
+
foreign_key.map { |key| record._read_attribute(key) } != Array(key)
|
489
|
+
end
|
490
|
+
|
491
|
+
def inverse_polymorphic_association_changed?(reflection, record)
|
492
|
+
return false unless reflection.inverse_of&.polymorphic?
|
493
|
+
|
494
|
+
class_name = record._read_attribute(reflection.inverse_of.foreign_type)
|
495
|
+
|
496
|
+
reflection.active_record != record.class.polymorphic_class_for(class_name)
|
474
497
|
end
|
475
498
|
|
476
499
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
@@ -485,14 +508,21 @@ module ActiveRecord
|
|
485
508
|
autosave = reflection.options[:autosave]
|
486
509
|
|
487
510
|
if autosave && record.marked_for_destruction?
|
488
|
-
|
511
|
+
foreign_key = Array(reflection.foreign_key)
|
512
|
+
foreign_key.each { |key| self[key] = nil }
|
489
513
|
record.destroy
|
490
514
|
elsif autosave != false
|
491
515
|
saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
|
492
516
|
|
493
517
|
if association.updated?
|
494
|
-
|
495
|
-
|
518
|
+
primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
|
519
|
+
foreign_key = Array(reflection.foreign_key)
|
520
|
+
|
521
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
522
|
+
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
523
|
+
association_id = record._read_attribute(primary_key)
|
524
|
+
self[foreign_key] = association_id unless self[foreign_key] == association_id
|
525
|
+
end
|
496
526
|
association.loaded!
|
497
527
|
end
|
498
528
|
|
@@ -501,6 +531,22 @@ module ActiveRecord
|
|
501
531
|
end
|
502
532
|
end
|
503
533
|
|
534
|
+
def compute_primary_key(reflection, record)
|
535
|
+
if primary_key_options = reflection.options[:primary_key]
|
536
|
+
primary_key_options
|
537
|
+
elsif reflection.options[:query_constraints] && (query_constraints = record.class.query_constraints_list)
|
538
|
+
query_constraints
|
539
|
+
elsif record.class.has_query_constraints? && !reflection.options[:foreign_key]
|
540
|
+
record.class.query_constraints_list
|
541
|
+
elsif record.class.composite_primary_key?
|
542
|
+
# If record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
543
|
+
primary_key = record.class.primary_key
|
544
|
+
primary_key.include?("id") ? "id" : primary_key
|
545
|
+
else
|
546
|
+
record.class.primary_key
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
504
550
|
def custom_validation_context?
|
505
551
|
validation_context && [:create, :update].exclude?(validation_context)
|
506
552
|
end
|
data/lib/active_record/base.rb
CHANGED
@@ -311,11 +311,12 @@ module ActiveRecord # :nodoc:
|
|
311
311
|
include Attributes
|
312
312
|
include Locking::Optimistic
|
313
313
|
include Locking::Pessimistic
|
314
|
+
include Encryption::EncryptableRecord
|
314
315
|
include AttributeMethods
|
315
316
|
include Callbacks
|
316
317
|
include Timestamp
|
317
318
|
include Associations
|
318
|
-
include
|
319
|
+
include SecurePassword
|
319
320
|
include AutosaveAssociation
|
320
321
|
include NestedAttributes
|
321
322
|
include Transactions
|
@@ -325,9 +326,13 @@ module ActiveRecord # :nodoc:
|
|
325
326
|
include Serialization
|
326
327
|
include Store
|
327
328
|
include SecureToken
|
329
|
+
include TokenFor
|
328
330
|
include SignedId
|
329
331
|
include Suppressor
|
330
|
-
include
|
332
|
+
include Normalization
|
333
|
+
include Marshalling::Methods
|
334
|
+
|
335
|
+
self.param_delimiter = "_"
|
331
336
|
end
|
332
337
|
|
333
338
|
ActiveSupport.run_load_hooks(:active_record, Base)
|
@@ -84,7 +84,7 @@ module ActiveRecord
|
|
84
84
|
# == Types of callbacks
|
85
85
|
#
|
86
86
|
# There are three types of callbacks accepted by the callback macros: method references (symbol), callback objects,
|
87
|
-
# inline methods (using a proc). Method references and callback objects are the recommended approaches,
|
87
|
+
# inline methods (using a proc). \Method references and callback objects are the recommended approaches,
|
88
88
|
# inline methods using a proc are sometimes appropriate (such as for creating mix-ins).
|
89
89
|
#
|
90
90
|
# The method reference callbacks work by specifying a protected or private method available in the object, like this:
|
@@ -173,7 +173,7 @@ module ActiveRecord
|
|
173
173
|
#
|
174
174
|
# If a <tt>before_*</tt> callback throws +:abort+, all the later callbacks and
|
175
175
|
# the associated action are cancelled.
|
176
|
-
# Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
|
176
|
+
# \Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
|
177
177
|
# methods on the model, which are called last.
|
178
178
|
#
|
179
179
|
# == Ordering callbacks
|
@@ -234,30 +234,16 @@ module ActiveRecord
|
|
234
234
|
# end
|
235
235
|
#
|
236
236
|
# In this case the +log_children+ is executed before +do_something_else+.
|
237
|
-
#
|
237
|
+
# This applies to all non-transactional callbacks, and to +before_commit+.
|
238
238
|
#
|
239
|
-
#
|
240
|
-
#
|
239
|
+
# For transactional +after_+ callbacks (+after_commit+, +after_rollback+, etc), the order
|
240
|
+
# can be set via configuration.
|
241
241
|
#
|
242
|
-
#
|
243
|
-
#
|
244
|
-
# class Topic < ActiveRecord::Base
|
245
|
-
# has_many :children
|
246
|
-
#
|
247
|
-
# after_commit :log_children
|
248
|
-
# after_commit :do_something_else
|
249
|
-
#
|
250
|
-
# private
|
251
|
-
# def log_children
|
252
|
-
# # Child processing
|
253
|
-
# end
|
254
|
-
#
|
255
|
-
# def do_something_else
|
256
|
-
# # Something else
|
257
|
-
# end
|
258
|
-
# end
|
242
|
+
# config.active_record.run_after_transaction_callbacks_in_order_defined = false
|
259
243
|
#
|
260
|
-
#
|
244
|
+
# If +true+ (the default from \Rails 7.1), callbacks are executed in the order they
|
245
|
+
# are defined, just like the example above. If +false+, the order is reversed, so
|
246
|
+
# +do_something_else+ is executed before +log_children+.
|
261
247
|
#
|
262
248
|
# == \Transactions
|
263
249
|
#
|
@@ -460,7 +446,7 @@ module ActiveRecord
|
|
460
446
|
end
|
461
447
|
|
462
448
|
def _update_record
|
463
|
-
_run_update_callbacks { super }
|
449
|
+
_run_update_callbacks { record_update_timestamps { super } }
|
464
450
|
end
|
465
451
|
end
|
466
452
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Coders # :nodoc:
|
5
|
+
class ColumnSerializer # :nodoc:
|
6
|
+
attr_reader :object_class
|
7
|
+
attr_reader :coder
|
8
|
+
|
9
|
+
def initialize(attr_name, coder, object_class = Object)
|
10
|
+
@attr_name = attr_name
|
11
|
+
@object_class = object_class
|
12
|
+
@coder = coder
|
13
|
+
check_arity_of_constructor
|
14
|
+
end
|
15
|
+
|
16
|
+
def init_with(coder) # :nodoc:
|
17
|
+
@attr_name = coder["attr_name"]
|
18
|
+
@object_class = coder["object_class"]
|
19
|
+
@coder = coder["coder"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def dump(object)
|
23
|
+
return if object.nil?
|
24
|
+
|
25
|
+
assert_valid_value(object, action: "dump")
|
26
|
+
coder.dump(object)
|
27
|
+
end
|
28
|
+
|
29
|
+
def load(payload)
|
30
|
+
if payload.nil?
|
31
|
+
if @object_class != ::Object
|
32
|
+
return @object_class.new
|
33
|
+
end
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
object = coder.load(payload)
|
38
|
+
|
39
|
+
assert_valid_value(object, action: "load")
|
40
|
+
object ||= object_class.new if object_class != Object
|
41
|
+
|
42
|
+
object
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public because it's called by Type::Serialized
|
46
|
+
def assert_valid_value(object, action:)
|
47
|
+
unless object.nil? || object_class === object
|
48
|
+
raise SerializationTypeMismatch,
|
49
|
+
"can't #{action} `#{@attr_name}`: was supposed to be a #{object_class}, but was a #{object.class}. -- #{object.inspect}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def check_arity_of_constructor
|
55
|
+
load(nil)
|
56
|
+
rescue ArgumentError
|
57
|
+
raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -4,37 +4,83 @@ require "yaml"
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Coders # :nodoc:
|
7
|
-
class YAMLColumn # :nodoc:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
check_arity_of_constructor
|
14
|
-
end
|
7
|
+
class YAMLColumn < ColumnSerializer # :nodoc:
|
8
|
+
class SafeCoder
|
9
|
+
def initialize(permitted_classes: [], unsafe_load: nil)
|
10
|
+
@permitted_classes = permitted_classes
|
11
|
+
@unsafe_load = unsafe_load
|
12
|
+
end
|
15
13
|
|
16
|
-
|
17
|
-
|
14
|
+
if Gem::Version.new(Psych::VERSION) >= Gem::Version.new("5.1")
|
15
|
+
def dump(object)
|
16
|
+
if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
|
17
|
+
::YAML.dump(object)
|
18
|
+
else
|
19
|
+
::YAML.safe_dump(
|
20
|
+
object,
|
21
|
+
permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
|
22
|
+
aliases: true,
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
else
|
27
|
+
def dump(object)
|
28
|
+
YAML.dump(object)
|
29
|
+
end
|
30
|
+
end
|
18
31
|
|
19
|
-
|
20
|
-
|
32
|
+
if YAML.respond_to?(:unsafe_load)
|
33
|
+
def load(payload)
|
34
|
+
if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
|
35
|
+
YAML.unsafe_load(payload)
|
36
|
+
else
|
37
|
+
YAML.safe_load(
|
38
|
+
payload,
|
39
|
+
permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
|
40
|
+
aliases: true,
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
def load(payload)
|
46
|
+
if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
|
47
|
+
YAML.load(payload)
|
48
|
+
else
|
49
|
+
YAML.safe_load(
|
50
|
+
payload,
|
51
|
+
permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
|
52
|
+
aliases: true,
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
21
57
|
end
|
22
58
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
59
|
+
def initialize(attr_name, object_class = Object, permitted_classes: [], unsafe_load: nil)
|
60
|
+
super(
|
61
|
+
attr_name,
|
62
|
+
SafeCoder.new(permitted_classes: permitted_classes || [], unsafe_load: unsafe_load),
|
63
|
+
object_class,
|
64
|
+
)
|
65
|
+
check_arity_of_constructor
|
66
|
+
end
|
30
67
|
|
31
|
-
|
68
|
+
def init_with(coder) # :nodoc:
|
69
|
+
unless coder["coder"]
|
70
|
+
permitted_classes = coder["permitted_classes"] || []
|
71
|
+
unsafe_load = coder["unsafe_load"] || false
|
72
|
+
coder["coder"] = SafeCoder.new(permitted_classes: permitted_classes, unsafe_load: unsafe_load)
|
73
|
+
end
|
74
|
+
super(coder)
|
32
75
|
end
|
33
76
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
77
|
+
def coder
|
78
|
+
# This is to retain forward compatibility when loading records serialized with Marshal
|
79
|
+
# from a previous version of Rails.
|
80
|
+
@coder ||= begin
|
81
|
+
permitted_classes = defined?(@permitted_classes) ? @permitted_classes : []
|
82
|
+
unsafe_load = defined?(@unsafe_load) && @unsafe_load.nil?
|
83
|
+
SafeCoder.new(permitted_classes: permitted_classes, unsafe_load: unsafe_load)
|
38
84
|
end
|
39
85
|
end
|
40
86
|
|
@@ -44,24 +90,6 @@ module ActiveRecord
|
|
44
90
|
rescue ArgumentError
|
45
91
|
raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
|
46
92
|
end
|
47
|
-
|
48
|
-
if YAML.respond_to?(:unsafe_load)
|
49
|
-
def yaml_load(payload)
|
50
|
-
if ActiveRecord.use_yaml_unsafe_load
|
51
|
-
YAML.unsafe_load(payload)
|
52
|
-
else
|
53
|
-
YAML.safe_load(payload, permitted_classes: ActiveRecord.yaml_column_permitted_classes, aliases: true)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
else
|
57
|
-
def yaml_load(payload)
|
58
|
-
if ActiveRecord.use_yaml_unsafe_load
|
59
|
-
YAML.load(payload)
|
60
|
-
else
|
61
|
-
YAML.safe_load(payload, permitted_classes: ActiveRecord.yaml_column_permitted_classes, aliases: true)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
93
|
end
|
66
94
|
end
|
67
95
|
end
|