activerecord 4.2.11.3 → 5.0.7.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1638 -1132
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +7 -2
- data/lib/active_record/aggregations.rb +34 -21
- data/lib/active_record/association_relation.rb +7 -4
- data/lib/active_record/associations.rb +347 -218
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +22 -10
- data/lib/active_record/associations/association_scope.rb +75 -104
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +13 -11
- data/lib/active_record/associations/collection_association.rb +85 -69
- data/lib/active_record/associations/collection_proxy.rb +104 -46
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +21 -78
- data/lib/active_record/associations/has_many_through_association.rb +6 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +38 -22
- data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +52 -71
- data/lib/active_record/associations/preloader/collection_association.rb +0 -7
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +0 -1
- data/lib/active_record/associations/preloader/through_association.rb +36 -17
- data/lib/active_record/associations/singular_association.rb +13 -1
- data/lib/active_record/associations/through_association.rb +12 -4
- data/lib/active_record/attribute.rb +69 -19
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +69 -44
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +16 -3
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +32 -3
- data/lib/active_record/attribute_set/builder.rb +42 -16
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +54 -17
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- 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 +108 -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 +25 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- 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 +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- 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 +151 -194
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +92 -108
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +116 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain.rb +20 -9
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +77 -41
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +17 -14
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +48 -24
- data/lib/active_record/migration.rb +362 -111
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +270 -73
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +152 -90
- data/lib/active_record/query_cache.rb +18 -23
- data/lib/active_record/querying.rb +12 -11
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +52 -41
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +302 -115
- data/lib/active_record/relation.rb +187 -120
- data/lib/active_record/relation/batches.rb +141 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +92 -117
- data/lib/active_record/relation/delegation.rb +8 -20
- data/lib/active_record/relation/finder_methods.rb +173 -89
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- 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 +1 -1
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- 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/result.rb +11 -4
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +105 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +54 -37
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +34 -16
- data/lib/active_record/scoping/default.rb +28 -10
- data/lib/active_record/scoping/named.rb +59 -26
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +3 -5
- data/lib/active_record/statement_cache.rb +17 -15
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +69 -0
- data/lib/active_record/tasks/database_tasks.rb +66 -49
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +63 -0
- data/lib/active_record/transactions.rb +139 -57
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -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/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +33 -33
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +58 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- 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/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- 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/decimal_without_scale.rb +0 -11
- 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/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -81,6 +81,9 @@ module ActiveRecord
|
|
81
81
|
#
|
82
82
|
# Note that the model will _not_ be destroyed until the parent is saved.
|
83
83
|
#
|
84
|
+
# Also note that the model will not be destroyed unless you also specify
|
85
|
+
# its id in the updated hash.
|
86
|
+
#
|
84
87
|
# === One-to-many
|
85
88
|
#
|
86
89
|
# Consider a member that has a number of posts:
|
@@ -111,7 +114,7 @@ module ActiveRecord
|
|
111
114
|
# member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
|
112
115
|
# member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
|
113
116
|
#
|
114
|
-
# You may also set a
|
117
|
+
# You may also set a +:reject_if+ proc to silently ignore any new record
|
115
118
|
# hashes if they fail to pass your criteria. For example, the previous
|
116
119
|
# example could be rewritten as:
|
117
120
|
#
|
@@ -133,7 +136,7 @@ module ActiveRecord
|
|
133
136
|
# member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
|
134
137
|
# member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
|
135
138
|
#
|
136
|
-
# Alternatively,
|
139
|
+
# Alternatively, +:reject_if+ also accepts a symbol for using methods:
|
137
140
|
#
|
138
141
|
# class Member < ActiveRecord::Base
|
139
142
|
# has_many :posts
|
@@ -144,8 +147,8 @@ module ActiveRecord
|
|
144
147
|
# has_many :posts
|
145
148
|
# accepts_nested_attributes_for :posts, reject_if: :reject_posts
|
146
149
|
#
|
147
|
-
# def reject_posts(
|
148
|
-
#
|
150
|
+
# def reject_posts(attributes)
|
151
|
+
# attributes['title'].blank?
|
149
152
|
# end
|
150
153
|
# end
|
151
154
|
#
|
@@ -163,6 +166,11 @@ module ActiveRecord
|
|
163
166
|
# member.posts.first.title # => '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!'
|
164
167
|
# member.posts.second.title # => '[UPDATED] other post'
|
165
168
|
#
|
169
|
+
# However, the above applies if the parent model is being updated as well.
|
170
|
+
# For example, If you wanted to create a +member+ named _joe_ and wanted to
|
171
|
+
# update the +posts+ at the same time, that would give an
|
172
|
+
# ActiveRecord::RecordNotFound error.
|
173
|
+
#
|
166
174
|
# By default the associated records are protected from being destroyed. If
|
167
175
|
# you want to destroy any of the associated records through the attributes
|
168
176
|
# hash, you have to enable it first using the <tt>:allow_destroy</tt>
|
@@ -187,38 +195,46 @@ module ActiveRecord
|
|
187
195
|
# Nested attributes for an associated collection can also be passed in
|
188
196
|
# the form of a hash of hashes instead of an array of hashes:
|
189
197
|
#
|
190
|
-
# Member.create(
|
191
|
-
#
|
192
|
-
#
|
198
|
+
# Member.create(
|
199
|
+
# name: 'joe',
|
200
|
+
# posts_attributes: {
|
201
|
+
# first: { title: 'Foo' },
|
202
|
+
# second: { title: 'Bar' }
|
203
|
+
# }
|
204
|
+
# )
|
193
205
|
#
|
194
206
|
# has the same effect as
|
195
207
|
#
|
196
|
-
# Member.create(
|
197
|
-
#
|
198
|
-
#
|
208
|
+
# Member.create(
|
209
|
+
# name: 'joe',
|
210
|
+
# posts_attributes: [
|
211
|
+
# { title: 'Foo' },
|
212
|
+
# { title: 'Bar' }
|
213
|
+
# ]
|
214
|
+
# )
|
199
215
|
#
|
200
216
|
# The keys of the hash which is the value for +:posts_attributes+ are
|
201
217
|
# ignored in this case.
|
202
|
-
# However, it is not allowed to use
|
218
|
+
# However, it is not allowed to use <tt>'id'</tt> or <tt>:id</tt> for one of
|
203
219
|
# such keys, otherwise the hash will be wrapped in an array and
|
204
220
|
# interpreted as an attribute hash for a single post.
|
205
221
|
#
|
206
222
|
# Passing attributes for an associated collection in the form of a hash
|
207
223
|
# of hashes can be used with hashes generated from HTTP/HTML parameters,
|
208
|
-
# where there
|
224
|
+
# where there may be no natural way to submit an array of hashes.
|
209
225
|
#
|
210
226
|
# === Saving
|
211
227
|
#
|
212
228
|
# All changes to models, including the destruction of those marked for
|
213
229
|
# destruction, are saved and destroyed automatically and atomically when
|
214
230
|
# the parent model is saved. This happens inside the transaction initiated
|
215
|
-
# by the
|
231
|
+
# by the parent's save method. See ActiveRecord::AutosaveAssociation.
|
216
232
|
#
|
217
233
|
# === Validating the presence of a parent model
|
218
234
|
#
|
219
235
|
# If you want to validate that a child record is associated with a parent
|
220
|
-
# record, you can use
|
221
|
-
#
|
236
|
+
# record, you can use the +validates_presence_of+ method and the +:inverse_of+
|
237
|
+
# key as this example illustrates:
|
222
238
|
#
|
223
239
|
# class Member < ActiveRecord::Base
|
224
240
|
# has_many :posts, inverse_of: :member
|
@@ -230,7 +246,7 @@ module ActiveRecord
|
|
230
246
|
# validates_presence_of :member
|
231
247
|
# end
|
232
248
|
#
|
233
|
-
# Note that if you do not specify the
|
249
|
+
# Note that if you do not specify the +:inverse_of+ option, then
|
234
250
|
# Active Record will try to automatically guess the inverse association
|
235
251
|
# based on heuristics.
|
236
252
|
#
|
@@ -264,29 +280,31 @@ module ActiveRecord
|
|
264
280
|
# Allows you to specify a Proc or a Symbol pointing to a method
|
265
281
|
# that checks whether a record should be built for a certain attribute
|
266
282
|
# hash. The hash is passed to the supplied Proc or the method
|
267
|
-
# and it should return either +true+ or +false+. When no
|
283
|
+
# and it should return either +true+ or +false+. When no +:reject_if+
|
268
284
|
# is specified, a record will be built for all attribute hashes that
|
269
285
|
# do not have a <tt>_destroy</tt> value that evaluates to true.
|
270
286
|
# Passing <tt>:all_blank</tt> instead of a Proc will create a proc
|
271
287
|
# that will reject a record where all the attributes are blank excluding
|
272
|
-
# any value for _destroy
|
288
|
+
# any value for +_destroy+.
|
273
289
|
# [:limit]
|
274
|
-
# Allows you to specify the maximum number of
|
275
|
-
# can be processed with the nested attributes. Limit also can be specified
|
276
|
-
# Proc or a Symbol pointing to a method that should return number.
|
277
|
-
# nested attributes array exceeds the specified limit,
|
278
|
-
# exception is raised. If omitted, any
|
279
|
-
#
|
290
|
+
# Allows you to specify the maximum number of associated records that
|
291
|
+
# can be processed with the nested attributes. Limit also can be specified
|
292
|
+
# as a Proc or a Symbol pointing to a method that should return a number.
|
293
|
+
# If the size of the nested attributes array exceeds the specified limit,
|
294
|
+
# NestedAttributes::TooManyRecords exception is raised. If omitted, any
|
295
|
+
# number of associations can be processed.
|
296
|
+
# Note that the +:limit+ option is only applicable to one-to-many
|
297
|
+
# associations.
|
280
298
|
# [:update_only]
|
281
299
|
# For a one-to-one association, this option allows you to specify how
|
282
|
-
# nested attributes are to be used when an associated record already
|
300
|
+
# nested attributes are going to be used when an associated record already
|
283
301
|
# exists. In general, an existing record may either be updated with the
|
284
302
|
# new set of attribute values or be replaced by a wholly new record
|
285
|
-
# containing those values. By default the
|
303
|
+
# containing those values. By default the +:update_only+ option is +false+
|
286
304
|
# and the nested attributes are used to update the existing record only
|
287
305
|
# if they include the record's <tt>:id</tt> value. Otherwise a new
|
288
306
|
# record will be instantiated and used to replace the existing one.
|
289
|
-
# However if the
|
307
|
+
# However if the +:update_only+ option is +true+, the nested attributes
|
290
308
|
# are used to update the record's attributes always, regardless of
|
291
309
|
# whether the <tt>:id</tt> is present. The option is ignored for collection
|
292
310
|
# associations.
|
@@ -376,6 +394,9 @@ module ActiveRecord
|
|
376
394
|
# then the existing record will be marked for destruction.
|
377
395
|
def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
|
378
396
|
options = self.nested_attributes_options[association_name]
|
397
|
+
if attributes.respond_to?(:permitted?)
|
398
|
+
attributes = attributes.to_h
|
399
|
+
end
|
379
400
|
attributes = attributes.with_indifferent_access
|
380
401
|
existing_record = send(association_name)
|
381
402
|
|
@@ -432,6 +453,9 @@ module ActiveRecord
|
|
432
453
|
# ])
|
433
454
|
def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
|
434
455
|
options = self.nested_attributes_options[association_name]
|
456
|
+
if attributes_collection.respond_to?(:permitted?)
|
457
|
+
attributes_collection = attributes_collection.to_h
|
458
|
+
end
|
435
459
|
|
436
460
|
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
|
437
461
|
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
|
@@ -458,6 +482,9 @@ module ActiveRecord
|
|
458
482
|
end
|
459
483
|
|
460
484
|
attributes_collection.each do |attributes|
|
485
|
+
if attributes.respond_to?(:permitted?)
|
486
|
+
attributes = attributes.to_h
|
487
|
+
end
|
461
488
|
attributes = attributes.with_indifferent_access
|
462
489
|
|
463
490
|
if attributes['id'].blank?
|
@@ -516,7 +543,7 @@ module ActiveRecord
|
|
516
543
|
|
517
544
|
# Determines if a hash contains a truthy _destroy key.
|
518
545
|
def has_destroy_flag?(hash)
|
519
|
-
Type::Boolean.new.
|
546
|
+
Type::Boolean.new.cast(hash['_destroy'])
|
520
547
|
end
|
521
548
|
|
522
549
|
# Determines if a new record should be rejected by checking
|
@@ -552,7 +579,9 @@ module ActiveRecord
|
|
552
579
|
end
|
553
580
|
|
554
581
|
def raise_nested_attributes_record_not_found!(association_name, record_id)
|
555
|
-
|
582
|
+
model = self.class._reflect_on_association(association_name).klass.name
|
583
|
+
raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
|
584
|
+
model, 'id', record_id)
|
556
585
|
end
|
557
586
|
end
|
558
587
|
end
|
@@ -1,9 +1,7 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module NullRelation # :nodoc:
|
5
3
|
def exec_queries
|
6
|
-
@records = []
|
4
|
+
@records = [].freeze
|
7
5
|
end
|
8
6
|
|
9
7
|
def pluck(*column_names)
|
@@ -14,7 +12,7 @@ module ActiveRecord
|
|
14
12
|
0
|
15
13
|
end
|
16
14
|
|
17
|
-
def update_all(_updates
|
15
|
+
def update_all(_updates)
|
18
16
|
0
|
19
17
|
end
|
20
18
|
|
@@ -30,10 +28,18 @@ module ActiveRecord
|
|
30
28
|
true
|
31
29
|
end
|
32
30
|
|
31
|
+
def none?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
33
35
|
def any?
|
34
36
|
false
|
35
37
|
end
|
36
38
|
|
39
|
+
def one?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
37
43
|
def many?
|
38
44
|
false
|
39
45
|
end
|
@@ -62,9 +68,7 @@ module ActiveRecord
|
|
62
68
|
calculate :maximum, nil
|
63
69
|
end
|
64
70
|
|
65
|
-
def calculate(operation, _column_name
|
66
|
-
# TODO: Remove _options argument as soon we remove support to
|
67
|
-
# activerecord-deprecated_finders.
|
71
|
+
def calculate(operation, _column_name)
|
68
72
|
if [:count, :sum, :size].include? operation
|
69
73
|
group_values.any? ? Hash.new : 0
|
70
74
|
elsif [:average, :minimum, :maximum].include?(operation) && group_values.any?
|
@@ -74,8 +78,12 @@ module ActiveRecord
|
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
77
|
-
def exists?(
|
81
|
+
def exists?(_conditions = :none)
|
78
82
|
false
|
79
83
|
end
|
84
|
+
|
85
|
+
def or(other)
|
86
|
+
other.spawn
|
87
|
+
end
|
80
88
|
end
|
81
89
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# = Active Record Persistence
|
2
|
+
# = Active Record \Persistence
|
3
3
|
module Persistence
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
@@ -61,12 +61,12 @@ module ActiveRecord
|
|
61
61
|
# +instantiate+ instead of +new+, finder methods ensure they get new
|
62
62
|
# instances of the appropriate class for each record.
|
63
63
|
#
|
64
|
-
# See
|
64
|
+
# See <tt>ActiveRecord::Inheritance#discriminate_class_for_record</tt> to see
|
65
65
|
# how this "single-table" inheritance mapping is implemented.
|
66
|
-
def instantiate(attributes, column_types = {})
|
66
|
+
def instantiate(attributes, column_types = {}, &block)
|
67
67
|
klass = discriminate_class_for_record(attributes)
|
68
68
|
attributes = klass.attributes_builder.build_from_database(attributes, column_types)
|
69
|
-
klass.allocate.init_with(
|
69
|
+
klass.allocate.init_with("attributes" => attributes, "new_record" => false, &block)
|
70
70
|
end
|
71
71
|
|
72
72
|
private
|
@@ -96,50 +96,68 @@ module ActiveRecord
|
|
96
96
|
# Returns true if the record is persisted, i.e. it's not a new record and it was
|
97
97
|
# not destroyed, otherwise returns false.
|
98
98
|
def persisted?
|
99
|
-
|
99
|
+
sync_with_transaction_state
|
100
|
+
!(@new_record || @destroyed)
|
100
101
|
end
|
101
102
|
|
103
|
+
##
|
104
|
+
# :call-seq:
|
105
|
+
# save(*args)
|
106
|
+
#
|
102
107
|
# Saves the model.
|
103
108
|
#
|
104
|
-
# If the model is new a record gets created in the database, otherwise
|
109
|
+
# If the model is new, a record gets created in the database, otherwise
|
105
110
|
# the existing record gets updated.
|
106
111
|
#
|
107
|
-
# By default, save always
|
108
|
-
# is cancelled and
|
112
|
+
# By default, save always runs validations. If any of them fail the action
|
113
|
+
# is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
|
109
114
|
# validate: false, validations are bypassed altogether. See
|
110
115
|
# ActiveRecord::Validations for more information.
|
111
116
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
117
|
+
# By default, #save also sets the +updated_at+/+updated_on+ attributes to
|
118
|
+
# the current time. However, if you supply <tt>touch: false</tt>, these
|
119
|
+
# timestamps will not be updated.
|
120
|
+
#
|
121
|
+
# There's a series of callbacks associated with #save. If any of the
|
122
|
+
# <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled and
|
123
|
+
# #save returns +false+. See ActiveRecord::Callbacks for further
|
115
124
|
# details.
|
116
125
|
#
|
117
126
|
# Attributes marked as readonly are silently ignored if the record is
|
118
127
|
# being updated.
|
119
|
-
def save(*)
|
120
|
-
create_or_update
|
128
|
+
def save(*args, &block)
|
129
|
+
create_or_update(*args, &block)
|
121
130
|
rescue ActiveRecord::RecordInvalid
|
122
131
|
false
|
123
132
|
end
|
124
133
|
|
134
|
+
##
|
135
|
+
# :call-seq:
|
136
|
+
# save!(*args)
|
137
|
+
#
|
125
138
|
# Saves the model.
|
126
139
|
#
|
127
|
-
# If the model is new a record gets created in the database, otherwise
|
140
|
+
# If the model is new, a record gets created in the database, otherwise
|
128
141
|
# the existing record gets updated.
|
129
142
|
#
|
130
|
-
#
|
131
|
-
# ActiveRecord::RecordInvalid gets raised.
|
132
|
-
#
|
143
|
+
# By default, #save! always runs validations. If any of them fail
|
144
|
+
# ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
|
145
|
+
# validate: false, validations are bypassed altogether. See
|
146
|
+
# ActiveRecord::Validations for more information.
|
147
|
+
#
|
148
|
+
# By default, #save! also sets the +updated_at+/+updated_on+ attributes to
|
149
|
+
# the current time. However, if you supply <tt>touch: false</tt>, these
|
150
|
+
# timestamps will not be updated.
|
133
151
|
#
|
134
|
-
# There's a series of callbacks associated with
|
135
|
-
# the <tt>before_*</tt> callbacks
|
136
|
-
# and
|
152
|
+
# There's a series of callbacks associated with #save!. If any of
|
153
|
+
# the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
|
154
|
+
# and #save! raises ActiveRecord::RecordNotSaved. See
|
137
155
|
# ActiveRecord::Callbacks for further details.
|
138
156
|
#
|
139
157
|
# Attributes marked as readonly are silently ignored if the record is
|
140
158
|
# being updated.
|
141
|
-
def save!(*)
|
142
|
-
create_or_update || raise(RecordNotSaved.new("Failed to save the record", self))
|
159
|
+
def save!(*args, &block)
|
160
|
+
create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
|
143
161
|
end
|
144
162
|
|
145
163
|
# Deletes the record in the database and freezes this instance to
|
@@ -149,6 +167,8 @@ module ActiveRecord
|
|
149
167
|
# The row is simply removed with an SQL +DELETE+ statement on the
|
150
168
|
# record's primary key, and no callbacks are executed.
|
151
169
|
#
|
170
|
+
# Note that this will also delete records marked as {#readonly?}[rdoc-ref:Core#readonly?].
|
171
|
+
#
|
152
172
|
# To enforce the object's +before_destroy+ and +after_destroy+
|
153
173
|
# callbacks or any <tt>:dependent</tt> association
|
154
174
|
# options, use <tt>#destroy</tt>.
|
@@ -161,10 +181,10 @@ module ActiveRecord
|
|
161
181
|
# Deletes the record in the database and freezes this instance to reflect
|
162
182
|
# that no changes should be made (since they can't be persisted).
|
163
183
|
#
|
164
|
-
# There's a series of callbacks associated with
|
165
|
-
#
|
166
|
-
# and
|
167
|
-
# ActiveRecord::Callbacks for further details.
|
184
|
+
# There's a series of callbacks associated with #destroy. If the
|
185
|
+
# <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
|
186
|
+
# and #destroy returns +false+.
|
187
|
+
# See ActiveRecord::Callbacks for further details.
|
168
188
|
def destroy
|
169
189
|
raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
|
170
190
|
destroy_associations
|
@@ -177,12 +197,12 @@ module ActiveRecord
|
|
177
197
|
# Deletes the record in the database and freezes this instance to reflect
|
178
198
|
# that no changes should be made (since they can't be persisted).
|
179
199
|
#
|
180
|
-
# There's a series of callbacks associated with
|
181
|
-
#
|
182
|
-
# and
|
183
|
-
# ActiveRecord::Callbacks for further details.
|
200
|
+
# There's a series of callbacks associated with #destroy!. If the
|
201
|
+
# <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
|
202
|
+
# and #destroy! raises ActiveRecord::RecordNotDestroyed.
|
203
|
+
# See ActiveRecord::Callbacks for further details.
|
184
204
|
def destroy!
|
185
|
-
destroy ||
|
205
|
+
destroy || _raise_record_not_destroyed
|
186
206
|
end
|
187
207
|
|
188
208
|
# Returns an instance of the specified +klass+ with the attributes of the
|
@@ -194,19 +214,21 @@ module ActiveRecord
|
|
194
214
|
# instance using the companies/company partial instead of clients/client.
|
195
215
|
#
|
196
216
|
# Note: The new instance will share a link to the same attributes as the original class.
|
197
|
-
#
|
217
|
+
# Therefore the sti column value will still be the same.
|
218
|
+
# Any change to the attributes on either instance will affect both instances.
|
219
|
+
# If you want to change the sti column as well, use #becomes! instead.
|
198
220
|
def becomes(klass)
|
199
221
|
became = klass.new
|
200
222
|
became.instance_variable_set("@attributes", @attributes)
|
201
|
-
|
202
|
-
became.instance_variable_set("@changed_attributes",
|
223
|
+
became.instance_variable_set("@mutation_tracker", @mutation_tracker) if defined?(@mutation_tracker)
|
224
|
+
became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
|
203
225
|
became.instance_variable_set("@new_record", new_record?)
|
204
226
|
became.instance_variable_set("@destroyed", destroyed?)
|
205
|
-
became.
|
227
|
+
became.errors.copy!(errors)
|
206
228
|
became
|
207
229
|
end
|
208
230
|
|
209
|
-
# Wrapper around
|
231
|
+
# Wrapper around #becomes that also changes the instance's sti column value.
|
210
232
|
# This is especially useful if you want to persist the changed class in your
|
211
233
|
# database.
|
212
234
|
#
|
@@ -226,19 +248,20 @@ module ActiveRecord
|
|
226
248
|
# This is especially useful for boolean flags on existing records. Also note that
|
227
249
|
#
|
228
250
|
# * Validation is skipped.
|
229
|
-
# * Callbacks are invoked.
|
251
|
+
# * \Callbacks are invoked.
|
230
252
|
# * updated_at/updated_on column is updated if that column is available.
|
231
253
|
# * Updates all the attributes that are dirty in this object.
|
232
254
|
#
|
233
|
-
# This method raises an
|
255
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
234
256
|
# attribute is marked as readonly.
|
235
257
|
#
|
236
|
-
#
|
258
|
+
# Also see #update_column.
|
237
259
|
def update_attribute(name, value)
|
238
260
|
name = name.to_s
|
239
261
|
verify_readonly_attribute(name)
|
240
|
-
|
241
|
-
|
262
|
+
public_send("#{name}=", value)
|
263
|
+
|
264
|
+
changed? ? save(validate: false) : true
|
242
265
|
end
|
243
266
|
|
244
267
|
# Updates the attributes of the model from the passed-in hash and saves the
|
@@ -255,8 +278,8 @@ module ActiveRecord
|
|
255
278
|
|
256
279
|
alias update_attributes update
|
257
280
|
|
258
|
-
# Updates its receiver just like
|
259
|
-
# of +save+, so an exception is raised if the record is invalid.
|
281
|
+
# Updates its receiver just like #update but calls #save! instead
|
282
|
+
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
260
283
|
def update!(attributes)
|
261
284
|
# The following transaction covers any possible database side-effects of the
|
262
285
|
# attributes assignment. For example, setting the IDs of a child collection.
|
@@ -282,11 +305,12 @@ module ActiveRecord
|
|
282
305
|
# the database, but take into account that in consequence the regular update
|
283
306
|
# procedures are totally bypassed. In particular:
|
284
307
|
#
|
285
|
-
# * Validations are skipped.
|
286
|
-
# * Callbacks are skipped.
|
308
|
+
# * \Validations are skipped.
|
309
|
+
# * \Callbacks are skipped.
|
287
310
|
# * +updated_at+/+updated_on+ are not updated.
|
311
|
+
# * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
|
288
312
|
#
|
289
|
-
# This method raises an
|
313
|
+
# This method raises an ActiveRecord::ActiveRecordError when called on new
|
290
314
|
# objects, or when at least one of the attributes is marked as readonly.
|
291
315
|
def update_columns(attributes)
|
292
316
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
@@ -314,42 +338,52 @@ module ActiveRecord
|
|
314
338
|
self
|
315
339
|
end
|
316
340
|
|
317
|
-
# Wrapper around
|
318
|
-
#
|
319
|
-
#
|
320
|
-
#
|
341
|
+
# Wrapper around #increment that writes the update to the database.
|
342
|
+
# Only +attribute+ is updated; the record itself is not saved.
|
343
|
+
# This means that any other modified attributes will still be dirty.
|
344
|
+
# Validations and callbacks are skipped. Returns +self+.
|
321
345
|
def increment!(attribute, by = 1)
|
322
|
-
increment(attribute, by)
|
346
|
+
increment(attribute, by)
|
347
|
+
change = public_send(attribute) - (attribute_was(attribute.to_s) || 0)
|
348
|
+
self.class.update_counters(id, attribute => change)
|
349
|
+
clear_attribute_change(attribute) # eww
|
350
|
+
self
|
323
351
|
end
|
324
352
|
|
325
353
|
# Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
|
326
354
|
# The decrement is performed directly on the underlying attribute, no setter is invoked.
|
327
355
|
# Only makes sense for number-based attributes. Returns +self+.
|
328
356
|
def decrement(attribute, by = 1)
|
329
|
-
|
330
|
-
self[attribute] -= by
|
331
|
-
self
|
357
|
+
increment(attribute, -by)
|
332
358
|
end
|
333
359
|
|
334
|
-
# Wrapper around
|
335
|
-
#
|
336
|
-
#
|
337
|
-
#
|
360
|
+
# Wrapper around #decrement that writes the update to the database.
|
361
|
+
# Only +attribute+ is updated; the record itself is not saved.
|
362
|
+
# This means that any other modified attributes will still be dirty.
|
363
|
+
# Validations and callbacks are skipped. Returns +self+.
|
338
364
|
def decrement!(attribute, by = 1)
|
339
|
-
|
365
|
+
increment!(attribute, -by)
|
340
366
|
end
|
341
367
|
|
342
368
|
# Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
|
343
369
|
# if the predicate returns +true+ the attribute will become +false+. This
|
344
370
|
# method toggles directly the underlying value without calling any setter.
|
345
371
|
# Returns +self+.
|
372
|
+
#
|
373
|
+
# Example:
|
374
|
+
#
|
375
|
+
# user = User.first
|
376
|
+
# user.banned? # => false
|
377
|
+
# user.toggle(:banned)
|
378
|
+
# user.banned? # => true
|
379
|
+
#
|
346
380
|
def toggle(attribute)
|
347
|
-
self[attribute] = !
|
381
|
+
self[attribute] = !public_send("#{attribute}?")
|
348
382
|
self
|
349
383
|
end
|
350
384
|
|
351
|
-
# Wrapper around
|
352
|
-
# its non-bang version in that it passes through the attribute setter.
|
385
|
+
# Wrapper around #toggle that saves the record. This method differs from
|
386
|
+
# its non-bang version in the sense that it passes through the attribute setter.
|
353
387
|
# Saving is not subjected to validation checks. Returns +true+ if the
|
354
388
|
# record could be saved.
|
355
389
|
def toggle!(attribute)
|
@@ -371,7 +405,7 @@ module ActiveRecord
|
|
371
405
|
# Attributes are reloaded from the database, and caches busted, in
|
372
406
|
# particular the associations cache and the QueryCache.
|
373
407
|
#
|
374
|
-
# If the record no longer exists in the database
|
408
|
+
# If the record no longer exists in the database ActiveRecord::RecordNotFound
|
375
409
|
# is raised. Otherwise, in addition to the in-place modification the method
|
376
410
|
# returns +self+ for convenience.
|
377
411
|
#
|
@@ -405,8 +439,6 @@ module ActiveRecord
|
|
405
439
|
# end
|
406
440
|
#
|
407
441
|
def reload(options = nil)
|
408
|
-
clear_aggregation_cache
|
409
|
-
clear_association_cache
|
410
442
|
self.class.connection.clear_query_cache
|
411
443
|
|
412
444
|
fresh_object =
|
@@ -421,19 +453,22 @@ module ActiveRecord
|
|
421
453
|
self
|
422
454
|
end
|
423
455
|
|
424
|
-
# Saves the record with the updated_at/on attributes set to the current time
|
456
|
+
# Saves the record with the updated_at/on attributes set to the current time
|
457
|
+
# or the time specified.
|
425
458
|
# Please note that no validation is performed and only the +after_touch+,
|
426
459
|
# +after_commit+ and +after_rollback+ callbacks are executed.
|
427
460
|
#
|
461
|
+
# This method can be passed attribute names and an optional time argument.
|
428
462
|
# If attribute names are passed, they are updated along with updated_at/on
|
429
|
-
# attributes.
|
463
|
+
# attributes. If no time argument is passed, the current time is used as default.
|
430
464
|
#
|
431
|
-
# product.touch # updates updated_at/on
|
465
|
+
# product.touch # updates updated_at/on with current time
|
466
|
+
# product.touch(time: Time.new(2015, 2, 16, 0, 0, 0)) # updates updated_at/on with specified time
|
432
467
|
# product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
|
433
468
|
# product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
|
434
469
|
#
|
435
|
-
# If used along with
|
436
|
-
# associated object.
|
470
|
+
# If used along with {belongs_to}[rdoc-ref:Associations::ClassMethods#belongs_to]
|
471
|
+
# then +touch+ will invoke +touch+ method on associated object.
|
437
472
|
#
|
438
473
|
# class Brake < ActiveRecord::Base
|
439
474
|
# belongs_to :car, touch: true
|
@@ -452,26 +487,43 @@ module ActiveRecord
|
|
452
487
|
# ball = Ball.new
|
453
488
|
# ball.touch(:updated_at) # => raises ActiveRecordError
|
454
489
|
#
|
455
|
-
def touch(*names)
|
456
|
-
|
490
|
+
def touch(*names, time: nil)
|
491
|
+
unless persisted?
|
492
|
+
raise ActiveRecordError, <<-MSG.squish
|
493
|
+
cannot touch on a new or destroyed record object. Consider using
|
494
|
+
persisted?, new_record?, or destroyed? before touching
|
495
|
+
MSG
|
496
|
+
end
|
457
497
|
|
498
|
+
time ||= current_time_from_proper_timezone
|
458
499
|
attributes = timestamp_attributes_for_update_in_model
|
459
500
|
attributes.concat(names)
|
460
501
|
|
461
502
|
unless attributes.empty?
|
462
|
-
current_time = current_time_from_proper_timezone
|
463
503
|
changes = {}
|
464
504
|
|
465
505
|
attributes.each do |column|
|
466
506
|
column = column.to_s
|
467
|
-
changes[column] = write_attribute(column,
|
507
|
+
changes[column] = write_attribute(column, time)
|
468
508
|
end
|
469
509
|
|
470
|
-
changes[self.class.locking_column] = increment_lock if locking_enabled?
|
471
|
-
|
472
510
|
clear_attribute_changes(changes.keys)
|
473
511
|
primary_key = self.class.primary_key
|
474
|
-
self.class.unscoped.where(primary_key =>
|
512
|
+
scope = self.class.unscoped.where(primary_key => _read_attribute(primary_key))
|
513
|
+
|
514
|
+
if locking_enabled?
|
515
|
+
locking_column = self.class.locking_column
|
516
|
+
scope = scope.where(locking_column => _read_attribute(locking_column))
|
517
|
+
changes[locking_column] = increment_lock
|
518
|
+
end
|
519
|
+
|
520
|
+
result = scope.update_all(changes) == 1
|
521
|
+
|
522
|
+
if !result && locking_enabled?
|
523
|
+
raise ActiveRecord::StaleObjectError.new(self, "touch")
|
524
|
+
end
|
525
|
+
|
526
|
+
result
|
475
527
|
else
|
476
528
|
true
|
477
529
|
end
|
@@ -488,20 +540,12 @@ module ActiveRecord
|
|
488
540
|
end
|
489
541
|
|
490
542
|
def relation_for_destroy
|
491
|
-
|
492
|
-
column = self.class.columns_hash[pk]
|
493
|
-
substitute = self.class.connection.substitute_at(column)
|
494
|
-
|
495
|
-
relation = self.class.unscoped.where(
|
496
|
-
self.class.arel_table[pk].eq(substitute))
|
497
|
-
|
498
|
-
relation.bind_values = [[column, id]]
|
499
|
-
relation
|
543
|
+
self.class.unscoped.where(self.class.primary_key => id)
|
500
544
|
end
|
501
545
|
|
502
|
-
def create_or_update
|
546
|
+
def create_or_update(*args, &block)
|
503
547
|
raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
|
504
|
-
result = new_record? ? _create_record : _update_record
|
548
|
+
result = new_record? ? _create_record(&block) : _update_record(*args, &block)
|
505
549
|
result != false
|
506
550
|
end
|
507
551
|
|
@@ -510,10 +554,14 @@ module ActiveRecord
|
|
510
554
|
def _update_record(attribute_names = self.attribute_names)
|
511
555
|
attributes_values = arel_attributes_with_values_for_update(attribute_names)
|
512
556
|
if attributes_values.empty?
|
513
|
-
0
|
557
|
+
rows_affected = 0
|
514
558
|
else
|
515
|
-
self.class.unscoped._update_record attributes_values, id, id_was
|
559
|
+
rows_affected = self.class.unscoped._update_record attributes_values, id, id_was
|
516
560
|
end
|
561
|
+
|
562
|
+
yield(self) if block_given?
|
563
|
+
|
564
|
+
rows_affected
|
517
565
|
end
|
518
566
|
|
519
567
|
# Creates a record with values matching those of the instance attributes
|
@@ -525,11 +573,25 @@ module ActiveRecord
|
|
525
573
|
self.id ||= new_id if self.class.primary_key
|
526
574
|
|
527
575
|
@new_record = false
|
576
|
+
|
577
|
+
yield(self) if block_given?
|
578
|
+
|
528
579
|
id
|
529
580
|
end
|
530
581
|
|
531
582
|
def verify_readonly_attribute(name)
|
532
583
|
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
|
533
584
|
end
|
585
|
+
|
586
|
+
def _raise_record_not_destroyed
|
587
|
+
@_association_destroy_exception ||= nil
|
588
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
|
589
|
+
ensure
|
590
|
+
@_association_destroy_exception = nil
|
591
|
+
end
|
592
|
+
|
593
|
+
def belongs_to_touch_method
|
594
|
+
:touch
|
595
|
+
end
|
534
596
|
end
|
535
597
|
end
|