mirror of
https://github.com/aasm/aasm
synced 2023-03-27 23:22:41 -04:00
Fixed conflicts after merging with rubyist-aasm
This commit is contained in:
commit
fe13954e93
21 changed files with 486 additions and 263 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@ rdoc
|
|||
pkg
|
||||
coverage
|
||||
*~
|
||||
*.sw?
|
||||
|
|
62
README.rdoc
62
README.rdoc
|
@ -11,15 +11,33 @@ AASM has the following features:
|
|||
* Events
|
||||
* Transitions
|
||||
|
||||
== New Callbacks
|
||||
|
||||
The callback chain & order on a successful event looks like:
|
||||
|
||||
oldstate:exit*
|
||||
event:before
|
||||
__find transition, if possible__
|
||||
transition:on_transition*
|
||||
oldstate:before_exit
|
||||
newstate:before_enter
|
||||
newstate:enter*
|
||||
__update state__
|
||||
event:success*
|
||||
oldstate:after_exit
|
||||
oldstate:after_enter
|
||||
event:after
|
||||
obj:aasm_event_fired*
|
||||
|
||||
(*) marks old callbacks
|
||||
|
||||
|
||||
== Download
|
||||
|
||||
The latest AASM can currently be pulled from the git repository on github.
|
||||
|
||||
* http://github.com/rubyist/aasm/tree/master
|
||||
|
||||
A release and a gem are forthcoming.
|
||||
|
||||
|
||||
|
||||
== Installation
|
||||
|
||||
|
@ -31,7 +49,7 @@ A release and a gem are forthcoming.
|
|||
=== Building your own gems
|
||||
|
||||
% rake gem
|
||||
% sudo gem install pkg/aasm-2.0.1.gem
|
||||
% sudo gem install pkg/aasm-2.1.gem
|
||||
|
||||
|
||||
== Simple Example
|
||||
|
@ -57,14 +75,44 @@ Here's a quick example highlighting some of the features.
|
|||
end
|
||||
end
|
||||
|
||||
== A Slightly More Complex Example
|
||||
|
||||
This example uses a few of the more complex features available.
|
||||
|
||||
class Relationship
|
||||
include AASM
|
||||
|
||||
aasm_initial_state Proc.new { |relationship| relationship.strictly_for_fun? ? :intimate : :dating }
|
||||
|
||||
aasm_state :dating, :enter => :make_happy, :exit => :make_depressed
|
||||
aasm_state :intimate, :enter => :make_very_happy, :exit => :never_speak_again
|
||||
aasm_state :married, :enter => :give_up_intimacy, :exit => :buy_exotic_car_and_wear_a_combover
|
||||
|
||||
aasm_event :get_intimate do
|
||||
transitions :to => :intimate, :from => [:dating], :guard => :drunk?
|
||||
end
|
||||
|
||||
aasm_event :get_married do
|
||||
transitions :to => :married, :from => [:dating, :intimate], :guard => :willing_to_give_up_manhood?
|
||||
end
|
||||
|
||||
def strictly_for_fun?; end
|
||||
def drunk?; end
|
||||
def willing_to_give_up_manhood?; end
|
||||
def make_happy; end
|
||||
def make_depressed; end
|
||||
def make_very_happy; end
|
||||
def never_speak_again; end
|
||||
def give_up_intimacy; end
|
||||
def buy_exotic_car_and_wear_a_combover; end
|
||||
end
|
||||
|
||||
= Other Stuff
|
||||
|
||||
Author:: Scott Barron <scott at elitists dot net>
|
||||
License:: Copyright 2006, 2007, 2008 by Scott Barron.
|
||||
License:: Original code Copyright 2006, 2007, 2008 by Scott Barron.
|
||||
Released under an MIT-style license. See the LICENSE file
|
||||
included in the distribution.
|
||||
Bugs:: http://rubyist.lighthouseapp.com/projects/13207-aasm/
|
||||
GitHub:: http://github.com/rubyist/aasm/tree/master
|
||||
|
||||
== Warranty
|
||||
|
||||
|
|
19
Rakefile
19
Rakefile
|
@ -22,8 +22,8 @@ end
|
|||
$package_version = CURRENT_VERSION
|
||||
|
||||
PKG_FILES = FileList['[A-Z]*',
|
||||
'lib/**/*.rb',
|
||||
'doc/**/*'
|
||||
'lib/**/*.rb',
|
||||
'doc/**/*'
|
||||
]
|
||||
|
||||
desc 'Generate documentation for the acts as state machine plugin.'
|
||||
|
@ -34,7 +34,7 @@ rd = Rake::RDocTask.new(:rdoc) do |rdoc|
|
|||
rdoc.title = 'AASM'
|
||||
rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc' << '--title' << 'AASM'
|
||||
rdoc.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'TODO', 'CHANGELOG')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc')
|
||||
rdoc.rdoc_files.include('lib/*.rb', 'lib/**/*.rb', 'doc/**/*.rdoc')
|
||||
end
|
||||
|
||||
if !defined?(Gem)
|
||||
|
@ -44,18 +44,19 @@ else
|
|||
s.name = 'aasm'
|
||||
s.version = $package_version
|
||||
s.summary = 'State machine mixin for Ruby objects'
|
||||
s.description = <<-EOF
|
||||
AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects.
|
||||
EOF
|
||||
s.description = <<EOF
|
||||
AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects.
|
||||
EOF
|
||||
|
||||
s.files = PKG_FILES.to_a
|
||||
s.require_path = 'lib'
|
||||
s.has_rdoc = true
|
||||
s.extra_rdoc_files = rd.rdoc_files.reject {|fn| fn =~ /\.rb$/}.to_a
|
||||
s.rdoc_options = rd.options
|
||||
|
||||
s.author = 'Scott Barron'
|
||||
s.email = 'scott@elitists.net'
|
||||
s.homepage = 'http://rubyi.st/aasm'
|
||||
s.authors = ['Scott Barron', 'Scott Petersen', 'Travis Tilley']
|
||||
s.email = 'ttilley@gmail.com'
|
||||
s.homepage = 'http://github.com/ttilley/aasm'
|
||||
end
|
||||
|
||||
package_task = Rake::GemPackageTask.new(spec) do |pkg|
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
PKG_FILES = ["CHANGELOG", "MIT-LICENSE", "Rakefile", "README.rdoc", "TODO", "lib/aasm.rb", "lib/event.rb", "lib/persistence/active_record_persistence.rb", "lib/persistence.rb", "lib/state.rb", "lib/state_machine.rb", "lib/state_transition.rb", "lib/version.rb", "doc/jamis.rb"]
|
||||
PKG_FILES = ["CHANGELOG", "MIT-LICENSE", "Rakefile", "README.rdoc", "TODO", "lib/aasm.rb", "lib/aasm/aasm.rb", "lib/aasm/event.rb", "lib/aasm/persistence/active_record_persistence.rb", "lib/aasm/persistence.rb", "lib/aasm/state.rb", "lib/aasm/state_machine.rb", "lib/aasm/state_transition.rb", "doc/jamis.rb"]
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'aasm'
|
||||
s.version = "2.0.5"
|
||||
s.version = "2.1.1"
|
||||
s.summary = 'State machine mixin for Ruby objects'
|
||||
s.description = <<-EOF
|
||||
AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects. This fork adds Ruby 1.9.1 compatibility.
|
||||
|
|
1
aasm.rb
1
aasm.rb
|
@ -1 +0,0 @@
|
|||
require File.join(File.dirname(__FILE__), 'lib', 'aasm')
|
160
lib/aasm.rb
160
lib/aasm.rb
|
@ -1,159 +1 @@
|
|||
require File.join(File.dirname(__FILE__), 'event')
|
||||
require File.join(File.dirname(__FILE__), 'state')
|
||||
require File.join(File.dirname(__FILE__), 'state_machine')
|
||||
require File.join(File.dirname(__FILE__), 'persistence')
|
||||
|
||||
module AASM
|
||||
def self.Version
|
||||
'2.0.5'
|
||||
end
|
||||
|
||||
class InvalidTransition < RuntimeError
|
||||
end
|
||||
|
||||
class UndefinedState < RuntimeError
|
||||
end
|
||||
|
||||
def self.included(base) #:nodoc:
|
||||
# TODO - need to ensure that a machine is being created because
|
||||
# AASM was either included or arrived at via inheritance. It
|
||||
# cannot be both.
|
||||
base.extend AASM::ClassMethods
|
||||
AASM::Persistence.set_persistence(base)
|
||||
AASM::StateMachine[base] = AASM::StateMachine.new('')
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def inherited(klass)
|
||||
AASM::StateMachine[klass] = AASM::StateMachine[self].clone
|
||||
super
|
||||
end
|
||||
|
||||
def aasm_initial_state(set_state=nil)
|
||||
if set_state
|
||||
AASM::StateMachine[self].initial_state = set_state
|
||||
else
|
||||
AASM::StateMachine[self].initial_state
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_initial_state=(state)
|
||||
AASM::StateMachine[self].initial_state = state
|
||||
end
|
||||
|
||||
def aasm_state(name, options={})
|
||||
sm = AASM::StateMachine[self]
|
||||
sm.create_state(name, options)
|
||||
sm.initial_state = name unless sm.initial_state
|
||||
|
||||
define_method("#{name.to_s}?") do
|
||||
aasm_current_state == name
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_event(name, options = {}, &block)
|
||||
sm = AASM::StateMachine[self]
|
||||
|
||||
unless sm.events.has_key?(name)
|
||||
sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
|
||||
end
|
||||
|
||||
define_method("#{name.to_s}!") do |*args|
|
||||
aasm_fire_event(name, true, *args)
|
||||
end
|
||||
|
||||
define_method("#{name.to_s}") do |*args|
|
||||
aasm_fire_event(name, false, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_states
|
||||
AASM::StateMachine[self].states
|
||||
end
|
||||
|
||||
def aasm_events
|
||||
AASM::StateMachine[self].events
|
||||
end
|
||||
|
||||
def aasm_states_for_select
|
||||
AASM::StateMachine[self].states.map { |state| state.for_select }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Instance methods
|
||||
def aasm_current_state
|
||||
return @aasm_current_state if @aasm_current_state
|
||||
|
||||
if self.respond_to?(:aasm_read_state) || self.private_methods.include?('aasm_read_state')
|
||||
@aasm_current_state = aasm_read_state
|
||||
end
|
||||
return @aasm_current_state if @aasm_current_state
|
||||
self.class.aasm_initial_state
|
||||
end
|
||||
|
||||
def aasm_events_for_current_state
|
||||
aasm_events_for_state(aasm_current_state)
|
||||
end
|
||||
|
||||
def aasm_events_for_state(state)
|
||||
events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
|
||||
events.map {|event| event.name}
|
||||
end
|
||||
|
||||
private
|
||||
def set_aasm_current_state_with_persistence(state)
|
||||
save_success = true
|
||||
if self.respond_to?(:aasm_write_state) || self.private_methods.include?('aasm_write_state')
|
||||
save_success = aasm_write_state(state)
|
||||
end
|
||||
self.aasm_current_state = state if save_success
|
||||
|
||||
save_success
|
||||
end
|
||||
|
||||
def aasm_current_state=(state)
|
||||
if self.respond_to?(:aasm_write_state_without_persistence) || self.private_methods.include?('aasm_write_state_without_persistence')
|
||||
aasm_write_state_without_persistence(state)
|
||||
end
|
||||
@aasm_current_state = state
|
||||
end
|
||||
|
||||
def aasm_state_object_for_state(name)
|
||||
obj = self.class.aasm_states.find {|s| s == name}
|
||||
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
|
||||
obj
|
||||
end
|
||||
|
||||
def aasm_fire_event(name, persist, *args)
|
||||
aasm_state_object_for_state(aasm_current_state).call_action(:exit, self)
|
||||
|
||||
new_state = self.class.aasm_events[name].fire(self, *args)
|
||||
|
||||
unless new_state.nil?
|
||||
aasm_state_object_for_state(new_state).call_action(:enter, self)
|
||||
|
||||
persist_successful = true
|
||||
if persist
|
||||
persist_successful = set_aasm_current_state_with_persistence(new_state)
|
||||
self.class.aasm_events[name].execute_success_callback(self) if persist_successful
|
||||
else
|
||||
self.aasm_current_state = new_state
|
||||
end
|
||||
|
||||
if persist_successful
|
||||
self.aasm_event_fired(self.aasm_current_state, new_state) if self.respond_to?(:aasm_event_fired)
|
||||
else
|
||||
self.aasm_event_failed(name) if self.respond_to?(:aasm_event_failed)
|
||||
end
|
||||
|
||||
persist_successful
|
||||
else
|
||||
if self.respond_to?(:aasm_event_failed)
|
||||
self.aasm_event_failed(name)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
require File.join(File.dirname(__FILE__), 'aasm', 'aasm')
|
||||
|
|
185
lib/aasm/aasm.rb
Normal file
185
lib/aasm/aasm.rb
Normal file
|
@ -0,0 +1,185 @@
|
|||
require File.join(File.dirname(__FILE__), 'event')
|
||||
require File.join(File.dirname(__FILE__), 'state')
|
||||
require File.join(File.dirname(__FILE__), 'state_machine')
|
||||
require File.join(File.dirname(__FILE__), 'persistence')
|
||||
|
||||
module AASM
|
||||
def self.Version
|
||||
'2.1.1'
|
||||
end
|
||||
|
||||
class InvalidTransition < RuntimeError
|
||||
end
|
||||
|
||||
class UndefinedState < RuntimeError
|
||||
end
|
||||
|
||||
def self.included(base) #:nodoc:
|
||||
base.extend AASM::ClassMethods
|
||||
AASM::Persistence.set_persistence(base)
|
||||
unless AASM::StateMachine[base]
|
||||
AASM::StateMachine[base] = AASM::StateMachine.new('')
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def inherited(klass)
|
||||
AASM::StateMachine[klass] = AASM::StateMachine[self].clone
|
||||
super
|
||||
end
|
||||
|
||||
def aasm_initial_state(set_state=nil)
|
||||
if set_state
|
||||
AASM::StateMachine[self].initial_state = set_state
|
||||
else
|
||||
AASM::StateMachine[self].initial_state
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_initial_state=(state)
|
||||
AASM::StateMachine[self].initial_state = state
|
||||
end
|
||||
|
||||
def aasm_state(name, options={})
|
||||
sm = AASM::StateMachine[self]
|
||||
sm.create_state(name, options)
|
||||
sm.initial_state = name unless sm.initial_state
|
||||
|
||||
define_method("#{name.to_s}?") do
|
||||
aasm_current_state == name
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_event(name, options = {}, &block)
|
||||
sm = AASM::StateMachine[self]
|
||||
|
||||
unless sm.events.has_key?(name)
|
||||
sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block)
|
||||
end
|
||||
|
||||
define_method("#{name.to_s}!") do |*args|
|
||||
aasm_fire_event(name, true, *args)
|
||||
end
|
||||
|
||||
define_method("#{name.to_s}") do |*args|
|
||||
aasm_fire_event(name, false, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_states
|
||||
AASM::StateMachine[self].states
|
||||
end
|
||||
|
||||
def aasm_events
|
||||
AASM::StateMachine[self].events
|
||||
end
|
||||
|
||||
def aasm_states_for_select
|
||||
AASM::StateMachine[self].states.map { |state| state.for_select }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Instance methods
|
||||
def aasm_current_state
|
||||
return @aasm_current_state if @aasm_current_state
|
||||
|
||||
if self.respond_to?(:aasm_read_state) || self.private_methods.include?('aasm_read_state')
|
||||
@aasm_current_state = aasm_read_state
|
||||
end
|
||||
return @aasm_current_state if @aasm_current_state
|
||||
aasm_determine_state_name(self.class.aasm_initial_state)
|
||||
end
|
||||
|
||||
def aasm_events_for_current_state
|
||||
aasm_events_for_state(aasm_current_state)
|
||||
end
|
||||
|
||||
def aasm_events_for_state(state)
|
||||
events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
|
||||
events.map {|event| event.name}
|
||||
end
|
||||
|
||||
private
|
||||
def set_aasm_current_state_with_persistence(state)
|
||||
save_success = true
|
||||
if self.respond_to?(:aasm_write_state) || self.private_methods.include?('aasm_write_state')
|
||||
save_success = aasm_write_state(state)
|
||||
end
|
||||
self.aasm_current_state = state if save_success
|
||||
|
||||
save_success
|
||||
end
|
||||
|
||||
def aasm_current_state=(state)
|
||||
if self.respond_to?(:aasm_write_state_without_persistence) || self.private_methods.include?('aasm_write_state_without_persistence')
|
||||
aasm_write_state_without_persistence(state)
|
||||
end
|
||||
@aasm_current_state = state
|
||||
end
|
||||
|
||||
def aasm_determine_state_name(state)
|
||||
case state
|
||||
when Symbol, String
|
||||
state
|
||||
when Proc
|
||||
state.call(self)
|
||||
else
|
||||
raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_state_object_for_state(name)
|
||||
obj = self.class.aasm_states.find {|s| s == name}
|
||||
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
|
||||
obj
|
||||
end
|
||||
|
||||
def aasm_fire_event(name, persist, *args)
|
||||
old_state = aasm_state_object_for_state(aasm_current_state)
|
||||
event = self.class.aasm_events[name]
|
||||
|
||||
old_state.call_action(:exit, self)
|
||||
|
||||
# new event before callback
|
||||
event.call_action(:before, self)
|
||||
|
||||
new_state_name = event.fire(self, *args)
|
||||
|
||||
unless new_state_name.nil?
|
||||
new_state = aasm_state_object_for_state(new_state_name)
|
||||
|
||||
# new before_ callbacks
|
||||
old_state.call_action(:before_exit, self)
|
||||
new_state.call_action(:before_enter, self)
|
||||
|
||||
new_state.call_action(:enter, self)
|
||||
|
||||
persist_successful = true
|
||||
if persist
|
||||
persist_successful = set_aasm_current_state_with_persistence(new_state_name)
|
||||
event.execute_success_callback(self) if persist_successful
|
||||
else
|
||||
self.aasm_current_state = new_state_name
|
||||
end
|
||||
|
||||
if persist_successful
|
||||
old_state.call_action(:after_exit, self)
|
||||
new_state.call_action(:after_enter, self)
|
||||
event.call_action(:after, self)
|
||||
|
||||
self.aasm_event_fired(name, old_state.name, self.aasm_current_state) if self.respond_to?(:aasm_event_fired)
|
||||
else
|
||||
self.aasm_event_failed(name, old_state.name) if self.respond_to?(:aasm_event_failed)
|
||||
end
|
||||
|
||||
persist_successful
|
||||
else
|
||||
if self.respond_to?(:aasm_event_failed)
|
||||
self.aasm_event_failed(name, old_state.name)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,12 +3,13 @@ require File.join(File.dirname(__FILE__), 'state_transition')
|
|||
module AASM
|
||||
module SupportingClasses
|
||||
class Event
|
||||
attr_reader :name, :success
|
||||
attr_reader :name, :success, :options
|
||||
|
||||
def initialize(name, options = {}, &block)
|
||||
@name = name
|
||||
@success = options[:success]
|
||||
@transitions = []
|
||||
@options = options
|
||||
instance_eval(&block) if block
|
||||
end
|
||||
|
||||
|
@ -31,18 +32,39 @@ module AASM
|
|||
def transitions_from_state?(state)
|
||||
@transitions.any? { |t| t.from == state }
|
||||
end
|
||||
|
||||
def execute_success_callback(obj)
|
||||
case success
|
||||
|
||||
def transitions_from_state(state)
|
||||
@transitions.select { |t| t.from == state }
|
||||
end
|
||||
|
||||
def execute_success_callback(obj, success = nil)
|
||||
callback = success || @success
|
||||
case(callback)
|
||||
when String, Symbol
|
||||
obj.send(success)
|
||||
when Array
|
||||
success.each { |meth| obj.send(meth) }
|
||||
obj.send(callback)
|
||||
when Proc
|
||||
success.call(obj)
|
||||
callback.call(obj)
|
||||
when Array
|
||||
callback.each{|meth|self.execute_success_callback(obj, meth)}
|
||||
end
|
||||
end
|
||||
|
||||
def call_action(action, record)
|
||||
action = @options[action]
|
||||
case action
|
||||
when Symbol, String
|
||||
record.send(action)
|
||||
when Proc
|
||||
action.call(record)
|
||||
when Array
|
||||
action.each { |a| record.send(a) }
|
||||
end
|
||||
end
|
||||
|
||||
def all_transitions
|
||||
@transitions
|
||||
end
|
||||
|
||||
private
|
||||
def transitions(trans_opts)
|
||||
Array(trans_opts[:from]).each do |s|
|
|
@ -1,12 +1,12 @@
|
|||
module AASM
|
||||
module Persistence
|
||||
|
||||
|
||||
# Checks to see this class or any of it's superclasses inherit from
|
||||
# ActiveRecord::Base and if so includes ActiveRecordPersistence
|
||||
def self.set_persistence(base)
|
||||
# Use a fancier auto-loading thingy, perhaps. When there are more persistence engines.
|
||||
hierarchy = base.ancestors.map {|klass| klass.to_s}
|
||||
|
||||
|
||||
if hierarchy.include?("ActiveRecord::Base")
|
||||
require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
|
||||
base.send(:include, AASM::Persistence::ActiveRecordPersistence)
|
|
@ -43,8 +43,10 @@ module AASM
|
|||
|
||||
base.class_eval do
|
||||
class << self
|
||||
alias_method :aasm_state_without_named_scope, :aasm_state
|
||||
alias_method :aasm_state, :aasm_state_with_named_scope
|
||||
unless method_defined?(:aasm_state_without_named_scope)
|
||||
alias_method :aasm_state_without_named_scope, :aasm_state
|
||||
alias_method :aasm_state, :aasm_state_with_named_scope
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -191,7 +193,7 @@ module AASM
|
|||
old_value = read_attribute(self.class.aasm_column)
|
||||
write_attribute(self.class.aasm_column, state.to_s)
|
||||
|
||||
unless self.save
|
||||
unless self.save(false)
|
||||
write_attribute(self.class.aasm_column, old_value)
|
||||
return false
|
||||
end
|
||||
|
@ -228,7 +230,7 @@ module AASM
|
|||
# This allows for nil aasm states - be sure to add validation to your model
|
||||
def aasm_read_state
|
||||
if new_record?
|
||||
send(self.class.aasm_column).blank? ? self.class.aasm_initial_state : send(self.class.aasm_column).to_sym
|
||||
send(self.class.aasm_column).blank? ? aasm_determine_state_name(self.class.aasm_initial_state) : send(self.class.aasm_column).to_sym
|
||||
else
|
||||
send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym
|
||||
end
|
||||
|
@ -238,7 +240,7 @@ module AASM
|
|||
module NamedScopeMethods
|
||||
def aasm_state_with_named_scope name, options = {}
|
||||
aasm_state_without_named_scope name, options
|
||||
self.named_scope name, :conditions => {self.aasm_column => name.to_s} unless self.respond_to?(name)
|
||||
self.named_scope name, :conditions => { "#{table_name}.#{self.aasm_column}" => name.to_s} unless self.respond_to?(name)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,6 +22,8 @@ module AASM
|
|||
record.send(action)
|
||||
when Proc
|
||||
action.call(record)
|
||||
when Array
|
||||
action.each { |a| record.send(a) }
|
||||
end
|
||||
end
|
||||
|
|
@ -10,10 +10,10 @@ module AASM
|
|||
val = args.pop
|
||||
(@machines ||= {})[args] = val
|
||||
end
|
||||
|
||||
|
||||
attr_accessor :states, :events, :initial_state, :config
|
||||
attr_reader :name
|
||||
|
||||
|
||||
def initialize(name)
|
||||
@name = name
|
||||
@initial_state = nil
|
|
@ -1,3 +1,6 @@
|
|||
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
||||
require File.join(File.dirname(__FILE__), 'conversation')
|
||||
|
||||
describe Conversation, 'description' do
|
||||
it '.aasm_states should contain all of the states' do
|
||||
Conversation.aasm_states.should == [:needs_attention, :read, :closed, :awaiting_response, :junk]
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
require File.join(File.dirname(__FILE__), '..', 'aasm')
|
||||
require 'spec'
|
||||
require File.join(File.dirname(__FILE__), '..', 'lib', 'aasm', 'aasm')
|
||||
|
|
|
@ -27,6 +27,11 @@ class Foo
|
|||
end
|
||||
end
|
||||
|
||||
class FooTwo < Foo
|
||||
include AASM
|
||||
aasm_state :foo
|
||||
end
|
||||
|
||||
class Bar
|
||||
include AASM
|
||||
|
||||
|
@ -41,6 +46,17 @@ end
|
|||
class Baz < Bar
|
||||
end
|
||||
|
||||
class Banker
|
||||
include AASM
|
||||
aasm_initial_state Proc.new { |banker| banker.rich? ? :retired : :selling_bad_mortgages }
|
||||
aasm_state :retired
|
||||
aasm_state :selling_bad_mortgages
|
||||
RICH = 1_000_000
|
||||
attr_accessor :balance
|
||||
def initialize(balance = 0); self.balance = balance; end
|
||||
def rich?; self.balance >= RICH; end
|
||||
end
|
||||
|
||||
|
||||
describe AASM, '- class level definitions' do
|
||||
it 'should define a class level aasm_initial_state() method on its including class' do
|
||||
|
@ -71,20 +87,14 @@ end
|
|||
|
||||
|
||||
describe AASM, '- subclassing' do
|
||||
before(:each) do
|
||||
@parent = Class.new do
|
||||
include AASM
|
||||
it 'should have the parent states' do
|
||||
Foo.aasm_states.each do |state|
|
||||
FooTwo.aasm_states.should include(state)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should invoke the original inherited callback' do
|
||||
@parent.should_receive(:inherited)
|
||||
Class.new(@parent)
|
||||
end
|
||||
|
||||
it 'should have a unique states hash' do
|
||||
child = Class.new(@parent)
|
||||
child.aasm_states.equal?(@parent.aasm_states).should be_false
|
||||
|
||||
it 'should not add the child states to the parent machine' do
|
||||
Foo.aasm_states.should_not include(:foo)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -130,6 +140,11 @@ describe AASM, '- initial states' do
|
|||
it 'should use the first state defined if no initial state is given' do
|
||||
@bar.aasm_current_state.should == :read
|
||||
end
|
||||
|
||||
it 'should determine initial state from the Proc results' do
|
||||
Banker.new(Banker::RICH - 1).aasm_current_state.should == :selling_bad_mortgages
|
||||
Banker.new(Banker::RICH + 1).aasm_current_state.should == :retired
|
||||
end
|
||||
end
|
||||
|
||||
describe AASM, '- event firing with persistence' do
|
||||
|
@ -255,66 +270,52 @@ describe AASM, '- getting events for a state' do
|
|||
end
|
||||
|
||||
describe AASM, '- event callbacks' do
|
||||
it 'should call aasm_event_fired if defined and successful for bang fire' do
|
||||
foo = Foo.new
|
||||
def foo.aasm_event_fired(from, to)
|
||||
describe "with aasm_event_fired defined" do
|
||||
before do
|
||||
@foo = Foo.new
|
||||
def @foo.aasm_event_fired(event, from, to)
|
||||
end
|
||||
end
|
||||
|
||||
foo.should_receive(:aasm_event_fired)
|
||||
it 'should call it for successful bang fire' do
|
||||
@foo.should_receive(:aasm_event_fired).with(:close, :open, :closed)
|
||||
@foo.close!
|
||||
end
|
||||
|
||||
foo.close!
|
||||
it 'should call it for successful non-bang fire' do
|
||||
@foo.should_receive(:aasm_event_fired)
|
||||
@foo.close
|
||||
end
|
||||
|
||||
it 'should not call it for failing bang fire' do
|
||||
@foo.stub!(:set_aasm_current_state_with_persistence).and_return(false)
|
||||
@foo.should_not_receive(:aasm_event_fired)
|
||||
@foo.close!
|
||||
end
|
||||
end
|
||||
|
||||
it 'should not call aasm_event_fired if defined but persist fails for bang fire' do
|
||||
foo = Foo.new
|
||||
def foo.aasm_event_fired(from, to)
|
||||
end
|
||||
foo.stub!(:set_aasm_current_state_with_persistence).and_return(false)
|
||||
|
||||
foo.should_not_receive(:aasm_event_fired)
|
||||
|
||||
foo.close!
|
||||
end
|
||||
|
||||
it 'should not call aasm_event_failed if defined and persist fails for bang fire' do
|
||||
foo = Foo.new
|
||||
def foo.aasm_event_failed(from, to)
|
||||
end
|
||||
foo.stub!(:set_aasm_current_state_with_persistence).and_return(false)
|
||||
|
||||
foo.should_receive(:aasm_event_failed)
|
||||
|
||||
foo.close!
|
||||
end
|
||||
|
||||
it 'should call aasm_event_fired if defined and successful for non-bang fire' do
|
||||
foo = Foo.new
|
||||
def foo.aasm_event_fired(from, to)
|
||||
describe "with aasm_event_failed defined" do
|
||||
before do
|
||||
@foo = Foo.new
|
||||
def @foo.aasm_event_failed(event, from)
|
||||
end
|
||||
end
|
||||
|
||||
foo.should_receive(:aasm_event_fired)
|
||||
|
||||
foo.close
|
||||
end
|
||||
|
||||
it 'should call aasm_event_failed if defined and transition failed for bang fire' do
|
||||
foo = Foo.new
|
||||
def foo.aasm_event_failed(event)
|
||||
it 'should call it when transition failed for bang fire' do
|
||||
@foo.should_receive(:aasm_event_failed).with(:null, :open)
|
||||
@foo.null!
|
||||
end
|
||||
|
||||
foo.should_receive(:aasm_event_failed)
|
||||
|
||||
foo.null!
|
||||
end
|
||||
|
||||
it 'should call aasm_event_failed if defined and transition failed for non-bang fire' do
|
||||
foo = Foo.new
|
||||
def foo.aasm_event_failed(event)
|
||||
it 'should call it when transition failed for non-bang fire' do
|
||||
@foo.should_receive(:aasm_event_failed).with(:null, :open)
|
||||
@foo.null
|
||||
end
|
||||
|
||||
foo.should_receive(:aasm_event_failed)
|
||||
|
||||
foo.null
|
||||
it 'should not call it if persist fails for bang fire' do
|
||||
@foo.stub!(:set_aasm_current_state_with_persistence).and_return(false)
|
||||
@foo.should_receive(:aasm_event_failed)
|
||||
@foo.close!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -51,6 +51,14 @@ begin
|
|||
class Beaver < June
|
||||
end
|
||||
|
||||
class Thief < ActiveRecord::Base
|
||||
include AASM
|
||||
aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed }
|
||||
aasm_state :rich
|
||||
aasm_state :jailed
|
||||
attr_accessor :skilled, :aasm_state
|
||||
end
|
||||
|
||||
describe "aasm model", :shared => true do
|
||||
it "should include AASM::Persistence::ActiveRecordPersistence" do
|
||||
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
|
||||
|
@ -220,6 +228,21 @@ begin
|
|||
end
|
||||
end
|
||||
|
||||
describe 'Thieves' do
|
||||
before(:each) do
|
||||
connection = mock(Connection, :columns => [])
|
||||
Thief.stub!(:connection).and_return(connection)
|
||||
end
|
||||
|
||||
it 'should be rich if they\'re skilled' do
|
||||
Thief.new(:skilled => true).aasm_current_state.should == :rich
|
||||
end
|
||||
|
||||
it 'should be jailed if they\'re unskilled' do
|
||||
Thief.new(:skilled => false).aasm_current_state.should == :jailed
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: figure out how to test ActiveRecord reload! without a database
|
||||
|
||||
rescue LoadError => e
|
||||
|
|
79
spec/unit/before_after_callbacks_spec.rb
Normal file
79
spec/unit/before_after_callbacks_spec.rb
Normal file
|
@ -0,0 +1,79 @@
|
|||
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
||||
|
||||
class Foo2
|
||||
include AASM
|
||||
aasm_initial_state :open
|
||||
aasm_state :open,
|
||||
:before_enter => :before_enter_open,
|
||||
:before_exit => :before_exit_open,
|
||||
:after_enter => :after_enter_open,
|
||||
:after_exit => :after_exit_open
|
||||
aasm_state :closed,
|
||||
:before_enter => :before_enter_closed,
|
||||
:before_exit => :before_exit_closed,
|
||||
:after_enter => :after_enter_closed,
|
||||
:after_exit => :after_exit_closed
|
||||
|
||||
aasm_event :close, :before => :before, :after => :after do
|
||||
transitions :to => :closed, :from => [:open]
|
||||
end
|
||||
|
||||
aasm_event :open, :before => :before, :after => :after do
|
||||
transitions :to => :open, :from => :closed
|
||||
end
|
||||
|
||||
def before_enter_open
|
||||
end
|
||||
def before_exit_open
|
||||
end
|
||||
def after_enter_open
|
||||
end
|
||||
def after_exit_open
|
||||
end
|
||||
|
||||
def before_enter_closed
|
||||
end
|
||||
def before_exit_closed
|
||||
end
|
||||
def after_enter_closed
|
||||
end
|
||||
def after_exit_closed
|
||||
end
|
||||
|
||||
def before
|
||||
end
|
||||
def after
|
||||
end
|
||||
end
|
||||
|
||||
describe Foo2, '- new callbacks' do
|
||||
before(:each) do
|
||||
@foo = Foo2.new
|
||||
end
|
||||
|
||||
it "should get close callbacks" do
|
||||
@foo.should_receive(:before).once.ordered
|
||||
@foo.should_receive(:before_exit_open).once.ordered # these should be before the state changes
|
||||
@foo.should_receive(:before_enter_closed).once.ordered
|
||||
@foo.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
|
||||
@foo.should_receive(:after_exit_open).once.ordered # these should be after the state changes
|
||||
@foo.should_receive(:after_enter_closed).once.ordered
|
||||
@foo.should_receive(:after).once.ordered
|
||||
|
||||
@foo.close!
|
||||
end
|
||||
|
||||
it "should get open callbacks" do
|
||||
@foo.close!
|
||||
|
||||
@foo.should_receive(:before).once.ordered
|
||||
@foo.should_receive(:before_exit_closed).once.ordered # these should be before the state changes
|
||||
@foo.should_receive(:before_enter_open).once.ordered
|
||||
@foo.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
|
||||
@foo.should_receive(:after_exit_closed).once.ordered # these should be after the state changes
|
||||
@foo.should_receive(:after_enter_open).once.ordered
|
||||
@foo.should_receive(:after).once.ordered
|
||||
|
||||
@foo.open!
|
||||
end
|
||||
end
|
|
@ -98,6 +98,20 @@ describe AASM::SupportingClasses::Event, 'when executing the success callback' d
|
|||
model.with_array!
|
||||
end
|
||||
|
||||
it "should call each success callback if passed an array of strings and/or symbols and/or procs" do
|
||||
ThisNameBetterNotBeInUse.instance_eval {
|
||||
aasm_event :with_array_including_procs, :success => [:success_callback1, 'success_callback2', lambda { |obj| obj.proc_success_callback }] do
|
||||
transitions :to => :array, :from => [:initial]
|
||||
end
|
||||
}
|
||||
|
||||
model = ThisNameBetterNotBeInUse.new
|
||||
model.should_receive(:success_callback1)
|
||||
model.should_receive(:success_callback2)
|
||||
model.should_receive(:proc_success_callback)
|
||||
model.with_array_including_procs!
|
||||
end
|
||||
|
||||
it "should call the success callback if it's a proc" do
|
||||
ThisNameBetterNotBeInUse.instance_eval {
|
||||
aasm_event :with_proc, :success => lambda { |obj| obj.proc_success_callback } do
|
||||
|
|
|
@ -16,10 +16,10 @@ describe AASM::SupportingClasses::State do
|
|||
|
||||
state.name.should == :astate
|
||||
end
|
||||
|
||||
|
||||
it 'should set the options and expose them as options' do
|
||||
state = new_state
|
||||
|
||||
|
||||
state.options.should == @options
|
||||
end
|
||||
|
||||
|
@ -56,7 +56,7 @@ describe AASM::SupportingClasses::State do
|
|||
|
||||
record = mock('record')
|
||||
record.should_receive(:foobar)
|
||||
|
||||
|
||||
state.call_action(:entering, record)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,7 +31,7 @@ describe AASM::SupportingClasses::StateTransition do
|
|||
|
||||
st.should_not == obj
|
||||
end
|
||||
|
||||
|
||||
it 'should fail equality check if to are not the same' do
|
||||
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
|
||||
st = AASM::SupportingClasses::StateTransition.new(opts)
|
||||
|
@ -58,7 +58,7 @@ describe AASM::SupportingClasses::StateTransition, '- when performing guard chec
|
|||
|
||||
obj = mock('object')
|
||||
obj.should_receive(:test)
|
||||
|
||||
|
||||
st.perform(obj)
|
||||
end
|
||||
|
||||
|
@ -68,7 +68,7 @@ describe AASM::SupportingClasses::StateTransition, '- when performing guard chec
|
|||
|
||||
obj = mock('object')
|
||||
obj.should_receive(:test)
|
||||
|
||||
|
||||
st.perform(obj)
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue