rjr 0.17.1 → 0.18.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: df6830242efa865dde4af4008827688172011746
4
- data.tar.gz: 33c559f6fa14957b7aa5929f3030ab4455e93b8e
3
+ metadata.gz: f5a404d0bde0749357c01f0e57696e73ff39ea6d
4
+ data.tar.gz: 0c5394cf7df7774def9553b772c1cb92c53504de
5
5
  SHA512:
6
- metadata.gz: 240533c8a5aafa5980337b18fae10bf4f24a9e72e93ce6e88f42c82169ee81fbb5f850a5817a6ed1091f9ed62e96145e3b478f67b2122c15c39dbf13a29ff131
7
- data.tar.gz: 56ef3f372c94eead71df0d5b534122a244c3602b4aab84c3c181d40711066c8ab7da303f3cb8f676531d6150e0f1c6be0c2836320bbda749a53f3451cdc7f5f0
6
+ metadata.gz: cf24b7e0fc705957af5ffe74879ab053a78393f33b28070cde205cb04a3879282d492dec85efd29938a6f51c27bee5a1ec3b6b6294e683d6bed7ed1cc0dfb1bb
7
+ data.tar.gz: ed2bf6a3203387e00aeb5c84f0d836c50833ddd4901c581085ae66c8b286cddbba356ea0023bf8cbd6221c5b693a5508675ad9697f70023cdc319dd6f1f58335
data/examples/unix.rb ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/ruby
2
+ # A RJR unix-node example
3
+ #
4
+ # Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
5
+ # Licensed under the Apache License, Version 2.0
6
+
7
+ require 'rjr/nodes/unix'
8
+
9
+ server = RJR::Nodes::Unix.new :socketname => './server.sock', :node_id => "server"
10
+ server.dispatcher.handle('method') { |i|
11
+ puts "server: #{i}"
12
+ @rjr_callback.notify "callback", i.downcase
13
+ "#{i}".upcase
14
+ }
15
+ server.listen
16
+
17
+ client = RJR::Nodes::Unix.new :node_id => "client"
18
+ client.dispatcher.handle('callback') { |i|
19
+ puts "callback: #{i}"
20
+ #client.halt
21
+ }
22
+
23
+ client.notify "./server.sock", "method", "Hello World"
24
+ # => nil
25
+
26
+ client.invoke "./server.sock", "method", "Hello World"
27
+ # => HELLO WORLD
28
+
29
+ #client.join
@@ -0,0 +1 @@
1
+ # TODO read/write jsonrpc from a watched file (eg EM's inotify interface)
data/lib/rjr/nodes/tcp.rb CHANGED
@@ -16,7 +16,7 @@ module RJR
16
16
  module Nodes
17
17
 
18
18
  # @private
19
- # Helper class intialized by eventmachine encapsulating a socket connection
19
+ # Helper class intialized by eventmachine encapsulating a tcp socket connection
20
20
  class TCPConnection < EventMachine::Connection
21
21
  attr_reader :host
22
22
  attr_reader :port
@@ -209,7 +209,7 @@ class TCP < RJR::Node
209
209
  #sleep 0.01 until conn.get_outbound_data_size == 0
210
210
  nil
211
211
  end
212
- end
212
+ end # class TCP
213
213
 
214
214
  end # module Nodes
215
215
  end # module RJR
