grape 1.5.0 → 1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +51 -2
- data/README.md +43 -10
- data/UPGRADING.md +91 -0
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +13 -17
- data/lib/grape/api.rb +7 -14
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/helpers.rb +6 -4
- data/lib/grape/dsl/inside_route.rb +18 -9
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +11 -7
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +7 -6
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/endpoint.rb +21 -36
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/validation.rb +2 -3
- data/lib/grape/exceptions/validation_errors.rb +1 -1
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/base.rb +4 -2
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/formatter.rb +4 -4
- data/lib/grape/middleware/stack.rb +10 -16
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/parser/json.rb +1 -1
- data/lib/grape/parser/xml.rb +1 -1
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +3 -0
- data/lib/grape/router/attribute_translator.rb +1 -1
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +6 -0
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -2
- data/lib/grape/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +92 -58
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +3 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +3 -3
- data/lib/grape/validations/types.rb +1 -4
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/all_or_none.rb +1 -0
- data/lib/grape/validations/validators/as.rb +4 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -0
- data/lib/grape/validations/validators/base.rb +12 -7
- data/lib/grape/validations/validators/coerce.rb +8 -9
- data/lib/grape/validations/validators/default.rb +1 -0
- data/lib/grape/validations/validators/exactly_one_of.rb +1 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +5 -2
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -0
- data/lib/grape/validations/validators/presence.rb +1 -0
- data/lib/grape/validations/validators/regexp.rb +1 -0
- data/lib/grape/validations/validators/same_as.rb +1 -0
- data/lib/grape/validations/validators/values.rb +3 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +3 -1
- data/spec/grape/api/custom_validations_spec.rb +1 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api_remount_spec.rb +9 -4
- data/spec/grape/api_spec.rb +203 -37
- data/spec/grape/dsl/callbacks_spec.rb +1 -1
- data/spec/grape/dsl/middleware_spec.rb +1 -1
- data/spec/grape/dsl/parameters_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/endpoint/declared_spec.rb +259 -1
- data/spec/grape/endpoint_spec.rb +18 -5
- data/spec/grape/entity_spec.rb +10 -10
- data/spec/grape/middleware/auth/dsl_spec.rb +1 -1
- data/spec/grape/middleware/error_spec.rb +1 -2
- data/spec/grape/middleware/formatter_spec.rb +2 -2
- data/spec/grape/middleware/stack_spec.rb +4 -3
- data/spec/grape/request_spec.rb +1 -1
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
- data/spec/grape/validations/params_scope_spec.rb +37 -3
- data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
- data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
- data/spec/grape/validations/validators/coerce_spec.rb +129 -22
- data/spec/grape/validations/validators/except_values_spec.rb +2 -2
- data/spec/grape/validations/validators/values_spec.rb +15 -11
- data/spec/grape/validations_spec.rb +280 -0
- data/spec/shared/versioning_examples.rb +22 -22
- data/spec/spec_helper.rb +1 -1
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- data/spec/support/versioned_helpers.rb +1 -1
- metadata +8 -6
@@ -48,6 +48,8 @@ module Grape
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def declared_hash(passed_params, options, declared_params, params_nested_path)
|
51
|
+
renamed_params = route_setting(:renamed_params) || {}
|
52
|
+
|
51
53
|
declared_params.each_with_object(passed_params.class.new) do |declared_param, memo|
|
52
54
|
if declared_param.is_a?(Hash)
|
53
55
|
declared_param.each_pair do |declared_parent_param, declared_children_params|
|
@@ -55,8 +57,11 @@ module Grape
|
|
55
57
|
params_nested_path_dup << declared_parent_param.to_s
|
56
58
|
next unless options[:include_missing] || passed_params.key?(declared_parent_param)
|
57
59
|
|
60
|
+
rename_path = params_nested_path + [declared_parent_param.to_s]
|
61
|
+
renamed_param_name = renamed_params[rename_path]
|
62
|
+
|
63
|
+
memo_key = optioned_param_key(renamed_param_name || declared_parent_param, options)
|
58
64
|
passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
|
59
|
-
memo_key = optioned_param_key(declared_parent_param, options)
|
60
65
|
|
61
66
|
memo[memo_key] = handle_passed_param(params_nested_path_dup, passed_children_params.any?) do
|
62
67
|
declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
|
@@ -65,13 +70,13 @@ module Grape
|
|
65
70
|
else
|
66
71
|
# If it is not a Hash then it does not have children.
|
67
72
|
# Find its value or set it to nil.
|
68
|
-
|
69
|
-
param_renaming = has_renaming[declared_param] if has_renaming
|
73
|
+
next unless options[:include_missing] || passed_params.key?(declared_param)
|
70
74
|
|
71
|
-
|
75
|
+
rename_path = params_nested_path + [declared_param.to_s]
|
76
|
+
renamed_param_name = renamed_params[rename_path]
|
72
77
|
|
73
|
-
memo_key = optioned_param_key(
|
74
|
-
passed_param = passed_params[
|
78
|
+
memo_key = optioned_param_key(renamed_param_name || declared_param, options)
|
79
|
+
passed_param = passed_params[declared_param]
|
75
80
|
|
76
81
|
params_nested_path_dup = params_nested_path.dup
|
77
82
|
params_nested_path_dup << declared_param.to_s
|
@@ -86,7 +91,7 @@ module Grape
|
|
86
91
|
return yield if has_passed_children
|
87
92
|
|
88
93
|
key = params_nested_path[0]
|
89
|
-
key +=
|
94
|
+
key += "[#{params_nested_path[1..-1].join('][')}]" if params_nested_path.size > 1
|
90
95
|
|
91
96
|
route_options_params = options[:route_options][:params] || {}
|
92
97
|
type = route_options_params.dig(key, :type)
|
@@ -94,7 +99,7 @@ module Grape
|
|
94
99
|
|
95
100
|
if type == 'Hash' && !has_children
|
96
101
|
{}
|
97
|
-
elsif type == 'Array' || type&.start_with?('[')
|
102
|
+
elsif type == 'Array' || (type&.start_with?('[') && !type&.include?(','))
|
98
103
|
[]
|
99
104
|
elsif type == 'Set' || type&.start_with?('#<Set')
|
100
105
|
Set.new
|
@@ -117,6 +122,7 @@ module Grape
|
|
117
122
|
end
|
118
123
|
|
119
124
|
raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
|
125
|
+
|
120
126
|
declared_params
|
121
127
|
end
|
122
128
|
end
|
@@ -187,11 +193,13 @@ module Grape
|
|
187
193
|
case status
|
188
194
|
when Symbol
|
189
195
|
raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(status)
|
196
|
+
|
190
197
|
@status = Rack::Utils.status_code(status)
|
191
198
|
when Integer
|
192
199
|
@status = status
|
193
200
|
when nil
|
194
201
|
return @status if instance_variable_defined?(:@status) && @status
|
202
|
+
|
195
203
|
case request.request_method.to_s.upcase
|
196
204
|
when Grape::Http::Headers::POST
|
197
205
|
201
|
@@ -369,6 +377,7 @@ module Grape
|
|
369
377
|
representation = (body || {}).merge(key => representation)
|
370
378
|
elsif entity_class.present? && body
|
371
379
|
raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
|
380
|
+
|
372
381
|
representation = body.merge(representation)
|
373
382
|
end
|
374
383
|
|
@@ -399,7 +408,7 @@ module Grape
|
|
399
408
|
entity_class = options.delete(:with)
|
400
409
|
|
401
410
|
if entity_class.nil?
|
402
|
-
# entity class not
|
411
|
+
# entity class not explicitly defined, auto-detect from relation#klass or first object in the collection
|
403
412
|
object_class = if object.respond_to?(:klass)
|
404
413
|
object.klass
|
405
414
|
else
|
data/lib/grape/dsl/middleware.rb
CHANGED
@@ -18,28 +18,28 @@ module Grape
|
|
18
18
|
# to inject.
|
19
19
|
def use(middleware_class, *args, &block)
|
20
20
|
arr = [:use, middleware_class, *args]
|
21
|
-
arr << block if
|
21
|
+
arr << block if block
|
22
22
|
|
23
23
|
namespace_stackable(:middleware, arr)
|
24
24
|
end
|
25
25
|
|
26
26
|
def insert(*args, &block)
|
27
27
|
arr = [:insert, *args]
|
28
|
-
arr << block if
|
28
|
+
arr << block if block
|
29
29
|
|
30
30
|
namespace_stackable(:middleware, arr)
|
31
31
|
end
|
32
32
|
|
33
33
|
def insert_before(*args, &block)
|
34
34
|
arr = [:insert_before, *args]
|
35
|
-
arr << block if
|
35
|
+
arr << block if block
|
36
36
|
|
37
37
|
namespace_stackable(:middleware, arr)
|
38
38
|
end
|
39
39
|
|
40
40
|
def insert_after(*args, &block)
|
41
41
|
arr = [:insert_after, *args]
|
42
|
-
arr << block if
|
42
|
+
arr << block if block
|
43
43
|
|
44
44
|
namespace_stackable(:middleware, arr)
|
45
45
|
end
|
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -72,7 +72,7 @@ module Grape
|
|
72
72
|
|
73
73
|
# Require one or more parameters for the current endpoint.
|
74
74
|
#
|
75
|
-
# @param attrs list of
|
75
|
+
# @param attrs list of parameters names, or, if :using is
|
76
76
|
# passed as an option, which keys to include (:all or :none) from
|
77
77
|
# the :using hash. The last key can be a hash, which specifies
|
78
78
|
# options for the parameters
|
@@ -133,7 +133,7 @@ module Grape
|
|
133
133
|
require_required_and_optional_fields(attrs.first, opts)
|
134
134
|
else
|
135
135
|
validate_attributes(attrs, opts, &block)
|
136
|
-
|
136
|
+
block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
@@ -149,7 +149,7 @@ module Grape
|
|
149
149
|
opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
|
150
150
|
|
151
151
|
# check type for optional parameter group
|
152
|
-
if attrs &&
|
152
|
+
if attrs && block
|
153
153
|
raise Grape::Exceptions::MissingGroupTypeError.new if type.nil?
|
154
154
|
raise Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
|
155
155
|
end
|
@@ -159,7 +159,7 @@ module Grape
|
|
159
159
|
else
|
160
160
|
validate_attributes(attrs, opts, &block)
|
161
161
|
|
162
|
-
|
162
|
+
block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
@@ -227,13 +227,17 @@ module Grape
|
|
227
227
|
|
228
228
|
alias group requires
|
229
229
|
|
230
|
-
|
230
|
+
class EmptyOptionalValue; end
|
231
|
+
|
232
|
+
def map_params(params, element, is_array = false)
|
231
233
|
if params.is_a?(Array)
|
232
234
|
params.map do |el|
|
233
|
-
map_params(el, element)
|
235
|
+
map_params(el, element, true)
|
234
236
|
end
|
235
237
|
elsif params.is_a?(Hash)
|
236
|
-
params[element] || {}
|
238
|
+
params[element] || (@optional && is_array ? EmptyOptionalValue : {})
|
239
|
+
elsif params == EmptyOptionalValue
|
240
|
+
EmptyOptionalValue
|
237
241
|
else
|
238
242
|
{}
|
239
243
|
end
|
@@ -26,6 +26,7 @@ module Grape
|
|
26
26
|
# define a single mime type
|
27
27
|
mime_type = content_types[new_format.to_sym]
|
28
28
|
raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
|
29
|
+
|
29
30
|
namespace_stackable(:content_types, new_format.to_sym => mime_type)
|
30
31
|
else
|
31
32
|
namespace_inheritable(:format)
|
@@ -102,14 +103,13 @@ module Grape
|
|
102
103
|
def rescue_from(*args, &block)
|
103
104
|
if args.last.is_a?(Proc)
|
104
105
|
handler = args.pop
|
105
|
-
elsif
|
106
|
+
elsif block
|
106
107
|
handler = block
|
107
108
|
end
|
108
109
|
|
109
110
|
options = args.extract_options!
|
110
|
-
if
|
111
|
-
|
112
|
-
end
|
111
|
+
raise ArgumentError, 'both :with option and block cannot be passed' if block && options.key?(:with)
|
112
|
+
|
113
113
|
handler ||= extract_with(options)
|
114
114
|
|
115
115
|
if args.include?(:all)
|
@@ -127,7 +127,7 @@ module Grape
|
|
127
127
|
:base_only_rescue_handlers
|
128
128
|
end
|
129
129
|
|
130
|
-
namespace_reverse_stackable handler_type,
|
130
|
+
namespace_reverse_stackable handler_type, args.map { |arg| [arg, handler] }.to_h
|
131
131
|
end
|
132
132
|
|
133
133
|
namespace_stackable(:rescue_options, options)
|
@@ -154,7 +154,8 @@ module Grape
|
|
154
154
|
# @param model_class [Class] The model class that will be represented.
|
155
155
|
# @option options [Class] :with The entity class that will represent the model.
|
156
156
|
def represent(model_class, options)
|
157
|
-
raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with]
|
157
|
+
raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with].is_a?(Class)
|
158
|
+
|
158
159
|
namespace_stackable(:representations, model_class => options[:with])
|
159
160
|
end
|
160
161
|
|
@@ -162,9 +163,11 @@ module Grape
|
|
162
163
|
|
163
164
|
def extract_with(options)
|
164
165
|
return unless options.key?(:with)
|
166
|
+
|
165
167
|
with_option = options.delete(:with)
|
166
168
|
return with_option if with_option.instance_of?(Proc)
|
167
169
|
return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
|
170
|
+
|
168
171
|
raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
|
169
172
|
end
|
170
173
|
end
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -38,7 +38,7 @@ module Grape
|
|
38
38
|
|
39
39
|
@versions = versions | requested_versions
|
40
40
|
|
41
|
-
if
|
41
|
+
if block
|
42
42
|
within_namespace do
|
43
43
|
namespace_inheritable(:version, requested_versions)
|
44
44
|
namespace_inheritable(:version_options, options)
|
@@ -79,11 +79,12 @@ module Grape
|
|
79
79
|
namespace_inheritable(:do_not_route_options, true)
|
80
80
|
end
|
81
81
|
|
82
|
-
def mount(mounts,
|
82
|
+
def mount(mounts, *opts)
|
83
83
|
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
84
84
|
mounts.each_pair do |app, path|
|
85
85
|
if app.respond_to?(:mount_instance)
|
86
|
-
|
86
|
+
opts_with = opts.any? ? opts.shift[:with] : {}
|
87
|
+
mount({ app.mount_instance(configuration: opts_with) => path })
|
87
88
|
next
|
88
89
|
end
|
89
90
|
in_setting = inheritable_setting
|
@@ -151,7 +152,7 @@ module Grape
|
|
151
152
|
end
|
152
153
|
|
153
154
|
# Declare a "namespace", which prefixes all subordinate routes with its
|
154
|
-
# name. Any endpoints within a namespace,
|
155
|
+
# name. Any endpoints within a namespace, group, resource or segment,
|
155
156
|
# etc., will share their parent context as well as any configuration
|
156
157
|
# done in the namespace context.
|
157
158
|
#
|
@@ -165,7 +166,7 @@ module Grape
|
|
165
166
|
def namespace(space = nil, options = {}, &block)
|
166
167
|
@namespace_description = nil unless instance_variable_defined?(:@namespace_description) && @namespace_description
|
167
168
|
|
168
|
-
if space ||
|
169
|
+
if space || block
|
169
170
|
within_namespace do
|
170
171
|
previous_namespace_description = @namespace_description
|
171
172
|
@namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
|
@@ -199,7 +200,7 @@ module Grape
|
|
199
200
|
@endpoints = []
|
200
201
|
end
|
201
202
|
|
202
|
-
#
|
203
|
+
# This method allows you to quickly define a parameter route segment
|
203
204
|
# in your API.
|
204
205
|
#
|
205
206
|
# @param param [Symbol] The name of the parameter you wish to declare.
|
data/lib/grape/dsl/settings.rb
CHANGED
@@ -103,12 +103,14 @@ module Grape
|
|
103
103
|
def namespace_stackable_with_hash(key)
|
104
104
|
settings = get_or_set :namespace_stackable, key, nil
|
105
105
|
return if settings.blank?
|
106
|
+
|
106
107
|
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
|
107
108
|
end
|
108
109
|
|
109
110
|
def namespace_reverse_stackable_with_hash(key)
|
110
111
|
settings = get_or_set :namespace_reverse_stackable, key, nil
|
111
112
|
return if settings.blank?
|
113
|
+
|
112
114
|
result = {}
|
113
115
|
settings.each do |setting|
|
114
116
|
setting.each do |field, value|
|
@@ -154,10 +156,10 @@ module Grape
|
|
154
156
|
|
155
157
|
# Execute the block within a context where our inheritable settings are forked
|
156
158
|
# to a new copy (see #namespace_start).
|
157
|
-
def within_namespace(&
|
159
|
+
def within_namespace(&block)
|
158
160
|
namespace_start
|
159
161
|
|
160
|
-
result = yield if
|
162
|
+
result = yield if block
|
161
163
|
|
162
164
|
namespace_end
|
163
165
|
reset_validations!
|
@@ -175,9 +177,7 @@ module Grape
|
|
175
177
|
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
|
176
178
|
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
|
177
179
|
# in the chain for every endpoint.
|
178
|
-
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
179
|
-
setting.inherit_from superclass.inheritable_setting
|
180
|
-
end
|
180
|
+
setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
data/lib/grape/endpoint.rb
CHANGED
@@ -20,7 +20,8 @@ module Grape
|
|
20
20
|
def before_each(new_setup = false, &block)
|
21
21
|
@before_each ||= []
|
22
22
|
if new_setup == false
|
23
|
-
return @before_each unless
|
23
|
+
return @before_each unless block
|
24
|
+
|
24
25
|
@before_each << block
|
25
26
|
else
|
26
27
|
@before_each = [new_setup]
|
@@ -46,9 +47,7 @@ module Grape
|
|
46
47
|
# @return [Proc]
|
47
48
|
# @raise [NameError] an instance method with the same name already exists
|
48
49
|
def generate_api_method(method_name, &block)
|
49
|
-
if method_defined?(method_name)
|
50
|
-
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
|
51
|
-
end
|
50
|
+
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name)
|
52
51
|
|
53
52
|
define_method(method_name, &block)
|
54
53
|
method = instance_method(method_name)
|
@@ -106,7 +105,7 @@ module Grape
|
|
106
105
|
@body = nil
|
107
106
|
@proc = nil
|
108
107
|
|
109
|
-
return unless
|
108
|
+
return unless block
|
110
109
|
|
111
110
|
@source = block
|
112
111
|
@block = self.class.generate_api_method(method_name, &block)
|
@@ -118,11 +117,9 @@ module Grape
|
|
118
117
|
inheritable_setting.route[:saved_validations] += namespace_stackable[:validations]
|
119
118
|
parent_declared_params = namespace_stackable[:declared_params]
|
120
119
|
|
121
|
-
if parent_declared_params
|
122
|
-
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten)
|
123
|
-
end
|
120
|
+
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
|
124
121
|
|
125
|
-
endpoints
|
122
|
+
endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
|
126
123
|
end
|
127
124
|
|
128
125
|
def require_option(options, key)
|
@@ -142,7 +139,7 @@ module Grape
|
|
142
139
|
end
|
143
140
|
|
144
141
|
def reset_routes!
|
145
|
-
endpoints
|
142
|
+
endpoints&.each(&:reset_routes!)
|
146
143
|
@namespace = nil
|
147
144
|
@routes = nil
|
148
145
|
end
|
@@ -154,13 +151,9 @@ module Grape
|
|
154
151
|
reset_routes!
|
155
152
|
routes.each do |route|
|
156
153
|
methods = [route.request_method]
|
157
|
-
if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
|
158
|
-
methods << Grape::Http::Headers::HEAD
|
159
|
-
end
|
154
|
+
methods << Grape::Http::Headers::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
|
160
155
|
methods.each do |method|
|
161
|
-
unless route.request_method == method
|
162
|
-
route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h)
|
163
|
-
end
|
156
|
+
route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
|
164
157
|
router.append(route.apply(self))
|
165
158
|
end
|
166
159
|
end
|
@@ -200,6 +193,7 @@ module Grape
|
|
200
193
|
def prepare_version
|
201
194
|
version = namespace_inheritable(:version) || []
|
202
195
|
return if version.empty?
|
196
|
+
|
203
197
|
version.length == 1 ? version.first.to_s : version
|
204
198
|
end
|
205
199
|
|
@@ -234,7 +228,7 @@ module Grape
|
|
234
228
|
# Return the collection of endpoints within this endpoint.
|
235
229
|
# This is the case when an Grape::API mounts another Grape::API.
|
236
230
|
def endpoints
|
237
|
-
options[:app].endpoints if options[:app]
|
231
|
+
options[:app].endpoints if options[:app].respond_to?(:endpoints)
|
238
232
|
end
|
239
233
|
|
240
234
|
def equals?(e)
|
@@ -255,14 +249,14 @@ module Grape
|
|
255
249
|
run_filters befores, :before
|
256
250
|
|
257
251
|
if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
|
258
|
-
raise Grape::Exceptions::MethodNotAllowed
|
252
|
+
raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allowed_methods)) unless options?
|
253
|
+
|
259
254
|
header 'Allow', allowed_methods
|
260
255
|
response_object = ''
|
261
256
|
status 204
|
262
257
|
else
|
263
258
|
run_filters before_validations, :before_validation
|
264
259
|
run_validators validations, request
|
265
|
-
remove_renamed_params
|
266
260
|
run_filters after_validations, :after_validation
|
267
261
|
response_object = execute
|
268
262
|
end
|
@@ -328,14 +322,7 @@ module Grape
|
|
328
322
|
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
329
323
|
end
|
330
324
|
|
331
|
-
|
332
|
-
return unless route_setting(:renamed_params)
|
333
|
-
route_setting(:renamed_params).flat_map(&:keys).each do |renamed_param|
|
334
|
-
@params.delete(renamed_param)
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
private :build_stack, :build_helpers, :remove_renamed_params
|
325
|
+
private :build_stack, :build_helpers
|
339
326
|
|
340
327
|
def execute
|
341
328
|
@block ? @block.call(self) : nil
|
@@ -365,15 +352,13 @@ module Grape
|
|
365
352
|
|
366
353
|
ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
|
367
354
|
validators.each do |validator|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
break if validator.fail_fast?
|
376
|
-
end
|
355
|
+
validator.validate(request)
|
356
|
+
rescue Grape::Exceptions::Validation => e
|
357
|
+
validation_errors << e
|
358
|
+
break if validator.fail_fast?
|
359
|
+
rescue Grape::Exceptions::ValidationArrayErrors => e
|
360
|
+
validation_errors.concat e.errors
|
361
|
+
break if validator.fail_fast?
|
377
362
|
end
|
378
363
|
end
|
379
364
|
|
@@ -10,12 +10,8 @@ module Grape
|
|
10
10
|
result = wrap_message(present(message, env))
|
11
11
|
|
12
12
|
rescue_options = options[:rescue_options] || {}
|
13
|
-
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
14
|
-
|
15
|
-
end
|
16
|
-
if rescue_options[:original_exception] && original_exception
|
17
|
-
result = result.merge(original_exception: original_exception.inspect)
|
18
|
-
end
|
13
|
+
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
14
|
+
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
19
15
|
::Grape::Json.dump(result)
|
20
16
|
end
|
21
17
|
|
@@ -11,12 +11,8 @@ module Grape
|
|
11
11
|
|
12
12
|
result = message.is_a?(Hash) ? message : { message: message }
|
13
13
|
rescue_options = options[:rescue_options] || {}
|
14
|
-
if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
15
|
-
|
16
|
-
end
|
17
|
-
if rescue_options[:original_exception] && original_exception
|
18
|
-
result = result.merge(original_exception: original_exception.inspect)
|
19
|
-
end
|
14
|
+
result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
|
15
|
+
result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
|
20
16
|
result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
|
21
17
|
end
|
22
18
|
end
|
@@ -5,8 +5,7 @@ require 'grape/exceptions/base'
|
|
5
5
|
module Grape
|
6
6
|
module Exceptions
|
7
7
|
class Validation < Grape::Exceptions::Base
|
8
|
-
attr_accessor :params
|
9
|
-
attr_accessor :message_key
|
8
|
+
attr_accessor :params, :message_key
|
10
9
|
|
11
10
|
def initialize(params:, message: nil, **args)
|
12
11
|
@params = params
|
@@ -17,7 +16,7 @@ module Grape
|
|
17
16
|
super(**args)
|
18
17
|
end
|
19
18
|
|
20
|
-
#
|
19
|
+
# Remove all the unnecessary stuff from Grape::Exceptions::Base like status
|
21
20
|
# and headers when converting a validation error to json or string
|
22
21
|
def as_json(*_args)
|
23
22
|
to_s
|
data/lib/grape/formatter/json.rb
CHANGED
@@ -8,13 +8,14 @@ module Grape
|
|
8
8
|
return object if object.is_a?(String)
|
9
9
|
return ::Grape::Json.dump(serialize(object)) if serializable?(object)
|
10
10
|
return object.to_json if object.respond_to?(:to_json)
|
11
|
+
|
11
12
|
::Grape::Json.dump(object)
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
15
16
|
|
16
17
|
def serializable?(object)
|
17
|
-
object.respond_to?(:serializable_hash) || object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash } || object.is_a?(Hash)
|
18
|
+
object.respond_to?(:serializable_hash) || (object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }) || object.is_a?(Hash)
|
18
19
|
end
|
19
20
|
|
20
21
|
def serialize(object)
|
data/lib/grape/formatter/xml.rb
CHANGED
data/lib/grape/locale/en.yml
CHANGED
@@ -44,6 +44,7 @@ en:
|
|
44
44
|
"when specifying %{body_format} as content-type, you must pass valid
|
45
45
|
%{body_format} in the request's 'body'
|
46
46
|
"
|
47
|
+
empty_message_body: 'Empty message body supplied with %{body_format} content-type'
|
47
48
|
invalid_accept_header:
|
48
49
|
problem: 'Invalid accept header'
|
49
50
|
resolution: '%{message}'
|
@@ -51,4 +52,3 @@ en:
|
|
51
52
|
problem: 'Invalid version header'
|
52
53
|
resolution: '%{message}'
|
53
54
|
invalid_response: 'Invalid response'
|
54
|
-
|
@@ -10,9 +10,9 @@ module Grape
|
|
10
10
|
|
11
11
|
attr_accessor :options, :app, :env
|
12
12
|
|
13
|
-
def initialize(app,
|
13
|
+
def initialize(app, *options)
|
14
14
|
@app = app
|
15
|
-
@options = options
|
15
|
+
@options = options.shift
|
16
16
|
end
|
17
17
|
|
18
18
|
def call(env)
|
@@ -23,7 +23,7 @@ module Grape
|
|
23
23
|
self.env = env
|
24
24
|
|
25
25
|
if options.key?(:type)
|
26
|
-
auth_proc
|
26
|
+
auth_proc = options[:proc]
|
27
27
|
auth_proc_context = context
|
28
28
|
|
29
29
|
strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]
|
@@ -15,9 +15,9 @@ module Grape
|
|
15
15
|
|
16
16
|
# @param [Rack Application] app The standard argument for a Rack middleware.
|
17
17
|
# @param [Hash] options A hash of options, simply stored for use by subclasses.
|
18
|
-
def initialize(app,
|
18
|
+
def initialize(app, *options)
|
19
19
|
@app = app
|
20
|
-
@options = default_options.merge(options)
|
20
|
+
@options = options.any? ? default_options.merge(options.shift) : default_options
|
21
21
|
@app_response = nil
|
22
22
|
end
|
23
23
|
|
@@ -59,6 +59,7 @@ module Grape
|
|
59
59
|
|
60
60
|
def response
|
61
61
|
return @app_response if @app_response.is_a?(Rack::Response)
|
62
|
+
|
62
63
|
Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
|
63
64
|
end
|
64
65
|
|
@@ -84,6 +85,7 @@ module Grape
|
|
84
85
|
|
85
86
|
def merge_headers(response)
|
86
87
|
return unless headers.is_a?(Hash)
|
88
|
+
|
87
89
|
case response
|
88
90
|
when Rack::Response then response.headers.merge!(headers)
|
89
91
|
when Array then response[1].merge!(headers)
|