aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/persistence.rb
diff options
context:
space:
mode:
authorRyuta Kamizono <kamipo@gmail.com>2017-04-02 08:24:23 +0900
committerRyuta Kamizono <kamipo@gmail.com>2017-04-21 07:20:52 +0900
commitc0038f7c362fa0c92fc9e1ea3bdb2706f42386c6 (patch)
tree0713cb06b1f2be74776d0b52c88ac89b2e3acbf8 /activerecord/lib/active_record/persistence.rb
parent972df059bbedfe60d29caa8a561f5aff76883e63 (diff)
downloadrails-c0038f7c362fa0c92fc9e1ea3bdb2706f42386c6.tar.gz
rails-c0038f7c362fa0c92fc9e1ea3bdb2706f42386c6.tar.bz2
rails-c0038f7c362fa0c92fc9e1ea3bdb2706f42386c6.zip
Prevent double firing the before save callback of new object when the parent association saved in the callback
Related #18155, #26661, 268a5bb, #27434, #27442, and #28599. Originally #18155 was introduced for preventing double insertion caused by the after save callback. But it was caused the before save issue (#26661). 268a5bb fixed #26661, but it was caused the performance regression (#27434). #27442 added new record to `target` before calling callbacks for fixing #27434. But it was caused double firing before save callback (#28599). We cannot add new object to `target` before saving the object. This is improving #18155 to only track callbacks after `save`. Fixes #28599.
Diffstat (limited to 'activerecord/lib/active_record/persistence.rb')
-rw-r--r--activerecord/lib/active_record/persistence.rb26
1 files changed, 20 insertions, 6 deletions
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 7ceb7d1a55..f652c7c3a1 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -100,6 +100,10 @@ module ActiveRecord
!(@new_record || @destroyed)
end
+ ##
+ # :call-seq:
+ # save(*args)
+ #
# Saves the model.
#
# If the model is new, a record gets created in the database, otherwise
@@ -121,12 +125,16 @@ module ActiveRecord
#
# Attributes marked as readonly are silently ignored if the record is
# being updated.
- def save(*args)
- create_or_update(*args)
+ def save(*args, &block)
+ create_or_update(*args, &block)
rescue ActiveRecord::RecordInvalid
false
end
+ ##
+ # :call-seq:
+ # save!(*args)
+ #
# Saves the model.
#
# If the model is new, a record gets created in the database, otherwise
@@ -150,8 +158,8 @@ module ActiveRecord
# being updated.
#
# Unless an error is raised, returns true.
- def save!(*args)
- create_or_update(*args) || raise(RecordNotSaved.new("Failed to save the record", self))
+ def save!(*args, &block)
+ create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
end
# Deletes the record in the database and freezes this instance to
@@ -550,9 +558,9 @@ module ActiveRecord
self.class.unscoped.where(self.class.primary_key => id)
end
- def create_or_update(*args)
+ def create_or_update(*args, &block)
_raise_readonly_record_error if readonly?
- result = new_record? ? _create_record : _update_record(*args)
+ result = new_record? ? _create_record(&block) : _update_record(*args, &block)
result != false
end
@@ -567,6 +575,9 @@ module ActiveRecord
rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database
@_trigger_update_callback = rows_affected > 0
end
+
+ yield(self) if block_given?
+
rows_affected
end
@@ -579,6 +590,9 @@ module ActiveRecord
self.id ||= new_id if self.class.primary_key
@new_record = false
+
+ yield(self) if block_given?
+
id
end