activerecord 4.2.0 → 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 +4 -4
- data/CHANGELOG.md +1537 -789
- 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/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- 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 +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- 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 +3 -10
- data/lib/active_record/associations/collection_association.rb +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- 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/through_association.rb +27 -14
- data/lib/active_record/associations/preloader.rb +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- 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 +65 -14
- data/lib/active_record/attribute_methods/write.rb +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- 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 +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- 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 +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 +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -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/type_map_initializer.rb +17 -5
- 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/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- 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 +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -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 +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- 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 +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -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/predicate_builder.rb +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- 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 +176 -115
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -17
- data/lib/active_record/scoping/default.rb +24 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- 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 +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- 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 +58 -0
- data/lib/active_record/transactions.rb +159 -67
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- 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 +21 -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.rb +66 -17
- 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 +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 +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- 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 +60 -34
- 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/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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -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/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 -101
@@ -1,10 +1,10 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
# = Active Record Autosave Association
|
3
3
|
#
|
4
|
-
#
|
4
|
+
# AutosaveAssociation is a module that takes care of automatically saving
|
5
5
|
# associated records when their parent is saved. In addition to saving, it
|
6
6
|
# also destroys any associated records that were marked for destruction.
|
7
|
-
# (See
|
7
|
+
# (See #mark_for_destruction and #marked_for_destruction?).
|
8
8
|
#
|
9
9
|
# Saving of the parent, its associations, and the destruction of marked
|
10
10
|
# associations, all happen inside a transaction. This should never leave the
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
#
|
23
23
|
# == Validation
|
24
24
|
#
|
25
|
-
#
|
25
|
+
# Child records are validated unless <tt>:validate</tt> is +false+.
|
26
26
|
#
|
27
27
|
# == Callbacks
|
28
28
|
#
|
@@ -125,7 +125,6 @@ module ActiveRecord
|
|
125
125
|
# Now it _is_ removed from the database:
|
126
126
|
#
|
127
127
|
# Comment.find_by(id: id).nil? # => true
|
128
|
-
|
129
128
|
module AutosaveAssociation
|
130
129
|
extend ActiveSupport::Concern
|
131
130
|
|
@@ -141,9 +140,11 @@ module ActiveRecord
|
|
141
140
|
|
142
141
|
included do
|
143
142
|
Associations::Builder::Association.extensions << AssociationBuilderExtension
|
143
|
+
mattr_accessor :index_nested_attribute_errors, instance_writer: false
|
144
|
+
self.index_nested_attribute_errors = false
|
144
145
|
end
|
145
146
|
|
146
|
-
module ClassMethods
|
147
|
+
module ClassMethods # :nodoc:
|
147
148
|
private
|
148
149
|
|
149
150
|
def define_non_cyclic_method(name, &block)
|
@@ -177,10 +178,8 @@ module ActiveRecord
|
|
177
178
|
# before actually defining them.
|
178
179
|
def add_autosave_association_callbacks(reflection)
|
179
180
|
save_method = :"autosave_associated_records_for_#{reflection.name}"
|
180
|
-
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
181
|
-
collection = reflection.collection?
|
182
181
|
|
183
|
-
if collection
|
182
|
+
if reflection.collection?
|
184
183
|
before_save :before_save_collection_association
|
185
184
|
|
186
185
|
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
@@ -200,14 +199,31 @@ module ActiveRecord
|
|
200
199
|
after_create save_method
|
201
200
|
after_update save_method
|
202
201
|
else
|
203
|
-
define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
|
202
|
+
define_non_cyclic_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
|
204
203
|
before_save save_method
|
205
204
|
end
|
206
205
|
|
206
|
+
define_autosave_validation_callbacks(reflection)
|
207
|
+
end
|
208
|
+
|
209
|
+
def define_autosave_validation_callbacks(reflection)
|
210
|
+
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
207
211
|
if reflection.validate? && !method_defined?(validation_method)
|
208
|
-
|
209
|
-
|
212
|
+
if reflection.collection?
|
213
|
+
method = :validate_collection_association
|
214
|
+
else
|
215
|
+
method = :validate_single_association
|
216
|
+
end
|
217
|
+
|
218
|
+
define_non_cyclic_method(validation_method) do
|
219
|
+
send(method, reflection)
|
220
|
+
# TODO: remove the following line as soon as the return value of
|
221
|
+
# callbacks is ignored, that is, returning `false` does not
|
222
|
+
# display a deprecation warning or halts the callback chain.
|
223
|
+
true
|
224
|
+
end
|
210
225
|
validate validation_method
|
226
|
+
after_validation :_ensure_no_duplicate_errors
|
211
227
|
end
|
212
228
|
end
|
213
229
|
end
|
@@ -219,7 +235,7 @@ module ActiveRecord
|
|
219
235
|
super
|
220
236
|
end
|
221
237
|
|
222
|
-
# Marks this record to be destroyed as part of the
|
238
|
+
# Marks this record to be destroyed as part of the parent's save transaction.
|
223
239
|
# This does _not_ actually destroy the record instantly, rather child record will be destroyed
|
224
240
|
# when <tt>parent.save</tt> is called.
|
225
241
|
#
|
@@ -228,7 +244,7 @@ module ActiveRecord
|
|
228
244
|
@marked_for_destruction = true
|
229
245
|
end
|
230
246
|
|
231
|
-
# Returns whether or not this record will be destroyed as part of the
|
247
|
+
# Returns whether or not this record will be destroyed as part of the parent's save transaction.
|
232
248
|
#
|
233
249
|
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
|
234
250
|
def marked_for_destruction?
|
@@ -263,20 +279,27 @@ module ActiveRecord
|
|
263
279
|
if new_record
|
264
280
|
association && association.target
|
265
281
|
elsif autosave
|
266
|
-
association.target.find_all
|
282
|
+
association.target.find_all(&:changed_for_autosave?)
|
267
283
|
else
|
268
|
-
association.target.find_all
|
284
|
+
association.target.find_all(&:new_record?)
|
269
285
|
end
|
270
286
|
end
|
271
287
|
|
272
288
|
# go through nested autosave associations that are loaded in memory (without loading
|
273
289
|
# any new ones), and return true if is changed for autosave
|
274
290
|
def nested_records_changed_for_autosave?
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
291
|
+
@_nested_records_changed_for_autosave_already_called ||= false
|
292
|
+
return false if @_nested_records_changed_for_autosave_already_called
|
293
|
+
begin
|
294
|
+
@_nested_records_changed_for_autosave_already_called = true
|
295
|
+
self.class._reflections.values.any? do |reflection|
|
296
|
+
if reflection.options[:autosave]
|
297
|
+
association = association_instance_get(reflection.name)
|
298
|
+
association && Array.wrap(association.target).any?(&:changed_for_autosave?)
|
299
|
+
end
|
279
300
|
end
|
301
|
+
ensure
|
302
|
+
@_nested_records_changed_for_autosave_already_called = false
|
280
303
|
end
|
281
304
|
end
|
282
305
|
|
@@ -294,7 +317,7 @@ module ActiveRecord
|
|
294
317
|
def validate_collection_association(reflection)
|
295
318
|
if association = association_instance_get(reflection.name)
|
296
319
|
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
|
297
|
-
records.
|
320
|
+
records.each_with_index { |record, index| association_valid?(reflection, record, index) }
|
298
321
|
end
|
299
322
|
end
|
300
323
|
end
|
@@ -302,17 +325,36 @@ module ActiveRecord
|
|
302
325
|
# Returns whether or not the association is valid and applies any errors to
|
303
326
|
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
|
304
327
|
# 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?
|
328
|
+
def association_valid?(reflection, record, index=nil)
|
329
|
+
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
|
307
330
|
|
308
331
|
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
|
309
332
|
unless valid = record.valid?(validation_context)
|
310
333
|
if reflection.options[:autosave]
|
334
|
+
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
|
335
|
+
|
311
336
|
record.errors.each do |attribute, message|
|
312
|
-
|
337
|
+
if indexed_attribute
|
338
|
+
attribute = "#{reflection.name}[#{index}].#{attribute}"
|
339
|
+
else
|
340
|
+
attribute = "#{reflection.name}.#{attribute}"
|
341
|
+
end
|
313
342
|
errors[attribute] << message
|
314
343
|
errors[attribute].uniq!
|
315
344
|
end
|
345
|
+
|
346
|
+
record.errors.details.each_key do |attribute|
|
347
|
+
if indexed_attribute
|
348
|
+
reflection_attribute = "#{reflection.name}[#{index}].#{attribute}"
|
349
|
+
else
|
350
|
+
reflection_attribute = "#{reflection.name}.#{attribute}"
|
351
|
+
end
|
352
|
+
|
353
|
+
record.errors.details[attribute].each do |error|
|
354
|
+
errors.details[reflection_attribute] << error
|
355
|
+
errors.details[reflection_attribute].uniq!
|
356
|
+
end
|
357
|
+
end
|
316
358
|
else
|
317
359
|
errors.add(reflection.name)
|
318
360
|
end
|
@@ -331,7 +373,7 @@ module ActiveRecord
|
|
331
373
|
# <tt>:autosave</tt> is enabled on the association.
|
332
374
|
#
|
333
375
|
# In addition, it destroys all children that were marked for destruction
|
334
|
-
# with mark_for_destruction.
|
376
|
+
# with #mark_for_destruction.
|
335
377
|
#
|
336
378
|
# This all happens inside a transaction, _if_ the Transactions module is included into
|
337
379
|
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
@@ -374,7 +416,7 @@ module ActiveRecord
|
|
374
416
|
# on the association.
|
375
417
|
#
|
376
418
|
# In addition, it will destroy the association if it was marked for
|
377
|
-
# destruction with mark_for_destruction.
|
419
|
+
# destruction with #mark_for_destruction.
|
378
420
|
#
|
379
421
|
# This all happens inside a transaction, _if_ the Transactions module is included into
|
380
422
|
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
@@ -435,5 +477,11 @@ module ActiveRecord
|
|
435
477
|
end
|
436
478
|
end
|
437
479
|
end
|
480
|
+
|
481
|
+
def _ensure_no_duplicate_errors
|
482
|
+
errors.messages.each_key do |attribute|
|
483
|
+
errors[attribute].uniq!
|
484
|
+
end
|
485
|
+
end
|
438
486
|
end
|
439
487
|
end
|
data/lib/active_record/base.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'yaml'
|
2
|
-
require 'set'
|
3
2
|
require 'active_support/benchmarkable'
|
4
3
|
require 'active_support/dependencies'
|
5
4
|
require 'active_support/descendants_tracker'
|
6
5
|
require 'active_support/time'
|
7
6
|
require 'active_support/core_ext/module/attribute_accessors'
|
8
|
-
require 'active_support/core_ext/class/delegating_attributes'
|
9
7
|
require 'active_support/core_ext/array/extract_options'
|
10
8
|
require 'active_support/core_ext/hash/deep_merge'
|
11
9
|
require 'active_support/core_ext/hash/slice'
|
@@ -15,13 +13,13 @@ require 'active_support/core_ext/kernel/singleton_class'
|
|
15
13
|
require 'active_support/core_ext/module/introspection'
|
16
14
|
require 'active_support/core_ext/object/duplicable'
|
17
15
|
require 'active_support/core_ext/class/subclasses'
|
18
|
-
require 'arel'
|
19
16
|
require 'active_record/attribute_decorators'
|
20
17
|
require 'active_record/errors'
|
21
18
|
require 'active_record/log_subscriber'
|
22
19
|
require 'active_record/explain_subscriber'
|
23
20
|
require 'active_record/relation/delegation'
|
24
21
|
require 'active_record/attributes'
|
22
|
+
require 'active_record/type_caster'
|
25
23
|
|
26
24
|
module ActiveRecord #:nodoc:
|
27
25
|
# = Active Record
|
@@ -119,29 +117,25 @@ module ActiveRecord #:nodoc:
|
|
119
117
|
# All column values are automatically available through basic accessors on the Active Record
|
120
118
|
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
|
121
119
|
# the default accessors (using the same name as the attribute) and calling
|
122
|
-
#
|
123
|
-
# change things.
|
120
|
+
# +super+ to actually change things.
|
124
121
|
#
|
125
122
|
# class Song < ActiveRecord::Base
|
126
123
|
# # Uses an integer of seconds to hold the length of the song
|
127
124
|
#
|
128
125
|
# def length=(minutes)
|
129
|
-
#
|
126
|
+
# super(minutes.to_i * 60)
|
130
127
|
# end
|
131
128
|
#
|
132
129
|
# def length
|
133
|
-
#
|
130
|
+
# super / 60
|
134
131
|
# end
|
135
132
|
# end
|
136
133
|
#
|
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
134
|
# == Attribute query methods
|
141
135
|
#
|
142
136
|
# In addition to the basic accessors, query methods are also automatically available on the Active Record object.
|
143
137
|
# Query methods allow you to test whether an attribute value is present.
|
144
|
-
#
|
138
|
+
# Additionally, when dealing with numeric values, a query method will return false if the value is zero.
|
145
139
|
#
|
146
140
|
# For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
|
147
141
|
# to determine whether the user has a name:
|
@@ -172,10 +166,11 @@ module ActiveRecord #:nodoc:
|
|
172
166
|
# <tt>Person.find_by_user_name(user_name)</tt>.
|
173
167
|
#
|
174
168
|
# It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an
|
175
|
-
#
|
169
|
+
# ActiveRecord::RecordNotFound error if they do not return any records,
|
176
170
|
# like <tt>Person.find_by_last_name!</tt>.
|
177
171
|
#
|
178
|
-
# It's also possible to use multiple attributes in the same
|
172
|
+
# It's also possible to use multiple attributes in the same <tt>find_by_</tt> by separating them with
|
173
|
+
# "_and_".
|
179
174
|
#
|
180
175
|
# Person.find_by(user_name: user_name, password: password)
|
181
176
|
# Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
|
@@ -187,7 +182,8 @@ module ActiveRecord #:nodoc:
|
|
187
182
|
# == Saving arrays, hashes, and other non-mappable objects in text columns
|
188
183
|
#
|
189
184
|
# 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
|
185
|
+
# specify this with a call to the class method
|
186
|
+
# {serialize}[rdoc-ref:AttributeMethods::Serialization::ClassMethods#serialize].
|
191
187
|
# This makes it possible to store arrays, hashes, and other non-mappable objects without doing
|
192
188
|
# any additional work.
|
193
189
|
#
|
@@ -227,39 +223,47 @@ module ActiveRecord #:nodoc:
|
|
227
223
|
#
|
228
224
|
# == Connection to multiple databases in different models
|
229
225
|
#
|
230
|
-
# Connections are usually created through
|
226
|
+
# Connections are usually created through
|
227
|
+
# {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] and retrieved
|
231
228
|
# by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this
|
232
229
|
# connection. But you can also set a class-specific connection. For example, if Course is an
|
233
230
|
# ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
|
234
231
|
# and Course and all of its subclasses will use this connection instead.
|
235
232
|
#
|
236
233
|
# This feature is implemented by keeping a connection pool in ActiveRecord::Base that is
|
237
|
-
# a
|
234
|
+
# a hash indexed by the class. If a connection is requested, the
|
235
|
+
# {ActiveRecord::Base.retrieve_connection}[rdoc-ref:ConnectionHandling#retrieve_connection] method
|
238
236
|
# will go up the class-hierarchy until a connection is found in the connection pool.
|
239
237
|
#
|
240
238
|
# == Exceptions
|
241
239
|
#
|
242
240
|
# * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
|
243
|
-
# * AdapterNotSpecified - The configuration hash used in
|
244
|
-
#
|
245
|
-
#
|
246
|
-
#
|
241
|
+
# * AdapterNotSpecified - The configuration hash used in
|
242
|
+
# {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection]
|
243
|
+
# didn't include an <tt>:adapter</tt> key.
|
244
|
+
# * AdapterNotFound - The <tt>:adapter</tt> key used in
|
245
|
+
# {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection]
|
246
|
+
# specified a non-existent adapter
|
247
247
|
# (or a bad spelling of an existing one).
|
248
248
|
# * AssociationTypeMismatch - The object assigned to the association wasn't of the type
|
249
249
|
# specified in the association definition.
|
250
250
|
# * AttributeAssignmentError - An error occurred while doing a mass assignment through the
|
251
|
-
#
|
251
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
252
252
|
# You can inspect the +attribute+ property of the exception object to determine which attribute
|
253
253
|
# triggered the error.
|
254
|
-
# * ConnectionNotEstablished - No connection has been established.
|
255
|
-
# before querying.
|
254
|
+
# * ConnectionNotEstablished - No connection has been established.
|
255
|
+
# Use {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] before querying.
|
256
256
|
# * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
|
257
|
-
#
|
257
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
258
|
+
# The +errors+ property of this exception contains an array of
|
258
259
|
# AttributeAssignmentError
|
259
260
|
# objects that should be inspected to determine which attributes triggered the errors.
|
260
|
-
# * RecordInvalid - raised by save! and
|
261
|
-
#
|
262
|
-
#
|
261
|
+
# * RecordInvalid - raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
|
262
|
+
# {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
|
263
|
+
# when the record is invalid.
|
264
|
+
# * RecordNotFound - No record responded to the {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method.
|
265
|
+
# Either the row with the given ID doesn't exist or the row didn't meet the additional restrictions.
|
266
|
+
# Some {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] calls do not raise this exception to signal
|
263
267
|
# nothing was found, please check its documentation for further details.
|
264
268
|
# * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
|
265
269
|
# * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
|
@@ -281,6 +285,7 @@ module ActiveRecord #:nodoc:
|
|
281
285
|
extend Explain
|
282
286
|
extend Enum
|
283
287
|
extend Delegation::DelegateCache
|
288
|
+
extend CollectionCacheKey
|
284
289
|
|
285
290
|
include Core
|
286
291
|
include Persistence
|
@@ -308,9 +313,12 @@ module ActiveRecord #:nodoc:
|
|
308
313
|
include Aggregations
|
309
314
|
include Transactions
|
310
315
|
include NoTouching
|
316
|
+
include TouchLater
|
311
317
|
include Reflection
|
312
318
|
include Serialization
|
313
319
|
include Store
|
320
|
+
include SecureToken
|
321
|
+
include Suppressor
|
314
322
|
end
|
315
323
|
|
316
324
|
ActiveSupport.run_load_hooks(:active_record, Base)
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# = Active Record Callbacks
|
2
|
+
# = Active Record \Callbacks
|
3
3
|
#
|
4
|
-
# Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
|
4
|
+
# \Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
|
5
5
|
# before or after an alteration of the object state. This can be used to make sure that associated and
|
6
|
-
# dependent objects are deleted when
|
7
|
-
# before they're validated (by overwriting +before_validation+).
|
8
|
-
# the
|
6
|
+
# dependent objects are deleted when {ActiveRecord::Base#destroy}[rdoc-ref:Persistence#destroy] is called (by overwriting +before_destroy+) or
|
7
|
+
# to massage attributes before they're validated (by overwriting +before_validation+).
|
8
|
+
# As an example of the callbacks initiated, consider the {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] call for a new record:
|
9
9
|
#
|
10
10
|
# * (-) <tt>save</tt>
|
11
11
|
# * (-) <tt>valid</tt>
|
@@ -20,7 +20,7 @@ module ActiveRecord
|
|
20
20
|
# * (7) <tt>after_commit</tt>
|
21
21
|
#
|
22
22
|
# Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued.
|
23
|
-
# Check out
|
23
|
+
# Check out ActiveRecord::Transactions for more details about <tt>after_commit</tt> and
|
24
24
|
# <tt>after_rollback</tt>.
|
25
25
|
#
|
26
26
|
# Additionally, an <tt>after_touch</tt> callback is triggered whenever an
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# are instantiated as well.
|
32
32
|
#
|
33
33
|
# There are nineteen callbacks in total, which give you immense power to react and prepare for each state in the
|
34
|
-
# Active Record life cycle. The sequence for calling
|
34
|
+
# Active Record life cycle. The sequence for calling {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] for an existing record is similar,
|
35
35
|
# except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
|
36
36
|
#
|
37
37
|
# Examples:
|
@@ -53,9 +53,9 @@ module ActiveRecord
|
|
53
53
|
# end
|
54
54
|
#
|
55
55
|
# class Firm < ActiveRecord::Base
|
56
|
-
# #
|
57
|
-
# before_destroy { |record| Person.
|
58
|
-
# before_destroy { |record| Client.
|
56
|
+
# # Disables access to the system, for associated clients and people when the firm is destroyed
|
57
|
+
# before_destroy { |record| Person.where(firm_id: record.id).update_all(access: 'disabled') }
|
58
|
+
# before_destroy { |record| Client.where(client_of: record.id).update_all(access: 'disabled') }
|
59
59
|
# end
|
60
60
|
#
|
61
61
|
# == Inheritable callback queues
|
@@ -175,43 +175,30 @@ module ActiveRecord
|
|
175
175
|
# end
|
176
176
|
# end
|
177
177
|
#
|
178
|
-
# The callback macros usually accept a symbol for the method they're supposed to run, but you can also
|
179
|
-
# pass a "method string", which will then be evaluated within the binding of the callback. Example:
|
180
|
-
#
|
181
|
-
# class Topic < ActiveRecord::Base
|
182
|
-
# before_destroy 'self.class.delete_all "parent_id = #{id}"'
|
183
|
-
# end
|
184
|
-
#
|
185
|
-
# Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
|
186
|
-
# is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
|
187
|
-
#
|
188
|
-
# class Topic < ActiveRecord::Base
|
189
|
-
# before_destroy 'self.class.delete_all "parent_id = #{id}"',
|
190
|
-
# 'puts "Evaluated after parents are destroyed"'
|
191
|
-
# end
|
192
|
-
#
|
193
178
|
# == <tt>before_validation*</tt> returning statements
|
194
179
|
#
|
195
|
-
# If the
|
196
|
-
# aborted and
|
197
|
-
# ActiveRecord::
|
180
|
+
# If the +before_validation+ callback throws +:abort+, the process will be
|
181
|
+
# aborted and {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] will return +false+.
|
182
|
+
# If {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] is called it will raise an ActiveRecord::RecordInvalid exception.
|
183
|
+
# Nothing will be appended to the errors object.
|
198
184
|
#
|
199
185
|
# == Canceling callbacks
|
200
186
|
#
|
201
|
-
# If a <tt>before_*</tt> callback
|
202
|
-
#
|
187
|
+
# If a <tt>before_*</tt> callback throws +:abort+, all the later callbacks and
|
188
|
+
# the associated action are cancelled.
|
203
189
|
# Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
|
204
190
|
# methods on the model, which are called last.
|
205
191
|
#
|
206
192
|
# == Ordering callbacks
|
207
193
|
#
|
208
194
|
# Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
|
209
|
-
# callback (+log_children+ in this case) should be executed before the children get destroyed by the
|
195
|
+
# callback (+log_children+ in this case) should be executed before the children get destroyed by the
|
196
|
+
# <tt>dependent: :destroy</tt> option.
|
210
197
|
#
|
211
198
|
# Let's look at the code below:
|
212
199
|
#
|
213
200
|
# class Topic < ActiveRecord::Base
|
214
|
-
# has_many :children, dependent: destroy
|
201
|
+
# has_many :children, dependent: :destroy
|
215
202
|
#
|
216
203
|
# before_destroy :log_children
|
217
204
|
#
|
@@ -222,10 +209,11 @@ module ActiveRecord
|
|
222
209
|
# end
|
223
210
|
#
|
224
211
|
# In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
|
225
|
-
# because the
|
212
|
+
# because the {ActiveRecord::Base#destroy}[rdoc-ref:Persistence#destroy] callback gets executed first.
|
213
|
+
# You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
|
226
214
|
#
|
227
215
|
# class Topic < ActiveRecord::Base
|
228
|
-
# has_many :children, dependent: destroy
|
216
|
+
# has_many :children, dependent: :destroy
|
229
217
|
#
|
230
218
|
# before_destroy :log_children, prepend: true
|
231
219
|
#
|
@@ -235,23 +223,23 @@ module ActiveRecord
|
|
235
223
|
# end
|
236
224
|
# end
|
237
225
|
#
|
238
|
-
# This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
|
226
|
+
# This way, the +before_destroy+ gets executed before the <tt>dependent: :destroy</tt> is called, and the data is still available.
|
239
227
|
#
|
240
|
-
# == Transactions
|
228
|
+
# == \Transactions
|
241
229
|
#
|
242
|
-
# The entire callback chain of a
|
243
|
-
# within a transaction. That includes <tt>after_*</tt> hooks.
|
244
|
-
# goes fine a COMMIT is executed once the chain has been completed.
|
230
|
+
# The entire callback chain of a {#save}[rdoc-ref:Persistence#save], {#save!}[rdoc-ref:Persistence#save!],
|
231
|
+
# or {#destroy}[rdoc-ref:Persistence#destroy] call runs within a transaction. That includes <tt>after_*</tt> hooks.
|
232
|
+
# If everything goes fine a COMMIT is executed once the chain has been completed.
|
245
233
|
#
|
246
234
|
# If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
|
247
235
|
# can also trigger a ROLLBACK raising an exception in any of the callbacks,
|
248
236
|
# including <tt>after_*</tt> hooks. Note, however, that in that case the client
|
249
|
-
# needs to be aware of it because an ordinary
|
237
|
+
# needs to be aware of it because an ordinary {#save}[rdoc-ref:Persistence#save] will raise such exception
|
250
238
|
# instead of quietly returning +false+.
|
251
239
|
#
|
252
240
|
# == Debugging callbacks
|
253
241
|
#
|
254
|
-
# The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object.
|
242
|
+
# The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. Active Model \Callbacks support
|
255
243
|
# <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
|
256
244
|
# defines what part of the chain the callback runs in.
|
257
245
|
#
|
@@ -277,7 +265,7 @@ module ActiveRecord
|
|
277
265
|
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
|
278
266
|
]
|
279
267
|
|
280
|
-
module ClassMethods
|
268
|
+
module ClassMethods # :nodoc:
|
281
269
|
include ActiveModel::Callbacks
|
282
270
|
end
|
283
271
|
|
@@ -289,7 +277,15 @@ module ActiveRecord
|
|
289
277
|
end
|
290
278
|
|
291
279
|
def destroy #:nodoc:
|
280
|
+
@_destroy_callback_already_called ||= false
|
281
|
+
return if @_destroy_callback_already_called
|
282
|
+
@_destroy_callback_already_called = true
|
292
283
|
_run_destroy_callbacks { super }
|
284
|
+
rescue RecordNotDestroyed => e
|
285
|
+
@_association_destroy_exception = e
|
286
|
+
false
|
287
|
+
ensure
|
288
|
+
@_destroy_callback_already_called = false
|
293
289
|
end
|
294
290
|
|
295
291
|
def touch(*) #:nodoc:
|
@@ -298,7 +294,7 @@ module ActiveRecord
|
|
298
294
|
|
299
295
|
private
|
300
296
|
|
301
|
-
def create_or_update #:nodoc:
|
297
|
+
def create_or_update(*) #:nodoc:
|
302
298
|
_run_save_callbacks { super }
|
303
299
|
end
|
304
300
|
|
@@ -8,15 +8,13 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
def initialize(object_class = Object)
|
10
10
|
@object_class = object_class
|
11
|
+
check_arity_of_constructor
|
11
12
|
end
|
12
13
|
|
13
14
|
def dump(obj)
|
14
15
|
return if obj.nil?
|
15
16
|
|
16
|
-
|
17
|
-
raise SerializationTypeMismatch,
|
18
|
-
"Attribute was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
|
19
|
-
end
|
17
|
+
assert_valid_value(obj)
|
20
18
|
YAML.dump obj
|
21
19
|
end
|
22
20
|
|
@@ -25,14 +23,28 @@ module ActiveRecord
|
|
25
23
|
return yaml unless yaml.is_a?(String) && yaml =~ /^---/
|
26
24
|
obj = YAML.load(yaml)
|
27
25
|
|
28
|
-
|
29
|
-
raise SerializationTypeMismatch,
|
30
|
-
"Attribute was supposed to be a #{object_class}, but was a #{obj.class}"
|
31
|
-
end
|
26
|
+
assert_valid_value(obj)
|
32
27
|
obj ||= object_class.new if object_class != Object
|
33
28
|
|
34
29
|
obj
|
35
30
|
end
|
31
|
+
|
32
|
+
def assert_valid_value(obj)
|
33
|
+
unless obj.nil? || obj.is_a?(object_class)
|
34
|
+
raise SerializationTypeMismatch,
|
35
|
+
"Attribute was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def check_arity_of_constructor
|
42
|
+
begin
|
43
|
+
load(nil)
|
44
|
+
rescue ArgumentError
|
45
|
+
raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
|
46
|
+
end
|
47
|
+
end
|
36
48
|
end
|
37
49
|
end
|
38
50
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module CollectionCacheKey
|
3
|
+
|
4
|
+
def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
|
5
|
+
query_signature = Digest::MD5.hexdigest(collection.to_sql)
|
6
|
+
key = "#{collection.model_name.cache_key}/query-#{query_signature}"
|
7
|
+
|
8
|
+
if collection.loaded?
|
9
|
+
size = collection.size
|
10
|
+
if size > 0
|
11
|
+
timestamp = collection.max_by(×tamp_column).public_send(timestamp_column)
|
12
|
+
end
|
13
|
+
else
|
14
|
+
column_type = type_for_attribute(timestamp_column.to_s)
|
15
|
+
column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}"
|
16
|
+
|
17
|
+
query = collection
|
18
|
+
.unscope(:select)
|
19
|
+
.select("COUNT(*) AS #{connection.quote_column_name("size")}", "MAX(#{column}) AS timestamp")
|
20
|
+
.unscope(:order)
|
21
|
+
result = connection.select_one(query)
|
22
|
+
|
23
|
+
if result.blank?
|
24
|
+
size = 0
|
25
|
+
timestamp = nil
|
26
|
+
else
|
27
|
+
size = result["size"]
|
28
|
+
timestamp = column_type.deserialize(result["timestamp"])
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
if timestamp
|
34
|
+
"#{key}-#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
|
35
|
+
else
|
36
|
+
"#{key}-#{size}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|