@@ -0,0 +1,66 @@
1
+ # RJR Template Node
2
+ #
3
+ # Just serves as a minimal example of a node, should
4
+ # not be used. Developers can copy / base additional
5
+ # transport types off this template
6
+ #
7
+ # Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
8
+ # Licensed under the Apache License, Version 2.0
9
+
10
+ require 'rjr/node'
11
+
12
+ module RJR
13
+ module Nodes
14
+
15
+ class Template < RJR::Node
16
+ RJR_NODE_TYPE = :tpl
17
+
18
+ # Template Node Initializer
19
+ def initialize(args = {})
20
+ super(args)
21
+ end
22
+
23
+ def to_s
24
+ "RJR::Nodes::Template<>"
25
+ end
26
+
27
+ # Send data using specified connection
28
+ #
29
+ # Implementation of {RJR::Node#send_msg}
30
+ def send_msg(data, connection)
31
+ # TODO
32
+ end
33
+
34
+ # Instruct Node to start listening for and dispatching rpc requests
35
+ #
36
+ # Implementation of {RJR::Node#listen}
37
+ def listen
38
+ # TODO
39
+ self
40
+ end
41
+
42
+ # Instructs node to send rpc request, and wait for / return response.
43
+ #
44
+ # Implementation of {RJR::Node#invoke}
45
+ # @param [String] optional_destination if the transport requires it, param
46
+ # to specify the target of this request, if not remove this param
47
+ # @param [String] rpc_method json-rpc method to invoke on destination
48
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
49
+ def invoke(optional_destination, rpc_method, *args)
50
+ # TODO
51
+ end
52
+
53
+ # Instructs node to send rpc notification (immadiately returns / no response is generated)
54
+ #
55
+ # Implementation of {RJR::Node#notify}
56
+ # @param [String] optional_destination if the transport requires it, param
57
+ # to specify the target of this request, if not remove this param
58
+ # @param [String] rpc_method json-rpc method to invoke on destination
59
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
60
+ def notify(optional_destination, rpc_method, *args)
61
+ # TODO
62
+ end
63
+ end # class Template
64
+
65
+ end # module Nodes
66
+ end # module RJR
@@ -0,0 +1,192 @@
1
+ # RJR Unix Socket Node
2
+ #
3
+ # Implements the RJR::Node interface to issue and satisty JSON-RPC requests
4
+ # via Unix Sockets
5
+ #
6
+ # Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
7
+ # Licensed under the Apache License, Version 2.0
8
+
9
+ # TODO extract common bits between here &
10
+ # tcp node into common base socked module
11
+
12
+ require 'thread'
13
+ require 'eventmachine'
14
+
15
+ require 'rjr/node'
16
+ require 'rjr/message'
17
+
18
+ module RJR
19
+ module Nodes
20
+
21
+ # @private
22
+ # Helper class intialized by eventmachine encapsulating a unix socket connection
23
+ class UnixConnection < EventMachine::Connection
24
+ attr_reader :socketname
25
+
26
+ # UnixConnection intializer
27
+ #
28
+ # Specify the Unix Node establishing the connection and
29
+ # optionaly socketname which this connection is connected to
30
+ def initialize(args = {})
31
+ @rjr_node = args[:rjr_node]
32
+ @socketname = args[:socketname]
33
+
34
+ @send_lock = Mutex.new
35
+ @data = ""
36
+ end
37
+
38
+ # {EventMachine::Connection#receive_data} callback, handle request / response messages
39
+ def receive_data(data)
40
+ # a large json-rpc message may be split over multiple packets
41
+ # (invocations of receive_data)
42
+ # and multiple messages may be concatinated into one packet
43
+ @data += data
44
+ while extracted = MessageUtil.retrieve_json(@data)
45
+ msg, @data = *extracted
46
+ @rjr_node.send(:handle_message, msg, self) # XXX private method
47
+ end
48
+ end
49
+
50
+ # Send data safely using local connection
51
+ def send_msg(data)
52
+ @send_lock.synchronize{
53
+ Unix.em.schedule { send_data(data) }
54
+ }
55
+ end
56
+ end
57
+
58
+ # Unix node definition, listen for and invoke json-rpc requests via Unix Sockets
59
+ #
60
+ # Clients should specify the socketname when listening for requests and
61
+ # when invoking them.
62
+ #
63
+ # TODO client / server examples
64
+ #
65
+ class Unix < RJR::Node
66
+ RJR_NODE_TYPE = :unix
67
+
68
+ attr_accessor :connections
69
+
70
+ private
71
+ # Internal helper, initialize new client
72
+ def init_client(args={}, &on_init)
73
+ socketname = args[:socketname]
74
+ connection = nil
75
+ @connections_lock.synchronize {
76
+ connection = @connections.find { |c|
77
+ socketname == c.socketname
78
+ }
79
+ if connection.nil?
80
+ connection =
81
+ EventMachine::connect_unix_domain socketname,
82
+ nil, UnixConnection, args
83
+ @connections << connection
84
+ end
85
+ }
86
+ on_init.call(connection) # TODO move to unixnode event ?
87
+ end
88
+
89
+ public
90
+
91
+ # Unix initializer
92
+ # @param [Hash] args the options to create the unix node with
93
+ # @option args [String] :socketname the name of the socket which to listen on
94
+ def initialize(args = {})
95
+ super(args)
96
+ @socketname = args[:socketname]
97
+
98
+ @connections = []
99
+ @connections_lock = Mutex.new
100
+ end
101
+
102
+ def to_s
103
+ "RJR::Nodes::Unix<#{@node_id},#{@socketname}>"
104
+ end
105
+
106
+ # Send data using specified connection
107
+ #
108
+ # Implementation of {RJR::Node#send_msg}
109
+ def send_msg(data, connection)
110
+ connection.send_msg(data)
111
+ end
112
+
113
+ # Instruct Node to start listening for and dispatching rpc requests
114
+ #
115
+ # Implementation of {RJR::Node#listen}
116
+ def listen
117
+ @@em.schedule {
118
+ @@em.start_unix_domain_server @socketname, nil, UnixConnection, { :rjr_node => self }
119
+ }
120
+ self
121
+ end
122
+
123
+ # Instructs node to send rpc request, and wait for / return response.
124
+ #
125
+ # Implementation of {RJR::Node#invoke}
126
+ #
127
+ # Do not invoke directly from em event loop or callback as will block the message
128
+ # subscription used to receive responses
129
+ #
130
+ # @param [String] socketname name of socket which destination node is
131
+ # listening on
132
+ # @param [String] rpc_method json-rpc method to invoke on destination
133
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
134
+ def invoke(socketname, rpc_method, *args)
135
+ message = RequestMessage.new :method => rpc_method,
136
+ :args => args,
137
+ :headers => @message_headers
138
+ connection = nil
139
+ @@em.schedule {
140
+ init_client(:socketname => socketname,
141
+ :rjr_node => self) { |c|
142
+ connection = c
143
+ c.send_msg message.to_s
144
+ }
145
+ }
146
+
147
+ # TODO optional timeout for response ?
148
+ result = wait_for_result(message)
149
+
150
+ if result.size > 2
151
+ raise Exception, result[2]
152
+ end
153
+ return result[1]
154
+ end
155
+
156
+ # Instructs node to send rpc notification (immadiately returns / no response is generated)
157
+ #
158
+ # Implementation of {RJR::Node#notify}
159
+ #
160
+ # @param [String] socketname name of socket which
161
+ # destination node is listening on
162
+ # @param [String] rpc_method json-rpc method to invoke on destination
163
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
164
+ def notify(socketname, rpc_method, *args)
165
+ # will block until message is published
166
+ published_l = Mutex.new
167
+ published_c = ConditionVariable.new
168
+
169
+ invoked = false
170
+ conn = nil
171
+ message = NotificationMessage.new :method => rpc_method,
172
+ :args => args,
173
+ :headers => @message_headers
174
+ @@em.schedule {
175
+ init_client(:socketname => socketname,
176
+ :rjr_node => self) { |c|
177
+ conn = c
178
+ c.send_msg message.to_s
179
+ # XXX, this should be invoked only when we are sure event
180
+ # machine sent message. Shouldn't pose a problem unless event
181
+ # machine is killed immediately after
182
+ published_l.synchronize { invoked = true ; published_c.signal }
183
+ }
184
+ }
185
+ published_l.synchronize { published_c.wait published_l unless invoked }
186
+ #sleep 0.01 until conn.get_outbound_data_size == 0
187
+ nil
188
+ end
189
+ end # class Unix
190
+
191
+ end # module Nodes
192
+ end # module RJR
data/lib/rjr/nodes/web.rb CHANGED
@@ -8,6 +8,8 @@
8
8
  # Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
