jimson-temp 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|