grape 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -4
  3. data/Gemfile.lock +13 -13
  4. data/README.md +290 -12
  5. data/UPGRADING.md +68 -1
  6. data/gemfiles/rails_3.gemfile +1 -1
  7. data/lib/grape.rb +8 -2
  8. data/lib/grape/api.rb +40 -34
  9. data/lib/grape/dsl/configuration.rb +2 -115
  10. data/lib/grape/dsl/desc.rb +101 -0
  11. data/lib/grape/dsl/headers.rb +16 -0
  12. data/lib/grape/dsl/helpers.rb +5 -9
  13. data/lib/grape/dsl/inside_route.rb +3 -11
  14. data/lib/grape/dsl/logger.rb +20 -0
  15. data/lib/grape/dsl/parameters.rb +12 -10
  16. data/lib/grape/dsl/request_response.rb +17 -4
  17. data/lib/grape/dsl/routing.rb +24 -7
  18. data/lib/grape/dsl/settings.rb +8 -2
  19. data/lib/grape/endpoint.rb +30 -26
  20. data/lib/grape/error_formatter.rb +31 -0
  21. data/lib/grape/error_formatter/base.rb +0 -28
  22. data/lib/grape/error_formatter/json.rb +13 -2
  23. data/lib/grape/error_formatter/txt.rb +3 -1
  24. data/lib/grape/error_formatter/xml.rb +3 -1
  25. data/lib/grape/exceptions/base.rb +11 -4
  26. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  27. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  28. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  29. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  30. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  31. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  32. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
  33. data/lib/grape/exceptions/method_not_allowed.rb +10 -0
  34. data/lib/grape/exceptions/missing_group_type.rb +1 -1
  35. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  36. data/lib/grape/exceptions/missing_option.rb +1 -1
  37. data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
  38. data/lib/grape/exceptions/unknown_options.rb +1 -1
  39. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  40. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  41. data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
  42. data/lib/grape/exceptions/validation.rb +2 -1
  43. data/lib/grape/formatter.rb +31 -0
  44. data/lib/grape/middleware/base.rb +28 -2
  45. data/lib/grape/middleware/error.rb +24 -1
  46. data/lib/grape/middleware/formatter.rb +4 -3
  47. data/lib/grape/middleware/versioner/param.rb +13 -2
  48. data/lib/grape/parser.rb +29 -0
  49. data/lib/grape/util/sendfile_response.rb +19 -0
  50. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  51. data/lib/grape/validations/params_scope.rb +39 -9
  52. data/lib/grape/validations/types.rb +16 -0
  53. data/lib/grape/validations/validators/all_or_none.rb +1 -1
  54. data/lib/grape/validations/validators/allow_blank.rb +2 -2
  55. data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
  56. data/lib/grape/validations/validators/base.rb +26 -0
  57. data/lib/grape/validations/validators/coerce.rb +16 -14
  58. data/lib/grape/validations/validators/default.rb +1 -1
  59. data/lib/grape/validations/validators/exactly_one_of.rb +10 -1
  60. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
  61. data/lib/grape/validations/validators/presence.rb +1 -1
  62. data/lib/grape/validations/validators/regexp.rb +2 -2
  63. data/lib/grape/validations/validators/values.rb +2 -2
  64. data/lib/grape/version.rb +1 -1
  65. data/spec/grape/api/custom_validations_spec.rb +156 -21
  66. data/spec/grape/api/namespace_parameters_in_route_spec.rb +38 -0
  67. data/spec/grape/api/optional_parameters_in_route_spec.rb +43 -0
  68. data/spec/grape/api/required_parameters_in_route_spec.rb +37 -0
  69. data/spec/grape/api_spec.rb +118 -60
  70. data/spec/grape/dsl/configuration_spec.rb +0 -75
  71. data/spec/grape/dsl/desc_spec.rb +77 -0
  72. data/spec/grape/dsl/headers_spec.rb +32 -0
  73. data/spec/grape/dsl/inside_route_spec.rb +0 -18
  74. data/spec/grape/dsl/logger_spec.rb +26 -0
  75. data/spec/grape/dsl/parameters_spec.rb +13 -7
  76. data/spec/grape/dsl/request_response_spec.rb +17 -3
  77. data/spec/grape/dsl/routing_spec.rb +8 -1
  78. data/spec/grape/dsl/settings_spec.rb +42 -0
  79. data/spec/grape/endpoint_spec.rb +60 -9
  80. data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
  81. data/spec/grape/exceptions/validation_spec.rb +7 -0
  82. data/spec/grape/integration/rack_sendfile_spec.rb +44 -0
  83. data/spec/grape/middleware/base_spec.rb +100 -0
  84. data/spec/grape/middleware/exception_spec.rb +1 -2
  85. data/spec/grape/middleware/formatter_spec.rb +12 -2
  86. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
  87. data/spec/grape/middleware/versioner/header_spec.rb +11 -1
  88. data/spec/grape/middleware/versioner/param_spec.rb +105 -1
  89. data/spec/grape/validations/params_scope_spec.rb +77 -0
  90. data/spec/grape/validations/validators/allow_blank_spec.rb +277 -0
  91. data/spec/grape/validations/validators/coerce_spec.rb +91 -0
  92. data/spec/grape/validations/validators/default_spec.rb +6 -0
  93. data/spec/grape/validations/validators/presence_spec.rb +27 -0
  94. data/spec/grape/validations/validators/regexp_spec.rb +36 -0
  95. data/spec/grape/validations/validators/values_spec.rb +44 -0
  96. data/spec/grape/validations_spec.rb +149 -4
  97. data/spec/spec_helper.rb +1 -0
  98. metadata +26 -5
  99. data/lib/grape/formatter/base.rb +0 -31
  100. data/lib/grape/parser/base.rb +0 -29
  101. data/pkg/grape-0.13.0.gem +0 -0
