grape 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/README.md +7 -6
  4. data/UPGRADING.md +19 -0
  5. data/grape.gemspec +5 -5
  6. data/lib/grape/api/instance.rb +22 -58
  7. data/lib/grape/api.rb +2 -11
  8. data/lib/grape/dsl/desc.rb +27 -24
  9. data/lib/grape/dsl/inside_route.rb +12 -23
  10. data/lib/grape/dsl/parameters.rb +2 -2
  11. data/lib/grape/dsl/routing.rb +5 -12
  12. data/lib/grape/endpoint.rb +76 -79
  13. data/lib/grape/error_formatter/base.rb +51 -21
  14. data/lib/grape/error_formatter/json.rb +7 -24
  15. data/lib/grape/error_formatter/jsonapi.rb +7 -0
  16. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  17. data/lib/grape/error_formatter/txt.rb +13 -20
  18. data/lib/grape/error_formatter/xml.rb +3 -13
  19. data/lib/grape/error_formatter.rb +4 -12
  20. data/lib/grape/exceptions/base.rb +18 -30
  21. data/lib/grape/exceptions/validation.rb +5 -4
  22. data/lib/grape/exceptions/validation_errors.rb +2 -2
  23. data/lib/grape/formatter/base.rb +16 -0
  24. data/lib/grape/formatter/json.rb +4 -6
  25. data/lib/grape/formatter/serializable_hash.rb +1 -1
  26. data/lib/grape/formatter/txt.rb +3 -5
  27. data/lib/grape/formatter/xml.rb +4 -6
  28. data/lib/grape/formatter.rb +4 -12
  29. data/lib/grape/http/headers.rb +1 -0
  30. data/lib/grape/middleware/error.rb +2 -0
  31. data/lib/grape/middleware/formatter.rb +1 -1
  32. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -3
  33. data/lib/grape/middleware/versioner/base.rb +82 -0
  34. data/lib/grape/middleware/versioner/header.rb +3 -9
  35. data/lib/grape/middleware/versioner/param.rb +0 -2
  36. data/lib/grape/middleware/versioner/path.rb +0 -2
  37. data/lib/grape/middleware/versioner.rb +5 -3
  38. data/lib/grape/namespace.rb +1 -1
  39. data/lib/grape/parser/base.rb +16 -0
  40. data/lib/grape/parser/json.rb +6 -8
  41. data/lib/grape/parser/jsonapi.rb +7 -0
  42. data/lib/grape/parser/xml.rb +6 -8
  43. data/lib/grape/parser.rb +5 -7
  44. data/lib/grape/path.rb +39 -56
  45. data/lib/grape/request.rb +2 -2
  46. data/lib/grape/router/base_route.rb +2 -2
  47. data/lib/grape/router/greedy_route.rb +2 -2
  48. data/lib/grape/router/pattern.rb +23 -18
  49. data/lib/grape/router/route.rb +13 -5
  50. data/lib/grape/router.rb +5 -5
  51. data/lib/grape/util/registry.rb +27 -0
  52. data/lib/grape/validations/contract_scope.rb +2 -39
  53. data/lib/grape/validations/params_scope.rb +7 -11
  54. data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
  55. data/lib/grape/validations/validator_factory.rb +2 -2
  56. data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
  57. data/lib/grape/validations/validators/base.rb +5 -9
  58. data/lib/grape/validations/validators/coerce_validator.rb +1 -1
  59. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  60. data/lib/grape/validations/validators/default_validator.rb +1 -1
  61. data/lib/grape/validations/validators/except_values_validator.rb +1 -1
  62. data/lib/grape/validations/validators/length_validator.rb +1 -1
  63. data/lib/grape/validations/validators/regexp_validator.rb +1 -1
  64. data/lib/grape/validations/validators/values_validator.rb +15 -57
  65. data/lib/grape/validations.rb +8 -17
  66. data/lib/grape/version.rb +1 -1
  67. data/lib/grape.rb +1 -1
  68. metadata +14 -11
  69. data/lib/grape/middleware/versioner_helpers.rb +0 -75
  70. data/lib/grape/validations/types/build_coercer.rb +0 -92
