rjr 0.18.2 → 0.19.1

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +16 -9
  4. data/bin/rjr-server +2 -1
  5. data/examples/client.rb +21 -19
  6. data/examples/server.rb +1 -1
  7. data/examples/structured_server.rb +1 -0
  8. data/examples/tcp.rb +1 -0
  9. data/lib/rjr/common.rb +1 -226
  10. data/lib/rjr/core_ext.rb +63 -0
  11. data/lib/rjr/dispatcher.rb +75 -219
  12. data/lib/rjr/messages.rb +8 -0
  13. data/lib/rjr/messages/compressed.rb +264 -0
  14. data/lib/rjr/messages/notification.rb +95 -0
  15. data/lib/rjr/messages/request.rb +99 -0
  16. data/lib/rjr/messages/response.rb +128 -0
  17. data/lib/rjr/node.rb +100 -97
  18. data/lib/rjr/node_callback.rb +43 -0
  19. data/lib/rjr/nodes/amqp.rb +12 -11
  20. data/lib/rjr/nodes/easy.rb +4 -4
  21. data/lib/rjr/nodes/local.rb +13 -12
  22. data/lib/rjr/nodes/multi.rb +1 -1
  23. data/lib/rjr/nodes/tcp.rb +15 -13
  24. data/lib/rjr/nodes/template.rb +4 -4
  25. data/lib/rjr/nodes/unix.rb +15 -13
  26. data/lib/rjr/nodes/web.rb +15 -14
  27. data/lib/rjr/nodes/ws.rb +12 -11
  28. data/lib/rjr/request.rb +128 -0
  29. data/lib/rjr/result.rb +75 -0
  30. data/lib/rjr/util/args.rb +145 -0
  31. data/lib/rjr/{em_adapter.rb → util/em_adapter.rb} +0 -0
  32. data/lib/rjr/util/handles_methods.rb +115 -0
  33. data/lib/rjr/util/has_messages.rb +50 -0
  34. data/lib/rjr/{inspect.rb → util/inspect.rb} +1 -1
  35. data/lib/rjr/util/json_parser.rb +101 -0
  36. data/lib/rjr/util/logger.rb +128 -0
  37. data/lib/rjr/{thread_pool.rb → util/thread_pool.rb} +2 -0
  38. data/lib/rjr/version.rb +1 -1
  39. data/site/jrw.js +1 -1
  40. data/specs/args_spec.rb +144 -0
  41. data/specs/dispatcher_spec.rb +399 -211
  42. data/specs/em_adapter_spec.rb +31 -18
  43. data/specs/handles_methods_spec.rb +154 -0
  44. data/specs/has_messages_spec.rb +54 -0
  45. data/specs/inspect_spec.rb +1 -1
  46. data/specs/json_parser_spec.rb +169 -0
  47. data/specs/messages/notification_spec.rb +59 -0
  48. data/specs/messages/request_spec.rb +66 -0
  49. data/specs/messages/response_spec.rb +94 -0
  50. data/specs/node_callbacks_spec.rb +47 -0
  51. data/specs/node_spec.rb +465 -56
  52. data/specs/request_spec.rb +147 -0
  53. data/specs/result_spec.rb +144 -0
  54. data/specs/thread_pool_spec.rb +1 -1
  55. metadata +41 -11
  56. data/lib/rjr/errors.rb +0 -23
  57. data/lib/rjr/message.rb +0 -351
  58. data/lib/rjr/semaphore.rb +0 -58
  59. data/specs/message_spec.rb +0 -229
