grape 2.0.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -1
  3. data/README.md +362 -316
  4. data/UPGRADING.md +197 -7
  5. data/grape.gemspec +5 -6
  6. data/lib/grape/api/instance.rb +13 -10
  7. data/lib/grape/api.rb +17 -8
  8. data/lib/grape/content_types.rb +0 -2
  9. data/lib/grape/cookies.rb +2 -1
  10. data/lib/grape/dry_types.rb +0 -2
  11. data/lib/grape/dsl/desc.rb +22 -20
  12. data/lib/grape/dsl/headers.rb +1 -1
  13. data/lib/grape/dsl/inside_route.rb +42 -13
  14. data/lib/grape/dsl/parameters.rb +4 -3
  15. data/lib/grape/dsl/routing.rb +20 -4
  16. data/lib/grape/dsl/validations.rb +13 -0
  17. data/lib/grape/endpoint.rb +12 -15
  18. data/lib/grape/{util/env.rb → env.rb} +0 -5
  19. data/lib/grape/error_formatter/txt.rb +11 -10
  20. data/lib/grape/exceptions/base.rb +3 -3
  21. data/lib/grape/exceptions/validation.rb +0 -2
  22. data/lib/grape/exceptions/validation_array_errors.rb +1 -0
  23. data/lib/grape/exceptions/validation_errors.rb +1 -3
  24. data/lib/grape/extensions/hash.rb +5 -1
  25. data/lib/grape/http/headers.rb +18 -34
  26. data/lib/grape/{util/json.rb → json.rb} +1 -3
  27. data/lib/grape/locale/en.yml +3 -0
  28. data/lib/grape/middleware/auth/base.rb +0 -2
  29. data/lib/grape/middleware/auth/dsl.rb +0 -2
  30. data/lib/grape/middleware/base.rb +0 -2
  31. data/lib/grape/middleware/error.rb +55 -50
  32. data/lib/grape/middleware/formatter.rb +16 -13
  33. data/lib/grape/middleware/globals.rb +1 -3
  34. data/lib/grape/middleware/stack.rb +2 -3
  35. data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
  36. data/lib/grape/middleware/versioner/header.rb +17 -163
  37. data/lib/grape/middleware/versioner/param.rb +2 -4
  38. data/lib/grape/middleware/versioner/path.rb +1 -3
  39. data/lib/grape/namespace.rb +3 -4
  40. data/lib/grape/path.rb +24 -29
  41. data/lib/grape/request.rb +4 -12
  42. data/lib/grape/router/base_route.rb +39 -0
  43. data/lib/grape/router/greedy_route.rb +20 -0
  44. data/lib/grape/router/pattern.rb +39 -30
  45. data/lib/grape/router/route.rb +22 -59
  46. data/lib/grape/router.rb +32 -37
  47. data/lib/grape/util/accept/header.rb +19 -0
  48. data/lib/grape/util/accept_header_handler.rb +105 -0
  49. data/lib/grape/util/base_inheritable.rb +4 -4
  50. data/lib/grape/util/cache.rb +0 -3
  51. data/lib/grape/util/endpoint_configuration.rb +1 -1
  52. data/lib/grape/util/header.rb +13 -0
  53. data/lib/grape/util/inheritable_values.rb +0 -2
  54. data/lib/grape/util/lazy/block.rb +29 -0
  55. data/lib/grape/util/lazy/object.rb +45 -0
  56. data/lib/grape/util/lazy/value.rb +38 -0
  57. data/lib/grape/util/lazy/value_array.rb +21 -0
  58. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  59. data/lib/grape/util/lazy/value_hash.rb +21 -0
  60. data/lib/grape/util/media_type.rb +70 -0
  61. data/lib/grape/util/reverse_stackable_values.rb +1 -6
  62. data/lib/grape/util/stackable_values.rb +1 -6
  63. data/lib/grape/util/strict_hash_configuration.rb +3 -3
  64. data/lib/grape/validations/attributes_doc.rb +38 -36
  65. data/lib/grape/validations/contract_scope.rb +71 -0
  66. data/lib/grape/validations/params_scope.rb +10 -9
  67. data/lib/grape/validations/types/array_coercer.rb +0 -2
  68. data/lib/grape/validations/types/build_coercer.rb +69 -71
  69. data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
  70. data/lib/grape/validations/types/json.rb +0 -2
  71. data/lib/grape/validations/types/primitive_coercer.rb +0 -2
  72. data/lib/grape/validations/types/set_coercer.rb +0 -3
  73. data/lib/grape/validations/types.rb +0 -3
  74. data/lib/grape/validations/validators/base.rb +1 -0
  75. data/lib/grape/validations/validators/default_validator.rb +5 -1
  76. data/lib/grape/validations/validators/length_validator.rb +42 -0
  77. data/lib/grape/validations/validators/values_validator.rb +6 -1
  78. data/lib/grape/validations.rb +3 -7
  79. data/lib/grape/version.rb +1 -1
  80. data/lib/grape/{util/xml.rb → xml.rb} +1 -1
  81. data/lib/grape.rb +30 -274
  82. metadata +31 -37
  83. data/lib/grape/eager_load.rb +0 -20
  84. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
  85. data/lib/grape/router/attribute_translator.rb +0 -63
  86. data/lib/grape/util/lazy_block.rb +0 -27
  87. data/lib/grape/util/lazy_object.rb +0 -43
  88. data/lib/grape/util/lazy_value.rb +0 -91
