interpol 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +14 -3
- data/lib/interpol/configuration/built_in_param_parsers.rb +86 -0
- data/lib/interpol/configuration/default_callbacks.rb +46 -0
- data/lib/interpol/configuration.rb +71 -33
- data/lib/interpol/dynamic_struct.rb +7 -1
- data/lib/interpol/endpoint.rb +0 -8
- data/lib/interpol/errors.rb +30 -0
- data/lib/interpol/hash_set_default_proc_18.rb +9 -0
- data/lib/interpol/request_params_parser.rb +23 -111
- data/lib/interpol/sinatra/request_params_parser.rb +12 -5
- data/lib/interpol/test_helper.rb +3 -3
- data/lib/interpol/version.rb +1 -1
- metadata +5 -3
- data/lib/interpol/define_singleton_method.rb +0 -10
data/Rakefile
CHANGED
@@ -14,11 +14,9 @@ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' # MRI only
|
|
14
14
|
desc "Run cane to check quality metrics"
|
15
15
|
Cane::RakeTask.new(:quality) do |cane|
|
16
16
|
cane.abc_max = 13
|
17
|
-
cane.add_threshold 'coverage/coverage_percent.txt', :==, 100
|
18
17
|
cane.style_measure = 100
|
19
18
|
|
20
19
|
cane.abc_exclude = %w[
|
21
|
-
Interpol::Configuration#register_default_callbacks
|
22
20
|
Interpol::StubApp::Builder#initialize
|
23
21
|
]
|
24
22
|
|
@@ -26,11 +24,24 @@ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' # MRI only
|
|
26
24
|
spec/unit/interpol/sinatra/request_params_parser_spec.rb
|
27
25
|
]
|
28
26
|
end
|
27
|
+
|
28
|
+
desc "Checks the spec coverage and fails if it is less than 100%"
|
29
|
+
task :check_coverage do
|
30
|
+
puts "Checking code coverage..."
|
31
|
+
percent = File.read("coverage/coverage_percent.txt").to_f
|
32
|
+
|
33
|
+
if percent < 100
|
34
|
+
raise "Failed to achieve 100% code coverage: #{percent}"
|
35
|
+
else
|
36
|
+
puts "Nice work! Code coverage is still 100%"
|
37
|
+
end
|
38
|
+
end
|
29
39
|
else
|
30
40
|
task(:quality) { } # no-op
|
41
|
+
task(:check_coverage) { } # no-op
|
31
42
|
end
|
32
43
|
|
33
|
-
task :default => [:spec, :quality]
|
44
|
+
task :default => [:spec, :quality, :check_coverage]
|
34
45
|
|
35
46
|
desc "Watch Documentation App Compass Files"
|
36
47
|
task :compass_watch do
|
@@ -0,0 +1,86 @@
|
|
1
|
+
define_request_param_parser('integer') do |param|
|
2
|
+
param.string_validation_options 'pattern' => '^\-?\d+$'
|
3
|
+
|
4
|
+
param.parse do |value|
|
5
|
+
begin
|
6
|
+
raise TypeError unless value # On 1.8.7 Integer(nil) does not raise an error
|
7
|
+
Integer(value)
|
8
|
+
rescue TypeError
|
9
|
+
raise ArgumentError, "Could not convert #{value.inspect} to an integer"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
define_request_param_parser('number') do |param|
|
15
|
+
param.string_validation_options 'pattern' => '^\-?\d+(\.\d+)?$'
|
16
|
+
|
17
|
+
param.parse do |value|
|
18
|
+
begin
|
19
|
+
Float(value)
|
20
|
+
rescue TypeError
|
21
|
+
raise ArgumentError, "Could not convert #{value.inspect} to a float"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
define_request_param_parser('boolean') do |param|
|
27
|
+
param.string_validation_options 'enum' => %w[ true false ]
|
28
|
+
|
29
|
+
booleans = { 'true' => true, true => true,
|
30
|
+
'false' => false, false => false }
|
31
|
+
param.parse do |value|
|
32
|
+
booleans.fetch(value) do
|
33
|
+
raise ArgumentError, "Could not convert #{value.inspect} to a boolean"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
define_request_param_parser('null') do |param|
|
39
|
+
param.string_validation_options 'enum' => ['']
|
40
|
+
|
41
|
+
nulls = { '' => nil, nil => nil }
|
42
|
+
param.parse do |value|
|
43
|
+
nulls.fetch(value) do
|
44
|
+
raise ArgumentError, "Could not convert #{value.inspect} to a null"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
define_request_param_parser('string') do |param|
|
50
|
+
param.parse do |value|
|
51
|
+
unless value.is_a?(String)
|
52
|
+
raise ArgumentError, "#{value.inspect} is not a string"
|
53
|
+
end
|
54
|
+
|
55
|
+
value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
define_request_param_parser('string', 'format' => 'date') do |param|
|
60
|
+
param.parse do |value|
|
61
|
+
unless value =~ /\A\d{4}\-\d{2}\-\d{2}\z/
|
62
|
+
raise ArgumentError, "#{value.inspect} is not in iso8601 format"
|
63
|
+
end
|
64
|
+
|
65
|
+
Date.new(*value.split('-').map(&:to_i))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
define_request_param_parser('string', 'format' => 'date-time') do |param|
|
70
|
+
param.parse &Time.method(:iso8601)
|
71
|
+
end
|
72
|
+
|
73
|
+
define_request_param_parser('string', 'format' => 'uri') do |param|
|
74
|
+
param.parse do |value|
|
75
|
+
begin
|
76
|
+
URI(value).tap do |uri|
|
77
|
+
unless uri.scheme && uri.host
|
78
|
+
raise ArgumentError, "#{uri.inspect} is not a valid full URI"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
rescue URI::InvalidURIError => e
|
82
|
+
raise ArgumentError, e.message, e.backtrace
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
request_version do
|
2
|
+
raise ConfigurationError, "request_version has not been configured"
|
3
|
+
end
|
4
|
+
|
5
|
+
response_version do
|
6
|
+
raise ConfigurationError, "response_version has not been configured"
|
7
|
+
end
|
8
|
+
|
9
|
+
validate_response_if do |env, status, headers, body|
|
10
|
+
headers['Content-Type'].to_s.include?('json') &&
|
11
|
+
status >= 200 && status <= 299 && status != 204 # No Content
|
12
|
+
end
|
13
|
+
|
14
|
+
validate_request_if do |env|
|
15
|
+
env['CONTENT_TYPE'].to_s.include?('json') &&
|
16
|
+
%w[ POST PUT PATCH ].include?(env.fetch('REQUEST_METHOD'))
|
17
|
+
end
|
18
|
+
|
19
|
+
on_unavailable_request_version do |env, requested, available|
|
20
|
+
message = "The requested request version is invalid. " +
|
21
|
+
"Requested: #{requested}. " +
|
22
|
+
"Available: #{available}"
|
23
|
+
|
24
|
+
rack_json_response(406, :error => message)
|
25
|
+
end
|
26
|
+
|
27
|
+
on_unavailable_sinatra_request_version do |requested, available|
|
28
|
+
message = "The requested request version is invalid. " +
|
29
|
+
"Requested: #{requested}. " +
|
30
|
+
"Available: #{available}"
|
31
|
+
|
32
|
+
halt 406, JSON.dump(:error => message)
|
33
|
+
end
|
34
|
+
|
35
|
+
on_invalid_request_body do |env, error|
|
36
|
+
rack_json_response(400, :error => error.message)
|
37
|
+
end
|
38
|
+
|
39
|
+
on_invalid_sinatra_request_params do |error|
|
40
|
+
halt 400, JSON.dump(:error => error.message)
|
41
|
+
end
|
42
|
+
|
43
|
+
select_example_response do |endpoint_def, _|
|
44
|
+
endpoint_def.examples.first
|
45
|
+
end
|
46
|
+
|
@@ -2,6 +2,7 @@ require 'interpol/endpoint'
|
|
2
2
|
require 'interpol/errors'
|
3
3
|
require 'yaml'
|
4
4
|
require 'interpol/configuration_ruby_18_extensions' if RUBY_VERSION.to_f < 1.9
|
5
|
+
require 'uri'
|
5
6
|
|
6
7
|
module Interpol
|
7
8
|
module DefinitionFinder
|
@@ -40,6 +41,7 @@ module Interpol
|
|
40
41
|
self.endpoint_definition_merge_key_files = []
|
41
42
|
self.documentation_title = "API Documentation Provided by Interpol"
|
42
43
|
register_default_callbacks
|
44
|
+
register_built_in_param_parsers
|
43
45
|
@filter_example_data_blocks = []
|
44
46
|
|
45
47
|
yield self if block_given?
|
@@ -163,6 +165,23 @@ module Interpol
|
|
163
165
|
dup.tap(&block)
|
164
166
|
end
|
165
167
|
|
168
|
+
def define_request_param_parser(type, options = {}, &block)
|
169
|
+
ParamParser.new(type, options, &block).tap do |parser|
|
170
|
+
# Use unshift so that new parsers take precedence over older ones.
|
171
|
+
param_parsers[type].unshift parser
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def param_parser_for(type, options)
|
176
|
+
match = param_parsers[type].find do |parser|
|
177
|
+
parser.matches_options?(options)
|
178
|
+
end
|
179
|
+
|
180
|
+
return match if match
|
181
|
+
|
182
|
+
raise UnsupportedTypeError.new(type, options)
|
183
|
+
end
|
184
|
+
|
166
185
|
private
|
167
186
|
|
168
187
|
# 1.9 version
|
@@ -204,51 +223,70 @@ module Interpol
|
|
204
223
|
@named_example_selectors ||= {}
|
205
224
|
end
|
206
225
|
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
end
|
226
|
+
def param_parsers
|
227
|
+
@param_parsers ||= Hash.new { |h, k| h[k] = [] }
|
228
|
+
end
|
211
229
|
|
212
|
-
|
213
|
-
|
214
|
-
|
230
|
+
def self.instance_eval_args_for(file)
|
231
|
+
filename = File.expand_path("../configuration/#{file}.rb", __FILE__)
|
232
|
+
contents = File.read(filename)
|
233
|
+
[contents, filename, 1]
|
234
|
+
end
|
215
235
|
|
216
|
-
|
217
|
-
headers['Content-Type'].to_s.include?('json') &&
|
218
|
-
status >= 200 && status <= 299 && status != 204 # No Content
|
219
|
-
end
|
236
|
+
BUILT_IN_PARSER_EVAL_ARGS = instance_eval_args_for("built_in_param_parsers")
|
220
237
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
end
|
238
|
+
def register_built_in_param_parsers
|
239
|
+
instance_eval(*BUILT_IN_PARSER_EVAL_ARGS)
|
240
|
+
end
|
225
241
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
242
|
+
DEFAULT_CALLBACK_EVAL_ARGS = instance_eval_args_for("default_callbacks")
|
243
|
+
def register_default_callbacks
|
244
|
+
instance_eval(*DEFAULT_CALLBACK_EVAL_ARGS)
|
245
|
+
end
|
246
|
+
end
|
230
247
|
|
231
|
-
|
232
|
-
|
248
|
+
# Holds the validation/parsing logic for a particular parameter
|
249
|
+
# type (w/ additional options).
|
250
|
+
class ParamParser
|
251
|
+
def initialize(type, options = {})
|
252
|
+
@type = type
|
253
|
+
@options = options
|
254
|
+
yield self
|
255
|
+
end
|
233
256
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
"Available: #{available}"
|
257
|
+
def string_validation_options(options = nil, &block)
|
258
|
+
@string_validation_options_block = block || Proc.new { options }
|
259
|
+
end
|
238
260
|
|
239
|
-
|
240
|
-
|
261
|
+
def parse(&block)
|
262
|
+
@parse_block = block
|
263
|
+
end
|
241
264
|
|
242
|
-
|
243
|
-
|
265
|
+
def matches_options?(options)
|
266
|
+
@options.all? do |key, value|
|
267
|
+
options.has_key?(key) && options[key] == value
|
244
268
|
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def type_validation_options_for(type, options)
|
272
|
+
return type unless @string_validation_options_block
|
273
|
+
string_options = @string_validation_options_block.call(options)
|
274
|
+
Array(type) + [string_options.merge('type' => 'string')]
|
275
|
+
end
|
245
276
|
|
246
|
-
|
247
|
-
|
277
|
+
def parse_value(value)
|
278
|
+
unless @parse_block
|
279
|
+
raise "No parse callback has been set for param type definition: #{description}"
|
248
280
|
end
|
249
281
|
|
250
|
-
|
251
|
-
|
282
|
+
@parse_block.call(value)
|
283
|
+
end
|
284
|
+
|
285
|
+
def description
|
286
|
+
@description ||= @type.inspect.tap do |desc|
|
287
|
+
if @options.any?
|
288
|
+
desc << " (with options: #{@options.inspect})"
|
289
|
+
end
|
252
290
|
end
|
253
291
|
end
|
254
292
|
end
|
@@ -25,10 +25,16 @@ module Interpol
|
|
25
25
|
when Array
|
26
26
|
object.each { |obj| recursively_freeze(obj) }
|
27
27
|
when Hash
|
28
|
-
object
|
28
|
+
set_default_proc_on(object)
|
29
29
|
recursively_freeze(object.values)
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
def self.set_default_proc_on(hash)
|
34
|
+
hash.default_proc = DEFAULT_PROC
|
35
|
+
end
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
39
|
+
require 'interpol/hash_set_default_proc_18' unless {}.respond_to?(:default_proc=)
|
40
|
+
|
data/lib/interpol/endpoint.rb
CHANGED
@@ -201,14 +201,6 @@ module Interpol
|
|
201
201
|
@example_status_code ||= @status_codes.example_status_code
|
202
202
|
end
|
203
203
|
|
204
|
-
def parse_request_params(request_params)
|
205
|
-
request_params_parser.parse(request_params)
|
206
|
-
end
|
207
|
-
|
208
|
-
def request_params_parser
|
209
|
-
@request_params_parser ||= RequestParamsParser.new(self)
|
210
|
-
end
|
211
|
-
|
212
204
|
private
|
213
205
|
|
214
206
|
def make_schema_strict!(raw_schema, modify_object=true)
|
data/lib/interpol/errors.rb
CHANGED
@@ -38,5 +38,35 @@ module Interpol
|
|
38
38
|
|
39
39
|
# Raised when an invalid status code is found during validate_codes!
|
40
40
|
class StatusCodeMatcherArgumentError < ArgumentError; end
|
41
|
+
|
42
|
+
# Raised when an unsupported parameter type is defined.
|
43
|
+
class UnsupportedTypeError < ArgumentError
|
44
|
+
attr_reader :type, :options
|
45
|
+
|
46
|
+
def initialize(type, options = {})
|
47
|
+
@type = type
|
48
|
+
@options = options
|
49
|
+
|
50
|
+
description = type.inspect
|
51
|
+
description << " (#{options.inspect})" if options.any?
|
52
|
+
super("No param parser can be found for #{description}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Raised when the path_params are not part of the endpoint route.
|
57
|
+
class InvalidPathParamsError < ArgumentError
|
58
|
+
attr_reader :invalid_params
|
59
|
+
|
60
|
+
def initialize(*invalid_params)
|
61
|
+
@invalid_params = invalid_params
|
62
|
+
super("The path params #{invalid_params.join(', ')} are not in the route")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Raised when a parameter value cannot be parsed.
|
67
|
+
CannotBeParsedError = Class.new(ArgumentError)
|
68
|
+
|
69
|
+
# Raised when a params definition is invalid.
|
70
|
+
InvalidParamsDefinitionError = Class.new(ArgumentError)
|
41
71
|
end
|
42
72
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'interpol'
|
2
2
|
require 'interpol/dynamic_struct'
|
3
|
-
require 'uri'
|
4
3
|
require 'interpol/each_with_object' unless Enumerable.method_defined?(:each_with_object)
|
5
4
|
|
6
5
|
module Interpol
|
@@ -20,10 +19,10 @@ module Interpol
|
|
20
19
|
# The parsed params object supports dot-syntax for accessing parameters
|
21
20
|
# and will convert values where feasible (e.g. '3' = 3, 'true' => true, etc).
|
22
21
|
class RequestParamsParser
|
23
|
-
def initialize(endpoint_definition)
|
24
|
-
@validator = ParamValidator.new(endpoint_definition)
|
22
|
+
def initialize(endpoint_definition, configuration)
|
23
|
+
@validator = ParamValidator.new(endpoint_definition, configuration)
|
25
24
|
@validator.validate_path_params_valid_for_route!
|
26
|
-
@converter = ParamConverter.new(@validator.param_definitions)
|
25
|
+
@converter = ParamConverter.new(@validator.param_definitions, configuration)
|
27
26
|
end
|
28
27
|
|
29
28
|
def parse(params)
|
@@ -37,8 +36,9 @@ module Interpol
|
|
37
36
|
|
38
37
|
# Private: This takes care of the validation.
|
39
38
|
class ParamValidator
|
40
|
-
def initialize(endpoint_definition)
|
39
|
+
def initialize(endpoint_definition, configuration)
|
41
40
|
@endpoint_definition = endpoint_definition
|
41
|
+
@configuration = configuration
|
42
42
|
@params_schema = build_params_schema
|
43
43
|
end
|
44
44
|
|
@@ -107,26 +107,19 @@ module Interpol
|
|
107
107
|
].none? { |params| params['additionalProperties'] }
|
108
108
|
end
|
109
109
|
|
110
|
-
STRING_EQUIVALENTS = {
|
111
|
-
'string' => nil,
|
112
|
-
'integer' => { 'type' => 'string', 'pattern' => '^\-?\d+$' },
|
113
|
-
'number' => { 'type' => 'string', 'pattern' => '^\-?\d+(\.\d+)?$' },
|
114
|
-
'boolean' => { 'type' => 'string', 'enum' => %w[ true false ] },
|
115
|
-
'null' => { 'type' => 'string', 'enum' => [''] }
|
116
|
-
}
|
117
|
-
|
118
110
|
def adjusted_schema(schema)
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
raise UnsupportedTypeError.new(type)
|
125
|
-
end
|
111
|
+
adjusted_types = Array(schema['type']).inject([]) do |type_list, type|
|
112
|
+
type_string, options = if type.is_a?(Hash)
|
113
|
+
[type.fetch('type'), type]
|
114
|
+
else
|
115
|
+
[type, schema]
|
126
116
|
end
|
127
|
-
end.compact
|
128
117
|
|
129
|
-
|
118
|
+
@configuration.param_parser_for(type_string, options).
|
119
|
+
type_validation_options_for([type] + type_list, options)
|
120
|
+
end
|
121
|
+
|
122
|
+
schema.merge('type' => adjusted_types).tap do |adjusted|
|
130
123
|
adjusted['required'] = true unless adjusted['optional']
|
131
124
|
end
|
132
125
|
end
|
@@ -136,8 +129,9 @@ module Interpol
|
|
136
129
|
class ParamConverter
|
137
130
|
attr_reader :param_definitions
|
138
131
|
|
139
|
-
def initialize(param_definitions)
|
132
|
+
def initialize(param_definitions, configuration)
|
140
133
|
@param_definitions = param_definitions
|
134
|
+
@configuration = configuration
|
141
135
|
end
|
142
136
|
|
143
137
|
def convert(params)
|
@@ -156,10 +150,10 @@ module Interpol
|
|
156
150
|
definition = param_definitions.fetch(name)
|
157
151
|
|
158
152
|
Array(definition['type']).each do |type|
|
159
|
-
|
153
|
+
parser = parser_for(type, definition)
|
160
154
|
|
161
155
|
begin
|
162
|
-
return
|
156
|
+
return parser.parse_value(value)
|
163
157
|
rescue ArgumentError => e
|
164
158
|
# Try the next unioned type...
|
165
159
|
end
|
@@ -168,96 +162,14 @@ module Interpol
|
|
168
162
|
raise CannotBeParsedError, "The #{name} #{value.inspect} cannot be parsed"
|
169
163
|
end
|
170
164
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
BOOLEANS.fetch(value) do
|
175
|
-
raise ArgumentError, "#{value} is not convertable to a boolean"
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
NULLS = { '' => nil, nil => nil }
|
180
|
-
def self.convert_null(value)
|
181
|
-
NULLS.fetch(value) do
|
182
|
-
raise ArgumentError, "#{value} is not convertable to null"
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def self.convert_date(value)
|
187
|
-
unless value =~ /\A\d{4}\-\d{2}\-\d{2}\z/
|
188
|
-
raise ArgumentError, "Not in iso8601 format"
|
165
|
+
def parser_for(type, options)
|
166
|
+
if type.is_a?(Hash)
|
167
|
+
return parser_for(type.fetch('type'), type)
|
189
168
|
end
|
190
169
|
|
191
|
-
|
192
|
-
end
|
193
|
-
|
194
|
-
def self.convert_uri(value)
|
195
|
-
URI(value).tap do |uri|
|
196
|
-
unless uri.scheme && uri.host
|
197
|
-
raise ArgumentError, "Not a valid full URI"
|
198
|
-
end
|
199
|
-
end
|
200
|
-
rescue URI::InvalidURIError => e
|
201
|
-
raise ArgumentError, e.message, e.backtrace
|
202
|
-
end
|
203
|
-
|
204
|
-
CONVERTERS = {
|
205
|
-
'integer' => method(:Integer),
|
206
|
-
'number' => method(:Float),
|
207
|
-
'boolean' => method(:convert_boolean),
|
208
|
-
'null' => method(:convert_null)
|
209
|
-
}
|
210
|
-
|
211
|
-
IDENTITY_CONVERTER = lambda { |v| v }
|
212
|
-
|
213
|
-
def converter_for(type, definition)
|
214
|
-
CONVERTERS.fetch(type) do
|
215
|
-
if Hash === type && type['type']
|
216
|
-
converter_for(type['type'], type)
|
217
|
-
elsif type == 'string'
|
218
|
-
string_converter_for(definition)
|
219
|
-
else
|
220
|
-
raise CannotBeParsedError, "#{type} cannot be parsed"
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
STRING_CONVERTERS = {
|
226
|
-
'date' => method(:convert_date),
|
227
|
-
'date-time' => Time.method(:iso8601),
|
228
|
-
'uri' => method(:convert_uri)
|
229
|
-
}
|
230
|
-
|
231
|
-
def string_converter_for(definition)
|
232
|
-
STRING_CONVERTERS.fetch(definition['format'], IDENTITY_CONVERTER)
|
170
|
+
@configuration.param_parser_for(type, options)
|
233
171
|
end
|
234
172
|
end
|
235
|
-
|
236
|
-
# Raised when an unsupported parameter type is defined.
|
237
|
-
class UnsupportedTypeError < ArgumentError
|
238
|
-
attr_reader :type
|
239
|
-
|
240
|
-
def initialize(type)
|
241
|
-
@type = type
|
242
|
-
super("#{type} params are not supported")
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
# Raised when the path_params are not part of the endpoint route.
|
247
|
-
class InvalidPathParamsError < ArgumentError
|
248
|
-
attr_reader :invalid_params
|
249
|
-
|
250
|
-
def initialize(*invalid_params)
|
251
|
-
@invalid_params = invalid_params
|
252
|
-
super("The path params #{invalid_params.join(', ')} are not in the route")
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
# Raised when a parameter value cannot be parsed.
|
257
|
-
CannotBeParsedError = Class.new(ArgumentError)
|
258
|
-
|
259
|
-
# Raised when a params definition is invalid.
|
260
|
-
InvalidParamsDefinitionError = Class.new(ArgumentError)
|
261
173
|
end
|
262
174
|
end
|
263
175
|
|
@@ -27,7 +27,13 @@ module Interpol
|
|
27
27
|
# receive the app instance as an argument here.
|
28
28
|
def validate_and_parse_params(app)
|
29
29
|
return unless app.settings.parse_params?
|
30
|
-
SingleRequestParamsParser.parse_params(config, app)
|
30
|
+
SingleRequestParamsParser.parse_params(config, app, endpoint_parsers)
|
31
|
+
end
|
32
|
+
|
33
|
+
def endpoint_parsers
|
34
|
+
@endpoint_parsers ||= Hash.new do |hash, endpoint|
|
35
|
+
hash[endpoint] = Interpol::RequestParamsParser.new(endpoint, @config)
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
private
|
@@ -57,17 +63,18 @@ module Interpol
|
|
57
63
|
|
58
64
|
# Handles finding parsing request params for a single request.
|
59
65
|
class SingleRequestParamsParser
|
60
|
-
def self.parse_params(config, app)
|
61
|
-
new(config, app).parse_params
|
66
|
+
def self.parse_params(config, app, endpoint_parsers)
|
67
|
+
new(config, app, endpoint_parsers).parse_params
|
62
68
|
end
|
63
69
|
|
64
|
-
def initialize(config, app)
|
70
|
+
def initialize(config, app, endpoint_parsers)
|
65
71
|
@config = config
|
66
72
|
@app = app
|
73
|
+
@endpoint_parsers = endpoint_parsers
|
67
74
|
end
|
68
75
|
|
69
76
|
def parse_params
|
70
|
-
endpoint_definition.
|
77
|
+
@endpoint_parsers[endpoint_definition].parse(params_to_parse)
|
71
78
|
rescue Interpol::ValidationError => error
|
72
79
|
request_params_invalid(error)
|
73
80
|
end
|
data/lib/interpol/test_helper.rb
CHANGED
@@ -9,7 +9,7 @@ module Interpol
|
|
9
9
|
config = Configuration.default.customized_duplicate(&block)
|
10
10
|
|
11
11
|
each_definition_from(config.endpoints) do |endpoint, definition|
|
12
|
-
define_definition_test(endpoint, definition)
|
12
|
+
define_definition_test(config, endpoint, definition)
|
13
13
|
|
14
14
|
each_example_from(definition) do |example, example_index|
|
15
15
|
define_example_test(config, endpoint, definition, example, example_index)
|
@@ -40,13 +40,13 @@ module Interpol
|
|
40
40
|
define_test(description) { example.validate! }
|
41
41
|
end
|
42
42
|
|
43
|
-
def define_definition_test(endpoint, definition)
|
43
|
+
def define_definition_test(config, endpoint, definition)
|
44
44
|
return unless definition.request?
|
45
45
|
|
46
46
|
description = "#{endpoint.name} (v #{definition.version}) request " +
|
47
47
|
"definition has valid params schema"
|
48
48
|
define_test description do
|
49
|
-
definition
|
49
|
+
RequestParamsParser.new(definition, config) # it will raise an error if it is invalid
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
data/lib/interpol/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: interpol
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
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-10-
|
12
|
+
date: 2012-10-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -199,9 +199,10 @@ files:
|
|
199
199
|
- LICENSE
|
200
200
|
- Gemfile
|
201
201
|
- Rakefile
|
202
|
+
- lib/interpol/configuration/built_in_param_parsers.rb
|
203
|
+
- lib/interpol/configuration/default_callbacks.rb
|
202
204
|
- lib/interpol/configuration.rb
|
203
205
|
- lib/interpol/configuration_ruby_18_extensions.rb
|
204
|
-
- lib/interpol/define_singleton_method.rb
|
205
206
|
- lib/interpol/documentation.rb
|
206
207
|
- lib/interpol/documentation_app/config.rb
|
207
208
|
- lib/interpol/documentation_app.rb
|
@@ -209,6 +210,7 @@ files:
|
|
209
210
|
- lib/interpol/each_with_object.rb
|
210
211
|
- lib/interpol/endpoint.rb
|
211
212
|
- lib/interpol/errors.rb
|
213
|
+
- lib/interpol/hash_set_default_proc_18.rb
|
212
214
|
- lib/interpol/request_body_validator.rb
|
213
215
|
- lib/interpol/request_params_parser.rb
|
214
216
|
- lib/interpol/response_schema_validator.rb
|
@@ -1,10 +0,0 @@
|
|
1
|
-
module Interpol
|
2
|
-
# 1.9 has Object#define_singleton_method but 1.8 does not.
|
3
|
-
# This provides 1.8 compatibility for the places we need it.
|
4
|
-
module DefineSingletonMethod
|
5
|
-
def define_singleton_method(name, &block)
|
6
|
-
(class << self; self; end).send(:define_method, name, &block)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|