Merge pull request #4 from stacksocial/master

Allow transitions without from, which will allow transitioning from ANY state
This commit is contained in:
Thorsten Böttger 2012-10-19 00:19:00 -07:00
commit 7bc5180c5d
4 changed files with 45 additions and 27 deletions

View File

@ -1,4 +1,4 @@
# AASM - Ruby state machines [![Build Status](https://secure.travis-ci.org/aasm/aasm.png)](http://travis-ci.org/aasm/aasm)
# AASM - Ruby state machines [![Build Status](https://secure.travis-ci.org/aasm/aasm.png)](http://travis-ci.org/aasm/aasm) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/aasm/aasm)
This package contains AASM, a library for adding finite state machines to Ruby classes.
@ -101,8 +101,9 @@ This example uses a few of the more complex features available.
transitions :to => :intimate, :from => [:dating], :guard => :drunk?
end
# Will allow transitioning from any state if guard allows it
event :get_married do
transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood?
transitions :to => :married, :guard => :willing_to_give_up_manhood?
end
end
aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }

View File

@ -13,34 +13,11 @@ module AASM
# executes the transition guards to determine if a transition is even
# an option given current conditions.
def may_fire?(obj, to_state=nil, *args)
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
return false if transitions.size == 0
result = false
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj, *args)
result = true
break
end
end
result
_fire(obj, true, to_state, *args) # true indicates test firing
end
def fire(obj, to_state=nil, *args)
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
return nil if transitions.size == 0
next_state = nil
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj, *args)
next_state = to_state || Array(transition.to).first
transition.execute(obj, *args)
break
end
end
next_state
_fire(obj, false, to_state, *args) # false indicates this is not a test (fire!)
end
def transitions_from_state?(state)
@ -112,6 +89,32 @@ module AASM
self
end
# Execute if test? == false, otherwise return true/false depending on whether it would fire
def _fire(obj, test, to_state=nil, *args)
if @transitions.map(&:from).any?
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
return nil if transitions.size == 0
else
transitions = @transitions
end
result = test ? false : nil
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj, *args)
if test
result = true
else
result = to_state || Array(transition.to).first
transition.execute(obj, *args)
end
break
end
end
result
end
def _fire_callbacks(action, record)
case action
when Symbol, String
@ -122,9 +125,12 @@ module AASM
end
def transitions(trans_opts)
# Create a separate transition for each from state to the given state
Array(trans_opts[:from]).each do |s|
@transitions << AASM::SupportingClasses::StateTransition.new(trans_opts.merge({:from => s.to_sym}))
end
# Create a transition if to is specified without from (transitions from ANY state)
@transitions << AASM::SupportingClasses::StateTransition.new(trans_opts) if @transitions.empty? && trans_opts[:to]
end
end

View File

@ -9,9 +9,14 @@ class Silencer
event :cry do
transitions :from => :silent, :to => :crying
end
event :smile do
transitions :from => :crying, :to => :smiling
end
event :smile_any do
transitions :to => :smiling
end
end
end

View File

@ -19,6 +19,12 @@ describe 'transitions' do
sub.smile!.should be_false
sub.should be_silent
end
it 'should not raise an exception when from is nil even if whiny' do
silencer = Silencer.new
silencer.smile_any!.should be_true
silencer.should be_smiling
end
end