@@ -145,27 +145,28 @@ module Grape
145
145
  end
146
146
 
147
147
  def mount_in(router)
148
- if endpoints
149
- endpoints.each { |e| e.mount_in(router) }
150
- else
151
- reset_routes!
152
- routes.each do |route|
153
- methods = [route.request_method]
154
- methods << Rack::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Rack::GET
155
- methods.each do |method|
156
- route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
157
- router.append(route.apply(self))
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
- route_options = prepare_default_route_attributes
165
- map_routes do |method, path|
166
- path = prepare_path(path)
167
- params = merge_route_options(**route_options.merge(suffix: path.suffix))
168
- route = Router::Route.new(method, path.path, **params)
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
@@ -196,19 +197,14 @@ module Grape
196
197
  version.length == 1 ? version.first : version
197
198
  end
198
199
 
199
- def merge_route_options(**default)
200
- options[:route_options].clone.merge!(**default)
201
- end
202
-
203
200
  def map_routes
204
201
  options[:method].map { |method| options[:path].map { |path| yield method, path } }
205
202
  end
206
203
 
207
- def prepare_path(path)
204
+ def prepare_default_path_settings
208
205
  namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash
209
206
  namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash
210
- path_settings = namespace_stackable_hash.merge!(namespace_inheritable_hash)
211
- Path.new(path, namespace, path_settings)
207
+ namespace_stackable_hash.merge!(namespace_inheritable_hash)
212
208
  end
213
209
 
214
210
  def namespace
@@ -259,9 +255,10 @@ module Grape
259
255
  run_filters befores, :before
260
256
 
261
257
  if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
262
- raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allowed_methods)) unless options?
258
+ allow_header_value = allowed_methods.join(', ')
259
+ raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allow_header_value)) unless options?
263
260
 
264
- header Grape::Http::Headers::ALLOW, allowed_methods
261
+ header Grape::Http::Headers::ALLOW, allow_header_value
265
262
  response_object = ''
266
263
  status 204
267
264
  else
@@ -287,59 +284,6 @@ module Grape
287
284
  end
288
285
  end
289
286
 
290
- def build_stack(helpers)
291
- stack = Grape::Middleware::Stack.new
292
-
293
- content_types = namespace_stackable_with_hash(:content_types)
294
- format = namespace_inheritable(:format)
295
-
296
- stack.use Rack::Head
297
- stack.use Class.new(Grape::Middleware::Error),
298
- helpers: helpers,
299
- format: format,
300
- content_types: content_types,
301
- default_status: namespace_inheritable(:default_error_status),
302
- rescue_all: namespace_inheritable(:rescue_all),
303
- rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
304
- default_error_formatter: namespace_inheritable(:default_error_formatter),
305
- error_formatters: namespace_stackable_with_hash(:error_formatters),
306
- rescue_options: namespace_stackable_with_hash(:rescue_options),
307
- rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers),
308
- base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers),
309
- all_rescue_handler: namespace_inheritable(:all_rescue_handler),
310
- grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
311
-
312
- stack.concat namespace_stackable(:middleware)
313
-
314
- if namespace_inheritable(:version).present?
315
- stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
316
- versions: namespace_inheritable(:version).flatten,
317
- version_options: namespace_inheritable(:version_options),
318
- prefix: namespace_inheritable(:root_prefix),
319
- mount_path: namespace_stackable(:mount_path).first
320
- end
321
-
322
- stack.use Grape::Middleware::Formatter,
323
- format: format,
324
- default_format: namespace_inheritable(:default_format) || :txt,
325
- content_types: content_types,
326
- formatters: namespace_stackable_with_hash(:formatters),
327
- parsers: namespace_stackable_with_hash(:parsers)
328
-
329
- builder = stack.build
330
- builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
331
- builder.to_app
332
- end
333
-
334
- def build_helpers
335
- helpers = namespace_stackable(:helpers)
336
- return if helpers.empty?
337
-
338
- Module.new { helpers.each { |mod_to_include| include mod_to_include } }
339
- end
340
-
341
- private :build_stack, :build_helpers
342
-
343
287
  def execute
344
288
  @block&.call(self)
