提交 b32ba367 编写于 作者: D Damien Mathieu

add #no_touching on ActiveRecord models

上级 bba8bb8b
* Added `ActiveRecord::Base.no_touching`, which allows ignoring touch on models.
Examples:
Post.no_touching do
Post.first.touch
end
*Sam Stephenson*, *Damien Mathieu*
* Prevent the counter cache from being decremented twice when destroying
a record on a has_many :through association.
......
......@@ -45,6 +45,7 @@ module ActiveRecord
autoload :Migrator, 'active_record/migration'
autoload :ModelSchema
autoload :NestedAttributes
autoload :NoTouching
autoload :Persistence
autoload :QueryCache
autoload :Querying
......
......@@ -295,6 +295,7 @@ class Base
extend Delegation::DelegateCache
include Persistence
include NoTouching
include ReadonlyAttributes
include ModelSchema
include Inheritance
......
module ActiveRecord
# = Active Record No Touching
module NoTouching
extend ActiveSupport::Concern
module ClassMethods
# Lets you selectively disable calls to `touch` for the
# duration of a block.
#
# ==== Examples
# ActiveRecord::Base.no_touching do
# Project.first.touch # does nothing
# Message.first.touch # does nothing
# end
#
# Project.no_touching do
# Project.first.touch # does nothing
# Message.first.touch # works, but does not touch the associated project
# end
#
def no_touching(&block)
NoTouching.apply_to(self, &block)
end
end
class << self
def apply_to(klass) #:nodoc:
klasses.push(klass)
yield
ensure
klasses.pop
end
def applied_to?(klass) #:nodoc:
klasses.any? { |k| k >= klass }
end
private
def klasses
Thread.current[:no_touching_classes] ||= []
end
end
def no_touching?
NoTouching.applied_to?(self.class)
end
def touch(*)
super unless no_touching?
end
end
end
......@@ -11,6 +11,7 @@ class TimestampTest < ActiveRecord::TestCase
def setup
@developer = Developer.first
@owner = Owner.first
@developer.update_columns(updated_at: Time.now.prev_month)
@previously_updated_at = @developer.updated_at
end
......@@ -92,6 +93,53 @@ def test_touching_a_record_without_timestamps_is_unexceptional
assert_nothing_raised { Car.first.touch }
end
def test_touching_a_no_touching_object
Developer.no_touching do
assert @developer.no_touching?
assert !@owner.no_touching?
@developer.touch
end
assert !@developer.no_touching?
assert !@owner.no_touching?
assert_equal @previously_updated_at, @developer.updated_at
end
def test_touching_related_objects
@owner = Owner.first
@previously_updated_at = @owner.updated_at
Owner.no_touching do
@owner.pets.first.touch
end
assert_equal @previously_updated_at, @owner.updated_at
end
def test_global_no_touching
ActiveRecord::Base.no_touching do
assert @developer.no_touching?
assert @owner.no_touching?
@developer.touch
end
assert !@developer.no_touching?
assert !@owner.no_touching?
assert_equal @previously_updated_at, @developer.updated_at
end
def test_no_touching_threadsafe
Thread.new do
Developer.no_touching do
assert @developer.no_touching?
sleep(1)
end
end
assert !@developer.no_touching?
end
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at
pet = Pet.first
owner = pet.owner
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册