rjr 0.18.2 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
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,147 @@
1
+ require 'rjr/request'
2
+
3
+ module RJR
4
+ describe Request do
5
+ describe "#initialize" do
6
+ it "sets rjr method" do
7
+ request = Request.new :rjr_method => 'foobar'
8
+ request.rjr_method.should == 'foobar'
9
+ end
10
+
11
+ it "sets rjr method args" do
12
+ args = ['a', 'r', 'g']
13
+ request = Request.new :rjr_method_args => args
14
+ request.rjr_method_args.should == args
15
+ end
16
+
17
+ it "sets rjr headers" do
18
+ headers = {:h => :s}
19
+ request = Request.new :rjr_headers => headers
20
+ request.rjr_headers.should == headers
21
+ end
22
+
23
+ it "sets client ip" do
24
+ request = Request.new :rjr_client_ip => '127.0.0.1'
25
+ request.rjr_client_ip.should == '127.0.0.1'
26
+ end
27
+
28
+ it "sets client port" do
29
+ request = Request.new :rjr_client_port => 1234
30
+ request.rjr_client_port.should == 1234
31
+ end
32
+
33
+ it "sets callback" do
34
+ request = Request.new :rjr_callback => :cb
35
+ request.rjr_callback.should == :cb
36
+ end
37
+
38
+ it "sets node" do
39
+ request = Request.new :rjr_node => :node
40
+ request.rjr_node.should == :node
41
+ end
42
+
43
+ it "sets node id" do
44
+ request = Request.new :rjr_node_id => :node_id
45
+ request.rjr_node_id.should == :node_id
46
+ end
47
+
48
+ it "node type" do
49
+ request = Request.new :rjr_node_type => :node_type
50
+ request.rjr_node_type.should == :node_type
51
+ end
52
+
53
+ it "rjr handler" do
54
+ request = Request.new :rjr_handler => :handler
55
+ request.rjr_handler.should == :handler
56
+ end
57
+
58
+ it "initialies new RJR::Arguments object from argument list" do
59
+ args = ['a', 'r', 'g']
60
+ request = Request.new :rjr_method_args => args
61
+ request.rjr_method_args.should == args
62
+ request.rjr_args.should be_an_instance_of(Arguments)
63
+ request.rjr_args.args.should == args
64
+ end
65
+
66
+ it "sets default result to nil" do
67
+ Request.new.result.should be_nil
68
+ end
69
+ end
70
+
71
+ describe "#handle" do
72
+ it "invokes the registered handler with args" do
73
+ received = nil
74
+ handler = proc { |arg| received = arg }
75
+ request = Request.new :rjr_handler => handler,
76
+ :rjr_method_args => ['foo']
77
+ request.handle
78
+ received.should == 'foo'
79
+ end
80
+
81
+ it "invokes registered handler in request contenxt" do
82
+ received = nil
83
+ handler = proc { |arg| received = @var }
84
+
85
+ request = Request.new :rjr_handler => handler
86
+ request.instance_variable_set(:@var, 'foo')
87
+ request.handle
88
+ received.should == 'foo'
89
+ end
90
+
91
+ it "returns the handler return value" do
92
+ handler = proc { |arg| 42 }
93
+ request = Request.new :rjr_handler => handler
94
+ request.handle.should == 42
95
+ end
96
+ end
97
+
98
+ describe "#to_json" do
99
+ it "returns the request in json format" do
100
+ request = Request.new :rjr_method => 'foobar',
101
+ :rjr_method_args => [:a, :b],
102
+ :rjr_headers => { :header1 => :val1 },
103
+ :rjr_node_type => :local,
104
+ :rjr_node_id => :loc1
105
+ j = request.to_json
106
+ j.should include('"json_class":"RJR::Request"')
107
+ j.should include('"rjr_method":"foobar"')
108
+ j.should include('"rjr_method_args":["a","b"]')
109
+ j.should include('"rjr_headers":{"header1":"val1"}')
110
+ j.should include('"rjr_node_type":"local"')
111
+ j.should include('"rjr_node_id":"loc1"')
112
+ end
113
+
114
+ it "includes the result in the json" do
115
+ request = Request.new
116
+ result = RJR::Result.new :result => 42,
117
+ :error_code => 123,
118
+ :error_msg => 'error occurred',
119
+ :error_class => 'ArgumentError'
120
+ request.result = result
121
+
122
+ j = request.to_json
123
+ j.should include('"result":42')
124
+ j.should include('"error_code":123')
125
+ j.should include('"error_msg":"error occurred"')
126
+ j.should include('"error_class":"ArgumentError"')
127
+ end
128
+ end
129
+
130
+ describe "#json_create" do
131
+ it "returns a new request from json" do
132
+ j = '{"json_class":"RJR::Request","data":{"request":{"rjr_method":"foobar","rjr_method_args":["a","b"],"rjr_headers":{"foo":"bar"},"rjr_node_type":"local","rjr_node_id":"loc1"},"result":{"result":42,"error_code":null,"error_msg":null,"error_class":null}}}'
133
+ request = JSON.parse(j, :create_additions => true)
134
+
135
+ request.should be_an_instance_of(RJR::Request)
136
+ request.rjr_method.should == 'foobar'
137
+ request.rjr_method_args.should == ['a', 'b']
138
+ request.rjr_headers.should == { 'foo' => 'bar' }
139
+ request.rjr_node_type.should == 'local'
140
+ request.rjr_node_id.should == 'loc1'
141
+
142
+ request.result.should be_an_instance_of(RJR::Result)
143
+ request.result.result.should == 42
144
+ end
145
+ end
146
+ end # describe Request
147
+ end # module RJR
@@ -0,0 +1,144 @@
1
+ require 'rjr/result'
2
+
3
+ module RJR
4
+ describe Result do
5
+ describe "#initialize" do
6
+ it "initializes default attributes" do
7
+ result = Result.new
8
+ result.result.should be_nil
9
+ result.error_code.should be_nil
10
+ result.error_msg.should be_nil
11
+ result.error_class.should be_nil
12
+ end
13
+
14
+ it "stores result" do
15
+ result = Result.new :result => 'foobar'
16
+ result.result.should == 'foobar'
17
+ end
18
+
19
+ it "stores error" do
20
+ result = Result.new :error_code => 123, :error_msg => 'abc',
21
+ :error_class => ArgumentError
22
+ result.error_code.should == 123
23
+ result.error_msg.should == 'abc'
24
+ result.error_class.should == ArgumentError
25
+ end
26
+
27
+ context "when an error code is not specified" do
28
+ it "should be marked as successful" do
29
+ result = Result.new
30
+ result.success.should == true
31
+ result.failed.should == false
32
+ end
33
+ end
34
+
35
+ context "when an error code is specified" do
36
+ it "should be marked as failed" do
37
+ result = Result.new :error_code => 123
38
+ result.success.should == false
39
+ result.failed.should == true
40
+ end
41
+ end
42
+ end # describe #initialize
43
+
44
+ describe "#==" do
45
+ context "all fields are equal" do
46
+ it "returns true" do
47
+ Result.new.should == Result.new
48
+ Result.new(:result => 1).should == Result.new(:result => 1)
49
+ Result.new(:error_code => 50).should == Result.new(:error_code => 50)
50
+ end
51
+ end
52
+
53
+ context "'success' is different" do
54
+ it "returns false" do
55
+ r1 = Result.new
56
+ r2 = Result.new
57
+ r1.success = true
58
+ r2.success = false
59
+ r1.should_not == r2
60
+ end
61
+ end
62
+
63
+ context "'failed' is different" do
64
+ it "returns false" do
65
+ r1 = Result.new
66
+ r2 = Result.new
67
+ r1.failed = true
68
+ r2.failed = false
69
+ r1.should_not == r2
70
+ end
71
+ end
72
+
73
+ context "'result' is different" do
74
+ it "returns false" do
75
+ r1 = Result.new
76
+ r2 = Result.new
77
+ r1.result = 1
78
+ r2.result = 2
79
+ r1.should_not == r2
80
+ end
81
+ end
82
+
83
+ context "'error_code' is different" do
84
+ it "returns false" do
85
+ r1 = Result.new
86
+ r2 = Result.new
87
+ r1.error_code = 1
88
+ r2.error_code = 2
89
+ r1.should_not == r2
90
+ end
91
+ end
92
+
93
+ context "'error_msg' is different" do
94
+ it "returns false" do
95
+ r1 = Result.new
96
+ r2 = Result.new
97
+ r1.error_msg = 'something'
98
+ r2.error_msg = 'bad'
99
+ r1.should_not == r2
100
+ end
101
+ end
102
+
103
+ context "'error_class' is different" do
104
+ it "returns false" do
105
+ r1 = Result.new
106
+ r2 = Result.new
107
+ r1.error_class = 'something'
108
+ r2.error_class = 'bad'
109
+ r1.should_not == r2
110
+ end
111
+ end
112
+ end # describe #==
113
+
114
+ describe "#invalid_request" do
115
+ it "is an instance of result" do
116
+ Result.invalid_request.should be_an_instance_of(Result)
117
+ end
118
+
119
+ it "has error code -32600" do
120
+ Result.invalid_request.error_code.should == -32600
121
+ end
122
+
123
+ it "has error message 'Invalid Request'" do
124
+ Result.invalid_request.error_msg.should == "Invalid Request"
125
+ end
126
+ end
127
+
128
+ describe "#method_not_found" do
129
+ it "is an instance of result" do
130
+ Result.method_not_found('foobar').should be_an_instance_of(Result)
131
+ end
132
+
133
+ it "has error code -32602" do
134
+ Result.method_not_found('foobar').error_code.should == -32602
135
+ end
136
+
137
+ it "has error message 'Method '<name>' not found'" do
138
+ Result.method_not_found('foobar').error_msg.should ==
139
+ "Method 'foobar' not found"
140
+ end
141
+ end
142
+
143
+ end # describe Result
144
+ end # module RJR
@@ -1,5 +1,5 @@
1
1
  require 'thread'