@@ -0,0 +1,95 @@
1
+ # RJR Notification Message
2
+ #
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ require 'json'
7
+ require 'rjr/util/json_parser'
8
+
9
+ module RJR
10
+ module Messages
11
+
12
+ # Message sent to a JSON-RPC server to invoke a rpc method but
13
+ # indicate the result should _not_ be returned
14
+ class Notification
15
+ # Message string received from the source
16
+ attr_accessor :json_message
17
+
18
+ # Method source is invoking on the destination
19
+ attr_accessor :jr_method
20
+
21
+ # Arguments source is passing to destination method
22
+ attr_accessor :jr_args
23
+
24
+ # Optional headers to add to json outside of standard json-rpc request
25
+ attr_accessor :headers
26
+
27
+ # RJR Notification Message initializer
28
+ #
29
+ # No message id will be generated in accordance w/ the jsonrpc standard
30
+ #
31
+ # @param [Hash] args options to set on request
32
+ # @option args [String] :message json string received from sender
33
+ # @option args [Hash] :headers optional headers to set in request and subsequent messages
34
+ # @option args [String] :method method to invoke on server
35
+ # @option args [Array<Object>] :args to pass to server method, all must be convertable to/from json
36
+ def initialize(args = {})
37
+ parse_args(args)
38
+ end
39
+
40
+ private
41
+
42
+ def parse_args(args)
43
+ @jr_method = args[:method]
44
+ @jr_args = args[:args] || []
45
+ @headers = args[:headers] || {}
46
+
47
+ parse_message(args[:message]) if args.has_key?(:message)
48
+ end
49
+
50
+ def parse_message(message)
51
+ @json_message = message
52
+ notification = JSONParser.parse(@json_message)
53
+ @jr_method = notification['method']
54
+ @jr_args = notification['params']
55
+
56
+ parse_headers(notification)
57
+ end
58
+
59
+ def parse_headers(notification)
60
+ notification.keys.select { |k|
61
+ !['jsonrpc', 'method', 'params'].include?(k)
62
+ }.each { |k| @headers[k] = notification[k] }
63
+ end
64
+
65
+ public
66
+
67
+ # Class helper to determine if the specified string is a valid json-rpc
68
+ # notification
69
+ #
70
+ # @param [String] message string message to check
71
+ # @return [true,false] indicating if message is a notification message
72
+ def self.is_notification_message?(message)
73
+ begin
74
+ # FIXME log error
75
+ parsed = JSONParser.parse(message)
76
+ parsed.has_key?('method') && !parsed.has_key?('id')
77
+ rescue Exception => e
78
+ false
79
+ end
80
+ end
81
+
82
+ # Convert notification message to json
83
+ def to_json(*a)
84
+ {'jsonrpc' => '2.0',
85
+ 'method' => @jr_method,
86
+ 'params' => @jr_args}.merge(@headers).to_json(*a)
87
+ end
88
+
89
+ # Convert request to string format
90
+ def to_s
91
+ to_json.to_s
92
+ end
93
+ end # class Notification
94
+ end # module Messages
95
+ end # module RJR
@@ -0,0 +1,99 @@
1
+ # RJR Request Message
2
+ #
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ require 'json'
7
+ require 'rjr/util/json_parser'
8
+
9
+ module RJR
10
+ module Messages
11
+
12
+ # Message sent from client to server to invoke a JSON-RPC method
13
+ class Request
14
+ # Message string received from the source
15
+ attr_accessor :json_message
16
+
17
+ # Method source is invoking on the destination
18
+ attr_accessor :jr_method
19
+
20
+ # Arguments source is passing to destination method
21
+ attr_accessor :jr_args
22
+
23
+ # ID of the message in accordance w/ json-rpc specification
24
+ attr_accessor :msg_id
25
+
26
+ # Optional headers to add to json outside of standard json-rpc request
27
+ attr_accessor :headers
28
+
29
+ # RJR Request Message initializer
30
+ #
31
+ # @param [Hash] args options to set on request
32
+ # @option args [String] :message json string received from sender
33
+ # @option args [Hash] :headers optional headers to set in request
34
+ # and subsequent messages
35
+ # @option args [String] :method method to invoke on server
36
+ # @option args [Array<Object>] :args to pass to server method, all
37
+ # must be convertable to/from json
38
+ def initialize(args = {})
39
+ parse_args(args)
40
+ end
41
+
42
+ private
43
+
44
+ def parse_args(args)
45
+ @jr_method = args[:method]
46
+ @jr_args = args[:args] || []
47
+ @headers = args[:headers] || {}
48
+ @msg_id = args[:id] || gen_uuid
49
+
50
+ parse_message(args[:message]) if args.has_key?(:message)
51
+ end
52
+
53
+ def parse_message(message)
54
+ @json_message = message
55
+ request = JSONParser.parse(@json_message)
56
+ @jr_method = request['method']
57
+ @jr_args = request['params']
58
+ @msg_id = request['id']
59
+
60
+ parse_headers(request)
61
+ end
62
+
63
+ def parse_headers(request)
64
+ request.keys.select { |k|
65
+ !['jsonrpc', 'id', 'method', 'params'].include?(k)
66
+ }.each { |k| @headers[k] = request[k] }
67
+ end
68
+
69
+ public
70
+
71
+ # Class helper to determine if the specified string is a valid json-rpc
72
+ # method request
73
+ # @param [String] message string message to check
74
+ # @return [true,false] indicating if message is request message
75
+ def self.is_request_message?(message)
76
+ begin
77
+ # FIXME log error
78
+ parsed = JSONParser.parse(message)
79
+ parsed.has_key?('method') && parsed.has_key?('id')
80
+ rescue Exception => e
81
+ false
82
+ end
83
+ end
84
+
85
+ # Convert request message to json
86
+ def to_json(*a)
87
+ {'jsonrpc' => '2.0',
88
+ 'id' => @msg_id,
89
+ 'method' => @jr_method,
90
+ 'params' => @jr_args}.merge(@headers).to_json(*a)
91
+ end
92
+
93
+ # Convert request to string format
94
+ def to_s
95
+ to_json.to_s
96
+ end
97
+ end # class Request
98
+ end # module Messages
99
+ end # module RJR
@@ -0,0 +1,128 @@
1
+ # RJR Response Message
2
+ #
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ require 'json'
7
+ require 'rjr/result'
8
+ require 'rjr/util/json_parser'
9
+
10
+ module RJR
11
+ module Messages
12
+
13
+ # Message sent from server to client in response to a JSON-RPC request
14
+ class Response
15
+ # Message string received from the source
16
+ attr_accessor :json_message
17
+
18
+ # ID of the message in accordance w/ json-rpc specification
19
+ attr_accessor :msg_id
20
+
21
+ # Result encapsulated in the response message
22
+ # @see RJR::Result
23
+ attr_accessor :result
24
+
25
+ # Optional headers to add to json outside of standard json-rpc request
26
+ attr_accessor :headers
27
+
28
+ # ResponseMessage initializer
29
+ #
30
+ # @param [Hash] args options to set on request
31
+ # @option args [String] :message json string received from sender
32
+ # @option args [Hash] :headers optional headers to set in request
33
+ # and subsequent messages
34
+ # @option args [String] :id id to set in response message, should
35
+ # be same as that in received message
36
+ # @option args [RJR::Result] :result result of json-rpc method invocation
37
+ def initialize(args = {})
38
+ parse_args(args)
39
+ end
40
+
41
+ private
42
+
43
+ def parse_args(args)
44
+ @msg_id = args[:id]
45
+ @result = args[:result]
46
+ @headers = args[:headers] || {}
47
+
48
+ parse_message(args[:message]) if args.has_key?(:message)
49
+ end
50
+
51
+ def parse_message(message)
52
+ @json_message = message
53
+ response = JSONParser.parse(@json_message)
54
+ @msg_id = response['id']
55
+
56
+ parse_result(response)
57
+ parse_headers(response)
58
+ end
59
+
60
+ def parse_result(response)
61
+ @result = Result.new
62
+ @result.success = response.has_key?('result')
63
+ @result.failed = !@result.success
64
+
65
+ if @result.success
66
+ @result.result = response['result']
67
+
68
+ elsif response.has_key?('error')
69
+ @result.error_code = response['error']['code']
70
+ @result.error_msg = response['error']['message']
71
+
72
+ # TODO can we safely constantize this ?
73
+ @result.error_class = response['error']['class']
74
+ end
75
+
76
+ @result
77
+ end
78
+
79
+ def parse_headers(request)
80
+ request.keys.select { |k|
81
+ !['jsonrpc', 'id', 'method', 'result', 'error'].include?(k)
82
+ }.each { |k| @headers[k] = request[k] }
83
+ end
84
+
85
+ public
86
+
87
+ # Class helper to determine if the specified string is a valid json-rpc
88
+ # method response
89
+ # @param [String] message string message to check
90
+ # @return [true,false] indicating if message is response message
91
+ def self.is_response_message?(message)
92
+ begin
93
+ # FIXME log error
94
+ json = JSONParser.parse(message)
95
+ json.has_key?('result') || json.has_key?('error')
96
+ rescue Exception => e
97
+ puts e.to_s
98
+ false
99
+ end
100
+ end
101
+
102
+ def success_json
103
+ {'result' => @result.result}
104
+ end
105
+
106
+ def error_json
107
+ {'error' => {'code' => @result.error_code,
108
+ 'message' => @result.error_msg,
109
+ 'class' => @result.error_class}}
110
+ end
111
+
112
+ # Convert request message to json
113
+ def to_json(*a)
114
+ result_json = @result.success ? success_json : error_json
115
+
116
+ {'jsonrpc' => '2.0',
117
+ 'id' => @msg_id}.merge(@headers).
118
+ merge(result_json).to_json(*a)
119
+ end
120
+
121
+ # Convert request to string format
122
+ def to_s
123
+ to_json.to_s
124
+ end
125
+
126
+ end # class Response
127
+ end # module Messages
128
+ end # module RJR
@@ -1,15 +1,14 @@
1
1
  # RJR Base Node Interface
