mirror of
https://github.com/aasm/aasm
synced 2023-03-27 23:22:41 -04:00
Merge branch 'master' of https://github.com/wickkidd/aasm into wickkidd-master
Conflicts: Rakefile spec/spec_helper.rb
This commit is contained in:
commit
6bc99dcc39
8 changed files with 153 additions and 7 deletions
|
@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
||||||
s.add_development_dependency 'shoulda'
|
s.add_development_dependency 'shoulda'
|
||||||
s.add_development_dependency 'sqlite3'
|
s.add_development_dependency 'sqlite3'
|
||||||
s.add_development_dependency 'minitest'
|
s.add_development_dependency 'minitest'
|
||||||
|
s.add_development_dependency 'ruby-debug-completion'
|
||||||
|
|
||||||
s.files = `git ls-files`.split("\n")
|
s.files = `git ls-files`.split("\n")
|
||||||
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
||||||
|
|
|
@ -50,6 +50,13 @@ module AASM
|
||||||
sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
|
sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
|
||||||
end
|
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|
|
define_method("#{name.to_s}!") do |*args|
|
||||||
aasm_fire_event(name, true, *args)
|
aasm_fire_event(name, true, *args)
|
||||||
end
|
end
|
||||||
|
@ -104,6 +111,12 @@ module AASM
|
||||||
aasm_events_for_state(aasm_current_state)
|
aasm_events_for_state(aasm_current_state)
|
||||||
end
|
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)
|
def aasm_events_for_state(state)
|
||||||
events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
|
events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
|
||||||
events.map {|event| event.name}
|
events.map {|event| event.name}
|
||||||
|
@ -149,6 +162,11 @@ module AASM
|
||||||
obj
|
obj
|
||||||
end
|
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)
|
def aasm_fire_event(name, persist, *args)
|
||||||
event = self.class.aasm_events[name]
|
event = self.class.aasm_events[name]
|
||||||
begin
|
begin
|
||||||
|
|
|
@ -7,6 +7,24 @@ class AASM::SupportingClasses::Event
|
||||||
update(options, &block)
|
update(options, &block)
|
||||||
end
|
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)
|
def fire(obj, to_state=nil, *args)
|
||||||
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
|
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
|
raise AASM::InvalidTransition, "Event '#{name}' cannot transition from '#{obj.aasm_current_state}'" if transitions.size == 0
|
||||||
|
|
|
@ -36,10 +36,10 @@ class AASM::SupportingClasses::StateTransition
|
||||||
|
|
||||||
def _execute(obj, on_transition, *args)
|
def _execute(obj, on_transition, *args)
|
||||||
case on_transition
|
case on_transition
|
||||||
when Symbol, String
|
when Proc
|
||||||
obj.send(on_transition, *args)
|
on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)
|
||||||
when Proc
|
when Symbol, String
|
||||||
on_transition.call(obj, *args)
|
obj.send(:method, on_transition.to_sym).arity == 0 ? obj.send(on_transition) : obj.send(on_transition, *args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ require 'aasm'
|
||||||
|
|
||||||
require 'rspec'
|
require 'rspec'
|
||||||
require 'rspec/autorun'
|
require 'rspec/autorun'
|
||||||
|
require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
|
||||||
|
require 'ruby-debug/completion'
|
||||||
|
|
||||||
def load_schema
|
def load_schema
|
||||||
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
||||||
|
|
|
@ -82,3 +82,82 @@ describe AASM::SupportingClasses::StateTransition, '- when performing guard chec
|
||||||
st.perform(obj)
|
st.perform(obj)
|
||||||
end
|
end
|
||||||
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
|
||||||
|
|
|
@ -29,6 +29,11 @@ class AuthMachine
|
||||||
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
|
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
|
||||||
end
|
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
|
aasm_event :unsuspend do
|
||||||
transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
|
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? }
|
transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
|
||||||
|
@ -89,6 +94,29 @@ class AuthMachineTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when being unsuspended' do
|
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
|
should 'be active if previously activated' do
|
||||||
@auth = AuthMachine.new
|
@auth = AuthMachine.new
|
||||||
@auth.activate!
|
@auth.activate!
|
||||||
|
|
|
@ -33,11 +33,11 @@ class Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
require 'ruby-debug'
|
require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
|
||||||
Debugger.start
|
require 'ruby-debug/completion'
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
end
|
end
|
||||||
|
|
||||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
||||||
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
||||||
require 'aasm'
|
require 'aasm'
|
||||||
|
|
Loading…
Reference in a new issue