fire guards only once per transition #184

This commit is contained in:
Thorsten Böttger 2014-11-19 23:34:39 +01:00
parent 8ce7062aa8
commit aac1ece778
6 changed files with 20 additions and 18 deletions

View File

@ -4,6 +4,10 @@
* `aasm_human_event_name` is deprecated, use `aasm.human_event_name` instead * `aasm_human_event_name` is deprecated, use `aasm.human_event_name` instead
## 4.0.1
* fire guards only once per transition (see [issue #184](https://github.com/aasm/aasm/issues/184) for details)
## 4.0.0 ## 4.0.0
* support `if` and `unless` guard syntax: (see [issue #179](https://github.com/aasm/aasm/issues/179) and [issue #181](https://github.com/aasm/aasm/issues/181)), thanks to [@bigtunacan](https://github.com/bigtunacan) * support `if` and `unless` guard syntax: (see [issue #179](https://github.com/aasm/aasm/issues/179) and [issue #181](https://github.com/aasm/aasm/issues/181)), thanks to [@bigtunacan](https://github.com/bigtunacan)

View File

@ -141,14 +141,12 @@ Here you can see a list of all possible callbacks, together with their order of
```ruby ```ruby
begin begin
event before event before
event guards # test run event guards
transition guards # test run transition guards
old_state before_exit old_state before_exit
old_state exit old_state exit
new_state before_enter new_state before_enter
new_state enter new_state enter
event guards
transition guards
...update state... ...update state...
transition after transition after
event success # if persist successful event success # if persist successful

View File

@ -86,11 +86,11 @@ private
*process_args(event, aasm.current_state, *args) *process_args(event, aasm.current_state, *args)
) )
if event.may_fire?(self, *args) if may_fire_to = event.may_fire?(self, *args)
old_state.fire_callbacks(:before_exit, self) old_state.fire_callbacks(:before_exit, self)
old_state.fire_callbacks(:exit, self) # TODO: remove for AASM 4? old_state.fire_callbacks(:exit, self) # TODO: remove for AASM 4?
if new_state_name = event.fire(self, *args) if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
aasm_fired(event, old_state, new_state_name, options, *args, &block) aasm_fired(event, old_state, new_state_name, options, *args, &block)
else else
aasm_failed(event_name, old_state) aasm_failed(event_name, old_state)

View File

@ -19,11 +19,11 @@ module AASM
# executes the transition guards to determine if a transition is even # executes the transition guards to determine if a transition is even
# an option given current conditions. # an option given current conditions.
def may_fire?(obj, to_state=nil, *args) def may_fire?(obj, to_state=nil, *args)
_fire(obj, true, to_state, *args) # true indicates test firing _fire(obj, {:test_only => true}, to_state, *args) # true indicates test firing
end end
def fire(obj, to_state=nil, *args) def fire(obj, options={}, to_state=nil, *args)
_fire(obj, false, to_state, *args) # false indicates this is not a test (fire!) _fire(obj, options, to_state, *args) # false indicates this is not a test (fire!)
end end
def transitions_from_state?(state) def transitions_from_state?(state)
@ -86,8 +86,8 @@ module AASM
end end
# Execute if test == false, otherwise return true/false depending on whether it would fire # Execute if test == false, otherwise return true/false depending on whether it would fire
def _fire(obj, test, to_state=nil, *args) def _fire(obj, options={}, to_state=nil, *args)
result = test ? false : nil result = options[:test_only] ? false : nil
if @transitions.map(&:from).any? if @transitions.map(&:from).any?
transitions = @transitions.select { |t| t.from == obj.aasm.current_state } transitions = @transitions.select { |t| t.from == obj.aasm.current_state }
return result if transitions.size == 0 return result if transitions.size == 0
@ -106,11 +106,11 @@ 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 transition.allowed?(obj, *args) if Array(transition.to).include?(options[:may_fire]) || transition.allowed?(obj, *args)
if test result = to_state || Array(transition.to).first
result = true if options[:test_only]
# result = true
else else
result = to_state || Array(transition.to).first
transition.execute(obj, *args) transition.execute(obj, *args)
end end

View File

@ -11,8 +11,8 @@ describe 'callbacks for the new DSL' do
expect(callback).to receive(:transition_guard).once.ordered.and_return(true) expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes
expect(callback).to receive(:exit_open).once.ordered expect(callback).to receive(:exit_open).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(true) # expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
expect(callback).to receive(:transitioning).once.ordered expect(callback).to receive(:transitioning).once.ordered
expect(callback).to receive(:before_enter_closed).once.ordered expect(callback).to receive(:before_enter_closed).once.ordered
expect(callback).to receive(:enter_closed).once.ordered expect(callback).to receive(:enter_closed).once.ordered

View File

@ -101,7 +101,7 @@ describe 'firing an event' do
obj = double('object', :aasm => double('aasm', :current_state => :open)) obj = double('object', :aasm => double('aasm', :current_state => :open))
expect(obj).to receive(:guard_fn).with('arg1', 'arg2').and_return(true) expect(obj).to receive(:guard_fn).with('arg1', 'arg2').and_return(true)
expect(event.fire(obj, nil, 'arg1', 'arg2')).to eq(:closed) expect(event.fire(obj, {}, nil, 'arg1', 'arg2')).to eq(:closed)
end end
end end