data/lib/grape/router.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/router/route'
4
- require 'grape/util/cache'
5
-
6
3
  module Grape
7
4
  class Router
8
5
  attr_reader :map, :compiled
@@ -15,10 +12,6 @@ module Grape
15
12
  path
16
13
  end
17
14
 
18
- def self.supported_methods
19
- @supported_methods ||= Grape::Http::Headers::SUPPORTED_METHODS + ['*']
20
- end
21
-
22
15
  def initialize
23
16
  @neutral_map = []
24
17
  @neutral_regexes = []
@@ -31,13 +24,12 @@ module Grape
31
24
 
32
25
  @union = Regexp.union(@neutral_regexes)
33
26
  @neutral_regexes = nil
34
- self.class.supported_methods.each do |method|
27
+ (Grape::Http::Headers::SUPPORTED_METHODS + ['*']).each do |method|
28
+ next unless map.key?(method)
29
+
35
30
  routes = map[method]
36
- @optimized_map[method] = routes.map.with_index do |route, index|
37
- route.index = index
38
- Regexp.new("(?<_#{index}>#{route.pattern.to_regexp})")
39
- end
40
- @optimized_map[method] = Regexp.union(@optimized_map[method])
31
+ optimized_map = routes.map.with_index { |route, index| route.to_regexp(index) }
32
+ @optimized_map[method] = Regexp.union(optimized_map)
41
33
  end
42
34
  @compiled = true
43
35
  end
@@ -47,8 +39,10 @@ module Grape
47
39
  end
48
40
 
49
41
  def associate_routes(pattern, **options)
50
- @neutral_regexes << Regexp.new("(?<_#{@neutral_map.length}>)#{pattern.to_regexp}")
51
- @neutral_map << Grape::Router::AttributeTranslator.new(**options, pattern: pattern, index: @neutral_map.length)
42
+ Grape::Router::GreedyRoute.new(pattern: pattern, **options).then do |greedy_route|
43
+ @neutral_regexes << greedy_route.to_regexp(@neutral_map.length)
44
+ @neutral_map << greedy_route
45
+ end
52
46
  end
53
47
 
54
48
  def call(env)
@@ -91,26 +85,33 @@ module Grape
91
85
 
92
86
  def transaction(env)
93
87
  input, method = *extract_input_and_method(env)
94
- response = yield(input, method)
95
88
 
96
- return response if response && !(cascade = cascade?(response))
89
+ # using a Proc is important since `return` will exit the enclosing function
90
+ cascade_or_return_response = proc do |response|
91
+ if response
92
+ cascade?(response).tap do |cascade|
93
+ return response unless cascade
97
94
 
