diff --git a/lib/aasm.rb b/lib/aasm.rb index bbf868d..bcc57e3 100644 --- a/lib/aasm.rb +++ b/lib/aasm.rb @@ -49,12 +49,12 @@ module AASM sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block) end - define_method("#{name.to_s}!") do - aasm_fire_event(name, true) + define_method("#{name.to_s}!") do |*args| + aasm_fire_event(name, true, *args) end - define_method("#{name.to_s}") do - aasm_fire_event(name, false) + define_method("#{name.to_s}") do |*args| + aasm_fire_event(name, false, *args) end end @@ -111,10 +111,10 @@ module AASM self.class.aasm_states.find {|s| s == name} end - def aasm_fire_event(name, persist) + def aasm_fire_event(name, persist, *args) aasm_state_object_for_state(aasm_current_state).call_action(:exit, self) - new_state = self.class.aasm_events[name].fire(self) + new_state = self.class.aasm_events[name].fire(self, *args) unless new_state.nil? aasm_state_object_for_state(new_state).call_action(:enter, self) diff --git a/lib/event.rb b/lib/event.rb index 6ec86b5..2e943ef 100644 --- a/lib/event.rb +++ b/lib/event.rb @@ -12,14 +12,16 @@ module AASM instance_eval(&block) if block end - def fire(obj) + def fire(obj, to_state=nil, *args) transitions = @transitions.select { |t| t.from == obj.aasm_current_state } raise AASM::InvalidTransition if transitions.size == 0 next_state = nil transitions.each do |transition| + next if to_state and !Array(transition.to).include?(to_state) if transition.perform(obj) - next_state = transition.to + next_state = to_state || Array(transition.to).first + transition.execute(obj, *args) break end end diff --git a/lib/state_transition.rb b/lib/state_transition.rb index 5955f34..f82f5d4 100644 --- a/lib/state_transition.rb +++ b/lib/state_transition.rb @@ -4,7 +4,7 @@ module AASM attr_reader :from, :to, :opts def initialize(opts) - @from, @to, @guard = opts[:from], opts[:to], opts[:guard] + @from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition] @opts = opts end @@ -19,6 +19,15 @@ module AASM end end + def execute(obj, *args) + case @on_transition + when Symbol, String + obj.send(@on_transition, *args) + when Proc + @on_transition.call(obj, *args) + end + end + def ==(obj) @from == obj.from && @to == obj.to end diff --git a/spec/unit/aasm_spec.rb b/spec/unit/aasm_spec.rb index 7d7bf90..33e9d9b 100644 --- a/spec/unit/aasm_spec.rb +++ b/spec/unit/aasm_spec.rb @@ -270,3 +270,72 @@ describe Baz do Baz.aasm_events.should == Bar.aasm_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