empathy 0.0.1.RC0 → 0.0.1.RC2
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/.yardopts +5 -0
- data/CHANGES.md +8 -0
- data/README.md +144 -0
- data/Rakefile +4 -7
- data/TESTING.md +27 -0
- data/empathy.gemspec +5 -1
- data/lib/empathy/em/condition_variable.rb +18 -11
- data/lib/empathy/em/monitor.rb +8 -0
- data/lib/empathy/em/mutex.rb +17 -0
- data/lib/empathy/em/queue.rb +10 -30
- data/lib/empathy/em/thread.rb +91 -31
- data/lib/empathy/object.rb +3 -3
- data/lib/empathy/version.rb +1 -1
- data/lib/empathy/with_all_of_ruby.rb +8 -0
- data/lib/empathy.rb +81 -45
- data/lib/monitor.rb +209 -0
- data/mspec/lib/mspec/empathy.rb +1 -1
- data/rubyspec/monitor_spec.rb +162 -0
- data/spec/empathy_spec.rb +7 -2
- data/spec/library_spec.rb +95 -48
- data/yard/extensions.rb +24 -0
- metadata +50 -11
- data/README.rdoc +0 -135
- data/TESTING.rdoc +0 -11
- data/lib/empathy/thread.rb +0 -8
data/lib/empathy/em/thread.rb
CHANGED
@@ -2,19 +2,35 @@ require "fiber"
|
|
2
2
|
require "eventmachine"
|
3
3
|
require 'empathy/em/mutex'
|
4
4
|
require 'empathy/em/condition_variable'
|
5
|
+
require 'empathy/em/queue'
|
5
6
|
|
6
7
|
module Empathy
|
7
8
|
|
8
9
|
module EM
|
9
10
|
|
11
|
+
@empathised = self
|
12
|
+
# Thread like errors are actually raw Fiber errors
|
13
|
+
ThreadError = ::FiberError
|
14
|
+
|
15
|
+
# Create alias constants in each of the supplied modules so that code within those modules
|
16
|
+
# will use modules from the Empathy::EM namespace instead of the native ruby ones
|
17
|
+
#
|
18
|
+
# Also monkey patches {Object} to provide EM safe Kernel methods
|
19
|
+
# @return [void]
|
10
20
|
def self.empathise(*modules)
|
11
21
|
modules.each do |m|
|
12
|
-
Empathy::map_classes(m,self
|
22
|
+
Empathy::map_classes(m,self)
|
13
23
|
end
|
14
24
|
end
|
15
25
|
|
16
26
|
module Kernel
|
17
|
-
|
27
|
+
|
28
|
+
# Like ::Kernel.sleep
|
29
|
+
# @overload sleep()
|
30
|
+
# Sleep forever
|
31
|
+
# @overload sleep(seconds)
|
32
|
+
# @param [Numeric] seconds The number of seconds (including fractional seconds) to sleep
|
33
|
+
# @return [Fixnum] rounded number of seconds actually slept, which can be less than specified if woken early
|
18
34
|
def self.sleep(seconds=:__empathy_sleep_forever)
|
19
35
|
|
20
36
|
::Kernel.raise TypeError, "seconds #{seconds} must be a number" unless seconds == :__empathy_sleep_forever or seconds.is_a? Numeric
|
@@ -27,30 +43,50 @@ module Empathy
|
|
27
43
|
(Time.now - n).round()
|
28
44
|
end
|
29
45
|
|
46
|
+
# Like ::Kernel.at_exit
|
47
|
+
#
|
48
|
+
# Queues block to run at shutdown of the reactor
|
49
|
+
# @return [void]
|
30
50
|
def self.at_exit(&block)
|
31
51
|
EventMachine.add_shutdown_hook(&block)
|
32
52
|
end
|
53
|
+
|
33
54
|
end
|
34
55
|
|
35
|
-
#Acts like a ::Thread using Fibers and EventMachine
|
56
|
+
# Acts like a ::Thread using Fibers and EventMachine
|
57
|
+
#
|
58
|
+
# {::Thread} methods not implemented by Empathy
|
59
|
+
#
|
60
|
+
# * .exclusive - not implemented
|
61
|
+
# * #critical - not implemented
|
62
|
+
# * #set_trace_func - not implemented
|
63
|
+
# * #safe_level - not implemented
|
64
|
+
# * #priority - not implemented
|
65
|
+
|
36
66
|
class Thread
|
37
67
|
|
38
68
|
@@em_threads = {}
|
39
69
|
|
40
|
-
# The underlying fiber.
|
70
|
+
# @return [Fiber] The underlying fiber.
|
41
71
|
attr_reader :fiber
|
42
72
|
|
43
73
|
# Like ::Thread::list. Return an array of all EM::Threads that are alive.
|
74
|
+
#
|
75
|
+
# @return [Array<Thread>]
|
44
76
|
def self.list
|
45
77
|
@@em_threads.values.select { |s| s.alive? }
|
46
78
|
end
|
47
79
|
|
80
|
+
# Like ::Thread.main
|
81
|
+
#
|
82
|
+
# @return [Thread]
|
48
83
|
def self.main
|
49
84
|
@@main
|
50
85
|
end
|
51
86
|
|
52
87
|
# Like ::Thread::current. Get the currently running EM::Thread, eg to access thread local
|
53
88
|
# variables
|
89
|
+
# @return [Thread] representing the current Fiber
|
54
90
|
def self.current
|
55
91
|
@@em_threads[Fiber.current] || ProxyThread.new(Fiber.current)
|
56
92
|
end
|
@@ -68,12 +104,15 @@ module Empathy
|
|
68
104
|
|
69
105
|
|
70
106
|
# Like ::Thread::stop. Sleep forever (until woken)
|
107
|
+
# @return [void]
|
71
108
|
def self.stop
|
72
109
|
Kernel.sleep()
|
73
110
|
end
|
74
111
|
|
75
112
|
# Like ::Thread::pass.
|
76
|
-
# The fiber is resumed on the next_tick of EM's event loop
|
113
|
+
# The fiber is paused and resumed on the next_tick of EM's event loop
|
114
|
+
#
|
115
|
+
# @return [nil]
|
77
116
|
def self.pass
|
78
117
|
em_thread = current
|
79
118
|
::EM.next_tick{ em_thread.__send__(:wake_resume) }
|
@@ -82,20 +121,27 @@ module Empathy
|
|
82
121
|
end
|
83
122
|
|
84
123
|
# Like ::Thread.exit
|
124
|
+
# @return [Thread]
|
85
125
|
def self.exit
|
86
126
|
current.exit
|
87
127
|
end
|
88
128
|
|
129
|
+
# Like ::Thread.kill
|
130
|
+
# @return [Thread]
|
89
131
|
def self.kill(thread)
|
90
132
|
thread.exit
|
91
133
|
end
|
92
134
|
|
135
|
+
# @private
|
93
136
|
def self.new(*args,&block)
|
94
137
|
instance = super(*args,&block)
|
95
|
-
::Kernel.raise
|
138
|
+
::Kernel.raise ThreadError, "super not called for subclass of Thread" unless instance.instance_variable_defined?("@fiber")
|
96
139
|
instance
|
97
140
|
end
|
98
141
|
|
142
|
+
# Like ::Thread.start
|
143
|
+
#
|
144
|
+
# @return [Thread]
|
99
145
|
def self.start(*args,&block)
|
100
146
|
::Kernel.raise ArgumentError, "no block" unless block_given?
|
101
147
|
c = if self != Thread
|
@@ -106,22 +152,20 @@ module Empathy
|
|
106
152
|
end
|
107
153
|
else
|
108
154
|
self
|
109
|
-
end
|
155
|
+
end
|
110
156
|
c.new(*args,&block)
|
111
157
|
end
|
112
158
|
|
113
|
-
#
|
159
|
+
# Like ::Thread#initialize
|
114
160
|
def initialize(*args,&block)
|
115
161
|
|
116
|
-
::Kernel.raise
|
162
|
+
::Kernel.raise ThreadError, "no block" unless block_given?
|
117
163
|
initialize_fiber(*args,&block)
|
118
164
|
end
|
119
165
|
|
120
166
|
# Like ::Thread#join.
|
121
|
-
#
|
122
|
-
#
|
123
|
-
# s1.join
|
124
|
-
# s2.join
|
167
|
+
# @param [Numeric] limit seconds to wait for thread to expire
|
168
|
+
# @return [nil,Thread] nil if timeout expires, otherwise this Thread
|
125
169
|
def join(limit = nil)
|
126
170
|
@mutex.synchronize { @join_cond.wait(@mutex,limit) } if alive?
|
127
171
|
::Kernel.raise @exception if @exception
|
@@ -136,16 +180,22 @@ module Empathy
|
|
136
180
|
end
|
137
181
|
|
138
182
|
# Like ::Thread#alive? or Fiber#alive?
|
183
|
+
# @return [true,false]
|
139
184
|
def alive?
|
140
185
|
fiber.alive?
|
141
186
|
end
|
142
187
|
|
143
|
-
# Like ::Thread#stop?
|
188
|
+
# Like ::Thread#stop?
|
189
|
+
# @return [false] if called on the current fiber
|
190
|
+
# @return [true] otherwise
|
144
191
|
def stop?
|
145
192
|
Fiber.current != fiber
|
146
193
|
end
|
147
194
|
|
148
195
|
# Like ::Thread#status
|
196
|
+
# @return [String]
|
197
|
+
# @return [false]
|
198
|
+
# @return [nil]
|
149
199
|
def status
|
150
200
|
case @status
|
151
201
|
when :run
|
@@ -165,13 +215,13 @@ module Empathy
|
|
165
215
|
end
|
166
216
|
|
167
217
|
# Like ::Thread#value. Implicitly calls #join.
|
168
|
-
# em_thread = Empathy.new{ 1+2 }
|
169
|
-
# em_thread.value # => 3
|
170
218
|
def value
|
171
219
|
join and @value
|
172
220
|
end
|
173
221
|
|
174
222
|
# Like ::Thread#exit. Signals thread to wakeup and die
|
223
|
+
#
|
224
|
+
# @return [nil, Thread]
|
175
225
|
def exit
|
176
226
|
case @status
|
177
227
|
when :sleep
|
@@ -185,13 +235,25 @@ module Empathy
|
|
185
235
|
alias :terminate :exit
|
186
236
|
|
187
237
|
# Like ::Thread#wakeup Wakes a sleeping Thread
|
238
|
+
# @return [Thread]
|
188
239
|
def wakeup
|
189
|
-
::Kernel.raise
|
190
|
-
wake_resume()
|
240
|
+
::Kernel.raise ThreadError, "dead em_thread" unless status
|
241
|
+
wake_resume()
|
191
242
|
self
|
192
243
|
end
|
193
244
|
|
194
245
|
# Like ::Thread#raise, raise an exception on a sleeping Thread
|
246
|
+
# @overload raise()
|
247
|
+
# @raise RuntimeError
|
248
|
+
# @overload raise(string)
|
249
|
+
# @param [String] string
|
250
|
+
# @raise RuntimeError
|
251
|
+
# @overload raise(exception,string=nil,array=caller())
|
252
|
+
# @param [Class,String,Object) exception
|
253
|
+
# @param [String] string exception message
|
254
|
+
# @param [Array<String>] array caller information
|
255
|
+
# @raise Exception
|
256
|
+
# @return [void]
|
195
257
|
def raise(*args)
|
196
258
|
args << RuntimeError if args.empty?
|
197
259
|
if fiber == Fiber.current
|
@@ -207,45 +269,43 @@ module Empathy
|
|
207
269
|
|
208
270
|
|
209
271
|
# Access to "fiber local" variables, akin to "thread local" variables.
|
210
|
-
#
|
211
|
-
#
|
212
|
-
# Empathy.current[:connection].send(data)
|
213
|
-
# ...
|
214
|
-
# end
|
272
|
+
# @param [Symbol] name
|
273
|
+
# @return [Object,nil]
|
215
274
|
def [](name)
|
216
275
|
::Kernel.raise TypeError, "name #{name} must convert to_sym" unless name and name.respond_to?(:to_sym)
|
217
276
|
@locals[name.to_sym]
|
218
277
|
end
|
219
278
|
|
220
279
|
# Access to "fiber local" variables, akin to "thread local" variables.
|
221
|
-
# Empathy.new do
|
222
|
-
# ...
|
223
|
-
# Empathy.current[:connection] = SomeConnectionClass.new(host, port)
|
224
|
-
# ...
|
225
|
-
# end
|
226
280
|
def []=(name, value)
|
227
281
|
::Kernel.raise TypeError, "name #{name} must convert to_sym" unless name and name.respond_to?(:to_sym)
|
228
282
|
@locals[name.to_sym] = value
|
229
283
|
end
|
230
284
|
|
231
285
|
# Like ::Thread#key? Is there a "fiber local" variable defined called +name+
|
286
|
+
# @param [Symbol] name
|
287
|
+
# @return [true,false]
|
232
288
|
def key?(name)
|
233
289
|
::Kernel.raise TypeError, "name #{name} must convert to_sym" unless name and name.respond_to?(:to_sym)
|
234
290
|
@locals.has_key?(name.to_sym)
|
235
291
|
end
|
236
292
|
|
237
293
|
# Like ::Thread#keys The set of "em_thread local" variable keys
|
294
|
+
# @return [Array<Symbol>]
|
238
295
|
def keys()
|
239
296
|
@locals.keys
|
240
297
|
end
|
241
298
|
|
242
|
-
|
299
|
+
# Like ::Thread#inspect
|
300
|
+
# @return [String]
|
301
|
+
def inspect
|
243
302
|
"#<Empathy::EM::Thread:0x%s %s %s" % [object_id, @fiber == Fiber.current ? "run" : "yielded", status || "dead" ]
|
244
303
|
end
|
245
304
|
|
246
305
|
# Do something when the fiber completes.
|
306
|
+
# @return [void]
|
247
307
|
def ensure_hook(key,&block)
|
248
|
-
if block_given? then
|
308
|
+
if block_given? then
|
249
309
|
@ensure_hooks[key] = block
|
250
310
|
else
|
251
311
|
@ensure_hooks.delete(key)
|
@@ -286,7 +346,7 @@ module Empathy
|
|
286
346
|
private
|
287
347
|
|
288
348
|
def initialize_fiber(*args,&block)
|
289
|
-
::Kernel.raise
|
349
|
+
::Kernel.raise ThreadError, "already initialized" if @fiber
|
290
350
|
# Create our fiber.
|
291
351
|
fiber = Fiber.new{ fiber_body(*args,&block) }
|
292
352
|
|
data/lib/empathy/object.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
|
2
|
-
warn "empathy/object: Monkey patching Object#sleep and Object
|
2
|
+
warn "empathy/object: Monkey patching Object#sleep and Object#at_exit for EventMachine reactor awareness"
|
3
3
|
|
4
4
|
# Monkey patch object to make EM safe
|
5
5
|
class Object
|
6
6
|
|
7
|
-
# use EM
|
7
|
+
# use {Empathy::EM::Kernel.sleep} if we are in the reactor, Kernel.sleep otherwise
|
8
8
|
def sleep(*args)
|
9
9
|
kernel = Empathy.event_machine? ? Empathy::EM::Kernel : Kernel
|
10
10
|
kernel.sleep(*args)
|
11
11
|
end
|
12
12
|
|
13
|
-
#
|
13
|
+
# use {Empathy::EM::Kernel.at_exit} if we are in the reactor, Kernel.sleep otherwise
|
14
14
|
def at_exit(&block)
|
15
15
|
kernel = Empathy.event_machine? ? Empathy::EM::Kernel : Kernel
|
16
16
|
kernel.at_exit(&block)
|
data/lib/empathy/version.rb
CHANGED
data/lib/empathy.rb
CHANGED
@@ -1,61 +1,62 @@
|
|
1
1
|
require 'empathy/version'
|
2
2
|
require 'thread'
|
3
|
+
require 'monitor'
|
3
4
|
require 'fiber'
|
4
5
|
|
5
6
|
# This module provides a shim between using standard ruby Threads
|
6
7
|
# and the thread-like behaviour for Fibers provided by classes in
|
7
|
-
# the Empathy::EM
|
8
|
+
# the {Empathy::EM} namespace.
|
8
9
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# # For the Empathy::EM classes to be available
|
11
|
+
# # you must first load EventMachine
|
12
|
+
# 'require eventmachine'
|
12
13
|
#
|
13
|
-
#
|
14
|
+
# 'require empathy'
|
14
15
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# end
|
16
|
+
# t = Empathy::Thread.new() do
|
17
|
+
# # "t" is a standard ::Thread
|
18
|
+
# # ...something...
|
19
|
+
# end
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
21
|
+
# EventMachine.run do
|
22
|
+
# t = Empathy::Thread.new() do
|
23
|
+
# # "t" is a ::Empathy::EM::Thread
|
24
|
+
# # which wraps a ::Fiber
|
25
|
+
# end
|
26
|
+
# end
|
27
27
|
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
28
|
+
# # Outside of event machine
|
29
|
+
# t = Empathy::Thread.new() do
|
30
|
+
# # "t" is a raw ::Thread
|
31
|
+
# end
|
32
32
|
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# *[FiberError,ThreadError]
|
33
|
+
# #Code using Empathy that may be used in both Fiber or Thread contexts
|
34
|
+
# #should take care to rescue Empathy::ThreadError
|
36
35
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
36
|
+
# def maybe_em_method
|
37
|
+
# # ...
|
38
|
+
# rescue Empathy::ThreadError
|
39
|
+
# # ... will rescue either ::FiberError or ::ThreadError
|
40
|
+
# end
|
40
41
|
#
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# {::Thread} methods not implemented by Empathy
|
44
|
-
# * #exclusive - not implemented
|
45
|
-
# * #critical - not implemented
|
46
|
-
# * #set_trace_func - not implemented
|
47
|
-
# * #safe_level - not implemented
|
48
|
-
# * #priority - not implemented
|
49
42
|
module Empathy
|
50
43
|
|
44
|
+
@empathised = self
|
45
|
+
@empathic_classes = []
|
46
|
+
|
47
|
+
#This is never thrown but can be used to rescue both ThreadError and FiberError
|
51
48
|
class ThreadError < StandardError
|
49
|
+
RUBY_ThreadError = ::ThreadError
|
52
50
|
def self.===(other)
|
53
|
-
super || ::FiberError === other ||
|
51
|
+
super || ::FiberError === other || RUBY_ThreadError === other
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
55
|
+
@empathic_classes << 'ThreadError'
|
56
|
+
|
57
57
|
# Start EventMachine and run reactor block within a surrounding Empathy::EM::Thread (Fiber).
|
58
58
|
# The reactor loop is terminated when the supplied block finishes
|
59
|
+
# @return the value of the block
|
59
60
|
def self.run
|
60
61
|
reload()
|
61
62
|
exception = nil
|
@@ -75,17 +76,21 @@ module Empathy
|
|
75
76
|
value
|
76
77
|
end
|
77
78
|
|
79
|
+
# Create alias constants in each of the supplied modules so that code witin those modules
|
80
|
+
# will use modules from the Empathy namespace instaad of the native ruby ones
|
81
|
+
#
|
82
|
+
# Also monkey patches {Object} to provide EM safe Kernel methods
|
83
|
+
# @param [Array<Module>] modules
|
84
|
+
# @return [void]
|
78
85
|
def self.empathise(*modules)
|
79
|
-
modules.each
|
80
|
-
map_classes(m, self, "Thread","Queue","Mutex","ConditionVariable", "ThreadError")
|
81
|
-
end
|
86
|
+
modules.each { |m| map_classes(m, self) }
|
82
87
|
end
|
83
88
|
|
84
|
-
|
85
|
-
def self.map_classes(into,from
|
89
|
+
# @private
|
90
|
+
def self.map_classes(into,from)
|
86
91
|
# Make Object reactor aware
|
87
92
|
require 'empathy/object'
|
88
|
-
|
93
|
+
@empathic_classes.each do |cname|
|
89
94
|
case cname
|
90
95
|
when Hash
|
91
96
|
cname.each { |cn,replace_class| replace_class_constant(into,cn,replace_class) }
|
@@ -93,8 +98,10 @@ module Empathy
|
|
93
98
|
replace_class_constant(into,cname,from.const_get(cname))
|
94
99
|
end
|
95
100
|
end
|
101
|
+
into.instance_variable_set(:@empathised,from)
|
96
102
|
end
|
97
103
|
|
104
|
+
private
|
98
105
|
def self.replace_class_constant(into,cname,replace_class)
|
99
106
|
if into != Object && into.const_defined?(cname,false)
|
100
107
|
existing_const = into.const_get(cname)
|
@@ -107,7 +114,7 @@ module Empathy
|
|
107
114
|
warn "empathy: Defined fake class constant #{into}::#{cname} => #{replace_class.name}"
|
108
115
|
into.const_set(cname,replace_class)
|
109
116
|
end
|
110
|
-
|
117
|
+
public
|
111
118
|
# Test whether we have real fibers or a thread based fiber implmentation
|
112
119
|
t = Thread.current
|
113
120
|
ft = nil
|
@@ -121,7 +128,7 @@ module Empathy
|
|
121
128
|
@loaded ||= false
|
122
129
|
if !@loaded && defined?(EventMachine)
|
123
130
|
require 'empathy/em/thread.rb'
|
124
|
-
require 'empathy/em/
|
131
|
+
require 'empathy/em/monitor.rb'
|
125
132
|
@loaded = true
|
126
133
|
end
|
127
134
|
return @loaded
|
@@ -142,16 +149,44 @@ module Empathy
|
|
142
149
|
end
|
143
150
|
|
144
151
|
private
|
152
|
+
|
153
|
+
# Create a module under the Empathy namespace that delegates class methods (potentially including #new)
|
154
|
+
# to either the top level class, or to its equivalent under the {Empathy::EM} namespace
|
155
|
+
# based on being in/out of the reactor
|
156
|
+
#
|
157
|
+
# Library authors can use this to define an eventmachine aware replacement class for their own purposes
|
158
|
+
# @example
|
159
|
+
#
|
160
|
+
# class MyBinding
|
161
|
+
# #...code that does not use event machine
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# module Empathy
|
165
|
+
# module EM
|
166
|
+
# class MyBinding
|
167
|
+
# # ... code that uses event machine ...
|
168
|
+
# end
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# create_delegate_module('MyBinding',:new, :my_class_method)
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
# @visibility public
|
175
|
+
# @param [String] cname name of the top level class or module to delegate, there must
|
176
|
+
# also be a class with the same name in the Empathy::EM namespace
|
177
|
+
# @param [Array<Sumbol>] methods names of class/module methods to delegate
|
145
178
|
def self.create_delegate_module(cname,*methods)
|
146
179
|
mod = Module.new
|
180
|
+
native_mod = Object.const_get(cname)
|
181
|
+
em_mod = Empathy::EM.const_get(cname)
|
147
182
|
self.const_set(cname,mod)
|
148
183
|
methods.each do |m|
|
149
184
|
mod.define_singleton_method(m) do |*args,&block|
|
150
|
-
|
151
|
-
delegate = parent.const_get(cname)
|
185
|
+
delegate = Empathy.event_machine? ? em_mod : native_mod
|
152
186
|
delegate.send(m,*args,&block)
|
153
187
|
end
|
154
188
|
end
|
189
|
+
@empathic_classes << cname unless cname == 'Kernel'
|
155
190
|
end
|
156
191
|
|
157
192
|
create_delegate_module('Kernel',:sleep,:at_exit)
|
@@ -159,4 +194,5 @@ module Empathy
|
|
159
194
|
create_delegate_module('Queue',:new)
|
160
195
|
create_delegate_module('ConditionVariable',:new)
|
161
196
|
create_delegate_module('Mutex',:new)
|
197
|
+
create_delegate_module('Monitor',:new)
|
162
198
|
end
|