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.
@@ -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
@@ -0,0 +1,5 @@
1
+ # JSONRPC2 namespace module
2
+ module JSONRPC2
3
+ # Version
4
+ VERSION = "0.0.1"
5
+ 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: