Skip to content

Commit

Permalink
Fixes incorrect permitted states
Browse files Browse the repository at this point in the history
Permitted states now returns the correct states when an event has more than one transition, and if a transition uses a guard.
  • Loading branch information
eebs committed Jan 13, 2016
1 parent bdf7e4c commit 2449fcc
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
22 changes: 17 additions & 5 deletions lib/aasm/instance_base.rb
Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions 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
15 changes: 15 additions & 0 deletions spec/unit/inspection_spec.rb
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 2449fcc

Please sign in to comment.