mirror of
https://github.com/aasm/aasm
synced 2023-03-27 23:22:41 -04:00
more tests
This commit is contained in:
parent
719dadf1ff
commit
206b1991bc
18 changed files with 750 additions and 152 deletions
|
@ -3,7 +3,23 @@
|
||||||
## version 4.3
|
## version 4.3
|
||||||
|
|
||||||
* add support for multiple state machines per class
|
* add support for multiple state machines per class
|
||||||
|
* check all tests
|
||||||
|
* what happen's if someone accesses `aasm`, but has defined a
|
||||||
|
state machine for `aasm(:my_name)`?
|
||||||
|
* persistence
|
||||||
|
* _ActiveRecord_
|
||||||
|
* _Mongoid_
|
||||||
|
* _MongoMapper_
|
||||||
|
* _Sequel_
|
||||||
* documentation
|
* documentation
|
||||||
* events are chained
|
* silence warnings?
|
||||||
* :default state machine won't be provided, if state machine name is used
|
|
||||||
* if used raise an error
|
# Changes so far
|
||||||
|
|
||||||
|
## version 4.3
|
||||||
|
|
||||||
|
* add support for multiple state machines per class
|
||||||
|
* class- and instance-level `aasm` methods accept a state machine selector
|
||||||
|
(aka the state machine _name_)
|
||||||
|
* if no selector/name is provided, `:default` will be used
|
||||||
|
* duplicate definitions of states and events will issue warnings
|
||||||
|
|
|
@ -67,7 +67,7 @@ module AASM
|
||||||
@state_machine.add_state(name, @klass, options)
|
@state_machine.add_state(name, @klass, options)
|
||||||
|
|
||||||
if @klass.instance_methods.include?("#{name}?")
|
if @klass.instance_methods.include?("#{name}?")
|
||||||
warn "The state name #{name} is already used!"
|
warn "#{@klass.name}: The state name #{name} is already used!"
|
||||||
end
|
end
|
||||||
|
|
||||||
@klass.class_eval <<-EORUBY, __FILE__, __LINE__ + 1
|
@klass.class_eval <<-EORUBY, __FILE__, __LINE__ + 1
|
||||||
|
@ -86,7 +86,7 @@ module AASM
|
||||||
@state_machine.add_event(name, options, &block)
|
@state_machine.add_event(name, options, &block)
|
||||||
|
|
||||||
if @klass.instance_methods.include?("may_#{name}?".to_sym)
|
if @klass.instance_methods.include?("may_#{name}?".to_sym)
|
||||||
warn "The event name #{name} is already used!"
|
warn "#{@klass.name}: The event name #{name} is already used!"
|
||||||
end
|
end
|
||||||
|
|
||||||
# an addition over standard aasm so that, before firing an event, you can ask
|
# an addition over standard aasm so that, before firing an event, you can ask
|
||||||
|
|
|
@ -86,3 +86,137 @@ class ComplexExample
|
||||||
phrase == :please
|
phrase == :please
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ComplexExampleMultiple
|
||||||
|
include AASM
|
||||||
|
|
||||||
|
attr_accessor :activation_code, :activated_at, :deleted_at
|
||||||
|
|
||||||
|
aasm(:left) do
|
||||||
|
state :passive
|
||||||
|
state :pending, :initial => true, :before_enter => :make_activation_code
|
||||||
|
state :active, :before_enter => :do_activate
|
||||||
|
state :suspended
|
||||||
|
state :deleted, :before_enter => :do_delete#, :exit => :do_undelete
|
||||||
|
state :waiting
|
||||||
|
|
||||||
|
event :left_register do
|
||||||
|
transitions :from => :passive, :to => :pending do
|
||||||
|
guard do
|
||||||
|
can_register?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
event :left_activate do
|
||||||
|
transitions :from => :pending, :to => :active
|
||||||
|
end
|
||||||
|
|
||||||
|
event :left_suspend do
|
||||||
|
transitions :from => [:passive, :pending, :active], :to => :suspended
|
||||||
|
end
|
||||||
|
|
||||||
|
event :left_delete do
|
||||||
|
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
|
||||||
|
end
|
||||||
|
|
||||||
|
# a dummy event that can never happen
|
||||||
|
event :left_unpassify do
|
||||||
|
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
|
||||||
|
end
|
||||||
|
|
||||||
|
event :left_unsuspend do
|
||||||
|
transitions :from => :suspended, :to => :active, :guard => Proc.new { has_activated? }
|
||||||
|
transitions :from => :suspended, :to => :pending, :guard => :has_activation_code?
|
||||||
|
transitions :from => :suspended, :to => :passive
|
||||||
|
end
|
||||||
|
|
||||||
|
event :left_wait do
|
||||||
|
transitions :from => :suspended, :to => :waiting, :guard => :if_polite?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
aasm(:right) do
|
||||||
|
state :passive
|
||||||
|
state :pending, :initial => true, :before_enter => :make_activation_code
|
||||||
|
state :active, :before_enter => :do_activate
|
||||||
|
state :suspended
|
||||||
|
state :deleted, :before_enter => :do_delete#, :exit => :do_undelete
|
||||||
|
state :waiting
|
||||||
|
|
||||||
|
event :right_register do
|
||||||
|
transitions :from => :passive, :to => :pending do
|
||||||
|
guard do
|
||||||
|
can_register?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
event :right_activate do
|
||||||
|
transitions :from => :pending, :to => :active
|
||||||
|
end
|
||||||
|
|
||||||
|
event :right_suspend do
|
||||||
|
transitions :from => [:passive, :pending, :active], :to => :suspended
|
||||||
|
end
|
||||||
|
|
||||||
|
event :right_delete do
|
||||||
|
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
|
||||||
|
end
|
||||||
|
|
||||||
|
# a dummy event that can never happen
|
||||||
|
event :right_unpassify do
|
||||||
|
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
|
||||||
|
end
|
||||||
|
|
||||||
|
event :right_unsuspend do
|
||||||
|
transitions :from => :suspended, :to => :active, :guard => Proc.new { has_activated? }
|
||||||
|
transitions :from => :suspended, :to => :pending, :guard => :has_activation_code?
|
||||||
|
transitions :from => :suspended, :to => :passive
|
||||||
|
end
|
||||||
|
|
||||||
|
event :right_wait do
|
||||||
|
transitions :from => :suspended, :to => :waiting, :guard => :if_polite?
|
||||||
|
end
|
||||||
|
end # right
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
# the AR backend uses a before_validate_on_create :aasm_ensure_initial_state
|
||||||
|
# lets do something similar here for testing purposes.
|
||||||
|
aasm(:left).enter_initial_state
|
||||||
|
aasm(:right).enter_initial_state
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_activation_code
|
||||||
|
@activation_code = @activation_code ? @activation_code + '2' : '1'
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_activate
|
||||||
|
@activated_at = Time.now
|
||||||
|
@activation_code = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_delete
|
||||||
|
@deleted_at = Time.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_undelete
|
||||||
|
@deleted_at = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_register?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_activated?
|
||||||
|
!!@activated_at
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_activation_code?
|
||||||
|
!!@activation_code
|
||||||
|
end
|
||||||
|
|
||||||
|
def if_polite?(phrase = nil)
|
||||||
|
phrase == :please
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
class ComplexMultipleExample
|
|
||||||
include AASM
|
|
||||||
|
|
||||||
attr_accessor :activation_code, :activated_at, :deleted_at
|
|
||||||
|
|
||||||
aasm(:left) do
|
|
||||||
state :passive
|
|
||||||
state :pending, :initial => true, :before_enter => :make_activation_code
|
|
||||||
state :active, :before_enter => :do_activate
|
|
||||||
state :suspended
|
|
||||||
state :deleted, :before_enter => :do_delete#, :exit => :do_undelete
|
|
||||||
state :waiting
|
|
||||||
|
|
||||||
event :left_register do
|
|
||||||
transitions :from => :passive, :to => :pending do
|
|
||||||
guard do
|
|
||||||
can_register?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
event :left_activate do
|
|
||||||
transitions :from => :pending, :to => :active
|
|
||||||
end
|
|
||||||
|
|
||||||
event :left_suspend do
|
|
||||||
transitions :from => [:passive, :pending, :active], :to => :suspended
|
|
||||||
end
|
|
||||||
|
|
||||||
event :left_delete do
|
|
||||||
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
|
|
||||||
end
|
|
||||||
|
|
||||||
# a dummy event that can never happen
|
|
||||||
event :left_unpassify do
|
|
||||||
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
|
|
||||||
end
|
|
||||||
|
|
||||||
event :left_unsuspend do
|
|
||||||
transitions :from => :suspended, :to => :active, :guard => Proc.new { has_activated? }
|
|
||||||
transitions :from => :suspended, :to => :pending, :guard => :has_activation_code?
|
|
||||||
transitions :from => :suspended, :to => :passive
|
|
||||||
end
|
|
||||||
|
|
||||||
event :left_wait do
|
|
||||||
transitions :from => :suspended, :to => :waiting, :guard => :if_polite?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
aasm(:right) do
|
|
||||||
state :passive
|
|
||||||
state :pending, :initial => true, :before_enter => :make_activation_code
|
|
||||||
state :active, :before_enter => :do_activate
|
|
||||||
state :suspended
|
|
||||||
state :deleted, :before_enter => :do_delete#, :exit => :do_undelete
|
|
||||||
state :waiting
|
|
||||||
|
|
||||||
event :right_register do
|
|
||||||
transitions :from => :passive, :to => :pending do
|
|
||||||
guard do
|
|
||||||
can_register?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
event :right_activate do
|
|
||||||
transitions :from => :pending, :to => :active
|
|
||||||
end
|
|
||||||
|
|
||||||
event :right_suspend do
|
|
||||||
transitions :from => [:passive, :pending, :active], :to => :suspended
|
|
||||||
end
|
|
||||||
|
|
||||||
event :right_delete do
|
|
||||||
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
|
|
||||||
end
|
|
||||||
|
|
||||||
# a dummy event that can never happen
|
|
||||||
event :right_unpassify do
|
|
||||||
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
|
|
||||||
end
|
|
||||||
|
|
||||||
event :right_unsuspend do
|
|
||||||
transitions :from => :suspended, :to => :active, :guard => Proc.new { has_activated? }
|
|
||||||
transitions :from => :suspended, :to => :pending, :guard => :has_activation_code?
|
|
||||||
transitions :from => :suspended, :to => :passive
|
|
||||||
end
|
|
||||||
|
|
||||||
event :right_wait do
|
|
||||||
transitions :from => :suspended, :to => :waiting, :guard => :if_polite?
|
|
||||||
end
|
|
||||||
end # right
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
# the AR backend uses a before_validate_on_create :aasm_ensure_initial_state
|
|
||||||
# lets do something similar here for testing purposes.
|
|
||||||
aasm(:left).enter_initial_state
|
|
||||||
aasm(:right).enter_initial_state
|
|
||||||
end
|
|
||||||
|
|
||||||
def make_activation_code
|
|
||||||
@activation_code = @activation_code ? @activation_code + '2' : '1'
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_activate
|
|
||||||
@activated_at = Time.now
|
|
||||||
@activation_code = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_delete
|
|
||||||
@deleted_at = Time.now
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_undelete
|
|
||||||
@deleted_at = false
|
|
||||||
end
|
|
||||||
|
|
||||||
def can_register?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_activated?
|
|
||||||
!!@activated_at
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_activation_code?
|
|
||||||
!!@activation_code
|
|
||||||
end
|
|
||||||
|
|
||||||
def if_polite?(phrase = nil)
|
|
||||||
phrase == :please
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -34,8 +34,55 @@ class Conversation
|
||||||
@persister = persister
|
@persister = persister
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def aasm_read_state
|
||||||
|
@persister.read_state
|
||||||
|
end
|
||||||
|
|
||||||
|
def aasm_write_state(state)
|
||||||
|
@persister.write_state(state)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ConversationMultiple
|
||||||
|
include AASM
|
||||||
|
|
||||||
|
aasm(:left) do
|
||||||
|
state :needs_attention, :initial => true
|
||||||
|
state :read
|
||||||
|
state :closed
|
||||||
|
state :awaiting_response
|
||||||
|
state :junk
|
||||||
|
|
||||||
|
event :new_message do
|
||||||
|
end
|
||||||
|
|
||||||
|
event :view do
|
||||||
|
transitions :to => :read, :from => [:needs_attention]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :reply do
|
||||||
|
end
|
||||||
|
|
||||||
|
event :close do
|
||||||
|
transitions :to => :closed, :from => [:read, :awaiting_response]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :junk do
|
||||||
|
transitions :to => :junk, :from => [:read]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :unjunk do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(persister)
|
||||||
|
@persister = persister
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def aasm_read_state
|
def aasm_read_state
|
||||||
@persister.read_state
|
@persister.read_state
|
||||||
end
|
end
|
||||||
|
@ -43,5 +90,4 @@ class Conversation
|
||||||
def aasm_write_state(state)
|
def aasm_write_state(state)
|
||||||
@persister.write_state(state)
|
@persister.write_state(state)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,3 +33,60 @@ class FooTwo < Foo
|
||||||
state :foo
|
state :foo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class FooMultiple
|
||||||
|
include AASM
|
||||||
|
|
||||||
|
aasm(:left) do
|
||||||
|
state :open, :initial => true, :before_exit => :before_exit
|
||||||
|
state :closed, :before_enter => :before_enter
|
||||||
|
state :final
|
||||||
|
|
||||||
|
event :close, :success => :success_callback do
|
||||||
|
transitions :from => [:open], :to => [:closed]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :null do
|
||||||
|
transitions :from => [:open], :to => [:closed, :final], :guard => :always_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
aasm(:right, :column => :right) do
|
||||||
|
state :green, :initial => true
|
||||||
|
state :yellow
|
||||||
|
state :red
|
||||||
|
|
||||||
|
event :green do
|
||||||
|
transitions :from => [:yellow], :to => :green
|
||||||
|
end
|
||||||
|
event :yellow do
|
||||||
|
transitions :from => [:green, :red], :to => :yellow
|
||||||
|
end
|
||||||
|
event :red do
|
||||||
|
transitions :from => [:yellow], :to => :red
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def always_false
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def success_callback
|
||||||
|
end
|
||||||
|
|
||||||
|
def before_enter
|
||||||
|
end
|
||||||
|
def before_exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class FooTwoMultiple < FooMultiple
|
||||||
|
include AASM
|
||||||
|
aasm(:left) do
|
||||||
|
state :foo
|
||||||
|
end
|
||||||
|
|
||||||
|
aasm(:right) do
|
||||||
|
state :bar
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
class FooMultiple
|
class FooCallbackMultiple
|
||||||
include AASM
|
include AASM
|
||||||
|
|
||||||
aasm(:left) do
|
aasm(:left) do
|
||||||
state :open, :initial => true, :before_exit => :before_exit
|
state :open, :initial => true, :before_exit => :before_exit
|
||||||
state :closed, :before_enter => :before_enter
|
state :closed, :before_enter => :before_enter
|
||||||
|
@ -14,6 +15,22 @@ class FooMultiple
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
aasm(:right, :column => :right) do
|
||||||
|
state :green, :initial => true
|
||||||
|
state :yellow
|
||||||
|
state :red
|
||||||
|
|
||||||
|
event :green do
|
||||||
|
transitions :from => [:yellow], :to => :green
|
||||||
|
end
|
||||||
|
event :yellow do
|
||||||
|
transitions :from => [:green, :red], :to => :yellow
|
||||||
|
end
|
||||||
|
event :red do
|
||||||
|
transitions :from => [:yellow], :to => :red
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def always_false
|
def always_false
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -26,10 +43,3 @@ class FooMultiple
|
||||||
def before_exit
|
def before_exit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class FooTwoMultiple < FooMultiple
|
|
||||||
include AASM
|
|
||||||
aasm(:left) do
|
|
||||||
state :foo
|
|
||||||
end
|
|
||||||
end
|
|
48
spec/models/guardian_multiple.rb
Normal file
48
spec/models/guardian_multiple.rb
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
class GuardianMultiple
|
||||||
|
include AASM
|
||||||
|
|
||||||
|
aasm(:left) do
|
||||||
|
state :alpha, :initial => true
|
||||||
|
state :beta
|
||||||
|
|
||||||
|
event :use_one_guard_that_succeeds do
|
||||||
|
transitions :from => :alpha, :to => :beta, :guard => :succeed
|
||||||
|
end
|
||||||
|
event :use_one_guard_that_fails do
|
||||||
|
transitions :from => :alpha, :to => :beta, :guard => :fail
|
||||||
|
end
|
||||||
|
|
||||||
|
event :use_guards_that_succeed do
|
||||||
|
transitions :from => :alpha, :to => :beta, :guards => [:succeed, :another_succeed]
|
||||||
|
end
|
||||||
|
event :use_guards_where_the_first_fails do
|
||||||
|
transitions :from => :alpha, :to => :beta, :guards => [:succeed, :fail]
|
||||||
|
end
|
||||||
|
event :use_guards_where_the_second_fails do
|
||||||
|
transitions :from => :alpha, :to => :beta, :guards => [:fail, :succeed]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :use_event_guards_that_succeed, :guards => [:succeed, :another_succeed] do
|
||||||
|
transitions :from => :alpha, :to => :beta
|
||||||
|
end
|
||||||
|
event :use_event_and_transition_guards_that_succeed, :guards => [:succeed, :another_succeed] do
|
||||||
|
transitions :from => :alpha, :to => :beta, :guards => [:more_succeed]
|
||||||
|
end
|
||||||
|
event :use_event_guards_where_the_first_fails, :guards => [:succeed, :fail] do
|
||||||
|
transitions :from => :alpha, :to => :beta
|
||||||
|
end
|
||||||
|
event :use_event_guards_where_the_second_fails, :guards => [:fail, :succeed] do
|
||||||
|
transitions :from => :alpha, :to => :beta, :guard => :another_succeed
|
||||||
|
end
|
||||||
|
event :use_event_and_transition_guards_where_third_fails, :guards => [:succeed, :another_succeed] do
|
||||||
|
transitions :from => :alpha, :to => :beta, :guards => [:fail]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def fail; false; end
|
||||||
|
|
||||||
|
def succeed; true; end
|
||||||
|
def another_succeed; true; end
|
||||||
|
def more_succeed; true; end
|
||||||
|
|
||||||
|
end
|
|
@ -13,3 +13,19 @@ class InitialStateProc
|
||||||
def initialize(balance = 0); self.balance = balance; end
|
def initialize(balance = 0); self.balance = balance; end
|
||||||
def rich?; self.balance >= RICH; end
|
def rich?; self.balance >= RICH; end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class InitialStateProcMultiple
|
||||||
|
RICH = 1_000_000
|
||||||
|
|
||||||
|
attr_accessor :balance
|
||||||
|
|
||||||
|
include AASM
|
||||||
|
aasm(:left) do
|
||||||
|
state :retired
|
||||||
|
state :selling_bad_mortgages
|
||||||
|
initial_state Proc.new { |banker| banker.rich? ? :retired : :selling_bad_mortgages }
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(balance = 0); self.balance = balance; end
|
||||||
|
def rich?; self.balance >= RICH; end
|
||||||
|
end
|
||||||
|
|
|
@ -10,3 +10,16 @@ class NoInitialState
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class NoInitialStateMultiple
|
||||||
|
include AASM
|
||||||
|
|
||||||
|
aasm(:left) do
|
||||||
|
state :read
|
||||||
|
state :ended
|
||||||
|
|
||||||
|
event :foo do
|
||||||
|
transitions :to => :ended, :from => [:read]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
29
spec/models/parametrised_event_multiple.rb
Normal file
29
spec/models/parametrised_event_multiple.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
class ParametrisedEventMultiple
|
||||||
|
include AASM
|
||||||
|
aasm(:left) do
|
||||||
|
state :sleeping, :initial => true
|
||||||
|
state :showering
|
||||||
|
state :working
|
||||||
|
state :dating
|
||||||
|
state :prettying_up
|
||||||
|
|
||||||
|
event :wakeup do
|
||||||
|
transitions :from => :sleeping, :to => [:showering, :working]
|
||||||
|
end
|
||||||
|
|
||||||
|
event :dress do
|
||||||
|
transitions :from => :sleeping, :to => :working, :after => :wear_clothes
|
||||||
|
transitions :from => :showering, :to => [:working, :dating], :after => Proc.new { |*args| wear_clothes(*args) }
|
||||||
|
transitions :from => :showering, :to => :prettying_up, :after => [:condition_hair, :fix_hair]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def wear_clothes(shirt_color, trouser_type=nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def condition_hair
|
||||||
|
end
|
||||||
|
|
||||||
|
def fix_hair
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,3 +9,15 @@ class ValidStateName
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ValidStateNameMultiple
|
||||||
|
include AASM
|
||||||
|
aasm(:left) do
|
||||||
|
state :invalid, :initial => true
|
||||||
|
state :valid
|
||||||
|
|
||||||
|
event :valid do
|
||||||
|
transitions :to => :valid, :from => [:invalid]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -185,7 +185,7 @@ end # callbacks for the new DSL
|
||||||
describe 'event callbacks' do
|
describe 'event callbacks' do
|
||||||
describe "with an error callback defined" do
|
describe "with an error callback defined" do
|
||||||
before do
|
before do
|
||||||
class FooMultiple
|
class FooCallbackMultiple
|
||||||
# this hack is needed to allow testing of parameters, since RSpec
|
# this hack is needed to allow testing of parameters, since RSpec
|
||||||
# destroys a method's arity when mocked
|
# destroys a method's arity when mocked
|
||||||
attr_accessor :data
|
attr_accessor :data
|
||||||
|
@ -197,7 +197,7 @@ describe 'event callbacks' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@foo = FooMultiple.new
|
@foo = FooCallbackMultiple.new
|
||||||
end
|
end
|
||||||
|
|
||||||
context "error_callback defined" do
|
context "error_callback defined" do
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe 'on initialization' do
|
describe 'on initialization' do
|
||||||
let(:auth) {ComplexMultipleExample.new}
|
let(:auth) {ComplexExampleMultiple.new}
|
||||||
|
|
||||||
it 'should be in the pending state' do
|
it 'should be in the pending state' do
|
||||||
expect(auth.aasm(:left).current_state).to eq(:pending)
|
expect(auth.aasm(:left).current_state).to eq(:pending)
|
||||||
|
@ -15,7 +15,7 @@ describe 'on initialization' do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when being unsuspended' do
|
describe 'when being unsuspended' do
|
||||||
let(:auth) {ComplexMultipleExample.new}
|
let(:auth) {ComplexExampleMultiple.new}
|
||||||
|
|
||||||
it 'should be able to unsuspend' do
|
it 'should be able to unsuspend' do
|
||||||
auth.left_activate!
|
auth.left_activate!
|
||||||
|
|
73
spec/unit/event_multiple_spec.rb
Normal file
73
spec/unit/event_multiple_spec.rb
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'current event' do
|
||||||
|
let(:pe) {ParametrisedEventMultiple.new}
|
||||||
|
|
||||||
|
it 'if no event has been triggered' do
|
||||||
|
expect(pe.aasm(:left).current_event).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'if a event has been triggered' do
|
||||||
|
pe.wakeup
|
||||||
|
expect(pe.aasm(:left).current_event).to eql :wakeup
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'if no event has been triggered' do
|
||||||
|
pe.wakeup!
|
||||||
|
expect(pe.aasm(:left).current_event).to eql :wakeup!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'parametrised events' do
|
||||||
|
let(:pe) {ParametrisedEventMultiple.new}
|
||||||
|
|
||||||
|
it 'should transition to specified next state (sleeping to showering)' do
|
||||||
|
pe.wakeup!(:showering)
|
||||||
|
expect(pe.aasm(:left).current_state).to eq(:showering)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should transition to specified next state (sleeping to working)' do
|
||||||
|
pe.wakeup!(:working)
|
||||||
|
expect(pe.aasm(:left).current_state).to eq(:working)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should transition to default (first or showering) state' do
|
||||||
|
pe.wakeup!
|
||||||
|
expect(pe.aasm(:left).current_state).to eq(:showering)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should transition to default state when :after transition invoked' do
|
||||||
|
pe.dress!(nil, 'purple', 'dressy')
|
||||||
|
expect(pe.aasm(:left).current_state).to eq(:working)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should call :after transition method with args' do
|
||||||
|
pe.wakeup!(:showering)
|
||||||
|
expect(pe).to receive(:wear_clothes).with('blue', 'jeans')
|
||||||
|
pe.dress!(:working, 'blue', 'jeans')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should call :after transition proc' do
|
||||||
|
pe.wakeup!(:showering)
|
||||||
|
expect(pe).to receive(:wear_clothes).with('purple', 'slacks')
|
||||||
|
pe.dress!(:dating, 'purple', 'slacks')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should call :after transition with an array of methods' do
|
||||||
|
pe.wakeup!(:showering)
|
||||||
|
expect(pe).to receive(:condition_hair)
|
||||||
|
expect(pe).to receive(:fix_hair)
|
||||||
|
pe.dress!(:prettying_up)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'event firing without persistence' do
|
||||||
|
it 'should attempt to persist if aasm_write_state is defined' do
|
||||||
|
foo = Foo.new
|
||||||
|
def foo.aasm_write_state; end
|
||||||
|
expect(foo).to be_open
|
||||||
|
|
||||||
|
expect(foo).to receive(:aasm_write_state_without_persistence)
|
||||||
|
foo.close
|
||||||
|
end
|
||||||
|
end
|
60
spec/unit/guard_multiple_spec.rb
Normal file
60
spec/unit/guard_multiple_spec.rb
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe "per-transition guards" do
|
||||||
|
let(:guardian) { GuardianMultiple.new }
|
||||||
|
|
||||||
|
it "allows the transition if the guard succeeds" do
|
||||||
|
expect { guardian.use_one_guard_that_succeeds! }.to_not raise_error
|
||||||
|
expect(guardian).to be_beta
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stops the transition if the guard fails" do
|
||||||
|
expect { guardian.use_one_guard_that_fails! }.to raise_error(AASM::InvalidTransition)
|
||||||
|
expect(guardian).to be_alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows the transition if all guards succeeds" do
|
||||||
|
expect { guardian.use_guards_that_succeed! }.to_not raise_error
|
||||||
|
expect(guardian).to be_beta
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stops the transition if the first guard fails" do
|
||||||
|
expect { guardian.use_guards_where_the_first_fails! }.to raise_error(AASM::InvalidTransition)
|
||||||
|
expect(guardian).to be_alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stops the transition if the second guard fails" do
|
||||||
|
expect { guardian.use_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
|
||||||
|
expect(guardian).to be_alpha
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "event guards" do
|
||||||
|
let(:guardian) { GuardianMultiple.new }
|
||||||
|
|
||||||
|
it "allows the transition if the event guards succeed" do
|
||||||
|
expect { guardian.use_event_guards_that_succeed! }.to_not raise_error
|
||||||
|
expect(guardian).to be_beta
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows the transition if the event and transition guards succeed" do
|
||||||
|
expect { guardian.use_event_and_transition_guards_that_succeed! }.to_not raise_error
|
||||||
|
expect(guardian).to be_beta
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stops the transition if the first event guard fails" do
|
||||||
|
expect { guardian.use_event_guards_where_the_first_fails! }.to raise_error(AASM::InvalidTransition)
|
||||||
|
expect(guardian).to be_alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stops the transition if the second event guard fails" do
|
||||||
|
expect { guardian.use_event_guards_where_the_second_fails! }.to raise_error(AASM::InvalidTransition)
|
||||||
|
expect(guardian).to be_alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
it "stops the transition if the transition guard fails" do
|
||||||
|
expect { guardian.use_event_and_transition_guards_where_third_fails! }.to raise_error(AASM::InvalidTransition)
|
||||||
|
expect(guardian).to be_alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
15
spec/unit/initial_state_multiple_spec.rb
Normal file
15
spec/unit/initial_state_multiple_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'initial states' do
|
||||||
|
it 'should use the first state defined if no initial state is given' do
|
||||||
|
expect(NoInitialStateMultiple.new.aasm(:left).current_state).to eq(:read)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should determine initial state from the Proc results' do
|
||||||
|
balance = InitialStateProcMultiple::RICH - 1
|
||||||
|
expect(InitialStateProcMultiple.new(balance).aasm(:left).current_state).to eq(:selling_bad_mortgages)
|
||||||
|
|
||||||
|
balance = InitialStateProcMultiple::RICH + 1
|
||||||
|
expect(InitialStateProcMultiple.new(balance).aasm(:left).current_state).to eq(:retired)
|
||||||
|
end
|
||||||
|
end
|
202
spec/unit/inspection_multiple_spec.rb
Normal file
202
spec/unit/inspection_multiple_spec.rb
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'inspection for common cases' do
|
||||||
|
it 'should support the new DSL' do
|
||||||
|
# 1st state machine
|
||||||
|
expect(FooMultiple.aasm(:left)).to respond_to(:states)
|
||||||
|
expect(FooMultiple.aasm(:left).states.size).to eql 3
|
||||||
|
expect(FooMultiple.aasm(:left).states).to include(:open)
|
||||||
|
expect(FooMultiple.aasm(:left).states).to include(:closed)
|
||||||
|
expect(FooMultiple.aasm(:left).states).to include(:final)
|
||||||
|
|
||||||
|
expect(FooMultiple.aasm(:left)).to respond_to(:initial_state)
|
||||||
|
expect(FooMultiple.aasm(:left).initial_state).to eq(:open)
|
||||||
|
|
||||||
|
expect(FooMultiple.aasm(:left)).to respond_to(:events)
|
||||||
|
puts FooMultiple.aasm(:left).events.map(&:name).inspect
|
||||||
|
expect(FooMultiple.aasm(:left).events.size).to eql 2
|
||||||
|
expect(FooMultiple.aasm(:left).events).to include(:close)
|
||||||
|
expect(FooMultiple.aasm(:left).events).to include(:null)
|
||||||
|
|
||||||
|
# 2nd state machine
|
||||||
|
expect(FooMultiple.aasm(:right)).to respond_to(:states)
|
||||||
|
expect(FooMultiple.aasm(:right).states.size).to eql 3
|
||||||
|
expect(FooMultiple.aasm(:right).states).to include(:green)
|
||||||
|
expect(FooMultiple.aasm(:right).states).to include(:yellow)
|
||||||
|
expect(FooMultiple.aasm(:right).states).to include(:red)
|
||||||
|
|
||||||
|
expect(FooMultiple.aasm(:right)).to respond_to(:initial_state)
|
||||||
|
expect(FooMultiple.aasm(:right).initial_state).to eq(:green)
|
||||||
|
|
||||||
|
expect(FooMultiple.aasm(:right)).to respond_to(:events)
|
||||||
|
expect(FooMultiple.aasm(:right).events.size).to eql 3
|
||||||
|
expect(FooMultiple.aasm(:right).events).to include(:green)
|
||||||
|
expect(FooMultiple.aasm(:right).events).to include(:yellow)
|
||||||
|
expect(FooMultiple.aasm(:right).events).to include(:red)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "instance level inspection" do
|
||||||
|
let(:foo) { FooMultiple.new }
|
||||||
|
let(:two) { FooTwoMultiple.new }
|
||||||
|
|
||||||
|
it "delivers all states" do
|
||||||
|
# 1st state machine
|
||||||
|
states = foo.aasm(:left).states
|
||||||
|
expect(states.size).to eql 3
|
||||||
|
expect(states).to include(:open)
|
||||||
|
expect(states).to include(:closed)
|
||||||
|
expect(states).to include(:final)
|
||||||
|
|
||||||
|
states = foo.aasm(:left).states(:permitted => true)
|
||||||
|
expect(states.size).to eql 1
|
||||||
|
expect(states).to include(:closed)
|
||||||
|
expect(states).not_to include(:open)
|
||||||
|
expect(states).not_to include(:final)
|
||||||
|
|
||||||
|
foo.close
|
||||||
|
expect(foo.aasm(:left).states(:permitted => true)).to be_empty
|
||||||
|
|
||||||
|
# 2nd state machine
|
||||||
|
states = foo.aasm(:right).states
|
||||||
|
expect(states.size).to eql 3
|
||||||
|
expect(states).to include(:green)
|
||||||
|
expect(states).to include(:yellow)
|
||||||
|
expect(states).to include(:red)
|
||||||
|
|
||||||
|
states = foo.aasm(:right).states(:permitted => true)
|
||||||
|
expect(states.size).to eql 1
|
||||||
|
expect(states).to include(:yellow)
|
||||||
|
expect(states).not_to include(:green)
|
||||||
|
expect(states).not_to include(:red)
|
||||||
|
|
||||||
|
foo.yellow
|
||||||
|
states = foo.aasm(:right).states(:permitted => true)
|
||||||
|
expect(states.size).to eql 2
|
||||||
|
expect(states).to include(:red)
|
||||||
|
expect(states).to include(:green)
|
||||||
|
expect(states).not_to include(:yellow)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "delivers all states for subclasses" do
|
||||||
|
# 1st state machine
|
||||||
|
states = two.aasm(:left).states
|
||||||
|
expect(states.size).to eql 4
|
||||||
|
expect(states).to include(:open)
|
||||||
|
expect(states).to include(:closed)
|
||||||
|
expect(states).to include(:final)
|
||||||
|
expect(states).to include(:foo)
|
||||||
|
|
||||||
|
states = two.aasm(:left).states(:permitted => true)
|
||||||
|
expect(states.size).to eql 1
|
||||||
|
expect(states).to include(:closed)
|
||||||
|
expect(states).not_to include(:open)
|
||||||
|
|
||||||
|
two.close
|
||||||
|
expect(two.aasm(:left).states(:permitted => true)).to be_empty
|
||||||
|
|
||||||
|
# 2nd state machine
|
||||||
|
states = two.aasm(:right).states
|
||||||
|
expect(states.size).to eql 4
|
||||||
|
expect(states).to include(:green)
|
||||||
|
expect(states).to include(:yellow)
|
||||||
|
expect(states).to include(:red)
|
||||||
|
expect(states).to include(:bar)
|
||||||
|
|
||||||
|
states = two.aasm(:right).states(:permitted => true)
|
||||||
|
expect(states.size).to eql 1
|
||||||
|
expect(states).to include(:yellow)
|
||||||
|
expect(states).not_to include(:red)
|
||||||
|
expect(states).not_to include(:green)
|
||||||
|
expect(states).not_to include(:bar)
|
||||||
|
|
||||||
|
two.yellow
|
||||||
|
states = two.aasm(:right).states(:permitted => true)
|
||||||
|
expect(states.size).to eql 2
|
||||||
|
expect(states).to include(:green)
|
||||||
|
expect(states).to include(:red)
|
||||||
|
expect(states).not_to include(:yellow)
|
||||||
|
expect(states).not_to include(:bar)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "delivers all events" do
|
||||||
|
# 1st state machine
|
||||||
|
events = foo.aasm(:left).events
|
||||||
|
expect(events.size).to eql 2
|
||||||
|
expect(events).to include(:close)
|
||||||
|
expect(events).to include(:null)
|
||||||
|
|
||||||
|
foo.close
|
||||||
|
expect(foo.aasm(:left).events).to be_empty
|
||||||
|
|
||||||
|
# 2nd state machine
|
||||||
|
events = foo.aasm(:right).events
|
||||||
|
expect(events.size).to eql 1
|
||||||
|
expect(events).to include(:yellow)
|
||||||
|
expect(events).not_to include(:green)
|
||||||
|
expect(events).not_to include(:red)
|
||||||
|
|
||||||
|
foo.yellow
|
||||||
|
events = foo.aasm(:right).events
|
||||||
|
expect(events.size).to eql 2
|
||||||
|
expect(events).to include(:green)
|
||||||
|
expect(events).to include(:red)
|
||||||
|
expect(events).not_to include(:yellow)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should list states in the order they have been defined' do
|
||||||
|
expect(ConversationMultiple.aasm(:left).states).to eq([
|
||||||
|
:needs_attention, :read, :closed, :awaiting_response, :junk
|
||||||
|
])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "special cases" do
|
||||||
|
it "should support valid as state name" do
|
||||||
|
expect(ValidStateNameMultiple.aasm(:left).states).to include(:invalid)
|
||||||
|
expect(ValidStateNameMultiple.aasm(:left).states).to include(:valid)
|
||||||
|
|
||||||
|
argument = ValidStateNameMultiple.new
|
||||||
|
expect(argument.invalid?).to be_truthy
|
||||||
|
expect(argument.aasm(:left).current_state).to eq(:invalid)
|
||||||
|
|
||||||
|
argument.valid!
|
||||||
|
expect(argument.valid?).to be_truthy
|
||||||
|
expect(argument.aasm(:left).current_state).to eq(:valid)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'aasm.states_for_select' do
|
||||||
|
it "should return a select friendly array of states" do
|
||||||
|
expect(FooMultiple.aasm(:left)).to respond_to(:states_for_select)
|
||||||
|
expect(FooMultiple.aasm(:left).states_for_select).to eq(
|
||||||
|
[['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'aasm.from_states_for_state' do
|
||||||
|
it "should return all from states for a state" do
|
||||||
|
expect(ComplexExampleMultiple.aasm(:left)).to respond_to(:from_states_for_state)
|
||||||
|
froms = ComplexExampleMultiple.aasm(:left).from_states_for_state(:active)
|
||||||
|
[:pending, :passive, :suspended].each {|from| expect(froms).to include(from)}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return from states for a state for a particular transition only" do
|
||||||
|
froms = ComplexExampleMultiple.aasm(:left).from_states_for_state(:active, :transition => :left_unsuspend)
|
||||||
|
[:suspended].each {|from| expect(froms).to include(from)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'permitted events' do
|
||||||
|
let(:foo) {FooMultiple.new}
|
||||||
|
|
||||||
|
it 'work' do
|
||||||
|
expect(foo.aasm(:left).events(:permitted => true)).to include(:close)
|
||||||
|
expect(foo.aasm(:left).events(:permitted => true)).not_to include(:null)
|
||||||
|
|
||||||
|
expect(foo.aasm(:right).events(:permitted => true)).to include(:yellow)
|
||||||
|
expect(foo.aasm(:right).events(:permitted => true)).not_to include(:green)
|
||||||
|
expect(foo.aasm(:right).events(:permitted => true)).not_to include(:red)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue