Skip to content

Commit

Permalink
Merge pull request #308 from eebs/permitted-states-guarded-transitions
Browse files Browse the repository at this point in the history
Fixes incorrect permitted states
  • Loading branch information
alto committed Feb 4, 2016
2 parents e5e663e + 2449fcc commit 862e2cb
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 862e2cb

Please sign in to comment.