@@ -0,0 +1,16 @@
1
+ module Grape
2
+ module DSL
3
+ module Headers
4
+ # Set an individual header or retrieve
5
+ # all headers that have been set.
6
+ def header(key = nil, val = nil)
7
+ if key
8
+ val ? header[key.to_s] = val : header.delete(key.to_s)
9
+ else
10
+ @header ||= {}
11
+ end
12
+ end
13
+ alias_method :headers, :header
14
+ end
15
+ end
16
+ end
@@ -30,14 +30,10 @@ module Grape
30
30
  if block_given? || new_mod
31
31
  mod = new_mod || Module.new
32
32
  define_boolean_in_mod(mod)
33
- if new_mod
34
- inject_api_helpers_to_mod(new_mod) if new_mod.is_a?(BaseHelper)
35
- end
36
- if block_given?
37
- inject_api_helpers_to_mod(mod) do
38
- mod.class_eval(&block)
39
- end
40
- end
33
+ inject_api_helpers_to_mod(mod) if new_mod
34
+ inject_api_helpers_to_mod(mod) do
35
+ mod.class_eval(&block)
36
+ end if block_given?
41
37
 
42
38
  namespace_stackable(:helpers, mod)
43
39
  else
@@ -58,7 +54,7 @@ module Grape
58
54
  end
59
55
 
60
56
  def inject_api_helpers_to_mod(mod, &_block)
61
- mod.extend(BaseHelper)
57
+ mod.extend(BaseHelper) unless mod.is_a?(BaseHelper)
62
58
  yield if block_given?
63
59
  mod.api_changed(self)
64
60
  end
@@ -1,10 +1,12 @@
1
1
  require 'active_support/concern'
2
+ require 'grape/dsl/headers'
2
3
 
3
4
  module Grape
4
5
  module DSL
5
6
  module InsideRoute
6
7
  extend ActiveSupport::Concern
7
8
  include Grape::DSL::Settings
9
+ include Grape::DSL::Headers
8
10
 
9
11
  # Denotes a situation where a DSL method has been invoked in a
10
12
  # filter which it should not yet be available in
@@ -138,16 +140,6 @@ module Grape
138
140
  end
