asir 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +16 -0
- data/README.textile +50 -0
- data/Rakefile +83 -0
- data/VERSION +1 -0
- data/asir.gemspec +36 -0
- data/asir.riterate.yml +114 -0
- data/bin/asir +6 -0
- data/doc/Rakefile +8 -0
- data/doc/asir-sequence.pic +84 -0
- data/doc/asir-sequence.svg +1559 -0
- data/doc/sequence.pic +430 -0
- data/example/asir_control.sh +24 -0
- data/example/asir_control_client_http.rb +14 -0
- data/example/asir_control_client_zmq.rb +15 -0
- data/example/config/asir_config.rb +63 -0
- data/example/delayed_service.rb +15 -0
- data/example/ex01.rb +12 -0
- data/example/ex02.rb +12 -0
- data/example/ex03.rb +19 -0
- data/example/ex04.rb +33 -0
- data/example/ex05.rb +16 -0
- data/example/ex06.rb +26 -0
- data/example/ex07.rb +28 -0
- data/example/ex08.rb +30 -0
- data/example/ex09.rb +25 -0
- data/example/ex10.rb +24 -0
- data/example/ex11.rb +48 -0
- data/example/ex12.rb +34 -0
- data/example/ex13.rb +35 -0
- data/example/ex14.rb +30 -0
- data/example/ex15.rb +13 -0
- data/example/ex16.rb +33 -0
- data/example/ex17.rb +41 -0
- data/example/ex18.rb +62 -0
- data/example/ex19.rb +32 -0
- data/example/ex20.rb +28 -0
- data/example/ex21.rb +28 -0
- data/example/ex22.rb +15 -0
- data/example/ex23.rb +20 -0
- data/example/ex24.rb +35 -0
- data/example/example_helper.rb +51 -0
- data/example/sample_service.rb +162 -0
- data/example/unsafe_service.rb +12 -0
- data/hack_night/README.txt +18 -0
- data/hack_night/exercise/prob-1.rb +18 -0
- data/hack_night/exercise/prob-2.rb +21 -0
- data/hack_night/exercise/prob-3.rb +16 -0
- data/hack_night/exercise/prob-4.rb +36 -0
- data/hack_night/exercise/prob-5.rb +36 -0
- data/hack_night/exercise/prob-6.rb +95 -0
- data/hack_night/exercise/prob-7.rb +34 -0
- data/hack_night/solution/math_service.rb +11 -0
- data/hack_night/solution/prob-1.rb +12 -0
- data/hack_night/solution/prob-2.rb +15 -0
- data/hack_night/solution/prob-3.rb +17 -0
- data/hack_night/solution/prob-4.rb +37 -0
- data/hack_night/solution/prob-5.rb +21 -0
- data/hack_night/solution/prob-6.rb +33 -0
- data/hack_night/solution/prob-7.rb +36 -0
- data/lab/phony_proc.rb +31 -0
- data/lib/asir.rb +253 -0
- data/lib/asir/additional_data.rb +25 -0
- data/lib/asir/channel.rb +130 -0
- data/lib/asir/client.rb +111 -0
- data/lib/asir/code_block.rb +57 -0
- data/lib/asir/code_more.rb +50 -0
- data/lib/asir/coder.rb +26 -0
- data/lib/asir/coder/base64.rb +19 -0
- data/lib/asir/coder/chain.rb +30 -0
- data/lib/asir/coder/identity.rb +23 -0
- data/lib/asir/coder/json.rb +30 -0
- data/lib/asir/coder/marshal.rb +17 -0
- data/lib/asir/coder/null.rb +23 -0
- data/lib/asir/coder/proc.rb +22 -0
- data/lib/asir/coder/sign.rb +48 -0
- data/lib/asir/coder/xml.rb +213 -0
- data/lib/asir/coder/yaml.rb +33 -0
- data/lib/asir/coder/zlib.rb +21 -0
- data/lib/asir/configuration.rb +32 -0
- data/lib/asir/error.rb +34 -0
- data/lib/asir/identity.rb +36 -0
- data/lib/asir/initialization.rb +23 -0
- data/lib/asir/log.rb +82 -0
- data/lib/asir/main.rb +396 -0
- data/lib/asir/message.rb +31 -0
- data/lib/asir/message/delay.rb +35 -0
- data/lib/asir/object_resolving.rb +15 -0
- data/lib/asir/result.rb +39 -0
- data/lib/asir/retry_behavior.rb +54 -0
- data/lib/asir/transport.rb +241 -0
- data/lib/asir/transport/beanstalk.rb +217 -0
- data/lib/asir/transport/broadcast.rb +34 -0
- data/lib/asir/transport/buffer.rb +115 -0
- data/lib/asir/transport/composite.rb +19 -0
- data/lib/asir/transport/connection_oriented.rb +180 -0
- data/lib/asir/transport/delay.rb +38 -0
- data/lib/asir/transport/delegation.rb +53 -0
- data/lib/asir/transport/fallback.rb +36 -0
- data/lib/asir/transport/file.rb +88 -0
- data/lib/asir/transport/http.rb +54 -0
- data/lib/asir/transport/local.rb +21 -0
- data/lib/asir/transport/null.rb +14 -0
- data/lib/asir/transport/payload_io.rb +52 -0
- data/lib/asir/transport/rack.rb +73 -0
- data/lib/asir/transport/retry.rb +41 -0
- data/lib/asir/transport/stream.rb +35 -0
- data/lib/asir/transport/subprocess.rb +30 -0
- data/lib/asir/transport/tcp_socket.rb +34 -0
- data/lib/asir/transport/webrick.rb +50 -0
- data/lib/asir/transport/zmq.rb +110 -0
- data/lib/asir/uuid.rb +32 -0
- data/lib/asir/version.rb +3 -0
- data/spec/const_get_speed_spec.rb +33 -0
- data/spec/debug_helper.rb +20 -0
- data/spec/example_spec.rb +88 -0
- data/spec/json_spec.rb +128 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/xml_spec.rb +144 -0
- data/stylesheets/slides.css +105 -0
- metadata +173 -0
data/lib/asir/main.rb
ADDED
@@ -0,0 +1,396 @@
|
|
1
|
+
require 'asir'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
|
5
|
+
module ASIR
|
6
|
+
class Main
|
7
|
+
attr_accessor :verb, :adjective, :object, :identifier
|
8
|
+
attr_accessor :config_rb, :config
|
9
|
+
attr_accessor :log_dir, :pid_dir
|
10
|
+
attr_accessor :verbose
|
11
|
+
attr_accessor :exit_code
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@verbose = 0
|
15
|
+
@progname = File.basename($0)
|
16
|
+
@log_dir = find_writable_directory :log_dir,
|
17
|
+
ENV['ASIR_LOG_DIR'],
|
18
|
+
'/var/log/asir',
|
19
|
+
'~/asir/log',
|
20
|
+
'/tmp'
|
21
|
+
@pid_dir = find_writable_directory :pid_dir,
|
22
|
+
ENV['ASIR_PID_DIR'],
|
23
|
+
'/var/run/asir',
|
24
|
+
'~/asir/run',
|
25
|
+
'/tmp'
|
26
|
+
@exit_code = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_writable_directory kind, *list
|
30
|
+
list.
|
31
|
+
reject { | p | ! p }.
|
32
|
+
map { | p | File.expand_path(p) }.
|
33
|
+
find { | p | File.writable?(p) } or
|
34
|
+
raise "Cannot find writable directory for #{kind}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_args! args = ARGV.dup
|
38
|
+
@args = args
|
39
|
+
until args.empty?
|
40
|
+
case args.first
|
41
|
+
when /^([a-z0-9_]+=)(.*)/i
|
42
|
+
k, v = $1.to_sym, $2
|
43
|
+
args.shift
|
44
|
+
v = v.to_i if v == v.to_i.to_s
|
45
|
+
send(k, v)
|
46
|
+
else
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@verb, @adjective, @object, @identifier = args.map{|x| x.to_sym}
|
51
|
+
@identifier ||= :'0'
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def log_str
|
56
|
+
"#{Time.now.gmtime.iso8601(4)} #{$$} #{log_str_no_time}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def log_str_no_time
|
60
|
+
"#{@progname} #{@verb} #{@adjective} #{@object} #{@identifier}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def run!
|
64
|
+
unless verb && adjective && object
|
65
|
+
@exit_code = 1
|
66
|
+
return usage!
|
67
|
+
end
|
68
|
+
config!(:config)
|
69
|
+
# $stderr.puts "log_file = #{log_file.inspect}"
|
70
|
+
case self.verb
|
71
|
+
when :restart
|
72
|
+
self.verb = :stop
|
73
|
+
_run_verb! && sleep(1)
|
74
|
+
self.verb = :start
|
75
|
+
_run_verb!
|
76
|
+
else
|
77
|
+
_run_verb!
|
78
|
+
end
|
79
|
+
self
|
80
|
+
rescue ::Exception => exc
|
81
|
+
$stderr.puts "#{log_str} ERROR\n#{exc.inspect}\n #{exc.backtrace * "\n "}"
|
82
|
+
@exit_code += 1
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def _run_verb!
|
87
|
+
sel = :"#{verb}_#{adjective}_#{object}!"
|
88
|
+
if @verbose >= 3
|
89
|
+
$stderr.puts "verb = #{verb.inspect}"
|
90
|
+
$stderr.puts "adjective = #{adjective.inspect}"
|
91
|
+
$stderr.puts "object = #{object.inspect}"
|
92
|
+
$stderr.puts "sel = #{sel.inspect}"
|
93
|
+
end
|
94
|
+
send(sel)
|
95
|
+
rescue ::Exception => exc
|
96
|
+
$stderr.puts "#{log_str} ERROR\n#{exc.inspect}\n #{exc.backtrace * "\n "}"
|
97
|
+
@exit_code += 1
|
98
|
+
raise
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
|
102
|
+
def method_missing sel, *args
|
103
|
+
log "method_missing #{sel}" if @verbose >= 3
|
104
|
+
case sel.to_s
|
105
|
+
when /^start_([^_]+)_worker!$/
|
106
|
+
_start_worker!
|
107
|
+
when /^status_([^_]+)_([^_]+)!$/
|
108
|
+
pid = server_pid
|
109
|
+
puts "#{log_str} pid #{pid}"
|
110
|
+
system("ps -fw -p #{pid}")
|
111
|
+
when /^log_([^_]+)_([^_]+)!$/
|
112
|
+
puts log_file
|
113
|
+
when /^taillog_([^_]+)_([^_]+)!$/
|
114
|
+
exec "tail -f #{log_file.inspect}"
|
115
|
+
when /^pid_([^_]+)_([^_]+)!$/
|
116
|
+
puts "#{pid_file} #{File.read(pid_file) rescue nil}"
|
117
|
+
when /^stop_([^_]+)_([^_]+)!$/
|
118
|
+
kill_server!
|
119
|
+
else
|
120
|
+
super
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def usage!
|
125
|
+
$stderr.puts <<"END"
|
126
|
+
SYNOPSIS:
|
127
|
+
asir [ <<options>> ... ] <<verb>> <<adjective>> <<object>> [ <<identifier>> ]
|
128
|
+
|
129
|
+
OPTIONS:
|
130
|
+
config_rb=file.rb ($ASIR_LOG_DIR)
|
131
|
+
pid_dir=dir/ ($ASIR_PID_DIR)
|
132
|
+
log_dir=dir/ ($ASIR_LOG_DIR)
|
133
|
+
verbose=[0-9]
|
134
|
+
|
135
|
+
VERBS:
|
136
|
+
start
|
137
|
+
stop
|
138
|
+
restart
|
139
|
+
status
|
140
|
+
log
|
141
|
+
pid
|
142
|
+
|
143
|
+
ADJECTIVE-OBJECTs:
|
144
|
+
beanstalk conduit
|
145
|
+
beanstalk worker
|
146
|
+
zmq worker
|
147
|
+
webrick worker
|
148
|
+
|
149
|
+
EXAMPLES:
|
150
|
+
|
151
|
+
export ASIR_CONFIG_RB="some_system/asir_config.rb"
|
152
|
+
asir start beanstalk conduit
|
153
|
+
asir status beanstalk conduit
|
154
|
+
|
155
|
+
asir start webrick worker
|
156
|
+
|
157
|
+
asir start beanstalk worker 1
|
158
|
+
asir start beanstalk worker 2
|
159
|
+
|
160
|
+
asir start zmq worker
|
161
|
+
asir start zmq worker 1
|
162
|
+
asir start zmq worker 2
|
163
|
+
END
|
164
|
+
end
|
165
|
+
|
166
|
+
def start_beanstalk_conduit!
|
167
|
+
fork_server! "beanstalkd"
|
168
|
+
end
|
169
|
+
|
170
|
+
def _start_worker! type = adjective
|
171
|
+
log "start_worker! #{type}"
|
172
|
+
type = type.to_s
|
173
|
+
fork_server! do
|
174
|
+
transport_file = "asir/transport/#{type}"
|
175
|
+
log "loading #{transport_file}"
|
176
|
+
require transport_file
|
177
|
+
_create_transport ASIR::Transport.const_get(type[0..0].upcase + type[1..-1])
|
178
|
+
_run_workers!
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
################################################################
|
183
|
+
|
184
|
+
def config_rb
|
185
|
+
@config_rb ||=
|
186
|
+
File.expand_path(ENV['ASIR_CONFIG_RB'] || 'config/asir_config.rb')
|
187
|
+
end
|
188
|
+
|
189
|
+
def config_lambda
|
190
|
+
@config_lambda ||=
|
191
|
+
begin
|
192
|
+
file = config_rb
|
193
|
+
$stderr.puts "#{log_str} loading #{file} ..." if @verbose >= 1
|
194
|
+
expr = File.read(file)
|
195
|
+
expr = "begin; lambda do | asir |; #{expr}\n end; end"
|
196
|
+
cfg = Object.new.send(:eval, expr, binding, file, 1)
|
197
|
+
# cfg = load file
|
198
|
+
# $stderr.puts "#{log_str} loading #{file} DONE" if @verbose >= 1
|
199
|
+
raise "#{file} did not return a Proc, returned a #{cfg.class}" unless Proc === cfg
|
200
|
+
cfg
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def config! verb = @verb
|
205
|
+
(@config ||= { })[verb] ||=
|
206
|
+
begin
|
207
|
+
save_verb = @verb
|
208
|
+
@verb = verb
|
209
|
+
$stderr.puts "#{log_str} calling #{config_rb} asir.verb=#{@verb.inspect} ..." if @verbose >= 1
|
210
|
+
cfg = config_lambda.call(self)
|
211
|
+
$stderr.puts "#{log_str} calling #{config_rb} asir.verb=#{@verb.inspect} DONE" if @verbose >= 1
|
212
|
+
cfg
|
213
|
+
ensure
|
214
|
+
@verb = save_verb
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def pid_file
|
219
|
+
"#{pid_dir}/#{asir_basename}.pid"
|
220
|
+
end
|
221
|
+
|
222
|
+
def log_file
|
223
|
+
"#{log_dir}/#{asir_basename}.log"
|
224
|
+
end
|
225
|
+
|
226
|
+
def asir_basename
|
227
|
+
"asir-#{adjective}-#{object}-#{identifier}"
|
228
|
+
end
|
229
|
+
|
230
|
+
def fork_server! cmd = nil, &blk
|
231
|
+
pid = Process.fork do
|
232
|
+
run_server! cmd, &blk
|
233
|
+
end
|
234
|
+
log "forked pid #{pid}"
|
235
|
+
Process.detach(pid) # Forks a Thread? We are gonna exit anyway.
|
236
|
+
File.open(pid_file, "w+") { | o | o.puts pid }
|
237
|
+
File.chmod(0666, pid_file) rescue nil
|
238
|
+
|
239
|
+
# Wait and check if process still exists.
|
240
|
+
sleep 3
|
241
|
+
unless process_running? pid
|
242
|
+
raise "Server process #{pid} died to soon?"
|
243
|
+
end
|
244
|
+
|
245
|
+
self
|
246
|
+
end
|
247
|
+
|
248
|
+
def run_server! cmd = nil
|
249
|
+
lf = File.open(log_file, "a+")
|
250
|
+
File.chmod(0666, log_file) rescue nil
|
251
|
+
$stdin.close rescue nil
|
252
|
+
STDIN.close rescue nil
|
253
|
+
STDOUT.reopen(lf)
|
254
|
+
$stdout.reopen(lf) if $stdout.object_id != STDOUT.object_id
|
255
|
+
STDERR.reopen(lf)
|
256
|
+
$stderr.reopen(lf) if $stderr.object_id != STDERR.object_id
|
257
|
+
# Process.daemon rescue nil # Ruby 1.9.x only.
|
258
|
+
lf.puts "#{log_str} starting pid #{$$}"
|
259
|
+
begin
|
260
|
+
if cmd
|
261
|
+
exec(cmd)
|
262
|
+
else
|
263
|
+
yield
|
264
|
+
end
|
265
|
+
ensure
|
266
|
+
lf.puts "#{log_str} finished pid #{$$}"
|
267
|
+
File.unlink(pid_file) rescue nil
|
268
|
+
end
|
269
|
+
self
|
270
|
+
rescue ::Exception => exc
|
271
|
+
msg = "ERROR pid #{$$}\n#{exc.inspect}\n #{exc.backtrace * "\n "}"
|
272
|
+
log msg, :stderr
|
273
|
+
raise
|
274
|
+
self
|
275
|
+
end
|
276
|
+
|
277
|
+
def kill_server!
|
278
|
+
log "#{log_str} kill"
|
279
|
+
pid = server_pid
|
280
|
+
stop_pid! pid
|
281
|
+
rescue ::Exception => exc
|
282
|
+
log "#{log_str} ERROR\n#{exc.inspect}\n #{exc.backtrace * "\n "}", :stderr
|
283
|
+
raise
|
284
|
+
end
|
285
|
+
|
286
|
+
def log msg, to_stderr = false
|
287
|
+
if to_stderr
|
288
|
+
$stderr.puts "#{log_str_no_time} #{msg}"
|
289
|
+
end
|
290
|
+
File.open(log_file, "a+") do | log |
|
291
|
+
log.puts "#{log_str} #{msg}"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def server_pid
|
296
|
+
pid = File.read(pid_file).chomp!
|
297
|
+
pid.to_i
|
298
|
+
end
|
299
|
+
|
300
|
+
def _create_transport default_class
|
301
|
+
config!(:environment)
|
302
|
+
case transport = config!(:transport)
|
303
|
+
when default_class
|
304
|
+
@transport = transport
|
305
|
+
else
|
306
|
+
raise "Expected config to return a #{default_class}, not a #{transport.class}"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def worker_pids
|
311
|
+
(@worker_pids ||= { })[@adjective] ||= { }
|
312
|
+
end
|
313
|
+
|
314
|
+
def _run_workers!
|
315
|
+
$0 = "#{@progname} #{@adjective} #{@object} #{@identifier}"
|
316
|
+
|
317
|
+
worker_id = 0
|
318
|
+
@transport.prepare_server!
|
319
|
+
worker_processes = @transport[:worker_processes] || 1
|
320
|
+
(worker_processes - 1).times do
|
321
|
+
wid = worker_id += 1
|
322
|
+
pid = Process.fork do
|
323
|
+
_run_transport_server! wid
|
324
|
+
end
|
325
|
+
Process.setgprp(pid, 0) rescue nil
|
326
|
+
worker_pids[wid] = pid
|
327
|
+
log "forked #{wid} pid #{pid}"
|
328
|
+
end
|
329
|
+
|
330
|
+
_run_transport_server!
|
331
|
+
ensure
|
332
|
+
log "worker 0 stopped"
|
333
|
+
_stop_workers!
|
334
|
+
end
|
335
|
+
|
336
|
+
def _run_transport_server! wid = 0
|
337
|
+
log "running transport worker #{@transport.class} #{wid}"
|
338
|
+
config!(:start)
|
339
|
+
$0 += " #{wid} #{@transport.uri rescue nil}"
|
340
|
+
old_arg0 = $0.dup
|
341
|
+
after_receive_message = @transport.after_receive_message || lambda { | transport, message | nil }
|
342
|
+
@transport.after_receive_message = lambda do | transport, message |
|
343
|
+
$0 = "#{old_arg0} #{transport.message_count} #{message.identifier}"
|
344
|
+
after_receive_message.call(transport, message)
|
345
|
+
end
|
346
|
+
@transport.run_server!
|
347
|
+
self
|
348
|
+
end
|
349
|
+
|
350
|
+
def _stop_workers!
|
351
|
+
workers = worker_pids.dup
|
352
|
+
worker_pids.clear
|
353
|
+
workers.each do | wid, pid |
|
354
|
+
config!(:stop)
|
355
|
+
stop_pid! pid, "wid #{wid} "
|
356
|
+
end
|
357
|
+
workers.each do | wid, pid |
|
358
|
+
wr = Process.waitpid(pid) rescue nil
|
359
|
+
log "stopped #{wid} pid #{pid} => #{wr.inspect}", :stderr
|
360
|
+
end
|
361
|
+
ensure
|
362
|
+
worker_pids.clear
|
363
|
+
end
|
364
|
+
|
365
|
+
def stop_pid! pid, msg = nil
|
366
|
+
log "stopping #{msg}pid #{pid}", :stderr
|
367
|
+
if process_running? pid
|
368
|
+
log "TERM pid #{pid}"
|
369
|
+
Process.kill('TERM', pid) rescue nil
|
370
|
+
sleep 3
|
371
|
+
if @force or process_running? pid
|
372
|
+
log "KILL pid #{pid}", :stderr
|
373
|
+
Process.kill('KILL', pid) rescue nil
|
374
|
+
end
|
375
|
+
if process_running? pid
|
376
|
+
log "cant-stop pid #{pid}", :stderr
|
377
|
+
end
|
378
|
+
else
|
379
|
+
log "not-running? pid #{pid}", :stderr
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def process_running? pid
|
384
|
+
Process.kill(0, pid)
|
385
|
+
true
|
386
|
+
rescue ::Errno::ESRCH
|
387
|
+
false
|
388
|
+
rescue ::Exception => exc
|
389
|
+
$stderr.puts " DEBUG: process_running? #{pid} => #{exc.inspect}"
|
390
|
+
false
|
391
|
+
end
|
392
|
+
|
393
|
+
end # class
|
394
|
+
end # module
|
395
|
+
|
396
|
+
|
data/lib/asir/message.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
module ASIR
|
3
|
+
# !SLIDE
|
4
|
+
# Message
|
5
|
+
#
|
6
|
+
# Encapsulate the Ruby message from the Client to be handled by the Service.
|
7
|
+
class Message
|
8
|
+
include AdditionalData, Identity, CodeMore
|
9
|
+
attr_accessor :receiver, :receiver_class, :selector, :arguments, :block
|
10
|
+
attr_accessor :result, :one_way
|
11
|
+
|
12
|
+
def initialize r, s, a, b, p
|
13
|
+
@receiver, @selector, @arguments = r, s, a
|
14
|
+
@block = b if b
|
15
|
+
@receiver_class = @receiver.class
|
16
|
+
@one_way = p._one_way if p
|
17
|
+
end
|
18
|
+
|
19
|
+
def invoke!
|
20
|
+
@result = Result.new(self, @receiver.__send__(@selector, *@arguments))
|
21
|
+
rescue *Error::Unforwardable.unforwardable => exc
|
22
|
+
@result = Result.new(self, nil, Error::Unforwardable.new(exc))
|
23
|
+
rescue ::Exception => exc
|
24
|
+
@result = Result.new(self, nil, exc)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Optional: Specifies the Numeric seconds or absolute Time to delay the Message until actual processing.
|
28
|
+
attr_accessor :delay
|
29
|
+
end
|
30
|
+
# !SLIDE END
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ASIR
|
2
|
+
class Message
|
3
|
+
module Delay
|
4
|
+
# Returns the number of seconds from now, that the message should be delayed.
|
5
|
+
# If message.delay is Numeric, sets message.delay to the Time to delay til.
|
6
|
+
# If message.delay is Time, returns (now - message.delay).to_f
|
7
|
+
# Returns Float if message.delay was set, or nil.
|
8
|
+
# Returns 0 if delay has already expired.
|
9
|
+
def relative_message_delay! message, now = nil
|
10
|
+
case delay = message.delay
|
11
|
+
when nil
|
12
|
+
when Numeric
|
13
|
+
now ||= Time.now
|
14
|
+
delay = delay.to_f
|
15
|
+
message.delay = (now + delay).utc
|
16
|
+
when Time
|
17
|
+
now ||= Time.now
|
18
|
+
delay = (delay - now).to_f
|
19
|
+
delay = 0 if delay < 0
|
20
|
+
else
|
21
|
+
raise TypeError, "Expected message.delay to be Numeric or Time, given #{delay.class}"
|
22
|
+
end
|
23
|
+
delay
|
24
|
+
end
|
25
|
+
|
26
|
+
def wait_for_delay! message
|
27
|
+
while (delay = relative_message_delay!(message)) && delay > 0
|
28
|
+
sleep delay
|
29
|
+
end
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|