aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/lib/active_model/state_machine/event.rb7
-rw-r--r--activemodel/lib/active_model/state_machine/machine.rb26
-rw-r--r--activemodel/test/state_machine/event_test.rb6
-rw-r--r--activemodel/test/state_machine/machine_test.rb4
-rw-r--r--activemodel/test/state_machine_test.rb222
5 files changed, 142 insertions, 123 deletions
diff --git a/activemodel/lib/active_model/state_machine/event.rb b/activemodel/lib/active_model/state_machine/event.rb
index cc7d563214..ea4df343de 100644
--- a/activemodel/lib/active_model/state_machine/event.rb
+++ b/activemodel/lib/active_model/state_machine/event.rb
@@ -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
diff --git a/activemodel/lib/active_model/state_machine/machine.rb b/activemodel/lib/active_model/state_machine/machine.rb
index 1da48290c5..53ce71794f 100644
--- a/activemodel/lib/active_model/state_machine/machine.rb
+++ b/activemodel/lib/active_model/state_machine/machine.rb
@@ -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
diff --git a/activemodel/test/state_machine/event_test.rb b/activemodel/test/state_machine/event_test.rb
index 01f3464cf2..7db4f8d887 100644
--- a/activemodel/test/state_machine/event_test.rb
+++ b/activemodel/test/state_machine/event_test.rb
@@ -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
diff --git a/activemodel/test/state_machine/machine_test.rb b/activemodel/test/state_machine/machine_test.rb
index 64dea42b1f..2cdfcd9554 100644
--- a/activemodel/test/state_machine/machine_test.rb
+++ b/activemodel/test/state_machine/machine_test.rb
@@ -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 \ No newline at end of file
diff --git a/activemodel/test/state_machine_test.rb b/activemodel/test/state_machine_test.rb
index 963ce84248..2f08b522d9 100644
--- a/activemodel/test/state_machine_test.rb
+++ b/activemodel/test/state_machine_test.rb
@@ -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 \ No newline at end of file
+
+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 \ No newline at end of file