activerecord 5.1.0 → 5.2.0.rc1
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 +410 -530
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- 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 +4 -2
- data/lib/active_record/associations/alias_tracker.rb +23 -32
- data/lib/active_record/associations/association.rb +20 -21
- data/lib/active_record/associations/association_scope.rb +49 -49
- data/lib/active_record/associations/belongs_to_association.rb +12 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +10 -6
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +50 -41
- data/lib/active_record/associations/collection_proxy.rb +22 -39
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +4 -2
- data/lib/active_record/associations/has_many_through_association.rb +12 -18
- data/lib/active_record/associations/has_one_association.rb +5 -1
- data/lib/active_record/associations/has_one_through_association.rb +8 -7
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -64
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
- data/lib/active_record/associations/join_dependency.rb +27 -44
- data/lib/active_record/associations/preloader/association.rb +53 -92
- data/lib/active_record/associations/preloader/through_association.rb +72 -73
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/singular_association.rb +14 -10
- data/lib/active_record/associations/through_association.rb +26 -11
- data/lib/active_record/associations.rb +68 -76
- data/lib/active_record/attribute_assignment.rb +2 -0
- 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 +24 -214
- data/lib/active_record/attribute_methods/primary_key.rb +10 -13
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +8 -2
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +22 -19
- data/lib/active_record/attribute_methods.rb +48 -12
- data/lib/active_record/attributes.rb +7 -6
- data/lib/active_record/autosave_association.rb +8 -11
- 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 +14 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -33
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +58 -3
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +165 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +118 -180
- data/lib/active_record/connection_adapters/column.rb +4 -2
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -17
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/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_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +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 +2 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -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 +269 -126
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +64 -85
- 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 +18 -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 +71 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +92 -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 +39 -60
- data/lib/active_record/counter_cache.rb +3 -2
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +17 -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 +9 -9
- 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 +8 -6
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +46 -4
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +74 -22
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +181 -137
- data/lib/active_record/model_schema.rb +73 -58
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +153 -18
- data/lib/active_record/query_cache.rb +17 -12
- data/lib/active_record/querying.rb +4 -2
- data/lib/active_record/railtie.rb +61 -3
- 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 +47 -37
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +131 -204
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +32 -17
- data/lib/active_record/relation/calculations.rb +58 -20
- data/lib/active_record/relation/delegation.rb +10 -29
- data/lib/active_record/relation/finder_methods.rb +74 -85
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +51 -20
- 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 +54 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/predicate_builder.rb +53 -78
- data/lib/active_record/relation/query_attribute.rb +9 -2
- data/lib/active_record/relation/query_methods.rb +101 -95
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -1
- data/lib/active_record/relation/where_clause.rb +65 -67
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +99 -202
- 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 +10 -7
- data/lib/active_record/scoping/named.rb +38 -12
- data/lib/active_record/scoping.rb +12 -10
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +22 -12
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +37 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +5 -5
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +9 -7
- 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 +25 -37
- 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 -113
- data/lib/active_record/attribute_set/builder.rb +0 -124
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/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 -33
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
# = Active Record Association Collection
|
@@ -30,7 +32,8 @@ module ActiveRecord
|
|
30
32
|
reload
|
31
33
|
end
|
32
34
|
|
33
|
-
CollectionProxy.create(klass, self)
|
35
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
36
|
+
@proxy.reset_scope
|
34
37
|
end
|
35
38
|
|
36
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -43,23 +46,25 @@ module ActiveRecord
|
|
43
46
|
if loaded?
|
44
47
|
target.pluck(reflection.association_primary_key)
|
45
48
|
else
|
46
|
-
@association_ids ||= (
|
47
|
-
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
|
48
|
-
scope.pluck(column)
|
49
|
-
)
|
49
|
+
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
53
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
54
54
|
def ids_writer(ids)
|
55
|
-
|
55
|
+
primary_key = reflection.association_primary_key
|
56
|
+
pk_type = klass.type_for_attribute(primary_key)
|
56
57
|
ids = Array(ids).reject(&:blank?)
|
57
58
|
ids.map! { |i| pk_type.cast(i) }
|
58
|
-
|
59
|
-
|
59
|
+
|
60
|
+
records = klass.where(primary_key => ids).index_by do |r|
|
61
|
+
r.public_send(primary_key)
|
60
62
|
end.values_at(*ids).compact
|
63
|
+
|
61
64
|
if records.size != ids.size
|
62
|
-
|
65
|
+
found_ids = records.map { |record| record.public_send(primary_key) }
|
66
|
+
not_found_ids = ids - found_ids
|
67
|
+
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
|
63
68
|
else
|
64
69
|
replace(records)
|
65
70
|
end
|
@@ -68,26 +73,29 @@ module ActiveRecord
|
|
68
73
|
def reset
|
69
74
|
super
|
70
75
|
@target = []
|
76
|
+
@association_ids = nil
|
71
77
|
end
|
72
78
|
|
73
79
|
def find(*args)
|
74
|
-
if
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
80
|
+
if options[:inverse_of] && loaded?
|
81
|
+
args_flatten = args.flatten
|
82
|
+
model = scope.klass
|
83
|
+
|
84
|
+
if args_flatten.blank?
|
85
|
+
error_message = "Couldn't find #{model.name} without an ID"
|
86
|
+
raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
|
87
|
+
end
|
88
|
+
|
89
|
+
result = find_by_scan(*args)
|
90
|
+
|
91
|
+
result_size = Array(result).size
|
92
|
+
if !result || result_size != args_flatten.size
|
93
|
+
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
88
94
|
else
|
89
|
-
|
95
|
+
result
|
90
96
|
end
|
97
|
+
else
|
98
|
+
scope.find(*args)
|
91
99
|
end
|
92
100
|
end
|
93
101
|
|
@@ -179,8 +187,6 @@ module ActiveRecord
|
|
179
187
|
# are actually removed from the database, that depends precisely on
|
180
188
|
# +delete_records+. They are in any case removed from the collection.
|
181
189
|
def delete(*records)
|
182
|
-
return if records.empty?
|
183
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
184
190
|
delete_or_destroy(records, options[:dependent])
|
185
191
|
end
|
186
192
|
|
@@ -190,8 +196,6 @@ module ActiveRecord
|
|
190
196
|
# Note that this method removes records from the database ignoring the
|
191
197
|
# +:dependent+ option.
|
192
198
|
def destroy(*records)
|
193
|
-
return if records.empty?
|
194
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
195
199
|
delete_or_destroy(records, :destroy)
|
196
200
|
end
|
197
201
|
|
@@ -299,18 +303,17 @@ module ActiveRecord
|
|
299
303
|
private
|
300
304
|
|
301
305
|
def find_target
|
302
|
-
|
306
|
+
scope = self.scope
|
307
|
+
return scope.to_a if skip_statement_cache?(scope)
|
303
308
|
|
304
309
|
conn = klass.connection
|
305
|
-
sc = reflection.association_scope_cache(conn, owner) do
|
306
|
-
|
307
|
-
|
308
|
-
target_scope.merge as.scope(self, conn)
|
309
|
-
}
|
310
|
+
sc = reflection.association_scope_cache(conn, owner) do |params|
|
311
|
+
as = AssociationScope.create { params.bind }
|
312
|
+
target_scope.merge!(as.scope(self))
|
310
313
|
end
|
311
314
|
|
312
315
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
313
|
-
sc.execute(binds,
|
316
|
+
sc.execute(binds, conn) do |record|
|
314
317
|
set_inverse_instance(record)
|
315
318
|
end
|
316
319
|
end
|
@@ -356,7 +359,10 @@ module ActiveRecord
|
|
356
359
|
transaction do
|
357
360
|
add_to_target(build_record(attributes)) do |record|
|
358
361
|
yield(record) if block_given?
|
359
|
-
insert_record(record, true, raise) {
|
362
|
+
insert_record(record, true, raise) {
|
363
|
+
@_was_loaded = loaded?
|
364
|
+
@association_ids = nil
|
365
|
+
}
|
360
366
|
end
|
361
367
|
end
|
362
368
|
end
|
@@ -371,11 +377,9 @@ module ActiveRecord
|
|
371
377
|
end
|
372
378
|
end
|
373
379
|
|
374
|
-
def create_scope
|
375
|
-
scope.scope_for_create.stringify_keys
|
376
|
-
end
|
377
|
-
|
378
380
|
def delete_or_destroy(records, method)
|
381
|
+
return if records.empty?
|
382
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
379
383
|
records = records.flatten
|
380
384
|
records.each { |record| raise_on_type_mismatch!(record) }
|
381
385
|
existing_records = records.reject(&:new_record?)
|
@@ -429,7 +433,12 @@ module ActiveRecord
|
|
429
433
|
records.each do |record|
|
430
434
|
raise_on_type_mismatch!(record)
|
431
435
|
add_to_target(record) do
|
432
|
-
|
436
|
+
unless owner.new_record?
|
437
|
+
result &&= insert_record(record, true, raise) {
|
438
|
+
@_was_loaded = loaded?
|
439
|
+
@association_ids = nil
|
440
|
+
}
|
441
|
+
end
|
433
442
|
end
|
434
443
|
end
|
435
444
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
# Association proxies in Active Record are middlemen between the object that
|
@@ -30,7 +32,10 @@ module ActiveRecord
|
|
30
32
|
class CollectionProxy < Relation
|
31
33
|
def initialize(klass, association) #:nodoc:
|
32
34
|
@association = association
|
33
|
-
super klass
|
35
|
+
super klass
|
36
|
+
|
37
|
+
extensions = association.extensions
|
38
|
+
extend(*extensions) if extensions.any?
|
34
39
|
end
|
35
40
|
|
36
41
|
def target
|
@@ -130,8 +135,9 @@ module ActiveRecord
|
|
130
135
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
131
136
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
132
137
|
# # ]
|
133
|
-
def find(*args
|
134
|
-
|
138
|
+
def find(*args)
|
139
|
+
return super if block_given?
|
140
|
+
@association.find(*args)
|
135
141
|
end
|
136
142
|
|
137
143
|
##
|
@@ -743,10 +749,6 @@ module ActiveRecord
|
|
743
749
|
# # ]
|
744
750
|
|
745
751
|
#--
|
746
|
-
def uniq
|
747
|
-
load_target.uniq
|
748
|
-
end
|
749
|
-
|
750
752
|
def calculate(operation, column_name)
|
751
753
|
null_scope? ? scope.calculate(operation, column_name) : super
|
752
754
|
end
|
@@ -986,6 +988,12 @@ module ActiveRecord
|
|
986
988
|
load_target == other
|
987
989
|
end
|
988
990
|
|
991
|
+
##
|
992
|
+
# :method: to_ary
|
993
|
+
#
|
994
|
+
# :call-seq:
|
995
|
+
# to_ary()
|
996
|
+
#
|
989
997
|
# Returns a new array of objects from the collection. If the collection
|
990
998
|
# hasn't been loaded, it fetches the records from the database.
|
991
999
|
#
|
@@ -1019,10 +1027,6 @@ module ActiveRecord
|
|
1019
1027
|
# # #<Pet id: 5, name: "Brain", person_id: 1>,
|
1020
1028
|
# # #<Pet id: 6, name: "Boss", person_id: 1>
|
1021
1029
|
# # ]
|
1022
|
-
def to_ary
|
1023
|
-
load_target.dup
|
1024
|
-
end
|
1025
|
-
alias_method :to_a, :to_ary
|
1026
1030
|
|
1027
1031
|
def records # :nodoc:
|
1028
1032
|
load_target
|
@@ -1070,7 +1074,6 @@ module ActiveRecord
|
|
1070
1074
|
end
|
1071
1075
|
|
1072
1076
|
# Reloads the collection from the database. Returns +self+.
|
1073
|
-
# Equivalent to <tt>collection(true)</tt>.
|
1074
1077
|
#
|
1075
1078
|
# class Person < ActiveRecord::Base
|
1076
1079
|
# has_many :pets
|
@@ -1084,13 +1087,9 @@ module ActiveRecord
|
|
1084
1087
|
#
|
1085
1088
|
# person.pets.reload # fetches pets from the database
|
1086
1089
|
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1087
|
-
#
|
1088
|
-
# person.pets(true) # fetches pets from the database
|
1089
|
-
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1090
1090
|
def reload
|
1091
|
-
@scope = nil
|
1092
1091
|
proxy_association.reload
|
1093
|
-
|
1092
|
+
reset_scope
|
1094
1093
|
end
|
1095
1094
|
|
1096
1095
|
# Unloads the association. Returns +self+.
|
@@ -1110,9 +1109,14 @@ module ActiveRecord
|
|
1110
1109
|
# person.pets # fetches pets from the database
|
1111
1110
|
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1112
1111
|
def reset
|
1113
|
-
@scope = nil
|
1114
1112
|
proxy_association.reset
|
1115
1113
|
proxy_association.reset_scope
|
1114
|
+
reset_scope
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
def reset_scope # :nodoc:
|
1118
|
+
@offsets = {}
|
1119
|
+
@scope = nil
|
1116
1120
|
self
|
1117
1121
|
end
|
1118
1122
|
|
@@ -1125,19 +1129,6 @@ module ActiveRecord
|
|
1125
1129
|
|
1126
1130
|
delegate(*delegate_methods, to: :scope)
|
1127
1131
|
|
1128
|
-
module DelegateExtending # :nodoc:
|
1129
|
-
private
|
1130
|
-
def method_missing(method, *args, &block)
|
1131
|
-
extending_values = association_scope.extending_values
|
1132
|
-
if extending_values.any? && (extending_values - self.class.included_modules).any?
|
1133
|
-
self.class.include(*extending_values)
|
1134
|
-
public_send(method, *args, &block)
|
1135
|
-
else
|
1136
|
-
super
|
1137
|
-
end
|
1138
|
-
end
|
1139
|
-
end
|
1140
|
-
|
1141
1132
|
private
|
1142
1133
|
|
1143
1134
|
def find_nth_with_limit(index, limit)
|
@@ -1158,17 +1149,9 @@ module ActiveRecord
|
|
1158
1149
|
@association.find_from_target?
|
1159
1150
|
end
|
1160
1151
|
|
1161
|
-
def association_scope
|
1162
|
-
@association.association_scope
|
1163
|
-
end
|
1164
|
-
|
1165
1152
|
def exec_queries
|
1166
1153
|
load_target
|
1167
1154
|
end
|
1168
|
-
|
1169
|
-
def respond_to_missing?(method, _)
|
1170
|
-
association_scope.respond_to?(method) || super
|
1171
|
-
end
|
1172
1155
|
end
|
1173
1156
|
end
|
1174
1157
|
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
|
-
# = Active Record Has Many Association
|
3
4
|
module Associations
|
5
|
+
# = Active Record Has Many Association
|
4
6
|
# This is the proxy that handles a has many association.
|
5
7
|
#
|
6
8
|
# If the association has a <tt>:through</tt> option further specialization
|
@@ -61,7 +63,7 @@ module ActiveRecord
|
|
61
63
|
count = if reflection.has_cached_counter?
|
62
64
|
owner._read_attribute(reflection.counter_cache_column).to_i
|
63
65
|
else
|
64
|
-
scope.count
|
66
|
+
scope.count(:all)
|
65
67
|
end
|
66
68
|
|
67
69
|
# If there's nothing in the database and @target has no new records
|
@@ -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
|
#
|
@@ -109,6 +104,11 @@ module ActiveRecord
|
|
109
104
|
record
|
110
105
|
end
|
111
106
|
|
107
|
+
def remove_records(existing_records, records, method)
|
108
|
+
super
|
109
|
+
delete_through_records(records)
|
110
|
+
end
|
111
|
+
|
112
112
|
def target_reflection_has_associated_record?
|
113
113
|
!(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
|
114
114
|
end
|
@@ -133,21 +133,15 @@ module ActiveRecord
|
|
133
133
|
|
134
134
|
scope = through_association.scope
|
135
135
|
scope.where! construct_join_attributes(*records)
|
136
|
+
scope = scope.where(through_scope_attributes)
|
136
137
|
|
137
138
|
case method
|
138
139
|
when :destroy
|
139
140
|
if scope.klass.primary_key
|
140
|
-
count = scope.destroy_all.
|
141
|
+
count = scope.destroy_all.count(&:destroyed?)
|
141
142
|
else
|
142
143
|
scope.each(&:_run_destroy_callbacks)
|
143
|
-
|
144
|
-
arel = scope.arel
|
145
|
-
|
146
|
-
stmt = Arel::DeleteManager.new
|
147
|
-
stmt.from scope.klass.arel_table
|
148
|
-
stmt.wheres = arel.constraints
|
149
|
-
|
150
|
-
count = scope.klass.connection.delete(stmt, "SQL", scope.bound_attributes)
|
144
|
+
count = scope.delete_all
|
151
145
|
end
|
152
146
|
when :nullify
|
153
147
|
count = scope.update_all(source_reflection.foreign_key => nil)
|
@@ -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
|
|
@@ -56,6 +58,7 @@ module ActiveRecord
|
|
56
58
|
when :delete
|
57
59
|
target.delete
|
58
60
|
when :destroy
|
61
|
+
target.destroyed_by_association = reflection
|
59
62
|
target.destroy
|
60
63
|
when :nullify
|
61
64
|
target.update_columns(reflection.foreign_key => nil) if target.persisted?
|
@@ -78,6 +81,7 @@ module ActiveRecord
|
|
78
81
|
when :delete
|
79
82
|
target.delete
|
80
83
|
when :destroy
|
84
|
+
target.destroyed_by_association = reflection
|
81
85
|
target.destroy
|
82
86
|
else
|
83
87
|
nullify_owner_attributes(target)
|
@@ -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
|
@@ -28,7 +29,7 @@ module ActiveRecord
|
|
28
29
|
|
29
30
|
if through_record
|
30
31
|
through_record.update(attributes)
|
31
|
-
elsif owner.new_record?
|
32
|
+
elsif owner.new_record? || !save
|
32
33
|
through_proxy.build(attributes)
|
33
34
|
else
|
34
35
|
through_proxy.create(attributes)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_record/associations/join_dependency/join_part"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -9,11 +11,12 @@ module ActiveRecord
|
|
9
11
|
|
10
12
|
attr_accessor :tables
|
11
13
|
|
12
|
-
def initialize(reflection, children)
|
14
|
+
def initialize(reflection, children, alias_tracker)
|
13
15
|
super(reflection.klass, children)
|
14
16
|
|
15
|
-
@
|
16
|
-
@
|
17
|
+
@alias_tracker = alias_tracker
|
18
|
+
@reflection = reflection
|
19
|
+
@tables = nil
|
17
20
|
end
|
18
21
|
|
19
22
|
def match?(other)
|
@@ -21,11 +24,8 @@ module ActiveRecord
|
|
21
24
|
super && reflection == other.reflection
|
22
25
|
end
|
23
26
|
|
24
|
-
JoinInformation = Struct.new :joins, :binds
|
25
|
-
|
26
27
|
def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
|
27
28
|
joins = []
|
28
|
-
binds = []
|
29
29
|
tables = tables.reverse
|
30
30
|
|
31
31
|
# The chain starts with the target table, but we want to end with it here (makes
|
@@ -34,79 +34,32 @@ module ActiveRecord
|
|
34
34
|
table = tables.shift
|
35
35
|
klass = reflection.klass
|
36
36
|
|
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)
|
42
|
-
|
43
|
-
predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
|
44
|
-
scope_chain_items = reflection.join_scopes(table, predicate_builder)
|
45
|
-
klass_scope = reflection.klass_join_scope(table, predicate_builder)
|
37
|
+
constraint = reflection.build_join_constraint(table, foreign_table)
|
46
38
|
|
47
|
-
|
48
|
-
|
49
|
-
rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
|
50
|
-
left.merge right
|
51
|
-
end
|
52
|
-
|
53
|
-
if rel && !rel.arel.constraints.empty?
|
54
|
-
binds += rel.bound_attributes
|
55
|
-
constraint = constraint.and rel.arel.constraints
|
56
|
-
end
|
39
|
+
joins << table.create_join(table, table.create_on(constraint), join_type)
|
57
40
|
|
58
|
-
|
59
|
-
|
60
|
-
column = klass.columns_hash[reflection.type.to_s]
|
41
|
+
join_scope = reflection.join_scope(table, foreign_klass)
|
42
|
+
arel = join_scope.arel(alias_tracker.aliases)
|
61
43
|
|
62
|
-
|
63
|
-
|
44
|
+
if arel.constraints.any?
|
45
|
+
joins.concat arel.join_sources
|
46
|
+
right = joins.last.right
|
47
|
+
right.expr = right.expr.and(arel.constraints)
|
64
48
|
end
|
65
49
|
|
66
|
-
joins << table.create_join(table, table.create_on(constraint), join_type)
|
67
|
-
|
68
50
|
# The current table in this iteration becomes the foreign table in the next
|
69
51
|
foreign_table, foreign_klass = table, klass
|
70
52
|
end
|
71
53
|
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
# Builds equality condition.
|
76
|
-
#
|
77
|
-
# Example:
|
78
|
-
#
|
79
|
-
# class Physician < ActiveRecord::Base
|
80
|
-
# has_many :appointments
|
81
|
-
# end
|
82
|
-
#
|
83
|
-
# If I execute `Physician.joins(:appointments).to_a` then
|
84
|
-
# klass # => Physician
|
85
|
-
# table # => #<Arel::Table @name="appointments" ...>
|
86
|
-
# key # => physician_id
|
87
|
-
# foreign_table # => #<Arel::Table @name="physicians" ...>
|
88
|
-
# foreign_key # => id
|
89
|
-
#
|
90
|
-
def build_constraint(klass, table, key, foreign_table, foreign_key)
|
91
|
-
constraint = table[key].eq(foreign_table[foreign_key])
|
92
|
-
|
93
|
-
if klass.finder_needs_type_condition?
|
94
|
-
constraint = table.create_and([
|
95
|
-
constraint,
|
96
|
-
klass.send(:type_condition, table)
|
97
|
-
])
|
98
|
-
end
|
99
|
-
|
100
|
-
constraint
|
54
|
+
joins
|
101
55
|
end
|
102
56
|
|
103
57
|
def table
|
104
58
|
tables.first
|
105
59
|
end
|
106
60
|
|
107
|
-
|
108
|
-
|
109
|
-
end
|
61
|
+
protected
|
62
|
+
attr_reader :alias_tracker
|
110
63
|
end
|
111
64
|
end
|
112
65
|
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
|
@@ -40,11 +38,6 @@ module ActiveRecord
|
|
40
38
|
raise NotImplementedError
|
41
39
|
end
|
42
40
|
|
43
|
-
# The alias for the active_record's table
|
44
|
-
def aliased_table_name
|
45
|
-
raise NotImplementedError
|
46
|
-
end
|
47
|
-
|
48
41
|
def extract_record(row, column_names_with_alias)
|
49
42
|
# This code is performance critical as it is called per row.
|
50
43
|
# see: https://github.com/rails/rails/pull/12185
|