activerecord 7.1.5.2 → 8.1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -11,6 +11,7 @@ module ActiveRecord
|
|
|
11
11
|
class_attribute :_reflections, instance_writer: false, default: {}
|
|
12
12
|
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
|
13
13
|
class_attribute :automatic_scope_inversing, instance_writer: false, default: false
|
|
14
|
+
class_attribute :automatically_invert_plural_associations, instance_writer: false, default: false
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
class << self
|
|
@@ -21,12 +22,12 @@ module ActiveRecord
|
|
|
21
22
|
|
|
22
23
|
def add_reflection(ar, name, reflection)
|
|
23
24
|
ar.clear_reflections_cache
|
|
24
|
-
name =
|
|
25
|
+
name = name.to_sym
|
|
25
26
|
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def add_aggregate_reflection(ar, name, reflection)
|
|
29
|
-
ar.aggregate_reflections = ar.aggregate_reflections.merge(
|
|
30
|
+
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_sym => reflection)
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
private
|
|
@@ -67,7 +68,7 @@ module ActiveRecord
|
|
|
67
68
|
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
|
68
69
|
#
|
|
69
70
|
def reflect_on_aggregation(aggregation)
|
|
70
|
-
aggregate_reflections[aggregation.
|
|
71
|
+
aggregate_reflections[aggregation.to_sym]
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
# Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
|
|
@@ -75,6 +76,10 @@ module ActiveRecord
|
|
|
75
76
|
# Account.reflections # => {"balance" => AggregateReflection}
|
|
76
77
|
#
|
|
77
78
|
def reflections
|
|
79
|
+
normalized_reflections.stringify_keys
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def normalized_reflections # :nodoc:
|
|
78
83
|
@__reflections ||= begin
|
|
79
84
|
ref = {}
|
|
80
85
|
|
|
@@ -83,13 +88,13 @@ module ActiveRecord
|
|
|
83
88
|
|
|
84
89
|
if parent_reflection
|
|
85
90
|
parent_name = parent_reflection.name
|
|
86
|
-
ref[parent_name
|
|
91
|
+
ref[parent_name] = parent_reflection
|
|
87
92
|
else
|
|
88
93
|
ref[name] = reflection
|
|
89
94
|
end
|
|
90
95
|
end
|
|
91
96
|
|
|
92
|
-
ref
|
|
97
|
+
ref.freeze
|
|
93
98
|
end
|
|
94
99
|
end
|
|
95
100
|
|
|
@@ -104,7 +109,7 @@ module ActiveRecord
|
|
|
104
109
|
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
|
105
110
|
#
|
|
106
111
|
def reflect_on_all_associations(macro = nil)
|
|
107
|
-
association_reflections =
|
|
112
|
+
association_reflections = normalized_reflections.values
|
|
108
113
|
association_reflections.select! { |reflection| reflection.macro == macro } if macro
|
|
109
114
|
association_reflections
|
|
110
115
|
end
|
|
@@ -115,16 +120,18 @@ module ActiveRecord
|
|
|
115
120
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
|
116
121
|
#
|
|
117
122
|
def reflect_on_association(association)
|
|
118
|
-
|
|
123
|
+
normalized_reflections[association.to_sym]
|
|
119
124
|
end
|
|
120
125
|
|
|
121
126
|
def _reflect_on_association(association) # :nodoc:
|
|
122
|
-
_reflections[association.
|
|
127
|
+
_reflections[association.to_sym]
|
|
123
128
|
end
|
|
124
129
|
|
|
125
130
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
|
126
131
|
def reflect_on_all_autosave_associations
|
|
127
|
-
reflections
|
|
132
|
+
reflections = normalized_reflections.values
|
|
133
|
+
reflections.select! { |reflection| reflection.options[:autosave] }
|
|
134
|
+
reflections
|
|
128
135
|
end
|
|
129
136
|
|
|
130
137
|
def clear_reflections_cache # :nodoc:
|
|
@@ -191,7 +198,7 @@ module ActiveRecord
|
|
|
191
198
|
end
|
|
192
199
|
|
|
193
200
|
def join_scope(table, foreign_table, foreign_klass)
|
|
194
|
-
predicate_builder = predicate_builder(table)
|
|
201
|
+
predicate_builder = klass.predicate_builder.with(TableMetadata.new(klass, table))
|
|
195
202
|
scope_chain_items = join_scopes(table, predicate_builder)
|
|
196
203
|
klass_scope = klass_join_scope(table, predicate_builder)
|
|
197
204
|
|
|
@@ -217,7 +224,7 @@ module ActiveRecord
|
|
|
217
224
|
klass_scope
|
|
218
225
|
end
|
|
219
226
|
|
|
220
|
-
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
|
227
|
+
def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
|
|
221
228
|
if scope
|
|
222
229
|
[scope_for(build_scope(table, predicate_builder, klass), record)]
|
|
223
230
|
else
|
|
@@ -225,7 +232,7 @@ module ActiveRecord
|
|
|
225
232
|
end
|
|
226
233
|
end
|
|
227
234
|
|
|
228
|
-
def klass_join_scope(table, predicate_builder) # :nodoc:
|
|
235
|
+
def klass_join_scope(table, predicate_builder = nil) # :nodoc:
|
|
229
236
|
relation = build_scope(table, predicate_builder)
|
|
230
237
|
klass.scope_for_association(relation)
|
|
231
238
|
end
|
|
@@ -235,14 +242,16 @@ module ActiveRecord
|
|
|
235
242
|
end
|
|
236
243
|
|
|
237
244
|
def counter_cache_column
|
|
238
|
-
@counter_cache_column ||=
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
245
|
+
@counter_cache_column ||= begin
|
|
246
|
+
counter_cache = options[:counter_cache]
|
|
247
|
+
|
|
248
|
+
if belongs_to?
|
|
249
|
+
if counter_cache
|
|
250
|
+
counter_cache[:column] || -"#{active_record.name.demodulize.underscore.pluralize}_count"
|
|
251
|
+
end
|
|
252
|
+
else
|
|
253
|
+
-((counter_cache && -counter_cache[:column]) || "#{name}_count")
|
|
243
254
|
end
|
|
244
|
-
else
|
|
245
|
-
-(options[:counter_cache]&.to_s || "#{name}_count")
|
|
246
255
|
end
|
|
247
256
|
end
|
|
248
257
|
|
|
@@ -291,7 +300,7 @@ module ActiveRecord
|
|
|
291
300
|
inverse_of && inverse_which_updates_counter_cache == inverse_of
|
|
292
301
|
end
|
|
293
302
|
|
|
294
|
-
# Returns whether a counter cache
|
|
303
|
+
# Returns whether this association has a counter cache.
|
|
295
304
|
#
|
|
296
305
|
# The counter_cache option must be given on either the owner or inverse
|
|
297
306
|
# association, and the column must be present on the owner.
|
|
@@ -301,6 +310,17 @@ module ActiveRecord
|
|
|
301
310
|
active_record.has_attribute?(counter_cache_column)
|
|
302
311
|
end
|
|
303
312
|
|
|
313
|
+
# Returns whether this association has a counter cache and its column values were backfilled
|
|
314
|
+
# (and so it is used internally by methods like +size+/+any?+/etc).
|
|
315
|
+
def has_active_cached_counter?
|
|
316
|
+
return false unless has_cached_counter?
|
|
317
|
+
|
|
318
|
+
counter_cache = options[:counter_cache] ||
|
|
319
|
+
(inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache])
|
|
320
|
+
|
|
321
|
+
counter_cache[:active] != false
|
|
322
|
+
end
|
|
323
|
+
|
|
304
324
|
def counter_must_be_updated_by_has_many?
|
|
305
325
|
!inverse_updates_counter_in_memory? && has_cached_counter?
|
|
306
326
|
end
|
|
@@ -313,12 +333,8 @@ module ActiveRecord
|
|
|
313
333
|
collect_join_chain
|
|
314
334
|
end
|
|
315
335
|
|
|
316
|
-
def build_scope(table, predicate_builder =
|
|
317
|
-
Relation.create(
|
|
318
|
-
klass,
|
|
319
|
-
table: table,
|
|
320
|
-
predicate_builder: predicate_builder
|
|
321
|
-
)
|
|
336
|
+
def build_scope(table, predicate_builder = nil, klass = self.klass)
|
|
337
|
+
Relation.create(klass, table:, predicate_builder:)
|
|
322
338
|
end
|
|
323
339
|
|
|
324
340
|
def strict_loading?
|
|
@@ -337,10 +353,6 @@ module ActiveRecord
|
|
|
337
353
|
end
|
|
338
354
|
|
|
339
355
|
private
|
|
340
|
-
def predicate_builder(table)
|
|
341
|
-
PredicateBuilder.new(TableMetadata.new(klass, table))
|
|
342
|
-
end
|
|
343
|
-
|
|
344
356
|
def primary_key(klass)
|
|
345
357
|
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
|
346
358
|
end
|
|
@@ -377,12 +389,11 @@ module ActiveRecord
|
|
|
377
389
|
super()
|
|
378
390
|
@name = name
|
|
379
391
|
@scope = scope
|
|
380
|
-
@options = options
|
|
392
|
+
@options = normalize_options(options)
|
|
381
393
|
@active_record = active_record
|
|
382
394
|
@klass = options[:anonymous_class]
|
|
383
395
|
@plural_name = active_record.pluralize_table_names ?
|
|
384
396
|
name.to_s.pluralize : name.to_s
|
|
385
|
-
validate_reflection!
|
|
386
397
|
end
|
|
387
398
|
|
|
388
399
|
def autosave=(autosave)
|
|
@@ -409,7 +420,15 @@ module ActiveRecord
|
|
|
409
420
|
# a new association object. Use +build_association+ or +create_association+
|
|
410
421
|
# instead. This allows plugins to hook into association object creation.
|
|
411
422
|
def klass
|
|
412
|
-
@klass ||=
|
|
423
|
+
@klass ||= _klass(class_name)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def _klass(class_name) # :nodoc:
|
|
427
|
+
if active_record.name.demodulize == class_name
|
|
428
|
+
return compute_class("::#{class_name}") rescue NameError
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
compute_class(class_name)
|
|
413
432
|
end
|
|
414
433
|
|
|
415
434
|
def compute_class(name)
|
|
@@ -435,15 +454,24 @@ module ActiveRecord
|
|
|
435
454
|
name.to_s.camelize
|
|
436
455
|
end
|
|
437
456
|
|
|
438
|
-
def
|
|
439
|
-
|
|
457
|
+
def normalize_options(options)
|
|
458
|
+
counter_cache = options.delete(:counter_cache)
|
|
440
459
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
460
|
+
if counter_cache
|
|
461
|
+
active = true
|
|
462
|
+
|
|
463
|
+
case counter_cache
|
|
464
|
+
when String, Symbol
|
|
465
|
+
column = -counter_cache.to_s
|
|
466
|
+
when Hash
|
|
467
|
+
active = counter_cache.fetch(:active, true)
|
|
468
|
+
column = counter_cache[:column]&.to_s
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
options[:counter_cache] = { active: active, column: column }
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
options
|
|
447
475
|
end
|
|
448
476
|
end
|
|
449
477
|
|
|
@@ -488,12 +516,27 @@ module ActiveRecord
|
|
|
488
516
|
|
|
489
517
|
def initialize(name, scope, options, active_record)
|
|
490
518
|
super
|
|
519
|
+
|
|
520
|
+
@validated = false
|
|
491
521
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
|
492
522
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
|
493
523
|
@join_table = nil
|
|
494
524
|
@foreign_key = nil
|
|
495
525
|
@association_foreign_key = nil
|
|
496
526
|
@association_primary_key = nil
|
|
527
|
+
if options[:query_constraints]
|
|
528
|
+
raise ConfigurationError, <<~MSG.squish
|
|
529
|
+
Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is not allowed.
|
|
530
|
+
To get the same behavior, use the `foreign_key` option instead.
|
|
531
|
+
MSG
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
# If the foreign key is an array, set query constraints options and don't use the foreign key
|
|
535
|
+
if options[:foreign_key].is_a?(Array)
|
|
536
|
+
options[:query_constraints] = options.delete(:foreign_key)
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
@deprecated = !!options[:deprecated]
|
|
497
540
|
|
|
498
541
|
ensure_option_not_given_as_class!(:class_name)
|
|
499
542
|
end
|
|
@@ -503,7 +546,9 @@ module ActiveRecord
|
|
|
503
546
|
if polymorphic?
|
|
504
547
|
key = [key, owner._read_attribute(@foreign_type)]
|
|
505
548
|
end
|
|
506
|
-
klass.
|
|
549
|
+
klass.with_connection do |connection|
|
|
550
|
+
klass.cached_find_by_statement(connection, key, &block)
|
|
551
|
+
end
|
|
507
552
|
end
|
|
508
553
|
|
|
509
554
|
def join_table
|
|
@@ -511,10 +556,14 @@ module ActiveRecord
|
|
|
511
556
|
end
|
|
512
557
|
|
|
513
558
|
def foreign_key(infer_from_inverse_of: true)
|
|
514
|
-
@foreign_key ||= if options[:
|
|
515
|
-
options[:
|
|
516
|
-
|
|
517
|
-
|
|
559
|
+
@foreign_key ||= if options[:foreign_key]
|
|
560
|
+
if options[:foreign_key].is_a?(Array)
|
|
561
|
+
options[:foreign_key].map { |fk| -fk.to_s.freeze }.freeze
|
|
562
|
+
else
|
|
563
|
+
options[:foreign_key].to_s.freeze
|
|
564
|
+
end
|
|
565
|
+
elsif options[:query_constraints]
|
|
566
|
+
options[:query_constraints].map { |fk| -fk.to_s.freeze }.freeze
|
|
518
567
|
else
|
|
519
568
|
derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
|
|
520
569
|
|
|
@@ -522,7 +571,12 @@ module ActiveRecord
|
|
|
522
571
|
derived_fk = derive_fk_query_constraints(derived_fk)
|
|
523
572
|
end
|
|
524
573
|
|
|
525
|
-
derived_fk
|
|
574
|
+
if derived_fk.is_a?(Array)
|
|
575
|
+
derived_fk.map! { |fk| -fk.freeze }
|
|
576
|
+
derived_fk.freeze
|
|
577
|
+
else
|
|
578
|
+
-derived_fk.freeze
|
|
579
|
+
end
|
|
526
580
|
end
|
|
527
581
|
end
|
|
528
582
|
|
|
@@ -566,6 +620,8 @@ module ActiveRecord
|
|
|
566
620
|
end
|
|
567
621
|
|
|
568
622
|
def check_validity!
|
|
623
|
+
return if @validated
|
|
624
|
+
|
|
569
625
|
check_validity_of_inverse!
|
|
570
626
|
|
|
571
627
|
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
|
@@ -575,6 +631,8 @@ module ActiveRecord
|
|
|
575
631
|
raise CompositePrimaryKeyMismatchError.new(self)
|
|
576
632
|
end
|
|
577
633
|
end
|
|
634
|
+
|
|
635
|
+
@validated = true
|
|
578
636
|
end
|
|
579
637
|
|
|
580
638
|
def check_eager_loadable!
|
|
@@ -692,6 +750,10 @@ module ActiveRecord
|
|
|
692
750
|
Array(options[:extend])
|
|
693
751
|
end
|
|
694
752
|
|
|
753
|
+
def deprecated?
|
|
754
|
+
@deprecated
|
|
755
|
+
end
|
|
756
|
+
|
|
695
757
|
private
|
|
696
758
|
# Attempts to find the inverse association name automatically.
|
|
697
759
|
# If it cannot find a suitable inverse association name, it returns
|
|
@@ -711,6 +773,10 @@ module ActiveRecord
|
|
|
711
773
|
|
|
712
774
|
begin
|
|
713
775
|
reflection = klass._reflect_on_association(inverse_name)
|
|
776
|
+
if !reflection && active_record.automatically_invert_plural_associations
|
|
777
|
+
plural_inverse_name = ActiveSupport::Inflector.pluralize(inverse_name)
|
|
778
|
+
reflection = klass._reflect_on_association(plural_inverse_name)
|
|
779
|
+
end
|
|
714
780
|
rescue NameError => error
|
|
715
781
|
raise unless error.name.to_s == class_name
|
|
716
782
|
|
|
@@ -720,7 +786,7 @@ module ActiveRecord
|
|
|
720
786
|
end
|
|
721
787
|
|
|
722
788
|
if valid_inverse_reflection?(reflection)
|
|
723
|
-
|
|
789
|
+
reflection.name
|
|
724
790
|
end
|
|
725
791
|
end
|
|
726
792
|
end
|
|
@@ -921,6 +987,8 @@ module ActiveRecord
|
|
|
921
987
|
|
|
922
988
|
def initialize(delegate_reflection)
|
|
923
989
|
super()
|
|
990
|
+
|
|
991
|
+
@validated = false
|
|
924
992
|
@delegate_reflection = delegate_reflection
|
|
925
993
|
@klass = delegate_reflection.options[:anonymous_class]
|
|
926
994
|
@source_reflection_name = delegate_reflection.options[:source]
|
|
@@ -933,7 +1001,7 @@ module ActiveRecord
|
|
|
933
1001
|
end
|
|
934
1002
|
|
|
935
1003
|
def klass
|
|
936
|
-
@klass ||= delegate_reflection.
|
|
1004
|
+
@klass ||= delegate_reflection._klass(class_name)
|
|
937
1005
|
end
|
|
938
1006
|
|
|
939
1007
|
# Returns the source of the through reflection. It checks both a singularized
|
|
@@ -954,6 +1022,8 @@ module ActiveRecord
|
|
|
954
1022
|
# # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
|
|
955
1023
|
#
|
|
956
1024
|
def source_reflection
|
|
1025
|
+
return unless source_reflection_name
|
|
1026
|
+
|
|
957
1027
|
through_reflection.klass._reflect_on_association(source_reflection_name)
|
|
958
1028
|
end
|
|
959
1029
|
|
|
@@ -1006,7 +1076,7 @@ module ActiveRecord
|
|
|
1006
1076
|
source_reflection.scopes + super
|
|
1007
1077
|
end
|
|
1008
1078
|
|
|
1009
|
-
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
|
1079
|
+
def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
|
|
1010
1080
|
source_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
|
1011
1081
|
end
|
|
1012
1082
|
|
|
@@ -1082,6 +1152,8 @@ module ActiveRecord
|
|
|
1082
1152
|
end
|
|
1083
1153
|
|
|
1084
1154
|
def check_validity!
|
|
1155
|
+
return if @validated
|
|
1156
|
+
|
|
1085
1157
|
if through_reflection.nil?
|
|
1086
1158
|
raise HasManyThroughAssociationNotFoundError.new(active_record, self)
|
|
1087
1159
|
end
|
|
@@ -1111,7 +1183,7 @@ module ActiveRecord
|
|
|
1111
1183
|
end
|
|
1112
1184
|
|
|
1113
1185
|
if parent_reflection.nil?
|
|
1114
|
-
reflections = active_record.
|
|
1186
|
+
reflections = active_record.normalized_reflections.keys
|
|
1115
1187
|
|
|
1116
1188
|
if reflections.index(through_reflection.name) > reflections.index(name)
|
|
1117
1189
|
raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
|
|
@@ -1119,6 +1191,8 @@ module ActiveRecord
|
|
|
1119
1191
|
end
|
|
1120
1192
|
|
|
1121
1193
|
check_validity_of_inverse!
|
|
1194
|
+
|
|
1195
|
+
@validated = true
|
|
1122
1196
|
end
|
|
1123
1197
|
|
|
1124
1198
|
def constraints
|
|
@@ -1139,6 +1213,10 @@ module ActiveRecord
|
|
|
1139
1213
|
collect_join_reflections(seed + [self])
|
|
1140
1214
|
end
|
|
1141
1215
|
|
|
1216
|
+
def deprecated_nested_reflections
|
|
1217
|
+
@deprecated_nested_reflections ||= collect_deprecated_nested_reflections
|
|
1218
|
+
end
|
|
1219
|
+
|
|
1142
1220
|
protected
|
|
1143
1221
|
def actual_source_reflection # FIXME: this is a horrible name
|
|
1144
1222
|
source_reflection.actual_source_reflection
|
|
@@ -1163,6 +1241,19 @@ module ActiveRecord
|
|
|
1163
1241
|
options[:source_type] || source_reflection.class_name
|
|
1164
1242
|
end
|
|
1165
1243
|
|
|
1244
|
+
def collect_deprecated_nested_reflections
|
|
1245
|
+
result = []
|
|
1246
|
+
[through_reflection, source_reflection].each do |reflection|
|
|
1247
|
+
result << reflection if reflection.deprecated?
|
|
1248
|
+
# Both the through and the source reflections could be through
|
|
1249
|
+
# themselves. Nesting can go an arbitrary number of levels down.
|
|
1250
|
+
if reflection.through_reflection?
|
|
1251
|
+
result.concat(reflection.deprecated_nested_reflections)
|
|
1252
|
+
end
|
|
1253
|
+
end
|
|
1254
|
+
result
|
|
1255
|
+
end
|
|
1256
|
+
|
|
1166
1257
|
delegate_methods = AssociationReflection.public_instance_methods -
|
|
1167
1258
|
public_instance_methods
|
|
1168
1259
|
|
|
@@ -1179,8 +1270,11 @@ module ActiveRecord
|
|
|
1179
1270
|
@previous_reflection = previous_reflection
|
|
1180
1271
|
end
|
|
1181
1272
|
|
|
1182
|
-
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
|
1183
|
-
scopes =
|
|
1273
|
+
def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
|
|
1274
|
+
scopes = super
|
|
1275
|
+
unless @previous_reflection.through_reflection?
|
|
1276
|
+
scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
|
|
1277
|
+
end
|
|
1184
1278
|
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
|
1185
1279
|
end
|
|
1186
1280
|
|
|
@@ -5,11 +5,12 @@ module ActiveRecord
|
|
|
5
5
|
class BatchEnumerator
|
|
6
6
|
include Enumerable
|
|
7
7
|
|
|
8
|
-
def initialize(of: 1000, start: nil, finish: nil, relation:, order: :asc, use_ranges: nil) # :nodoc:
|
|
8
|
+
def initialize(of: 1000, start: nil, finish: nil, relation:, cursor:, order: :asc, use_ranges: nil) # :nodoc:
|
|
9
9
|
@of = of
|
|
10
10
|
@relation = relation
|
|
11
11
|
@start = start
|
|
12
12
|
@finish = finish
|
|
13
|
+
@cursor = cursor
|
|
13
14
|
@order = order
|
|
14
15
|
@use_ranges = use_ranges
|
|
15
16
|
end
|
|
@@ -52,7 +53,7 @@ module ActiveRecord
|
|
|
52
53
|
def each_record(&block)
|
|
53
54
|
return to_enum(:each_record) unless block_given?
|
|
54
55
|
|
|
55
|
-
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true, order: @order).each do |relation|
|
|
56
|
+
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true, cursor: @cursor, order: @order).each do |relation|
|
|
56
57
|
relation.records.each(&block)
|
|
57
58
|
end
|
|
58
59
|
end
|
|
@@ -77,13 +78,26 @@ module ActiveRecord
|
|
|
77
78
|
end
|
|
78
79
|
end
|
|
79
80
|
|
|
80
|
-
#
|
|
81
|
+
# Touches records in batches. Returns the total number of rows affected.
|
|
82
|
+
#
|
|
83
|
+
# Person.in_batches.touch_all
|
|
84
|
+
#
|
|
85
|
+
# See Relation#touch_all for details of how each batch is touched.
|
|
86
|
+
def touch_all(...)
|
|
87
|
+
sum do |relation|
|
|
88
|
+
relation.touch_all(...)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Destroys records in batches. Returns the total number of rows affected.
|
|
81
93
|
#
|
|
82
94
|
# Person.where("age < 10").in_batches.destroy_all
|
|
83
95
|
#
|
|
84
96
|
# See Relation#destroy_all for details of how each batch is destroyed.
|
|
85
97
|
def destroy_all
|
|
86
|
-
|
|
98
|
+
sum do |relation|
|
|
99
|
+
relation.destroy_all.count(&:destroyed?)
|
|
100
|
+
end
|
|
87
101
|
end
|
|
88
102
|
|
|
89
103
|
# Yields an ActiveRecord::Relation object for each batch of records.
|
|
@@ -92,7 +106,7 @@ module ActiveRecord
|
|
|
92
106
|
# relation.update_all(awesome: true)
|
|
93
107
|
# end
|
|
94
108
|
def each(&block)
|
|
95
|
-
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false, order: @order, use_ranges: @use_ranges)
|
|
109
|
+
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false, cursor: @cursor, order: @order, use_ranges: @use_ranges)
|
|
96
110
|
return enum.each(&block) if block_given?
|
|
97
111
|
enum
|
|
98
112
|
end
|