jsonrpc2 0.0.7 → 0.0.8
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.markdown +16 -0
- data/lib/jsonrpc2/html.rb +6 -3
- data/lib/jsonrpc2/interface.rb +165 -160
- data/lib/jsonrpc2/version.rb +1 -1
- metadata +2 -2
data/README.markdown
CHANGED
@@ -2,6 +2,22 @@
|
|
2
2
|
|
3
3
|
A Rack compatible, documenting JSON-RPC 2 DSL/server implementation for ruby.
|
4
4
|
|
5
|
+
## Changes
|
6
|
+
|
7
|
+
* 0.0.8 - 3-Sep-2012
|
8
|
+
Add #request to access Rack::Request object
|
9
|
+
Make URLs in HTML interface clickable
|
10
|
+
|
11
|
+
* 0.0.7 - 27-Aug-2012
|
12
|
+
Add bundled Bootstrap assets for HTML test interface
|
13
|
+
|
14
|
+
* 0.0.6 - 24-Aug-2012
|
15
|
+
Add Date/Time/DateTime as special string types with regex checks for validation
|
16
|
+
|
17
|
+
* 0.0.5 - 19-Jul-2012
|
18
|
+
Add commandline client jsonrpc2
|
19
|
+
Add #auth to access currently authenticated username
|
20
|
+
|
5
21
|
## Features
|
6
22
|
|
7
23
|
* Inline documentation
|
data/lib/jsonrpc2/html.rb
CHANGED
@@ -167,9 +167,12 @@ EOS
|
|
167
167
|
end
|
168
168
|
# Format JSON result
|
169
169
|
def format_result(result)
|
170
|
-
CGI.escapeHTML(JSON.pretty_unparse(JSON.parse(result)))
|
171
|
-
|
172
|
-
|
170
|
+
CGI.escapeHTML(JSON.pretty_unparse(JSON.parse(result))).gsub(%r<("|")https?://[^"]+?("|")>) do |str|
|
171
|
+
url = CGI.unescapeHTML(str)[1...-1]
|
172
|
+
%Q["<a href="#{CGI.escapeHTML(url)}">#{CGI.escapeHTML(url)}</a>"]
|
173
|
+
end
|
174
|
+
rescue Exception => e
|
175
|
+
CGI.escapeHTML(result.to_s)
|
173
176
|
end
|
174
177
|
end
|
175
178
|
end
|
data/lib/jsonrpc2/interface.rb
CHANGED
@@ -17,7 +17,7 @@ module JSONRPC2
|
|
17
17
|
|
18
18
|
# Base class for JSONRPC2 interface
|
19
19
|
class Interface
|
20
|
-
|
20
|
+
class << self
|
21
21
|
|
22
22
|
# @!group Authentication
|
23
23
|
|
@@ -25,13 +25,13 @@ class Interface
|
|
25
25
|
#
|
26
26
|
# @param [#check] args An object that responds to check(environment, json_call_data)
|
27
27
|
# @return [#check, nil] Currently set object or nil
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
def auth_with *args
|
29
|
+
if args.empty?
|
30
|
+
return @auth_with
|
31
|
+
else
|
32
|
+
@auth_with = args[0]
|
33
|
+
end
|
34
|
+
end
|
35
35
|
|
36
36
|
# @!endgroup
|
37
37
|
|
@@ -66,14 +66,15 @@ class Interface
|
|
66
66
|
|
67
67
|
# @!endgroup
|
68
68
|
|
69
|
-
|
69
|
+
end
|
70
70
|
|
71
71
|
# Create new interface object
|
72
72
|
#
|
73
73
|
# @param [Hash] env Rack environment
|
74
|
-
|
75
|
-
|
76
|
-
|
74
|
+
def initialize(env)
|
75
|
+
@_jsonrpc_env = env
|
76
|
+
@_jsonrpc_request = Rack::Request.new(env)
|
77
|
+
end
|
77
78
|
# Internal
|
78
79
|
def rack_dispatch(rpcData)
|
79
80
|
catch(:rack_response) do
|
@@ -87,54 +88,58 @@ class Interface
|
|
87
88
|
# @param [Hash,Array] rpc_data Array of calls or Hash containing one call
|
88
89
|
# @return [Hash,Array] Depends on input, but either a hash result or an array of results corresponding to calls.
|
89
90
|
def dispatch(rpc_data)
|
90
|
-
|
91
|
+
case rpc_data
|
91
92
|
when Array
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
93
|
+
rpc_data.map { |rpc| dispatch_single(rpc) }.to_json
|
94
|
+
else
|
95
|
+
dispatch_single(rpc_data).to_json
|
96
|
+
end
|
97
|
+
end
|
97
98
|
|
98
|
-
|
99
|
+
protected
|
99
100
|
# JSON result helper
|
100
|
-
|
101
|
-
|
102
|
-
|
101
|
+
def response_ok(id, result)
|
102
|
+
{ 'jsonrpc' => '2.0', 'result' => result, 'id' => id }
|
103
|
+
end
|
103
104
|
# JSON error helper
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
def response_error(code, message, data)
|
106
|
+
{ 'jsonrpc' => '2.0', 'error' => { 'code' => code, 'message' => message, 'data' => data }, 'id' => (@_jsonrpc_call && @_jsonrpc_call['id'] || nil) }
|
107
|
+
end
|
107
108
|
# Params helper
|
108
109
|
def params
|
109
|
-
@
|
110
|
+
@_jsonrpc_call['params']
|
110
111
|
end
|
111
112
|
# Auth info
|
112
113
|
def auth
|
113
|
-
@
|
114
|
+
@_jsonrpc_auth
|
115
|
+
end
|
116
|
+
# Rack::Request
|
117
|
+
def request
|
118
|
+
@_jsonrpc_request
|
114
119
|
end
|
115
120
|
# Check call validity and authentication & make a single method call
|
116
121
|
#
|
117
122
|
# @param [Hash] rpc JSON-RPC-2 call
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
@
|
123
|
-
|
124
|
-
|
125
|
-
if self.class.auth_with && ! @
|
126
|
-
(@
|
123
|
+
def dispatch_single(rpc)
|
124
|
+
unless rpc.has_key?('id') && rpc.has_key?('method') && rpc['jsonrpc'].eql?('2.0')
|
125
|
+
return response_error(-32600, 'Invalid request', nil)
|
126
|
+
end
|
127
|
+
@_jsonrpc_call = rpc
|
128
|
+
|
129
|
+
begin
|
130
|
+
if self.class.auth_with && ! @_jsonrpc_auth
|
131
|
+
(@_jsonrpc_auth = self.class.auth_with.client_check(@_jsonrpc_env, rpc)) or raise AuthFail, "Invalid credentials"
|
127
132
|
end
|
128
133
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
134
|
+
call(rpc['method'], rpc['id'], rpc['params'])
|
135
|
+
rescue AuthFail => e
|
136
|
+
response_error(-32000, "AuthFail: #{e.class}: #{e.message}", {}) # XXX: Change me
|
137
|
+
rescue APIFail => e
|
138
|
+
response_error(-32000, "APIFail: #{e.class}: #{e.message}", {}) # XXX: Change me
|
139
|
+
rescue Exception => e
|
140
|
+
response_error(-32000, "#{e.class}: #{e.message}", e.backtrace) # XXX: Change me
|
141
|
+
end
|
142
|
+
end
|
138
143
|
# List API methods
|
139
144
|
#
|
140
145
|
# @return [Array] List of api method names
|
@@ -148,41 +153,41 @@ class Interface
|
|
148
153
|
# @param [Integer] id Method call ID - for response
|
149
154
|
# @param [Hash] params Method parameters
|
150
155
|
# @return [Hash] JSON response
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
156
|
+
def call(method, id, params)
|
157
|
+
if api_methods.include?(method)
|
158
|
+
begin
|
159
|
+
Types.valid_params?(self.class, method, params)
|
160
|
+
rescue Exception => e
|
161
|
+
return response_error(-32602, "Invalid params - #{e.message}", {})
|
162
|
+
end
|
158
163
|
|
159
164
|
if self.method(method).arity.zero?
|
160
|
-
|
165
|
+
result = send(method)
|
161
166
|
else
|
162
|
-
|
167
|
+
result = send(method, params)
|
163
168
|
end
|
164
169
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
170
|
+
begin
|
171
|
+
Types.valid_result?(self.class, method, result)
|
172
|
+
rescue Exception => e
|
173
|
+
return response_error(-32602, "Invalid result - #{e.message}", {})
|
174
|
+
end
|
175
|
+
|
176
|
+
response_ok(id, result)
|
177
|
+
else
|
178
|
+
response_error(-32601, "Unknown method `#{method.inspect}'", {})
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class << self
|
178
183
|
# Store parameter in internal hash when building API
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
184
|
+
def ___append_param name, type, options
|
185
|
+
@params ||= []
|
186
|
+
unless options.has_key?(:required)
|
187
|
+
options[:required] = true
|
188
|
+
end
|
189
|
+
@params << options.merge({ :name => name, :type => type })
|
190
|
+
end
|
186
191
|
private :___append_param
|
187
192
|
|
188
193
|
# @!group DSL
|
@@ -191,119 +196,119 @@ class Interface
|
|
191
196
|
#
|
192
197
|
# @param [String] name parameter name
|
193
198
|
# @param [String] type description of type see {Types}
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
199
|
+
def param name, type, desc = nil, options = nil
|
200
|
+
if options.nil? && desc.is_a?(Hash)
|
201
|
+
options, desc = desc, nil
|
202
|
+
end
|
203
|
+
options ||= {}
|
204
|
+
options[:desc] = desc if desc.is_a?(String)
|
205
|
+
|
206
|
+
___append_param name, type, options
|
207
|
+
end
|
203
208
|
|
204
209
|
# Define an optional parameter for next method
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
210
|
+
def optional name, type, desc = nil, options = nil
|
211
|
+
if options.nil? && desc.is_a?(Hash)
|
212
|
+
options, desc = desc, nil
|
213
|
+
end
|
214
|
+
options ||= {}
|
215
|
+
options[:desc] = desc if desc.is_a?(String)
|
211
216
|
|
212
|
-
|
213
|
-
|
217
|
+
___append_param(name, type, options.merge(:required => false))
|
218
|
+
end
|
214
219
|
|
215
220
|
# Define type of return value for next method
|
216
|
-
|
217
|
-
|
218
|
-
|
221
|
+
def result type, desc = nil
|
222
|
+
@result = { :type => type, :desc => desc }
|
223
|
+
end
|
219
224
|
|
220
225
|
# Set description for next method
|
221
|
-
|
222
|
-
|
223
|
-
|
226
|
+
def desc str
|
227
|
+
@desc = str
|
228
|
+
end
|
224
229
|
|
225
230
|
# Add an example for next method
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
231
|
+
def example desc, code
|
232
|
+
@examples ||= []
|
233
|
+
@examples << { :desc => desc, :code => code }
|
234
|
+
end
|
230
235
|
|
231
236
|
# Define a custom type
|
232
|
-
|
233
|
-
|
234
|
-
|
237
|
+
def type name, *fields
|
238
|
+
@types ||= {}
|
239
|
+
type = JsonObjectType.new(name, fields)
|
235
240
|
|
236
|
-
|
237
|
-
|
238
|
-
|
241
|
+
if block_given?
|
242
|
+
yield(type)
|
243
|
+
end
|
239
244
|
|
240
|
-
|
241
|
-
|
245
|
+
@types[name] = type
|
246
|
+
end
|
242
247
|
|
243
248
|
# Group methods
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
249
|
+
def section name, summary=nil
|
250
|
+
@sections ||= []
|
251
|
+
@sections << {:name => name, :summary => summary}
|
252
|
+
|
253
|
+
@current_section = name
|
254
|
+
if block_given?
|
255
|
+
yield
|
256
|
+
@current_section = nil
|
257
|
+
end
|
258
|
+
end
|
254
259
|
|
255
260
|
# Exclude next method from documentation
|
256
|
-
|
257
|
-
|
258
|
-
|
261
|
+
def nodoc
|
262
|
+
@nodoc = true
|
263
|
+
end
|
259
264
|
|
260
265
|
# Set interface title
|
261
|
-
|
262
|
-
|
263
|
-
|
266
|
+
def title str = nil
|
267
|
+
@title = str if str
|
268
|
+
end
|
264
269
|
|
265
270
|
# Sets introduction for interface
|
266
|
-
|
267
|
-
|
268
|
-
|
271
|
+
def introduction str = nil
|
272
|
+
@introduction = str if str
|
273
|
+
end
|
269
274
|
|
270
275
|
# @!endgroup
|
271
276
|
|
272
277
|
# Catch methods added to class & store documentation
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
278
|
+
def method_added(name)
|
279
|
+
return if self == JSONRPC2::Interface
|
280
|
+
@about ||= {}
|
281
|
+
method = {}
|
282
|
+
method[:params] = @params if @params
|
283
|
+
method[:returns] = @result if @result
|
284
|
+
method[:desc] = @desc if @desc
|
285
|
+
method[:examples] = @examples if @examples
|
286
|
+
|
287
|
+
if method.empty?
|
288
|
+
if public_methods(false).include?(name)
|
289
|
+
unless @nodoc
|
290
|
+
#logger.info("#{name} has no API documentation... :(")
|
291
|
+
end
|
292
|
+
else
|
293
|
+
#logger.debug("#{name} isn't public - so no API")
|
294
|
+
end
|
295
|
+
else
|
296
|
+
method[:name] = name
|
297
|
+
method[:section] = @current_section
|
298
|
+
method[:index] = @about.size
|
299
|
+
@about[name.to_s] = method
|
300
|
+
end
|
301
|
+
|
302
|
+
@result = nil
|
303
|
+
@params = nil
|
304
|
+
@desc = nil
|
305
|
+
@examples = nil
|
306
|
+
@nodoc = false
|
307
|
+
end
|
303
308
|
private :method_added
|
304
|
-
|
309
|
+
attr_reader :about, :types
|
305
310
|
|
306
|
-
|
311
|
+
end
|
307
312
|
|
308
313
|
extend JSONRPC2::TextileEmitter
|
309
314
|
end
|
data/lib/jsonrpc2/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonrpc2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httpclient
|