2
2
  #
3
- # Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
4
  # Licensed under the Apache License, Version 2.0
5
5
 
6
- require 'thread'
7
6
  require 'socket'
8
- require 'rjr/common'
9
- require 'rjr/message'
7
+ require 'rjr/messages'
10
8
  require 'rjr/dispatcher'
11
- require 'rjr/em_adapter'
12
- require 'rjr/thread_pool'
9
+ require 'rjr/util/em_adapter'
10
+ require 'rjr/util/thread_pool'
11
+ require 'rjr/node_callback'
13
12
 
14
13
  module RJR
15
14
 
@@ -21,7 +20,8 @@ module RJR
21
20
  # Each subclass should define
22
21
  # * RJR_NODE_TYPE - unique id of the transport
23
22
  # * listen method - begin listening for new requests and return
24
- # * send_message(msg, connection) - send message using the specified connection (transport dependent)
23
+ # * send_message(msg, connection) - send message using the specified connection
24
+ # (transport dependent)
25
25
  # * invoke - establish connection, send message, and wait for / return result
26
26
  # * notify - establish connection, send message, and immediately return
27
27
  #
@@ -44,12 +44,21 @@ class Node
44
44
  # Dispatcher to use to satisfy requests
45
45
  attr_accessor :dispatcher
46
46
 
