Repository: rubyist/aasm
Branch: master
Commit: a4bc27cff0d7
Files: 70
Total size: 106.7 KB
Directory structure:
gitextract_cxl63xfk/
├── .document
├── .gitignore
├── .travis.yml
├── API
├── CHANGELOG.md
├── Gemfile
├── HOWTO
├── LICENSE
├── README.md
├── Rakefile
├── aasm.gemspec
├── lib/
│ ├── aasm/
│ │ ├── aasm.rb
│ │ ├── base.rb
│ │ ├── deprecated/
│ │ │ └── aasm.rb
│ │ ├── errors.rb
│ │ ├── event.rb
│ │ ├── instance_base.rb
│ │ ├── localizer.rb
│ │ ├── persistence/
│ │ │ ├── active_record_persistence.rb
│ │ │ ├── base.rb
│ │ │ └── mongoid_persistence.rb
│ │ ├── persistence.rb
│ │ ├── state.rb
│ │ ├── state_machine.rb
│ │ ├── transition.rb
│ │ └── version.rb
│ └── aasm.rb
└── spec/
├── database.yml
├── en.yml
├── en_deprecated_style.yml
├── models/
│ ├── active_record/
│ │ └── api.rb
│ ├── argument.rb
│ ├── auth_machine.rb
│ ├── bar.rb
│ ├── callback_new_dsl.rb
│ ├── callback_old_dsl.rb
│ ├── conversation.rb
│ ├── father.rb
│ ├── foo.rb
│ ├── invalid_persistor.rb
│ ├── mongoid/
│ │ ├── simple_mongoid.rb
│ │ └── simple_new_dsl_mongoid.rb
│ ├── not_auto_loaded/
│ │ └── process.rb
│ ├── parametrised_event.rb
│ ├── persistence.rb
│ ├── process_with_new_dsl.rb
│ ├── silencer.rb
│ ├── son.rb
│ ├── sub_classing.rb
│ ├── this_name_better_not_be_in_use.rb
│ ├── transactor.rb
│ ├── validator.rb
│ └── worker.rb
├── schema.rb
├── spec_helper.rb
└── unit/
├── api_spec.rb
├── callbacks_spec.rb
├── complex_example_spec.rb
├── event_spec.rb
├── initial_state_spec.rb
├── inspection_spec.rb
├── localizer_spec.rb
├── memory_leak_spec.rb
├── new_dsl_spec.rb
├── persistence/
│ ├── active_record_persistence_spec.rb
│ └── mongoid_persistance_spec.rb
├── simple_example_spec.rb
├── state_spec.rb
├── subclassing_spec.rb
└── transition_spec.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .document
================================================
README.rdoc
lib/**/*.rb
bin/*
features/**/*.feature
-
LICENSE
================================================
FILE: .gitignore
================================================
*.sw?
*~
.DS_Store
.idea
coverage
pkg
rdoc
Gemfile.lock
spec/debug.log
spec/*.db
TODO
.rvmrc
alto
================================================
FILE: .travis.yml
================================================
language: ruby
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- 2.0.0
# - jruby-18mode # JRuby in 1.8 mode
# - jruby-19mode # JRuby in 1.9 mode
- rbx-18mode
- rbx-19mode
services: mongodb
================================================
FILE: API
================================================
Overwrite method to read the current state. Used to provide another storage mechanism,
different from the standard Rails read_attribute method.
class MyClass
include AASM
def aasm_read_state
# retrieve the current state manually
end
end
Overwrite method to write the current state (and actually persist it). Used to provide
another storage mechanism, different from the standard Rails write_attribute method.
class MyClass
include AASM
def aasm_write_state
# store and persist the current state manually
end
end
Overwrite method to write the current state (without persisting it).
class MyClass
include AASM
def aasm_write_state_without_persistence
# store the current state manually
end
end
================================================
FILE: CHANGELOG.md
================================================
# CHANGELOG
## 3.0.19
* fixed deprecation warning with *Rails 4* (`Relation#update_all` with conditions is deprecated)
* fixing [issue #69](https://github.com/aasm/aasm/issues/69) ( *ActiveRecord* scopes are not chainable)
## 3.0.18
* fixing [issue #66](https://github.com/aasm/aasm/issues/66) (state methods not reflecting the current state)
## 3.0.17
* supporting instance level inspection for states (including permissible state, see [issue #54](https://github.com/aasm/aasm/issues/54))
* added autocreation of constants for each state ([@jherdman](https://github.com/jherdman))
## 3.0.16
* added autocreation of state scopes for Mongoid (thanks to [@jonnyshields](https://github.com/johnnyshields))
## 3.0.15
* added support for localized state names (on a class level, like `Record.aasm.states.map(&:localized_name)`)
## 3.0.14
* supporting event inspection for to-states transitions (`Event#transitions_to_state?`)
## 3.0.13
* supporting *ActiveRecord* transactions when firing an event
## 3.0.12
* `aasm_from_states_for_state` now supports to filter for specific transition
## 3.0.11
* added class method `aasm_from_states_for_state` to retrieve all from states (regarding transitions) for a given state
## 3.0.10
* added support for transitions from all other states (thanks to [@swrobel](https://github.com/swrobel))
## 3.0.9
* guard checks (e.g. `may_edit?`) now support guard parameters as well
## 3.0.8
* fixed issue with generating docs using yard
## 3.0.7
* removed deprecation warning when localizing aasm state names (look at [issue #38](https://github.com/rubyist/aasm/issues/38) for details)
## 3.0.6
* bugfix: if configured to skip validation the code does not validate anymore
## 3.0.5
* bugfix: get rid of error with old rubygems versions
## 3.0.4
* bugfix: Subclasses of aasm-enabled classes don't lose settings anymore (thanks to codez)
## 3.0.3
* bugfix: ActiveRecord scopes are generated when using the new DSL
## 3.0.2
* ActiveRecord persistence can ignore validation when trying to save invalid models
## 3.0.1
* added support for Mongoid (Thanks, Michał Taberski)
## 3.0.0
* switched documentation to the new DSL
* whiny transactions: by default, raise an exception if an event transition is not possible
* you may disable whiny transactions
## 2.4.0
* supporting new DSL (which is much shorter)
## 2.3.1
* bugfix: avoid naming conflict with i18n
## 2.3.0
* supporting i18n
* supporting regular expressions for hash values and strings
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
gemspec
================================================
FILE: HOWTO
================================================
How to
1. Run tests for Mongoid
Start MongoDB
$> mongod
Run the specs
$> rspec spec/unit/persistence/mongoid_persistance_spec.rb
================================================
FILE: LICENSE
================================================
Copyright (c) 2006-2012 Scott Barron
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# AASM - Ruby state machines [](http://travis-ci.org/aasm/aasm) [](https://codeclimate.com/github/aasm/aasm) [](https://coveralls.io/r/aasm/aasm)
This package contains AASM, a library for adding finite state machines to Ruby classes.
AASM started as the *acts_as_state_machine* plugin but has evolved into a more generic library
that no longer targets only ActiveRecord models. It currently provides adapters for
[ActiveRecord](http://api.rubyonrails.org/classes/ActiveRecord/Base.html) and
[Mongoid](http://mongoid.org/), but it can be used for any Ruby class, no matter what
parent class it has (if any).
## Usage
Adding a state machine is as simple as including the AASM module and start defining
**states** and **events** together with their **transitions**:
```ruby
class Job
include AASM
aasm do
state :sleeping, :initial => true
state :running
state :cleaning
event :run do
transitions :from => :sleeping, :to => :running
end
event :clean do
transitions :from => :running, :to => :cleaning
end
event :sleep do
transitions :from => [:running, :cleaning], :to => :sleeping
end
end
end
```
This provides you with a couple of public methods for instances of the class `Job`:
```ruby
job = Job.new
job.sleeping? # => true
job.may_run? # => true
job.run
job.running? # => true
job.sleeping? # => false
job.may_run? # => false
job.run # => raises AASM::InvalidTransition
```
If you don't like exceptions and prefer a simple `true` or `false` as response, tell
AASM not to be *whiny*:
```ruby
class Job
...
aasm :whiny_transitions => false do
...
end
end
job.running? # => true
job.may_run? # => false
job.run # => false
```
### Callbacks
You can define a number of callbacks for your transitions. These methods will be
called, when certain criteria are met, like entering a particular state:
```ruby
class Job
include AASM
aasm do
state :sleeping, :initial => true, :before_enter => :do_something
state :running
event :run, :after => Proc.new { |user| notify_somebody(user) } do
transitions :from => :sleeping, :to => :running, :on_transition => Proc.new {|obj, *args| obj.set_process(*args) }
end
event :sleep do
after do
...
end
error do |e|
...
end
transitions :from => :running, :to => :sleeping
end
end
def set_process(name)
...
end
def do_something
...
end
def notify_somebody(user)
...
end
end
```
In this case `do_something` is called before actually entering the state `sleeping`,
while `notify_somebody` is called after the transition `run` (from `sleeping` to `running`)
is finished.
Here you can see a list of all possible callbacks, together with their order of calling:
```ruby
event:before
previous_state:before_exit
new_state:before_enter
...update state...
previous_state:after_exit
new_state:after_enter
event:after
```
Also, you can pass parameters to events:
```ruby
job = Job.new
job.run(:running, :defragmentation)
```
In this case the `set_process` would be called with `:defagmentation` argument.
In case of an error during the event processing the error is rescued and passed to `:error`
callback, which can handle it or re-raise it for further propagation.
### Guards
Let's assume you want to allow particular transitions only if a defined condition is
given. For this you can set up a guard per transition, which will run before actually
running the transition. If the guard returns `false` the transition will be
denied (raising `AASM::InvalidTransition` or returning `false` itself):
```ruby
class Job
include AASM
aasm do
state :sleeping, :initial => true
state :running
state :cleaning
event :run do
transitions :from => :sleeping, :to => :running
end
event :clean do
transitions :from => :running, :to => :cleaning
end
event :sleep do
transitions :from => :running, :to => :sleeping, :guard => :cleaning_needed?
end
end
def cleaning_needed?
false
end
end
job = Job.new
job.run
job.may_sleep? # => false
job.sleep # => raises AASM::InvalidTransition
```
### ActiveRecord
AASM comes with support for ActiveRecord and allows automatical persisting of the object's
state in the database.
```ruby
class Job < ActiveRecord::Base
include AASM
aasm do # default column: aasm_state
state :sleeping, :initial => true
state :running
event :run do
transitions :from => :sleeping, :to => :running
end
event :sleep do
transitions :from => :running, :to => :sleeping
end
end
end
```
You can tell AASM to auto-save the object or leave it unsaved
```ruby
job = Job.new
job.run # not saved
job.run! # saved
```
Saving includes running all validations on the `Job` class. If you want make sure
the state gets saved without running validations (and thereby maybe persisting an
invalid object state), simply tell AASM to skip the validations:
```ruby
class Job < ActiveRecord::Base
include AASM
aasm :skip_validation_on_save => true do
state :sleeping, :initial => true
state :running
event :run do
transitions :from => :sleeping, :to => :running
end
event :sleep do
transitions :from => :running, :to => :sleeping
end
end
end
```
### Automatic Scopes
AASM will automatically create scope methods for each state in the model.
```ruby
class Job < ActiveRecord::Base
include AASM
aasm do
state :sleeping, :initial => true
state :running
state :cleaning
end
def sleeping
"This method name is in already use"
end
end
```
```ruby
class JobsController < ApplicationController
def index
@running_jobs = jobs.running
@recent_cleaning_jobs = jobs.cleaning.where('created_at >= ?', 3.days.ago)
# @sleeping_jobs = jobs.sleeping #=> "This method name is in already use"
end
end
```
### Transaction support
Since version *3.0.13* AASM supports ActiveRecord transactions. So whenever a transition
callback or the state update fails, all changes to any database record are rolled back.
### Column name & migration
As a default AASM uses the column `aasm_state` to store the states. You can override
this by defining your favorite column name, using `:column` like this:
```ruby
class Job < ActiveRecord::Base
include AASM
aasm :column => 'my_state' do
...
end
end
```
Whatever column name is used, make sure to add a migration to provide this column
(of type `string`):
```ruby
class AddJobState < ActiveRecord::Migration
def self.up
add_column :jobs, :aasm_state, :string
end
def self.down
remove_column :jobs, :aasm_state
end
end
```
### <a id="inspection">Inspection
AASM supports a couple of methods to find out which states or events are provided or permissible.
Given the `Job` class from above:
```ruby
job = Job.new
job.aasm.states
=> [:sleeping, :running, :cleaning]
job.aasm.states(:permissible => true)
=> [:running]
job.run
job.aasm.states(:permissible => true)
=> [:cleaning, :sleeping]
job.aasm.events
=> [:run, :clean, :sleep]
```
## <a id="installation">Installation ##
### Manually from RubyGems.org ###
```sh
% gem install aasm
```
### Or if you are using Bundler ###
```ruby
# Gemfile
gem 'aasm'
```
### Building your own gems ###
```sh
% rake build
% sudo gem install pkg/aasm-x.y.z.gem
```
## Latest changes ##
Look at the [CHANGELOG](https://github.com/aasm/aasm/blob/master/CHANGELOG.md) for details.
## Questions? ##
Feel free to
* [create an issue on GitHub](https://github.com/aasm/aasm/issues)
* [ask a question on StackOverflow](http://stackoverflow.com) (tag with `aasm`)
* send us a tweet [@aasm](http://twitter.com/aasm)
## Authors ##
* [Scott Barron](https://github.com/rubyist)
* [Travis Tilley](https://github.com/ttilley)
* [Thorsten Böttger](http://github.com/alto)
## Warranty ##
This software is provided "as is" and without any express or
implied warranties, including, without limitation, the implied
warranties of merchantibility and fitness for a particular
purpose.
## License ##
Copyright (c) 2006-2012 Scott Barron
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: Rakefile
================================================
require 'bundler/gem_tasks'
require 'rspec/core'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |spec|
spec.pattern = FileList['spec/**/*_spec.rb']
end
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
require 'rdoc/task'
require 'aasm/version'
Rake::RDocTask.new do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "aasm #{AASM::VERSION}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
task :default => :spec
================================================
FILE: aasm.gemspec
================================================
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "aasm/version"
Gem::Specification.new do |s|
s.name = "aasm"
s.version = AASM::VERSION
s.authors = ["Scott Barron", "Scott Petersen", "Travis Tilley", "Thorsten Boettger"]
s.email = %q{scott@elitists.net, ttilley@gmail.com, aasm@mt7.de}
s.homepage = %q{https://github.com/aasm/aasm}
s.summary = %q{State machine mixin for Ruby objects}
s.description = %q{AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects.}
s.date = Time.now
s.licenses = ["MIT"]
s.add_development_dependency 'activerecord', '3.2.12'
# s.add_development_dependency 'activerecord', '4.0.0.rc1'
s.add_development_dependency 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
s.add_development_dependency 'rake'
s.add_development_dependency 'sdoc'
s.add_development_dependency 'rspec', '~> 2.0'
s.add_development_dependency 'rr'
s.add_development_dependency 'sqlite3'
s.add_development_dependency 'minitest'
# s.add_development_dependency 'debugger'
# s.add_development_dependency 'pry'
s.add_development_dependency 'ruby-debug-completion'
s.add_development_dependency 'coveralls'
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
end
================================================
FILE: lib/aasm/aasm.rb
================================================
module AASM
def self.included(base) #:nodoc:
base.extend AASM::ClassMethods
# do not overwrite existing state machines, which could have been created by
# inheritance, see class method inherited
AASM::StateMachine[base] ||= AASM::StateMachine.new('')
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
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
# TODO: maybe better: aasm.initial_state
def aasm_initial_state(set_state=nil)
if set_state
# deprecated way to set the value
AASM::StateMachine[self].initial_state = set_state
else
AASM::StateMachine[self].initial_state
end
end
# is this better?: aasm.states.name.from_states
def aasm_from_states_for_state(state, options={})
if options[:transition]
aasm.events[options[:transition]].transitions_to_state(state).flatten.map(&:from).flatten
else
aasm.events.map {|k,v| v.transitions_to_state(state)}.flatten.map(&:from).flatten
end
end
# deprecated
def aasm_initial_state=(state)
AASM::StateMachine[self].initial_state = state
end
# deprecated
def aasm_state(name, options={})
aasm.state(name, options)
end
# deprecated
def aasm_event(name, options = {}, &block)
aasm.event(name, options, &block)
end
# deprecated
def aasm_states
aasm.states
end
# deprecated
def aasm_events
aasm.events
end
# deprecated
def aasm_states_for_select
aasm.states_for_select
end
# aasm.event(:event_name).human?
def aasm_human_event_name(event) # event_name?
AASM::Localizer.new.human_event_name(self, event)
end
end # ClassMethods
def aasm
@aasm ||= AASM::InstanceBase.new(self)
end
# may be overwritten by persistence mixins
def aasm_read_state
# all the following lines behave like @current_state ||= aasm.enter_initial_state
current = aasm.instance_variable_get("@current_state")
return current if current
aasm.instance_variable_set("@current_state", aasm.enter_initial_state)
end
# may be overwritten by persistence mixins
def aasm_write_state(new_state)
true
end
# may be overwritten by persistence mixins
def aasm_write_state_without_persistence(new_state)
true
end
# deprecated
def aasm_current_state
# warn "#aasm_current_state is deprecated and will be removed in version 3.2.0; please use #aasm.state instead!"
aasm.current_state
end
# deprecated
def aasm_enter_initial_state
# warn "#aasm_enter_initial_state is deprecated and will be removed in version 3.2.0; please use #aasm.enter_initial_state instead!"
aasm.enter_initial_state
end
# deprecated
def aasm_events_for_current_state
# warn "#aasm_events_for_current_state is deprecated and will be removed in version 3.2.0; please use #aasm.events instead!"
aasm.events(aasm.current_state)
end
# deprecated
def aasm_permissible_events_for_current_state
# warn "#aasm_permissible_events_for_current_state is deprecated and will be removed in version 3.2.0; please use #aasm.permissible_events instead!"
aasm.permissible_events
end
# deprecated
def aasm_events_for_state(state_name)
# warn "#aasm_events_for_state(state_name) is deprecated and will be removed in version 3.2.0; please use #aasm.events(state_name) instead!"
aasm.events(state_name)
end
# deprecated
def aasm_human_state
# warn "#aasm_human_state is deprecated and will be removed in version 3.2.0; please use #aasm.human_state instead!"
aasm.human_state
end
private
def aasm_fire_event(event_name, options, *args)
event = self.class.aasm_events[event_name]
begin
old_state = aasm.state_object_for_name(aasm.current_state)
old_state.fire_callbacks(:exit, self)
# new event before callback
event.fire_callbacks(:before, self)
if new_state_name = event.fire(self, *args)
fired(event, old_state, new_state_name, options)
else
failed(event_name, old_state)
end
rescue StandardError => e
event.fire_callbacks(:error, self, e) || raise(e)
end
end
def fired(event, old_state, new_state_name, options)
persist = options[:persist]
new_state = aasm.state_object_for_name(new_state_name)
# new before_ callbacks
old_state.fire_callbacks(:before_exit, self)
new_state.fire_callbacks(:before_enter, self)
new_state.fire_callbacks(:enter, self)
persist_successful = true
if persist
persist_successful = aasm.set_current_state_with_persistence(new_state_name)
event.fire_callbacks(:success, self) if persist_successful
else
aasm.current_state = new_state_name
end
if persist_successful
old_state.fire_callbacks(:after_exit, self)
new_state.fire_callbacks(:after_enter, self)
event.fire_callbacks(:after, self)
self.aasm_event_fired(event.name, old_state.name, aasm.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
persist_successful
end
def failed(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, "Event '#{event_name}' cannot transition from '#{aasm.current_state}'"
else
false
end
end
end
================================================
FILE: lib/aasm/base.rb
================================================
module AASM
class Base
def initialize(clazz, options={}, &block)
@clazz = clazz
@state_machine = AASM::StateMachine[@clazz]
@state_machine.config.column = options[:column].to_sym if options[:column]
if options.key?(:whiny_transitions)
@state_machine.config.whiny_transitions = options[:whiny_transitions]
elsif @state_machine.config.whiny_transitions.nil?
@state_machine.config.whiny_transitions = true # this is the default, so let's cry
end
if options.key?(:skip_validation_on_save)
@state_machine.config.skip_validation_on_save = options[:skip_validation_on_save]
elsif @state_machine.config.skip_validation_on_save.nil?
@state_machine.config.skip_validation_on_save = false # this is the default, so don't store any new state if the model is invalid
end
end
def initial_state
@state_machine.initial_state
end
# define a state
def state(name, options={})
# @clazz.aasm_state(name, options)
@state_machine.add_state(name, @clazz, options)
@state_machine.initial_state = name if options[:initial] || !@state_machine.initial_state
@clazz.send(:define_method, "#{name.to_s}?") do
aasm.current_state == name
end
unless @clazz.const_defined?("STATE_#{name.to_s.upcase}")
@clazz.const_set("STATE_#{name.to_s.upcase}", name)
end
end
# define an event
def event(name, options={}, &block)
# @clazz.aasm_event(name, options, &block)
unless @state_machine.events.has_key?(name)
@state_machine.events[name] = AASM::Event.new(name, options, &block)
end
# an addition over standard aasm so that, before firing an event, you can ask
# may_event? and get back a boolean that tells you whether the guard method
# on the transition will let this happen.
@clazz.send(:define_method, "may_#{name.to_s}?") do |*args|
aasm.may_fire_event?(name, *args)
end
@clazz.send(:define_method, "#{name.to_s}!") do |*args|
aasm_fire_event(name, {:persist => true}, *args)
end
@clazz.send(:define_method, "#{name.to_s}") do |*args|
aasm_fire_event(name, {:persist => false}, *args)
end
end
def states
@state_machine.states
end
def events
@state_machine.events
end
def states_for_select
states.map { |state| state.for_select }
end
end
end
================================================
FILE: lib/aasm/deprecated/aasm.rb
================================================
module AASM
module ClassMethods
def human_event_name(*args)
warn "AASM.human_event_name is deprecated and will be removed in version 3.1.0; please use AASM.aasm_human_event_name instead!"
aasm_human_event_name(*args)
end
end
def human_state
warn "AASM#human_state is deprecated and will be removed in version 3.1.0; please use AASM#aasm_human_state instead!"
aasm_human_state
end
end
================================================
FILE: lib/aasm/errors.rb
================================================
module AASM
class InvalidTransition < RuntimeError; end
class UndefinedState < RuntimeError; end
end
================================================
FILE: lib/aasm/event.rb
================================================
module AASM
class Event
attr_reader :name, :options
def initialize(name, options = {}, &block)
@name = name
@transitions = []
update(options, &block)
end
# a neutered version of fire - it doesn't actually fire the event, it just
# executes the transition guards to determine if a transition is even
# an option given current conditions.
def may_fire?(obj, to_state=nil, *args)
_fire(obj, true, to_state, *args) # true indicates test firing
end
def fire(obj, to_state=nil, *args)
_fire(obj, false, to_state, *args) # false indicates this is not a test (fire!)
end
def transitions_from_state?(state)
transitions_from_state(state).any?
end
def transitions_from_state(state)
@transitions.select { |t| t.from == state }
end
def transitions_to_state?(state)
transitions_to_state(state).any?
end
def transitions_to_state(state)
@transitions.select { |t| t.to == state }
end
# deprecated
def all_transitions
# warn "Event#all_transitions is deprecated and will be removed in version 3.2.0; please use Event#transitions instead!"
transitions
end
def fire_callbacks(callback_name, record, *args)
invoke_callbacks(@options[callback_name], record, args)
end
def ==(event)
if event.is_a? Symbol
name == event
else
name == event.name
end
end
private
def update(options = {}, &block)
@options = options
if block then
instance_eval(&block)
end
self
end
# Execute if test == false, otherwise return true/false depending on whether it would fire
def _fire(obj, test, to_state=nil, *args)
result = test ? false : nil
if @transitions.map(&:from).any?
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
return result if transitions.size == 0
else
transitions = @transitions
end
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj, *args)
if test
result = true
else
result = to_state || Array(transition.to).first
transition.execute(obj, *args)
end
break
end
end
result
end
def invoke_callbacks(code, record, args)
case code
when Symbol, String
record.send(code, *args)
true
when Proc
record.instance_exec(*args, &code)
true
when Array
code.each {|a| invoke_callbacks(a, record, args)}
true
else
false
end
end
## DSL interface
def transitions(trans_opts=nil)
if trans_opts # define new transitions
# Create a separate transition for each from state to the given state
Array(trans_opts[:from]).each do |s|
@transitions << AASM::Transition.new(trans_opts.merge({:from => s.to_sym}))
end
# Create a transition if to is specified without from (transitions from ANY state)
@transitions << AASM::Transition.new(trans_opts) if @transitions.empty? && trans_opts[:to]
end
@transitions
end
[:after, :before, :error, :success].each do |callback_name|
define_method callback_name do |*args, &block|
options[callback_name] = Array(options[callback_name])
options[callback_name] << block if block
options[callback_name] += Array(args)
end
end
end
end # AASM
================================================
FILE: lib/aasm/instance_base.rb
================================================
module AASM
class InstanceBase
def initialize(instance)
@instance = instance
end
def current_state
@instance.aasm_read_state
end
def current_state=(state)
@instance.aasm_write_state_without_persistence(state)
@current_state = state
end
def enter_initial_state
state_name = determine_state_name(@instance.class.aasm_initial_state)
state_object = state_object_for_name(state_name)
state_object.fire_callbacks(:before_enter, @instance)
state_object.fire_callbacks(:enter, @instance)
self.current_state = state_name
state_object.fire_callbacks(:after_enter, @instance)
state_name
end
def human_state
AASM::Localizer.new.human_state_name(@instance.class, current_state)
end
def states(options={})
if options[:permissible]
# ugliness level 1000
transitions = @instance.class.aasm.events.values.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)}
else
@instance.class.aasm.states
end
end
# QUESTION: shouldn't events and permissible_events be the same thing?
# QUESTION: shouldn't events return objects instead of strings?
def events(state=current_state)
events = @instance.class.aasm.events.values.select {|e| e.transitions_from_state?(state) }
events.map {|e| e.name}
end
# filters the results of events_for_current_state so that only those that
# are really currently possible (given transition guards) are shown.
# QUESTION: what about events.permissible ?
def permissible_events
events.select{ |e| @instance.send(("may_" + e.to_s + "?").to_sym) }
end
def state_object_for_name(name)
obj = @instance.class.aasm.states.find {|s| s == name}
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
obj
end
def determine_state_name(state)
case state
when Symbol, String
state
when Proc
state.call(@instance)
else
raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
end
end
def may_fire_event?(name, *args)
event = @instance.class.aasm.events[name]
event.may_fire?(@instance, *args)
end
def set_current_state_with_persistence(state)
save_success = @instance.aasm_write_state(state)
self.current_state = state if save_success
save_success
end
end
end
================================================
FILE: lib/aasm/localizer.rb
================================================
module AASM
class Localizer
def human_event_name(klass, event)
checklist = ancestors_list(klass).inject([]) do |list, ancestor|
list << :"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
list
end
translate_queue(checklist) || I18n.translate(checklist.shift, :default => event.to_s.humanize)
end
def human_state_name(klass, state)
checklist = ancestors_list(klass).inject([]) do |list, ancestor|
list << item_for(klass, state, ancestor)
list << item_for(klass, state, ancestor, :old_style => true)
list
end
translate_queue(checklist) || I18n.translate(checklist.shift, :default => state.to_s.humanize)
end
private
def item_for(klass, state, ancestor, options={})
separator = options[:old_style] ? '.' : '/'
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm_column}#{separator}#{state}"
end
def translate_queue(checklist)
(0...(checklist.size-1)).each do |i|
begin
return I18n.translate(checklist.shift, :raise => true)
rescue I18n::MissingTranslationData
# that's okay
end
end
nil
end
# added for rails 2.x compatibility
def i18n_scope(klass)
klass.respond_to?(:i18n_scope) ? klass.i18n_scope : :activerecord
end
# added for rails < 3.0.3 compatibility
def i18n_klass(klass)
klass.model_name.respond_to?(:i18n_key) ? klass.model_name.i18n_key : klass.name.underscore
end
def ancestors_list(klass)
klass.ancestors.select do |ancestor|
ancestor.respond_to?(:model_name) unless ancestor == ActiveRecord::Base
end
end
end
end # AASM
================================================
FILE: lib/aasm/persistence/active_record_persistence.rb
================================================
module AASM
module Persistence
module ActiveRecordPersistence
# This method:
#
# * extends the model with ClassMethods
# * includes InstanceMethods
#
# Adds
#
# before_validation :aasm_ensure_initial_state, :on => :create
#
# As a result, it doesn't matter when you define your methods - the following 2 are equivalent
#
# class Foo < ActiveRecord::Base
# def aasm_write_state(state)
# "bar"
# end
# include AASM
# end
#
# class Foo < ActiveRecord::Base
# include AASM
# def aasm_write_state(state)
# "bar"
# end
# end
#
def self.included(base)
base.send(:include, AASM::Persistence::Base)
base.extend AASM::Persistence::ActiveRecordPersistence::ClassMethods
base.send(:include, AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
if ActiveRecord::VERSION::MAJOR >= 3
base.before_validation(:aasm_ensure_initial_state, :on => :create)
else
base.before_validation_on_create(:aasm_ensure_initial_state)
end
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
#
# foo = Foo.find(1)
# foo.aasm_current_state # => :opened
# foo.close!
# foo.aasm_current_state # => :closed
# 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_column)
write_attribute(self.class.aasm_column, state.to_s)
success = if AASM::StateMachine[self.class].config.skip_validation_on_save
self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm_column => state.to_s) == 1
else
self.save
end
unless success
write_attribute(self.class.aasm_column, old_value)
return false
end
true
end
# Writes <tt>state</tt> to the state column, but does not persist it to the database
#
# foo = Foo.find(1)
# foo.aasm_current_state # => :opened
# foo.close
# foo.aasm_current_state # => :closed
# Foo.find(1).aasm_current_state # => :opened
# foo.save
# foo.aasm_current_state # => :closed
# 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_column, state.to_s)
end
private
# Ensures that if the aasm_state column is nil and the record is new
# that the initial state gets populated before validation on create
#
# foo = Foo.new
# foo.aasm_state # => nil
# foo.valid?
# foo.aasm_state # => "open" (where :open is the initial state)
#
#
# foo = Foo.find(:first)
# foo.aasm_state # => 1
# foo.aasm_state = nil
# foo.valid?
# foo.aasm_state # => nil
#
def aasm_ensure_initial_state
aasm.enter_initial_state if send(self.class.aasm_column).blank?
end
def aasm_fire_event(name, options, *args)
transaction do
super
end
end
end # InstanceMethods
end
end
end
================================================
FILE: lib/aasm/persistence/base.rb
================================================
module AASM
module Persistence
module Base
def self.included(base) #:nodoc:
base.extend ClassMethods
end
# Returns the value of the aasm_column - called from <tt>aasm.current_state</tt>
#
# If it's a new record, and the aasm state column is blank it returns the initial state
# (example provided here for ActiveRecord, but it's true for Mongoid as well):
#
# class Foo < ActiveRecord::Base
# include AASM
# aasm :column => :status do
# state :opened
# state :closed
# end
# end
#
# foo = Foo.new
# foo.current_state # => :opened
# foo.close
# foo.current_state # => :closed
#
# foo = Foo.find(1)
# foo.current_state # => :opened
# foo.aasm_state = nil
# foo.current_state # => nil
#
# 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_column)
if new_record?
state.blank? ? aasm.determine_state_name(self.class.aasm_initial_state) : state.to_sym
else
state.nil? ? nil : state.to_sym
end
end
module ClassMethods
# Maps to the aasm_column in the database. Defaults to "aasm_state". You can write
# (example provided here for ActiveRecord, but it's true for Mongoid as well):
#
# create_table :foos do |t|
# t.string :name
# t.string :aasm_state
# end
#
# class Foo < ActiveRecord::Base
# include AASM
# end
#
# OR:
#
# create_table :foos do |t|
# t.string :name
# t.string :status
# end
#
# class Foo < ActiveRecord::Base
# include AASM
# aasm_column :status
# end
#
# This method is both a getter and a setter
def aasm_column(column_name=nil)
if column_name
AASM::StateMachine[self].config.column = column_name.to_sym
# @aasm_column = column_name.to_sym
else
AASM::StateMachine[self].config.column ||= :aasm_state
# @aasm_column ||= :aasm_state
end
# @aasm_column
AASM::StateMachine[self].config.column
end
end # ClassMethods
end # Base
end # Persistence
class Base
# make sure to create a (named) scope for each state
def state_with_scope(name, *args)
state_without_scope(name, *args)
unless @clazz.respond_to?(name)
if @clazz.ancestors.map {|klass| klass.to_s}.include?("ActiveRecord::Base")
conditions = {"#{@clazz.table_name}.#{@clazz.aasm_column}" => name.to_s}
if ActiveRecord::VERSION::MAJOR >= 4
@clazz.class_eval do
scope name, lambda { where(conditions) }
end
elsif ActiveRecord::VERSION::MAJOR >= 3
@clazz.class_eval do
scope name, where(conditions)
end
else
@clazz.class_eval do
named_scope name, :conditions => conditions
end
end
elsif @clazz.ancestors.map {|klass| klass.to_s}.include?("Mongoid::Document")
scope_options = lambda { @clazz.send(:where, {@clazz.aasm_column.to_sym => name.to_s}) }
@clazz.send(:scope, name, scope_options)
end
end
end
alias_method :state_without_scope, :state
alias_method :state, :state_with_scope
end # Base
end # AASM
================================================
FILE: lib/aasm/persistence/mongoid_persistence.rb
================================================
module AASM
module Persistence
module MongoidPersistence
# This method:
#
# * extends the model with ClassMethods
# * includes InstanceMethods
#
# Adds
#
# before_validation :aasm_ensure_initial_state
#
# As a result, it doesn't matter when you define your methods - the following 2 are equivalent
#
# class Foo
# include Mongoid::Document
# def aasm_write_state(state)
# "bar"
# end
# include AASM
# end
#
# class Foo
# include Mongoid::Document
# include AASM
# def aasm_write_state(state)
# "bar"
# end
# end
#
def self.included(base)
base.send(:include, AASM::Persistence::Base)
base.extend AASM::Persistence::MongoidPersistence::ClassMethods
base.send(:include, AASM::Persistence::MongoidPersistence::InstanceMethods)
# Mongoid's Validatable gem dependency goes not have a before_validation_on_xxx hook yet.
# base.before_validation_on_create :aasm_ensure_initial_state
base.before_validation :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_column.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
# using update_attribute (which bypasses validation)
#
# foo = Foo.find(1)
# foo.aasm_current_state # => :opened
# foo.close!
# foo.aasm_current_state # => :closed
# 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_column)
write_attribute(self.class.aasm_column, state.to_s)
unless self.save(:validate => false)
write_attribute(self.class.aasm_column, old_value)
return false
end
true
end
# Writes <tt>state</tt> to the state column, but does not persist it to the database
#
# foo = Foo.find(1)
# foo.aasm_current_state # => :opened
# foo.close
# foo.aasm_current_state # => :closed
# Foo.find(1).aasm_current_state # => :opened
# foo.save
# foo.aasm_current_state # => :closed
# 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_column, state.to_s)
end
private
# Ensures that if the aasm_state column is nil and the record is new
# that the initial state gets populated before validation on create
#
# foo = Foo.new
# foo.aasm_state # => nil
# foo.valid?
# foo.aasm_state # => "open" (where :open is the initial state)
#
#
# foo = Foo.find(:first)
# foo.aasm_state # => 1
# foo.aasm_state = nil
# foo.valid?
# foo.aasm_state # => nil
#
def aasm_ensure_initial_state
send("#{self.class.aasm_column}=", aasm.enter_initial_state.to_s) if send(self.class.aasm_column).blank?
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_column}" => name.to_s} unless self.respond_to?(name)
end
end
end
end
end
================================================
FILE: lib/aasm/persistence.rb
================================================
module AASM
module Persistence
class << self
def load_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_files_for(:active_record)
base.send(:include, AASM::Persistence::ActiveRecordPersistence)
elsif hierarchy.include?("Mongoid::Document")
require_files_for(:mongoid)
base.send(:include, AASM::Persistence::MongoidPersistence)
end
end
private
def require_files_for(persistence)
['base', "#{persistence}_persistence"].each do |file_name|
require File.join(File.dirname(__FILE__), 'persistence', file_name)
end
end
end # class << self
end
end # AASM
================================================
FILE: lib/aasm/state.rb
================================================
module AASM
class State
attr_reader :name, :options
def initialize(name, clazz, options={})
@name = name
@clazz = clazz
update(options)
end
def ==(state)
if state.is_a? Symbol
name == state
else
name == state.name
end
end
def <=>(state)
if state.is_a? Symbol
name <=> state
else
name <=> state.name
end
end
def to_s
name.to_s
end
def fire_callbacks(action, record)
action = @options[action]
catch :halt_aasm_chain do
action.is_a?(Array) ?
action.each {|a| _fire_callbacks(a, record)} :
_fire_callbacks(action, record)
end
end
def display_name
@display_name ||= begin
if Module.const_defined?(:I18n)
localized_name
else
name.to_s.gsub(/_/, ' ').capitalize
end
end
end
def localized_name
AASM::Localizer.new.human_state_name(@clazz, self)
end
def for_select
[display_name, name.to_s]
end
private
def update(options = {})
if options.key?(:display) then
@display_name = options.delete(:display)
end
@options = options
self
end
def _fire_callbacks(action, record)
case action
when Symbol, String
record.send(action)
when Proc
action.call(record)
end
end
end
end # AASM
================================================
FILE: lib/aasm/state_machine.rb
================================================
module AASM
class StateMachine
# the following two methods provide the storage of all state machines
def self.[](clazz)
(@machines ||= {})[clazz.to_s]
end
def self.[]=(clazz, machine)
(@machines ||= {})[clazz.to_s] = machine
end
attr_accessor :states, :events, :initial_state, :config
attr_reader :name
# QUESTION: what's the name for? [alto, 2012-11-28]
def initialize(name)
@name = name
@initial_state = nil
@states = []
@events = {}
@config = OpenStruct.new
end
# called internally by Ruby 1.9 after clone()
def initialize_copy(orig)
super
@states = @states.dup
@events = @events.dup
end
def add_state(name, clazz, options)
@states << AASM::State.new(name, clazz, options) unless @states.include?(name)
end
end # StateMachine
end # AASM
================================================
FILE: lib/aasm/transition.rb
================================================
module AASM
class Transition
attr_reader :from, :to, :opts
alias_method :options, :opts
def initialize(opts)
@from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
@opts = opts
end
# TODO: should be named allowed? or similar
def perform(obj, *args)
case @guard
when Symbol, String
obj.send(@guard, *args)
when Proc
@guard.call(obj, *args)
else
true
end
end
def execute(obj, *args)
@on_transition.is_a?(Array) ?
@on_transition.each {|ot| _execute(obj, ot, *args)} :
_execute(obj, @on_transition, *args)
end
def ==(obj)
@from == obj.from && @to == obj.to
end
def from?(value)
@from == value
end
private
def _execute(obj, on_transition, *args)
case on_transition
when Proc
on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)
when Symbol, String
obj.send(:method, on_transition.to_sym).arity == 0 ? obj.send(on_transition) : obj.send(on_transition, *args)
end
end
end
end # AASM
================================================
FILE: lib/aasm/version.rb
================================================
module AASM
VERSION = "3.0.19"
end
================================================
FILE: lib/aasm.rb
================================================
require 'ostruct'
%w(
version
errors
base
instance_base
transition
event
state
localizer
state_machine
persistence
aasm
).each { |file| require File.join(File.dirname(__FILE__), 'aasm', file) }
# load the deprecated methods and modules
Dir[File.join(File.dirname(__FILE__), 'aasm', 'deprecated', '*.rb')].sort.each { |f| require File.expand_path(f) }
================================================
FILE: spec/database.yml
================================================
sqlite3:
adapter: sqlite3
database: spec/aasm.sqlite3.db
================================================
FILE: spec/en.yml
================================================
en:
activerecord:
events:
localizer_test_model:
close: "Let's close it!"
attributes:
localizer_test_model:
aasm_state/opened: "It's open now!"
================================================
FILE: spec/en_deprecated_style.yml
================================================
en:
activerecord:
events:
localizer_test_model:
close: "Let's close it!"
attributes:
localizer_test_model:
aasm_state:
opened: "It's open now!"
================================================
FILE: spec/models/active_record/api.rb
================================================
class DefaultState
attr_accessor :transient_store, :persisted_store
include AASM
aasm do
state :alpha, :initial => true
state :beta
state :gamma
event :release do
transitions :from => [:alpha, :beta, :gamma], :to => :beta
end
end
end
class ProvidedState
attr_accessor :transient_store, :persisted_store
include AASM
aasm do
state :alpha, :initial => true
state :beta
state :gamma
event :release do
transitions :from => [:alpha, :beta, :gamma], :to => :beta
end
end
def aasm_read_state
:beta
end
def aasm_write_state(new_state)
@persisted_store = new_state
end
def aasm_write_state_without_persistence(new_state)
@transient_store = new_state
end
end
class PersistedState < ActiveRecord::Base
attr_accessor :transient_store, :persisted_store
include AASM
aasm do
state :alpha, :initial => true
state :beta
state :gamma
event :release do
transitions :from => [:alpha, :beta, :gamma], :to => :beta
end
end
end
class ProvidedAndPersistedState < ActiveRecord::Base
attr_accessor :transient_store, :persisted_store
include AASM
aasm do
state :alpha, :initial => true
state :beta
state :gamma
event :release do
transitions :from => [:alpha, :beta, :gamma], :to => :beta
end
end
def aasm_read_state
:gamma
end
def aasm_write_state(new_state)
@persisted_store = new_state
end
def aasm_write_state_without_persistence(new_state)
@transient_store = new_state
end
end
================================================
FILE: spec/models/argument.rb
================================================
class Argument
include AASM
aasm do
state :invalid, :initial => true
state :valid
event :valid do
transitions :to => :valid, :from => [:invalid]
end
end
end
================================================
FILE: spec/models/auth_machine.rb
================================================
class AuthMachine
include AASM
attr_accessor :activation_code, :activated_at, :deleted_at
aasm do
state :passive
state :pending, :initial => true, :enter => :make_activation_code
state :active, :enter => :do_activate
state :suspended
state :deleted, :enter => :do_delete, :exit => :do_undelete
state :waiting
event :register do
transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
end
event :activate do
transitions :from => :pending, :to => :active
end
event :suspend do
transitions :from => [:passive, :pending, :active], :to => :suspended
end
event :delete do
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
end
# a dummy event that can never happen
event :unpassify do
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
end
event :unsuspend do
transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
transitions :from => :suspended, :to => :passive
end
event :wait do
transitions :from => :suspended, :to => :waiting, :guard => :if_polite?
end
end
def initialize
# the AR backend uses a before_validate_on_create :aasm_ensure_initial_state
# lets do something similar here for testing purposes.
aasm.enter_initial_state
end
def make_activation_code
@activation_code = 'moo'
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
================================================
FILE: spec/models/bar.rb
================================================
class Bar
include AASM
aasm do
state :read
state :ended
event :foo do
transitions :to => :ended, :from => [:read]
end
end
end
class Baz < Bar
end
================================================
FILE: spec/models/callback_new_dsl.rb
================================================
class CallbackNewDsl
include AASM
aasm do
state :open, :initial => true,
:before_enter => :before_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,
:after_exit => :after_exit_closed
event :close, :before => :before, :after => :after do
transitions :to => :closed, :from => [:open]
end
event :open, :before => :before, :after => :after do
transitions :to => :open, :from => :closed
end
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
def enter_closed; end
def exit_open; end
end
================================================
FILE: spec/models/callback_old_dsl.rb
================================================
class CallbackOldDsl
include AASM
aasm_initial_state :open
aasm_state :open,
:before_enter => :before_enter_open,
:after_enter => :after_enter_open,
:before_exit => :before_exit_open,
:exit => :exit_open,
:after_exit => :after_exit_open
aasm_state :closed,
:before_enter => :before_enter_closed,
:enter => :enter_closed,
:after_enter => :after_enter_closed,
:before_exit => :before_exit_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
def enter_closed; end
def exit_open; end
end
================================================
FILE: spec/models/conversation.rb
================================================
class Conversation
include AASM
aasm 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
def aasm_write_state(state)
@persister.write_state(state)
end
end
================================================
FILE: spec/models/father.rb
================================================
require 'active_record'
class Father < ActiveRecord::Base
include AASM
aasm do
state :missing_details, :initial => true
state :pending_details_confirmation
event :add_details do
transitions :from => :missing_details, :to => :pending_details_confirmation
end
end
def update_state
if may_add_details?
add_details!
end
end
end
================================================
FILE: spec/models/foo.rb
================================================
class Foo
include AASM
aasm do
state :open, :initial => true, :exit => :exit
state :closed, :enter => :enter
event :close, :success => :success_callback do
transitions :from => [:open], :to => [:closed]
end
event :null do
transitions :from => [:open], :to => :closed, :guard => :always_false
end
end
def always_false
false
end
def success_callback
end
def enter
end
def exit
end
end
class FooTwo < Foo
include AASM
aasm do
state :foo
end
end
================================================
FILE: spec/models/invalid_persistor.rb
================================================
require 'active_record'
class InvalidPersistor < ActiveRecord::Base
include AASM
aasm :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
================================================
FILE: spec/models/mongoid/simple_mongoid.rb
================================================
class SimpleMongoid
include Mongoid::Document
include AASM
field :status, type: String
aasm_column :status
aasm_state :unknown_scope
aasm_state :new
end
================================================
FILE: spec/models/mongoid/simple_new_dsl_mongoid.rb
================================================
class SimpleNewDslMongoid
include Mongoid::Document
include AASM
field :status, type: String
aasm :column => :status
aasm do
state :unknown_scope
state :new
end
end
================================================
FILE: spec/models/not_auto_loaded/process.rb
================================================
module Models
class Process
include AASM
aasm_state :sleeping
aasm_state :running
aasm_state :suspended
aasm_event :start do
transitions :from => :sleeping, :to => :running
end
aasm_event :stop do
transitions :from => :running, :to => :suspended
end
end
end
================================================
FILE: spec/models/parametrised_event.rb
================================================
class ParametrisedEvent
include AASM
aasm 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, :on_transition => :wear_clothes
transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
transitions :from => :showering, :to => :prettying_up, :on_transition => [:condition_hair, :fix_hair]
end
end
def wear_clothes(shirt_color, trouser_type)
end
def condition_hair
end
def fix_hair
end
end
================================================
FILE: spec/models/persistence.rb
================================================
class Gate < ActiveRecord::Base
include AASM
# Fake this column for testing purposes
attr_accessor :aasm_state
aasm do
state :opened
state :closed
event :view do
transitions :to => :read, :from => [:needs_attention]
end
end
end
class Reader < ActiveRecord::Base
include AASM
def aasm_read_state
"fi"
end
end
class Writer < ActiveRecord::Base
def aasm_write_state(state)
"fo"
end
include AASM
end
class Transient < ActiveRecord::Base
def aasm_write_state_without_persistence(state)
"fum"
end
include AASM
end
class Simple < ActiveRecord::Base
include AASM
aasm_column :status
aasm_state :unknown_scope
aasm_state :new
end
class SimpleNewDsl < ActiveRecord::Base
include AASM
aasm :column => :status
aasm do
state :unknown_scope
state :new
end
end
class Derivate < Simple
end
class DerivateNewDsl < SimpleNewDsl
end
class Thief < ActiveRecord::Base
if ActiveRecord::VERSION::MAJOR >= 3
self.table_name = 'thieves'
else
set_table_name "thieves"
end
include AASM
aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed }
aasm_state :rich
aasm_state :jailed
attr_accessor :skilled, :aasm_state
end
================================================
FILE: spec/models/process_with_new_dsl.rb
================================================
class ProcessWithNewDsl
include AASM
def self.state(*args)
raise "wrong state method"
end
attr_accessor :flagged
aasm do
state :sleeping, :initial => true
state :running, :after_enter => :flag
state :suspended
event :start do
transitions :from => :sleeping, :to => :running
end
event :stop do
transitions :from => :running, :to => :suspended
end
end
def flag
self.flagged = true
end
def self.event(*args)
raise "wrong event method"
end
end
================================================
FILE: spec/models/silencer.rb
================================================
class Silencer
include AASM
aasm :whiny_transitions => false do
state :silent, :initial => true
state :crying
state :smiling
event :cry do
transitions :from => :silent, :to => :crying
end
event :smile do
transitions :from => :crying, :to => :smiling
end
event :smile_any do
transitions :to => :smiling
end
end
end
================================================
FILE: spec/models/son.rb
================================================
class Son < Father
include AASM
end
================================================
FILE: spec/models/sub_classing.rb
================================================
class SubClassing < Silencer
end
================================================
FILE: spec/models/this_name_better_not_be_in_use.rb
================================================
class ThisNameBetterNotBeInUse
include AASM
aasm do
state :initial
state :symbol
state :string
state :array
state :proc
end
end
================================================
FILE: spec/models/transactor.rb
================================================
require 'active_record'
class Transactor < ActiveRecord::Base
belongs_to :worker
include AASM
aasm :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
================================================
FILE: spec/models/validator.rb
================================================
require 'active_record'
class Validator < ActiveRecord::Base
include AASM
aasm :column => :status 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
================================================
FILE: spec/models/worker.rb
================================================
class Worker < ActiveRecord::Base
end
================================================
FILE: spec/schema.rb
================================================
ActiveRecord::Schema.define(:version => 0) do
%w{gates readers writers transients simples simple_new_dsls thieves localizer_test_models persisted_states provided_and_persisted_states}.each do |table_name|
create_table table_name, :force => true do |t|
t.string "aasm_state"
end
end
create_table "validators", :force => true do |t|
t.string "name"
t.string "status"
end
create_table "transactors", :force => true do |t|
t.string "name"
t.string "status"
t.integer "worker_id"
end
create_table "workers", :force => true do |t|
t.string "name"
t.string "status"
end
create_table "invalid_persistors", :force => true do |t|
t.string "name"
t.string "status"
end
create_table "fathers", :force => true do |t|
t.string "aasm_state"
t.string "type"
end
end
================================================
FILE: spec/spec_helper.rb
================================================
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
require 'aasm'
require 'rspec'
require 'rspec/autorun'
require 'coveralls'
Coveralls.wear!
# require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
# require 'ruby-debug/completion'
# require 'pry'
def load_schema
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
ActiveRecord::Base.establish_connection(config['sqlite3'])
load(File.dirname(__FILE__) + "/schema.rb")
end
# custom spec helpers
Dir[File.dirname(__FILE__) + "/spec_helpers/**/*.rb"].sort.each { |f| require File.expand_path(f) }
# example model classes
Dir[File.dirname(__FILE__) + "/models/*.rb"].sort.each { |f| require File.expand_path(f) }
================================================
FILE: spec/unit/api_spec.rb
================================================
require 'spec_helper'
require 'models/active_record/api.rb'
describe "reading the current state" do
it "uses the AASM default" do
DefaultState.new.aasm.current_state.should eql :alpha
end
it "uses the provided method" do
ProvidedState.new.aasm.current_state.should eql :beta
end
it "uses the persistence storage" do
PersistedState.new.aasm.current_state.should eql :alpha
end
it "uses the provided method even if persisted" do
ProvidedAndPersistedState.new.aasm.current_state.should eql :gamma
end
end
describe "writing and persisting the current state" do
it "uses the AASM default" do
o = DefaultState.new
o.release!
o.persisted_store.should be_nil
end
it "uses the provided method" do
o = ProvidedState.new
o.release!
o.persisted_store.should eql :beta
end
it "uses the persistence storage" do
o = PersistedState.new
o.release!
o.persisted_store.should be_nil
end
it "uses the provided method even if persisted" do
o = ProvidedAndPersistedState.new
o.release!
o.persisted_store.should eql :beta
end
end
describe "writing the current state without persisting it" do
it "uses the AASM default" do
o = DefaultState.new
o.release
o.transient_store.should be_nil
end
it "uses the provided method" do
o = ProvidedState.new
o.release
o.transient_store.should eql :beta
end
it "uses the persistence storage" do
o = PersistedState.new
o.release
o.transient_store.should be_nil
end
it "uses the provided method even if persisted" do
o = ProvidedAndPersistedState.new
o.release
o.transient_store.should eql :beta
end
end
================================================
FILE: spec/unit/callbacks_spec.rb
================================================
require 'spec_helper'
describe 'callbacks for the old DSL' do
let(:callback) {CallbackOldDsl.new}
it "should get close callbacks" do
callback.should_receive(:exit_open).once.ordered
callback.should_receive(:before).once.ordered
callback.should_receive(:before_exit_open).once.ordered # these should be before the state changes
callback.should_receive(:before_enter_closed).once.ordered
callback.should_receive(:enter_closed).once.ordered
callback.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
callback.should_receive(:after_exit_open).once.ordered # these should be after the state changes
callback.should_receive(:after_enter_closed).once.ordered
callback.should_receive(:after).once.ordered
callback.close!
end
end
describe 'callbacks for the new DSL' do
let(:callback) {CallbackNewDsl.new}
it "be called in order" do
callback.should_receive(:exit_open).once.ordered
callback.should_receive(:before).once.ordered
callback.should_receive(:before_exit_open).once.ordered # these should be before the state changes
callback.should_receive(:before_enter_closed).once.ordered
callback.should_receive(:enter_closed).once.ordered
callback.should_receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
callback.should_receive(:after_exit_open).once.ordered # these should be after the state changes
callback.should_receive(:after_enter_closed).once.ordered
callback.should_receive(:after).once.ordered
callback.close!
end
end
describe 'event callbacks' do
describe "with an error callback defined" do
before do
class Foo
aasm_event :safe_close, :success => :success_callback, :error => :error_callback do
transitions :to => :closed, :from => [:open]
end
end
@foo = Foo.new
end
it "should run error_callback if an exception is raised and error_callback defined" do
def @foo.error_callback(e); end
@foo.stub!(:enter).and_raise(e=StandardError.new)
@foo.should_receive(:error_callback).with(e)
@foo.safe_close!
end
it "should raise NoMethodError if exceptionis raised and error_callback is declared but not defined" do
@foo.stub!(:enter).and_raise(StandardError)
lambda{@foo.safe_close!}.should raise_error(NoMethodError)
end
it "should propagate an error if no error callback is declared" do
@foo.stub!(:enter).and_raise("Cannot enter safe")
lambda{@foo.close!}.should raise_error(StandardError, "Cannot enter safe")
end
end
describe "with aasm_event_fired defined" do
before do
@foo = Foo.new
def @foo.aasm_event_fired(event, from, to); end
end
it 'should call it for successful bang fire' do
@foo.should_receive(:aasm_event_fired).with(:close, :open, :closed)
@foo.close!
end
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.aasm.stub!(:set_current_state_with_persistence).and_return(false)
@foo.should_not_receive(:aasm_event_fired)
@foo.close!
end
end
describe "with aasm_event_failed defined" do
before do
@foo = Foo.new
def @foo.aasm_event_failed(event, from); end
end
it 'should call it when transition failed for bang fire' do
@foo.should_receive(:aasm_event_failed).with(:null, :open)
lambda {@foo.null!}.should raise_error(AASM::InvalidTransition)
end
it 'should call it when transition failed for non-bang fire' do
@foo.should_receive(:aasm_event_failed).with(:null, :open)
lambda {@foo.null}.should raise_error(AASM::InvalidTransition)
end
it 'should not call it if persist fails for bang fire' do
@foo.aasm.stub!(:set_current_state_with_persistence).and_return(false)
@foo.should_receive(:aasm_event_failed)
@foo.close!
end
end
end
================================================
FILE: spec/unit/complex_example_spec.rb
================================================
require 'spec_helper'
describe 'on initialization' do
let(:auth) {AuthMachine.new}
it 'should be in the pending state' do
auth.aasm_current_state.should == :pending
end
it 'should have an activation code' do
auth.has_activation_code?.should be_true
auth.activation_code.should_not be_nil
end
end
describe 'when being unsuspended' do
let(:auth) {AuthMachine.new}
it 'should be able to be unsuspended' do
auth.activate!
auth.suspend!
auth.may_unsuspend?.should be_true
end
it 'should not be able to be unsuspended into active' do
auth.suspend!
auth.may_unsuspend?(:active).should_not be_true
end
it 'should be able to be unsuspended into active if polite' do
auth.suspend!
auth.may_wait?(:waiting, :please).should be_true
auth.wait!(nil, :please)
end
it 'should not be able to be unsuspended into active if not polite' do
auth.suspend!
auth.may_wait?(:waiting).should_not be_true
auth.may_wait?(:waiting, :rude).should_not be_true
lambda {auth.wait!(nil, :rude)}.should raise_error(AASM::InvalidTransition)
lambda {auth.wait!}.should raise_error(AASM::InvalidTransition)
end
it 'should not be able to be unpassified' do
auth.activate!
auth.suspend!
auth.unsuspend!
auth.may_unpassify?.should_not be_true
lambda {auth.unpassify!}.should raise_error(AASM::InvalidTransition)
end
it 'should be active if previously activated' do
auth.activate!
auth.suspend!
auth.unsuspend!
auth.aasm_current_state.should == :active
end
it 'should be pending if not previously activated, but an activation code is present' do
auth.suspend!
auth.unsuspend!
auth.aasm_current_state.should == :pending
end
it 'should be passive if not previously activated and there is no activation code' do
auth.activation_code = nil
auth.suspend!
auth.unsuspend!
auth.aasm_current_state.should == :passive
end
end
================================================
FILE: spec/unit/event_spec.rb
================================================
require 'spec_helper'
describe 'adding an event' do
let(:event) do
AASM::Event.new(:close_order, {:success => :success_callback}) do
before :before_callback
after :after_callback
transitions :to => :closed, :from => [:open, :received]
end
end
it 'should set the name' do
event.name.should == :close_order
end
it 'should set the success callback' do
event.options[:success].should == :success_callback
end
it 'should set the after callback' do
event.options[:after].should == [:after_callback]
end
it 'should set the before callback' do
event.options[:before].should == [:before_callback]
end
it 'should create transitions' do
transitions = event.all_transitions
transitions[0].from.should == :open
transitions[0].to.should == :closed
transitions[1].from.should == :received
transitions[1].to.should == :closed
end
end
describe 'transition inspection' do
let(:event) do
AASM::Event.new(:run) do
transitions :to => :running, :from => :sleeping
end
end
it 'should support inspecting transitions from other states' do
event.transitions_from_state(:sleeping).map(&:to).should == [:running]
event.transitions_from_state?(:sleeping).should be_true
event.transitions_from_state(:cleaning).map(&:to).should == []
event.transitions_from_state?(:cleaning).should be_false
end
it 'should support inspecting transitions to other states' do
event.transitions_to_state(:running).map(&:from).should == [:sleeping]
event.transitions_to_state?(:running).should be_true
event.transitions_to_state(:cleaning).map(&:to).should == []
event.transitions_to_state?(:cleaning).should be_false
end
end
describe 'firing an event' do
it 'should return nil if the transitions are empty' do
obj = mock('object')
obj.stub!(:aasm_current_state)
event = AASM::Event.new(:event)
event.fire(obj).should be_nil
end
it 'should return the state of the first matching transition it finds' do
event = AASM::Event.new(:event) do
transitions :to => :closed, :from => [:open, :received]
end
obj = mock('object')
obj.stub!(:aasm_current_state).and_return(:open)
event.fire(obj).should == :closed
end
it 'should call the guard with the params passed in' do
event = AASM::Event.new(:event) do
transitions :to => :closed, :from => [:open, :received], :guard => :guard_fn
end
obj = mock('object')
obj.stub!(:aasm_current_state).and_return(:open)
obj.should_receive(:guard_fn).with('arg1', 'arg2').and_return(true)
event.fire(obj, nil, 'arg1', 'arg2').should == :closed
end
end
describe 'should fire callbacks' do
describe 'success' do
it "if it's a symbol" do
ThisNameBetterNotBeInUse.instance_eval {
aasm_event :with_symbol, :success => :symbol_success_callback do
transitions :to => :symbol, :from => [:initial]
end
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:symbol_success_callback)
model.with_symbol!
end
it "if it's a string" do
ThisNameBetterNotBeInUse.instance_eval {
aasm_event :with_string, :success => 'string_success_callback' do
transitions :to => :string, :from => [:initial]
end
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:string_success_callback)
model.with_string!
end
it "if passed an array of strings and/or symbols" do
ThisNameBetterNotBeInUse.instance_eval {
aasm_event :with_array, :success => [:success_callback1, 'success_callback2'] do
transitions :to => :array, :from => [:initial]
end
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:success_callback1)
model.should_receive(:success_callback2)
model.with_array!
end
it "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 { 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 "if it's a proc" do
ThisNameBetterNotBeInUse.instance_eval {
aasm_event :with_proc, :success => lambda { proc_success_callback } do
transitions :to => :proc, :from => [:initial]
end
}
model = ThisNameBetterNotBeInUse.new
model.should_receive(:proc_success_callback)
model.with_proc!
end
end
describe 'after' do
it "if they set different ways" do
ThisNameBetterNotBeInUse.instance_eval do
aasm_event :with_afters, :after => :do_one_thing_after do
after do
do_another_thing_after_too
end
after do
do_third_thing_at_last
end
transitions :to => :proc, :from => [:initial]
end
end
model = ThisNameBetterNotBeInUse.new
model.should_receive(:do_one_thing_after).once.ordered
model.should_receive(:do_another_thing_after_too).once.ordered
model.should_receive(:do_third_thing_at_last).once.ordered
model.with_afters!
end
end
describe 'before' do
it "if it's a proc" do
ThisNameBetterNotBeInUse.instance_eval do
aasm_event :before_as_proc do
before do
do_something_before
end
transitions :to => :proc, :from => [:initial]
end
end
model = ThisNameBetterNotBeInUse.new
model.should_receive(:do_something_before).once
model.before_as_proc!
end
end
it 'in right order' do
ThisNameBetterNotBeInUse.instance_eval do
aasm_event :in_right_order, :after => :do_something_after do
before do
do_something_before
end
transitions :to => :proc, :from => [:initial]
end
end
model = ThisNameBetterNotBeInUse.new
model.should_receive(:do_something_before).once.ordered
model.should_receive(:do_something_after).once.ordered
model.in_right_order!
end
end
describe 'parametrised events' do
let(:pe) {ParametrisedEvent.new}
it 'should transition to specified next state (sleeping to showering)' do
pe.wakeup!(:showering)
pe.aasm_current_state.should == :showering
end
it 'should transition to specified next state (sleeping to working)' do
pe.wakeup!(:working)
pe.aasm_current_state.should == :working
end
it 'should transition to default (first or showering) state' do
pe.wakeup!
pe.aasm_current_state.should == :showering
end
it 'should transition to default state when on_transition invoked' do
pe.dress!(nil, 'purple', 'dressy')
pe.aasm_current_state.should == :working
end
it 'should call on_transition method with args' do
pe.wakeup!(:showering)
pe.should_receive(:wear_clothes).with('blue', 'jeans')
pe.dress!(:working, 'blue', 'jeans')
end
it 'should call on_transition proc' do
pe.wakeup!(:showering)
pe.should_receive(:wear_clothes).with('purple', 'slacks')
pe.dress!(:dating, 'purple', 'slacks')
end
it 'should call on_transition with an array of methods' do
pe.wakeup!(:showering)
pe.should_receive(:condition_hair)
pe.should_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
foo.should be_open
foo.should_receive(:aasm_write_state_without_persistence)
foo.close
end
end
================================================
FILE: spec/unit/initial_state_spec.rb
================================================
require 'spec_helper'
class Banker
include AASM
aasm do
state :retired
state :selling_bad_mortgages
end
aasm_initial_state Proc.new { |banker| banker.rich? ? :retired : :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 'initial states' do
let(:bar) {Bar.new}
it 'should use the first state defined if no initial state is given' do
bar.aasm_current_state.should == :read
# bar.aasm.current_state.should == :read # not yet supported
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
================================================
FILE: spec/unit/inspection_spec.rb
================================================
require 'spec_helper'
describe 'inspection for common cases' do
it 'should support the old DSL' do
Foo.should respond_to(:aasm_states)
Foo.aasm_states.should include(:open)
Foo.aasm_states.should include(:closed)
Foo.should respond_to(:aasm_initial_state)
Foo.aasm_initial_state.should == :open
Foo.should respond_to(:aasm_events)
Foo.aasm_events.should include(:close)
Foo.aasm_events.should include(:null)
end
it 'should support the new DSL' do
Foo.aasm.should respond_to(:states)
Foo.aasm.states.should include(:open)
Foo.aasm.states.should include(:closed)
Foo.aasm.should respond_to(:initial_state)
Foo.aasm.initial_state.should == :open
Foo.aasm.should respond_to(:events)
Foo.aasm.events.should include(:close)
Foo.aasm.events.should include(:null)
end
context "instance level inspection" do
let(:foo) { Foo.new }
let(:two) { FooTwo.new }
it "delivers all states" do
states = foo.aasm.states
states.should include(:open)
states.should include(:closed)
states = foo.aasm.states(:permissible => true)
states.should include(:closed)
states.should_not include(:open)
foo.close
foo.aasm.states(:permissible => true).should be_empty
end
it "delivers all states for subclasses" do
states = two.aasm.states
states.should include(:open)
states.should include(:closed)
states.should include(:foo)
states = two.aasm.states(:permissible => true)
states.should include(:closed)
states.should_not include(:open)
two.close
two.aasm.states(:permissible => true).should be_empty
end
it "delivers all events" do
events = foo.aasm.events
events.should include(:close)
events.should include(:null)
foo.close
foo.aasm.events.should be_empty
end
end
it 'should list states in the order they have been defined' do
Conversation.aasm.states.should == [:needs_attention, :read, :closed, :awaiting_response, :junk]
end
end
describe "special cases" do
it "should support valid a state name" do
Argument.aasm_states.should include(:invalid)
Argument.aasm_states.should include(:valid)
argument = Argument.new
argument.invalid?.should be_true
argument.aasm_current_state.should == :invalid
argument.valid!
argument.valid?.should be_true
argument.aasm_current_state.should == :valid
end
end
describe :aasm_states_for_select do
it "should return a select friendly array of states" do
Foo.should respond_to(:aasm_states_for_select)
Foo.aasm_states_for_select.should == [['Open', 'open'], ['Closed', 'closed']]
end
end
describe :aasm_from_states_for_state do
it "should return all from states for a state" do
AuthMachine.should respond_to(:aasm_from_states_for_state)
froms = AuthMachine.aasm_from_states_for_state(:active)
[:pending, :passive, :suspended].each {|from| froms.should include(from)}
end
it "should return from states for a state for a particular transition only" do
froms = AuthMachine.aasm_from_states_for_state(:active, :transition => :unsuspend)
[:suspended].each {|from| froms.should include(from)}
end
end
describe 'permissible events' do
let(:foo) {Foo.new}
it 'work' do
foo.aasm.permissible_events.should include(:close)
foo.aasm.permissible_events.should_not include(:null)
end
end
================================================
FILE: spec/unit/localizer_spec.rb
================================================
require 'spec_helper'
require 'active_record'
require 'logger'
require 'i18n'
load_schema
class LocalizerTestModel < ActiveRecord::Base
include AASM
attr_accessor :aasm_state
aasm_initial_state :opened
aasm_state :opened
aasm_state :closed
aasm_event :close
aasm_event :open
end
describe 'localized state names' do
before(:all) do
I18n.load_path << 'spec/en.yml'
I18n.default_locale = :en
I18n.reload!
end
after(:all) do
I18n.load_path.clear
end
it 'should localize' do
LocalizerTestModel.aasm.states.detect {|s| s == :opened}.localized_name.should == "It's open now!"
end
it 'should use fallback' do
LocalizerTestModel.aasm.states.detect {|s| s == :closed}.localized_name.should == 'Closed'
end
end
describe AASM::Localizer, "new style" do
before(:all) do
I18n.load_path << 'spec/en.yml'
I18n.default_locale = :en
I18n.reload!
end
after(:all) do
I18n.load_path.clear
end
let (:foo_opened) { LocalizerTestModel.new }
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
context 'aasm_human_state' do
it 'should return translated state value' do
foo_opened.aasm_human_state.should == "It's open now!"
end
it 'should return humanized value if not localized' do
foo_closed.aasm_human_state.should == "Closed"
end
end
context 'aasm_human_event_name' do
it 'should return translated event name' do
LocalizerTestModel.aasm_human_event_name(:close).should == "Let's close it!"
end
it 'should return humanized event name' do
LocalizerTestModel.aasm_human_event_name(:open).should == "Open"
end
end
end
describe AASM::Localizer, "deprecated style" do
before(:all) do
I18n.load_path << 'spec/en_deprecated_style.yml'
I18n.default_locale = :en
I18n.reload!
end
after(:all) do
I18n.load_path.clear
end
let (:foo_opened) { LocalizerTestModel.new }
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
context 'aasm_human_state' do
it 'should return translated state value' do
foo_opened.aasm_human_state.should == "It's open now!"
end
it 'should return humanized value if not localized' do
foo_closed.aasm_human_state.should == "Closed"
end
end
context 'aasm_human_event_name' do
it 'should return translated event name' do
LocalizerTestModel.aasm_human_event_name(:close).should == "Let's close it!"
end
it 'should return humanized event name' do
LocalizerTestModel.aasm_human_event_name(:open).should == "Open"
end
end
end
================================================
FILE: spec/unit/memory_leak_spec.rb
================================================
# require 'spec_helper'
# describe "state machines" do
# def number_of_objects(clazz)
# ObjectSpace.each_object(clazz) {}
# end
# def machines
# AASM::StateMachine.instance_variable_get("@machines")
# end
# it "should be created without memory leak" do
# machines_count = machines.size
# state_count = number_of_objects(AASM::State)
# event_count = number_of_objects(AASM::Event)
# puts "event_count = #{event_count}"
# transition_count = number_of_objects(AASM::Transition)
# load File.expand_path(File.dirname(__FILE__) + '/../models/not_auto_loaded/process.rb')
# machines.size.should == machines_count + 1 # + Process
# number_of_objects(Models::Process).should == 0
# number_of_objects(AASM::State).should == state_count + 3 # + Process
# puts "event_count = #{number_of_objects(AASM::Event)}"
# number_of_objects(AASM::Event).should == event_count + 2 # + Process
# number_of_objects(AASM::Transition).should == transition_count + 2 # + Process
# Models.send(:remove_const, "Process") if Models.const_defined?("Process")
# load File.expand_path(File.dirname(__FILE__) + '/../models/not_auto_loaded/process.rb')
# machines.size.should == machines_count + 1 # + Process
# number_of_objects(AASM::State).should == state_count + 3 # + Process
# # ObjectSpace.each_object(AASM::Event) {|o| puts o.inspect}
# puts "event_count = #{number_of_objects(AASM::Event)}"
# number_of_objects(AASM::Event).should == event_count + 2 # + Process
# number_of_objects(AASM::Transition).should == transition_count + 2 # + Process
# end
# end
================================================
FILE: spec/unit/new_dsl_spec.rb
================================================
require 'spec_helper'
describe "the new dsl" do
let(:process) {ProcessWithNewDsl.new}
it 'should not conflict with other event or state methods' do
lambda {ProcessWithNewDsl.state}.should raise_error(RuntimeError, "wrong state method")
lambda {ProcessWithNewDsl.event}.should raise_error(RuntimeError, "wrong event method")
end
end
================================================
FILE: spec/unit/persistence/active_record_persistence_spec.rb
================================================
require 'active_record'
require 'logger'
require 'spec_helper'
load_schema
# if you want to see the statements while running the spec enable the following line
# ActiveRecord::Base.logger = Logger.new(STDERR)
shared_examples_for "aasm model" do
it "should include persistence mixins" do
klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
end
end
describe "instance methods" do
let(:gate) {Gate.new}
it "should respond to aasm persistence methods" do
gate.should respond_to(:aasm_read_state)
gate.should respond_to(:aasm_write_state)
gate.should respond_to(:aasm_write_state_without_persistence)
end
it "should return the initial state when new and the aasm field is nil" do
gate.aasm_current_state.should == :opened
end
it "should return the aasm column when new and the aasm field is not nil" do
gate.aasm_state = "closed"
gate.aasm_current_state.should == :closed
end
it "should return the aasm column when not new and the aasm_column is not nil" do
gate.stub!(:new_record?).and_return(false)
gate.aasm_state = "state"
gate.aasm_current_state.should == :state
end
it "should allow a nil state" do
gate.stub!(:new_record?).and_return(false)
gate.aasm_state = nil
gate.aasm_current_state.should be_nil
end
it "should call aasm_ensure_initial_state on validation before create" do
gate.should_receive(:aasm_ensure_initial_state).and_return(true)
gate.valid?
end
it "should not call aasm_ensure_initial_state on validation before update" do
gate.stub!(:new_record?).and_return(false)
gate.should_not_receive(:aasm_ensure_initial_state)
gate.valid?
end
end
describe 'subclasses' do
it "should have the same states as its parent class" do
Derivate.aasm_states.should == Simple.aasm_states
end
it "should have the same events as its parent class" do
Derivate.aasm_events.should == Simple.aasm_events
end
it "should have the same column as its parent class" do
Derivate.aasm_column.should == :status
end
it "should have the same column as its parent even for the new dsl" do
SimpleNewDsl.aasm_column.should == :status
DerivateNewDsl.aasm_column.should == :status
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
Simple.should respond_to(:unknown_scope)
SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation).should be_true
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
Simple.should respond_to(:new)
Simple.new.class.should == Simple
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
SimpleNewDsl.should respond_to(:unknown_scope)
SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation).should be_true
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
SimpleNewDsl.should respond_to(:new)
SimpleNewDsl.new.class.should == SimpleNewDsl
end
end
end
describe 'initial states' do
it 'should support conditions' do
Thief.new(:skilled => true).aasm_current_state.should == :rich
Thief.new(:skilled => false).aasm_current_state.should == :jailed
end
end
describe 'transitions with persistence' do
it "should work for valid models" do
valid_object = Validator.create(:name => 'name')
valid_object.should be_sleeping
valid_object.status = :running
valid_object.should be_running
end
it 'should not store states for invalid models' do
validator = Validator.create(:name => 'name')
validator.should be_valid
validator.should be_sleeping
validator.name = nil
validator.should_not be_valid
validator.run!.should be_false
validator.should be_sleeping
validator.reload
validator.should_not be_running
validator.should be_sleeping
validator.name = 'another name'
validator.should be_valid
validator.run!.should be_true
validator.should be_running
validator.reload
validator.should be_running
validator.should_not be_sleeping
end
it 'should store states for invalid models if configured' do
persistor = InvalidPersistor.create(:name => 'name')
persistor.should be_valid
persistor.should be_sleeping
persistor.name = nil
persistor.should_not be_valid
persistor.run!.should be_true
persistor.should be_running
persistor = InvalidPersistor.find(persistor.id)
persistor.valid?
persistor.should be_valid
persistor.should be_running
persistor.should_not be_sleeping
persistor.reload
persistor.should be_running
persistor.should_not be_sleeping
end
describe 'transactions' do
it 'should rollback all changes' do
worker = Worker.create!(:name => 'worker', :status => 'sleeping')
transactor = Transactor.create!(:name => 'transactor', :worker => worker)
transactor.should be_sleeping
worker.status.should == 'sleeping'
lambda {transactor.run!}.should raise_error(StandardError, 'failed on purpose')
transactor.should be_running
worker.reload.status.should == 'sleeping'
end
end
end
================================================
FILE: spec/unit/persistence/mongoid_persistance_spec.rb
================================================
describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3') do
# describe 'mongoid' do
before(:all) do
require 'mongoid'
require 'logger'
require 'spec_helper'
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
SimpleMongoid.should respond_to(:unknown_scope)
SimpleMongoid.unknown_scope.class.should == Mongoid::Criteria
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
SimpleMongoid.should respond_to(:new)
SimpleMongoid.new.class.should == 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
SimpleNewDslMongoid.should respond_to(:unknown_scope)
SimpleNewDslMongoid.unknown_scope.class.should == Mongoid::Criteria
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
SimpleNewDslMongoid.should respond_to(:new)
SimpleNewDslMongoid.new.class.should == SimpleNewDslMongoid
end
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
SimpleNewDslMongoid.should respond_to(:find_in_state)
end
it "should find the model when given the correct scope and model id" do
SimpleNewDslMongoid.find_in_state(model_id, 'unknown_scope').class.should == SimpleNewDslMongoid
SimpleNewDslMongoid.find_in_state(model_id, 'unknown_scope').should == 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
SimpleNewDslMongoid.should respond_to(:count_in_state)
end
it "should return n for a scope with n records persisted" do
SimpleNewDslMongoid.count_in_state('unknown_scope').class.should == Fixnum
SimpleNewDslMongoid.count_in_state('unknown_scope').should == 3
end
it "should return zero for a scope without records persisted" do
SimpleNewDslMongoid.count_in_state('new').class.should == Fixnum
SimpleNewDslMongoid.count_in_state('new').should == 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
SimpleNewDslMongoid.should respond_to(:with_state_scope)
end
it "should correctly process block" do
SimpleNewDslMongoid.with_state_scope('unknown_scope') do
SimpleNewDslMongoid.count
end.should == 3
SimpleNewDslMongoid.with_state_scope('new') do
SimpleNewDslMongoid.count
end.should == 2
end
end
end
================================================
FILE: spec/unit/simple_example_spec.rb
================================================
require 'spec_helper'
class Payment
include AASM
aasm do
state :initialised, :initial => true
state :filled_out
state :authorised
event :fill_out do
transitions :from => :initialised, :to => :filled_out
end
event :authorise do
transitions :from => :filled_out, :to => :authorised
end
end
end
describe 'state machine' do
let(:payment) {Payment.new}
it 'starts with an initial state' do
payment.aasm_current_state.should == :initialised
# payment.aasm.current_state.should == :initialised # not yet supported
payment.should respond_to(:initialised?)
payment.should be_initialised
end
it 'allows transitions to other states' do
payment.should respond_to(:fill_out)
payment.should respond_to(:fill_out!)
payment.fill_out!
payment.should respond_to(:filled_out?)
payment.should be_filled_out
payment.should respond_to(:authorise)
payment.should respond_to(:authorise!)
payment.authorise
payment.should respond_to(:authorised?)
payment.should be_authorised
end
it 'denies transitions to other states' do
lambda {payment.authorise}.should raise_error(AASM::InvalidTransition)
lambda {payment.authorise!}.should raise_error(AASM::InvalidTransition)
payment.fill_out
lambda {payment.fill_out}.should raise_error(AASM::InvalidTransition)
lambda {payment.fill_out!}.should raise_error(AASM::InvalidTransition)
payment.authorise
lambda {payment.fill_out}.should raise_error(AASM::InvalidTransition)
lambda {payment.fill_out!}.should raise_error(AASM::InvalidTransition)
end
it 'defines constants for each state name' do
Payment::STATE_INITIALISED.should eq(:initialised)
Payment::STATE_FILLED_OUT.should eq(:filled_out)
Payment::STATE_AUTHORISED.should eq(:authorised)
end
end
================================================
FILE: spec/unit/state_spec.rb
================================================
require 'spec_helper'
describe AASM::State do
before(:each) do
@name = :astate
@options = { :crazy_custom_key => 'key' }
end
def new_state(options={})
AASM::State.new(@name, Conversation, @options.merge(options))
end
it 'should set the name' do
state = new_state
state.name.should == :astate
end
it 'should set the display_name from name' do
new_state.display_name.should == 'Astate'
end
it 'should set the display_name from options' do
new_state(:display => "A State").display_name.should == 'A State'
end
it 'should set the options and expose them as options' do
new_state.options.should == @options
end
it 'should be equal to a symbol of the same name' do
new_state.should == :astate
end
it 'should be equal to a State of the same name' do
new_state.should == new_state
end
it 'should send a message to the record for an action if the action is present as a symbol' do
state = new_state(:entering => :foo)
record = mock('record')
record.should_receive(:foo)
state.fire_callbacks(:entering, record)
end
it 'should send a message to the record for an action if the action is present as a string' do
state = new_state(:entering => 'foo')
record = mock('record')
record.should_receive(:foo)
state.fire_callbacks(:entering, record)
end
it 'should send a message to the record for each action' do
state = new_state(:entering => [:a, :b, "c", lambda {|r| r.foobar }])
record = mock('record')
record.should_receive(:a)
record.should_receive(:b)
record.should_receive(:c)
record.should_receive(:foobar)
state.fire_callbacks(:entering, record)
end
it "should stop calling actions if one of them raises :halt_aasm_chain" do
state = new_state(:entering => [:a, :b, :c])
record = mock('record')
record.should_receive(:a)
record.should_receive(:b).and_throw(:halt_aasm_chain)
record.should_not_receive(:c)
state.fire_callbacks(:entering, record)
end
it 'should call a proc, passing in the record for an action if the action is present' do
state = new_state(:entering => Proc.new {|r| r.foobar})
record = mock('record')
record.should_receive(:foobar)
state.fire_callbacks(:entering, record)
end
end
================================================
FILE: spec/unit/subclassing_spec.rb
================================================
require 'spec_helper'
describe 'subclassing' do
let(:son) {Son.new}
it 'should have the parent states' do
Foo.aasm_states.each do |state|
FooTwo.aasm_states.should include(state)
end
Baz.aasm_states.should == Bar.aasm_states
end
it 'should not add the child states to the parent machine' do
Foo.aasm_states.should_not include(:foo)
end
it "should have the same events as its parent" do
Baz.aasm_events.should == Bar.aasm_events
end
it 'should know how to respond to `may_add_details?`' do
son.may_add_details?.should be_true
end
it 'should not break if I call Son#update_state' do
son.update_state
son.aasm_current_state.should == :pending_details_confirmation
end
end
================================================
FILE: spec/unit/transition_spec.rb
================================================
require 'spec_helper'
describe 'transitions' do
it 'should raise an exception when whiny' do
process = ProcessWithNewDsl.new
lambda { process.stop! }.should raise_error(AASM::InvalidTransition)
process.should be_sleeping
end
it 'should not raise an exception when not whiny' do
silencer = Silencer.new
silencer.smile!.should be_false
silencer.should be_silent
end
it 'should not raise an exception when superclass not whiny' do
sub = SubClassing.new
sub.smile!.should be_false
sub.should be_silent
end
it 'should not raise an exception when from is nil even if whiny' do
silencer = Silencer.new
silencer.smile_any!.should be_true
silencer.should be_smiling
end
end
describe AASM::Transition do
it 'should set from, to, and opts attr readers' do
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
st = AASM::Transition.new(opts)
st.from.should == opts[:from]
st.to.should == opts[:to]
st.opts.should == opts
end
it 'should pass equality check if from and to are the same' do
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
st = AASM::Transition.new(opts)
obj = mock('object')
obj.stub!(:from).and_return(opts[:from])
obj.stub!(:to).and_return(opts[:to])
st.should == obj
end
it 'should fail equality check if from are not the same' do
opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
st = AASM::Transition.new(opts)
obj = mock('object')
obj.stub!(:from).and_return('blah')
obj.stub!(:to).and_return(opts[:to])
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::Transition.new(opts)
obj = mock('object')
obj.stub!(:from).and_return(opts[:from])
obj.stub!(:to).and_return('blah')
st.should_not == obj
end
end
describe AASM::Transition, '- when performing guard checks' do
it 'should return true of there is no guard' do
opts = {:from => 'foo', :to => 'bar'}
st = AASM::Transition.new(opts)
st.perform(nil).should be_true
end
it 'should call the method on the object if guard is a symbol' do
opts = {:from => 'foo', :to => 'bar', :guard => :test}
st = AASM::Transition.new(opts)
obj = mock('object')
obj.should_receive(:test)
st.perform(obj)
end
it 'should call the method on the object if guard is a string' do
opts = {:from => 'foo', :to => 'bar', :guard => 'test'}
st = AASM::Transition.new(opts)
obj = mock('object')
obj.should_receive(:test)
st.perform(obj)
end
it 'should call the proc passing the object if the guard is a proc' do
opts = {:from => 'foo', :to => 'bar', :guard => Proc.new {|o| o.test}}
st = AASM::Transition.new(opts)
obj = mock('object')
obj.should_receive(:test)
st.perform(obj)
end
end
describe AASM::Transition, '- when executing the transition with a Proc' do
it 'should call a Proc on the object with args' do
opts = {:from => 'foo', :to => 'bar', :on_transition => Proc.new {|o| o.test}}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
opts[:on_transition].should_receive(:call).with(any_args)
st.execute(obj, args)
end
it 'should call a Proc on the object without args' do
opts = {:from => 'foo', :to => 'bar', :on_transition => Proc.new {||}}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
opts[:on_transition].should_receive(:call).with(no_args)
st.execute(obj, args)
end
end
describe AASM::Transition, '- when executing the transition with an :on_transtion method call' do
it 'should accept a String for the method name' do
opts = {:from => 'foo', :to => 'bar', :on_transition => 'test'}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
obj.should_receive(:test)
st.execute(obj, args)
end
it 'should accept a Symbol for the method name' do
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
obj.should_receive(:test)
st.execute(obj, args)
end
it 'should pass args if the target method accepts them' do
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
obj.class.class_eval do
define_method(:test) {|*args| 'success'}
end
return_value = st.execute(obj, args)
return_value.should == 'success'
end
it 'should NOT pass args if the target method does NOT accept them' do
opts = {:from => 'foo', :to => 'bar', :on_transition => :test}
st = AASM::Transition.new(opts)
args = {:arg1 => '1', :arg2 => '2'}
obj = mock('object')
obj.class.class_eval do
define_method(:test) {|*args| 'success'}
end
return_value = st.execute(obj, args)
return_value.should == 'success'
end
end
gitextract_cxl63xfk/
├── .document
├── .gitignore
├── .travis.yml
├── API
├── CHANGELOG.md
├── Gemfile
├── HOWTO
├── LICENSE
├── README.md
├── Rakefile
├── aasm.gemspec
├── lib/
│ ├── aasm/
│ │ ├── aasm.rb
│ │ ├── base.rb
│ │ ├── deprecated/
│ │ │ └── aasm.rb
│ │ ├── errors.rb
│ │ ├── event.rb
│ │ ├── instance_base.rb
│ │ ├── localizer.rb
│ │ ├── persistence/
│ │ │ ├── active_record_persistence.rb
│ │ │ ├── base.rb
│ │ │ └── mongoid_persistence.rb
│ │ ├── persistence.rb
│ │ ├── state.rb
│ │ ├── state_machine.rb
│ │ ├── transition.rb
│ │ └── version.rb
│ └── aasm.rb
└── spec/
├── database.yml
├── en.yml
├── en_deprecated_style.yml
├── models/
│ ├── active_record/
│ │ └── api.rb
│ ├── argument.rb
│ ├── auth_machine.rb
│ ├── bar.rb
│ ├── callback_new_dsl.rb
│ ├── callback_old_dsl.rb
│ ├── conversation.rb
│ ├── father.rb
│ ├── foo.rb
│ ├── invalid_persistor.rb
│ ├── mongoid/
│ │ ├── simple_mongoid.rb
│ │ └── simple_new_dsl_mongoid.rb
│ ├── not_auto_loaded/
│ │ └── process.rb
│ ├── parametrised_event.rb
│ ├── persistence.rb
│ ├── process_with_new_dsl.rb
│ ├── silencer.rb
│ ├── son.rb
│ ├── sub_classing.rb
│ ├── this_name_better_not_be_in_use.rb
│ ├── transactor.rb
│ ├── validator.rb
│ └── worker.rb
├── schema.rb
├── spec_helper.rb
└── unit/
├── api_spec.rb
├── callbacks_spec.rb
├── complex_example_spec.rb
├── event_spec.rb
├── initial_state_spec.rb
├── inspection_spec.rb
├── localizer_spec.rb
├── memory_leak_spec.rb
├── new_dsl_spec.rb
├── persistence/
│ ├── active_record_persistence_spec.rb
│ └── mongoid_persistance_spec.rb
├── simple_example_spec.rb
├── state_spec.rb
├── subclassing_spec.rb
└── transition_spec.rb
SYMBOL INDEX (258 symbols across 45 files)
FILE: lib/aasm/aasm.rb
type AASM (line 1) | module AASM
function included (line 3) | def self.included(base) #:nodoc:
type ClassMethods (line 14) | module ClassMethods
function inherited (line 17) | def inherited(base)
function aasm (line 23) | def aasm(options={}, &block)
function aasm_initial_state (line 30) | def aasm_initial_state(set_state=nil)
function aasm_from_states_for_state (line 40) | def aasm_from_states_for_state(state, options={})
function aasm_initial_state= (line 49) | def aasm_initial_state=(state)
function aasm_state (line 54) | def aasm_state(name, options={})
function aasm_event (line 59) | def aasm_event(name, options = {}, &block)
function aasm_states (line 64) | def aasm_states
function aasm_events (line 69) | def aasm_events
function aasm_states_for_select (line 74) | def aasm_states_for_select
function aasm_human_event_name (line 79) | def aasm_human_event_name(event) # event_name?
function aasm (line 84) | def aasm
function aasm_read_state (line 89) | def aasm_read_state
function aasm_write_state (line 97) | def aasm_write_state(new_state)
function aasm_write_state_without_persistence (line 102) | def aasm_write_state_without_persistence(new_state)
function aasm_current_state (line 107) | def aasm_current_state
function aasm_enter_initial_state (line 113) | def aasm_enter_initial_state
function aasm_events_for_current_state (line 119) | def aasm_events_for_current_state
function aasm_permissible_events_for_current_state (line 125) | def aasm_permissible_events_for_current_state
function aasm_events_for_state (line 131) | def aasm_events_for_state(state_name)
function aasm_human_state (line 137) | def aasm_human_state
function aasm_fire_event (line 144) | def aasm_fire_event(event_name, options, *args)
function fired (line 163) | def fired(event, old_state, new_state_name, options)
function failed (line 195) | def failed(event_name, old_state)
FILE: lib/aasm/base.rb
type AASM (line 1) | module AASM
class Base (line 2) | class Base
method initialize (line 4) | def initialize(clazz, options={}, &block)
method initial_state (line 22) | def initial_state
method state (line 27) | def state(name, options={})
method event (line 42) | def event(name, options={}, &block)
method states (line 65) | def states
method events (line 69) | def events
method states_for_select (line 73) | def states_for_select
FILE: lib/aasm/deprecated/aasm.rb
type AASM (line 1) | module AASM
type ClassMethods (line 3) | module ClassMethods
function human_event_name (line 4) | def human_event_name(*args)
function human_state (line 10) | def human_state
FILE: lib/aasm/errors.rb
type AASM (line 1) | module AASM
class InvalidTransition (line 2) | class InvalidTransition < RuntimeError; end
class UndefinedState (line 3) | class UndefinedState < RuntimeError; end
FILE: lib/aasm/event.rb
type AASM (line 1) | module AASM
class Event (line 2) | class Event
method initialize (line 6) | def initialize(name, options = {}, &block)
method may_fire? (line 15) | def may_fire?(obj, to_state=nil, *args)
method fire (line 19) | def fire(obj, to_state=nil, *args)
method transitions_from_state? (line 23) | def transitions_from_state?(state)
method transitions_from_state (line 27) | def transitions_from_state(state)
method transitions_to_state? (line 31) | def transitions_to_state?(state)
method transitions_to_state (line 35) | def transitions_to_state(state)
method all_transitions (line 40) | def all_transitions
method fire_callbacks (line 45) | def fire_callbacks(callback_name, record, *args)
method == (line 49) | def ==(event)
method update (line 59) | def update(options = {}, &block)
method _fire (line 68) | def _fire(obj, test, to_state=nil, *args)
method invoke_callbacks (line 93) | def invoke_callbacks(code, record, args)
method transitions (line 110) | def transitions(trans_opts=nil)
FILE: lib/aasm/instance_base.rb
type AASM (line 1) | module AASM
class InstanceBase (line 2) | class InstanceBase
method initialize (line 4) | def initialize(instance)
method current_state (line 8) | def current_state
method current_state= (line 12) | def current_state=(state)
method enter_initial_state (line 17) | def enter_initial_state
method human_state (line 29) | def human_state
method states (line 33) | def states(options={})
method events (line 46) | def events(state=current_state)
method permissible_events (line 54) | def permissible_events
method state_object_for_name (line 58) | def state_object_for_name(name)
method determine_state_name (line 64) | def determine_state_name(state)
method may_fire_event? (line 75) | def may_fire_event?(name, *args)
method set_current_state_with_persistence (line 80) | def set_current_state_with_persistence(state)
FILE: lib/aasm/localizer.rb
type AASM (line 1) | module AASM
class Localizer (line 2) | class Localizer
method human_event_name (line 3) | def human_event_name(klass, event)
method human_state_name (line 11) | def human_state_name(klass, state)
method item_for (line 22) | def item_for(klass, state, ancestor, options={})
method translate_queue (line 27) | def translate_queue(checklist)
method i18n_scope (line 39) | def i18n_scope(klass)
method i18n_klass (line 44) | def i18n_klass(klass)
method ancestors_list (line 48) | def ancestors_list(klass)
FILE: lib/aasm/persistence.rb
type AASM (line 1) | module AASM
type Persistence (line 2) | module Persistence
function load_persistence (line 5) | def load_persistence(base)
function require_files_for (line 20) | def require_files_for(persistence)
FILE: lib/aasm/persistence/active_record_persistence.rb
type AASM (line 1) | module AASM
type Persistence (line 2) | module Persistence
type ActiveRecordPersistence (line 3) | module ActiveRecordPersistence
function included (line 29) | def self.included(base)
type ClassMethods (line 41) | module ClassMethods
function find_in_state (line 43) | def find_in_state(number, state, *args)
function count_in_state (line 49) | def count_in_state(state, *args)
function calculate_in_state (line 55) | def calculate_in_state(state, *args)
function with_state_scope (line 62) | def with_state_scope(state)
type InstanceMethods (line 69) | module InstanceMethods
function aasm_write_state (line 80) | def aasm_write_state(state)
function aasm_write_state_without_persistence (line 109) | def aasm_write_state_without_persistence(state)
function aasm_ensure_initial_state (line 130) | def aasm_ensure_initial_state
function aasm_fire_event (line 134) | def aasm_fire_event(name, options, *args)
FILE: lib/aasm/persistence/base.rb
type AASM (line 1) | module AASM
type Persistence (line 2) | module Persistence
type Base (line 3) | module Base
function included (line 5) | def self.included(base) #:nodoc:
function aasm_read_state (line 35) | def aasm_read_state
type ClassMethods (line 44) | module ClassMethods
function aasm_column (line 70) | def aasm_column(column_name=nil)
class Base (line 86) | class Base
method state_with_scope (line 88) | def state_with_scope(name, *args)
FILE: lib/aasm/persistence/mongoid_persistence.rb
type AASM (line 1) | module AASM
type Persistence (line 2) | module Persistence
type MongoidPersistence (line 3) | module MongoidPersistence
function included (line 31) | def self.included(base)
type ClassMethods (line 41) | module ClassMethods
function find_in_state (line 43) | def find_in_state(number, state, *args)
function count_in_state (line 49) | def count_in_state(state, *args)
function with_state_scope (line 55) | def with_state_scope(state)
type InstanceMethods (line 63) | module InstanceMethods
function aasm_write_state (line 75) | def aasm_write_state(state)
function aasm_write_state_without_persistence (line 99) | def aasm_write_state_without_persistence(state)
function aasm_ensure_initial_state (line 120) | def aasm_ensure_initial_state
type NamedScopeMethods (line 125) | module NamedScopeMethods
function aasm_state_with_named_scope (line 126) | def aasm_state_with_named_scope name, options = {}
FILE: lib/aasm/state.rb
type AASM (line 1) | module AASM
class State (line 2) | class State
method initialize (line 5) | def initialize(name, clazz, options={})
method == (line 11) | def ==(state)
method <=> (line 19) | def <=>(state)
method to_s (line 27) | def to_s
method fire_callbacks (line 31) | def fire_callbacks(action, record)
method display_name (line 40) | def display_name
method localized_name (line 50) | def localized_name
method for_select (line 54) | def for_select
method update (line 60) | def update(options = {})
method _fire_callbacks (line 68) | def _fire_callbacks(action, record)
FILE: lib/aasm/state_machine.rb
type AASM (line 1) | module AASM
class StateMachine (line 2) | class StateMachine
method [] (line 5) | def self.[](clazz)
method []= (line 9) | def self.[]=(clazz, machine)
method initialize (line 17) | def initialize(name)
method initialize_copy (line 26) | def initialize_copy(orig)
method add_state (line 32) | def add_state(name, clazz, options)
FILE: lib/aasm/transition.rb
type AASM (line 1) | module AASM
class Transition (line 2) | class Transition
method initialize (line 6) | def initialize(opts)
method perform (line 12) | def perform(obj, *args)
method execute (line 23) | def execute(obj, *args)
method == (line 29) | def ==(obj)
method from? (line 33) | def from?(value)
method _execute (line 39) | def _execute(obj, on_transition, *args)
FILE: lib/aasm/version.rb
type AASM (line 1) | module AASM
FILE: spec/models/active_record/api.rb
class DefaultState (line 1) | class DefaultState
class ProvidedState (line 14) | class ProvidedState
method aasm_read_state (line 26) | def aasm_read_state
method aasm_write_state (line 30) | def aasm_write_state(new_state)
method aasm_write_state_without_persistence (line 34) | def aasm_write_state_without_persistence(new_state)
class PersistedState (line 39) | class PersistedState < ActiveRecord::Base
class ProvidedAndPersistedState (line 52) | class ProvidedAndPersistedState < ActiveRecord::Base
method aasm_read_state (line 64) | def aasm_read_state
method aasm_write_state (line 68) | def aasm_write_state(new_state)
method aasm_write_state_without_persistence (line 72) | def aasm_write_state_without_persistence(new_state)
FILE: spec/models/argument.rb
class Argument (line 1) | class Argument
FILE: spec/models/auth_machine.rb
class AuthMachine (line 1) | class AuthMachine
method initialize (line 46) | def initialize
method make_activation_code (line 52) | def make_activation_code
method do_activate (line 56) | def do_activate
method do_delete (line 61) | def do_delete
method do_undelete (line 65) | def do_undelete
method can_register? (line 69) | def can_register?
method has_activated? (line 73) | def has_activated?
method has_activation_code? (line 77) | def has_activation_code?
method if_polite? (line 81) | def if_polite?(phrase = nil)
FILE: spec/models/bar.rb
class Bar (line 1) | class Bar
class Baz (line 14) | class Baz < Bar
FILE: spec/models/callback_new_dsl.rb
class CallbackNewDsl (line 1) | class CallbackNewDsl
method before_enter_open (line 28) | def before_enter_open; end
method before_exit_open (line 29) | def before_exit_open; end
method after_enter_open (line 30) | def after_enter_open; end
method after_exit_open (line 31) | def after_exit_open; end
method before_enter_closed (line 33) | def before_enter_closed; end
method before_exit_closed (line 34) | def before_exit_closed; end
method after_enter_closed (line 35) | def after_enter_closed; end
method after_exit_closed (line 36) | def after_exit_closed; end
method before (line 38) | def before; end
method after (line 39) | def after; end
method enter_closed (line 41) | def enter_closed; end
method exit_open (line 42) | def exit_open; end
FILE: spec/models/callback_old_dsl.rb
class CallbackOldDsl (line 1) | class CallbackOldDsl
method before_enter_open (line 26) | def before_enter_open; end
method before_exit_open (line 27) | def before_exit_open; end
method after_enter_open (line 28) | def after_enter_open; end
method after_exit_open (line 29) | def after_exit_open; end
method before_enter_closed (line 31) | def before_enter_closed; end
method before_exit_closed (line 32) | def before_exit_closed; end
method after_enter_closed (line 33) | def after_enter_closed; end
method after_exit_closed (line 34) | def after_exit_closed; end
method before (line 36) | def before; end
method after (line 37) | def after; end
method enter_closed (line 39) | def enter_closed; end
method exit_open (line 40) | def exit_open; end
FILE: spec/models/conversation.rb
class Conversation (line 1) | class Conversation
method initialize (line 33) | def initialize(persister)
method aasm_read_state (line 39) | def aasm_read_state
method aasm_write_state (line 43) | def aasm_write_state(state)
FILE: spec/models/father.rb
class Father (line 3) | class Father < ActiveRecord::Base
method update_state (line 15) | def update_state
FILE: spec/models/foo.rb
class Foo (line 1) | class Foo
method always_false (line 16) | def always_false
method success_callback (line 20) | def success_callback
method enter (line 23) | def enter
method exit (line 25) | def exit
class FooTwo (line 29) | class FooTwo < Foo
FILE: spec/models/invalid_persistor.rb
class InvalidPersistor (line 3) | class InvalidPersistor < ActiveRecord::Base
FILE: spec/models/mongoid/simple_mongoid.rb
class SimpleMongoid (line 1) | class SimpleMongoid
FILE: spec/models/mongoid/simple_new_dsl_mongoid.rb
class SimpleNewDslMongoid (line 1) | class SimpleNewDslMongoid
FILE: spec/models/not_auto_loaded/process.rb
type Models (line 1) | module Models
class Process (line 2) | class Process
FILE: spec/models/parametrised_event.rb
class ParametrisedEvent (line 1) | class ParametrisedEvent
method wear_clothes (line 21) | def wear_clothes(shirt_color, trouser_type)
method condition_hair (line 24) | def condition_hair
method fix_hair (line 27) | def fix_hair
FILE: spec/models/persistence.rb
class Gate (line 1) | class Gate < ActiveRecord::Base
class Reader (line 17) | class Reader < ActiveRecord::Base
method aasm_read_state (line 20) | def aasm_read_state
class Writer (line 25) | class Writer < ActiveRecord::Base
method aasm_write_state (line 26) | def aasm_write_state(state)
class Transient (line 32) | class Transient < ActiveRecord::Base
method aasm_write_state_without_persistence (line 33) | def aasm_write_state_without_persistence(state)
class Simple (line 39) | class Simple < ActiveRecord::Base
class SimpleNewDsl (line 46) | class SimpleNewDsl < ActiveRecord::Base
class Derivate (line 55) | class Derivate < Simple
class DerivateNewDsl (line 58) | class DerivateNewDsl < SimpleNewDsl
class Thief (line 61) | class Thief < ActiveRecord::Base
FILE: spec/models/process_with_new_dsl.rb
class ProcessWithNewDsl (line 1) | class ProcessWithNewDsl
method state (line 4) | def self.state(*args)
method flag (line 23) | def flag
method event (line 27) | def self.event(*args)
FILE: spec/models/silencer.rb
class Silencer (line 1) | class Silencer
FILE: spec/models/son.rb
class Son (line 1) | class Son < Father
FILE: spec/models/sub_classing.rb
class SubClassing (line 1) | class SubClassing < Silencer
FILE: spec/models/this_name_better_not_be_in_use.rb
class ThisNameBetterNotBeInUse (line 1) | class ThisNameBetterNotBeInUse
FILE: spec/models/transactor.rb
class Transactor (line 2) | class Transactor < ActiveRecord::Base
method start_worker (line 18) | def start_worker
method fail (line 22) | def fail
FILE: spec/models/validator.rb
class Validator (line 3) | class Validator < ActiveRecord::Base
FILE: spec/models/worker.rb
class Worker (line 1) | class Worker < ActiveRecord::Base
FILE: spec/spec_helper.rb
function load_schema (line 15) | def load_schema
FILE: spec/unit/callbacks_spec.rb
class Foo (line 42) | class Foo
function error_callback (line 52) | def @foo.error_callback(e); end
function aasm_event_fired (line 74) | def @foo.aasm_event_fired(event, from, to); end
function aasm_event_failed (line 97) | def @foo.aasm_event_failed(event, from); end
FILE: spec/unit/event_spec.rb
function aasm_write_state (line 263) | def foo.aasm_write_state; end
FILE: spec/unit/initial_state_spec.rb
class Banker (line 3) | class Banker
method initialize (line 12) | def initialize(balance = 0); self.balance = balance; end
method rich? (line 13) | def rich?; self.balance >= RICH; end
FILE: spec/unit/localizer_spec.rb
class LocalizerTestModel (line 8) | class LocalizerTestModel < ActiveRecord::Base
FILE: spec/unit/simple_example_spec.rb
class Payment (line 3) | class Payment
FILE: spec/unit/state_spec.rb
function new_state (line 9) | def new_state(options={})
Condensed preview — 70 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (117K chars).
[
{
"path": ".document",
"chars": 62,
"preview": "README.rdoc\nlib/**/*.rb\nbin/*\nfeatures/**/*.feature\n-\nLICENSE\n"
},
{
"path": ".gitignore",
"chars": 98,
"preview": "*.sw?\n*~\n.DS_Store\n.idea\ncoverage\npkg\nrdoc\nGemfile.lock\nspec/debug.log\nspec/*.db\nTODO\n.rvmrc\nalto\n"
},
{
"path": ".travis.yml",
"chars": 186,
"preview": "language: ruby\nrvm:\n - 1.8.7\n - 1.9.2\n - 1.9.3\n - 2.0.0\n # - jruby-18mode # JRuby in 1.8 mode\n # - jruby-19mode # "
},
{
"path": "API",
"chars": 769,
"preview": "\nOverwrite method to read the current state. Used to provide another storage mechanism,\ndifferent from the standard Rail"
},
{
"path": "CHANGELOG.md",
"chars": 2538,
"preview": "# CHANGELOG\n\n## 3.0.19\n\n * fixed deprecation warning with *Rails 4* (`Relation#update_all` with conditions is deprecated"
},
{
"path": "Gemfile",
"chars": 39,
"preview": "source 'https://rubygems.org'\n\ngemspec\n"
},
{
"path": "HOWTO",
"chars": 139,
"preview": "How to\n\n1. Run tests for Mongoid\n\nStart MongoDB\n\n $> mongod\n\nRun the specs\n\n $> rspec spec/unit/persistence/mongoid_pe"
},
{
"path": "LICENSE",
"chars": 1061,
"preview": "Copyright (c) 2006-2012 Scott Barron\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of th"
},
{
"path": "README.md",
"chars": 9445,
"preview": "# AASM - Ruby state machines [](http://travis-ci.org/aasm/aas"
},
{
"path": "Rakefile",
"chars": 574,
"preview": "require 'bundler/gem_tasks'\n\nrequire 'rspec/core'\nrequire 'rspec/core/rake_task'\nRSpec::Core::RakeTask.new(:spec) do |sp"
},
{
"path": "aasm.gemspec",
"chars": 1522,
"preview": "# -*- encoding: utf-8 -*-\n$:.push File.expand_path(\"../lib\", __FILE__)\nrequire \"aasm/version\"\n\nGem::Specification.new do"
},
{
"path": "lib/aasm/aasm.rb",
"chars": 5921,
"preview": "module AASM\n\n def self.included(base) #:nodoc:\n base.extend AASM::ClassMethods\n\n # do not overwrite existing stat"
},
{
"path": "lib/aasm/base.rb",
"chars": 2462,
"preview": "module AASM\n class Base\n\n def initialize(clazz, options={}, &block)\n @clazz = clazz\n @state_machine = AASM"
},
{
"path": "lib/aasm/deprecated/aasm.rb",
"chars": 423,
"preview": "module AASM\n\n module ClassMethods\n def human_event_name(*args)\n warn \"AASM.human_event_name is deprecated and w"
},
{
"path": "lib/aasm/errors.rb",
"chars": 105,
"preview": "module AASM\n class InvalidTransition < RuntimeError; end\n class UndefinedState < RuntimeError; end\nend\n"
},
{
"path": "lib/aasm/event.rb",
"chars": 3610,
"preview": "module AASM\n class Event\n\n attr_reader :name, :options\n\n def initialize(name, options = {}, &block)\n @name ="
},
{
"path": "lib/aasm/instance_base.rb",
"chars": 2649,
"preview": "module AASM\n class InstanceBase\n\n def initialize(instance)\n @instance = instance\n end\n\n def current_state"
},
{
"path": "lib/aasm/localizer.rb",
"chars": 1721,
"preview": "module AASM\n class Localizer\n def human_event_name(klass, event)\n checklist = ancestors_list(klass).inject([]) "
},
{
"path": "lib/aasm/persistence/active_record_persistence.rb",
"chars": 4267,
"preview": "module AASM\n module Persistence\n module ActiveRecordPersistence\n # This method:\n #\n # * extends the m"
},
{
"path": "lib/aasm/persistence/base.rb",
"chars": 3708,
"preview": "module AASM\n module Persistence\n module Base\n\n def self.included(base) #:nodoc:\n base.extend ClassMethod"
},
{
"path": "lib/aasm/persistence/mongoid_persistence.rb",
"chars": 4136,
"preview": "module AASM\n module Persistence\n module MongoidPersistence\n # This method:\n #\n # * extends the model "
},
{
"path": "lib/aasm/persistence.rb",
"chars": 859,
"preview": "module AASM\n module Persistence\n class << self\n\n def load_persistence(base)\n # Use a fancier auto-loadin"
},
{
"path": "lib/aasm/state.rb",
"chars": 1464,
"preview": "module AASM\n class State\n attr_reader :name, :options\n\n def initialize(name, clazz, options={})\n @name = nam"
},
{
"path": "lib/aasm/state_machine.rb",
"chars": 877,
"preview": "module AASM\n class StateMachine\n\n # the following two methods provide the storage of all state machines\n def self"
},
{
"path": "lib/aasm/transition.rb",
"chars": 1183,
"preview": "module AASM\n class Transition\n attr_reader :from, :to, :opts\n alias_method :options, :opts\n\n def initialize(op"
},
{
"path": "lib/aasm/version.rb",
"chars": 37,
"preview": "module AASM\n VERSION = \"3.0.19\"\nend\n"
},
{
"path": "lib/aasm.rb",
"chars": 399,
"preview": "require 'ostruct'\n\n%w(\n version\n errors\n base\n instance_base\n transition\n event\n state\n localize"
},
{
"path": "spec/database.yml",
"chars": 61,
"preview": "sqlite3:\n adapter: sqlite3\n database: spec/aasm.sqlite3.db\n"
},
{
"path": "spec/en.yml",
"chars": 182,
"preview": "en:\n activerecord:\n events:\n localizer_test_model:\n close: \"Let's close it!\"\n\n attributes:\n loca"
},
{
"path": "spec/en_deprecated_style.yml",
"chars": 193,
"preview": "en:\n activerecord:\n events:\n localizer_test_model:\n close: \"Let's close it!\"\n\n attributes:\n loca"
},
{
"path": "spec/models/active_record/api.rb",
"chars": 1548,
"preview": "class DefaultState\n attr_accessor :transient_store, :persisted_store\n include AASM\n aasm do\n state :alpha, :initia"
},
{
"path": "spec/models/argument.rb",
"chars": 186,
"preview": "class Argument\n include AASM\n aasm do\n state :invalid, :initial => true\n state :valid\n\n event :valid do\n "
},
{
"path": "spec/models/auth_machine.rb",
"chars": 1982,
"preview": "class AuthMachine\n include AASM\n\n attr_accessor :activation_code, :activated_at, :deleted_at\n\n aasm do\n state :pas"
},
{
"path": "spec/models/bar.rb",
"chars": 177,
"preview": "class Bar\n include AASM\n\n aasm do\n state :read\n state :ended\n\n event :foo do\n transitions :to => :ended,"
},
{
"path": "spec/models/callback_new_dsl.rb",
"chars": 1076,
"preview": "class CallbackNewDsl\n include AASM\n\n aasm do\n state :open, :initial => true,\n :before_enter => :before_enter_o"
},
{
"path": "spec/models/callback_old_dsl.rb",
"chars": 1052,
"preview": "class CallbackOldDsl\n include AASM\n\n aasm_initial_state :open\n aasm_state :open,\n :before_enter => :before_enter_o"
},
{
"path": "spec/models/conversation.rb",
"chars": 738,
"preview": "class Conversation\n include AASM\n\n aasm do\n state :needs_attention, :initial => true\n state :read\n state :clo"
},
{
"path": "spec/models/father.rb",
"chars": 378,
"preview": "require 'active_record'\n\nclass Father < ActiveRecord::Base\n include AASM\n\n aasm do\n state :missing_details, :initia"
},
{
"path": "spec/models/foo.rb",
"chars": 520,
"preview": "class Foo\n include AASM\n aasm do\n state :open, :initial => true, :exit => :exit\n state :closed, :enter => :enter"
},
{
"path": "spec/models/invalid_persistor.rb",
"chars": 406,
"preview": "require 'active_record'\n\nclass InvalidPersistor < ActiveRecord::Base\n include AASM\n aasm :column => :status, :skip_val"
},
{
"path": "spec/models/mongoid/simple_mongoid.rb",
"chars": 167,
"preview": "class SimpleMongoid\n include Mongoid::Document\n include AASM\n\n field :status, type: String\n\n aasm_column :status\n a"
},
{
"path": "spec/models/mongoid/simple_new_dsl_mongoid.rb",
"chars": 187,
"preview": "class SimpleNewDslMongoid\n include Mongoid::Document\n include AASM\n\n field :status, type: String\n\n aasm :column => :"
},
{
"path": "spec/models/not_auto_loaded/process.rb",
"chars": 311,
"preview": "module Models\n class Process\n include AASM\n\n aasm_state :sleeping\n aasm_state :running\n aasm_state :suspend"
},
{
"path": "spec/models/parametrised_event.rb",
"chars": 739,
"preview": "class ParametrisedEvent\n include AASM\n aasm do\n state :sleeping, :initial => true\n state :showering\n state :w"
},
{
"path": "spec/models/persistence.rb",
"chars": 1248,
"preview": "class Gate < ActiveRecord::Base\n include AASM\n\n # Fake this column for testing purposes\n attr_accessor :aasm_state\n\n "
},
{
"path": "spec/models/process_with_new_dsl.rb",
"chars": 519,
"preview": "class ProcessWithNewDsl\n include AASM\n\n def self.state(*args)\n raise \"wrong state method\"\n end\n\n attr_accessor :f"
},
{
"path": "spec/models/silencer.rb",
"chars": 379,
"preview": "class Silencer\n include AASM\n\n aasm :whiny_transitions => false do\n state :silent, :initial => true\n state :cryi"
},
{
"path": "spec/models/son.rb",
"chars": 38,
"preview": "class Son < Father\n include AASM\nend\n"
},
{
"path": "spec/models/sub_classing.rb",
"chars": 35,
"preview": "class SubClassing < Silencer\n \nend"
},
{
"path": "spec/models/this_name_better_not_be_in_use.rb",
"chars": 155,
"preview": "class ThisNameBetterNotBeInUse\n include AASM\n\n aasm do\n state :initial\n state :symbol\n state :string\n stat"
},
{
"path": "spec/models/transactor.rb",
"chars": 483,
"preview": "require 'active_record'\nclass Transactor < ActiveRecord::Base\n\n belongs_to :worker\n\n include AASM\n aasm :column => :s"
},
{
"path": "spec/models/validator.rb",
"chars": 365,
"preview": "require 'active_record'\n\nclass Validator < ActiveRecord::Base\n include AASM\n aasm :column => :status do\n state :sle"
},
{
"path": "spec/models/worker.rb",
"chars": 38,
"preview": "class Worker < ActiveRecord::Base\nend\n"
},
{
"path": "spec/schema.rb",
"chars": 839,
"preview": "ActiveRecord::Schema.define(:version => 0) do\n\n %w{gates readers writers transients simples simple_new_dsls thieves loc"
},
{
"path": "spec/spec_helper.rb",
"chars": 909,
"preview": "$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))\n$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname("
},
{
"path": "spec/unit/api_spec.rb",
"chars": 1685,
"preview": "require 'spec_helper'\nrequire 'models/active_record/api.rb'\n\ndescribe \"reading the current state\" do\n it \"uses the AASM"
},
{
"path": "spec/unit/callbacks_spec.rb",
"chars": 4135,
"preview": "require 'spec_helper'\n\ndescribe 'callbacks for the old DSL' do\n let(:callback) {CallbackOldDsl.new}\n\n it \"should get c"
},
{
"path": "spec/unit/complex_example_spec.rb",
"chars": 1963,
"preview": "require 'spec_helper'\n\ndescribe 'on initialization' do\n let(:auth) {AuthMachine.new}\n\n it 'should be in the pending st"
},
{
"path": "spec/unit/event_spec.rb",
"chars": 7908,
"preview": "require 'spec_helper'\n\ndescribe 'adding an event' do\n let(:event) do\n AASM::Event.new(:close_order, {:success => :su"
},
{
"path": "spec/unit/initial_state_spec.rb",
"chars": 829,
"preview": "require 'spec_helper'\n\nclass Banker\n include AASM\n aasm do\n state :retired\n state :selling_bad_mortgages\n end\n "
},
{
"path": "spec/unit/inspection_spec.rb",
"chars": 3439,
"preview": "require 'spec_helper'\n\ndescribe 'inspection for common cases' do\n it 'should support the old DSL' do\n Foo.should res"
},
{
"path": "spec/unit/localizer_spec.rb",
"chars": 2629,
"preview": "require 'spec_helper'\nrequire 'active_record'\nrequire 'logger'\nrequire 'i18n'\n\nload_schema\n\nclass LocalizerTestModel < A"
},
{
"path": "spec/unit/memory_leak_spec.rb",
"chars": 1799,
"preview": "# require 'spec_helper'\n\n# describe \"state machines\" do\n\n# def number_of_objects(clazz)\n# ObjectSpace.each_object("
},
{
"path": "spec/unit/new_dsl_spec.rb",
"chars": 350,
"preview": "require 'spec_helper'\n\ndescribe \"the new dsl\" do\n\n let(:process) {ProcessWithNewDsl.new}\n\n it 'should not conflict wit"
},
{
"path": "spec/unit/persistence/active_record_persistence_spec.rb",
"chars": 5448,
"preview": "require 'active_record'\nrequire 'logger'\nrequire 'spec_helper'\n\nload_schema\n\n# if you want to see the statements while r"
},
{
"path": "spec/unit/persistence/mongoid_persistance_spec.rb",
"chars": 3871,
"preview": "describe 'mongoid', :if => Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3') do\n# describe 'mongoid'"
},
{
"path": "spec/unit/simple_example_spec.rb",
"chars": 1837,
"preview": "require 'spec_helper'\n\nclass Payment\n include AASM\n aasm do\n state :initialised, :initial => true\n state :filled"
},
{
"path": "spec/unit/state_spec.rb",
"chars": 2305,
"preview": "require 'spec_helper'\n\ndescribe AASM::State do\n before(:each) do\n @name = :astate\n @options = { :crazy_custom_"
},
{
"path": "spec/unit/subclassing_spec.rb",
"chars": 739,
"preview": "require 'spec_helper'\n\ndescribe 'subclassing' do\n let(:son) {Son.new}\n\n it 'should have the parent states' do\n Foo."
},
{
"path": "spec/unit/transition_spec.rb",
"chars": 5133,
"preview": "require 'spec_helper'\n\ndescribe 'transitions' do\n\n it 'should raise an exception when whiny' do\n process = ProcessWi"
}
]
About this extraction
This page contains the full source code of the rubyist/aasm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 70 files (106.7 KB), approximately 30.3k tokens, and a symbol index with 258 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.