diff --git a/lib/aasm/instance_base.rb b/lib/aasm/instance_base.rb index f7ac8f47..e5d97fea 100644 --- a/lib/aasm/instance_base.rb +++ b/lib/aasm/instance_base.rb @@ -35,11 +35,23 @@ def human_state def states(options={}) if options[:permitted] - # ugliness level 1000 - permitted_event_names = events(:permitted => true).map(&:name) - transitions = @instance.class.aasm(@name).state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) } - tos = transitions.map {|t| t[0] ? t[0].to : nil}.flatten.compact.map(&:to_sym).uniq - @instance.class.aasm(@name).states.select {|s| tos.include?(s.name.to_sym)} + permitted_events = events(:permitted => true) + + # An array of arrays. Each inner array represents the transitions that + # transition from the current state for an event + event_transitions = permitted_events.map {|e| e.transitions_from_state(current_state) } + + # An array of symbols that are possible :to transition states + to_state_names = event_transitions.map do |transitions| + return nil if transitions.empty? + + # Return the :to state of the first transition that is allowed or nil + transition = transitions.find { |t| t.allowed?(@instance) } + transition ? transition.to : nil + end.flatten.compact.uniq + + # Select states that are in to_state_names + @instance.class.aasm(@name).states.select {|s| to_state_names.include?(s.name)} else @instance.class.aasm(@name).states end diff --git a/spec/models/multi_transitioner.rb b/spec/models/multi_transitioner.rb new file mode 100644 index 00000000..db76dc1c --- /dev/null +++ b/spec/models/multi_transitioner.rb @@ -0,0 +1,24 @@ +class MultiTransitioner + include AASM + + attr_accessor :can_run + + def initialize + @can_run = false + end + + aasm do + state :sleeping, :initial => true + state :running + state :dancing + + event :start do + transitions :from => :sleeping, :to => :running, guard: :runnable? + transitions :from => :sleeping, :to => :dancing + end + end + + def runnable? + @can_run + end +end diff --git a/spec/unit/inspection_spec.rb b/spec/unit/inspection_spec.rb index ccbe53ab..d7e54998 100644 --- a/spec/unit/inspection_spec.rb +++ b/spec/unit/inspection_spec.rb @@ -17,6 +17,7 @@ context "instance level inspection" do let(:foo) { Foo.new } let(:two) { FooTwo.new } + let(:multi) { MultiTransitioner.new } it "delivers all states" do states = foo.aasm.states @@ -37,11 +38,13 @@ states = two.aasm.states expect(states).to include(:open) expect(states).to include(:closed) + expect(states).to include(:final) expect(states).to include(:foo) states = two.aasm.states(:permitted => true) expect(states).to include(:closed) expect(states).not_to include(:open) + expect(states).not_to include(:final) two.close expect(two.aasm.states(:permitted => true)).to be_empty @@ -54,6 +57,18 @@ foo.close expect(foo.aasm.events).to be_empty end + + it "delivers permitted states when multiple transitions are defined" do + multi.can_run = false + states = multi.aasm.states(:permitted => true) + expect(states).to_not include(:running) + expect(states).to include(:dancing) + + multi.can_run = true + states = multi.aasm.states(:permitted => true) + expect(states).to include(:running) + expect(states).to_not include(:dancing) + end end it 'should list states in the order they have been defined' do