47
+ # Handlers for various connection events
48
+ attr_reader :connection_event_handlers
49
+
47
50
  class <<self
48
51
  # Bool indiciting if this node is persistent
49
52
  def persistent?
50
53
  self.const_defined?(:PERSISTENT_NODE) &&
51
54
  self.const_get(:PERSISTENT_NODE)
52
55
  end
56
+
57
+ # Bool indiciting if this node is indirect
58
+ def indirect?
59
+ self.const_defined?(:INDIRECT_NODE) &&
60
+ self.const_get(:INDIRECT_NODE)
61
+ end
53
62
  end
54
63
 
55
64
  # Bool indicating if this node class is persistent
@@ -57,20 +66,39 @@ class Node
57
66
  self.class.persistent?
58
67
  end
59
68
 
69
+ # Bool indicating if this node class is indirect
70
+ def indirect?
71
+ self.class.indirect?
72
+ end
73
+
60
74
  # alias of RJR_NODE_TYPE
61
75
  def node_type
62
- self.class::RJR_NODE_TYPE
76
+ self.class.const_defined?(:RJR_NODE_TYPE) ?
77
+ self.class.const_get(:RJR_NODE_TYPE) : nil
78
+ end
79
+
80
+ def self.em
81
+ defined?(@@em) ? @@em : nil
82
+ end
83
+
84
+ def em
85
+ self.class.em
63
86
  end
64
87
 
65
- # XXX used by debugging / stats interface
66
- def self.em ; defined?(@@em) ? @@em : nil end
67
- def self.tp ; defined?(@@tp) ? @@tp : nil end
88
+ def self.tp
89
+ defined?(@@tp) ? @@tp : nil
90
+ end
91
+
92
+ def tp
93
+ self.class.tp
94
+ end
68
95
 
69
96
  # RJR::Node initializer
70
97
  #
71
98
  # @param [Hash] args options to set on request