139
141
  end
140
142
 
141
- # Set an individual header or retrieve
142
- # all headers that have been set.
143
- def header(key = nil, val = nil)
144
- if key
145
- val ? @header[key.to_s] = val : @header.delete(key.to_s)
146
- else
147
- @header
148
- end
149
- end
150
-
151
143
  # Set response content-type
152
144
  def content_type(val = nil)
153
145
  if val
@@ -303,7 +295,7 @@ module Grape
303
295
  end
304
296
 
305
297
  object_class.ancestors.each do |potential|
306
- entity_class ||= (Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:representations)) || {})[potential]
298
+ entity_class ||= (namespace_stackable_with_hash(:representations) || {})[potential]
307
299
  end
308
300
 
309
301
  entity_class ||= object_class.const_get(:Entity) if object_class.const_defined?(:Entity) && object_class.const_get(:Entity).respond_to?(:represent)
@@ -0,0 +1,20 @@
1
+ module Grape
2
+ module DSL
3
+ module Logger
4
+ include Grape::DSL::Settings
5
+
6
+ attr_writer :logger
7
+
8
+ # Set or retrive the configured logger. If none was configured, this
9
+ # method will create a new one, logging to stdout.
10
+ # @param logger [Object] the new logger to use
11
+ def logger(logger = nil)
12
+ if logger
13
+ global_setting(:logger, logger)
14
+ else
15
+ global_setting(:logger) || global_setting(:logger, ::Logger.new($stdout))
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -30,7 +30,7 @@ module Grape
30
30
  # end
31
31
  # end
32
32
  def use(*names)
33
- named_params = Grape::DSL::Configuration.stacked_hash_to_hash(@api.namespace_stackable(:named_params)) || {}
33
+ named_params = @api.namespace_stackable_with_hash(:named_params) || {}
34
34
  options = names.extract_options!
35
35
  names.each do |name|
36
36
  params_block = named_params.fetch(name) do
@@ -98,7 +98,7 @@ module Grape
98
98
  orig_attrs = attrs.clone
99
99
 
100
100
  opts = attrs.extract_options!.clone
101
- opts[:presence] = true
101
+ opts[:presence] = { value: true, message: opts[:message] }
102
102
 
103
103
  if opts[:using]
104
104
  require_required_and_optional_fields(attrs.first, opts)
@@ -122,7 +122,7 @@ module Grape
122
122
  # check type for optional parameter group
123
123
  if attrs && block_given?
124
124
  fail Grape::Exceptions::MissingGroupTypeError.new if type.nil?
125
- fail Grape::Exceptions::UnsupportedGroupTypeError.new unless [Array, Hash].include?(type)
125
+ fail Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
126
126
  end
127
127
 
128
128
  if opts[:using]
@@ -137,25 +137,25 @@ module Grape
137
137
  # Disallow the given parameters to be present in the same request.
138
138
  # @param attrs [*Symbol] parameters to validate
139
139
  def mutually_exclusive(*attrs)
140
- validates(attrs, mutual_exclusion: true)
140
+ validates(attrs, mutual_exclusion: { value: true, message: extract_message_option(attrs) })
141
141
  end
142
142
 
143
143
  # Require exactly one of the given parameters to be present.
144
144
  # @param (see #mutually_exclusive)
145
145
  def exactly_one_of(*attrs)
146
- validates(attrs, exactly_one_of: true)
146
+ validates(attrs, exactly_one_of: { value: true, message: extract_message_option(attrs) })
147
147
  end
148
148
 
149
149
  # Require at least one of the given parameters to be present.
150
150
  # @param (see #mutually_exclusive)
151
151
  def at_least_one_of(*attrs)
152
- validates(attrs, at_least_one_of: true)
152
+ validates(attrs, at_least_one_of: { value: true, message: extract_message_option(attrs) })
153
153
  end
154
154
 
155
155
  # Require that either all given params are present, or none are.
156
156
  # @param (see #mutually_exclusive)
157
157
  def all_or_none_of(*attrs)
