grape 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +8 -8
- data/.rubocop.yml +65 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +17 -1
- data/Gemfile +1 -0
- data/README.md +16 -8
- data/RELEASING.md +105 -0
- data/grape.gemspec +1 -1
- data/lib/grape.rb +1 -0
- data/lib/grape/api.rb +88 -54
- data/lib/grape/cookies.rb +4 -6
- data/lib/grape/endpoint.rb +81 -69
- data/lib/grape/error_formatter/base.rb +5 -4
- data/lib/grape/error_formatter/json.rb +3 -3
- data/lib/grape/error_formatter/txt.rb +1 -1
- data/lib/grape/error_formatter/xml.rb +4 -4
- data/lib/grape/exceptions/base.rb +7 -7
- data/lib/grape/exceptions/incompatible_option_values.rb +13 -0
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -4
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/validation.rb +2 -2
- data/lib/grape/exceptions/validation_errors.rb +1 -1
- data/lib/grape/formatter/base.rb +5 -4
- data/lib/grape/formatter/serializable_hash.rb +7 -6
- data/lib/grape/http/request.rb +2 -2
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/auth/basic.rb +1 -1
- data/lib/grape/middleware/auth/oauth2.rb +18 -20
- data/lib/grape/middleware/base.rb +1 -1
- data/lib/grape/middleware/error.rb +19 -19
- data/lib/grape/middleware/filter.rb +3 -3
- data/lib/grape/middleware/formatter.rb +29 -23
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/accept_version_header.rb +8 -6
- data/lib/grape/middleware/versioner/header.rb +16 -14
- data/lib/grape/middleware/versioner/param.rb +7 -7
- data/lib/grape/middleware/versioner/path.rb +7 -9
- data/lib/grape/parser/base.rb +3 -2
- data/lib/grape/path.rb +1 -1
- data/lib/grape/route.rb +6 -4
- data/lib/grape/util/content_types.rb +2 -1
- data/lib/grape/util/deep_merge.rb +5 -5
- data/lib/grape/util/hash_stack.rb +2 -2
- data/lib/grape/validations.rb +34 -30
- data/lib/grape/validations/coerce.rb +6 -5
- data/lib/grape/validations/default.rb +0 -1
- data/lib/grape/validations/presence.rb +1 -1
- data/lib/grape/validations/regexp.rb +2 -2
- data/lib/grape/validations/values.rb +16 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +229 -210
- data/spec/grape/endpoint_spec.rb +56 -54
- data/spec/grape/entity_spec.rb +31 -33
- data/spec/grape/exceptions/missing_mime_type_spec.rb +3 -9
- data/spec/grape/middleware/auth/basic_spec.rb +8 -8
- data/spec/grape/middleware/auth/digest_spec.rb +5 -5
- data/spec/grape/middleware/auth/oauth2_spec.rb +23 -23
- data/spec/grape/middleware/base_spec.rb +6 -6
- data/spec/grape/middleware/error_spec.rb +11 -15
- data/spec/grape/middleware/exception_spec.rb +45 -25
- data/spec/grape/middleware/formatter_spec.rb +56 -45
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +25 -25
- data/spec/grape/middleware/versioner/header_spec.rb +54 -54
- data/spec/grape/middleware/versioner/param_spec.rb +17 -18
- data/spec/grape/middleware/versioner/path_spec.rb +6 -6
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/util/hash_stack_spec.rb +26 -27
- data/spec/grape/validations/coerce_spec.rb +39 -34
- data/spec/grape/validations/default_spec.rb +12 -13
- data/spec/grape/validations/presence_spec.rb +18 -22
- data/spec/grape/validations/regexp_spec.rb +9 -9
- data/spec/grape/validations/values_spec.rb +64 -0
- data/spec/grape/validations_spec.rb +127 -70
- data/spec/shared/versioning_examples.rb +5 -5
- data/spec/support/basic_auth_encode_helpers.rb +0 -1
- data/spec/support/versioned_helpers.rb +5 -6
- metadata +10 -4
data/lib/grape/cookies.rb
CHANGED
@@ -13,12 +13,10 @@ module Grape
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def write(header)
|
16
|
-
@cookies.select { |key, value|
|
17
|
-
|
18
|
-
}.each { |name, value|
|
19
|
-
cookie_value = value.is_a?(Hash) ? value : { :value => value }
|
16
|
+
@cookies.select { |key, value| @send_cookies[key] == true }.each do |name, value|
|
17
|
+
cookie_value = value.is_a?(Hash) ? value : { value: value }
|
20
18
|
Rack::Utils.set_cookie_header! header, name, cookie_value
|
21
|
-
|
19
|
+
end
|
22
20
|
end
|
23
21
|
|
24
22
|
def [](name)
|
@@ -35,7 +33,7 @@ module Grape
|
|
35
33
|
end
|
36
34
|
|
37
35
|
def delete(name, opts = {})
|
38
|
-
options = opts.merge(
|
36
|
+
options = opts.merge(value: 'deleted', expires: Time.at(0))
|
39
37
|
self.[]=(name, options)
|
40
38
|
end
|
41
39
|
|
data/lib/grape/endpoint.rb
CHANGED
@@ -52,15 +52,15 @@ module Grape
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def require_option(options, key)
|
55
|
-
|
55
|
+
raise Grape::Exceptions::MissingOption.new(key) unless options.has_key?(key)
|
56
56
|
end
|
57
57
|
|
58
58
|
def method_name
|
59
|
-
[
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
[options[:method],
|
60
|
+
Namespace.joined_space(settings),
|
61
|
+
settings.gather(:mount_path).join('/'),
|
62
|
+
options[:path].join('/')
|
63
|
+
].join(" ")
|
64
64
|
end
|
65
65
|
|
66
66
|
def routes
|
@@ -72,15 +72,15 @@ module Grape
|
|
72
72
|
endpoints.each { |e| e.mount_in(route_set) }
|
73
73
|
else
|
74
74
|
routes.each do |route|
|
75
|
-
methods = [
|
76
|
-
if !
|
75
|
+
methods = [route.route_method]
|
76
|
+
if !settings[:do_not_route_head] && route.route_method == "GET"
|
77
77
|
methods << "HEAD"
|
78
78
|
end
|
79
79
|
methods.each do |method|
|
80
80
|
route_set.add_route(self, {
|
81
|
-
:
|
82
|
-
:
|
83
|
-
}, { :
|
81
|
+
path_info: route.route_compiled,
|
82
|
+
request_method: method,
|
83
|
+
}, { route_info: route })
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
@@ -105,21 +105,21 @@ module Grape
|
|
105
105
|
regex = Rack::Mount::RegexpWithNamedGroups.new(path)
|
106
106
|
path_params = {}
|
107
107
|
# named parameters in the api path
|
108
|
-
named_params = regex.named_captures.map { |nc| nc[0] } -
|
108
|
+
named_params = regex.named_captures.map { |nc| nc[0] } - %w{ version format }
|
109
109
|
named_params.each { |named_param| path_params[named_param] = "" }
|
110
110
|
# route parameters declared via desc or appended to the api declaration
|
111
111
|
route_params = (options[:route_options][:params] || {})
|
112
112
|
path_params.merge!(route_params)
|
113
113
|
request_method = (method.to_s.upcase unless method == :any)
|
114
|
-
routes << Route.new(options[:route_options].clone.merge(
|
115
|
-
:
|
116
|
-
:
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
120
|
-
:
|
121
|
-
:
|
122
|
-
|
114
|
+
routes << Route.new(options[:route_options].clone.merge(
|
115
|
+
prefix: settings[:root_prefix],
|
116
|
+
version: settings[:version] ? settings[:version].join('|') : nil,
|
117
|
+
namespace: namespace,
|
118
|
+
method: request_method,
|
119
|
+
path: prepared_path,
|
120
|
+
params: path_params,
|
121
|
+
compiled: path,
|
122
|
+
)
|
123
123
|
)
|
124
124
|
end
|
125
125
|
end
|
@@ -151,7 +151,7 @@ module Grape
|
|
151
151
|
options[:app].call(env)
|
152
152
|
else
|
153
153
|
builder = build_middleware
|
154
|
-
builder.run options[:app] || lambda{|
|
154
|
+
builder.run options[:app] || lambda { |arg| run(arg) }
|
155
155
|
builder.call(env)
|
156
156
|
end
|
157
157
|
end
|
@@ -187,10 +187,10 @@ module Grape
|
|
187
187
|
output_key = options[:stringify] ? parent.to_s : parent.to_sym
|
188
188
|
if params.key?(parent) || options[:include_missing]
|
189
189
|
hash[output_key] = if children
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
190
|
+
declared(params[parent] || {}, options, Array(children))
|
191
|
+
else
|
192
|
+
params[parent]
|
193
|
+
end
|
194
194
|
end
|
195
195
|
end
|
196
196
|
|
@@ -200,15 +200,17 @@ module Grape
|
|
200
200
|
end
|
201
201
|
|
202
202
|
# The API version as specified in the URL.
|
203
|
-
def version
|
203
|
+
def version
|
204
|
+
env['api.version']
|
205
|
+
end
|
204
206
|
|
205
207
|
# End the request and display an error to the
|
206
208
|
# end user with the specified message.
|
207
209
|
#
|
208
210
|
# @param message [String] The message to display.
|
209
211
|
# @param status [Integer] the HTTP Status Code. Defaults to 403.
|
210
|
-
def error!(message, status=403)
|
211
|
-
throw :error, :
|
212
|
+
def error!(message, status = 403)
|
213
|
+
throw :error, message: message, status: status
|
212
214
|
end
|
213
215
|
|
214
216
|
# Redirect to a new url.
|
@@ -217,7 +219,7 @@ module Grape
|
|
217
219
|
# @param options [Hash] The options used when redirect.
|
218
220
|
# :permanent, default true.
|
219
221
|
def redirect(url, options = {})
|
220
|
-
merged_options = {:
|
222
|
+
merged_options = { permanent: false }.merge(options)
|
221
223
|
if merged_options[:permanent]
|
222
224
|
status 301
|
223
225
|
else
|
@@ -240,10 +242,10 @@ module Grape
|
|
240
242
|
else
|
241
243
|
return @status if @status
|
242
244
|
case request.request_method.to_s.upcase
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
245
|
+
when 'POST'
|
246
|
+
201
|
247
|
+
else
|
248
|
+
200
|
247
249
|
end
|
248
250
|
end
|
249
251
|
end
|
@@ -273,7 +275,7 @@ module Grape
|
|
273
275
|
# @example
|
274
276
|
# cookies[:mycookie] = 'mycookie val'
|
275
277
|
# cookies['mycookie-string'] = 'mycookie string val'
|
276
|
-
# cookies[:more] = { :
|
278
|
+
# cookies[:more] = { value: '123', expires: Time.at(0) }
|
277
279
|
# cookies.delete :more
|
278
280
|
#
|
279
281
|
def cookies
|
@@ -310,8 +312,8 @@ module Grape
|
|
310
312
|
#
|
311
313
|
# get '/users/:id' do
|
312
314
|
# present User.find(params[:id]),
|
313
|
-
# :
|
314
|
-
# :
|
315
|
+
# with: API::Entities::User,
|
316
|
+
# admin: current_user.admin?
|
315
317
|
# end
|
316
318
|
def present(*args)
|
317
319
|
options = args.count > 1 ? args.extract_options! : {}
|
@@ -334,15 +336,15 @@ module Grape
|
|
334
336
|
root = options.delete(:root)
|
335
337
|
|
336
338
|
representation = if entity_class
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
339
|
+
embeds = { env: env }
|
340
|
+
embeds[:version] = env['api.version'] if env['api.version']
|
341
|
+
entity_class.represent(object, embeds.merge(options))
|
342
|
+
else
|
343
|
+
object
|
344
|
+
end
|
343
345
|
|
344
346
|
representation = { root => representation } if root
|
345
|
-
representation = (@body || {}).merge(
|
347
|
+
representation = (@body || {}).merge(key => representation) if key
|
346
348
|
body representation
|
347
349
|
end
|
348
350
|
|
@@ -375,7 +377,7 @@ module Grape
|
|
375
377
|
@header = {}
|
376
378
|
@request = Grape::Request.new(@env)
|
377
379
|
|
378
|
-
|
380
|
+
extend helpers
|
379
381
|
cookies.read(@request)
|
380
382
|
|
381
383
|
run_filters befores
|
@@ -408,22 +410,22 @@ module Grape
|
|
408
410
|
|
409
411
|
b.use Rack::Head
|
410
412
|
b.use Grape::Middleware::Error,
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
413
|
+
format: settings[:format],
|
414
|
+
default_status: settings[:default_error_status] || 403,
|
415
|
+
rescue_all: settings[:rescue_all],
|
416
|
+
rescued_errors: aggregate_setting(:rescued_errors),
|
417
|
+
default_error_formatter: settings[:default_error_formatter],
|
418
|
+
error_formatters: settings[:error_formatters],
|
419
|
+
rescue_options: settings[:rescue_options],
|
420
|
+
rescue_handlers: merged_setting(:rescue_handlers)
|
419
421
|
|
420
422
|
aggregate_setting(:middleware).each do |m|
|
421
423
|
m = m.dup
|
422
424
|
block = m.pop if m.last.is_a?(Proc)
|
423
425
|
if block
|
424
|
-
b.use
|
426
|
+
b.use(*m, &block)
|
425
427
|
else
|
426
|
-
b.use
|
428
|
+
b.use(*m)
|
427
429
|
end
|
428
430
|
end
|
429
431
|
|
@@ -432,31 +434,33 @@ module Grape
|
|
432
434
|
|
433
435
|
if settings[:version]
|
434
436
|
b.use Grape::Middleware::Versioner.using(settings[:version_options][:using]), {
|
435
|
-
:
|
436
|
-
:
|
437
|
-
:
|
437
|
+
versions: settings[:version] ? settings[:version].flatten : nil,
|
438
|
+
version_options: settings[:version_options],
|
439
|
+
prefix: settings[:root_prefix]
|
438
440
|
}
|
439
441
|
end
|
440
442
|
|
441
443
|
b.use Grape::Middleware::Formatter,
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
444
|
+
format: settings[:format],
|
445
|
+
default_format: settings[:default_format] || :txt,
|
446
|
+
content_types: settings[:content_types],
|
447
|
+
formatters: settings[:formatters],
|
448
|
+
parsers: settings[:parsers]
|
447
449
|
|
448
450
|
b
|
449
451
|
end
|
450
452
|
|
451
453
|
def helpers
|
452
454
|
m = Module.new
|
453
|
-
settings.stack.each
|
455
|
+
settings.stack.each do |frame|
|
456
|
+
m.send :include, frame[:helpers] if frame[:helpers]
|
457
|
+
end
|
454
458
|
m
|
455
459
|
end
|
456
460
|
|
457
461
|
def aggregate_setting(key)
|
458
462
|
settings.stack.inject([]) do |aggregate, frame|
|
459
|
-
aggregate
|
463
|
+
aggregate + (frame[key] || [])
|
460
464
|
end
|
461
465
|
end
|
462
466
|
|
@@ -468,12 +472,20 @@ module Grape
|
|
468
472
|
|
469
473
|
def run_filters(filters)
|
470
474
|
(filters || []).each do |filter|
|
471
|
-
instance_eval
|
475
|
+
instance_eval(&filter)
|
472
476
|
end
|
473
477
|
end
|
474
478
|
|
475
|
-
def befores
|
476
|
-
|
477
|
-
|
479
|
+
def befores
|
480
|
+
aggregate_setting(:befores)
|
481
|
+
end
|
482
|
+
|
483
|
+
def after_validations
|
484
|
+
aggregate_setting(:after_validations)
|
485
|
+
end
|
486
|
+
|
487
|
+
def afters
|
488
|
+
aggregate_setting(:afters)
|
489
|
+
end
|
478
490
|
end
|
479
491
|
end
|
@@ -5,10 +5,11 @@ module Grape
|
|
5
5
|
class << self
|
6
6
|
|
7
7
|
FORMATTERS = {
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
8
|
+
serializable_hash: Grape::ErrorFormatter::Json,
|
9
|
+
json: Grape::ErrorFormatter::Json,
|
10
|
+
jsonapi: Grape::ErrorFormatter::Json,
|
11
|
+
txt: Grape::ErrorFormatter::Txt,
|
12
|
+
xml: Grape::ErrorFormatter::Xml
|
12
13
|
}
|
13
14
|
|
14
15
|
def formatters(options)
|
@@ -4,9 +4,9 @@ module Grape
|
|
4
4
|
class << self
|
5
5
|
|
6
6
|
def call(message, backtrace, options = {}, env = nil)
|
7
|
-
result = message.is_a?(Hash) ? message : { :
|
8
|
-
if (options[:rescue_options] || {})[:backtrace] && backtrace && !
|
9
|
-
result = result.merge(
|
7
|
+
result = message.is_a?(Hash) ? message : { error: message }
|
8
|
+
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
9
|
+
result = result.merge(backtrace: backtrace)
|
10
10
|
end
|
11
11
|
MultiJson.dump(result)
|
12
12
|
end
|
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
|
6
6
|
def call(message, backtrace, options = {}, env = nil)
|
7
7
|
result = message.is_a?(Hash) ? MultiJson.dump(message) : message
|
8
|
-
if (options[:rescue_options] || {})[:backtrace] && backtrace && !
|
8
|
+
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
9
9
|
result += "\r\n "
|
10
10
|
result += backtrace.join("\r\n ")
|
11
11
|
end
|
@@ -4,11 +4,11 @@ module Grape
|
|
4
4
|
class << self
|
5
5
|
|
6
6
|
def call(message, backtrace, options = {}, env = nil)
|
7
|
-
result = message.is_a?(Hash) ? message : { :
|
8
|
-
if (options[:rescue_options] || {})[:backtrace] && backtrace && !
|
9
|
-
result = result.merge(
|
7
|
+
result = message.is_a?(Hash) ? message : { message: message }
|
8
|
+
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
9
|
+
result = result.merge(backtrace: backtrace)
|
10
10
|
end
|
11
|
-
result.respond_to?(:to_xml) ? result.to_xml(:
|
11
|
+
result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
|
12
12
|
end
|
13
13
|
|
14
14
|
end
|
@@ -15,20 +15,21 @@ module Grape
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def [](index)
|
18
|
-
|
18
|
+
send index
|
19
19
|
end
|
20
20
|
|
21
21
|
protected
|
22
|
+
|
22
23
|
# TODO: translate attribute first
|
23
24
|
# if BASE_ATTRIBUTES_KEY.key respond to a string message, then short_message is returned
|
24
25
|
# if BASE_ATTRIBUTES_KEY.key respond to a Hash, means it may have problem , summary and resolution
|
25
|
-
def compose_message(key, attributes = {}
|
26
|
+
def compose_message(key, attributes = {})
|
26
27
|
short_message = translate_message(key, attributes)
|
27
28
|
if short_message.is_a? Hash
|
28
29
|
@problem = problem(key, attributes)
|
29
30
|
@summary = summary(key, attributes)
|
30
31
|
@resolution = resolution(key, attributes)
|
31
|
-
[
|
32
|
+
[["Problem", @problem], ["Summary", @summary], ["Resolution", @resolution]].reduce("") do |message, detail_array|
|
32
33
|
message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
|
33
34
|
message
|
34
35
|
end
|
@@ -49,18 +50,17 @@ module Grape
|
|
49
50
|
translate_message("#{key}.resolution", attributes)
|
50
51
|
end
|
51
52
|
|
52
|
-
|
53
53
|
def translate_attribute(key, options = {})
|
54
|
-
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", { :
|
54
|
+
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", { default: key }.merge(options))
|
55
55
|
end
|
56
56
|
|
57
57
|
def translate_message(key, options = {})
|
58
|
-
translate("#{BASE_MESSAGES_KEY}.#{key}", { :
|
58
|
+
translate("#{BASE_MESSAGES_KEY}.#{key}", { default: '' }.merge(options))
|
59
59
|
end
|
60
60
|
|
61
61
|
def translate(key, options = {})
|
62
62
|
message = ::I18n.translate(key, options)
|
63
|
-
message.present? ? message : ::I18n.translate(key, options.merge(
|
63
|
+
message.present? ? message : ::I18n.translate(key, options.merge(locale: FALLBACK_LOCALE))
|
64
64
|
end
|
65
65
|
|
66
66
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Grape
|
3
|
+
module Exceptions
|
4
|
+
class IncompatibleOptionValues < Base
|
5
|
+
|
6
|
+
def initialize(option1, value1, option2, value2)
|
7
|
+
super(message: compose_message("incompatible_option_values", option1: option1, value1: value1, option2: option2, value2: value2))
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -4,7 +4,7 @@ module Grape
|
|
4
4
|
class InvalidFormatter < Base
|
5
5
|
|
6
6
|
def initialize(klass, to_format)
|
7
|
-
super(:
|
7
|
+
super(message: compose_message("invalid_formatter", klass: klass, to_format: to_format))
|
8
8
|
end
|
9
9
|
|
10
10
|
end
|