72
99
  # @option args [String] :node_id unique id of the node
73
- # @option args [Hash<String,String>] :headers optional headers to set on all json-rpc messages
100
+ # @option args [Hash<String,String>] :headers optional headers to set
101
+ # on all json-rpc messages
74
102
  # @option args [Dispatcher] :dispatcher dispatcher to assign to the node
75
103
  def initialize(args = {})
76
104
  clear_event_handlers
@@ -86,28 +114,29 @@ class Node
86
114
  @@em ||= EMAdapter.new
87
115
 
88
116
  # will do nothing if already started
89
- @@tp.start
90
- @@em.start
117
+ tp.start
118
+ em.start
91
119
  end
92
120
 
93
- # Block until the eventmachine reactor and thread pool have both completed running
121
+ # Block until the eventmachine reactor and thread pool have both
122
+ # completed running.
94
123
  #
95
124
  # @return self
96
125
  def join
97
- @@tp.join
98
- @@em.join
126
+ tp.join
127
+ em.join
99
128
  self
100
129
  end
101
130
 
102
131
  # Immediately terminate the node
103
132
  #
104
- # *Warning* this does what it says it does. All running threads, and reactor
105
- # jobs are immediately killed
133
+ # *Warning* this does what it says it does. All running threads,
134
+ # and reactor jobs are immediately killed
106
135
  #
107
136
  # @return self
108
137
  def halt
109
- @@em.stop_event_loop
110
- @@tp.stop
138
+ em.stop_event_loop
139
+ tp.stop
111
140
  self
112
141
  end
113
142
 
@@ -119,36 +148,48 @@ class Node
119
148
 
120
149
  # Register connection event handler
121
150
  # @param [:error, :close] event the event to register the handler for
122
- # @param [Callable] handler block param to be added to array of handlers that are called when event occurs
151
+ # @param [Callable] handler block param to be added to array of handlers
152
+ # that are called when event occurs
123
153
  # @yield [Node] self is passed to each registered handler when event occurs
124
154
  def on(event, &handler)
125
- if @connection_event_handlers.keys.include?(event)
126
- @connection_event_handlers[event] << handler
127
- end
155
+ return unless @connection_event_handlers.keys.include?(event)
156
+ @connection_event_handlers[event] << handler
128
157
  end
129
158
 
130
159
  private
131
160
 
132
161
  # Internal helper, run connection event handlers for specified event
133
162
  def connection_event(event)
134
- if @connection_event_handlers.keys.include?(event)
135
- @connection_event_handlers[event].each { |h|
136
- h.call self
137
- }
138
- end
163
+ return unless @connection_event_handlers.keys.include?(event)
164
+ @connection_event_handlers[event].each { |h|
165
+ h.call self
166
+ }
139
167
  end
140
168
 
141
169
  ##################################################################
142
170
 
171
+ # Internal helper, extract client info from connection
172
+ def client_for(connection)
173
+ # skip if an indirect node type or local
174
+ return nil, nil if self.indirect? || self.node_type == :local
175
+
176
+ begin
177
+ return Socket.unpack_sockaddr_in(connection.get_peername)
178
+ rescue Exception=>e
179
+ end
180
+
181
+ return nil, nil
182
+ end
183
+
143
184
  # Internal helper, handle message received
144
185
  def handle_message(msg, connection = {})
145
- if RequestMessage.is_request_message?(msg)
146
- @@tp << ThreadPoolJob.new(msg) { |m| handle_request(m, false, connection) }
186
+ if Messages::Request.is_request_message?(msg)
187
+ tp << ThreadPoolJob.new(msg) { |m| handle_request(m, false, connection) }
147
188
 
148
- elsif NotificationMessage.is_notification_message?(msg)
149
- @@tp << ThreadPoolJob.new(msg) { |m| handle_request(m, true, connection) }
189
+ elsif Messages::Notification.is_notification_message?(msg)
190
+ tp << ThreadPoolJob.new(msg) { |m| handle_request(m, true, connection) }
150
191
 
151
- elsif ResponseMessage.is_response_message?(msg)
192
+ elsif Messages::Response.is_response_message?(msg)
152
193
  handle_response(msg)
153
194
 
154
195
  end
@@ -158,39 +199,32 @@ class Node
158
199
  def handle_request(data, notification=false, connection={})