345
289
  end
@@ -411,7 +355,7 @@ module Grape
411
355
  return enum_for(:validations) unless block_given?
412
356
 
413
357
  route_setting(:saved_validations)&.each do |saved_validation|
414
- yield Grape::Validations::ValidatorFactory.create_validator(**saved_validation)
358
+ yield Grape::Validations::ValidatorFactory.create_validator(saved_validation)
415
359
  end
416
360
  end
417
361
 
@@ -419,5 +363,58 @@ module Grape
419
363
  options[:options_route_enabled] &&
420
364
  env[Rack::REQUEST_METHOD] == Rack::OPTIONS
421
365
  end
366
+
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 } }
418
+ end
422
419
  end
423
420
  end
@@ -2,36 +2,66 @@
2
2
 
3
3
  module Grape
4
4
  module ErrorFormatter
5
- module Base
6
- def present(message, env)
7
- present_options = {}
8
- presented_message = message
9
- if presented_message.is_a?(Hash)
10
- presented_message = presented_message.dup
11
- present_options[:with] = presented_message.delete(:with)
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
- presenter = env[Grape::Env::API_ENDPOINT].entity_class_for_obj(presented_message, present_options)
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
- unless presenter || env[Grape::Env::GRAPE_ROUTING_ARGS].nil?
17
- # env['api.endpoint'].route does not work when the error occurs within a middleware
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
- found_code = http_codes.find do |http_code|
22
- (http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
23
- end if env[Grape::Env::API_ENDPOINT].request
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
- presenter = found_code[2] if found_code
48
+ presented_message
26
49
  end
27
50
 
28
- if presenter
29
- embeds = { env: env }
30
- embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
31
- presented_message = presenter.represent(presented_message, embeds).serializable_hash
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
- presented_message
61
+ def inherited(klass)
62
+ super
63
+ ErrorFormatter.register(klass)
64
+ end
35
65
  end
36
66
  end
37
67
  end
@@ -2,28 +2,19 @@
2
2
 
3
3
  module Grape
4
4
  module ErrorFormatter
5
- module Json
6
- extend Base
7
-
5
+ class Json < Base
8
6
  class << self
9
- def call(message, backtrace, options = {}, env = nil, original_exception = nil)
10
- result = wrap_message(present(message, env))
11
-
12
- result = merge_rescue_options(result, backtrace, options, original_exception) if result.is_a?(Hash)
13
-
14
- ::Grape::Json.dump(result)
7
+ def format_structured_message(structured_message)
8
+ ::Grape::Json.dump(structured_message)
15
9
  end
16
10
 
17
11
  private
18
12
 
19
13
  def wrap_message(message)
20
- if message.is_a?(Hash)
21
- message
22
- elsif message.is_a?(Exceptions::ValidationErrors)
23
- message.as_json
24
- else
25
- { error: ensure_utf8(message) }
26
- 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) }
27
18
  end
28
19
 
29
20
  def ensure_utf8(message)
@@ -31,14 +22,6 @@ module Grape
31
22
 
32
23
  message.encode('UTF-8', invalid: :replace, undef: :replace)
33
24
  end
34
-
35
- def merge_rescue_options(result, backtrace, options, original_exception)
36
- rescue_options = options[:rescue_options] || {}
37
- result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
38
- result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
39
-
40
- result
41
- end
42
25
  end
43
26
  end
44
27
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module ErrorFormatter
5
+ class Jsonapi < Json; end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module ErrorFormatter
5
+ class SerializableHash < Json; end
6
+ end
7
+ end
@@ -2,26 +2,19 @@
2
2
 
3
3
  module Grape
4
4
  module ErrorFormatter
5
- module Txt
6
- extend Base
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) ? ::Grape::Json.dump(message) : message
13
- Array.wrap(result).tap do |final_result|
14
- rescue_options = options[:rescue_options] || {}
15
- if rescue_options[:backtrace] && backtrace.present?
16
- final_result << 'backtrace:'
17
- final_result.concat(backtrace)
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
- module Xml
6
- extend Base
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,22 +2,14 @@
2
2
 
3
3
  module Grape
