grape 1.4.0 → 1.5.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 (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