grape 0.13.0 → 0.14.0
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 +4 -4
- data/Appraisals +9 -4
- data/CHANGELOG.md +28 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +166 -0
- data/README.md +305 -163
- data/Rakefile +30 -33
- data/UPGRADING.md +31 -0
- data/benchmark/simple.rb +27 -0
- data/gemfiles/rack_1.5.2.gemfile +13 -0
- data/gemfiles/rails_3.gemfile +2 -2
- data/gemfiles/rails_4.gemfile +1 -2
- data/grape.gemspec +5 -4
- data/lib/grape.rb +9 -5
- data/lib/grape/dsl/configuration.rb +5 -2
- data/lib/grape/dsl/helpers.rb +8 -3
- data/lib/grape/dsl/inside_route.rb +67 -44
- data/lib/grape/dsl/parameters.rb +21 -12
- data/lib/grape/dsl/request_response.rb +1 -1
- data/lib/grape/dsl/routing.rb +3 -4
- data/lib/grape/endpoint.rb +63 -28
- data/lib/grape/error_formatter/base.rb +6 -6
- data/lib/grape/exceptions/base.rb +5 -5
- data/lib/grape/exceptions/invalid_version_header.rb +10 -0
- data/lib/grape/formatter/serializable_hash.rb +3 -2
- data/lib/grape/locale/en.yml +4 -1
- data/lib/grape/middleware/auth/base.rb +2 -2
- data/lib/grape/middleware/auth/dsl.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/base.rb +7 -4
- data/lib/grape/middleware/error.rb +3 -2
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +47 -44
- data/lib/grape/middleware/globals.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
- data/lib/grape/middleware/versioner/header.rb +113 -50
- data/lib/grape/middleware/versioner/param.rb +5 -8
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
- data/lib/grape/middleware/versioner/path.rb +3 -6
- data/lib/grape/path.rb +3 -3
- data/lib/grape/request.rb +40 -0
- data/lib/grape/util/content_types.rb +9 -9
- data/lib/grape/util/env.rb +22 -0
- data/lib/grape/util/strict_hash_configuration.rb +2 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -3
- data/lib/grape/validations/params_scope.rb +83 -15
- data/lib/grape/validations/types.rb +144 -0
- data/lib/grape/validations/types/build_coercer.rb +53 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
- data/lib/grape/validations/types/file.rb +28 -0
- data/lib/grape/validations/types/json.rb +65 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
- data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -3
- data/lib/grape/validations/validators/base.rb +7 -0
- data/lib/grape/validations/validators/coerce.rb +31 -42
- data/lib/grape/validations/validators/presence.rb +2 -3
- data/lib/grape/validations/validators/regexp.rb +2 -4
- data/lib/grape/validations/validators/values.rb +3 -3
- data/lib/grape/version.rb +1 -1
- data/pkg/grape-0.13.0.gem +0 -0
- data/spec/grape/api/custom_validations_spec.rb +5 -4
- data/spec/grape/api/deeply_included_options_spec.rb +7 -7
- data/spec/grape/api/nested_helpers_spec.rb +4 -2
- data/spec/grape/api/shared_helpers_spec.rb +8 -8
- data/spec/grape/api_spec.rb +88 -54
- data/spec/grape/dsl/configuration_spec.rb +13 -0
- data/spec/grape/dsl/helpers_spec.rb +16 -2
- data/spec/grape/dsl/inside_route_spec.rb +3 -2
- data/spec/grape/dsl/parameters_spec.rb +0 -6
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/endpoint_spec.rb +61 -20
- data/spec/grape/entity_spec.rb +10 -8
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
- data/spec/grape/integration/rack_spec.rb +3 -2
- data/spec/grape/middleware/base_spec.rb +7 -5
- data/spec/grape/middleware/error_spec.rb +16 -15
- data/spec/grape/middleware/exception_spec.rb +45 -43
- data/spec/grape/middleware/formatter_spec.rb +34 -0
- data/spec/grape/middleware/versioner/header_spec.rb +79 -47
- data/spec/grape/path_spec.rb +10 -10
- data/spec/grape/presenters/presenter_spec.rb +2 -2
- data/spec/grape/request_spec.rb +100 -0
- data/spec/grape/validations/params_scope_spec.rb +11 -9
- data/spec/grape/validations/types_spec.rb +95 -0
- data/spec/grape/validations/validators/coerce_spec.rb +335 -2
- data/spec/grape/validations/validators/values_spec.rb +15 -15
- data/spec/grape/validations_spec.rb +53 -24
- data/spec/shared/versioning_examples.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/versioned_helpers.rb +2 -2
- metadata +51 -13
- data/.gitignore +0 -46
- data/.rspec +0 -2
- data/.rubocop.yml +0 -7
- data/.rubocop_todo.yml +0 -84
- data/.travis.yml +0 -20
- data/.yardopts +0 -2
- data/lib/grape/http/request.rb +0 -35
- data/lib/grape/util/parameter_types.rb +0 -58
- data/spec/grape/util/parameter_types_spec.rb +0 -54
@@ -47,6 +47,7 @@ module Grape
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def rescuable?(klass)
|
50
|
+
return false if klass == Grape::Exceptions::InvalidVersionHeader
|
50
51
|
options[:rescue_all] || (options[:rescue_handlers] || []).any? { |error, _handler| klass <= error } || (options[:base_only_rescue_handlers] || []).include?(klass)
|
51
52
|
end
|
52
53
|
|
@@ -59,7 +60,7 @@ module Grape
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def error!(message, status = options[:default_status], headers = {}, backtrace = [])
|
62
|
-
headers =
|
63
|
+
headers = headers.reverse_merge(Grape::Http::Headers::CONTENT_TYPE => content_type)
|
63
64
|
rack_response(format_message(message, backtrace), status, headers)
|
64
65
|
end
|
65
66
|
|
@@ -82,7 +83,7 @@ module Grape
|
|
82
83
|
end
|
83
84
|
|
84
85
|
def format_message(message, backtrace)
|
85
|
-
format = env[
|
86
|
+
format = env[Grape::Env::API_FORMAT] || options[:format]
|
86
87
|
formatter = Grape::ErrorFormatter::Base.formatter_for(format, options)
|
87
88
|
throw :error, status: 406, message: "The requested format '#{format}' is not supported." unless formatter
|
88
89
|
formatter.call(message, backtrace, options, env)
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
# This is a simple middleware for adding before and after filters
|
4
4
|
# to Grape APIs. It is used like so:
|
5
5
|
#
|
6
|
-
# use Grape::Middleware::Filter, before:
|
6
|
+
# use Grape::Middleware::Filter, before: -> { do_something }, after: -> { do_something }
|
7
7
|
class Filter < Base
|
8
8
|
def before
|
9
9
|
app.instance_eval(&options[:before]) if options[:before]
|
@@ -3,6 +3,8 @@ require 'grape/middleware/base'
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
class Formatter < Base
|
6
|
+
CHUNKED = 'chunked'.freeze
|
7
|
+
|
6
8
|
def default_options
|
7
9
|
{
|
8
10
|
default_format: :txt,
|
@@ -19,35 +21,36 @@ module Grape
|
|
19
21
|
def after
|
20
22
|
status, headers, bodies = *@app_response
|
21
23
|
|
22
|
-
if
|
23
|
-
|
24
|
-
|
25
|
-
response =
|
26
|
-
Rack::Response.new([], status, headers) do |resp|
|
27
|
-
resp.body = bodies.file
|
28
|
-
end
|
24
|
+
if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
|
25
|
+
@app_response
|
29
26
|
else
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
build_formatted_response(status, headers, bodies)
|
28
|
+
end
|
29
|
+
end
|
33
30
|
|
34
|
-
|
35
|
-
bodymap = bodies.collect do |body|
|
36
|
-
formatter.call(body, env)
|
37
|
-
end
|
31
|
+
private
|
38
32
|
|
39
|
-
|
33
|
+
def build_formatted_response(status, headers, bodies)
|
34
|
+
headers = ensure_content_type(headers)
|
40
35
|
|
41
|
-
|
42
|
-
|
43
|
-
|
36
|
+
if bodies.is_a?(Grape::Util::FileResponse)
|
37
|
+
Rack::Response.new([], status, headers) do |resp|
|
38
|
+
resp.body = bodies.file
|
44
39
|
end
|
40
|
+
else
|
41
|
+
# Allow content-type to be explicitly overwritten
|
42
|
+
formatter = fetch_formatter(headers, options)
|
43
|
+
bodymap = bodies.collect { |body| formatter.call(body, env) }
|
44
|
+
Rack::Response.new(bodymap, status, headers)
|
45
45
|
end
|
46
|
-
|
47
|
-
|
46
|
+
rescue Grape::Exceptions::InvalidFormatter => e
|
47
|
+
throw :error, status: 500, message: e.message
|
48
48
|
end
|
49
49
|
|
50
|
-
|
50
|
+
def fetch_formatter(headers, options)
|
51
|
+
api_format = mime_types[headers[Grape::Http::Headers::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT]
|
52
|
+
Grape::Formatter::Base.formatter_for(api_format, options)
|
53
|
+
end
|
51
54
|
|
52
55
|
# Set the content type header for the API format if it is not already present.
|
53
56
|
#
|
@@ -57,7 +60,7 @@ module Grape
|
|
57
60
|
if headers[Grape::Http::Headers::CONTENT_TYPE]
|
58
61
|
headers
|
59
62
|
else
|
60
|
-
headers.merge(Grape::Http::Headers::CONTENT_TYPE => content_type_for(env[
|
63
|
+
headers.merge(Grape::Http::Headers::CONTENT_TYPE => content_type_for(env[Grape::Env::API_FORMAT]))
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
@@ -67,20 +70,20 @@ module Grape
|
|
67
70
|
|
68
71
|
# store read input in env['api.request.input']
|
69
72
|
def read_body_input
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
73
|
+
return unless
|
74
|
+
(request.post? || request.put? || request.patch? || request.delete?) &&
|
75
|
+
(!request.form_data? || !request.media_type) &&
|
76
|
+
(!request.parseable_data?) &&
|
77
|
+
(request.content_length.to_i > 0 || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
|
78
|
+
|
79
|
+
return unless (input = env[Grape::Env::RACK_INPUT])
|
80
|
+
|
81
|
+
input.rewind
|
82
|
+
body = env[Grape::Env::API_REQUEST_INPUT] = input.read
|
83
|
+
begin
|
84
|
+
read_rack_input(body) if body && body.length > 0
|
85
|
+
ensure
|
86
|
+
input.rewind
|
84
87
|
end
|
85
88
|
end
|
86
89
|
|
@@ -92,14 +95,14 @@ module Grape
|
|
92
95
|
parser = Grape::Parser::Base.parser_for fmt, options
|
93
96
|
if parser
|
94
97
|
begin
|
95
|
-
body = (env[
|
98
|
+
body = (env[Grape::Env::API_REQUEST_BODY] = parser.call(body, env))
|
96
99
|
if body.is_a?(Hash)
|
97
|
-
if env[
|
98
|
-
env[
|
100
|
+
if env[Grape::Env::RACK_REQUEST_FORM_HASH]
|
101
|
+
env[Grape::Env::RACK_REQUEST_FORM_HASH] = env[Grape::Env::RACK_REQUEST_FORM_HASH].merge(body)
|
99
102
|
else
|
100
|
-
env[
|
103
|
+
env[Grape::Env::RACK_REQUEST_FORM_HASH] = body
|
101
104
|
end
|
102
|
-
env[
|
105
|
+
env[Grape::Env::RACK_REQUEST_FORM_INPUT] = env[Grape::Env::RACK_INPUT]
|
103
106
|
end
|
104
107
|
rescue Grape::Exceptions::Base => e
|
105
108
|
raise e
|
@@ -107,7 +110,7 @@ module Grape
|
|
107
110
|
throw :error, status: 400, message: e.message
|
108
111
|
end
|
109
112
|
else
|
110
|
-
env[
|
113
|
+
env[Grape::Env::API_REQUEST_BODY] = body
|
111
114
|
end
|
112
115
|
else
|
113
116
|
throw :error, status: 406, message: "The requested content-type '#{request.media_type}' is not supported."
|
@@ -117,7 +120,7 @@ module Grape
|
|
117
120
|
def negotiate_content_type
|
118
121
|
fmt = format_from_extension || format_from_params || options[:format] || format_from_header || options[:default_format]
|
119
122
|
if content_type_for(fmt)
|
120
|
-
env[
|
123
|
+
env[Grape::Env::API_FORMAT] = fmt
|
121
124
|
else
|
122
125
|
throw :error, status: 406, message: "The requested format '#{fmt}' is not supported."
|
123
126
|
end
|
@@ -165,7 +168,7 @@ module Grape
|
|
165
168
|
|
166
169
|
accept.scan(accept_into_mime_and_quality)
|
167
170
|
.sort_by { |_, quality_preference| -quality_preference.to_f }
|
168
|
-
.
|
171
|
+
.flat_map { |mime, _| [mime, mime.sub(vendor_prefix_pattern, '')] }
|
169
172
|
end
|
170
173
|
end
|
171
174
|
end
|
@@ -5,9 +5,9 @@ module Grape
|
|
5
5
|
class Globals < Base
|
6
6
|
def before
|
7
7
|
request = Grape::Request.new(@env)
|
8
|
-
@env[
|
9
|
-
@env[
|
10
|
-
@env[
|
8
|
+
@env[Grape::Env::GRAPE_REQUEST] = request
|
9
|
+
@env[Grape::Env::GRAPE_REQUEST_HEADERS] = request.headers
|
10
|
+
@env[Grape::Env::GRAPE_REQUEST_PARAMS] = request.params if @env[Grape::Env::RACK_INPUT]
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -27,14 +27,12 @@ module Grape
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
# If the requested version is not supported:
|
32
|
-
unless versions.any? { |v| v.to_s == potential_version }
|
33
|
-
throw :error, status: 406, headers: error_headers, message: 'The requested version is not supported.'
|
34
|
-
end
|
30
|
+
return if potential_version.empty?
|
35
31
|
|
36
|
-
|
37
|
-
|
32
|
+
# If the requested version is not supported:
|
33
|
+
throw :error, status: 406, headers: error_headers, message: 'The requested version is not supported.' unless versions.any? { |v| v.to_s == potential_version }
|
34
|
+
|
35
|
+
env[Grape::Env::API_VERSION] = potential_version
|
38
36
|
end
|
39
37
|
|
40
38
|
private
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'grape/middleware/base'
|
2
|
+
require 'grape/middleware/versioner/parse_media_type_patch'
|
2
3
|
|
3
4
|
module Grape
|
4
5
|
module Middleware
|
@@ -8,13 +9,13 @@ module Grape
|
|
8
9
|
# application/vnd.:vendor-:version+:format
|
9
10
|
#
|
10
11
|
# Example: For request header
|
11
|
-
# Accept: application/vnd.mycompany-v1+json
|
12
|
+
# Accept: application/vnd.mycompany.a-cool-resource-v1+json
|
12
13
|
#
|
13
14
|
# The following rack env variables are set:
|
14
15
|
#
|
15
16
|
# env['api.type'] => 'application'
|
16
|
-
# env['api.subtype'] => 'vnd.mycompany-v1+json'
|
17
|
-
# env['api.vendor] => 'mycompany'
|
17
|
+
# env['api.subtype'] => 'vnd.mycompany.a-cool-resource-v1+json'
|
18
|
+
# env['api.vendor] => 'mycompany.a-cool-resource'
|
18
19
|
# env['api.version] => 'v1'
|
19
20
|
# env['api.format] => 'json'
|
20
21
|
#
|
@@ -22,54 +23,88 @@ module Grape
|
|
22
23
|
# X-Cascade header to alert Rack::Mount to attempt the next matched
|
23
24
|
# route.
|
24
25
|
class Header < Base
|
25
|
-
|
26
|
-
|
26
|
+
VENDOR_VERSION_HEADER_REGEX =
|
27
|
+
/\Avnd\.([a-z0-9.\-_!#\$&\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
if header.qvalues.empty?
|
31
|
-
fail Grape::Exceptions::InvalidAcceptHeader.new('Accept header must be set.', error_headers)
|
32
|
-
end
|
33
|
-
# Remove any acceptable content types with ranges.
|
34
|
-
header.qvalues.reject! do |media_type, _|
|
35
|
-
Rack::Accept::Header.parse_media_type(media_type).find { |s| s == '*' }
|
36
|
-
end
|
37
|
-
# If all Accept headers included a range:
|
38
|
-
if header.qvalues.empty?
|
39
|
-
fail Grape::Exceptions::InvalidAcceptHeader.new('Accept header must not contain ranges ("*").',
|
40
|
-
error_headers)
|
41
|
-
end
|
42
|
-
end
|
29
|
+
HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#\$&\^]+/
|
30
|
+
HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#\$&\^]+?)(?:-([a-z0-9*.]+))+/
|
43
31
|
|
44
|
-
|
32
|
+
def before
|
33
|
+
strict_header_checks if strict?
|
45
34
|
|
46
35
|
if media_type
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
env['api.vendor'] = Regexp.last_match[1]
|
53
|
-
env['api.version'] = Regexp.last_match[2]
|
54
|
-
env['api.format'] = Regexp.last_match[3] # weird that Grape::Middleware::Formatter also does this
|
55
|
-
end
|
56
|
-
# If none of the available content types are acceptable:
|
57
|
-
elsif strict?
|
58
|
-
fail Grape::Exceptions::InvalidAcceptHeader.new('406 Not Acceptable', error_headers)
|
59
|
-
# If all acceptable content types specify a vendor or version that doesn't exist:
|
60
|
-
elsif header.values.all? { |header_value| has_vendor?(header_value) || version?(header_value) }
|
61
|
-
fail Grape::Exceptions::InvalidAcceptHeader.new('API vendor or version not found.', error_headers)
|
36
|
+
media_type_header_handler
|
37
|
+
elsif headers_contain_wrong_vendor?
|
38
|
+
fail_with_invalid_accept_header!('API vendor not found.')
|
39
|
+
elsif headers_contain_wrong_version?
|
40
|
+
fail_with_invalid_version_header!('API version not found.')
|
62
41
|
end
|
63
42
|
end
|
64
43
|
|
65
44
|
private
|
66
45
|
|
46
|
+
def strict_header_checks
|
47
|
+
strict_accept_header_presence_check
|
48
|
+
strict_version_vendor_accept_header_presence_check
|
49
|
+
end
|
50
|
+
|
51
|
+
def strict_accept_header_presence_check
|
52
|
+
return unless header.qvalues.empty?
|
53
|
+
fail_with_invalid_accept_header!('Accept header must be set.')
|
54
|
+
end
|
55
|
+
|
56
|
+
def strict_version_vendor_accept_header_presence_check
|
57
|
+
return unless versions.present?
|
58
|
+
return if an_accept_header_with_version_and_vendor_is_present?
|
59
|
+
fail_with_invalid_accept_header!('API vendor or version not found.')
|
60
|
+
end
|
61
|
+
|
62
|
+
def an_accept_header_with_version_and_vendor_is_present?
|
63
|
+
header.qvalues.keys.any? do |h|
|
64
|
+
VENDOR_VERSION_HEADER_REGEX =~ h.sub('application/', '')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def header
|
69
|
+
@header ||= rack_accept_header
|
70
|
+
end
|
71
|
+
|
72
|
+
def media_type
|
73
|
+
@media_type ||= header.best_of(available_media_types)
|
74
|
+
end
|
75
|
+
|
76
|
+
def media_type_header_handler
|
77
|
+
type, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
78
|
+
env[Grape::Env::API_TYPE] = type
|
79
|
+
env[Grape::Env::API_SUBTYPE] = subtype
|
80
|
+
|
81
|
+
return unless VENDOR_VERSION_HEADER_REGEX =~ subtype
|
82
|
+
|
83
|
+
env[Grape::Env::API_VENDOR] = Regexp.last_match[1]
|
84
|
+
env[Grape::Env::API_VERSION] = Regexp.last_match[2]
|
85
|
+
# weird that Grape::Middleware::Formatter also does this
|
86
|
+
env[Grape::Env::API_FORMAT] = Regexp.last_match[3]
|
87
|
+
end
|
88
|
+
|
89
|
+
def fail_with_invalid_accept_header!(message)
|
90
|
+
fail Grape::Exceptions::InvalidAcceptHeader
|
91
|
+
.new(message, error_headers)
|
92
|
+
end
|
93
|
+
|
94
|
+
def fail_with_invalid_version_header!(message)
|
95
|
+
fail Grape::Exceptions::InvalidVersionHeader
|
96
|
+
.new(message, error_headers)
|
97
|
+
end
|
98
|
+
|
67
99
|
def available_media_types
|
68
100
|
available_media_types = []
|
69
101
|
|
70
102
|
content_types.each do |extension, _media_type|
|
71
103
|
versions.reverse_each do |version|
|
72
|
-
available_media_types += [
|
104
|
+
available_media_types += [
|
105
|
+
"application/vnd.#{vendor}-#{version}+#{extension}",
|
106
|
+
"application/vnd.#{vendor}-#{version}"
|
107
|
+
]
|
73
108
|
end
|
74
109
|
available_media_types << "application/vnd.#{vendor}+#{extension}"
|
75
110
|
end
|
@@ -83,10 +118,22 @@ module Grape
|
|
83
118
|
available_media_types.flatten
|
84
119
|
end
|
85
120
|
|
121
|
+
def headers_contain_wrong_vendor?
|
122
|
+
header.values.all? do |header_value|
|
123
|
+
vendor?(header_value) && request_vendor(header_value) != vendor
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def headers_contain_wrong_version?
|
128
|
+
header.values.all? do |header_value|
|
129
|
+
version?(header_value) && !versions.include?(request_version(header_value))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
86
133
|
def rack_accept_header
|
87
134
|
Rack::Accept::MediaType.new env[Grape::Http::Headers::HTTP_ACCEPT]
|
88
135
|
rescue RuntimeError => e
|
89
|
-
|
136
|
+
fail_with_invalid_accept_header!(e.message)
|
90
137
|
end
|
91
138
|
|
92
139
|
def versions
|
@@ -94,19 +141,25 @@ module Grape
|
|
94
141
|
end
|
95
142
|
|
96
143
|
def vendor
|
97
|
-
|
144
|
+
version_options && version_options[:vendor]
|
98
145
|
end
|
99
146
|
|
100
147
|
def strict?
|
101
|
-
|
148
|
+
version_options && version_options[:strict]
|
149
|
+
end
|
150
|
+
|
151
|
+
def version_options
|
152
|
+
options[:version_options]
|
102
153
|
end
|
103
154
|
|
104
|
-
# By default those errors contain an `X-Cascade` header set to `pass`,
|
105
|
-
#
|
106
|
-
#
|
155
|
+
# By default those errors contain an `X-Cascade` header set to `pass`,
|
156
|
+
# which allows nesting and stacking of routes
|
157
|
+
# (see [Rack::Mount](https://github.com/josh/rack-mount) for more
|
158
|
+
# information). To prevent # this behavior, and not add the `X-Cascade`
|
159
|
+
# header, one can set the `:cascade` option to `false`.
|
107
160
|
def cascade?
|
108
|
-
if
|
109
|
-
!!
|
161
|
+
if version_options && version_options.key?(:cascade)
|
162
|
+
!!version_options[:cascade]
|
110
163
|
else
|
111
164
|
true
|
112
165
|
end
|
@@ -118,16 +171,26 @@ module Grape
|
|
118
171
|
|
119
172
|
# @param [String] media_type a content type
|
120
173
|
# @return [Boolean] whether the content type sets a vendor
|
121
|
-
def
|
122
|
-
_, subtype = Rack::Accept::Header.parse_media_type
|
123
|
-
subtype[
|
174
|
+
def vendor?(media_type)
|
175
|
+
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
176
|
+
subtype[HAS_VENDOR_REGEX]
|
177
|
+
end
|
178
|
+
|
179
|
+
def request_vendor(media_type)
|
180
|
+
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
181
|
+
subtype.match(VENDOR_VERSION_HEADER_REGEX)[1]
|
182
|
+
end
|
183
|
+
|
184
|
+
def request_version(media_type)
|
185
|
+
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
186
|
+
subtype.match(VENDOR_VERSION_HEADER_REGEX)[2]
|
124
187
|
end
|
125
188
|
|
126
189
|
# @param [String] media_type a content type
|
127
190
|
# @return [Boolean] whether the content type sets an API version
|
128
191
|
def version?(media_type)
|
129
|
-
_, subtype = Rack::Accept::Header.parse_media_type
|
130
|
-
subtype[
|
192
|
+
_, subtype = Rack::Accept::Header.parse_media_type(media_type)
|
193
|
+
subtype[HAS_VERSION_REGEX]
|
131
194
|
end
|
132
195
|
end
|
133
196
|
end
|