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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -2
  3. data/README.md +43 -10
  4. data/UPGRADING.md +91 -0
  5. data/grape.gemspec +5 -5
  6. data/lib/grape/api/instance.rb +13 -17
  7. data/lib/grape/api.rb +7 -14
  8. data/lib/grape/cookies.rb +2 -0
  9. data/lib/grape/dsl/callbacks.rb +1 -1
  10. data/lib/grape/dsl/desc.rb +3 -5
  11. data/lib/grape/dsl/helpers.rb +6 -4
  12. data/lib/grape/dsl/inside_route.rb +18 -9
  13. data/lib/grape/dsl/middleware.rb +4 -4
  14. data/lib/grape/dsl/parameters.rb +11 -7
  15. data/lib/grape/dsl/request_response.rb +9 -6
  16. data/lib/grape/dsl/routing.rb +7 -6
  17. data/lib/grape/dsl/settings.rb +5 -5
  18. data/lib/grape/endpoint.rb +21 -36
  19. data/lib/grape/error_formatter/json.rb +2 -6
  20. data/lib/grape/error_formatter/xml.rb +2 -6
  21. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  22. data/lib/grape/exceptions/validation.rb +2 -3
  23. data/lib/grape/exceptions/validation_errors.rb +1 -1
  24. data/lib/grape/formatter/json.rb +1 -0
  25. data/lib/grape/formatter/serializable_hash.rb +2 -1
  26. data/lib/grape/formatter/xml.rb +1 -0
  27. data/lib/grape/locale/en.yml +1 -1
  28. data/lib/grape/middleware/auth/base.rb +3 -3
  29. data/lib/grape/middleware/base.rb +4 -2
  30. data/lib/grape/middleware/error.rb +1 -1
  31. data/lib/grape/middleware/formatter.rb +4 -4
  32. data/lib/grape/middleware/stack.rb +10 -16
  33. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  34. data/lib/grape/middleware/versioner/header.rb +6 -4
  35. data/lib/grape/middleware/versioner/param.rb +1 -0
  36. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  37. data/lib/grape/middleware/versioner/path.rb +2 -0
  38. data/lib/grape/parser/json.rb +1 -1
  39. data/lib/grape/parser/xml.rb +1 -1
  40. data/lib/grape/path.rb +1 -0
  41. data/lib/grape/request.rb +3 -0
  42. data/lib/grape/router/attribute_translator.rb +1 -1
  43. data/lib/grape/router/pattern.rb +1 -1
  44. data/lib/grape/router/route.rb +2 -2
  45. data/lib/grape/router.rb +6 -0
  46. data/lib/grape/util/inheritable_setting.rb +1 -3
  47. data/lib/grape/util/lazy_value.rb +3 -2
  48. data/lib/grape/validations/attributes_iterator.rb +8 -0
  49. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  50. data/lib/grape/validations/params_scope.rb +92 -58
  51. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  52. data/lib/grape/validations/types/custom_type_coercer.rb +3 -2
  53. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  54. data/lib/grape/validations/types/invalid_value.rb +24 -0
  55. data/lib/grape/validations/types/json.rb +2 -1
  56. data/lib/grape/validations/types/primitive_coercer.rb +3 -3
  57. data/lib/grape/validations/types.rb +1 -4
  58. data/lib/grape/validations/validator_factory.rb +1 -1
  59. data/lib/grape/validations/validators/all_or_none.rb +1 -0
  60. data/lib/grape/validations/validators/as.rb +4 -8
  61. data/lib/grape/validations/validators/at_least_one_of.rb +1 -0
  62. data/lib/grape/validations/validators/base.rb +12 -7
  63. data/lib/grape/validations/validators/coerce.rb +8 -9
  64. data/lib/grape/validations/validators/default.rb +1 -0
  65. data/lib/grape/validations/validators/exactly_one_of.rb +1 -0
  66. data/lib/grape/validations/validators/multiple_params_base.rb +5 -2
  67. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -0
  68. data/lib/grape/validations/validators/presence.rb +1 -0
  69. data/lib/grape/validations/validators/regexp.rb +1 -0
  70. data/lib/grape/validations/validators/same_as.rb +1 -0
  71. data/lib/grape/validations/validators/values.rb +3 -0
  72. data/lib/grape/version.rb +1 -1
  73. data/lib/grape.rb +3 -1
  74. data/spec/grape/api/custom_validations_spec.rb +1 -0
  75. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  76. data/spec/grape/api_remount_spec.rb +9 -4
  77. data/spec/grape/api_spec.rb +203 -37
  78. data/spec/grape/dsl/callbacks_spec.rb +1 -1
  79. data/spec/grape/dsl/middleware_spec.rb +1 -1
  80. data/spec/grape/dsl/parameters_spec.rb +1 -0
  81. data/spec/grape/dsl/routing_spec.rb +1 -1
  82. data/spec/grape/endpoint/declared_spec.rb +259 -1
  83. data/spec/grape/endpoint_spec.rb +18 -5
  84. data/spec/grape/entity_spec.rb +10 -10
  85. data/spec/grape/middleware/auth/dsl_spec.rb +1 -1
  86. data/spec/grape/middleware/error_spec.rb +1 -2
  87. data/spec/grape/middleware/formatter_spec.rb +2 -2
  88. data/spec/grape/middleware/stack_spec.rb +4 -3
  89. data/spec/grape/request_spec.rb +1 -1
  90. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
  91. data/spec/grape/validations/params_scope_spec.rb +37 -3
  92. data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
  93. data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
  94. data/spec/grape/validations/validators/coerce_spec.rb +129 -22
  95. data/spec/grape/validations/validators/except_values_spec.rb +2 -2
  96. data/spec/grape/validations/validators/values_spec.rb +15 -11
  97. data/spec/grape/validations_spec.rb +280 -0
  98. data/spec/shared/versioning_examples.rb +22 -22
  99. data/spec/spec_helper.rb +1 -1
  100. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  101. data/spec/support/versioned_helpers.rb +1 -1
  102. 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
