activerecord 4.2.9 → 5.2.8
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 +614 -1572
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +263 -249
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +77 -43
- data/lib/active_record/associations/association_scope.rb +106 -133
- data/lib/active_record/associations/belongs_to_association.rb +52 -41
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +9 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +139 -280
- data/lib/active_record/associations/collection_proxy.rb +231 -133
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_association.rb +34 -89
- data/lib/active_record/associations/has_many_through_association.rb +49 -76
- data/lib/active_record/associations/has_one_association.rb +38 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -89
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +133 -159
- data/lib/active_record/associations/preloader/association.rb +85 -120
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +81 -91
- data/lib/active_record/associations/singular_association.rb +27 -34
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1732 -1597
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
- data/lib/active_record/attribute_methods/dirty.rb +94 -135
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +30 -45
- data/lib/active_record/attribute_methods.rb +166 -109
- data/lib/active_record/attributes.rb +201 -82
- data/lib/active_record/autosave_association.rb +94 -36
- data/lib/active_record/base.rb +57 -44
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
- data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
- data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -188
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- 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 +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +178 -198
- data/lib/active_record/counter_cache.rb +79 -36
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +135 -88
- data/lib/active_record/errors.rb +179 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +188 -132
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +88 -96
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +581 -282
- data/lib/active_record/model_schema.rb +290 -111
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +7 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +347 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +94 -32
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +149 -156
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +414 -267
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +256 -248
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +288 -239
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +86 -86
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +116 -119
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +448 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +287 -340
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -16
- data/lib/active_record/scoping/default.rb +102 -85
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +134 -96
- data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +199 -124
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +40 -41
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +34 -22
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +72 -50
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
@@ -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
|
@@ -15,35 +17,27 @@ module ActiveRecord
|
|
15
17
|
|
16
18
|
when :restrict_with_error
|
17
19
|
unless empty?
|
18
|
-
record =
|
19
|
-
owner.errors.add(:base, :
|
20
|
-
|
20
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
21
|
+
owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)
|
22
|
+
throw(:abort)
|
21
23
|
end
|
22
24
|
|
25
|
+
when :destroy
|
26
|
+
# No point in executing the counter update since we're going to destroy the parent anyway
|
27
|
+
load_target.each { |t| t.destroyed_by_association = reflection }
|
28
|
+
destroy_all
|
23
29
|
else
|
24
|
-
|
25
|
-
# No point in executing the counter update since we're going to destroy the parent anyway
|
26
|
-
load_target.each { |t| t.destroyed_by_association = reflection }
|
27
|
-
destroy_all
|
28
|
-
else
|
29
|
-
delete_all
|
30
|
-
end
|
30
|
+
delete_all
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
def insert_record(record, validate = true, raise = false)
|
35
35
|
set_owner_attributes(record)
|
36
|
-
|
37
|
-
|
38
|
-
if raise
|
39
|
-
record.save!(:validate => validate)
|
40
|
-
else
|
41
|
-
record.save(:validate => validate)
|
42
|
-
end
|
36
|
+
super
|
43
37
|
end
|
44
38
|
|
45
39
|
def empty?
|
46
|
-
if has_cached_counter?
|
40
|
+
if reflection.has_cached_counter?
|
47
41
|
size.zero?
|
48
42
|
else
|
49
43
|
super
|
@@ -66,92 +60,34 @@ module ActiveRecord
|
|
66
60
|
# If the collection is empty the target is set to an empty array and
|
67
61
|
# the loaded flag is set to true as well.
|
68
62
|
def count_records
|
69
|
-
count = if has_cached_counter?
|
70
|
-
owner._read_attribute
|
63
|
+
count = if reflection.has_cached_counter?
|
64
|
+
owner._read_attribute(reflection.counter_cache_column).to_i
|
71
65
|
else
|
72
|
-
scope.count
|
66
|
+
scope.count(:all)
|
73
67
|
end
|
74
68
|
|
75
69
|
# If there's nothing in the database and @target has no new records
|
76
70
|
# we are certain the current target is an empty array. This is a
|
77
71
|
# documented side-effect of the method that may avoid an extra SELECT.
|
78
|
-
@target ||= []
|
72
|
+
(@target ||= []) && loaded! if count == 0
|
79
73
|
|
80
74
|
[association_scope.limit_value, count].compact.min
|
81
75
|
end
|
82
76
|
|
83
|
-
|
84
|
-
# Returns whether a counter cache should be used for this association.
|
85
|
-
#
|
86
|
-
# The counter_cache option must be given on either the owner or inverse
|
87
|
-
# association, and the column must be present on the owner.
|
88
|
-
def has_cached_counter?(reflection = reflection())
|
89
|
-
if reflection.options[:counter_cache] || (inverse = inverse_which_updates_counter_cache(reflection)) && inverse.options[:counter_cache]
|
90
|
-
owner.attribute_present?(cached_counter_attribute_name(reflection))
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def cached_counter_attribute_name(reflection = reflection())
|
95
|
-
if reflection.options[:counter_cache]
|
96
|
-
reflection.options[:counter_cache].to_s
|
97
|
-
else
|
98
|
-
"#{reflection.name}_count"
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
77
|
def update_counter(difference, reflection = reflection())
|
103
|
-
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
def update_counter_in_database(difference, reflection = reflection())
|
108
|
-
if has_cached_counter?(reflection)
|
109
|
-
counter = cached_counter_attribute_name(reflection)
|
110
|
-
owner.class.update_counters(owner.id, counter => difference)
|
78
|
+
if reflection.has_cached_counter?
|
79
|
+
owner.increment!(reflection.counter_cache_column, difference)
|
111
80
|
end
|
112
81
|
end
|
113
82
|
|
114
83
|
def update_counter_in_memory(difference, reflection = reflection())
|
115
|
-
if counter_must_be_updated_by_has_many?
|
116
|
-
counter =
|
117
|
-
owner
|
118
|
-
owner.send(:
|
84
|
+
if reflection.counter_must_be_updated_by_has_many?
|
85
|
+
counter = reflection.counter_cache_column
|
86
|
+
owner.increment(counter, difference)
|
87
|
+
owner.send(:clear_attribute_change, counter) # eww
|
119
88
|
end
|
120
89
|
end
|
121
90
|
|
122
|
-
# This shit is nasty. We need to avoid the following situation:
|
123
|
-
#
|
124
|
-
# * An associated record is deleted via record.destroy
|
125
|
-
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
126
|
-
# :counter_cache options which points back at our owner. So they update the
|
127
|
-
# counter cache.
|
128
|
-
# * In which case, we must make sure to *not* update the counter cache, or else
|
129
|
-
# it will be decremented twice.
|
130
|
-
#
|
131
|
-
# Hence this method.
|
132
|
-
def inverse_which_updates_counter_cache(reflection = reflection())
|
133
|
-
counter_name = cached_counter_attribute_name(reflection)
|
134
|
-
inverse_which_updates_counter_named(counter_name, reflection)
|
135
|
-
end
|
136
|
-
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
137
|
-
|
138
|
-
def inverse_which_updates_counter_named(counter_name, reflection)
|
139
|
-
reflection.klass._reflections.values.find { |inverse_reflection|
|
140
|
-
inverse_reflection.belongs_to? &&
|
141
|
-
inverse_reflection.counter_cache_column == counter_name
|
142
|
-
}
|
143
|
-
end
|
144
|
-
alias inverse_updates_counter_named? inverse_which_updates_counter_named
|
145
|
-
|
146
|
-
def inverse_updates_counter_in_memory?(reflection)
|
147
|
-
inverse = inverse_which_updates_counter_cache(reflection)
|
148
|
-
inverse && inverse == reflection.inverse_of
|
149
|
-
end
|
150
|
-
|
151
|
-
def counter_must_be_updated_by_has_many?(reflection)
|
152
|
-
!inverse_updates_counter_in_memory?(reflection) && has_cached_counter?(reflection)
|
153
|
-
end
|
154
|
-
|
155
91
|
def delete_count(method, scope)
|
156
92
|
if method == :delete_all
|
157
93
|
scope.delete_all
|
@@ -161,15 +97,16 @@ module ActiveRecord
|
|
161
97
|
end
|
162
98
|
|
163
99
|
def delete_or_nullify_all_records(method)
|
164
|
-
count = delete_count(method,
|
100
|
+
count = delete_count(method, scope)
|
165
101
|
update_counter(-count)
|
102
|
+
count
|
166
103
|
end
|
167
104
|
|
168
105
|
# Deletes the records according to the <tt>:dependent</tt> option.
|
169
106
|
def delete_records(records, method)
|
170
107
|
if method == :destroy
|
171
108
|
records.each(&:destroy!)
|
172
|
-
update_counter(-records.length) unless inverse_updates_counter_cache?
|
109
|
+
update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
|
173
110
|
else
|
174
111
|
scope = self.scope.where(reflection.klass.primary_key => records)
|
175
112
|
update_counter(-delete_count(method, scope))
|
@@ -194,6 +131,14 @@ module ActiveRecord
|
|
194
131
|
end
|
195
132
|
saved_successfully
|
196
133
|
end
|
134
|
+
|
135
|
+
def difference(a, b)
|
136
|
+
a - b
|
137
|
+
end
|
138
|
+
|
139
|
+
def intersection(a, b)
|
140
|
+
a & b
|
141
|
+
end
|
197
142
|
end
|
198
143
|
end
|
199
144
|
end
|
@@ -1,31 +1,14 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
# = Active Record Has Many Through Association
|
5
4
|
module Associations
|
5
|
+
# = Active Record Has Many Through Association
|
6
6
|
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
|
7
7
|
include ThroughAssociation
|
8
8
|
|
9
9
|
def initialize(owner, reflection)
|
10
10
|
super
|
11
|
-
|
12
|
-
@through_records = {}
|
13
|
-
@through_association = nil
|
14
|
-
end
|
15
|
-
|
16
|
-
# Returns the size of the collection by executing a SELECT COUNT(*) query
|
17
|
-
# if the collection hasn't been loaded, and by calling collection.size if
|
18
|
-
# it has. If the collection will likely have a size greater than zero,
|
19
|
-
# and if fetching the collection will be needed afterwards, one less
|
20
|
-
# SELECT query will be generated by using #length instead.
|
21
|
-
def size
|
22
|
-
if has_cached_counter?
|
23
|
-
owner._read_attribute cached_counter_attribute_name(reflection)
|
24
|
-
elsif loaded?
|
25
|
-
target.size
|
26
|
-
else
|
27
|
-
super
|
28
|
-
end
|
11
|
+
@through_records = {}
|
29
12
|
end
|
30
13
|
|
31
14
|
def concat(*records)
|
@@ -55,34 +38,16 @@ module ActiveRecord
|
|
55
38
|
def insert_record(record, validate = true, raise = false)
|
56
39
|
ensure_not_nested
|
57
40
|
|
58
|
-
if record.new_record?
|
59
|
-
|
60
|
-
record.save!(:validate => validate)
|
61
|
-
else
|
62
|
-
return unless record.save(:validate => validate)
|
63
|
-
end
|
41
|
+
if record.new_record? || record.has_changes_to_save?
|
42
|
+
return unless super
|
64
43
|
end
|
65
44
|
|
66
45
|
save_through_record(record)
|
67
|
-
if has_cached_counter? && !through_reflection_updates_counter_cache?
|
68
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
69
|
-
Automatic updating of counter caches on through associations has been
|
70
|
-
deprecated, and will be removed in Rails 5. Instead, please set the
|
71
|
-
appropriate `counter_cache` options on the `has_many` and `belongs_to`
|
72
|
-
for your associations to #{through_reflection.name}.
|
73
|
-
MSG
|
74
46
|
|
75
|
-
update_counter_in_database(1)
|
76
|
-
end
|
77
47
|
record
|
78
48
|
end
|
79
49
|
|
80
50
|
private
|
81
|
-
|
82
|
-
def through_association
|
83
|
-
@through_association ||= owner.association(through_reflection.name)
|
84
|
-
end
|
85
|
-
|
86
51
|
# The through record (built with build_record) is temporarily cached
|
87
52
|
# so that it may be reused if insert_record is subsequently called.
|
88
53
|
#
|
@@ -92,21 +57,14 @@ module ActiveRecord
|
|
92
57
|
@through_records[record.object_id] ||= begin
|
93
58
|
ensure_mutable
|
94
59
|
|
95
|
-
|
96
|
-
|
60
|
+
attributes = through_scope_attributes
|
61
|
+
attributes[source_reflection.name] = record
|
62
|
+
attributes[source_reflection.foreign_type] = options[:source_type] if options[:source_type]
|
97
63
|
|
98
|
-
|
99
|
-
through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
|
100
|
-
end
|
101
|
-
|
102
|
-
through_record
|
64
|
+
through_association.build(attributes)
|
103
65
|
end
|
104
66
|
end
|
105
67
|
|
106
|
-
def options_for_through_record
|
107
|
-
[through_scope_attributes]
|
108
|
-
end
|
109
|
-
|
110
68
|
def through_scope_attributes
|
111
69
|
scope.where_values_hash(through_association.reflection.name.to_s).
|
112
70
|
except!(through_association.reflection.foreign_key,
|
@@ -114,7 +72,10 @@ module ActiveRecord
|
|
114
72
|
end
|
115
73
|
|
116
74
|
def save_through_record(record)
|
117
|
-
build_through_record(record)
|
75
|
+
association = build_through_record(record)
|
76
|
+
if association.changed?
|
77
|
+
association.save!
|
78
|
+
end
|
118
79
|
ensure
|
119
80
|
@through_records.delete(record.object_id)
|
120
81
|
end
|
@@ -122,7 +83,7 @@ module ActiveRecord
|
|
122
83
|
def build_record(attributes)
|
123
84
|
ensure_not_nested
|
124
85
|
|
125
|
-
record = super
|
86
|
+
record = super
|
126
87
|
|
127
88
|
inverse = source_reflection.inverse_of
|
128
89
|
if inverse
|
@@ -136,6 +97,11 @@ module ActiveRecord
|
|
136
97
|
record
|
137
98
|
end
|
138
99
|
|
100
|
+
def remove_records(existing_records, records, method)
|
101
|
+
super
|
102
|
+
delete_through_records(records)
|
103
|
+
end
|
104
|
+
|
139
105
|
def target_reflection_has_associated_record?
|
140
106
|
!(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
|
141
107
|
end
|
@@ -143,7 +109,7 @@ module ActiveRecord
|
|
143
109
|
def update_through_counter?(method)
|
144
110
|
case method
|
145
111
|
when :destroy
|
146
|
-
!inverse_updates_counter_cache?
|
112
|
+
!through_reflection.inverse_updates_counter_cache?
|
147
113
|
when :nullify
|
148
114
|
false
|
149
115
|
else
|
@@ -160,23 +126,15 @@ module ActiveRecord
|
|
160
126
|
|
161
127
|
scope = through_association.scope
|
162
128
|
scope.where! construct_join_attributes(*records)
|
129
|
+
scope = scope.where(through_scope_attributes)
|
163
130
|
|
164
131
|
case method
|
165
132
|
when :destroy
|
166
133
|
if scope.klass.primary_key
|
167
|
-
count = scope.destroy_all.
|
134
|
+
count = scope.destroy_all.count(&:destroyed?)
|
168
135
|
else
|
169
|
-
scope.each
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
arel = scope.arel
|
174
|
-
|
175
|
-
stmt = Arel::DeleteManager.new arel.engine
|
176
|
-
stmt.from scope.klass.arel_table
|
177
|
-
stmt.wheres = arel.constraints
|
178
|
-
|
179
|
-
count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values)
|
136
|
+
scope.each(&:_run_destroy_callbacks)
|
137
|
+
count = scope.delete_all
|
180
138
|
end
|
181
139
|
when :nullify
|
182
140
|
count = scope.update_all(source_reflection.foreign_key => nil)
|
@@ -196,6 +154,30 @@ module ActiveRecord
|
|
196
154
|
else
|
197
155
|
update_counter(-count)
|
198
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
|
199
181
|
end
|
200
182
|
|
201
183
|
def through_records_for(record)
|
@@ -226,22 +208,13 @@ module ActiveRecord
|
|
226
208
|
|
227
209
|
def find_target
|
228
210
|
return [] unless target_reflection_has_associated_record?
|
229
|
-
|
211
|
+
super
|
230
212
|
end
|
231
213
|
|
232
214
|
# NOTE - not sure that we can actually cope with inverses here
|
233
215
|
def invertible_for?(record)
|
234
216
|
false
|
235
217
|
end
|
236
|
-
|
237
|
-
def has_cached_counter?(reflection = reflection())
|
238
|
-
owner.attribute_present?(cached_counter_attribute_name(reflection))
|
239
|
-
end
|
240
|
-
|
241
|
-
def through_reflection_updates_counter_cache?
|
242
|
-
counter_name = cached_counter_attribute_name
|
243
|
-
inverse_updates_counter_named?(counter_name, through_reflection)
|
244
|
-
end
|
245
218
|
end
|
246
219
|
end
|
247
220
|
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
|
-
# = Active Record Belongs To 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
|
|
@@ -11,9 +13,9 @@ module ActiveRecord
|
|
11
13
|
|
12
14
|
when :restrict_with_error
|
13
15
|
if load_target
|
14
|
-
record =
|
15
|
-
owner.errors.add(:base, :
|
16
|
-
|
16
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
17
|
+
owner.errors.add(:base, :'restrict_dependent_destroy.has_one', record: record)
|
18
|
+
throw(:abort)
|
17
19
|
end
|
18
20
|
|
19
21
|
else
|
@@ -25,10 +27,10 @@ module ActiveRecord
|
|
25
27
|
raise_on_type_mismatch!(record) if record
|
26
28
|
load_target
|
27
29
|
|
28
|
-
return
|
30
|
+
return target unless target || record
|
29
31
|
|
30
32
|
assigning_another_record = target != record
|
31
|
-
if assigning_another_record || record.
|
33
|
+
if assigning_another_record || record.has_changes_to_save?
|
32
34
|
save &&= owner.persisted?
|
33
35
|
|
34
36
|
transaction_if(save) do
|
@@ -53,12 +55,14 @@ module ActiveRecord
|
|
53
55
|
def delete(method = options[:dependent])
|
54
56
|
if load_target
|
55
57
|
case method
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
when :delete
|
59
|
+
target.delete
|
60
|
+
when :destroy
|
61
|
+
target.destroyed_by_association = reflection
|
62
|
+
target.destroy
|
63
|
+
throw(:abort) unless target.destroyed?
|
64
|
+
when :nullify
|
65
|
+
target.update_columns(reflection.foreign_key => nil) if target.persisted?
|
62
66
|
end
|
63
67
|
end
|
64
68
|
end
|
@@ -75,18 +79,20 @@ module ActiveRecord
|
|
75
79
|
|
76
80
|
def remove_target!(method)
|
77
81
|
case method
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
82
|
+
when :delete
|
83
|
+
target.delete
|
84
|
+
when :destroy
|
85
|
+
target.destroyed_by_association = reflection
|
86
|
+
target.destroy
|
87
|
+
else
|
88
|
+
nullify_owner_attributes(target)
|
89
|
+
remove_inverse_instance(target)
|
90
|
+
|
91
|
+
if target.persisted? && owner.persisted? && !target.save
|
92
|
+
set_owner_attributes(target)
|
93
|
+
raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " \
|
94
|
+
"The record failed to save after its foreign key was set to nil."
|
95
|
+
end
|
90
96
|
end
|
91
97
|
end
|
92
98
|
|
@@ -101,6 +107,14 @@ module ActiveRecord
|
|
101
107
|
yield
|
102
108
|
end
|
103
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
|
104
118
|
end
|
105
119
|
end
|
106
120
|
end
|
@@ -1,30 +1,39 @@
|
|
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_record = through_proxy.
|
18
|
+
through_proxy = through_association
|
19
|
+
through_record = through_proxy.load_target
|
19
20
|
|
20
21
|
if through_record && !record
|
21
22
|
through_record.destroy
|
22
23
|
elsif record
|
23
24
|
attributes = construct_join_attributes(record)
|
24
25
|
|
26
|
+
if through_record && through_record.destroyed?
|
27
|
+
through_record = through_proxy.tap(&:reload).target
|
28
|
+
end
|
29
|
+
|
25
30
|
if through_record
|
26
|
-
through_record.
|
27
|
-
|
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
|
28
37
|
through_proxy.build(attributes)
|
29
38
|
else
|
30
39
|
through_proxy.create(attributes)
|