simplificator-fsm 0.3.5 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|