fsm 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/gemspec.rb +23 -0
- data/install.rb +206 -0
- data/instance_exec.rb +26 -0
- data/lib/fsm-0.0.0.rb +78 -0
- data/lib/fsm-0.0.0/dsl.rb +71 -0
- data/lib/fsm-0.0.0/event.rb +68 -0
- data/lib/fsm-0.0.0/fsm.rb +211 -0
- data/lib/fsm-0.0.0/fsm.rb.bak +366 -0
- data/lib/fsm-0.0.0/graph/base_extensions.rb +70 -0
- data/lib/fsm-0.0.0/graph/directed_graph.rb +481 -0
- data/lib/fsm-0.0.0/graph/graphviz_dot.rb +188 -0
- data/lib/fsm-0.0.0/observer.rb +194 -0
- data/lib/fsm-0.0.0/system.rb +65 -0
- data/lib/fsm-0.0.0/util.rb +172 -0
- data/lib/fsm.rb +78 -0
- data/sample/a.rb +81 -0
- metadata +58 -0
@@ -0,0 +1,188 @@
|
|
1
|
+
class DotGraphPrinter
|
2
|
+
attr_accessor :orientation, :size, :color
|
3
|
+
|
4
|
+
# The following can be set to blocks of code that gives a default
|
5
|
+
# value for the node shapes, node labels and link labels, respectively.
|
6
|
+
attr_accessor :node_shaper, :node_labeler, :link_labeler
|
7
|
+
|
8
|
+
# A node shaper maps each node to a string describing its shape.
|
9
|
+
# Valid shapes are:
|
10
|
+
# "ellipse" (default)
|
11
|
+
# "box"
|
12
|
+
# "circle"
|
13
|
+
# "plaintext" (no outline)
|
14
|
+
# "doublecircle"
|
15
|
+
# "diamond"
|
16
|
+
# Not yet supported or untested once are:
|
17
|
+
# "polygon", "record", "epsf"
|
18
|
+
@@default_node_shaper = proc{|n| "box"}
|
19
|
+
|
20
|
+
@@default_node_labeler = proc{|n|
|
21
|
+
if Symbol===n
|
22
|
+
n.id2name
|
23
|
+
elsif String===n
|
24
|
+
n
|
25
|
+
else
|
26
|
+
n.inspect
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
@@default_link_labeler = proc{|info| info ? info.inspect : nil}
|
31
|
+
|
32
|
+
# links is either array of
|
33
|
+
# arrays [fromNode, toNode [, infoOnLink]], or
|
34
|
+
# objects with attributes :from, :to, :info
|
35
|
+
# nodes is array of node objects
|
36
|
+
# All nodes used in the links are used as nodes even if they are not
|
37
|
+
# in the "nodes" parameters.
|
38
|
+
def initialize(links = [], nodes = [])
|
39
|
+
@links, @nodes = links, add_nodes_in_links(links, nodes)
|
40
|
+
@node_attributes, @edge_attributes = Hash.new, Hash.new
|
41
|
+
set_default_values
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_default_values
|
45
|
+
@color = "black"
|
46
|
+
@size = "9,11"
|
47
|
+
@orientation = "portrait"
|
48
|
+
@node_shaper = @@default_node_shaper
|
49
|
+
@node_labeler = @@default_node_labeler
|
50
|
+
@link_labeler = @@default_link_labeler
|
51
|
+
end
|
52
|
+
|
53
|
+
def write_to_file(filename, fileType = "ps")
|
54
|
+
dotfile = temp_filename(filename)
|
55
|
+
File.open(dotfile, "w") {|f| f.write to_dot_specification}
|
56
|
+
system "dot -T#{fileType} -o #{filename} #{dotfile}"
|
57
|
+
File.delete(dotfile)
|
58
|
+
end
|
59
|
+
|
60
|
+
def set_edge_attributes(anEdge, aHash)
|
61
|
+
# TODO check if attributes are valid dot edge attributes
|
62
|
+
edge = find_edge(anEdge)
|
63
|
+
set_attributes(edge, @edge_attributes, true, aHash)
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_node_attributes(aNode, aHash)
|
67
|
+
# TODO check if attributes are valid dot node attributes
|
68
|
+
set_attributes(aNode, @node_attributes, true, aHash)
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_dot_specification
|
72
|
+
set_edge_labels(@links)
|
73
|
+
set_node_labels_and_shape(@nodes)
|
74
|
+
"digraph G {\n" +
|
75
|
+
graph_parameters_to_dot_specification +
|
76
|
+
@nodes.uniq.map {|n| format_node(n)}.join(";\n") + ";\n" +
|
77
|
+
@links.uniq.map {|l| format_link(l)}.join(";\n") + ";\n" +
|
78
|
+
"}"
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
def find_edge(anEdge)
|
84
|
+
@links.each do |link|
|
85
|
+
return link if source_and_dest(link) == source_and_dest(anEdge)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_attributes(key, hash, override, newAttributeHash)
|
90
|
+
h = hash[key] || Hash.new
|
91
|
+
newAttributeHash = all_keys_to_s(newAttributeHash)
|
92
|
+
newAttributeHash.each do |k, value|
|
93
|
+
h[k] = value unless h[k] and !override
|
94
|
+
end
|
95
|
+
hash[key] = h
|
96
|
+
end
|
97
|
+
|
98
|
+
def graph_parameters_to_dot_specification
|
99
|
+
"graph [\n" +
|
100
|
+
(self.size ? " size = #{@size.inspect},\n" : "") +
|
101
|
+
(self.orientation ? " orientation = #{@orientation},\n" : "") +
|
102
|
+
(self.color ? " color = #{@color}\n" : "") +
|
103
|
+
"]\n"
|
104
|
+
|
105
|
+
<<-dot_spec
|
106
|
+
graph[
|
107
|
+
rankdir = LR
|
108
|
+
]
|
109
|
+
dot_spec
|
110
|
+
end
|
111
|
+
|
112
|
+
def each_node_in_links(links)
|
113
|
+
links.each do |l|
|
114
|
+
src, dest = source_and_dest(l)
|
115
|
+
yield src
|
116
|
+
yield dest
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def add_nodes_in_links(links, nodes)
|
121
|
+
new_nodes = []
|
122
|
+
each_node_in_links(links) {|node| new_nodes.push node}
|
123
|
+
(nodes + new_nodes).uniq
|
124
|
+
end
|
125
|
+
|
126
|
+
def all_keys_to_s(aHash)
|
127
|
+
# MAYBE reuse existing hash?
|
128
|
+
Hash[*(aHash.map{|p| p[0] = p[0].to_s; p}.flatten)]
|
129
|
+
end
|
130
|
+
|
131
|
+
def set_edge_labels(edges)
|
132
|
+
edges.each do |edge|
|
133
|
+
src, dest, info = get_link_data(edge)
|
134
|
+
if info
|
135
|
+
label = @link_labeler.call(info)
|
136
|
+
set_attributes(edge, @edge_attributes, false, :label =>label) if label
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def set_node_labels_and_shape(nodes)
|
142
|
+
nodes.each do |node|
|
143
|
+
set_attributes(node, @node_attributes, false,
|
144
|
+
:label => @node_labeler.call(node).inspect,
|
145
|
+
:shape => @node_shaper.call(node).inspect)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def get_link_data(link)
|
150
|
+
begin
|
151
|
+
return link.from, link.to, link.info
|
152
|
+
rescue Exception
|
153
|
+
return link[0], link[1], link[2]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def source_and_dest(link)
|
158
|
+
get_link_data(link)[0,2]
|
159
|
+
end
|
160
|
+
|
161
|
+
def format_attributes(attributes)
|
162
|
+
return "" unless attributes
|
163
|
+
strings = attributes.map {|a, v| "#{a}=#{v}"}
|
164
|
+
strings.length > 0 ? (" [" + strings.join(", ") + "]") : ("")
|
165
|
+
end
|
166
|
+
|
167
|
+
def mangle_node_name(node)
|
168
|
+
"n" + node.hash.abs.inspect
|
169
|
+
end
|
170
|
+
|
171
|
+
def format_link(link)
|
172
|
+
from, to, info = get_link_data(link)
|
173
|
+
mangle_node_name(from) + " -> " + mangle_node_name(to) +
|
174
|
+
format_attributes(@edge_attributes[link])
|
175
|
+
end
|
176
|
+
|
177
|
+
def format_node(node)
|
178
|
+
mangle_node_name(node) + format_attributes(@node_attributes[node])
|
179
|
+
end
|
180
|
+
|
181
|
+
def temp_filename(base = "tmp")
|
182
|
+
tmpfile = base + rand(100000).inspect
|
183
|
+
while test(?f, tmpfile)
|
184
|
+
tmpfile = base + rand(100000).inspect
|
185
|
+
end
|
186
|
+
tmpfile
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
unless defined? $__fsm_observer__
|
2
|
+
$__fsm_observer__ = __FILE__
|
3
|
+
|
4
|
+
module FSM
|
5
|
+
FSM::LIBDIR =
|
6
|
+
File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
7
|
+
defined? FSM::LIBDIR
|
8
|
+
|
9
|
+
FSM::INCDIR =
|
10
|
+
File::dirname(FSM::LIBDIR) + File::SEPARATOR unless
|
11
|
+
defined? FSM::INCDIR
|
12
|
+
|
13
|
+
require INCDIR + 'fsm'
|
14
|
+
|
15
|
+
class Observer
|
16
|
+
include Util
|
17
|
+
|
18
|
+
ANY = const 'ANY'
|
19
|
+
|
20
|
+
tattrs %w[
|
21
|
+
fsm
|
22
|
+
hooks
|
23
|
+
q
|
24
|
+
thread
|
25
|
+
dsl
|
26
|
+
]
|
27
|
+
|
28
|
+
def initialize fsm, &b
|
29
|
+
sync_initialize
|
30
|
+
|
31
|
+
@fsm = fsm
|
32
|
+
@fsm.add_observer self
|
33
|
+
|
34
|
+
@hooks = Hash.new{|etype_h, etype|
|
35
|
+
etype_h[etype] = Hash.new{|fstate_h, fstate|
|
36
|
+
fstate_h[fstate] = Array.new
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
@q = Queue.new
|
41
|
+
|
42
|
+
@thread = Thread.new{
|
43
|
+
Thread.current.abort_on_exception = true
|
44
|
+
loop{
|
45
|
+
b, *a = @q.pop
|
46
|
+
break unless b
|
47
|
+
bcall b, *a
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
@dsl = DSL.new self
|
52
|
+
configure &b if b
|
53
|
+
end
|
54
|
+
|
55
|
+
def configure &b
|
56
|
+
ex{
|
57
|
+
@dsl.configure &b
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def defer hook, *a
|
62
|
+
ex{
|
63
|
+
@q.push [hook, *a]
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def idefer hook, *a
|
68
|
+
ihook = lambda{|*a| instance_exec *a, &hook }
|
69
|
+
defer ihook, *a
|
70
|
+
end
|
71
|
+
|
72
|
+
def on etype = Event::Any, fstate = ANY, &hook
|
73
|
+
ex{
|
74
|
+
idx = (@hooks[etype][fstate] << hook).size - 1
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def on_entry *a, &b
|
79
|
+
on Event::Entry, *a, &b
|
80
|
+
end
|
81
|
+
|
82
|
+
def on_transition *a, &b
|
83
|
+
on Event::Transition, *a, &b
|
84
|
+
end
|
85
|
+
|
86
|
+
def on_exit *a, &b
|
87
|
+
on Event::Exit, *a, &b
|
88
|
+
end
|
89
|
+
|
90
|
+
def on_input *a, &b
|
91
|
+
on Event::Input, *a, &b
|
92
|
+
end
|
93
|
+
|
94
|
+
module Once; end
|
95
|
+
|
96
|
+
def once_on etype = Event::Any, fstate = ANY, &hook
|
97
|
+
on etype, fstate, &hook.extend(Once)
|
98
|
+
end
|
99
|
+
|
100
|
+
def once_on_entry *a, &b
|
101
|
+
once_on Event::Entry, *a, &b
|
102
|
+
end
|
103
|
+
|
104
|
+
def once_on_transition *a, &b
|
105
|
+
once_on Event::Transition, *a, &b
|
106
|
+
end
|
107
|
+
|
108
|
+
def once_on_exit *a, &b
|
109
|
+
once_on Event::Exit, *a, &b
|
110
|
+
end
|
111
|
+
|
112
|
+
def once_on_input *a, &b
|
113
|
+
once_on Event::Input, *a, &b
|
114
|
+
end
|
115
|
+
|
116
|
+
def wait_for_entry fstate, &hook
|
117
|
+
argv = [fstate]
|
118
|
+
unless fsm.state == fstate
|
119
|
+
q = Queue.new
|
120
|
+
once_on(Event::Entry, fstate){|*a| q.push a }
|
121
|
+
argv.replace q.pop
|
122
|
+
end
|
123
|
+
icall hook, *argv if hook
|
124
|
+
end
|
125
|
+
alias_method 'wait_for', 'wait_for_entry'
|
126
|
+
|
127
|
+
def wait_for_transition fstate, &hook
|
128
|
+
argv = [fstate]
|
129
|
+
unless fsm.state == fstate
|
130
|
+
q = Queue.new
|
131
|
+
once_on(Event::Transition, fstate){|*a| q.push a }
|
132
|
+
argv.replace q.pop
|
133
|
+
end
|
134
|
+
icall hook, *argv if hook
|
135
|
+
end
|
136
|
+
|
137
|
+
def wait_for_exit fstate, &hook
|
138
|
+
argv = [fstate]
|
139
|
+
unless fsm.state == fstate
|
140
|
+
q = Queue.new
|
141
|
+
once_on(Event::Exit, fstate){|*a| q.push a }
|
142
|
+
argv.replace q.pop
|
143
|
+
end
|
144
|
+
icall hook, *argv if hook
|
145
|
+
end
|
146
|
+
|
147
|
+
def wait_for_input fstate, &hook
|
148
|
+
argv = [fstate]
|
149
|
+
unless fsm.state == fstate
|
150
|
+
q = Queue.new
|
151
|
+
once_on(Event::Input, fstate){|*a| q.push a }
|
152
|
+
argv.replace q.pop
|
153
|
+
end
|
154
|
+
icall hook, *argv if hook
|
155
|
+
end
|
156
|
+
|
157
|
+
def fsm_event event
|
158
|
+
ex{
|
159
|
+
[event.type, Event::Any].each do |etype|
|
160
|
+
[event.state, ANY].each do |estate|
|
161
|
+
#@hooks[etype][estate].each{|hook| idefer hook, event}
|
162
|
+
hooks = []
|
163
|
+
while((hook = @hooks[etype][estate].shift))
|
164
|
+
idefer hook, event
|
165
|
+
hooks.push hook unless hook.is_a? Once
|
166
|
+
end
|
167
|
+
@hooks[etype][estate].replace hooks
|
168
|
+
end
|
169
|
+
end
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
def join
|
174
|
+
@thread.join
|
175
|
+
end
|
176
|
+
|
177
|
+
def kill
|
178
|
+
@thread.kill
|
179
|
+
end
|
180
|
+
|
181
|
+
alias_method 'stop', 'kill'
|
182
|
+
|
183
|
+
delegate %w[
|
184
|
+
start
|
185
|
+
transition
|
186
|
+
input
|
187
|
+
display
|
188
|
+
plot
|
189
|
+
state
|
190
|
+
] => '@fsm'
|
191
|
+
|
192
|
+
end # class Observer
|
193
|
+
end # module FSM
|
194
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
unless defined? $__fsm_system__
|
2
|
+
$__fsm_system__ = __FILE__
|
3
|
+
|
4
|
+
module FSM
|
5
|
+
FSM::LIBDIR =
|
6
|
+
File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
7
|
+
defined? FSM::LIBDIR
|
8
|
+
|
9
|
+
FSM::INCDIR =
|
10
|
+
File::dirname(FSM::LIBDIR) + File::SEPARATOR unless
|
11
|
+
defined? FSM::INCDIR
|
12
|
+
|
13
|
+
require INCDIR + 'fsm'
|
14
|
+
|
15
|
+
class System
|
16
|
+
include Util
|
17
|
+
|
18
|
+
%w[
|
19
|
+
fsm
|
20
|
+
observer
|
21
|
+
].each{|a| attr a}
|
22
|
+
|
23
|
+
def initialize fsm = FSM.new, &b
|
24
|
+
@fsm = fsm
|
25
|
+
@observer = Observer.new @fsm
|
26
|
+
@dsl = DSL.new self
|
27
|
+
configure &b if b
|
28
|
+
end
|
29
|
+
|
30
|
+
def configure &b
|
31
|
+
@dsl.configure &b
|
32
|
+
end
|
33
|
+
|
34
|
+
delegate %w[
|
35
|
+
graph
|
36
|
+
state_attributes
|
37
|
+
state
|
38
|
+
subscribers
|
39
|
+
subscribe
|
40
|
+
inspect
|
41
|
+
start
|
42
|
+
add_observer
|
43
|
+
transition
|
44
|
+
traverse
|
45
|
+
input
|
46
|
+
add_state
|
47
|
+
add_transition
|
48
|
+
plot
|
49
|
+
to_dot
|
50
|
+
display
|
51
|
+
] => '@fsm'
|
52
|
+
|
53
|
+
delegate %w[
|
54
|
+
on
|
55
|
+
on_entry
|
56
|
+
on_transition
|
57
|
+
on_exit
|
58
|
+
on_input
|
59
|
+
join
|
60
|
+
kill
|
61
|
+
stop
|
62
|
+
] => '@observer'
|
63
|
+
end # class System
|
64
|
+
end # module FSM
|
65
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
unless defined? $__fsm_util__
|
2
|
+
$__fsm_util__ = __FILE__
|
3
|
+
|
4
|
+
module FSM
|
5
|
+
FSM::LIBDIR =
|
6
|
+
File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
7
|
+
defined? FSM::LIBDIR
|
8
|
+
|
9
|
+
FSM::INCDIR =
|
10
|
+
File::dirname(FSM::LIBDIR) + File::SEPARATOR unless
|
11
|
+
defined? FSM::INCDIR
|
12
|
+
|
13
|
+
require INCDIR + 'fsm'
|
14
|
+
|
15
|
+
module Util
|
16
|
+
class Const
|
17
|
+
def self.new s
|
18
|
+
s = s.to_s
|
19
|
+
(@instances ||= {})[s] ||= super(s)
|
20
|
+
end
|
21
|
+
|
22
|
+
attr 'to_s'
|
23
|
+
alias_method 'to_str', 'to_s'
|
24
|
+
alias_method 'inspect', 'to_s'
|
25
|
+
|
26
|
+
def initialize s
|
27
|
+
@to_s = s.to_s
|
28
|
+
freeze
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module Methods
|
33
|
+
include Sync_m
|
34
|
+
|
35
|
+
def const(*a)
|
36
|
+
Const.new(*a)
|
37
|
+
end
|
38
|
+
|
39
|
+
def string_list *list
|
40
|
+
list.flatten.map{|elem| elem.to_s}
|
41
|
+
end
|
42
|
+
|
43
|
+
def string *list
|
44
|
+
string_list(*list).first
|
45
|
+
end
|
46
|
+
|
47
|
+
def klass
|
48
|
+
self.class
|
49
|
+
end
|
50
|
+
|
51
|
+
def bcall b, *a
|
52
|
+
arity = b.arity
|
53
|
+
if arity > 0
|
54
|
+
a = a[0...arity]
|
55
|
+
elsif arity < 0
|
56
|
+
arity = arity.abs
|
57
|
+
a = a[0...arity] + (a[arity..-1] || [])
|
58
|
+
else
|
59
|
+
a.clear
|
60
|
+
end
|
61
|
+
b.call *a
|
62
|
+
end
|
63
|
+
|
64
|
+
def mcall obj, msg, *a
|
65
|
+
m = obj.method(msg).to_proc
|
66
|
+
bcall m, *a
|
67
|
+
end
|
68
|
+
|
69
|
+
def icall b, *a
|
70
|
+
arity = b.arity
|
71
|
+
if arity > 0
|
72
|
+
a = a[0...arity]
|
73
|
+
elsif arity < 0
|
74
|
+
arity = arity.abs
|
75
|
+
a = a[0...arity] + (a[arity..-1] || [])
|
76
|
+
else
|
77
|
+
a.clear
|
78
|
+
end
|
79
|
+
instance_exec *a, &b
|
80
|
+
end
|
81
|
+
|
82
|
+
def system *a, &b
|
83
|
+
verbose = $VERBOSE
|
84
|
+
$VERBOSE = nil
|
85
|
+
::Kernel.system *a, &b
|
86
|
+
ensure
|
87
|
+
$VERBOSE = verbose
|
88
|
+
end
|
89
|
+
|
90
|
+
unless instance_methods.include? 'instance_exec'
|
91
|
+
def instance_exec *a, &b
|
92
|
+
m, n = nil, -1
|
93
|
+
loop{
|
94
|
+
m = "__instance_exec_#{ Thread.current.object_id.abs }_#{ n += 1 }__"
|
95
|
+
break unless respond_to? m
|
96
|
+
}
|
97
|
+
singleton_class{ define_method m, &b }
|
98
|
+
send m, *a
|
99
|
+
ensure
|
100
|
+
singleton_class{ undef_method m }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
unless instance_methods.include? 'singleton_class'
|
105
|
+
def singleton_class &b
|
106
|
+
sc =
|
107
|
+
class << self; self; end
|
108
|
+
sc.module_eval &b if b
|
109
|
+
sc
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def sh(&b) synchronize(:SH, &b) end
|
114
|
+
def ex(&b) synchronize(:EX, &b) end
|
115
|
+
|
116
|
+
|
117
|
+
def initialize(*a, &b)
|
118
|
+
sync_initialize
|
119
|
+
end
|
120
|
+
alias_method 'sync_init', 'sync_initialize'
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
module ClassMethods
|
125
|
+
include Methods
|
126
|
+
|
127
|
+
def tattrs *ms
|
128
|
+
ms.flatten.each do |m|
|
129
|
+
module_eval <<-code
|
130
|
+
def #{ m }(*a, &b)
|
131
|
+
if a.empty?
|
132
|
+
sh{ @#{ m } }
|
133
|
+
else
|
134
|
+
self.#{ m }= a.shift
|
135
|
+
end
|
136
|
+
end
|
137
|
+
alias_method '#{ m }?', '#{ m }'
|
138
|
+
def #{ m }= val
|
139
|
+
ex{ @#{ m } = val }
|
140
|
+
end
|
141
|
+
code
|
142
|
+
end
|
143
|
+
end
|
144
|
+
alias_method 'tattr', 'tattrs'
|
145
|
+
|
146
|
+
def delegate hash = {}
|
147
|
+
methods, to = hash.to_a.first
|
148
|
+
methods.each do |m|
|
149
|
+
module_eval <<-code
|
150
|
+
def #{ m } *a, &b
|
151
|
+
#{ to }.#{ m } *a, &b
|
152
|
+
end
|
153
|
+
code
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
module InstanceMethods
|
159
|
+
include Methods
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.included other
|
163
|
+
other.extend ClassMethods
|
164
|
+
other.module_eval{
|
165
|
+
include InstanceMethods
|
166
|
+
include Sync_m
|
167
|
+
}
|
168
|
+
super
|
169
|
+
end
|
170
|
+
end # module Util
|
171
|
+
end # module FSM
|
172
|
+
end
|