1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

converted tests for more complex state transitions

This commit is contained in:
rick 2008-06-28 11:01:40 -07:00
parent 74cb056986
commit a9d9ca16c7
5 changed files with 142 additions and 123 deletions

View file

@ -3,9 +3,8 @@ module ActiveModel
class Event
attr_reader :name, :success
def initialize(name, options = {}, &block)
@name, @transitions = name, []
machine = options.delete(:machine)
def initialize(machine, name, options = {}, &block)
@machine, @name, @transitions = machine, name, []
if machine
machine.klass.send(:define_method, "#{name.to_s}!") do |*args|
machine.fire_event(name, self, true, *args)
@ -19,7 +18,7 @@ module ActiveModel
end
def fire(obj, to_state = nil, *args)
transitions = @transitions.select { |t| t.from == obj.current_state }
transitions = @transitions.select { |t| t.from == obj.current_state(@machine ? @machine.name : nil) }
raise InvalidTransition if transitions.size == 0
next_state = nil

View file

@ -19,12 +19,21 @@ module ActiveModel
self
end
def fire_event(name, record, persist, *args)
state_index[record.current_state].call_action(:exit, record)
if new_state = @events[name].fire(record, *args)
def fire_event(event, record, persist, *args)
state_index[record.current_state(@name)].call_action(:exit, record)
if new_state = @events[event].fire(record, *args)
state_index[new_state].call_action(:enter, record)
if record.respond_to?(event_fired_callback)
record.send(event_fired_callback, record.current_state, new_state)
end
record.current_state(@name, new_state)
else
if record.respond_to?(event_failed_callback)
record.send(event_failed_callback, event)
end
false
end
end
@ -37,13 +46,22 @@ module ActiveModel
events = @events.values.select { |event| event.transitions_from_state?(state) }
events.map! { |event| event.name }
end
private
def state(name, options = {})
@states << (state_index[name] ||= State.new(name, :machine => self)).update(options)
end
def event(name, options = {}, &block)
(@events[name] ||= Event.new(name, :machine => self)).update(options, &block)
(@events[name] ||= Event.new(self, name)).update(options, &block)
end
def event_fired_callback
@event_fired_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_fired'
end
def event_failed_callback
@event_failed_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_failed'
end
end
end

View file

@ -7,7 +7,7 @@ class EventTest < ActiveModel::TestCase
end
def new_event
@event = ActiveModel::StateMachine::Event.new(@name, {:success => @success}) do
@event = ActiveModel::StateMachine::Event.new(nil, @name, {:success => @success}) do
transitions :to => :closed, :from => [:open, :received]
end
end
@ -31,7 +31,7 @@ end
class EventBeingFiredTest < ActiveModel::TestCase
test 'should raise an AASM::InvalidTransition error if the transitions are empty' do
event = ActiveModel::StateMachine::Event.new(:event)
event = ActiveModel::StateMachine::Event.new(nil, :event)
assert_raises ActiveModel::StateMachine::InvalidTransition do
event.fire(nil)
@ -39,7 +39,7 @@ class EventBeingFiredTest < ActiveModel::TestCase
end
test 'should return the state of the first matching transition it finds' do
event = ActiveModel::StateMachine::Event.new(:event) do
event = ActiveModel::StateMachine::Event.new(nil, :event) do
transitions :to => :closed, :from => [:open, :received]
end

View file

@ -36,6 +36,8 @@ class StateMachineMachineTest < ActiveModel::TestCase
end
test "finds events for given state" do
assert_equal [:shutdown, :timeout], MachineTestSubject.state_machine.events_for(:open)
events = MachineTestSubject.state_machine.events_for(:open)
assert events.include?(:shutdown)
assert events.include?(:timeout)
end
end

View file

