activerecord 6.0.6 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +783 -910
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +43 -26
- data/lib/active_record/associations/association_scope.rb +11 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -13
- data/lib/active_record/associations/collection_proxy.rb +12 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +13 -5
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +114 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +32 -7
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +2 -24
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
- data/lib/active_record/connection_adapters/abstract_adapter.rb +49 -72
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +223 -66
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +27 -10
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +54 -8
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +34 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +13 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +67 -17
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/model_schema.rb +88 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/databases.rake +266 -95
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +100 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- data/lib/active_record/relation/query_methods.rb +318 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/relation.rb +90 -64
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/named.rb +1 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -51
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +36 -33
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +28 -30
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
# Implements the details of eager loading of Active Record associations.
|
@@ -58,7 +60,7 @@ module ActiveRecord
|
|
58
60
|
# == Parameters
|
59
61
|
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
|
60
62
|
# i.e. +records+ itself may also contain arrays of records. In any case,
|
61
|
-
# +preload_associations+ will preload
|
63
|
+
# +preload_associations+ will preload all associations records by
|
62
64
|
# flattening +records+.
|
63
65
|
#
|
64
66
|
# +associations+ specifies one or more associations that you want to
|
@@ -175,8 +177,8 @@ module ActiveRecord
|
|
175
177
|
end
|
176
178
|
|
177
179
|
def records_by_owner
|
178
|
-
@records_by_owner ||= owners.
|
179
|
-
|
180
|
+
@records_by_owner ||= owners.index_with do |owner|
|
181
|
+
Array(owner.association(reflection.name).target)
|
180
182
|
end
|
181
183
|
end
|
182
184
|
|
@@ -2,38 +2,116 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/enumerable"
|
4
4
|
require "active_support/core_ext/string/conversions"
|
5
|
-
require "active_support/core_ext/module/remove_method"
|
6
|
-
require "active_record/errors"
|
7
5
|
|
8
6
|
module ActiveRecord
|
9
7
|
class AssociationNotFoundError < ConfigurationError #:nodoc:
|
8
|
+
attr_reader :record, :association_name
|
10
9
|
def initialize(record = nil, association_name = nil)
|
10
|
+
@record = record
|
11
|
+
@association_name = association_name
|
11
12
|
if record && association_name
|
12
13
|
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
|
13
14
|
else
|
14
15
|
super("Association was not found.")
|
15
16
|
end
|
16
17
|
end
|
18
|
+
|
19
|
+
class Correction
|
20
|
+
def initialize(error)
|
21
|
+
@error = error
|
22
|
+
end
|
23
|
+
|
24
|
+
def corrections
|
25
|
+
if @error.association_name
|
26
|
+
maybe_these = @error.record.class.reflections.keys
|
27
|
+
|
28
|
+
maybe_these.sort_by { |n|
|
29
|
+
DidYouMean::Jaro.distance(@error.association_name.to_s, n)
|
30
|
+
}.reverse.first(4)
|
31
|
+
else
|
32
|
+
[]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
38
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
39
|
+
DidYouMean.correct_error(self, Correction)
|
40
|
+
end
|
17
41
|
end
|
18
42
|
|
19
43
|
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
|
44
|
+
attr_reader :reflection, :associated_class
|
20
45
|
def initialize(reflection = nil, associated_class = nil)
|
21
46
|
if reflection
|
47
|
+
@reflection = reflection
|
48
|
+
@associated_class = associated_class.nil? ? reflection.klass : associated_class
|
22
49
|
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
|
23
50
|
else
|
24
51
|
super("Could not find the inverse association.")
|
25
52
|
end
|
26
53
|
end
|
54
|
+
|
55
|
+
class Correction
|
56
|
+
def initialize(error)
|
57
|
+
@error = error
|
58
|
+
end
|
59
|
+
|
60
|
+
def corrections
|
61
|
+
if @error.reflection && @error.associated_class
|
62
|
+
maybe_these = @error.associated_class.reflections.keys
|
63
|
+
|
64
|
+
maybe_these.sort_by { |n|
|
65
|
+
DidYouMean::Jaro.distance(@error.reflection.options[:inverse_of].to_s, n)
|
66
|
+
}.reverse.first(4)
|
67
|
+
else
|
68
|
+
[]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
74
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
75
|
+
DidYouMean.correct_error(self, Correction)
|
76
|
+
end
|
27
77
|
end
|
28
78
|
|
29
79
|
class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
|
30
|
-
|
31
|
-
|
32
|
-
|
80
|
+
attr_reader :owner_class, :reflection
|
81
|
+
|
82
|
+
def initialize(owner_class = nil, reflection = nil)
|
83
|
+
if owner_class && reflection
|
84
|
+
@owner_class = owner_class
|
85
|
+
@reflection = reflection
|
86
|
+
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
|
33
87
|
else
|
34
88
|
super("Could not find the association.")
|
35
89
|
end
|
36
90
|
end
|
91
|
+
|
92
|
+
class Correction
|
93
|
+
def initialize(error)
|
94
|
+
@error = error
|
95
|
+
end
|
96
|
+
|
97
|
+
def corrections
|
98
|
+
if @error.reflection && @error.owner_class
|
99
|
+
maybe_these = @error.owner_class.reflections.keys
|
100
|
+
maybe_these -= [@error.reflection.name.to_s] # remove failing reflection
|
101
|
+
|
102
|
+
maybe_these.sort_by { |n|
|
103
|
+
DidYouMean::Jaro.distance(@error.reflection.options[:through].to_s, n)
|
104
|
+
}.reverse.first(4)
|
105
|
+
else
|
106
|
+
[]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
112
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
113
|
+
DidYouMean.correct_error(self, Correction)
|
114
|
+
end
|
37
115
|
end
|
38
116
|
|
39
117
|
class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
|
@@ -702,9 +780,8 @@ module ActiveRecord
|
|
702
780
|
# inverse detection only works on #has_many, #has_one, and
|
703
781
|
# #belongs_to associations.
|
704
782
|
#
|
705
|
-
#
|
706
|
-
#
|
707
|
-
# constant, or a custom scope, will also prevent the association's inverse
|
783
|
+
# <tt>:foreign_key</tt> and <tt>:through</tt> options on the associations,
|
784
|
+
# or a custom scope, will also prevent the association's inverse
|
708
785
|
# from being found automatically.
|
709
786
|
#
|
710
787
|
# The automatic guessing of the inverse association uses a heuristic based
|
@@ -1292,7 +1369,9 @@ module ActiveRecord
|
|
1292
1369
|
# similar callbacks may affect the <tt>:dependent</tt> behavior, and the
|
1293
1370
|
# <tt>:dependent</tt> behavior may affect other callbacks.
|
1294
1371
|
#
|
1372
|
+
# * <tt>nil</tt> do nothing (default).
|
1295
1373
|
# * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
|
1374
|
+
# * <tt>:destroy_async</tt> destroys all the associated objects in a background job.
|
1296
1375
|
# * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
|
1297
1376
|
# * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified
|
1298
1377
|
# on polymorphic associations. Callbacks are not executed.
|
@@ -1357,6 +1436,11 @@ module ActiveRecord
|
|
1357
1436
|
# Specifies a module or array of modules that will be extended into the association object returned.
|
1358
1437
|
# Useful for defining methods on associations, especially when they should be shared between multiple
|
1359
1438
|
# association objects.
|
1439
|
+
# [:strict_loading]
|
1440
|
+
# Enforces strict loading every time the associated record is loaded through this association.
|
1441
|
+
# [:ensuring_owner_was]
|
1442
|
+
# Specifies an instance method to be called on the owner. The method must return true in order for the
|
1443
|
+
# associated records to be deleted in a background job.
|
1360
1444
|
#
|
1361
1445
|
# Option examples:
|
1362
1446
|
# has_many :comments, -> { order("posted_on") }
|
@@ -1367,6 +1451,7 @@ module ActiveRecord
|
|
1367
1451
|
# has_many :tags, as: :taggable
|
1368
1452
|
# has_many :reports, -> { readonly }
|
1369
1453
|
# has_many :subscribers, through: :subscriptions, source: :user
|
1454
|
+
# has_many :comments, strict_loading: true
|
1370
1455
|
def has_many(name, scope = nil, **options, &extension)
|
1371
1456
|
reflection = Builder::HasMany.build(self, name, scope, options, &extension)
|
1372
1457
|
Reflection.add_reflection self, name, reflection
|
@@ -1436,7 +1521,9 @@ module ActiveRecord
|
|
1436
1521
|
# Controls what happens to the associated object when
|
1437
1522
|
# its owner is destroyed:
|
1438
1523
|
#
|
1524
|
+
# * <tt>nil</tt> do nothing (default).
|
1439
1525
|
# * <tt>:destroy</tt> causes the associated object to also be destroyed
|
1526
|
+
# * <tt>:destroy_async</tt> causes all the associated object to be destroyed in a background job.
|
1440
1527
|
# * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
|
1441
1528
|
# * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified
|
1442
1529
|
# on polymorphic associations. Callbacks are not executed.
|
@@ -1495,6 +1582,11 @@ module ActiveRecord
|
|
1495
1582
|
# When set to +true+, the association will also have its presence validated.
|
1496
1583
|
# This will validate the association itself, not the id. You can use
|
1497
1584
|
# +:inverse_of+ to avoid an extra query during validation.
|
1585
|
+
# [:strict_loading]
|
1586
|
+
# Enforces strict loading every time the associated record is loaded through this association.
|
1587
|
+
# [:ensuring_owner_was]
|
1588
|
+
# Specifies an instance method to be called on the owner. The method must return true in order for the
|
1589
|
+
# associated records to be deleted in a background job.
|
1498
1590
|
#
|
1499
1591
|
# Option examples:
|
1500
1592
|
# has_one :credit_card, dependent: :destroy # destroys the associated credit card
|
@@ -1507,6 +1599,7 @@ module ActiveRecord
|
|
1507
1599
|
# has_one :club, through: :membership
|
1508
1600
|
# has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
|
1509
1601
|
# has_one :credit_card, required: true
|
1602
|
+
# has_one :credit_card, strict_loading: true
|
1510
1603
|
def has_one(name, scope = nil, **options)
|
1511
1604
|
reflection = Builder::HasOne.build(self, name, scope, options)
|
1512
1605
|
Reflection.add_reflection self, name, reflection
|
@@ -1588,7 +1681,8 @@ module ActiveRecord
|
|
1588
1681
|
# By default this is +id+.
|
1589
1682
|
# [:dependent]
|
1590
1683
|
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
|
1591
|
-
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
|
1684
|
+
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to
|
1685
|
+
# <tt>:destroy_async</tt>, the associated object is scheduled to be destroyed in a background job.
|
1592
1686
|
# This option should not be specified when #belongs_to is used in conjunction with
|
1593
1687
|
# a #has_many relationship on another class because of the potential to leave
|
1594
1688
|
# orphaned records behind.
|
@@ -1640,6 +1734,11 @@ module ActiveRecord
|
|
1640
1734
|
# [:default]
|
1641
1735
|
# Provide a callable (i.e. proc or lambda) to specify that the association should
|
1642
1736
|
# be initialized with a particular record before validation.
|
1737
|
+
# [:strict_loading]
|
1738
|
+
# Enforces strict loading every time the associated record is loaded through this association.
|
1739
|
+
# [:ensuring_owner_was]
|
1740
|
+
# Specifies an instance method to be called on the owner. The method must return true in order for the
|
1741
|
+
# associated records to be deleted in a background job.
|
1643
1742
|
#
|
1644
1743
|
# Option examples:
|
1645
1744
|
# belongs_to :firm, foreign_key: "client_of"
|
@@ -1654,6 +1753,7 @@ module ActiveRecord
|
|
1654
1753
|
# belongs_to :company, touch: :employees_last_updated_at
|
1655
1754
|
# belongs_to :user, optional: true
|
1656
1755
|
# belongs_to :account, default: -> { company.account }
|
1756
|
+
# belongs_to :account, strict_loading: true
|
1657
1757
|
def belongs_to(name, scope = nil, **options)
|
1658
1758
|
reflection = Builder::BelongsTo.build(self, name, scope, options)
|
1659
1759
|
Reflection.add_reflection self, name, reflection
|
@@ -1676,7 +1776,7 @@ module ActiveRecord
|
|
1676
1776
|
# The join table should not have a primary key or a model associated with it. You must manually generate the
|
1677
1777
|
# join table with a migration such as this:
|
1678
1778
|
#
|
1679
|
-
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[
|
1779
|
+
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[6.0]
|
1680
1780
|
# def change
|
1681
1781
|
# create_join_table :developers, :projects
|
1682
1782
|
# end
|
@@ -1816,6 +1916,8 @@ module ActiveRecord
|
|
1816
1916
|
#
|
1817
1917
|
# Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
|
1818
1918
|
# <tt>:autosave</tt> to <tt>true</tt>.
|
1919
|
+
# [:strict_loading]
|
1920
|
+
# Enforces strict loading every time an associated record is loaded through this association.
|
1819
1921
|
#
|
1820
1922
|
# Option examples:
|
1821
1923
|
# has_and_belongs_to_many :projects
|
@@ -1823,6 +1925,7 @@ module ActiveRecord
|
|
1823
1925
|
# has_and_belongs_to_many :nations, class_name: "Country"
|
1824
1926
|
# has_and_belongs_to_many :categories, join_table: "prods_cats"
|
1825
1927
|
# has_and_belongs_to_many :categories, -> { readonly }
|
1928
|
+
# has_and_belongs_to_many :categories, strict_loading: true
|
1826
1929
|
def has_and_belongs_to_many(name, scope = nil, **options, &extension)
|
1827
1930
|
habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
|
1828
1931
|
|
@@ -1853,7 +1956,7 @@ module ActiveRecord
|
|
1853
1956
|
hm_options[:through] = middle_reflection.name
|
1854
1957
|
hm_options[:source] = join_model.right_reflection.name
|
1855
1958
|
|
1856
|
-
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
|
1959
|
+
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend, :strict_loading].each do |k|
|
1857
1960
|
hm_options[k] = options[k] if options.key? k
|
1858
1961
|
end
|
1859
1962
|
|
@@ -8,20 +8,22 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
private
|
10
10
|
def _assign_attributes(attributes)
|
11
|
-
multi_parameter_attributes
|
12
|
-
nested_parameter_attributes = {}
|
11
|
+
multi_parameter_attributes = nested_parameter_attributes = nil
|
13
12
|
|
14
13
|
attributes.each do |k, v|
|
15
|
-
|
16
|
-
|
14
|
+
key = k.to_s
|
15
|
+
|
16
|
+
if key.include?("(")
|
17
|
+
(multi_parameter_attributes ||= {})[key] = v
|
17
18
|
elsif v.is_a?(Hash)
|
18
|
-
nested_parameter_attributes[
|
19
|
+
(nested_parameter_attributes ||= {})[key] = v
|
20
|
+
else
|
21
|
+
_assign_attribute(key, v)
|
19
22
|
end
|
20
23
|
end
|
21
|
-
super(attributes)
|
22
24
|
|
23
|
-
assign_nested_parameter_attributes(nested_parameter_attributes)
|
24
|
-
assign_multiparameter_attributes(multi_parameter_attributes)
|
25
|
+
assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
|
26
|
+
assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
|
25
27
|
end
|
26
28
|
|
27
29
|
# Assign any deferred nested attributes after the base attributes have been set.
|
@@ -29,7 +29,7 @@ module ActiveRecord
|
|
29
29
|
extend ActiveSupport::Concern
|
30
30
|
|
31
31
|
included do
|
32
|
-
attribute_method_suffix "_before_type_cast"
|
32
|
+
attribute_method_suffix "_before_type_cast", "_for_database"
|
33
33
|
attribute_method_suffix "_came_from_user?"
|
34
34
|
end
|
35
35
|
|
@@ -46,8 +46,10 @@ module ActiveRecord
|
|
46
46
|
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
47
47
|
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
48
48
|
def read_attribute_before_type_cast(attr_name)
|
49
|
-
|
50
|
-
|
49
|
+
name = attr_name.to_s
|
50
|
+
name = self.class.attribute_aliases[name] || name
|
51
|
+
|
52
|
+
attribute_before_type_cast(name)
|
51
53
|
end
|
52
54
|
|
53
55
|
# Returns a hash of attributes before typecasting and deserialization.
|
@@ -61,19 +63,21 @@ module ActiveRecord
|
|
61
63
|
# task.attributes_before_type_cast
|
62
64
|
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
63
65
|
def attributes_before_type_cast
|
64
|
-
sync_with_transaction_state if @transaction_state&.finalized?
|
65
66
|
@attributes.values_before_type_cast
|
66
67
|
end
|
67
68
|
|
68
69
|
private
|
69
70
|
# Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
|
70
|
-
def attribute_before_type_cast(
|
71
|
-
|
71
|
+
def attribute_before_type_cast(attr_name)
|
72
|
+
@attributes[attr_name].value_before_type_cast
|
73
|
+
end
|
74
|
+
|
75
|
+
def attribute_for_database(attr_name)
|
76
|
+
@attributes[attr_name].value_for_database
|
72
77
|
end
|
73
78
|
|
74
|
-
def attribute_came_from_user?(
|
75
|
-
|
76
|
-
@attributes[attribute_name].came_from_user?
|
79
|
+
def attribute_came_from_user?(attr_name)
|
80
|
+
@attributes[attr_name].came_from_user?
|
77
81
|
end
|
78
82
|
end
|
79
83
|
end
|
@@ -89,7 +89,7 @@ module ActiveRecord
|
|
89
89
|
# This method is useful in validations and before callbacks to determine
|
90
90
|
# if the next call to +save+ will change a particular attribute. It can be
|
91
91
|
# invoked as +will_save_change_to_name?+ instead of
|
92
|
-
# <tt>will_save_change_to_attribute("name")</tt>.
|
92
|
+
# <tt>will_save_change_to_attribute?("name")</tt>.
|
93
93
|
#
|
94
94
|
# ==== Options
|
95
95
|
#
|
@@ -156,16 +156,6 @@ module ActiveRecord
|
|
156
156
|
end
|
157
157
|
|
158
158
|
private
|
159
|
-
def mutations_from_database
|
160
|
-
sync_with_transaction_state if @transaction_state&.finalized?
|
161
|
-
super
|
162
|
-
end
|
163
|
-
|
164
|
-
def mutations_before_last_save
|
165
|
-
sync_with_transaction_state if @transaction_state&.finalized?
|
166
|
-
super
|
167
|
-
end
|
168
|
-
|
169
159
|
def write_attribute_without_type_cast(attr_name, value)
|
170
160
|
result = super
|
171
161
|
clear_attribute_change(attr_name)
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
|
32
32
|
# Returns the primary key column's value before type cast.
|
33
33
|
def id_before_type_cast
|
34
|
-
|
34
|
+
attribute_before_type_cast(@primary_key)
|
35
35
|
end
|
36
36
|
|
37
37
|
# Returns the primary key column's previous value.
|
@@ -44,13 +44,17 @@ module ActiveRecord
|
|
44
44
|
attribute_in_database(@primary_key)
|
45
45
|
end
|
46
46
|
|
47
|
+
def id_for_database # :nodoc:
|
48
|
+
@attributes[@primary_key].value_for_database
|
49
|
+
end
|
50
|
+
|
47
51
|
private
|
48
52
|
def attribute_method?(attr_name)
|
49
53
|
attr_name == "id" || super
|
50
54
|
end
|
51
55
|
|
52
56
|
module ClassMethods
|
53
|
-
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
|
57
|
+
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database id_for_database).to_set
|
54
58
|
|
55
59
|
def instance_method_already_implemented?(method_name)
|
56
60
|
super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
when false, nil then false
|
18
18
|
else
|
19
19
|
if !type_for_attribute(attr_name) { false }
|
20
|
-
if Numeric === value || value
|
20
|
+
if Numeric === value || !value.match?(/[^0-9]/)
|
21
21
|
!value.to_i.zero?
|
22
22
|
else
|
23
23
|
return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
|
@@ -31,11 +31,8 @@ module ActiveRecord
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
def attribute?(attribute_name)
|
37
|
-
query_attribute(attribute_name)
|
38
|
-
end
|
34
|
+
alias :attribute? :query_attribute
|
35
|
+
private :attribute?
|
39
36
|
end
|
40
37
|
end
|
41
38
|
end
|
@@ -7,16 +7,14 @@ module ActiveRecord
|
|
7
7
|
|
8
8
|
module ClassMethods # :nodoc:
|
9
9
|
private
|
10
|
-
def define_method_attribute(name)
|
10
|
+
def define_method_attribute(name, owner:)
|
11
11
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
12
|
-
|
12
|
+
owner, name
|
13
13
|
) do |temp_method_name, attr_name_expr|
|
14
|
-
|
15
|
-
def #{temp_method_name}
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
RUBY
|
14
|
+
owner <<
|
15
|
+
"def #{temp_method_name}" <<
|
16
|
+
" _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
|
17
|
+
"end"
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -29,14 +27,13 @@ module ActiveRecord
|
|
29
27
|
name = self.class.attribute_aliases[name] || name
|
30
28
|
|
31
29
|
name = @primary_key if name == "id" && @primary_key
|
32
|
-
|
30
|
+
@attributes.fetch_value(name, &block)
|
33
31
|
end
|
34
32
|
|
35
33
|
# This method exists to avoid the expensive primary_key check internally, without
|
36
34
|
# breaking compatibility with the read_attribute API
|
37
35
|
def _read_attribute(attr_name, &block) # :nodoc
|
38
|
-
|
39
|
-
@attributes.fetch_value(attr_name.to_s, &block)
|
36
|
+
@attributes.fetch_value(attr_name, &block)
|
40
37
|
end
|
41
38
|
|
42
39
|
alias :attribute :_read_attribute
|
@@ -41,6 +41,12 @@ module ActiveRecord
|
|
41
41
|
# * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
|
42
42
|
# or a class name that the object type should be equal to.
|
43
43
|
#
|
44
|
+
# ==== Options
|
45
|
+
#
|
46
|
+
# +default+ The default value to use when no value is provided. If this option
|
47
|
+
# is not passed, the previous default value (if any) will be used.
|
48
|
+
# Otherwise, the default will be +nil+.
|
49
|
+
#
|
44
50
|
# ==== Example
|
45
51
|
#
|
46
52
|
# # Serialize a preferences attribute.
|
@@ -57,7 +63,7 @@ module ActiveRecord
|
|
57
63
|
# class User < ActiveRecord::Base
|
58
64
|
# serialize :preferences, Hash
|
59
65
|
# end
|
60
|
-
def serialize(attr_name, class_name_or_coder = Object)
|
66
|
+
def serialize(attr_name, class_name_or_coder = Object, **options)
|
61
67
|
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
62
68
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
63
69
|
# using the #as_json hook.
|
@@ -69,12 +75,12 @@ module ActiveRecord
|
|
69
75
|
Coders::YAMLColumn.new(attr_name, class_name_or_coder)
|
70
76
|
end
|
71
77
|
|
72
|
-
decorate_attribute_type(attr_name,
|
73
|
-
if type_incompatible_with_serialize?(
|
74
|
-
raise ColumnNotSerializableError.new(attr_name,
|
78
|
+
decorate_attribute_type(attr_name.to_s, **options) do |cast_type|
|
79
|
+
if type_incompatible_with_serialize?(cast_type, class_name_or_coder)
|
80
|
+
raise ColumnNotSerializableError.new(attr_name, cast_type)
|
75
81
|
end
|
76
82
|
|
77
|
-
Type::Serialized.new(
|
83
|
+
Type::Serialized.new(cast_type, coder)
|
78
84
|
end
|
79
85
|
end
|
80
86
|
|
@@ -1,9 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/object/try"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module AttributeMethods
|
5
7
|
module TimeZoneConversion
|
6
8
|
class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
|
9
|
+
def self.new(subtype)
|
10
|
+
self === subtype ? subtype : super
|
11
|
+
end
|
12
|
+
|
7
13
|
def deserialize(value)
|
8
14
|
convert_time_to_time_zone(super)
|
9
15
|
end
|
@@ -62,21 +68,14 @@ module ActiveRecord
|
|
62
68
|
end
|
63
69
|
|
64
70
|
module ClassMethods # :nodoc:
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# We need to apply this decorator here, rather than on module inclusion. The closure
|
69
|
-
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
70
|
-
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
|
71
|
-
# `skip_time_zone_conversion_for_attributes` would not be picked up.
|
72
|
-
subclass.class_eval do
|
73
|
-
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
|
74
|
-
decorate_matching_attribute_types(matcher, "_time_zone_conversion") do |type|
|
75
|
-
TimeZoneConverter.new(type)
|
76
|
-
end
|
77
|
-
end
|
71
|
+
def define_attribute(name, cast_type, **)
|
72
|
+
if create_time_zone_conversion_attribute?(name, cast_type)
|
73
|
+
cast_type = TimeZoneConverter.new(cast_type)
|
78
74
|
end
|
75
|
+
super
|
76
|
+
end
|
79
77
|
|
78
|
+
private
|
80
79
|
def create_time_zone_conversion_attribute?(name, cast_type)
|
81
80
|
enabled_for_column = time_zone_aware_attributes &&
|
82
81
|
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
|
@@ -11,16 +11,14 @@ module ActiveRecord
|
|
11
11
|
|
12
12
|
module ClassMethods # :nodoc:
|
13
13
|
private
|
14
|
-
def define_method_attribute=(name)
|
14
|
+
def define_method_attribute=(name, owner:)
|
15
15
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
16
|
-
|
16
|
+
owner, name, writer: true,
|
17
17
|
) do |temp_method_name, attr_name_expr|
|
18
|
-
|
19
|
-
def #{temp_method_name}(value)
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
RUBY
|
18
|
+
owner <<
|
19
|
+
"def #{temp_method_name}(value)" <<
|
20
|
+
" _write_attribute(#{attr_name_expr}, value)" <<
|
21
|
+
"end"
|
24
22
|
end
|
25
23
|
end
|
26
24
|
end
|
@@ -33,27 +31,21 @@ module ActiveRecord
|
|
33
31
|
name = self.class.attribute_aliases[name] || name
|
34
32
|
|
35
33
|
name = @primary_key if name == "id" && @primary_key
|
36
|
-
|
34
|
+
@attributes.write_from_user(name, value)
|
37
35
|
end
|
38
36
|
|
39
37
|
# This method exists to avoid the expensive primary_key check internally, without
|
40
38
|
# breaking compatibility with the write_attribute API
|
41
39
|
def _write_attribute(attr_name, value) # :nodoc:
|
42
|
-
|
43
|
-
@attributes.write_from_user(attr_name.to_s, value)
|
44
|
-
value
|
40
|
+
@attributes.write_from_user(attr_name, value)
|
45
41
|
end
|
46
42
|
|
43
|
+
alias :attribute= :_write_attribute
|
44
|
+
private :attribute=
|
45
|
+
|
47
46
|
private
|
48
47
|
def write_attribute_without_type_cast(attr_name, value)
|
49
|
-
|
50
|
-
@attributes.write_cast_value(attr_name.to_s, value)
|
51
|
-
value
|
52
|
-
end
|
53
|
-
|
54
|
-
# Dispatch target for <tt>*=</tt> attribute methods.
|
55
|
-
def attribute=(attribute_name, value)
|
56
|
-
_write_attribute(attribute_name, value)
|
48
|
+
@attributes.write_cast_value(attr_name, value)
|
57
49
|
end
|
58
50
|
end
|
59
51
|
end
|