grape 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -3
  3. data/README.md +56 -8
  4. data/UPGRADING.md +43 -4
  5. data/lib/grape/api.rb +2 -2
  6. data/lib/grape/dsl/helpers.rb +1 -0
  7. data/lib/grape/dsl/inside_route.rb +26 -38
  8. data/lib/grape/dsl/routing.rb +2 -4
  9. data/lib/grape/middleware/base.rb +2 -1
  10. data/lib/grape/middleware/error.rb +10 -12
  11. data/lib/grape/middleware/stack.rb +17 -4
  12. data/lib/grape/request.rb +1 -1
  13. data/lib/grape/router.rb +1 -1
  14. data/lib/grape/router/attribute_translator.rb +2 -2
  15. data/lib/grape/util/base_inheritable.rb +2 -2
  16. data/lib/grape/util/lazy_value.rb +1 -0
  17. data/lib/grape/validations/params_scope.rb +2 -1
  18. data/lib/grape/validations/types/custom_type_coercer.rb +13 -1
  19. data/lib/grape/validations/validators/as.rb +1 -1
  20. data/lib/grape/validations/validators/base.rb +2 -4
  21. data/lib/grape/validations/validators/default.rb +3 -4
  22. data/lib/grape/validations/validators/except_values.rb +1 -1
  23. data/lib/grape/validations/validators/values.rb +1 -1
  24. data/lib/grape/version.rb +1 -1
  25. data/spec/grape/api_spec.rb +10 -0
  26. data/spec/grape/dsl/inside_route_spec.rb +7 -0
  27. data/spec/grape/endpoint/declared_spec.rb +590 -0
  28. data/spec/grape/endpoint_spec.rb +0 -534
  29. data/spec/grape/entity_spec.rb +6 -0
  30. data/spec/grape/middleware/error_spec.rb +1 -1
  31. data/spec/grape/middleware/stack_spec.rb +3 -1
  32. data/spec/grape/validations/params_scope_spec.rb +26 -0
  33. data/spec/grape/validations/validators/coerce_spec.rb +24 -0
  34. data/spec/grape/validations/validators/default_spec.rb +49 -0
  35. data/spec/grape/validations/validators/except_values_spec.rb +1 -0
  36. data/spec/spec_helper.rb +0 -10
  37. data/spec/support/chunks.rb +14 -0
  38. data/spec/support/versioned_helpers.rb +3 -5
  39. metadata +9 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 342f290903dcc993f0e60d0d15bee151f3425abf2b68a191da459d9ac47ac943
4
- data.tar.gz: 5b03c2c50e3b485787fd9d7ae64130b7cfe56ca9a9128d51133e46e58562d151
3
+ metadata.gz: f34ddc6f9f11346067b19a6d7f8e333e433a14c1866e68714815a9e8a457487a
4
+ data.tar.gz: c95b6956bcb2e5f2e61035ec4af7ee3dcaaa53f7743bde69e7363c9a24edc317
5
5
  SHA512:
