diff --git a/lib/aasm/supporting_classes/event.rb b/lib/aasm/supporting_classes/event.rb index 3ac2ba8..0ae4acf 100644 --- a/lib/aasm/supporting_classes/event.rb +++ b/lib/aasm/supporting_classes/event.rb @@ -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 diff --git a/spec/models/silencer.rb b/spec/models/silencer.rb index b679f80..8e76e83 100644 --- a/spec/models/silencer.rb +++ b/spec/models/silencer.rb @@ -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 diff --git a/spec/unit/state_transition_spec.rb b/spec/unit/state_transition_spec.rb index 513e17c..4d1ec84 100644 --- a/spec/unit/state_transition_spec.rb +++ b/spec/unit/state_transition_spec.rb @@ -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