Skip to content
This repository has been archived by the owner on Apr 11, 2024. It is now read-only.

Commit

Permalink
Adding a class level method 'skip_version' that will allow skipping v…
Browse files Browse the repository at this point in the history
…ersions across multiple models
  • Loading branch information
adamcooper committed Dec 19, 2010
1 parent 6adf025 commit 4fa33c6
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 38 deletions.
106 changes: 69 additions & 37 deletions lib/vestal_versions/control.rb
Expand Up @@ -4,6 +4,11 @@ module VestalVersions
module Control module Control
extend ActiveSupport::Concern extend ActiveSupport::Concern


included do
class_attribute :_skip_version, :instance_writer => false
end


# Control blocks are called on ActiveRecord::Base instances as to not cause any conflict with # Control blocks are called on ActiveRecord::Base instances as to not cause any conflict with
# other instances of the versioned class whose behavior could be inadvertently altered within # other instances of the versioned class whose behavior could be inadvertently altered within
# a control block. # a control block.
Expand All @@ -24,7 +29,7 @@ module InstanceMethods
# end # end
# user.version # => 1 # user.version # => 1
def skip_version def skip_version
with_version_flag(:skip_version) do _with_version_flag(:_skip_version) do
yield if block_given? yield if block_given?
save save
end end
Expand All @@ -34,18 +39,12 @@ def skip_version
# +skip_version!+ block is that the save automatically performed at the close of the block # +skip_version!+ block is that the save automatically performed at the close of the block
# is a +save!+, meaning that an exception will be raised if the object cannot be saved. # is a +save!+, meaning that an exception will be raised if the object cannot be saved.
def skip_version! def skip_version!
with_version_flag(:skip_version) do _with_version_flag(:_skip_version) do
yield if block_given? yield if block_given?
save! save!
end end
end end


# A convenience method for determining whether a versioned instance is set to skip its next
# version creation.
def skip_version?
!!@skip_version
end

# Merging versions with the +merge_version+ block will take all of the versions that would # Merging versions with the +merge_version+ block will take all of the versions that would
# be created within the block and merge them into one version and pushing that single version # be created within the block and merge them into one version and pushing that single version
# onto the ActiveRecord::Base instance's version history. A new version will be created and # onto the ActiveRecord::Base instance's version history. A new version will be created and
Expand All @@ -66,7 +65,7 @@ def skip_version?
# #
# See VestalVersions::Changes for an explanation on how changes are appended. # See VestalVersions::Changes for an explanation on how changes are appended.
def merge_version def merge_version
with_version_flag(:merge_version) do _with_version_flag(:merge_version) do
yield if block_given? yield if block_given?
end end
save save
Expand All @@ -76,7 +75,7 @@ def merge_version
# +merge_version!+ block is that the save automatically performed at the close of the block # +merge_version!+ block is that the save automatically performed at the close of the block
# is a +save!+, meaning that an exception will be raised if the object cannot be saved. # is a +save!+, meaning that an exception will be raised if the object cannot be saved.
def merge_version! def merge_version!
with_version_flag(:merge_version) do _with_version_flag(:merge_version) do
yield if block_given? yield if block_given?
end end
save! save!
Expand Down Expand Up @@ -109,11 +108,11 @@ def merge_version?
# #
# See VestalVersions::Changes for an explanation on how changes are appended. # See VestalVersions::Changes for an explanation on how changes are appended.
def append_version def append_version
with_version_flag(:merge_version) do _with_version_flag(:merge_version) do
yield if block_given? yield if block_given?
end end


with_version_flag(:append_version) do _with_version_flag(:append_version) do
save save
end end
end end
Expand All @@ -122,11 +121,11 @@ def append_version
# +append_version!+ block is that the save automatically performed at the close of the block # +append_version!+ block is that the save automatically performed at the close of the block
# is a +save!+, meaning that an exception will be raised if the object cannot be saved. # is a +save!+, meaning that an exception will be raised if the object cannot be saved.
def append_version! def append_version!
with_version_flag(:merge_version) do _with_version_flag(:merge_version) do
yield if block_given? yield if block_given?
end end


with_version_flag(:append_version) do _with_version_flag(:append_version) do
save! save!
end end
end end
Expand All @@ -137,32 +136,65 @@ def append_version?
!!@append_version !!@append_version
end end


