1
0
Fork 0
mirror of https://github.com/aasm/aasm synced 2023-03-27 23:22:41 -04:00

A basic, functioning state machine working for regular ruby objects

Started persistence hook
Complete spec coverage
This commit is contained in:
Scott Barron 2008-02-21 10:16:08 -05:00
parent 14fa68da09
commit b4c69afcf2
8 changed files with 88 additions and 108 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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