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:
parent
14fa68da09
commit
b4c69afcf2
8 changed files with 88 additions and 108 deletions
12
lib/aasm.rb
12
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue