mirror of
https://github.com/aasm/aasm
synced 2023-03-27 23:22:41 -04:00
Merge pull request #240 from aasm/multiple_state_machines_per_class
Multiple state machines per class
This commit is contained in:
commit
8754149ed6
94 changed files with 3758 additions and 544 deletions
|
@ -2,6 +2,9 @@
|
|||
|
||||
## 4.3.0 (not yet released)
|
||||
|
||||
* add support for multiple state machines per class (see [issue #158](https://github.com/aasm/aasm/issues/158) and [issue #240](https://github.com/aasm/aasm/issues/240) for details)
|
||||
* special thanks to [@evadne](https://github.com/evadne) for testing this feature, and providing comments and patches (see [issue #245](https://github.com/aasm/aasm/issues/245) for details)
|
||||
|
||||
## 4.2.0
|
||||
|
||||
* support turning off and on the configuration option for `no_direct_assignment` (see [issue #223](https://github.com/aasm/aasm/issues/223) for details)
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -9,7 +9,7 @@ gem 'mongoid', '~>4.0' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.
|
|||
gem 'sequel'
|
||||
# Since mongoid V4 requires incompatible bson V2, cannot have mongoid (V4 or greater)
|
||||
# and mongo_mapper ( or mongo ) in the same application
|
||||
#gem 'mongo_mapper', '~> 0.13'
|
||||
#gem 'bson_ext', :platforms => :ruby
|
||||
# gem 'mongo_mapper', '~> 0.13'
|
||||
# gem 'bson_ext', :platforms => :ruby
|
||||
|
||||
gemspec
|
||||
|
|
|
@ -1,7 +1,27 @@
|
|||
# Planned changes for AASM 4.1
|
||||
# Planned changes
|
||||
|
||||
* remove support for `:on_transition` callback
|
||||
## later
|
||||
|
||||
# Planned changes for AASM 4.0
|
||||
* drop support for aasm_column ?
|
||||
|
||||
* nothing left
|
||||
# Currently working on
|
||||
|
||||
|
||||
# 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
|
||||
* check all tests
|
||||
* _ActiveRecord_
|
||||
* _Mongoid_
|
||||
* _MongoMapper_
|
||||
* _Sequel_
|
||||
* what happen's if someone accesses `aasm`, but has defined a
|
||||
state machine for `aasm(:my_name)`?
|
||||
* documentation
|
||||
* drop support for find_in_state, count_in_state, calculate_in_state, with_state_scope
|
||||
|
|
70
README.md
70
README.md
|
@ -312,6 +312,76 @@ job.stage1_completed
|
|||
job.aasm.current_state # stage3
|
||||
```
|
||||
|
||||
|
||||
### Multiple state machine per class
|
||||
|
||||
Multiple state machines per class are supported. Be aware though, that _AASM_ has been
|
||||
built with one state machine per class in mind. Nonetheless, here's how to do it:
|
||||
|
||||
```ruby
|
||||
class SimpleMultipleExample
|
||||
include AASM
|
||||
aasm(:move) do
|
||||
state :standing, :initial => true
|
||||
state :walking
|
||||
state :running
|
||||
|
||||
event :walk do
|
||||
transitions :from => :standing, :to => :walking
|
||||
end
|
||||
event :run do
|
||||
transitions :from => [:standing, :walking], :to => :running
|
||||
end
|
||||
event :hold do
|
||||
transitions :from => [:walking, :running], :to => :standing
|
||||
end
|
||||
end
|
||||
|
||||
aasm(:work) do
|
||||
state :sleeping, :initial => true
|
||||
state :processing
|
||||
|
||||
event :start do
|
||||
transitions :from => :sleeping, :to => :processing
|
||||
end
|
||||
event :stop do
|
||||
transitions :from => :processing, :to => :sleeping
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
simple = SimpleMultipleExample.new
|
||||
|
||||
simple.aasm(:move).current_state
|
||||
# => :standing
|
||||
simple.aasm(:work).current
|
||||
# => :sleeping
|
||||
|
||||
simple.start
|
||||
simple.aasm(:move).current_state
|
||||
# => :standing
|
||||
simple.aasm(:work).current
|
||||
# => :processing
|
||||
|
||||
```
|
||||
|
||||
_AASM_ doesn't prohibit to define the same event in both state machines. The
|
||||
latest definition "wins" and overrides previous definitions. A warning is issued:
|
||||
`SimpleMultipleExample: The event name run is already used!`.
|
||||
|
||||
All _AASM_ class- and instance-level `aasm` methods accept a state machine selector.
|
||||
So, for example, to use inspection on a class level, you have to use
|
||||
|
||||
```ruby
|
||||
SimpleMultipleExample.aasm(:work).states
|
||||
# => [:standing, :walking, :running]
|
||||
```
|
||||
|
||||
*Final note*: Support for multiple state machines per class is a pretty new feature
|
||||
(since version `4.3`), so please bear with us in case it doesn't as expected.
|
||||
|
||||
|
||||
|
||||
### ActiveRecord
|
||||
|
||||
AASM comes with support for ActiveRecord and allows automatic persisting of the object's
|
||||
|
|
|
@ -8,36 +8,50 @@ module AASM
|
|||
|
||||
# do not overwrite existing state machines, which could have been created by
|
||||
# inheritance, see class method inherited
|
||||
AASM::StateMachine[base] ||= AASM::StateMachine.new
|
||||
AASM::StateMachine[base] ||= {}
|
||||
|
||||
AASM::Persistence.load_persistence(base)
|
||||
super
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
# make sure inheritance (aka subclassing) works with AASM
|
||||
def inherited(base)
|
||||
AASM::StateMachine[base] = AASM::StateMachine[self].clone
|
||||
AASM::StateMachine[base] = {}
|
||||
AASM::StateMachine[self].keys.each do |state_machine_name|
|
||||
AASM::StateMachine[base][state_machine_name] = AASM::StateMachine[self][state_machine_name].clone
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
# this is the entry point for all state and event definitions
|
||||
def aasm(options={}, &block)
|
||||
@aasm ||= AASM::Base.new(self, options)
|
||||
@aasm.instance_eval(&block) if block # new DSL
|
||||
@aasm
|
||||
end
|
||||
def aasm(*args, &block)
|
||||
if args[0].is_a?(Symbol) || args[0].is_a?(String)
|
||||
# using custom name
|
||||
state_machine_name = args[0].to_sym
|
||||
options = args[1] || {}
|
||||
else
|
||||
# using the default state_machine_name
|
||||
state_machine_name = :default
|
||||
options = args[0] || {}
|
||||
end
|
||||
|
||||
# deprecated, remove in version 4.1
|
||||
def aasm_human_event_name(event) # event_name?
|
||||
warn '[DEPRECATION] AASM: aasm_human_event_name is deprecated, use aasm.human_event_name instead'
|
||||
aasm.human_event_name(event)
|
||||
AASM::StateMachine[self][state_machine_name] ||= AASM::StateMachine.new(state_machine_name)
|
||||
|
||||
@aasm ||= {}
|
||||
@aasm[state_machine_name] ||= AASM::Base.new(self, state_machine_name, AASM::StateMachine[self][state_machine_name], options)
|
||||
@aasm[state_machine_name].instance_eval(&block) if block # new DSL
|
||||
@aasm[state_machine_name]
|
||||
end
|
||||
end # ClassMethods
|
||||
|
||||
def aasm
|
||||
@aasm ||= AASM::InstanceBase.new(self)
|
||||
# this is the entry point for all instance-level access to AASM
|
||||
def aasm(name=:default)
|
||||
unless AASM::StateMachine[self.class][name.to_sym]
|
||||
raise AASM::UnknownStateMachineError.new("There is no state machine with the name '#{name}' defined in #{self.class.name}!")
|
||||
end
|
||||
@aasm ||= {}
|
||||
@aasm[name.to_sym] ||= AASM::InstanceBase.new(self, name.to_sym)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -56,72 +70,72 @@ private
|
|||
return args
|
||||
end
|
||||
|
||||
def aasm_fire_event(event_name, options, *args, &block)
|
||||
event = self.class.aasm.state_machine.events[event_name]
|
||||
def aasm_fire_event(state_machine_name, event_name, options, *args, &block)
|
||||
event = self.class.aasm(state_machine_name).state_machine.events[event_name]
|
||||
begin
|
||||
old_state = aasm.state_object_for_name(aasm.current_state)
|
||||
old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
|
||||
|
||||
# new event before callback
|
||||
event.fire_callbacks(
|
||||
:before,
|
||||
self,
|
||||
*process_args(event, aasm.current_state, *args)
|
||||
*process_args(event, aasm(state_machine_name).current_state, *args)
|
||||
)
|
||||
|
||||
if may_fire_to = event.may_fire?(self, *args)
|
||||
old_state.fire_callbacks(:before_exit, self,
|
||||
*process_args(event, aasm.current_state, *args))
|
||||
*process_args(event, aasm(state_machine_name).current_state, *args))
|
||||
old_state.fire_callbacks(:exit, self,
|
||||
*process_args(event, aasm.current_state, *args)) # TODO: remove for AASM 4?
|
||||
*process_args(event, aasm(state_machine_name).current_state, *args))
|
||||
|
||||
if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
|
||||
aasm_fired(event, old_state, new_state_name, options, *args, &block)
|
||||
aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
|
||||
else
|
||||
aasm_failed(event_name, old_state)
|
||||
aasm_failed(state_machine_name, event_name, old_state)
|
||||
end
|
||||
else
|
||||
aasm_failed(event_name, old_state)
|
||||
aasm_failed(state_machine_name, event_name, old_state)
|
||||
end
|
||||
rescue StandardError => e
|
||||
event.fire_callbacks(:error, self, e, *process_args(event, aasm.current_state, *args)) || raise(e)
|
||||
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) || raise(e)
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_fired(event, old_state, new_state_name, options, *args)
|
||||
def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
|
||||
persist = options[:persist]
|
||||
|
||||
new_state = aasm.state_object_for_name(new_state_name)
|
||||
new_state = aasm(state_machine_name).state_object_for_name(new_state_name)
|
||||
|
||||
new_state.fire_callbacks(:before_enter, self,
|
||||
*process_args(event, aasm.current_state, *args))
|
||||
*process_args(event, aasm(state_machine_name).current_state, *args))
|
||||
|
||||
new_state.fire_callbacks(:enter, self,
|
||||
*process_args(event, aasm.current_state, *args)) # TODO: remove for AASM 4?
|
||||
*process_args(event, aasm(state_machine_name).current_state, *args)) # TODO: remove for AASM 4?
|
||||
|
||||
persist_successful = true
|
||||
if persist
|
||||
persist_successful = aasm.set_current_state_with_persistence(new_state_name)
|
||||
persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
|
||||
if persist_successful
|
||||
yield if block_given?
|
||||
event.fire_callbacks(:success, self)
|
||||
end
|
||||
else
|
||||
aasm.current_state = new_state_name
|
||||
aasm(state_machine_name).current_state = new_state_name
|
||||
yield if block_given?
|
||||
end
|
||||
|
||||
if persist_successful
|
||||
old_state.fire_callbacks(:after_exit, self,
|
||||
*process_args(event, aasm.current_state, *args))
|
||||
*process_args(event, aasm(state_machine_name).current_state, *args))
|
||||
new_state.fire_callbacks(:after_enter, self,
|
||||
*process_args(event, aasm.current_state, *args))
|
||||
*process_args(event, aasm(state_machine_name).current_state, *args))
|
||||
event.fire_callbacks(
|
||||
:after,
|
||||
self,
|
||||
*process_args(event, old_state.name, *args)
|
||||
)
|
||||
|
||||
self.aasm_event_fired(event.name, old_state.name, aasm.current_state) if self.respond_to?(:aasm_event_fired)
|
||||
self.aasm_event_fired(event.name, old_state.name, aasm(state_machine_name).current_state) if self.respond_to?(:aasm_event_fired)
|
||||
else
|
||||
self.aasm_event_failed(event.name, old_state.name) if self.respond_to?(:aasm_event_failed)
|
||||
end
|
||||
|
@ -129,13 +143,13 @@ private
|
|||
persist_successful
|
||||
end
|
||||
|
||||
def aasm_failed(event_name, old_state)
|
||||
def aasm_failed(state_machine_name, event_name, old_state)
|
||||
if self.respond_to?(:aasm_event_failed)
|
||||
self.aasm_event_failed(event_name, old_state.name)
|
||||
end
|
||||
|
||||
if AASM::StateMachine[self.class].config.whiny_transitions
|
||||
raise AASM::InvalidTransition.new(self, event_name)
|
||||
if AASM::StateMachine[self.class][state_machine_name].config.whiny_transitions
|
||||
raise AASM::InvalidTransition.new(self, event_name, state_machine_name)
|
||||
else
|
||||
false
|
||||
end
|
||||
|
|
|
@ -3,10 +3,12 @@ module AASM
|
|||
|
||||
attr_reader :state_machine
|
||||
|
||||
def initialize(klass, options={}, &block)
|
||||
def initialize(klass, name, state_machine, options={}, &block)
|
||||
@klass = klass
|
||||
@state_machine = AASM::StateMachine[@klass]
|
||||
@state_machine.config.column ||= (options[:column] || :aasm_state).to_sym # aasm4
|
||||
@name = name
|
||||
# @state_machine = @klass.aasm(@name).state_machine
|
||||
@state_machine = state_machine
|
||||
@state_machine.config.column ||= (options[:column] || default_column).to_sym
|
||||
# @state_machine.config.column = options[:column].to_sym if options[:column] # master
|
||||
@options = options
|
||||
|
||||
|
@ -31,7 +33,7 @@ module AASM
|
|||
# and attribute is directly assigned though
|
||||
@klass.class_eval %Q(
|
||||
def #{@state_machine.config.column}=(state_name)
|
||||
if self.class.aasm.state_machine.config.no_direct_assignment
|
||||
if self.class.aasm(:#{@name}).state_machine.config.no_direct_assignment
|
||||
raise AASM::NoDirectAssignmentError.new(
|
||||
'direct assignment of AASM column has been disabled (see AASM configuration for this class)'
|
||||
)
|
||||
|
@ -64,10 +66,16 @@ module AASM
|
|||
def state(name, options={})
|
||||
@state_machine.add_state(name, @klass, options)
|
||||
|
||||
@klass.send(:define_method, "#{name}?") do
|
||||
aasm.current_state == name
|
||||
if @klass.instance_methods.include?("#{name}?")
|
||||
warn "#{@klass.name}: The state name #{name} is already used!"
|
||||
end
|
||||
|
||||
@klass.class_eval <<-EORUBY, __FILE__, __LINE__ + 1
|
||||
def #{name}?
|
||||
aasm(:#{@name}).current_state == :#{name}
|
||||
end
|
||||
EORUBY
|
||||
|
||||
unless @klass.const_defined?("STATE_#{name.upcase}")
|
||||
@klass.const_set("STATE_#{name.upcase}", name)
|
||||
end
|
||||
|
@ -75,24 +83,30 @@ module AASM
|
|||
|
||||
# define an event
|
||||
def event(name, options={}, &block)
|
||||
@state_machine.events[name] = AASM::Core::Event.new(name, options, &block)
|
||||
@state_machine.add_event(name, options, &block)
|
||||
|
||||
if @klass.instance_methods.include?("may_#{name}?".to_sym)
|
||||
warn "#{@klass.name}: The event name #{name} is already used!"
|
||||
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.
|
||||
@klass.send(:define_method, "may_#{name}?") do |*args|
|
||||
aasm.may_fire_event?(name, *args)
|
||||
end
|
||||
@klass.class_eval <<-EORUBY, __FILE__, __LINE__ + 1
|
||||
def may_#{name}?(*args)
|
||||
aasm(:#{@name}).may_fire_event?(:#{name}, *args)
|
||||
end
|
||||
|
||||
@klass.send(:define_method, "#{name}!") do |*args, &block|
|
||||
aasm.current_event = "#{name}!".to_sym
|
||||
aasm_fire_event(name, {:persist => true}, *args, &block)
|
||||
end
|
||||
def #{name}!(*args, &block)
|
||||
aasm(:#{@name}).current_event = :#{name}!
|
||||
aasm_fire_event(:#{@name}, :#{name}, {:persist => true}, *args, &block)
|
||||
end
|
||||
|
||||
@klass.send(:define_method, "#{name}") do |*args, &block|
|
||||
aasm.current_event = name.to_sym
|
||||
aasm_fire_event(name, {:persist => false}, *args, &block)
|
||||
end
|
||||
def #{name}(*args, &block)
|
||||
aasm(:#{@name}).current_event = :#{name}
|
||||
aasm_fire_event(:#{@name}, :#{name}, {:persist => false}, *args, &block)
|
||||
end
|
||||
EORUBY
|
||||
end
|
||||
|
||||
def states
|
||||
|
@ -122,6 +136,10 @@ module AASM
|
|||
|
||||
private
|
||||
|
||||
def default_column
|
||||
@name.to_sym == :default ? :aasm_state : @name.to_sym
|
||||
end
|
||||
|
||||
def configure(key, default_value)
|
||||
if @options.key?(key)
|
||||
@state_machine.config.send("#{key}=", @options[key])
|
||||
|
|
|
@ -2,10 +2,11 @@ module AASM::Core
|
|||
class Event
|
||||
include DslHelper
|
||||
|
||||
attr_reader :name, :options
|
||||
attr_reader :name, :state_machine, :options
|
||||
|
||||
def initialize(name, options = {}, &block)
|
||||
def initialize(name, state_machine, options = {}, &block)
|
||||
@name = name
|
||||
@state_machine = state_machine
|
||||
@transitions = []
|
||||
@guards = Array(options[:guard] || options[:guards] || options[:if])
|
||||
@unless = Array(options[:unless]) #TODO: This could use a better name
|
||||
|
@ -61,11 +62,11 @@ module AASM::Core
|
|||
if definitions # define new transitions
|
||||
# Create a separate transition for each from-state to the given state
|
||||
Array(definitions[:from]).each do |s|
|
||||
@transitions << AASM::Core::Transition.new(attach_event_guards(definitions.merge(:from => s.to_sym)), &block)
|
||||
@transitions << AASM::Core::Transition.new(self, attach_event_guards(definitions.merge(:from => s.to_sym)), &block)
|
||||
end
|
||||
# Create a transition if :to is specified without :from (transitions from ANY state)
|
||||
if @transitions.empty? && definitions[:to]
|
||||
@transitions << AASM::Core::Transition.new(attach_event_guards(definitions), &block)
|
||||
@transitions << AASM::Core::Transition.new(self, attach_event_guards(definitions), &block)
|
||||
end
|
||||
end
|
||||
@transitions
|
||||
|
@ -89,7 +90,7 @@ module AASM::Core
|
|||
def _fire(obj, options={}, to_state=nil, *args)
|
||||
result = options[:test_only] ? false : nil
|
||||
if @transitions.map(&:from).any?
|
||||
transitions = @transitions.select { |t| t.from == obj.aasm.current_state }
|
||||
transitions = @transitions.select { |t| t.from == obj.aasm(state_machine.name).current_state }
|
||||
return result if transitions.size == 0
|
||||
else
|
||||
transitions = @transitions
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
module AASM::Core
|
||||
class State
|
||||
attr_reader :name, :options
|
||||
attr_reader :name, :state_machine, :options
|
||||
|
||||
def initialize(name, klass, options={})
|
||||
def initialize(name, klass, state_machine, options={})
|
||||
@name = name
|
||||
@klass = klass
|
||||
@state_machine = state_machine
|
||||
update(options)
|
||||
end
|
||||
|
||||
|
|
|
@ -2,12 +2,13 @@ module AASM::Core
|
|||
class Transition
|
||||
include DslHelper
|
||||
|
||||
attr_reader :from, :to, :opts
|
||||
attr_reader :from, :to, :event, :opts
|
||||
alias_method :options, :opts
|
||||
|
||||
def initialize(opts, &block)
|
||||
def initialize(event, opts, &block)
|
||||
add_options_from_dsl(opts, [:on_transition, :guard, :after], &block) if block
|
||||
|
||||
@event = event
|
||||
@from = opts[:from]
|
||||
@to = opts[:to]
|
||||
@guards = Array(opts[:guards]) + Array(opts[:guard]) + Array(opts[:if])
|
||||
|
@ -44,8 +45,8 @@ module AASM::Core
|
|||
|
||||
def invoke_callbacks_compatible_with_guard(code, record, args, options={})
|
||||
if record.respond_to?(:aasm)
|
||||
record.aasm.from_state = @from if record.aasm.respond_to?(:from_state=)
|
||||
record.aasm.to_state = @to if record.aasm.respond_to?(:to_state=)
|
||||
record.aasm(event.state_machine.name).from_state = @from if record.aasm(event.state_machine.name).respond_to?(:from_state=)
|
||||
record.aasm(event.state_machine.name).to_state = @to if record.aasm(event.state_machine.name).respond_to?(:to_state=)
|
||||
end
|
||||
|
||||
case code
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
module AASM
|
||||
|
||||
class UnknownStateMachineError < RuntimeError; end
|
||||
|
||||
class InvalidTransition < RuntimeError
|
||||
attr_reader :object, :event_name
|
||||
def initialize(object, event_name)
|
||||
@object, @event_name = object, event_name
|
||||
attr_reader :object, :event_name, :state_machine_name
|
||||
|
||||
def initialize(object, event_name, state_machine_name)
|
||||
@object, @event_name, @state_machine_name = object, event_name, state_machine_name
|
||||
end
|
||||
|
||||
def message
|
||||
"Event '#{event_name}' cannot transition from '#{object.aasm.current_state}'"
|
||||
"Event '#{event_name}' cannot transition from '#{object.aasm(state_machine_name).current_state}'"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,21 +3,22 @@ module AASM
|
|||
|
||||
attr_accessor :from_state, :to_state, :current_event
|
||||
|
||||
def initialize(instance)
|
||||
def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
|
||||
@instance = instance
|
||||
@name = name
|
||||
end
|
||||
|
||||
def current_state
|
||||
@instance.aasm_read_state
|
||||
@instance.aasm_read_state(@name)
|
||||
end
|
||||
|
||||
def current_state=(state)
|
||||
@instance.aasm_write_state_without_persistence(state)
|
||||
@current_state = state
|
||||
@instance.aasm_write_state_without_persistence(state, @name)
|
||||
# @current_state = state
|
||||
end
|
||||
|
||||
def enter_initial_state
|
||||
state_name = determine_state_name(@instance.class.aasm.initial_state)
|
||||
state_name = determine_state_name(@instance.class.aasm(@name).initial_state)
|
||||
state_object = state_object_for_name(state_name)
|
||||
|
||||
state_object.fire_callbacks(:before_enter, @instance)
|
||||
|
@ -29,24 +30,24 @@ module AASM
|
|||
end
|
||||
|
||||
def human_state
|
||||
AASM::Localizer.new.human_state_name(@instance.class, current_state)
|
||||
AASM::Localizer.new.human_state_name(@instance.class, state_object_for_name(current_state))
|
||||
end
|
||||
|
||||
def states(options={})
|
||||
if options[:permitted]
|
||||
# ugliness level 1000
|
||||
permitted_event_names = events(:permitted => true).map(&:name)
|
||||
transitions = @instance.class.aasm.state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
|
||||
transitions = @instance.class.aasm(@name).state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
|
||||
tos = transitions.map {|t| t[0] ? t[0].to : nil}.flatten.compact.map(&:to_sym).uniq
|
||||
@instance.class.aasm.states.select {|s| tos.include?(s.name.to_sym)}
|
||||
@instance.class.aasm(@name).states.select {|s| tos.include?(s.name.to_sym)}
|
||||
else
|
||||
@instance.class.aasm.states
|
||||
@instance.class.aasm(@name).states
|
||||
end
|
||||
end
|
||||
|
||||
def events(options={})
|
||||
state = options[:state] || current_state
|
||||
events = @instance.class.aasm.events.select {|e| e.transitions_from_state?(state) }
|
||||
events = @instance.class.aasm(@name).events.select {|e| e.transitions_from_state?(state) }
|
||||
|
||||
if options[:permitted]
|
||||
# filters the results of events_for_current_state so that only those that
|
||||
|
@ -58,7 +59,7 @@ module AASM
|
|||
end
|
||||
|
||||
def state_object_for_name(name)
|
||||
obj = @instance.class.aasm.states.find {|s| s == name}
|
||||
obj = @instance.class.aasm(@name).states.find {|s| s == name}
|
||||
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
|
||||
obj
|
||||
end
|
||||
|
@ -75,7 +76,7 @@ module AASM
|
|||
end
|
||||
|
||||
def may_fire_event?(name, *args)
|
||||
if event = @instance.class.aasm.state_machine.events[name]
|
||||
if event = @instance.class.aasm(@name).state_machine.events[name]
|
||||
!!event.may_fire?(@instance, *args)
|
||||
else
|
||||
false # unknown event
|
||||
|
@ -83,7 +84,7 @@ module AASM
|
|||
end
|
||||
|
||||
def set_current_state_with_persistence(state)
|
||||
save_success = @instance.aasm_write_state(state)
|
||||
save_success = @instance.aasm_write_state(state, @name)
|
||||
self.current_state = state if save_success
|
||||
save_success
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ module AASM
|
|||
|
||||
def item_for(klass, state, ancestor, options={})
|
||||
separator = options[:old_style] ? '.' : '/'
|
||||
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm.attribute_name}#{separator}#{state}"
|
||||
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm(state.state_machine.name).attribute_name}#{separator}#{state}"
|
||||
end
|
||||
|
||||
def translate_queue(checklist)
|
||||
|
|
|
@ -30,7 +30,6 @@ module AASM
|
|||
#
|
||||
def self.included(base)
|
||||
base.send(:include, AASM::Persistence::Base)
|
||||
base.extend AASM::Persistence::ActiveRecordPersistence::ClassMethods
|
||||
base.send(:include, AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
|
||||
|
||||
base.after_initialize do
|
||||
|
@ -41,34 +40,6 @@ module AASM
|
|||
base.validate :aasm_validate_states
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
def find_in_state(number, state, *args)
|
||||
with_state_scope state do
|
||||
find(number, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def count_in_state(state, *args)
|
||||
with_state_scope state do
|
||||
count(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_in_state(state, *args)
|
||||
with_state_scope state do
|
||||
calculate(*args)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def with_state_scope(state)
|
||||
with_scope :find => {:conditions => ["#{table_name}.#{aasm_column} = ?", state.to_s]} do
|
||||
yield if block_given?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
# Writes <tt>state</tt> to the state column and persists it to the database
|
||||
|
@ -80,18 +51,18 @@ module AASM
|
|||
# Foo.find(1).aasm.current_state # => :closed
|
||||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state(state)
|
||||
old_value = read_attribute(self.class.aasm.attribute_name)
|
||||
aasm_write_attribute state
|
||||
def aasm_write_state(state, name=:default)
|
||||
old_value = read_attribute(self.class.aasm(name).attribute_name)
|
||||
aasm_write_attribute state, name
|
||||
|
||||
success = if aasm_skipping_validations
|
||||
value = aasm_raw_attribute_value state
|
||||
self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm.attribute_name => value) == 1
|
||||
success = if aasm_skipping_validations(name)
|
||||
value = aasm_raw_attribute_value(state, name)
|
||||
self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm(name).attribute_name => value) == 1
|
||||
else
|
||||
self.save
|
||||
end
|
||||
unless success
|
||||
write_attribute(self.class.aasm.attribute_name, old_value)
|
||||
write_attribute(self.class.aasm(name).attribute_name, old_value)
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -110,39 +81,39 @@ module AASM
|
|||
# Foo.find(1).aasm.current_state # => :closed
|
||||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state_without_persistence(state)
|
||||
aasm_write_attribute state
|
||||
def aasm_write_state_without_persistence(state, name=:default)
|
||||
aasm_write_attribute(state, name)
|
||||
end
|
||||
|
||||
private
|
||||
def aasm_enum
|
||||
case AASM::StateMachine[self.class].config.enum
|
||||
def aasm_enum(name=:default)
|
||||
case AASM::StateMachine[self.class][name].config.enum
|
||||
when false then nil
|
||||
when true then aasm_guess_enum_method
|
||||
when nil then aasm_guess_enum_method if aasm_column_looks_like_enum
|
||||
else AASM::StateMachine[self.class].config.enum
|
||||
when true then aasm_guess_enum_method(name)
|
||||
when nil then aasm_guess_enum_method(name) if aasm_column_looks_like_enum(name)
|
||||
else AASM::StateMachine[self.class][name].config.enum
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_column_looks_like_enum
|
||||
self.class.columns_hash[self.class.aasm.attribute_name.to_s].type == :integer
|
||||
def aasm_column_looks_like_enum(name=:default)
|
||||
self.class.columns_hash[self.class.aasm(name).attribute_name.to_s].type == :integer
|
||||
end
|
||||
|
||||
def aasm_guess_enum_method
|
||||
self.class.aasm.attribute_name.to_s.pluralize.to_sym
|
||||
def aasm_guess_enum_method(name=:default)
|
||||
self.class.aasm(name).attribute_name.to_s.pluralize.to_sym
|
||||
end
|
||||
|
||||
def aasm_skipping_validations
|
||||
AASM::StateMachine[self.class].config.skip_validation_on_save
|
||||
def aasm_skipping_validations(state_machine_name)
|
||||
AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
|
||||
end
|
||||
|
||||
def aasm_write_attribute(state)
|
||||
write_attribute self.class.aasm.attribute_name, aasm_raw_attribute_value(state)
|
||||
def aasm_write_attribute(state, name=:default)
|
||||
write_attribute(self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name))
|
||||
end
|
||||
|
||||
def aasm_raw_attribute_value(state)
|
||||
if aasm_enum
|
||||
self.class.send(aasm_enum)[state]
|
||||
def aasm_raw_attribute_value(state, name=:default)
|
||||
if aasm_enum(name)
|
||||
self.class.send(aasm_enum(name))[state]
|
||||
else
|
||||
state.to_s
|
||||
end
|
||||
|
@ -164,32 +135,36 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
# checking via respond_to? does not work in Rails <= 3
|
||||
# if respond_to?(self.class.aasm.attribute_name) && send(self.class.aasm.attribute_name).blank? # Rails 4
|
||||
if attribute_names.include?(self.class.aasm.attribute_name.to_s) && send(self.class.aasm.attribute_name).blank?
|
||||
aasm.enter_initial_state
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
# checking via respond_to? does not work in Rails <= 3
|
||||
# if respond_to?(self.class.aasm(state_machine_name).attribute_name) && send(self.class.aasm(state_machine_name).attribute_name).blank? # Rails 4
|
||||
if attribute_names.include?(self.class.aasm(state_machine_name).attribute_name.to_s) && send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
aasm(state_machine_name).enter_initial_state
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_fire_event(name, options, *args, &block)
|
||||
success = options[:persist] ? self.class.transaction(:requires_new => requires_new?) { super } : super
|
||||
def aasm_fire_event(state_machine_name, name, options, *args, &block)
|
||||
success = options[:persist] ? self.class.transaction(:requires_new => requires_new?(state_machine_name)) { super } : super
|
||||
|
||||
if success && options[:persist]
|
||||
event = self.class.aasm.state_machine.events[name]
|
||||
event = self.class.aasm(state_machine_name).state_machine.events[name]
|
||||
event.fire_callbacks(:after_commit, self, *args)
|
||||
end
|
||||
|
||||
success
|
||||
end
|
||||
|
||||
def requires_new?
|
||||
AASM::StateMachine[self.class].config.requires_new_transaction
|
||||
def requires_new?(state_machine_name)
|
||||
AASM::StateMachine[self.class][state_machine_name].config.requires_new_transaction
|
||||
end
|
||||
|
||||
def aasm_validate_states
|
||||
unless aasm_skipping_validations
|
||||
if aasm.current_state && !aasm.states.include?(aasm.current_state)
|
||||
self.errors.add(AASM::StateMachine[self.class].config.column , "is invalid")
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
unless aasm_skipping_validations(state_machine_name)
|
||||
if aasm(state_machine_name).current_state && !aasm(state_machine_name).states.include?(aasm(state_machine_name).current_state)
|
||||
self.errors.add(AASM::StateMachine[self.class][state_machine_name].config.column , "is invalid")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,10 +32,10 @@ module AASM
|
|||
# NOTE: intended to be called from an event
|
||||
#
|
||||
# This allows for nil aasm states - be sure to add validation to your model
|
||||
def aasm_read_state
|
||||
state = send(self.class.aasm.attribute_name)
|
||||
def aasm_read_state(name=:default)
|
||||
state = send(self.class.aasm(name).attribute_name)
|
||||
if new_record?
|
||||
state.blank? ? aasm.determine_state_name(self.class.aasm.initial_state) : state.to_sym
|
||||
state.blank? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : state.to_sym
|
||||
else
|
||||
state.blank? ? nil : state.to_sym
|
||||
end
|
||||
|
@ -55,10 +55,10 @@ module AASM
|
|||
# make sure to create a (named) scope for each state
|
||||
def state_with_scope(name, *args)
|
||||
state_without_scope(name, *args)
|
||||
if AASM::StateMachine[@klass].config.create_scopes && !@klass.respond_to?(name)
|
||||
if @state_machine.config.create_scopes && !@klass.respond_to?(name)
|
||||
if @klass.ancestors.map {|klass| klass.to_s}.include?("ActiveRecord::Base")
|
||||
|
||||
conditions = {"#{@klass.table_name}.#{@klass.aasm.attribute_name}" => name.to_s}
|
||||
conditions = {"#{@klass.table_name}.#{@klass.aasm(@name).attribute_name}" => name.to_s}
|
||||
if ActiveRecord::VERSION::MAJOR >= 3
|
||||
@klass.class_eval do
|
||||
scope name, lambda { where(conditions) }
|
||||
|
@ -69,10 +69,10 @@ module AASM
|
|||
end
|
||||
end
|
||||
elsif @klass.ancestors.map {|klass| klass.to_s}.include?("Mongoid::Document")
|
||||
scope_options = lambda { @klass.send(:where, {@klass.aasm.attribute_name.to_sym => name.to_s}) }
|
||||
scope_options = lambda { @klass.send(:where, {@klass.aasm(@name).attribute_name.to_sym => name.to_s}) }
|
||||
@klass.send(:scope, name, scope_options)
|
||||
elsif @klass.ancestors.map {|klass| klass.to_s}.include?("MongoMapper::Document")
|
||||
conditions = { @klass.aasm.attribute_name.to_sym => name.to_s }
|
||||
conditions = { @klass.aasm(@name).attribute_name.to_sym => name.to_s }
|
||||
@klass.scope(name, lambda { @klass.where(conditions) })
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,6 @@ module AASM
|
|||
#
|
||||
def self.included(base)
|
||||
base.send(:include, AASM::Persistence::Base)
|
||||
base.extend AASM::Persistence::MongoMapperPersistence::ClassMethods
|
||||
base.send(:include, AASM::Persistence::MongoMapperPersistence::InstanceMethods)
|
||||
|
||||
base.before_create :aasm_ensure_initial_state
|
||||
|
@ -41,26 +40,6 @@ module AASM
|
|||
base.validate :aasm_validate_states
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
def find_in_state(number, state, *args)
|
||||
with_state_scope(state).find!(number, *args)
|
||||
end
|
||||
|
||||
def count_in_state(state, *args)
|
||||
with_state_scope(state).count(*args)
|
||||
end
|
||||
|
||||
def calculate_in_state(state, *args)
|
||||
with_state_scope(state).calculate(*args)
|
||||
end
|
||||
|
||||
protected
|
||||
def with_state_scope(state)
|
||||
where(aasm.attribute_name.to_sym => state.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
# Writes <tt>state</tt> to the state column and persists it to the database
|
||||
|
@ -72,18 +51,18 @@ module AASM
|
|||
# Foo.find(1).aasm.current_state # => :closed
|
||||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state(state)
|
||||
old_value = read_attribute(self.class.aasm.attribute_name)
|
||||
aasm_write_attribute state
|
||||
def aasm_write_state(state, name=:default)
|
||||
old_value = read_attribute(self.class.aasm(name).attribute_name)
|
||||
write_attribute(self.class.aasm(name).attribute_name, state)
|
||||
|
||||
success = if aasm_skipping_validations
|
||||
value = aasm_raw_attribute_value state
|
||||
self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm.attribute_name => value) == 1
|
||||
success = if aasm_skipping_validations(name)
|
||||
value = aasm_raw_attribute_value(state, name)
|
||||
self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm(name).attribute_name => value) == 1
|
||||
else
|
||||
self.save
|
||||
end
|
||||
unless success
|
||||
write_attribute(self.class.aasm.attribute_name, old_value)
|
||||
write_attribute(self.class.aasm(name).attribute_name, old_value)
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -102,39 +81,39 @@ module AASM
|
|||
# Foo.find(1).aasm.current_state # => :closed
|
||||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state_without_persistence(state)
|
||||
aasm_write_attribute state
|
||||
def aasm_write_state_without_persistence(state, name=:default)
|
||||
aasm_write_attribute(state, name)
|
||||
end
|
||||
|
||||
private
|
||||
def aasm_enum
|
||||
case AASM::StateMachine[self.class].config.enum
|
||||
def aasm_enum(name=:default)
|
||||
case AASM::StateMachine[self.class][name].config.enum
|
||||
when false then nil
|
||||
when true then aasm_guess_enum_method
|
||||
when nil then aasm_guess_enum_method if aasm_column_looks_like_enum
|
||||
else AASM::StateMachine[self.class].config.enum
|
||||
when true then aasm_guess_enum_method(name)
|
||||
when nil then aasm_guess_enum_method(name) if aasm_column_looks_like_enum(name)
|
||||
else AASM::StateMachine[self.class][name].config.enum
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_column_looks_like_enum
|
||||
self.class.keys[self.class.aasm.attribute_name.to_s].type == Integer
|
||||
def aasm_column_looks_like_enum(name)
|
||||
self.class.keys[self.class.aasm(name).attribute_name.to_s].type == Integer
|
||||
end
|
||||
|
||||
def aasm_guess_enum_method
|
||||
self.class.aasm.attribute_name.to_s.pluralize.to_sym
|
||||
def aasm_guess_enum_method(name)
|
||||
self.class.aasm(name).attribute_name.to_s.pluralize.to_sym
|
||||
end
|
||||
|
||||
def aasm_skipping_validations
|
||||
AASM::StateMachine[self.class].config.skip_validation_on_save
|
||||
def aasm_skipping_validations(state_machine_name)
|
||||
AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
|
||||
end
|
||||
|
||||
def aasm_write_attribute(state)
|
||||
write_attribute self.class.aasm.attribute_name, aasm_raw_attribute_value(state)
|
||||
def aasm_write_attribute(state, name=:default)
|
||||
write_attribute self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name)
|
||||
end
|
||||
|
||||
def aasm_raw_attribute_value(state)
|
||||
if aasm_enum
|
||||
self.class.send(aasm_enum)[state]
|
||||
def aasm_raw_attribute_value(state, name=:default)
|
||||
if aasm_enum(name)
|
||||
self.class.send(aasm_enum(name))[state]
|
||||
else
|
||||
state.to_s
|
||||
end
|
||||
|
@ -156,14 +135,18 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
return send("#{self.class.aasm.attribute_name}=", aasm.enter_initial_state.to_s) if send(self.class.aasm.attribute_name).blank?
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_validate_states
|
||||
send("#{self.class.aasm.attribute_name}=", aasm.enter_initial_state.to_s) if send(self.class.aasm.attribute_name).blank?
|
||||
unless AASM::StateMachine[self.class].config.skip_validation_on_save
|
||||
if aasm.current_state && !aasm.states.include?(aasm.current_state)
|
||||
self.errors.add(AASM::StateMachine[self.class].config.column , "is invalid")
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
unless AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
|
||||
if aasm(state_machine_name).current_state && !aasm(state_machine_name).states.include?(aasm(state_machine_name).current_state)
|
||||
self.errors.add(AASM::StateMachine[self.class][state_machine_name].config.column , "is invalid")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,34 +32,11 @@ module AASM
|
|||
#
|
||||
def self.included(base)
|
||||
base.send(:include, AASM::Persistence::Base)
|
||||
base.extend AASM::Persistence::MongoidPersistence::ClassMethods
|
||||
base.send(:include, AASM::Persistence::MongoidPersistence::InstanceMethods)
|
||||
|
||||
base.after_initialize :aasm_ensure_initial_state
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
def find_in_state(number, state, *args)
|
||||
with_state_scope state do
|
||||
find(number, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def count_in_state(state, *args)
|
||||
with_state_scope state do
|
||||
count(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def with_state_scope(state)
|
||||
with_scope where(aasm.attribute_name.to_sym => state.to_s) do
|
||||
yield if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
# Writes <tt>state</tt> to the state column and persists it to the database
|
||||
|
@ -72,12 +49,12 @@ module AASM
|
|||
# Foo.find(1).aasm.current_state # => :closed
|
||||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state(state)
|
||||
old_value = read_attribute(self.class.aasm.attribute_name)
|
||||
write_attribute(self.class.aasm.attribute_name, state.to_s)
|
||||
def aasm_write_state(state, name=:default)
|
||||
old_value = read_attribute(self.class.aasm(name).attribute_name)
|
||||
write_attribute(self.class.aasm(name).attribute_name, state.to_s)
|
||||
|
||||
unless self.save(:validate => false)
|
||||
write_attribute(self.class.aasm.attribute_name, old_value)
|
||||
write_attribute(self.class.aasm(name).attribute_name, old_value)
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -96,8 +73,8 @@ module AASM
|
|||
# Foo.find(1).aasm.current_state # => :closed
|
||||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state_without_persistence(state)
|
||||
write_attribute(self.class.aasm.attribute_name, state.to_s)
|
||||
def aasm_write_state_without_persistence(state, name=:default)
|
||||
write_attribute(self.class.aasm(name).attribute_name, state.to_s)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -118,16 +95,18 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
send("#{self.class.aasm.attribute_name}=", aasm.enter_initial_state.to_s) if send(self.class.aasm.attribute_name).blank?
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
end
|
||||
end
|
||||
end # InstanceMethods
|
||||
|
||||
module NamedScopeMethods
|
||||
def aasm_state_with_named_scope name, options = {}
|
||||
aasm_state_without_named_scope name, options
|
||||
self.named_scope name, :conditions => { "#{table_name}.#{self.aasm.attribute_name}" => name.to_s} unless self.respond_to?(name)
|
||||
end
|
||||
end
|
||||
# module NamedScopeMethods
|
||||
# def aasm_state_with_named_scope name, options = {}
|
||||
# aasm_state_without_named_scope name, options
|
||||
# self.named_scope name, :conditions => { "#{table_name}.#{self.aasm.attribute_name}" => name.to_s} unless self.respond_to?(name)
|
||||
# end
|
||||
# end
|
||||
end
|
||||
end # Persistence
|
||||
end # AASM
|
||||
|
|
|
@ -2,21 +2,22 @@ module AASM
|
|||
module Persistence
|
||||
module PlainPersistence
|
||||
|
||||
def aasm_read_state
|
||||
# all the following lines behave like @current_state ||= aasm.enter_initial_state
|
||||
current = aasm.instance_variable_get("@current_state")
|
||||
# may be overwritten by persistence mixins
|
||||
def aasm_read_state(name=:default)
|
||||
# all the following lines behave like @current_state ||= aasm(name).enter_initial_state
|
||||
current = aasm(name).instance_variable_get("@current_state_#{name}")
|
||||
return current if current
|
||||
aasm.instance_variable_set("@current_state", aasm.enter_initial_state)
|
||||
aasm(name).instance_variable_set("@current_state_#{name}", aasm(name).enter_initial_state)
|
||||
end
|
||||
|
||||
# may be overwritten by persistence mixins
|
||||
def aasm_write_state(new_state)
|
||||
def aasm_write_state(new_state, name=:default)
|
||||
true
|
||||
end
|
||||
|
||||
# may be overwritten by persistence mixins
|
||||
def aasm_write_state_without_persistence(new_state)
|
||||
true
|
||||
def aasm_write_state_without_persistence(new_state, name=:default)
|
||||
aasm(name).instance_variable_set("@current_state_#{name}", new_state)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -44,10 +44,10 @@ module AASM
|
|||
# NOTE: intended to be called from an event
|
||||
#
|
||||
# This allows for nil aasm states - be sure to add validation to your model
|
||||
def aasm_read_state
|
||||
state = send(self.class.aasm.attribute_name)
|
||||
def aasm_read_state(name=:default)
|
||||
state = send(self.class.aasm(name).attribute_name)
|
||||
if new? && state.to_s.strip.empty?
|
||||
aasm.determine_state_name(self.class.aasm.initial_state)
|
||||
aasm(name).determine_state_name(self.class.aasm(name).initial_state)
|
||||
elsif state.nil?
|
||||
nil
|
||||
else
|
||||
|
@ -71,9 +71,11 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
aasm.enter_initial_state if
|
||||
(new? || values.key?(self.class.aasm.attribute_name)) &&
|
||||
send(self.class.aasm.attribute_name).to_s.strip.empty?
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
aasm(state_machine_name).enter_initial_state if
|
||||
(new? || values.key?(self.class.aasm(state_machine_name).attribute_name)) &&
|
||||
send(self.class.aasm(state_machine_name).attribute_name).to_s.strip.empty?
|
||||
end
|
||||
end
|
||||
|
||||
# Writes <tt>state</tt> to the state column and persists it to the database
|
||||
|
@ -85,8 +87,8 @@ module AASM
|
|||
# Foo[1].aasm.current_state # => :closed
|
||||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state state
|
||||
aasm_column = self.class.aasm.attribute_name
|
||||
def aasm_write_state state, name=:default
|
||||
aasm_column = self.class.aasm(name).attribute_name
|
||||
update_only({aasm_column => state.to_s}, aasm_column)
|
||||
end
|
||||
|
||||
|
@ -102,8 +104,8 @@ module AASM
|
|||
# Foo[1].aasm.current_state # => :closed
|
||||
#
|
||||
# NOTE: intended to be called from an event
|
||||
def aasm_write_state_without_persistence state
|
||||
send("#{self.class.aasm.attribute_name}=", state.to_s)
|
||||
def aasm_write_state_without_persistence state, name=:default
|
||||
send("#{self.class.aasm(name).attribute_name}=", state.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,13 +10,14 @@ module AASM
|
|||
(@machines ||= {})[klass.to_s] = machine
|
||||
end
|
||||
|
||||
attr_accessor :states, :events, :initial_state, :config
|
||||
attr_accessor :states, :events, :initial_state, :config, :name
|
||||
|
||||
def initialize
|
||||
def initialize(name)
|
||||
@initial_state = nil
|
||||
@states = []
|
||||
@events = {}
|
||||
@config = AASM::Configuration.new
|
||||
@name = name
|
||||
end
|
||||
|
||||
# called internally by Ruby 1.9 after clone()
|
||||
|
@ -26,13 +27,17 @@ module AASM
|
|||
@events = @events.dup
|
||||
end
|
||||
|
||||
def add_state(name, klass, options)
|
||||
set_initial_state(name, options)
|
||||
def add_state(state_name, klass, options)
|
||||
set_initial_state(state_name, options)
|
||||
|
||||
# allow reloading, extending or redefining a state
|
||||
@states.delete(name) if @states.include?(name)
|
||||
@states.delete(state_name) if @states.include?(state_name)
|
||||
|
||||
@states << AASM::Core::State.new(name, klass, options)
|
||||
@states << AASM::Core::State.new(state_name, klass, self, options)
|
||||
end
|
||||
|
||||
def add_event(name, options, &block)
|
||||
@events[name] = AASM::Core::Event.new(name, self, options, &block)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
ActiveRecord::Migration.suppress_messages do
|
||||
%w{gates readers writers transients simples no_scopes no_direct_assignments thieves localizer_test_models persisted_states provided_and_persisted_states with_enums with_true_enums with_false_enums false_states}.each do |table_name|
|
||||
%w{gates multiple_gates readers writers transients simples no_scopes multiple_no_scopes no_direct_assignments multiple_no_direct_assignments thieves multiple_thieves localizer_test_models persisted_states provided_and_persisted_states with_enums with_true_enums with_false_enums false_states multiple_with_enums multiple_with_true_enums multiple_with_false_enums multiple_false_states}.each do |table_name|
|
||||
ActiveRecord::Migration.create_table table_name, :force => true do |t|
|
||||
t.string "aasm_state"
|
||||
end
|
||||
|
@ -8,17 +8,34 @@ ActiveRecord::Migration.suppress_messages do
|
|||
ActiveRecord::Migration.create_table "simple_new_dsls", :force => true do |t|
|
||||
t.string "status"
|
||||
end
|
||||
ActiveRecord::Migration.create_table "multiple_simple_new_dsls", :force => true do |t|
|
||||
t.string "status"
|
||||
end
|
||||
|
||||
ActiveRecord::Migration.create_table "complex_active_record_examples", :force => true do |t|
|
||||
t.string "left"
|
||||
t.string "right"
|
||||
end
|
||||
|
||||
ActiveRecord::Migration.create_table "validators", :force => true do |t|
|
||||
t.string "name"
|
||||
t.string "status"
|
||||
end
|
||||
ActiveRecord::Migration.create_table "multiple_validators", :force => true do |t|
|
||||
t.string "name"
|
||||
t.string "status"
|
||||
end
|
||||
|
||||
ActiveRecord::Migration.create_table "transactors", :force => true do |t|
|
||||
t.string "name"
|
||||
t.string "status"
|
||||
t.integer "worker_id"
|
||||
end
|
||||
ActiveRecord::Migration.create_table "multiple_transactors", :force => true do |t|
|
||||
t.string "name"
|
||||
t.string "status"
|
||||
t.integer "worker_id"
|
||||
end
|
||||
|
||||
ActiveRecord::Migration.create_table "workers", :force => true do |t|
|
||||
t.string "name"
|
||||
|
@ -29,9 +46,18 @@ ActiveRecord::Migration.suppress_messages do
|
|||
t.string "name"
|
||||
t.string "status"
|
||||
end
|
||||
ActiveRecord::Migration.create_table "multiple_invalid_persistors", :force => true do |t|
|
||||
t.string "name"
|
||||
t.string "status"
|
||||
end
|
||||
|
||||
ActiveRecord::Migration.create_table "fathers", :force => true do |t|
|
||||
t.string "aasm_state"
|
||||
t.string "type"
|
||||
end
|
||||
|
||||
ActiveRecord::Migration.create_table "basic_active_record_two_state_machines_examples", :force => true do |t|
|
||||
t.string "search"
|
||||
t.string "sync"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
class BasicActiveRecordTwoStateMachinesExample < ActiveRecord::Base
|
||||
include AASM
|
||||
|
||||
aasm :search do
|
||||
state :initialised, :initial => true
|
||||
state :queried
|
||||
state :requested
|
||||
|
||||
event :query do
|
||||
transitions :from => [:initialised, :requested], :to => :queried
|
||||
end
|
||||
event :request do
|
||||
transitions :from => :queried, :to => :requested
|
||||
end
|
||||
end
|
||||
|
||||
aasm :sync do
|
||||
state :unsynced, :initial => true
|
||||
state :synced
|
||||
|
||||
event :synchronise do
|
||||
transitions :from => :unsynced, :to => :synced
|
||||
end
|
||||
end
|
||||
end
|
33
spec/models/active_record/complex_active_record_example.rb
Normal file
33
spec/models/active_record/complex_active_record_example.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
class ComplexActiveRecordExample < ActiveRecord::Base
|
||||
include AASM
|
||||
|
||||
aasm :left, :column => 'left' do
|
||||
state :one, :initial => true
|
||||
state :two
|
||||
state :three
|
||||
|
||||
event :increment do
|
||||
transitions :from => :one, :to => :two
|
||||
transitions :from => :two, :to => :three
|
||||
end
|
||||
event :reset do
|
||||
transitions :from => :three, :to => :one
|
||||
end
|
||||
end
|
||||
|
||||
aasm :right, :column => 'right' do
|
||||
state :alpha, :initial => true
|
||||
state :beta
|
||||
state :gamma
|
||||
|
||||
event :level_up do
|
||||
transitions :from => :alpha, :to => :beta
|
||||
transitions :from => :beta, :to => :gamma
|
||||
end
|
||||
event :level_down do
|
||||
transitions :from => :gamma, :to => :beta
|
||||
transitions :from => :beta, :to => :alpha
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,3 +1,7 @@
|
|||
require_relative 'simple_new_dsl'
|
||||
|
||||
class DerivateNewDsl < SimpleNewDsl
|
||||
end
|
||||
|
||||
class MultipleDerivateNewDsl < MultipleSimpleNewDsl
|
||||
end
|
||||
|
|
|
@ -15,3 +15,21 @@ class FalseState < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleFalseState < ActiveRecord::Base
|
||||
include AASM
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
self.aasm_state = false
|
||||
end
|
||||
|
||||
aasm :left do
|
||||
state :opened
|
||||
state :closed
|
||||
|
||||
event :view do
|
||||
transitions :to => :read, :from => [:needs_attention]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,3 +17,23 @@ class Gate < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleGate < ActiveRecord::Base
|
||||
include AASM
|
||||
|
||||
# Fake this column for testing purposes
|
||||
# attr_accessor :aasm_state
|
||||
|
||||
def value
|
||||
'value'
|
||||
end
|
||||
|
||||
aasm :left, :column => :aasm_state do
|
||||
state :opened
|
||||
state :closed
|
||||
|
||||
event :view do
|
||||
transitions :to => :read, :from => [:needs_attention]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,3 +8,14 @@ class NoDirectAssignment < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleNoDirectAssignment < ActiveRecord::Base
|
||||
include AASM
|
||||
aasm :left, :column => :aasm_state, :no_direct_assignment => true do
|
||||
state :pending, :initial => true
|
||||
state :running
|
||||
event :run do
|
||||
transitions :from => :pending, :to => :running
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,3 +8,14 @@ class NoScope < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleNoScope < ActiveRecord::Base
|
||||
include AASM
|
||||
aasm :left, :column => :aasm_state, :create_scopes => false do
|
||||
state :pending, :initial => true
|
||||
state :running
|
||||
event :run do
|
||||
transitions :from => :pending, :to => :running
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,15 +10,15 @@ class ProvidedAndPersistedState < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def aasm_read_state
|
||||
def aasm_read_state(*args)
|
||||
:gamma
|
||||
end
|
||||
|
||||
def aasm_write_state(new_state)
|
||||
def aasm_write_state(new_state, *args)
|
||||
@persisted_store = new_state
|
||||
end
|
||||
|
||||
def aasm_write_state_without_persistence(new_state)
|
||||
def aasm_write_state_without_persistence(new_state, *args)
|
||||
@transient_store = new_state
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,3 +6,12 @@ class SimpleNewDsl < ActiveRecord::Base
|
|||
state :new
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleSimpleNewDsl < ActiveRecord::Base
|
||||
include AASM
|
||||
aasm :left, :column => :status
|
||||
aasm :left do
|
||||
state :unknown_scope
|
||||
state :new
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,3 +12,18 @@ class Thief < ActiveRecord::Base
|
|||
end
|
||||
attr_accessor :skilled, :aasm_state
|
||||
end
|
||||
|
||||
class MultipleThief < ActiveRecord::Base
|
||||
if ActiveRecord::VERSION::MAJOR >= 3
|
||||
self.table_name = 'multiple_thieves'
|
||||
else
|
||||
set_table_name "multiple_thieves"
|
||||
end
|
||||
include AASM
|
||||
aasm :left, :column => :aasm_state do
|
||||
state :rich
|
||||
state :jailed
|
||||
initial_state Proc.new {|thief| thief.skilled ? :rich : :jailed }
|
||||
end
|
||||
attr_accessor :skilled, :aasm_state
|
||||
end
|
||||
|
|
|
@ -17,3 +17,23 @@ class WithEnum < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleWithEnum < ActiveRecord::Base
|
||||
include AASM
|
||||
|
||||
# Fake this column for testing purposes
|
||||
attr_accessor :aasm_state
|
||||
|
||||
def self.test
|
||||
{}
|
||||
end
|
||||
|
||||
aasm :left, :enum => :test do
|
||||
state :opened
|
||||
state :closed
|
||||
|
||||
event :view do
|
||||
transitions :to => :read, :from => [:needs_attention]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,3 +13,19 @@ class WithFalseEnum < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleWithFalseEnum < ActiveRecord::Base
|
||||
include AASM
|
||||
|
||||
# Fake this column for testing purposes
|
||||
attr_accessor :aasm_state
|
||||
|
||||
aasm :left, :enum => false do
|
||||
state :opened
|
||||
state :closed
|
||||
|
||||
event :view do
|
||||
transitions :to => :read, :from => [:needs_attention]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,3 +17,23 @@ class WithTrueEnum < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleWithTrueEnum < ActiveRecord::Base
|
||||
include AASM
|
||||
|
||||
# Fake this column for testing purposes
|
||||
attr_accessor :aasm_state
|
||||
|
||||
def value
|
||||
'value'
|
||||
end
|
||||
|
||||
aasm :left, :enum => true do
|
||||
state :opened
|
||||
state :closed
|
||||
|
||||
event :view do
|
||||
transitions :to => :read, :from => [:needs_attention]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
25
spec/models/basic_two_state_machines_example.rb
Normal file
25
spec/models/basic_two_state_machines_example.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
class BasicTwoStateMachinesExample
|
||||
include AASM
|
||||
|
||||
aasm :search do
|
||||
state :initialised, :initial => true
|
||||
state :queried
|
||||
state :requested
|
||||
|
||||
event :query do
|
||||
transitions :from => [:initialised, :requested], :to => :queried
|
||||
end
|
||||
event :request do
|
||||
transitions :from => :queried, :to => :requested
|
||||
end
|
||||
end
|
||||
|
||||
aasm :sync do
|
||||
state :unsynced, :initial => true
|
||||
state :synced
|
||||
|
||||
event :sync do
|
||||
transitions :from => :unsynced, :to => :synced
|
||||
end
|
||||
end
|
||||
end
|
75
spec/models/callbacks/basic_multiple.rb
Normal file
75
spec/models/callbacks/basic_multiple.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
module Callbacks
|
||||
class BasicMultiple
|
||||
include AASM
|
||||
|
||||
def initialize(options={})
|
||||
@fail_event_guard = options[:fail_event_guard]
|
||||
@fail_transition_guard = options[:fail_transition_guard]
|
||||
@log = options[:log]
|
||||
reset_data
|
||||
end
|
||||
|
||||
def reset_data
|
||||
@data = []
|
||||
end
|
||||
|
||||
def data
|
||||
@data.join(' ')
|
||||
end
|
||||
|
||||
aasm(:left) do
|
||||
state :open, :initial => true,
|
||||
:before_enter => :before_enter_open,
|
||||
:enter => :enter_open,
|
||||
:after_enter => :after_enter_open,
|
||||
:before_exit => :before_exit_open,
|
||||
:exit => :exit_open,
|
||||
:after_exit => :after_exit_open
|
||||
|
||||
state :closed,
|
||||
:before_enter => :before_enter_closed,
|
||||
:enter => :enter_closed,
|
||||
:after_enter => :after_enter_closed,
|
||||
:before_exit => :before_exit_closed,
|
||||
:exit => :exit_closed,
|
||||
:after_exit => :after_exit_closed
|
||||
|
||||
event :left_close, :before => :before_event, :after => :after_event, :guard => :event_guard do
|
||||
transitions :to => :closed, :from => [:open], :guard => :transition_guard, :after => :after_transition
|
||||
end
|
||||
|
||||
event :left_open, :before => :before_event, :after => :after_event do
|
||||
transitions :to => :open, :from => :closed
|
||||
end
|
||||
end
|
||||
|
||||
def log(text)
|
||||
@data << text
|
||||
puts text if @log
|
||||
end
|
||||
|
||||
def aasm_write_state(*args); log("aasm_write_state(#{args.inspect})"); true; end
|
||||
|
||||
def before_enter_open; log('before_enter_open'); end
|
||||
def enter_open; log('enter_open'); end
|
||||
def before_exit_open; log('before_exit_open'); end
|
||||
def after_enter_open; log('after_enter_open'); end
|
||||
def exit_open; log('exit_open'); end
|
||||
def after_exit_open; log('after_exit_open'); end
|
||||
|
||||
def before_enter_closed; log('before_enter_closed'); end
|
||||
def enter_closed; log('enter_closed'); end
|
||||
def before_exit_closed; log('before_exit_closed'); end
|
||||
def exit_closed; log('exit_closed'); end
|
||||
def after_enter_closed; log('after_enter_closed'); end
|
||||
def after_exit_closed; log('after_exit_closed'); end
|
||||
|
||||
def event_guard; log('event_guard'); !@fail_event_guard; end
|
||||
def transition_guard; log('transition_guard'); !@fail_transition_guard; end
|
||||
|
||||
def after_transition; log('after_transition'); end
|
||||
|
||||
def before_event; log('before_event'); end
|
||||
def after_event; log('after_event'); end
|
||||
end
|
||||
end
|
66
spec/models/callbacks/guard_within_block_multiple.rb
Normal file
66
spec/models/callbacks/guard_within_block_multiple.rb
Normal file
|
@ -0,0 +1,66 @@
|
|||
module Callbacks
|
||||
class GuardWithinBlockMultiple
|
||||
include AASM
|
||||
|
||||
def initialize(options={})
|
||||
@fail_event_guard = options[:fail_event_guard]
|
||||
@fail_transition_guard = options[:fail_transition_guard]
|
||||
@log = options[:log]
|
||||
end
|
||||
|
||||
aasm(:left) do
|
||||
state :open, :initial => true,
|
||||
:before_enter => :before_enter_open,
|
||||
:enter => :enter_open,
|
||||
:after_enter => :after_enter_open,
|
||||
:before_exit => :before_exit_open,
|
||||
:exit => :exit_open,
|
||||
:after_exit => :after_exit_open
|
||||
|
||||
state :closed,
|
||||
:before_enter => :before_enter_closed,
|
||||
:enter => :enter_closed,
|
||||
:after_enter => :after_enter_closed,
|
||||
:before_exit => :before_exit_closed,
|
||||
:exit => :exit_closed,
|
||||
:after_exit => :after_exit_closed
|
||||
|
||||
event :close, :before => :before, :after => :after, :guard => :event_guard do
|
||||
transitions :to => :closed, :from => [:open], :after => :transitioning do
|
||||
guard do
|
||||
transition_guard
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
event :open, :before => :before, :after => :after do
|
||||
transitions :to => :open, :from => :closed
|
||||
end
|
||||
end
|
||||
|
||||
def log(text)
|
||||
puts text if @log
|
||||
end
|
||||
|
||||
def before_enter_open; log('before_enter_open'); end
|
||||
def enter_open; log('enter_open'); end
|
||||
def before_exit_open; log('before_exit_open'); end
|
||||
def after_enter_open; log('after_enter_open'); end
|
||||
def exit_open; log('exit_open'); end
|
||||
def after_exit_open; log('after_exit_open'); end
|
||||
|
||||
def before_enter_closed; log('before_enter_closed'); end
|
||||
def enter_closed; log('enter_closed'); end
|
||||
def before_exit_closed; log('before_exit_closed'); end
|
||||
def exit_closed; log('exit_closed'); end
|
||||
def after_enter_closed; log('after_enter_closed'); end
|
||||
def after_exit_closed; log('after_exit_closed'); end
|
||||
|
||||
def event_guard; log('event_guard'); !@fail_event_guard; end
|
||||
def transition_guard; log('transition_guard'); !@fail_transition_guard; end
|
||||
def transitioning; log('transitioning'); end
|
||||
|
||||
def before; log('before'); end
|
||||
def after; log('after'); end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
module Callbacks
|
||||
class MultipleTransitionsTransitionGuardMultiple
|
||||
include AASM
|
||||
|
||||
def initialize(options={})
|
||||
@fail_event_guard = options[:fail_event_guard]
|
||||
@fail_transition_guard = options[:fail_transition_guard]
|
||||
@log = options[:log]
|
||||
end
|
||||
|
||||
aasm(:left) do
|
||||
state :open, :initial => true,
|
||||
:before_enter => :before_enter_open,
|
||||
:enter => :enter_open,
|
||||
:after_enter => :after_enter_open,
|
||||
:before_exit => :before_exit_open,
|
||||
:exit => :exit_open,
|
||||
:after_exit => :after_exit_open
|
||||
|
||||
state :closed,
|
||||
:before_enter => :before_enter_closed,
|
||||
:enter => :enter_closed,
|
||||
:after_enter => :after_enter_closed,
|
||||
:before_exit => :before_exit_closed,
|
||||
:exit => :exit_closed,
|
||||
:after_exit => :after_exit_closed
|
||||
|
||||
state :failed
|
||||
|
||||
event :close, :before => :before, :after => :after, :guard => :event_guard do
|
||||
transitions :to => :closed, :from => [:open], :guard => :transition_guard, :after => :transitioning
|
||||
transitions :to => :failed, :from => [:open]
|
||||
end
|
||||
|
||||
event :open, :before => :before, :after => :after do
|
||||
transitions :to => :open, :from => :closed
|
||||
end
|
||||
end
|
||||
|
||||
def log(text)
|
||||
puts text if @log
|
||||
end
|
||||
|
||||
def before_enter_open; log('before_enter_open'); end
|
||||
def enter_open; log('enter_open'); end
|
||||
def before_exit_open; log('before_exit_open'); end
|
||||
def after_enter_open; log('after_enter_open'); end
|
||||
def exit_open; log('exit_open'); end
|
||||
def after_exit_open; log('after_exit_open'); end
|
||||
|
||||
def before_enter_closed; log('before_enter_closed'); end
|
||||
def enter_closed; log('enter_closed'); end
|
||||
def before_exit_closed; log('before_exit_closed'); end
|
||||
def exit_closed; log('exit_closed'); end
|
||||
def after_enter_closed; log('after_enter_closed'); end
|
||||
def after_exit_closed; log('after_exit_closed'); end
|
||||
|
||||
def event_guard; log('event_guard'); !@fail_event_guard; end
|
||||
def transition_guard; log('transition_guard'); !@fail_transition_guard; end
|
||||
def transitioning; log('transitioning'); end
|
||||
|
||||
def before; log('before'); end
|
||||
def after; log('after'); end
|
||||
end
|
||||
end
|
44
spec/models/callbacks/private_method_multiple.rb
Normal file
44
spec/models/callbacks/private_method_multiple.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
module Callbacks
|
||||
class PrivateMethodMultiple
|
||||
include AASM
|
||||
|
||||
def initialize(options={})
|
||||
@fail_event_guard = options[:fail_event_guard]
|
||||
@fail_transition_guard = options[:fail_transition_guard]
|
||||
@log = options[:log]
|
||||
reset_data
|
||||
end
|
||||
|
||||
def reset_data
|
||||
@data = []
|
||||
end
|
||||
|
||||
def data
|
||||
@data.join(' ')
|
||||
end
|
||||
|
||||
aasm(:left) do
|
||||
state :open, :initial => true
|
||||
state :closed
|
||||
|
||||
event :close, :after => :after_event do
|
||||
transitions :to => :closed, :from => [:open]
|
||||
end
|
||||
|
||||
event :open, :after => :after_event do
|
||||
transitions :to => :open, :from => :closed
|
||||
end
|
||||
end
|
||||
|
||||
def log(text)
|
||||
@data << text
|
||||
puts text if @log
|
||||
end
|
||||
|
||||
def aasm_write_state(*args); log('aasm_write_state'); true; end
|
||||
|
||||
private
|
||||
|
||||
def after_event; log('after_event'); end
|
||||
end
|
||||
end
|
61
spec/models/callbacks/with_args_multiple.rb
Normal file
61
spec/models/callbacks/with_args_multiple.rb
Normal file
|
@ -0,0 +1,61 @@
|
|||
module Callbacks
|
||||
class WithArgsMultiple
|
||||
include AASM
|
||||
|
||||
def initialize(options={})
|
||||
@log = options[:log]
|
||||
reset_data
|
||||
end
|
||||
|
||||
def reset_data
|
||||
@data = []
|
||||
end
|
||||
|
||||
def data
|
||||
@data.join(' ')
|
||||
end
|
||||
|
||||
aasm(:left) do
|
||||
state :open, :initial => true,
|
||||
:before_enter => :before_enter_open,
|
||||
:after_enter => :after_enter_open,
|
||||
:before_exit => :before_exit_open,
|
||||
:after_exit => :after_exit_open
|
||||
|
||||
state :closed,
|
||||
:before_enter => :before_enter_closed,
|
||||
:after_enter => :after_enter_closed,
|
||||
:before_exit => :before_exit_closed,
|
||||
:after_exit => :after_exit_closed
|
||||
|
||||
event :close, :before => :before, :after => :after do
|
||||
transitions :to => :closed, :from => [:open], :after => :transition_proc
|
||||
end
|
||||
|
||||
event :open, :before => :before, :after => :after do
|
||||
transitions :to => :open, :from => :closed
|
||||
end
|
||||
end
|
||||
|
||||
def log(text)
|
||||
@data << text
|
||||
puts text if @log
|
||||
end
|
||||
|
||||
def aasm_write_state(*args); log('aasm_write_state'); true; end
|
||||
|
||||
def before_enter_open(*args); log("before_enter_open(#{args.map(&:inspect).join(',')})"); end
|
||||
def before_exit_open(*args); log("before_exit_open(#{args.map(&:inspect).join(',')})"); end
|
||||
def after_enter_open(*args); log("after_enter_open(#{args.map(&:inspect).join(',')})"); end
|
||||
def after_exit_open(*args); log("after_exit_open(#{args.map(&:inspect).join(',')})"); end
|
||||
|
||||
def before_enter_closed(*args); log("before_enter_closed(#{args.map(&:inspect).join(',')})"); end
|
||||
def before_exit_closed(*args); log("before_enter_closed(#{args.map(&:inspect).join(',')})"); end
|
||||
def after_enter_closed(*args); log("after_enter_closed(#{args.map(&:inspect).join(',')})"); end
|
||||
def after_exit_closed(*args); log("after_exit_closed(#{args.map(&:inspect).join(',')})"); end
|
||||
|
||||
def before(arg1, *args); log("before(#{arg1.inspect},#{args.map(&:inspect).join(',')})"); end
|
||||
def transition_proc(arg1, arg2); log("transition_proc(#{arg1.inspect},#{arg2.inspect})"); end
|
||||
def after(*args); log("after(#{args.map(&:inspect).join(',')})"); end
|
||||
end
|
||||
end
|
26
spec/models/callbacks/with_state_arg_multiple.rb
Normal file
26
spec/models/callbacks/with_state_arg_multiple.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
module Callbacks
|
||||
class WithStateArgMultiple
|
||||
|
||||
include AASM
|
||||
|
||||
aasm(:left) do
|
||||
state :open, :inital => true
|
||||
state :closed
|
||||
state :out_to_lunch
|
||||
|
||||
event :close, :before => :before_method, :after => :after_method do
|
||||
transitions :to => :closed, :from => [:open], :after => :transition_method
|
||||
transitions :to => :out_to_lunch, :from => [:open], :after => :transition_method2
|
||||
end
|
||||
end
|
||||
|
||||
def before_method(arg); end
|
||||
|
||||
def after_method(arg); end
|
||||
|
||||
def transition_method(arg); end
|
||||
|
||||
def transition_method2(arg); end
|
||||
|
||||
end
|
||||
end
|
|
@ -86,3 +86,137 @@ class ComplexExample
|
|||
phrase == :please
|
||||
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
|
||||
|
|
|
@ -34,8 +34,55 @@ class Conversation
|
|||
@persister = persister
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
@persister.read_state
|
||||
end
|
||||
|
@ -43,5 +90,4 @@ class Conversation
|
|||
def aasm_write_state(state)
|
||||
@persister.write_state(state)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -33,3 +33,60 @@ class FooTwo < Foo
|
|||
state :foo
|
||||
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
|
||||
|
|
45
spec/models/foo_callback_multiple.rb
Normal file
45
spec/models/foo_callback_multiple.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
class FooCallbackMultiple
|
||||
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
|
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 rich?; self.balance >= RICH; 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
|
||||
|
|
|
@ -14,3 +14,18 @@ class InvalidPersistor < ActiveRecord::Base
|
|||
end
|
||||
validates_presence_of :name
|
||||
end
|
||||
|
||||
class MultipleInvalidPersistor < ActiveRecord::Base
|
||||
include AASM
|
||||
aasm :left, :column => :status, :skip_validation_on_save => true do
|
||||
state :sleeping, :initial => true
|
||||
state :running
|
||||
event :run do
|
||||
transitions :to => :running, :from => :sleeping
|
||||
end
|
||||
event :sleep do
|
||||
transitions :to => :sleeping, :from => :running
|
||||
end
|
||||
end
|
||||
validates_presence_of :name
|
||||
end
|
||||
|
|
37
spec/models/mongo_mapper/complex_mongo_mapper_example.rb
Normal file
37
spec/models/mongo_mapper/complex_mongo_mapper_example.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
class ComplexMongoMapperExample
|
||||
include MongoMapper::Document
|
||||
include AASM
|
||||
|
||||
key :left, String
|
||||
key :right, String
|
||||
|
||||
aasm :left, :column => 'left' do
|
||||
state :one, :initial => true
|
||||
state :two
|
||||
state :three
|
||||
|
||||
event :increment do
|
||||
transitions :from => :one, :to => :two
|
||||
transitions :from => :two, :to => :three
|
||||
end
|
||||
event :reset do
|
||||
transitions :from => :three, :to => :one
|
||||
end
|
||||
end
|
||||
|
||||
aasm :right, :column => 'right' do
|
||||
state :alpha, :initial => true
|
||||
state :beta
|
||||
state :gamma
|
||||
|
||||
event :level_up do
|
||||
transitions :from => :alpha, :to => :beta
|
||||
transitions :from => :beta, :to => :gamma
|
||||
end
|
||||
event :level_down do
|
||||
transitions :from => :gamma, :to => :beta
|
||||
transitions :from => :beta, :to => :alpha
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -8,3 +8,14 @@ class NoScopeMongoMapper
|
|||
state :ignored_scope
|
||||
end
|
||||
end
|
||||
|
||||
class NoScopeMongoMapperMultiple
|
||||
include MongoMapper::Document
|
||||
include AASM
|
||||
|
||||
key :status, String
|
||||
|
||||
aasm :left, :create_scopes => false, :column => :status do
|
||||
state :ignored_scope
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,3 +9,15 @@ class SimpleMongoMapper
|
|||
state :next
|
||||
end
|
||||
end
|
||||
|
||||
class SimpleMongoMapperMultiple
|
||||
include MongoMapper::Document
|
||||
include AASM
|
||||
|
||||
key :status, String
|
||||
|
||||
aasm :left, column: :status do
|
||||
state :unknown_scope
|
||||
state :next
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,3 +10,16 @@ class SimpleNewDslMongoMapper
|
|||
state :next
|
||||
end
|
||||
end
|
||||
|
||||
class SimpleNewDslMongoMapperMultiple
|
||||
include MongoMapper::Document
|
||||
include AASM
|
||||
|
||||
key :status, String
|
||||
|
||||
aasm :left, :column => :status
|
||||
aasm :left do
|
||||
state :unknown_scope
|
||||
state :next
|
||||
end
|
||||
end
|
||||
|
|
37
spec/models/mongoid/complex_mongoid_example.rb
Normal file
37
spec/models/mongoid/complex_mongoid_example.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
class ComplexMongoidExample
|
||||
include Mongoid::Document
|
||||
include AASM
|
||||
|
||||
field :left, :type => String
|
||||
field :right, :type => String
|
||||
|
||||
aasm :left, :column => 'left' do
|
||||
state :one, :initial => true
|
||||
state :two
|
||||
state :three
|
||||
|
||||
event :increment do
|
||||
transitions :from => :one, :to => :two
|
||||
transitions :from => :two, :to => :three
|
||||
end
|
||||
event :reset do
|
||||
transitions :from => :three, :to => :one
|
||||
end
|
||||
end
|
||||
|
||||
aasm :right, :column => 'right' do
|
||||
state :alpha, :initial => true
|
||||
state :beta
|
||||
state :gamma
|
||||
|
||||
event :level_up do
|
||||
transitions :from => :alpha, :to => :beta
|
||||
transitions :from => :beta, :to => :gamma
|
||||
end
|
||||
event :level_down do
|
||||
transitions :from => :gamma, :to => :beta
|
||||
transitions :from => :beta, :to => :alpha
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -8,3 +8,14 @@ class NoScopeMongoid
|
|||
state :ignored_scope
|
||||
end
|
||||
end
|
||||
|
||||
class NoScopeMongoidMultiple
|
||||
include Mongoid::Document
|
||||
include AASM
|
||||
|
||||
field :status, :type => String
|
||||
|
||||
aasm :left, :create_scopes => false, :column => :status do
|
||||
state :ignored_scope
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,3 +9,15 @@ class SimpleMongoid
|
|||
state :new
|
||||
end
|
||||
end
|
||||
|
||||
class SimpleMongoidMultiple
|
||||
include Mongoid::Document
|
||||
include AASM
|
||||
|
||||
field :status, :type => String
|
||||
|
||||
aasm :left, column: :status do
|
||||
state :unknown_scope
|
||||
state :new
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,3 +10,16 @@ class SimpleNewDslMongoid
|
|||
state :new
|
||||
end
|
||||
end
|
||||
|
||||
class SimpleNewDslMongoidMultiple
|
||||
include Mongoid::Document
|
||||
include AASM
|
||||
|
||||
field :status, :type => String
|
||||
|
||||
aasm :left, :column => :status
|
||||
aasm :left do
|
||||
state :unknown_scope, :initial => true
|
||||
state :new
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,3 +10,16 @@ class NoInitialState
|
|||
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
|
||||
|
|
|
@ -18,7 +18,7 @@ class ParametrisedEvent
|
|||
end
|
||||
end
|
||||
|
||||
def wear_clothes(shirt_color, trouser_type)
|
||||
def wear_clothes(shirt_color, trouser_type=nil)
|
||||
end
|
||||
|
||||
def condition_hair
|
||||
|
|
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
|
|
@ -10,15 +10,15 @@ class ProvidedState
|
|||
end
|
||||
end
|
||||
|
||||
def aasm_read_state
|
||||
def aasm_read_state(*args)
|
||||
:beta
|
||||
end
|
||||
|
||||
def aasm_write_state(new_state)
|
||||
def aasm_write_state(new_state, *args)
|
||||
@persisted_store = new_state
|
||||
end
|
||||
|
||||
def aasm_write_state_without_persistence(new_state)
|
||||
def aasm_write_state_without_persistence(new_state, *args)
|
||||
@transient_store = new_state
|
||||
end
|
||||
end
|
||||
|
|
45
spec/models/sequel/complex_sequel_example.rb
Normal file
45
spec/models/sequel/complex_sequel_example.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
db = Sequel.connect(SEQUEL_DB)
|
||||
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# db.loggers << Logger.new($stderr)
|
||||
db.create_table(:complex_sequel_examples) do
|
||||
primary_key :id
|
||||
String :left
|
||||
String :right
|
||||
end
|
||||
|
||||
class ComplexSequelExample < Sequel::Model(db)
|
||||
set_dataset(:complex_sequel_examples)
|
||||
|
||||
include AASM
|
||||
|
||||
aasm :left, :column => 'left' do
|
||||
state :one, :initial => true
|
||||
state :two
|
||||
state :three
|
||||
|
||||
event :increment do
|
||||
transitions :from => :one, :to => :two
|
||||
transitions :from => :two, :to => :three
|
||||
end
|
||||
event :reset do
|
||||
transitions :from => :three, :to => :one
|
||||
end
|
||||
end
|
||||
|
||||
aasm :right, :column => 'right' do
|
||||
state :alpha, :initial => true
|
||||
state :beta
|
||||
state :gamma
|
||||
|
||||
event :level_up do
|
||||
transitions :from => :alpha, :to => :beta
|
||||
transitions :from => :beta, :to => :gamma
|
||||
end
|
||||
event :level_down do
|
||||
transitions :from => :gamma, :to => :beta
|
||||
transitions :from => :beta, :to => :alpha
|
||||
end
|
||||
end
|
||||
|
||||
end
|
25
spec/models/sequel/sequel_multiple.rb
Normal file
25
spec/models/sequel/sequel_multiple.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
db = Sequel.connect(SEQUEL_DB)
|
||||
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# db.loggers << Logger.new($stderr)
|
||||
db.create_table(:multiples) do
|
||||
primary_key :id
|
||||
String :status
|
||||
end
|
||||
|
||||
class SequelMultiple < Sequel::Model(db)
|
||||
set_dataset(:multiples)
|
||||
include AASM
|
||||
|
||||
attr_accessor :default
|
||||
|
||||
aasm :left, :column => :status
|
||||
aasm :left do
|
||||
state :alpha, :initial => true
|
||||
state :beta
|
||||
state :gamma
|
||||
event :release do
|
||||
transitions :from => [:alpha, :beta, :gamma], :to => :beta
|
||||
end
|
||||
end
|
||||
end
|
25
spec/models/sequel/sequel_simple.rb
Normal file
25
spec/models/sequel/sequel_simple.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
db = Sequel.connect(SEQUEL_DB)
|
||||
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# db.loggers << Logger.new($stderr)
|
||||
db.create_table(:simples) do
|
||||
primary_key :id
|
||||
String :status
|
||||
end
|
||||
|
||||
class SequelSimple < Sequel::Model(db)
|
||||
set_dataset(:simples)
|
||||
include AASM
|
||||
|
||||
attr_accessor :default
|
||||
|
||||
aasm :column => :status
|
||||
aasm do
|
||||
state :alpha, :initial => true
|
||||
state :beta
|
||||
state :gamma
|
||||
event :release do
|
||||
transitions :from => [:alpha, :beta, :gamma], :to => :beta
|
||||
end
|
||||
end
|
||||
end
|
30
spec/models/simple_multiple_example.rb
Normal file
30
spec/models/simple_multiple_example.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
class SimpleMultipleExample
|
||||
include AASM
|
||||
aasm(:move) do
|
||||
state :standing, :initial => true
|
||||
state :walking
|
||||
state :running
|
||||
|
||||
event :walk do
|
||||
transitions :from => :standing, :to => :walking
|
||||
end
|
||||
event :run do
|
||||
transitions :from => [:standing, :walking], :to => :running
|
||||
end
|
||||
event :hold do
|
||||
transitions :from => [:walking, :running], :to => :standing
|
||||
end
|
||||
end
|
||||
|
||||
aasm(:work) do
|
||||
state :sleeping, :initial => true
|
||||
state :processing
|
||||
|
||||
event :start do
|
||||
transitions :from => :sleeping, :to => :processing
|
||||
end
|
||||
event :stop do
|
||||
transitions :from => :processing, :to => :sleeping
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,7 @@
|
|||
require_relative 'super_class'
|
||||
|
||||
class SubClass < SuperClass
|
||||
end
|
||||
|
||||
class SubClassMultiple < SuperClassMultiple
|
||||
end
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
require_relative 'super_class'
|
||||
|
||||
class SubClassWithMoreStates < SuperClass
|
||||
include AASM
|
||||
aasm do
|
||||
state :foo
|
||||
end
|
||||
end
|
||||
|
||||
class SubClassWithMoreStatesMultiple < SuperClassMultiple
|
||||
include AASM
|
||||
aasm(:left) do
|
||||
state :foo
|
||||
end
|
||||
aasm(:right) do
|
||||
state :archived
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,3 +16,31 @@ class SuperClass
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SuperClassMultiple
|
||||
include AASM
|
||||
|
||||
aasm(:left) do
|
||||
state :read
|
||||
state :ended
|
||||
|
||||
event :foo do
|
||||
transitions :to => :ended, :from => [:read]
|
||||
end
|
||||
end
|
||||
|
||||
aasm(:right) do
|
||||
state :opened
|
||||
state :closed
|
||||
|
||||
event :close do
|
||||
transitions :to => :closed, :from => [:opened]
|
||||
end
|
||||
end
|
||||
|
||||
def update_state
|
||||
if may_foo?
|
||||
foo!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'active_record'
|
||||
|
||||
class Transactor < ActiveRecord::Base
|
||||
|
||||
belongs_to :worker
|
||||
|
@ -24,3 +25,29 @@ private
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
class MultipleTransactor < ActiveRecord::Base
|
||||
|
||||
belongs_to :worker
|
||||
|
||||
include AASM
|
||||
aasm :left, :column => :status do
|
||||
state :sleeping, :initial => true
|
||||
state :running, :before_enter => :start_worker, :after_enter => :fail
|
||||
|
||||
event :run do
|
||||
transitions :to => :running, :from => :sleeping
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def start_worker
|
||||
worker.update_attribute(:status, 'running')
|
||||
end
|
||||
|
||||
def fail
|
||||
raise StandardError.new('failed on purpose')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -9,3 +9,15 @@ class ValidStateName
|
|||
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
|
||||
|
|
|
@ -38,3 +38,42 @@ class Validator < ActiveRecord::Base
|
|||
raise StandardError.new('failed on purpose')
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleValidator < ActiveRecord::Base
|
||||
|
||||
include AASM
|
||||
aasm :left, :column => :status do
|
||||
state :sleeping, :initial => true
|
||||
state :running
|
||||
state :failed, :after_enter => :fail
|
||||
|
||||
event :run, :after_commit => :change_name! do
|
||||
transitions :to => :running, :from => :sleeping
|
||||
end
|
||||
event :sleep do
|
||||
after_commit do |name|
|
||||
change_name_on_sleep name
|
||||
end
|
||||
transitions :to => :sleeping, :from => :running
|
||||
end
|
||||
event :fail do
|
||||
transitions :to => :failed, :from => [:sleeping, :running]
|
||||
end
|
||||
end
|
||||
|
||||
validates_presence_of :name
|
||||
|
||||
def change_name!
|
||||
self.name = "name changed"
|
||||
save!
|
||||
end
|
||||
|
||||
def change_name_on_sleep name
|
||||
self.name = name
|
||||
save!
|
||||
end
|
||||
|
||||
def fail
|
||||
raise StandardError.new('failed on purpose')
|
||||
end
|
||||
end
|
||||
|
|
10
spec/unit/basic_two_state_machines_example_spec.rb
Normal file
10
spec/unit/basic_two_state_machines_example_spec.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'on initialization' do
|
||||
let(:example) { BasicTwoStateMachinesExample.new }
|
||||
|
||||
it 'should be in the initial state' do
|
||||
expect(example.aasm(:search).current_state).to eql :initialised
|
||||
expect(example.aasm(:sync).current_state).to eql :unsynced
|
||||
end
|
||||
end
|
295
spec/unit/callback_multiple_spec.rb
Normal file
295
spec/unit/callback_multiple_spec.rb
Normal file
|
@ -0,0 +1,295 @@
|
|||
require 'spec_helper'
|
||||
Dir[File.dirname(__FILE__) + "/../models/callbacks/*.rb"].sort.each { |f| require File.expand_path(f) }
|
||||
|
||||
describe 'callbacks for the new DSL' do
|
||||
|
||||
it "be called in order" do
|
||||
show_debug_log = false
|
||||
|
||||
callback = Callbacks::BasicMultiple.new(:log => show_debug_log)
|
||||
callback.aasm(:left).current_state
|
||||
|
||||
unless show_debug_log
|
||||
expect(callback).to receive(:before_event).once.ordered
|
||||
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
|
||||
expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
|
||||
expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes
|
||||
expect(callback).to receive(:exit_open).once.ordered
|
||||
# expect(callback).to receive(:event_guard).once.ordered.and_return(true)
|
||||
# expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
|
||||
expect(callback).to receive(:after_transition).once.ordered
|
||||
expect(callback).to receive(:before_enter_closed).once.ordered
|
||||
expect(callback).to receive(:enter_closed).once.ordered
|
||||
expect(callback).to receive(:aasm_write_state).with(:closed, :left).once.ordered.and_return(true) # this is when the state changes
|
||||
expect(callback).to receive(:after_exit_open).once.ordered # these should be after the state changes
|
||||
expect(callback).to receive(:after_enter_closed).once.ordered
|
||||
expect(callback).to receive(:after_event).once.ordered
|
||||
end
|
||||
|
||||
# puts "------- close!"
|
||||
callback.left_close!
|
||||
end
|
||||
|
||||
it "does not run any state callback if the event guard fails" do
|
||||
callback = Callbacks::BasicMultiple.new(:log => false)
|
||||
callback.aasm(:left).current_state
|
||||
|
||||
expect(callback).to receive(:before_event).once.ordered
|
||||
expect(callback).to receive(:event_guard).once.ordered.and_return(false)
|
||||
expect(callback).to_not receive(:transition_guard)
|
||||
expect(callback).to_not receive(:before_exit_open)
|
||||
expect(callback).to_not receive(:exit_open)
|
||||
expect(callback).to_not receive(:after_transition)
|
||||
expect(callback).to_not receive(:before_enter_closed)
|
||||
expect(callback).to_not receive(:enter_closed)
|
||||
expect(callback).to_not receive(:aasm_write_state)
|
||||
expect(callback).to_not receive(:after_exit_open)
|
||||
expect(callback).to_not receive(:after_enter_closed)
|
||||
expect(callback).to_not receive(:after_event)
|
||||
|
||||
expect {
|
||||
callback.left_close!
|
||||
}.to raise_error(AASM::InvalidTransition)
|
||||
end
|
||||
|
||||
it "handles private callback methods as well" do
|
||||
show_debug_log = false
|
||||
|
||||
callback = Callbacks::PrivateMethodMultiple.new(:log => show_debug_log)
|
||||
callback.aasm(:left).current_state
|
||||
|
||||
# puts "------- close!"
|
||||
expect {
|
||||
callback.close!
|
||||
}.to_not raise_error
|
||||
end
|
||||
|
||||
context "if the transition guard fails" do
|
||||
it "does not run any state callback if guard is defined inline" do
|
||||
show_debug_log = false
|
||||
callback = Callbacks::BasicMultiple.new(:log => show_debug_log, :fail_transition_guard => true)
|
||||
callback.aasm(:left).current_state
|
||||
|
||||
unless show_debug_log
|
||||
expect(callback).to receive(:before_event).once.ordered
|
||||
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
|
||||
expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
|
||||
expect(callback).to_not receive(:before_exit_open)
|
||||
expect(callback).to_not receive(:exit_open)
|
||||
expect(callback).to_not receive(:after_transition)
|
||||
expect(callback).to_not receive(:before_enter_closed)
|
||||
expect(callback).to_not receive(:enter_closed)
|
||||
expect(callback).to_not receive(:aasm_write_state)
|
||||
expect(callback).to_not receive(:after_exit_open)
|
||||
expect(callback).to_not receive(:after_enter_closed)
|
||||
expect(callback).to_not receive(:after_event)
|
||||
end
|
||||
|
||||
expect {
|
||||
callback.left_close!
|
||||
}.to raise_error(AASM::InvalidTransition)
|
||||
end
|
||||
|
||||
it "does not run transition_guard twice for multiple permitted transitions" do
|
||||
show_debug_log = false
|
||||
callback = Callbacks::MultipleTransitionsTransitionGuardMultiple.new(:log => show_debug_log, :fail_transition_guard => true)
|
||||
callback.aasm(:left).current_state
|
||||
|
||||
unless show_debug_log
|
||||
expect(callback).to receive(:before).once.ordered
|
||||
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
|
||||
expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
|
||||
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
|
||||
expect(callback).to receive(:before_exit_open).once.ordered
|
||||
expect(callback).to receive(:exit_open).once.ordered
|
||||
expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
|
||||
expect(callback).to receive(:after_exit_open).once.ordered
|
||||
expect(callback).to receive(:after).once.ordered
|
||||
|
||||
expect(callback).to_not receive(:transitioning)
|
||||
expect(callback).to_not receive(:before_enter_closed)
|
||||
expect(callback).to_not receive(:enter_closed)
|
||||
expect(callback).to_not receive(:after_enter_closed)
|
||||
end
|
||||
|
||||
callback.close!
|
||||
expect(callback.aasm(:left).current_state).to eql :failed
|
||||
end
|
||||
|
||||
it "does not run any state callback if guard is defined with block" do
|
||||
callback = Callbacks::GuardWithinBlockMultiple.new #(:log => true, :fail_transition_guard => true)
|
||||
callback.aasm(:left).current_state
|
||||
|
||||
expect(callback).to receive(:before).once.ordered
|
||||
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
|
||||
expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
|
||||
expect(callback).to_not receive(:before_exit_open)
|
||||
expect(callback).to_not receive(:exit_open)
|
||||
expect(callback).to_not receive(:transitioning)
|
||||
expect(callback).to_not receive(:before_enter_closed)
|
||||
expect(callback).to_not receive(:enter_closed)
|
||||
expect(callback).to_not receive(:aasm_write_state)
|
||||
expect(callback).to_not receive(:after_exit_open)
|
||||
expect(callback).to_not receive(:after_enter_closed)
|
||||
expect(callback).to_not receive(:after)
|
||||
|
||||
expect {
|
||||
callback.close!
|
||||
}.to raise_error(AASM::InvalidTransition)
|
||||
end
|
||||
end
|
||||
|
||||
it "should properly pass arguments" do
|
||||
cb = Callbacks::WithArgsMultiple.new(:log => false)
|
||||
cb.aasm(:left).current_state
|
||||
|
||||
cb.reset_data
|
||||
cb.close!(:arg1, :arg2)
|
||||
expect(cb.data).to eql 'before(:arg1,:arg2) before_exit_open(:arg1,:arg2) transition_proc(:arg1,:arg2) before_enter_closed(:arg1,:arg2) aasm_write_state after_exit_open(:arg1,:arg2) after_enter_closed(:arg1,:arg2) after(:arg1,:arg2)'
|
||||
end
|
||||
|
||||
it "should call the callbacks given the to-state as argument" do
|
||||
cb = Callbacks::WithStateArgMultiple.new
|
||||
expect(cb).to receive(:before_method).with(:arg1).once.ordered
|
||||
expect(cb).to receive(:transition_method).never
|
||||
expect(cb).to receive(:transition_method2).with(:arg1).once.ordered
|
||||
expect(cb).to receive(:after_method).with(:arg1).once.ordered
|
||||
cb.close!(:out_to_lunch, :arg1)
|
||||
|
||||
cb = Callbacks::WithStateArgMultiple.new
|
||||
some_object = double('some object')
|
||||
expect(cb).to receive(:before_method).with(some_object).once.ordered
|
||||
expect(cb).to receive(:transition_method2).with(some_object).once.ordered
|
||||
expect(cb).to receive(:after_method).with(some_object).once.ordered
|
||||
cb.close!(:out_to_lunch, some_object)
|
||||
end
|
||||
|
||||
it "should call the proper methods just with arguments" do
|
||||
cb = Callbacks::WithStateArgMultiple.new
|
||||
expect(cb).to receive(:before_method).with(:arg1).once.ordered
|
||||
expect(cb).to receive(:transition_method).with(:arg1).once.ordered
|
||||
expect(cb).to receive(:transition_method).never
|
||||
expect(cb).to receive(:after_method).with(:arg1).once.ordered
|
||||
cb.close!(:arg1)
|
||||
|
||||
cb = Callbacks::WithStateArgMultiple.new
|
||||
some_object = double('some object')
|
||||
expect(cb).to receive(:before_method).with(some_object).once.ordered
|
||||
expect(cb).to receive(:transition_method).with(some_object).once.ordered
|
||||
expect(cb).to receive(:transition_method).never
|
||||
expect(cb).to receive(:after_method).with(some_object).once.ordered
|
||||
cb.close!(some_object)
|
||||
end
|
||||
end # callbacks for the new DSL
|
||||
|
||||
describe 'event callbacks' do
|
||||
describe "with an error callback defined" do
|
||||
before do
|
||||
class FooCallbackMultiple
|
||||
# this hack is needed to allow testing of parameters, since RSpec
|
||||
# destroys a method's arity when mocked
|
||||
attr_accessor :data
|
||||
|
||||
aasm(:left) do
|
||||
event :safe_close, :success => :success_callback, :error => :error_callback do
|
||||
transitions :to => :closed, :from => [:open]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@foo = FooCallbackMultiple.new
|
||||
end
|
||||
|
||||
context "error_callback defined" do
|
||||
it "should run error_callback if an exception is raised" do
|
||||
def @foo.error_callback(e)
|
||||
@data = [e]
|
||||
end
|
||||
|
||||
allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
|
||||
|
||||
@foo.safe_close!
|
||||
expect(@foo.data).to eql [e]
|
||||
end
|
||||
|
||||
it "should run error_callback without parameters if callback does not support any" do
|
||||
def @foo.error_callback(e)
|
||||
@data = []
|
||||
end
|
||||
|
||||
allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
|
||||
|
||||
@foo.safe_close!('arg1', 'arg2')
|
||||
expect(@foo.data).to eql []
|
||||
end
|
||||
|
||||
it "should run error_callback with parameters if callback supports them" do
|
||||
def @foo.error_callback(e, arg1, arg2)
|
||||
@data = [arg1, arg2]
|
||||
end
|
||||
|
||||
allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
|
||||
|
||||
@foo.safe_close!('arg1', 'arg2')
|
||||
expect(@foo.data).to eql ['arg1', 'arg2']
|
||||
end
|
||||
end
|
||||
|
||||
it "should raise NoMethodError if exception is raised and error_callback is declared but not defined" do
|
||||
allow(@foo).to receive(:before_enter).and_raise(StandardError)
|
||||
expect{@foo.safe_close!}.to raise_error(NoMethodError)
|
||||
end
|
||||
|
||||
it "should propagate an error if no error callback is declared" do
|
||||
allow(@foo).to receive(:before_enter).and_raise("Cannot enter safe")
|
||||
expect{@foo.close!}.to raise_error(StandardError, "Cannot enter safe")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with aasm_event_fired defined" do
|
||||
before do
|
||||
@foo = FooMultiple.new
|
||||
def @foo.aasm_event_fired(event, from, to); end
|
||||
end
|
||||
|
||||
it 'should call it for successful bang fire' do
|
||||
expect(@foo).to receive(:aasm_event_fired).with(:close, :open, :closed)
|
||||
@foo.close!
|
||||
end
|
||||
|
||||
it 'should call it for successful non-bang fire' do
|
||||
expect(@foo).to receive(:aasm_event_fired)
|
||||
@foo.close
|
||||
end
|
||||
|
||||
it 'should not call it for failing bang fire' do
|
||||
allow(@foo.aasm(:left)).to receive(:set_current_state_with_persistence).and_return(false)
|
||||
expect(@foo).not_to receive(:aasm_event_fired)
|
||||
@foo.close!
|
||||
end
|
||||
end
|
||||
|
||||
describe "with aasm_event_failed defined" do
|
||||
before do
|
||||
@foo = FooMultiple.new
|
||||
def @foo.aasm_event_failed(event, from); end
|
||||
end
|
||||
|
||||
it 'should call it when transition failed for bang fire' do
|
||||
expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
|
||||
expect {@foo.null!}.to raise_error(AASM::InvalidTransition)
|
||||
end
|
||||
|
||||
it 'should call it when transition failed for non-bang fire' do
|
||||
expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
|
||||
expect {@foo.null}.to raise_error(AASM::InvalidTransition)
|
||||
end
|
||||
|
||||
it 'should not call it if persist fails for bang fire' do
|
||||
allow(@foo.aasm(:left)).to receive(:set_current_state_with_persistence).and_return(false)
|
||||
expect(@foo).to receive(:aasm_event_failed)
|
||||
@foo.close!
|
||||
end
|
||||
end
|
||||
|
||||
end # event callbacks
|
|
@ -52,7 +52,7 @@ describe 'callbacks for the new DSL' do
|
|||
}.to raise_error(AASM::InvalidTransition)
|
||||
end
|
||||
|
||||
it "it handles private callback methods as well" do
|
||||
it "handles private callback methods as well" do
|
||||
show_debug_log = false
|
||||
|
||||
callback = Callbacks::PrivateMethod.new(:log => show_debug_log)
|
||||
|
|
99
spec/unit/complex_multiple_example_spec.rb
Normal file
99
spec/unit/complex_multiple_example_spec.rb
Normal file
|
@ -0,0 +1,99 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'on initialization' do
|
||||
let(:auth) {ComplexExampleMultiple.new}
|
||||
|
||||
it 'should be in the initial state' do
|
||||
expect(auth.aasm(:left).current_state).to eq(:pending)
|
||||
expect(auth.aasm(:right).current_state).to eq(:pending)
|
||||
end
|
||||
|
||||
it 'should have an activation code' do
|
||||
expect(auth.has_activation_code?).to be_truthy
|
||||
expect(auth.activation_code).to eq '12'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when being unsuspended' do
|
||||
let(:auth) {ComplexExampleMultiple.new}
|
||||
|
||||
it 'should be able to unsuspend' do
|
||||
auth.left_activate!
|
||||
auth.left_suspend!
|
||||
expect(auth.may_left_unsuspend?).to be true
|
||||
|
||||
auth.right_activate!
|
||||
auth.right_suspend!
|
||||
expect(auth.may_right_unsuspend?).to be true
|
||||
end
|
||||
|
||||
it 'should not be able to unsuspend into active' do
|
||||
auth.left_suspend!
|
||||
expect(auth.may_left_unsuspend?(:active)).not_to be true
|
||||
|
||||
auth.right_activate!
|
||||
auth.right_suspend!
|
||||
expect(auth.may_right_unsuspend?(:active)).to be true
|
||||
end
|
||||
|
||||
it 'should be able to wait into waiting if polite' do
|
||||
auth.left_suspend!
|
||||
expect(auth.may_left_wait?(:waiting, :please)).to be true
|
||||
auth.left_wait!(nil, :please)
|
||||
|
||||
auth.right_suspend!
|
||||
expect(auth.may_right_wait?(:waiting)).to be false
|
||||
auth.right_wait!(nil, :please)
|
||||
end
|
||||
|
||||
it 'should not be able to be unsuspended into active if not polite' do
|
||||
auth.left_suspend!
|
||||
expect(auth.may_left_wait?(:waiting)).not_to be true
|
||||
expect(auth.may_left_wait?(:waiting, :rude)).not_to be true
|
||||
expect {auth.left_wait!(nil, :rude)}.to raise_error(AASM::InvalidTransition)
|
||||
expect {auth.left_wait!}.to raise_error(AASM::InvalidTransition)
|
||||
end
|
||||
|
||||
it 'should not be able to be unpassified' do
|
||||
auth.left_activate!
|
||||
auth.left_suspend!
|
||||
auth.left_unsuspend!
|
||||
|
||||
expect(auth.may_left_unpassify?).not_to be true
|
||||
expect {auth.left_unpassify!}.to raise_error(AASM::InvalidTransition)
|
||||
end
|
||||
|
||||
it 'should be active if previously activated' do
|
||||
auth.left_activate!
|
||||
auth.left_suspend!
|
||||
auth.left_unsuspend!
|
||||
|
||||
expect(auth.aasm(:left).current_state).to eq(:active)
|
||||
end
|
||||
|
||||
it 'should be pending if not previously activated, but an activation code is present' do
|
||||
auth.left_suspend!
|
||||
auth.left_unsuspend!
|
||||
|
||||
expect(auth.aasm(:left).current_state).to eq(:pending)
|
||||
end
|
||||
|
||||
it 'should be passive if not previously activated and there is no activation code' do
|
||||
auth.activation_code = nil
|
||||
auth.left_suspend!
|
||||
auth.left_unsuspend!
|
||||
|
||||
expect(auth.aasm(:left).current_state).to eq(:passive)
|
||||
end
|
||||
|
||||
it "should be able to fire known events" do
|
||||
expect(auth.aasm(:left).may_fire_event?(:left_activate)).to be true
|
||||
expect(auth.aasm(:right).may_fire_event?(:right_activate)).to be true
|
||||
end
|
||||
|
||||
it "should not be able to fire unknown events" do
|
||||
expect(auth.aasm(:left).may_fire_event?(:unknown)).to be false
|
||||
expect(auth.aasm(:right).may_fire_event?(:unknown)).to be false
|
||||
end
|
||||
|
||||
end
|
16
spec/unit/edge_cases_spec.rb
Normal file
16
spec/unit/edge_cases_spec.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "edge cases" do
|
||||
describe "for classes with multiple state machines" do
|
||||
it "allows accessing a multiple state machine class without state machine name" do
|
||||
# it's like starting to define a new state machine within the
|
||||
# requested class
|
||||
expect(SimpleMultipleExample.aasm.states.map(&:name)).to be_empty
|
||||
end
|
||||
|
||||
it "do not know yet" do
|
||||
example = ComplexExampleMultiple.new
|
||||
expect { example.aasm.states.inspect }.to raise_error(AASM::UnknownStateMachineError)
|
||||
end
|
||||
end
|
||||
end
|
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
|
|
@ -1,8 +1,9 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'adding an event' do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
let(:event) do
|
||||
AASM::Core::Event.new(:close_order, {:success => :success_callback}) do
|
||||
AASM::Core::Event.new(:close_order, state_machine, {:success => :success_callback}) do
|
||||
before :before_callback
|
||||
after :after_callback
|
||||
transitions :to => :closed, :from => [:open, :received]
|
||||
|
@ -35,8 +36,9 @@ describe 'adding an event' do
|
|||
end
|
||||
|
||||
describe 'transition inspection' do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
let(:event) do
|
||||
AASM::Core::Event.new(:run) do
|
||||
AASM::Core::Event.new(:run, state_machine) do
|
||||
transitions :to => :running, :from => :sleeping
|
||||
end
|
||||
end
|
||||
|
@ -59,8 +61,9 @@ describe 'transition inspection' do
|
|||
end
|
||||
|
||||
describe 'transition inspection without from' do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
let(:event) do
|
||||
AASM::Core::Event.new(:run) do
|
||||
AASM::Core::Event.new(:run, state_machine) do
|
||||
transitions :to => :running
|
||||
end
|
||||
end
|
||||
|
@ -76,15 +79,17 @@ describe 'transition inspection without from' do
|
|||
end
|
||||
|
||||
describe 'firing an event' do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
|
||||
it 'should return nil if the transitions are empty' do
|
||||
obj = double('object', :aasm => double('aasm', :current_state => 'open'))
|
||||
|
||||
event = AASM::Core::Event.new(:event)
|
||||
event = AASM::Core::Event.new(:event, state_machine)
|
||||
expect(event.fire(obj)).to be_nil
|
||||
end
|
||||
|
||||
it 'should return the state of the first matching transition it finds' do
|
||||
event = AASM::Core::Event.new(:event) do
|
||||
event = AASM::Core::Event.new(:event, state_machine) do
|
||||
transitions :to => :closed, :from => [:open, :received]
|
||||
end
|
||||
|
||||
|
@ -94,7 +99,7 @@ describe 'firing an event' do
|
|||
end
|
||||
|
||||
it 'should call the guard with the params passed in' do
|
||||
event = AASM::Core::Event.new(:event) do
|
||||
event = AASM::Core::Event.new(:event, state_machine) do
|
||||
transitions :to => :closed, :from => [:open, :received], :guard => :guard_fn
|
||||
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
|
201
spec/unit/inspection_multiple_spec.rb
Normal file
201
spec/unit/inspection_multiple_spec.rb
Normal file
|
@ -0,0 +1,201 @@
|
|||
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)
|
||||
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
|
560
spec/unit/persistence/active_record_persistence_multiple_spec.rb
Normal file
560
spec/unit/persistence/active_record_persistence_multiple_spec.rb
Normal file
|
@ -0,0 +1,560 @@
|
|||
require 'active_record'
|
||||
require 'spec_helper'
|
||||
Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each do |f|
|
||||
require File.expand_path(f)
|
||||
end
|
||||
|
||||
load_schema
|
||||
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# require 'logger'
|
||||
# ActiveRecord::Base.logger = Logger.new(STDERR)
|
||||
|
||||
describe "instance methods" do
|
||||
let(:gate) {MultipleGate.new}
|
||||
|
||||
it "should respond to aasm persistence methods" do
|
||||
expect(gate).to respond_to(:aasm_read_state)
|
||||
expect(gate).to respond_to(:aasm_write_state)
|
||||
expect(gate).to respond_to(:aasm_write_state_without_persistence)
|
||||
end
|
||||
|
||||
describe "aasm_column_looks_like_enum" do
|
||||
subject { lambda{ gate.send(:aasm_column_looks_like_enum, :left) } }
|
||||
|
||||
let(:column_name) { "value" }
|
||||
let(:columns_hash) { Hash[column_name, column] }
|
||||
|
||||
before :each do
|
||||
allow(gate.class.aasm(:left)).to receive(:attribute_name).and_return(column_name.to_sym)
|
||||
allow(gate.class).to receive(:columns_hash).and_return(columns_hash)
|
||||
end
|
||||
|
||||
context "when AASM column has integer type" do
|
||||
let(:column) { double(Object, type: :integer) }
|
||||
|
||||
it "returns true" do
|
||||
expect(subject.call).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM column has string type" do
|
||||
let(:column) { double(Object, type: :string) }
|
||||
|
||||
it "returns false" do
|
||||
expect(subject.call).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_guess_enum_method" do
|
||||
subject { lambda{ gate.send(:aasm_guess_enum_method, :left) } }
|
||||
|
||||
before :each do
|
||||
allow(gate.class.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
||||
end
|
||||
|
||||
it "pluralizes AASM column name" do
|
||||
expect(subject.call).to eq :values
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_enum" do
|
||||
context "when AASM enum setting contains an explicit enum method name" do
|
||||
let(:with_enum) { MultipleWithEnum.new }
|
||||
|
||||
it "returns whatever value was set in AASM config" do
|
||||
expect(with_enum.send(:aasm_enum, :left)).to eq :test
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM enum setting is simply set to true" do
|
||||
let(:with_true_enum) { MultipleWithTrueEnum.new }
|
||||
before :each do
|
||||
allow(MultipleWithTrueEnum.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
||||
end
|
||||
|
||||
it "infers enum method name from pluralized column name" do
|
||||
expect(with_true_enum.send(:aasm_enum, :left)).to eq :values
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM enum setting is explicitly disabled" do
|
||||
let(:with_false_enum) { MultipleWithFalseEnum.new }
|
||||
|
||||
it "returns nil" do
|
||||
expect(with_false_enum.send(:aasm_enum, :left)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM enum setting is not enabled" do
|
||||
before :each do
|
||||
allow(MultipleGate.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
||||
end
|
||||
|
||||
context "when AASM column looks like enum" do
|
||||
before :each do
|
||||
allow(gate).to receive(:aasm_column_looks_like_enum).with(:left).and_return(true)
|
||||
end
|
||||
|
||||
it "infers enum method name from pluralized column name" do
|
||||
expect(gate.send(:aasm_enum, :left)).to eq :values
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM column doesn't look like enum'" do
|
||||
before :each do
|
||||
allow(gate).to receive(:aasm_column_looks_like_enum)
|
||||
.and_return(false)
|
||||
end
|
||||
|
||||
it "returns nil, as we're not using enum" do
|
||||
expect(gate.send(:aasm_enum, :left)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM is configured to use enum" do
|
||||
let(:state_sym) { :running }
|
||||
let(:state_code) { 2 }
|
||||
let(:enum_name) { :states }
|
||||
let(:enum) { Hash[state_sym, state_code] }
|
||||
|
||||
before :each do
|
||||
allow(gate).to receive(:aasm_enum).and_return(enum_name)
|
||||
allow(gate).to receive(:aasm_write_attribute)
|
||||
allow(gate).to receive(:write_attribute)
|
||||
|
||||
allow(MultipleGate).to receive(enum_name).and_return(enum)
|
||||
end
|
||||
|
||||
describe "aasm_write_state" do
|
||||
context "when AASM is configured to skip validations on save" do
|
||||
before :each do
|
||||
allow(gate).to receive(:aasm_skipping_validations).and_return(true)
|
||||
end
|
||||
|
||||
it "passes state code instead of state symbol to update_all" do
|
||||
# stub_chain does not allow us to give expectations on call
|
||||
# parameters in the middle of the chain, so we need to use
|
||||
# intermediate object instead.
|
||||
obj = double(Object, update_all: 1)
|
||||
allow(MultipleGate).to receive(:where).and_return(obj)
|
||||
|
||||
gate.aasm_write_state state_sym, :left
|
||||
|
||||
expect(obj).to have_received(:update_all)
|
||||
.with(Hash[gate.class.aasm(:left).attribute_name, state_code])
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM is not skipping validations" do
|
||||
it "delegates state update to the helper method" do
|
||||
# Let's pretend that validation is passed
|
||||
allow(gate).to receive(:save).and_return(true)
|
||||
|
||||
gate.aasm_write_state state_sym, :left
|
||||
|
||||
expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :left)
|
||||
expect(gate).to_not have_received :write_attribute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_write_state_without_persistence" do
|
||||
it "delegates state update to the helper method" do
|
||||
gate.aasm_write_state_without_persistence state_sym, :left
|
||||
|
||||
expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :left)
|
||||
expect(gate).to_not have_received :write_attribute
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_raw_attribute_value" do
|
||||
it "converts state symbol to state code" do
|
||||
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
||||
.to eq state_code
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when AASM is configured to use string field" do
|
||||
let(:state_sym) { :running }
|
||||
|
||||
before :each do
|
||||
allow(gate).to receive(:aasm_enum).and_return(nil)
|
||||
end
|
||||
|
||||
describe "aasm_raw_attribute_value" do
|
||||
it "converts state symbol to string" do
|
||||
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
||||
.to eq state_sym.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "aasm_write_attribute helper method" do
|
||||
let(:sym) { :sym }
|
||||
let(:value) { 42 }
|
||||
|
||||
before :each do
|
||||
allow(gate).to receive(:write_attribute)
|
||||
allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
|
||||
|
||||
gate.send(:aasm_write_attribute, sym, :left)
|
||||
end
|
||||
|
||||
it "generates attribute value using a helper method" do
|
||||
expect(gate).to have_received(:aasm_raw_attribute_value).with(sym, :left)
|
||||
end
|
||||
|
||||
it "writes attribute to the model" do
|
||||
expect(gate).to have_received(:write_attribute).with(:aasm_state, value)
|
||||
end
|
||||
end
|
||||
|
||||
it "should return the initial state when new and the aasm field is nil" do
|
||||
expect(gate.aasm(:left).current_state).to eq(:opened)
|
||||
end
|
||||
|
||||
it "should return the aasm column when new and the aasm field is not nil" do
|
||||
gate.aasm_state = "closed"
|
||||
expect(gate.aasm(:left).current_state).to eq(:closed)
|
||||
end
|
||||
|
||||
it "should return the aasm column when not new and the aasm.attribute_name is not nil" do
|
||||
allow(gate).to receive(:new_record?).and_return(false)
|
||||
gate.aasm_state = "state"
|
||||
expect(gate.aasm(:left).current_state).to eq(:state)
|
||||
end
|
||||
|
||||
it "should allow a nil state" do
|
||||
allow(gate).to receive(:new_record?).and_return(false)
|
||||
gate.aasm_state = nil
|
||||
expect(gate.aasm(:left).current_state).to be_nil
|
||||
end
|
||||
|
||||
context 'on initialization' do
|
||||
it "should initialize the aasm state" do
|
||||
expect(MultipleGate.new.aasm_state).to eql 'opened'
|
||||
expect(MultipleGate.new.aasm(:left).current_state).to eql :opened
|
||||
end
|
||||
|
||||
it "should not initialize the aasm state if it has not been loaded" do
|
||||
# we have to create a gate in the database, for which we only want to
|
||||
# load the id, and not the state
|
||||
gate = MultipleGate.create!
|
||||
|
||||
# then we just load the gate ids
|
||||
MultipleGate.select(:id).where(id: gate.id).first
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if ActiveRecord::VERSION::MAJOR < 4 && ActiveRecord::VERSION::MINOR < 2 # won't work with Rails >= 4.2
|
||||
describe "direct state column access" do
|
||||
it "accepts false states" do
|
||||
f = MultipleFalseState.create!
|
||||
expect(f.aasm_state).to eql false
|
||||
expect {
|
||||
f.aasm(:left).events.map(&:name)
|
||||
}.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'subclasses' do
|
||||
it "should have the same states as its parent class" do
|
||||
expect(MultipleDerivateNewDsl.aasm(:left).states).to eq(MultipleSimpleNewDsl.aasm(:left).states)
|
||||
end
|
||||
|
||||
it "should have the same events as its parent class" do
|
||||
expect(MultipleDerivateNewDsl.aasm(:left).events).to eq(MultipleSimpleNewDsl.aasm(:left).events)
|
||||
end
|
||||
|
||||
it "should have the same column as its parent even for the new dsl" do
|
||||
expect(MultipleSimpleNewDsl.aasm(:left).attribute_name).to eq(:status)
|
||||
expect(MultipleDerivateNewDsl.aasm(:left).attribute_name).to eq(:status)
|
||||
end
|
||||
end
|
||||
|
||||
describe "named scopes with the new DSL" do
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(MultipleSimpleNewDsl).to respond_to(:unknown_scope)
|
||||
expect(MultipleSimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(MultipleSimpleNewDsl).to respond_to(:new)
|
||||
expect(MultipleSimpleNewDsl.new.class).to eq(MultipleSimpleNewDsl)
|
||||
end
|
||||
end
|
||||
|
||||
it "does not create scopes if requested" do
|
||||
expect(MultipleNoScope).not_to respond_to(:pending)
|
||||
end
|
||||
end # scopes
|
||||
|
||||
describe "direct assignment" do
|
||||
it "is allowed by default" do
|
||||
obj = MultipleNoScope.create
|
||||
expect(obj.aasm_state.to_sym).to eql :pending
|
||||
|
||||
obj.aasm_state = :running
|
||||
expect(obj.aasm_state.to_sym).to eql :running
|
||||
end
|
||||
|
||||
it "is forbidden if configured" do
|
||||
obj = MultipleNoDirectAssignment.create
|
||||
expect(obj.aasm_state.to_sym).to eql :pending
|
||||
|
||||
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
||||
expect(obj.aasm_state.to_sym).to eql :pending
|
||||
end
|
||||
|
||||
it 'can be turned off and on again' do
|
||||
obj = MultipleNoDirectAssignment.create
|
||||
expect(obj.aasm_state.to_sym).to eql :pending
|
||||
|
||||
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
||||
expect(obj.aasm_state.to_sym).to eql :pending
|
||||
|
||||
# allow it temporarily
|
||||
MultipleNoDirectAssignment.aasm(:left).state_machine.config.no_direct_assignment = false
|
||||
obj.aasm_state = :pending
|
||||
expect(obj.aasm_state.to_sym).to eql :pending
|
||||
|
||||
# and forbid it again
|
||||
MultipleNoDirectAssignment.aasm(:left).state_machine.config.no_direct_assignment = true
|
||||
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
||||
expect(obj.aasm_state.to_sym).to eql :pending
|
||||
end
|
||||
end # direct assignment
|
||||
|
||||
describe 'initial states' do
|
||||
it 'should support conditions' do
|
||||
expect(MultipleThief.new(:skilled => true).aasm(:left).current_state).to eq(:rich)
|
||||
expect(MultipleThief.new(:skilled => false).aasm(:left).current_state).to eq(:jailed)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'transitions with persistence' do
|
||||
|
||||
it "should work for valid models" do
|
||||
valid_object = MultipleValidator.create(:name => 'name')
|
||||
expect(valid_object).to be_sleeping
|
||||
valid_object.status = :running
|
||||
expect(valid_object).to be_running
|
||||
end
|
||||
|
||||
it 'should not store states for invalid models' do
|
||||
validator = MultipleValidator.create(:name => 'name')
|
||||
expect(validator).to be_valid
|
||||
expect(validator).to be_sleeping
|
||||
|
||||
validator.name = nil
|
||||
expect(validator).not_to be_valid
|
||||
expect(validator.run!).to be_falsey
|
||||
expect(validator).to be_sleeping
|
||||
|
||||
validator.reload
|
||||
expect(validator).not_to be_running
|
||||
expect(validator).to be_sleeping
|
||||
|
||||
validator.name = 'another name'
|
||||
expect(validator).to be_valid
|
||||
expect(validator.run!).to be_truthy
|
||||
expect(validator).to be_running
|
||||
|
||||
validator.reload
|
||||
expect(validator).to be_running
|
||||
expect(validator).not_to be_sleeping
|
||||
end
|
||||
|
||||
it 'should store states for invalid models if configured' do
|
||||
persistor = MultipleInvalidPersistor.create(:name => 'name')
|
||||
expect(persistor).to be_valid
|
||||
expect(persistor).to be_sleeping
|
||||
|
||||
persistor.name = nil
|
||||
expect(persistor).not_to be_valid
|
||||
expect(persistor.run!).to be_truthy
|
||||
expect(persistor).to be_running
|
||||
|
||||
persistor = MultipleInvalidPersistor.find(persistor.id)
|
||||
persistor.valid?
|
||||
expect(persistor).to be_valid
|
||||
expect(persistor).to be_running
|
||||
expect(persistor).not_to be_sleeping
|
||||
|
||||
persistor.reload
|
||||
expect(persistor).to be_running
|
||||
expect(persistor).not_to be_sleeping
|
||||
end
|
||||
|
||||
describe 'transactions' do
|
||||
let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
|
||||
let(:transactor) { MultipleTransactor.create!(:name => 'transactor', :worker => worker) }
|
||||
|
||||
it 'should rollback all changes' do
|
||||
expect(transactor).to be_sleeping
|
||||
expect(worker.status).to eq('sleeping')
|
||||
|
||||
expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
|
||||
expect(transactor).to be_running
|
||||
expect(worker.reload.status).to eq('sleeping')
|
||||
end
|
||||
|
||||
context "nested transactions" do
|
||||
it "should rollback all changes in nested transaction" do
|
||||
expect(transactor).to be_sleeping
|
||||
expect(worker.status).to eq('sleeping')
|
||||
|
||||
Worker.transaction do
|
||||
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
||||
end
|
||||
|
||||
expect(transactor).to be_running
|
||||
expect(worker.reload.status).to eq('sleeping')
|
||||
end
|
||||
|
||||
it "should only rollback changes in the main transaction not the nested one" do
|
||||
# change configuration to not require new transaction
|
||||
AASM::StateMachine[MultipleTransactor][:left].config.requires_new_transaction = false
|
||||
|
||||
expect(transactor).to be_sleeping
|
||||
expect(worker.status).to eq('sleeping')
|
||||
|
||||
Worker.transaction do
|
||||
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
||||
end
|
||||
|
||||
expect(transactor).to be_running
|
||||
expect(worker.reload.status).to eq('running')
|
||||
end
|
||||
end
|
||||
|
||||
describe "after_commit callback" do
|
||||
it "should fire :after_commit if transaction was successful" do
|
||||
validator = MultipleValidator.create(:name => 'name')
|
||||
expect(validator).to be_sleeping
|
||||
|
||||
validator.run!
|
||||
expect(validator).to be_running
|
||||
expect(validator.name).to eq("name changed")
|
||||
|
||||
validator.sleep!("sleeper")
|
||||
expect(validator).to be_sleeping
|
||||
expect(validator.name).to eq("sleeper")
|
||||
end
|
||||
|
||||
it "should not fire :after_commit if transaction failed" do
|
||||
validator = MultipleValidator.create(:name => 'name')
|
||||
expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
|
||||
expect(validator.name).to eq("name")
|
||||
end
|
||||
|
||||
it "should not fire if not saving" do
|
||||
validator = MultipleValidator.create(:name => 'name')
|
||||
expect(validator).to be_sleeping
|
||||
validator.run
|
||||
expect(validator).to be_running
|
||||
expect(validator.name).to eq("name")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "when not persisting" do
|
||||
it 'should not rollback all changes' do
|
||||
expect(transactor).to be_sleeping
|
||||
expect(worker.status).to eq('sleeping')
|
||||
|
||||
# Notice here we're calling "run" and not "run!" with a bang.
|
||||
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
||||
expect(transactor).to be_running
|
||||
expect(worker.reload.status).to eq('running')
|
||||
end
|
||||
|
||||
it 'should not create a database transaction' do
|
||||
expect(transactor.class).not_to receive(:transaction)
|
||||
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "invalid states with persistence" do
|
||||
it "should not store states" do
|
||||
validator = MultipleValidator.create(:name => 'name')
|
||||
validator.status = 'invalid_state'
|
||||
expect(validator.save).to be_falsey
|
||||
expect {validator.save!}.to raise_error(ActiveRecord::RecordInvalid)
|
||||
|
||||
validator.reload
|
||||
expect(validator).to be_sleeping
|
||||
end
|
||||
|
||||
it "should store invalid states if configured" do
|
||||
persistor = MultipleInvalidPersistor.create(:name => 'name')
|
||||
persistor.status = 'invalid_state'
|
||||
expect(persistor.save).to be_truthy
|
||||
|
||||
persistor.reload
|
||||
expect(persistor.status).to eq('invalid_state')
|
||||
end
|
||||
end
|
||||
|
||||
describe "complex example" do
|
||||
it "works" do
|
||||
record = ComplexActiveRecordExample.new
|
||||
expect_aasm_states record, :one, :alpha
|
||||
|
||||
record.save!
|
||||
expect_aasm_states record, :one, :alpha
|
||||
record.reload
|
||||
expect_aasm_states record, :one, :alpha
|
||||
|
||||
record.increment!
|
||||
expect_aasm_states record, :two, :alpha
|
||||
record.reload
|
||||
expect_aasm_states record, :two, :alpha
|
||||
|
||||
record.level_up!
|
||||
expect_aasm_states record, :two, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :two, :beta
|
||||
|
||||
record.increment!
|
||||
expect { record.increment! }.to raise_error(AASM::InvalidTransition)
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :beta
|
||||
|
||||
record.level_up!
|
||||
expect_aasm_states record, :three, :gamma
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :gamma
|
||||
|
||||
record.level_down # without saving
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :gamma
|
||||
|
||||
record.level_down # without saving
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reset!
|
||||
expect_aasm_states record, :one, :beta
|
||||
end
|
||||
|
||||
def expect_aasm_states(record, left_state, right_state)
|
||||
expect(record.aasm(:left).current_state).to eql left_state.to_sym
|
||||
expect(record.left).to eql left_state.to_s
|
||||
expect(record.aasm(:right).current_state).to eql right_state.to_sym
|
||||
expect(record.right).to eql right_state.to_s
|
||||
end
|
||||
end
|
|
@ -1,19 +1,15 @@
|
|||
require 'active_record'
|
||||
require 'spec_helper'
|
||||
Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each { |f| require File.expand_path(f) }
|
||||
Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each do |f|
|
||||
require File.expand_path(f)
|
||||
end
|
||||
|
||||
load_schema
|
||||
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# require 'logger'
|
||||
# ActiveRecord::Base.logger = Logger.new(STDERR)
|
||||
|
||||
shared_examples_for "aasm model" do
|
||||
it "should include persistence mixins" do
|
||||
expect(klass.included_modules).to be_include(AASM::Persistence::ActiveRecordPersistence)
|
||||
expect(klass.included_modules).to be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
|
||||
end
|
||||
end
|
||||
|
||||
describe "instance methods" do
|
||||
let(:gate) {Gate.new}
|
||||
|
||||
|
@ -160,7 +156,7 @@ describe "instance methods" do
|
|||
|
||||
gate.aasm_write_state state_sym
|
||||
|
||||
expect(gate).to have_received(:aasm_write_attribute).with(state_sym)
|
||||
expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :default)
|
||||
expect(gate).to_not have_received :write_attribute
|
||||
end
|
||||
end
|
||||
|
@ -170,7 +166,7 @@ describe "instance methods" do
|
|||
it "delegates state update to the helper method" do
|
||||
gate.aasm_write_state_without_persistence state_sym
|
||||
|
||||
expect(gate).to have_received(:aasm_write_attribute).with(state_sym)
|
||||
expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :default)
|
||||
expect(gate).to_not have_received :write_attribute
|
||||
end
|
||||
end
|
||||
|
@ -210,7 +206,7 @@ describe "instance methods" do
|
|||
end
|
||||
|
||||
it "generates attribute value using a helper method" do
|
||||
expect(gate).to have_received(:aasm_raw_attribute_value).with(sym)
|
||||
expect(gate).to have_received(:aasm_raw_attribute_value).with(sym, :default)
|
||||
end
|
||||
|
||||
it "writes attribute to the model" do
|
||||
|
@ -431,7 +427,7 @@ describe 'transitions with persistence' do
|
|||
|
||||
it "should only rollback changes in the main transaction not the nested one" do
|
||||
# change configuration to not require new transaction
|
||||
AASM::StateMachine[Transactor].config.requires_new_transaction = false
|
||||
AASM::StateMachine[Transactor][:default].config.requires_new_transaction = false
|
||||
|
||||
expect(transactor).to be_sleeping
|
||||
expect(worker.status).to eq('sleeping')
|
||||
|
@ -516,3 +512,12 @@ describe "invalid states with persistence" do
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'basic example with two state machines' do
|
||||
let(:example) { BasicActiveRecordTwoStateMachinesExample.new }
|
||||
|
||||
it 'should initialise properly' do
|
||||
expect(example.aasm(:search).current_state).to eql :initialised
|
||||
expect(example.aasm(:sync).current_state).to eql :unsynced
|
||||
end
|
||||
end
|
||||
|
|
146
spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb
Normal file
146
spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb
Normal file
|
@ -0,0 +1,146 @@
|
|||
describe 'mongo_mapper' do
|
||||
begin
|
||||
require 'mongo_mapper'
|
||||
require 'logger'
|
||||
require 'spec_helper'
|
||||
|
||||
Dir[File.dirname(__FILE__) + "/../../models/mongo_mapper/*.rb"].sort.each do |f|
|
||||
require File.expand_path(f)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
config = {
|
||||
'test' => {
|
||||
'database' => "mongo_mapper_#{Process.pid}"
|
||||
}
|
||||
}
|
||||
|
||||
MongoMapper.setup(config, 'test') #, :logger => Logger.new(STDERR))
|
||||
end
|
||||
|
||||
after do
|
||||
# Clear Out all non-system Mongo collections between tests
|
||||
MongoMapper.database.collections.each do |collection|
|
||||
collection.drop unless collection.capped? || (collection.name =~ /\Asystem/)
|
||||
end
|
||||
end
|
||||
|
||||
describe "named scopes with the old DSL" do
|
||||
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(SimpleMongoMapperMultiple).to respond_to(:unknown_scope)
|
||||
expect(SimpleMongoMapperMultiple.unknown_scope.class).to eq(MongoMapper::Plugins::Querying::DecoratedPluckyQuery)
|
||||
#expect(SimpleMongoMapperMultiple.unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(SimpleMongoMapperMultiple).to respond_to(:next)
|
||||
expect(SimpleMongoMapperMultiple.new.class).to eq(SimpleMongoMapperMultiple)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "named scopes with the new DSL" do
|
||||
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(SimpleNewDslMongoMapperMultiple).to respond_to(:unknown_scope)
|
||||
expect(SimpleNewDslMongoMapperMultiple.unknown_scope.class).to eq(MongoMapper::Plugins::Querying::DecoratedPluckyQuery)
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(SimpleNewDslMongoMapperMultiple).to respond_to(:next)
|
||||
expect(SimpleNewDslMongoMapperMultiple.new.class).to eq(SimpleNewDslMongoMapperMultiple)
|
||||
end
|
||||
end
|
||||
|
||||
it "does not create scopes if requested" do
|
||||
expect(NoScopeMongoMapperMultiple).not_to respond_to(:ignored_scope)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "instance methods" do
|
||||
|
||||
let(:simple) {SimpleNewDslMongoMapperMultiple.new}
|
||||
|
||||
it "should call aasm_ensure_initial_state on validation before create" do
|
||||
expect(SimpleNewDslMongoMapperMultiple.aasm(:left).initial_state).to eq(:unknown_scope)
|
||||
expect(SimpleNewDslMongoMapperMultiple.aasm(:left).attribute_name).to eq(:status)
|
||||
expect(simple.status).to eq(nil)
|
||||
simple.valid?
|
||||
expect(simple.status).to eq('unknown_scope')
|
||||
end
|
||||
|
||||
it "should call aasm_ensure_initial_state before create, even if skipping validations" do
|
||||
expect(simple.status).to eq(nil)
|
||||
simple.save(:validate => false)
|
||||
expect(simple.status).to eq('unknown_scope')
|
||||
end
|
||||
end
|
||||
|
||||
describe "complex example" do
|
||||
it "works" do
|
||||
record = ComplexMongoMapperExample.new
|
||||
expect(record.aasm(:left).current_state).to eql :one
|
||||
expect(record.left).to be_nil
|
||||
expect(record.aasm(:right).current_state).to eql :alpha
|
||||
expect(record.right).to be_nil
|
||||
|
||||
record.save!
|
||||
expect_aasm_states record, :one, :alpha
|
||||
record.reload
|
||||
expect_aasm_states record, :one, :alpha
|
||||
|
||||
record.increment!
|
||||
expect_aasm_states record, :two, :alpha
|
||||
record.reload
|
||||
expect_aasm_states record, :two, :alpha
|
||||
|
||||
record.level_up!
|
||||
expect_aasm_states record, :two, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :two, :beta
|
||||
|
||||
record.increment!
|
||||
expect { record.increment! }.to raise_error(AASM::InvalidTransition)
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :beta
|
||||
|
||||
record.level_up!
|
||||
expect_aasm_states record, :three, :gamma
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :gamma
|
||||
|
||||
record.level_down # without saving
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :gamma
|
||||
|
||||
record.level_down # without saving
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reset!
|
||||
expect_aasm_states record, :one, :beta
|
||||
end
|
||||
|
||||
def expect_aasm_states(record, left_state, right_state)
|
||||
expect(record.aasm(:left).current_state).to eql left_state.to_sym
|
||||
expect(record.left).to eql left_state.to_s
|
||||
expect(record.aasm(:right).current_state).to eql right_state.to_sym
|
||||
expect(record.right).to eql right_state.to_s
|
||||
end
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
puts "--------------------------------------------------------------------------"
|
||||
puts "Not running MongoMapper specs because mongo_mapper gem is not installed!!!"
|
||||
puts "--------------------------------------------------------------------------"
|
||||
end
|
||||
end
|
|
@ -4,9 +4,11 @@ describe 'mongo_mapper' do
|
|||
require 'logger'
|
||||
require 'spec_helper'
|
||||
|
||||
before(:all) do
|
||||
Dir[File.dirname(__FILE__) + "/../../models/mongo_mapper/*.rb"].sort.each { |f| require File.expand_path(f) }
|
||||
Dir[File.dirname(__FILE__) + "/../../models/mongo_mapper/*.rb"].sort.each do |f|
|
||||
require File.expand_path(f)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
config = {
|
||||
'test' => {
|
||||
'database' => "mongo_mapper_#{Process.pid}"
|
||||
|
@ -64,52 +66,6 @@ describe 'mongo_mapper' do
|
|||
|
||||
end
|
||||
|
||||
describe "#find_in_state" do
|
||||
|
||||
let!(:model) { SimpleNewDslMongoMapper.create!(:status => :unknown_scope) }
|
||||
let!(:model_id) { model._id }
|
||||
|
||||
it "should respond to method" do
|
||||
expect(SimpleNewDslMongoMapper).to respond_to(:find_in_state)
|
||||
end
|
||||
|
||||
it "should find the model when given the correct scope and model id" do
|
||||
expect(SimpleNewDslMongoMapper.find_in_state(model_id, 'unknown_scope').class).to eq(SimpleNewDslMongoMapper)
|
||||
expect(SimpleNewDslMongoMapper.find_in_state(model_id, 'unknown_scope')).to eq(model)
|
||||
end
|
||||
|
||||
it "should raise DocumentNotFound error when given incorrect scope" do
|
||||
expect {SimpleNewDslMongoMapper.find_in_state(model_id, 'next')}.to raise_error MongoMapper::DocumentNotFound
|
||||
end
|
||||
|
||||
it "should raise DocumentNotFound error when given incorrect model id" do
|
||||
expect {SimpleNewDslMongoMapper.find_in_state('bad_id', 'unknown_scope')}.to raise_error MongoMapper::DocumentNotFound
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#count_in_state" do
|
||||
|
||||
before do
|
||||
3.times { SimpleNewDslMongoMapper.create!(:status => :unknown_scope) }
|
||||
end
|
||||
|
||||
it "should respond to method" do
|
||||
expect(SimpleNewDslMongoMapper).to respond_to(:count_in_state)
|
||||
end
|
||||
|
||||
it "should return n for a scope with n records persisted" do
|
||||
expect(SimpleNewDslMongoMapper.count_in_state('unknown_scope').class).to eq(Fixnum)
|
||||
expect(SimpleNewDslMongoMapper.count_in_state('unknown_scope')).to eq(3)
|
||||
end
|
||||
|
||||
it "should return zero for a scope without records persisted" do
|
||||
expect(SimpleNewDslMongoMapper.count_in_state('next').class).to eq(Fixnum)
|
||||
expect(SimpleNewDslMongoMapper.count_in_state('next')).to eq(0)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "instance methods" do
|
||||
|
||||
let(:simple) {SimpleNewDslMongoMapper.new}
|
||||
|
@ -130,6 +86,8 @@ describe 'mongo_mapper' do
|
|||
end
|
||||
|
||||
rescue LoadError
|
||||
puts "Not running MongoMapper specs because mongo_mapper gem is not installed!!!"
|
||||
puts "--------------------------------------------------------------------------"
|
||||
puts "Not running MongoMapper multiple-specs because mongo_mapper gem is not installed!!!"
|
||||
puts "--------------------------------------------------------------------------"
|
||||
end
|
||||
end
|
|
@ -1,146 +0,0 @@
|
|||
describe 'mongoid' do
|
||||
begin
|
||||
require 'mongoid'
|
||||
require 'logger'
|
||||
require 'spec_helper'
|
||||
|
||||
before(:all) do
|
||||
Dir[File.dirname(__FILE__) + "/../../models/mongoid/*.rb"].sort.each { |f| require File.expand_path(f) }
|
||||
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# Mongoid.logger = Logger.new(STDERR)
|
||||
|
||||
DATABASE_NAME = "mongoid_#{Process.pid}"
|
||||
|
||||
Mongoid.configure do |config|
|
||||
config.connect_to DATABASE_NAME
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Mongoid.purge!
|
||||
end
|
||||
|
||||
describe "named scopes with the old DSL" do
|
||||
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(SimpleMongoid).to respond_to(:unknown_scope)
|
||||
expect(SimpleMongoid.unknown_scope.class).to eq(Mongoid::Criteria)
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(SimpleMongoid).to respond_to(:new)
|
||||
expect(SimpleMongoid.new.class).to eq(SimpleMongoid)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "named scopes with the new DSL" do
|
||||
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(SimpleNewDslMongoid).to respond_to(:unknown_scope)
|
||||
expect(SimpleNewDslMongoid.unknown_scope.class).to eq(Mongoid::Criteria)
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(SimpleNewDslMongoid).to respond_to(:new)
|
||||
expect(SimpleNewDslMongoid.new.class).to eq(SimpleNewDslMongoid)
|
||||
end
|
||||
end
|
||||
|
||||
it "does not create scopes if requested" do
|
||||
expect(NoScopeMongoid).not_to respond_to(:ignored_scope)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#find_in_state" do
|
||||
|
||||
let!(:model) { SimpleNewDslMongoid.create!(:status => :unknown_scope) }
|
||||
let!(:model_id) { model._id }
|
||||
|
||||
it "should respond to method" do
|
||||
expect(SimpleNewDslMongoid).to respond_to(:find_in_state)
|
||||
end
|
||||
|
||||
it "should find the model when given the correct scope and model id" do
|
||||
expect(SimpleNewDslMongoid.find_in_state(model_id, 'unknown_scope').class).to eq(SimpleNewDslMongoid)
|
||||
expect(SimpleNewDslMongoid.find_in_state(model_id, 'unknown_scope')).to eq(model)
|
||||
end
|
||||
|
||||
it "should raise DocumentNotFound error when given incorrect scope" do
|
||||
expect {SimpleNewDslMongoid.find_in_state(model_id, 'new')}.to raise_error Mongoid::Errors::DocumentNotFound
|
||||
end
|
||||
|
||||
it "should raise DocumentNotFound error when given incorrect model id" do
|
||||
expect {SimpleNewDslMongoid.find_in_state('bad_id', 'unknown_scope')}.to raise_error Mongoid::Errors::DocumentNotFound
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#count_in_state" do
|
||||
|
||||
before do
|
||||
3.times { SimpleNewDslMongoid.create!(:status => :unknown_scope) }
|
||||
end
|
||||
|
||||
it "should respond to method" do
|
||||
expect(SimpleNewDslMongoid).to respond_to(:count_in_state)
|
||||
end
|
||||
|
||||
it "should return n for a scope with n records persisted" do
|
||||
expect(SimpleNewDslMongoid.count_in_state('unknown_scope').class).to eq(Fixnum)
|
||||
expect(SimpleNewDslMongoid.count_in_state('unknown_scope')).to eq(3)
|
||||
end
|
||||
|
||||
it "should return zero for a scope without records persisted" do
|
||||
expect(SimpleNewDslMongoid.count_in_state('new').class).to eq(Fixnum)
|
||||
expect(SimpleNewDslMongoid.count_in_state('new')).to eq(0)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#with_state_scope" do
|
||||
|
||||
before do
|
||||
3.times { SimpleNewDslMongoid.create!(:status => :unknown_scope) }
|
||||
2.times { SimpleNewDslMongoid.create!(:status => :new) }
|
||||
end
|
||||
|
||||
it "should respond to method" do
|
||||
expect(SimpleNewDslMongoid).to respond_to(:with_state_scope)
|
||||
end
|
||||
|
||||
it "should correctly process block" do
|
||||
expect(SimpleNewDslMongoid.with_state_scope('unknown_scope') do
|
||||
SimpleNewDslMongoid.count
|
||||
end).to eq(3)
|
||||
expect(SimpleNewDslMongoid.with_state_scope('new') do
|
||||
SimpleNewDslMongoid.count
|
||||
end).to eq(2)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
describe "instance methods" do
|
||||
let(:simple) {SimpleNewDslMongoid.new}
|
||||
|
||||
it "should initialize the aasm state on instantiation" do
|
||||
expect(SimpleNewDslMongoid.new.status).to eql 'unknown_scope'
|
||||
expect(SimpleNewDslMongoid.new.aasm.current_state).to eql :unknown_scope
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
puts "Not running Mongoid specs because mongoid gem is not installed!!!"
|
||||
end
|
||||
end
|
127
spec/unit/persistence/mongoid_persistence_multiple_spec.rb
Normal file
127
spec/unit/persistence/mongoid_persistence_multiple_spec.rb
Normal file
|
@ -0,0 +1,127 @@
|
|||
describe 'mongoid' do
|
||||
begin
|
||||
require 'mongoid'
|
||||
require 'logger'
|
||||
require 'spec_helper'
|
||||
|
||||
Dir[File.dirname(__FILE__) + "/../../models/mongoid/*.rb"].sort.each do |f|
|
||||
require File.expand_path(f)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# Mongoid.logger = Logger.new(STDERR)
|
||||
|
||||
Mongoid.configure do |config|
|
||||
config.connect_to "mongoid_#{Process.pid}"
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Mongoid.purge!
|
||||
end
|
||||
|
||||
describe "named scopes with the old DSL" do
|
||||
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(SimpleMongoidMultiple).to respond_to(:unknown_scope)
|
||||
expect(SimpleMongoidMultiple.unknown_scope.class).to eq(Mongoid::Criteria)
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(SimpleMongoidMultiple).to respond_to(:new)
|
||||
expect(SimpleMongoidMultiple.new.class).to eq(SimpleMongoidMultiple)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "named scopes with the new DSL" do
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(SimpleNewDslMongoidMultiple).to respond_to(:unknown_scope)
|
||||
expect(SimpleNewDslMongoidMultiple.unknown_scope.class).to eq(Mongoid::Criteria)
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(SimpleNewDslMongoidMultiple).to respond_to(:new)
|
||||
expect(SimpleNewDslMongoidMultiple.new.class).to eq(SimpleNewDslMongoidMultiple)
|
||||
end
|
||||
end
|
||||
|
||||
it "does not create scopes if requested" do
|
||||
expect(NoScopeMongoidMultiple).not_to respond_to(:ignored_scope)
|
||||
end
|
||||
end
|
||||
|
||||
describe "instance methods" do
|
||||
let(:simple) {SimpleNewDslMongoidMultiple.new}
|
||||
|
||||
it "should initialize the aasm state on instantiation" do
|
||||
expect(SimpleNewDslMongoidMultiple.new.status).to eql 'unknown_scope'
|
||||
expect(SimpleNewDslMongoidMultiple.new.aasm(:left).current_state).to eql :unknown_scope
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "complex example" do
|
||||
it "works" do
|
||||
record = ComplexMongoidExample.new
|
||||
expect_aasm_states record, :one, :alpha
|
||||
|
||||
record.save!
|
||||
expect_aasm_states record, :one, :alpha
|
||||
record.reload
|
||||
expect_aasm_states record, :one, :alpha
|
||||
|
||||
record.increment!
|
||||
expect_aasm_states record, :two, :alpha
|
||||
record.reload
|
||||
expect_aasm_states record, :two, :alpha
|
||||
|
||||
record.level_up!
|
||||
expect_aasm_states record, :two, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :two, :beta
|
||||
|
||||
record.increment!
|
||||
expect { record.increment! }.to raise_error(AASM::InvalidTransition)
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :beta
|
||||
|
||||
record.level_up!
|
||||
expect_aasm_states record, :three, :gamma
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :gamma
|
||||
|
||||
record.level_down # without saving
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :gamma
|
||||
|
||||
record.level_down # without saving
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reset!
|
||||
expect_aasm_states record, :one, :beta
|
||||
end
|
||||
|
||||
def expect_aasm_states(record, left_state, right_state)
|
||||
expect(record.aasm(:left).current_state).to eql left_state.to_sym
|
||||
expect(record.left).to eql left_state.to_s
|
||||
expect(record.aasm(:right).current_state).to eql right_state.to_sym
|
||||
expect(record.right).to eql right_state.to_s
|
||||
end
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
puts "--------------------------------------------------------------------------"
|
||||
puts "Not running Mongoid multiple-specs because mongoid gem is not installed!!!"
|
||||
puts "--------------------------------------------------------------------------"
|
||||
end
|
||||
end
|
79
spec/unit/persistence/mongoid_persistence_spec.rb
Normal file
79
spec/unit/persistence/mongoid_persistence_spec.rb
Normal file
|
@ -0,0 +1,79 @@
|
|||
describe 'mongoid' do
|
||||
begin
|
||||
require 'mongoid'
|
||||
require 'logger'
|
||||
require 'spec_helper'
|
||||
|
||||
Dir[File.dirname(__FILE__) + "/../../models/mongoid/*.rb"].sort.each do |f|
|
||||
require File.expand_path(f)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# Mongoid.logger = Logger.new(STDERR)
|
||||
|
||||
Mongoid.configure do |config|
|
||||
config.connect_to "mongoid_#{Process.pid}"
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Mongoid.purge!
|
||||
end
|
||||
|
||||
describe "named scopes with the old DSL" do
|
||||
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(SimpleMongoid).to respond_to(:unknown_scope)
|
||||
expect(SimpleMongoid.unknown_scope.class).to eq(Mongoid::Criteria)
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(SimpleMongoid).to respond_to(:new)
|
||||
expect(SimpleMongoid.new.class).to eq(SimpleMongoid)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "named scopes with the new DSL" do
|
||||
|
||||
context "Does not already respond_to? the scope name" do
|
||||
it "should add a scope" do
|
||||
expect(SimpleNewDslMongoid).to respond_to(:unknown_scope)
|
||||
expect(SimpleNewDslMongoid.unknown_scope.class).to eq(Mongoid::Criteria)
|
||||
end
|
||||
end
|
||||
|
||||
context "Already respond_to? the scope name" do
|
||||
it "should not add a scope" do
|
||||
expect(SimpleNewDslMongoid).to respond_to(:new)
|
||||
expect(SimpleNewDslMongoid.new.class).to eq(SimpleNewDslMongoid)
|
||||
end
|
||||
end
|
||||
|
||||
it "does not create scopes if requested" do
|
||||
expect(NoScopeMongoid).not_to respond_to(:ignored_scope)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "instance methods" do
|
||||
let(:simple) {SimpleNewDslMongoid.new}
|
||||
|
||||
it "should initialize the aasm state on instantiation" do
|
||||
expect(SimpleNewDslMongoid.new.status).to eql 'unknown_scope'
|
||||
expect(SimpleNewDslMongoid.new.aasm.current_state).to eql :unknown_scope
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
puts "--------------------------------------------------------------------------"
|
||||
puts "Not running Mongoid specs because mongoid gem is not installed!!!"
|
||||
puts "--------------------------------------------------------------------------"
|
||||
end
|
||||
end
|
153
spec/unit/persistence/sequel_persistence_multiple_spec.rb
Normal file
153
spec/unit/persistence/sequel_persistence_multiple_spec.rb
Normal file
|
@ -0,0 +1,153 @@
|
|||
describe 'sequel' do
|
||||
begin
|
||||
require 'sequel'
|
||||
require 'logger'
|
||||
require 'spec_helper'
|
||||
|
||||
Dir[File.dirname(__FILE__) + "/../../models/sequel/*.rb"].sort.each do |f|
|
||||
require File.expand_path(f)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
@model = SequelMultiple
|
||||
end
|
||||
|
||||
describe "instance methods" do
|
||||
let(:model) {@model.new}
|
||||
|
||||
it "should respond to aasm persistence methods" do
|
||||
expect(model).to respond_to(:aasm_read_state)
|
||||
expect(model).to respond_to(:aasm_write_state)
|
||||
expect(model).to respond_to(:aasm_write_state_without_persistence)
|
||||
end
|
||||
|
||||
it "should return the initial state when new and the aasm field is nil" do
|
||||
expect(model.aasm(:left).current_state).to eq(:alpha)
|
||||
end
|
||||
|
||||
it "should save the initial state" do
|
||||
model.save
|
||||
expect(model.status).to eq("alpha")
|
||||
end
|
||||
|
||||
it "should return the aasm column when new and the aasm field is not nil" do
|
||||
model.status = "beta"
|
||||
expect(model.aasm(:left).current_state).to eq(:beta)
|
||||
end
|
||||
|
||||
it "should return the aasm column when not new and the aasm_column is not nil" do
|
||||
allow(model).to receive(:new?).and_return(false)
|
||||
model.status = "gamma"
|
||||
expect(model.aasm(:left).current_state).to eq(:gamma)
|
||||
end
|
||||
|
||||
it "should allow a nil state" do
|
||||
allow(model).to receive(:new?).and_return(false)
|
||||
model.status = nil
|
||||
expect(model.aasm(:left).current_state).to be_nil
|
||||
end
|
||||
|
||||
it "should not change the state if state is not loaded" do
|
||||
model.release
|
||||
model.save
|
||||
model.class.select(:id).first.save
|
||||
model.reload
|
||||
expect(model.aasm(:left).current_state).to eq(:beta)
|
||||
end
|
||||
|
||||
it "should call aasm_ensure_initial_state on validation before create" do
|
||||
expect(model).to receive(:aasm_ensure_initial_state).and_return(true)
|
||||
model.valid?
|
||||
end
|
||||
|
||||
it "should call aasm_ensure_initial_state before create, even if skipping validations" do
|
||||
expect(model).to receive(:aasm_ensure_initial_state).and_return(true)
|
||||
model.save(:validate => false)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'subclasses' do
|
||||
it "should have the same states as its parent class" do
|
||||
expect(Class.new(@model).aasm(:left).states).to eq(@model.aasm(:left).states)
|
||||
end
|
||||
|
||||
it "should have the same events as its parent class" do
|
||||
expect(Class.new(@model).aasm(:left).events).to eq(@model.aasm(:left).events)
|
||||
end
|
||||
|
||||
it "should have the same column as its parent even for the new dsl" do
|
||||
expect(@model.aasm(:left).attribute_name).to eq(:status)
|
||||
expect(Class.new(@model).aasm(:left).attribute_name).to eq(:status)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'initial states' do
|
||||
it 'should support conditions' do
|
||||
@model.aasm(:left) do
|
||||
initial_state lambda{ |m| m.default }
|
||||
end
|
||||
|
||||
expect(@model.new(:default => :beta).aasm(:left).current_state).to eq(:beta)
|
||||
expect(@model.new(:default => :gamma).aasm(:left).current_state).to eq(:gamma)
|
||||
end
|
||||
end
|
||||
|
||||
describe "complex example" do
|
||||
it "works" do
|
||||
record = ComplexSequelExample.new
|
||||
expect(record.aasm(:left).current_state).to eql :one
|
||||
expect(record.left).to be_nil
|
||||
expect(record.aasm(:right).current_state).to eql :alpha
|
||||
expect(record.right).to be_nil
|
||||
|
||||
record.save
|
||||
expect_aasm_states record, :one, :alpha
|
||||
record.reload
|
||||
expect_aasm_states record, :one, :alpha
|
||||
|
||||
record.increment!
|
||||
expect_aasm_states record, :two, :alpha
|
||||
record.reload
|
||||
expect_aasm_states record, :two, :alpha
|
||||
|
||||
record.level_up!
|
||||
expect_aasm_states record, :two, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :two, :beta
|
||||
|
||||
record.increment!
|
||||
expect { record.increment! }.to raise_error(AASM::InvalidTransition)
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :beta
|
||||
|
||||
record.level_up!
|
||||
expect_aasm_states record, :three, :gamma
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :gamma
|
||||
|
||||
record.level_down # without saving
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reload
|
||||
expect_aasm_states record, :three, :gamma
|
||||
|
||||
record.level_down # without saving
|
||||
expect_aasm_states record, :three, :beta
|
||||
record.reset!
|
||||
expect_aasm_states record, :one, :beta
|
||||
end
|
||||
|
||||
def expect_aasm_states(record, left_state, right_state)
|
||||
expect(record.aasm(:left).current_state).to eql left_state.to_sym
|
||||
expect(record.left).to eql left_state.to_s
|
||||
expect(record.aasm(:right).current_state).to eql right_state.to_sym
|
||||
expect(record.right).to eql right_state.to_s
|
||||
end
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
puts "------------------------------------------------------------------------"
|
||||
puts "Not running Sequel multiple-specs because sequel gem is not installed!!!"
|
||||
puts "------------------------------------------------------------------------"
|
||||
end
|
||||
end
|
|
@ -1,34 +1,15 @@
|
|||
|
||||
describe 'sequel' do
|
||||
begin
|
||||
require 'sequel'
|
||||
require 'logger'
|
||||
require 'spec_helper'
|
||||
|
||||
Dir[File.dirname(__FILE__) + "/../../models/sequel/*.rb"].sort.each do |f|
|
||||
require File.expand_path(f)
|
||||
end
|
||||
|
||||
before(:all) do
|
||||
db = Sequel.connect(SEQUEL_DB)
|
||||
|
||||
# if you want to see the statements while running the spec enable the following line
|
||||
# db.loggers << Logger.new($stderr)
|
||||
db.create_table(:models) do
|
||||
primary_key :id
|
||||
String :status
|
||||
end
|
||||
|
||||
@model = Class.new(Sequel::Model(db)) do
|
||||
set_dataset(:models)
|
||||
attr_accessor :default
|
||||
include AASM
|
||||
aasm :column => :status
|
||||
aasm do
|
||||
state :alpha, :initial => true
|
||||
state :beta
|
||||
state :gamma
|
||||
event :release do
|
||||
transitions :from => [:alpha, :beta, :gamma], :to => :beta
|
||||
end
|
||||
end
|
||||
end
|
||||
@model = SequelSimple
|
||||
end
|
||||
|
||||
describe "instance methods" do
|
||||
|
@ -112,6 +93,8 @@ describe 'sequel' do
|
|||
end
|
||||
|
||||
rescue LoadError
|
||||
puts "------------------------------------------------------------------------"
|
||||
puts "Not running Sequel specs because sequel gem is not installed!!!"
|
||||
puts "------------------------------------------------------------------------"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'callbacks for the new DSL' do
|
||||
describe 'when redefining states' do
|
||||
let(:definer) { DoubleDefiner.new }
|
||||
|
||||
it "allows extending states" do
|
||||
|
|
63
spec/unit/simple_multiple_example_spec.rb
Normal file
63
spec/unit/simple_multiple_example_spec.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'state machine' do
|
||||
let(:simple) { SimpleMultipleExample.new }
|
||||
|
||||
it 'starts with an initial state' do
|
||||
expect(simple.aasm(:move).current_state).to eq(:standing)
|
||||
expect(simple).to respond_to(:standing?)
|
||||
expect(simple).to be_standing
|
||||
|
||||
expect(simple.aasm(:work).current_state).to eq(:sleeping)
|
||||
expect(simple).to respond_to(:sleeping?)
|
||||
expect(simple).to be_sleeping
|
||||
end
|
||||
|
||||
it 'allows transitions to other states' do
|
||||
expect(simple).to respond_to(:walk)
|
||||
expect(simple).to respond_to(:walk!)
|
||||
simple.walk!
|
||||
expect(simple).to respond_to(:walking?)
|
||||
expect(simple).to be_walking
|
||||
|
||||
expect(simple).to respond_to(:run)
|
||||
expect(simple).to respond_to(:run!)
|
||||
simple.run
|
||||
expect(simple).to respond_to(:running?)
|
||||
expect(simple).to be_running
|
||||
|
||||
expect(simple).to respond_to(:start)
|
||||
expect(simple).to respond_to(:start!)
|
||||
simple.start
|
||||
expect(simple).to respond_to(:processing?)
|
||||
expect(simple).to be_processing
|
||||
end
|
||||
|
||||
it 'denies transitions to other states' do
|
||||
expect {simple.hold}.to raise_error(AASM::InvalidTransition)
|
||||
expect {simple.hold!}.to raise_error(AASM::InvalidTransition)
|
||||
simple.walk
|
||||
expect {simple.walk}.to raise_error(AASM::InvalidTransition)
|
||||
expect {simple.walk!}.to raise_error(AASM::InvalidTransition)
|
||||
simple.run
|
||||
expect {simple.walk}.to raise_error(AASM::InvalidTransition)
|
||||
expect {simple.walk!}.to raise_error(AASM::InvalidTransition)
|
||||
|
||||
expect {simple.stop}.to raise_error(AASM::InvalidTransition)
|
||||
expect {simple.stop!}.to raise_error(AASM::InvalidTransition)
|
||||
simple.start
|
||||
expect {simple.start}.to raise_error(AASM::InvalidTransition)
|
||||
expect {simple.start!}.to raise_error(AASM::InvalidTransition)
|
||||
simple.stop
|
||||
end
|
||||
|
||||
it 'defines constants for each state name' do
|
||||
expect(SimpleMultipleExample::STATE_STANDING).to eq(:standing)
|
||||
expect(SimpleMultipleExample::STATE_WALKING).to eq(:walking)
|
||||
expect(SimpleMultipleExample::STATE_RUNNING).to eq(:running)
|
||||
|
||||
expect(SimpleMultipleExample::STATE_SLEEPING).to eq(:sleeping)
|
||||
expect(SimpleMultipleExample::STATE_PROCESSING).to eq(:processing)
|
||||
expect(SimpleMultipleExample::STATE_RUNNING).to eq(:running)
|
||||
end
|
||||
end
|
|
@ -1,13 +1,15 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe AASM::Core::State do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
|
||||
before(:each) do
|
||||
@name = :astate
|
||||
@options = { :crazy_custom_key => 'key' }
|
||||
end
|
||||
|
||||
def new_state(options={})
|
||||
AASM::Core::State.new(@name, Conversation, @options.merge(options))
|
||||
AASM::Core::State.new(@name, Conversation, state_machine, @options.merge(options))
|
||||
end
|
||||
|
||||
it 'should set the name' do
|
||||
|
|
39
spec/unit/subclassing_multiple_spec.rb
Normal file
39
spec/unit/subclassing_multiple_spec.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'subclassing with multiple state machines' do
|
||||
|
||||
it 'should have the parent states' do
|
||||
SuperClassMultiple.aasm(:left).states.each do |state|
|
||||
expect(SubClassWithMoreStatesMultiple.aasm(:left).states).to include(state)
|
||||
end
|
||||
expect(SubClassMultiple.aasm(:left).states).to eq(SuperClassMultiple.aasm(:left).states)
|
||||
|
||||
SuperClassMultiple.aasm(:right).states.each do |state|
|
||||
expect(SubClassWithMoreStatesMultiple.aasm(:right).states).to include(state)
|
||||
end
|
||||
expect(SubClassMultiple.aasm(:right).states).to eq(SuperClassMultiple.aasm(:right).states)
|
||||
end
|
||||
|
||||
it 'should not add the child states to the parent machine' do
|
||||
expect(SuperClassMultiple.aasm(:left).states).not_to include(:foo)
|
||||
expect(SuperClassMultiple.aasm(:right).states).not_to include(:archived)
|
||||
end
|
||||
|
||||
it "should have the same events as its parent" do
|
||||
expect(SubClassMultiple.aasm(:left).events).to eq(SuperClassMultiple.aasm(:left).events)
|
||||
expect(SubClassMultiple.aasm(:right).events).to eq(SuperClassMultiple.aasm(:right).events)
|
||||
end
|
||||
|
||||
it 'should know how to respond to question methods' do
|
||||
expect(SubClassMultiple.new.may_foo?).to be_truthy
|
||||
expect(SubClassMultiple.new.may_close?).to be_truthy
|
||||
end
|
||||
|
||||
it 'should not break if I call methods from super class' do
|
||||
son = SubClassMultiple.new
|
||||
son.update_state
|
||||
expect(son.aasm(:left).current_state).to eq(:ended)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -53,13 +53,13 @@ describe 'transitions' do
|
|||
|
||||
end
|
||||
|
||||
describe 'blocks' do
|
||||
end
|
||||
|
||||
describe AASM::Core::Transition do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
let(:event) { AASM::Core::Event.new(:event, state_machine) }
|
||||
|
||||
it 'should set from, to, and opts attr readers' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
expect(st.from).to eq(opts[:from])
|
||||
expect(st.to).to eq(opts[:to])
|
||||
|
@ -71,7 +71,7 @@ describe AASM::Core::Transition do
|
|||
st = AASM::Core::Transition.allocate
|
||||
expect(st).to receive(:warn).with('[DEPRECATION] :on_transition is deprecated, use :after instead')
|
||||
|
||||
st.send :initialize, opts do
|
||||
st.send :initialize, event, opts do
|
||||
guard :gg
|
||||
on_transition :after_callback
|
||||
end
|
||||
|
@ -81,7 +81,7 @@ describe AASM::Core::Transition do
|
|||
|
||||
it 'should set after and guard from dsl' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
|
||||
st = AASM::Core::Transition.new(opts) do
|
||||
st = AASM::Core::Transition.new(event, opts) do
|
||||
guard :gg
|
||||
after :after_callback
|
||||
end
|
||||
|
@ -92,7 +92,7 @@ describe AASM::Core::Transition do
|
|||
|
||||
it 'should pass equality check if from and to are the same' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
obj = double('object')
|
||||
allow(obj).to receive(:from).and_return(opts[:from])
|
||||
|
@ -103,7 +103,7 @@ describe AASM::Core::Transition do
|
|||
|
||||
it 'should fail equality check if from are not the same' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
obj = double('object')
|
||||
allow(obj).to receive(:from).and_return('blah')
|
||||
|
@ -114,7 +114,7 @@ describe AASM::Core::Transition do
|
|||
|
||||
it 'should fail equality check if to are not the same' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
obj = double('object')
|
||||
allow(obj).to receive(:from).and_return(opts[:from])
|
||||
|
@ -125,16 +125,19 @@ describe AASM::Core::Transition do
|
|||
end
|
||||
|
||||
describe AASM::Core::Transition, '- when performing guard checks' do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
let(:event) { AASM::Core::Event.new(:event, state_machine) }
|
||||
|
||||
it 'should return true of there is no guard' do
|
||||
opts = {:from => 'foo', :to => 'bar'}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
expect(st.allowed?(nil)).to be_truthy
|
||||
end
|
||||
|
||||
it 'should call the method on the object if guard is a symbol' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => :test}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
obj = double('object')
|
||||
expect(obj).to receive(:test)
|
||||
|
@ -144,7 +147,7 @@ describe AASM::Core::Transition, '- when performing guard checks' do
|
|||
|
||||
it 'should call the method on the object if unless is a symbol' do
|
||||
opts = {:from => 'foo', :to => 'bar', :unless => :test}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
obj = double('object')
|
||||
expect(obj).to receive(:test)
|
||||
|
@ -154,7 +157,7 @@ describe AASM::Core::Transition, '- when performing guard checks' do
|
|||
|
||||
it 'should call the method on the object if guard is a string' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => 'test'}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
obj = double('object')
|
||||
expect(obj).to receive(:test)
|
||||
|
@ -164,7 +167,7 @@ describe AASM::Core::Transition, '- when performing guard checks' do
|
|||
|
||||
it 'should call the method on the object if unless is a string' do
|
||||
opts = {:from => 'foo', :to => 'bar', :unless => 'test'}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
obj = double('object')
|
||||
expect(obj).to receive(:test)
|
||||
|
@ -174,7 +177,7 @@ describe AASM::Core::Transition, '- when performing guard checks' do
|
|||
|
||||
it 'should call the proc passing the object if the guard is a proc' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => Proc.new { test }}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
|
||||
obj = double('object')
|
||||
expect(obj).to receive(:test)
|
||||
|
@ -184,9 +187,12 @@ describe AASM::Core::Transition, '- when performing guard checks' do
|
|||
end
|
||||
|
||||
describe AASM::Core::Transition, '- when executing the transition with a Proc' do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
let(:event) { AASM::Core::Event.new(:event, state_machine) }
|
||||
|
||||
it 'should call a Proc on the object with args' do
|
||||
opts = {:from => 'foo', :to => 'bar', :after => Proc.new {|a| test(a) }}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
args = {:arg1 => '1', :arg2 => '2'}
|
||||
obj = double('object', :aasm => 'aasm')
|
||||
|
||||
|
@ -202,7 +208,7 @@ describe AASM::Core::Transition, '- when executing the transition with a Proc' d
|
|||
prc = Proc.new { prc_object = self }
|
||||
|
||||
opts = {:from => 'foo', :to => 'bar', :after => prc }
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
args = {:arg1 => '1', :arg2 => '2'}
|
||||
obj = double('object', :aasm => 'aasm')
|
||||
|
||||
|
@ -212,9 +218,12 @@ describe AASM::Core::Transition, '- when executing the transition with a Proc' d
|
|||
end
|
||||
|
||||
describe AASM::Core::Transition, '- when executing the transition with an :after method call' do
|
||||
let(:state_machine) { AASM::StateMachine.new(:name) }
|
||||
let(:event) { AASM::Core::Event.new(:event, state_machine) }
|
||||
|
||||
it 'should accept a String for the method name' do
|
||||
opts = {:from => 'foo', :to => 'bar', :after => 'test'}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
args = {:arg1 => '1', :arg2 => '2'}
|
||||
obj = double('object', :aasm => 'aasm')
|
||||
|
||||
|
@ -225,7 +234,7 @@ describe AASM::Core::Transition, '- when executing the transition with an :after
|
|||
|
||||
it 'should accept a Symbol for the method name' do
|
||||
opts = {:from => 'foo', :to => 'bar', :after => :test}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
args = {:arg1 => '1', :arg2 => '2'}
|
||||
obj = double('object', :aasm => 'aasm')
|
||||
|
||||
|
@ -236,7 +245,7 @@ describe AASM::Core::Transition, '- when executing the transition with an :after
|
|||
|
||||
it 'should pass args if the target method accepts them' do
|
||||
opts = {:from => 'foo', :to => 'bar', :after => :test}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
args = {:arg1 => '1', :arg2 => '2'}
|
||||
obj = double('object', :aasm => 'aasm')
|
||||
|
||||
|
@ -251,7 +260,7 @@ describe AASM::Core::Transition, '- when executing the transition with an :after
|
|||
|
||||
it 'should NOT pass args if the target method does NOT accept them' do
|
||||
opts = {:from => 'foo', :to => 'bar', :after => :test}
|
||||
st = AASM::Core::Transition.new(opts)
|
||||
st = AASM::Core::Transition.new(event, opts)
|
||||
args = {:arg1 => '1', :arg2 => '2'}
|
||||
obj = double('object', :aasm => 'aasm')
|
||||
|
||||
|
@ -266,7 +275,7 @@ describe AASM::Core::Transition, '- when executing the transition with an :after
|
|||
|
||||
it 'should allow accessing the from_state and the to_state' do
|
||||
opts = {:from => 'foo', :to => 'bar', :after => :test}
|
||||
transition = AASM::Core::Transition.new(opts)
|
||||
transition = AASM::Core::Transition.new(event, opts)
|
||||
args = {:arg1 => '1', :arg2 => '2'}
|
||||
obj = double('object', :aasm => AASM::InstanceBase.new('object'))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue