activerecord 4.2.0 → 5.2.8.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +640 -928
- 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 +264 -247
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +87 -41
- data/lib/active_record/associations/association_scope.rb +106 -132
- data/lib/active_record/associations/belongs_to_association.rb +55 -36
- 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 +14 -23
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
- 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 +145 -266
- data/lib/active_record/associations/collection_proxy.rb +242 -138
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +35 -75
- data/lib/active_record/associations/has_many_through_association.rb +51 -69
- data/lib/active_record/associations/has_one_association.rb +39 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
- 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 +134 -154
- data/lib/active_record/associations/preloader/association.rb +85 -116
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +83 -93
- data/lib/active_record/associations/singular_association.rb +27 -40
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1732 -1596
- 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 +12 -5
- data/lib/active_record/attribute_methods/dirty.rb +94 -125
- 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 +62 -36
- data/lib/active_record/attribute_methods/write.rb +31 -46
- data/lib/active_record/attribute_methods.rb +170 -117
- data/lib/active_record/attributes.rb +201 -74
- data/lib/active_record/autosave_association.rb +118 -45
- data/lib/active_record/base.rb +60 -48
- 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 +37 -13
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
- data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
- 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 +42 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- 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 +5 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
- 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 +466 -280
- 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 +439 -330
- 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 -324
- 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 +205 -202
- data/lib/active_record/counter_cache.rb +80 -37
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +136 -90
- data/lib/active_record/errors.rb +180 -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 +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +193 -135
- data/lib/active_record/gem_version.rb +5 -3
- 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 +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +92 -98
- 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 +594 -267
- data/lib/active_record/model_schema.rb +292 -111
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +8 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +350 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +117 -35
- 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 +160 -174
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +447 -288
- 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 +259 -244
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +290 -253
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +91 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
- 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 +118 -92
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +446 -389
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- 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 -339
- 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 -19
- data/lib/active_record/scoping/default.rb +102 -84
- 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 +136 -95
- data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
- 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 +208 -123
- 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 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- 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 +30 -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 +41 -32
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -21
- 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 -6
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
- 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.rb +7 -5
- metadata +77 -53
- 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 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record Autosave Association
|
3
5
|
#
|
4
|
-
#
|
6
|
+
# AutosaveAssociation is a module that takes care of automatically saving
|
5
7
|
# associated records when their parent is saved. In addition to saving, it
|
6
8
|
# also destroys any associated records that were marked for destruction.
|
7
|
-
# (See
|
9
|
+
# (See #mark_for_destruction and #marked_for_destruction?).
|
8
10
|
#
|
9
11
|
# Saving of the parent, its associations, and the destruction of marked
|
10
12
|
# associations, all happen inside a transaction. This should never leave the
|
@@ -22,7 +24,7 @@ module ActiveRecord
|
|
22
24
|
#
|
23
25
|
# == Validation
|
24
26
|
#
|
25
|
-
#
|
27
|
+
# Child records are validated unless <tt>:validate</tt> is +false+.
|
26
28
|
#
|
27
29
|
# == Callbacks
|
28
30
|
#
|
@@ -125,7 +127,6 @@ module ActiveRecord
|
|
125
127
|
# Now it _is_ removed from the database:
|
126
128
|
#
|
127
129
|
# Comment.find_by(id: id).nil? # => true
|
128
|
-
|
129
130
|
module AutosaveAssociation
|
130
131
|
extend ActiveSupport::Concern
|
131
132
|
|
@@ -141,9 +142,10 @@ module ActiveRecord
|
|
141
142
|
|
142
143
|
included do
|
143
144
|
Associations::Builder::Association.extensions << AssociationBuilderExtension
|
145
|
+
mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
|
144
146
|
end
|
145
147
|
|
146
|
-
module ClassMethods
|
148
|
+
module ClassMethods # :nodoc:
|
147
149
|
private
|
148
150
|
|
149
151
|
def define_non_cyclic_method(name, &block)
|
@@ -153,10 +155,10 @@ module ActiveRecord
|
|
153
155
|
# Loop prevention for validation of associations
|
154
156
|
unless @_already_called[name]
|
155
157
|
begin
|
156
|
-
@_already_called[name]=true
|
158
|
+
@_already_called[name] = true
|
157
159
|
result = instance_eval(&block)
|
158
160
|
ensure
|
159
|
-
@_already_called[name]=false
|
161
|
+
@_already_called[name] = false
|
160
162
|
end
|
161
163
|
end
|
162
164
|
|
@@ -177,11 +179,10 @@ module ActiveRecord
|
|
177
179
|
# before actually defining them.
|
178
180
|
def add_autosave_association_callbacks(reflection)
|
179
181
|
save_method = :"autosave_associated_records_for_#{reflection.name}"
|
180
|
-
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
181
|
-
collection = reflection.collection?
|
182
182
|
|
183
|
-
if collection
|
183
|
+
if reflection.collection?
|
184
184
|
before_save :before_save_collection_association
|
185
|
+
after_save :after_save_collection_association
|
185
186
|
|
186
187
|
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
187
188
|
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
@@ -200,14 +201,25 @@ module ActiveRecord
|
|
200
201
|
after_create save_method
|
201
202
|
after_update save_method
|
202
203
|
else
|
203
|
-
define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
|
204
|
+
define_non_cyclic_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
|
204
205
|
before_save save_method
|
205
206
|
end
|
206
207
|
|
208
|
+
define_autosave_validation_callbacks(reflection)
|
209
|
+
end
|
210
|
+
|
211
|
+
def define_autosave_validation_callbacks(reflection)
|
212
|
+
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
207
213
|
if reflection.validate? && !method_defined?(validation_method)
|
208
|
-
|
214
|
+
if reflection.collection?
|
215
|
+
method = :validate_collection_association
|
216
|
+
else
|
217
|
+
method = :validate_single_association
|
218
|
+
end
|
219
|
+
|
209
220
|
define_non_cyclic_method(validation_method) { send(method, reflection) }
|
210
221
|
validate validation_method
|
222
|
+
after_validation :_ensure_no_duplicate_errors
|
211
223
|
end
|
212
224
|
end
|
213
225
|
end
|
@@ -219,7 +231,7 @@ module ActiveRecord
|
|
219
231
|
super
|
220
232
|
end
|
221
233
|
|
222
|
-
# Marks this record to be destroyed as part of the
|
234
|
+
# Marks this record to be destroyed as part of the parent's save transaction.
|
223
235
|
# This does _not_ actually destroy the record instantly, rather child record will be destroyed
|
224
236
|
# when <tt>parent.save</tt> is called.
|
225
237
|
#
|
@@ -228,7 +240,7 @@ module ActiveRecord
|
|
228
240
|
@marked_for_destruction = true
|
229
241
|
end
|
230
242
|
|
231
|
-
# Returns whether or not this record will be destroyed as part of the
|
243
|
+
# Returns whether or not this record will be destroyed as part of the parent's save transaction.
|
232
244
|
#
|
233
245
|
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
|
234
246
|
def marked_for_destruction?
|
@@ -251,7 +263,7 @@ module ActiveRecord
|
|
251
263
|
# Returns whether or not this record has been changed in any way (including whether
|
252
264
|
# any of its nested autosave associations are likewise changed)
|
253
265
|
def changed_for_autosave?
|
254
|
-
new_record? ||
|
266
|
+
new_record? || has_changes_to_save? || marked_for_destruction? || nested_records_changed_for_autosave?
|
255
267
|
end
|
256
268
|
|
257
269
|
private
|
@@ -260,23 +272,30 @@ module ActiveRecord
|
|
260
272
|
# or saved. If +autosave+ is +false+ only new records will be returned,
|
261
273
|
# unless the parent is/was a new record itself.
|
262
274
|
def associated_records_to_validate_or_save(association, new_record, autosave)
|
263
|
-
if new_record
|
275
|
+
if new_record || custom_validation_context?
|
264
276
|
association && association.target
|
265
277
|
elsif autosave
|
266
|
-
association.target.find_all
|
278
|
+
association.target.find_all(&:changed_for_autosave?)
|
267
279
|
else
|
268
|
-
association.target.find_all
|
280
|
+
association.target.find_all(&:new_record?)
|
269
281
|
end
|
270
282
|
end
|
271
283
|
|
272
284
|
# go through nested autosave associations that are loaded in memory (without loading
|
273
285
|
# any new ones), and return true if is changed for autosave
|
274
286
|
def nested_records_changed_for_autosave?
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
287
|
+
@_nested_records_changed_for_autosave_already_called ||= false
|
288
|
+
return false if @_nested_records_changed_for_autosave_already_called
|
289
|
+
begin
|
290
|
+
@_nested_records_changed_for_autosave_already_called = true
|
291
|
+
self.class._reflections.values.any? do |reflection|
|
292
|
+
if reflection.options[:autosave]
|
293
|
+
association = association_instance_get(reflection.name)
|
294
|
+
association && Array.wrap(association.target).any?(&:changed_for_autosave?)
|
295
|
+
end
|
279
296
|
end
|
297
|
+
ensure
|
298
|
+
@_nested_records_changed_for_autosave_already_called = false
|
280
299
|
end
|
281
300
|
end
|
282
301
|
|
@@ -285,7 +304,7 @@ module ActiveRecord
|
|
285
304
|
def validate_single_association(reflection)
|
286
305
|
association = association_instance_get(reflection.name)
|
287
306
|
record = association && association.reader
|
288
|
-
association_valid?(reflection, record) if record
|
307
|
+
association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
|
289
308
|
end
|
290
309
|
|
291
310
|
# Validate the associated records if <tt>:validate</tt> or
|
@@ -294,7 +313,7 @@ module ActiveRecord
|
|
294
313
|
def validate_collection_association(reflection)
|
295
314
|
if association = association_instance_get(reflection.name)
|
296
315
|
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
|
297
|
-
records.
|
316
|
+
records.each_with_index { |record, index| association_valid?(reflection, record, index) }
|
298
317
|
end
|
299
318
|
end
|
300
319
|
end
|
@@ -302,17 +321,30 @@ module ActiveRecord
|
|
302
321
|
# Returns whether or not the association is valid and applies any errors to
|
303
322
|
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
|
304
323
|
# enabled records if they're marked_for_destruction? or destroyed.
|
305
|
-
def association_valid?(reflection, record)
|
306
|
-
return true if record.destroyed? || record.marked_for_destruction?
|
324
|
+
def association_valid?(reflection, record, index = nil)
|
325
|
+
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
|
307
326
|
|
308
|
-
|
309
|
-
|
327
|
+
context = validation_context if custom_validation_context?
|
328
|
+
|
329
|
+
unless valid = record.valid?(context)
|
310
330
|
if reflection.options[:autosave]
|
331
|
+
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
|
332
|
+
|
311
333
|
record.errors.each do |attribute, message|
|
312
|
-
attribute =
|
334
|
+
attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
|
313
335
|
errors[attribute] << message
|
314
336
|
errors[attribute].uniq!
|
315
337
|
end
|
338
|
+
|
339
|
+
record.errors.details.each_key do |attribute|
|
340
|
+
reflection_attribute =
|
341
|
+
normalize_reflection_attribute(indexed_attribute, reflection, index, attribute).to_sym
|
342
|
+
|
343
|
+
record.errors.details[attribute].each do |error|
|
344
|
+
errors.details[reflection_attribute] << error
|
345
|
+
errors.details[reflection_attribute].uniq!
|
346
|
+
end
|
347
|
+
end
|
316
348
|
else
|
317
349
|
errors.add(reflection.name)
|
318
350
|
end
|
@@ -320,18 +352,29 @@ module ActiveRecord
|
|
320
352
|
valid
|
321
353
|
end
|
322
354
|
|
355
|
+
def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
|
356
|
+
if indexed_attribute
|
357
|
+
"#{reflection.name}[#{index}].#{attribute}"
|
358
|
+
else
|
359
|
+
"#{reflection.name}.#{attribute}"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
323
363
|
# Is used as a before_save callback to check while saving a collection
|
324
364
|
# association whether or not the parent was a new record before saving.
|
325
365
|
def before_save_collection_association
|
326
366
|
@new_record_before_save = new_record?
|
327
|
-
|
367
|
+
end
|
368
|
+
|
369
|
+
def after_save_collection_association
|
370
|
+
@new_record_before_save = false
|
328
371
|
end
|
329
372
|
|
330
373
|
# Saves any new associated records, or all loaded autosave associations if
|
331
374
|
# <tt>:autosave</tt> is enabled on the association.
|
332
375
|
#
|
333
376
|
# In addition, it destroys all children that were marked for destruction
|
334
|
-
# with mark_for_destruction.
|
377
|
+
# with #mark_for_destruction.
|
335
378
|
#
|
336
379
|
# This all happens inside a transaction, _if_ the Transactions module is included into
|
337
380
|
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
@@ -339,7 +382,14 @@ module ActiveRecord
|
|
339
382
|
if association = association_instance_get(reflection.name)
|
340
383
|
autosave = reflection.options[:autosave]
|
341
384
|
|
342
|
-
|
385
|
+
# By saving the instance variable in a local variable,
|
386
|
+
# we make the whole callback re-entrant.
|
387
|
+
new_record_before_save = @new_record_before_save
|
388
|
+
|
389
|
+
# reconstruct the scope now that we know the owner's id
|
390
|
+
association.reset_scope
|
391
|
+
|
392
|
+
if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
|
343
393
|
if autosave
|
344
394
|
records_to_destroy = records.select(&:marked_for_destruction?)
|
345
395
|
records_to_destroy.each { |record| association.destroy(record) }
|
@@ -351,22 +401,24 @@ module ActiveRecord
|
|
351
401
|
|
352
402
|
saved = true
|
353
403
|
|
354
|
-
if autosave != false && (
|
404
|
+
if autosave != false && (new_record_before_save || record.new_record?)
|
355
405
|
if autosave
|
356
406
|
saved = association.insert_record(record, false)
|
357
|
-
|
358
|
-
association.insert_record(record)
|
407
|
+
elsif !reflection.nested?
|
408
|
+
association_saved = association.insert_record(record)
|
409
|
+
|
410
|
+
if reflection.validate?
|
411
|
+
errors.add(reflection.name) unless association_saved
|
412
|
+
saved = association_saved
|
413
|
+
end
|
359
414
|
end
|
360
415
|
elsif autosave
|
361
|
-
saved = record.save(:
|
416
|
+
saved = record.save(validate: false)
|
362
417
|
end
|
363
418
|
|
364
419
|
raise ActiveRecord::Rollback unless saved
|
365
420
|
end
|
366
421
|
end
|
367
|
-
|
368
|
-
# reconstruct the scope now that we know the owner's id
|
369
|
-
association.reset_scope if association.respond_to?(:reset_scope)
|
370
422
|
end
|
371
423
|
end
|
372
424
|
|
@@ -374,7 +426,7 @@ module ActiveRecord
|
|
374
426
|
# on the association.
|
375
427
|
#
|
376
428
|
# In addition, it will destroy the association if it was marked for
|
377
|
-
# destruction with mark_for_destruction.
|
429
|
+
# destruction with #mark_for_destruction.
|
378
430
|
#
|
379
431
|
# This all happens inside a transaction, _if_ the Transactions module is included into
|
380
432
|
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
@@ -393,9 +445,12 @@ module ActiveRecord
|
|
393
445
|
if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
|
394
446
|
unless reflection.through_reflection
|
395
447
|
record[reflection.foreign_key] = key
|
448
|
+
if inverse_reflection = reflection.inverse_of
|
449
|
+
record.association(inverse_reflection.name).loaded!
|
450
|
+
end
|
396
451
|
end
|
397
452
|
|
398
|
-
saved = record.save(:
|
453
|
+
saved = record.save(validate: !autosave)
|
399
454
|
raise ActiveRecord::Rollback if !saved && autosave
|
400
455
|
saved
|
401
456
|
end
|
@@ -406,8 +461,14 @@ module ActiveRecord
|
|
406
461
|
# If the record is new or it has changed, returns true.
|
407
462
|
def record_changed?(reflection, record, key)
|
408
463
|
record.new_record? ||
|
409
|
-
|
410
|
-
record.
|
464
|
+
association_foreign_key_changed?(reflection, record, key) ||
|
465
|
+
record.will_save_change_to_attribute?(reflection.foreign_key)
|
466
|
+
end
|
467
|
+
|
468
|
+
def association_foreign_key_changed?(reflection, record, key)
|
469
|
+
return false if reflection.through_reflection?
|
470
|
+
|
471
|
+
record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key
|
411
472
|
end
|
412
473
|
|
413
474
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
@@ -415,7 +476,9 @@ module ActiveRecord
|
|
415
476
|
# In addition, it will destroy the association if it was marked for destruction.
|
416
477
|
def save_belongs_to_association(reflection)
|
417
478
|
association = association_instance_get(reflection.name)
|
418
|
-
|
479
|
+
return unless association && association.loaded? && !association.stale_target?
|
480
|
+
|
481
|
+
record = association.load_target
|
419
482
|
if record && !record.destroyed?
|
420
483
|
autosave = reflection.options[:autosave]
|
421
484
|
|
@@ -423,7 +486,7 @@ module ActiveRecord
|
|
423
486
|
self[reflection.foreign_key] = nil
|
424
487
|
record.destroy
|
425
488
|
elsif autosave != false
|
426
|
-
saved = record.save(:
|
489
|
+
saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
|
427
490
|
|
428
491
|
if association.updated?
|
429
492
|
association_id = record.send(reflection.options[:primary_key] || :id)
|
@@ -435,5 +498,15 @@ module ActiveRecord
|
|
435
498
|
end
|
436
499
|
end
|
437
500
|
end
|
501
|
+
|
502
|
+
def custom_validation_context?
|
503
|
+
validation_context && [:create, :update].exclude?(validation_context)
|
504
|
+
end
|
505
|
+
|
506
|
+
def _ensure_no_duplicate_errors
|
507
|
+
errors.messages.each_key do |attribute|
|
508
|
+
errors[attribute].uniq!
|
509
|
+
end
|
510
|
+
end
|
438
511
|
end
|
439
512
|
end
|
data/lib/active_record/base.rb
CHANGED
@@ -1,27 +1,28 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
24
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "active_support/benchmarkable"
|
5
|
+
require "active_support/dependencies"
|
6
|
+
require "active_support/descendants_tracker"
|
7
|
+
require "active_support/time"
|
8
|
+
require "active_support/core_ext/module/attribute_accessors"
|
9
|
+
require "active_support/core_ext/array/extract_options"
|
10
|
+
require "active_support/core_ext/hash/deep_merge"
|
11
|
+
require "active_support/core_ext/hash/slice"
|
12
|
+
require "active_support/core_ext/hash/transform_values"
|
13
|
+
require "active_support/core_ext/string/behavior"
|
14
|
+
require "active_support/core_ext/kernel/singleton_class"
|
15
|
+
require "active_support/core_ext/module/introspection"
|
16
|
+
require "active_support/core_ext/object/duplicable"
|
17
|
+
require "active_support/core_ext/class/subclasses"
|
18
|
+
require "active_record/attribute_decorators"
|
19
|
+
require "active_record/define_callbacks"
|
20
|
+
require "active_record/errors"
|
21
|
+
require "active_record/log_subscriber"
|
22
|
+
require "active_record/explain_subscriber"
|
23
|
+
require "active_record/relation/delegation"
|
24
|
+
require "active_record/attributes"
|
25
|
+
require "active_record/type_caster"
|
25
26
|
|
26
27
|
module ActiveRecord #:nodoc:
|
27
28
|
# = Active Record
|
@@ -119,29 +120,25 @@ module ActiveRecord #:nodoc:
|
|
119
120
|
# All column values are automatically available through basic accessors on the Active Record
|
120
121
|
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
|
121
122
|
# the default accessors (using the same name as the attribute) and calling
|
122
|
-
#
|
123
|
-
# change things.
|
123
|
+
# +super+ to actually change things.
|
124
124
|
#
|
125
125
|
# class Song < ActiveRecord::Base
|
126
126
|
# # Uses an integer of seconds to hold the length of the song
|
127
127
|
#
|
128
128
|
# def length=(minutes)
|
129
|
-
#
|
129
|
+
# super(minutes.to_i * 60)
|
130
130
|
# end
|
131
131
|
#
|
132
132
|
# def length
|
133
|
-
#
|
133
|
+
# super / 60
|
134
134
|
# end
|
135
135
|
# end
|
136
136
|
#
|
137
|
-
# You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
|
138
|
-
# instead of <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
|
139
|
-
#
|
140
137
|
# == Attribute query methods
|
141
138
|
#
|
142
139
|
# In addition to the basic accessors, query methods are also automatically available on the Active Record object.
|
143
140
|
# Query methods allow you to test whether an attribute value is present.
|
144
|
-
#
|
141
|
+
# Additionally, when dealing with numeric values, a query method will return false if the value is zero.
|
145
142
|
#
|
146
143
|
# For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
|
147
144
|
# to determine whether the user has a name:
|
@@ -172,10 +169,11 @@ module ActiveRecord #:nodoc:
|
|
172
169
|
# <tt>Person.find_by_user_name(user_name)</tt>.
|
173
170
|
#
|
174
171
|
# It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an
|
175
|
-
#
|
172
|
+
# ActiveRecord::RecordNotFound error if they do not return any records,
|
176
173
|
# like <tt>Person.find_by_last_name!</tt>.
|
177
174
|
#
|
178
|
-
# It's also possible to use multiple attributes in the same
|
175
|
+
# It's also possible to use multiple attributes in the same <tt>find_by_</tt> by separating them with
|
176
|
+
# "_and_".
|
179
177
|
#
|
180
178
|
# Person.find_by(user_name: user_name, password: password)
|
181
179
|
# Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
|
@@ -187,7 +185,8 @@ module ActiveRecord #:nodoc:
|
|
187
185
|
# == Saving arrays, hashes, and other non-mappable objects in text columns
|
188
186
|
#
|
189
187
|
# Active Record can serialize any object in text columns using YAML. To do so, you must
|
190
|
-
# specify this with a call to the class method
|
188
|
+
# specify this with a call to the class method
|
189
|
+
# {serialize}[rdoc-ref:AttributeMethods::Serialization::ClassMethods#serialize].
|
191
190
|
# This makes it possible to store arrays, hashes, and other non-mappable objects without doing
|
192
191
|
# any additional work.
|
193
192
|
#
|
@@ -227,39 +226,47 @@ module ActiveRecord #:nodoc:
|
|
227
226
|
#
|
228
227
|
# == Connection to multiple databases in different models
|
229
228
|
#
|
230
|
-
# Connections are usually created through
|
229
|
+
# Connections are usually created through
|
230
|
+
# {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] and retrieved
|
231
231
|
# by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this
|
232
232
|
# connection. But you can also set a class-specific connection. For example, if Course is an
|
233
233
|
# ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
|
234
234
|
# and Course and all of its subclasses will use this connection instead.
|
235
235
|
#
|
236
236
|
# This feature is implemented by keeping a connection pool in ActiveRecord::Base that is
|
237
|
-
# a
|
237
|
+
# a hash indexed by the class. If a connection is requested, the
|
238
|
+
# {ActiveRecord::Base.retrieve_connection}[rdoc-ref:ConnectionHandling#retrieve_connection] method
|
238
239
|
# will go up the class-hierarchy until a connection is found in the connection pool.
|
239
240
|
#
|
240
241
|
# == Exceptions
|
241
242
|
#
|
242
243
|
# * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
|
243
|
-
# * AdapterNotSpecified - The configuration hash used in
|
244
|
-
#
|
245
|
-
#
|
246
|
-
#
|
244
|
+
# * AdapterNotSpecified - The configuration hash used in
|
245
|
+
# {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection]
|
246
|
+
# didn't include an <tt>:adapter</tt> key.
|
247
|
+
# * AdapterNotFound - The <tt>:adapter</tt> key used in
|
248
|
+
# {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection]
|
249
|
+
# specified a non-existent adapter
|
247
250
|
# (or a bad spelling of an existing one).
|
248
251
|
# * AssociationTypeMismatch - The object assigned to the association wasn't of the type
|
249
252
|
# specified in the association definition.
|
250
253
|
# * AttributeAssignmentError - An error occurred while doing a mass assignment through the
|
251
|
-
#
|
254
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
252
255
|
# You can inspect the +attribute+ property of the exception object to determine which attribute
|
253
256
|
# triggered the error.
|
254
|
-
# * ConnectionNotEstablished - No connection has been established.
|
255
|
-
# before querying.
|
257
|
+
# * ConnectionNotEstablished - No connection has been established.
|
258
|
+
# Use {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] before querying.
|
256
259
|
# * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
|
257
|
-
#
|
260
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
261
|
+
# The +errors+ property of this exception contains an array of
|
258
262
|
# AttributeAssignmentError
|
259
263
|
# objects that should be inspected to determine which attributes triggered the errors.
|
260
|
-
# * RecordInvalid - raised by save! and
|
261
|
-
#
|
262
|
-
#
|
264
|
+
# * RecordInvalid - raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
|
265
|
+
# {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
|
266
|
+
# when the record is invalid.
|
267
|
+
# * RecordNotFound - No record responded to the {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method.
|
268
|
+
# Either the row with the given ID doesn't exist or the row didn't meet the additional restrictions.
|
269
|
+
# Some {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] calls do not raise this exception to signal
|
263
270
|
# nothing was found, please check its documentation for further details.
|
264
271
|
# * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
|
265
272
|
# * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
|
@@ -281,6 +288,7 @@ module ActiveRecord #:nodoc:
|
|
281
288
|
extend Explain
|
282
289
|
extend Enum
|
283
290
|
extend Delegation::DelegateCache
|
291
|
+
extend CollectionCacheKey
|
284
292
|
|
285
293
|
include Core
|
286
294
|
include Persistence
|
@@ -298,6 +306,7 @@ module ActiveRecord #:nodoc:
|
|
298
306
|
include AttributeDecorators
|
299
307
|
include Locking::Optimistic
|
300
308
|
include Locking::Pessimistic
|
309
|
+
include DefineCallbacks
|
301
310
|
include AttributeMethods
|
302
311
|
include Callbacks
|
303
312
|
include Timestamp
|
@@ -307,10 +316,13 @@ module ActiveRecord #:nodoc:
|
|
307
316
|
include NestedAttributes
|
308
317
|
include Aggregations
|
309
318
|
include Transactions
|
319
|
+
include TouchLater
|
310
320
|
include NoTouching
|
311
321
|
include Reflection
|
312
322
|
include Serialization
|
313
323
|
include Store
|
324
|
+
include SecureToken
|
325
|
+
include Suppressor
|
314
326
|
end
|
315
327
|
|
316
328
|
ActiveSupport.run_load_hooks(:active_record, Base)
|