grape 1.1.0 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -43
  3. data/LICENSE +1 -1
  4. data/README.md +394 -47
  5. data/UPGRADING.md +111 -0
  6. data/grape.gemspec +3 -1
  7. data/lib/grape.rb +98 -66
  8. data/lib/grape/api.rb +136 -175
  9. data/lib/grape/api/instance.rb +280 -0
  10. data/lib/grape/config.rb +32 -0
  11. data/lib/grape/dsl/callbacks.rb +20 -0
  12. data/lib/grape/dsl/desc.rb +39 -7
  13. data/lib/grape/dsl/inside_route.rb +12 -6
  14. data/lib/grape/dsl/middleware.rb +7 -0
  15. data/lib/grape/dsl/parameters.rb +9 -4
  16. data/lib/grape/dsl/routing.rb +5 -1
  17. data/lib/grape/dsl/validations.rb +4 -3
  18. data/lib/grape/eager_load.rb +18 -0
  19. data/lib/grape/endpoint.rb +42 -26
  20. data/lib/grape/error_formatter.rb +1 -1
  21. data/lib/grape/exceptions/base.rb +9 -1
  22. data/lib/grape/exceptions/invalid_response.rb +9 -0
  23. data/lib/grape/exceptions/validation_errors.rb +4 -2
  24. data/lib/grape/formatter.rb +1 -1
  25. data/lib/grape/locale/en.yml +2 -0
  26. data/lib/grape/middleware/auth/base.rb +2 -4
  27. data/lib/grape/middleware/base.rb +2 -0
  28. data/lib/grape/middleware/error.rb +9 -4
  29. data/lib/grape/middleware/helpers.rb +10 -0
  30. data/lib/grape/middleware/stack.rb +1 -1
  31. data/lib/grape/middleware/versioner/header.rb +4 -4
  32. data/lib/grape/parser.rb +1 -1
  33. data/lib/grape/request.rb +1 -1
  34. data/lib/grape/router/attribute_translator.rb +2 -0
  35. data/lib/grape/router/route.rb +2 -2
  36. data/lib/grape/util/base_inheritable.rb +34 -0
  37. data/lib/grape/util/endpoint_configuration.rb +6 -0
  38. data/lib/grape/util/inheritable_values.rb +5 -25
  39. data/lib/grape/util/lazy_block.rb +25 -0
  40. data/lib/grape/util/lazy_value.rb +95 -0
  41. data/lib/grape/util/reverse_stackable_values.rb +7 -36
  42. data/lib/grape/util/stackable_values.rb +19 -22
  43. data/lib/grape/validations/attributes_iterator.rb +5 -3
  44. data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
  45. data/lib/grape/validations/params_scope.rb +20 -14
  46. data/lib/grape/validations/single_attribute_iterator.rb +13 -0
  47. data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
  48. data/lib/grape/validations/types/file.rb +1 -1
  49. data/lib/grape/validations/validator_factory.rb +6 -11
  50. data/lib/grape/validations/validators/all_or_none.rb +6 -13
  51. data/lib/grape/validations/validators/as.rb +2 -3
  52. data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
  53. data/lib/grape/validations/validators/base.rb +11 -10
  54. data/lib/grape/validations/validators/coerce.rb +4 -0
  55. data/lib/grape/validations/validators/default.rb +1 -1
  56. data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
  57. data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
  58. data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
  59. data/lib/grape/validations/validators/same_as.rb +23 -0
  60. data/lib/grape/version.rb +1 -1
  61. data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
  62. data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
  63. data/spec/grape/api_remount_spec.rb +466 -0
  64. data/spec/grape/api_spec.rb +379 -1
  65. data/spec/grape/config_spec.rb +17 -0
  66. data/spec/grape/dsl/desc_spec.rb +40 -16
  67. data/spec/grape/dsl/middleware_spec.rb +8 -0
  68. data/spec/grape/dsl/routing_spec.rb +10 -0
  69. data/spec/grape/endpoint_spec.rb +40 -4
  70. data/spec/grape/exceptions/base_spec.rb +65 -0
  71. data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
  72. data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
  73. data/spec/grape/integration/rack_spec.rb +22 -6
  74. data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
  75. data/spec/grape/middleware/base_spec.rb +8 -0
  76. data/spec/grape/middleware/exception_spec.rb +1 -1
  77. data/spec/grape/middleware/formatter_spec.rb +15 -5
  78. data/spec/grape/middleware/versioner/header_spec.rb +6 -0
  79. data/spec/grape/named_api_spec.rb +19 -0
  80. data/spec/grape/request_spec.rb +24 -0
  81. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
  82. data/spec/grape/validations/params_scope_spec.rb +184 -8
  83. data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
  84. data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
  85. data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
  86. data/spec/grape/validations/validators/coerce_spec.rb +10 -2
  87. data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
  88. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
  89. data/spec/grape/validations/validators/same_as_spec.rb +63 -0
  90. data/spec/grape/validations_spec.rb +33 -21
  91. data/spec/spec_helper.rb +4 -1
  92. metadata +35 -23
  93. data/Appraisals +0 -32
  94. data/Dangerfile +0 -2
  95. data/Gemfile +0 -33
  96. data/Gemfile.lock +0 -231
  97. data/Guardfile +0 -10
  98. data/RELEASING.md +0 -111
  99. data/Rakefile +0 -25
  100. data/benchmark/simple.rb +0 -27
  101. data/benchmark/simple_with_type_coercer.rb +0 -22
  102. data/gemfiles/multi_json.gemfile +0 -35
  103. data/gemfiles/multi_xml.gemfile +0 -35
  104. data/gemfiles/rack_1.5.2.gemfile +0 -35
  105. data/gemfiles/rack_edge.gemfile +0 -35
  106. data/gemfiles/rails_3.gemfile +0 -36
  107. data/gemfiles/rails_4.gemfile +0 -35
  108. data/gemfiles/rails_5.gemfile +0 -35
  109. data/gemfiles/rails_edge.gemfile +0 -35
  110. data/pkg/grape-0.17.0.gem +0 -0
  111. data/pkg/grape-0.19.0.gem +0 -0
