grape 2.1.3 → 2.3.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 +45 -0
- data/README.md +9 -7
- data/UPGRADING.md +27 -0
- data/grape.gemspec +7 -6
- data/lib/grape/api/instance.rb +22 -58
- data/lib/grape/api.rb +2 -11
- data/lib/grape/content_types.rb +13 -8
- data/lib/grape/dsl/desc.rb +27 -24
- data/lib/grape/dsl/helpers.rb +7 -3
- data/lib/grape/dsl/inside_route.rb +18 -24
- data/lib/grape/dsl/parameters.rb +2 -2
- data/lib/grape/dsl/request_response.rb +14 -18
- data/lib/grape/dsl/routing.rb +5 -12
- data/lib/grape/endpoint.rb +90 -82
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -15
- data/lib/grape/error_formatter/jsonapi.rb +7 -0
- 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 +5 -25
- data/lib/grape/exceptions/base.rb +18 -30
- data/lib/grape/exceptions/validation.rb +5 -4
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- 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 +7 -25
- data/lib/grape/http/headers.rb +1 -0
- data/lib/grape/locale/en.yml +1 -0
- data/lib/grape/middleware/base.rb +14 -13
- data/lib/grape/middleware/error.rb +13 -9
- data/lib/grape/middleware/formatter.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +7 -30
- data/lib/grape/middleware/versioner/base.rb +82 -0
- data/lib/grape/middleware/versioner/header.rb +89 -10
- data/lib/grape/middleware/versioner/param.rb +4 -22
- data/lib/grape/middleware/versioner/path.rb +10 -32
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/jsonapi.rb +7 -0
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +39 -56
- data/lib/grape/request.rb +2 -2
- 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 +13 -5
- data/lib/grape/router.rb +5 -5
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/validations/contract_scope.rb +2 -39
- data/lib/grape/validations/params_scope.rb +7 -11
- 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 +5 -9
- 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 +1 -1
- data/lib/grape/validations/validators/length_validator.rb +11 -4
- data/lib/grape/validations/validators/regexp_validator.rb +1 -1
- 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 +1 -1
- metadata +15 -12
- data/lib/grape/util/accept_header_handler.rb +0 -105
- data/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/validations/types/build_coercer.rb +0 -92
data/lib/grape/endpoint.rb
CHANGED
@@ -114,10 +114,10 @@ module Grape
|
|
114
114
|
# Update our settings from a given set of stackable parameters. Used when
|
115
115
|
# the endpoint's API is mounted under another one.
|
116
116
|
def inherit_settings(namespace_stackable)
|
117
|
-
|
117
|
+
parent_validations = namespace_stackable[:validations]
|
118
|
+
inheritable_setting.route[:saved_validations].concat(parent_validations) if parent_validations.any?
|
118
119
|
parent_declared_params = namespace_stackable[:declared_params]
|
119
|
-
|
120
|
-
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
|
120
|
+
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params.any?
|
121
121
|
|
122
122
|
endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
|
123
123
|
end
|
@@ -145,27 +145,28 @@ module Grape
|
|
145
145
|
end
|
146
146
|
|
147
147
|
def mount_in(router)
|
148
|
-
if endpoints
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
148
|
+
return endpoints.each { |e| e.mount_in(router) } if endpoints
|
149
|
+
|
150
|
+
reset_routes!
|
151
|
+
routes.each do |route|
|
152
|
+
router.append(route.apply(self))
|
153
|
+
next unless !namespace_inheritable(:do_not_route_head) && route.request_method == Rack::GET
|
154
|
+
|
155
|
+
route.dup.then do |head_route|
|
156
|
+
head_route.convert_to_head_request!
|
157
|
+
router.append(head_route.apply(self))
|
159
158
|
end
|
160
159
|
end
|
161
160
|
end
|
162
161
|
|
163
162
|
def to_routes
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
163
|
+
default_route_options = prepare_default_route_attributes
|
164
|
+
default_path_settings = prepare_default_path_settings
|
165
|
+
|
166
|
+
map_routes do |method, raw_path|
|
167
|
+
prepared_path = Path.new(raw_path, namespace, default_path_settings)
|
168
|
+
params = options[:route_options].present? ? options[:route_options].merge(default_route_options) : default_route_options
|
169
|
+
route = Grape::Router::Route.new(method, prepared_path.origin, prepared_path.suffix, params)
|
169
170
|
route.apply(self)
|
170
171
|
end.flatten
|
171
172
|
end
|
@@ -191,23 +192,19 @@ module Grape
|
|
191
192
|
|
192
193
|
def prepare_version
|
193
194
|
version = namespace_inheritable(:version)
|
194
|
-
return
|
195
|
-
return if version.empty?
|
195
|
+
return if version.blank?
|
196
196
|
|
197
197
|
version.length == 1 ? version.first : version
|
198
198
|
end
|
199
199
|
|
200
|
-
def merge_route_options(**default)
|
201
|
-
options[:route_options].clone.merge!(**default)
|
202
|
-
end
|
203
|
-
|
204
200
|
def map_routes
|
205
201
|
options[:method].map { |method| options[:path].map { |path| yield method, path } }
|
206
202
|
end
|
207
203
|
|
208
|
-
def
|
209
|
-
|
210
|
-
|
204
|
+
def prepare_default_path_settings
|
205
|
+
namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash
|
206
|
+
namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash
|
207
|
+
namespace_stackable_hash.merge!(namespace_inheritable_hash)
|
211
208
|
end
|
212
209
|
|
213
210
|
def namespace
|
@@ -235,6 +232,15 @@ module Grape
|
|
235
232
|
(options == endpoint.options) && (inheritable_setting.to_hash == endpoint.inheritable_setting.to_hash)
|
236
233
|
end
|
237
234
|
|
235
|
+
# The purpose of this override is solely for stripping internals when an error occurs while calling
|
236
|
+
# an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
|
237
|
+
# Otherwise, it calls super.
|
238
|
+
def inspect
|
239
|
+
return super unless env
|
240
|
+
|
241
|
+
"#{self.class} in '#{route.origin}' endpoint"
|
242
|
+
end
|
243
|
+
|
238
244
|
protected
|
239
245
|
|
240
246
|
def run
|
@@ -249,9 +255,10 @@ module Grape
|
|
249
255
|
run_filters befores, :before
|
250
256
|
|
251
257
|
if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
|
252
|
-
|
258
|
+
allow_header_value = allowed_methods.join(', ')
|
259
|
+
raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allow_header_value)) unless options?
|
253
260
|
|
254
|
-
header Grape::Http::Headers::ALLOW,
|
261
|
+
header Grape::Http::Headers::ALLOW, allow_header_value
|
255
262
|
response_object = ''
|
256
263
|
status 204
|
257
264
|
else
|
@@ -277,54 +284,6 @@ module Grape
|
|
277
284
|
end
|
278
285
|
end
|
279
286
|
|
280
|
-
def build_stack(helpers)
|
281
|
-
stack = Grape::Middleware::Stack.new
|
282
|
-
|
283
|
-
stack.use Rack::Head
|
284
|
-
stack.use Class.new(Grape::Middleware::Error),
|
285
|
-
helpers: helpers,
|
286
|
-
format: namespace_inheritable(:format),
|
287
|
-
content_types: namespace_stackable_with_hash(:content_types),
|
288
|
-
default_status: namespace_inheritable(:default_error_status),
|
289
|
-
rescue_all: namespace_inheritable(:rescue_all),
|
290
|
-
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
291
|
-
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
292
|
-
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
293
|
-
rescue_options: namespace_stackable_with_hash(:rescue_options) || {},
|
294
|
-
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers) || {},
|
295
|
-
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {},
|
296
|
-
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
297
|
-
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
298
|
-
|
299
|
-
stack.concat namespace_stackable(:middleware)
|
300
|
-
|
301
|
-
if namespace_inheritable(:version)
|
302
|
-
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
303
|
-
versions: namespace_inheritable(:version)&.flatten,
|
304
|
-
version_options: namespace_inheritable(:version_options),
|
305
|
-
prefix: namespace_inheritable(:root_prefix),
|
306
|
-
mount_path: namespace_stackable(:mount_path).first
|
307
|
-
end
|
308
|
-
|
309
|
-
stack.use Grape::Middleware::Formatter,
|
310
|
-
format: namespace_inheritable(:format),
|
311
|
-
default_format: namespace_inheritable(:default_format) || :txt,
|
312
|
-
content_types: namespace_stackable_with_hash(:content_types),
|
313
|
-
formatters: namespace_stackable_with_hash(:formatters),
|
314
|
-
parsers: namespace_stackable_with_hash(:parsers)
|
315
|
-
|
316
|
-
builder = stack.build
|
317
|
-
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
318
|
-
builder.to_app
|
319
|
-
end
|
320
|
-
|
321
|
-
def build_helpers
|
322
|
-
helpers = namespace_stackable(:helpers)
|
323
|
-
Module.new { helpers&.each { |mod_to_include| include mod_to_include } }
|
324
|
-
end
|
325
|
-
|
326
|
-
private :build_stack, :build_helpers
|
327
|
-
|
328
287
|
def execute
|
329
288
|
@block&.call(self)
|
330
289
|
end
|
@@ -339,7 +298,7 @@ module Grape
|
|
339
298
|
@lazy_initialize_lock.synchronize do
|
340
299
|
return true if @lazy_initialized
|
341
300
|
|
342
|
-
@helpers = build_helpers
|
301
|
+
@helpers = build_helpers&.tap { |mod| self.class.include mod }
|
343
302
|
@app = options[:app] || build_stack(@helpers)
|
344
303
|
|
345
304
|
@lazy_initialized = true
|
@@ -396,7 +355,7 @@ module Grape
|
|
396
355
|
return enum_for(:validations) unless block_given?
|
397
356
|
|
398
357
|
route_setting(:saved_validations)&.each do |saved_validation|
|
399
|
-
yield Grape::Validations::ValidatorFactory.create_validator(
|
358
|
+
yield Grape::Validations::ValidatorFactory.create_validator(saved_validation)
|
400
359
|
end
|
401
360
|
end
|
402
361
|
|
@@ -405,8 +364,57 @@ module Grape
|
|
405
364
|
env[Rack::REQUEST_METHOD] == Rack::OPTIONS
|
406
365
|
end
|
407
366
|
|
408
|
-
|
409
|
-
|
367
|
+
private
|
368
|
+
|
369
|
+
def build_stack(helpers)
|
370
|
+
stack = Grape::Middleware::Stack.new
|
371
|
+
|
372
|
+
content_types = namespace_stackable_with_hash(:content_types)
|
373
|
+
format = namespace_inheritable(:format)
|
374
|
+
|
375
|
+
stack.use Rack::Head
|
376
|
+
stack.use Class.new(Grape::Middleware::Error),
|
377
|
+
helpers: helpers,
|
378
|
+
format: format,
|
379
|
+
content_types: content_types,
|
380
|
+
default_status: namespace_inheritable(:default_error_status),
|
381
|
+
rescue_all: namespace_inheritable(:rescue_all),
|
382
|
+
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
383
|
+
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
384
|
+
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
385
|
+
rescue_options: namespace_stackable_with_hash(:rescue_options),
|
386
|
+
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers),
|
387
|
+
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers),
|
388
|
+
all_rescue_handler: namespace_inheritable(:all_rescue_handler),
|
389
|
+
grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
|
390
|
+
|
391
|
+
stack.concat namespace_stackable(:middleware)
|
392
|
+
|
393
|
+
if namespace_inheritable(:version).present?
|
394
|
+
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
395
|
+
versions: namespace_inheritable(:version).flatten,
|
396
|
+
version_options: namespace_inheritable(:version_options),
|
397
|
+
prefix: namespace_inheritable(:root_prefix),
|
398
|
+
mount_path: namespace_stackable(:mount_path).first
|
399
|
+
end
|
400
|
+
|
401
|
+
stack.use Grape::Middleware::Formatter,
|
402
|
+
format: format,
|
403
|
+
default_format: namespace_inheritable(:default_format) || :txt,
|
404
|
+
content_types: content_types,
|
405
|
+
formatters: namespace_stackable_with_hash(:formatters),
|
406
|
+
parsers: namespace_stackable_with_hash(:parsers)
|
407
|
+
|
408
|
+
builder = stack.build
|
409
|
+
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
410
|
+
builder.to_app
|
411
|
+
end
|
412
|
+
|
413
|
+
def build_helpers
|
414
|
+
helpers = namespace_stackable(:helpers)
|
415
|
+
return if helpers.empty?
|
416
|
+
|
417
|
+
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
410
418
|
end
|
411
419
|
end
|
412
420
|
end
|
@@ -2,36 +2,66 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
|
8
|
+
merge_backtrace = backtrace.present? && options.dig(:rescue_options, :backtrace)
|
9
|
+
merge_original_exception = original_exception && options.dig(:rescue_options, :original_exception)
|
10
|
+
|
11
|
+
wrapped_message = wrap_message(present(message, env))
|
12
|
+
if wrapped_message.is_a?(Hash)
|
13
|
+
wrapped_message[:backtrace] = backtrace if merge_backtrace
|
14
|
+
wrapped_message[:original_exception] = original_exception.inspect if merge_original_exception
|
15
|
+
end
|
16
|
+
|
17
|
+
format_structured_message(wrapped_message)
|
12
18
|
end
|
13
19
|
|
14
|
-
|
20
|
+
def present(message, env)
|
21
|
+
present_options = {}
|
22
|
+
presented_message = message
|
23
|
+
if presented_message.is_a?(Hash)
|
24
|
+
presented_message = presented_message.dup
|
25
|
+
present_options[:with] = presented_message.delete(:with)
|
26
|
+
end
|
27
|
+
|
28
|
+
presenter = env[Grape::Env::API_ENDPOINT].entity_class_for_obj(presented_message, present_options)
|
29
|
+
|
30
|
+
unless presenter || env[Grape::Env::GRAPE_ROUTING_ARGS].nil?
|
31
|
+
# env['api.endpoint'].route does not work when the error occurs within a middleware
|
32
|
+
# the Endpoint does not have a valid env at this moment
|
33
|
+
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
34
|
+
|
35
|
+
found_code = http_codes.find do |http_code|
|
36
|
+
(http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
|
37
|
+
end if env[Grape::Env::API_ENDPOINT].request
|
15
38
|
|
16
|
-
|
17
|
-
|
18
|
-
# the Endpoint does not have a valid env at this moment
|
19
|
-
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
39
|
+
presenter = found_code[2] if found_code
|
40
|
+
end
|
20
41
|
|
21
|
-
|
22
|
-
|
23
|
-
|
42
|
+
if presenter
|
43
|
+
embeds = { env: env }
|
44
|
+
embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
|
45
|
+
presented_message = presenter.represent(presented_message, embeds).serializable_hash
|
46
|
+
end
|
24
47
|
|
25
|
-
|
48
|
+
presented_message
|
26
49
|
end
|
27
50
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
51
|
+
def wrap_message(message)
|
52
|
+
return message if message.is_a?(Hash)
|
53
|
+
|
54
|
+
{ message: message }
|
55
|
+
end
|
56
|
+
|
57
|
+
def format_structured_message(_structured_message)
|
58
|
+
raise NotImplementedError
|
32
59
|
end
|
33
60
|
|
34
|
-
|
61
|
+
def inherited(klass)
|
62
|
+
super
|
63
|
+
ErrorFormatter.register(klass)
|
64
|
+
end
|
35
65
|
end
|
36
66
|
end
|
37
67
|
end
|
@@ -2,27 +2,19 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
extend Base
|
7
|
-
|
5
|
+
class Json < Base
|
8
6
|
class << self
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
rescue_options = options[:rescue_options] || {}
|
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
|
15
|
-
::Grape::Json.dump(result)
|
7
|
+
def format_structured_message(structured_message)
|
8
|
+
::Grape::Json.dump(structured_message)
|
16
9
|
end
|
17
10
|
|
18
11
|
private
|
19
12
|
|
20
13
|
def wrap_message(message)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
14
|
+
return message if message.is_a?(Hash)
|
15
|
+
return message.as_json if message.is_a?(Exceptions::ValidationErrors)
|
16
|
+
|
17
|
+
{ error: ensure_utf8(message) }
|
26
18
|
end
|
27
19
|
|
28
20
|
def ensure_utf8(message)
|
@@ -2,26 +2,19 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
if rescue_options[:original_exception] && original_exception
|
20
|
-
final_result << 'original exception:'
|
21
|
-
final_result << original_exception.inspect
|
22
|
-
end
|
23
|
-
end.join("\r\n ")
|
24
|
-
end
|
5
|
+
class Txt < Base
|
6
|
+
def self.format_structured_message(structured_message)
|
7
|
+
message = structured_message[:message] || Grape::Json.dump(structured_message)
|
8
|
+
Array.wrap(message).tap do |final_message|
|
9
|
+
if structured_message.key?(:backtrace)
|
10
|
+
final_message << 'backtrace:'
|
11
|
+
final_message.concat(structured_message[:backtrace])
|
12
|
+
end
|
13
|
+
if structured_message.key?(:original_exception)
|
14
|
+
final_message << 'original exception:'
|
15
|
+
final_message << structured_message[:original_exception]
|
16
|
+
end
|
17
|
+
end.join("\r\n ")
|
25
18
|
end
|
26
19
|
end
|
27
20
|
end
|
@@ -2,19 +2,9 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
class << self
|
9
|
-
def call(message, backtrace, options = {}, env = nil, original_exception = nil)
|
10
|
-
message = present(message, env)
|
11
|
-
|
12
|
-
result = message.is_a?(Hash) ? message : { message: message }
|
13
|
-
rescue_options = options[:rescue_options] || {}
|
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
|
16
|
-
result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
|
17
|
-
end
|
5
|
+
class Xml < Base
|
6
|
+
def self.format_structured_message(structured_message)
|
7
|
+
structured_message.respond_to?(:to_xml) ? structured_message.to_xml(root: :error) : structured_message.to_s
|
18
8
|
end
|
19
9
|
end
|
20
10
|
end
|
@@ -2,34 +2,14 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module ErrorFormatter
|
5
|
-
extend Util::
|
5
|
+
extend Grape::Util::Registry
|
6
6
|
|
7
|
-
|
8
|
-
def builtin_formatters
|
9
|
-
@builtin_formatters ||= {
|
10
|
-
serializable_hash: Grape::ErrorFormatter::Json,
|
11
|
-
json: Grape::ErrorFormatter::Json,
|
12
|
-
jsonapi: Grape::ErrorFormatter::Json,
|
13
|
-
txt: Grape::ErrorFormatter::Txt,
|
14
|
-
xml: Grape::ErrorFormatter::Xml
|
15
|
-
}
|
16
|
-
end
|
7
|
+
module_function
|
17
8
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
9
|
+
def formatter_for(format, error_formatters = nil, default_error_formatter = nil)
|
10
|
+
return error_formatters[format] if error_formatters&.key?(format)
|
21
11
|
|
22
|
-
|
23
|
-
spec = formatters(**options)[api_format]
|
24
|
-
case spec
|
25
|
-
when nil
|
26
|
-
options[:default_error_formatter] || Grape::ErrorFormatter::Txt
|
27
|
-
when Symbol
|
28
|
-
method(spec)
|
29
|
-
else
|
30
|
-
spec
|
31
|
-
end
|
32
|
-
end
|
12
|
+
registry[format] || default_error_formatter || Grape::ErrorFormatter::Txt
|
33
13
|
end
|
34
14
|
end
|
35
15
|
end
|
@@ -9,7 +9,7 @@ module Grape
|
|
9
9
|
|
10
10
|
attr_reader :status, :headers
|
11
11
|
|
12
|
-
def initialize(status: nil, message: nil, headers: nil
|
12
|
+
def initialize(status: nil, message: nil, headers: nil)
|
13
13
|
super(message)
|
14
14
|
|
15
15
|
@status = status
|
@@ -26,42 +26,32 @@ module Grape
|
|
26
26
|
# if BASE_ATTRIBUTES_KEY.key respond to a string message, then short_message is returned
|
27
27
|
# if BASE_ATTRIBUTES_KEY.key respond to a Hash, means it may have problem , summary and resolution
|
28
28
|
def compose_message(key, **attributes)
|
29
|
-
short_message = translate_message(key,
|
30
|
-
|
31
|
-
@problem = problem(key, **attributes)
|
32
|
-
@summary = summary(key, **attributes)
|
33
|
-
@resolution = resolution(key, **attributes)
|
34
|
-
[['Problem', @problem], ['Summary', @summary], ['Resolution', @resolution]].each_with_object(+'') do |detail_array, message|
|
35
|
-
message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
|
36
|
-
message
|
37
|
-
end
|
38
|
-
else
|
39
|
-
short_message
|
40
|
-
end
|
41
|
-
end
|
29
|
+
short_message = translate_message(key, attributes)
|
30
|
+
return short_message unless short_message.is_a?(Hash)
|
42
31
|
|
43
|
-
|
44
|
-
|
32
|
+
each_steps(key, attributes).with_object(+'') do |detail_array, message|
|
33
|
+
message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
|
34
|
+
end
|
45
35
|
end
|
46
36
|
|
47
|
-
def
|
48
|
-
|
49
|
-
end
|
37
|
+
def each_steps(key, attributes)
|
38
|
+
return enum_for(:each_steps, key, attributes) unless block_given?
|
50
39
|
|
51
|
-
|
52
|
-
translate_message(:"#{key}.
|
40
|
+
yield 'Problem', translate_message(:"#{key}.problem", attributes)
|
41
|
+
yield 'Summary', translate_message(:"#{key}.summary", attributes)
|
42
|
+
yield 'Resolution', translate_message(:"#{key}.resolution", attributes)
|
53
43
|
end
|
54
44
|
|
55
|
-
def translate_attributes(keys,
|
45
|
+
def translate_attributes(keys, options = {})
|
56
46
|
keys.map do |key|
|
57
|
-
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", default: key
|
47
|
+
translate("#{BASE_ATTRIBUTES_KEY}.#{key}", options.merge(default: key.to_s))
|
58
48
|
end.join(', ')
|
59
49
|
end
|
60
50
|
|
61
|
-
def translate_message(key,
|
51
|
+
def translate_message(key, options = {})
|
62
52
|
case key
|
63
53
|
when Symbol
|
64
|
-
translate("#{BASE_MESSAGES_KEY}.#{key}", default: ''
|
54
|
+
translate("#{BASE_MESSAGES_KEY}.#{key}", options.merge(default: ''))
|
65
55
|
when Proc
|
66
56
|
key.call
|
67
57
|
else
|
@@ -69,14 +59,12 @@ module Grape
|
|
69
59
|
end
|
70
60
|
end
|
71
61
|
|
72
|
-
def translate(key,
|
73
|
-
options = options.dup
|
74
|
-
options[:default] &&= options[:default].to_s
|
62
|
+
def translate(key, options)
|
75
63
|
message = ::I18n.translate(key, **options)
|
76
|
-
message.presence || fallback_message(key,
|
64
|
+
message.presence || fallback_message(key, options)
|
77
65
|
end
|
78
66
|
|
79
|
-
def fallback_message(key,
|
67
|
+
def fallback_message(key, options)
|
80
68
|
if ::I18n.enforce_available_locales && ::I18n.available_locales.exclude?(FALLBACK_LOCALE)
|
81
69
|
key
|
82
70
|
else
|
@@ -2,16 +2,17 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class Validation <
|
5
|
+
class Validation < Base
|
6
6
|
attr_accessor :params, :message_key
|
7
7
|
|
8
|
-
def initialize(params:, message: nil,
|
8
|
+
def initialize(params:, message: nil, status: nil, headers: nil)
|
9
9
|
@params = params
|
10
10
|
if message
|
11
11
|
@message_key = message if message.is_a?(Symbol)
|
12
|
-
|
12
|
+
message = translate_message(message)
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
|
+
super(status: status, message: message, headers: headers)
|
15
16
|
end
|
16
17
|
|
17
18
|
# Remove all the unnecessary stuff from Grape::Exceptions::Base like status
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class ValidationErrors <
|
5
|
+
class ValidationErrors < Base
|
6
6
|
ERRORS_FORMAT_KEY = 'grape.errors.format'
|
7
7
|
DEFAULT_ERRORS_FORMAT = '%<attributes>s %<message>s'
|
8
8
|
|
@@ -10,7 +10,7 @@ module Grape
|
|
10
10
|
|
11
11
|
attr_reader :errors
|
12
12
|
|
13
|
-
def initialize(errors: [], headers: {}
|
13
|
+
def initialize(errors: [], headers: {})
|
14
14
|
@errors = errors.group_by(&:params)
|
15
15
|
super(message: full_messages.join(', '), status: 400, headers: headers)
|
16
16
|
end
|
data/lib/grape/formatter/json.rb
CHANGED
@@ -2,13 +2,11 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Formatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
return object.to_json if object.respond_to?(:to_json)
|
5
|
+
class Json < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
return object.to_json if object.respond_to?(:to_json)
|
9
8
|
|
10
|
-
|
11
|
-
end
|
9
|
+
::Grape::Json.dump(object)
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|