jimson 0.3.1 → 0.4.0
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.
- data/README.md +21 -0
- data/VERSION +1 -1
- data/lib/jimson.rb +1 -0
- data/lib/jimson/client.rb +30 -25
- data/lib/jimson/client/error.rb +23 -0
- data/lib/jimson/handler.rb +25 -0
- data/lib/jimson/server.rb +64 -22
- data/lib/jimson/server/error.rb +64 -0
- data/spec/client_spec.rb +50 -0
- data/spec/handler_spec.rb +62 -0
- data/spec/server_spec.rb +134 -19
- data/spec/spec_helper.rb +0 -12
- metadata +26 -14
- data/README.rdoc +0 -0
- data/lib/jimson/client_error.rb +0 -31
- data/lib/jimson/server_error.rb +0 -64
- data/spec/server_helper.rb +0 -34
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Jimson
|
2
|
+
### JSON-RPC 2.0 Client and Server for Ruby
|
3
|
+
|
4
|
+
## Client: Quick Start
|
5
|
+
require 'jimson'
|
6
|
+
client = Jimson::Client.new("http://www.example.com:8999") # the URL for the JSON-RPC 2.0 server to connect to
|
7
|
+
result = client.sum(1,2) # call the 'sum' method on the RPC server and save the result '3'
|
8
|
+
|
9
|
+
## Server: Quick Start
|
10
|
+
require 'jimson'
|
11
|
+
|
12
|
+
class MyHandler
|
13
|
+
extend Jimson::Handler
|
14
|
+
|
15
|
+
def sum(a,b)
|
16
|
+
a + b
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
server = Jimson::Server.new(MyHandler.new)
|
21
|
+
server.start # serve with webrick on http://0.0.0.0:8999/
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/jimson.rb
CHANGED
data/lib/jimson/client.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
+
require 'blankslate'
|
2
|
+
module Jimson
|
3
|
+
class Client < BlankSlate
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
1
7
|
require 'rest-client'
|
2
|
-
require 'jimson/
|
3
|
-
require 'jimson/client_error'
|
8
|
+
require 'jimson/client/error'
|
4
9
|
require 'jimson/request'
|
5
10
|
require 'jimson/response'
|
6
11
|
|
@@ -24,10 +29,14 @@ module Jimson
|
|
24
29
|
begin
|
25
30
|
data = JSON.parse(resp)
|
26
31
|
rescue
|
27
|
-
raise
|
32
|
+
raise Client::Error::InvalidJSON.new(resp)
|
28
33
|
end
|
29
34
|
|
30
35
|
return process_single_response(data)
|
36
|
+
|
37
|
+
rescue Exception, StandardError => e
|
38
|
+
e.extend(Client::Error) unless e.is_a?(Client::Error)
|
39
|
+
raise e
|
31
40
|
end
|
32
41
|
|
33
42
|
def send_single_request(method, args)
|
@@ -39,20 +48,17 @@ module Jimson
|
|
39
48
|
}.to_json
|
40
49
|
resp = RestClient.post(@url, post_data, :content_type => 'application/json')
|
41
50
|
if resp.nil? || resp.body.nil? || resp.body.empty?
|
42
|
-
raise
|
51
|
+
raise Client::Error::InvalidResponse.new
|
43
52
|
end
|
44
53
|
|
45
54
|
return resp.body
|
46
|
-
|
47
|
-
rescue Exception, StandardError
|
48
|
-
raise Jimson::ClientError::InternalError.new($!)
|
49
55
|
end
|
50
56
|
|
51
57
|
def send_batch_request(batch)
|
52
58
|
post_data = batch.to_json
|
53
59
|
resp = RestClient.post(@url, post_data, :content_type => 'application/json')
|
54
60
|
if resp.nil? || resp.body.nil? || resp.body.empty?
|
55
|
-
raise
|
61
|
+
raise Client::Error::InvalidResponse.new
|
56
62
|
end
|
57
63
|
|
58
64
|
return resp.body
|
@@ -61,29 +67,21 @@ module Jimson
|
|
61
67
|
def process_batch_response(responses)
|
62
68
|
responses.each do |resp|
|
63
69
|
saved_response = @batch.map { |r| r[1] }.select { |r| r.id == resp['id'] }.first
|
64
|
-
raise
|
70
|
+
raise Client::Error::InvalidResponse.new if saved_response.nil?
|
65
71
|
saved_response.populate!(resp)
|
66
72
|
end
|
67
73
|
end
|
68
74
|
|
69
75
|
def process_single_response(data)
|
70
|
-
raise
|
76
|
+
raise Client::Error::InvalidResponse.new if !valid_response?(data)
|
71
77
|
|
72
78
|
if !!data['error']
|
73
79
|
code = data['error']['code']
|
74
|
-
|
75
|
-
|
76
|
-
else
|
77
|
-
raise Jimson::ClientError::UnknownServerError.new(code, data['error']['message'])
|
78
|
-
end
|
80
|
+
msg = data['error']['message']
|
81
|
+
raise Client::Error::ServerError.new(code, msg)
|
79
82
|
end
|
80
83
|
|
81
84
|
return data['result']
|
82
|
-
|
83
|
-
rescue Jimson::ClientError::Generic, Jimson::ServerError::Generic => e
|
84
|
-
raise e
|
85
|
-
rescue Exception, StandardError => e
|
86
|
-
raise Jimson::ClientError::InternalError.new(e)
|
87
85
|
end
|
88
86
|
|
89
87
|
def valid_response?(data)
|
@@ -113,7 +111,7 @@ module Jimson
|
|
113
111
|
|
114
112
|
def push_batch_request(request)
|
115
113
|
request.id = self.class.make_id
|
116
|
-
response =
|
114
|
+
response = Response.new(request.id)
|
117
115
|
@batch << [request, response]
|
118
116
|
return response
|
119
117
|
end
|
@@ -125,7 +123,7 @@ module Jimson
|
|
125
123
|
begin
|
126
124
|
responses = JSON.parse(response)
|
127
125
|
rescue
|
128
|
-
raise
|
126
|
+
raise Client::Error::InvalidJSON.new(json)
|
129
127
|
end
|
130
128
|
|
131
129
|
process_batch_response(responses)
|
@@ -134,7 +132,7 @@ module Jimson
|
|
134
132
|
|
135
133
|
end
|
136
134
|
|
137
|
-
class BatchClient
|
135
|
+
class BatchClient < BlankSlate
|
138
136
|
|
139
137
|
def initialize(helper)
|
140
138
|
@helper = helper
|
@@ -147,7 +145,10 @@ module Jimson
|
|
147
145
|
|
148
146
|
end
|
149
147
|
|
150
|
-
class Client
|
148
|
+
class Client < BlankSlate
|
149
|
+
reveal :instance_variable_get
|
150
|
+
reveal :inspect
|
151
|
+
reveal :to_s
|
151
152
|
|
152
153
|
def self.batch(client)
|
153
154
|
helper = client.instance_variable_get(:@helper)
|
@@ -161,7 +162,11 @@ module Jimson
|
|
161
162
|
end
|
162
163
|
|
163
164
|
def method_missing(sym, *args, &block)
|
164
|
-
|
165
|
+
self[sym, args]
|
166
|
+
end
|
167
|
+
|
168
|
+
def [](method, *args)
|
169
|
+
@helper.process_call(method, args.flatten)
|
165
170
|
end
|
166
171
|
|
167
172
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Jimson
|
2
|
+
class Client
|
3
|
+
module Error
|
4
|
+
class InvalidResponse < StandardError
|
5
|
+
def initialize()
|
6
|
+
super('Invalid or empty response from server.')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class InvalidJSON < StandardError
|
11
|
+
def initialize(json)
|
12
|
+
super("Couldn't parse JSON string received from server:\n#{json}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ServerError < StandardError
|
17
|
+
def initialize(code, message)
|
18
|
+
super("Server error #{code}: #{message}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Jimson
|
2
|
+
module Handler
|
3
|
+
|
4
|
+
def jimson_default_methods
|
5
|
+
self.instance_methods.map(&:to_s) - Object.methods.map(&:to_s)
|
6
|
+
end
|
7
|
+
|
8
|
+
def jimson_expose(*methods)
|
9
|
+
@jimson_exposed_methods ||= []
|
10
|
+
@jimson_exposed_methods += methods.map(&:to_s)
|
11
|
+
end
|
12
|
+
|
13
|
+
def jimson_exclude(*methods)
|
14
|
+
@jimson_excluded_methods ||= []
|
15
|
+
@jimson_excluded_methods += methods.map(&:to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
def jimson_exposed_methods
|
19
|
+
@jimson_exposed_methods ||= []
|
20
|
+
@jimson_excluded_methods ||= []
|
21
|
+
(jimson_default_methods - @jimson_excluded_methods + @jimson_exposed_methods).sort.map(&:to_sym)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/lib/jimson/server.rb
CHANGED
@@ -2,10 +2,28 @@ require 'rack'
|
|
2
2
|
require 'rack/request'
|
3
3
|
require 'rack/response'
|
4
4
|
require 'json'
|
5
|
+
require 'jimson/handler'
|
6
|
+
require 'jimson/server/error'
|
5
7
|
|
6
8
|
module Jimson
|
7
9
|
class Server
|
8
10
|
|
11
|
+
class System
|
12
|
+
extend Handler
|
13
|
+
|
14
|
+
def initialize(handler)
|
15
|
+
@handler = handler
|
16
|
+
end
|
17
|
+
|
18
|
+
def listMethods
|
19
|
+
@handler.class.jimson_exposed_methods
|
20
|
+
end
|
21
|
+
|
22
|
+
def isAlive
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
9
27
|
JSON_RPC_VERSION = '2.0'
|
10
28
|
|
11
29
|
attr_accessor :handler, :host, :port
|
@@ -52,17 +70,17 @@ module Jimson
|
|
52
70
|
begin
|
53
71
|
request = parse_request(content)
|
54
72
|
if request.is_a?(Array)
|
55
|
-
raise
|
73
|
+
raise Server::Error::InvalidRequest.new if request.empty?
|
56
74
|
response = request.map { |req| handle_request(req) }
|
57
75
|
else
|
58
76
|
response = handle_request(request)
|
59
77
|
end
|
60
|
-
rescue
|
78
|
+
rescue Server::Error::ParseError, Server::Error::InvalidRequest => e
|
61
79
|
response = error_response(e)
|
62
|
-
rescue
|
80
|
+
rescue Server::Error => e
|
63
81
|
response = error_response(e, request)
|
64
|
-
rescue StandardError, Exception
|
65
|
-
response = error_response(
|
82
|
+
rescue StandardError, Exception => e
|
83
|
+
response = error_response(Server::Error::InternalError.new(e))
|
66
84
|
end
|
67
85
|
|
68
86
|
response.compact! if response.is_a?(Array)
|
@@ -76,11 +94,11 @@ module Jimson
|
|
76
94
|
response = nil
|
77
95
|
begin
|
78
96
|
if !validate_request(request)
|
79
|
-
response = error_response(
|
97
|
+
response = error_response(Server::Error::InvalidRequest.new)
|
80
98
|
else
|
81
99
|
response = create_response(request)
|
82
100
|
end
|
83
|
-
rescue
|
101
|
+
rescue Server::Error => e
|
84
102
|
response = error_response(e, request)
|
85
103
|
end
|
86
104
|
|
@@ -112,20 +130,9 @@ module Jimson
|
|
112
130
|
end
|
113
131
|
|
114
132
|
def create_response(request)
|
133
|
+
method = request['method']
|
115
134
|
params = request['params']
|
116
|
-
|
117
|
-
if params.is_a?(Hash)
|
118
|
-
result = @handler.send(request['method'], params)
|
119
|
-
else
|
120
|
-
result = @handler.send(request['method'], *params)
|
121
|
-
end
|
122
|
-
rescue NoMethodError
|
123
|
-
raise Jimson::ServerError::MethodNotFound.new
|
124
|
-
rescue ArgumentError
|
125
|
-
raise Jimson::ServerError::InvalidParams.new
|
126
|
-
rescue
|
127
|
-
raise Jimson::ServerError::ApplicationError.new($!)
|
128
|
-
end
|
135
|
+
result = dispatch_request(method, params)
|
129
136
|
|
130
137
|
response = success_response(request, result)
|
131
138
|
|
@@ -134,7 +141,42 @@ module Jimson
|
|
134
141
|
# that are within a batch request.
|
135
142
|
response = nil if !request.has_key?('id')
|
136
143
|
|
137
|
-
response
|
144
|
+
return response
|
145
|
+
|
146
|
+
rescue Server::Error => e
|
147
|
+
raise e
|
148
|
+
rescue ArgumentError
|
149
|
+
raise Server::Error::InvalidParams.new
|
150
|
+
rescue Exception, StandardError => e
|
151
|
+
raise Server::Error::ApplicationError.new(e)
|
152
|
+
end
|
153
|
+
|
154
|
+
def dispatch_request(method, params)
|
155
|
+
# normally route requests to the user-suplied handler
|
156
|
+
handler = @handler
|
157
|
+
|
158
|
+
# switch to the System handler if a system method was called
|
159
|
+
sys_regex = /^system\./
|
160
|
+
if method =~ sys_regex
|
161
|
+
handler = System.new(@handler)
|
162
|
+
# remove the 'system.' prefix before from the method name
|
163
|
+
method.gsub!(sys_regex, '')
|
164
|
+
end
|
165
|
+
|
166
|
+
method = method.to_sym
|
167
|
+
|
168
|
+
if !handler.class.jimson_exposed_methods.include?(method) \
|
169
|
+
|| !handler.respond_to?(method)
|
170
|
+
raise Server::Error::MethodNotFound.new(method)
|
171
|
+
end
|
172
|
+
|
173
|
+
if params.nil?
|
174
|
+
return handler.send(method)
|
175
|
+
elsif params.is_a?(Hash)
|
176
|
+
return handler.send(method, params)
|
177
|
+
else
|
178
|
+
return handler.send(method, *params)
|
179
|
+
end
|
138
180
|
end
|
139
181
|
|
140
182
|
def error_response(error, request = nil)
|
@@ -162,7 +204,7 @@ module Jimson
|
|
162
204
|
def parse_request(post)
|
163
205
|
data = JSON.parse(post)
|
164
206
|
rescue
|
165
|
-
raise
|
207
|
+
raise Server::Error::ParseError.new
|
166
208
|
end
|
167
209
|
|
168
210
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Jimson
|
2
|
+
class Server
|
3
|
+
class Error < StandardError
|
4
|
+
attr_accessor :code, :message
|
5
|
+
|
6
|
+
def initialize(code, message)
|
7
|
+
@code = code
|
8
|
+
@message = message
|
9
|
+
super(message)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
{
|
14
|
+
'code' => @code,
|
15
|
+
'message' => @message
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
class ParseError < Error
|
20
|
+
def initialize
|
21
|
+
super(-32700, 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class InvalidRequest < Error
|
26
|
+
def initialize
|
27
|
+
super(-32600, 'The JSON sent is not a valid Request object.')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class MethodNotFound < Error
|
32
|
+
def initialize(method)
|
33
|
+
super(-32601, "Method '#{method}' not found.")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class InvalidParams < Error
|
38
|
+
def initialize
|
39
|
+
super(-32602, 'Invalid method parameter(s).')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class InternalError < Error
|
44
|
+
def initialize(e)
|
45
|
+
super(-32603, "Internal server error: #{e}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class ApplicationError < Error
|
50
|
+
def initialize(err)
|
51
|
+
super(-32099, "Application error: #{err} #{err.backtrace.join("\n")}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
CODES = {
|
56
|
+
-32700 => ParseError,
|
57
|
+
-32600 => InvalidRequest,
|
58
|
+
-32601 => MethodNotFound,
|
59
|
+
-32602 => InvalidParams,
|
60
|
+
-32603 => InternalError
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/spec/client_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
1
3
|
module Jimson
|
2
4
|
describe Client do
|
3
5
|
BOILERPLATE = {'jsonrpc' => '2.0', 'id' => 1}
|
@@ -10,6 +12,42 @@ module Jimson
|
|
10
12
|
after(:each) do
|
11
13
|
end
|
12
14
|
|
15
|
+
describe "hidden methods" do
|
16
|
+
it "should reveal inspect" do
|
17
|
+
Client.new(SPEC_URL).inspect.should match /Jimson::Client/
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should reveal to_s" do
|
21
|
+
Client.new(SPEC_URL).to_s.should match /Jimson::Client/
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#[]" do
|
26
|
+
before(:each) do
|
27
|
+
expected = {
|
28
|
+
'jsonrpc' => '2.0',
|
29
|
+
'method' => 'foo',
|
30
|
+
'params' => [1,2,3],
|
31
|
+
'id' => 1
|
32
|
+
}.to_json
|
33
|
+
response = BOILERPLATE.merge({'result' => 42}).to_json
|
34
|
+
RestClient.should_receive(:post).with(SPEC_URL, expected, {:content_type => 'application/json'}).and_return(@resp_mock)
|
35
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
36
|
+
@client = Client.new(SPEC_URL)
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when using an array of args" do
|
40
|
+
it "sends a request with the correct method and args" do
|
41
|
+
@client['foo', [1,2,3]].should == 42
|
42
|
+
end
|
43
|
+
end
|
44
|
+
context "when using a splat for args" do
|
45
|
+
it "sends a request with the correct method and args" do
|
46
|
+
@client['foo', 1, 2, 3].should == 42
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
13
51
|
describe "sending a single request" do
|
14
52
|
context "when using positional parameters" do
|
15
53
|
before(:each) do
|
@@ -73,5 +111,17 @@ module Jimson
|
|
73
111
|
end
|
74
112
|
end
|
75
113
|
|
114
|
+
describe "error handling" do
|
115
|
+
context "when an error occurs in the Jimson::Client code" do
|
116
|
+
it "tags the raised exception with Jimson::Client::Error" do
|
117
|
+
client_helper = ClientHelper.new(SPEC_URL)
|
118
|
+
ClientHelper.stub!(:new).and_return(client_helper)
|
119
|
+
client = Client.new(SPEC_URL)
|
120
|
+
client_helper.stub!(:send_single_request).and_raise "intentional error"
|
121
|
+
lambda { client.foo }.should raise_error(Jimson::Client::Error)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
76
126
|
end
|
77
127
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Jimson
|
4
|
+
describe Handler do
|
5
|
+
|
6
|
+
class FooHandler
|
7
|
+
extend Jimson::Handler
|
8
|
+
|
9
|
+
jimson_expose :to_s, :bye
|
10
|
+
|
11
|
+
jimson_exclude :hi, :bye
|
12
|
+
|
13
|
+
def hi
|
14
|
+
'hi'
|
15
|
+
end
|
16
|
+
|
17
|
+
def bye
|
18
|
+
'bye'
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
'foo'
|
23
|
+
end
|
24
|
+
|
25
|
+
def so_exposed
|
26
|
+
"I'm so exposed!"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:foo) { FooHandler.new }
|
31
|
+
|
32
|
+
describe "#jimson_expose" do
|
33
|
+
it "exposes a method even if it was defined on Object" do
|
34
|
+
foo.class.jimson_exposed_methods.should include(:to_s)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#jimson_exclude" do
|
39
|
+
context "when a method was not explicitly exposed" do
|
40
|
+
it "excludes the method" do
|
41
|
+
foo.class.jimson_exposed_methods.should_not include(:hi)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
context "when a method was explicitly exposed" do
|
45
|
+
it "does not exclude the method" do
|
46
|
+
foo.class.jimson_exposed_methods.should include(:bye)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#jimson_exposed_methods" do
|
52
|
+
it "doesn't include methods defined on Object" do
|
53
|
+
foo.class.jimson_exposed_methods.should_not include(:object_id)
|
54
|
+
end
|
55
|
+
it "includes methods defined on the extending class but not on Object" do
|
56
|
+
foo.class.jimson_exposed_methods.should include(:so_exposed)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
data/spec/server_spec.rb
CHANGED
@@ -1,7 +1,37 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'rack/test'
|
2
3
|
|
3
4
|
module Jimson
|
4
5
|
describe Server do
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
class TestHandler
|
9
|
+
extend Jimson::Handler
|
10
|
+
|
11
|
+
def subtract(a, b = nil)
|
12
|
+
if a.is_a?(Hash)
|
13
|
+
return a['minuend'] - a['subtrahend']
|
14
|
+
else
|
15
|
+
return a - b
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def sum(a,b,c)
|
20
|
+
a + b + c
|
21
|
+
end
|
22
|
+
|
23
|
+
def notify_hello(*args)
|
24
|
+
# notification, doesn't do anything
|
25
|
+
end
|
26
|
+
|
27
|
+
def update(*args)
|
28
|
+
# notification, doesn't do anything
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_data
|
32
|
+
['hello', 5]
|
33
|
+
end
|
34
|
+
end
|
5
35
|
|
6
36
|
INVALID_RESPONSE_EXPECTATION = {
|
7
37
|
'jsonrpc' => '2.0',
|
@@ -11,6 +41,14 @@ module Jimson
|
|
11
41
|
},
|
12
42
|
'id' => nil
|
13
43
|
}
|
44
|
+
def app
|
45
|
+
Server.new(TestHandler.new)
|
46
|
+
end
|
47
|
+
|
48
|
+
def post_json(hash)
|
49
|
+
post '/', hash.to_json, {'Content-Type' => 'application/json'}
|
50
|
+
end
|
51
|
+
|
14
52
|
before(:each) do
|
15
53
|
@url = SPEC_URL
|
16
54
|
end
|
@@ -24,7 +62,10 @@ module Jimson
|
|
24
62
|
'params' => [24, 20],
|
25
63
|
'id' => 1
|
26
64
|
}
|
27
|
-
|
65
|
+
post_json(req)
|
66
|
+
|
67
|
+
last_response.should be_ok
|
68
|
+
resp = JSON.parse(last_response.body)
|
28
69
|
resp.should == {
|
29
70
|
'jsonrpc' => '2.0',
|
30
71
|
'result' => 4,
|
@@ -43,7 +84,10 @@ module Jimson
|
|
43
84
|
'params' => {'subtrahend'=> 20, 'minuend' => 24},
|
44
85
|
'id' => 1
|
45
86
|
}
|
46
|
-
|
87
|
+
post_json(req)
|
88
|
+
|
89
|
+
last_response.should be_ok
|
90
|
+
resp = JSON.parse(last_response.body)
|
47
91
|
resp.should == {
|
48
92
|
'jsonrpc' => '2.0',
|
49
93
|
'result' => 4,
|
@@ -61,8 +105,8 @@ module Jimson
|
|
61
105
|
'method' => 'update',
|
62
106
|
'params' => [1,2,3,4,5]
|
63
107
|
}
|
64
|
-
|
65
|
-
|
108
|
+
post_json(req)
|
109
|
+
last_response.body.should be_empty
|
66
110
|
end
|
67
111
|
end
|
68
112
|
end
|
@@ -74,12 +118,35 @@ module Jimson
|
|
74
118
|
'method' => 'foobar',
|
75
119
|
'id' => 1
|
76
120
|
}
|
77
|
-
|
121
|
+
post_json(req)
|
122
|
+
|
123
|
+
resp = JSON.parse(last_response.body)
|
124
|
+
resp.should == {
|
125
|
+
'jsonrpc' => '2.0',
|
126
|
+
'error' => {
|
127
|
+
'code' => -32601,
|
128
|
+
'message' => "Method 'foobar' not found."
|
129
|
+
},
|
130
|
+
'id' => 1
|
131
|
+
}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "receiving a call for a method which exists but is not exposed" do
|
136
|
+
it "returns an error response" do
|
137
|
+
req = {
|
138
|
+
'jsonrpc' => '2.0',
|
139
|
+
'method' => 'object_id',
|
140
|
+
'id' => 1
|
141
|
+
}
|
142
|
+
post_json(req)
|
143
|
+
|
144
|
+
resp = JSON.parse(last_response.body)
|
78
145
|
resp.should == {
|
79
146
|
'jsonrpc' => '2.0',
|
80
147
|
'error' => {
|
81
148
|
'code' => -32601,
|
82
|
-
'message' =>
|
149
|
+
'message' => "Method 'object_id' not found."
|
83
150
|
},
|
84
151
|
'id' => 1
|
85
152
|
}
|
@@ -94,7 +161,9 @@ module Jimson
|
|
94
161
|
'params' => [1,2,3],
|
95
162
|
'id' => 1
|
96
163
|
}
|
97
|
-
|
164
|
+
post_json(req)
|
165
|
+
|
166
|
+
resp = JSON.parse(last_response.body)
|
98
167
|
resp.should == {
|
99
168
|
'jsonrpc' => '2.0',
|
100
169
|
'error' => {
|
@@ -114,7 +183,9 @@ module Jimson
|
|
114
183
|
'id' => 1
|
115
184
|
}.to_json
|
116
185
|
req += '}' # make the json invalid
|
117
|
-
|
186
|
+
post '/', req, {'Content-type' => 'application/json'}
|
187
|
+
|
188
|
+
resp = JSON.parse(last_response.body)
|
118
189
|
resp.should == {
|
119
190
|
'jsonrpc' => '2.0',
|
120
191
|
'error' => {
|
@@ -132,24 +203,27 @@ module Jimson
|
|
132
203
|
req = {
|
133
204
|
'jsonrpc' => '2.0',
|
134
205
|
'method' => 1 # method as int is invalid
|
135
|
-
}
|
136
|
-
|
206
|
+
}
|
207
|
+
post_json(req)
|
208
|
+
resp = JSON.parse(last_response.body)
|
137
209
|
resp.should == INVALID_RESPONSE_EXPECTATION
|
138
210
|
end
|
139
211
|
end
|
140
212
|
|
141
213
|
context "when the request is an empty batch" do
|
142
214
|
it "returns an error response" do
|
143
|
-
req = []
|
144
|
-
|
215
|
+
req = []
|
216
|
+
post_json(req)
|
217
|
+
resp = JSON.parse(last_response.body)
|
145
218
|
resp.should == INVALID_RESPONSE_EXPECTATION
|
146
219
|
end
|
147
220
|
end
|
148
221
|
|
149
222
|
context "when the request is an invalid batch" do
|
150
223
|
it "returns an error response" do
|
151
|
-
req = [1,2]
|
152
|
-
|
224
|
+
req = [1,2]
|
225
|
+
post_json(req)
|
226
|
+
resp = JSON.parse(last_response.body)
|
153
227
|
resp.should == [INVALID_RESPONSE_EXPECTATION, INVALID_RESPONSE_EXPECTATION]
|
154
228
|
end
|
155
229
|
end
|
@@ -165,13 +239,14 @@ module Jimson
|
|
165
239
|
{'foo' => 'boo'},
|
166
240
|
{'jsonrpc' => '2.0', 'method' => 'foo.get', 'params' => {'name' => 'myself'}, 'id' => '5'},
|
167
241
|
{'jsonrpc' => '2.0', 'method' => 'get_data', 'id' => '9'}
|
168
|
-
]
|
169
|
-
|
242
|
+
]
|
243
|
+
post_json(reqs)
|
244
|
+
resp = JSON.parse(last_response.body)
|
170
245
|
resp.should == [
|
171
246
|
{'jsonrpc' => '2.0', 'result' => 7, 'id' => '1'},
|
172
247
|
{'jsonrpc' => '2.0', 'result' => 19, 'id' => '2'},
|
173
248
|
{'jsonrpc' => '2.0', 'error' => {'code' => -32600, 'message' => 'The JSON sent is not a valid Request object.'}, 'id' => nil},
|
174
|
-
{'jsonrpc' => '2.0', 'error' => {'code' => -32601, 'message' =>
|
249
|
+
{'jsonrpc' => '2.0', 'error' => {'code' => -32601, 'message' => "Method 'foo.get' not found."}, 'id' => '5'},
|
175
250
|
{'jsonrpc' => '2.0', 'result' => ['hello', 5], 'id' => '9'}
|
176
251
|
]
|
177
252
|
end
|
@@ -191,11 +266,51 @@ module Jimson
|
|
191
266
|
'params' => [1,2,3,4,5]
|
192
267
|
}
|
193
268
|
]
|
194
|
-
|
195
|
-
|
269
|
+
post_json(req)
|
270
|
+
last_response.body.should be_empty
|
196
271
|
end
|
197
272
|
end
|
198
273
|
end
|
199
274
|
|
275
|
+
describe "receiving a 'system.' request" do
|
276
|
+
context "when the request is 'isAlive'" do
|
277
|
+
it "returns response 'true'" do
|
278
|
+
req = {
|
279
|
+
'jsonrpc' => '2.0',
|
280
|
+
'method' => 'system.isAlive',
|
281
|
+
'params' => [],
|
282
|
+
'id' => 1
|
283
|
+
}
|
284
|
+
post_json(req)
|
285
|
+
|
286
|
+
last_response.should be_ok
|
287
|
+
resp = JSON.parse(last_response.body)
|
288
|
+
resp.should == {
|
289
|
+
'jsonrpc' => '2.0',
|
290
|
+
'result' => true,
|
291
|
+
'id' => 1
|
292
|
+
}
|
293
|
+
end
|
294
|
+
end
|
295
|
+
context "when the request is 'listMethods'" do
|
296
|
+
it "returns response with all listMethods on the handler as strings" do
|
297
|
+
req = {
|
298
|
+
'jsonrpc' => '2.0',
|
299
|
+
'method' => 'system.listMethods',
|
300
|
+
'params' => [],
|
301
|
+
'id' => 1
|
302
|
+
}
|
303
|
+
post_json(req)
|
304
|
+
|
305
|
+
last_response.should be_ok
|
306
|
+
resp = JSON.parse(last_response.body)
|
307
|
+
resp.should == {
|
308
|
+
'jsonrpc' => '2.0',
|
309
|
+
'result' => ['subtract', 'sum', 'notify_hello', 'update', 'get_data'].sort,
|
310
|
+
'id' => 1
|
311
|
+
}
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
200
315
|
end
|
201
316
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,18 +2,6 @@ require 'rubygems'
|
|
2
2
|
$:.unshift(File.dirname(__FILE__) + '/../lib/')
|
3
3
|
require 'jimson/server'
|
4
4
|
require 'jimson/client'
|
5
|
-
require 'open-uri'
|
6
5
|
require 'json'
|
7
6
|
|
8
7
|
SPEC_URL = 'http://localhost:8999'
|
9
|
-
|
10
|
-
pid = Process.spawn(File.dirname(__FILE__) + '/server_helper.rb')
|
11
|
-
|
12
|
-
RSpec.configure do |config|
|
13
|
-
config.after(:all) do
|
14
|
-
Process.kill(9, pid)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
sleep 1
|
19
|
-
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jimson
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-09-07 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: blankslate
|
16
|
+
requirement: &13756100 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.1.2.3
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *13756100
|
14
25
|
- !ruby/object:Gem::Dependency
|
15
26
|
name: rest-client
|
16
|
-
requirement: &
|
27
|
+
requirement: &13755420 !ruby/object:Gem::Requirement
|
17
28
|
none: false
|
18
29
|
requirements:
|
19
30
|
- - ! '>='
|
@@ -21,10 +32,10 @@ dependencies:
|
|
21
32
|
version: 1.6.3
|
22
33
|
type: :runtime
|
23
34
|
prerelease: false
|
24
|
-
version_requirements: *
|
35
|
+
version_requirements: *13755420
|
25
36
|
- !ruby/object:Gem::Dependency
|
26
37
|
name: json
|
27
|
-
requirement: &
|
38
|
+
requirement: &13754820 !ruby/object:Gem::Requirement
|
28
39
|
none: false
|
29
40
|
requirements:
|
30
41
|
- - ! '>='
|
@@ -32,10 +43,10 @@ dependencies:
|
|
32
43
|
version: 1.5.1
|
33
44
|
type: :runtime
|
34
45
|
prerelease: false
|
35
|
-
version_requirements: *
|
46
|
+
version_requirements: *13754820
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: rack
|
38
|
-
requirement: &
|
49
|
+
requirement: &13754200 !ruby/object:Gem::Requirement
|
39
50
|
none: false
|
40
51
|
requirements:
|
41
52
|
- - ! '>='
|
@@ -43,29 +54,30 @@ dependencies:
|
|
43
54
|
version: '1.3'
|
44
55
|
type: :runtime
|
45
56
|
prerelease: false
|
46
|
-
version_requirements: *
|
57
|
+
version_requirements: *13754200
|
47
58
|
description:
|
48
59
|
email:
|
49
60
|
executables: []
|
50
61
|
extensions: []
|
51
62
|
extra_rdoc_files:
|
52
|
-
- README.
|
63
|
+
- README.md
|
53
64
|
files:
|
54
65
|
- VERSION
|
55
66
|
- LICENSE.txt
|
56
67
|
- CHANGELOG.rdoc
|
57
|
-
- README.
|
68
|
+
- README.md
|
58
69
|
- Rakefile
|
59
70
|
- lib/jimson.rb
|
60
|
-
- lib/jimson/
|
71
|
+
- lib/jimson/server/error.rb
|
72
|
+
- lib/jimson/handler.rb
|
61
73
|
- lib/jimson/server.rb
|
62
|
-
- lib/jimson/server_error.rb
|
63
74
|
- lib/jimson/response.rb
|
75
|
+
- lib/jimson/client/error.rb
|
64
76
|
- lib/jimson/request.rb
|
65
77
|
- lib/jimson/client.rb
|
78
|
+
- spec/handler_spec.rb
|
66
79
|
- spec/server_spec.rb
|
67
80
|
- spec/client_spec.rb
|
68
|
-
- spec/server_helper.rb
|
69
81
|
- spec/spec_helper.rb
|
70
82
|
homepage: http://www.github.com/chriskite/jimson
|
71
83
|
licenses: []
|
@@ -92,7 +104,7 @@ signing_key:
|
|
92
104
|
specification_version: 3
|
93
105
|
summary: JSON-RPC 2.0 client and server
|
94
106
|
test_files:
|
107
|
+
- spec/handler_spec.rb
|
95
108
|
- spec/server_spec.rb
|
96
109
|
- spec/client_spec.rb
|
97
|
-
- spec/server_helper.rb
|
98
110
|
- spec/spec_helper.rb
|
data/README.rdoc
DELETED
File without changes
|
data/lib/jimson/client_error.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
module Jimson
|
2
|
-
module ClientError
|
3
|
-
class Generic < Exception
|
4
|
-
end
|
5
|
-
|
6
|
-
class InvalidResponse < Generic
|
7
|
-
def initialize()
|
8
|
-
super('Invalid or empty response from server.')
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
class InvalidJSON < Generic
|
13
|
-
def initialize(json)
|
14
|
-
super("Couldn't parse JSON string received from server:\n#{json}")
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class InternalError < Generic
|
19
|
-
def initialize(e)
|
20
|
-
super("An internal client error occurred when processing the request: #{e}\n#{e.backtrace.join("\n")}")
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class UnknownServerError < Generic
|
25
|
-
def initialize(code, message)
|
26
|
-
super("The server specified an error the client doesn't know about: #{code} #{message}")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
data/lib/jimson/server_error.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
module Jimson
|
2
|
-
module ServerError
|
3
|
-
class Generic < Exception
|
4
|
-
attr_accessor :code, :message
|
5
|
-
|
6
|
-
def initialize(code, message)
|
7
|
-
@code = code
|
8
|
-
@message = message
|
9
|
-
super(message)
|
10
|
-
end
|
11
|
-
|
12
|
-
def to_h
|
13
|
-
{
|
14
|
-
'code' => @code,
|
15
|
-
'message' => @message
|
16
|
-
}
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class ParseError < Generic
|
21
|
-
def initialize
|
22
|
-
super(-32700, 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class InvalidRequest < Generic
|
27
|
-
def initialize
|
28
|
-
super(-32600, 'The JSON sent is not a valid Request object.')
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class MethodNotFound < Generic
|
33
|
-
def initialize
|
34
|
-
super(-32601, 'Method not found.')
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class InvalidParams < Generic
|
39
|
-
def initialize
|
40
|
-
super(-32602, 'Invalid method parameter(s).')
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class InternalError < Generic
|
45
|
-
def initialize
|
46
|
-
super(-32603, 'Internal server error.')
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class ApplicationError < Generic
|
51
|
-
def initialize(err)
|
52
|
-
super(-32099, "The application being served raised an error: #{err}")
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
CODES = {
|
57
|
-
-32700 => ParseError,
|
58
|
-
-32600 => InvalidRequest,
|
59
|
-
-32601 => MethodNotFound,
|
60
|
-
-32602 => InvalidParams,
|
61
|
-
-32603 => InternalError
|
62
|
-
}
|
63
|
-
end
|
64
|
-
end
|
data/spec/server_helper.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
$:.unshift(File.dirname(__FILE__) + '/../lib/')
|
5
|
-
require 'jimson'
|
6
|
-
|
7
|
-
class TestHandler
|
8
|
-
def subtract(a, b = nil)
|
9
|
-
if a.is_a?(Hash)
|
10
|
-
return a['minuend'] - a['subtrahend']
|
11
|
-
else
|
12
|
-
return a - b
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def sum(a,b,c)
|
17
|
-
a + b + c
|
18
|
-
end
|
19
|
-
|
20
|
-
def notify_hello(*args)
|
21
|
-
# notification, doesn't do anything
|
22
|
-
end
|
23
|
-
|
24
|
-
def update(*args)
|
25
|
-
# notification, doesn't do anything
|
26
|
-
end
|
27
|
-
|
28
|
-
def get_data
|
29
|
-
['hello', 5]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
server = Jimson::Server.new(TestHandler.new)
|
34
|
-
server.start
|