interpol 0.5.0 → 0.6.0
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/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
|
-
|