golem_statemachine 0.9
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/.gitignore +3 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +472 -0
- data/Rakefile +23 -0
- data/examples/document.rb +44 -0
- data/examples/monster.rb +100 -0
- data/examples/seminar.rb +138 -0
- data/examples/seminar_enrollment.rb +141 -0
- data/init.rb +5 -0
- data/install.rb +1 -0
- data/lib/golem.rb +207 -0
- data/lib/golem/dsl/decision_def.rb +38 -0
- data/lib/golem/dsl/event_def.rb +89 -0
- data/lib/golem/dsl/state_def.rb +34 -0
- data/lib/golem/dsl/state_machine_def.rb +73 -0
- data/lib/golem/dsl/transition_def.rb +51 -0
- data/lib/golem/model/callback.rb +32 -0
- data/lib/golem/model/condition.rb +12 -0
- data/lib/golem/model/event.rb +12 -0
- data/lib/golem/model/state.rb +26 -0
- data/lib/golem/model/state_machine.rb +224 -0
- data/lib/golem/model/transition.rb +37 -0
- data/lib/golem/util/element_collection.rb +33 -0
- data/tasks/golem_statemachine_tasks.rake +4 -0
- data/test/active_record_test.rb +189 -0
- data/test/dsl_test.rb +429 -0
- data/test/monster_test.rb +110 -0
- data/test/problematic_test.rb +95 -0
- data/test/seminar_test.rb +106 -0
- data/test/statemachine_assertions.rb +79 -0
- data/test/test_helper.rb +5 -0
- data/uninstall.rb +1 -0
- metadata +119 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ruby-debug'
|
3
|
+
|
4
|
+
# Tests specifically designed to address bugs/problems discovered along the way.
|
5
|
+
class ProblematicTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@klass = Class.new
|
9
|
+
@klass.instance_eval do
|
10
|
+
include Golem
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_fire_event_for_multiple_statemachines
|
15
|
+
|
16
|
+
@klass.instance_eval do
|
17
|
+
define_statemachine(:engine) do
|
18
|
+
initial_state :stopped
|
19
|
+
state :stopped do
|
20
|
+
on :start, :to => :idle
|
21
|
+
end
|
22
|
+
state :idle do
|
23
|
+
on :start, :to => :running
|
24
|
+
end
|
25
|
+
end
|
26
|
+
define_statemachine(:fan) do
|
27
|
+
initial_state :stopped
|
28
|
+
state :stopped do
|
29
|
+
on :start, :to => :spinning
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
widget = @klass.new
|
35
|
+
|
36
|
+
assert_equal :stopped, widget.engine_state
|
37
|
+
assert_equal :stopped, widget.fan_state
|
38
|
+
|
39
|
+
widget.start
|
40
|
+
|
41
|
+
assert_equal :idle, widget.engine_state
|
42
|
+
assert_equal :spinning, widget.fan_state
|
43
|
+
|
44
|
+
assert_nothing_raised do
|
45
|
+
# :fan will raise Golem::ImpossibleEvent, but :engine can proceed, so nothing is raised
|
46
|
+
widget.start!
|
47
|
+
end
|
48
|
+
|
49
|
+
assert_equal :running, widget.engine_state
|
50
|
+
assert_equal :spinning, widget.fan_state
|
51
|
+
|
52
|
+
assert !widget.start
|
53
|
+
|
54
|
+
assert_raise(Golem::ImpossibleEvent) do
|
55
|
+
# neither :fan nor :engine can proceed
|
56
|
+
widget.start!
|
57
|
+
end
|
58
|
+
|
59
|
+
assert !widget.start
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_fire_entry_action_on_initial_state
|
63
|
+
@klass.instance_eval do
|
64
|
+
class_eval do
|
65
|
+
attr_accessor :off
|
66
|
+
end
|
67
|
+
|
68
|
+
define_statemachine(:engine) do
|
69
|
+
initial_state :stopped
|
70
|
+
state :stopped do
|
71
|
+
enter do |engine|
|
72
|
+
engine.off = true
|
73
|
+
end
|
74
|
+
on :start, :to => :idle
|
75
|
+
end
|
76
|
+
state :idle do
|
77
|
+
enter do |engine|
|
78
|
+
engine.off = false
|
79
|
+
end
|
80
|
+
on :start, :to => :running
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
widget = @klass.new
|
86
|
+
|
87
|
+
assert_equal :stopped, widget.engine_state
|
88
|
+
assert_equal true, widget.off
|
89
|
+
|
90
|
+
widget.start!
|
91
|
+
|
92
|
+
assert_equal false, widget.off
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require File.dirname(__FILE__)+'/../examples/seminar'
|
3
|
+
|
4
|
+
# Test the seminar.rb example.
|
5
|
+
class SeminarTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@seminar = Seminar.new
|
9
|
+
Seminar.output = File.open("#{self.class.name}_#{self.method_name}.log", 'w')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_initial_status
|
13
|
+
assert_equal :proposed, @seminar.statemachine.initial_state
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_open_for_enrollment
|
17
|
+
@seminar.max_class_size = 3
|
18
|
+
|
19
|
+
@seminar.schedule
|
20
|
+
@seminar.open
|
21
|
+
|
22
|
+
assert_equal :open_for_enrollment, @seminar.status
|
23
|
+
|
24
|
+
@seminar.enroll_student("socrates")
|
25
|
+
@seminar.enroll_student("plato")
|
26
|
+
|
27
|
+
assert_equal :open_for_enrollment, @seminar.status
|
28
|
+
|
29
|
+
@seminar.drop_student("socrates")
|
30
|
+
|
31
|
+
assert_equal :open_for_enrollment, @seminar.status
|
32
|
+
assert_equal ["plato"], @seminar.students
|
33
|
+
|
34
|
+
@seminar.enroll_student("aristotle")
|
35
|
+
@seminar.enroll_student("socrates")
|
36
|
+
|
37
|
+
assert_equal :full, @seminar.status
|
38
|
+
|
39
|
+
@seminar.drop_student("plato")
|
40
|
+
|
41
|
+
assert_equal :open_for_enrollment, @seminar.status
|
42
|
+
assert_equal ["aristotle", "socrates"], @seminar.students
|
43
|
+
|
44
|
+
@seminar.enroll_student("zeno")
|
45
|
+
|
46
|
+
assert_equal :full, @seminar.status
|
47
|
+
|
48
|
+
@seminar.cancel
|
49
|
+
|
50
|
+
assert_equal :cancelled, @seminar.status
|
51
|
+
@seminar.students.each do |student|
|
52
|
+
assert @seminar.notifications_sent.include?("#{student}: the seminar has been cancelled")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_full
|
57
|
+
@seminar.max_class_size = 3
|
58
|
+
|
59
|
+
@seminar.schedule
|
60
|
+
@seminar.open
|
61
|
+
|
62
|
+
@seminar.enroll_student("socrates")
|
63
|
+
@seminar.enroll_student("plato")
|
64
|
+
@seminar.enroll_student("aristotle")
|
65
|
+
|
66
|
+
assert_equal :full, @seminar.status
|
67
|
+
|
68
|
+
@seminar.enroll_student("zeno")
|
69
|
+
|
70
|
+
assert_equal :full, @seminar.status
|
71
|
+
assert_equal ["socrates", "plato", "aristotle"], @seminar.students
|
72
|
+
assert_equal ["zeno"], @seminar.waiting_list
|
73
|
+
|
74
|
+
@seminar.enroll_student("sofia")
|
75
|
+
|
76
|
+
assert_equal :full, @seminar.status
|
77
|
+
assert_equal ["socrates", "plato", "aristotle"], @seminar.students
|
78
|
+
assert_equal ["zeno", "sofia"], @seminar.waiting_list
|
79
|
+
|
80
|
+
@seminar.drop_student("socrates")
|
81
|
+
|
82
|
+
assert_equal :full, @seminar.status
|
83
|
+
assert_equal ["plato", "aristotle", "zeno"], @seminar.students
|
84
|
+
assert_equal ["sofia"], @seminar.waiting_list
|
85
|
+
|
86
|
+
@seminar.drop_student("aristotle")
|
87
|
+
|
88
|
+
assert_equal :full, @seminar.status
|
89
|
+
assert_equal ["plato", "zeno", "sofia"], @seminar.students
|
90
|
+
assert_equal [], @seminar.waiting_list
|
91
|
+
|
92
|
+
@seminar.enroll_student("dennett")
|
93
|
+
|
94
|
+
assert_equal :full, @seminar.status
|
95
|
+
assert_equal ["plato", "zeno", "sofia"], @seminar.students
|
96
|
+
assert_equal ["dennett"], @seminar.waiting_list
|
97
|
+
|
98
|
+
@seminar.cancel
|
99
|
+
|
100
|
+
assert_equal :cancelled, @seminar.status
|
101
|
+
(@seminar.students + @seminar.waiting_list).each do |student|
|
102
|
+
assert @seminar.notifications_sent.include?("#{student}: the seminar has been cancelled")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module StatemachineAssertions
|
2
|
+
def assert_equal_arrays(arr1, arr2)
|
3
|
+
assert_equal [], arr1 - arr2, "array #{arr1} is not equal to array #{arr2}"
|
4
|
+
end
|
5
|
+
|
6
|
+
def assert_transition_on_event(statemachine, event, from_state, to_state)
|
7
|
+
from_state = statemachine.states[from_state]
|
8
|
+
to_state = statemachine.states[to_state]
|
9
|
+
event = statemachine.events[event]
|
10
|
+
|
11
|
+
assert_not_nil from_state
|
12
|
+
assert_not_nil to_state
|
13
|
+
assert_not_nil event
|
14
|
+
|
15
|
+
failure_msg = "#{statemachine} does not have a transition from #{from_state.name.inspect} to #{to_state.name.inspect} on event #{event.name.inspect}"
|
16
|
+
failure_msg << "\n\tHas: #{from_state.transitions_on_event[event].collect{|tr| tr.to_s}.join("\n")}"
|
17
|
+
|
18
|
+
|
19
|
+
assert from_state.transitions_on_event[event].any?{|tr| tr.from == from_state && tr.to == to_state}, failure_msg
|
20
|
+
end
|
21
|
+
|
22
|
+
def assert_no_transition_on_event(statemachine, event, from_state, to_state)
|
23
|
+
from_state = statemachine.states[from_state]
|
24
|
+
to_state = statemachine.states[to_state]
|
25
|
+
event = statemachine.events[event]
|
26
|
+
|
27
|
+
assert_not_nil from_state
|
28
|
+
assert_not_nil to_state
|
29
|
+
assert_not_nil event
|
30
|
+
|
31
|
+
failure_msg = "#{statemachine} should not have a transition from #{from_state.name.inspect} to #{to_state.name.inspect} on event #{event.name.inspect}"
|
32
|
+
|
33
|
+
assert !from_state.transitions_on_event[event].any?{|tr| tr.from == from_state && tr.to == to_state}, failure_msg
|
34
|
+
end
|
35
|
+
|
36
|
+
def assert_transition_on_event_has_callback(statemachine, event, from_state, to_state, callback, callback_method = nil)
|
37
|
+
from_state = statemachine.states[from_state]
|
38
|
+
to_state = statemachine.states[to_state]
|
39
|
+
event = statemachine.events[event]
|
40
|
+
|
41
|
+
assert_transition_on_event(statemachine, event, from_state, to_state)
|
42
|
+
|
43
|
+
tr = from_state.transitions_on_event[event].find{|tr| tr.to == to_state}
|
44
|
+
|
45
|
+
failure_msg = "#{tr.to_s.inspect} does not have an #{callback.inspect} callback"
|
46
|
+
|
47
|
+
assert_not_nil tr.callbacks[callback], failure_msg
|
48
|
+
|
49
|
+
assert_equal callback_method, tr.callbacks[callback].callback if callback_method
|
50
|
+
end
|
51
|
+
|
52
|
+
def assert_transition_on_event_has_guard(statemachine, event, from_state, to_state, guard)
|
53
|
+
from_state = statemachine.states[from_state]
|
54
|
+
to_state = statemachine.states[to_state]
|
55
|
+
event = statemachine.events[event]
|
56
|
+
|
57
|
+
assert_transition_on_event(statemachine, event, from_state, to_state)
|
58
|
+
|
59
|
+
tr = from_state.transitions_on_event[event].find{|tr| tr.to == to_state}
|
60
|
+
|
61
|
+
failure_msg = "#{tr.to_s.inspect} does not have #{guard.inspect} as a guard condition"
|
62
|
+
|
63
|
+
assert tr.guards.collect{|g| g.callback}.include?(guard), failure_msg
|
64
|
+
end
|
65
|
+
|
66
|
+
def assert_transition_on_event_does_not_have_guard(statemachine, event, from_state, to_state, guard)
|
67
|
+
from_state = statemachine.states[from_state]
|
68
|
+
to_state = statemachine.states[to_state]
|
69
|
+
event = statemachine.events[event]
|
70
|
+
|
71
|
+
assert_transition_on_event(statemachine, event, from_state, to_state)
|
72
|
+
|
73
|
+
tr = from_state.transitions_on_event[event].find{|tr| tr.to == to_state}
|
74
|
+
|
75
|
+
failure_msg = "#{tr.to_s.inspect} should not have #{guard.inspect} as a guard condition"
|
76
|
+
|
77
|
+
assert !tr.guards.collect{|g| g.callback}.include?(guard), failure_msg
|
78
|
+
end
|
79
|
+
end
|
data/test/test_helper.rb
ADDED
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: golem_statemachine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 9
|
9
|
+
version: "0.9"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Matt Zukowski
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-04-28 00:00:00 Z
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: activesupport
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
description: Adds finite state machine behaviour to Ruby classes. Meant as an alternative to acts_as_state_machine/AASM.
|
34
|
+
email:
|
35
|
+
- matt@roughest.net
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- README.rdoc
|
42
|
+
- MIT-LICENSE
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- MIT-LICENSE
|
46
|
+
- README.rdoc
|
47
|
+
- Rakefile
|
48
|
+
- examples/document.rb
|
49
|
+
- examples/monster.rb
|
50
|
+
- examples/seminar.rb
|
51
|
+
- examples/seminar_enrollment.rb
|
52
|
+
- init.rb
|
53
|
+
- install.rb
|
54
|
+
- lib/golem.rb
|
55
|
+
- lib/golem/dsl/decision_def.rb
|
56
|
+
- lib/golem/dsl/event_def.rb
|
57
|
+
- lib/golem/dsl/state_def.rb
|
58
|
+
- lib/golem/dsl/state_machine_def.rb
|
59
|
+
- lib/golem/dsl/transition_def.rb
|
60
|
+
- lib/golem/model/callback.rb
|
61
|
+
- lib/golem/model/condition.rb
|
62
|
+
- lib/golem/model/event.rb
|
63
|
+
- lib/golem/model/state.rb
|
64
|
+
- lib/golem/model/state_machine.rb
|
65
|
+
- lib/golem/model/transition.rb
|
66
|
+
- lib/golem/util/element_collection.rb
|
67
|
+
- tasks/golem_statemachine_tasks.rake
|
68
|
+
- test/active_record_test.rb
|
69
|
+
- test/dsl_test.rb
|
70
|
+
- test/monster_test.rb
|
71
|
+
- test/problematic_test.rb
|
72
|
+
- test/seminar_test.rb
|
73
|
+
- test/statemachine_assertions.rb
|
74
|
+
- test/test.db
|
75
|
+
- test/test_helper.rb
|
76
|
+
- uninstall.rb
|
77
|
+
homepage: http://github.com/zuk/golem_statemachine
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options:
|
82
|
+
- --quiet
|
83
|
+
- --title
|
84
|
+
- Golem Statmeachine Docs
|
85
|
+
- --opname
|
86
|
+
- index.html
|
87
|
+
- --line-numbers
|
88
|
+
- --main
|
89
|
+
- README.rdoc
|
90
|
+
- --inline-source
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: 3
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
version: "0"
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
hash: 3
|
108
|
+
segments:
|
109
|
+
- 0
|
110
|
+
version: "0"
|
111
|
+
requirements: []
|
112
|
+
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.7.2
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: Adds finite state machine behaviour to Ruby classes.
|
118
|
+
test_files: []
|
119
|
+
|