95
+ # we need to close the body if possible before dismissing
96
+ response[2].close if response[2].respond_to?(:close)
97
+ end
98
+ end
99
+ end
100
+
101
+ last_response_cascade = cascade_or_return_response.call(yield(input, method))
98
102
  last_neighbor_route = greedy_match?(input)
99
103
 
100
104
  # If last_neighbor_route exists and request method is OPTIONS,
101
105
  # return response by using #call_with_allow_headers.
102
- return call_with_allow_headers(env, last_neighbor_route) if last_neighbor_route && method == Grape::Http::Headers::OPTIONS && !cascade
106
+ return call_with_allow_headers(env, last_neighbor_route) if last_neighbor_route && method == Rack::OPTIONS && !last_response_cascade
103
107
 
104
108
  route = match?(input, '*')
105
109
 
106
- return last_neighbor_route.endpoint.call(env) if last_neighbor_route && cascade && route
110
+ return last_neighbor_route.endpoint.call(env) if last_neighbor_route && last_response_cascade && route
107
111
 
108
- if route
109
- response = process_route(route, env)
110
- return response if response && !(cascade = cascade?(response))
111
- end
112
+ last_response_cascade = cascade_or_return_response.call(process_route(route, env)) if route
112
113
 
113
- return call_with_allow_headers(env, last_neighbor_route) if !cascade && last_neighbor_route
114
+ return call_with_allow_headers(env, last_neighbor_route) if !last_response_cascade && last_neighbor_route
114
115
 
115
116
  nil
116
117
  end
@@ -122,12 +123,12 @@ module Grape
122
123
 
123
124
  def make_routing_args(default_args, route, input)
124
125
  args = default_args || { route_info: route }
125
- args.merge(route.params(input) || {})
126
+ args.merge(route.params(input))
126
127
  end
127
128
 
128
129
  def extract_input_and_method(env)
129
- input = string_for(env[Grape::Http::Headers::PATH_INFO])
130
- method = env[Grape::Http::Headers::REQUEST_METHOD]
130
+ input = string_for(env[Rack::PATH_INFO])
131
+ method = env[Rack::REQUEST_METHOD]
131
132
  [input, method]
132
133
  end
133
134
 
@@ -137,22 +138,16 @@ module Grape
137
138
  end
138
139
 
139
140
  def default_response
140
- [404, { Grape::Http::Headers::X_CASCADE => 'pass' }, ['404 Not Found']]
141
+ headers = Grape::Util::Header.new.merge(Grape::Http::Headers::X_CASCADE => 'pass')
142
+ [404, headers, ['404 Not Found']]
141
143
  end
142
144
 
143
145
  def match?(input, method)
144
- current_regexp = @optimized_map[method]
145
- return unless current_regexp.match(input)
146
-
147
- last_match = Regexp.last_match
148
- @map[method].detect { |route| last_match["_#{route.index}"] }
146
+ @optimized_map[method].match(input) { |m| @map[method].detect { |route| m[route.regexp_capture_index] } }
149
147
  end
150
148
 
151
149
  def greedy_match?(input)
152
- return unless @union.match(input)
153
-
154
- last_match = Regexp.last_match
155
- @neutral_map.detect { |route| last_match["_#{route.index}"] }
150
+ @union.match(input) { |m| @neutral_map.detect { |route| m[route.regexp_capture_index] } }
156
151
  end
157
152
 
