Merge branch 'master' of https://github.com/wickkidd/aasm into wickkidd-master

Conflicts:
	Rakefile
	spec/spec_helper.rb
This commit is contained in:
Thorsten Böttger 2011-09-08 19:09:16 +02:00
commit 6bc99dcc39
8 changed files with 153 additions and 7 deletions

View File

@ -19,6 +19,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'shoulda'
s.add_development_dependency 'sqlite3'
s.add_development_dependency 'minitest'
s.add_development_dependency 'ruby-debug-completion'
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")

View File

@ -50,6 +50,13 @@ module AASM
sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
end
# an addition over standard aasm so that, before firing an event, you can ask
# may_event? and get back a boolean that tells you whether the guard method
# on the transition will let this happen.
define_method("may_#{name.to_s}?") do |*args|
aasm_test_event(name, *args)
end
define_method("#{name.to_s}!") do |*args|
aasm_fire_event(name, true, *args)
end
@ -104,6 +111,12 @@ module AASM
aasm_events_for_state(aasm_current_state)
end
# filters the results of events_for_current_state so that only those that
# are really currently possible (given transition guards) are shown.
def aasm_permissible_events_for_current_state
aasm_events_for_current_state.select{ |e| self.send(("may_" + e.to_s + "?").to_sym) }
end
def aasm_events_for_state(state)
events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
events.map {|event| event.name}
@ -149,6 +162,11 @@ module AASM
obj
end
def aasm_test_event(name, *args)
event = self.class.aasm_events[name]
event.may_fire?(self, *args)
end
def aasm_fire_event(name, persist, *args)
event = self.class.aasm_events[name]
begin

View File

@ -7,6 +7,24 @@ class AASM::SupportingClasses::Event
update(options, &block)
end
# a neutered version of fire - it doesn't actually fir the event, it just
# executes the transition guards to determine if a transition is even
# an option given current conditions.
def may_fire?(obj, to_state=nil)
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
return false if transitions.size == 0
result = false
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj)
result = true
break
end
end
result
end
def fire(obj, to_state=nil, *args)
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
raise AASM::InvalidTransition, "Event '#{name}' cannot transition from '#{obj.aasm_current_state}'" if transitions.size == 0

View File

@ -36,10 +36,10 @@ class AASM::SupportingClasses::StateTransition
def _execute(obj, on_transition, *args)
case on_transition
when Symbol, String
obj.send(on_transition, *args)
when Proc
on_transition.call(obj, *args)
when Proc
on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)
when Symbol, String
obj.send(:method, on_transition.to_sym).arity == 0 ? obj.send(on_transition) : obj.send(on_transition, *args)
end
end

View File

@ -4,6 +4,8 @@ require 'aasm'
require 'rspec'
require 'rspec/autorun'
require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
require 'ruby-debug/completion'
def load_schema
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))

View File

@ -82,3 +82,82 @@ describe AASM::SupportingClasses::StateTransition, '- when performing guard chec
st.perform(obj)
end
end
describe AASM::SupportingClasses::StateTransition, '- when executing the transition with a Proc' do
it 'should call a Proc on the object with args' do
opts = {:from => 'foo', :to => 'bar', :on_transition => Proc.new {|o| o.test}}
st = AASM::SupportingClasses::StateTransition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
opts[:on_transition].should_receive(:call).with(any_args)
st.execute(obj, args)
end
it 'should call a Proc on the object without args' do
opts = {:from => 'foo', :to => 'bar', :on_transition => Proc.new {||}}
st = AASM::SupportingClasses::StateTransition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
opts[:on_transition].should_receive(:call).with(no_args)
st.execute(obj, args)
end
end
describe AASM::SupportingClasses::StateTransition, '- when executing the transition with an :on_transtion method call' do
it 'should accept a String for the method name' do
opts = {:from => 'foo', :to => 'bar', :on_transition => 'test'}
st = AASM::SupportingClasses::StateTransition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
obj.should_receive(:test)
st.execute(obj, args)
end
it 'should accept a Symbol for the method name' do
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::SupportingClasses::StateTransition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
obj.should_receive(:test)
st.execute(obj, args)
end
it 'should pass args if the target method accepts them' do
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::SupportingClasses::StateTransition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
obj.class.class_eval do
define_method(:test) {|*args| 'success'}
end
return_value = st.execute(obj, args)
return_value.should == 'success'
end
it 'should NOT pass args if the target method does NOT accept them' do
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::SupportingClasses::StateTransition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
obj.class.class_eval do
define_method(:test) {|*args| 'success'}
end
return_value = st.execute(obj, args)
return_value.should == 'success'
end
end

View File

@ -29,6 +29,11 @@ class AuthMachine
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
end
# a dummy event that can never happen
aasm_event :unpassify do
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
end
aasm_event :unsuspend do
transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
@ -89,6 +94,29 @@ class AuthMachineTest < Test::Unit::TestCase
end
context 'when being unsuspended' do
should 'be able to be unsuspended' do
@auth = AuthMachine.new
@auth.activate!
@auth.suspend!
assert @auth.may_unsuspend?
end
should 'not be able to be unsuspended into active' do
@auth = AuthMachine.new
@auth.suspend!
assert_equal false, @auth.may_unsuspend?(:active)
end
should 'not be able to be unpassified' do
@auth = AuthMachine.new
@auth.activate!
@auth.suspend!
@auth.unsuspend!
assert_equal false, @auth.may_unpassify?
end
should 'be active if previously activated' do
@auth = AuthMachine.new
@auth.activate!

View File

@ -33,11 +33,11 @@ class Test::Unit::TestCase
end
begin
require 'ruby-debug'
Debugger.start
require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
require 'ruby-debug/completion'
rescue LoadError
end
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'aasm'
require 'aasm'