@ -186,48 +186,50 @@ end
# foo.aasm_current_state
# end
#end
#describe AASM, '- event callbacks' do
# it 'should call aasm_event_fired if defined and successful for bang fire' do
# foo = Foo.new
# def foo.aasm_event_fired(from, to)
# end
#
# foo.should_receive(:aasm_event_fired)
#
# foo.close!
# end
#
# it 'should call aasm_event_fired if defined and successful for non-bang fire' do
# foo = Foo.new
# def foo.aasm_event_fired(from, to)
# end
#
# foo.should_receive(:aasm_event_fired)
#
# foo.close
# end
#
# it 'should call aasm_event_failed if defined and transition failed for bang fire' do
# foo = Foo.new
# def foo.aasm_event_failed(event)
# end
#
# foo.should_receive(:aasm_event_failed)
#
# foo.null!
# end
#
# it 'should call aasm_event_failed if defined and transition failed for non-bang fire' do
# foo = Foo.new
# def foo.aasm_event_failed(event)
# end
#
# foo.should_receive(:aasm_event_failed)
#
# foo.null
# end
#end
uses_mocha 'StateMachineEventCallbacksTest' do
class StateMachineEventCallbacksTest < ActiveModel::TestCase
test 'should call aasm_event_fired if defined and successful for bang fire' do
subj = StateMachineSubject.new
def subj.aasm_event_fired(from, to)
end
subj.expects(:event_fired)
subj.close!
end
test 'should call aasm_event_fired if defined and successful for non-bang fire' do
subj = StateMachineSubject.new
def subj.aasm_event_fired(from, to)
end
subj.expects(:event_fired)
subj.close
end
test 'should call aasm_event_failed if defined and transition failed for bang fire' do
subj = StateMachineSubject.new
def subj.event_failed(event)
end
subj.expects(:event_failed)
subj.null!
end
test 'should call aasm_event_failed if defined and transition failed for non-bang fire' do
subj = StateMachineSubject.new
def subj.aasm_event_failed(event)
end
subj.expects(:event_failed)
subj.null
end
end
end
uses_mocha 'StateMachineStateActionsTest' do
class StateMachineStateActionsTest < ActiveModel::TestCase
@ -254,72 +256,70 @@ class StateMachineInheritanceTest < ActiveModel::TestCase
assert_equal StateMachineSubject.state_machine.events, StateMachineSubjectSubclass.state_machine.events
end
end
#
#
#class ChetanPatil
# include AASM
# aasm_initial_state :sleeping
# aasm_state :sleeping
# aasm_state :showering
# aasm_state :working
# aasm_state :dating
#
# aasm_event :wakeup do
# transitions :from => :sleeping, :to => [:showering, :working]
# end
#
# aasm_event :dress do
# transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
# transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
# end
#
# def wear_clothes(shirt_color, trouser_type)
# end
#end
#
#
#describe ChetanPatil do
# it 'should transition to specified next state (sleeping to showering)' do
# cp = ChetanPatil.new
# cp.wakeup! :showering
#
# cp.aasm_current_state.should == :showering
# end
#
# it 'should transition to specified next state (sleeping to working)' do
# cp = ChetanPatil.new
# cp.wakeup! :working
#
# cp.aasm_current_state.should == :working
# end
#
# it 'should transition to default (first or showering) state' do
# cp = ChetanPatil.new
# cp.wakeup!
#
# cp.aasm_current_state.should == :showering
# end
#
# it 'should transition to default state when on_transition invoked' do
# cp = ChetanPatil.new
# cp.dress!(nil, 'purple', 'dressy')
#
# cp.aasm_current_state.should == :working
# end
#
# it 'should call on_transition method with args' do
# cp = ChetanPatil.new
# cp.wakeup! :showering
#
# cp.should_receive(:wear_clothes).with('blue', 'jeans')
# cp.dress! :working, 'blue', 'jeans'
# end
#
# it 'should call on_transition proc' do
# cp = ChetanPatil.new
# cp.wakeup! :showering
#
# cp.should_receive(:wear_clothes).with('purple', 'slacks')
# cp.dress!(:dating, 'purple', 'slacks')
# end
#end
class StateMachineSubject
state_machine :chetan_patil, :initial => :sleeping do
state :sleeping
state :showering
state :working
state :dating
event :wakeup do
transitions :from => :sleeping, :to => [:showering, :working]
end
event :dress do
transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
end
end
def wear_clothes(shirt_color, trouser_type)
end
end
class StateMachineWithComplexTransitionsTest < ActiveModel::TestCase
def setup
@subj = StateMachineSubject.new
end
test 'transitions to specified next state (sleeping to showering)' do
@subj.wakeup! :showering
assert_equal :showering, @subj.current_state(:chetan_patil)
end
test 'transitions to specified next state (sleeping to working)' do
@subj.wakeup! :working
assert_equal :working, @subj.current_state(:chetan_patil)
end
test 'transitions to default (first or showering) state' do
@subj.wakeup!
assert_equal :showering, @subj.current_state(:chetan_patil)
end
test 'transitions to default state when on_transition invoked' do
@subj.dress!(nil, 'purple', 'dressy')
assert_equal :working, @subj.current_state(:chetan_patil)
end
uses_mocha "StateMachineWithComplexTransitionsTest on_transition tests" do
test 'calls on_transition method with args' do
@subj.wakeup! :showering
@subj.expects(:wear_clothes).with('blue', 'jeans')
@subj.dress! :working, 'blue', 'jeans'
end
test 'calls on_transition proc' do
@subj.wakeup! :showering
@subj.expects(:wear_clothes).with('purple', 'slacks')
@subj.dress!(:dating, 'purple', 'slacks')
end
end
end