activerecord 5.1.0 → 5.2.3
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +596 -450
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record.rb +11 -4
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +7 -5
- data/lib/active_record/associations.rb +77 -85
- data/lib/active_record/associations/alias_tracker.rb +23 -32
- data/lib/active_record/associations/association.rb +49 -35
- data/lib/active_record/associations/association_scope.rb +55 -55
- data/lib/active_record/associations/belongs_to_association.rb +30 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +66 -53
- data/lib/active_record/associations/collection_proxy.rb +30 -73
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +13 -2
- data/lib/active_record/associations/has_many_through_association.rb +37 -19
- data/lib/active_record/associations/has_one_association.rb +14 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency.rb +52 -96
- data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/preloader/association.rb +53 -92
- data/lib/active_record/associations/preloader/through_association.rb +72 -73
- data/lib/active_record/associations/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +27 -12
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +33 -216
- data/lib/active_record/attribute_methods/primary_key.rb +10 -13
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +9 -3
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +22 -19
- data/lib/active_record/attributes.rb +7 -6
- data/lib/active_record/autosave_association.rb +15 -13
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +12 -6
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +2 -0
- data/lib/active_record/collection_cache_key.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
- data/lib/active_record/connection_adapters/column.rb +4 -2
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +258 -129
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
- data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
- data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
- data/lib/active_record/connection_handling.rb +4 -2
- data/lib/active_record/core.rb +41 -61
- data/lib/active_record/counter_cache.rb +20 -15
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +18 -13
- data/lib/active_record/errors.rb +60 -15
- data/lib/active_record/explain.rb +3 -1
- data/lib/active_record/explain_registry.rb +2 -0
- data/lib/active_record/explain_subscriber.rb +2 -0
- data/lib/active_record/fixture_set/file.rb +2 -0
- data/lib/active_record/fixtures.rb +67 -60
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +49 -19
- data/lib/active_record/integration.rb +58 -19
- data/lib/active_record/internal_metadata.rb +2 -0
- data/lib/active_record/legacy_yaml_adapter.rb +3 -1
- data/lib/active_record/locking/optimistic.rb +30 -42
- data/lib/active_record/locking/pessimistic.rb +10 -7
- data/lib/active_record/log_subscriber.rb +46 -4
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +81 -29
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/model_schema.rb +74 -58
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +199 -54
- data/lib/active_record/query_cache.rb +8 -10
- data/lib/active_record/querying.rb +5 -3
- data/lib/active_record/railtie.rb +62 -6
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +2 -0
- data/lib/active_record/railties/databases.rake +48 -38
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +137 -207
- data/lib/active_record/relation.rb +132 -207
- data/lib/active_record/relation/batches.rb +32 -17
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/calculations.rb +66 -25
- data/lib/active_record/relation/delegation.rb +45 -29
- data/lib/active_record/relation/finder_methods.rb +76 -85
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +53 -23
- data/lib/active_record/relation/predicate_builder.rb +60 -79
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/query_attribute.rb +28 -2
- data/lib/active_record/relation/query_methods.rb +135 -103
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -2
- data/lib/active_record/relation/where_clause.rb +65 -67
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +129 -121
- data/lib/active_record/schema.rb +4 -2
- data/lib/active_record/schema_dumper.rb +36 -26
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping.rb +12 -10
- data/lib/active_record/scoping/default.rb +10 -7
- data/lib/active_record/scoping/named.rb +40 -12
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +22 -12
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +38 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +13 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +32 -27
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- data/lib/active_record/type/adapter_specific_registry.rb +2 -0
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +2 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +6 -0
- data/lib/active_record/type/text.rb +2 -0
- data/lib/active_record/type/time.rb +2 -0
- data/lib/active_record/type/type_map.rb +2 -0
- data/lib/active_record/type/unsigned_integer.rb +2 -0
- data/lib/active_record/type_caster.rb +2 -0
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +2 -0
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +36 -6
- data/lib/active_record/version.rb +2 -0
- data/lib/rails/generators/active_record.rb +3 -1
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +2 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +24 -36
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -15
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -18
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute_mutation_tracker.rb +0 -113
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/attribute_set/builder.rb +0 -124
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
# Implements the details of eager loading of Active Record associations.
|
@@ -42,20 +44,10 @@ module ActiveRecord
|
|
42
44
|
extend ActiveSupport::Autoload
|
43
45
|
|
44
46
|
eager_autoload do
|
45
|
-
autoload :Association,
|
46
|
-
autoload :
|
47
|
-
autoload :CollectionAssociation, "active_record/associations/preloader/collection_association"
|
48
|
-
autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
|
49
|
-
|
50
|
-
autoload :HasMany, "active_record/associations/preloader/has_many"
|
51
|
-
autoload :HasManyThrough, "active_record/associations/preloader/has_many_through"
|
52
|
-
autoload :HasOne, "active_record/associations/preloader/has_one"
|
53
|
-
autoload :HasOneThrough, "active_record/associations/preloader/has_one_through"
|
54
|
-
autoload :BelongsTo, "active_record/associations/preloader/belongs_to"
|
47
|
+
autoload :Association, "active_record/associations/preloader/association"
|
48
|
+
autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
|
55
49
|
end
|
56
50
|
|
57
|
-
NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
|
58
|
-
|
59
51
|
# Eager loads the named associations for the given Active Record record(s).
|
60
52
|
#
|
61
53
|
# In this description, 'association name' shall refer to the name passed
|
@@ -91,14 +83,13 @@ module ActiveRecord
|
|
91
83
|
# { author: :avatar }
|
92
84
|
# [ :books, { author: :avatar } ]
|
93
85
|
def preload(records, associations, preload_scope = nil)
|
94
|
-
records
|
95
|
-
associations = Array.wrap(associations)
|
96
|
-
preload_scope = preload_scope || NULL_RELATION
|
86
|
+
records = Array.wrap(records).compact
|
97
87
|
|
98
88
|
if records.empty?
|
99
89
|
[]
|
100
90
|
else
|
101
|
-
|
91
|
+
records.uniq!
|
92
|
+
Array.wrap(associations).flat_map { |association|
|
102
93
|
preloaders_on association, records, preload_scope
|
103
94
|
}
|
104
95
|
end
|
@@ -147,7 +138,7 @@ module ActiveRecord
|
|
147
138
|
def preloaders_for_one(association, records, scope)
|
148
139
|
grouped_records(association, records).flat_map do |reflection, klasses|
|
149
140
|
klasses.map do |rhs_klass, rs|
|
150
|
-
loader = preloader_for(reflection, rs
|
141
|
+
loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
|
151
142
|
loader.run self
|
152
143
|
loader
|
153
144
|
end
|
@@ -159,6 +150,7 @@ module ActiveRecord
|
|
159
150
|
records.each do |record|
|
160
151
|
next unless record
|
161
152
|
assoc = record.association(association)
|
153
|
+
next unless assoc.klass
|
162
154
|
klasses = h[assoc.reflection] ||= {}
|
163
155
|
(klasses[assoc.klass] ||= []) << record
|
164
156
|
end
|
@@ -166,8 +158,6 @@ module ActiveRecord
|
|
166
158
|
end
|
167
159
|
|
168
160
|
class AlreadyLoaded # :nodoc:
|
169
|
-
attr_reader :owners, :reflection
|
170
|
-
|
171
161
|
def initialize(klass, owners, reflection, preload_scope)
|
172
162
|
@owners = owners
|
173
163
|
@reflection = reflection
|
@@ -178,34 +168,24 @@ module ActiveRecord
|
|
178
168
|
def preloaded_records
|
179
169
|
owners.flat_map { |owner| owner.association(reflection.name).target }
|
180
170
|
end
|
181
|
-
end
|
182
171
|
|
183
|
-
|
184
|
-
|
185
|
-
def self.run(preloader); end
|
186
|
-
def self.preloaded_records; []; end
|
187
|
-
def self.owners; []; end
|
172
|
+
protected
|
173
|
+
attr_reader :owners, :reflection
|
188
174
|
end
|
189
175
|
|
190
176
|
# Returns a class containing the logic needed to load preload the data
|
191
|
-
# and attach it to a relation.
|
192
|
-
# +Preloader::HasManyThrough+. The class returned implements a `run` method
|
177
|
+
# and attach it to a relation. The class returned implements a `run` method
|
193
178
|
# that accepts a preloader.
|
194
|
-
def preloader_for(reflection, owners
|
195
|
-
return NullPreloader unless rhs_klass
|
196
|
-
|
179
|
+
def preloader_for(reflection, owners)
|
197
180
|
if owners.first.association(reflection.name).loaded?
|
198
181
|
return AlreadyLoaded
|
199
182
|
end
|
200
183
|
reflection.check_preloadable!
|
201
184
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
reflection.options[:through] ? HasOneThrough : HasOne
|
207
|
-
when :belongs_to
|
208
|
-
BelongsTo
|
185
|
+
if reflection.options[:through]
|
186
|
+
ThroughAssociation
|
187
|
+
else
|
188
|
+
Association
|
209
189
|
end
|
210
190
|
end
|
211
191
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class Preloader
|
4
6
|
class Association #:nodoc:
|
5
|
-
attr_reader :owners, :reflection, :preload_scope, :model, :klass
|
6
7
|
attr_reader :preloaded_records
|
7
8
|
|
8
9
|
def initialize(klass, owners, reflection, preload_scope)
|
@@ -11,86 +12,65 @@ module ActiveRecord
|
|
11
12
|
@reflection = reflection
|
12
13
|
@preload_scope = preload_scope
|
13
14
|
@model = owners.first && owners.first.class
|
14
|
-
@scope = nil
|
15
15
|
@preloaded_records = []
|
16
16
|
end
|
17
17
|
|
18
18
|
def run(preloader)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def scope
|
27
|
-
@scope ||= build_scope
|
28
|
-
end
|
29
|
-
|
30
|
-
def records_for(ids)
|
31
|
-
scope.where(association_key_name => ids)
|
32
|
-
end
|
33
|
-
|
34
|
-
def table
|
35
|
-
klass.arel_table
|
36
|
-
end
|
37
|
-
|
38
|
-
# The name of the key on the associated records
|
39
|
-
def association_key_name
|
40
|
-
raise NotImplementedError
|
41
|
-
end
|
42
|
-
|
43
|
-
# This is overridden by HABTM as the condition should be on the foreign_key column in
|
44
|
-
# the join table
|
45
|
-
def association_key
|
46
|
-
klass.arel_attribute(association_key_name, table)
|
47
|
-
end
|
19
|
+
records = load_records do |record|
|
20
|
+
owner = owners_by_key[convert_key(record[association_key_name])]
|
21
|
+
association = owner.association(reflection.name)
|
22
|
+
association.set_inverse_instance(record)
|
23
|
+
end
|
48
24
|
|
49
|
-
|
50
|
-
|
51
|
-
|
25
|
+
owners.each do |owner|
|
26
|
+
associate_records_to_owner(owner, records[convert_key(owner[owner_key_name])] || [])
|
27
|
+
end
|
52
28
|
end
|
53
29
|
|
54
|
-
|
55
|
-
reflection
|
56
|
-
end
|
30
|
+
protected
|
31
|
+
attr_reader :owners, :reflection, :preload_scope, :model, :klass
|
57
32
|
|
58
33
|
private
|
34
|
+
# The name of the key on the associated records
|
35
|
+
def association_key_name
|
36
|
+
reflection.join_primary_key(klass)
|
37
|
+
end
|
59
38
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
association.set_inverse_instance(record)
|
65
|
-
end
|
39
|
+
# The name of the key on the model which declares the association
|
40
|
+
def owner_key_name
|
41
|
+
reflection.join_foreign_key
|
42
|
+
end
|
66
43
|
|
67
|
-
|
68
|
-
|
44
|
+
def associate_records_to_owner(owner, records)
|
45
|
+
association = owner.association(reflection.name)
|
46
|
+
association.loaded!
|
47
|
+
if reflection.collection?
|
48
|
+
association.target.concat(records)
|
49
|
+
else
|
50
|
+
association.target = records.first unless records.empty?
|
69
51
|
end
|
70
52
|
end
|
71
53
|
|
72
54
|
def owner_keys
|
73
|
-
|
74
|
-
@owner_keys = owners.map do |owner|
|
75
|
-
owner[owner_key_name]
|
76
|
-
end
|
77
|
-
@owner_keys.uniq!
|
78
|
-
@owner_keys.compact!
|
79
|
-
end
|
80
|
-
@owner_keys
|
55
|
+
@owner_keys ||= owners_by_key.keys
|
81
56
|
end
|
82
57
|
|
83
58
|
def owners_by_key
|
84
59
|
unless defined?(@owners_by_key)
|
85
60
|
@owners_by_key = owners.each_with_object({}) do |owner, h|
|
86
|
-
|
61
|
+
key = convert_key(owner[owner_key_name])
|
62
|
+
h[key] = owner if key
|
87
63
|
end
|
88
64
|
end
|
89
65
|
@owners_by_key
|
90
66
|
end
|
91
67
|
|
92
68
|
def key_conversion_required?
|
93
|
-
@key_conversion_required
|
69
|
+
unless defined?(@key_conversion_required)
|
70
|
+
@key_conversion_required = (association_key_type != owner_key_type)
|
71
|
+
end
|
72
|
+
|
73
|
+
@key_conversion_required
|
94
74
|
end
|
95
75
|
|
96
76
|
def convert_key(key)
|
@@ -102,11 +82,11 @@ module ActiveRecord
|
|
102
82
|
end
|
103
83
|
|
104
84
|
def association_key_type
|
105
|
-
@klass.type_for_attribute(association_key_name
|
85
|
+
@klass.type_for_attribute(association_key_name).type
|
106
86
|
end
|
107
87
|
|
108
88
|
def owner_key_type
|
109
|
-
@model.type_for_attribute(owner_key_name
|
89
|
+
@model.type_for_attribute(owner_key_name).type
|
110
90
|
end
|
111
91
|
|
112
92
|
def load_records(&block)
|
@@ -115,54 +95,35 @@ module ActiveRecord
|
|
115
95
|
# Make several smaller queries if necessary or make one query if the adapter supports it
|
116
96
|
slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
117
97
|
@preloaded_records = slices.flat_map do |slice|
|
118
|
-
records_for(slice
|
98
|
+
records_for(slice, &block)
|
119
99
|
end
|
120
100
|
@preloaded_records.group_by do |record|
|
121
101
|
convert_key(record[association_key_name])
|
122
102
|
end
|
123
103
|
end
|
124
104
|
|
125
|
-
def
|
126
|
-
|
105
|
+
def records_for(ids, &block)
|
106
|
+
scope.where(association_key_name => ids).load(&block)
|
127
107
|
end
|
128
108
|
|
129
|
-
def
|
130
|
-
scope
|
131
|
-
|
132
|
-
values = reflection_scope.values
|
133
|
-
preload_values = preload_scope.values
|
134
|
-
|
135
|
-
scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
|
136
|
-
scope.references_values = Array(values[:references]) + Array(preload_values[:references])
|
137
|
-
|
138
|
-
if preload_values[:select] || values[:select]
|
139
|
-
scope._select!(preload_values[:select] || values[:select])
|
140
|
-
end
|
141
|
-
scope.includes! preload_values[:includes] || values[:includes]
|
142
|
-
if preload_scope.joins_values.any?
|
143
|
-
scope.joins!(preload_scope.joins_values)
|
144
|
-
else
|
145
|
-
scope.joins!(reflection_scope.joins_values)
|
146
|
-
end
|
147
|
-
|
148
|
-
if order_values = preload_values[:order] || values[:order]
|
149
|
-
scope.order!(order_values)
|
150
|
-
end
|
109
|
+
def scope
|
110
|
+
@scope ||= build_scope
|
111
|
+
end
|
151
112
|
|
152
|
-
|
153
|
-
|
154
|
-
|
113
|
+
def reflection_scope
|
114
|
+
@reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
|
115
|
+
end
|
155
116
|
|
156
|
-
|
157
|
-
|
158
|
-
end
|
117
|
+
def build_scope
|
118
|
+
scope = klass.scope_for_association
|
159
119
|
|
160
|
-
if
|
161
|
-
scope.where!(
|
120
|
+
if reflection.type
|
121
|
+
scope.where!(reflection.type => model.polymorphic_name)
|
162
122
|
end
|
163
123
|
|
164
|
-
scope.
|
165
|
-
|
124
|
+
scope.merge!(reflection_scope) if reflection.scope
|
125
|
+
scope.merge!(preload_scope) if preload_scope
|
126
|
+
scope
|
166
127
|
end
|
167
128
|
end
|
168
129
|
end
|
@@ -1,106 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class Preloader
|
4
|
-
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def associated_records_by_owner(preloader)
|
14
|
-
preloader.preload(owners,
|
15
|
-
through_reflection.name,
|
16
|
-
through_scope)
|
17
|
-
|
18
|
-
through_records = owners.map do |owner|
|
19
|
-
association = owner.association through_reflection.name
|
20
|
-
|
21
|
-
center = target_records_from_association(association)
|
22
|
-
[owner, Array(center)]
|
23
|
-
end
|
24
|
-
|
25
|
-
reset_association owners, through_reflection.name
|
26
|
-
|
27
|
-
middle_records = through_records.flat_map { |(_, rec)| rec }
|
28
|
-
|
29
|
-
preloaders = preloader.preload(middle_records,
|
30
|
-
source_reflection.name,
|
31
|
-
reflection_scope)
|
32
|
-
|
6
|
+
class ThroughAssociation < Association # :nodoc:
|
7
|
+
def run(preloader)
|
8
|
+
already_loaded = owners.first.association(through_reflection.name).loaded?
|
9
|
+
through_scope = through_scope()
|
10
|
+
reflection_scope = target_reflection_scope
|
11
|
+
through_preloaders = preloader.preload(owners, through_reflection.name, through_scope)
|
12
|
+
middle_records = through_preloaders.flat_map(&:preloaded_records)
|
13
|
+
preloaders = preloader.preload(middle_records, source_reflection.name, reflection_scope)
|
33
14
|
@preloaded_records = preloaders.flat_map(&:preloaded_records)
|
34
15
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
pl_to_middle = center.group_by { |record| middle_to_pl[record] }
|
43
|
-
|
44
|
-
records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
|
45
|
-
rhs_records = middles.flat_map { |r|
|
46
|
-
association = r.association source_reflection.name
|
47
|
-
|
48
|
-
target_records_from_association(association)
|
49
|
-
}.compact
|
50
|
-
|
51
|
-
# Respect the order on `reflection_scope` if it exists, else use the natural order.
|
52
|
-
if reflection_scope.values[:order].present?
|
53
|
-
@id_map ||= id_to_index_map @preloaded_records
|
54
|
-
rhs_records.sort_by { |rhs| @id_map[rhs] }
|
55
|
-
else
|
56
|
-
rhs_records
|
16
|
+
owners.each do |owner|
|
17
|
+
through_records = Array(owner.association(through_reflection.name).target)
|
18
|
+
if already_loaded
|
19
|
+
if source_type = reflection.options[:source_type]
|
20
|
+
through_records = through_records.select do |record|
|
21
|
+
record[reflection.foreign_type] == source_type
|
22
|
+
end
|
57
23
|
end
|
24
|
+
else
|
25
|
+
owner.association(through_reflection.name).reset if through_scope
|
26
|
+
end
|
27
|
+
result = through_records.flat_map do |record|
|
28
|
+
association = record.association(source_reflection.name)
|
29
|
+
target = association.target
|
30
|
+
association.reset if preload_scope
|
31
|
+
target
|
32
|
+
end
|
33
|
+
result.compact!
|
34
|
+
if reflection_scope
|
35
|
+
result.sort_by! { |rhs| preload_index[rhs] } if reflection_scope.order_values.any?
|
36
|
+
result.uniq! if reflection_scope.distinct_value
|
58
37
|
end
|
38
|
+
associate_records_to_owner(owner, result)
|
59
39
|
end
|
60
40
|
end
|
61
41
|
|
62
42
|
private
|
63
|
-
|
64
|
-
|
65
|
-
id_map = {}
|
66
|
-
ids.each_with_index { |id, index| id_map[id] = index }
|
67
|
-
id_map
|
43
|
+
def through_reflection
|
44
|
+
reflection.through_reflection
|
68
45
|
end
|
69
46
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
47
|
+
def source_reflection
|
48
|
+
reflection.source_reflection
|
49
|
+
end
|
73
50
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
owner.association(association_name).reset
|
78
|
-
}
|
51
|
+
def preload_index
|
52
|
+
@preload_index ||= @preloaded_records.each_with_object({}).with_index do |(id, result), index|
|
53
|
+
result[id] = index
|
79
54
|
end
|
80
55
|
end
|
81
56
|
|
82
57
|
def through_scope
|
83
58
|
scope = through_reflection.klass.unscoped
|
59
|
+
options = reflection.options
|
84
60
|
|
85
61
|
if options[:source_type]
|
86
62
|
scope.where! reflection.foreign_type => options[:source_type]
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
63
|
+
elsif !reflection_scope.where_clause.empty?
|
64
|
+
scope.where_clause = reflection_scope.where_clause
|
65
|
+
values = reflection_scope.values
|
66
|
+
|
67
|
+
if includes = values[:includes]
|
68
|
+
scope.includes!(source_reflection.name => includes)
|
69
|
+
else
|
70
|
+
scope.includes!(source_reflection.name)
|
91
71
|
end
|
92
72
|
|
93
|
-
|
94
|
-
|
73
|
+
if values[:references] && !values[:references].empty?
|
74
|
+
scope.references!(values[:references])
|
75
|
+
else
|
76
|
+
scope.references!(source_reflection.table_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
if joins = values[:joins]
|
80
|
+
scope.joins!(source_reflection.name => joins)
|
81
|
+
end
|
82
|
+
|
83
|
+
if left_outer_joins = values[:left_outer_joins]
|
84
|
+
scope.left_outer_joins!(source_reflection.name => left_outer_joins)
|
85
|
+
end
|
86
|
+
|
87
|
+
if scope.eager_loading? && order_values = values[:order]
|
95
88
|
scope = scope.order(order_values)
|
96
89
|
end
|
97
90
|
end
|
98
91
|
|
99
|
-
scope
|
92
|
+
scope unless scope.empty_scope?
|
100
93
|
end
|
101
94
|
|
102
|
-
def
|
103
|
-
|
95
|
+
def target_reflection_scope
|
96
|
+
if preload_scope
|
97
|
+
reflection_scope.merge(preload_scope)
|
98
|
+
elsif reflection.scope
|
99
|
+
reflection_scope
|
100
|
+
else
|
101
|
+
nil
|
102
|
+
end
|
104
103
|
end
|
105
104
|
end
|
106
105
|
end
|