json-rpc-client 0.1.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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +16 -0
- data/README.md +87 -0
- data/Rakefile +7 -0
- data/lib/json-rpc-client.rb +261 -0
- data/test/json-rpc-test.rb +162 -0
- data/test/test-helper.rb +23 -0
- metadata +207 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 45369d3297682f37bcf9119e27b3756a89ed464c
|
4
|
+
data.tar.gz: 9e1d8b669fcb8c51dd814161c7337abf7a7769d0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e5d14d9afa48ab3f7b084d9d435aa081b54b411490b4530b189f27f90fa585942a1cb266b14a93e440656eaa663db528f40e683af47d02c1eb484edc5375a64b
|
7
|
+
data.tar.gz: 3c6093337ccce5a371c8115a5ef5ef248869405b5e00a628813d080e48d7d5b9eb3f5db2823ccda06bc4b002168b817a4d6dbd0628f41dc9d29dbeadcd2d90d6
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Copyright (C) 2012-2013 Textalk AB
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
4
|
+
and associated documentation files (the "Software"), to deal in the Software without restriction,
|
5
|
+
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
6
|
+
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
7
|
+
furnished to do so, subject to the following conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
10
|
+
portions of the Software.
|
11
|
+
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
13
|
+
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
14
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
15
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
16
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
Asynchronous (EventMachine) JSON-RPC 2.0 client
|
2
|
+
===============================================
|
3
|
+
|
4
|
+
This gem is a client implementation for JSON-RPC 2.0. It uses EventMachine to
|
5
|
+
enable asynchronous communication with a JSON-RPC server. It can be used synchronously if
|
6
|
+
called within a (non-root) fiber.
|
7
|
+
|
8
|
+
Usage example for asynchronous behaviour:
|
9
|
+
```Ruby
|
10
|
+
wallet = JsonRpcClient.new('https://localhost:8332/') # Local bitcoin wallet
|
11
|
+
balance_rpc = wallet.getbalance()
|
12
|
+
balance_rpc.callback do |result|
|
13
|
+
puts result # => 90.12345678
|
14
|
+
end
|
15
|
+
|
16
|
+
balance_rpc.errback do |error|
|
17
|
+
puts error
|
18
|
+
# => "JsonRpcClient.Error: Bad method, code: -32601, data: nil"
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
Usage example for synchronous behaviour:
|
23
|
+
```Ruby
|
24
|
+
require 'eventmachine'
|
25
|
+
require 'json-rpc-client'
|
26
|
+
require 'fiber'
|
27
|
+
|
28
|
+
EventMachine.run do
|
29
|
+
# To use the syncing behaviour, use it in a fiber.
|
30
|
+
fiber = Fiber.new do
|
31
|
+
article = JsonRpcClient.new(
|
32
|
+
'https://shop.textalk.se/backend/jsonrpc/Article/14284660',
|
33
|
+
{asynchronous_calls: false}
|
34
|
+
)
|
35
|
+
puts article.get({name: true})
|
36
|
+
# => {:name=>{:sv=>"Presentkort 1000 kr", :en=>"Gift certificate 1000 SEK"}}
|
37
|
+
|
38
|
+
EventMachine.stop
|
39
|
+
end
|
40
|
+
|
41
|
+
fiber.resume
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
Logging
|
46
|
+
-------
|
47
|
+
|
48
|
+
The client supports both a default logger (for all instances) and a per instance logger.
|
49
|
+
Simply attach a logger of your choice(that responds to info, warning, error and debug) and
|
50
|
+
any interaction will be output as debug, and any errors as errors. Any per instance logger will
|
51
|
+
override the default logger for that instance.
|
52
|
+
|
53
|
+
```Ruby
|
54
|
+
require 'logger'
|
55
|
+
JsonRpcClient.default_logger = Logger.new($STDOUT)
|
56
|
+
wallet = JsonRpcClient.new('https://localhost:8332/') # Local bitcoin wallet
|
57
|
+
wallet.logger = MyCustomLogger.new()
|
58
|
+
```
|
59
|
+
|
60
|
+
Development
|
61
|
+
-----------
|
62
|
+
|
63
|
+
To set up a development environment, simply do:
|
64
|
+
|
65
|
+
```bash
|
66
|
+
bundle install
|
67
|
+
bundle exec rake # run the test suite
|
68
|
+
```
|
69
|
+
|
70
|
+
There are autotests located in the test folder and the framework used is
|
71
|
+
[Bacon](https://github.com/chneukirchen/bacon). They're all mocked with
|
72
|
+
[VCR](https://github.com/vcr/vcr)/[Webmock](https://github.com/bblimke/webmock)
|
73
|
+
so no internet connection is required to run them.
|
74
|
+
|
75
|
+
JSON-RPC 2.0
|
76
|
+
------------
|
77
|
+
|
78
|
+
JSON-RPC 2.0 is a very simple protocol for remote procedure calls,
|
79
|
+
agnostic of carrier (http, websocket, tcp, whatever…).
|
80
|
+
|
81
|
+
[JSON-RPC 2.0 Specification](http://www.jsonrpc.org/specification)
|
82
|
+
|
83
|
+
Copyright
|
84
|
+
---------
|
85
|
+
Copyright (C) 2012-2013, Textalk AB <http://textalk.se/>
|
86
|
+
|
87
|
+
JSON-RPC client is freely distributable under the terms of an MIT license. See [LICENCE](LICENSE).
|
data/Rakefile
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'em-http-request'
|
2
|
+
require 'json'
|
3
|
+
require 'addressable/uri'
|
4
|
+
|
5
|
+
# This implements a client for JSON-RPC (version 2) calls.
|
6
|
+
#
|
7
|
+
# @example Asynchronous
|
8
|
+
# wallet = JsonRpcClient.new('https://localhost:8332/') # Local bitcoin wallet
|
9
|
+
# balance_rpc = wallet.getbalance()
|
10
|
+
# balance_rpc.callback do |result|
|
11
|
+
# puts result # => 90.12345678
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# balance_rpc.errback do |error|
|
15
|
+
# puts error
|
16
|
+
# # => "JsonRpcClient.Error: Bad method, code: -32601, data: nil"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example Synchronous
|
20
|
+
# require 'eventmachine'
|
21
|
+
# require 'json-rpc-client'
|
22
|
+
# require 'fiber'
|
23
|
+
#
|
24
|
+
# EventMachine.run do
|
25
|
+
# # To use the syncing behaviour, use it in a fiber.
|
26
|
+
# fiber = Fiber.new do
|
27
|
+
# article = JsonRpcClient.new(
|
28
|
+
# 'https://shop.textalk.se/backend/jsonrpc/Article/14284660',
|
29
|
+
# {asynchronous_calls: false}
|
30
|
+
# )
|
31
|
+
# puts article.get({name: true})
|
32
|
+
# # => {:name=>{:sv=>"Presentkort 1000 kr", :en=>"Gift certificate 1000 SEK"}}
|
33
|
+
|
34
|
+
# EventMachine.stop
|
35
|
+
# end
|
36
|
+
|
37
|
+
# fiber.resume
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# @!attribute asynchronous_calls
|
41
|
+
# @return [Boolean] If method_missing calls are made asynchronously. Default: true
|
42
|
+
# @!attribute symbolize_names
|
43
|
+
# @return [Boolean] If the result of sync calls should have the names be symbols. Default: true
|
44
|
+
# @!attribute logger
|
45
|
+
# @return [Logger] The logger instance attached to the instance of JsonRpcClient.
|
46
|
+
# Should accept method calls to debug, info, warning & error. Use JsonRpcClient.log for logging
|
47
|
+
class JsonRpcClient
|
48
|
+
attr_accessor :asynchronous_calls, :symbolize_names, :logger
|
49
|
+
|
50
|
+
# Invalid JSON was received by the server.
|
51
|
+
# An error occurred on the server while parsing the JSON text.
|
52
|
+
INVALID_JSON = -32700
|
53
|
+
# The JSON sent is not a valid Request object.
|
54
|
+
INVALID_REQUEST = -32600
|
55
|
+
# The method does not exist / is not available.
|
56
|
+
METHOD_NOT_FOUND = -32601
|
57
|
+
# Invalid method parameter(s).
|
58
|
+
INVALID_PARAMS = -32602
|
59
|
+
# Internal JSON-RPC error.
|
60
|
+
INTERNAL_ERROR = -32603
|
61
|
+
|
62
|
+
# Create an instance to call the RPC methods on.
|
63
|
+
#
|
64
|
+
# @param [String, Addressable::URI, #to_str] service_uri The URI to connect to.
|
65
|
+
# @param [Hash] options Options hash to pass to the instance.
|
66
|
+
# See Instance Attribute Details in the documentation for more details on supported options.
|
67
|
+
def initialize(service_uri, options = {})
|
68
|
+
@uri = Addressable::URI.parse(service_uri)
|
69
|
+
@asynchronous_calls = options.has_key?(:asynchronous_calls) ?
|
70
|
+
!!options[:asynchronous_calls] :
|
71
|
+
true
|
72
|
+
@symbolize_names = options.has_key?(:symbolize_names) ? !!options[:symbolize_names] : true
|
73
|
+
@logger = options[:logger]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Called whenever the current object receives a method call that it does not respond to.
|
77
|
+
# Will make a call asynchronously or synchronously depending on asynchronous_calls.
|
78
|
+
#
|
79
|
+
# @param [String] method the API method, ie get, set.
|
80
|
+
# @param [Array] params the parameters sent with the method call.
|
81
|
+
def method_missing(method, *params)
|
82
|
+
@asynchronous_calls ? self._call_async(method, params) : self._call_sync(method, params)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Makes the call asynchronously and returns a EM::Deferrable.
|
86
|
+
# The underscore is there to avoid conflicts with server methods, not to denote a private method.
|
87
|
+
#
|
88
|
+
# @param [String] method The API method, ie get, set etc.
|
89
|
+
# @param [Array, Hash] params The parameters that should be sent along in the post body.
|
90
|
+
# @return [EM::Deferrable] The JsonRpcClient::Request as data.
|
91
|
+
def _call_async(method, params)
|
92
|
+
return Request.new({
|
93
|
+
service_uri: @uri.to_s,
|
94
|
+
method: method,
|
95
|
+
params: params,
|
96
|
+
logger: @logger,
|
97
|
+
symbolize_names: @symbolize_names
|
98
|
+
});
|
99
|
+
end
|
100
|
+
|
101
|
+
# Make the call synchronously, returns the result directly.
|
102
|
+
# The underscore is there to avoid conflicts with server methods, not to denote a private method.
|
103
|
+
#
|
104
|
+
# @param [String] method The API method, ie get, set etc.
|
105
|
+
# @param [Array, Hash] params The parameters that should be sent along in the post body.
|
106
|
+
# @return [Hash] The result.
|
107
|
+
# @raise JsonRpcClient::Error When the request responds with failed status.
|
108
|
+
def _call_sync(method, params)
|
109
|
+
f = Fiber.current
|
110
|
+
|
111
|
+
request = _call_async(method, params)
|
112
|
+
|
113
|
+
request.callback do |*args|
|
114
|
+
# If we happen to be in the calling fiber, return the data directly.
|
115
|
+
return args.size == 1 ? args.first : args if f == Fiber.current
|
116
|
+
# else, return it to the yield call below (in the correct fiber).
|
117
|
+
f.resume(*args)
|
118
|
+
end
|
119
|
+
|
120
|
+
request.errback do |error|
|
121
|
+
json_rpc_error = Error.new(error[:message], error[:code], error[:data])
|
122
|
+
# If we happen to be in the calling fiber, raise the error directly.
|
123
|
+
raise json_rpc_error if f == Fiber.current
|
124
|
+
# else, return it to the yield call below (in the correct fiber).
|
125
|
+
f.resume(json_rpc_error)
|
126
|
+
end
|
127
|
+
|
128
|
+
begin
|
129
|
+
response = Fiber.yield # will yield and return the data or raise the error.
|
130
|
+
rescue FiberError
|
131
|
+
raise "To to use the syncing behaviour in JsonRpcClient, the call must be in a fiber."
|
132
|
+
end
|
133
|
+
raise response if response.kind_of?(Error)
|
134
|
+
return response
|
135
|
+
end
|
136
|
+
|
137
|
+
# Makes a notify call by just sending a HTTP request and not caring about the response.
|
138
|
+
# The underscore is there to avoid conflicts with server methods, not to denote a private method.
|
139
|
+
#
|
140
|
+
# @param [String] method The API method, ie get, set etc.
|
141
|
+
# @param [Array, Hash] params The parameters that should be sent along in the post body.
|
142
|
+
def _notify(method, params)
|
143
|
+
post_body = {
|
144
|
+
method: method,
|
145
|
+
params: params,
|
146
|
+
jsonrpc: '2.0'
|
147
|
+
}.to_json
|
148
|
+
|
149
|
+
|
150
|
+
EM::HttpRequest.new(@uri.to_s).post :body => post_body
|
151
|
+
self.class.log(:debug, "NOTIFY: #{@uri.to_s} --> #{post_body}", @logger)
|
152
|
+
end
|
153
|
+
|
154
|
+
# The logger to be used for an instances if they don't have a logger set on that instance.
|
155
|
+
@@default_logger = nil
|
156
|
+
|
157
|
+
# @return The default logger object.
|
158
|
+
def self.default_logger()
|
159
|
+
@@default_logger
|
160
|
+
end
|
161
|
+
|
162
|
+
# Add a default logging instance, that should accept method calls to debug, info, warning & error.
|
163
|
+
# Don't use directly, use self.log.
|
164
|
+
def self.default_logger=(logger)
|
165
|
+
@@default_logger = logger
|
166
|
+
end
|
167
|
+
|
168
|
+
# Logging class that takes severity and message. Only logs if a logger is attached.
|
169
|
+
#
|
170
|
+
# @param [Symbol, String] level The severity, ie a method of a logger, (info, debug, warn, error).
|
171
|
+
# @param [String] message The log message.
|
172
|
+
# @param [Logger] logger An instance of a logger class.
|
173
|
+
def self.log(level, message, logger = nil)
|
174
|
+
logger = logger || @@default_logger
|
175
|
+
logger.send(level.to_sym, message) if logger.respond_to?(level.to_sym)
|
176
|
+
end
|
177
|
+
|
178
|
+
# This class corresponds to the JSON-RPC error object gotten from the server.
|
179
|
+
# A "faked" instance of this will be thrown for communication errors as well.
|
180
|
+
class Error < RuntimeError
|
181
|
+
attr_reader :code, :data
|
182
|
+
def initialize(msg, code, data)
|
183
|
+
super(msg)
|
184
|
+
@code = code
|
185
|
+
@data = data
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
# Returns the contents of the current error object as a string.
|
190
|
+
#
|
191
|
+
# @return [String]
|
192
|
+
def inspect
|
193
|
+
%|#{self.class}: #{self.message}, code: #{@code.inspect}, data: #{@data.inspect}|
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# This class makes a single request to the JSON-RPC service as a EventMachine::Deferrable.
|
198
|
+
# The deferrable object will give a successful callback in the result-part of the response.
|
199
|
+
# A unsuccessful request will set the deferred status as failed, and will not deliver a result
|
200
|
+
# only the JSON-RPC error object as a Hash.
|
201
|
+
class Request
|
202
|
+
include EM::Deferrable
|
203
|
+
|
204
|
+
def initialize(params)
|
205
|
+
service_uri = params[:service_uri]
|
206
|
+
post_body = {
|
207
|
+
method: params[:method],
|
208
|
+
params: params[:params],
|
209
|
+
id: 'jsonrpc',
|
210
|
+
jsonrpc: '2.0',
|
211
|
+
}.to_json
|
212
|
+
|
213
|
+
http = EM::HttpRequest.new(service_uri).post :body => post_body
|
214
|
+
JsonRpcClient.log(:debug, "NEW REQUEST: #{service_uri} --> #{post_body}", params[:logger])
|
215
|
+
|
216
|
+
http.callback do |response|
|
217
|
+
begin
|
218
|
+
resp = JSON.parse(response.response, {symbolize_names: params[:symbolize_names]})
|
219
|
+
JsonRpcClient.log(
|
220
|
+
:debug,
|
221
|
+
"REQUEST FINISH: #{service_uri} METHOD: #{params[:method]} RESULT: #{resp}",
|
222
|
+
params[:logger]
|
223
|
+
)
|
224
|
+
|
225
|
+
if resp.has_key?(:error) || resp.has_key?("error")
|
226
|
+
JsonRpcClient.log(
|
227
|
+
:error,
|
228
|
+
"Error in response from #{service_uri}: #{resp[:error]}",
|
229
|
+
params[:logger]
|
230
|
+
)
|
231
|
+
self.set_deferred_status :failed, resp[:error] || resp["error"]
|
232
|
+
end
|
233
|
+
self.set_deferred_status :succeeded, resp[:result] || resp["result"]
|
234
|
+
rescue JSON::ParserError => e
|
235
|
+
JsonRpcClient.log(
|
236
|
+
:error,
|
237
|
+
"Got exception during parsing of #{response}: #{e}",
|
238
|
+
params[:logger]
|
239
|
+
)
|
240
|
+
|
241
|
+
# Making an error object in the same style as a JSON RPC error.
|
242
|
+
set_deferred_status :failed, {
|
243
|
+
code: JsonRpcClient::INVALID_JSON,
|
244
|
+
message: e.message,
|
245
|
+
data: e
|
246
|
+
}
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
http.errback do |response|
|
251
|
+
JsonRpcClient.log(:error, "Error in http request: #{response.error}", params[:logger])
|
252
|
+
set_deferred_status :failed, {
|
253
|
+
code: JsonRpcClient::INVALID_JSON,
|
254
|
+
message: response.error
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
self
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'em-spec/bacon'
|
3
|
+
require 'vcr'
|
4
|
+
require 'json'
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + '/test-helper.rb')
|
6
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib/json-rpc-client'))
|
7
|
+
|
8
|
+
# Sets up a eventmachine channel to be able to notify subscribers when a http request has been done
|
9
|
+
# With the async nature and with _notify not returning a Deferrable we can't know if it sent
|
10
|
+
# Or when it finished it's request, hence why we hook onto after_http_request and use that.
|
11
|
+
vcr_channel = EventMachine::Channel.new
|
12
|
+
|
13
|
+
VCR.configure do |c|
|
14
|
+
c.cassette_library_dir = File.expand_path(File.dirname(__FILE__)) + '/vcr_cassettes'
|
15
|
+
c.hook_into :webmock
|
16
|
+
# After a http request, push a notification in the EM channel with the request and response.
|
17
|
+
c.after_http_request do |request, response|
|
18
|
+
vcr_channel.push({request: request, response: response})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
EM.spec_backend = EventMachine::Spec::Bacon
|
23
|
+
EM.describe 'json-rpc-test' do
|
24
|
+
|
25
|
+
should 'Test default logging' do
|
26
|
+
VCR.use_cassette('logger-test') do
|
27
|
+
custom_logger = CustomLogger.new()
|
28
|
+
JsonRpcClient.default_logger = custom_logger
|
29
|
+
client = JsonRpcClient.new('https://shop.textalk.se/backend/jsonrpc/Article/12565607')
|
30
|
+
(JsonRpcClient.default_logger == custom_logger).should.equal true
|
31
|
+
JsonRpcClient.default_logger = nil #cleanup
|
32
|
+
end
|
33
|
+
|
34
|
+
done
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'Make a sucessful sync json-rpc call and recieve symbolized values' do
|
38
|
+
VCR.use_cassette('sync-symbolized') do
|
39
|
+
client = JsonRpcClient.new(
|
40
|
+
'https://shop.textalk.se/backend/jsonrpc/Article/12565484',
|
41
|
+
{asynchronous_calls: false}
|
42
|
+
)
|
43
|
+
client.logger = CustomLogger.new()
|
44
|
+
uid = client.get(uid: true) # Calling method get on RPC backend, only asking for uid
|
45
|
+
client.logger.logs[0] == "NEW REQUEST: https://shop.textalk.se/backend/jsonrpc/Article/12565484 --> {\"method\":\"get\",\"params\":[{\"uid\":true}],\"id\":\"jsonrpc\",\"jsonrpc\":\"2.0\"}"
|
46
|
+
client.logger.logs[1] == "REQUEST FINISH: https://shop.textalk.se/backend/jsonrpc/Article/12565484 METHOD: get RESULT: {:jsonrpc=>\"2.0\", :id=>\"jsonrpc\", :result=>{:uid=>\"12565484\"}}"
|
47
|
+
(uid == {uid: "12565484"}).should.equal true
|
48
|
+
end
|
49
|
+
|
50
|
+
done
|
51
|
+
end
|
52
|
+
|
53
|
+
should 'Make a sucessful sync json-rpc call and recieve non-symbolized values' do
|
54
|
+
VCR.use_cassette('sync-nonsymbolized') do
|
55
|
+
client = JsonRpcClient.new('https://shop.textalk.se/backend/jsonrpc/Article/14284660', {
|
56
|
+
asynchronous_calls: false,
|
57
|
+
symbolize_names: false
|
58
|
+
})
|
59
|
+
name = client.get(name: true) # Calling same method, asking for name. Should not symobolize result
|
60
|
+
(name == {"name"=>{"sv"=>"Presentkort 1000 kr", "en"=>"Gift certificate 1000 SEK"}}).should.equal true
|
61
|
+
end
|
62
|
+
|
63
|
+
done
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'Make a sucessful async json-rpc call and recieve a JsonRpcClient::Request' do
|
67
|
+
VCR.insert_cassette("async")
|
68
|
+
client = JsonRpcClient.new('https://shop.textalk.se/backend/jsonrpc/Article/12565605')
|
69
|
+
request = client.uid() # Calling a valid uri, should get a request back
|
70
|
+
request.is_a?(JsonRpcClient::Request).should.equal true
|
71
|
+
request.is_a?(EM::Deferrable).should.equal true
|
72
|
+
request.callback do |result|
|
73
|
+
result.should.equal "12565605"
|
74
|
+
VCR.eject_cassette
|
75
|
+
done
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
should 'Make a notify call' do
|
80
|
+
VCR.insert_cassette("notify")
|
81
|
+
# Subscribe to the EM channel, so that we only after the request do the matching and
|
82
|
+
# eject the VCR cassette and end the request.
|
83
|
+
subscription = vcr_channel.subscribe do |request_response_hash|
|
84
|
+
request = request_response_hash[:request]
|
85
|
+
(JSON.parse(request[:body]) == {"method"=>"uid", "params"=>{}, "jsonrpc"=>"2.0"}).should.equal true
|
86
|
+
vcr_channel.unsubscribe(subscription) # Unsub from the channel
|
87
|
+
VCR.eject_cassette
|
88
|
+
done # Exit out of the request
|
89
|
+
end
|
90
|
+
client = JsonRpcClient.new('https://shop.textalk.se/backend/jsonrpc/Article/12565604')
|
91
|
+
client._notify(:uid, {}) # Just shouldn't raise an error, and should hit backend.
|
92
|
+
end
|
93
|
+
|
94
|
+
should 'raise jsonrpcerror on bad response' do
|
95
|
+
should.raise(JsonRpcClient::Error) do
|
96
|
+
VCR.use_cassette('bad-response') do
|
97
|
+
client = JsonRpcClient.new('http://www.google.com/', {asynchronous_calls: false})
|
98
|
+
client.dummy() # Calling non-existing method on non-rpc backend.
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
done
|
103
|
+
end
|
104
|
+
|
105
|
+
should 'get an http error and raise jsonrpcerror on bad backend url' do
|
106
|
+
should.raise(JsonRpcClient::Error) do
|
107
|
+
VCR.use_cassette('weird-url') do
|
108
|
+
client = JsonRpcClient.new('http://@ł€®þ.com/', {asynchronous_calls: false})
|
109
|
+
client.dummy() # Calling non-existing method on non-rpc backend.
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
done
|
114
|
+
end
|
115
|
+
|
116
|
+
should 'get a jsonrpcerror from the backend' do
|
117
|
+
should.raise(JsonRpcClient::Error) do
|
118
|
+
VCR.use_cassette('jsonrpcerror-method-missing') do
|
119
|
+
client = JsonRpcClient.new(
|
120
|
+
'http://shop.textalk.se/backend/jsonrpc/Foo',
|
121
|
+
{asynchronous_calls: false}
|
122
|
+
)
|
123
|
+
error = client.dummy() # Calling non-existing method on rpc backend.
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
done
|
128
|
+
end
|
129
|
+
|
130
|
+
should 'test the error inspect method' do
|
131
|
+
VCR.use_cassette('inspect') do
|
132
|
+
begin
|
133
|
+
client = JsonRpcClient.new(
|
134
|
+
'http://shop.textalk.se/backend/jsonrpc/Article/12565604',
|
135
|
+
{asynchronous_calls: false}
|
136
|
+
)
|
137
|
+
client.dummy()
|
138
|
+
rescue JsonRpcClient::Error => e
|
139
|
+
e.inspect.should.equal "JsonRpcClient::Error: Method not found: dummy, code: -32601, data: nil"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
done
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe 'Non-fiber' do
|
148
|
+
|
149
|
+
should 'raise an error about no fibers being used' do
|
150
|
+
VCR.use_cassette('non-fiber') do
|
151
|
+
should.raise(RuntimeError) do
|
152
|
+
EM.run do
|
153
|
+
client = JsonRpcClient.new(
|
154
|
+
'http://shop.textalk.se/backend/jsonrpc/Article/12565604',
|
155
|
+
{asynchronous_calls: false}
|
156
|
+
)
|
157
|
+
client.get()
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
data/test/test-helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'simplecov-rcov'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
6
|
+
SimpleCov.command_name 'bacon'
|
7
|
+
SimpleCov.start
|
8
|
+
|
9
|
+
# A small custom logger for the tests, all log messages are saved to an array.
|
10
|
+
class CustomLogger < Logger
|
11
|
+
attr_reader :logs
|
12
|
+
def initialize()
|
13
|
+
@logs = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def debug(message)
|
17
|
+
@logs << message
|
18
|
+
end
|
19
|
+
|
20
|
+
def error(message)
|
21
|
+
@logs << message
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: json-rpc-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fredrik Liljegren
|
8
|
+
- Lars Olsson
|
9
|
+
- Denis Dervisevic
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-04-18 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: addressable
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - '>='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: em-http-request
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: json
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: bacon
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: em-spec
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rack
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: rake
|
101
|
+
requirement: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
type: :development
|
107
|
+
prerelease: false
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: simplecov
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
type: :development
|
121
|
+
prerelease: false
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: simplecov-rcov
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
- !ruby/object:Gem::Dependency
|
142
|
+
name: vcr
|
143
|
+
requirement: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
type: :development
|
149
|
+
prerelease: false
|
150
|
+
version_requirements: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - '>='
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
- !ruby/object:Gem::Dependency
|
156
|
+
name: webmock
|
157
|
+
requirement: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - '='
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: 1.9.0
|
162
|
+
type: :development
|
163
|
+
prerelease: false
|
164
|
+
version_requirements: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - '='
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: 1.9.0
|
169
|
+
description: Asynchronous (EventMachine) JSON-RPC 2.0 over HTTP client.
|
170
|
+
email:
|
171
|
+
executables: []
|
172
|
+
extensions: []
|
173
|
+
extra_rdoc_files: []
|
174
|
+
files:
|
175
|
+
- lib/json-rpc-client.rb
|
176
|
+
- Gemfile
|
177
|
+
- LICENSE
|
178
|
+
- README.md
|
179
|
+
- Rakefile
|
180
|
+
- test/json-rpc-test.rb
|
181
|
+
- test/test-helper.rb
|
182
|
+
homepage: https://github.com/Textalk/json-rpc-client-ruby
|
183
|
+
licenses:
|
184
|
+
- MIT
|
185
|
+
metadata: {}
|
186
|
+
post_install_message:
|
187
|
+
rdoc_options: []
|
188
|
+
require_paths:
|
189
|
+
- lib
|
190
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - '>='
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 1.9.0
|
195
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - '>='
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: '0'
|
200
|
+
requirements: []
|
201
|
+
rubyforge_project:
|
202
|
+
rubygems_version: 2.0.3
|
203
|
+
signing_key:
|
204
|
+
specification_version: 4
|
205
|
+
summary: JSON-RPC 2.0 client.
|
206
|
+
test_files: []
|
207
|
+
has_rdoc:
|