@@ -0,0 +1,280 @@
1
+ require 'grape/router'
2
+
3
+ module Grape
4
+ class API
5
+ # The API Instance class, is the engine behind Grape::API. Each class that inherits
6
+ # from this will represent a different API instance
7
+ class Instance
8
+ include Grape::DSL::API
9
+
10
+ class << self
11
+ attr_reader :instance
12
+ attr_reader :base
13
+ attr_accessor :configuration
14
+
15
+ def given(conditional_option, &block)
16
+ evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option && block_given?
17
+ end
18
+
19
+ def mounted(&block)
20
+ evaluate_as_instance_with_configuration(block, lazy: true)
21
+ end
22
+
23
+ def base=(grape_api)
24
+ @base = grape_api
25
+ grape_api.instances << self
26
+ end
27
+
28
+ def to_s
29
+ (base && base.to_s) || super
30
+ end
31
+
32
+ def base_instance?
33
+ self == base.base_instance
34
+ end
35
+
36
+ # A class-level lock to ensure the API is not compiled by multiple
37
+ # threads simultaneously within the same process.
38
+ LOCK = Mutex.new
39
+
40
+ # Clears all defined routes, endpoints, etc., on this API.
41
+ def reset!
42
+ reset_endpoints!
43
+ reset_routes!
44
+ reset_validations!
45
+ end
46
+
47
+ # Parses the API's definition and compiles it into an instance of
48
+ # Grape::API.
49
+ def compile
50
+ @instance ||= new
51
+ end
52
+
53
+ # Wipe the compiled API so we can recompile after changes were made.
54
+ def change!
55
+ @instance = nil
56
+ end
57
+
58
+ # This is the interface point between Rack and Grape; it accepts a request
59
+ # from Rack and ultimately returns an array of three values: the status,
60
+ # the headers, and the body. See [the rack specification]
61
+ # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
62
+ def call(env)
63
+ compile!
64
+ call!(env)
65
+ end
66
+
67
+ # A non-synchronized version of ::call.
68
+ def call!(env)
69
+ instance.call(env)
70
+ end
71
+
72
+ # (see #cascade?)
73
+ def cascade(value = nil)
74
+ if value.nil?
75
+ inheritable_setting.namespace_inheritable.keys.include?(:cascade) ? !namespace_inheritable(:cascade).nil? : true
76
+ else
77
+ namespace_inheritable(:cascade, value)
78
+ end
79
+ end
80
+
81
+ def compile!
82
+ return if instance
83
+ LOCK.synchronize { compile unless instance }
84
+ end
85
+
86
+ # see Grape::Router#recognize_path
87
+ def recognize_path(path)
88
+ compile!
89
+ instance.router.recognize_path(path)
90
+ end
91
+
92
+ protected
93
+
94
+ def prepare_routes
95
+ endpoints.map(&:routes).flatten
96
+ end
97
+
98
+ # Execute first the provided block, then each of the
99
+ # block passed in. Allows for simple 'before' setups
100
+ # of settings stack pushes.
101
+ def nest(*blocks, &block)
102
+ blocks.reject!(&:nil?)
103
+ if blocks.any?
104
+ evaluate_as_instance_with_configuration(block) if block_given?
105
+ blocks.each { |b| evaluate_as_instance_with_configuration(b) }
106
+ reset_validations!
107
+ else
108
+ instance_eval(&block)
109
+ end
110
+ end
111
+
112
+ def evaluate_as_instance_with_configuration(block, lazy: false)
113
+ lazy_block = Grape::Util::LazyBlock.new do |configuration|
114
+ value_for_configuration = configuration
115
+ if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
116
+ self.configuration = value_for_configuration.evaluate
117
+ end
118
+ response = instance_eval(&block)
119
+ self.configuration = value_for_configuration
120
+ response
121
+ end
122
+ if base_instance? && lazy
123
+ lazy_block
124
+ else
125
+ lazy_block.evaluate_from(configuration)
126
+ end
127
+ end
128
+
129
+ def inherited(subclass)
130
+ subclass.reset!
131
+ subclass.logger = logger.clone
132
+ end
133
+
134
+ def inherit_settings(other_settings)
135
+ top_level_setting.inherit_from other_settings.point_in_time_copy
136
+
137
+ # Propagate any inherited params down to our endpoints, and reset any
138
+ # compiled routes.
139
+ endpoints.each do |e|
140
+ e.inherit_settings(top_level_setting.namespace_stackable)
141
+ e.reset_routes!
142
+ end
143
+
144
+ reset_routes!
145
+ end
146
+ end
147
+
148
+ attr_reader :router
149
+
150
+ # Builds the routes from the defined endpoints, effectively compiling
151
+ # this API into a usable form.
152
+ def initialize
153
+ @router = Router.new
154
+ add_head_not_allowed_methods_and_options_methods
155
+ self.class.endpoints.each do |endpoint|
156
+ endpoint.mount_in(@router)
157
+ end
158
+
159
+ @router.compile!
160
+ @router.freeze
161
+ end
162
+
163
+ # Handle a request. See Rack documentation for what `env` is.
164
+ def call(env)
165
+ result = @router.call(env)
166
+ result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
167
+ result
168
+ end
169
+
170
+ # Some requests may return a HTTP 404 error if grape cannot find a matching
171
+ # route. In this case, Grape::Router adds a X-Cascade header to the response
172
+ # and sets it to 'pass', indicating to grape's parents they should keep
173
+ # looking for a matching route on other resources.
174
+ #
175
+ # In some applications (e.g. mounting grape on rails), one might need to trap
176
+ # errors from reaching upstream. This is effectivelly done by unsetting
177
+ # X-Cascade. Default :cascade is true.
178
+ def cascade?
179
+ return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
180
+ return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
181
+ true
182
+ end
183
+
184
+ reset!
185
+
186
+ private
187
+
188
+ # For every resource add a 'OPTIONS' route that returns an HTTP 204 response
189
+ # with a list of HTTP methods that can be called. Also add a route that
190
+ # will return an HTTP 405 response for any HTTP method that the resource
191
+ # cannot handle.
192
+ def add_head_not_allowed_methods_and_options_methods
193
+ routes_map = {}
194
+
195
+ self.class.endpoints.each do |endpoint|
196
+ routes = endpoint.routes
197
+ routes.each do |route|
198
+ # using the :any shorthand produces [nil] for route methods, substitute all manually
199
+ route_key = route.pattern.to_regexp
200
+ routes_map[route_key] ||= {}
201
+ route_settings = routes_map[route_key]
202
+ route_settings[:pattern] = route.pattern
203
+ route_settings[:requirements] = route.requirements
204
+ route_settings[:path] = route.origin
205
+ route_settings[:methods] ||= []
206
+ route_settings[:methods] << route.request_method
207
+ route_settings[:endpoint] = route.app
208
+
209
+ # using the :any shorthand produces [nil] for route methods, substitute all manually
210
+ route_settings[:methods] = %w[GET PUT POST DELETE PATCH HEAD OPTIONS] if route_settings[:methods].include?('*')
211
+ end
212
+ end
213
+
214
+ # The paths we collected are prepared (cf. Path#prepare), so they
215
+ # contain already versioning information when using path versioning.
216
+ # Disable versioning so adding a route won't prepend versioning
217
+ # informations again.
218
+ without_root_prefix do
219
+ without_versioning do
220
+ routes_map.each_value do |config|
221
+ methods = config[:methods]
222
+ allowed_methods = methods.dup
223
+
224
+ unless self.class.namespace_inheritable(:do_not_route_head)
225
+ allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
226
+ end
227
+
228
+ allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')
229
+
230
+ unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
231
+ config[:endpoint].options[:options_route_enabled] = true
232
+ end
233
+
234
+ attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
235
+ generate_not_allowed_method(config[:pattern], attributes)
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ # Generate a route that returns an HTTP 405 response for a user defined
242
+ # path on methods not specified
243
+ def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
244
+ not_allowed_methods = %w[GET PUT POST DELETE PATCH HEAD] - allowed_methods
245
+ not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
246
+
247
+ return if not_allowed_methods.empty?
248
+
249
+ @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
250
+ end
251
+
252
+ # Allows definition of endpoints that ignore the versioning configuration
253
+ # used by the rest of your API.
254
+ def without_versioning(&_block)
255
+ old_version = self.class.namespace_inheritable(:version)
256
+ old_version_options = self.class.namespace_inheritable(:version_options)
257
+
258
+ self.class.namespace_inheritable_to_nil(:version)
259
+ self.class.namespace_inheritable_to_nil(:version_options)
260
+
261
+ yield
262
+
263
+ self.class.namespace_inheritable(:version, old_version)
264
+ self.class.namespace_inheritable(:version_options, old_version_options)
265
+ end
266
+
267
+ # Allows definition of endpoints that ignore the root prefix used by the
268
+ # rest of your API.
269
+ def without_root_prefix(&_block)
270
+ old_prefix = self.class.namespace_inheritable(:root_prefix)
271
+
272
+ self.class.namespace_inheritable_to_nil(:root_prefix)
273
+
274
+ yield
275
+
276
+ self.class.namespace_inheritable(:root_prefix, old_prefix)
277
+ end
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,32 @@
1
+ module Grape
2
+ module Config
3
+ class Configuration
4
+ ATTRIBUTES = %i[
5
+ param_builder
6
+ ].freeze
7
+
8
+ attr_accessor(*ATTRIBUTES)
9
+
10
+ def initialize
11
+ reset
12
+ end
13
+
14
+ def reset
15
+ self.param_builder = Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
16
+ end
17
+ end
18
+
19
+ def self.extended(base)
20
+ def base.configure
21
+ block_given? ? yield(config) : config
22
+ end
23
+
24
+ def base.config
25
+ @configuration ||= Grape::Config::Configuration.new
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ Grape.extend Grape::Config
32
+ Grape.config.reset
@@ -43,6 +43,26 @@ module Grape
43
43
  def after(&block)