158
- validates(attrs, all_or_none_of: true)
158
+ validates(attrs, all_or_none_of: { value: true, message: extract_message_option(attrs) })
159
159
  end
160
160
 
161
161
  # Define a block of validations which should be applied if and only if
@@ -165,9 +165,11 @@ module Grape
165
165
  # @raise Grape::Exceptions::UnknownParameter if `attr` has not been
166
166
  # defined in this scope yet
167
167
  # @yield a parameter definition DSL
168
- def given(attr, &block)
169
- fail Grape::Exceptions::UnknownParameter.new(attr) unless declared_param?(attr)
170
- new_lateral_scope(dependent_on: attr, &block)
168
+ def given(*attrs, &block)
169
+ attrs.each do |attr|
170
+ fail Grape::Exceptions::UnknownParameter.new(attr) unless declared_param?(attr)
171
+ end
172
+ new_lateral_scope(dependent_on: attrs, &block)
171
173
  end
172
174
 
173
175
  # Test for whether a certain parameter has been defined in this params
@@ -20,7 +20,7 @@ module Grape
20
20
  if new_format
21
21
  namespace_inheritable(:format, new_format.to_sym)
22
22
  # define the default error formatters
23
- namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter::Base.formatter_for(new_format, {}))
23
+ namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(new_format, {}))
24
24
  # define a single mime type
25
25
  mime_type = content_types[new_format.to_sym]
26
26
  fail Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
@@ -43,7 +43,7 @@ module Grape
43
43
  # Specify a default error formatter.
44
44
  def default_error_formatter(new_formatter_name = nil)
45
45
  if new_formatter_name
46
- new_formatter = Grape::ErrorFormatter::Base.formatter_for(new_formatter_name, {})
46
+ new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name, {})
47
47
  namespace_inheritable(:default_error_formatter, new_formatter)
48
48
  else
49
49
  namespace_inheritable(:default_error_formatter)
@@ -68,7 +68,7 @@ module Grape
68
68
 
69
69
  # All available content types.
70
70
  def content_types
71
- c_types = Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:content_types))
71
+ c_types = namespace_stackable_with_hash(:content_types)
72
72
  Grape::ContentTypes.content_types_for c_types
73
73
  end
74
74
 
@@ -105,7 +105,10 @@ module Grape
105
105
  end
106
106
 
107
107
  options = args.extract_options!
108
- handler ||= proc { options[:with] } if options.key?(:with)
108
+ if block_given? && options.key?(:with)
109
+ fail ArgumentError, 'both :with option and block cannot be passed'
110
+ end
111
+ handler ||= extract_with(options)
109
112
 
110
113
  if args.include?(:all)
111
114
  namespace_inheritable(:rescue_all, true)
@@ -149,6 +152,16 @@ module Grape
149
152
  fail Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with] && options[:with].is_a?(Class)
150
153
  namespace_stackable(:representations, model_class => options[:with])
151
154
  end
155
+
156
+ private
157
+
158
+ def extract_with(options)
159
+ return unless options.key?(:with)
160
+ with_option = options[:with]
161
+ return with_option if with_option.instance_of?(Proc)
162
+ return if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
163
+ fail ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
164
+ end
152
165
  end
153
166
  end
154
167
  end
@@ -7,7 +7,7 @@ module Grape
7
7
  include Grape::DSL::Configuration
8
8
 
9
9
  module ClassMethods
10
- attr_reader :endpoints, :routes, :route_set
10
+ attr_reader :endpoints, :routes
11
11
 
12
12
  # Specify an API version.
13
13
  #
@@ -46,8 +46,6 @@ module Grape
46
46
  namespace_inheritable(:version, args)
47
47
  namespace_inheritable(:version_options, options)
48
48
  end
49
-
50
- # reset_validations!
51
49
  end
52
50
 
53
51
  @versions.last unless @versions.nil?
@@ -58,6 +56,16 @@ module Grape
58
56
  namespace_inheritable(:root_prefix, prefix)
59
57
  end
60
58
 
