micro_state_machine 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.travis.yml +8 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +69 -0
- data/LICENSE.txt +20 -0
- data/README.md +93 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/lib/micro_state_machine.rb +122 -0
- data/micro_state_machine.gemspec +66 -0
- data/test/helper.rb +27 -0
- data/test/test_micro_state_machine.rb +135 -0
- metadata +159 -0
data/.document
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
gem "activesupport", ">= 2.3.5" # For HashWithIndifferentAccess
|
3
|
+
|
4
|
+
group :development do
|
5
|
+
gem "minitest", ">= 0"
|
6
|
+
gem "rdoc"
|
7
|
+
gem "bundler", "~> 1.0"
|
8
|
+
gem "jeweler", "~> 2.0.1"
|
9
|
+
gem "simplecov", ">= 0"
|
10
|
+
# gem 'debugger'
|
11
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.2.17)
|
5
|
+
i18n (~> 0.6, >= 0.6.4)
|
6
|
+
multi_json (~> 1.0)
|
7
|
+
addressable (2.3.5)
|
8
|
+
builder (3.2.2)
|
9
|
+
descendants_tracker (0.0.3)
|
10
|
+
docile (1.1.3)
|
11
|
+
faraday (0.9.0)
|
12
|
+
multipart-post (>= 1.2, < 3)
|
13
|
+
git (1.2.6)
|
14
|
+
github_api (0.11.3)
|
15
|
+
addressable (~> 2.3)
|
16
|
+
descendants_tracker (~> 0.0.1)
|
17
|
+
faraday (~> 0.8, < 0.10)
|
18
|
+
hashie (>= 1.2)
|
19
|
+
multi_json (>= 1.7.5, < 2.0)
|
20
|
+
nokogiri (~> 1.6.0)
|
21
|
+
oauth2
|
22
|
+
hashie (2.0.5)
|
23
|
+
highline (1.6.20)
|
24
|
+
i18n (0.6.9)
|
25
|
+
jeweler (2.0.1)
|
26
|
+
builder
|
27
|
+
bundler (>= 1.0)
|
28
|
+
git (>= 1.2.5)
|
29
|
+
github_api
|
30
|
+
highline (>= 1.6.15)
|
31
|
+
nokogiri (>= 1.5.10)
|
32
|
+
rake
|
33
|
+
rdoc
|
34
|
+
json (1.8.1)
|
35
|
+
jwt (0.1.11)
|
36
|
+
multi_json (>= 1.5)
|
37
|
+
mini_portile (0.5.2)
|
38
|
+
minitest (5.3.0)
|
39
|
+
multi_json (1.8.4)
|
40
|
+
multi_xml (0.5.5)
|
41
|
+
multipart-post (2.0.0)
|
42
|
+
nokogiri (1.6.1)
|
43
|
+
mini_portile (~> 0.5.0)
|
44
|
+
oauth2 (0.9.3)
|
45
|
+
faraday (>= 0.8, < 0.10)
|
46
|
+
jwt (~> 0.1.8)
|
47
|
+
multi_json (~> 1.3)
|
48
|
+
multi_xml (~> 0.5)
|
49
|
+
rack (~> 1.2)
|
50
|
+
rack (1.5.2)
|
51
|
+
rake (10.1.1)
|
52
|
+
rdoc (4.1.1)
|
53
|
+
json (~> 1.4)
|
54
|
+
simplecov (0.8.2)
|
55
|
+
docile (~> 1.1.0)
|
56
|
+
multi_json
|
57
|
+
simplecov-html (~> 0.8.0)
|
58
|
+
simplecov-html (0.8.0)
|
59
|
+
|
60
|
+
PLATFORMS
|
61
|
+
ruby
|
62
|
+
|
63
|
+
DEPENDENCIES
|
64
|
+
activesupport (>= 2.3.5)
|
65
|
+
bundler (~> 1.0)
|
66
|
+
jeweler (~> 2.0.1)
|
67
|
+
minitest
|
68
|
+
rdoc
|
69
|
+
simplecov
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Steven Soroka
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
micro\_state\_machine
|
2
|
+
-------------------
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/ssoroka/micro_state_machine.png?branch=master)](https://travis-ci.org/ssoroka/micro_state_machine)
|
5
|
+
|
6
|
+
A small state machine! You don't need all that other crap. What's not included here is as important as what's included.
|
7
|
+
|
8
|
+
### Features Included:
|
9
|
+
|
10
|
+
- default state
|
11
|
+
- customizable state field
|
12
|
+
- before and after transition events
|
13
|
+
- "state?" current state query methods dynamically defined
|
14
|
+
- "state!" transition method to switch states
|
15
|
+
- exceptions thrown on invalid state transitions
|
16
|
+
- super-simple syntax
|
17
|
+
|
18
|
+
### Features NOT included:
|
19
|
+
|
20
|
+
|
21
|
+
- Transition Guards. (small pull-requests may be accepted).
|
22
|
+
- Multiple state machines per object.
|
23
|
+
|
24
|
+
# Usage Example
|
25
|
+
|
26
|
+
Rails Example:
|
27
|
+
|
28
|
+
include MicroStateMachine
|
29
|
+
|
30
|
+
# first state declared is automatically the default state for new objects
|
31
|
+
state :new
|
32
|
+
state :running, from: %w(new paused deferred)
|
33
|
+
state :paused, from: %w(running)
|
34
|
+
state :deferred, from: %w(new running paused deferred)
|
35
|
+
state :completed, from: %w(new running paused deferred)
|
36
|
+
state :closed, from: %w(new running paused deferred)
|
37
|
+
|
38
|
+
|
39
|
+
Ruby Object Example:
|
40
|
+
|
41
|
+
include MicroStateMachine
|
42
|
+
|
43
|
+
# first state declared is automatically the default state for new objects
|
44
|
+
state :new
|
45
|
+
state :running, from: %w(new paused deferred)
|
46
|
+
state :paused, from: %w(running)
|
47
|
+
state :deferred, from: %w(new running paused deferred)
|
48
|
+
state :completed, from: %w(new running paused deferred)
|
49
|
+
state :closed, from: %w(new running paused deferred)
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
set_initial_state
|
53
|
+
end
|
54
|
+
|
55
|
+
Event Transitions:
|
56
|
+
|
57
|
+
after_state_change do |from_state, to_state|
|
58
|
+
state_histories.create!(from_state: from_state, to_state: to_state, user: current_user)
|
59
|
+
pause_users_other_tasks if to_state.to_s == 'running'
|
60
|
+
end
|
61
|
+
|
62
|
+
on_enter_state :running do
|
63
|
+
self.started_at = Time.now.utc
|
64
|
+
end
|
65
|
+
|
66
|
+
on_exit_state :running do
|
67
|
+
running_time = (Time.now.utc - started_at).round
|
68
|
+
time_logs.create!(task: self, user: current_user, seconds: running_time)
|
69
|
+
end
|
70
|
+
|
71
|
+
To change the name of the state column:
|
72
|
+
|
73
|
+
def state_column
|
74
|
+
'something_other_than_state'
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
Contributing to micro\_state\_machine
|
79
|
+
-----------------------------------
|
80
|
+
|
81
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
82
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
83
|
+
* Fork the project.
|
84
|
+
* Start a feature/bugfix branch.
|
85
|
+
* Commit and push until you are happy with your contribution.
|
86
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
87
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
88
|
+
|
89
|
+
Copyright
|
90
|
+
---------
|
91
|
+
|
92
|
+
Copyright (c) 2014 Steven Soroka. See LICENSE.txt for
|
93
|
+
further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
17
|
+
gem.name = "micro_state_machine"
|
18
|
+
gem.homepage = "http://github.com/ssoroka/micro_state_machine"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{A small state machine!}
|
21
|
+
gem.description = %Q{A small state machine! You don't need all that other crap. What's not included here is as important as what's included.}
|
22
|
+
gem.email = "ssoroka78@gmail.com"
|
23
|
+
gem.authors = ["Steven Soroka"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Code coverage detail"
|
36
|
+
task :simplecov do
|
37
|
+
ENV['COVERAGE'] = "true"
|
38
|
+
Rake::Task['test'].execute
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rdoc/task'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "micro_state_machine #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
3
|
+
|
4
|
+
module MicroStateMachine
|
5
|
+
class InvalidState < StandardError
|
6
|
+
def initialize(old_state, new_state)
|
7
|
+
super("Cannot transition from #{old_state} to #{new_state}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.included(klass)
|
12
|
+
klass.class_attribute :_on_enter_state, :_on_exit_state, :_initial_state, :_after_state_change, :_states
|
13
|
+
klass.send(:extend, ClassMethods)
|
14
|
+
if klass.respond_to?(:after_initialize)
|
15
|
+
klass.instance_eval do
|
16
|
+
after_initialize(:set_initial_state)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
# override this to change the default state column name
|
23
|
+
def state_column
|
24
|
+
'state'
|
25
|
+
end
|
26
|
+
|
27
|
+
# used to define the possible states the "machine" could be in.
|
28
|
+
# defines convenience {state}! and {state}? methods
|
29
|
+
# worth noting that transition_from_{from_state}_to_{state} could be used to implement guards.
|
30
|
+
def state(state, options = {})
|
31
|
+
self._initial_state ||= state
|
32
|
+
self._after_state_change ||= []
|
33
|
+
self._on_enter_state ||= HashWithIndifferentAccess.new
|
34
|
+
self._on_exit_state ||= HashWithIndifferentAccess.new
|
35
|
+
self._states ||= {}
|
36
|
+
_states[state] = options
|
37
|
+
from = options[:from] || _states.keys
|
38
|
+
|
39
|
+
from.each do |from_state|
|
40
|
+
define_method("#{state}!") do
|
41
|
+
transition_to(state)
|
42
|
+
end
|
43
|
+
|
44
|
+
define_method("#{state}?") do
|
45
|
+
is?(state)
|
46
|
+
end
|
47
|
+
|
48
|
+
define_method("transition_from_#{from_state}_to_#{state}") do
|
49
|
+
on_exit_state = self.class._on_exit_state
|
50
|
+
on_enter_state = self.class._on_enter_state
|
51
|
+
if on_exit_state[from_state]
|
52
|
+
on_exit_state[from_state].each do |blk|
|
53
|
+
instance_eval &blk
|
54
|
+
end
|
55
|
+
end
|
56
|
+
send("#{state_column}=", state)
|
57
|
+
if on_enter_state[state]
|
58
|
+
on_enter_state[state].each do |blk|
|
59
|
+
instance_eval &blk
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def on_enter_state(state, &block)
|
67
|
+
h = _on_enter_state
|
68
|
+
h[state] ||= []
|
69
|
+
h[state].push block
|
70
|
+
self._on_enter_state = h
|
71
|
+
end
|
72
|
+
|
73
|
+
def on_exit_state(state, &block)
|
74
|
+
h = _on_exit_state
|
75
|
+
h[state] ||= []
|
76
|
+
h[state].push block
|
77
|
+
self._on_exit_state = h
|
78
|
+
end
|
79
|
+
|
80
|
+
def after_state_change(*args, &block)
|
81
|
+
_after_state_change.push block
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Instance Methods
|
86
|
+
|
87
|
+
def state_column
|
88
|
+
self.class.state_column
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_state
|
92
|
+
send(state_column)
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_initial_state
|
96
|
+
current_value = send(state_column)
|
97
|
+
if current_value.nil?
|
98
|
+
send("#{state_column}=", self.class._initial_state)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def is?(state)
|
103
|
+
get_state.to_s == state.to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
def can_transition_to?(new_state)
|
107
|
+
states = self.class._states
|
108
|
+
options = HashWithIndifferentAccess.new(states)[new_state]
|
109
|
+
options.blank? || options[:from].include?(get_state)
|
110
|
+
end
|
111
|
+
|
112
|
+
def transition_to(new_state)
|
113
|
+
old_state = get_state
|
114
|
+
transition_method_name = "transition_from_#{old_state}_to_#{new_state}"
|
115
|
+
raise InvalidState.new(old_state, new_state) unless respond_to?(transition_method_name)
|
116
|
+
send(transition_method_name)
|
117
|
+
self.class._after_state_change.each do |blk|
|
118
|
+
instance_exec(old_state, new_state, &blk)
|
119
|
+
end
|
120
|
+
self
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "micro_state_machine"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Steven Soroka"]
|
12
|
+
s.date = "2014-02-27"
|
13
|
+
s.description = "A small state machine! You don't need all that other crap. What's not included here is as important as what's included."
|
14
|
+
s.email = "ssoroka78@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".travis.yml",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"lib/micro_state_machine.rb",
|
29
|
+
"micro_state_machine.gemspec",
|
30
|
+
"test/helper.rb",
|
31
|
+
"test/test_micro_state_machine.rb"
|
32
|
+
]
|
33
|
+
s.homepage = "http://github.com/ssoroka/micro_state_machine"
|
34
|
+
s.licenses = ["MIT"]
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
s.rubygems_version = "1.8.23"
|
37
|
+
s.summary = "A small state machine!"
|
38
|
+
|
39
|
+
if s.respond_to? :specification_version then
|
40
|
+
s.specification_version = 3
|
41
|
+
|
42
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
43
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.5"])
|
44
|
+
s.add_development_dependency(%q<minitest>, [">= 0"])
|
45
|
+
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
46
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
47
|
+
s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
|
48
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
49
|
+
else
|
50
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
51
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
52
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
53
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
54
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
55
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
59
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
60
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
61
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
62
|
+
s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
|
63
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
SimpleCov.configure do
|
4
|
+
filters.clear
|
5
|
+
load_profile 'test_frameworks'
|
6
|
+
end
|
7
|
+
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter "/(.rbenv|.rvm)/"
|
10
|
+
add_filter 'vendor'
|
11
|
+
end
|
12
|
+
require 'rubygems'
|
13
|
+
require 'bundler'
|
14
|
+
begin
|
15
|
+
Bundler.setup(:default, :development)
|
16
|
+
rescue Bundler::BundlerError => e
|
17
|
+
$stderr.puts "#{e.message}\nRun `bundle install` to install missing gems"
|
18
|
+
exit e.status_code
|
19
|
+
end
|
20
|
+
require 'minitest/autorun'
|
21
|
+
require 'minitest/spec'
|
22
|
+
|
23
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
24
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
25
|
+
require 'micro_state_machine'
|
26
|
+
|
27
|
+
Minitest.autorun
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'micro_state_machine'
|
3
|
+
|
4
|
+
class TestModel
|
5
|
+
attr_accessor :state
|
6
|
+
|
7
|
+
include MicroStateMachine
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
set_initial_state
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class TestKanbanTask < TestModel
|
15
|
+
state 'new'
|
16
|
+
state 'wip', from: %w(new)
|
17
|
+
state 'review', from: %w(new wip)
|
18
|
+
state 'accepted', from: %w(review)
|
19
|
+
state 'archived', from: %w(accepted)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe MicroStateMachine do
|
23
|
+
it "state defaults to first state" do
|
24
|
+
TestKanbanTask.new.state.must_equal 'new'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'allows overriding the state column in the class definition' do
|
28
|
+
override_test_class = Class.new(TestKanbanTask) do
|
29
|
+
def state_column
|
30
|
+
:foo_state
|
31
|
+
end
|
32
|
+
def foo_state
|
33
|
+
'review'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
assert !override_test_class.new.new?
|
37
|
+
assert override_test_class.new.review?
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'allows overriding the state column on the instance' do
|
41
|
+
task = TestKanbanTask.new
|
42
|
+
# alter the meta class to include "fish" column
|
43
|
+
task.define_singleton_method :fish_state do
|
44
|
+
'wip'
|
45
|
+
end
|
46
|
+
|
47
|
+
# define a method on the instance
|
48
|
+
task.define_singleton_method :state_column do
|
49
|
+
'fish_state'
|
50
|
+
end
|
51
|
+
|
52
|
+
task.get_state.must_equal 'wip'
|
53
|
+
assert task.wip?
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'fires on_enter_state and on_exit_state hooks on changing state' do
|
57
|
+
enter_hit = false
|
58
|
+
exit_hit = false
|
59
|
+
hook_class = Class.new(TestKanbanTask) do
|
60
|
+
on_enter_state :wip do
|
61
|
+
enter_hit = true
|
62
|
+
end
|
63
|
+
on_exit_state :wip do
|
64
|
+
exit_hit = true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
assert !enter_hit
|
69
|
+
assert !exit_hit
|
70
|
+
|
71
|
+
obj = hook_class.new
|
72
|
+
obj.get_state.must_equal 'new'
|
73
|
+
|
74
|
+
obj.wip!
|
75
|
+
assert enter_hit
|
76
|
+
assert !exit_hit
|
77
|
+
|
78
|
+
obj.review!
|
79
|
+
assert exit_hit
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'fires after_state_change hooks after changing state' do
|
83
|
+
from_state = nil
|
84
|
+
to_state = nil
|
85
|
+
|
86
|
+
hook_class = Class.new(TestKanbanTask) do
|
87
|
+
after_state_change do |_from_state, _to_state|
|
88
|
+
from_state = _from_state
|
89
|
+
to_state = _to_state
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
obj = hook_class.new
|
94
|
+
obj.get_state.must_equal 'new'
|
95
|
+
|
96
|
+
obj.wip!
|
97
|
+
from_state.must_equal 'new'
|
98
|
+
to_state.must_equal 'wip'
|
99
|
+
|
100
|
+
obj.review!
|
101
|
+
from_state.must_equal 'wip'
|
102
|
+
to_state.must_equal 'review'
|
103
|
+
|
104
|
+
assert_raises MicroStateMachine::InvalidState do
|
105
|
+
obj.wip!
|
106
|
+
end
|
107
|
+
|
108
|
+
# make sure it's unchanged:
|
109
|
+
from_state.must_equal 'wip'
|
110
|
+
to_state.must_equal 'review'
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'throws an InvalidState error when transitioning to an invalid state' do
|
114
|
+
assert_raises MicroStateMachine::InvalidState do
|
115
|
+
TestKanbanTask.new.accepted!
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'can transition through all states' do
|
120
|
+
task = TestKanbanTask.new
|
121
|
+
task.wip!
|
122
|
+
assert task.wip?
|
123
|
+
|
124
|
+
task.review!
|
125
|
+
assert task.review?
|
126
|
+
|
127
|
+
task.accepted!
|
128
|
+
assert task.accepted?
|
129
|
+
|
130
|
+
task.archived!
|
131
|
+
assert task.archived?
|
132
|
+
assert task.is?(:archived)
|
133
|
+
assert task.is?('archived')
|
134
|
+
end
|
135
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: micro_state_machine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Steven Soroka
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-02-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.3.5
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.3.5
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
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: rdoc
|
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: bundler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.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: '1.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: jeweler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.0.1
|
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: 2.0.1
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: simplecov
|
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
|
+
description: A small state machine! You don't need all that other crap. What's not
|
111
|
+
included here is as important as what's included.
|
112
|
+
email: ssoroka78@gmail.com
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files:
|
116
|
+
- LICENSE.txt
|
117
|
+
- README.md
|
118
|
+
files:
|
119
|
+
- .document
|
120
|
+
- .travis.yml
|
121
|
+
- Gemfile
|
122
|
+
- Gemfile.lock
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- VERSION
|
127
|
+
- lib/micro_state_machine.rb
|
128
|
+
- micro_state_machine.gemspec
|
129
|
+
- test/helper.rb
|
130
|
+
- test/test_micro_state_machine.rb
|
131
|
+
homepage: http://github.com/ssoroka/micro_state_machine
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
segments:
|
145
|
+
- 0
|
146
|
+
hash: -3232762694017464134
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 1.8.23
|
156
|
+
signing_key:
|
157
|
+
specification_version: 3
|
158
|
+
summary: A small state machine!
|
159
|
+
test_files: []
|