9
9
  # Licensed under the Apache License, Version 2.0
10
10
 
11
+ # TODO rename to HTTP
12
+
11
13
  skip_module = false
12
14
  begin
13
15
  require 'evma_httpserver'
@@ -0,0 +1 @@
1
+ # TODO
data/lib/rjr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RJR
2
- VERSION = '0.17.1'
2
+ VERSION = '0.18.1'
3
3
  end
data/site/jrw.js CHANGED
@@ -65,19 +65,80 @@ RJR.JRMessage.convert_params = function(params){
65
65
  for(var p = 0; p < params.length; p++){
66
66
  var param = params[p];
67
67
  if(RJR.is_jr_object(param)){
68
- // FIXME need to check each param property as well (converting if necessary)
69
- var json_class = param['json_class'];
70
- delete param['json_class'];
71
- jrparams.push({json_class: json_class,
72
- data : param});
73
- }else if(RJR.is_array(param))
68
+ jrparams.push(RJR.JRMessage.convert_obj_to_jr_obj(param));
69
+ }else if(RJR.is_array(param)){
70
+ /// TODO s/convert_params/convert_array_to_jr_array here ?
71
+ /// (or could the whole convert_params method could be
72
+ /// consolidated with that one ?)
74
73
  jrparams.push(RJR.JRMessage.convert_params(param));
75
- else
74
+ }else
76
75
  jrparams.push(param);
77
76
  }