private # Used for each control block, the +_with_version_flag+ method sets a given variable to
# Used for each control block, the +with_version_flag+ method sets a given variable to # true and then executes the given block, ensuring that the variable is returned to a nil
# true and then executes the given block, ensuring that the variable is returned to a nil # value before returning. This is useful to be certain that one of the control flag
# value before returning. This is useful to be certain that one of the control flag # instance variables isn't inadvertently left in the "on" position by execution within the
# instance variables isn't inadvertently left in the "on" position by execution within the # block raising an exception.
# block raising an exception. def _with_version_flag(flag)
def with_version_flag(flag) instance_variable_set("@#{flag}", true)
begin yield
instance_variable_set("@#{flag}", true) ensure
yield remove_instance_variable("@#{flag}")
ensure end
instance_variable_set("@#{flag}", nil)
end
end


# Overrides the basal +create_version?+ method to make sure that new versions are not # Overrides the basal +create_version?+ method to make sure that new versions are not
# created when inside any of the control blocks (until the block terminates). # created when inside any of the control blocks (until the block terminates).
def create_version? def create_version?
!skip_version? && !merge_version? && !append_version? && super !_skip_version? && !merge_version? && !append_version? && super
end end


# Overrides the basal +update_version?+ method to allow the last version of an versioned # Overrides the basal +update_version?+ method to allow the last version of an versioned
# ActiveRecord::Base instance to be updated at the end of an +append_version+ block. # ActiveRecord::Base instance to be updated at the end of an +append_version+ block.
def update_version? def update_version?
append_version? append_version?
end

end
module ClassMethods
# The +skip_version+ block simply allows for updates to be made to an instance of a versioned
# ActiveRecord model while ignoring all new version creation. The <tt>:if</tt> and
# <tt>:unless</tt> conditions (if given) will not be evaulated inside a +skip_version+ block.
#
# When the block closes, the instance is automatically saved, so explicitly saving the
# object within the block is unnecessary.
#
# == Example
#
# user = User.find_by_first_name("Steve")
# user.version # => 1
# user.skip_version do
# user.first_name = "Stephen"
# end
# user.version # => 1
def skip_version
_with_version_flag(:_skip_version) do
yield if block_given?
end end
end

# Used for each control block, the +with_version_flag+ method sets a given variable to
# true and then executes the given block, ensuring that the variable is returned to a nil
# value before returning. This is useful to be certain that one of the control flag
# instance variables isn't inadvertently left in the "on" position by execution within the
# block raising an exception.
def _with_version_flag(flag)
self.send("#{flag}=", true)
yield
ensure
self.send("#{flag}=", nil)
end

end end
end end
end end
27 changes: 26 additions & 1 deletion spec/vestal_versions/control_spec.rb
Expand Up @@ -2,7 +2,7 @@


describe VestalVersions::Control do describe VestalVersions::Control do
let(:user){ User.create(:name => 'Steve Richert') } let(:user){ User.create(:name => 'Steve Richert') }

let(:other_user){ User.create(:name => 'Michael Rossin') }
before do before do
@count = user.versions.count @count = user.versions.count
end end
Expand All @@ -23,6 +23,7 @@


user.versions.count.should == @count user.versions.count.should == @count
end end

end end


shared_examples_for 'a version incrementer' do |method| shared_examples_for 'a version incrementer' do |method|
Expand All @@ -41,11 +42,35 @@


user.versions.count.should == @count + 1 user.versions.count.should == @count + 1
end end

end end


it_should_behave_like 'a version preserver', :skip_version it_should_behave_like 'a version preserver', :skip_version
it_should_behave_like 'a version incrementer', :merge_version it_should_behave_like 'a version incrementer', :merge_version


context "when operating on the class level" do
before do
@count = user.versions.count
@other_user_count = other_user.versions.count
end
it 'skip_version doesn\' create versions on multiple models' do
other_user_count = other_user.versions.count

User.skip_version do
user.update_attribute(:first_name, 'Stephen')
user.update_attribute(:last_name, 'Jobs')
user.update_attribute(:first_name, 'Steve')

other_user.update_attribute(:first_name, 'Stephen')
other_user.update_attribute(:last_name, 'Jobs')
other_user.update_attribute(:first_name, 'Steve')
end
user.versions.count.should == @count
other_user.versions.count.should == @other_user_count
end

end

context 'within a append_version block' do context 'within a append_version block' do


context 'when no versions exist' do context 'when no versions exist' do
Expand Down

0 comments on commit 4fa33c6

Please sign in to comment.