simpler_state_machine 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.DS_Store +0 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +98 -0
- data/Rakefile +28 -0
- data/lib/simple_state_machine/base.rb +169 -0
- data/lib/simple_state_machine/enum.rb +29 -0
- data/lib/simple_state_machine/errors.rb +6 -0
- data/lib/simple_state_machine/event.rb +5 -0
- data/lib/simple_state_machine/simple_state_machine.rb +86 -0
- data/lib/simple_state_machine/transition.rb +13 -0
- data/lib/simple_state_machine/version.rb +3 -0
- data/lib/simple_state_machine.rb +9 -0
- data/simpler_state_machine.gemspec +28 -0
- data/spec/database.yml +3 -0
- data/spec/models/express_gate.rb +21 -0
- data/spec/models/gate.rb +33 -0
- data/spec/models/king_gate.rb +14 -0
- data/spec/schema.rb +9 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/unit/simple_state_machine_spec.rb +297 -0
- metadata +227 -0
data/.DS_Store
ADDED
Binary file
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Elad Meidar
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# Note:
|
2
|
+
|
3
|
+
This code used to contain some offensive wording as part of our test suite that was not removed or at least moderated while
|
4
|
+
moving this project from private to public status. the people that worked on this project and myself especially are sincerely sorry and we absolutely 100% didn't mean
|
5
|
+
to offend anyone. sorry. :(
|
6
|
+
|
7
|
+
# SimplerStateMachine
|
8
|
+
|
9
|
+
__SimplerStateMachine__ is a simple (suprisingly) state machine aimed to provide a simpler implementation and setup for ruby based state machines.
|
10
|
+
It supports:
|
11
|
+
|
12
|
+
* States as enum
|
13
|
+
* Events / Callbacks
|
14
|
+
* Transitions
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
gem 'simpler_state_machine'
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
$ gem install simple_state_machine
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
__SimplerStateMachine__ provides an easy interface to add states and transitions:
|
33
|
+
|
34
|
+
|
35
|
+
### Declaration
|
36
|
+
|
37
|
+
Class Elad
|
38
|
+
|
39
|
+
include SimpleStateMachine
|
40
|
+
|
41
|
+
state_machine do |sm|
|
42
|
+
sm.initial_state "happy"
|
43
|
+
sm.state_field "status"
|
44
|
+
sm.add_state "mad", :before_enter => :kill_miki,
|
45
|
+
:after_enter => :kiss_miki,
|
46
|
+
:before_leave => :kick_miki,
|
47
|
+
:after_leave => :keep_miki,
|
48
|
+
:on_error => :tell_mom
|
49
|
+
sm.add_state 'calm', :after_enter => :kiss_miki
|
50
|
+
sm.add_transition :get_angry, :from => "happy", :to => "mad"
|
51
|
+
sm.add_transition :calmate, :from => ["happy", "mad"], :to => "calm"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
### Configuration Options
|
56
|
+
|
57
|
+
* `initial_state`: Sets up the initial state the class will get when instantiating a new instance.
|
58
|
+
* `state_field`: the field / attribute that keeps the status enum (defaults to "status")
|
59
|
+
* `add_state`: adding a state, accepts a state name and a list of callbacks.
|
60
|
+
* `add_transition`: defines a transtions with destination state and source state (or multiple in an array)
|
61
|
+
* `persistency_method`: __SimpleStateMachine__ doesn't rely on the existance of a persistency layer (ActiveRecord for example), so if you want to have this option you'll need to specify the persistency method to use (in ActiveRecord's case, use "save" or "save!")
|
62
|
+
|
63
|
+
### Accessing States
|
64
|
+
|
65
|
+
__SimplerStateMachine__ will save the state's enum value as integer to the defined attribute, in order to access the string / constant representation of the state use `#enum_state`:
|
66
|
+
|
67
|
+
> elad = Elad.new
|
68
|
+
> elad.status
|
69
|
+
0
|
70
|
+
> elad.human_status_name
|
71
|
+
"happy"
|
72
|
+
|
73
|
+
### Callbacks / Events
|
74
|
+
|
75
|
+
__SimplerStateMachine__ makes several callbacks available when defining a state and will fire them when a state transtion is made.
|
76
|
+
The available callbacks are:
|
77
|
+
|
78
|
+
* `:before_enter`: runs before the state is entered.
|
79
|
+
* `:after_enter`: runs after the state transition is completed
|
80
|
+
* `:before_leave`: runs before a transition to another state starts
|
81
|
+
* `:after_leave`: runs after a transition to another state runs
|
82
|
+
* `:on_error`: runs when the transition fails
|
83
|
+
|
84
|
+
The order in which the callbacks are called is:
|
85
|
+
|
86
|
+
before_leave (of the current state)
|
87
|
+
before_enter (of the destination state)
|
88
|
+
<transition>
|
89
|
+
after_leave (of the current state)
|
90
|
+
after_enter (of the destination state)
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
1. Fork it
|
95
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
96
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
97
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
98
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
8
|
+
spec.rspec_opts = "--format documentation --color"
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'rake/testtask'
|
12
|
+
Rake::TestTask.new(:test) do |test|
|
13
|
+
test.libs << 'lib' << 'test'
|
14
|
+
test.pattern = 'test/**/*_test.rb'
|
15
|
+
test.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'rdoc/task'
|
19
|
+
require 'simple_state_machine/version'
|
20
|
+
|
21
|
+
Rake::RDocTask.new do |rdoc|
|
22
|
+
rdoc.rdoc_dir = 'rdoc'
|
23
|
+
rdoc.title = "Simple State Machine #{SimpleStateMachine::VERSION}"
|
24
|
+
rdoc.rdoc_files.include('README*')
|
25
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
26
|
+
end
|
27
|
+
|
28
|
+
task :default => :spec
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module SimpleStateMachine
|
2
|
+
class Base
|
3
|
+
|
4
|
+
attr_reader :states, # Holds the state enum
|
5
|
+
:events, # Hash of hashes, holds the callbacks list from each state when the state name is the key
|
6
|
+
:transitions, # Hash of hashes, holds the list of available transitions for each state.
|
7
|
+
:initial_state, # Define the initial state.
|
8
|
+
:state_field, # The attribute that will store the status integer representation
|
9
|
+
:persistency_method, # a method to ensure some persistency on a record. refer to the readme.
|
10
|
+
:states_constant_name # the name of the constant to be defined with the states enum
|
11
|
+
|
12
|
+
attr_accessor :clazz
|
13
|
+
|
14
|
+
def initialize(clazz)
|
15
|
+
@clazz = clazz
|
16
|
+
@_states = []
|
17
|
+
@events = {}
|
18
|
+
@transitions = {}
|
19
|
+
@state_field = "status"
|
20
|
+
@states_constant_name = "STATES"
|
21
|
+
@persistency_method = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# A #clone callback. we ensure that even the sub elements are cloned - 'deep copy'
|
25
|
+
def initialize_copy(orig)
|
26
|
+
super
|
27
|
+
@states = orig.states.clone
|
28
|
+
@_states = orig.instance_variable_get("@_states").clone
|
29
|
+
@events = orig.events.clone
|
30
|
+
@transitions = orig.transitions.clone
|
31
|
+
@initial_state = orig.initial_state.clone
|
32
|
+
@state_field = orig.state_field
|
33
|
+
@persistency_method = orig.persistency_method.clone unless @persistency_method.nil?
|
34
|
+
@states_constant_name = orig.states_constant_name.clone
|
35
|
+
end
|
36
|
+
|
37
|
+
def states_constant_name(constant_name = nil)
|
38
|
+
if constant_name.nil?
|
39
|
+
@states_constant_name
|
40
|
+
else
|
41
|
+
@states_constant_name = constant_name.upcase
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def persistency_method(method_name = nil)
|
46
|
+
if method_name.nil?
|
47
|
+
@persistency_method
|
48
|
+
else
|
49
|
+
@persistency_method = method_name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def state_field(state_field = nil)
|
54
|
+
if state_field.nil?
|
55
|
+
@state_field
|
56
|
+
else
|
57
|
+
@state_field = state_field
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def initial_state(initial_state = nil)
|
62
|
+
if initial_state.nil?
|
63
|
+
@initial_state
|
64
|
+
else
|
65
|
+
add_state(initial_state)
|
66
|
+
@initial_state = initial_state
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Add a new state
|
71
|
+
def add_state(state_name, options = {})
|
72
|
+
|
73
|
+
# Fill in missing with callbacks with empty procs.
|
74
|
+
options = { :before_enter => Proc.new { true },
|
75
|
+
:after_enter => Proc.new { true },
|
76
|
+
:before_leave => Proc.new { true },
|
77
|
+
:after_leave => Proc.new { true },
|
78
|
+
:on_error => Proc.new { |error| true } }.merge(options)
|
79
|
+
|
80
|
+
# Append to the states arry
|
81
|
+
@_states << human_state_name(state_name)
|
82
|
+
|
83
|
+
# Register events
|
84
|
+
@events[human_state_name(state_name)] = options.dup
|
85
|
+
|
86
|
+
@clazz.instance_eval do
|
87
|
+
|
88
|
+
# define a "#state?" method on the object instance
|
89
|
+
define_method("#{state_name}?") do
|
90
|
+
self.send(self.class.state_machine.state_field) == self.class.state_machine.states[state_name].to_i
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Add transition
|
96
|
+
def add_transition(transition_name, options = {})
|
97
|
+
|
98
|
+
# Convert from states to array
|
99
|
+
options[:from] = options[:from].is_a?(Array) ? options[:from] : options[:from].to_a
|
100
|
+
|
101
|
+
# Register transactions
|
102
|
+
@transitions[transition_name.to_s] = options
|
103
|
+
@clazz.instance_eval do
|
104
|
+
|
105
|
+
# Define the transition method
|
106
|
+
define_method("#{transition_name}") do |*args|
|
107
|
+
|
108
|
+
persist = args.first if args.any?
|
109
|
+
persist |= false
|
110
|
+
|
111
|
+
# check if the transition is available to the current state
|
112
|
+
if self.class.state_machine.transitions[transition_name.to_s] && self.class.state_machine.transitions[transition_name.to_s][:from].include?(self.enum_status.to_sym.to_s)
|
113
|
+
|
114
|
+
# Get the current state events
|
115
|
+
current_state_events = self.class.state_machine.events[self.enum_status.to_sym.to_s]
|
116
|
+
next_state_events = self.class.state_machine.events[self.class.state_machine.transitions[transition_name.to_s][:to]]
|
117
|
+
|
118
|
+
begin
|
119
|
+
# Fire events and perform transition (and persistence if needed)
|
120
|
+
fire_state_machine_event(current_state_events[:before_leave])
|
121
|
+
fire_state_machine_event(next_state_events[:before_enter])
|
122
|
+
self.send("#{self.class.state_machine.state_field}=", self.class.state_machine.states[self.class.state_machine.transitions[transition_name.to_s][:to]].to_i)
|
123
|
+
self.send(self.class.state_machine.persistency_method) if self.class.state_machine.persistent_mode? && persist == true
|
124
|
+
fire_state_machine_event(current_state_events[:after_leave])
|
125
|
+
fire_state_machine_event(next_state_events[:after_enter])
|
126
|
+
rescue Exception => e
|
127
|
+
fire_state_machine_event(current_state_events[:on_error])
|
128
|
+
end
|
129
|
+
return true
|
130
|
+
else
|
131
|
+
return false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Define a transition method with a shebang
|
136
|
+
define_method("#{transition_name}!") do
|
137
|
+
if !(self.send(transition_name, true))
|
138
|
+
raise Exceptions::InvalidTransition, "Could not transit '#{self.enum_status.to_sym.to_s}' to '#{options[:to]}'"
|
139
|
+
else
|
140
|
+
return true
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
# Transform the states array to enum
|
148
|
+
def enumize!
|
149
|
+
@states = Enum.new(*@_states)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Persistency method
|
153
|
+
def persistent_mode?
|
154
|
+
!(@persistency_method.nil?)
|
155
|
+
end
|
156
|
+
|
157
|
+
def states
|
158
|
+
@states
|
159
|
+
end
|
160
|
+
|
161
|
+
protected
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def human_state_name(state)
|
166
|
+
state.to_s.downcase
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Enum < Hash
|
2
|
+
|
3
|
+
def initialize(*members)
|
4
|
+
super()
|
5
|
+
@rev = {}
|
6
|
+
members.each_with_index {|m,i| self[i] = m }
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](k)
|
10
|
+
super || @rev[k]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(k,v)
|
14
|
+
@rev[v] = k
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class StrictEnum < Hash
|
20
|
+
|
21
|
+
module Exceptions
|
22
|
+
class MemberNotFound < ArgumentError; end
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](k)
|
26
|
+
super || raise(Exceptions::MemberNotFound, "index/memeber '#{k}' not found in enum")
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
=begin
|
2
|
+
SimpleStateMachine
|
3
|
+
==================
|
4
|
+
|
5
|
+
This module should be mixed in to a class that needs a state machine implementation. refer to the readme.
|
6
|
+
=end
|
7
|
+
module SimpleStateMachine
|
8
|
+
|
9
|
+
def self.included(base) #:nodoc:
|
10
|
+
base.class_eval do
|
11
|
+
extend ClassMethods
|
12
|
+
include InstanceMethods
|
13
|
+
|
14
|
+
# Alias the enum reference method for the confused end user.
|
15
|
+
alias :enum_state :enum_status
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
|
22
|
+
# When inherited - duplicate the state machine from the parent class and allow the extend it
|
23
|
+
def inherited(child)
|
24
|
+
me = self
|
25
|
+
child.class_eval do
|
26
|
+
@sm = me.state_machine.clone
|
27
|
+
@sm.clazz = self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Create or return a state machine
|
32
|
+
def state_machine(&block)
|
33
|
+
return @sm if !(@sm.nil?) && !(block_given?)
|
34
|
+
@sm ||= SimpleStateMachine::Base.new(self)
|
35
|
+
@sm.instance_eval(&block) if block_given?
|
36
|
+
@sm.enumize!
|
37
|
+
self.const_set(@sm.states_constant_name,@sm.states).freeze
|
38
|
+
return @sm
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module InstanceMethods
|
43
|
+
|
44
|
+
# Set the initial status value
|
45
|
+
def initialize(*args)
|
46
|
+
self.send("#{self.class.state_machine.state_field}=", self.class.state_machine.states[self.class.state_machine.initial_state].to_i)
|
47
|
+
|
48
|
+
begin
|
49
|
+
super
|
50
|
+
rescue ArgumentError # Catch in case the super does not get parameters
|
51
|
+
super()
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return the enum status
|
56
|
+
def enum_status
|
57
|
+
self.class.state_machine.states[self.current_state]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return the current state
|
61
|
+
def current_state
|
62
|
+
self.send(self.class.state_machine.state_field)
|
63
|
+
end
|
64
|
+
|
65
|
+
# human name for status
|
66
|
+
def human_status_name
|
67
|
+
enum_status
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Fire a callback, support a symbol, string or an array of symbols / strings
|
73
|
+
def fire_state_machine_event(event)
|
74
|
+
case event.class.name
|
75
|
+
when "Proc"
|
76
|
+
event.call
|
77
|
+
when "Symbol", "String"
|
78
|
+
self.send(event)
|
79
|
+
when "Array"
|
80
|
+
event.each do |e|
|
81
|
+
fire_state_machine_event(e)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Require all the files
|
2
|
+
require File.join(File.dirname(__FILE__), 'simple_state_machine', 'version')
|
3
|
+
require File.join(File.dirname(__FILE__), 'simple_state_machine', 'simple_state_machine')
|
4
|
+
require File.join(File.dirname(__FILE__), 'simple_state_machine', 'base')
|
5
|
+
require File.join(File.dirname(__FILE__), 'simple_state_machine', 'event')
|
6
|
+
require File.join(File.dirname(__FILE__), 'simple_state_machine', 'enum')
|
7
|
+
require File.join(File.dirname(__FILE__), 'simple_state_machine', 'errors')
|
8
|
+
require File.join(File.dirname(__FILE__), 'simple_state_machine', 'transition')
|
9
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/simple_state_machine/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Elad Meidar"]
|
6
|
+
gem.email = ["elad@eizesus.com"]
|
7
|
+
gem.description = "A simple state machine that supports enum"
|
8
|
+
gem.summary = "A simple lightweight state machine that uses an enum type to store states"
|
9
|
+
gem.homepage = "http://devandpencil.herokuapp.com/blog/2013/01/30/simplestatemachine-a-simple-enum-based-state-machine-for-ruby/"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "simpler_state_machine"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = SimpleStateMachine::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'activerecord'
|
19
|
+
gem.add_development_dependency 'rake'
|
20
|
+
gem.add_development_dependency 'sdoc'
|
21
|
+
gem.add_development_dependency 'rspec', '~> 2.0'
|
22
|
+
gem.add_development_dependency 'rr'
|
23
|
+
gem.add_development_dependency 'shoulda'
|
24
|
+
gem.add_development_dependency 'sqlite3'
|
25
|
+
gem.add_development_dependency 'minitest'
|
26
|
+
gem.add_development_dependency 'ruby-debug'
|
27
|
+
gem.add_development_dependency 'ruby-debug-completion'
|
28
|
+
end
|
data/spec/database.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
class ExpressGate < Gate
|
2
|
+
|
3
|
+
attr_accessor :new_express_record
|
4
|
+
|
5
|
+
state_machine do |sm|
|
6
|
+
sm.initial_state "express_beginner"
|
7
|
+
sm.add_state "express_expert", :after_enter => :add_prize
|
8
|
+
sm.add_state "miki_level", :after_enter => :add_prize
|
9
|
+
|
10
|
+
#sm.add_state "expert", :after_enter => :add_express_prize
|
11
|
+
|
12
|
+
sm.add_transition :make_express_beginner, :from => "expert", :to => "express_beginner"
|
13
|
+
sm.add_transition :demote_express_beginner, :from => "express_beginner", :to => "expert"
|
14
|
+
sm.add_transition :make_express_expert, :from => "express_beginner", :to => "express_expert"
|
15
|
+
sm.add_transition :make_miki, :from => "express_expert", :to => "miki_level"
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(initial_status = nil)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
data/spec/models/gate.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class Gate
|
2
|
+
include SimpleStateMachine
|
3
|
+
|
4
|
+
attr_accessor :prize_won, :status, :new_record
|
5
|
+
|
6
|
+
state_machine do |sm|
|
7
|
+
sm.initial_state "beginner"
|
8
|
+
sm.add_state "novice", :after_enter => :add_prize
|
9
|
+
sm.add_state "expert", :after_enter => :add_prize
|
10
|
+
sm.add_transition :make_novice, :from => ["beginner", "expert"], :to => "novice"
|
11
|
+
sm.add_transition :make_expert, :from => "novice", :to => "expert"
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_prize
|
15
|
+
self.prize_won ||= ""
|
16
|
+
self.prize_won += "*"
|
17
|
+
end
|
18
|
+
|
19
|
+
def dummy_save
|
20
|
+
@new_record = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_record?
|
24
|
+
@new_record
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(initial_status = nil)
|
28
|
+
@status = initial_status
|
29
|
+
@new_record = true
|
30
|
+
@prize_won = ""
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class KingGate < Gate
|
2
|
+
|
3
|
+
state_machine do |sm|
|
4
|
+
sm.initial_state "king_beginner"
|
5
|
+
sm.add_state "king_novice", :after_enter => :add_prize
|
6
|
+
sm.add_state "king_expert", :after_enter => :add_prize
|
7
|
+
sm.add_transition :make_king_novice, :from => "king_beginner", :to => "king_novice"
|
8
|
+
sm.add_transition :make_king_expert, :from => "king_novice", :to => "king_expert"
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(initial_status = nil)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
|
3
|
+
%w{gates readers writers transients simples simple_new_dsls thieves localizer_test_models}.each do |table_name|
|
4
|
+
create_table table_name, :force => true do |t|
|
5
|
+
t.string "status"
|
6
|
+
t.string "better_status"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
3
|
+
require 'simple_state_machine'
|
4
|
+
|
5
|
+
require 'rspec'
|
6
|
+
require 'rspec/autorun'
|
7
|
+
|
8
|
+
# require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
|
9
|
+
# require 'ruby-debug/completion'
|
10
|
+
|
11
|
+
# Requiring custom spec helpers
|
12
|
+
Dir[File.dirname(__FILE__) + "/spec_helpers/**/*.rb"].sort.each { |f| require File.expand_path(f) }
|
13
|
+
#Dir[File.dirname(__FILE__) + "/models/*.rb"].each { |f| require File.expand_path(f) }
|
14
|
+
require "models/gate"
|
15
|
+
require 'models/express_gate'
|
16
|
+
require 'models/king_gate'
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ruby-debug'
|
3
|
+
|
4
|
+
describe SimpleStateMachine do
|
5
|
+
describe "- Normal state machine" do
|
6
|
+
before(:each) do
|
7
|
+
@gate = Gate.new
|
8
|
+
@gate.should respond_to(:status)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "- Basic operations" do
|
12
|
+
|
13
|
+
it "Should allow accessible methods to check if certain state is active on the including class" do
|
14
|
+
@gate.methods.should include("beginner?")
|
15
|
+
@gate.methods.should include("novice?")
|
16
|
+
@gate.methods.should include("expert?")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have a state_machine reference" do
|
20
|
+
@gate.class.should respond_to(:state_machine)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should respond to the default state field" do
|
24
|
+
@gate.should respond_to(Gate.state_machine.state_field)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should set the default state field to 'status'" do
|
28
|
+
Gate.state_machine.state_field.should eql("status")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set an initial status" do
|
32
|
+
@gate.status.should eql(Gate.state_machine.states["beginner"].to_i)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should force an initial status even if one specified by a default class constructor" do
|
36
|
+
@gate = Gate.new("bogus_status")
|
37
|
+
@gate.beginner?.should be_true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "- Transitions" do
|
42
|
+
|
43
|
+
it "should allow a gate to transition from 'beginner' to 'novice', normal method" do
|
44
|
+
lambda {
|
45
|
+
@gate.make_novice.should be_true
|
46
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
47
|
+
|
48
|
+
@gate.novice?.should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should allow a gate to transition from 'beginner' to 'novice', with shebang" do
|
52
|
+
lambda {
|
53
|
+
@gate.make_novice!
|
54
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
55
|
+
|
56
|
+
@gate.novice?.should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not allow a gate to transition from 'beginner' to 'expert'" do
|
60
|
+
lambda {
|
61
|
+
@gate.make_expert.should be_false
|
62
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
63
|
+
|
64
|
+
@gate.beginner?.should be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should raise an exception if a gate tries to transition from 'beginner' to 'expert' with a shebang" do
|
68
|
+
lambda {
|
69
|
+
@gate.make_expert!
|
70
|
+
}.should raise_error(SimpleStateMachine::Exceptions::InvalidTransition, "Could not transit 'beginner' to 'expert'")
|
71
|
+
|
72
|
+
@gate.beginner?.should be_true
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should allow a gate to transtition from 'expert' back to 'novice', normal method" do
|
76
|
+
@gate.status = Gate.state_machine.states["expert"]
|
77
|
+
lambda {
|
78
|
+
@gate.make_novice.should be_true
|
79
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
80
|
+
|
81
|
+
@gate.novice?.should be_true
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should allow a gate to transtition from 'expert' back to 'novice', with shebang" do
|
85
|
+
@gate.status = Gate.state_machine.states["expert"]
|
86
|
+
lambda {
|
87
|
+
@gate.make_novice!
|
88
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
89
|
+
|
90
|
+
@gate.novice?.should be_true
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "- Persistency" do
|
96
|
+
before(:all) do
|
97
|
+
Gate.state_machine.persistency_method(:dummy_save)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should persist on a shebang persistency transition method" do
|
101
|
+
@gate.new_record?.should be_true
|
102
|
+
@gate.make_novice!
|
103
|
+
@gate.new_record?.should be_false
|
104
|
+
@gate.novice?.should be_true
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should not persist on a non-shebang persistency transition method" do
|
108
|
+
@gate.new_record?.should be_true
|
109
|
+
@gate.make_novice.should be_true
|
110
|
+
@gate.new_record?.should be_true
|
111
|
+
@gate.novice?.should be_true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "- Events" do
|
116
|
+
it "should add prizes to the gate when transitioning from 'beginner' to 'novice'" do
|
117
|
+
@gate.prize_won.should be_empty
|
118
|
+
@gate.make_novice.should be_true
|
119
|
+
@gate.prize_won.should eql("*")
|
120
|
+
@gate.novice?.should be_true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "- Inheritence" do
|
126
|
+
describe "- parent class state machine" do
|
127
|
+
before(:each) do
|
128
|
+
#create both KingGate & ExpressGate to fail class level attributes integrity
|
129
|
+
@kinggate = KingGate.new
|
130
|
+
@gate = ExpressGate.new
|
131
|
+
@gate.should respond_to(:status)
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "- Basic operations" do
|
135
|
+
it "Should allow accessible constants on the including class" do
|
136
|
+
ExpressGate.constants.should include("STATES")
|
137
|
+
ExpressGate::STATES["beginner"].should_not be_nil
|
138
|
+
ExpressGate::STATES["novice"].should_not be_nil
|
139
|
+
ExpressGate::STATES["expert"].should_not be_nil
|
140
|
+
ExpressGate::STATES["express_beginner"].should_not be_nil
|
141
|
+
ExpressGate::STATES["express_expert"].should_not be_nil
|
142
|
+
ExpressGate::STATES["miki_level"].should_not be_nil
|
143
|
+
end
|
144
|
+
|
145
|
+
it "Should allow accessible methods to check if certain state is active on the including class for parent class' states" do
|
146
|
+
@gate.methods.should include("beginner?")
|
147
|
+
@gate.methods.should include("novice?")
|
148
|
+
@gate.methods.should include("expert?")
|
149
|
+
end
|
150
|
+
|
151
|
+
it "Should allow accessible methods to check if certain state is active on the including class for child class' states" do
|
152
|
+
@gate.methods.should include("express_beginner?")
|
153
|
+
@gate.methods.should include("express_expert?")
|
154
|
+
@gate.methods.should include("miki_level?")
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should have a state_machine reference" do
|
158
|
+
@gate.class.should respond_to(:state_machine)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should respond to the default state field" do
|
162
|
+
@gate.should respond_to(ExpressGate.state_machine.state_field)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should set the default state field to 'status'" do
|
166
|
+
ExpressGate.state_machine.state_field.should eql("status")
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should set an initial status" do
|
170
|
+
@gate.status.should eql(ExpressGate.state_machine.states["express_beginner"].to_i)
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should force an initial status even if one specified by a default class constructor" do
|
174
|
+
@gate = ExpressGate.new("bogus_status")
|
175
|
+
@gate.express_beginner?.should be_true
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "- Transitions" do
|
180
|
+
|
181
|
+
it "should allow a gate to transition from a state that comes from parent 'expert' to 'express_beginner', normal method" do
|
182
|
+
lambda {
|
183
|
+
@gate.status = ExpressGate.state_machine.states["expert"]
|
184
|
+
@gate.make_express_beginner.should be_true
|
185
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
186
|
+
|
187
|
+
@gate.express_beginner?.should be_true
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should allow a gate to transition from a state that comes from parent 'expert' to 'express_beginner', shebang method" do
|
191
|
+
lambda {
|
192
|
+
@gate.status = ExpressGate.state_machine.states["expert"]
|
193
|
+
@gate.make_express_beginner!
|
194
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
195
|
+
|
196
|
+
@gate.express_beginner?.should be_true
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should not allow a gate to transition from a state that comes from parent 'beginner' to 'express_beginner', normal method" do
|
200
|
+
@gate.status = ExpressGate.state_machine.states["beginner"]
|
201
|
+
lambda {
|
202
|
+
@gate.make_express_beginner.should be_false
|
203
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
204
|
+
|
205
|
+
@gate.beginner?.should be_true
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should not allow a gate to transition from a state that comes from parent 'beginner' to 'express_beginner', shebang method" do
|
209
|
+
@gate.status = ExpressGate.state_machine.states["beginner"]
|
210
|
+
lambda {
|
211
|
+
@gate.make_express_beginner!
|
212
|
+
}.should raise_error(SimpleStateMachine::Exceptions::InvalidTransition, "Could not transit 'beginner' to 'express_beginner'")
|
213
|
+
|
214
|
+
@gate.beginner?.should be_true
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should allow a gate to transition from 'express_beginner' to 'express_expert', normal method" do
|
218
|
+
lambda {
|
219
|
+
@gate.make_express_expert.should be_true
|
220
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
221
|
+
|
222
|
+
@gate.express_expert?.should be_true
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should allow a gate to transition from 'express_beginner' to 'express_expert', with shebang" do
|
226
|
+
lambda {
|
227
|
+
@gate.make_express_expert!
|
228
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
229
|
+
|
230
|
+
@gate.express_expert?.should be_true
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should not allow a gate to transition from 'express_beginner' to 'miki_level'" do
|
234
|
+
lambda {
|
235
|
+
@gate.make_miki.should be_false
|
236
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
237
|
+
|
238
|
+
@gate.express_beginner?.should be_true
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should raise an exception if a gate tries to transition from 'express_beginner' to 'miki_level' with a shebang" do
|
242
|
+
lambda {
|
243
|
+
@gate.make_miki!
|
244
|
+
}.should raise_error(SimpleStateMachine::Exceptions::InvalidTransition, "Could not transit 'express_beginner' to 'miki_level'")
|
245
|
+
|
246
|
+
@gate.express_beginner?.should be_true
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should allow a gate to transition from a state that comes from child 'express_beginner' to 'expert', normal method" do
|
250
|
+
@gate.status = ExpressGate.state_machine.states["express_beginner"]
|
251
|
+
lambda {
|
252
|
+
@gate.demote_express_beginner.should be_true
|
253
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
254
|
+
@gate.expert?.should be_true
|
255
|
+
end
|
256
|
+
|
257
|
+
it "should allow a gate to transition from a state that comes from child 'express_beginner' to state from parent 'expert', shebang method" do
|
258
|
+
@gate.status = ExpressGate.state_machine.states["express_beginner"]
|
259
|
+
lambda {
|
260
|
+
@gate.demote_express_beginner!
|
261
|
+
}.should_not raise_error(SimpleStateMachine::Exceptions::InvalidTransition)
|
262
|
+
|
263
|
+
@gate.expert?.should be_true
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe "- Persistency" do
|
268
|
+
before(:all) do
|
269
|
+
ExpressGate.state_machine.persistency_method(:dummy_save)
|
270
|
+
end
|
271
|
+
|
272
|
+
it "should persist on a shebang transition method" do
|
273
|
+
@gate.new_record?.should be_true
|
274
|
+
@gate.make_express_expert!
|
275
|
+
@gate.new_record?.should be_false
|
276
|
+
@gate.express_expert?.should be_true
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should not persist on a non-shebang transition method" do
|
280
|
+
@gate.new_record?.should be_true
|
281
|
+
@gate.make_express_expert
|
282
|
+
@gate.new_record?.should be_true
|
283
|
+
@gate.express_expert?.should be_true
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "- Events" do
|
288
|
+
it "should add prizes to the gate when transitioning from 'beginner' to 'novice'" do
|
289
|
+
@gate.prize_won.should be_empty
|
290
|
+
@gate.make_express_expert
|
291
|
+
@gate.prize_won.should eql("*")
|
292
|
+
@gate.express_expert?.should be_true
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
metadata
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simpler_state_machine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Elad Meidar
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: sdoc
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '2.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rr
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: shoulda
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: sqlite3
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: minitest
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: ruby-debug
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: ruby-debug-completion
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
description: A simple state machine that supports enum
|
175
|
+
email:
|
176
|
+
- elad@eizesus.com
|
177
|
+
executables: []
|
178
|
+
extensions: []
|
179
|
+
extra_rdoc_files: []
|
180
|
+
files:
|
181
|
+
- .DS_Store
|
182
|
+
- .gitignore
|
183
|
+
- Gemfile
|
184
|
+
- LICENSE
|
185
|
+
- README.md
|
186
|
+
- Rakefile
|
187
|
+
- lib/simple_state_machine.rb
|
188
|
+
- lib/simple_state_machine/base.rb
|
189
|
+
- lib/simple_state_machine/enum.rb
|
190
|
+
- lib/simple_state_machine/errors.rb
|
191
|
+
- lib/simple_state_machine/event.rb
|
192
|
+
- lib/simple_state_machine/simple_state_machine.rb
|
193
|
+
- lib/simple_state_machine/transition.rb
|
194
|
+
- lib/simple_state_machine/version.rb
|
195
|
+
- simpler_state_machine.gemspec
|
196
|
+
- spec/database.yml
|
197
|
+
- spec/models/express_gate.rb
|
198
|
+
- spec/models/gate.rb
|
199
|
+
- spec/models/king_gate.rb
|
200
|
+
- spec/schema.rb
|
201
|
+
- spec/spec_helper.rb
|
202
|
+
- spec/unit/simple_state_machine_spec.rb
|
203
|
+
homepage: http://devandpencil.herokuapp.com/blog/2013/01/30/simplestatemachine-a-simple-enum-based-state-machine-for-ruby/
|
204
|
+
licenses: []
|
205
|
+
post_install_message:
|
206
|
+
rdoc_options: []
|
207
|
+
require_paths:
|
208
|
+
- lib
|
209
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
210
|
+
none: false
|
211
|
+
requirements:
|
212
|
+
- - ! '>='
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
215
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
216
|
+
none: false
|
217
|
+
requirements:
|
218
|
+
- - ! '>='
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '0'
|
221
|
+
requirements: []
|
222
|
+
rubyforge_project:
|
223
|
+
rubygems_version: 1.8.25
|
224
|
+
signing_key:
|
225
|
+
specification_version: 3
|
226
|
+
summary: A simple lightweight state machine that uses an enum type to store states
|
227
|
+
test_files: []
|