59
+ # Create a scope without affecting the URL.
60
+ #
61
+ # @param _name [Symbol] Purely placebo, just allows to name the scope to
62
+ # make the code more readable.
63
+ def scope(_name = nil, &block)
64
+ within_namespace do
65
+ nest(block)
66
+ end
67
+ end
68
+
61
69
  # Do not route HEAD requests to GET requests automatically.
62
70
  def do_not_route_head!
63
71
  namespace_inheritable(:do_not_route_head, true)
@@ -81,8 +89,6 @@ module Grape
81
89
 
82
90
  in_setting = app.top_level_setting
83
91
 
84
- # app.regenerate_endpoints(in_setting)
85
-
86
92
  app.change!
87
93
  change!
88
94
  end
@@ -115,7 +121,7 @@ module Grape
115
121
  path: paths,
116
122
  for: self,
117
123
  route_options: ({
118
- params: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:params)) || {}
124
+ params: namespace_stackable_with_hash(:params) || {}
119
125
  }).deep_merge(route_setting(:description) || {}).deep_merge(route_options || {})
120
126
  }
121
127
 
@@ -175,9 +181,14 @@ module Grape
175
181
 
176
182
  # Remove all defined routes.
177
183
  def reset_routes!
184
+ endpoints.each(&:reset_routes!)
178
185
  @routes = nil
179
186
  end
180
187
 
188
+ def reset_endpoints!
189
+ @endpoints = []
190
+ end
191
+
181
192
  # Thie method allows you to quickly define a parameter route segment
182
193
  # in your API.
183
194
  #
@@ -185,7 +196,13 @@ module Grape
185
196
  # @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
186
197
  def route_param(param, options = {}, &block)
187
198
  options = options.dup
188
- options[:requirements] = { param.to_sym => options[:requirements] } if options[:requirements].is_a?(Regexp)
199
+ options[:requirements] = {
200
+ param.to_sym => options[:requirements]
201
+ } if options[:requirements].is_a?(Regexp)
202
+
203
+ Grape::Validations::ParamsScope.new(api: self) do
204
+ requires param, type: options[:type]
205
+ end if options.key?(:type)
189
206
  namespace(":#{param}", options, &block)
190
207
  end
191
208
 
@@ -71,7 +71,7 @@ module Grape
71
71
 
72
72
  # (see #unset_global_setting)
73
73
  def unset_namespace_setting(key)
74
- unset :namespace_setting, key
74
+ unset :namespace, key
75
75
  end
76
76
 
77
77
  # (see #global_setting)
@@ -94,6 +94,12 @@ module Grape
94
94
  get_or_set :namespace_stackable, key, value
95
95
  end
96
96
 
97
+ def namespace_stackable_with_hash(key)
98
+ settings = get_or_set :namespace_stackable, key, nil
99
+ return if settings.blank?
100
+ settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
101
+ end
102
+
97
103
  # (see #unset_global_setting)
98
104
  def unset_namespace_stackable(key)
99
105
  unset :namespace_stackable, key
@@ -106,7 +112,7 @@ module Grape
106
112
 
107
113
  # (see #unset_global_setting)
108
114
  def unset_api_class_setting(key)
109
- unset :api_class_setting, key
115
+ unset :api_class, key
110
116
  end
111
117
 
112
118
  # Fork our inheritable settings to a new instance, copied from our
@@ -5,12 +5,11 @@ module Grape
5
5
  # from inside a `get`, `post`, etc.
6
6
  class Endpoint
7
7
  include Grape::DSL::Settings
8
+ include Grape::DSL::InsideRoute
8
9
 
9
10
  attr_accessor :block, :source, :options
10
11
  attr_reader :env, :request, :headers, :params
11
12
 
12
- include Grape::DSL::InsideRoute
13
-
14
13
  class << self
15
14
  def new(*args, &block)
16
15
  if self == Endpoint
@@ -115,7 +114,7 @@ module Grape
115
114
  end
116
115
 
117
116
  def reset_routes!