78
77
  return jrparams;
79
78
  }
80
79
 
80
+ // Convert single json object to jr object
81
+ RJR.JRMessage.convert_obj_to_jr_obj= function(obj){
82
+ var jr_obj = $.extend(true, {}, obj)
83
+ var json_class = jr_obj['json_class'];
84
+ delete jr_obj['json_class'];
85
+
86
+ /// iterate over object properties
87
+ for(var p in jr_obj){
88
+ if(RJR.is_jr_object(jr_obj[p]))
89
+ jr_obj[p] = RJR.JRMessage.convert_obj_to_jr_obj(jr_obj[p]);
90
+ else if(RJR.is_array(jr_obj[p]))
91
+ jr_obj[p] = RJR.JRMessage.convert_array_to_jr_array(jr_obj[p]);
92
+ }
93
+
94
+ return {json_class: json_class,
95
+ data : jr_obj};
96
+ }
97
+
98
+ // Convert single json object from jr object
99
+ RJR.JRMessage.convert_obj_from_jr_obj = function(jr_obj){
100
+ var obj = $.extend(true, {}, jr_obj)
101
+ if(obj.data){
102
+ $.extend(obj, obj.data);
103
+ delete obj['data'];
104
+ }
105
+
106
+ for(var p in obj){
107
+ if(RJR.is_jr_object(obj[p]))
108
+ obj[p] = RJR.JRMessage.convert_obj_from_jr_obj(obj[p]);
109
+ else if(RJR.is_array(obj[p]))
110
+ obj[p] = RJR.JRMessage.convert_array_from_jr_array(obj[p]);
111
+ }
112
+
113
+ return obj;
114
+ }
115
+
116
+ // Recursively convert array to jr object array
117
+ RJR.JRMessage.convert_array_to_jr_array = function(array){
118
+ var jr_array = $.extend(true, [], array)
119
+ for(var a = 0; a < jr_array.length; a++){
120
+ if(RJR.is_jr_object(jr_array[a]))
121
+ jr_array[a] = RJR.JRMessage.convert_obj_to_jr_obj(jr_array[a]);
122
+ else if(RJR.is_array(jr_array[a]))
123
+ jr_array[a] = RJR.JRMesage.convert_array_to_jr_array(jr_array[a]);
124
+ }
125
+
126
+ return jr_array;
127
+ }
128
+
129
+ // Convert json object array from jr object array
130
+ RJR.JRMessage.convert_array_from_jr_array = function(jr_array){
131
+ var array = $.extend(true, [], jr_array)
132
+ for(var a = 0; a < array.length; a++){
133
+ if(RJR.is_jr_object(array[a]))
134
+ array[a] = RJR.JRMessage.convert_obj_from_jr_obj(array[a]);
135
+ else if(RJR.is_array(jr_array[a]))
136
+ array[a] = RJR.JRMessage.convert_array_from_jr_array(array[a]);
137
+ }
138
+
139
+ return array;
140
+ }
141
+
81
142
  // Parse a json string to a JRMessage