2
- require 'rjr/thread_pool'
2
+ require 'rjr/util/thread_pool'
3
3
 
4
4
  # TODO ? test ThreadPoolJob being_executed?, completed?, exec, handle_timeout!
5
5
 
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.18.2
4
+ version: 0.19.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: 2014-01-18 00:00:00.000000000 Z
11
+ date: 2014-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -68,17 +68,27 @@ files:
68
68
  - examples/local.rb
69
69
  - examples/complete.rb
70
70
  - examples/amqp.rb
71
+ - examples/structured_server.rb
71
72
  - examples/client.rb
72
73
  - examples/unix.rb
73
- - lib/rjr/semaphore.rb
74
- - lib/rjr/em_adapter.rb
75
- - lib/rjr/errors.rb
76
- - lib/rjr/inspect.rb
74
+ - lib/rjr/node_callback.rb
75
+ - lib/rjr/messages/compressed.rb
76
+ - lib/rjr/messages/response.rb
77
+ - lib/rjr/messages/request.rb
78
+ - lib/rjr/messages/notification.rb
79
+ - lib/rjr/messages.rb
77
80
  - lib/rjr/version.rb
78
81
  - lib/rjr/node.rb
79
82
  - lib/rjr/common.rb
80
- - lib/rjr/thread_pool.rb
81
- - lib/rjr/message.rb
83
+ - lib/rjr/util/logger.rb
84
+ - lib/rjr/util/em_adapter.rb
85
+ - lib/rjr/util/handles_methods.rb
86
+ - lib/rjr/util/inspect.rb
87
+ - lib/rjr/util/has_messages.rb
88
+ - lib/rjr/util/thread_pool.rb
89
+ - lib/rjr/util/json_parser.rb
90
+ - lib/rjr/util/args.rb
91
+ - lib/rjr/request.rb
82
92
  - lib/rjr/nodes/web.rb