118
- endpoints.map(&:reset_routes!) if endpoints
117
+ endpoints.each(&:reset_routes!) if endpoints
119
118
  @namespace = nil
120
119
  @routes = nil
121
120
  end
@@ -126,7 +125,7 @@ module Grape
126
125
  e.mount_in(route_set)
127
126
  end
128
127
  else
129
- @routes = nil
128
+ reset_routes!
130
129
 
131
130
  routes.each do |route|
132
131
  methods = [route.route_method]
@@ -243,20 +242,7 @@ module Grape
243
242
 
244
243
  run_filters before_validations, :before_validation
245
244
 
246
- # Retrieve validations from this namespace and all parent namespaces.
247
- validation_errors = []
248
-
249
- route_setting(:saved_validations).each do |validator|
250
- begin
251
- validator.validate!(params)
252
- rescue Grape::Exceptions::Validation => e
253
- validation_errors << e
254
- end
255
- end
256
-
257
- if validation_errors.any?
258
- fail Grape::Exceptions::ValidationErrors, errors: validation_errors, headers: header
259
- end
245
+ run_validators validations, request
260
246
 
261
247
  run_filters after_validations, :after_validation
262
248
 
@@ -276,14 +262,14 @@ module Grape
276
262
  b.use Rack::Head
277
263
  b.use Grape::Middleware::Error,
278
264
  format: namespace_inheritable(:format),
279
- content_types: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:content_types)),
265
+ content_types: namespace_stackable_with_hash(:content_types),
280
266
  default_status: namespace_inheritable(:default_error_status),
281
267
  rescue_all: namespace_inheritable(:rescue_all),
282
268
  default_error_formatter: namespace_inheritable(:default_error_formatter),
283
- error_formatters: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:error_formatters)),
284
- rescue_options: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:rescue_options)) || {},
285
- rescue_handlers: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:rescue_handlers)) || {},
286
- base_only_rescue_handlers: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:base_only_rescue_handlers)) || {},
269
+ error_formatters: namespace_stackable_with_hash(:error_formatters),
270
+ rescue_options: namespace_stackable_with_hash(:rescue_options) || {},
271
+ rescue_handlers: namespace_stackable_with_hash(:rescue_handlers) || {},
272
+ base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {},
287
273
  all_rescue_handler: namespace_inheritable(:all_rescue_handler)
288
274
 
289
275
  (namespace_stackable(:middleware) || []).each do |m|
@@ -307,9 +293,9 @@ module Grape
307
293
  b.use Grape::Middleware::Formatter,
308
294
  format: namespace_inheritable(:format),
309
295
  default_format: namespace_inheritable(:default_format) || :txt,
310
- content_types: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:content_types)),
311
- formatters: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:formatters)),
312
- parsers: Grape::DSL::Configuration.stacked_hash_to_hash(namespace_stackable(:parsers))
296
+ content_types: namespace_stackable_with_hash(:content_types),
297
+ formatters: namespace_stackable_with_hash(:formatters),
298
+ parsers: namespace_stackable_with_hash(:parsers)
313
299
 
314
300
  b.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
315
301
 
@@ -346,6 +332,20 @@ module Grape
346
332
  end
347
333
  end
348
334
 
335
+ def run_validators(validators, request)
336
+ validation_errors = []
337
+
338
+ validators.each do |validator|
339
+ begin
340
+ validator.validate(request)
341
+ rescue Grape::Exceptions::Validation => e
342
+ validation_errors << e
343
+ end
344
+ end
345
+
346
+ validation_errors.any? && fail(Grape::Exceptions::ValidationErrors, errors: validation_errors, headers: header)
347
+ end
348
+
349
349
  def run_filters(filters, type = :other)
350
350
  ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters: filters, type: type) do
351
351
  (filters || []).each do |filter|
@@ -371,5 +371,9 @@ module Grape
371
371
  def afters
372
372
  namespace_stackable(:afters) || []
373
373
  end
374
+
375
+ def validations
376
+ route_setting(:saved_validations) || []
377
+ end
374
378
  end
375
379
  end