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,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