83
93
  - lib/rjr/nodes/udp.rb
84
94
  - lib/rjr/nodes/tcp.rb
@@ -94,6 +104,8 @@ files:
94
104
  - lib/rjr/nodes/unix.rb
95
105
  - lib/rjr/nodes/missing.rb
96
106
  - lib/rjr/nodes/multi.rb
107
+ - lib/rjr/core_ext.rb
108
+ - lib/rjr/result.rb
97
109
  - lib/rjr/dispatcher.rb
98
110
  - site/index.html
99
111
  - site/jquery-latest.js
@@ -102,8 +114,18 @@ files:
102
114
  - LICENSE
103
115
  - Rakefile
104
116
  - README.md
117
+ - specs/json_parser_spec.rb
118
+ - specs/messages/response_spec.rb
119
+ - specs/messages/request_spec.rb
120
+ - specs/messages/notification_spec.rb
105
121
  - specs/dispatcher_spec.rb
122
+ - specs/handles_methods_spec.rb
123
+ - specs/has_messages_spec.rb
106
124
  - specs/node_spec.rb
125
+ - specs/args_spec.rb
126
+ - specs/request_spec.rb
127
+ - specs/node_callbacks_spec.rb
128
+ - specs/result_spec.rb
107
129
  - specs/nodes/unix_spec.rb
