diff --git a/lib/aasm.rb b/lib/aasm.rb index 59933e9..b16bce5 100644 --- a/lib/aasm.rb +++ b/lib/aasm.rb @@ -19,21 +19,21 @@ module AASM alias :initial_state :aasm_initial_state= def state(name, options={}) + self.aasm_initial_state = name unless self.aasm_initial_state + define_method("#{name.to_s}?") do aasm_current_state == name end - self.aasm_initial_state = name unless self.aasm_initial_state end - + def event(name, &block) + aasm_events[name] = AASM::SupportingClasses::Event.new(name, &block) + define_method("#{name.to_s}!") do new_state = self.class.aasm_events[name].fire(self) self.aasm_current_state = new_state nil end - - aasm_events[name] = AASM::SupportingClasses::Event.new(name, &block) - # Error if event defines no transitions? end def aasm_events @@ -41,7 +41,9 @@ module AASM end end + # Instance methods def aasm_current_state + # Persistance? This won't work for activerecord objects @aasm_current_state || self.class.aasm_initial_state end diff --git a/lib/event.rb b/lib/event.rb index 58d8300..bc4f960 100644 --- a/lib/event.rb +++ b/lib/event.rb @@ -16,7 +16,7 @@ module AASM if transitions.size == 0 raise AASM::InvalidTransition else - transitions.first.to # Should be performing here - but what's involved + transitions.first.to end end diff --git a/lib/state_factory.rb b/lib/state_factory.rb deleted file mode 100644 index f6d3242..0000000 --- a/lib/state_factory.rb +++ /dev/null @@ -1,14 +0,0 @@ -module AASM - module SupportingClasses - class StateFactory - def self.create(name, opts={}) - @states ||= {} - @states[name] ||= State.new(name, opts) - end - - def self.[](name) - @states[name] - end - end - end -end diff --git a/lib/state_transition.rb b/lib/state_transition.rb index 23c3c58..0f96006 100644 --- a/lib/state_transition.rb +++ b/lib/state_transition.rb @@ -8,30 +8,6 @@ module AASM @opts = opts end -# def guard(obj) -# # TODO should probably not be using obj -# @guard ? obj.send(:run_transition_action, @guard) : true -# end - -# def perform(obj) -# # TODO should probably not be using obj -# return false unless guard(obj) -# loopback = obj.current_state == to -# # TODO Maybe State should be a factory? -# # State[:open] => returns same instance of State.new(:open) -# next_state = StateFactory[to] -# old_state = StateFactory[obj.current_state] -# old_state = states[obj.current_state] - -# next_state.entering(obj) unless loopback - -# obj.update_attribute(obj.class.state_column, to.to_s) - -# next_state.entered(obj) unless loopback -# old_state.exited(obj) unless loopback -# true -# end - def ==(obj) @from == obj.from && @to == obj.to end diff --git a/spec/aasm_spec.rb b/spec/aasm_spec.rb index 4d08561..75a6e7c 100644 --- a/spec/aasm_spec.rb +++ b/spec/aasm_spec.rb @@ -1,6 +1,5 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'aasm') require File.join(File.dirname(__FILE__), '..', 'lib', 'state') -require File.join(File.dirname(__FILE__), '..', 'lib', 'state_factory') class Foo include AASM @@ -9,6 +8,7 @@ class Foo state :closed event :close do + transitions :to => :closed, :from => [:open] end end @@ -45,12 +45,6 @@ describe AASM, '- instance level definitions' do it 'should define an event! inance method' do @foo.should respond_to(:close!) end - - # TODO This isn't necessarily "in play" just yet - #it 'using the state macro should create a new State object' do - # AASM::SupportingClasses::State.should_receive(:new).with(:open, {}) - # Foo.state :open - #end end describe AASM, '- initial states' do @@ -75,3 +69,30 @@ describe AASM, '- initial states' do @bar.aasm_current_state.should == :read end end + +describe AASM, '- event firing' do + it 'should fire the Event' do + foo = Foo.new + + Foo.aasm_events[:close].should_receive(:fire).with(foo) + foo.close! + end + + it 'should update the current state' do + foo = Foo.new + foo.close! + + foo.aasm_current_state.should == :closed + end + + it 'should attempt to persist if aasm_persist is defined' do + foo = Foo.new + + def foo.aasm_persist + end + + foo.should_receive(:aasm_persist) + + foo.close! + end +end diff --git a/spec/event_spec.rb b/spec/event_spec.rb index 9ba6280..0b0ce89 100644 --- a/spec/event_spec.rb +++ b/spec/event_spec.rb @@ -16,26 +16,28 @@ describe AASM::SupportingClasses::Event do @event.name.should == @name end - it 'create StateTransitions' do + it 'should create StateTransitions' do AASM::SupportingClasses::StateTransition.should_receive(:new).with({:to => :closed, :from => :open}) AASM::SupportingClasses::StateTransition.should_receive(:new).with({:to => :closed, :from => :received}) new_event end - -# it 'should return an array of the next possible transitions for a state' do -# new_event -# @event.next_states(:open).size.should == 1 -# @event.next_states(:received).size.should == 1 -# end - -# it '#fire should run #perform on each state transition' do -# st = mock('StateTransition') -# st.should_receive(:perform) -# -# new_event -# -# @event.stub!(:next_states).and_return([st]) -# @event.fire(:closed) -# end end +describe AASM::SupportingClasses::Event, 'when firing an event' do + it 'should raise an AASM::InvalidTransition error if the transitions are empty' do + event = AASM::SupportingClasses::Event.new(:event) + + lambda { event.fire(nil) }.should raise_error(AASM::InvalidTransition) + end + + it 'should return the state of the first matching transition it finds' do + event = AASM::SupportingClasses::Event.new(:event) do + transitions :to => :closed, :from => [:open, :received] + end + + obj = mock('object') + obj.stub!(:aasm_current_state).and_return(:open) + + event.fire(obj).should == :closed + end +end diff --git a/spec/state_factory_spec.rb b/spec/state_factory_spec.rb deleted file mode 100644 index 4adb9da..0000000 --- a/spec/state_factory_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -require File.join(File.dirname(__FILE__), '..', 'lib', 'state') -require File.join(File.dirname(__FILE__), '..', 'lib', 'state_factory') - -describe AASM::SupportingClasses::StateFactory, '- when creating a new State' do - before(:each) do - @state = :scott - @opts = {:a => 'b'} - - AASM::SupportingClasses::StateFactory.create(@state, @opts) - end - - it 'should create a new State if it has not been created yet' do - AASM::SupportingClasses::State.should_receive(:new).with(:foo, :bar => 'baz') - - AASM::SupportingClasses::StateFactory.create(:foo, :bar => 'baz') - end - - it 'should not create a new State if it has already been created' do - AASM::SupportingClasses::State.should_not_receive(:new).with(@state, @opts) - - AASM::SupportingClasses::StateFactory.create(@state, @opts) - end -end - -describe AASM::SupportingClasses::StateFactory, '- when retrieving a State via []' do - before(:each) do - @state_name = :scottb - @opts = {:a => 'b'} - - AASM::SupportingClasses::StateFactory.create(@state_name, @opts) - end - - it 'should return nil if the State was never created' do - AASM::SupportingClasses::StateFactory[:foo].should be_nil - end - - it 'should return the State' do - AASM::SupportingClasses::StateFactory[@state_name].should_not be_nil - end -end diff --git a/spec/state_transition_spec.rb b/spec/state_transition_spec.rb index 6846d35..2f1b6c6 100644 --- a/spec/state_transition_spec.rb +++ b/spec/state_transition_spec.rb @@ -9,5 +9,38 @@ describe AASM::SupportingClasses::StateTransition do st.to.should == opts[:to] st.opts.should == opts end + + it 'should pass equality check if from and to are the same' do + opts = {:from => 'foo', :to => 'bar', :guard => 'g'} + st = AASM::SupportingClasses::StateTransition.new(opts) + + obj = mock('object') + obj.stub!(:from).and_return(opts[:from]) + obj.stub!(:to).and_return(opts[:to]) + + st.should == obj + end + + it 'should fail equality check if from are not the same' do + opts = {:from => 'foo', :to => 'bar', :guard => 'g'} + st = AASM::SupportingClasses::StateTransition.new(opts) + + obj = mock('object') + obj.stub!(:from).and_return('blah') + obj.stub!(:to).and_return(opts[:to]) + + st.should_not == obj + end + + it 'should fail equality check if to are not the same' do + opts = {:from => 'foo', :to => 'bar', :guard => 'g'} + st = AASM::SupportingClasses::StateTransition.new(opts) + + obj = mock('object') + obj.stub!(:from).and_return(opts[:from]) + obj.stub!(:to).and_return('blah') + + st.should_not == obj + end end