jsonrpc2 0.0.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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/README.markdown +176 -0
- data/Rakefile +2 -0
- data/example/config.ru +34 -0
- data/jsonrpc2.gemspec +55 -0
- data/lib/jsonrpc2.rb +4 -0
- data/lib/jsonrpc2/accept.rb +67 -0
- data/lib/jsonrpc2/auth.rb +66 -0
- data/lib/jsonrpc2/client.rb +59 -0
- data/lib/jsonrpc2/html.rb +137 -0
- data/lib/jsonrpc2/interface.rb +298 -0
- data/lib/jsonrpc2/textile.rb +131 -0
- data/lib/jsonrpc2/types.rb +174 -0
- data/lib/jsonrpc2/version.rb +5 -0
- metadata +156 -0
@@ -0,0 +1,174 @@
|
|
1
|
+
module JSONRPC2
|
2
|
+
# Types are checked against textual descriptions of the contents:
|
3
|
+
#
|
4
|
+
# * String - A string
|
5
|
+
# * Number - Any kind of number
|
6
|
+
# * Integer - Integer value
|
7
|
+
# * Boolean - true or false
|
8
|
+
# * true - True value
|
9
|
+
# * false - False value
|
10
|
+
# * null - nil value
|
11
|
+
# * Object - An object
|
12
|
+
# * Array - An array
|
13
|
+
# * Array [Type] - An array of type Type
|
14
|
+
# * Value (or Any or void) - Any value of any type
|
15
|
+
# * CustomType - A defined custom object type
|
16
|
+
module Types
|
17
|
+
module_function
|
18
|
+
|
19
|
+
# Checks that object is of given type (and that any custom types
|
20
|
+
# comply with interface)
|
21
|
+
#
|
22
|
+
# @param [Interface] interface API class
|
23
|
+
# @param [String] type_string Description of type(s) - comma
|
24
|
+
# separated if more than one
|
25
|
+
# @param object Value to check check type
|
26
|
+
# @return [Boolean] true if ok
|
27
|
+
def valid?(interface, type_string, object)
|
28
|
+
res = type_string.split(/,/).any? do |type|
|
29
|
+
case type
|
30
|
+
when 'String'
|
31
|
+
object.is_a?(String)
|
32
|
+
when 'Number'
|
33
|
+
object.kind_of?(Numeric)
|
34
|
+
when 'true'
|
35
|
+
object == true
|
36
|
+
when 'false'
|
37
|
+
object == false
|
38
|
+
when 'Boolean'
|
39
|
+
object == true || object == false
|
40
|
+
when 'null'
|
41
|
+
object.nil?
|
42
|
+
when 'Integer'
|
43
|
+
object.kind_of?(Numeric) && (object.to_i.to_f == object.to_f)
|
44
|
+
when 'Object'
|
45
|
+
object.is_a?(Hash)
|
46
|
+
when 'Array'
|
47
|
+
object.is_a?(Array)
|
48
|
+
when /\AArray\[(.*)\]\z/
|
49
|
+
object.is_a?(Array) && object.all? { |value| valid?(interface, $1, value) }
|
50
|
+
when 'Value', 'Any', 'void'
|
51
|
+
true
|
52
|
+
else # Custom type
|
53
|
+
subset = (type[-1] == ?*)
|
54
|
+
type = type[0...-1] if subset
|
55
|
+
|
56
|
+
custom = interface.types[type]
|
57
|
+
#STDERR.puts "Type Info: #{custom.inspect}"
|
58
|
+
if custom
|
59
|
+
custom.valid_object?(interface, object, subset)
|
60
|
+
else
|
61
|
+
raise "Invalid/unknown type: #{type} for #{interface.name}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
#STDERR.puts "#{interface.name}: #{type} - #{object.inspect} - #{res ? "OK" : "FAIL"}"
|
66
|
+
res
|
67
|
+
end
|
68
|
+
|
69
|
+
# Checks that param hash is valid for API call
|
70
|
+
#
|
71
|
+
# @param [Interface] interface API class
|
72
|
+
# @param [String] method Method name
|
73
|
+
# @param [Hash] data params hash to check
|
74
|
+
# @return [Boolean] true if ok
|
75
|
+
def valid_params?(interface, method, data)
|
76
|
+
about = interface.about[method.to_s]
|
77
|
+
return true if about.nil? # No defined params
|
78
|
+
|
79
|
+
params = (about[:params] || [])
|
80
|
+
param_names = params.map { |param| param[:name] }
|
81
|
+
|
82
|
+
if params.empty? && data.empty?
|
83
|
+
return true
|
84
|
+
end
|
85
|
+
|
86
|
+
extra_keys = data.keys - param_names
|
87
|
+
unless extra_keys.empty?
|
88
|
+
raise "Extra parameters #{extra_keys.inspect} for #{method}."
|
89
|
+
end
|
90
|
+
|
91
|
+
params.each do |param|
|
92
|
+
if data.has_key?(param[:name])
|
93
|
+
value = data[param[:name].to_s]
|
94
|
+
unless valid?(interface, param[:type], value)
|
95
|
+
raise "'#{param[:name]}' should be of type #{param[:type]}, was #{value.class.name}"
|
96
|
+
end
|
97
|
+
elsif ! param[:required]
|
98
|
+
next true
|
99
|
+
else
|
100
|
+
raise "Missing parameter: '#{param[:name]}' of type #{param[:type]} for #{method}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Checks that result is valid for API call
|
106
|
+
#
|
107
|
+
# @param [Interface] interface API class
|
108
|
+
# @param [String] method Method name
|
109
|
+
# @param [Hash] value Value to check
|
110
|
+
# @return [Boolean] true if ok
|
111
|
+
def valid_result?(interface, method, value)
|
112
|
+
about = interface.about[method.to_s]
|
113
|
+
return true if about.nil? # Undefined
|
114
|
+
if about[:returns].nil?
|
115
|
+
return value.nil?
|
116
|
+
end
|
117
|
+
valid?(interface, about[:returns][:type], value) or
|
118
|
+
raise "Invalid return type: should have been #{about[:returns][:type]}, was #{value.class.name}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Description of JSON object
|
123
|
+
class JsonObjectType
|
124
|
+
attr_accessor :name, :fields
|
125
|
+
def initialize(name, fields)
|
126
|
+
@name, @fields = name, fields
|
127
|
+
@required = true
|
128
|
+
end
|
129
|
+
|
130
|
+
# Check that #object Hash is valid version of this type
|
131
|
+
def valid_object?(interface, object, subset = false)
|
132
|
+
object.keys.all? { |key| fields.any? { |field| field[:name] == key } } &&
|
133
|
+
fields.all? { |field| (object.keys.include?(field[:name]) &&
|
134
|
+
Types.valid?(interface, field[:type], object[field[:name]])) || subset || (! field[:required]) }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Add field of #name and #type to type description
|
138
|
+
def field(name, type, desc, options={})
|
139
|
+
@fields << { :name => name, :type => type, :desc => desc, :required => @required }.merge(options)
|
140
|
+
end
|
141
|
+
# Shortcut to define string field
|
142
|
+
def string name, desc, options={}; field(name, 'String', desc, options); end
|
143
|
+
# Shortcut to define number field
|
144
|
+
def number name, desc, options={}; field(name, 'Number', desc, options); end
|
145
|
+
# Shortcut to define integer field
|
146
|
+
def integer name, desc, options={}; field(name, 'Integer', desc, options); end
|
147
|
+
# Shortcut to define boolean field
|
148
|
+
def boolean name, desc, options={}; field(name, 'Boolean', desc, options); end
|
149
|
+
|
150
|
+
# Make fields defined in block optional by default
|
151
|
+
def optional(&block)
|
152
|
+
old_required = @required
|
153
|
+
begin
|
154
|
+
@required = false
|
155
|
+
yield(self)
|
156
|
+
ensure
|
157
|
+
@required = old_required
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Make fields defined in block required by default
|
162
|
+
def required(&block)
|
163
|
+
old_required = @required
|
164
|
+
begin
|
165
|
+
@required = true
|
166
|
+
yield(self)
|
167
|
+
ensure
|
168
|
+
@required = old_required
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsonrpc2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Geoff Youngs
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-07-17 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: httpclient
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: json
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: RedCloth
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
description: |+
|
63
|
+
JSON-RPC2 server DSL - allows APIs to be created as mountable Rack applications
|
64
|
+
with inline documentation, authentication and type checking.
|
65
|
+
|
66
|
+
e.g.
|
67
|
+
|
68
|
+
class Calculator < JSONRPC2::Interface
|
69
|
+
title "JSON-RPC2 Calculator"
|
70
|
+
introduction "This interface allows basic maths calculations via JSON-RPC2"
|
71
|
+
auth_with JSONRPC2::BasicAuth.new({'user' => 'secretword'})
|
72
|
+
|
73
|
+
section 'Simple Ops' do
|
74
|
+
desc 'Multiply two numbers'
|
75
|
+
param 'a', 'Number', 'a'
|
76
|
+
param 'b', 'Number', 'b'
|
77
|
+
result 'Number', 'a * b'
|
78
|
+
def mul args
|
79
|
+
args['a'] * args['b']
|
80
|
+
end
|
81
|
+
|
82
|
+
desc 'Add numbers'
|
83
|
+
example "Calculate 1 + 1 = 2", :params => { 'a' => 1, 'b' => 1}, :result => 2
|
84
|
+
|
85
|
+
param 'a', 'Number', 'First number'
|
86
|
+
param 'b', 'Number', 'Second number'
|
87
|
+
optional 'c', 'Number', 'Third number'
|
88
|
+
result 'Number', 'a + b + c'
|
89
|
+
def sum args
|
90
|
+
val = args['a'] + args['b']
|
91
|
+
val += args['c'] if args['c']
|
92
|
+
val
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
email:
|
98
|
+
- git@intersect-uk.co.uk
|
99
|
+
executables: []
|
100
|
+
|
101
|
+
extensions: []
|
102
|
+
|
103
|
+
extra_rdoc_files: []
|
104
|
+
|
105
|
+
files:
|
106
|
+
- .gitignore
|
107
|
+
- Gemfile
|
108
|
+
- README.markdown
|
109
|
+
- Rakefile
|
110
|
+
- example/config.ru
|
111
|
+
- jsonrpc2.gemspec
|
112
|
+
- lib/jsonrpc2.rb
|
113
|
+
- lib/jsonrpc2/accept.rb
|
114
|
+
- lib/jsonrpc2/auth.rb
|
115
|
+
- lib/jsonrpc2/client.rb
|
116
|
+
- lib/jsonrpc2/html.rb
|
117
|
+
- lib/jsonrpc2/interface.rb
|
118
|
+
- lib/jsonrpc2/textile.rb
|
119
|
+
- lib/jsonrpc2/types.rb
|
120
|
+
- lib/jsonrpc2/version.rb
|
121
|
+
homepage: http://github.com/geoffyoungs/jsonrpc2
|
122
|
+
licenses: []
|
123
|
+
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
hash: 3
|
135
|
+
segments:
|
136
|
+
- 0
|
137
|
+
version: "0"
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
hash: 3
|
144
|
+
segments:
|
145
|
+
- 0
|
146
|
+
version: "0"
|
147
|
+
requirements: []
|
148
|
+
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 1.8.15
|
151
|
+
signing_key:
|
152
|
+
specification_version: 3
|
153
|
+
summary: JSON-RPC2 server DSL
|
154
|
+
test_files: []
|
155
|
+
|
156
|
+
has_rdoc:
|