158
153
  def call_with_allow_headers(env, route)
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module Accept
6
+ module Header
7
+ ALLOWED_CHARACTERS = %r{^([a-z*]+)/([a-z0-9*&\^\-_#{$ERROR_INFO}.+]+)(?:;([a-z0-9=;]+))?$}.freeze
8
+ class << self
9
+ # Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
10
+ def parse_media_type(media_type)
11
+ # see http://tools.ietf.org/html/rfc6838#section-4.2 for allowed characters in media type names
12
+ m = media_type&.match(ALLOWED_CHARACTERS)
13
+ m ? [m[1], m[2], m[3] || ''] : []
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ class AcceptHeaderHandler
6
+ attr_reader :accept_header, :versions, :vendor, :strict, :cascade
7
+
8
+ def initialize(accept_header:, versions:, **options)
9
+ @accept_header = accept_header
10
+ @versions = versions
11
+ @vendor = options.fetch(:vendor, nil)
12
+ @strict = options.fetch(:strict, false)
13
+ @cascade = options.fetch(:cascade, true)
14
+ end
15
+
16
+ def match_best_quality_media_type!(content_types: Grape::ContentTypes::CONTENT_TYPES, allowed_methods: nil)
17
+ return unless vendor
18
+
19
+ strict_header_checks!
20
+ media_type = Grape::Util::MediaType.best_quality(accept_header, available_media_types(content_types))
21
+ if media_type
22
+ yield media_type
23
+ else
24
+ fail!(allowed_methods)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def strict_header_checks!
31
+ return unless strict
32
+
33
+ accept_header_check!
34
+ version_and_vendor_check!
35
+ end
36
+
37
+ def accept_header_check!
38
+ return if accept_header.present?
39
+
40
+ invalid_accept_header!('Accept header must be set.')
41
+ end
42
+
43
+ def version_and_vendor_check!
44
+ return if versions.blank? || version_and_vendor?
45
+
46
+ invalid_accept_header!('API vendor or version not found.')
47
+ end
48
+
49
+ def q_values_mime_types
50
+ @q_values_mime_types ||= Rack::Utils.q_values(accept_header).map(&:first)
51
+ end
52
+
53
+ def version_and_vendor?
54
+ q_values_mime_types.any? { |mime_type| Grape::Util::MediaType.match?(mime_type) }
55
+ end
56
+
57
+ def invalid_accept_header!(message)
58
+ raise Grape::Exceptions::InvalidAcceptHeader.new(message, error_headers)
59
+ end
60
+
61
+ def invalid_version_header!(message)
62
+ raise Grape::Exceptions::InvalidVersionHeader.new(message, error_headers)
63
+ end
64
+
65
+ def fail!(grape_allowed_methods)
66
+ return grape_allowed_methods if grape_allowed_methods.present?
67
+
68
+ media_types = q_values_mime_types.map { |mime_type| Grape::Util::MediaType.parse(mime_type) }
69
+ vendor_not_found!(media_types) || version_not_found!(media_types)
70
+ end
71
+
72
+ def vendor_not_found!(media_types)
73
+ return unless media_types.all? { |media_type| media_type&.vendor && media_type.vendor != vendor }
74
+
75
+ invalid_accept_header!('API vendor not found.')
76
+ end
77
+
78
+ def version_not_found!(media_types)
79
+ return unless media_types.all? { |media_type| media_type&.version && versions.exclude?(media_type.version) }
80
+
81
+ invalid_version_header!('API version not found.')
82
+ end
83
+
84
+ def error_headers
85
+ cascade ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
86
+ end
87
+
88
+ def available_media_types(content_types)
89
+ [].tap do |available_media_types|
90
+ base_media_type = "application/vnd.#{vendor}"
91
+ content_types.each_key do |extension|
92
+ versions&.reverse_each do |version|
93
+ available_media_types << "#{base_media_type}-#{version}+#{extension}"
94
+ available_media_types << "#{base_media_type}-#{version}"
95
+ end
96
+ available_media_types << "#{base_media_type}+#{extension}"
97
+ end
98
+
99
+ available_media_types << base_media_type
100
+ available_media_types.concat(content_types.values.flatten)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -26,10 +26,10 @@ module Grape
26
26
 
27
27
  def keys
28
28
  if new_values.any?
29
- combined = inherited_values.keys
30
- combined.concat(new_values.keys)
31
- combined.uniq!
32
- combined
29
+ inherited_values.keys.tap do |combined|
30
+ combined.concat(new_values.keys)
31
+ combined.uniq!
32
+ end
33
33
  else
34
34
  inherited_values.keys
35
35
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'singleton'
4
- require 'forwardable'
5
-
6
3
  module Grape
7
4
  module Util
8
5
  class Cache
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Grape
4
4
  module Util
5
- class EndpointConfiguration < LazyValueHash
5
+ class EndpointConfiguration < Lazy::ValueHash
6
6
  end
7
7
  end
8
8
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ if Gem::Version.new(Rack.release) >= Gem::Version.new('3')
6
+ require 'rack/headers'
7
+ Header = Rack::Headers
8
+ else
9
+ require 'rack/utils'
10
+ Header = Rack::Utils::HeaderHash
11
+ end
12
+ end
13
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'base_inheritable'
4
-
5
3
  module Grape
6
4
  module Util
7
5
  class InheritableValues < BaseInheritable
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module Lazy
6
+ class Block
7
+ def initialize(&new_block)
8
+ @block = new_block
9
+ end
10
+
11
+ def evaluate_from(configuration)
12
+ @block.call(configuration)
13
+ end
14
+
15
+ def evaluate
16
+ @block.call({})
17
+ end
18
+
19
+ def lazy?
20
+ true
21
+ end
22
+
23
+ def to_s
24
+ evaluate.to_s
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Based on https://github.com/HornsAndHooves/lazy_object
4
+
5
+ module Grape
6
+ module Util
7
+ module Lazy
8
+ class Object < BasicObject
9
+ attr_reader :callable
10
+
11
+ def initialize(&callable)
12
+ @callable = callable
13
+ end
14
+
15
+ def __target_object__
16
+ @__target_object__ ||= callable.call
17
+ end
18
+
19
+ def ==(other)
20
+ __target_object__ == other
21
+ end
22
+
23
+ def !=(other)
24
+ __target_object__ != other
25
+ end
26
+
27
+ def !
28
+ !__target_object__
29
+ end
30
+
31
+ def method_missing(method_name, *args, &block)
32
+ if __target_object__.respond_to?(method_name)
33
+ __target_object__.send(method_name, *args, &block)
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def respond_to_missing?(method_name, include_priv = false)
40
+ __target_object__.respond_to?(method_name, include_priv)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module Lazy
6
+ class Value
7
+ attr_reader :access_keys
8
+
9
+ def initialize(value, access_keys = [])
10
+ @value = value
11
+ @access_keys = access_keys
12
+ end
13
+
14
+ def evaluate_from(configuration)
15
+ matching_lazy_value = configuration.fetch(@access_keys)
16
+ matching_lazy_value.evaluate
17
+ end
18
+
19
+ def evaluate
20
+ @value
21
+ end
22
+
23
+ def lazy?
24
+ true
25
+ end
26
+
27
+ def reached_by(parent_access_keys, access_key)
28
+ @access_keys = parent_access_keys + [access_key]
29
+ self
30
+ end
31
+
32
+ def to_s
33
+ evaluate.to_s
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module Lazy
6
+ class ValueArray < ValueEnumerable
7
+ def initialize(array)
8
+ super
9
+ @value_hash = []
10
+ array.each_with_index do |value, index|
11
+ self[index] = value
12
+ end
13
+ end
14
+
15
+ def evaluate
16
+ @value_hash.map(&:evaluate)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module Lazy
6
+ class ValueEnumerable < Value
7
+ def [](key)
8
+ if @value_hash[key].nil?
9
+ Value.new(nil).reached_by(access_keys, key)
10
+ else
11
+ @value_hash[key].reached_by(access_keys, key)
12
+ end
13
+ end
14
+
15
+ def fetch(access_keys)
16
+ fetched_keys = access_keys.dup
17
+ value = self[fetched_keys.shift]
18
+ fetched_keys.any? ? value.fetch(fetched_keys) : value
19
+ end
20
+
21
+ def []=(key, value)
22
+ @value_hash[key] = case value
23
+ when Hash
24
+ ValueHash.new(value)
25
+ when Array
26
+ ValueArray.new(value)
27
+ else
28
+ Value.new(value)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ module Lazy
6
+ class ValueHash < ValueEnumerable
7
+ def initialize(hash)
8
+ super
9
+ @value_hash = ActiveSupport::HashWithIndifferentAccess.new
10
+ hash.each do |key, value|
11
+ self[key] = value
12
+ end
13
+ end
14
+
15
+ def evaluate
16
+ @value_hash.transform_values(&:evaluate)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Util
5
+ class MediaType
6
+ attr_reader :type, :subtype, :vendor, :version, :format
7
+
8
+ # based on the HTTP Accept header with the pattern:
9
+ # application/vnd.:vendor-:version+:format
10
+ VENDOR_VERSION_HEADER_REGEX = /\Avnd\.(?<vendor>[a-z0-9.\-_!^]+?)(?:-(?<version>[a-z0-9*.]+))?(?:\+(?<format>[a-z0-9*\-.]+))?\z/.freeze
11
+
12
+ def initialize(type:, subtype:)
13
+ @type = type
14
+ @subtype = subtype
15
+ VENDOR_VERSION_HEADER_REGEX.match(subtype) do |m|
16
+ @vendor = m[:vendor]
17
+ @version = m[:version]
18
+ @format = m[:format]
19
+ end
20
+ end
21
+
22
+ def ==(other)
23
+ eql?(other)
24
+ end
25
+
26
+ def eql?(other)
27
+ self.class == other.class &&
28
+ other.type == type &&
29
+ other.subtype == subtype &&
30
+ other.vendor == vendor &&
31
+ other.version == version &&
32
+ other.format == format
33
+ end
34
+
35
+ def hash
36
+ [self.class, type, subtype, vendor, version, format].hash
37
+ end
38
+
39
+ class << self
40
+ def best_quality(header, available_media_types)
41
+ parse(best_quality_media_type(header, available_media_types))
42
+ end
43
+
44
+ def parse(media_type)
45
+ return if media_type.blank?
46
+
47
+ type, subtype = media_type.split('/', 2)
48
+ return if type.blank? || subtype.blank?
49
+
50
+ new(type: type, subtype: subtype)
51
+ end
52
+
53
+ def match?(media_type)
54
+ return false if media_type.blank?
55
+
56
+ subtype = media_type.split('/', 2).last
57
+ return false if subtype.blank?
58
+
59
+ VENDOR_VERSION_HEADER_REGEX.match?(subtype)
60
+ end
61
+
62
+ def best_quality_media_type(header, available_media_types)
63
+ header.blank? ? available_media_types.first : Rack::Utils.best_q_match(header, available_media_types)
64
+ end
65
+ end
66
+
67
+ private_class_method :best_quality_media_type
68
+ end
69
+ end
70
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'stackable_values'
4
-
5
3
  module Grape
6
4
  module Util
7
5
  class ReverseStackableValues < StackableValues
@@ -10,10 +8,7 @@ module Grape
10
8
  def concat_values(inherited_value, new_value)
11
9
  return inherited_value unless new_value
12
10
 
13
- [].tap do |value|
14
- value.concat(new_value)
15
- value.concat(inherited_value)
16
- end
11
+ new_value + inherited_value
17
12
  end
18
13
  end
19
14
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'base_inheritable'
4
-
5
3
  module Grape
6
4
  module Util
7
5
  class StackableValues < BaseInheritable
@@ -31,10 +29,7 @@ module Grape
31
29
  def concat_values(inherited_value, new_value)
32
30
  return inherited_value unless new_value
33
31
 
34
- [].tap do |value|
35
- value.concat(inherited_value)
36
- value.concat(new_value)
37
- end
32
+ inherited_value + new_value
38
33
  end
39
34
  end
40
35
  end
@@ -56,19 +56,19 @@ module Grape
56
56
  def self.nested_settings_methods(setting_name, new_config_class)
57
57
  new_config_class.class_eval do
58
58
  setting_name.each_pair do |key, value|
59
- define_method "#{key}_context" do
59
+ define_method :"#{key}_context" do
60
60
  @contexts[key] ||= Grape::Util::StrictHashConfiguration.config_class(*value).new
61
61
  end
62
62
 
63
63
  define_method key do |&block|
64
- send("#{key}_context").instance_exec(&block)
64
+ send(:"#{key}_context").instance_exec(&block)
65
65
  end
66
66
  end
67
67
 
68
68
  define_method :to_hash do
69
69
  @settings.to_hash.merge(
70
70
  setting_name.each_key.with_object({}) do |k, merge_hash|
71
- merge_hash[k] = send("#{k}_context").to_hash
71
+ merge_hash[k] = send(:"#{k}_context").to_hash
72
72
  end
73
73
  )
74
74
  end