44
44
  namespace_stackable(:afters, block)
45
45
  end
46
+
47
+ # Allows you to specify a something that will always be executed after a call
48
+ # API call. Unlike the `after` block, this code will run even on
49
+ # unsuccesful requests.
50
+ # @example
51
+ # class ExampleAPI < Grape::API
52
+ # before do
53
+ # ApiLogger.start
54
+ # end
55
+ # finally do
56
+ # ApiLogger.close
57
+ # end
58
+ # end
59
+ #
60
+ # This will make sure that the ApiLogger is opened and close around every
61
+ # request
62
+ # @param ensured_block [Proc] The block to be executed after every api_call
63
+ def finally(&block)
64
+ namespace_stackable(:finallies, block)
65
+ end
46
66
  end
47
67
  end
48
68
  end
@@ -9,6 +9,7 @@ module Grape
9
9
  # @param options [Hash] other properties you can set to describe the
10
10
  # endpoint or namespace. Optional.
11
11
  # @option options :detail [String] additional detail about this endpoint
12
+ # @option options :summary [String] summary for this endpoint
12
13
  # @option options :params [Hash] param types and info. normally, you set
13
14
  # these via the `params` dsl method.
14
15
  # @option options :entity [Grape::Entity] the entity returned upon a
@@ -16,7 +17,16 @@ module Grape
16
17
  # @option options :http_codes [Array[Array]] possible HTTP codes this
