rjr 0.15.1 → 0.16.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/rjr-client +5 -3
- data/lib/rjr/common.rb +41 -8
- data/lib/rjr/dispatcher.rb +65 -23
- data/lib/rjr/em_adapter.rb +0 -3
- data/lib/rjr/{stats.rb → inspect.rb} +34 -9
- data/lib/rjr/message.rb +3 -2
- data/lib/rjr/node.rb +25 -10
- data/lib/rjr/nodes/amqp.rb +29 -22
- data/lib/rjr/nodes/local.rb +35 -16
- data/lib/rjr/nodes/multi.rb +4 -0
- data/lib/rjr/nodes/ssh.rb +1 -0
- data/lib/rjr/nodes/tcp.rb +8 -4
- data/lib/rjr/nodes/web.rb +7 -3
- data/lib/rjr/nodes/ws.rb +8 -4
- data/lib/rjr/thread_pool.rb +0 -4
- data/site/jrw.js +18 -13
- data/specs/dispatcher_spec.rb +8 -0
- data/specs/em_adapter_spec.rb +18 -24
- data/specs/{stats_spec.rb → inspect_spec.rb} +1 -1
- data/specs/node_spec.rb +5 -6
- data/specs/nodes/local_spec.rb +7 -1
- data/specs/thread_pool_spec.rb +15 -20
- metadata +5 -4
data/bin/rjr-client
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
require 'optparse'
|
8
8
|
require 'rjr/common'
|
9
|
-
require 'rjr/
|
9
|
+
require 'rjr/message'
|
10
10
|
require 'rjr/nodes/easy'
|
11
11
|
|
12
12
|
RJR::Logger.log_level = ::Logger::DEBUG
|
@@ -59,7 +59,7 @@ optparse = OptionParser.new do |opts|
|
|
59
59
|
config[:block] = !ret
|
60
60
|
end
|
61
61
|
|
62
|
-
opts.on('-n', '--num number_of_messages', 'Number of messages to send to server (may be :rand or indefinite)') do |n|
|
62
|
+
opts.on('-n', '--num number_of_messages', 'Number of messages to send to server (may be a number, :rand, or :indefinite)') do |n|
|
63
63
|
config[:num_msg] = case n.to_s.intern
|
64
64
|
when :rand then rand(MAX_MESSAGES)
|
65
65
|
when :indefinite then 1000000000 # XXX 'indefinite ;-)'
|
@@ -99,7 +99,9 @@ client_path = File.join(ENV['RJR_LOAD_PATH'] || File.join(cdir, '..', 'example
|
|
99
99
|
##########################################################
|
100
100
|
|
101
101
|
node = RJR::Nodes::Easy.new(NODES)
|
102
|
-
|
102
|
+
client_path.split(':').each { |cp|
|
103
|
+
node.dispatcher.add_modules(cp)
|
104
|
+
}
|
103
105
|
|
104
106
|
if config[:disconnect]
|
105
107
|
disconnect_thread = Thread.new {
|
data/lib/rjr/common.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
#
|
5
5
|
# Copyright (C) 2011-2013 Mohammed Morsi <mo@morsi.org>
|
6
6
|
# Licensed under the Apache License, Version 2.0
|
7
|
-
|
8
7
|
require 'logger'
|
9
8
|
|
10
9
|
# Return a random uuid
|
@@ -34,24 +33,51 @@ class Logger
|
|
34
33
|
@logger = ::Logger.new(output)
|
35
34
|
@logger.level = @log_level || ::Logger::FATAL
|
36
35
|
@logger_mutex = Mutex.new
|
36
|
+
@filters = []
|
37
|
+
@highlights = []
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
41
|
public
|
41
42
|
|
43
|
+
# Add method which to call on every log message to determine
|
44
|
+
# if messages should be included/excluded
|
45
|
+
def self.add_filter(filter)
|
46
|
+
@logger_mutex.synchronize{
|
47
|
+
@filters << filter
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Add a method which to call on every log message to determine
|
52
|
+
# if message should be highlighted
|
53
|
+
def self.highlight(hlight)
|
54
|
+
@logger_mutex.synchronize{
|
55
|
+
@highlights << hlight
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
42
59
|
def self.method_missing(method_id, *args)
|
43
60
|
_instantiate_logger
|
44
61
|
@logger_mutex.synchronize {
|
45
|
-
if args.first.is_a?(Array)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
@
|
51
|
-
|
62
|
+
args = args.first if args.first.is_a?(Array)
|
63
|
+
args.each { |a|
|
64
|
+
# run highlights / filters against output before
|
65
|
+
# sending formatted output to logger
|
66
|
+
# TODO allow user to customize highlight mechanism/text
|
67
|
+
na = @highlights.any? { |h| h.call a } ?
|
68
|
+
"\e[1m\e[31m#{a}\e[0m\e[0m" : a
|
69
|
+
@logger.send(method_id, na) if @filters.all? { |f| f.call a }
|
70
|
+
}
|
52
71
|
}
|
53
72
|
end
|
54
73
|
|
74
|
+
def self.safe_exec(*args, &bl)
|
75
|
+
_instantiate_logger
|
76
|
+
@logger_mutex.synchronize {
|
77
|
+
bl.call *args
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
55
81
|
def self.logger
|
56
82
|
_instantiate_logger
|
57
83
|
@logger
|
@@ -95,6 +121,13 @@ end
|
|
95
121
|
|
96
122
|
end # module RJR
|
97
123
|
|
124
|
+
# Serialized puts, uses logger lock to serialize puts output
|
125
|
+
def sputs(*args)
|
126
|
+
::RJR::Logger.safe_exec {
|
127
|
+
puts *args
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
98
131
|
class Object
|
99
132
|
def eigenclass
|
100
133
|
class << self
|
data/lib/rjr/dispatcher.rb
CHANGED
@@ -135,9 +135,16 @@ class Request
|
|
135
135
|
# Invoke the request by calling the registered handler with the registered
|
136
136
|
# method parameters in the local scope
|
137
137
|
def handle
|
138
|
-
|
138
|
+
node_sig = "#{@rjr_node_id}(#{@rjr_node_type})"
|
139
|
+
method_sig = "#{@rjr_method}(#{@rjr_method_args.join(',')})"
|
140
|
+
|
141
|
+
RJR::Logger.info "#{node_sig}->#{method_sig}"
|
142
|
+
|
139
143
|
retval = instance_exec(*@rjr_method_args, &@rjr_handler)
|
140
|
-
|
144
|
+
|
145
|
+
RJR::Logger.info \
|
146
|
+
"#{node_sig}<-#{method_sig}<-#{retval.nil? ? "nil" : retval}"
|
147
|
+
|
141
148
|
return retval
|
142
149
|
end
|
143
150
|
|
@@ -174,12 +181,16 @@ class Dispatcher
|
|
174
181
|
# Registered json-rpc request signatures and corresponding handlers
|
175
182
|
attr_reader :handlers
|
176
183
|
|
184
|
+
# Registered json-rpc request signatures and environments which to execute handlers in
|
185
|
+
attr_reader :environments
|
186
|
+
|
177
187
|
# Requests which have been dispatched
|
178
188
|
def requests ; @requests_lock.synchronize { Array.new(@requests) } ; end
|
179
189
|
|
180
190
|
# RJR::Dispatcher intializer
|
181
191
|
def initialize
|
182
192
|
@handlers = Hash.new()
|
193
|
+
@environments = Hash.new()
|
183
194
|
|
184
195
|
@requests_lock = Mutex.new
|
185
196
|
@requests = []
|
@@ -194,57 +205,88 @@ class Dispatcher
|
|
194
205
|
# a file, directory, or path specification (dirs seperated with ':')
|
195
206
|
# @return self
|
196
207
|
def add_module(name)
|
197
|
-
name
|
198
|
-
if File.directory?(p)
|
199
|
-
# TODO also .so files? allow user to specify suffix
|
200
|
-
Dir.glob(File.join(p, '*.rb')).all? { |m|
|
201
|
-
require m
|
202
|
-
}
|
208
|
+
require name
|
203
209
|
|
204
|
-
|
205
|
-
|
210
|
+
m = name.downcase.gsub(File::SEPARATOR, '_')
|
211
|
+
method("dispatch_#{m}".intern).call(self)
|
206
212
|
|
207
|
-
end
|
208
|
-
|
209
|
-
m = p.split(File::SEPARATOR).last
|
210
|
-
method("dispatch_#{m}".intern).call(self)
|
211
|
-
}
|
212
213
|
self
|
213
214
|
end
|
214
215
|
alias :add_modules :add_module
|
215
216
|
|
216
217
|
# Register json-rpc handler with dispatcher
|
217
218
|
#
|
218
|
-
# @param [String] signature request signature to match
|
219
|
+
# @param [String,Regex] signature request signature to match
|
219
220
|
# @param [Callable] callable callable object which to bind to signature
|
220
221
|
# @param [Callable] &bl block parameter will be set to callback if specified
|
221
222
|
# @return self
|
222
223
|
def handle(signature, callback = nil, &bl)
|
223
224
|
if signature.is_a?(Array)
|
224
225
|
signature.each { |s| handle(s, callback, &bl) }
|
225
|
-
return
|
226
|
+
return self
|
226
227
|
end
|
227
228
|
@handlers[signature] = callback unless callback.nil?
|
228
229
|
@handlers[signature] = bl unless bl.nil?
|
229
230
|
self
|
230
231
|
end
|
231
232
|
|
233
|
+
# Return boolean indicating if dispatcher can handle method
|
234
|
+
#
|
235
|
+
# @param [String] string rjr method to match
|
236
|
+
# @return [true,false] indicating if requests to specified method will be matched
|
237
|
+
def handles?(rjr_method)
|
238
|
+
!@handlers.find { |k,v|
|
239
|
+
k.is_a?(String) ?
|
240
|
+
k == rjr_method :
|
241
|
+
k =~ rjr_method
|
242
|
+
}.nil?
|
243
|
+
end
|
244
|
+
|
245
|
+
# Register environment to run json-rpc handler w/ dispatcher.
|
246
|
+
#
|
247
|
+
# Currently environments may be set to modules which requests
|
248
|
+
# will extend before executing handler
|
249
|
+
#
|
250
|
+
# @param [String,Regex] signature request signature to match
|
251
|
+
# @param [Module] module which to extend requests with
|
252
|
+
# @return self
|
253
|
+
def env(signature, environment)
|
254
|
+
if signature.is_a?(Array)
|
255
|
+
signature.each { |s| env(s, environment) }
|
256
|
+
return self
|
257
|
+
end
|
258
|
+
@environments[signature] = environment
|
259
|
+
self
|
260
|
+
end
|
261
|
+
|
232
262
|
# Dispatch received request. (used internally by nodes).
|
233
263
|
#
|
234
264
|
# Arguments should include :rjr_method and other parameters
|
235
265
|
# required to construct a valid Request instance
|
236
266
|
def dispatch(args = {})
|
237
|
-
# currently we
|
238
|
-
# TODO
|
239
|
-
# or if callable, invoke it w/ request checking boolean return value for match
|
240
|
-
# TODO not using concurrent access portection, assumes all handlers are registered
|
267
|
+
# currently we match method name string or regex against signature
|
268
|
+
# TODO not using concurrent access protection, assumes all handlers are registered
|
241
269
|
# before first dispatch occurs
|
242
|
-
handler = @handlers
|
270
|
+
handler = @handlers.find { |k,v|
|
271
|
+
k.is_a?(String) ?
|
272
|
+
k == args[:rjr_method] :
|
273
|
+
k =~ args[:rjr_method] }
|
274
|
+
|
275
|
+
# TODO currently just using last environment that matches,
|
276
|
+
# allow multiple environments to be used?
|
277
|
+
environment = @environments.keys.select { |k|
|
278
|
+
k.is_a?(String) ?
|
279
|
+
k == args[:rjr_method] :
|
280
|
+
k =~ args[:rjr_method]
|
281
|
+
}.last
|
243
282
|
|
244
283
|
return Result.method_not_found(args[:rjr_method]) if handler.nil?
|
245
284
|
|
246
285
|
# TODO compare arity of handler to number of method_args passed in
|
247
|
-
request = Request.new args.merge(:rjr_handler => handler)
|
286
|
+
request = Request.new args.merge(:rjr_handler => handler.last)
|
287
|
+
|
288
|
+
# set request environment
|
289
|
+
request.extend(@environments[environment]) unless environment.nil?
|
248
290
|
|
249
291
|
begin
|
250
292
|
retval = request.handle
|
data/lib/rjr/em_adapter.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
|
4
4
|
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
|
-
require 'singleton'
|
7
6
|
require 'eventmachine'
|
8
7
|
|
9
8
|
module RJR
|
@@ -11,8 +10,6 @@ module RJR
|
|
11
10
|
# EventMachine adapater interface, ties reactor
|
12
11
|
# lifecycle to an instance of this class.
|
13
12
|
class EMAdapter
|
14
|
-
include Singleton
|
15
|
-
|
16
13
|
# Run reactor in its own interally managed thread
|
17
14
|
attr_accessor :reactor_thread
|
18
15
|
|
@@ -8,6 +8,14 @@
|
|
8
8
|
# Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
|
9
9
|
# Licensed under the Apache License, Version 2.0
|
10
10
|
|
11
|
+
# TODO
|
12
|
+
# - data received/sent (on different interfaces)
|
13
|
+
# - messages received/sent (on different interfaces)
|
14
|
+
# - dispatches on a per node basis
|
15
|
+
# - unresolved/invalid dispatches/messages
|
16
|
+
# - em jobs
|
17
|
+
# - thread pool jobs (started / completed / etc)
|
18
|
+
|
11
19
|
require 'eventmachine'
|
12
20
|
|
13
21
|
# Helper method to process user params / select stats
|
@@ -37,34 +45,51 @@ def select_stats(dispatcher, *filter)
|
|
37
45
|
dispatcher.requests.select { |ds| lf.all? { |lfi| lfi.call(ds) } }
|
38
46
|
end
|
39
47
|
|
40
|
-
# Add
|
41
|
-
def
|
48
|
+
# Add inspection methods to specified dispatcher
|
49
|
+
def dispatch_rjr_inspect(dispatcher)
|
42
50
|
# Retrieve all the dispatches this node served matching the specified criteri
|
43
51
|
dispatcher.handle "rjr::dispatches" do |filter|
|
44
|
-
select_stats(*filter)
|
52
|
+
select_stats(dispatcher, *filter)
|
45
53
|
end
|
46
54
|
|
47
55
|
# Retrieve the number of dispatches this node served matching the specified criteria
|
48
56
|
dispatcher.handle "rjr::num_dispatches" do |filter|
|
49
|
-
select_stats(*filter).size
|
57
|
+
select_stats(dispatcher, *filter).size
|
50
58
|
end
|
51
59
|
|
52
60
|
# Retrieve the internal status of this node
|
53
61
|
dispatcher.handle "rjr::status" do
|
62
|
+
nodes = []
|
63
|
+
ObjectSpace.each_object RJR::Node do |node|
|
64
|
+
nodes << node.to_s
|
65
|
+
end
|
66
|
+
|
54
67
|
{
|
68
|
+
# nodes
|
69
|
+
:nodes => nodes,
|
70
|
+
|
71
|
+
# dispatcher
|
72
|
+
:dispatcher => {
|
73
|
+
:requests => dispatcher.requests.size,
|
74
|
+
:handlers =>
|
75
|
+
dispatcher.handlers.keys,
|
76
|
+
#dispatcher.handlers.collect { |k,v|
|
77
|
+
# [k, v.source_location] },
|
78
|
+
:environments => dispatcher.environments
|
79
|
+
},
|
80
|
+
|
55
81
|
# event machine
|
56
82
|
:event_machine => { :running => EventMachine.reactor_running?,
|
57
|
-
:thread_status =>
|
58
|
-
|
59
|
-
|
83
|
+
:thread_status =>
|
84
|
+
(RJR::Node.em && RJR::Node.em.reactor_thread) ?
|
85
|
+
RJR::Node.em.reactor_thread.status : nil,
|
60
86
|
:connections => EventMachine.connection_count },
|
61
87
|
|
62
88
|
# thread pool
|
63
|
-
:thread_pool => { :running => RJR::
|
89
|
+
:thread_pool => { :running => RJR::Node.tp ? RJR::Node.tp.running? : nil }
|
64
90
|
}
|
65
91
|
end
|
66
92
|
|
67
93
|
#:log =>
|
68
94
|
# lambda {},
|
69
95
|
end
|
70
|
-
|
data/lib/rjr/message.rb
CHANGED
@@ -5,8 +5,9 @@
|
|
5
5
|
# Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
|
6
6
|
# Licensed under the Apache License, Version 2.0
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# FIXME https://github.com/flori/json/issues/179
|
9
|
+
# if pull request doesn't get accepted implement
|
10
|
+
# one of the workarounds in rjr
|
10
11
|
|
11
12
|
require 'json'
|
12
13
|
require 'rjr/common'
|
data/lib/rjr/node.rb
CHANGED
@@ -44,6 +44,15 @@ class Node
|
|
44
44
|
# Dispatcher to use to satisfy requests
|
45
45
|
attr_accessor :dispatcher
|
46
46
|
|
47
|
+
# alias of RJR_NODE_TYPE
|
48
|
+
def node_type
|
49
|
+
self.class::RJR_NODE_TYPE
|
50
|
+
end
|
51
|
+
|
52
|
+
# XXX used by debugging / stats interface
|
53
|
+
def self.em ; defined?(@@em) ? @@em : nil end
|
54
|
+
def self.tp ; defined?(@@tp) ? @@tp : nil end
|
55
|
+
|
47
56
|
# RJR::Node initializer
|
48
57
|
#
|
49
58
|
# @param [Hash] args options to set on request
|
@@ -60,16 +69,20 @@ class Node
|
|
60
69
|
@dispatcher = args[:dispatcher] || RJR::Dispatcher.new
|
61
70
|
@message_headers = args.has_key?(:headers) ? {}.merge(args[:headers]) : {}
|
62
71
|
|
63
|
-
|
64
|
-
|
72
|
+
@@tp ||= ThreadPool.new
|
73
|
+
@@em ||= EMAdapter.new
|
74
|
+
|
75
|
+
# will do nothing if already started
|
76
|
+
@@tp.start
|
77
|
+
@@em.start
|
65
78
|
end
|
66
79
|
|
67
80
|
# Block until the eventmachine reactor and thread pool have both completed running
|
68
81
|
#
|
69
82
|
# @return self
|
70
83
|
def join
|
71
|
-
|
72
|
-
|
84
|
+
@@tp.join
|
85
|
+
@@em.join
|
73
86
|
self
|
74
87
|
end
|
75
88
|
|
@@ -80,8 +93,8 @@ class Node
|
|
80
93
|
#
|
81
94
|
# @return self
|
82
95
|
def halt
|
83
|
-
|
84
|
-
|
96
|
+
@@em.stop_event_loop
|
97
|
+
@@tp.stop
|
85
98
|
self
|
86
99
|
end
|
87
100
|
|
@@ -113,10 +126,10 @@ class Node
|
|
113
126
|
# Internal helper, handle message received
|
114
127
|
def handle_message(msg, connection = {})
|
115
128
|
if RequestMessage.is_request_message?(msg)
|
116
|
-
|
129
|
+
@@tp << ThreadPoolJob.new(msg) { |m| handle_request(m, false, connection) }
|
117
130
|
|
118
131
|
elsif NotificationMessage.is_notification_message?(msg)
|
119
|
-
|
132
|
+
@@tp << ThreadPoolJob.new(msg) { |m| handle_request(m, true, connection) }
|
120
133
|
|
121
134
|
elsif ResponseMessage.is_response_message?(msg)
|
122
135
|
handle_response(msg)
|
@@ -142,12 +155,12 @@ class Node
|
|
142
155
|
NotificationMessage.new(:message => data,
|
143
156
|
:headers => @message_headers) :
|
144
157
|
RequestMessage.new(:message => data,
|
145
|
-
|
158
|
+
:headers => @message_headers)
|
146
159
|
|
147
160
|
result =
|
148
161
|
@dispatcher.dispatch(:rjr_method => msg.jr_method,
|
149
162
|
:rjr_method_args => msg.jr_args,
|
150
|
-
:
|
163
|
+
:rjr_headers => msg.headers,
|
151
164
|
:rjr_client_ip => client_ip,
|
152
165
|
:rjr_client_port => client_port,
|
153
166
|
:rjr_node => self,
|
@@ -164,6 +177,8 @@ class Node
|
|
164
177
|
self.send_msg(response.to_s, connection)
|
165
178
|
return response
|
166
179
|
end
|
180
|
+
|
181
|
+
nil
|
167
182
|
end
|
168
183
|
|
169
184
|
# Internal helper, handle response message received
|
data/lib/rjr/nodes/amqp.rb
CHANGED
@@ -61,30 +61,33 @@ class AMQP < RJR::Node
|
|
61
61
|
return
|
62
62
|
end
|
63
63
|
|
64
|
-
@conn = ::AMQP.connect(:host => @broker) do
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
@conn = ::AMQP.connect(:host => @broker) do |conn|
|
65
|
+
::AMQP.connection = conn # XXX not sure why this is needed but the amqp
|
66
|
+
# em interface won't shut down cleanly otherwise
|
67
|
+
|
68
|
+
conn.on_tcp_connection_failure { puts "OTCF #{@node_id}" }
|
68
69
|
|
69
|
-
|
70
|
+
### connect to qpid broker
|
71
|
+
@channel = ::AMQP::Channel.new(conn)
|
70
72
|
|
71
|
-
|
72
|
-
|
73
|
+
# qpid constructs that will be created for node
|
74
|
+
@queue_name = "#{@node_id.to_s}-queue"
|
75
|
+
@queue = @channel.queue(@queue_name, :auto_delete => true)
|
76
|
+
@exchange = @channel.default_exchange
|
73
77
|
|
74
|
-
|
75
|
-
|
76
|
-
@queue = @channel.queue(@queue_name, :auto_delete => true)
|
77
|
-
@exchange = @channel.default_exchange
|
78
|
+
@listening = false
|
79
|
+
#@disconnected = false
|
78
80
|
|
79
|
-
|
80
|
-
|
81
|
+
@exchange.on_return do |basic_return, metadata, payload|
|
82
|
+
puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
|
83
|
+
#@disconnected = true # FIXME member will be set on wrong class
|
84
|
+
# TODO these are only run when we fail to send message to queue,
|
85
|
+
# need to detect when that queue is shutdown & other events
|
86
|
+
connection_event(:error)
|
87
|
+
connection_event(:closed)
|
88
|
+
end
|
81
89
|
|
82
|
-
|
83
|
-
puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
|
84
|
-
#@disconnected = true # FIXME member will be set on wrong class
|
85
|
-
# TODO these are only run when we fail to send message to queue, need to detect when that queue is shutdown & other events
|
86
|
-
connection_event(:error)
|
87
|
-
connection_event(:closed)
|
90
|
+
on_init.call
|
88
91
|
end
|
89
92
|
end
|
90
93
|
|
@@ -116,6 +119,10 @@ class AMQP < RJR::Node
|
|
116
119
|
@amqp_lock = Mutex.new
|
117
120
|
end
|
118
121
|
|
122
|
+
def to_s
|
123
|
+
"RJR::Nodes::AMQP<#{@node_id},#{@broker},#{@queue_name}>"
|
124
|
+
end
|
125
|
+
|
119
126
|
# Publish a message using the amqp exchange
|
120
127
|
#
|
121
128
|
# Implementation of {RJR::Node#send_msg}
|
@@ -137,7 +144,7 @@ class AMQP < RJR::Node
|
|
137
144
|
#
|
138
145
|
# Implementation of {RJR::Node#listen}
|
139
146
|
def listen
|
140
|
-
|
147
|
+
@@em.schedule do
|
141
148
|
init_node {
|
142
149
|
subscribe # start receiving messages
|
143
150
|
}
|
@@ -161,7 +168,7 @@ class AMQP < RJR::Node
|
|
161
168
|
message = RequestMessage.new :method => rpc_method,
|
162
169
|
:args => args,
|
163
170
|
:headers => @message_headers
|
164
|
-
|
171
|
+
@@em.schedule do
|
165
172
|
init_node {
|
166
173
|
subscribe # begin listening for result
|
167
174
|
send_msg(message.to_s, :routing_key => routing_key, :reply_to => @queue_name)
|
@@ -197,7 +204,7 @@ class AMQP < RJR::Node
|
|
197
204
|
message = NotificationMessage.new :method => rpc_method,
|
198
205
|
:args => args,
|
199
206
|
:headers => @message_headers
|
200
|
-
|
207
|
+
@@em.schedule do
|
201
208
|
init_node {
|
202
209
|
send_msg(message.to_s, :routing_key => routing_key, :reply_to => @queue_name){
|
203
210
|
published_l.synchronize { invoked = true ; published_c.signal }
|
data/lib/rjr/nodes/local.rb
CHANGED
@@ -26,7 +26,7 @@ module Nodes
|
|
26
26
|
#
|
27
27
|
# @example Listening for and dispatching json-rpc requests locally
|
28
28
|
# # initialize node
|
29
|
-
# node = RJR::
|
29
|
+
# node = RJR::Nodes::Local.new :node_id => 'node'
|
30
30
|
#
|
31
31
|
# node.dispatcher.handle('hello') do |name|
|
32
32
|
# @rjr_node_type == :local ? "Hello superuser #{name}" : "Hello #{name}!"
|
@@ -41,13 +41,17 @@ class Local < RJR::Node
|
|
41
41
|
# allows clients to override the node type for the local node
|
42
42
|
attr_accessor :node_type
|
43
43
|
|
44
|
-
#
|
44
|
+
# Nodes::Local initializer
|
45
45
|
# @param [Hash] args the options to create the local node with
|
46
46
|
def initialize(args = {})
|
47
47
|
super(args)
|
48
48
|
@node_type = RJR_NODE_TYPE
|
49
49
|
end
|
50
50
|
|
51
|
+
def to_s
|
52
|
+
"RJR::Nodes::Local<#{@node_id}>"
|
53
|
+
end
|
54
|
+
|
51
55
|
# Send data using specified connection.
|
52
56
|
#
|
53
57
|
# Simply dispatch local notification.
|
@@ -55,8 +59,8 @@ class Local < RJR::Node
|
|
55
59
|
# Implementation of {RJR::Node#send_msg}
|
56
60
|
def send_msg(msg, connection)
|
57
61
|
# ignore response message
|
58
|
-
unless
|
59
|
-
|
62
|
+
unless ResponseMessage.is_response_message?(msg)
|
63
|
+
launch_request(msg, true) # .join?
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
@@ -68,6 +72,18 @@ class Local < RJR::Node
|
|
68
72
|
self
|
69
73
|
end
|
70
74
|
|
75
|
+
# Helper to launch request in new thread
|
76
|
+
#
|
77
|
+
# This needs to happen so that each request runs in its own context
|
78
|
+
# (or close to it, globals will still be available, but locks will
|
79
|
+
# not be locally held, etc)
|
80
|
+
def launch_request(req, notification)
|
81
|
+
Thread.new(req,notification) { |req,notification|
|
82
|
+
res = handle_request(req, notification, nil)
|
83
|
+
handle_response(res.to_s) unless res.nil?
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
71
87
|
# Instructs node to send rpc request, and wait for and return response
|
72
88
|
#
|
73
89
|
# Implementation of {RJR::Node#invoke}
|
@@ -85,9 +101,16 @@ class Local < RJR::Node
|
|
85
101
|
0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
|
86
102
|
message = RequestMessage.new(:method => rpc_method,
|
87
103
|
:args => args,
|
88
|
-
:headers => @message_headers)
|
89
|
-
|
90
|
-
|
104
|
+
:headers => @message_headers)
|
105
|
+
launch_request(message.to_s, false)
|
106
|
+
|
107
|
+
# TODO optional timeout for response ?
|
108
|
+
res = wait_for_result(message)
|
109
|
+
|
110
|
+
if res.size > 2
|
111
|
+
raise Exception, res[2]
|
112
|
+
end
|
113
|
+
return res[1]
|
91
114
|
end
|
92
115
|
|
93
116
|
# Instructs node to send rpc notification (immediately returns / no response is generated)
|
@@ -99,15 +122,11 @@ class Local < RJR::Node
|
|
99
122
|
# @param [String] rpc_method json-rpc method to invoke on destination
|
100
123
|
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
101
124
|
def notify(rpc_method, *args)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
:headers => @message_headers).to_s
|
108
|
-
handle_request(message, true, nil)
|
109
|
-
rescue
|
110
|
-
end
|
125
|
+
0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
|
126
|
+
message = NotificationMessage.new(:method => rpc_method,
|
127
|
+
:args => args,
|
128
|
+
:headers => @message_headers)
|
129
|
+
launch_request(message.to_s, true) #.join ?
|
111
130
|
nil
|
112
131
|
end
|
113
132
|
|
data/lib/rjr/nodes/multi.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
# TODO
|
data/lib/rjr/nodes/tcp.rb
CHANGED
@@ -116,6 +116,10 @@ class TCP < RJR::Node
|
|
116
116
|
@connections_lock = Mutex.new
|
117
117
|
end
|
118
118
|
|
119
|
+
def to_s
|
120
|
+
"RJR::Nodes::TCP<#{@node_id},#{@host},#{@port}>"
|
121
|
+
end
|
122
|
+
|
119
123
|
# Send data using specified connection
|
120
124
|
#
|
121
125
|
# Implementation of {RJR::Node#send_msg}
|
@@ -127,8 +131,8 @@ class TCP < RJR::Node
|
|
127
131
|
#
|
128
132
|
# Implementation of {RJR::Node#listen}
|
129
133
|
def listen
|
130
|
-
|
131
|
-
|
134
|
+
@@em.schedule {
|
135
|
+
@@em.start_server @host, @port, TCPConnection, { :rjr_node => self }
|
132
136
|
}
|
133
137
|
self
|
134
138
|
end
|
@@ -152,7 +156,7 @@ class TCP < RJR::Node
|
|
152
156
|
:args => args,
|
153
157
|
:headers => @message_headers
|
154
158
|
connection = nil
|
155
|
-
|
159
|
+
@@em.schedule {
|
156
160
|
init_client(:host => host, :port => port,
|
157
161
|
:rjr_node => self) { |c|
|
158
162
|
connection = c
|
@@ -190,7 +194,7 @@ class TCP < RJR::Node
|
|
190
194
|
message = NotificationMessage.new :method => rpc_method,
|
191
195
|
:args => args,
|
192
196
|
:headers => @message_headers
|
193
|
-
|
197
|
+
@@em.schedule {
|
194
198
|
init_client(:host => host, :port => port,
|
195
199
|
:rjr_node => self) { |c|
|
196
200
|
conn = c
|
data/lib/rjr/nodes/web.rb
CHANGED
@@ -103,6 +103,10 @@ class Web < RJR::Node
|
|
103
103
|
@port = args[:port]
|
104
104
|
end
|
105
105
|
|
106
|
+
def to_s
|
107
|
+
"RJR::Nodes::Web<#{@node_id},#{@host},#{@port}>"
|
108
|
+
end
|
109
|
+
|
106
110
|
# Send data using specified http connection
|
107
111
|
#
|
108
112
|
# Implementation of {RJR::Node#send_msg}
|
@@ -123,7 +127,7 @@ class Web < RJR::Node
|
|
123
127
|
#
|
124
128
|
# Implementation of {RJR::Node#listen}
|
125
129
|
def listen
|
126
|
-
|
130
|
+
@@em.schedule do
|
127
131
|
EventMachine::start_server(@host, @port, WebConnection, :rjr_node => self)
|
128
132
|
end
|
129
133
|
self
|
@@ -149,7 +153,7 @@ class Web < RJR::Node
|
|
149
153
|
handle_message(http.response, http)
|
150
154
|
}
|
151
155
|
|
152
|
-
|
156
|
+
@@em.schedule do
|
153
157
|
http = EventMachine::HttpRequest.new(uri).post :body => message.to_s
|
154
158
|
http.errback &cb
|
155
159
|
http.callback &cb
|
@@ -182,7 +186,7 @@ class Web < RJR::Node
|
|
182
186
|
:args => args,
|
183
187
|
:headers => @message_headers
|
184
188
|
cb = lambda { |arg| published_l.synchronize { invoked = true ; published_c.signal }}
|
185
|
-
|
189
|
+
@@em.schedule do
|
186
190
|
http = EventMachine::HttpRequest.new(uri).post :body => message.to_s
|
187
191
|
http.errback &cb
|
188
192
|
http.callback &cb
|
data/lib/rjr/nodes/ws.rb
CHANGED
@@ -93,6 +93,10 @@ class WS < RJR::Node
|
|
93
93
|
@connections_lock = Mutex.new
|
94
94
|
end
|
95
95
|
|
96
|
+
def to_s
|
97
|
+
"RJR::Nodes::WS<#{@node_id},#{@host},#{@port}>"
|
98
|
+
end
|
99
|
+
|
96
100
|
# Send data using specified websocket safely
|
97
101
|
#
|
98
102
|
# Implementation of {RJR::Node#send_msg}
|
@@ -104,8 +108,8 @@ class WS < RJR::Node
|
|
104
108
|
#
|
105
109
|
# Implementation of {RJR::Node#listen}
|
106
110
|
def listen
|
107
|
-
|
108
|
-
EventMachine::WebSocket.
|
111
|
+
@@em.schedule do
|
112
|
+
EventMachine::WebSocket.run(:host => @host, :port => @port) do |ws|
|
109
113
|
ws.onopen { }
|
110
114
|
ws.onclose { @connection_event_handlers[:closed].each { |h| h.call self } }
|
111
115
|
ws.onerror { |e| @connection_event_handlers[:error].each { |h| h.call self } }
|
@@ -131,7 +135,7 @@ class WS < RJR::Node
|
|
131
135
|
:args => args,
|
132
136
|
:headers => @message_headers
|
133
137
|
|
134
|
-
|
138
|
+
@@em.schedule {
|
135
139
|
init_client(uri) do |c|
|
136
140
|
c.stream { |msg| handle_message(msg, c) }
|
137
141
|
|
@@ -165,7 +169,7 @@ class WS < RJR::Node
|
|
165
169
|
message = NotificationMessage.new :method => rpc_method,
|
166
170
|
:args => args,
|
167
171
|
:headers => @message_headers
|
168
|
-
|
172
|
+
@@em.schedule {
|
169
173
|
init_client(uri) do |c|
|
170
174
|
c.stream { |msg| handle_message(msg, c) }
|
171
175
|
|
data/lib/rjr/thread_pool.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
# Copyright (C) 2010-2013 Mohammed Morsi <mo@morsi.org>
|
4
4
|
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
|
-
require 'singleton'
|
7
|
-
|
8
6
|
module RJR
|
9
7
|
|
10
8
|
# Work item to be executed in a thread launched by {ThreadPool}.
|
@@ -81,8 +79,6 @@ end
|
|
81
79
|
# Supports optional timeout which allows the user to kill and restart
|
82
80
|
# threads if a job is taking too long to run.
|
83
81
|
class ThreadPool
|
84
|
-
include Singleton
|
85
|
-
|
86
82
|
class << self
|
87
83
|
# @!group Config options (must be set before first node is instantiated)
|
88
84
|
|
data/site/jrw.js
CHANGED
@@ -120,7 +120,7 @@ function JRObject (type, value, ignore_properties){
|
|
120
120
|
this.type = type;
|
121
121
|
this.value = value;
|
122
122
|
this.ignore_properties = (typeof(ignore_properties) != 'undefined') ?
|
123
|
-
ignore_properties : ["toJSON"];
|
123
|
+
ignore_properties : ["toJSON", "json_class"];
|
124
124
|
this.toJSON = function(){
|
125
125
|
var data = {};
|
126
126
|
for(p in this.value)
|
@@ -169,6 +169,7 @@ JRObject.from_json_array = function(json){
|
|
169
169
|
// Main json-rpc client websocket interface
|
170
170
|
function WSNode (host, port){
|
171
171
|
var node = this;
|
172
|
+
this.opening = false;
|
172
173
|
this.opened = false;
|
173
174
|
this.node_id = null;
|
174
175
|
this.headers = {};
|
@@ -176,6 +177,8 @@ function WSNode (host, port){
|
|
176
177
|
|
177
178
|
// Open socket connection
|
178
179
|
this.open = function(){
|
180
|
+
if(this.opening) return;
|
181
|
+
this.opening = true;
|
179
182
|
node.socket = new WebSocket("ws://" + host + ":" + port);
|
180
183
|
|
181
184
|
node.socket.onclose = function (){
|
@@ -197,14 +200,14 @@ function WSNode (host, port){
|
|
197
200
|
if(node.onerror)
|
198
201
|
node.onerror(msg)
|
199
202
|
|
200
|
-
}else{
|
201
|
-
// relying on clients to handle notifications via message_received
|
202
|
-
// TODO add notification (and request?) handler support here
|
203
|
-
//node.invoke_method(msg.rpc_method, msg.params)
|
204
|
-
if(node.message_received)
|
205
|
-
node.message_received(msg);
|
206
|
-
|
207
203
|
}
|
204
|
+
|
205
|
+
// relying on clients to handle notifications via message_received
|
206
|
+
// TODO add notification (and request?) handler support here
|
207
|
+
// clients may user this to register additional handlers to be invoked
|
208
|
+
// upon request responses
|
209
|
+
if(node.message_received)
|
210
|
+
node.message_received(msg);
|
208
211
|
};
|
209
212
|
|
210
213
|
node.socket.onerror = function(e){
|
@@ -213,12 +216,13 @@ function WSNode (host, port){
|
|
213
216
|
}
|
214
217
|
|
215
218
|
node.socket.onopen = function (){
|
219
|
+
node.opened = true;
|
220
|
+
node.opening = false;
|
221
|
+
|
216
222
|
// send queued messages
|
217
223
|
for(var m in node.messages)
|
218
224
|
node.socket.send(node.messages[m].to_json());
|
219
225
|
|
220
|
-
node.opened = true;
|
221
|
-
|
222
226
|
// invoke client callback
|
223
227
|
if(node.onopen)
|
224
228
|
node.onopen();
|
@@ -270,9 +274,10 @@ function WebNode (uri){
|
|
270
274
|
|
271
275
|
success: function(data){
|
272
276
|
var msg = JRMessage.from_msg(data);
|
273
|
-
//
|
274
|
-
//
|
275
|
-
|
277
|
+
// clients may register additional callbacks
|
278
|
+
// to handle web node responses
|
279
|
+
if(node.message_received)
|
280
|
+
node.message_received(msg);
|
276
281
|
|
277
282
|
req.handle_response(msg)
|
278
283
|
|
data/specs/dispatcher_spec.rb
CHANGED
@@ -145,6 +145,7 @@ module RJR
|
|
145
145
|
h = proc {}
|
146
146
|
d.handle('foobar', h)
|
147
147
|
d.handlers['foobar'].should == h
|
148
|
+
d.handles?('foobar').should be_true
|
148
149
|
end
|
149
150
|
|
150
151
|
it "should set handler from block param" do
|
@@ -152,6 +153,7 @@ module RJR
|
|
152
153
|
h = proc {}
|
153
154
|
d.handle('foobar', &h)
|
154
155
|
d.handlers['foobar'].should == h
|
156
|
+
d.handles?('foobar').should be_true
|
155
157
|
end
|
156
158
|
|
157
159
|
it "should register handler for multiple methods" do
|
@@ -160,6 +162,8 @@ module RJR
|
|
160
162
|
d.handle(['foobar', 'barfoo'], &h)
|
161
163
|
d.handlers['foobar'].should == h
|
162
164
|
d.handlers['barfoo'].should == h
|
165
|
+
d.handles?('foobar').should be_true
|
166
|
+
d.handles?('barfoo').should be_true
|
163
167
|
end
|
164
168
|
end
|
165
169
|
|
@@ -183,6 +187,10 @@ module RJR
|
|
183
187
|
invoked.should be_true
|
184
188
|
end
|
185
189
|
|
190
|
+
context "handler is regex" do
|
191
|
+
it "should match method"
|
192
|
+
end
|
193
|
+
|
186
194
|
it "should pass params to handler" do
|
187
195
|
param = nil
|
188
196
|
d = Dispatcher.new
|
data/specs/em_adapter_spec.rb
CHANGED
@@ -3,49 +3,43 @@ require 'rjr/em_adapter'
|
|
3
3
|
|
4
4
|
module RJR
|
5
5
|
describe EMAdapter do
|
6
|
-
|
7
|
-
EMAdapter.
|
8
|
-
EMAdapter.instance.join
|
6
|
+
before(:each) do
|
7
|
+
@em = EMAdapter.new
|
9
8
|
end
|
10
9
|
|
11
|
-
|
12
|
-
em
|
13
|
-
EMAdapter.instance.should == em
|
10
|
+
after(:each) do
|
11
|
+
@em.halt.join
|
14
12
|
end
|
15
13
|
|
16
14
|
it "should start the reactor" do
|
17
|
-
em
|
18
|
-
em.
|
19
|
-
|
20
|
-
['sleep', 'run'].should include(em.reactor_thread.status)
|
15
|
+
@em.start
|
16
|
+
@em.reactor_running?.should be_true
|
17
|
+
['sleep', 'run'].should include(@em.reactor_thread.status)
|
21
18
|
end
|
22
19
|
|
23
20
|
it "should only start the reactor once" do
|
24
|
-
em
|
25
|
-
em.start
|
21
|
+
@em.start
|
26
22
|
|
27
|
-
ot = em.reactor_thread
|
28
|
-
em.start
|
29
|
-
em.reactor_thread.should == ot
|
23
|
+
ot = @em.reactor_thread
|
24
|
+
@em.start
|
25
|
+
@em.reactor_thread.should == ot
|
30
26
|
end
|
31
27
|
|
32
28
|
it "should halt the reactor" do
|
33
|
-
em
|
34
|
-
em.start
|
29
|
+
@em.start
|
35
30
|
|
36
|
-
em.halt
|
37
|
-
em.join
|
38
|
-
em.reactor_running?.should be_false
|
39
|
-
em.reactor_thread.should be_nil
|
31
|
+
@em.halt
|
32
|
+
@em.join
|
33
|
+
@em.reactor_running?.should be_false
|
34
|
+
@em.reactor_thread.should be_nil
|
40
35
|
end
|
41
36
|
|
42
37
|
it "should dispatch all requests to eventmachine" do
|
43
|
-
em
|
44
|
-
em.start
|
38
|
+
@em.start
|
45
39
|
|
46
40
|
invoked = false
|
47
41
|
m,c = Mutex.new, ConditionVariable.new
|
48
|
-
em.schedule {
|
42
|
+
@em.schedule {
|
49
43
|
invoked = true
|
50
44
|
m.synchronize { c.signal }
|
51
45
|
}
|
data/specs/node_spec.rb
CHANGED
@@ -21,27 +21,26 @@ module RJR
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should start the thread pool" do
|
24
|
-
ThreadPool.instance.stop.join
|
25
24
|
node = Node.new
|
26
|
-
|
25
|
+
node.class.class_variable_get(:@@tp).should be_running
|
27
26
|
end
|
28
27
|
|
29
28
|
it "should start event machine" do
|
30
|
-
|
29
|
+
EventMachine.stop_event_loop
|
31
30
|
node = Node.new
|
32
|
-
|
31
|
+
EventMachine.reactor_running?.should be_true
|
33
32
|
end
|
34
33
|
|
35
34
|
it "should halt the thread pool" do
|
36
35
|
node = Node.new
|
37
36
|
node.halt.join
|
38
|
-
|
37
|
+
node.class.class_variable_get(:@@tp).should_not be_running
|
39
38
|
end
|
40
39
|
|
41
40
|
it "should halt event machine" do
|
42
41
|
node = Node.new
|
43
42
|
node.halt.join
|
44
|
-
|
43
|
+
EventMachine.reactor_running?.should be_false
|
45
44
|
end
|
46
45
|
|
47
46
|
it "should handle connection events" do
|
data/specs/nodes/local_spec.rb
CHANGED
@@ -32,16 +32,22 @@ module RJR::Nodes
|
|
32
32
|
|
33
33
|
describe "#notify" do
|
34
34
|
it "should dispatch local notification" do
|
35
|
+
# notify will most likely return before
|
36
|
+
# handler is executed (in seperate thread), wait
|
37
|
+
m,c = Mutex.new, ConditionVariable.new
|
38
|
+
|
35
39
|
invoked = nil
|
36
40
|
node = Local.new :node_id => 'aaa'
|
37
41
|
node.dispatcher.handle('foobar') { |param|
|
38
42
|
invoked = true
|
43
|
+
m.synchronize { c.signal }
|
39
44
|
'retval'
|
40
45
|
}
|
41
46
|
|
42
47
|
res = node.notify 'foobar', 'myparam'
|
43
|
-
invoked.should == true
|
44
48
|
res.should == nil
|
49
|
+
m.synchronize { c.wait m, 0.1 } unless invoked
|
50
|
+
invoked.should == true
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
data/specs/thread_pool_spec.rb
CHANGED
@@ -5,40 +5,35 @@ require 'rjr/thread_pool'
|
|
5
5
|
|
6
6
|
module RJR
|
7
7
|
describe ThreadPool do
|
8
|
-
|
9
|
-
ThreadPool.
|
10
|
-
ThreadPool.instance.join
|
8
|
+
before(:each) do
|
9
|
+
@tp = ThreadPool.new
|
11
10
|
end
|
12
11
|
|
13
|
-
|
14
|
-
tp
|
15
|
-
ThreadPool.instance.should == tp
|
12
|
+
after(:each) do
|
13
|
+
@tp.stop.join
|
16
14
|
end
|
17
15
|
|
18
16
|
it "should start the thread pool" do
|
19
|
-
tp
|
20
|
-
tp.
|
21
|
-
tp.
|
22
|
-
tp.should be_running
|
17
|
+
@tp.start
|
18
|
+
@tp.instance_variable_get(:@worker_threads).size.should == ThreadPool.num_threads
|
19
|
+
@tp.should be_running
|
23
20
|
end
|
24
21
|
|
25
22
|
it "should stop the thread pool" do
|
26
|
-
tp
|
27
|
-
tp.
|
28
|
-
tp.
|
29
|
-
tp.
|
30
|
-
tp.
|
31
|
-
tp.should_not be_running
|
23
|
+
@tp.start
|
24
|
+
@tp.stop
|
25
|
+
@tp.join
|
26
|
+
@tp.instance_variable_get(:@worker_threads).size.should == 0
|
27
|
+
@tp.should_not be_running
|
32
28
|
end
|
33
29
|
|
34
30
|
it "should run work" do
|
35
|
-
tp
|
36
|
-
tp.start
|
31
|
+
@tp.start
|
37
32
|
|
38
33
|
jobs_executed = []
|
39
34
|
m,c = Mutex.new, ConditionVariable.new
|
40
|
-
tp << ThreadPoolJob.new { jobs_executed << 1 ; m.synchronize { c.signal } }
|
41
|
-
tp << ThreadPoolJob.new { jobs_executed << 2 ; m.synchronize { c.signal } }
|
35
|
+
@tp << ThreadPoolJob.new { jobs_executed << 1 ; m.synchronize { c.signal } }
|
36
|
+
@tp << ThreadPoolJob.new { jobs_executed << 2 ; m.synchronize { c.signal } }
|
42
37
|
|
43
38
|
m.synchronize { c.wait m, 0.1 } unless jobs_executed.include?(1)
|
44
39
|
m.synchronize { c.wait m, 0.1 } unless jobs_executed.include?(2)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rjr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -79,14 +79,15 @@ files:
|
|
79
79
|
- lib/rjr/semaphore.rb
|
80
80
|
- lib/rjr/em_adapter.rb
|
81
81
|
- lib/rjr/errors.rb
|
82
|
+
- lib/rjr/inspect.rb
|
82
83
|
- lib/rjr/node.rb
|
83
84
|
- lib/rjr/common.rb
|
84
85
|
- lib/rjr/thread_pool.rb
|
85
86
|
- lib/rjr/message.rb
|
86
|
-
- lib/rjr/stats.rb
|
87
87
|
- lib/rjr/nodes/web.rb
|
88
88
|
- lib/rjr/nodes/udp.rb
|
89
89
|
- lib/rjr/nodes/tcp.rb
|
90
|
+
- lib/rjr/nodes/ssh.rb
|
90
91
|
- lib/rjr/nodes/ws.rb
|
91
92
|
- lib/rjr/nodes/tcp2.rb
|
92
93
|
- lib/rjr/nodes/local.rb
|
@@ -97,7 +98,6 @@ files:
|
|
97
98
|
- lib/rjr/dispatcher.rb
|
98
99
|
- specs/dispatcher_spec.rb
|
99
100
|
- specs/node_spec.rb
|
100
|
-
- specs/stats_spec.rb
|
101
101
|
- specs/nodes/multi_spec.rb
|
102
102
|
- specs/nodes/tcp_spec.rb
|
103
103
|
- specs/nodes/web_spec.rb
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- specs/nodes/amqp_spec.rb
|
106
106
|
- specs/nodes/ws_spec.rb
|
107
107
|
- specs/nodes/easy_spec.rb
|
108
|
+
- specs/inspect_spec.rb
|
108
109
|
- specs/thread_pool_spec.rb
|
109
110
|
- specs/message_spec.rb
|
110
111
|
- specs/em_adapter_spec.rb
|