jsonrpc2 0.0.1

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