6
- metadata.gz: c6f78f911d63d73b8cd2422b8918d53fadfd963e4b212e555f9c8fac8d1dfe801ca2e29a9e1ca236e41a82c1b75a0e503062d5a87430c1821817fae83cfd8ffd
7
- data.tar.gz: c856baf9f14a2eebb371f69bd35f6b7a555113e92eb9e35384901df09ac86dfcea691753ef9990123e54a335a0b9dca66b97170fb27a377cf0bb3f4f6e5233ae
6
+ metadata.gz: fef19eceff9814554a70cc0fa04d24e2cdbbba91af8f663a07a3278694c4cda2664b521d5390a57f8f1ccf7dc41dff85d602423d47aa27c87ca7d73b9d6e64ba
7
+ data.tar.gz: f1b2f404754216ba613e0250a6788ce9bbd02bd0560f2981c1847ca917398ede0f429ae03cdaf31bd3447e20248310f4ba7837511fe811ceb4ddee3b7c59df23
@@ -1,3 +1,19 @@
1
+ ### 1.5.0 (2020/10/05)
2
+
3
+ #### Fixes
4
+
5
+ * [#2104](https://github.com/ruby-grape/grape/pull/2104): Fix Ruby 2.7 keyword deprecation warning - [@stanhu](https://github.com/stanhu).
6
+ * [#2103](https://github.com/ruby-grape/grape/pull/2103): Ensure complete declared params structure is present - [@tlconnor](https://github.com/tlconnor).
7
+ * [#2099](https://github.com/ruby-grape/grape/pull/2099): Added truffleruby to Travis-CI - [@gogainda](https://github.com/gogainda).
8
+ * [#2089](https://github.com/ruby-grape/grape/pull/2089): Specify order of mounting Grape with Rack::Cascade in README - [@jonmchan](https://github.com/jonmchan).
9
+ * [#2083](https://github.com/ruby-grape/grape/pull/2083): Set `Cache-Control` header only for streamed responses - [@stanhu](https://github.com/stanhu).
10
+ * [#2092](https://github.com/ruby-grape/grape/pull/2092): Correct an example params in Include Missing doc - [@huyvohcmc](https://github.com/huyvohcmc).
11
+ * [#2091](https://github.com/ruby-grape/grape/pull/2091): Fix ruby 2.7 keyword deprecations - [@dim](https://github.com/dim).
12
+ * [#2097](https://github.com/ruby-grape/grape/pull/2097): Skip to set default value unless `meets_dependency?` - [@wanabe](https://github.com/wanabe).
13
+ * [#2096](https://github.com/ruby-grape/grape/pull/2096): Fix redundant dependency check - [@braktar](https://github.com/braktar).
14
+ * [#2096](https://github.com/ruby-grape/grape/pull/2098): Fix nested coercion - [@braktar](https://github.com/braktar).
15
+ * [#2102](https://github.com/ruby-grape/grape/pull/2102): Fix retaining setup blocks when remounting APIs - [@jylamont](https://github.com/jylamont).
16
+
1
17
  ### 1.4.0 (2020/07/10)
2
18
 
3
19
  #### Features
@@ -6,14 +22,14 @@
6
22
  * [#2060](https://github.com/ruby-grape/grape/pull/2060): Drop support for Ruby 2.4 - [@dblock](https://github.com/dblock).
7
23
  * [#2060](https://github.com/ruby-grape/grape/pull/2060): Upgraded Rubocop to 0.84.0 - [@dblock](https://github.com/dblock).
8
24
  * [#2077](https://github.com/ruby-grape/grape/pull/2077): Simplify logic for defining declared params - [@dnesteryuk](https://github.com/dnesteryuk).
9
- * [#2084](https://github.com/ruby-grape/grape/pull/2084): Fix memory leak in path normalization - [@fcheung](https://github.com/fcheung).
25
+ * [#2076](https://github.com/ruby-grape/grape/pull/2076): Make route information available for hooks when the automatically generated endpoints are invoked - [@anakinj](https://github.com/anakinj).
10
26
 
11
27
  #### Fixes
12
28
 
13
- * [#2067](https://github.com/ruby-grape/grape/pull/2067): Coerce empty string to nil for all primitive types except String - [@petekinnecom](https://github.com/petekinnecom).
29
+ * [#2067](https://github.com/ruby-grape/grape/pull/2067): Coerce empty String to `nil` for all primitive types except `String` - [@petekinnecom](https://github.com/petekinnecom).
14
30
  * [#2064](https://github.com/ruby-grape/grape/pull/2064): Fix Ruby 2.7 deprecation warning in `Grape::Middleware::Base#initialize` - [@skarger](https://github.com/skarger).
15
31
  * [#2072](https://github.com/ruby-grape/grape/pull/2072): Fix `Grape.eager_load!` and `compile!` - [@stanhu](https://github.com/stanhu).
16
- * [#2076](https://github.com/ruby-grape/grape/pull/2076): Make route information available for hooks when the automatically generated endpoints are invoked - [@anakinj](https://github.com/anakinj).
32
+ * [#2084](https://github.com/ruby-grape/grape/pull/2084): Fix memory leak in path normalization - [@fcheung](https://github.com/fcheung).
17
33
 
18
34
  ### 1.3.3 (2020/05/23)
19
35
 
data/README.md CHANGED
@@ -156,7 +156,7 @@ content negotiation, versioning and much more.
156
156
 
157
157
  ## Stable Release
158
158
 
159
- You're reading the documentation for the stable release of Grape, 1.4.0.
159
+ You're reading the documentation for the stable release of Grape, 1.5.0.
160
160
  Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
161
161
 
162
162
  ## Project Resources
@@ -349,9 +349,12 @@ class Web < Sinatra::Base
349
349
  end
350
350
 
351
351
  use Rack::Session::Cookie
352
- run Rack::Cascade.new [API, Web]
352
+ run Rack::Cascade.new [Web, API]
353
353
  ```
354
354
 
355
+ Note that order of loading apps using `Rack::Cascade` matters. The grape application must be last if you want to raise custom 404 errors from grape (such as `error!('Not Found',404)`). If the grape application is not last and returns 404 or 405 response, [cascade utilizes that as a signal to try the next app](https://www.rubydoc.info/gems/rack/Rack/Cascade). This may lead to undesirable behavior showing the [wrong 404 page from the wrong app](https://github.com/ruby-grape/grape/issues/1515).
356
+
357
+
355
358
  ### Rails
356
359
 
357
360
  Place API files into `app/api`. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for `Twitter::API` should be `app/api/twitter/api.rb`.
@@ -783,7 +786,12 @@ Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape
783
786
 
784
787
  ### Declared
785
788
 
786
- Grape allows you to access only the parameters that have been declared by your `params` block. It filters out the params that have been passed, but are not allowed. Consider the following API endpoint:
789
+ Grape allows you to access only the parameters that have been declared by your `params` block. It will:
790
+
791
+ * Filter out the params that have been passed, but are not allowed.
792
+ * Include any optional params that are declared but not passed.
793
+
794
+ Consider the following API endpoint:
787
795
 
788
796
  ````ruby
789
797
  format :json
@@ -816,9 +824,9 @@ Once we add parameters requirements, grape will start returning only the declare
816
824
  format :json
817
825
 
818
826
  params do
819
- requires :user, type: Hash do
820
- requires :first_name, type: String
821
- requires :last_name, type: String
827
+ optional :user, type: Hash do
828
+ optional :first_name, type: String
829
+ optional :last_name, type: String
822
830
  end
823
831
  end
824
832
 
@@ -846,6 +854,44 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
846
854
  }
847
855
  ````
848
856
 
857
+ Missing params that are declared as type `Hash` or `Array` will be included.
858
+
859
+ ````ruby
860
+ format :json
861
+
862
+ params do
863
+ optional :user, type: Hash do
864
+ optional :first_name, type: String
865
+ optional :last_name, type: String
866
+ end
867
+ optional :widgets, type: Array
868
+ end
869
+
870
+ post 'users/signup' do
871
+ { 'declared_params' => declared(params) }
872
+ end
873
+ ````
874
+
875
+ **Request**
876
+
877
+ ````bash
878
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{}'
879
+ ````
880
+
881
+ **Response**
882
+
883
+ ````json
884
+ {
885
+ "declared_params": {
886
+ "user": {
887
+ "first_name": null,
888
+ "last_name": null
889
+ },
890
+ "widgets": []
891
+ }
892
+ }
893
+ ````
894
+
849
895
  The returned hash is an `ActiveSupport::HashWithIndifferentAccess`.
850
896
 
851
897
  The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.
@@ -904,8 +950,10 @@ By default `declared(params)` includes parameters that have `nil` values. If you
904
950
  format :json
905
951
 
906
952
  params do
907
- requires :first_name, type: String
908
- optional :last_name, type: String
953
+ requires :user, type: Hash do
954
+ requires :first_name, type: String
955
+ optional :last_name, type: String
956
+ end
909
957
  end
910
958
 
911
959
  post 'users/signup' do
@@ -1,6 +1,45 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 1.5.0
5
+
6
+ Prior to 1.3.3, the `declared` helper would always return the complete params structure if `include_missing=true` was set. In 1.3.3 a regression was introduced such that a missing Hash with or without nested parameters would always resolve to `{}`.
7
+
8
+ In 1.5.0 this behavior is reverted, so the whole params structure will always be available via `declared`, regardless of whether any params are passed.
9
+
10
+ The following rules now apply to the `declared` helper when params are missing and `include_missing=true`:
11
+
12
+ * Hash params with children will resolve to a Hash with keys for each declared child.
13
+ * Hash params with no children will resolve to `{}`.
14
+ * Set params will resolve to `Set.new`.
15
+ * Array params will resolve to `[]`.
16
+ * All other params will resolve to `nil`.
17
+
18
+ #### Example
19
+
20
+ ```ruby
21
+ class Api < Grape::API
22
+ params do
23
+ optional :outer, type: Hash do
24
+ optional :inner, type: Hash do
25
+ optional :value, type: String
26
+ end
27
+ end
28
+ end
29
+ get 'example' do
30
+ declared(params, include_missing: true)
31
+ end
32
+ end
33
+ ```
34
+
35
+ ```
36
+ get '/example'
37
+ # 1.3.3 = {}
38
+ # 1.5.0 = {outer: {inner: {value:null}}}
39
+ ```
40
+
41
+ For more information see [#2103](https://github.com/ruby-grape/grape/pull/2103).
42
+
4
43
  ### Upgrading to >= 1.4.0
5
44
 
6
45
  #### Reworking stream and file and un-deprecating stream like-objects
@@ -28,17 +67,17 @@ class API < Grape::API
28
67
  end
29
68
  ```
30
69
 
31
- Or use `stream` to stream other kinds of content. In the following example a streamer class
70
+ Or use `stream` to stream other kinds of content. In the following example a streamer class
32
71
  streams paginated data from a database.
33
72
 
34
73
  ```ruby
35
- class MyObject
74
+ class MyObject
36
75
  attr_accessor :result
37
76
 
38
77
  def initialize(query)
39
78
  @result = query
40
79
  end
41
-
80
+
42
81
  def each
43
82
  yield '['
44
83
  # Do paginated DB fetches and return each page formatted
@@ -47,7 +86,7 @@ class MyObject
47
86
  yield process_records(records, first)
48
87
  first = false
49
88
  end
50
- yield ']'
89
+ yield ']'
51
90
  end
52
91
 
53
92
  def process_records(records, first)
@@ -30,7 +30,7 @@ module Grape
30
30
  # an instance that will be used to create the set up but will not be mounted
31
31
  def initial_setup(base_instance_parent)
32
32
  @instances = []
33
- @setup = []
33
+ @setup = Set.new
34
34
  @base_parent = base_instance_parent
35
35
  @base_instance = mount_instance
36
36
  end
@@ -90,7 +90,7 @@ module Grape
90
90
  # For instance, a descripcion could be done using: `desc configuration[:description]` if it may vary
91
91
  # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
92
92
  # too much, you may actually want to provide a new API rather than remount it.
93
- def mount_instance(opts = {})
93
+ def mount_instance(**opts)
94
94
  instance = Class.new(@base_parent)
95
95
  instance.configuration = Grape::Util::EndpointConfiguration.new(opts[:configuration] || {})
96
96
  instance.base = self
@@ -81,6 +81,7 @@ module Grape
81
81
  # to provide some API-specific functionality.
82
82
  module BaseHelper
83
83
  attr_accessor :api
84
+
84
85
  def params(name, &block)
85
86
  @named_params ||= {}
86
87
  @named_params[name] = block
@@ -58,7 +58,7 @@ module Grape
58
58
  passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
59
59
  memo_key = optioned_param_key(declared_parent_param, options)
60
60
 
61
- memo[memo_key] = handle_passed_param(passed_children_params, params_nested_path_dup) do
61
+ memo[memo_key] = handle_passed_param(params_nested_path_dup, passed_children_params.any?) do
62
62
  declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
63
63
  end
64
64
  end
@@ -70,57 +70,43 @@ module Grape
70
70
 
71
71
  next unless options[:include_missing] || passed_params.key?(declared_param) || (param_renaming && passed_params.key?(param_renaming))
72
72
 
73
- if param_renaming
74
- memo[optioned_param_key(param_renaming, options)] = passed_params[param_renaming]
75
- else
76
- memo[optioned_param_key(declared_param, options)] = passed_params[declared_param]
73
+ memo_key = optioned_param_key(param_renaming || declared_param, options)
74
+ passed_param = passed_params[param_renaming || declared_param]
75
+
76
+ params_nested_path_dup = params_nested_path.dup
77
+ params_nested_path_dup << declared_param.to_s
78
+ memo[memo_key] = passed_param || handle_passed_param(params_nested_path_dup) do
79
+ passed_param
77
80
  end
78
81
  end
79
82
  end
80
83
  end
81
84
 
82
- def handle_passed_param(passed_children_params, params_nested_path, &_block)
83
- if should_be_empty_hash?(passed_children_params, params_nested_path)
85
+ def handle_passed_param(params_nested_path, has_passed_children = false, &_block)
86
+ return yield if has_passed_children
87
+
88
+ key = params_nested_path[0]
89
+ key += '[' + params_nested_path[1..-1].join('][') + ']' if params_nested_path.size > 1
90
+
91
+ route_options_params = options[:route_options][:params] || {}
92
+ type = route_options_params.dig(key, :type)
93
+ has_children = route_options_params.keys.any? { |k| k != key && k.start_with?(key) }
94
+
95
+ if type == 'Hash' && !has_children
84
96
  {}
85
- elsif should_be_empty_array?(passed_children_params, params_nested_path)
97
+ elsif type == 'Array' || type&.start_with?('[')
86
98
  []
99
+ elsif type == 'Set' || type&.start_with?('#<Set')
100
+ Set.new
87
101
  else
88
102
  yield
89
103
  end
90
104
  end
91
105
 
92
- def should_be_empty_array?(passed_children_params, params_nested_path)
93
- passed_children_params.empty? && declared_param_is_array?(params_nested_path)
94
- end
95
-
96
- def declared_param_is_array?(params_nested_path)
97
- key = route_options_params_key(params_nested_path)
98
- route_options_params[key] && route_options_params[key][:type] == 'Array'
99
- end
100
-
101
- def should_be_empty_hash?(passed_children_params, params_nested_path)
102
- passed_children_params.empty? && declared_param_is_hash?(params_nested_path)
103
- end
104
-
105
- def declared_param_is_hash?(params_nested_path)
106
- key = route_options_params_key(params_nested_path)
107
- route_options_params[key] && route_options_params[key][:type] == 'Hash'
108
- end
109
-
110
- def route_options_params
111
- options[:route_options][:params] || {}
112
- end
113
-
114
106
  def optioned_param_key(declared_param, options)
115
107
  options[:stringify] ? declared_param.to_s : declared_param.to_sym
116
108
  end
117
109
 
118
- def route_options_params_key(params_nested_path)
119
- key = params_nested_path[0]
120
- key += '[' + params_nested_path[1..-1].join('][') + ']' if params_nested_path.size > 1
121
- key
122
- end
123
-
124
110
  def optioned_declared_params(**options)
125
111
  declared_params = if options[:include_parent_namespaces]
126
112
  # Declared params including parent namespaces
@@ -328,6 +314,8 @@ module Grape
328
314
  # * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/chunked.rb
329
315
  # * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/etag.rb
330
316
  def stream(value = nil)
317
+ return if value.nil? && @stream.nil?
318
+
331
319
  header 'Content-Length', nil
332
320
  header 'Transfer-Encoding', nil
333
321
  header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front)
@@ -339,7 +327,7 @@ module Grape
339
327
  elsif !value.is_a?(NilClass)
340
328
  raise ArgumentError, 'Stream object must respond to :each.'
341
329
  else
342
- instance_variable_defined?(:@stream) ? @stream : nil
330
+ @stream
343
331
  end
344
332
  end
345
333
 
@@ -433,7 +421,7 @@ module Grape
433
421
  def entity_representation_for(entity_class, object, options)
434
422
  embeds = { env: env }
435
423
  embeds[:version] = env[Grape::Env::API_VERSION] if env[Grape::Env::API_VERSION]
436
- entity_class.represent(object, embeds.merge(options))
424
+ entity_class.represent(object, **embeds.merge(options))
437
425
  end
438
426
  end
439
427
  end
@@ -79,7 +79,7 @@ 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)
@@ -170,9 +170,7 @@ module Grape
170
170
  previous_namespace_description = @namespace_description
171
171
  @namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
172
172
  nest(block) do
173
- if space
174
- namespace_stackable(:namespace, Namespace.new(space, **options))
175
- end
173
+ namespace_stackable(:namespace, Namespace.new(space, **options)) if space
176
174
  end
177
175
  @namespace_description = previous_namespace_description
178
176
  end
@@ -8,13 +8,14 @@ module Grape
8
8
  include Helpers
9
9
 
10
10
  attr_reader :app, :env, :options
11
+
11
12
  TEXT_HTML = 'text/html'
12
13
 
13
14
  include Grape::DSL::Headers
14
15
 
15
16
  # @param [Rack Application] app The standard argument for a Rack middleware.
16
17
  # @param [Hash] options A hash of options, simply stored for use by subclasses.
17
- def initialize(app, options = {})
18
+ def initialize(app, **options)
18
19
  @app = app
19
20
  @options = default_options.merge(options)
20
21
  @app_response = nil
@@ -19,11 +19,11 @@ module Grape
19
19
  rescue_subclasses: true, # rescue subclasses of exceptions listed
20
20
  rescue_options: {
21
21
  backtrace: false, # true to display backtrace, true to let Grape handle Grape::Exceptions
22
- original_exception: false, # true to display exception
22
+ original_exception: false # true to display exception
23
23
  },
24
24
  rescue_handlers: {}, # rescue handler blocks
25
25
  base_only_rescue_handlers: {}, # rescue handler blocks rescuing only the base class
26
- all_rescue_handler: nil, # rescue handler block to rescue from all exceptions
26
+ all_rescue_handler: nil # rescue handler block to rescue from all exceptions
27
27
  }
28
28
  end
29
29
 
@@ -38,15 +38,15 @@ module Grape
38
38
  error_response(catch(:error) do
39
39
  return @app.call(@env)
40
40
  end)
41
- rescue Exception => error # rubocop:disable Lint/RescueException
41
+ rescue Exception => e # rubocop:disable Lint/RescueException
42
42
  handler =
43
- rescue_handler_for_base_only_class(error.class) ||
44
- rescue_handler_for_class_or_its_ancestor(error.class) ||
45
- rescue_handler_for_grape_exception(error.class) ||
46
- rescue_handler_for_any_class(error.class) ||
43
+ rescue_handler_for_base_only_class(e.class) ||
44
+ rescue_handler_for_class_or_its_ancestor(e.class) ||
45
+ rescue_handler_for_grape_exception(e.class) ||
46
+ rescue_handler_for_any_class(e.class) ||
47
47
  raise
48
48
 
49
- run_rescue_handler(handler, error)
49
+ run_rescue_handler(handler, e)
50
50
  end
51
51
  end
52
52
 
@@ -65,15 +65,13 @@ module Grape
65
65
  message = error[:message] || options[:default_message]
66
66
  headers = { Grape::Http::Headers::CONTENT_TYPE => content_type }
67
67
  headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
68
- backtrace = error[:backtrace] || error[:original_exception] && error[:original_exception].backtrace || []
68
+ backtrace = error[:backtrace] || error[:original_exception]&.backtrace || []
69
69
  original_exception = error.is_a?(Exception) ? error : error[:original_exception] || nil
70
70
  rack_response(format_message(message, backtrace, original_exception), status, headers)
71
71
  end
72
72
 
73
73
  def rack_response(message, status = options[:default_status], headers = { Grape::Http::Headers::CONTENT_TYPE => content_type })
74
- if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
75
- message = ERB::Util.html_escape(message)
76
- end
74
+ message = ERB::Util.html_escape(message) if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
77
75
  Rack::Response.new([message], status, headers)
78
76
  end
79
77