4
4
  module ErrorFormatter
5
- module_function
5
+ extend Grape::Util::Registry
6
6
 
7
- DEFAULTS = {
8
- serializable_hash: Grape::ErrorFormatter::Json,
9
- json: Grape::ErrorFormatter::Json,
10
- jsonapi: Grape::ErrorFormatter::Json,
11
- txt: Grape::ErrorFormatter::Txt,
12
- xml: Grape::ErrorFormatter::Xml
13
- }.freeze
7
+ module_function
14
8
 
15
9
  def formatter_for(format, error_formatters = nil, default_error_formatter = nil)
16
- select_formatter(error_formatters, format) || default_error_formatter || DEFAULTS[:txt]
17
- end
10
+ return error_formatters[format] if error_formatters&.key?(format)
18
11
 
19
- def select_formatter(error_formatters, format)
20
- error_formatters&.key?(format) ? error_formatters[format] : DEFAULTS[format]
12
+ registry[format] || default_error_formatter || Grape::ErrorFormatter::Txt
21
13
  end
22
14
  end
23
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, **_options)
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, **attributes)
30
- if short_message.is_a? Hash
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
- def problem(key, **attributes)
44
- translate_message(:"#{key}.problem", **attributes)
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 summary(key, **attributes)
48
- translate_message(:"#{key}.summary", **attributes)
49
- end
37
+ def each_steps(key, attributes)
38
+ return enum_for(:each_steps, key, attributes) unless block_given?
50
39
 
51
- def resolution(key, **attributes)
52
- translate_message(:"#{key}.resolution", **attributes)
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, **options)
45
+ def translate_attributes(keys, options = {})
56
46
  keys.map do |key|
57
- translate("#{BASE_ATTRIBUTES_KEY}.#{key}", default: key, **options)
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, **options)
51
+ def translate_message(key, options = {})
62
52
  case key
63
53
  when Symbol
64
- translate("#{BASE_MESSAGES_KEY}.#{key}", default: '', **options)
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, **options)
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, **options)
64
+ message.presence || fallback_message(key, options)
77
65
  end
78
66
 
79
- def fallback_message(key, **options)
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 < Grape::Exceptions::Base
5
+ class Validation < Base
6
6
  attr_accessor :params, :message_key
7
7
 
8
- def initialize(params:, message: nil, **args)
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
- args[:message] = translate_message(message)
12
+ message = translate_message(message)
13
13
  end
14
- super(**args)
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 < Grape::Exceptions::Base
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: {}, **_options)
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
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Formatter
5
+ class Base
6
+ def self.call(_object, _env)
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def self.inherited(klass)
11
+ super
12
+ Formatter.register(klass)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -2,13 +2,11 @@
2
2
 
3
3
  module Grape
4
4
  module Formatter
5
- module Json
6
- class << self
7
- def call(object, _env)
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
- ::Grape::Json.dump(object)
11
- end
9
+ ::Grape::Json.dump(object)
12
10
  end
13
11
  end
14
12
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Grape
4
4
  module Formatter
5
- module SerializableHash
5
+ class SerializableHash < Base
6
6
  class << self
7
7
  def call(object, _env)
8
8
  return object if object.is_a?(String)
@@ -2,11 +2,9 @@
2
2
 
3
3
  module Grape
4
4
  module Formatter
5
- module Txt
6
- class << self
7
- def call(object, _env)
8
- object.respond_to?(:to_txt) ? object.to_txt : object.to_s
9
- end
5
+ class Txt < Base
6
+ def self.call(object, _env)
7
+ object.respond_to?(:to_txt) ? object.to_txt : object.to_s
10
8
  end
11
9
  end
12
10
  end
@@ -2,13 +2,11 @@
2
2
 
3
3
  module Grape
4
4
  module Formatter
5
- module Xml
6
- class << self
7
- def call(object, _env)
8
- return object.to_xml if object.respond_to?(:to_xml)
5
+ class Xml < Base
6
+ def self.call(object, _env)
7
+ return object.to_xml if object.respond_to?(:to_xml)
9
8
 
10
- raise Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
11
- end
9
+ raise Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
12
10
  end
13
11
  end
14
12
  end