jimson-temp 0.9.2
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/CHANGELOG.rdoc +86 -0
- data/LICENSE.txt +17 -0
- data/README.md +29 -0
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/lib/jimson-temp.rb +8 -0
- data/lib/jimson/client.rb +180 -0
- data/lib/jimson/client/error.rb +23 -0
- data/lib/jimson/handler.rb +25 -0
- data/lib/jimson/request.rb +25 -0
- data/lib/jimson/response.rb +30 -0
- data/lib/jimson/router.rb +24 -0
- data/lib/jimson/router/map.rb +75 -0
- data/lib/jimson/server.rb +225 -0
- data/lib/jimson/server/error.rb +66 -0
- data/spec/client_spec.rb +191 -0
- data/spec/handler_spec.rb +62 -0
- data/spec/router_spec.rb +75 -0
- data/spec/server_spec.rb +466 -0
- data/spec/spec_helper.rb +7 -0
- metadata +134 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'jimson/router/map'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Jimson
|
5
|
+
class Router
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@map, :handler_for_method,
|
9
|
+
:root,
|
10
|
+
:namespace,
|
11
|
+
:jimson_methods,
|
12
|
+
:strip_method_namespace
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@map = Map.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def draw(&block)
|
19
|
+
@map.instance_eval &block
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Jimson
|
2
|
+
class Router
|
3
|
+
|
4
|
+
#
|
5
|
+
# Provides a DSL for routing method namespaces to handlers.
|
6
|
+
# Only handles root-level and non-nested namespaces, e.g. 'foo.bar' or 'foo'.
|
7
|
+
#
|
8
|
+
class Map
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@routes = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Set the root handler, i.e. the handler used for a bare method like 'foo'
|
16
|
+
#
|
17
|
+
def root(handler)
|
18
|
+
handler = handler.new if handler.is_a?(Class)
|
19
|
+
@routes[''] = handler
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Define the handler for a namespace
|
24
|
+
#
|
25
|
+
def namespace(ns, handler = nil, &block)
|
26
|
+
if !!handler
|
27
|
+
handler = handler.new if handler.is_a?(Class)
|
28
|
+
@routes[ns.to_s] = handler
|
29
|
+
else
|
30
|
+
# passed a block for nested namespacing
|
31
|
+
map = Jimson::Router::Map.new
|
32
|
+
@routes[ns.to_s] = map
|
33
|
+
map.instance_eval &block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Return the handler for a (possibly namespaced) method name
|
39
|
+
#
|
40
|
+
def handler_for_method(method)
|
41
|
+
parts = method.split('.')
|
42
|
+
ns = (method.index('.') == nil ? '' : parts.first)
|
43
|
+
handler = @routes[ns]
|
44
|
+
if handler.is_a?(Jimson::Router::Map)
|
45
|
+
return handler.handler_for_method(parts[1..-1].join('.'))
|
46
|
+
end
|
47
|
+
handler
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Strip off the namespace part of a method and return the bare method name
|
52
|
+
#
|
53
|
+
def strip_method_namespace(method)
|
54
|
+
method.split('.').last
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Return an array of all methods on handlers in the map, fully namespaced
|
59
|
+
#
|
60
|
+
def jimson_methods
|
61
|
+
arr = @routes.keys.map do |ns|
|
62
|
+
prefix = (ns == '' ? '' : "#{ns}.")
|
63
|
+
handler = @routes[ns]
|
64
|
+
if handler.is_a?(Jimson::Router::Map)
|
65
|
+
handler.jimson_methods
|
66
|
+
else
|
67
|
+
handler.class.jimson_exposed_methods.map { |method| prefix + method }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
arr.flatten
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/request'
|
3
|
+
require 'rack/response'
|
4
|
+
require 'multi_json'
|
5
|
+
require 'jimson/handler'
|
6
|
+
require 'jimson/router'
|
7
|
+
require 'jimson/server/error'
|
8
|
+
|
9
|
+
module Jimson
|
10
|
+
class Server
|
11
|
+
|
12
|
+
class System
|
13
|
+
extend Handler
|
14
|
+
|
15
|
+
def initialize(router)
|
16
|
+
@router = router
|
17
|
+
end
|
18
|
+
|
19
|
+
def listMethods
|
20
|
+
@router.jimson_methods
|
21
|
+
end
|
22
|
+
|
23
|
+
def isAlive
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
JSON_RPC_VERSION = '2.0'
|
29
|
+
|
30
|
+
attr_accessor :router, :host, :port, :show_errors, :opts
|
31
|
+
|
32
|
+
#
|
33
|
+
# Create a Server with routes defined
|
34
|
+
#
|
35
|
+
def self.with_routes(opts = {}, &block)
|
36
|
+
router = Router.new
|
37
|
+
router.send(:draw, &block)
|
38
|
+
self.new(router, opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# +router_or_handler+ is an instance of Jimson::Router or extends Jimson::Handler
|
43
|
+
#
|
44
|
+
# +opts+ may include:
|
45
|
+
# * :host - the hostname or ip to bind to
|
46
|
+
# * :port - the port to listen on
|
47
|
+
# * :server - the rack handler to use, e.g. 'webrick' or 'thin'
|
48
|
+
# * :show_errors - true or false, send backtraces in error responses?
|
49
|
+
#
|
50
|
+
# Remaining options are forwarded to the underlying Rack server.
|
51
|
+
#
|
52
|
+
def initialize(router_or_handler, opts = {})
|
53
|
+
if !router_or_handler.is_a?(Router)
|
54
|
+
# arg is a handler, wrap it in a Router
|
55
|
+
@router = Router.new
|
56
|
+
@router.root router_or_handler
|
57
|
+
else
|
58
|
+
# arg is a router
|
59
|
+
@router = router_or_handler
|
60
|
+
end
|
61
|
+
@router.namespace 'system', System.new(@router)
|
62
|
+
|
63
|
+
@host = opts.delete(:host) || '0.0.0.0'
|
64
|
+
@port = opts.delete(:port) || 8999
|
65
|
+
@show_errors = opts.delete(:show_errors) || false
|
66
|
+
@opts = opts
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Starts the server so it can process requests
|
71
|
+
#
|
72
|
+
def start
|
73
|
+
Rack::Server.start(opts.merge(
|
74
|
+
:app => self,
|
75
|
+
:Host => @host,
|
76
|
+
:Port => @port
|
77
|
+
))
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Entry point for Rack
|
82
|
+
#
|
83
|
+
def call(env)
|
84
|
+
req = Rack::Request.new(env)
|
85
|
+
resp = Rack::Response.new
|
86
|
+
return resp.finish if !req.post?
|
87
|
+
resp.write process(req.body.read)
|
88
|
+
resp.finish
|
89
|
+
end
|
90
|
+
|
91
|
+
def process(content)
|
92
|
+
begin
|
93
|
+
request = parse_request(content)
|
94
|
+
if request.is_a?(Array)
|
95
|
+
raise Server::Error::InvalidRequest.new if request.empty?
|
96
|
+
response = request.map { |req| handle_request(req) }
|
97
|
+
else
|
98
|
+
response = handle_request(request)
|
99
|
+
end
|
100
|
+
rescue Server::Error::ParseError, Server::Error::InvalidRequest => e
|
101
|
+
response = error_response(e)
|
102
|
+
rescue Server::Error => e
|
103
|
+
response = error_response(e, request)
|
104
|
+
rescue StandardError, Exception => e
|
105
|
+
response = error_response(Server::Error::InternalError.new(e))
|
106
|
+
end
|
107
|
+
|
108
|
+
response.compact! if response.is_a?(Array)
|
109
|
+
|
110
|
+
return nil if response.nil? || (response.respond_to?(:empty?) && response.empty?)
|
111
|
+
|
112
|
+
MultiJson.encode(response)
|
113
|
+
end
|
114
|
+
|
115
|
+
def handle_request(request)
|
116
|
+
response = nil
|
117
|
+
begin
|
118
|
+
if !validate_request(request)
|
119
|
+
response = error_response(Server::Error::InvalidRequest.new)
|
120
|
+
else
|
121
|
+
response = create_response(request)
|
122
|
+
end
|
123
|
+
rescue Server::Error => e
|
124
|
+
response = error_response(e, request)
|
125
|
+
end
|
126
|
+
|
127
|
+
response
|
128
|
+
end
|
129
|
+
|
130
|
+
def validate_request(request)
|
131
|
+
required_keys = %w(jsonrpc method)
|
132
|
+
required_types = {
|
133
|
+
'jsonrpc' => [String],
|
134
|
+
'method' => [String],
|
135
|
+
'params' => [Hash, Array],
|
136
|
+
'id' => [String, Fixnum, Bignum, NilClass]
|
137
|
+
}
|
138
|
+
|
139
|
+
return false if !request.is_a?(Hash)
|
140
|
+
|
141
|
+
required_keys.each do |key|
|
142
|
+
return false if !request.has_key?(key)
|
143
|
+
end
|
144
|
+
|
145
|
+
required_types.each do |key, types|
|
146
|
+
return false if request.has_key?(key) && !types.any? { |type| request[key].is_a?(type) }
|
147
|
+
end
|
148
|
+
|
149
|
+
return false if request['jsonrpc'] != JSON_RPC_VERSION
|
150
|
+
|
151
|
+
true
|
152
|
+
end
|
153
|
+
|
154
|
+
def create_response(request)
|
155
|
+
method = request['method']
|
156
|
+
params = request['params']
|
157
|
+
result = dispatch_request(method, params)
|
158
|
+
|
159
|
+
response = success_response(request, result)
|
160
|
+
|
161
|
+
# A Notification is a Request object without an "id" member.
|
162
|
+
# The Server MUST NOT reply to a Notification, including those
|
163
|
+
# that are within a batch request.
|
164
|
+
response = nil if !request.has_key?('id')
|
165
|
+
|
166
|
+
return response
|
167
|
+
|
168
|
+
rescue Server::Error => e
|
169
|
+
raise e
|
170
|
+
rescue ArgumentError
|
171
|
+
raise Server::Error::InvalidParams.new
|
172
|
+
rescue Exception, StandardError => e
|
173
|
+
raise Server::Error::ApplicationError.new(e, @show_errors)
|
174
|
+
end
|
175
|
+
|
176
|
+
def dispatch_request(method, params)
|
177
|
+
method_name = method.to_s
|
178
|
+
handler = @router.handler_for_method(method_name)
|
179
|
+
method_name = @router.strip_method_namespace(method_name)
|
180
|
+
|
181
|
+
if handler.nil? \
|
182
|
+
|| !handler.class.jimson_exposed_methods.include?(method_name) \
|
183
|
+
|| !handler.respond_to?(method_name)
|
184
|
+
raise Server::Error::MethodNotFound.new(method)
|
185
|
+
end
|
186
|
+
|
187
|
+
if params.nil?
|
188
|
+
return handler.send(method_name)
|
189
|
+
elsif params.is_a?(Hash)
|
190
|
+
return handler.send(method_name, params)
|
191
|
+
else
|
192
|
+
return handler.send(method_name, *params)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def error_response(error, request = nil)
|
197
|
+
resp = {
|
198
|
+
'jsonrpc' => JSON_RPC_VERSION,
|
199
|
+
'error' => error.to_h,
|
200
|
+
}
|
201
|
+
if !!request && request.has_key?('id')
|
202
|
+
resp['id'] = request['id']
|
203
|
+
else
|
204
|
+
resp['id'] = nil
|
205
|
+
end
|
206
|
+
|
207
|
+
resp
|
208
|
+
end
|
209
|
+
|
210
|
+
def success_response(request, result)
|
211
|
+
{
|
212
|
+
'jsonrpc' => JSON_RPC_VERSION,
|
213
|
+
'result' => result,
|
214
|
+
'id' => request['id']
|
215
|
+
}
|
216
|
+
end
|
217
|
+
|
218
|
+
def parse_request(post)
|
219
|
+
data = MultiJson.decode(post)
|
220
|
+
rescue
|
221
|
+
raise Server::Error::ParseError.new
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,66 @@
|
|
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, show_error = false)
|
51
|
+
msg = "Server application error"
|
52
|
+
msg += ': ' + err.message + ' at ' + err.backtrace.first if show_error
|
53
|
+
super(-32099, msg)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
CODES = {
|
58
|
+
-32700 => ParseError,
|
59
|
+
-32600 => InvalidRequest,
|
60
|
+
-32601 => MethodNotFound,
|
61
|
+
-32602 => InvalidParams,
|
62
|
+
-32603 => InternalError
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Jimson
|
4
|
+
describe Client do
|
5
|
+
BOILERPLATE = {'jsonrpc' => '2.0', 'id' => 1}
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@resp_mock = mock('http_response')
|
9
|
+
ClientHelper.stub!(:make_id).and_return(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
after(:each) do
|
13
|
+
end
|
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
|
+
@client = Client.new(SPEC_URL)
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when using a symbol to specify a namespace" do
|
31
|
+
it "sends the method prefixed with the namespace in the request" do
|
32
|
+
expected = MultiJson.encode({
|
33
|
+
'jsonrpc' => '2.0',
|
34
|
+
'method' => 'foo.sum',
|
35
|
+
'params' => [1,2,3],
|
36
|
+
'id' => 1
|
37
|
+
})
|
38
|
+
response = MultiJson.encode(BOILERPLATE.merge({'result' => 42}))
|
39
|
+
RestClient.should_receive(:post).with(SPEC_URL, expected, {:content_type => 'application/json'}).and_return(@resp_mock)
|
40
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
41
|
+
@client[:foo].sum(1, 2, 3).should == 42
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when the namespace is nested" do
|
45
|
+
it "sends the method prefixed with the full namespace in the request" do
|
46
|
+
expected = MultiJson.encode({
|
47
|
+
'jsonrpc' => '2.0',
|
48
|
+
'method' => 'foo.bar.sum',
|
49
|
+
'params' => [1,2,3],
|
50
|
+
'id' => 1
|
51
|
+
})
|
52
|
+
response = MultiJson.encode(BOILERPLATE.merge({'result' => 42}))
|
53
|
+
RestClient.should_receive(:post).with(SPEC_URL, expected, {:content_type => 'application/json'}).and_return(@resp_mock)
|
54
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
55
|
+
@client[:foo][:bar].sum(1, 2, 3).should == 42
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when sending positional arguments" do
|
61
|
+
it "sends a request with the correct method and args" do
|
62
|
+
expected = MultiJson.encode({
|
63
|
+
'jsonrpc' => '2.0',
|
64
|
+
'method' => 'foo',
|
65
|
+
'params' => [1,2,3],
|
66
|
+
'id' => 1
|
67
|
+
})
|
68
|
+
response = MultiJson.encode(BOILERPLATE.merge({'result' => 42}))
|
69
|
+
RestClient.should_receive(:post).with(SPEC_URL, expected, {:content_type => 'application/json'}).and_return(@resp_mock)
|
70
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
71
|
+
@client['foo', 1, 2, 3].should == 42
|
72
|
+
end
|
73
|
+
|
74
|
+
context "when one of the args is an array" do
|
75
|
+
it "sends a request with the correct method and args" do
|
76
|
+
expected = MultiJson.encode({
|
77
|
+
'jsonrpc' => '2.0',
|
78
|
+
'method' => 'foo',
|
79
|
+
'params' => [[1,2],3],
|
80
|
+
'id' => 1
|
81
|
+
})
|
82
|
+
response = MultiJson.encode(BOILERPLATE.merge({'result' => 42}))
|
83
|
+
RestClient.should_receive(:post).with(SPEC_URL, expected, {:content_type => 'application/json'}).and_return(@resp_mock)
|
84
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
85
|
+
@client['foo', [1, 2], 3].should == 42
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "sending a single request" do
|
92
|
+
context "when using positional parameters" do
|
93
|
+
before(:each) do
|
94
|
+
@expected = MultiJson.encode({
|
95
|
+
'jsonrpc' => '2.0',
|
96
|
+
'method' => 'foo',
|
97
|
+
'params' => [1,2,3],
|
98
|
+
'id' => 1
|
99
|
+
})
|
100
|
+
end
|
101
|
+
it "sends a valid JSON-RPC request and returns the result" do
|
102
|
+
response = MultiJson.encode(BOILERPLATE.merge({'result' => 42}))
|
103
|
+
RestClient.should_receive(:post).with(SPEC_URL, @expected, {:content_type => 'application/json'}).and_return(@resp_mock)
|
104
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
105
|
+
client = Client.new(SPEC_URL)
|
106
|
+
client.foo(1,2,3).should == 42
|
107
|
+
end
|
108
|
+
|
109
|
+
it "sends a valid JSON-RPC request with custom options" do
|
110
|
+
response = MultiJson.encode(BOILERPLATE.merge({'result' => 42}))
|
111
|
+
RestClient.should_receive(:post).with(SPEC_URL, @expected, {:content_type => 'application/json', :timeout => 10000}).and_return(@resp_mock)
|
112
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
113
|
+
client = Client.new(SPEC_URL, :timeout => 10000)
|
114
|
+
client.foo(1,2,3).should == 42
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when one of the parameters is an array" do
|
119
|
+
it "sends a correct JSON-RPC request (array is preserved) and returns the result" do
|
120
|
+
expected = MultiJson.encode({
|
121
|
+
'jsonrpc' => '2.0',
|
122
|
+
'method' => 'foo',
|
123
|
+
'params' => [[1,2],3],
|
124
|
+
'id' => 1
|
125
|
+
})
|
126
|
+
response = MultiJson.encode(BOILERPLATE.merge({'result' => 42}))
|
127
|
+
RestClient.should_receive(:post).with(SPEC_URL, expected, {:content_type => 'application/json'}).and_return(@resp_mock)
|
128
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
129
|
+
client = Client.new(SPEC_URL)
|
130
|
+
client.foo([1,2],3).should == 42
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "sending a batch request" do
|
136
|
+
it "sends a valid JSON-RPC batch request and puts the results in the response objects" do
|
137
|
+
batch = MultiJson.encode([
|
138
|
+
{"jsonrpc" => "2.0", "method" => "sum", "params" => [1,2,4], "id" => "1"},
|
139
|
+
{"jsonrpc" => "2.0", "method" => "subtract", "params" => [42,23], "id" => "2"},
|
140
|
+
{"jsonrpc" => "2.0", "method" => "foo_get", "params" => [{"name" => "myself"}], "id" => "5"},
|
141
|
+
{"jsonrpc" => "2.0", "method" => "get_data", "id" => "9"}
|
142
|
+
])
|
143
|
+
|
144
|
+
response = MultiJson.encode([
|
145
|
+
{"jsonrpc" => "2.0", "result" => 7, "id" => "1"},
|
146
|
+
{"jsonrpc" => "2.0", "result" => 19, "id" => "2"},
|
147
|
+
{"jsonrpc" => "2.0", "error" => {"code" => -32601, "message" => "Method not found."}, "id" => "5"},
|
148
|
+
{"jsonrpc" => "2.0", "result" => ["hello", 5], "id" => "9"}
|
149
|
+
])
|
150
|
+
|
151
|
+
ClientHelper.stub!(:make_id).and_return('1', '2', '5', '9')
|
152
|
+
RestClient.should_receive(:post).with(SPEC_URL, batch, {:content_type => 'application/json'}).and_return(@resp_mock)
|
153
|
+
@resp_mock.should_receive(:body).at_least(:once).and_return(response)
|
154
|
+
client = Client.new(SPEC_URL)
|
155
|
+
|
156
|
+
sum = subtract = foo = data = nil
|
157
|
+
Jimson::Client.batch(client) do |batch|
|
158
|
+
sum = batch.sum(1,2,4)
|
159
|
+
subtract = batch.subtract(42,23)
|
160
|
+
foo = batch.foo_get('name' => 'myself')
|
161
|
+
data = batch.get_data
|
162
|
+
end
|
163
|
+
|
164
|
+
sum.succeeded?.should be_true
|
165
|
+
sum.is_error?.should be_false
|
166
|
+
sum.result.should == 7
|
167
|
+
|
168
|
+
subtract.result.should == 19
|
169
|
+
|
170
|
+
foo.is_error?.should be_true
|
171
|
+
foo.succeeded?.should be_false
|
172
|
+
foo.error['code'].should == -32601
|
173
|
+
|
174
|
+
data.result.should == ['hello', 5]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "error handling" do
|
179
|
+
context "when an error occurs in the Jimson::Client code" do
|
180
|
+
it "tags the raised exception with Jimson::Client::Error" do
|
181
|
+
client_helper = ClientHelper.new(SPEC_URL)
|
182
|
+
ClientHelper.stub!(:new).and_return(client_helper)
|
183
|
+
client = Client.new(SPEC_URL)
|
184
|
+
client_helper.stub!(:send_single_request).and_raise "intentional error"
|
185
|
+
lambda { client.foo }.should raise_error(Jimson::Client::Error)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|