82
143
  RJR.JRMessage.parse = function(json){
83
144
  var data = $.evalJSON(json);
@@ -0,0 +1,71 @@
1
+ require 'rjr/nodes/unix'
2
+
3
+ module RJR::Nodes
4
+ describe Unix do
5
+ describe "#send_msg" do
6
+ it "should send message using the specifed connection"
7
+ end
8
+
9
+ describe "#listen" do
10
+ it "should listen for messages" do
11
+ rn = rni = rnt = p = invoked = nil
12
+ server = Unix.new :node_id => 'unix',
13
+ :socketname => './test.sock'
14
+ server.dispatcher.handle('foobar') { |param|
15
+ rn = @rjr_node
16
+ rni = @rjr_node_id
17
+ rnt = @rjr_node_type
18
+ p = param
19
+ invoked = true
20
+ }
21
+ server.listen
22
+
23
+ # issue request
24
+ Unix.new.invoke './test.sock', 'foobar', 'myparam'
25
+ server.halt.join
26
+ rn.should == server
27
+ rni.should == 'unix'
28
+ rnt.should == :unix
29
+ p.should == 'myparam'
30
+ invoked.should == true
31
+ end
32
+ end
33
+
34
+ describe "#invoke" do
35
+ it "should invoke request" do
36
+ server = Unix.new :node_id => 'unix',
37
+ :socketname => './test.sock'
38
+ server.dispatcher.handle('foobar') { |param|
39
+ 'retval'
40
+ }
41
+ server.listen
42
+
43
+ client = Unix.new
44
+ res = client.invoke './test.sock', 'foobar', 'myparam'
45
+ server.halt.join
46
+ res.should == 'retval'
47
+ end
48
+ end
49
+
50
+ describe "#notify" do
51
+ it "should send notification" do
52
+ server = Unix.new :node_id => 'unix',
53
+ :socketname => './test.sock'
54
+ server.dispatcher.handle('foobar') { |param|
55
+ 'retval'
56
+ }
57
+ server.listen
58
+
59
+ client = Unix.new
60
+ res = client.notify './test.sock', 'foobar', 'myparam'
61
+ server.halt.join
62
+ res.should == nil
63
+ end
64
+ end
65
+
66
+ # TODO test callbacks over unix interface
67
+ # TODO ensure closed / error event handlers are invoked
68
+ end
69
+ end
70
+
71
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rjr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.1
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mo Morsi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-17 00:00:00.000000000 Z
11
+ date: 2013-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -69,6 +69,7 @@ files:
69
69
  - examples/complete.rb
70
70
  - examples/amqp.rb
71
71
  - examples/client.rb
72
+ - examples/unix.rb
72
73
  - lib/rjr/semaphore.rb
73
74
  - lib/rjr/em_adapter.rb
74
75
  - lib/rjr/errors.rb
@@ -86,7 +87,11 @@ files:
86
87
  - lib/rjr/nodes/tcp2.rb
87
88
  - lib/rjr/nodes/local.rb
88
89
  - lib/rjr/nodes/amqp.rb
90
+ - lib/rjr/nodes/xmpp.rb
91
+ - lib/rjr/nodes/file.rb
92
+ - lib/rjr/nodes/template.rb
89
93
  - lib/rjr/nodes/easy.rb
94
+ - lib/rjr/nodes/unix.rb
90
95
  - lib/rjr/nodes/missing.rb
91
96
  - lib/rjr/nodes/multi.rb
92
97
  - lib/rjr/dispatcher.rb
@@ -99,6 +104,7 @@ files:
99
104
  - README.md
100
105
  - specs/dispatcher_spec.rb
101
106
  - specs/node_spec.rb
107
+ - specs/nodes/unix_spec.rb
102
108
  - specs/nodes/multi_spec.rb
103
109
  - specs/nodes/tcp_spec.rb
104
110
  - specs/nodes/web_spec.rb
@@ -149,6 +155,7 @@ summary: JSON RPC server and client library over amqp, websockets, http, etc
149
155
  test_files:
150
156
  - specs/dispatcher_spec.rb
151
157
  - specs/node_spec.rb
158
+ - specs/nodes/unix_spec.rb
152
159
  - specs/nodes/multi_spec.rb
153
160
  - specs/nodes/tcp_spec.rb
154
161
  - specs/nodes/web_spec.rb