activerecord 3.2.19 → 5.0.0
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 +7 -0
- data/CHANGELOG.md +1715 -604
- data/MIT-LICENSE +2 -2
- data/README.rdoc +40 -45
- data/examples/performance.rb +33 -22
- data/examples/simple.rb +3 -4
- data/lib/active_record/aggregations.rb +76 -51
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +54 -40
- data/lib/active_record/associations/association.rb +76 -56
- data/lib/active_record/associations/association_scope.rb +125 -93
- data/lib/active_record/associations/belongs_to_association.rb +57 -28
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +120 -32
- data/lib/active_record/associations/builder/belongs_to.rb +115 -62
- data/lib/active_record/associations/builder/collection_association.rb +61 -53
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
- data/lib/active_record/associations/builder/has_many.rb +9 -65
- data/lib/active_record/associations/builder/has_one.rb +18 -52
- data/lib/active_record/associations/builder/singular_association.rb +18 -19
- data/lib/active_record/associations/collection_association.rb +268 -186
- data/lib/active_record/associations/collection_proxy.rb +1003 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +81 -41
- data/lib/active_record/associations/has_many_through_association.rb +76 -55
- data/lib/active_record/associations/has_one_association.rb +51 -21
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +239 -155
- data/lib/active_record/associations/preloader/association.rb +97 -62
- data/lib/active_record/associations/preloader/collection_association.rb +2 -8
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +75 -33
- data/lib/active_record/associations/preloader.rb +111 -79
- data/lib/active_record/associations/singular_association.rb +35 -13
- data/lib/active_record/associations/through_association.rb +41 -19
- data/lib/active_record/associations.rb +727 -501
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +213 -0
- data/lib/active_record/attribute_assignment.rb +32 -162
- data/lib/active_record/attribute_decorators.rb +67 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -61
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +7 -6
- data/lib/active_record/attribute_methods/read.rb +56 -117
- data/lib/active_record/attribute_methods/serialization.rb +43 -96
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
- data/lib/active_record/attribute_methods/write.rb +34 -45
- data/lib/active_record/attribute_methods.rb +333 -144
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +108 -0
- data/lib/active_record/attribute_set.rb +108 -0
- data/lib/active_record/attributes.rb +265 -0
- data/lib/active_record/autosave_association.rb +285 -223
- data/lib/active_record/base.rb +95 -490
- data/lib/active_record/callbacks.rb +95 -61
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +28 -19
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
- data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
- data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
- data/lib/active_record/connection_adapters/column.rb +30 -259
- data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
- data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +155 -0
- data/lib/active_record/core.rb +561 -0
- data/lib/active_record/counter_cache.rb +146 -105
- data/lib/active_record/dynamic_matchers.rb +101 -64
- data/lib/active_record/enum.rb +234 -0
- data/lib/active_record/errors.rb +153 -56
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +10 -6
- data/lib/active_record/fixture_set/file.rb +77 -0
- data/lib/active_record/fixtures.rb +355 -232
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +144 -79
- data/lib/active_record/integration.rb +66 -13
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +77 -56
- data/lib/active_record/locking/pessimistic.rb +6 -6
- data/lib/active_record/log_subscriber.rb +53 -28
- data/lib/active_record/migration/command_recorder.rb +166 -33
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +792 -264
- data/lib/active_record/model_schema.rb +192 -130
- data/lib/active_record/nested_attributes.rb +238 -145
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +89 -0
- data/lib/active_record/persistence.rb +357 -157
- data/lib/active_record/query_cache.rb +22 -43
- data/lib/active_record/querying.rb +34 -23
- data/lib/active_record/railtie.rb +88 -48
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +5 -4
- data/lib/active_record/railties/databases.rake +170 -422
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -5
- data/lib/active_record/reflection.rb +715 -189
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +203 -50
- data/lib/active_record/relation/calculations.rb +203 -194
- data/lib/active_record/relation/delegation.rb +103 -25
- data/lib/active_record/relation/finder_methods.rb +457 -261
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +167 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +153 -48
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +1019 -194
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +46 -150
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +450 -245
- data/lib/active_record/result.rb +104 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +120 -94
- data/lib/active_record/schema.rb +28 -18
- data/lib/active_record/schema_dumper.rb +141 -74
- data/lib/active_record/schema_migration.rb +50 -0
- data/lib/active_record/scoping/default.rb +64 -57
- data/lib/active_record/scoping/named.rb +93 -108
- data/lib/active_record/scoping.rb +73 -121
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +7 -5
- data/lib/active_record/statement_cache.rb +113 -0
- data/lib/active_record/store.rb +173 -15
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +313 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
- data/lib/active_record/timestamp.rb +42 -24
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +233 -105
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +7 -0
- data/lib/active_record/type/date_time.rb +7 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +63 -0
- data/lib/active_record/type/time.rb +20 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type.rb +72 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +33 -18
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +66 -0
- data/lib/active_record/validations/uniqueness.rb +128 -68
- data/lib/active_record/validations.rb +48 -40
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +71 -47
- data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
- data/lib/rails/generators/active_record/migration.rb +18 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +188 -134
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -6,6 +6,37 @@ module ActiveRecord
|
|
6
6
|
# If the association has a <tt>:through</tt> option further specialization
|
7
7
|
# is provided by its child HasManyThroughAssociation.
|
8
8
|
class HasManyAssociation < CollectionAssociation #:nodoc:
|
9
|
+
include ForeignAssociation
|
10
|
+
|
11
|
+
def handle_dependency
|
12
|
+
case options[:dependent]
|
13
|
+
when :restrict_with_exception
|
14
|
+
raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
|
15
|
+
|
16
|
+
when :restrict_with_error
|
17
|
+
unless empty?
|
18
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
19
|
+
message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.many', record: record, raise: true) rescue nil
|
20
|
+
if message
|
21
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
22
|
+
The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1.
|
23
|
+
Please use `:'restrict_dependent_destroy.has_many'` instead.
|
24
|
+
MESSAGE
|
25
|
+
end
|
26
|
+
owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
|
27
|
+
throw(:abort)
|
28
|
+
end
|
29
|
+
|
30
|
+
else
|
31
|
+
if options[:dependent] == :destroy
|
32
|
+
# No point in executing the counter update since we're going to destroy the parent anyway
|
33
|
+
load_target.each { |t| t.destroyed_by_association = reflection }
|
34
|
+
destroy_all
|
35
|
+
else
|
36
|
+
delete_all
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
9
40
|
|
10
41
|
def insert_record(record, validate = true, raise = false)
|
11
42
|
set_owner_attributes(record)
|
@@ -18,6 +49,14 @@ module ActiveRecord
|
|
18
49
|
end
|
19
50
|
end
|
20
51
|
|
52
|
+
def empty?
|
53
|
+
if reflection.has_cached_counter?
|
54
|
+
size.zero?
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
21
60
|
private
|
22
61
|
|
23
62
|
# Returns the number of records in this collection.
|
@@ -34,12 +73,10 @@ module ActiveRecord
|
|
34
73
|
# If the collection is empty the target is set to an empty array and
|
35
74
|
# the loaded flag is set to true as well.
|
36
75
|
def count_records
|
37
|
-
count = if has_cached_counter?
|
38
|
-
owner.
|
39
|
-
elsif options[:counter_sql] || options[:finder_sql]
|
40
|
-
reflection.klass.count_by_sql(custom_counter_sql)
|
76
|
+
count = if reflection.has_cached_counter?
|
77
|
+
owner._read_attribute reflection.counter_cache_column
|
41
78
|
else
|
42
|
-
|
79
|
+
scope.count
|
43
80
|
end
|
44
81
|
|
45
82
|
# If there's nothing in the database and @target has no new records
|
@@ -47,61 +84,64 @@ module ActiveRecord
|
|
47
84
|
# documented side-effect of the method that may avoid an extra SELECT.
|
48
85
|
@target ||= [] and loaded! if count == 0
|
49
86
|
|
50
|
-
[
|
87
|
+
[association_scope.limit_value, count].compact.min
|
51
88
|
end
|
52
89
|
|
53
|
-
def
|
54
|
-
|
90
|
+
def update_counter(difference, reflection = reflection())
|
91
|
+
if reflection.has_cached_counter?
|
92
|
+
owner.increment!(reflection.counter_cache_column, difference)
|
93
|
+
end
|
55
94
|
end
|
56
95
|
|
57
|
-
def
|
58
|
-
|
96
|
+
def update_counter_in_memory(difference, reflection = reflection())
|
97
|
+
if reflection.counter_must_be_updated_by_has_many?
|
98
|
+
counter = reflection.counter_cache_column
|
99
|
+
owner.increment(counter, difference)
|
100
|
+
owner.send(:clear_attribute_change, counter) # eww
|
101
|
+
end
|
59
102
|
end
|
60
103
|
|
61
|
-
def
|
62
|
-
if
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
owner.changed_attributes.delete(counter) # eww
|
104
|
+
def delete_count(method, scope)
|
105
|
+
if method == :delete_all
|
106
|
+
scope.delete_all
|
107
|
+
else
|
108
|
+
scope.update_all(reflection.foreign_key => nil)
|
67
109
|
end
|
68
110
|
end
|
69
111
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
74
|
-
# :counter_cache options which points back at our owner. So they update the
|
75
|
-
# counter cache.
|
76
|
-
# * In which case, we must make sure to *not* update the counter cache, or else
|
77
|
-
# it will be decremented twice.
|
78
|
-
#
|
79
|
-
# Hence this method.
|
80
|
-
def inverse_updates_counter_cache?(reflection = reflection)
|
81
|
-
counter_name = cached_counter_attribute_name(reflection)
|
82
|
-
reflection.klass.reflect_on_all_associations(:belongs_to).any? { |inverse_reflection|
|
83
|
-
inverse_reflection.counter_cache_column == counter_name
|
84
|
-
}
|
112
|
+
def delete_or_nullify_all_records(method)
|
113
|
+
count = delete_count(method, self.scope)
|
114
|
+
update_counter(-count)
|
85
115
|
end
|
86
116
|
|
87
117
|
# Deletes the records according to the <tt>:dependent</tt> option.
|
88
118
|
def delete_records(records, method)
|
89
119
|
if method == :destroy
|
90
|
-
records.each
|
91
|
-
update_counter(-records.length) unless inverse_updates_counter_cache?
|
120
|
+
records.each(&:destroy!)
|
121
|
+
update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
|
92
122
|
else
|
93
|
-
scope = self.
|
123
|
+
scope = self.scope.where(reflection.klass.primary_key => records)
|
124
|
+
update_counter(-delete_count(method, scope))
|
125
|
+
end
|
126
|
+
end
|
94
127
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
128
|
+
def concat_records(records, *)
|
129
|
+
update_counter_if_success(super, records.length)
|
130
|
+
end
|
131
|
+
|
132
|
+
def _create_record(attributes, *)
|
133
|
+
if attributes.is_a?(Array)
|
134
|
+
super
|
135
|
+
else
|
136
|
+
update_counter_if_success(super, 1)
|
100
137
|
end
|
101
138
|
end
|
102
139
|
|
103
|
-
def
|
104
|
-
|
140
|
+
def update_counter_if_success(saved_successfully, difference)
|
141
|
+
if saved_successfully
|
142
|
+
update_counter_in_memory(difference)
|
143
|
+
end
|
144
|
+
saved_successfully
|
105
145
|
end
|
106
146
|
end
|
107
147
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
# = Active Record Has Many Through Association
|
5
3
|
module Associations
|
@@ -13,90 +11,96 @@ module ActiveRecord
|
|
13
11
|
@through_association = nil
|
14
12
|
end
|
15
13
|
|
16
|
-
# Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been
|
17
|
-
# loaded and calling collection.size if it has. If it's more likely than not that the collection does
|
18
|
-
# have a size larger than zero, and you need to fetch that collection afterwards, it'll take one fewer
|
19
|
-
# SELECT query if you use #length.
|
20
|
-
def size
|
21
|
-
if has_cached_counter?
|
22
|
-
owner.send(:read_attribute, cached_counter_attribute_name)
|
23
|
-
elsif loaded?
|
24
|
-
target.size
|
25
|
-
else
|
26
|
-
count
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
14
|
def concat(*records)
|
31
15
|
unless owner.new_record?
|
32
16
|
records.flatten.each do |record|
|
33
|
-
raise_on_type_mismatch(record)
|
34
|
-
record.save! if record.new_record?
|
17
|
+
raise_on_type_mismatch!(record)
|
35
18
|
end
|
36
19
|
end
|
37
20
|
|
38
21
|
super
|
39
22
|
end
|
40
23
|
|
41
|
-
def
|
24
|
+
def concat_records(records)
|
42
25
|
ensure_not_nested
|
43
26
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
27
|
+
records = super(records, true)
|
28
|
+
|
29
|
+
if owner.new_record? && records
|
30
|
+
records.flatten.each do |record|
|
31
|
+
build_through_record(record)
|
49
32
|
end
|
50
33
|
end
|
51
34
|
|
35
|
+
records
|
36
|
+
end
|
37
|
+
|
38
|
+
def insert_record(record, validate = true, raise = false)
|
39
|
+
ensure_not_nested
|
40
|
+
|
41
|
+
if raise
|
42
|
+
record.save!(:validate => validate)
|
43
|
+
else
|
44
|
+
return unless record.save(:validate => validate)
|
45
|
+
end
|
46
|
+
|
52
47
|
save_through_record(record)
|
53
|
-
|
48
|
+
|
54
49
|
record
|
55
50
|
end
|
56
51
|
|
57
|
-
# ActiveRecord::Relation#delete_all needs to support joins before we can use a
|
58
|
-
# SQL-only implementation.
|
59
|
-
alias delete_all_on_destroy delete_all
|
60
|
-
|
61
52
|
private
|
62
53
|
|
63
54
|
def through_association
|
64
55
|
@through_association ||= owner.association(through_reflection.name)
|
65
56
|
end
|
66
57
|
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# want to use the exact same object.
|
58
|
+
# The through record (built with build_record) is temporarily cached
|
59
|
+
# so that it may be reused if insert_record is subsequently called.
|
70
60
|
#
|
71
|
-
# However, after insert_record has been called,
|
72
|
-
#
|
73
|
-
# association
|
61
|
+
# However, after insert_record has been called, the cache is cleared in
|
62
|
+
# order to allow multiple instances of the same record in an association.
|
74
63
|
def build_through_record(record)
|
75
64
|
@through_records[record.object_id] ||= begin
|
76
65
|
ensure_mutable
|
77
66
|
|
78
|
-
through_record = through_association.build
|
67
|
+
through_record = through_association.build(*options_for_through_record)
|
79
68
|
through_record.send("#{source_reflection.name}=", record)
|
69
|
+
|
70
|
+
if options[:source_type]
|
71
|
+
through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
|
72
|
+
end
|
73
|
+
|
80
74
|
through_record
|
81
75
|
end
|
82
76
|
end
|
83
77
|
|
78
|
+
def options_for_through_record
|
79
|
+
[through_scope_attributes]
|
80
|
+
end
|
81
|
+
|
82
|
+
def through_scope_attributes
|
83
|
+
scope.where_values_hash(through_association.reflection.name.to_s).
|
84
|
+
except!(through_association.reflection.foreign_key,
|
85
|
+
through_association.reflection.klass.inheritance_column)
|
86
|
+
end
|
87
|
+
|
84
88
|
def save_through_record(record)
|
85
89
|
build_through_record(record).save!
|
86
90
|
ensure
|
87
91
|
@through_records.delete(record.object_id)
|
88
92
|
end
|
89
93
|
|
90
|
-
def build_record(attributes
|
94
|
+
def build_record(attributes)
|
91
95
|
ensure_not_nested
|
92
96
|
|
93
|
-
record = super(attributes
|
97
|
+
record = super(attributes)
|
94
98
|
|
95
99
|
inverse = source_reflection.inverse_of
|
96
100
|
if inverse
|
97
|
-
if inverse.
|
101
|
+
if inverse.collection?
|
98
102
|
record.send(inverse.name) << build_through_record(record)
|
99
|
-
elsif inverse.
|
103
|
+
elsif inverse.has_one?
|
100
104
|
record.send("#{inverse.name}=", build_through_record(record))
|
101
105
|
end
|
102
106
|
end
|
@@ -105,17 +109,13 @@ module ActiveRecord
|
|
105
109
|
end
|
106
110
|
|
107
111
|
def target_reflection_has_associated_record?
|
108
|
-
|
109
|
-
false
|
110
|
-
else
|
111
|
-
true
|
112
|
-
end
|
112
|
+
!(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
|
113
113
|
end
|
114
114
|
|
115
115
|
def update_through_counter?(method)
|
116
116
|
case method
|
117
117
|
when :destroy
|
118
|
-
!inverse_updates_counter_cache?
|
118
|
+
!through_reflection.inverse_updates_counter_cache?
|
119
119
|
when :nullify
|
120
120
|
false
|
121
121
|
else
|
@@ -123,14 +123,31 @@ module ActiveRecord
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
+
def delete_or_nullify_all_records(method)
|
127
|
+
delete_records(load_target, method)
|
128
|
+
end
|
129
|
+
|
126
130
|
def delete_records(records, method)
|
127
131
|
ensure_not_nested
|
128
132
|
|
129
|
-
scope = through_association.
|
133
|
+
scope = through_association.scope
|
134
|
+
scope.where! construct_join_attributes(*records)
|
130
135
|
|
131
136
|
case method
|
132
137
|
when :destroy
|
133
|
-
|
138
|
+
if scope.klass.primary_key
|
139
|
+
count = scope.destroy_all.length
|
140
|
+
else
|
141
|
+
scope.each(&:_run_destroy_callbacks)
|
142
|
+
|
143
|
+
arel = scope.arel
|
144
|
+
|
145
|
+
stmt = Arel::DeleteManager.new
|
146
|
+
stmt.from scope.klass.arel_table
|
147
|
+
stmt.wheres = arel.constraints
|
148
|
+
|
149
|
+
count = scope.klass.connection.delete(stmt, 'SQL', scope.bound_attributes)
|
150
|
+
end
|
134
151
|
when :nullify
|
135
152
|
count = scope.update_all(source_reflection.foreign_key => nil)
|
136
153
|
else
|
@@ -139,29 +156,33 @@ module ActiveRecord
|
|
139
156
|
|
140
157
|
delete_through_records(records)
|
141
158
|
|
142
|
-
if source_reflection.options[:counter_cache]
|
159
|
+
if source_reflection.options[:counter_cache] && method != :destroy
|
143
160
|
counter = source_reflection.counter_cache_column
|
144
161
|
klass.decrement_counter counter, records.map(&:id)
|
145
162
|
end
|
146
163
|
|
147
|
-
if through_reflection.
|
164
|
+
if through_reflection.collection? && update_through_counter?(method)
|
148
165
|
update_counter(-count, through_reflection)
|
166
|
+
else
|
167
|
+
update_counter(-count)
|
149
168
|
end
|
150
|
-
|
151
|
-
update_counter(-count)
|
152
169
|
end
|
153
170
|
|
154
171
|
def through_records_for(record)
|
155
172
|
attributes = construct_join_attributes(record)
|
156
173
|
candidates = Array.wrap(through_association.target)
|
157
|
-
candidates.find_all
|
174
|
+
candidates.find_all do |c|
|
175
|
+
attributes.all? do |key, value|
|
176
|
+
c.public_send(key) == value
|
177
|
+
end
|
178
|
+
end
|
158
179
|
end
|
159
180
|
|
160
181
|
def delete_through_records(records)
|
161
182
|
records.each do |record|
|
162
183
|
through_records = through_records_for(record)
|
163
184
|
|
164
|
-
if through_reflection.
|
185
|
+
if through_reflection.collection?
|
165
186
|
through_records.each { |r| through_association.target.delete(r) }
|
166
187
|
else
|
167
188
|
if through_records.include?(through_association.target)
|
@@ -175,7 +196,7 @@ module ActiveRecord
|
|
175
196
|
|
176
197
|
def find_target
|
177
198
|
return [] unless target_reflection_has_associated_record?
|
178
|
-
|
199
|
+
get_records
|
179
200
|
end
|
180
201
|
|
181
202
|
# NOTE - not sure that we can actually cope with inverses here
|
@@ -1,24 +1,51 @@
|
|
1
|
-
require 'active_support/core_ext/object/inclusion'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
|
-
# = Active Record
|
2
|
+
# = Active Record Has One Association
|
5
3
|
module Associations
|
6
4
|
class HasOneAssociation < SingularAssociation #:nodoc:
|
5
|
+
include ForeignAssociation
|
6
|
+
|
7
|
+
def handle_dependency
|
8
|
+
case options[:dependent]
|
9
|
+
when :restrict_with_exception
|
10
|
+
raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target
|
11
|
+
|
12
|
+
when :restrict_with_error
|
13
|
+
if load_target
|
14
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
15
|
+
message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil
|
16
|
+
if message
|
17
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
18
|
+
The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
|
19
|
+
Please use `:'restrict_dependent_destroy.has_one'` instead.
|
20
|
+
MESSAGE
|
21
|
+
end
|
22
|
+
owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
|
23
|
+
throw(:abort)
|
24
|
+
end
|
25
|
+
|
26
|
+
else
|
27
|
+
delete
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
7
31
|
def replace(record, save = true)
|
8
|
-
raise_on_type_mismatch(record) if record
|
32
|
+
raise_on_type_mismatch!(record) if record
|
9
33
|
load_target
|
10
34
|
|
11
|
-
|
12
|
-
|
13
|
-
|
35
|
+
return self.target if !(target || record)
|
36
|
+
|
37
|
+
assigning_another_record = target != record
|
38
|
+
if assigning_another_record || record.changed?
|
39
|
+
save &&= owner.persisted?
|
40
|
+
|
14
41
|
transaction_if(save) do
|
15
|
-
remove_target!(options[:dependent]) if target && !target.destroyed?
|
16
|
-
|
42
|
+
remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
|
43
|
+
|
17
44
|
if record
|
18
45
|
set_owner_attributes(record)
|
19
46
|
set_inverse_instance(record)
|
20
|
-
|
21
|
-
if
|
47
|
+
|
48
|
+
if save && !record.save
|
22
49
|
nullify_owner_attributes(record)
|
23
50
|
set_owner_attributes(target) if target
|
24
51
|
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
|
@@ -38,7 +65,7 @@ module ActiveRecord
|
|
38
65
|
when :destroy
|
39
66
|
target.destroy
|
40
67
|
when :nullify
|
41
|
-
target.
|
68
|
+
target.update_columns(reflection.foreign_key => nil) if target.persisted?
|
42
69
|
end
|
43
70
|
end
|
44
71
|
end
|
@@ -54,16 +81,19 @@ module ActiveRecord
|
|
54
81
|
end
|
55
82
|
|
56
83
|
def remove_target!(method)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
84
|
+
case method
|
85
|
+
when :delete
|
86
|
+
target.delete
|
87
|
+
when :destroy
|
88
|
+
target.destroy
|
89
|
+
else
|
90
|
+
nullify_owner_attributes(target)
|
61
91
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
92
|
+
if target.persisted? && owner.persisted? && !target.save
|
93
|
+
set_owner_attributes(target)
|
94
|
+
raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
|
95
|
+
"The record failed to save after its foreign key was set to nil."
|
96
|
+
end
|
67
97
|
end
|
68
98
|
end
|
69
99
|
|