grape 2.2.0 → 2.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +41 -18
- data/UPGRADING.md +75 -1
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +25 -60
- data/lib/grape/api.rb +44 -76
- data/lib/grape/cookies.rb +31 -25
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/desc.rb +27 -24
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/helpers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +17 -40
- data/lib/grape/dsl/parameters.rb +5 -5
- data/lib/grape/dsl/routing.rb +14 -13
- data/lib/grape/endpoint.rb +100 -106
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -24
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +13 -20
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +4 -12
- data/lib/grape/exceptions/base.rb +18 -30
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/validation.rb +5 -4
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +2 -1
- data/lib/grape/extensions/hashie/mash.rb +3 -5
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +4 -6
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +4 -12
- data/lib/grape/locale/en.yml +44 -44
- data/lib/grape/middleware/auth/base.rb +11 -32
- data/lib/grape/middleware/auth/dsl.rb +23 -29
- data/lib/grape/middleware/base.rb +30 -11
- data/lib/grape/middleware/error.rb +18 -24
- data/lib/grape/middleware/formatter.rb +39 -73
- data/lib/grape/middleware/stack.rb +26 -36
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -3
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +4 -10
- data/lib/grape/middleware/versioner/param.rb +2 -5
- data/lib/grape/middleware/versioner/path.rb +0 -2
- data/lib/grape/middleware/versioner.rb +5 -3
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -7
- data/lib/grape/path.rb +39 -56
- data/lib/grape/request.rb +162 -23
- data/lib/grape/router/base_route.rb +2 -2
- data/lib/grape/router/greedy_route.rb +2 -2
- data/lib/grape/router/pattern.rb +23 -18
- data/lib/grape/router/route.rb +14 -6
- data/lib/grape/router.rb +30 -12
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/validations/contract_scope.rb +2 -39
- data/lib/grape/validations/params_scope.rb +15 -14
- data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
- data/lib/grape/validations/validator_factory.rb +2 -2
- data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
- data/lib/grape/validations/validators/base.rb +7 -11
- data/lib/grape/validations/validators/coerce_validator.rb +1 -1
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +1 -1
- data/lib/grape/validations/validators/except_values_validator.rb +2 -2
- data/lib/grape/validations/validators/length_validator.rb +1 -1
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +2 -2
- data/lib/grape/validations/validators/values_validator.rb +15 -57
- data/lib/grape/validations.rb +8 -17
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +14 -2
- metadata +24 -16
- data/lib/grape/http/headers.rb +0 -55
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner_helpers.rb +0 -75
- data/lib/grape/util/lazy/object.rb +0 -45
- data/lib/grape/validations/types/build_coercer.rb +0 -92
@@ -3,16 +3,9 @@
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
class Formatter < Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def default_options
|
10
|
-
{
|
11
|
-
default_format: :txt,
|
12
|
-
formatters: {},
|
13
|
-
parsers: {}
|
14
|
-
}
|
15
|
-
end
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
default_format: :txt
|
8
|
+
}.freeze
|
16
9
|
|
17
10
|
def before
|
18
11
|
negotiate_content_type
|
@@ -53,7 +46,7 @@ module Grape
|
|
53
46
|
end
|
54
47
|
|
55
48
|
def fetch_formatter(headers, options)
|
56
|
-
api_format = mime_types[headers[Rack::CONTENT_TYPE]]
|
49
|
+
api_format = env.fetch(Grape::Env::API_FORMAT) { mime_types[headers[Rack::CONTENT_TYPE]] }
|
57
50
|
Grape::Formatter.formatter_for(api_format, options[:formatters])
|
58
51
|
end
|
59
52
|
|
@@ -69,34 +62,27 @@ module Grape
|
|
69
62
|
end
|
70
63
|
end
|
71
64
|
|
72
|
-
def request
|
73
|
-
@request ||= Rack::Request.new(env)
|
74
|
-
end
|
75
|
-
|
76
|
-
# store read input in env['api.request.input']
|
77
65
|
def read_body_input
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
!request.parseable_data? &&
|
82
|
-
(request.content_length.to_i.positive? || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
|
66
|
+
input = rack_request.body # reads RACK_INPUT
|
67
|
+
return if input.nil?
|
68
|
+
return unless read_body_input?
|
83
69
|
|
84
|
-
|
85
|
-
|
86
|
-
rewind_input input
|
70
|
+
input.try(:rewind)
|
87
71
|
body = env[Grape::Env::API_REQUEST_INPUT] = input.read
|
88
72
|
begin
|
89
|
-
read_rack_input(body)
|
73
|
+
read_rack_input(body)
|
90
74
|
ensure
|
91
|
-
|
75
|
+
input.try(:rewind)
|
92
76
|
end
|
93
77
|
end
|
94
78
|
|
95
|
-
# store parsed input in env['api.request.body']
|
96
79
|
def read_rack_input(body)
|
97
|
-
|
80
|
+
return if body.empty?
|
81
|
+
|
82
|
+
media_type = rack_request.media_type
|
83
|
+
fmt = media_type ? mime_types[media_type] : options[:default_format]
|
98
84
|
|
99
|
-
throw :error, status: 415, message: "The provided content-type '#{
|
85
|
+
throw :error, status: 415, message: "The provided content-type '#{media_type}' is not supported." unless content_type_for(fmt)
|
100
86
|
parser = Grape::Parser.parser_for fmt, options[:parsers]
|
101
87
|
if parser
|
102
88
|
begin
|
@@ -119,63 +105,43 @@ module Grape
|
|
119
105
|
end
|
120
106
|
end
|
121
107
|
|
108
|
+
# this middleware will not try to format the following content-types since Rack already handles them
|
109
|
+
# when calling Rack's `params` function
|
110
|
+
# - application/x-www-form-urlencoded
|
111
|
+
# - multipart/form-data
|
112
|
+
# - multipart/related
|
113
|
+
# - multipart/mixed
|
114
|
+
def read_body_input?
|
115
|
+
(rack_request.post? || rack_request.put? || rack_request.patch? || rack_request.delete?) &&
|
116
|
+
!(rack_request.form_data? && rack_request.content_type) &&
|
117
|
+
!rack_request.parseable_data? &&
|
118
|
+
(rack_request.content_length.to_i.positive? || rack_request.env['HTTP_TRANSFER_ENCODING'] == 'chunked')
|
119
|
+
end
|
120
|
+
|
122
121
|
def negotiate_content_type
|
123
|
-
fmt = format_from_extension ||
|
122
|
+
fmt = format_from_extension || query_params['format'] || options[:format] || format_from_header || options[:default_format]
|
124
123
|
if content_type_for(fmt)
|
125
|
-
env[Grape::Env::API_FORMAT] = fmt
|
124
|
+
env[Grape::Env::API_FORMAT] = fmt.to_sym
|
126
125
|
else
|
127
126
|
throw :error, status: 406, message: "The requested format '#{fmt}' is not supported."
|
128
127
|
end
|
129
128
|
end
|
130
129
|
|
131
130
|
def format_from_extension
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
extension = parts.last
|
136
|
-
# avoid symbol memory leak on an unknown format
|
137
|
-
return extension.to_sym if content_type_for(extension)
|
138
|
-
end
|
139
|
-
nil
|
140
|
-
end
|
141
|
-
|
142
|
-
def format_from_params
|
143
|
-
fmt = Rack::Utils.parse_nested_query(env[Rack::QUERY_STRING])[FORMAT]
|
144
|
-
# avoid symbol memory leak on an unknown format
|
145
|
-
return fmt.to_sym if content_type_for(fmt)
|
131
|
+
request_path = rack_request.path.try(:scrub)
|
132
|
+
dot_pos = request_path.rindex('.')
|
133
|
+
return unless dot_pos
|
146
134
|
|
147
|
-
|
135
|
+
extension = request_path[dot_pos + 1..]
|
136
|
+
extension if content_type_for(extension)
|
148
137
|
end
|
149
138
|
|
150
139
|
def format_from_header
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
nil
|
155
|
-
end
|
156
|
-
|
157
|
-
def mime_array
|
158
|
-
accept = env[Grape::Http::Headers::HTTP_ACCEPT]
|
159
|
-
return [] unless accept
|
160
|
-
|
161
|
-
accept_into_mime_and_quality = %r{
|
162
|
-
(
|
163
|
-
\w+/[\w+.-]+) # eg application/vnd.example.myformat+xml
|
164
|
-
(?:
|
165
|
-
(?:;[^,]*?)? # optionally multiple formats in a row
|
166
|
-
;\s*q=([\w.]+) # optional "quality" preference (eg q=0.5)
|
167
|
-
)?
|
168
|
-
}x
|
169
|
-
|
170
|
-
vendor_prefix_pattern = /vnd\.[^+]+\+/
|
171
|
-
|
172
|
-
accept.scan(accept_into_mime_and_quality)
|
173
|
-
.sort_by { |_, quality_preference| -(quality_preference ? quality_preference.to_f : 1.0) }
|
174
|
-
.flat_map { |mime, _| [mime, mime.sub(vendor_prefix_pattern, '')] }
|
175
|
-
end
|
140
|
+
accept_header = env['HTTP_ACCEPT'].try(:scrub)
|
141
|
+
return if accept_header.blank?
|
176
142
|
|
177
|
-
|
178
|
-
|
143
|
+
media_type = Rack::Utils.best_q_match(accept_header, mime_types.keys)
|
144
|
+
mime_types[media_type] if media_type
|
179
145
|
end
|
180
146
|
end
|
181
147
|
end
|
@@ -5,10 +5,11 @@ module Grape
|
|
5
5
|
# Class to handle the stack of middlewares based on ActionDispatch::MiddlewareStack
|
6
6
|
# It allows to insert and insert after
|
7
7
|
class Stack
|
8
|
+
extend Forwardable
|
8
9
|
class Middleware
|
9
10
|
attr_reader :args, :block, :klass
|
10
11
|
|
11
|
-
def initialize(klass,
|
12
|
+
def initialize(klass, args, block)
|
12
13
|
@klass = klass
|
13
14
|
@args = args
|
14
15
|
@block = block
|
@@ -31,8 +32,12 @@ module Grape
|
|
31
32
|
klass.to_s
|
32
33
|
end
|
33
34
|
|
34
|
-
def
|
35
|
-
|
35
|
+
def build(builder)
|
36
|
+
# we need to force the ruby2_keywords_hash for middlewares that initialize contains keywords
|
37
|
+
# like ActionDispatch::RequestId since middleware arguments are serialized
|
38
|
+
# https://rubyapi.org/3.4/o/hash#method-c-ruby2_keywords_hash
|
39
|
+
args[-1] = Hash.ruby2_keywords_hash(args[-1]) if args.last.is_a?(Hash) && Hash.respond_to?(:ruby2_keywords_hash)
|
40
|
+
builder.use(klass, *args, &block)
|
36
41
|
end
|
37
42
|
end
|
38
43
|
|
@@ -40,33 +45,17 @@ module Grape
|
|
40
45
|
|
41
46
|
attr_accessor :middlewares, :others
|
42
47
|
|
48
|
+
def_delegators :middlewares, :each, :size, :last, :[]
|
49
|
+
|
43
50
|
def initialize
|
44
51
|
@middlewares = []
|
45
52
|
@others = []
|
46
53
|
end
|
47
54
|
|
48
|
-
def
|
49
|
-
@middlewares.each(&block)
|
50
|
-
end
|
51
|
-
|
52
|
-
def size
|
53
|
-
middlewares.size
|
54
|
-
end
|
55
|
-
|
56
|
-
def last
|
57
|
-
middlewares.last
|
58
|
-
end
|
59
|
-
|
60
|
-
def [](index)
|
61
|
-
middlewares[index]
|
62
|
-
end
|
63
|
-
|
64
|
-
def insert(index, *args, &block)
|
55
|
+
def insert(index, klass, *args, &block)
|
65
56
|
index = assert_index(index, :before)
|
66
|
-
|
67
|
-
middlewares.insert(index, middleware)
|
57
|
+
middlewares.insert(index, self.class::Middleware.new(klass, args, block))
|
68
58
|
end
|
69
|
-
ruby2_keywords :insert if respond_to?(:ruby2_keywords, true)
|
70
59
|
|
71
60
|
alias insert_before insert
|
72
61
|
|
@@ -74,38 +63,39 @@ module Grape
|
|
74
63
|
index = assert_index(index, :after)
|
75
64
|
insert(index + 1, *args, &block)
|
76
65
|
end
|
77
|
-
ruby2_keywords :insert_after if respond_to?(:ruby2_keywords, true)
|
78
66
|
|
79
|
-
def use(
|
80
|
-
middleware = self.class::Middleware.new(
|
67
|
+
def use(klass, *args, &block)
|
68
|
+
middleware = self.class::Middleware.new(klass, args, block)
|
81
69
|
middlewares.push(middleware)
|
82
70
|
end
|
83
71
|
|
84
72
|
def merge_with(middleware_specs)
|
85
|
-
middleware_specs.each do |operation, *args|
|
73
|
+
middleware_specs.each do |operation, klass, *args|
|
86
74
|
if args.last.is_a?(Proc)
|
87
75
|
last_proc = args.pop
|
88
|
-
public_send(operation, *args, &last_proc)
|
76
|
+
public_send(operation, klass, *args, &last_proc)
|
89
77
|
else
|
90
|
-
public_send(operation, *args)
|
78
|
+
public_send(operation, klass, *args)
|
91
79
|
end
|
92
80
|
end
|
93
81
|
end
|
94
82
|
|
95
83
|
# @return [Rack::Builder] the builder object with our middlewares applied
|
96
|
-
def build
|
97
|
-
|
98
|
-
|
99
|
-
m
|
84
|
+
def build
|
85
|
+
Rack::Builder.new.tap do |builder|
|
86
|
+
others.shift(others.size).each { |m| merge_with(m) }
|
87
|
+
middlewares.each do |m|
|
88
|
+
m.build(builder)
|
89
|
+
end
|
100
90
|
end
|
101
|
-
builder
|
102
91
|
end
|
103
92
|
|
104
93
|
# @description Add middlewares with :use operation to the stack. Store others with :insert_* operation for later
|
105
94
|
# @param [Array] other_specs An array of middleware specifications (e.g. [[:use, klass], [:insert_before, *args]])
|
106
95
|
def concat(other_specs)
|
107
|
-
|
108
|
-
|
96
|
+
use, not_use = other_specs.partition { |o| o.first == :use }
|
97
|
+
others << not_use
|
98
|
+
merge_with(use)
|
109
99
|
end
|
110
100
|
|
111
101
|
protected
|
@@ -17,10 +17,8 @@ module Grape
|
|
17
17
|
# X-Cascade header to alert Grape::Router to attempt the next matched
|
18
18
|
# route.
|
19
19
|
class AcceptVersionHeader < Base
|
20
|
-
include VersionerHelpers
|
21
|
-
|
22
20
|
def before
|
23
|
-
potential_version = env[
|
21
|
+
potential_version = env['HTTP_ACCEPT_VERSION'].try(:scrub)
|
24
22
|
not_acceptable!('Accept-Version header must be set.') if strict? && potential_version.blank?
|
25
23
|
|
26
24
|
return if potential_version.blank?
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
module Versioner
|
6
|
+
class Base < Grape::Middleware::Base
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
pattern: /.*/i.freeze,
|
9
|
+
version_options: {
|
10
|
+
strict: false,
|
11
|
+
cascade: true,
|
12
|
+
parameter: 'apiver'
|
13
|
+
}.freeze
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def self.inherited(klass)
|
17
|
+
super
|
18
|
+
Versioner.register(klass)
|
19
|
+
end
|
20
|
+
|
21
|
+
def versions
|
22
|
+
options[:versions]
|
23
|
+
end
|
24
|
+
|
25
|
+
def prefix
|
26
|
+
options[:prefix]
|
27
|
+
end
|
28
|
+
|
29
|
+
def mount_path
|
30
|
+
options[:mount_path]
|
31
|
+
end
|
32
|
+
|
33
|
+
def pattern
|
34
|
+
options[:pattern]
|
35
|
+
end
|
36
|
+
|
37
|
+
def version_options
|
38
|
+
options[:version_options]
|
39
|
+
end
|
40
|
+
|
41
|
+
def strict?
|
42
|
+
version_options[:strict]
|
43
|
+
end
|
44
|
+
|
45
|
+
# By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking
|
46
|
+
# of routes (see Grape::Router) for more information). To prevent
|
47
|
+
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
48
|
+
def cascade?
|
49
|
+
version_options[:cascade]
|
50
|
+
end
|
51
|
+
|
52
|
+
def parameter_key
|
53
|
+
version_options[:parameter]
|
54
|
+
end
|
55
|
+
|
56
|
+
def vendor
|
57
|
+
version_options[:vendor]
|
58
|
+
end
|
59
|
+
|
60
|
+
def error_headers
|
61
|
+
cascade? ? { 'X-Cascade' => 'pass' } : {}
|
62
|
+
end
|
63
|
+
|
64
|
+
def potential_version_match?(potential_version)
|
65
|
+
versions.blank? || versions.any? { |v| v.to_s == potential_version }
|
66
|
+
end
|
67
|
+
|
68
|
+
def version_not_found!
|
69
|
+
throw :error, status: 404, message: '404 API Version Not Found', headers: { 'X-Cascade' => 'pass' }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -22,8 +22,6 @@ module Grape
|
|
22
22
|
# X-Cascade header to alert Grape::Router to attempt the next matched
|
23
23
|
# route.
|
24
24
|
class Header < Base
|
25
|
-
include VersionerHelpers
|
26
|
-
|
27
25
|
def before
|
28
26
|
match_best_quality_media_type! do |media_type|
|
29
27
|
env.update(
|
@@ -46,16 +44,12 @@ module Grape
|
|
46
44
|
if media_type
|
47
45
|
yield media_type
|
48
46
|
else
|
49
|
-
fail!
|
47
|
+
fail!
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
53
|
-
def allowed_methods
|
54
|
-
env[Grape::Env::GRAPE_ALLOWED_METHODS]
|
55
|
-
end
|
56
|
-
|
57
51
|
def accept_header
|
58
|
-
env[
|
52
|
+
env['HTTP_ACCEPT']
|
59
53
|
end
|
60
54
|
|
61
55
|
def strict_header_checks!
|
@@ -93,8 +87,8 @@ module Grape
|
|
93
87
|
raise Grape::Exceptions::InvalidVersionHeader.new(message, error_headers)
|
94
88
|
end
|
95
89
|
|
96
|
-
def fail!
|
97
|
-
return
|
90
|
+
def fail!
|
91
|
+
return if env[Grape::Env::GRAPE_ALLOWED_METHODS].present?
|
98
92
|
|
99
93
|
media_types = q_values_mime_types.map { |mime_type| Grape::Util::MediaType.parse(mime_type) }
|
100
94
|
vendor_not_found!(media_types) || version_not_found!(media_types)
|
@@ -19,15 +19,12 @@ module Grape
|
|
19
19
|
#
|
20
20
|
# env['api.version'] => 'v1'
|
21
21
|
class Param < Base
|
22
|
-
include VersionerHelpers
|
23
|
-
|
24
22
|
def before
|
25
|
-
potential_version =
|
23
|
+
potential_version = query_params[parameter_key]
|
26
24
|
return if potential_version.blank?
|
27
25
|
|
28
26
|
version_not_found! unless potential_version_match?(potential_version)
|
29
|
-
env[Grape::Env::API_VERSION] =
|
30
|
-
env[Rack::RACK_REQUEST_QUERY_HASH].delete(parameter_key) if env.key? Rack::RACK_REQUEST_QUERY_HASH
|
27
|
+
env[Grape::Env::API_VERSION] = env[Rack::RACK_REQUEST_QUERY_HASH].delete(parameter_key)
|
31
28
|
end
|
32
29
|
end
|
33
30
|
end
|
@@ -11,14 +11,16 @@
|
|
11
11
|
module Grape
|
12
12
|
module Middleware
|
13
13
|
module Versioner
|
14
|
+
extend Grape::Util::Registry
|
15
|
+
|
14
16
|
module_function
|
15
17
|
|
16
18
|
# @param strategy [Symbol] :path, :header, :accept_version_header or :param
|
17
19
|
# @return a middleware class based on strategy
|
18
20
|
def using(strategy)
|
19
|
-
Grape::
|
20
|
-
|
21
|
-
|
21
|
+
raise Grape::Exceptions::InvalidVersionerOption, strategy unless registry.key?(strategy)
|
22
|
+
|
23
|
+
registry[strategy]
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
data/lib/grape/namespace.rb
CHANGED
@@ -12,7 +12,7 @@ module Grape
|
|
12
12
|
# @option options :requirements [Hash] param-regex pairs, all of which must
|
13
13
|
# be met by a request's params for all endpoints in this namespace, or
|
14
14
|
# validation will fail and return a 422.
|
15
|
-
def initialize(space,
|
15
|
+
def initialize(space, options)
|
16
16
|
@space = space.to_s
|
17
17
|
@options = options
|
18
18
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module ParamsBuilder
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
def call(_params)
|
8
|
+
raise NotImplementedError
|
9
|
+
end
|
10
|
+
|
11
|
+
def inherited(klass)
|
12
|
+
super
|
13
|
+
ParamsBuilder.register(klass)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module ParamsBuilder
|
5
|
+
extend Grape::Util::Registry
|
6
|
+
|
7
|
+
SHORT_NAME_LOOKUP = {
|
8
|
+
'Grape::Extensions::Hash::ParamBuilder' => :hash,
|
9
|
+
'Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder' => :hash_with_indifferent_access,
|
10
|
+
'Grape::Extensions::Hashie::Mash::ParamBuilder' => :hashie_mash
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
module_function
|
14
|
+
|
15
|
+
def params_builder_for(short_name)
|
16
|
+
verified_short_name = verify_short_name!(short_name)
|
17
|
+
|
18
|
+
raise Grape::Exceptions::UnknownParamsBuilder, verified_short_name unless registry.key?(verified_short_name)
|
19
|
+
|
20
|
+
registry[verified_short_name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def verify_short_name!(short_name)
|
24
|
+
return short_name if short_name.is_a?(Symbol)
|
25
|
+
|
26
|
+
class_name = short_name.name
|
27
|
+
SHORT_NAME_LOOKUP[class_name].tap do |real_short_name|
|
28
|
+
Grape.deprecator.warn "#{class_name} has been deprecated. Use short name :#{real_short_name} instead."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/grape/parser/json.rb
CHANGED
@@ -2,14 +2,12 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Parser
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
rescue
|
10
|
-
|
11
|
-
raise Grape::Exceptions::InvalidMessageBody.new('application/json')
|
12
|
-
end
|
5
|
+
class Json < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
::Grape::Json.load(object)
|
8
|
+
rescue ::Grape::Json::ParseError
|
9
|
+
# handle JSON parsing errors via the rescue handlers or provide error message
|
10
|
+
raise Grape::Exceptions::InvalidMessageBody.new('application/json')
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
data/lib/grape/parser/xml.rb
CHANGED
@@ -2,14 +2,12 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Parser
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
rescue
|
10
|
-
|
11
|
-
raise Grape::Exceptions::InvalidMessageBody.new('application/xml')
|
12
|
-
end
|
5
|
+
class Xml < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
::Grape::Xml.parse(object)
|
8
|
+
rescue ::Grape::Xml::ParseError
|
9
|
+
# handle XML parsing errors via the rescue handlers or provide error message
|
10
|
+
raise Grape::Exceptions::InvalidMessageBody.new('application/xml')
|
13
11
|
end
|
14
12
|
end
|
15
13
|
end
|
data/lib/grape/parser.rb
CHANGED
@@ -2,16 +2,14 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Parser
|
5
|
-
|
5
|
+
extend Grape::Util::Registry
|
6
6
|
|
7
|
-
|
8
|
-
json: Grape::Parser::Json,
|
9
|
-
jsonapi: Grape::Parser::Json,
|
10
|
-
xml: Grape::Parser::Xml
|
11
|
-
}.freeze
|
7
|
+
module_function
|
12
8
|
|
13
9
|
def parser_for(format, parsers = nil)
|
14
|
-
|
10
|
+
return parsers[format] if parsers&.key?(format)
|
11
|
+
|
12
|
+
registry[format]
|
15
13
|
end
|
16
14
|
end
|
17
15
|
end
|