159
200
  # get client for the specified connection
160
201
  # TODO should grap port/ip immediately on connection and use that
161
- client_port,client_ip = nil,nil
162
- begin
163
- # XXX skip if an 'indirect' node type or local
164
- unless [:amqp, :local].include?(self.class::RJR_NODE_TYPE)
165
- client_port, client_ip =
166
- Socket.unpack_sockaddr_in(connection.get_peername)
167
- end
168
- rescue Exception=>e
169
- end
202
+ client_port,client_ip = client_for(connection)
170
203
 
171
204
  msg = notification ?
172
- NotificationMessage.new(:message => data,
173
- :headers => @message_headers) :
174
- RequestMessage.new(:message => data,
175
- :headers => @message_headers)
176
-
177
- result =
178
- @dispatcher.dispatch(:rjr_method => msg.jr_method,
179
- :rjr_method_args => msg.jr_args,
180
- :rjr_headers => msg.headers,
181
- :rjr_client_ip => client_ip,
182
- :rjr_client_port => client_port,
183
- :rjr_node => self,
184
- :rjr_node_id => @node_id,
185
- :rjr_node_type => self.class::RJR_NODE_TYPE,
186
- :rjr_callback =>
187
- NodeCallback.new(:node => self,
188
- :connection => connection))
205
+ Messages::Notification.new(:message => data,
206
+ :headers => @message_headers) :
207
+ Messages::Request.new(:message => data,
208
+ :headers => @message_headers)
209
+
210
+ callback = NodeCallback.new(:node => self,
211
+ :connection => connection)
212
+
213
+ result = @dispatcher.dispatch(:rjr_method => msg.jr_method,
214
+ :rjr_method_args => msg.jr_args,
215
+ :rjr_headers => msg.headers,
216
+ :rjr_client_ip => client_ip,
217
+ :rjr_client_port => client_port,
218
+ :rjr_node => self,
219
+ :rjr_node_id => node_id,
220
+ :rjr_node_type => self.node_type,
221
+ :rjr_callback => callback)
189
222
 
190
223
  unless notification
191
- response = ResponseMessage.new(:id => msg.msg_id,
192
- :result => result,
193
- :headers => msg.headers)
224
+ response = Messages::Response.new(:id => msg.msg_id,
225
+ :result => result,
226
+ :headers => msg.headers,
227
+ :request => msg)
194
228
  self.send_msg(response.to_s, connection)
195
229
  return response
196
230
  end
@@ -200,7 +234,8 @@ class Node
200
234
 
201
235
  # Internal helper, handle response message received
202
236
  def handle_response(data)
203
- msg = ResponseMessage.new(:message => data, :headers => self.message_headers)
237
+ msg = Messages::Response.new(:message => data,
238
+ :headers => self.message_headers)
204
239
  res = err = nil
205
240
  begin
206
241
  res = @dispatcher.handle_response(msg.result)
@@ -243,37 +278,5 @@ class Node
243
278
  end
244
279
  return res
245
280
  end
246
-
247
281
  end # class Node
248
-
249
- # Node callback interface, used to invoke json-rpc methods
250
- # against a remote node via node connection previously established
251
- #
252
- # After a node sends a json-rpc request to another, the either node may send
253
- # additional requests to each other via the connection already established until
254
- # it is closed on either end
255
- class NodeCallback
256
-
257
- # NodeCallback initializer
258
- # @param [Hash] args the options to create the node callback with
259
- # @option args [node] :node node used to send messages
260
- # @option args [connection] :connection connection to be used in channel selection
261
- def initialize(args = {})
262
- @node = args[:node]
263
- @connection = args[:connection]
264
- end
265
-
266
- def notify(callback_method, *data)
267
- # XXX return if node type does not support
268
- # pesistent conntections (throw err instead?)
269
- return if @node.class::RJR_NODE_TYPE == :web
270
-
271
- msg = NotificationMessage.new :method => callback_method,
272
- :args => data, :headers => @node.message_headers
273
-
274
- # TODO surround w/ begin/rescue block incase of socket errors / raise RJR::ConnectionError
275
- @node.send_msg msg.to_s, @connection
276
- end
277
- end
278
-
279
282
  end # module RJR