activerecord 5.0.7 → 5.1.7
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 +5 -5
- data/CHANGELOG.md +657 -2080
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations/alias_tracker.rb +10 -11
- data/lib/active_record/associations/association.rb +23 -5
- data/lib/active_record/associations/association_scope.rb +95 -81
- data/lib/active_record/associations/belongs_to_association.rb +7 -4
- data/lib/active_record/associations/builder/belongs_to.rb +30 -16
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +36 -205
- data/lib/active_record/associations/collection_proxy.rb +132 -63
- data/lib/active_record/associations/has_many_association.rb +10 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -4
- data/lib/active_record/associations/has_one_association.rb +24 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +121 -118
- data/lib/active_record/associations/preloader/association.rb +64 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +41 -41
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +2 -5
- data/lib/active_record/associations.rb +1591 -1562
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +229 -46
- data/lib/active_record/attribute_methods/primary_key.rb +74 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +30 -33
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_mutation_tracker.rb +63 -11
- data/lib/active_record/attribute_set/builder.rb +27 -33
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attributes.rb +22 -22
- data/lib/active_record/autosave_association.rb +18 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +56 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +3 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
- data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
- data/lib/active_record/connection_adapters/column.rb +26 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +109 -93
- data/lib/active_record/counter_cache.rb +60 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +64 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +69 -74
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +23 -28
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +100 -47
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/migration.rb +153 -155
- data/lib/active_record/model_schema.rb +94 -107
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +11 -34
- data/lib/active_record/persistence.rb +65 -50
- data/lib/active_record/query_cache.rb +2 -6
- data/lib/active_record/querying.rb +3 -4
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +105 -133
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +154 -108
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/batches.rb +80 -51
- data/lib/active_record/relation/calculations.rb +169 -162
- data/lib/active_record/relation/delegation.rb +32 -31
- data/lib/active_record/relation/finder_methods.rb +197 -231
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +255 -293
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +80 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/relation.rb +93 -119
- data/lib/active_record/result.rb +41 -32
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +176 -192
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +15 -38
- data/lib/active_record/schema_migration.rb +8 -4
- data/lib/active_record/scoping/default.rb +90 -90
- data/lib/active_record/scoping/named.rb +11 -11
- data/lib/active_record/scoping.rb +6 -6
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +65 -55
- data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
- data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +46 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +97 -109
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +13 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/internal/abstract_json.rb +4 -0
- data/lib/active_record/type/serialized.rb +14 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +20 -20
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/migration.rb +1 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- data/lib/rails/generators/active_record.rb +4 -4
- metadata +24 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -28,10 +28,6 @@ module ActiveRecord
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def records_for(ids)
|
31
|
-
query_scope(ids)
|
32
|
-
end
|
33
|
-
|
34
|
-
def query_scope(ids)
|
35
31
|
scope.where(association_key_name => ids)
|
36
32
|
end
|
37
33
|
|
@@ -61,86 +57,90 @@ module ActiveRecord
|
|
61
57
|
|
62
58
|
private
|
63
59
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
60
|
+
def associated_records_by_owner(preloader)
|
61
|
+
records = load_records do |record|
|
62
|
+
owner = owners_by_key[convert_key(record[association_key_name])]
|
63
|
+
association = owner.association(reflection.name)
|
64
|
+
association.set_inverse_instance(record)
|
65
|
+
end
|
70
66
|
|
71
|
-
|
72
|
-
|
67
|
+
owners.each_with_object({}) do |owner, result|
|
68
|
+
result[owner] = records[convert_key(owner[owner_key_name])] || []
|
69
|
+
end
|
73
70
|
end
|
74
|
-
end
|
75
71
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
72
|
+
def owner_keys
|
73
|
+
unless defined?(@owner_keys)
|
74
|
+
@owner_keys = owners.map do |owner|
|
75
|
+
owner[owner_key_name]
|
76
|
+
end
|
77
|
+
@owner_keys.uniq!
|
78
|
+
@owner_keys.compact!
|
80
79
|
end
|
81
|
-
@owner_keys
|
82
|
-
@owner_keys.compact!
|
80
|
+
@owner_keys
|
83
81
|
end
|
84
|
-
@owner_keys
|
85
|
-
end
|
86
82
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
83
|
+
def owners_by_key
|
84
|
+
unless defined?(@owners_by_key)
|
85
|
+
@owners_by_key = owners.each_with_object({}) do |owner, h|
|
86
|
+
h[convert_key(owner[owner_key_name])] = owner
|
87
|
+
end
|
91
88
|
end
|
89
|
+
@owners_by_key
|
92
90
|
end
|
93
|
-
@owners_by_key
|
94
|
-
end
|
95
91
|
|
96
|
-
|
97
|
-
|
98
|
-
|
92
|
+
def key_conversion_required?
|
93
|
+
unless defined?(@key_conversion_required)
|
94
|
+
@key_conversion_required = (association_key_type != owner_key_type)
|
95
|
+
end
|
99
96
|
|
100
|
-
|
101
|
-
if key_conversion_required?
|
102
|
-
key.to_s
|
103
|
-
else
|
104
|
-
key
|
97
|
+
@key_conversion_required
|
105
98
|
end
|
106
|
-
end
|
107
99
|
|
108
|
-
|
109
|
-
|
110
|
-
|
100
|
+
def convert_key(key)
|
101
|
+
if key_conversion_required?
|
102
|
+
key.to_s
|
103
|
+
else
|
104
|
+
key
|
105
|
+
end
|
106
|
+
end
|
111
107
|
|
112
|
-
|
113
|
-
|
114
|
-
|
108
|
+
def association_key_type
|
109
|
+
@klass.type_for_attribute(association_key_name.to_s).type
|
110
|
+
end
|
115
111
|
|
116
|
-
|
117
|
-
|
118
|
-
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
|
119
|
-
# Make several smaller queries if necessary or make one query if the adapter supports it
|
120
|
-
slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
121
|
-
@preloaded_records = slices.flat_map do |slice|
|
122
|
-
records_for(slice).load(&block)
|
112
|
+
def owner_key_type
|
113
|
+
@model.type_for_attribute(owner_key_name.to_s).type
|
123
114
|
end
|
124
|
-
|
125
|
-
|
115
|
+
|
116
|
+
def load_records(&block)
|
117
|
+
return {} if owner_keys.empty?
|
118
|
+
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
|
119
|
+
# Make several smaller queries if necessary or make one query if the adapter supports it
|
120
|
+
slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
|
121
|
+
@preloaded_records = slices.flat_map do |slice|
|
122
|
+
records_for(slice).load(&block)
|
123
|
+
end
|
124
|
+
@preloaded_records.group_by do |record|
|
125
|
+
convert_key(record[association_key_name])
|
126
|
+
end
|
126
127
|
end
|
127
|
-
end
|
128
128
|
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
def reflection_scope
|
130
|
+
@reflection_scope ||= reflection.scope_for(klass)
|
131
|
+
end
|
132
132
|
|
133
|
-
|
134
|
-
|
133
|
+
def build_scope
|
134
|
+
scope = klass.scope_for_association
|
135
135
|
|
136
|
-
|
137
|
-
|
138
|
-
|
136
|
+
if reflection.type
|
137
|
+
scope.where!(reflection.type => model.base_class.sti_name)
|
138
|
+
end
|
139
139
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
140
|
+
scope.merge!(reflection_scope)
|
141
|
+
scope.merge!(preload_scope) if preload_scope != NULL_RELATION
|
142
|
+
scope
|
143
|
+
end
|
144
144
|
end
|
145
145
|
end
|
146
146
|
end
|
@@ -2,7 +2,6 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class Preloader
|
4
4
|
class BelongsTo < SingularAssociation #:nodoc:
|
5
|
-
|
6
5
|
def association_key_name
|
7
6
|
reflection.options[:primary_key] || klass && klass.primary_key
|
8
7
|
end
|
@@ -10,7 +9,6 @@ module ActiveRecord
|
|
10
9
|
def owner_key_name
|
11
10
|
reflection.foreign_key
|
12
11
|
end
|
13
|
-
|
14
12
|
end
|
15
13
|
end
|
16
14
|
end
|
@@ -4,13 +4,13 @@ module ActiveRecord
|
|
4
4
|
class CollectionAssociation < Association #:nodoc:
|
5
5
|
private
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
def preload(preloader)
|
8
|
+
associated_records_by_owner(preloader).each do |owner, records|
|
9
|
+
association = owner.association(reflection.name)
|
10
|
+
association.loaded!
|
11
|
+
association.target.concat(records)
|
12
|
+
end
|
12
13
|
end
|
13
|
-
end
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -2,7 +2,6 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class Preloader
|
4
4
|
class HasMany < CollectionAssociation #:nodoc:
|
5
|
-
|
6
5
|
def association_key_name
|
7
6
|
reflection.foreign_key
|
8
7
|
end
|
@@ -10,7 +9,6 @@ module ActiveRecord
|
|
10
9
|
def owner_key_name
|
11
10
|
reflection.active_record_primary_key
|
12
11
|
end
|
13
|
-
|
14
12
|
end
|
15
13
|
end
|
16
14
|
end
|
@@ -2,18 +2,16 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class Preloader
|
4
4
|
class SingularAssociation < Association #:nodoc:
|
5
|
-
|
6
5
|
private
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
def preload(preloader)
|
8
|
+
associated_records_by_owner(preloader).each do |owner, associated_records|
|
9
|
+
record = associated_records.first
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
association = owner.association(reflection.name)
|
12
|
+
association.target = record
|
13
|
+
end
|
14
14
|
end
|
15
|
-
end
|
16
|
-
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
|
25
25
|
reset_association owners, through_reflection.name
|
26
26
|
|
27
|
-
middle_records = through_records.flat_map { |(_,rec)| rec }
|
27
|
+
middle_records = through_records.flat_map { |(_, rec)| rec }
|
28
28
|
|
29
29
|
preloaders = preloader.preload(middle_records,
|
30
30
|
source_reflection.name,
|
@@ -32,13 +32,13 @@ module ActiveRecord
|
|
32
32
|
|
33
33
|
@preloaded_records = preloaders.flat_map(&:preloaded_records)
|
34
34
|
|
35
|
-
middle_to_pl = preloaders.each_with_object({}) do |pl,h|
|
35
|
+
middle_to_pl = preloaders.each_with_object({}) do |pl, h|
|
36
36
|
pl.owners.each { |middle|
|
37
37
|
h[middle] = pl
|
38
38
|
}
|
39
39
|
end
|
40
40
|
|
41
|
-
through_records.each_with_object({}) do |(lhs,center), records_by_owner|
|
41
|
+
through_records.each_with_object({}) do |(lhs, center), records_by_owner|
|
42
42
|
pl_to_middle = center.group_by { |record| middle_to_pl[record] }
|
43
43
|
|
44
44
|
records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
|
@@ -61,54 +61,54 @@ module ActiveRecord
|
|
61
61
|
|
62
62
|
private
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
def id_to_index_map(ids)
|
65
|
+
id_map = {}
|
66
|
+
ids.each_with_index { |id, index| id_map[id] = index }
|
67
|
+
id_map
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
def reset_association(owners, association_name)
|
71
|
+
should_reset = (through_scope != through_reflection.klass.unscoped) ||
|
72
|
+
(reflection.options[:source_type] && through_reflection.collection?)
|
73
73
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
74
|
+
# Don't cache the association - we would only be caching a subset
|
75
|
+
if should_reset
|
76
|
+
owners.each { |owner|
|
77
|
+
owner.association(association_name).reset
|
78
|
+
}
|
79
|
+
end
|
79
80
|
end
|
80
|
-
end
|
81
81
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
82
|
+
def through_scope
|
83
|
+
scope = through_reflection.klass.unscoped
|
84
|
+
values = reflection_scope.values
|
85
|
+
|
86
|
+
if options[:source_type]
|
87
|
+
scope.where! reflection.foreign_type => options[:source_type]
|
88
|
+
else
|
89
|
+
unless reflection_scope.where_clause.empty?
|
90
|
+
scope.includes_values = Array(values[:includes] || options[:source])
|
91
|
+
scope.where_clause = reflection_scope.where_clause
|
92
|
+
if joins = values[:joins]
|
93
|
+
scope.joins!(source_reflection.name => joins)
|
94
|
+
end
|
95
|
+
if left_outer_joins = values[:left_outer_joins]
|
96
|
+
scope.left_outer_joins!(source_reflection.name => left_outer_joins)
|
97
|
+
end
|
94
98
|
end
|
95
|
-
|
96
|
-
|
99
|
+
|
100
|
+
scope.references! values[:references]
|
101
|
+
if scope.eager_loading? && order_values = values[:order]
|
102
|
+
scope = scope.order(order_values)
|
97
103
|
end
|
98
104
|
end
|
99
105
|
|
100
|
-
scope
|
101
|
-
if scope.eager_loading? && order_values = values[:order]
|
102
|
-
scope = scope.order(order_values)
|
103
|
-
end
|
106
|
+
scope
|
104
107
|
end
|
105
108
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
def target_records_from_association(association)
|
110
|
-
association.loaded? ? association.target : association.reader
|
111
|
-
end
|
109
|
+
def target_records_from_association(association)
|
110
|
+
association.loaded? ? association.target : association.reader
|
111
|
+
end
|
112
112
|
end
|
113
113
|
end
|
114
114
|
end
|
@@ -42,16 +42,16 @@ module ActiveRecord
|
|
42
42
|
extend ActiveSupport::Autoload
|
43
43
|
|
44
44
|
eager_autoload do
|
45
|
-
autoload :Association,
|
46
|
-
autoload :SingularAssociation,
|
47
|
-
autoload :CollectionAssociation,
|
48
|
-
autoload :ThroughAssociation,
|
49
|
-
|
50
|
-
autoload :HasMany,
|
51
|
-
autoload :HasManyThrough,
|
52
|
-
autoload :HasOne,
|
53
|
-
autoload :HasOneThrough,
|
54
|
-
autoload :BelongsTo,
|
45
|
+
autoload :Association, "active_record/associations/preloader/association"
|
46
|
+
autoload :SingularAssociation, "active_record/associations/preloader/singular_association"
|
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"
|
55
55
|
end
|
56
56
|
|
57
57
|
NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
|
@@ -106,108 +106,108 @@ module ActiveRecord
|
|
106
106
|
|
107
107
|
private
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
109
|
+
# Loads all the given data into +records+ for the +association+.
|
110
|
+
def preloaders_on(association, records, scope)
|
111
|
+
case association
|
112
|
+
when Hash
|
113
|
+
preloaders_for_hash(association, records, scope)
|
114
|
+
when Symbol
|
115
|
+
preloaders_for_one(association, records, scope)
|
116
|
+
when String
|
117
|
+
preloaders_for_one(association.to_sym, records, scope)
|
118
|
+
else
|
119
|
+
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
120
|
+
end
|
120
121
|
end
|
121
|
-
end
|
122
122
|
|
123
|
-
|
124
|
-
|
125
|
-
|
123
|
+
def preloaders_for_hash(association, records, scope)
|
124
|
+
association.flat_map { |parent, child|
|
125
|
+
loaders = preloaders_for_one parent, records, scope
|
126
126
|
|
127
|
-
|
128
|
-
|
129
|
-
|
127
|
+
recs = loaders.flat_map(&:preloaded_records).uniq
|
128
|
+
loaders.concat Array.wrap(child).flat_map { |assoc|
|
129
|
+
preloaders_on assoc, recs, scope
|
130
|
+
}
|
131
|
+
loaders
|
130
132
|
}
|
131
|
-
|
132
|
-
}
|
133
|
-
end
|
133
|
+
end
|
134
134
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
135
|
+
# Loads all the given data into +records+ for a singular +association+.
|
136
|
+
#
|
137
|
+
# Functions by instantiating a preloader class such as Preloader::HasManyThrough and
|
138
|
+
# call the +run+ method for each passed in class in the +records+ argument.
|
139
|
+
#
|
140
|
+
# Not all records have the same class, so group then preload group on the reflection
|
141
|
+
# itself so that if various subclass share the same association then we do not split
|
142
|
+
# them unnecessarily
|
143
|
+
#
|
144
|
+
# Additionally, polymorphic belongs_to associations can have multiple associated
|
145
|
+
# classes, depending on the polymorphic_type field. So we group by the classes as
|
146
|
+
# well.
|
147
|
+
def preloaders_for_one(association, records, scope)
|
148
|
+
grouped_records(association, records).flat_map do |reflection, klasses|
|
149
|
+
klasses.map do |rhs_klass, rs|
|
150
|
+
loader = preloader_for(reflection, rs, rhs_klass).new(rhs_klass, rs, reflection, scope)
|
151
|
+
loader.run self
|
152
|
+
loader
|
153
|
+
end
|
153
154
|
end
|
154
155
|
end
|
155
|
-
end
|
156
156
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
157
|
+
def grouped_records(association, records)
|
158
|
+
h = {}
|
159
|
+
records.each do |record|
|
160
|
+
next unless record
|
161
|
+
assoc = record.association(association)
|
162
|
+
klasses = h[assoc.reflection] ||= {}
|
163
|
+
(klasses[assoc.klass] ||= []) << record
|
164
|
+
end
|
165
|
+
h
|
164
166
|
end
|
165
|
-
h
|
166
|
-
end
|
167
167
|
|
168
|
-
|
169
|
-
|
168
|
+
class AlreadyLoaded # :nodoc:
|
169
|
+
attr_reader :owners, :reflection
|
170
170
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
171
|
+
def initialize(klass, owners, reflection, preload_scope)
|
172
|
+
@owners = owners
|
173
|
+
@reflection = reflection
|
174
|
+
end
|
175
175
|
|
176
|
-
|
176
|
+
def run(preloader); end
|
177
177
|
|
178
|
-
|
179
|
-
|
178
|
+
def preloaded_records
|
179
|
+
owners.flat_map { |owner| owner.association(reflection.name).target }
|
180
|
+
end
|
180
181
|
end
|
181
|
-
end
|
182
182
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
183
|
+
class NullPreloader # :nodoc:
|
184
|
+
def self.new(klass, owners, reflection, preload_scope); self; end
|
185
|
+
def self.run(preloader); end
|
186
|
+
def self.preloaded_records; []; end
|
187
|
+
def self.owners; []; end
|
188
|
+
end
|
189
189
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
190
|
+
# Returns a class containing the logic needed to load preload the data
|
191
|
+
# and attach it to a relation. For example +Preloader::Association+ or
|
192
|
+
# +Preloader::HasManyThrough+. The class returned implements a `run` method
|
193
|
+
# that accepts a preloader.
|
194
|
+
def preloader_for(reflection, owners, rhs_klass)
|
195
|
+
return NullPreloader unless rhs_klass
|
196
196
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
197
|
+
if owners.first.association(reflection.name).loaded?
|
198
|
+
return AlreadyLoaded
|
199
|
+
end
|
200
|
+
reflection.check_preloadable!
|
201
|
+
|
202
|
+
case reflection.macro
|
203
|
+
when :has_many
|
204
|
+
reflection.options[:through] ? HasManyThrough : HasMany
|
205
|
+
when :has_one
|
206
|
+
reflection.options[:through] ? HasOneThrough : HasOne
|
207
|
+
when :belongs_to
|
208
|
+
BelongsTo
|
209
|
+
end
|
209
210
|
end
|
210
|
-
end
|
211
211
|
end
|
212
212
|
end
|
213
213
|
end
|
@@ -2,15 +2,8 @@ module ActiveRecord
|
|
2
2
|
module Associations
|
3
3
|
class SingularAssociation < Association #:nodoc:
|
4
4
|
# Implements the reader method, e.g. foo.bar for Foo.has_one :bar
|
5
|
-
def reader
|
6
|
-
if
|
7
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
8
|
-
Passing an argument to force an association to reload is now
|
9
|
-
deprecated and will be removed in Rails 5.1. Please call `reload_#{reflection.name}` instead.
|
10
|
-
MSG
|
11
|
-
|
12
|
-
klass.uncached { reload }
|
13
|
-
elsif !loaded? || stale_target?
|
5
|
+
def reader
|
6
|
+
if !loaded? || stale_target?
|
14
7
|
reload
|
15
8
|
end
|
16
9
|
|
@@ -22,14 +15,6 @@ module ActiveRecord
|
|
22
15
|
replace(record)
|
23
16
|
end
|
24
17
|
|
25
|
-
def create(attributes = {}, &block)
|
26
|
-
_create_record(attributes, &block)
|
27
|
-
end
|
28
|
-
|
29
|
-
def create!(attributes = {}, &block)
|
30
|
-
_create_record(attributes, true, &block)
|
31
|
-
end
|
32
|
-
|
33
18
|
def build(attributes = {})
|
34
19
|
record = build_record(attributes)
|
35
20
|
yield(record) if block_given?
|
@@ -50,8 +35,8 @@ module ActiveRecord
|
|
50
35
|
scope.scope_for_create.stringify_keys.except(klass.primary_key)
|
51
36
|
end
|
52
37
|
|
53
|
-
def
|
54
|
-
return scope.
|
38
|
+
def find_target
|
39
|
+
return scope.take if skip_statement_cache?
|
55
40
|
|
56
41
|
conn = klass.connection
|
57
42
|
sc = reflection.association_scope_cache(conn, owner) do
|
@@ -62,13 +47,11 @@ module ActiveRecord
|
|
62
47
|
end
|
63
48
|
|
64
49
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
65
|
-
sc.execute
|
66
|
-
end
|
67
|
-
|
68
|
-
def find_target
|
69
|
-
if record = get_records.first
|
50
|
+
sc.execute(binds, klass, conn) do |record|
|
70
51
|
set_inverse_instance record
|
71
|
-
end
|
52
|
+
end.first
|
53
|
+
rescue ::RangeError
|
54
|
+
nil
|
72
55
|
end
|
73
56
|
|
74
57
|
def replace(record)
|
@@ -2,10 +2,9 @@ module ActiveRecord
|
|
2
2
|
# = Active Record Through Association
|
3
3
|
module Associations
|
4
4
|
module ThroughAssociation #:nodoc:
|
5
|
+
delegate :source_reflection, :through_reflection, to: :reflection
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
protected
|
7
|
+
private
|
9
8
|
|
10
9
|
# We merge in these scopes for two reasons:
|
11
10
|
#
|
@@ -22,8 +21,6 @@ module ActiveRecord
|
|
22
21
|
scope
|
23
22
|
end
|
24
23
|
|
25
|
-
private
|
26
|
-
|
27
24
|
# Construct attributes for :through pointing to owner and associate. This is used by the
|
28
25
|
# methods which create and delete records on the association.
|
29
26
|
#
|