1
0
Fork 0
mirror of https://github.com/aasm/aasm synced 2023-03-27 23:22:41 -04:00

bugfix: fire guards only once per transition, part 2 #187

This commit is contained in:
Thorsten Böttger 2014-11-25 22:23:23 +01:00
parent 6fbdefd620
commit 9aa50f9c11
4 changed files with 113 additions and 15 deletions

View file

@ -5,8 +5,9 @@
* `aasm_column` has been removed. Use `aasm.attribute_name` instead * `aasm_column` has been removed. Use `aasm.attribute_name` instead
* `aasm_human_event_name` has been removed. Use `aasm.human_event_name` instead * `aasm_human_event_name` has been removed. Use `aasm.human_event_name` instead
## 4.0.x (not yet released) ## 4.0.3
* bugfix: fire guards only once per transition, part 2 (see [issue #187](https://github.com/aasm/aasm/issues/187) for details)
* `aasm_column` is deprecated. Use `aasm.attribute_name` instead * `aasm_column` is deprecated. Use `aasm.attribute_name` instead
## 4.0.2 ## 4.0.2

View file

@ -106,7 +106,8 @@ module AASM
transitions.each do |transition| transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state) next if to_state and !Array(transition.to).include?(to_state)
if Array(transition.to).include?(options[:may_fire]) || transition.allowed?(obj, *args) if (options.key?(:may_fire) && Array(transition.to).include?(options[:may_fire])) ||
(!options.key?(:may_fire) && transition.allowed?(obj, *args))
result = to_state || Array(transition.to).first result = to_state || Array(transition.to).first
if options[:test_only] if options[:test_only]
# result = true # result = true

View file

@ -0,0 +1,65 @@
module Callbacks
class MultipleTransitionsTransitionGuard
include AASM
def initialize(options={})
@fail_event_guard = options[:fail_event_guard]
@fail_transition_guard = options[:fail_transition_guard]
@log = options[:log]
end
aasm do
state :open, :initial => true,
:before_enter => :before_enter_open,
:enter => :enter_open,
:after_enter => :after_enter_open,
:before_exit => :before_exit_open,
:exit => :exit_open,
:after_exit => :after_exit_open
state :closed,
:before_enter => :before_enter_closed,
:enter => :enter_closed,
:after_enter => :after_enter_closed,
:before_exit => :before_exit_closed,
:exit => :exit_closed,
:after_exit => :after_exit_closed
state :failed
event :close, :before => :before, :after => :after, :guard => :event_guard do
transitions :to => :closed, :from => [:open], :guard => :transition_guard, :after => :transitioning
transitions :to => :failed, :from => [:open]
end
event :open, :before => :before, :after => :after do
transitions :to => :open, :from => :closed
end
end
def log(text)
puts text if @log
end
def before_enter_open; log('before_enter_open'); end
def enter_open; log('enter_open'); end
def before_exit_open; log('before_exit_open'); end
def after_enter_open; log('after_enter_open'); end
def exit_open; log('exit_open'); end
def after_exit_open; log('after_exit_open'); end
def before_enter_closed; log('before_enter_closed'); end
def enter_closed; log('enter_closed'); end
def before_exit_closed; log('before_exit_closed'); end
def exit_closed; log('exit_closed'); end
def after_enter_closed; log('after_enter_closed'); end
def after_exit_closed; log('after_exit_closed'); end
def event_guard; log('event_guard'); !@fail_event_guard; end
def transition_guard; log('transition_guard'); !@fail_transition_guard; end
def transitioning; log('transitioning'); end
def before; log('before'); end
def after; log('after'); end
end
end

View file

@ -49,9 +49,11 @@ describe 'callbacks for the new DSL' do
context "if the transition guard fails" do context "if the transition guard fails" do
it "does not run any state callback if guard is defined inline" do it "does not run any state callback if guard is defined inline" do
callback = CallbackNewDsl.new show_debug_log = false
callback = CallbackNewDsl.new(:log => show_debug_log, :fail_transition_guard => true)
callback.aasm.current_state callback.aasm.current_state
unless show_debug_log
expect(callback).to receive(:before).once.ordered expect(callback).to receive(:before).once.ordered
expect(callback).to receive(:event_guard).once.ordered.and_return(true) expect(callback).to receive(:event_guard).once.ordered.and_return(true)
expect(callback).to receive(:transition_guard).once.ordered.and_return(false) expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
@ -64,12 +66,41 @@ describe 'callbacks for the new DSL' do
expect(callback).to_not receive(:after_exit_open) expect(callback).to_not receive(:after_exit_open)
expect(callback).to_not receive(:after_enter_closed) expect(callback).to_not receive(:after_enter_closed)
expect(callback).to_not receive(:after) expect(callback).to_not receive(:after)
end
expect { expect {
callback.close! callback.close!
}.to raise_error(AASM::InvalidTransition) }.to raise_error(AASM::InvalidTransition)
end end
it "does not run transition_guard twice for multiple permitted transitions" do
require 'models/callbacks/multiple_transitions_transition_guard'
show_debug_log = false
callback = Callbacks::MultipleTransitionsTransitionGuard.new(:log => show_debug_log, :fail_transition_guard => true)
callback.aasm.current_state
unless show_debug_log
expect(callback).to receive(:before).once.ordered
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
expect(callback).to receive(:before_exit_open).once.ordered
expect(callback).to receive(:exit_open).once.ordered
expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
expect(callback).to receive(:after_exit_open).once.ordered
expect(callback).to receive(:after).once.ordered
expect(callback).to_not receive(:transitioning)
expect(callback).to_not receive(:before_enter_closed)
expect(callback).to_not receive(:enter_closed)
expect(callback).to_not receive(:after_enter_closed)
end
callback.close!
expect(callback.aasm.current_state).to eql :failed
end
it "does not run any state callback if guard is defined with block" do it "does not run any state callback if guard is defined with block" do
callback = GuardWithinBlock.new #(:log => true, :fail_transition_guard => true) callback = GuardWithinBlock.new #(:log => true, :fail_transition_guard => true)
callback.aasm.current_state callback.aasm.current_state