activerecord 5.1.7 → 5.2.6
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 +583 -673
- 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/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +7 -5
- data/lib/active_record/associations/alias_tracker.rb +19 -27
- data/lib/active_record/associations/association.rb +41 -37
- data/lib/active_record/associations/association_scope.rb +38 -50
- data/lib/active_record/associations/belongs_to_association.rb +27 -8
- 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 +12 -4
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- 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 +59 -47
- data/lib/active_record/associations/collection_proxy.rb +20 -49
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +12 -1
- data/lib/active_record/associations/has_many_through_association.rb +36 -30
- data/lib/active_record/associations/has_one_association.rb +12 -1
- data/lib/active_record/associations/has_one_through_association.rb +13 -8
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
- 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/join_dependency.rb +48 -93
- data/lib/active_record/associations/preloader/association.rb +45 -61
- data/lib/active_record/associations/preloader/through_association.rb +71 -79
- data/lib/active_record/associations/preloader.rb +18 -38
- data/lib/active_record/associations/singular_association.rb +14 -16
- data/lib/active_record/associations/through_association.rb +26 -11
- data/lib/active_record/associations.rb +40 -63
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +30 -214
- data/lib/active_record/attribute_methods/primary_key.rb +7 -6
- 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 +21 -9
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +35 -19
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -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 +12 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +139 -41
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -31
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +64 -6
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
- data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
- data/lib/active_record/connection_adapters/column.rb +3 -1
- 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 +47 -2
- 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 -30
- 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 +2 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
- 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 +2 -0
- 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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -3
- 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 +4 -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/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
- 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 +233 -111
- 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 +57 -73
- 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 +22 -0
- 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 +81 -94
- 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 +10 -3
- 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 +42 -3
- 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 +14 -17
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +43 -0
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +47 -9
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/model_schema.rb +16 -21
- 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 +167 -16
- data/lib/active_record/query_cache.rb +6 -8
- data/lib/active_record/querying.rb +4 -2
- 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 +46 -36
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +108 -194
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/calculations.rb +45 -19
- data/lib/active_record/relation/delegation.rb +45 -27
- data/lib/active_record/relation/finder_methods.rb +75 -76
- 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/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/predicate_builder.rb +60 -79
- data/lib/active_record/relation/query_attribute.rb +28 -2
- data/lib/active_record/relation/query_methods.rb +128 -99
- 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 -68
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +120 -214
- 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/default.rb +8 -9
- data/lib/active_record/scoping/named.rb +23 -7
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +23 -13
- 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 +25 -14
- data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
- data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +6 -6
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +33 -28
- data/lib/active_record/translation.rb +2 -0
- 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 +2 -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.rb +4 -1
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.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 +35 -5
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/version.rb +2 -0
- data/lib/active_record.rb +11 -4
- 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/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/migration.rb +2 -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
- data/lib/rails/generators/active_record.rb +3 -1
- metadata +23 -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/user_provided_default.rb +0 -30
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute_mutation_tracker.rb +0 -122
- data/lib/active_record/attribute_set/builder.rb +0 -126
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/attribute_set.rb +0 -113
- 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 -37
@@ -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,79 +12,54 @@ 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
|
@@ -106,11 +82,11 @@ module ActiveRecord
|
|
106
82
|
end
|
107
83
|
|
108
84
|
def association_key_type
|
109
|
-
@klass.type_for_attribute(association_key_name
|
85
|
+
@klass.type_for_attribute(association_key_name).type
|
110
86
|
end
|
111
87
|
|
112
88
|
def owner_key_type
|
113
|
-
@model.type_for_attribute(owner_key_name
|
89
|
+
@model.type_for_attribute(owner_key_name).type
|
114
90
|
end
|
115
91
|
|
116
92
|
def load_records(&block)
|
@@ -119,26 +95,34 @@ module ActiveRecord
|
|
119
95
|
# Make several smaller queries if necessary or make one query if the adapter supports it
|
120
96
|
slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
121
97
|
@preloaded_records = slices.flat_map do |slice|
|
122
|
-
records_for(slice
|
98
|
+
records_for(slice, &block)
|
123
99
|
end
|
124
100
|
@preloaded_records.group_by do |record|
|
125
101
|
convert_key(record[association_key_name])
|
126
102
|
end
|
127
103
|
end
|
128
104
|
|
105
|
+
def records_for(ids, &block)
|
106
|
+
scope.where(association_key_name => ids).load(&block)
|
107
|
+
end
|
108
|
+
|
109
|
+
def scope
|
110
|
+
@scope ||= build_scope
|
111
|
+
end
|
112
|
+
|
129
113
|
def reflection_scope
|
130
|
-
@reflection_scope ||= reflection.scope_for(klass)
|
114
|
+
@reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
|
131
115
|
end
|
132
116
|
|
133
117
|
def build_scope
|
134
118
|
scope = klass.scope_for_association
|
135
119
|
|
136
120
|
if reflection.type
|
137
|
-
scope.where!(reflection.type => model.
|
121
|
+
scope.where!(reflection.type => model.polymorphic_name)
|
138
122
|
end
|
139
123
|
|
140
|
-
scope.merge!(reflection_scope)
|
141
|
-
scope.merge!(preload_scope) if preload_scope
|
124
|
+
scope.merge!(reflection_scope) if reflection.scope
|
125
|
+
scope.merge!(preload_scope) if preload_scope
|
142
126
|
scope
|
143
127
|
end
|
144
128
|
end
|
@@ -1,113 +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
|
84
|
-
|
59
|
+
options = reflection.options
|
85
60
|
|
86
61
|
if options[:source_type]
|
87
62
|
scope.where! reflection.foreign_type => options[:source_type]
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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)
|
71
|
+
end
|
72
|
+
|
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)
|
98
85
|
end
|
99
86
|
|
100
|
-
scope.references! values[:references]
|
101
87
|
if scope.eager_loading? && order_values = values[:order]
|
102
88
|
scope = scope.order(order_values)
|
103
89
|
end
|
104
90
|
end
|
105
91
|
|
106
|
-
scope
|
92
|
+
scope unless scope.empty_scope?
|
107
93
|
end
|
108
94
|
|
109
|
-
def
|
110
|
-
|
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
|
111
103
|
end
|
112
104
|
end
|
113
105
|
end
|
@@ -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
|
-
|
196
|
-
|
197
|
-
if owners.first.association(reflection.name).loaded?
|
179
|
+
def preloader_for(reflection, owners)
|
180
|
+
if owners.all? { |o| o.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,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class SingularAssociation < Association #:nodoc:
|
@@ -15,9 +17,8 @@ module ActiveRecord
|
|
15
17
|
replace(record)
|
16
18
|
end
|
17
19
|
|
18
|
-
def build(attributes = {})
|
19
|
-
record = build_record(attributes)
|
20
|
-
yield(record) if block_given?
|
20
|
+
def build(attributes = {}, &block)
|
21
|
+
record = build_record(attributes, &block)
|
21
22
|
set_new_record(record)
|
22
23
|
record
|
23
24
|
end
|
@@ -30,24 +31,22 @@ module ActiveRecord
|
|
30
31
|
end
|
31
32
|
|
32
33
|
private
|
33
|
-
|
34
|
-
|
35
|
-
scope.scope_for_create.stringify_keys.except(klass.primary_key)
|
34
|
+
def scope_for_create
|
35
|
+
super.except!(klass.primary_key)
|
36
36
|
end
|
37
37
|
|
38
38
|
def find_target
|
39
|
-
|
39
|
+
scope = self.scope
|
40
|
+
return scope.take if skip_statement_cache?(scope)
|
40
41
|
|
41
42
|
conn = klass.connection
|
42
|
-
sc = reflection.association_scope_cache(conn, owner) do
|
43
|
-
|
44
|
-
|
45
|
-
target_scope.merge(as.scope(self, conn)).limit(1)
|
46
|
-
}
|
43
|
+
sc = reflection.association_scope_cache(conn, owner) do |params|
|
44
|
+
as = AssociationScope.create { params.bind }
|
45
|
+
target_scope.merge!(as.scope(self)).limit(1)
|
47
46
|
end
|
48
47
|
|
49
48
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
50
|
-
sc.execute(binds,
|
49
|
+
sc.execute(binds, conn) do |record|
|
51
50
|
set_inverse_instance record
|
52
51
|
end.first
|
53
52
|
rescue ::RangeError
|
@@ -62,9 +61,8 @@ module ActiveRecord
|
|
62
61
|
replace(record)
|
63
62
|
end
|
64
63
|
|
65
|
-
def _create_record(attributes, raise_error = false)
|
66
|
-
record = build_record(attributes)
|
67
|
-
yield(record) if block_given?
|
64
|
+
def _create_record(attributes, raise_error = false, &block)
|
65
|
+
record = build_record(attributes, &block)
|
68
66
|
saved = record.save
|
69
67
|
set_new_record(record)
|
70
68
|
raise RecordInvalid.new(record) if !saved && raise_error
|
@@ -1,10 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
|
-
# = Active Record Through Association
|
3
4
|
module Associations
|
5
|
+
# = Active Record Through Association
|
4
6
|
module ThroughAssociation #:nodoc:
|
5
|
-
delegate :source_reflection,
|
7
|
+
delegate :source_reflection, to: :reflection
|
6
8
|
|
7
9
|
private
|
10
|
+
def through_reflection
|
11
|
+
@through_reflection ||= begin
|
12
|
+
refl = reflection.through_reflection
|
13
|
+
|
14
|
+
while refl.through_reflection?
|
15
|
+
refl = refl.through_reflection
|
16
|
+
end
|
17
|
+
|
18
|
+
refl
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def through_association
|
23
|
+
@through_association ||= owner.association(through_reflection.name)
|
24
|
+
end
|
8
25
|
|
9
26
|
# We merge in these scopes for two reasons:
|
10
27
|
#
|
@@ -36,24 +53,22 @@ module ActiveRecord
|
|
36
53
|
def construct_join_attributes(*records)
|
37
54
|
ensure_mutable
|
38
55
|
|
39
|
-
|
56
|
+
association_primary_key = source_reflection.association_primary_key(reflection.klass)
|
57
|
+
|
58
|
+
if association_primary_key == reflection.klass.primary_key && !options[:source_type]
|
40
59
|
join_attributes = { source_reflection.name => records }
|
41
60
|
else
|
42
61
|
join_attributes = {
|
43
|
-
source_reflection.foreign_key =>
|
44
|
-
records.map { |record|
|
45
|
-
record.send(source_reflection.association_primary_key(reflection.klass))
|
46
|
-
}
|
62
|
+
source_reflection.foreign_key => records.map(&association_primary_key.to_sym)
|
47
63
|
}
|
48
64
|
end
|
49
65
|
|
50
66
|
if options[:source_type]
|
51
|
-
join_attributes[source_reflection.foreign_type] =
|
52
|
-
records.map { |record| record.class.base_class.name }
|
67
|
+
join_attributes[source_reflection.foreign_type] = [ options[:source_type] ]
|
53
68
|
end
|
54
69
|
|
55
70
|
if records.count == 1
|
56
|
-
|
71
|
+
join_attributes.transform_values!(&:first)
|
57
72
|
else
|
58
73
|
join_attributes
|
59
74
|
end
|
@@ -99,7 +114,7 @@ module ActiveRecord
|
|
99
114
|
attributes[inverse.foreign_key] = target.id
|
100
115
|
end
|
101
116
|
|
102
|
-
super
|
117
|
+
super
|
103
118
|
end
|
104
119
|
end
|
105
120
|
end
|