- has_renaming = route_setting(:renamed_params) && route_setting(:renamed_params).find { |current| current[declared_param] }
69
- param_renaming = has_renaming[declared_param] if has_renaming
73
+ next unless options[:include_missing] || passed_params.key?(declared_param)
70
74
 
71
- next unless options[:include_missing] || passed_params.key?(declared_param) || (param_renaming && passed_params.key?(param_renaming))
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(param_renaming || declared_param, options)
74
- passed_param = passed_params[param_renaming || declared_param]
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 += '[' + params_nested_path[1..-1].join('][') + ']' if params_nested_path.size > 1
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 explicitely defined, auto-detect from relation#klass or first object in the collection
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
@@ -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 block_given?
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 block_given?
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 block_given?
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 block_given?
42
+ arr << block if block
43
43
 
44
44
  namespace_stackable(:middleware, arr)
45
45
  end
@@ -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 parameter names, or, if :using is
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
- block_given? ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
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 && block_given?
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
- block_given? ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
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
- def map_params(params, element)
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 block_given?
106
+ elsif block
106
107
  handler = block
107
108
  end
108
109
 
109
110
  options = args.extract_options!
110
- if block_given? && options.key?(:with)
111
- raise ArgumentError, 'both :with option and block cannot be passed'
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, Hash[args.map { |arg| [arg, handler] }]
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] && options[:with].is_a?(Class)
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
@@ -38,7 +38,7 @@ module Grape
38
38
 
39
39
  @versions = versions | requested_versions
40
40
 
41
- if block_given?
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, **opts)
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
- mount(app.mount_instance(configuration: opts[:with] || {}) => path)
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, or group, resource, segment,
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 || block_given?
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
- # Thie method allows you to quickly define a parameter route segment
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.
@@ -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(&_block)
159
+ def within_namespace(&block)
158
160
  namespace_start
159
161
 
160
- result = yield if block_given?
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
@@ -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 block_given?
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 block_given?
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 && endpoints.each { |e| e.inherit_settings(namespace_stackable) }
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.each(&:reset_routes!) if 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] && options[:app].respond_to?(:endpoints)
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, header.merge('Allow' => allowed_methods) unless options?
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
- def remove_renamed_params
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
- begin
369
- validator.validate(request)
370
- rescue Grape::Exceptions::Validation => e
371
- validation_errors << e
372
- break if validator.fail_fast?
373
- rescue Grape::Exceptions::ValidationArrayErrors => e
374
- validation_errors.concat e.errors
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
- result = result.merge(backtrace: backtrace)
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
- result = result.merge(backtrace: backtrace)
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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Exceptions
5
+ class EmptyMessageBody < Base
6
+ def initialize(body_format)
7
+ super(message: compose_message(:empty_message_body, body_format: body_format), status: 400)
8
+ end
9
+ end
10
+ end
11
+ 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
- # remove all the unnecessary stuff from Grape::Exceptions::Base like status
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
@@ -39,7 +39,7 @@ module Grape
39
39
  end
40
40
  end
41
41
 
42
- def to_json(**_opts)
42
+ def to_json(*_opts)
43
43
  as_json.to_json
44
44
  end
45
45
 
@@ -6,6 +6,7 @@ module Grape
6
6
  class << self
7
7
  def call(object, _env)
8
8
  return object.to_json if object.respond_to?(:to_json)
9
+
9
10
  ::Grape::Json.dump(object)
10
11
  end
11
12
  end
@@ -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)
@@ -6,6 +6,7 @@ module Grape
6
6
  class << self
7
7
  def call(object, _env)
8
8
  return object.to_xml if object.respond_to?(:to_xml)
9
+
9
10
  raise Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
10
11
  end
11
12
  end
@@ -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, **options)
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 = options[: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, **options)
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)
@@ -27,7 +27,7 @@ module Grape
27
27
  }
28
28
  end
29
29
 
30
- def initialize(app, **options)
30
+ def initialize(app, *options)
31
31
  super
32
32
  self.class.send(:include, @options[:helpers]) if @options[:helpers]
33
33
  end