bugfix: really support block-guards (defined within a transition block) #186

This commit is contained in:
Thorsten Böttger 2014-11-24 13:46:15 +01:00
parent 9701a447d3
commit 4fdc2a73b5
5 changed files with 114 additions and 22 deletions

View File

@ -4,6 +4,10 @@
* `aasm_human_event_name` is deprecated, use `aasm.human_event_name` instead
## 4.0.2
* bugfix: really support block-guards (defined within a transition block) (see [issue #186](https://github.com/aasm/aasm/issues/186) for details)
## 4.0.1
* fire guards only once per transition (see [issue #184](https://github.com/aasm/aasm/issues/184) for details)

View File

@ -57,15 +57,15 @@ module AASM
end
## DSL interface
def transitions(definitions=nil)
def transitions(definitions=nil, &block)
if definitions # define new transitions
# Create a separate transition for each from-state to the given state
Array(definitions[:from]).each do |s|
@transitions << AASM::Transition.new(attach_event_guards(definitions.merge(:from => s.to_sym)))
@transitions << AASM::Transition.new(attach_event_guards(definitions.merge(:from => s.to_sym)), &block)
end
# Create a transition if :to is specified without :from (transitions from ANY state)
if @transitions.empty? && definitions[:to]
@transitions << AASM::Transition.new(attach_event_guards(definitions))
@transitions << AASM::Transition.new(attach_event_guards(definitions), &block)
end
end
@transitions

View File

@ -10,7 +10,7 @@ module AASM
@from = opts[:from]
@to = opts[:to]
@guards = Array(opts[:guard] || opts[:guards] || opts[:if])
@guards = Array(opts[:guards]) + Array(opts[:guard]) + Array(opts[:if])
@unless = Array(opts[:unless]) #TODO: This could use a better name
if opts[:on_transition]

View File

@ -0,0 +1,64 @@
class GuardWithinBlock
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
event :close, :before => :before, :after => :after, :guard => :event_guard do
transitions :to => :closed, :from => [:open], :after => :transitioning do
guard do
transition_guard
end
end
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

View File

@ -47,26 +47,50 @@ describe 'callbacks for the new DSL' do
}.to raise_error(AASM::InvalidTransition)
end
it "does not run any state callback if the transition guard fails" do
callback = CallbackNewDsl.new
callback.aasm.current_state
context "if the transition guard fails" do
it "does not run any state callback if guard is defined inline" do
callback = CallbackNewDsl.new
callback.aasm.current_state
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_not receive(:before_exit_open)
expect(callback).to_not receive(:exit_open)
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(:aasm_write_state)
expect(callback).to_not receive(:after_exit_open)
expect(callback).to_not receive(:after_enter_closed)
expect(callback).to_not receive(:after)
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_not receive(:before_exit_open)
expect(callback).to_not receive(:exit_open)
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(:aasm_write_state)
expect(callback).to_not receive(:after_exit_open)
expect(callback).to_not receive(:after_enter_closed)
expect(callback).to_not receive(:after)
expect {
callback.close!
}.to raise_error(AASM::InvalidTransition)
expect {
callback.close!
}.to raise_error(AASM::InvalidTransition)
end
it "does not run any state callback if guard is defined with block" do
callback = GuardWithinBlock.new #(:log => true, :fail_transition_guard => true)
callback.aasm.current_state
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_not receive(:before_exit_open)
expect(callback).to_not receive(:exit_open)
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(:aasm_write_state)
expect(callback).to_not receive(:after_exit_open)
expect(callback).to_not receive(:after_enter_closed)
expect(callback).to_not receive(:after)
expect {
callback.close!
}.to raise_error(AASM::InvalidTransition)
end
end
it "should properly pass arguments" do