From afa65d8603686a0021275c9a0cd87a85c793d741 Mon Sep 17 00:00:00 2001 From: Stefan Wrobel Date: Thu, 18 Oct 2012 23:18:03 -0700 Subject: [PATCH 1/3] Add ability to set up transitions without specifying from - these will allow transitioning from ANY state --- lib/aasm/supporting_classes/event.rb | 56 +++++++++++++++------------- spec/models/silencer.rb | 5 +++ spec/unit/state_transition_spec.rb | 6 +++ 3 files changed, 42 insertions(+), 25 deletions(-) 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 From 61ca3842587801b8ba929744647da1beaad98333 Mon Sep 17 00:00:00 2001 From: Stefan Wrobel Date: Thu, 18 Oct 2012 23:23:23 -0700 Subject: [PATCH 2/3] Add code climate badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38fa528..301bca3 100644 --- a/README.md +++ b/README.md @@ -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. From 8a15ba6e01f418e064cd2ce81d58a9d9e4f54fa5 Mon Sep 17 00:00:00 2001 From: Stefan Wrobel Date: Thu, 18 Oct 2012 23:29:05 -0700 Subject: [PATCH 3/3] Add detail to readme on the fact that from is optional --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 301bca3..ad5609e 100644 --- a/README.md +++ b/README.md @@ -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 }