17
18
  # endpoint may return, with their meanings, in a 2d array
18
19
  # @option options :named [String] a specific name to help find this route
20
+ # @option options :body_name [String] override the autogenerated body name param
19
21
  # @option options :headers [Hash] HTTP headers this method can accept
22
+ # @option options :hidden [Boolean] hide the endpoint or not
23
+ # @option options :deprecated [Boolean] deprecate the endpoint or not
24
+ # @option options :is_array [Boolean] response entity is array or not
25
+ # @option options :nickname [String] nickname of the endpoint
26
+ # @option options :produces [Array[String]] a list of MIME types the endpoint produce
27
+ # @option options :consumes [Array[String]] a list of MIME types the endpoint consume
28
+ # @option options :security [Array[Hash]] a list of security schemes
29
+ # @option options :tags [Array[String]] a list of tags
20
30
  # @yield a block yielding an instance context with methods mapping to
21
31
  # each of the above, except that :entity is also aliased as #success
22
32
  # and :http_codes is aliased as #failure.
@@ -39,7 +49,17 @@ module Grape
39
49
  #
40
50
  def desc(description, options = {}, &config_block)
41
51
  if block_given?
42
- config_class = desc_container
52
+ endpoint_configuration = if defined?(configuration)
53
+ # When the instance is mounted - the configuration is executed on mount time
54
+ if configuration.respond_to?(:evaluate)
55
+ configuration.evaluate
56
+ # Within `given` or `mounted blocks` the configuration is already evaluated
57
+ elsif configuration.is_a?(Hash)
58
+ configuration
59
+ end
60
+ end
61
+ endpoint_configuration ||= {}
62
+ config_class = desc_container(endpoint_configuration)
43
63
 
