simplificator-fsm 0.3.5 → 0.3.7
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 +8 -0
- data/README.markdown +1 -1
- data/Rakefile +1 -1
- data/VERSION.yml +2 -2
- data/lib/fsm/builder.rb +1 -1
- data/lib/fsm/dot.rb +61 -0
- data/lib/fsm/machine.rb +1 -22
- data/lib/fsm/state.rb +0 -17
- data/lib/fsm/state_attribute_interceptor.rb +2 -1
- data/lib/fsm.rb +16 -8
- data/samples/ticket/ticket.png +0 -0
- data/samples/ticket/ticket.ps +472 -0
- data/samples/ticket/ticket_sample.rb +20 -0
- data/test/ar_test.rb +16 -10
- data/test/invoice_sample_test.rb +34 -15
- data/test/test_helper.rb +8 -0
- metadata +11 -3
data/.document
ADDED
data/.gitignore
ADDED
data/README.markdown
CHANGED
@@ -27,7 +27,7 @@ for suggestions!
|
|
27
27
|
transition(:cool_down, [:gas, :liquid], :liquid, :event => :on_cool)
|
28
28
|
|
29
29
|
# define the attribute which is used to store the state (defaults to :state)
|
30
|
-
|
30
|
+
state_attribute(:state_of_material)
|
31
31
|
|
32
32
|
# define the initial state (defaults to the first state defined - :gas in this sample)
|
33
33
|
initial(:liquid)
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ require 'rake'
|
|
4
4
|
begin
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "fsm"
|
7
|
+
gem.name = "simplificator-fsm"
|
8
8
|
gem.summary = %Q{A simple finite state machine (FSM) gem.}
|
9
9
|
gem.email = "info@simplificator.com"
|
10
10
|
gem.homepage = "http://github.com/simplificator/fsm"
|
data/VERSION.yml
CHANGED
data/lib/fsm/builder.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module FSM
|
2
|
-
# Builder
|
2
|
+
# Builder exposes 'only' (well there are some other methods exposed) the methods that are required to build the configuration
|
3
3
|
class Builder
|
4
4
|
|
5
5
|
# Blank Slate
|
data/lib/fsm/dot.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module FSM
|
2
|
+
module Dot
|
3
|
+
def self.included(includer)
|
4
|
+
FSM::Machine.class_eval do
|
5
|
+
include(MachineInstanceMethods)
|
6
|
+
end
|
7
|
+
FSM::State.class_eval do
|
8
|
+
include(StateInstanceMethods)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module MachineInstanceMethods
|
13
|
+
# Convert this state machine to the dot format of graphviz
|
14
|
+
def to_dot(options = {})
|
15
|
+
s = self.states.map do |state|
|
16
|
+
" #{state.to_dot(options)};"
|
17
|
+
end
|
18
|
+
t = self.transitions.map do |transition|
|
19
|
+
" #{transition.to_dot(options)};"
|
20
|
+
end
|
21
|
+
"digraph FSM_#{@target_class.name} {\n#{s.join("\n")}\n\n#{t.join("\n")}\n}"
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
def draw_graph(options = {})
|
26
|
+
format = options[:format] || :png
|
27
|
+
extension = options[:extension] || format
|
28
|
+
file_name = options[:outfile] || "#{@target_class.name.downcase}.#{extension}"
|
29
|
+
cmd = "dot -T#{format} -o#{file_name}"
|
30
|
+
IO.popen cmd, 'w' do |io|
|
31
|
+
io.write to_dot
|
32
|
+
end
|
33
|
+
raise 'dot failed' unless $?.success?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module StateInstanceMethods
|
38
|
+
def to_dot(options = {})
|
39
|
+
if initial?
|
40
|
+
attrs = "style=bold, label=\"#{self.name}\\n(initial)\""
|
41
|
+
elsif final?
|
42
|
+
attrs = "style=bold"
|
43
|
+
else
|
44
|
+
attrs = ""
|
45
|
+
end
|
46
|
+
"#{self.name}[#{attrs}]"
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# Is this state final?
|
51
|
+
def final?
|
52
|
+
@transitions.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Is this the initial state
|
56
|
+
def initial?
|
57
|
+
Machine[@target_class].initial_state_name == self.name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/fsm/machine.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module FSM
|
2
2
|
class Machine
|
3
|
+
include FSM::Dot
|
3
4
|
attr_accessor(:initial_state_name, :current_state_attribute_name, :states, :transitions)
|
4
5
|
|
5
6
|
def initialize(target_class)
|
@@ -79,28 +80,6 @@ module FSM
|
|
79
80
|
state
|
80
81
|
end
|
81
82
|
|
82
|
-
# Convert this state machine to the dot format of graphviz
|
83
|
-
def to_dot(options = {})
|
84
|
-
s = self.states.map do |state|
|
85
|
-
" #{state.to_dot(options)};"
|
86
|
-
end
|
87
|
-
t = self.transitions.map do |transition|
|
88
|
-
" #{transition.to_dot(options)};"
|
89
|
-
end
|
90
|
-
"digraph FSM_#{@target_class.name} {\n#{s.join("\n")}\n\n#{t.join("\n")}\n}"
|
91
|
-
end
|
92
|
-
|
93
|
-
#
|
94
|
-
def draw_graph(options = {})
|
95
|
-
format = options[:format] || :png
|
96
|
-
extension = options[:extension] || format
|
97
|
-
file_name = options[:outfile] || "#{@target_class.name.downcase}.#{extension}"
|
98
|
-
cmd = "dot -T#{format} -o#{file_name}"
|
99
|
-
IO.popen cmd, 'w' do |io|
|
100
|
-
io.write to_dot
|
101
|
-
end
|
102
|
-
raise 'dot failed' unless $?.success?
|
103
|
-
end
|
104
83
|
|
105
84
|
private
|
106
85
|
|
data/lib/fsm/state.rb
CHANGED
@@ -41,27 +41,10 @@ module FSM
|
|
41
41
|
def to_states
|
42
42
|
@transitions.map { |to_name, transition| transition.to}
|
43
43
|
end
|
44
|
-
def final?
|
45
|
-
@transitions.empty?
|
46
|
-
end
|
47
|
-
|
48
|
-
def initial?
|
49
|
-
Machine[@target_class].initial_state_name == self.name
|
50
|
-
end
|
51
44
|
|
52
45
|
def to_s
|
53
46
|
"State '#{self.name}' is "
|
54
47
|
end
|
55
48
|
|
56
|
-
def to_dot(options = {})
|
57
|
-
if initial?
|
58
|
-
attrs = "style=bold, label=\"#{self.name}\\n(initial)\""
|
59
|
-
elsif final?
|
60
|
-
attrs = "style=bold"
|
61
|
-
else
|
62
|
-
attrs = ""
|
63
|
-
end
|
64
|
-
"#{self.name}[#{attrs}]"
|
65
|
-
end
|
66
49
|
end
|
67
50
|
end
|
@@ -27,7 +27,8 @@ module FSM
|
|
27
27
|
def self.bar(klass)
|
28
28
|
klass.instance_eval() do
|
29
29
|
define_method(Machine[klass].current_state_attribute_name) do
|
30
|
-
value = read_attribute(Machine[self.class].current_state_attribute_name)
|
30
|
+
value = read_attribute(Machine[self.class].current_state_attribute_name)
|
31
|
+
value = Machine[self.class].initial_state_name if value.blank?
|
31
32
|
value.is_a?(String) ? value.intern : value
|
32
33
|
end
|
33
34
|
end
|
data/lib/fsm.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
%w[options errors machine
|
1
|
+
%w[dot options state errors machine transition executable builder state_attribute_interceptor].each do |item|
|
2
2
|
require File.join(File.dirname(__FILE__), 'fsm', item)
|
3
3
|
end
|
4
4
|
|
@@ -13,14 +13,12 @@ module FSM
|
|
13
13
|
# TODO: other checks? islands?
|
14
14
|
|
15
15
|
# create alias for state attribute method to intercept it
|
16
|
-
# intercept
|
17
16
|
FSM::StateAttributeInterceptor.add_interceptor(self)
|
18
17
|
end
|
19
18
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
machine.draw_graph(options)
|
19
|
+
def fsm_draw_graph(options = {})
|
20
|
+
raise 'No FSM defined. Call define_fsm first' unless Machine[self]
|
21
|
+
Machine[self].draw_graph(options)
|
24
22
|
end
|
25
23
|
|
26
24
|
end
|
@@ -28,13 +26,23 @@ module FSM
|
|
28
26
|
module InstanceMethods
|
29
27
|
#
|
30
28
|
# Which states are reachable from the current state
|
31
|
-
def
|
29
|
+
def fsm_next_state_names
|
32
30
|
Machine[self.class].reachable_states(self).map() {|item| item.name}
|
33
31
|
end
|
34
32
|
|
35
|
-
def
|
33
|
+
def fsm_state_names
|
34
|
+
Machine[self.class].states.map() {|item| item.name}
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# What are the next transitions
|
39
|
+
def fsm_next_transition_names
|
36
40
|
Machine[self.class].available_transitions(self).map() {|item| item.name}
|
37
41
|
end
|
42
|
+
|
43
|
+
def fsm_transition_names
|
44
|
+
Machine[self.class].transitions.map() {|item| item.name}
|
45
|
+
end
|
38
46
|
end
|
39
47
|
|
40
48
|
def self.included(receiver)
|
Binary file
|
@@ -0,0 +1,472 @@
|
|
1
|
+
%!PS-Adobe-3.0
|
2
|
+
%%Creator: graphviz version 2.22.2 (20090313.1817)
|
3
|
+
%%Title: FSM_Ticket
|
4
|
+
%%Pages: (atend)
|
5
|
+
%%BoundingBox: (atend)
|
6
|
+
%%EndComments
|
7
|
+
save
|
8
|
+
%%BeginProlog
|
9
|
+
/DotDict 200 dict def
|
10
|
+
DotDict begin
|
11
|
+
|
12
|
+
/setupLatin1 {
|
13
|
+
mark
|
14
|
+
/EncodingVector 256 array def
|
15
|
+
EncodingVector 0
|
16
|
+
|
17
|
+
ISOLatin1Encoding 0 255 getinterval putinterval
|
18
|
+
EncodingVector 45 /hyphen put
|
19
|
+
|
20
|
+
% Set up ISO Latin 1 character encoding
|
21
|
+
/starnetISO {
|
22
|
+
dup dup findfont dup length dict begin
|
23
|
+
{ 1 index /FID ne { def }{ pop pop } ifelse
|
24
|
+
} forall
|
25
|
+
/Encoding EncodingVector def
|
26
|
+
currentdict end definefont
|
27
|
+
} def
|
28
|
+
/Times-Roman starnetISO def
|
29
|
+
/Times-Italic starnetISO def
|
30
|
+
/Times-Bold starnetISO def
|
31
|
+
/Times-BoldItalic starnetISO def
|
32
|
+
/Helvetica starnetISO def
|
33
|
+
/Helvetica-Oblique starnetISO def
|
34
|
+
/Helvetica-Bold starnetISO def
|
35
|
+
/Helvetica-BoldOblique starnetISO def
|
36
|
+
/Courier starnetISO def
|
37
|
+
/Courier-Oblique starnetISO def
|
38
|
+
/Courier-Bold starnetISO def
|
39
|
+
/Courier-BoldOblique starnetISO def
|
40
|
+
cleartomark
|
41
|
+
} bind def
|
42
|
+
|
43
|
+
%%BeginResource: procset graphviz 0 0
|
44
|
+
/coord-font-family /Times-Roman def
|
45
|
+
/default-font-family /Times-Roman def
|
46
|
+
/coordfont coord-font-family findfont 8 scalefont def
|
47
|
+
|
48
|
+
/InvScaleFactor 1.0 def
|
49
|
+
/set_scale {
|
50
|
+
dup 1 exch div /InvScaleFactor exch def
|
51
|
+
scale
|
52
|
+
} bind def
|
53
|
+
|
54
|
+
% styles
|
55
|
+
/solid { [] 0 setdash } bind def
|
56
|
+
/dashed { [9 InvScaleFactor mul dup ] 0 setdash } bind def
|
57
|
+
/dotted { [1 InvScaleFactor mul 6 InvScaleFactor mul] 0 setdash } bind def
|
58
|
+
/invis {/fill {newpath} def /stroke {newpath} def /show {pop newpath} def} bind def
|
59
|
+
/bold { 2 setlinewidth } bind def
|
60
|
+
/filled { } bind def
|
61
|
+
/unfilled { } bind def
|
62
|
+
/rounded { } bind def
|
63
|
+
/diagonals { } bind def
|
64
|
+
|
65
|
+
% hooks for setting color
|
66
|
+
/nodecolor { sethsbcolor } bind def
|
67
|
+
/edgecolor { sethsbcolor } bind def
|
68
|
+
/graphcolor { sethsbcolor } bind def
|
69
|
+
/nopcolor {pop pop pop} bind def
|
70
|
+
|
71
|
+
/beginpage { % i j npages
|
72
|
+
/npages exch def
|
73
|
+
/j exch def
|
74
|
+
/i exch def
|
75
|
+
/str 10 string def
|
76
|
+
npages 1 gt {
|
77
|
+
gsave
|
78
|
+
coordfont setfont
|
79
|
+
0 0 moveto
|
80
|
+
(\() show i str cvs show (,) show j str cvs show (\)) show
|
81
|
+
grestore
|
82
|
+
} if
|
83
|
+
} bind def
|
84
|
+
|
85
|
+
/set_font {
|
86
|
+
findfont exch
|
87
|
+
scalefont setfont
|
88
|
+
} def
|
89
|
+
|
90
|
+
% draw text fitted to its expected width
|
91
|
+
/alignedtext { % width text
|
92
|
+
/text exch def
|
93
|
+
/width exch def
|
94
|
+
gsave
|
95
|
+
width 0 gt {
|
96
|
+
[] 0 setdash
|
97
|
+
text stringwidth pop width exch sub text length div 0 text ashow
|
98
|
+
} if
|
99
|
+
grestore
|
100
|
+
} def
|
101
|
+
|
102
|
+
/boxprim { % xcorner ycorner xsize ysize
|
103
|
+
4 2 roll
|
104
|
+
moveto
|
105
|
+
2 copy
|
106
|
+
exch 0 rlineto
|
107
|
+
0 exch rlineto
|
108
|
+
pop neg 0 rlineto
|
109
|
+
closepath
|
110
|
+
} bind def
|
111
|
+
|
112
|
+
/ellipse_path {
|
113
|
+
/ry exch def
|
114
|
+
/rx exch def
|
115
|
+
/y exch def
|
116
|
+
/x exch def
|
117
|
+
matrix currentmatrix
|
118
|
+
newpath
|
119
|
+
x y translate
|
120
|
+
rx ry scale
|
121
|
+
0 0 1 0 360 arc
|
122
|
+
setmatrix
|
123
|
+
} bind def
|
124
|
+
|
125
|
+
/endpage { showpage } bind def
|
126
|
+
/showpage { } def
|
127
|
+
|
128
|
+
/layercolorseq
|
129
|
+
[ % layer color sequence - darkest to lightest
|
130
|
+
[0 0 0]
|
131
|
+
[.2 .8 .8]
|
132
|
+
[.4 .8 .8]
|
133
|
+
[.6 .8 .8]
|
134
|
+
[.8 .8 .8]
|
135
|
+
]
|
136
|
+
def
|
137
|
+
|
138
|
+
/layerlen layercolorseq length def
|
139
|
+
|
140
|
+
/setlayer {/maxlayer exch def /curlayer exch def
|
141
|
+
layercolorseq curlayer 1 sub layerlen mod get
|
142
|
+
aload pop sethsbcolor
|
143
|
+
/nodecolor {nopcolor} def
|
144
|
+
/edgecolor {nopcolor} def
|
145
|
+
/graphcolor {nopcolor} def
|
146
|
+
} bind def
|
147
|
+
|
148
|
+
/onlayer { curlayer ne {invis} if } def
|
149
|
+
|
150
|
+
/onlayers {
|
151
|
+
/myupper exch def
|
152
|
+
/mylower exch def
|
153
|
+
curlayer mylower lt
|
154
|
+
curlayer myupper gt
|
155
|
+
or
|
156
|
+
{invis} if
|
157
|
+
} def
|
158
|
+
|
159
|
+
/curlayer 0 def
|
160
|
+
|
161
|
+
%%EndResource
|
162
|
+
%%EndProlog
|
163
|
+
%%BeginSetup
|
164
|
+
14 default-font-family set_font
|
165
|
+
1 setmiterlimit
|
166
|
+
% /arrowlength 10 def
|
167
|
+
% /arrowwidth 5 def
|
168
|
+
|
169
|
+
% make sure pdfmark is harmless for PS-interpreters other than Distiller
|
170
|
+
/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse
|
171
|
+
% make '<<' and '>>' safe on PS Level 1 devices
|
172
|
+
/languagelevel where {pop languagelevel}{1} ifelse
|
173
|
+
2 lt {
|
174
|
+
userdict (<<) cvn ([) cvn load put
|
175
|
+
userdict (>>) cvn ([) cvn load put
|
176
|
+
} if
|
177
|
+
|
178
|
+
%%EndSetup
|
179
|
+
setupLatin1
|
180
|
+
%%Page: 1 1
|
181
|
+
%%PageBoundingBox: 36 36 358 526
|
182
|
+
%%PageOrientation: Portrait
|
183
|
+
0 0 1 beginpage
|
184
|
+
gsave
|
185
|
+
36 36 322 490 boxprim clip newpath
|
186
|
+
1 1 set_scale 0 rotate 40 41 translate
|
187
|
+
% created
|
188
|
+
gsave
|
189
|
+
2 setlinewidth
|
190
|
+
bold
|
191
|
+
0 0 0 nodecolor
|
192
|
+
221.51 456 41.93 25.46 ellipse_path stroke
|
193
|
+
0 0 0 nodecolor
|
194
|
+
14 /Times-Roman set_font
|
195
|
+
200.51 457.4 moveto 42 (created) alignedtext
|
196
|
+
0 0 0 nodecolor
|
197
|
+
14 /Times-Roman set_font
|
198
|
+
199.51 443.4 moveto 44 (\(initial\)) alignedtext
|
199
|
+
grestore
|
200
|
+
% reserved
|
201
|
+
gsave
|
202
|
+
1 setlinewidth
|
203
|
+
0 0 0 nodecolor
|
204
|
+
171.51 362 41.06 18 ellipse_path stroke
|
205
|
+
0 0 0 nodecolor
|
206
|
+
14 /Times-Roman set_font
|
207
|
+
147.01 356.4 moveto 49 (reserved) alignedtext
|
208
|
+
grestore
|
209
|
+
% created->reserved
|
210
|
+
gsave
|
211
|
+
1 setlinewidth
|
212
|
+
0 0 0 edgecolor
|
213
|
+
newpath 208.37 431.3 moveto
|
214
|
+
201.43 418.26 192.91 402.23 185.8 388.88 curveto
|
215
|
+
stroke
|
216
|
+
0 0 0 edgecolor
|
217
|
+
newpath 188.71 386.89 moveto
|
218
|
+
180.93 379.7 lineto
|
219
|
+
182.53 390.18 lineto
|
220
|
+
closepath fill
|
221
|
+
1 setlinewidth
|
222
|
+
solid
|
223
|
+
0 0 0 edgecolor
|
224
|
+
newpath 188.71 386.89 moveto
|
225
|
+
180.93 379.7 lineto
|
226
|
+
182.53 390.18 lineto
|
227
|
+
closepath stroke
|
228
|
+
0 0 0 edgecolor
|
229
|
+
14 /Times-Roman set_font
|
230
|
+
197.51 399.4 moveto 41 (reserve) alignedtext
|
231
|
+
grestore
|
232
|
+
% invalid
|
233
|
+
gsave
|
234
|
+
2 setlinewidth
|
235
|
+
bold
|
236
|
+
0 0 0 nodecolor
|
237
|
+
135.51 18 36.01 18 ellipse_path stroke
|
238
|
+
0 0 0 nodecolor
|
239
|
+
14 /Times-Roman set_font
|
240
|
+
115.01 12.4 moveto 41 (invalid) alignedtext
|
241
|
+
grestore
|
242
|
+
% created->invalid
|
243
|
+
gsave
|
244
|
+
1 setlinewidth
|
245
|
+
0 0 0 edgecolor
|
246
|
+
newpath 235.95 431.78 moveto
|
247
|
+
245.59 413.31 256.51 386.9 256.51 362 curveto
|
248
|
+
256.51 362 256.51 362 256.51 104 curveto
|
249
|
+
256.51 64.19 212.45 41.11 177.5 29.06 curveto
|
250
|
+
stroke
|
251
|
+
0 0 0 edgecolor
|
252
|
+
newpath 178.53 25.72 moveto
|
253
|
+
167.93 25.96 lineto
|
254
|
+
176.36 32.37 lineto
|
255
|
+
closepath fill
|
256
|
+
1 setlinewidth
|
257
|
+
solid
|
258
|
+
0 0 0 edgecolor
|
259
|
+
newpath 178.53 25.72 moveto
|
260
|
+
167.93 25.96 lineto
|
261
|
+
176.36 32.37 lineto
|
262
|
+
closepath stroke
|
263
|
+
0 0 0 edgecolor
|
264
|
+
14 /Times-Roman set_font
|
265
|
+
256.51 227.4 moveto 57 (invalidate) alignedtext
|
266
|
+
grestore
|
267
|
+
% sold
|
268
|
+
gsave
|
269
|
+
1 setlinewidth
|
270
|
+
0 0 0 nodecolor
|
271
|
+
105.51 276 27 18 ellipse_path stroke
|
272
|
+
0 0 0 nodecolor
|
273
|
+
14 /Times-Roman set_font
|
274
|
+
93.01 270.4 moveto 25 (sold) alignedtext
|
275
|
+
grestore
|
276
|
+
% reserved->sold
|
277
|
+
gsave
|
278
|
+
1 setlinewidth
|
279
|
+
0 0 0 edgecolor
|
280
|
+
newpath 153.98 345.48 moveto
|
281
|
+
148.13 339.59 141.77 332.73 136.51 326 curveto
|
282
|
+
130.66 318.51 124.97 309.87 120.09 301.89 curveto
|
283
|
+
stroke
|
284
|
+
0 0 0 edgecolor
|
285
|
+
newpath 123.09 300.08 moveto
|
286
|
+
114.97 293.27 lineto
|
287
|
+
117.07 303.66 lineto
|
288
|
+
closepath fill
|
289
|
+
1 setlinewidth
|
290
|
+
solid
|
291
|
+
0 0 0 edgecolor
|
292
|
+
newpath 123.09 300.08 moveto
|
293
|
+
114.97 293.27 lineto
|
294
|
+
117.07 303.66 lineto
|
295
|
+
closepath stroke
|
296
|
+
0 0 0 edgecolor
|
297
|
+
14 /Times-Roman set_font
|
298
|
+
136.51 313.4 moveto 21 (buy) alignedtext
|
299
|
+
grestore
|
300
|
+
% reserved->invalid
|
301
|
+
gsave
|
302
|
+
1 setlinewidth
|
303
|
+
0 0 0 edgecolor
|
304
|
+
newpath 171.51 343.88 moveto
|
305
|
+
171.51 326.48 171.51 299.44 171.51 276 curveto
|
306
|
+
171.51 276 171.51 276 171.51 104 curveto
|
307
|
+
171.51 82.76 162.05 60.69 152.82 44.23 curveto
|
308
|
+
stroke
|
309
|
+
0 0 0 edgecolor
|
310
|
+
newpath 155.6 42.06 moveto
|
311
|
+
147.5 35.24 lineto
|
312
|
+
149.58 45.62 lineto
|
313
|
+
closepath fill
|
314
|
+
1 setlinewidth
|
315
|
+
solid
|
316
|
+
0 0 0 edgecolor
|
317
|
+
newpath 155.6 42.06 moveto
|
318
|
+
147.5 35.24 lineto
|
319
|
+
149.58 45.62 lineto
|
320
|
+
closepath stroke
|
321
|
+
0 0 0 edgecolor
|
322
|
+
14 /Times-Roman set_font
|
323
|
+
171.51 184.4 moveto 57 (invalidate) alignedtext
|
324
|
+
grestore
|
325
|
+
% inside
|
326
|
+
gsave
|
327
|
+
1 setlinewidth
|
328
|
+
0 0 0 nodecolor
|
329
|
+
77.51 190 32.22 18 ellipse_path stroke
|
330
|
+
0 0 0 nodecolor
|
331
|
+
14 /Times-Roman set_font
|
332
|
+
60.01 184.4 moveto 35 (inside) alignedtext
|
333
|
+
grestore
|
334
|
+
% sold->inside
|
335
|
+
gsave
|
336
|
+
1 setlinewidth
|
337
|
+
0 0 0 edgecolor
|
338
|
+
newpath 99.71 258.18 moveto
|
339
|
+
95.88 246.42 90.78 230.78 86.44 217.44 curveto
|
340
|
+
stroke
|
341
|
+
0 0 0 edgecolor
|
342
|
+
newpath 89.73 216.22 moveto
|
343
|
+
83.3 207.8 lineto
|
344
|
+
83.07 218.39 lineto
|
345
|
+
closepath fill
|
346
|
+
1 setlinewidth
|
347
|
+
solid
|
348
|
+
0 0 0 edgecolor
|
349
|
+
newpath 89.73 216.22 moveto
|
350
|
+
83.3 207.8 lineto
|
351
|
+
83.07 218.39 lineto
|
352
|
+
closepath stroke
|
353
|
+
0 0 0 edgecolor
|
354
|
+
14 /Times-Roman set_font
|
355
|
+
92.51 227.4 moveto 29 (enter) alignedtext
|
356
|
+
grestore
|
357
|
+
% inside->invalid
|
358
|
+
gsave
|
359
|
+
1 setlinewidth
|
360
|
+
0 0 0 edgecolor
|
361
|
+
newpath 55.89 176.23 moveto
|
362
|
+
27.98 156.66 -14.74 119.44 5.51 86 curveto
|
363
|
+
24.23 55.07 62.16 37.53 92.24 28.05 curveto
|
364
|
+
stroke
|
365
|
+
0 0 0 edgecolor
|
366
|
+
newpath 93.51 31.32 moveto
|
367
|
+
102.1 25.13 lineto
|
368
|
+
91.52 24.61 lineto
|
369
|
+
closepath fill
|
370
|
+
1 setlinewidth
|
371
|
+
solid
|
372
|
+
0 0 0 edgecolor
|
373
|
+
newpath 93.51 31.32 moveto
|
374
|
+
102.1 25.13 lineto
|
375
|
+
91.52 24.61 lineto
|
376
|
+
closepath stroke
|
377
|
+
0 0 0 edgecolor
|
378
|
+
14 /Times-Roman set_font
|
379
|
+
5.51 98.4 moveto 57 (invalidate) alignedtext
|
380
|
+
grestore
|
381
|
+
% outside
|
382
|
+
gsave
|
383
|
+
1 setlinewidth
|
384
|
+
0 0 0 nodecolor
|
385
|
+
107.51 104 36.14 18 ellipse_path stroke
|
386
|
+
0 0 0 nodecolor
|
387
|
+
14 /Times-Roman set_font
|
388
|
+
86.51 98.4 moveto 42 (outside) alignedtext
|
389
|
+
grestore
|
390
|
+
% inside->outside
|
391
|
+
gsave
|
392
|
+
1 setlinewidth
|
393
|
+
0 0 0 edgecolor
|
394
|
+
newpath 70.03 172.29 moveto
|
395
|
+
67 162.45 65.13 150.18 69.51 140 curveto
|
396
|
+
71.71 134.89 75.03 130.16 78.81 125.93 curveto
|
397
|
+
stroke
|
398
|
+
0 0 0 edgecolor
|
399
|
+
newpath 81.31 128.38 moveto
|
400
|
+
85.97 118.86 lineto
|
401
|
+
76.39 123.39 lineto
|
402
|
+
closepath fill
|
403
|
+
1 setlinewidth
|
404
|
+
solid
|
405
|
+
0 0 0 edgecolor
|
406
|
+
newpath 81.31 128.38 moveto
|
407
|
+
85.97 118.86 lineto
|
408
|
+
76.39 123.39 lineto
|
409
|
+
closepath stroke
|
410
|
+
0 0 0 edgecolor
|
411
|
+
14 /Times-Roman set_font
|
412
|
+
69.51 141.4 moveto 22 (exit) alignedtext
|
413
|
+
grestore
|
414
|
+
% outside->invalid
|
415
|
+
gsave
|
416
|
+
1 setlinewidth
|
417
|
+
0 0 0 edgecolor
|
418
|
+
newpath 100.58 85.86 moveto
|
419
|
+
97.93 76.12 96.42 64.07 100.51 54 curveto
|
420
|
+
102.47 49.18 105.41 44.64 108.79 40.53 curveto
|
421
|
+
stroke
|
422
|
+
0 0 0 edgecolor
|
423
|
+
newpath 111.39 42.87 moveto
|
424
|
+
115.64 33.17 lineto
|
425
|
+
106.26 38.1 lineto
|
426
|
+
closepath fill
|
427
|
+
1 setlinewidth
|
428
|
+
solid
|
429
|
+
0 0 0 edgecolor
|
430
|
+
newpath 111.39 42.87 moveto
|
431
|
+
115.64 33.17 lineto
|
432
|
+
106.26 38.1 lineto
|
433
|
+
closepath stroke
|
434
|
+
0 0 0 edgecolor
|
435
|
+
14 /Times-Roman set_font
|
436
|
+
100.51 55.4 moveto 57 (invalidate) alignedtext
|
437
|
+
grestore
|
438
|
+
% outside->inside
|
439
|
+
gsave
|
440
|
+
1 setlinewidth
|
441
|
+
0 0 0 edgecolor
|
442
|
+
newpath 104.15 122.35 moveto
|
443
|
+
102.14 131.93 99.24 143.77 95.51 154 curveto
|
444
|
+
94.36 157.15 93.01 160.39 91.57 163.56 curveto
|
445
|
+
stroke
|
446
|
+
0 0 0 edgecolor
|
447
|
+
newpath 88.32 162.25 moveto
|
448
|
+
87.12 172.78 lineto
|
449
|
+
94.62 165.29 lineto
|
450
|
+
closepath fill
|
451
|
+
1 setlinewidth
|
452
|
+
solid
|
453
|
+
0 0 0 edgecolor
|
454
|
+
newpath 88.32 162.25 moveto
|
455
|
+
87.12 172.78 lineto
|
456
|
+
94.62 165.29 lineto
|
457
|
+
closepath stroke
|
458
|
+
0 0 0 edgecolor
|
459
|
+
14 /Times-Roman set_font
|
460
|
+
99.51 141.4 moveto 29 (enter) alignedtext
|
461
|
+
grestore
|
462
|
+
endpage
|
463
|
+
showpage
|
464
|
+
grestore
|
465
|
+
%%PageTrailer
|
466
|
+
%%EndPage: 1
|
467
|
+
%%Trailer
|
468
|
+
%%Pages: 1
|
469
|
+
%%BoundingBox: 36 36 358 526
|
470
|
+
end
|
471
|
+
restore
|
472
|
+
%%EOF
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../lib/fsm'
|
2
|
+
class Ticket
|
3
|
+
attr_accessor(:state)
|
4
|
+
include FSM
|
5
|
+
define_fsm do
|
6
|
+
states(:created, :reserved, :sold, :invalid, :inside, :outside)
|
7
|
+
|
8
|
+
transition(:reserve, :created, :reserved)
|
9
|
+
transition(:buy, :reserved, :sold)
|
10
|
+
|
11
|
+
transition(:enter, :sold, :inside)
|
12
|
+
transition(:enter, :outside, :inside)
|
13
|
+
transition(:exit, :inside, :outside)
|
14
|
+
|
15
|
+
transition(:invalidate, [:created, :reserved, :inside, :outside], :invalid)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
t = Ticket.fsm_draw_graph(:format => 'png')
|
data/test/ar_test.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'test_helper_ar'
|
2
2
|
class ArTest < Test::Unit::TestCase
|
3
|
+
|
3
4
|
context 'AR' do
|
4
5
|
|
5
6
|
should 'set the state' do
|
@@ -11,16 +12,21 @@ class ArTest < Test::Unit::TestCase
|
|
11
12
|
assert_equal :open, o2.state
|
12
13
|
end
|
13
14
|
|
15
|
+
should 'return initial state when state is empty string' do
|
16
|
+
o1 = Order.new(:state => '')
|
17
|
+
assert_equal :open, o1.state
|
18
|
+
end
|
19
|
+
|
14
20
|
should 'make transition' do
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
o = Order.new
|
22
|
+
assert_equal :open, o.state
|
23
|
+
o.deliver
|
24
|
+
assert_equal :delivered, o.state
|
25
|
+
o.save!
|
26
|
+
assert_equal :delivered, o.state
|
27
|
+
o.reload
|
28
|
+
o = Order.find(o.id)
|
29
|
+
assert_equal :delivered, o.state
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|
data/test/invoice_sample_test.rb
CHANGED
@@ -27,11 +27,12 @@ class Invoice
|
|
27
27
|
end
|
28
28
|
|
29
29
|
class InvoiceSampleTest < Test::Unit::TestCase
|
30
|
-
context 'Invoice' do
|
31
|
-
|
30
|
+
context 'an Invoice' do
|
31
|
+
setup do
|
32
|
+
@invoice = Invoice.new
|
33
|
+
end
|
32
34
|
should 'Initial State is the first state defined unless no initial() call was made' do
|
33
|
-
invoice
|
34
|
-
assert_equal(:open, invoice.state)
|
35
|
+
assert_equal(:open, @invoice.state)
|
35
36
|
end
|
36
37
|
|
37
38
|
should 'Accept an initial state from outside' do
|
@@ -40,28 +41,46 @@ class InvoiceSampleTest < Test::Unit::TestCase
|
|
40
41
|
end
|
41
42
|
|
42
43
|
should 'Trasition to paid and then refunded' do
|
43
|
-
invoice
|
44
|
-
assert_equal(:open, invoice.state)
|
44
|
+
assert_equal(:open, @invoice.state)
|
45
45
|
|
46
|
-
invoice.pay(1000)
|
47
|
-
assert_equal(:paid, invoice.state)
|
46
|
+
@invoice.pay(1000)
|
47
|
+
assert_equal(:paid, @invoice.state)
|
48
48
|
|
49
|
-
invoice.refund
|
50
|
-
assert_equal(:refunded, invoice.state)
|
49
|
+
@invoice.refund
|
50
|
+
assert_equal(:refunded, @invoice.state)
|
51
51
|
end
|
52
52
|
|
53
53
|
should 'Raise on illegal transition' do
|
54
|
-
invoice = Invoice.new
|
55
54
|
assert_raise(FSM::InvalidStateTransition) do
|
56
|
-
invoice.refund
|
55
|
+
@invoice.refund
|
57
56
|
end
|
58
57
|
end
|
59
58
|
|
60
59
|
should 'Pass the arguments to the event handler' do
|
61
|
-
invoice
|
62
|
-
invoice.
|
63
|
-
|
60
|
+
@invoice.pay(1000)
|
61
|
+
assert_equal(0, @invoice.amount)
|
62
|
+
end
|
63
|
+
|
64
|
+
should 'List all states' do
|
65
|
+
assert_equal_symbols [:paid, :refunded, :open], @invoice.fsm_state_names
|
66
|
+
end
|
67
|
+
should 'List all transitions' do
|
68
|
+
assert_equal_symbols [:pay, :refund], @invoice.fsm_transition_names
|
64
69
|
end
|
65
70
|
|
71
|
+
should 'List next states' do
|
72
|
+
assert_equal_symbols :paid, @invoice.fsm_next_state_names
|
73
|
+
@invoice.pay(1000)
|
74
|
+
assert_equal_symbols :refunded, @invoice.fsm_next_state_names
|
75
|
+
end
|
76
|
+
|
77
|
+
should 'List next transitions' do
|
78
|
+
assert_equal_symbols :pay, @invoice.fsm_next_transition_names
|
79
|
+
@invoice.pay(1000)
|
80
|
+
assert_equal_symbols :refund, @invoice.fsm_next_transition_names
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
|
66
85
|
end
|
67
86
|
end
|
data/test/test_helper.rb
CHANGED
@@ -5,3 +5,11 @@ require 'shoulda'
|
|
5
5
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
6
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
7
|
require 'fsm'
|
8
|
+
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
def assert_equal_symbols(a1, a2)
|
12
|
+
assert_equal Array(a1).map() {|item| item.to_s}.sort,
|
13
|
+
Array(a2).map() {|item| item.to_s}.sort
|
14
|
+
end
|
15
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplificator-fsm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- simplificator
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-07 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -23,12 +23,15 @@ extra_rdoc_files:
|
|
23
23
|
- LICENSE
|
24
24
|
- README.markdown
|
25
25
|
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
26
28
|
- LICENSE
|
27
29
|
- README.markdown
|
28
30
|
- Rakefile
|
29
31
|
- VERSION.yml
|
30
32
|
- lib/fsm.rb
|
31
33
|
- lib/fsm/builder.rb
|
34
|
+
- lib/fsm/dot.rb
|
32
35
|
- lib/fsm/errors.rb
|
33
36
|
- lib/fsm/executable.rb
|
34
37
|
- lib/fsm/machine.rb
|
@@ -36,6 +39,9 @@ files:
|
|
36
39
|
- lib/fsm/state.rb
|
37
40
|
- lib/fsm/state_attribute_interceptor.rb
|
38
41
|
- lib/fsm/transition.rb
|
42
|
+
- samples/ticket/ticket.png
|
43
|
+
- samples/ticket/ticket.ps
|
44
|
+
- samples/ticket/ticket_sample.rb
|
39
45
|
- test/ar_test.rb
|
40
46
|
- test/executable_test.rb
|
41
47
|
- test/invoice_sample_test.rb
|
@@ -47,6 +53,8 @@ files:
|
|
47
53
|
- test/water_sample_test.rb
|
48
54
|
has_rdoc: true
|
49
55
|
homepage: http://github.com/simplificator/fsm
|
56
|
+
licenses: []
|
57
|
+
|
50
58
|
post_install_message:
|
51
59
|
rdoc_options:
|
52
60
|
- --charset=UTF-8
|
@@ -67,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
75
|
requirements: []
|
68
76
|
|
69
77
|
rubyforge_project:
|
70
|
-
rubygems_version: 1.
|
78
|
+
rubygems_version: 1.3.5
|
71
79
|
signing_key:
|
72
80
|
specification_version: 3
|
73
81
|
summary: A simple finite state machine (FSM) gem.
|