activerecord 5.1.5 → 5.2.8.1
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 +655 -608
- 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 +28 -9
- 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 +14 -5
- 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 +32 -216
- 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 +7 -6
- data/lib/active_record/autosave_association.rb +35 -19
- 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 +15 -1
- data/lib/active_record/collection_cache_key.rb +12 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +142 -42
- 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 +15 -32
- 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 +110 -173
- 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 +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 -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 +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/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 +234 -112
- 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 +66 -74
- 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 +82 -95
- 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 +51 -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 +5 -3
- 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 +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 +19 -24
- 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 +198 -49
- data/lib/active_record/query_cache.rb +12 -14
- data/lib/active_record/querying.rb +4 -2
- data/lib/active_record/railtie.rb +80 -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 +46 -20
- data/lib/active_record/relation/delegation.rb +45 -27
- data/lib/active_record/relation/finder_methods.rb +77 -78
- 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 +129 -100
- 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 +26 -15
- 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 +13 -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 +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.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 +36 -6
- 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 +26 -40
- 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 -114
- 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/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,14 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
|
-
# = Active Record Has Many Through Association
|
3
4
|
module Associations
|
5
|
+
# = Active Record Has Many Through Association
|
4
6
|
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
|
5
7
|
include ThroughAssociation
|
6
8
|
|
7
9
|
def initialize(owner, reflection)
|
8
10
|
super
|
9
|
-
|
10
|
-
@through_records = {}
|
11
|
-
@through_association = nil
|
11
|
+
@through_records = {}
|
12
12
|
end
|
13
13
|
|
14
14
|
def concat(*records)
|
@@ -48,11 +48,6 @@ module ActiveRecord
|
|
48
48
|
end
|
49
49
|
|
50
50
|
private
|
51
|
-
|
52
|
-
def through_association
|
53
|
-
@through_association ||= owner.association(through_reflection.name)
|
54
|
-
end
|
55
|
-
|
56
51
|
# The through record (built with build_record) is temporarily cached
|
57
52
|
# so that it may be reused if insert_record is subsequently called.
|
58
53
|
#
|
@@ -62,21 +57,14 @@ module ActiveRecord
|
|
62
57
|
@through_records[record.object_id] ||= begin
|
63
58
|
ensure_mutable
|
64
59
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
if options[:source_type]
|
69
|
-
through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
|
70
|
-
end
|
60
|
+
attributes = through_scope_attributes
|
61
|
+
attributes[source_reflection.name] = record
|
62
|
+
attributes[source_reflection.foreign_type] = options[:source_type] if options[:source_type]
|
71
63
|
|
72
|
-
|
64
|
+
through_association.build(attributes)
|
73
65
|
end
|
74
66
|
end
|
75
67
|
|
76
|
-
def options_for_through_record
|
77
|
-
[through_scope_attributes]
|
78
|
-
end
|
79
|
-
|
80
68
|
def through_scope_attributes
|
81
69
|
scope.where_values_hash(through_association.reflection.name.to_s).
|
82
70
|
except!(through_association.reflection.foreign_key,
|
@@ -95,7 +83,7 @@ module ActiveRecord
|
|
95
83
|
def build_record(attributes)
|
96
84
|
ensure_not_nested
|
97
85
|
|
98
|
-
record = super
|
86
|
+
record = super
|
99
87
|
|
100
88
|
inverse = source_reflection.inverse_of
|
101
89
|
if inverse
|
@@ -138,21 +126,15 @@ module ActiveRecord
|
|
138
126
|
|
139
127
|
scope = through_association.scope
|
140
128
|
scope.where! construct_join_attributes(*records)
|
129
|
+
scope = scope.where(through_scope_attributes)
|
141
130
|
|
142
131
|
case method
|
143
132
|
when :destroy
|
144
133
|
if scope.klass.primary_key
|
145
|
-
count = scope.destroy_all.
|
134
|
+
count = scope.destroy_all.count(&:destroyed?)
|
146
135
|
else
|
147
136
|
scope.each(&:_run_destroy_callbacks)
|
148
|
-
|
149
|
-
arel = scope.arel
|
150
|
-
|
151
|
-
stmt = Arel::DeleteManager.new
|
152
|
-
stmt.from scope.klass.arel_table
|
153
|
-
stmt.wheres = arel.constraints
|
154
|
-
|
155
|
-
count = scope.klass.connection.delete(stmt, "SQL", scope.bound_attributes)
|
137
|
+
count = scope.delete_all
|
156
138
|
end
|
157
139
|
when :nullify
|
158
140
|
count = scope.update_all(source_reflection.foreign_key => nil)
|
@@ -172,6 +154,30 @@ module ActiveRecord
|
|
172
154
|
else
|
173
155
|
update_counter(-count)
|
174
156
|
end
|
157
|
+
|
158
|
+
count
|
159
|
+
end
|
160
|
+
|
161
|
+
def difference(a, b)
|
162
|
+
distribution = distribution(b)
|
163
|
+
|
164
|
+
a.reject { |record| mark_occurrence(distribution, record) }
|
165
|
+
end
|
166
|
+
|
167
|
+
def intersection(a, b)
|
168
|
+
distribution = distribution(b)
|
169
|
+
|
170
|
+
a.select { |record| mark_occurrence(distribution, record) }
|
171
|
+
end
|
172
|
+
|
173
|
+
def mark_occurrence(distribution, record)
|
174
|
+
distribution[record] > 0 && distribution[record] -= 1
|
175
|
+
end
|
176
|
+
|
177
|
+
def distribution(array)
|
178
|
+
array.each_with_object(Hash.new(0)) do |record, distribution|
|
179
|
+
distribution[record] += 1
|
180
|
+
end
|
175
181
|
end
|
176
182
|
|
177
183
|
def through_records_for(record)
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
|
-
# = Active Record Has One Association
|
3
4
|
module Associations
|
5
|
+
# = Active Record Has One Association
|
4
6
|
class HasOneAssociation < SingularAssociation #:nodoc:
|
5
7
|
include ForeignAssociation
|
6
8
|
|
@@ -58,6 +60,7 @@ module ActiveRecord
|
|
58
60
|
when :destroy
|
59
61
|
target.destroyed_by_association = reflection
|
60
62
|
target.destroy
|
63
|
+
throw(:abort) unless target.destroyed?
|
61
64
|
when :nullify
|
62
65
|
target.update_columns(reflection.foreign_key => nil) if target.persisted?
|
63
66
|
end
|
@@ -104,6 +107,14 @@ module ActiveRecord
|
|
104
107
|
yield
|
105
108
|
end
|
106
109
|
end
|
110
|
+
|
111
|
+
def _create_record(attributes, raise_error = false, &block)
|
112
|
+
unless owner.persisted?
|
113
|
+
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
114
|
+
end
|
115
|
+
|
116
|
+
super
|
117
|
+
end
|
107
118
|
end
|
108
119
|
end
|
109
120
|
end
|
@@ -1,20 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
|
-
# = Active Record Has One Through Association
|
3
4
|
module Associations
|
5
|
+
# = Active Record Has One Through Association
|
4
6
|
class HasOneThroughAssociation < HasOneAssociation #:nodoc:
|
5
7
|
include ThroughAssociation
|
6
8
|
|
7
|
-
def replace(record)
|
8
|
-
create_through_record(record)
|
9
|
+
def replace(record, save = true)
|
10
|
+
create_through_record(record, save)
|
9
11
|
self.target = record
|
10
12
|
end
|
11
13
|
|
12
14
|
private
|
13
|
-
|
14
|
-
def create_through_record(record)
|
15
|
+
def create_through_record(record, save)
|
15
16
|
ensure_not_nested
|
16
17
|
|
17
|
-
through_proxy =
|
18
|
+
through_proxy = through_association
|
18
19
|
through_record = through_proxy.load_target
|
19
20
|
|
20
21
|
if through_record && !record
|
@@ -27,8 +28,12 @@ module ActiveRecord
|
|
27
28
|
end
|
28
29
|
|
29
30
|
if through_record
|
30
|
-
through_record.
|
31
|
-
|
31
|
+
if through_record.new_record?
|
32
|
+
through_record.assign_attributes(attributes)
|
33
|
+
else
|
34
|
+
through_record.update(attributes)
|
35
|
+
end
|
36
|
+
elsif owner.new_record? || !save
|
32
37
|
through_proxy.build(attributes)
|
33
38
|
else
|
34
39
|
through_proxy.create(attributes)
|
@@ -1,19 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_record/associations/join_dependency/join_part"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
class JoinDependency # :nodoc:
|
6
8
|
class JoinAssociation < JoinPart # :nodoc:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
attr_accessor :tables
|
9
|
+
attr_reader :reflection, :tables
|
10
|
+
attr_accessor :table
|
11
11
|
|
12
12
|
def initialize(reflection, children)
|
13
13
|
super(reflection.klass, children)
|
14
14
|
|
15
|
-
@reflection
|
16
|
-
@tables
|
15
|
+
@reflection = reflection
|
16
|
+
@tables = nil
|
17
17
|
end
|
18
18
|
|
19
19
|
def match?(other)
|
@@ -21,84 +21,60 @@ module ActiveRecord
|
|
21
21
|
super && reflection == other.reflection
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
|
27
|
-
joins = []
|
28
|
-
binds = []
|
29
|
-
tables = tables.reverse
|
24
|
+
def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
|
25
|
+
joins = []
|
30
26
|
|
31
27
|
# The chain starts with the target table, but we want to end with it here (makes
|
32
28
|
# more sense in this context), so we reverse
|
33
|
-
chain.reverse_each do |reflection|
|
34
|
-
table = tables
|
29
|
+
reflection.chain.reverse_each.with_index(1) do |reflection, i|
|
30
|
+
table = tables[-i]
|
35
31
|
klass = reflection.klass
|
36
32
|
|
37
|
-
|
38
|
-
key = join_keys.key
|
39
|
-
foreign_key = join_keys.foreign_key
|
40
|
-
|
41
|
-
constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
|
33
|
+
join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
|
42
34
|
|
43
|
-
|
35
|
+
arel = join_scope.arel(alias_tracker.aliases)
|
36
|
+
nodes = arel.constraints.first
|
44
37
|
|
45
|
-
|
46
|
-
|
47
|
-
constraint = constraint.and rel.arel.constraints
|
38
|
+
others, children = nodes.children.partition do |node|
|
39
|
+
!fetch_arel_attribute(node) { |attr| attr.relation.name == table.name }
|
48
40
|
end
|
41
|
+
nodes = table.create_and(children)
|
49
42
|
|
50
|
-
|
51
|
-
value = foreign_klass.base_class.name
|
52
|
-
column = klass.columns_hash[reflection.type.to_s]
|
43
|
+
joins << table.create_join(table, table.create_on(nodes), join_type)
|
53
44
|
|
54
|
-
|
55
|
-
|
45
|
+
unless others.empty?
|
46
|
+
joins.concat arel.join_sources
|
47
|
+
append_constraints(joins.last, others)
|
56
48
|
end
|
57
49
|
|
58
|
-
joins << table.create_join(table, table.create_on(constraint), join_type)
|
59
|
-
|
60
50
|
# The current table in this iteration becomes the foreign table in the next
|
61
51
|
foreign_table, foreign_klass = table, klass
|
62
52
|
end
|
63
53
|
|
64
|
-
|
54
|
+
joins
|
65
55
|
end
|
66
56
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
#
|
71
|
-
# class Physician < ActiveRecord::Base
|
72
|
-
# has_many :appointments
|
73
|
-
# end
|
74
|
-
#
|
75
|
-
# If I execute `Physician.joins(:appointments).to_a` then
|
76
|
-
# klass # => Physician
|
77
|
-
# table # => #<Arel::Table @name="appointments" ...>
|
78
|
-
# key # => physician_id
|
79
|
-
# foreign_table # => #<Arel::Table @name="physicians" ...>
|
80
|
-
# foreign_key # => id
|
81
|
-
#
|
82
|
-
def build_constraint(klass, table, key, foreign_table, foreign_key)
|
83
|
-
constraint = table[key].eq(foreign_table[foreign_key])
|
84
|
-
|
85
|
-
if klass.finder_needs_type_condition?
|
86
|
-
constraint = table.create_and([
|
87
|
-
constraint,
|
88
|
-
klass.send(:type_condition, table)
|
89
|
-
])
|
90
|
-
end
|
91
|
-
|
92
|
-
constraint
|
57
|
+
def tables=(tables)
|
58
|
+
@tables = tables
|
59
|
+
@table = tables.first
|
93
60
|
end
|
94
61
|
|
95
|
-
|
96
|
-
|
97
|
-
|
62
|
+
private
|
63
|
+
def fetch_arel_attribute(value)
|
64
|
+
case value
|
65
|
+
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
66
|
+
yield value.left.is_a?(Arel::Attributes::Attribute) ? value.left : value.right
|
67
|
+
end
|
68
|
+
end
|
98
69
|
|
99
|
-
|
100
|
-
|
101
|
-
|
70
|
+
def append_constraints(join, constraints)
|
71
|
+
if join.is_a?(Arel::Nodes::StringJoin)
|
72
|
+
join_string = table.create_and(constraints.unshift(join.left))
|
73
|
+
join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
|
74
|
+
else
|
75
|
+
join.right.expr.children.concat(constraints)
|
76
|
+
end
|
77
|
+
end
|
102
78
|
end
|
103
79
|
end
|
104
80
|
end
|
@@ -1,20 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_record/associations/join_dependency/join_part"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
class JoinDependency # :nodoc:
|
6
8
|
class JoinBase < JoinPart # :nodoc:
|
7
|
-
|
8
|
-
return true if self == other
|
9
|
-
super && base_klass == other.base_klass
|
10
|
-
end
|
9
|
+
attr_reader :table
|
11
10
|
|
12
|
-
def table
|
13
|
-
base_klass
|
11
|
+
def initialize(base_klass, table, children)
|
12
|
+
super(base_klass, children)
|
13
|
+
@table = table
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def match?(other)
|
17
|
+
return true if self == other
|
18
|
+
super && base_klass == other.base_klass
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class JoinDependency # :nodoc:
|
@@ -22,10 +24,6 @@ module ActiveRecord
|
|
22
24
|
@children = children
|
23
25
|
end
|
24
26
|
|
25
|
-
def name
|
26
|
-
reflection.name
|
27
|
-
end
|
28
|
-
|
29
27
|
def match?(other)
|
30
28
|
self.class == other.class
|
31
29
|
end
|
@@ -35,13 +33,15 @@ module ActiveRecord
|
|
35
33
|
children.each { |child| child.each(&block) }
|
36
34
|
end
|
37
35
|
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
def each_children(&block)
|
37
|
+
children.each do |child|
|
38
|
+
yield self, child
|
39
|
+
child.each_children(&block)
|
40
|
+
end
|
41
41
|
end
|
42
42
|
|
43
|
-
#
|
44
|
-
def
|
43
|
+
# An Arel::Table for the active_record
|
44
|
+
def table
|
45
45
|
raise NotImplementedError
|
46
46
|
end
|
47
47
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class JoinDependency # :nodoc:
|
@@ -33,20 +35,14 @@ module ActiveRecord
|
|
33
35
|
end
|
34
36
|
|
35
37
|
Table = Struct.new(:node, :columns) do # :nodoc:
|
36
|
-
def table
|
37
|
-
Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
|
38
|
-
end
|
39
|
-
|
40
38
|
def column_aliases
|
41
|
-
t = table
|
39
|
+
t = node.table
|
42
40
|
columns.map { |column| t[column.name].as Arel.sql column.alias }
|
43
41
|
end
|
44
42
|
end
|
45
43
|
Column = Struct.new(:name, :alias)
|
46
44
|
end
|
47
45
|
|
48
|
-
attr_reader :alias_tracker, :base_klass, :join_root
|
49
|
-
|
50
46
|
def self.make_tree(associations)
|
51
47
|
hash = {}
|
52
48
|
walk_tree associations, hash
|
@@ -71,69 +67,31 @@ module ActiveRecord
|
|
71
67
|
end
|
72
68
|
end
|
73
69
|
|
74
|
-
|
75
|
-
# associations is the list of associations which are joined using hash, symbol or array.
|
76
|
-
# joins is the list of all string join commands and arel nodes.
|
77
|
-
#
|
78
|
-
# Example :
|
79
|
-
#
|
80
|
-
# class Physician < ActiveRecord::Base
|
81
|
-
# has_many :appointments
|
82
|
-
# has_many :patients, through: :appointments
|
83
|
-
# end
|
84
|
-
#
|
85
|
-
# If I execute `@physician.patients.to_a` then
|
86
|
-
# base # => Physician
|
87
|
-
# associations # => []
|
88
|
-
# joins # => [#<Arel::Nodes::InnerJoin: ...]
|
89
|
-
#
|
90
|
-
# However if I execute `Physician.joins(:appointments).to_a` then
|
91
|
-
# base # => Physician
|
92
|
-
# associations # => [:appointments]
|
93
|
-
# joins # => []
|
94
|
-
#
|
95
|
-
def initialize(base, associations, joins, eager_loading: true)
|
96
|
-
@alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins)
|
97
|
-
@eager_loading = eager_loading
|
70
|
+
def initialize(base, table, associations)
|
98
71
|
tree = self.class.make_tree associations
|
99
|
-
@join_root = JoinBase.new
|
100
|
-
@join_root.children.each { |child| construct_tables! @join_root, child }
|
72
|
+
@join_root = JoinBase.new(base, table, build(tree, base))
|
101
73
|
end
|
102
74
|
|
103
75
|
def reflections
|
104
76
|
join_root.drop(1).map!(&:reflection)
|
105
77
|
end
|
106
78
|
|
107
|
-
def join_constraints(
|
108
|
-
|
79
|
+
def join_constraints(joins_to_add, join_type, alias_tracker)
|
80
|
+
@alias_tracker = alias_tracker
|
109
81
|
|
110
|
-
|
111
|
-
|
112
|
-
else
|
113
|
-
make_inner_joins join_root, child
|
114
|
-
end
|
115
|
-
}
|
82
|
+
construct_tables!(join_root)
|
83
|
+
joins = make_join_constraints(join_root, join_type)
|
116
84
|
|
117
|
-
joins.concat
|
85
|
+
joins.concat joins_to_add.flat_map { |oj|
|
86
|
+
construct_tables!(oj.join_root)
|
118
87
|
if join_root.match? oj.join_root
|
119
88
|
walk join_root, oj.join_root
|
120
89
|
else
|
121
|
-
oj.join_root
|
122
|
-
make_outer_joins oj.join_root, child
|
123
|
-
}
|
90
|
+
make_join_constraints(oj.join_root, join_type)
|
124
91
|
end
|
125
92
|
}
|
126
93
|
end
|
127
94
|
|
128
|
-
def aliases
|
129
|
-
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
130
|
-
columns = join_part.column_names.each_with_index.map { |column_name, j|
|
131
|
-
Aliases::Column.new column_name, "t#{i}_r#{j}"
|
132
|
-
}
|
133
|
-
Aliases::Table.new(join_part, columns)
|
134
|
-
}
|
135
|
-
end
|
136
|
-
|
137
95
|
def instantiate(result_set, &block)
|
138
96
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
139
97
|
|
@@ -165,37 +123,40 @@ module ActiveRecord
|
|
165
123
|
parents.values
|
166
124
|
end
|
167
125
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
chain = child.reflection.chain
|
172
|
-
foreign_table = parent.table
|
173
|
-
foreign_klass = parent.base_klass
|
174
|
-
child.join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
|
175
|
-
end
|
126
|
+
def apply_column_aliases(relation)
|
127
|
+
relation._select!(-> { aliases.columns })
|
128
|
+
end
|
176
129
|
|
177
|
-
|
178
|
-
|
179
|
-
join_type = Arel::Nodes::OuterJoin
|
180
|
-
info = make_constraints parent, child, tables, join_type
|
130
|
+
protected
|
131
|
+
attr_reader :alias_tracker, :join_root
|
181
132
|
|
182
|
-
|
133
|
+
private
|
134
|
+
def aliases
|
135
|
+
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
136
|
+
columns = join_part.column_names.each_with_index.map { |column_name, j|
|
137
|
+
Aliases::Column.new column_name, "t#{i}_r#{j}"
|
138
|
+
}
|
139
|
+
Aliases::Table.new(join_part, columns)
|
140
|
+
}
|
183
141
|
end
|
184
142
|
|
185
|
-
def
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
[info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
|
143
|
+
def construct_tables!(join_root)
|
144
|
+
join_root.each_children do |parent, child|
|
145
|
+
child.tables = table_aliases_for(parent, child)
|
146
|
+
end
|
191
147
|
end
|
192
148
|
|
193
|
-
def
|
194
|
-
|
195
|
-
|
196
|
-
|
149
|
+
def make_join_constraints(join_root, join_type)
|
150
|
+
join_root.children.flat_map do |child|
|
151
|
+
make_constraints(join_root, child, join_type)
|
152
|
+
end
|
153
|
+
end
|
197
154
|
|
198
|
-
|
155
|
+
def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
|
156
|
+
foreign_table = parent.table
|
157
|
+
foreign_klass = parent.base_klass
|
158
|
+
joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
|
159
|
+
joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
|
199
160
|
end
|
200
161
|
|
201
162
|
def table_aliases_for(parent, node)
|
@@ -208,15 +169,9 @@ module ActiveRecord
|
|
208
169
|
}
|
209
170
|
end
|
210
171
|
|
211
|
-
def construct_tables!(parent, node)
|
212
|
-
node.tables = table_aliases_for(parent, node)
|
213
|
-
node.children.each { |child| construct_tables! node, child }
|
214
|
-
end
|
215
|
-
|
216
172
|
def table_alias_for(reflection, parent, join)
|
217
173
|
name = "#{reflection.plural_name}_#{parent.table_name}"
|
218
|
-
|
219
|
-
name
|
174
|
+
join ? "#{name}_join" : name
|
220
175
|
end
|
221
176
|
|
222
177
|
def walk(left, right)
|
@@ -224,8 +179,8 @@ module ActiveRecord
|
|
224
179
|
[left.children.find { |node2| node1.match? node2 }, node1]
|
225
180
|
}.partition(&:first)
|
226
181
|
|
227
|
-
|
228
|
-
|
182
|
+
joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r) }
|
183
|
+
joins.concat missing.flat_map { |_, n| make_constraints(left, n) }
|
229
184
|
end
|
230
185
|
|
231
186
|
def find_reflection(klass, name)
|
@@ -240,12 +195,11 @@ module ActiveRecord
|
|
240
195
|
reflection.check_eager_loadable!
|
241
196
|
|
242
197
|
if reflection.polymorphic?
|
243
|
-
next unless @eager_loading
|
244
198
|
raise EagerLoadPolymorphicError.new(reflection)
|
245
199
|
end
|
246
200
|
|
247
|
-
JoinAssociation.new
|
248
|
-
end
|
201
|
+
JoinAssociation.new(reflection, build(right, reflection.klass))
|
202
|
+
end
|
249
203
|
end
|
250
204
|
|
251
205
|
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
@@ -269,18 +223,19 @@ module ActiveRecord
|
|
269
223
|
next
|
270
224
|
end
|
271
225
|
|
272
|
-
model = seen[ar_parent.object_id][node
|
226
|
+
model = seen[ar_parent.object_id][node][id]
|
273
227
|
|
274
228
|
if model
|
275
229
|
construct(model, node, row, rs, seen, model_cache, aliases)
|
276
230
|
else
|
277
231
|
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
|
278
232
|
|
279
|
-
if node.reflection.
|
233
|
+
if node.reflection.scope &&
|
234
|
+
node.reflection.scope_for(node.base_klass.unscoped).readonly_value
|
280
235
|
model.readonly!
|
281
236
|
end
|
282
237
|
|
283
|
-
seen[ar_parent.object_id][node
|
238
|
+
seen[ar_parent.object_id][node][id] = model
|
284
239
|
construct(model, node, row, rs, seen, model_cache, aliases)
|
285
240
|
end
|
286
241
|
end
|