108
130
  - specs/nodes/multi_spec.rb
109
131
  - specs/nodes/tcp_spec.rb
@@ -114,7 +136,6 @@ files:
114
136
  - specs/nodes/easy_spec.rb
115
137
  - specs/inspect_spec.rb
116
138
  - specs/thread_pool_spec.rb
117
- - specs/message_spec.rb
118
139
  - specs/em_adapter_spec.rb
119
140
  - bin/rjr-server
120
141
  - bin/rjr-client
@@ -148,13 +169,23 @@ requirements:
148
169
  - The eventmachine_httpserver and em-http-request gems are needed to use the web node
149
170
  - The em-websocket and em-websocket-client gems are needed to use the web socket node
150
171
  rubyforge_project:
151
- rubygems_version: 2.0.11
172
+ rubygems_version: 2.0.14
152
173
  signing_key:
153
174
  specification_version: 4
154
175
  summary: JSON RPC server and client library over amqp, websockets, http, etc
155
176
  test_files:
177
+ - specs/json_parser_spec.rb
178
+ - specs/messages/response_spec.rb
179
+ - specs/messages/request_spec.rb
180
+ - specs/messages/notification_spec.rb
156
181
  - specs/dispatcher_spec.rb
182
+ - specs/handles_methods_spec.rb
183
+ - specs/has_messages_spec.rb
157
184
  - specs/node_spec.rb
185
+ - specs/args_spec.rb
186
+ - specs/request_spec.rb
187
+ - specs/node_callbacks_spec.rb
188
+ - specs/result_spec.rb
158
189
  - specs/nodes/unix_spec.rb
159
190
  - specs/nodes/multi_spec.rb
160
191
  - specs/nodes/tcp_spec.rb
@@ -165,6 +196,5 @@ test_files:
165
196
  - specs/nodes/easy_spec.rb
166
197
  - specs/inspect_spec.rb
167
198
  - specs/thread_pool_spec.rb
168
- - specs/message_spec.rb
169
199
  - specs/em_adapter_spec.rb
170
200
  has_rdoc:
