state_pattern 1.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/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +62 -0
- data/Rakefile +83 -0
- data/VERSION +1 -0
- data/lib/state_pattern.rb +92 -0
- data/lib/state_pattern/invalid_transition_exception.rb +14 -0
- data/lib/state_pattern/state.rb +16 -0
- data/state_pattern.gemspec +53 -0
- data/test/state_pattern_test.rb +102 -0
- data/test/test_class_creation_helper.rb +59 -0
- data/test/test_helper.rb +13 -0
- data/test/transition_validations_test.rb +55 -0
- metadata +72 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Daniel Cadenas
|
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.rdoc
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
= state_pattern
|
2
|
+
|
3
|
+
A Ruby state pattern implementation.
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'state_pattern'
|
7
|
+
|
8
|
+
class On < StatePattern::State
|
9
|
+
def press
|
10
|
+
transition_to(Off)
|
11
|
+
"#{stateable.button_name} is off"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Off < StatePattern::State
|
16
|
+
def press
|
17
|
+
transition_to(On)
|
18
|
+
"#{stateable.button_name} is on"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Button
|
23
|
+
include StatePattern
|
24
|
+
add_states On, Off
|
25
|
+
set_initial_state Off
|
26
|
+
valid_transitions [On, :press] => Off, [Off, :press] => On
|
27
|
+
|
28
|
+
#this method can be removed as it will be mapped automatically anyways
|
29
|
+
#but it is good to leave the option to do the delegation yourself in case you want more freedom
|
30
|
+
def press
|
31
|
+
delegate_to_event(:press)
|
32
|
+
end
|
33
|
+
|
34
|
+
def button_name
|
35
|
+
"Light button"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
button = Button.new
|
40
|
+
puts button.press # => "Light button is on"
|
41
|
+
puts button.press # => "Light button is off"
|
42
|
+
puts button.press # => "Light button is on"
|
43
|
+
|
44
|
+
== Validations
|
45
|
+
|
46
|
+
One of the few drawbacks the state pattern has is that it can get difficult to see the global picture of your state machine when dealing with complex cases.
|
47
|
+
To deal with this problem you have the option of using the valid_transitions statement to "draw" your state diagram in code. Whenever a state transition is performed, the valid_transitions hash is checked and if the transition is not valid a StatePattern::InvalidTransitionException is thrown.
|
48
|
+
|
49
|
+
Examples:
|
50
|
+
|
51
|
+
The most basic notation
|
52
|
+
valid_transitions On => Off, Off => On
|
53
|
+
|
54
|
+
With more than one target state
|
55
|
+
valid_transitions Up => [Middle, Down], Down => Middle, Middle => Up
|
56
|
+
|
57
|
+
Using event names to gain more detail
|
58
|
+
valid_transitions [Up, :switch] => [Middle, Down], [Down, :switch] => Middle, [Middle, :switch] => Up
|
59
|
+
|
60
|
+
== Copyright
|
61
|
+
|
62
|
+
Copyright (c) 2009 Daniel Cadenas. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "state_pattern"
|
8
|
+
gem.summary = %Q{A Ruby state pattern implementation}
|
9
|
+
gem.email = "dcadenas@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/dcadenas/state_pattern"
|
11
|
+
gem.authors = ["Daniel Cadenas"]
|
12
|
+
gem.rubyforge_project = "statepattern"
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/*_test.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'rcov/rcovtask'
|
29
|
+
Rcov::RcovTask.new do |test|
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/*_test.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
task :rcov do
|
36
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
if File.exist?('VERSION.yml')
|
46
|
+
config = YAML.load(File.read('VERSION.yml'))
|
47
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
48
|
+
else
|
49
|
+
version = ""
|
50
|
+
end
|
51
|
+
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
53
|
+
rdoc.title = "state_pattern #{version}"
|
54
|
+
rdoc.rdoc_files.include('README*')
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
|
+
end
|
57
|
+
|
58
|
+
begin
|
59
|
+
require 'rake/contrib/sshpublisher'
|
60
|
+
namespace :rubyforge do
|
61
|
+
|
62
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
63
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
64
|
+
|
65
|
+
namespace :release do
|
66
|
+
desc "Publish RDoc to RubyForge."
|
67
|
+
task :docs => [:rdoc] do
|
68
|
+
config = YAML.load(
|
69
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
70
|
+
)
|
71
|
+
|
72
|
+
host = "#{config['username']}@rubyforge.org"
|
73
|
+
remote_dir = "/var/www/gforge-projects/state_pattern/"
|
74
|
+
local_dir = 'rdoc'
|
75
|
+
|
76
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
rescue LoadError
|
81
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
82
|
+
end
|
83
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.1
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'state_pattern/state'
|
2
|
+
require 'state_pattern/invalid_transition_exception'
|
3
|
+
|
4
|
+
module StatePattern
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def state_classes
|
11
|
+
@state_classes ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def initial_state_class
|
15
|
+
@initial_state_class
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_initial_state(state_class)
|
19
|
+
@initial_state_class = state_class
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_states(*state_classes)
|
23
|
+
state_classes.each do |state_class|
|
24
|
+
add_state_class(state_class)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_state_class(state_class)
|
29
|
+
state_classes << state_class
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid_transitions(transitions_hash)
|
33
|
+
@transitions_hash = transitions_hash
|
34
|
+
@transitions_hash.each do |key, value|
|
35
|
+
if !value.respond_to?(:to_ary)
|
36
|
+
@transitions_hash[key] = [value]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def transitions_hash
|
42
|
+
@transitions_hash
|
43
|
+
end
|
44
|
+
|
45
|
+
def delegate_all_state_events
|
46
|
+
state_methods.each do |state_method|
|
47
|
+
define_method state_method do |*args|
|
48
|
+
delegate_to_event(state_method)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def state_methods
|
54
|
+
state_classes.map{|state_class| state_class.public_instance_methods(false)}.flatten.uniq
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_accessor :current_state, :current_event
|
59
|
+
def initialize(*args)
|
60
|
+
super(*args)
|
61
|
+
set_state(self.class.initial_state_class)
|
62
|
+
self.class.delegate_all_state_events
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_state(state_class)
|
66
|
+
self.current_state = state_class.new(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
def delegate_to_event(method_name, *args)
|
70
|
+
self.current_event = method_name.to_sym
|
71
|
+
self.current_state.send(current_event, *args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def transition_to(state_class)
|
75
|
+
raise InvalidTransitionException.new(self.current_state.class, state_class, self.current_event) unless self.valid_transition?(self.current_state.class, state_class)
|
76
|
+
set_state(state_class)
|
77
|
+
end
|
78
|
+
|
79
|
+
def valid_transition?(from_module, to_module)
|
80
|
+
trans = self.class.transitions_hash
|
81
|
+
return true if trans.nil?
|
82
|
+
|
83
|
+
valid_transition_targets = trans[from_module] || trans[[from_module, current_event]]
|
84
|
+
valid_transition_targets && valid_transition_targets.include?(to_module)
|
85
|
+
end
|
86
|
+
|
87
|
+
def state
|
88
|
+
self.current_state.state
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module StatePattern
|
2
|
+
class InvalidTransitionException < RuntimeError
|
3
|
+
attr_reader :from_module, :to_module, :event
|
4
|
+
def initialize(from_module, to_module, event)
|
5
|
+
@from_module = from_module
|
6
|
+
@to_module = to_module
|
7
|
+
@event = event
|
8
|
+
end
|
9
|
+
|
10
|
+
def message
|
11
|
+
"Event #@event cannot transition from #@from_module to #@to_module"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module StatePattern
|
2
|
+
class State
|
3
|
+
attr_reader :stateable
|
4
|
+
def initialize(stateable)
|
5
|
+
@stateable = stateable
|
6
|
+
end
|
7
|
+
|
8
|
+
def transition_to(state_class)
|
9
|
+
@stateable.transition_to(state_class)
|
10
|
+
end
|
11
|
+
|
12
|
+
def state
|
13
|
+
self.class.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{state_pattern}
|
5
|
+
s.version = "1.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Daniel Cadenas"]
|
9
|
+
s.date = %q{2009-06-08}
|
10
|
+
s.email = %q{dcadenas@gmail.com}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.rdoc"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".document",
|
17
|
+
".gitignore",
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION",
|
22
|
+
"lib/state_pattern.rb",
|
23
|
+
"lib/state_pattern/invalid_transition_exception.rb",
|
24
|
+
"lib/state_pattern/state.rb",
|
25
|
+
"state_pattern.gemspec",
|
26
|
+
"test/state_pattern_test.rb",
|
27
|
+
"test/test_class_creation_helper.rb",
|
28
|
+
"test/test_helper.rb",
|
29
|
+
"test/transition_validations_test.rb"
|
30
|
+
]
|
31
|
+
s.homepage = %q{http://github.com/dcadenas/state_pattern}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubyforge_project = %q{statepattern}
|
35
|
+
s.rubygems_version = %q{1.3.3}
|
36
|
+
s.summary = %q{A Ruby state pattern implementation}
|
37
|
+
s.test_files = [
|
38
|
+
"test/state_pattern_test.rb",
|
39
|
+
"test/test_class_creation_helper.rb",
|
40
|
+
"test/test_helper.rb",
|
41
|
+
"test/transition_validations_test.rb"
|
42
|
+
]
|
43
|
+
|
44
|
+
if s.respond_to? :specification_version then
|
45
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
46
|
+
s.specification_version = 3
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
49
|
+
else
|
50
|
+
end
|
51
|
+
else
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Family
|
4
|
+
class James < StatePattern::State
|
5
|
+
def name
|
6
|
+
transition_to(Lynn)
|
7
|
+
"James #{stateable.last_name}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Lynn < StatePattern::State
|
12
|
+
def name
|
13
|
+
transition_to(James)
|
14
|
+
"Lynn #{stateable.last_name}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Member
|
19
|
+
include StatePattern
|
20
|
+
add_states James, Lynn
|
21
|
+
set_initial_state Lynn
|
22
|
+
valid_transitions [James, :name] => Lynn, [Lynn, :name] => James
|
23
|
+
|
24
|
+
#notice this method is optional, it will be delegated automatically if removed
|
25
|
+
def name
|
26
|
+
delegate_to_event :name
|
27
|
+
end
|
28
|
+
|
29
|
+
def last_name
|
30
|
+
"Holbrook"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Expectations do
|
36
|
+
expect "Lynn Holbrook" do
|
37
|
+
member = Family::Member.new
|
38
|
+
member.name
|
39
|
+
end
|
40
|
+
|
41
|
+
expect "James Holbrook" do
|
42
|
+
member = Family::Member.new
|
43
|
+
member.name
|
44
|
+
member.name
|
45
|
+
end
|
46
|
+
|
47
|
+
expect "Lynn Holbrook" do
|
48
|
+
member = Family::Member.new
|
49
|
+
member.name
|
50
|
+
member.name
|
51
|
+
member.name
|
52
|
+
end
|
53
|
+
|
54
|
+
expect "on" do
|
55
|
+
with_test_class("Button", :states => ["On", "Off"], :initial_state => "Off",
|
56
|
+
:transitions => {["On", :press] => "Off", ["Off", :press] => "On"}) do
|
57
|
+
button = Button.new
|
58
|
+
button.press
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
expect "off" do
|
63
|
+
with_test_class("Button", :states => ["On", "Off"], :initial_state => "Off",
|
64
|
+
:transitions => {["On", :press] => "Off", ["Off", :press] => "On"}) do
|
65
|
+
button = Button.new
|
66
|
+
button.press
|
67
|
+
button.press
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
expect "on" do
|
72
|
+
with_test_class("Button", :states => ["On", "Off"], :initial_state => "Off",
|
73
|
+
:transitions => {["On", :press] => "Off", ["Off", :press] => "On"}) do
|
74
|
+
button = Button.new
|
75
|
+
button.press
|
76
|
+
button.press
|
77
|
+
button.press
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
expect "on" do
|
82
|
+
with_test_class("Button", :states => ["On", "Off"], :initial_state => "Off",
|
83
|
+
:transitions => {["On", :press] => "Off", ["Off", :press] => "On"}) do
|
84
|
+
button1 = Button.new
|
85
|
+
button2 = Button.new
|
86
|
+
button1.press
|
87
|
+
button2.press
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
expect ["ping", "on", "pong", "off"] do
|
92
|
+
with_test_class("PingPong", :states => ["Ping", "Pong"], :initial_state => "Pong", :transitions => {["Ping", :do_it] => "Pong", ["Pong", :do_it] => "Ping"}) do
|
93
|
+
with_test_class("Button", :states => ["On", "Off"], :initial_state => "Off", :transitions => {["On", :press] => "Off", ["Off", :press] => "On"}) do
|
94
|
+
pingpong = PingPong.new
|
95
|
+
button = Button.new
|
96
|
+
[pingpong.do_it, button.press, pingpong.do_it, button.press]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module TestClassCreationHelper
|
2
|
+
#TODO: ugly
|
3
|
+
def with_test_class(main_state_module_name, options = {})
|
4
|
+
created_consts = []
|
5
|
+
transitions = options[:transitions] || {}
|
6
|
+
state_methods = transitions.keys.map{|t| t.last}.uniq || []
|
7
|
+
|
8
|
+
if options.has_key?(:states)
|
9
|
+
options[:states].each do |state_name|
|
10
|
+
created_consts << create_class(state_name, StatePattern::State) do
|
11
|
+
state_methods.each do |method_name|
|
12
|
+
define_method method_name do
|
13
|
+
next_state_name = transitions[[state_name, method_name]]
|
14
|
+
next_state_module = next_state_name && Object.const_get(next_state_name)
|
15
|
+
transition_to next_state_module
|
16
|
+
next_state_name.downcase
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
created_consts << create_class(main_state_module_name) do
|
24
|
+
include StatePattern
|
25
|
+
add_states *options[:states].map{|s| Object.const_get(s)} if options.has_key?(:states)
|
26
|
+
set_initial_state Object.const_get(options[:initial_state]) if options.has_key?(:initial_state)
|
27
|
+
if options.has_key?(:valid_transitions)
|
28
|
+
valid_transitions_with_constants = {}
|
29
|
+
options[:valid_transitions].each do |from_module_string_or_array, to_module_string|
|
30
|
+
if from_module_string_or_array.respond_to?(:to_ary)
|
31
|
+
valid_transitions_with_constants[[Object.const_get(from_module_string_or_array.first), from_module_string_or_array.last]] = Object.const_get(to_module_string)
|
32
|
+
else
|
33
|
+
valid_transitions_with_constants[Object.const_get(from_module_string_or_array)] = Object.const_get(to_module_string)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
valid_transitions valid_transitions_with_constants
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
begin
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
created_consts.each do |created_const|
|
44
|
+
Object.send(:remove_const, created_const.to_s.to_sym)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def create_module(module_name, superklass = Object, module_or_class = Module, &block)
|
52
|
+
new_module = module_or_class.new(superklass, &block)
|
53
|
+
Object.const_set(module_name, new_module) unless Object.const_defined? module_name
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_class(class_name, superklass = Object, &block)
|
57
|
+
create_module(class_name, superklass, Class, &block)
|
58
|
+
end
|
59
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'expectations'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
require 'state_pattern'
|
7
|
+
require 'test_class_creation_helper'
|
8
|
+
|
9
|
+
include TestClassCreationHelper
|
10
|
+
|
11
|
+
class Test::Unit::TestCase
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
Expectations do
|
4
|
+
expect "up" do
|
5
|
+
with_test_class("Switch", :states => ["Up", "Down", "Middle"], :initial_state => "Middle",
|
6
|
+
:transitions => {["Up", :push_down] => "Middle",
|
7
|
+
["Down", :push_up] => "Middle",
|
8
|
+
["Middle", :push_up] => "Up",
|
9
|
+
["Middle", :push_down] => "Down"}) do
|
10
|
+
switch = Switch.new
|
11
|
+
switch.push_up
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
expect "up" do
|
16
|
+
with_test_class("Switch", :states => ["Up", "Down", "Middle"], :initial_state => "Middle",
|
17
|
+
:transitions => {["Up", :push_down] => "Middle",
|
18
|
+
["Down", :push_up] => "Middle",
|
19
|
+
["Middle", :push_up] => "Up",
|
20
|
+
["Middle", :push_down] => "Down"},
|
21
|
+
:valid_transitions => {["Up", :push_down] => "Middle",
|
22
|
+
["Down", :push_up] => "Middle",
|
23
|
+
["Middle", :push_up] => "Up",
|
24
|
+
["Middle", :push_down] => "Down"}) do
|
25
|
+
switch = Switch.new
|
26
|
+
switch.push_up
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
expect StatePattern::InvalidTransitionException do
|
31
|
+
with_test_class("Switch", :states => ["Up", "Down", "Middle"], :initial_state => "Middle",
|
32
|
+
:transitions => {["Up", :push_down] => "Middle",
|
33
|
+
["Down", :push_up] => "Middle",
|
34
|
+
["Middle", :push_up] => "Up",
|
35
|
+
["Middle", :push_down] => "Down"},
|
36
|
+
:valid_transitions => {["Up", :push_down] => "Middle",
|
37
|
+
["Down", :push_up] => "Middle",
|
38
|
+
["Middle", :push_down] => "Down"}) do
|
39
|
+
switch = Switch.new
|
40
|
+
switch.push_up
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
expect StatePattern::InvalidTransitionException do
|
45
|
+
with_test_class("Switch", :states => ["Up", "Down", "Middle"], :initial_state => "Middle",
|
46
|
+
:transitions => {["Up", :push_down] => "Middle",
|
47
|
+
["Down", :push_up] => "Middle",
|
48
|
+
["Middle", :push_up] => "Up",
|
49
|
+
["Middle", :push_down] => "Down"},
|
50
|
+
:valid_transitions => {"Up" => "Middle", "Down" => "Middle", "Middle" => "Down"}) do
|
51
|
+
switch = Switch.new
|
52
|
+
switch.push_up
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: state_pattern
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Cadenas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-08 00:00:00 -03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: dcadenas@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
28
|
+
- LICENSE
|
29
|
+
- README.rdoc
|
30
|
+
- Rakefile
|
31
|
+
- VERSION
|
32
|
+
- lib/state_pattern.rb
|
33
|
+
- lib/state_pattern/invalid_transition_exception.rb
|
34
|
+
- lib/state_pattern/state.rb
|
35
|
+
- state_pattern.gemspec
|
36
|
+
- test/state_pattern_test.rb
|
37
|
+
- test/test_class_creation_helper.rb
|
38
|
+
- test/test_helper.rb
|
39
|
+
- test/transition_validations_test.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/dcadenas/state_pattern
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --charset=UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project: statepattern
|
64
|
+
rubygems_version: 1.3.3
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: A Ruby state pattern implementation
|
68
|
+
test_files:
|
69
|
+
- test/state_pattern_test.rb
|
70
|
+
- test/test_class_creation_helper.rb
|
71
|
+
- test/test_helper.rb
|
72
|
+
- test/transition_validations_test.rb
|