44
64
  config_class.configure do
45
65
  description description
@@ -59,13 +79,12 @@ module Grape
59
79
  end
60
80
 
61
81
  def description_field(field, value = nil)
82
+ description = route_setting(:description)
62
83
  if value
63
- description = route_setting(:description)
64
84
  description ||= route_setting(:description, {})
65
85
  description[field] = value
66
- else
67
- description = route_setting(:description)
68
- description[field] if description
86
+ elsif description
87
+ description[field]
69
88
  end
70
89
  end
71
90
 
@@ -75,17 +94,30 @@ module Grape
75
94
  end
76
95
 
77
96
  # Returns an object which configures itself via an instance-context DSL.
78
- def desc_container
97
+ def desc_container(endpoint_configuration)
79
98
  Module.new do
80
99
  include Grape::Util::StrictHashConfiguration.module(
100
+ :summary,
81
101
  :description,
82
102
  :detail,
83
103
  :params,
84
104
  :entity,
85
105
  :http_codes,
86
106
  :named,
87
- :headers
107
+ :body_name,
108
+ :headers,
109
+ :hidden,
110
+ :deprecated,
111
+ :is_array,
112
+ :nickname,
113
+ :produces,
114
+ :consumes,
115
+ :security,
116
+ :tags
88
117
  )
118
+ config_context.define_singleton_method(:configuration) do
119
+ endpoint_configuration
120
+ end
89
121
 
90
122
  def config_context.success(*args)
91
123
  entity(*args)
@@ -61,13 +61,13 @@ module Grape
61
61
  else
62
62
  # If it is not a Hash then it does not have children.
63
63
  # Find its value or set it to nil.
64
- has_alias = route_setting(:aliased_params) && route_setting(:aliased_params).find { |current| current[declared_param] }
65
- param_alias = has_alias[declared_param] if has_alias
64
+ has_renaming = route_setting(:renamed_params) && route_setting(:renamed_params).find { |current| current[declared_param] }
65
+ param_renaming = has_renaming[declared_param] if has_renaming
66
66
 
67
- next unless options[:include_missing] || passed_params.key?(declared_param) || (param_alias && passed_params.key?(param_alias))
67
+ next unless options[:include_missing] || passed_params.key?(declared_param) || (param_renaming && passed_params.key?(param_renaming))
68
68
 
69
- if param_alias
70
- memo[optioned_param_key(param_alias, options)] = passed_params[param_alias]
69
+ if param_renaming
70
+ memo[optioned_param_key(param_renaming, options)] = passed_params[param_renaming]
71
71
  else
72
72
  memo[optioned_param_key(declared_param, options)] = passed_params[declared_param]
73
73
  end
@@ -129,13 +129,19 @@ module Grape
129
129
  env[Grape::Env::API_VERSION]
130
130
  end
131
131
 
132
+ def configuration
133
+ options[:for].configuration.evaluate
134
+ end
135
+
132
136
  # End the request and display an error to the
133
137
  # end user with the specified message.
134
138
  #
135
139
  # @param message [String] The message to display.
136
140
  # @param status [Integer] the HTTP Status Code. Defaults to default_error_status, 500 if not set.
137
- def error!(message, status = nil, headers = nil)
141
+ # @param additional_headers [Hash] Addtional headers for the response.
142
+ def error!(message, status = nil, additional_headers = nil)
138
143
  self.status(status || namespace_inheritable(:default_error_status))
144
+ headers = additional_headers.present? ? header.merge(additional_headers) : header
139
145
  throw :error, message: message, status: self.status, headers: headers
140
146
  end
141
147