@@ -1,23 +0,0 @@
1
- # RJR Errors
2
- #
3
- # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the Apache License, Version 2.0
5
-
6
- module RJR
7
-
8
- # The RJR::Errors module provides a mechanism to dynamically create errors
9
- # on demand as they are needed. At some point this will go away / be replaced
10
- # with a more rigidly / fixed defined error heirarchy.
11
- module Errors
12
-
13
- # Catches all Errors constants and define new RuntimeError subclass
14
- def self.const_missing(error_name) # :nodoc:
15
- if error_name.to_s =~ /Error\z/
16
- const_set(error_name, Class.new(RuntimeError))
17
- else
18
- super
19
- end
20
- end
21
-
22
- end
23
- end
@@ -1,351 +0,0 @@
1
- # RJR Message
2
- #
3
- # Representations of json-rpc messages in accordance with the standard
4
- #
5
- # Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
6
- # Licensed under the Apache License, Version 2.0
7
-
8
- require 'json'
9
- require 'rjr/common'
10
-
11
- module RJR
12
-
13
- # Message sent from client to server to invoke a json-rpc method
14
- class RequestMessage
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
- # ID of the message in accordance w/ json-rpc specification
25
- attr_accessor :msg_id
26
-
27
- # Optional headers to add to json outside of standard json-rpc request
28
- attr_accessor :headers
29
-
30
- # RJR Request Message initializer
31
- #
32
- # This should be invoked with one of two argument sets. If creating a new message
33
- # to send to the server, specify :method, :args, and :headers to include in the message
34
- # (message id will be autogenerated). If handling an new request message sent from the
35
- # client, simply specify :message and optionally any additional headers (they will
36
- # be merged with the headers contained in the message)
37
- #
38
- # @param [Hash] args options to set on request
39
- # @option args [String] :message json string received from sender
40
- # @option args [Hash] :headers optional headers to set in request and subsequent messages
41
- # @option args [String] :method method to invoke on server
42
- # @option args [Array<Object>] :args to pass to server method, all must be convertable to/from json
43
- def initialize(args = {})
44
- if args.has_key?(:message)
45
- begin
46
- @json_message = args[:message]
47
- request = RJR.parse_json(@json_message)
48
- @jr_method = request['method']
49
- @jr_args = request['params']
50
- @msg_id = request['id']
51
- @headers = args.has_key?(:headers) ? {}.merge!(args[:headers]) : {}
52
-
53
- request.keys.select { |k|
54
- !['jsonrpc', 'id', 'method', 'params'].include?(k)
55
- }.each { |k| @headers[k] = request[k] }
56
-
57
- rescue Exception => e
58
- #puts "Exception Parsing Request #{e}"
59
- raise e
60
- end
61
-
62
- elsif args.has_key?(:method)
63
- @jr_method = args[:method]
64
- @jr_args = args[:args]
65
- @headers = args[:headers]
66
- @msg_id = gen_uuid
67
-
68
- end
69
- end
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 = RJR.parse_json(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 string json format
86
- def to_s
87
- request = { 'jsonrpc' => '2.0',
88
- 'method' => @jr_method,
89
- 'params' => @jr_args }
90
- request['id'] = @msg_id unless @msg_id.nil?
91
- request.merge!(@headers) unless @headers.nil?
92
- request.to_json.to_s
93
- end
94
-
95
- end
96
-
97
- # Message sent from server to client in response to json-rpc request message
98
- class ResponseMessage
99
- # Message string received from the source
100
- attr_accessor :json_message
101
-
102
- # ID of the message in accordance w/ json-rpc specification
103
- attr_accessor :msg_id
104
-
105
- # Result encapsulated in the response message
106
- # @see RJR::Result
107
- attr_accessor :result
108
-
109
- # Optional headers to add to json outside of standard json-rpc request
110
- attr_accessor :headers
111
-
112
- # ResponseMessage initializer
113
- #
114
- # This should be invoked with one of two argument sets. If creating a new message
115
- # to send to the client, specify :id, :result, and :headers to include in the message.
116
- # If handling an new request message sent from the client, simply specify :message
117
- # and optionally any additional headers (they will be merged with the headers contained
118
- # in the message)
119
- #
120
- # @param [Hash] args options to set on request
121
- # @option args [String] :message json string received from sender
122
- # @option args [Hash] :headers optional headers to set in request and subsequent messages
123
- # @option args [String] :id id to set in response message, should be same as that in received message
124
- # @option args [RJR::Result] :result result of json-rpc method invocation
125
- def initialize(args = {})
126
- if args.has_key?(:message)
127
- @json_message = args[:message]
128
- response = RJR.parse_json(@json_message)
129
- @msg_id = response['id']
130
- @result = Result.new
131
- @result.success = response.has_key?('result')
132
- @result.failed = !response.has_key?('result')
133
- @headers = args.has_key?(:headers) ? {}.merge!(args[:headers]) : {}
134
-
135
- if @result.success
136
- @result.result = response['result']
137
-
138
- elsif response.has_key?('error')
139
- @result.error_code = response['error']['code']
140
- @result.error_msg = response['error']['message']
141
- @result.error_class = response['error']['class'] # TODO safely constantize this ?
142
-
143
- end
144
-
145
- response.keys.select { |k|
146
- !['jsonrpc', 'id', 'result', 'error'].include?(k)
147
- }.each { |k| @headers[k] = response[k] }
148
-
149
- elsif args.has_key?(:result)
150
- @msg_id = args[:id]
151
- @result = args[:result]
152
- @headers = args[:headers]
153
-
154
- #else
155
- # raise ArgumentError, "must specify :message or :result"
156
-
157
- end
158
-
159
- end
160
-
161
- # Class helper to determine if the specified string is a valid json-rpc
162
- # method response
163
- # @param [String] message string message to check
164
- # @return [true,false] indicating if message is response message
165
- def self.is_response_message?(message)
166
- begin
167
- json = RJR.parse_json(message)
168
- json.has_key?('result') || json.has_key?('error')
169
- rescue Exception => e
170
- # FIXME log error
171
- puts e.to_s
172
- false
173
- end
174
- end
175
-
176
- # Convert request message to string json format
177
- def to_s
178
- s = ''
179
- if result.success
180
- s = {'jsonrpc' => '2.0',
181
- 'id' => @msg_id,
182
- 'result' => @result.result}
183
-
184
- else
185
- s = {'jsonrpc' => '2.0',
186
- 'id' => @msg_id,
187
- 'error' => { 'code' => @result.error_code,
188
- 'message' => @result.error_msg,
189
- 'class' => @result.error_class}}
190
- end
191
-
192
- s.merge! @headers unless headers.nil?
193
- return s.to_json.to_s
194
- end
195
- end
196
-
197
- # Message sent to a jsonrpc node to invoke a rpc method but
198
- # indicate the result should _not_ be returned
199
- class NotificationMessage
200
- # Message string received from the source
201
- attr_accessor :json_message
202
-
203
- # Method source is invoking on the destination
204
- attr_accessor :jr_method
205
-
206
- # Arguments source is passing to destination method
207
- attr_accessor :jr_args
208
-
209
- # Optional headers to add to json outside of standard json-rpc request
210
- attr_accessor :headers
211
-
212
- # RJR Notification Message initializer
213
- #
214
- # This should be invoked with one of two argument sets. If creating a new message
215
- # to send to the server, specify :method, :args, and :headers to include in the message
216
- # If handling an new request message sent from the client, simply specify :message and
217
- # optionally any additional headers (they will be merged with the headers contained in
218
- # the message)
219
- #
220
- # No message id will be generated in accordance w/ the jsonrpc standard
221
- #
222
- # @param [Hash] args options to set on request
223
- # @option args [String] :message json string received from sender
224
- # @option args [Hash] :headers optional headers to set in request and subsequent messages
225
- # @option args [String] :method method to invoke on server
226
- # @option args [Array<Object>] :args to pass to server method, all must be convertable to/from json
227
- def initialize(args = {})
228
- if args.has_key?(:message)
229
- begin
230
- @json_message = args[:message]
231
- notification = RJR.parse_json(@json_message)
232
- @jr_method = notification['method']
233
- @jr_args = notification['params']
234
- @headers = args.has_key?(:headers) ? {}.merge!(args[:headers]) : {}
235
-
236
- notification.keys.select { |k|
237
- !['jsonrpc', 'method', 'params'].include?(k)
238
- }.each { |k| @headers[k] = notification[k] }
239
-
240
- rescue Exception => e
241
- #puts "Exception Parsing Notification #{e}"
242
- raise e
243
- end
244
-
245
- elsif args.has_key?(:method)
246
- @jr_method = args[:method]
247
- @jr_args = args[:args]
248
- @headers = args[:headers]
249
-
250
- end
251
- end
252
-
253
- # Class helper to determine if the specified string is a valid json-rpc
254
- # notification
255
- #
256
- # @param [String] message string message to check
257
- # @return [true,false] indicating if message is a notification message
258
- def self.is_notification_message?(message)
259
- begin
260
- # FIXME log error
261
- parsed = RJR.parse_json(message)
262
- parsed.has_key?('method') && !parsed.has_key?('id')
263
- rescue Exception => e
264
- false
265
- end
266
- end
267
-
268
- # Convert notification message to string json format
269
- def to_s
270
- notification = { 'jsonrpc' => '2.0',
271
- 'method' => @jr_method,
272
- 'params' => @jr_args }
273
- notification.merge!(@headers) unless @headers.nil?
274
- notification.to_json.to_s
275
- end
276
-
277
- end
278
-
279
- # Helper utilities for messages
280
- class MessageUtil
281
- # Retrieve and return a single json message from a data string.
282
- #
283
- # Returns the message and remaining portion of the data string,
284
- # if message is found, else nil
285
- #
286
- # XXX really don't like having to do this, but a quick solution
287
- # to the issue of multiple messages appearing in one tcp data packet.
288
- #
289
- # TODO efficiency can probably be optimized
290
- # in the case closing '}' hasn't arrived yet
291
- def self.retrieve_json(data)
292
- return nil if data.nil? || data.empty?
293
- start = 0
294
- start += 1 until start == data.length || data[start].chr == '{'
295
- on = mi = 0
296
- start.upto(data.length - 1).each { |i|
297
- if data[i].chr == '{'
298
- on += 1
299
- elsif data[i].chr == '}'
300
- on -= 1
301
- end
302
-
303
- if on == 0
304
- mi = i
305
- break
306
- end
307
- }
308
-
309
- return nil if mi == 0
310
- return data[start..mi], data[(mi+1)..-1]
311
- end
312
-
313
- # Mechanism to register / retrieve preformatted message
314
- #
315
- # @param [Symbol] id id of message to get / set
316
- # @param [String] msg optional preformatted message to store
317
- # @return [String] json rpc message
318
- def self.message(id, msg=nil)
319
- @rjr_messages ||= {}
320
- @rjr_messages[id] = msg unless msg.nil?
321
- @rjr_messages[id]
322
- end
323
-
324
- # Clear preformatted messages
325
- def self.clear
326
- @rjr_messages = {}
327
- end
328
-
329
- # Return random message from registry.
330
- #
331
- # Optionally specify the transport which the message must accept
332
- # (TODO turn this into a generic selection callback)
333
- def self.rand_msg(transport = nil)
334
- @rjr_messages ||= {}
335
- messages = @rjr_messages.select { |mid,m| m[:transports].nil? || transport.nil? ||
336
- m[:transports].include?(transport) }
337
- messages[messages.keys[rand(messages.keys.size)]]
338
- end
339
-
340
- end # MessageUtil
341
-
342
- # Module providing helper methods for messages
343
- module MessageMixins
344
-
345
- # Wrapper around MessageUtil.message
346
- def define_message(name, &bl)
347
- MessageUtil.message(name, bl.call)
348
- end
349
- end
350
-
351
- end