mirror of
https://github.com/github/rails.git
synced 2026-01-25 14:28:28 -05:00
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1411 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
61 lines
1.9 KiB
Ruby
61 lines
1.9 KiB
Ruby
module ActiveRecord
|
|
# Wraps a guard around #save to make sure that recursive calls don't actually
|
|
# invoke save multiple times. Recursive calls to save can occur quite
|
|
# easily, and unintentionally. Consider the following case:
|
|
#
|
|
# class Project < ActiveRecord::Base
|
|
# has_and_belongs_to_many :people
|
|
# after_create :grant_access_to_admins
|
|
#
|
|
# def grant_access_to_admins
|
|
# Person.admins.each do |admin|
|
|
# admin.projects.push_with_attributes(self, "access_level" => 42)
|
|
# end
|
|
# end
|
|
# end
|
|
#
|
|
# class Person < ActiveRecord::Base
|
|
# has_and_belongs_to_many :projects
|
|
# ...
|
|
# end
|
|
#
|
|
# teddy = Person.find_by_name("teddy")
|
|
# project = Project.new :name => "sumo wrestling"
|
|
# project.people << teddy
|
|
# project.save!
|
|
#
|
|
# The #push_with_attributes causes +self+ (the project) to be saved again,
|
|
# even though we're already in the midst of doing a save. This results in
|
|
# "teddy" _not_ being added to the project's people list, because the
|
|
# recursive call resets the new-record status and thus ignores any
|
|
# non-new records in the collection.
|
|
#
|
|
# Thus, the need for a recursive guard on save.
|
|
module Recursion
|
|
def self.append_features(base) # :nodoc:
|
|
super
|
|
|
|
base.class_eval do
|
|
alias_method :save_without_recursive_guard, :save
|
|
alias_method :save, :save_with_recursive_guard
|
|
end
|
|
end
|
|
|
|
# Wrap the save call with a sentinel that prevents saves from occuring if
|
|
# a save is already in progress.
|
|
def save_with_recursive_guard(*args)
|
|
critical = Thread.critical
|
|
Thread.critical = true
|
|
old_save_state = @currently_saving_record
|
|
return true if @currently_saving_record
|
|
@currently_saving_record = true
|
|
Thread.critical = critical
|
|
|
|
save_without_recursive_guard(*args)
|
|
ensure
|
|
Thread.critical = critical
|
|
@currently_saving_record = old_save_state
|
|
end
|
|
end
|
|
end
|