grape 2.4.0 → 4.0.1

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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +351 -0
  3. data/CONTRIBUTING.md +18 -10
  4. data/README.md +457 -228
  5. data/UPGRADING.md +1240 -1
  6. data/grape.gemspec +7 -7
  7. data/lib/grape/api/instance.rb +65 -116
  8. data/lib/grape/api.rb +53 -62
  9. data/lib/grape/content_types.rb +58 -3
  10. data/lib/grape/cookies.rb +2 -6
  11. data/lib/grape/declared_params_handler.rb +116 -0
  12. data/lib/grape/dry_types.rb +48 -4
  13. data/lib/grape/dsl/callbacks.rb +8 -58
  14. data/lib/grape/dsl/declared.rb +35 -0
  15. data/lib/grape/dsl/desc.rb +24 -67
  16. data/lib/grape/dsl/entity.rb +106 -0
  17. data/lib/grape/dsl/helpers.rb +80 -65
  18. data/lib/grape/dsl/inside_route.rb +56 -296
  19. data/lib/grape/dsl/logger.rb +4 -9
  20. data/lib/grape/dsl/middleware.rb +22 -40
  21. data/lib/grape/dsl/parameters.rb +86 -93
  22. data/lib/grape/dsl/request_response.rb +139 -139
  23. data/lib/grape/dsl/rescue_options.rb +25 -0
  24. data/lib/grape/dsl/routing.rb +328 -203
  25. data/lib/grape/dsl/settings.rb +29 -135
  26. data/lib/grape/dsl/validations.rb +38 -44
  27. data/lib/grape/dsl/version_options.rb +23 -0
  28. data/lib/grape/endpoint/options.rb +25 -0
  29. data/lib/grape/endpoint.rb +285 -245
  30. data/lib/grape/env.rb +8 -3
  31. data/lib/grape/error_formatter/base.rb +27 -21
  32. data/lib/grape/error_formatter/json.rb +6 -0
  33. data/lib/grape/error_formatter/txt.rb +10 -10
  34. data/lib/grape/error_formatter.rb +6 -2
  35. data/lib/grape/exceptions/base.rb +20 -44
  36. data/lib/grape/exceptions/error_response.rb +48 -0
  37. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  38. data/lib/grape/exceptions/internal_server_error.rb +16 -0
  39. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  40. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  41. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  42. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  43. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  44. data/lib/grape/exceptions/method_not_allowed.rb +1 -1
  45. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  46. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  47. data/lib/grape/exceptions/request_error.rb +11 -0
  48. data/lib/grape/exceptions/unknown_auth_strategy.rb +1 -1
  49. data/lib/grape/exceptions/unknown_error_formatter.rb +11 -0
  50. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  51. data/lib/grape/exceptions/unknown_params_builder.rb +1 -1
  52. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  53. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  54. data/lib/grape/exceptions/validation.rb +25 -7
  55. data/lib/grape/exceptions/validation_array_errors.rb +4 -0
  56. data/lib/grape/exceptions/validation_errors.rb +21 -23
  57. data/lib/grape/formatter/json.rb +1 -0
  58. data/lib/grape/formatter/serializable_hash.rb +6 -9
  59. data/lib/grape/json.rb +38 -2
  60. data/lib/grape/locale/en.yml +3 -5
  61. data/lib/grape/middleware/auth/base.rb +4 -3
  62. data/lib/grape/middleware/auth/dsl.rb +9 -23
  63. data/lib/grape/middleware/base.rb +41 -36
  64. data/lib/grape/middleware/error.rb +271 -73
  65. data/lib/grape/middleware/formatter.rb +152 -63
  66. data/lib/grape/middleware/precomputed_content_types.rb +51 -0
  67. data/lib/grape/middleware/stack.rb +20 -11
  68. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -3
  69. data/lib/grape/middleware/versioner/base.rb +40 -48
  70. data/lib/grape/middleware/versioner/header.rb +5 -23
  71. data/lib/grape/middleware/versioner/param.rb +2 -2
  72. data/lib/grape/middleware/versioner/path.rb +54 -7
  73. data/lib/grape/mountable.rb +22 -0
  74. data/lib/grape/namespace.rb +19 -11
  75. data/lib/grape/params_builder/base.rb +2 -0
  76. data/lib/grape/params_builder/hash_with_indifferent_access.rb +1 -1
  77. data/lib/grape/params_builder.rb +2 -19
  78. data/lib/grape/parser/json.rb +1 -1
  79. data/lib/grape/path.rb +8 -72
  80. data/lib/grape/precompiled_json.rb +50 -0
  81. data/lib/grape/request.rb +57 -20
  82. data/lib/grape/router/base_route.rb +53 -7
  83. data/lib/grape/router/greedy_route.rb +16 -6
  84. data/lib/grape/router/mustermann_pattern.rb +44 -0
  85. data/lib/grape/router/pattern/path.rb +78 -0
  86. data/lib/grape/router/pattern.rb +65 -37
  87. data/lib/grape/router/route.rb +66 -18
  88. data/lib/grape/router.rb +126 -115
  89. data/lib/grape/serve_stream/file_body.rb +7 -0
  90. data/lib/grape/serve_stream/sendfile_response.rb +3 -5
  91. data/lib/grape/serve_stream/stream_response.rb +7 -0
  92. data/lib/grape/testing.rb +33 -0
  93. data/lib/grape/util/api_description.rb +67 -0
  94. data/lib/grape/util/cache.rb +22 -2
  95. data/lib/grape/util/deep_freeze.rb +34 -0
  96. data/lib/grape/util/freeze_on_new.rb +20 -0
  97. data/lib/grape/util/inheritable_setting.rb +777 -39
  98. data/lib/grape/util/lazy/base.rb +16 -0
  99. data/lib/grape/util/lazy/block.rb +2 -9
  100. data/lib/grape/util/lazy/value.rb +2 -9
  101. data/lib/grape/util/lazy/value_enumerable.rb +13 -16
  102. data/lib/grape/util/media_type.rb +13 -9
  103. data/lib/grape/util/path_normalizer.rb +37 -0
  104. data/lib/grape/util/registry.rb +13 -3
  105. data/lib/grape/util/shadowed_rescue_handlers.rb +49 -0
  106. data/lib/grape/util/stackable_values.rb +39 -10
  107. data/lib/grape/util/translation.rb +42 -0
  108. data/lib/grape/validations/attributes_iterator.rb +61 -29
  109. data/lib/grape/validations/coerce_options.rb +21 -0
  110. data/lib/grape/validations/contract_scope.rb +8 -13
  111. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  112. data/lib/grape/validations/oneof_collector.rb +35 -0
  113. data/lib/grape/validations/param_scope_tracker.rb +62 -0
  114. data/lib/grape/validations/params_documentation.rb +52 -0
  115. data/lib/grape/validations/params_scope.rb +217 -304
  116. data/lib/grape/validations/shared_options.rb +19 -0
  117. data/lib/grape/validations/single_attribute_iterator.rb +6 -4
  118. data/lib/grape/validations/types/array_coercer.rb +7 -10
  119. data/lib/grape/validations/types/custom_type_coercer.rb +47 -85
  120. data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
  121. data/lib/grape/validations/types/dry_type_coercer.rb +10 -15
  122. data/lib/grape/validations/types/json.rb +1 -3
  123. data/lib/grape/validations/types/multiple_type_coercer.rb +5 -3
  124. data/lib/grape/validations/types/primitive_coercer.rb +14 -33
  125. data/lib/grape/validations/types/set_coercer.rb +1 -1
  126. data/lib/grape/validations/types/variant_collection_coercer.rb +16 -3
  127. data/lib/grape/validations/types.rb +29 -51
  128. data/lib/grape/validations/validations_spec.rb +164 -0
  129. data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
  130. data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
  131. data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
  132. data/lib/grape/validations/validators/base.rb +118 -34
  133. data/lib/grape/validations/validators/coerce_validator.rb +25 -37
  134. data/lib/grape/validations/validators/contract_scope_validator.rb +10 -5
  135. data/lib/grape/validations/validators/default_validator.rb +13 -20
  136. data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
  137. data/lib/grape/validations/validators/except_values_validator.rb +14 -4
  138. data/lib/grape/validations/validators/length_validator.rb +23 -22
  139. data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
  140. data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
  141. data/lib/grape/validations/validators/oneof_validator.rb +51 -0
  142. data/lib/grape/validations/validators/presence_validator.rb +4 -2
  143. data/lib/grape/validations/validators/regexp_validator.rb +11 -3
  144. data/lib/grape/validations/validators/same_as_validator.rb +7 -15
  145. data/lib/grape/validations/validators/values_validator.rb +34 -26
  146. data/lib/grape/version.rb +1 -2
  147. data/lib/grape/xml.rb +8 -1
  148. data/lib/grape.rb +58 -31
  149. metadata +62 -39
  150. data/lib/grape/api/helpers.rb +0 -9
  151. data/lib/grape/dsl/api.rb +0 -17
  152. data/lib/grape/dsl/configuration.rb +0 -15
  153. data/lib/grape/exceptions/conflicting_types.rb +0 -11
  154. data/lib/grape/exceptions/empty_message_body.rb +0 -11
  155. data/lib/grape/exceptions/invalid_parameters.rb +0 -11
  156. data/lib/grape/exceptions/missing_option.rb +0 -11
  157. data/lib/grape/exceptions/too_deep_parameters.rb +0 -11
  158. data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
  159. data/lib/grape/exceptions/unknown_options.rb +0 -11
  160. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -24
  161. data/lib/grape/extensions/hash.rb +0 -27
  162. data/lib/grape/extensions/hashie/mash.rb +0 -24
  163. data/lib/grape/middleware/globals.rb +0 -14
  164. data/lib/grape/types/invalid_value.rb +0 -8
  165. data/lib/grape/util/base_inheritable.rb +0 -43
  166. data/lib/grape/util/inheritable_values.rb +0 -29
  167. data/lib/grape/util/reverse_stackable_values.rb +0 -15
  168. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  169. data/lib/grape/validations/attributes_doc.rb +0 -60
  170. data/lib/grape/validations/validator_factory.rb +0 -15
@@ -5,43 +5,44 @@ module Grape
5
5
  module Validators
6
6
  class LengthValidator < Base
7
7
  def initialize(attrs, options, required, scope, opts)
8
- @min = options[:min]
9
- @max = options[:max]
10
- @is = options[:is]
11
-
12
8
  super
13
9
 
14
- raise ArgumentError, 'min must be an integer greater than or equal to zero' if !@min.nil? && (!@min.is_a?(Integer) || @min.negative?)
15
- raise ArgumentError, 'max must be an integer greater than or equal to zero' if !@max.nil? && (!@max.is_a?(Integer) || @max.negative?)
16
- raise ArgumentError, "min #{@min} cannot be greater than max #{@max}" if !@min.nil? && !@max.nil? && @min > @max
10
+ @min, @max, @is = options.values_at(:min, :max, :is)
11
+ validate_boundary!(:min, @min)
12
+ validate_boundary!(:max, @max)
13
+ raise ArgumentError, "min `#{@min}` cannot be greater than max `#{@max}`" if @min && @max && @min > @max
17
14
 
18
15
  return if @is.nil?
19
- raise ArgumentError, 'is must be an integer greater than zero' if !@is.is_a?(Integer) || !@is.positive?
20
- raise ArgumentError, 'is cannot be combined with min or max' if !@min.nil? || !@max.nil?
16
+ raise ArgumentError, 'is must be an integer greater than zero' unless @is.is_a?(Integer) && @is.positive?
17
+ raise ArgumentError, 'is cannot be combined with min or max' unless @min.nil? && @max.nil?
21
18
  end
22
19
 
23
20
  def validate_param!(attr_name, params)
21
+ return unless hash_like?(params)
22
+
24
23
  param = params[attr_name]
25
24
 
26
25
  return unless param.respond_to?(:length)
27
26
 
28
27
  return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max) || (!@is.nil? && param.length != @is)
29
28
 
30
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: build_message)
29
+ validation_error!(attr_name, message do
30
+ if @min && @max
31
+ translate(:length, min: @min, max: @max)
32
+ elsif @min
33
+ translate(:length_min, min: @min)
34
+ elsif @max
35
+ translate(:length_max, max: @max)
36
+ else
37
+ translate(:length_is, is: @is)
38
+ end
39
+ end)
31
40
  end
32
41
 
33
- def build_message
34
- if options_key?(:message)
35
- @option[:message]
36
- elsif @min && @max
37
- format I18n.t(:length, scope: 'grape.errors.messages'), min: @min, max: @max
38
- elsif @min
39
- format I18n.t(:length_min, scope: 'grape.errors.messages'), min: @min
40
- elsif @max
41
- format I18n.t(:length_max, scope: 'grape.errors.messages'), max: @max
42
- else
43
- format I18n.t(:length_is, scope: 'grape.errors.messages'), is: @is
44
- end
42
+ private
43
+
44
+ def validate_boundary!(name, val)
45
+ raise ArgumentError, "`#{name}` must be an integer greater than or equal to zero" if !val.nil? && (!val.is_a?(Integer) || val.negative?)
45
46
  end
46
47
  end
47
48
  end
@@ -5,28 +5,31 @@ module Grape
5
5
  module Validators
6
6
  class MultipleParamsBase < Base
7
7
  def validate!(params)
8
- attributes = MultipleAttributesIterator.new(self, @scope, params)
9
- array_errors = []
8
+ array_errors = nil
10
9
 
11
- attributes.each do |resource_params|
10
+ @iterator.each(params) do |resource_params|
12
11
  validate_params!(resource_params)
13
12
  rescue Grape::Exceptions::Validation => e
14
- array_errors << e
13
+ (array_errors ||= []) << e
15
14
  end
16
15
 
17
- raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
16
+ raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors
18
17
  end
19
18
 
20
19
  private
21
20
 
22
- def keys_in_common(resource_params)
23
- return [] unless resource_params.is_a?(Hash)
21
+ def iterator_class
22
+ MultipleAttributesIterator
23
+ end
24
+
25
+ def keys_in_common(resource_params, known_keys = all_keys)
26
+ return [] unless hash_like?(resource_params)
24
27
 
25
- all_keys & resource_params.keys.map! { |attr| @scope.full_name(attr) }
28
+ known_keys & resource_params.keys.map! { |attr| scope.full_name(attr) }
26
29
  end
27
30
 
28
31
  def all_keys
29
- attrs.map { |attr| @scope.full_name(attr) }
32
+ attrs.map { |attr| scope.full_name(attr) }
30
33
  end
31
34
  end
32
35
  end
@@ -3,12 +3,14 @@
3
3
  module Grape
4
4
  module Validations
5
5
  module Validators
6
- class MutualExclusionValidator < MultipleParamsBase
6
+ class MutuallyExclusiveValidator < MultipleParamsBase
7
+ default_message_key :mutual_exclusion
8
+
7
9
  def validate_params!(params)
8
10
  keys = keys_in_common(params)
9
11
  return if keys.length <= 1
10
12
 
11
- raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
13
+ validation_error!(keys)
12
14
  end
13
15
  end
14
16
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ # Validates that a Hash parameter matches at least one of a set of
7
+ # variant schemas. Each variant is a list of pre-built validators
8
+ # captured by evaluating the variant's block in a {Grape::Validations::OneofCollector}-backed
9
+ # {ParamsScope}. At request time we try each variant in order against
10
+ # a deep-dup of the value; the first variant that produces no errors
11
+ # wins and its (possibly coerced) hash replaces the original.
12
+ class OneofValidator < Base
13
+ default_message_key :oneof
14
+
15
+ def initialize(attrs, options, required, scope, opts)
16
+ super
17
+ @variants = Array(options)
18
+ end
19
+
20
+ def validate_param!(attr_name, params)
21
+ return unless hash_like?(params)
22
+
23
+ value = params[attr_name]
24
+ return if value.nil? && !required?
25
+
26
+ winning_candidate = nil
27
+ @variants.each do |variant_validators|
28
+ candidate = value.deep_dup
29
+ if variant_matches?(variant_validators, candidate)
30
+ winning_candidate = candidate
31
+ break
32
+ end
33
+ end
34
+
35
+ return params[attr_name] = winning_candidate if winning_candidate
36
+
37
+ validation_error!(attr_name)
38
+ end
39
+
40
+ private
41
+
42
+ def variant_matches?(variant_validators, candidate)
43
+ variant_validators.each { |v| v.validate!(candidate) }
44
+ true
45
+ rescue Grape::Exceptions::Validation, Grape::Exceptions::ValidationArrayErrors
46
+ false
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -4,10 +4,12 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class PresenceValidator < Base
7
+ default_message_key :presence
8
+
7
9
  def validate_param!(attr_name, params)
8
- return if params.try(:key?, attr_name)
10
+ return if hash_like?(params) && params.key?(attr_name)
9
11
 
10
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
12
+ validation_error!(attr_name)
11
13
  end
12
14
  end
13
15
  end
@@ -4,11 +4,19 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class RegexpValidator < Base
7
+ default_message_key :regexp
8
+
9
+ def initialize(attrs, options, required, scope, opts)
10
+ super
11
+ @value = option_value
12
+ end
13
+
7
14
  def validate_param!(attr_name, params)
8
- return unless params.try(:key?, attr_name)
9
- return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.scrub.match?((options_key?(:value) ? @option[:value] : @option)) }
15
+ return unless hash_like?(params) && params.key?(attr_name)
16
+
17
+ return if Array.wrap(params[attr_name]).all? { |param| param.nil? || scrub(param.to_s).match?(@value) }
10
18
 
11
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
19
+ validation_error!(attr_name)
12
20
  end
13
21
  end
14
22
  end
@@ -4,24 +4,16 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class SameAsValidator < Base
7
- def validate_param!(attr_name, params)
8
- confirmation = options_key?(:value) ? @option[:value] : @option
9
- return if params[attr_name] == params[confirmation]
10
-
11
- raise Grape::Exceptions::Validation.new(
12
- params: [@scope.full_name(attr_name)],
13
- message: build_message
14
- )
7
+ def initialize(attrs, options, required, scope, opts)
8
+ super
9
+ @value = option_value
15
10
  end
16
11
 
17
- private
12
+ def validate_param!(attr_name, params)
13
+ return unless hash_like?(params)
14
+ return if params[attr_name] == params[@value]
18
15
 
19
- def build_message
20
- if options_key?(:message)
21
- @option[:message]
22
- else
23
- format I18n.t(:same_as, scope: 'grape.errors.messages'), parameter: @option
24
- end
16
+ validation_error!(attr_name, message { translate(:same_as, parameter: @value) })
25
17
  end
26
18
  end
27
19
  end
@@ -4,55 +4,63 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class ValuesValidator < Base
7
+ default_message_key :values
8
+
7
9
  def initialize(attrs, options, required, scope, opts)
8
- @values = options.is_a?(Hash) ? options[:value] : options
9
10
  super
11
+ values = option_value
12
+
13
+ # Zero-arity procs return a collection per-request (e.g. DB-backed lists).
14
+ # Non-zero-arity procs are per-element predicates, called directly at validation time.
15
+ # Non-Proc values are wrapped in a zero-arity lambda for a uniform call interface.
16
+ if values.is_a?(Proc)
17
+ @values_call = values
18
+ @values_is_predicate = !values.arity.zero?
19
+ else
20
+ @values_call = -> { values }
21
+ @values_is_predicate = false
22
+ end
10
23
  end
11
24
 
12
25
  def validate_param!(attr_name, params)
13
- return unless params.is_a?(Hash)
26
+ return unless hash_like?(params)
14
27
 
15
- val = params[attr_name]
28
+ val = scrub(params[attr_name])
16
29
 
17
30
  return if val.nil? && !required_for_root_scope?
18
-
19
- val = val.scrub if val.respond_to?(:scrub)
20
-
21
- # don't forget that +false.blank?+ is true
22
- return if val != false && val.blank? && @allow_blank
23
-
31
+ return if val != false && val.blank? && allow_blank?
24
32
  return if check_values?(val, attr_name)
25
33
 
26
- raise Grape::Exceptions::Validation.new(
27
- params: [@scope.full_name(attr_name)],
28
- message: message(:values)
29
- )
34
+ validation_error!(attr_name)
30
35
  end
31
36
 
32
37
  private
33
38
 
34
39
  def check_values?(val, attr_name)
35
- values = @values.is_a?(Proc) && @values.arity.zero? ? @values.call : @values
36
- return true if values.nil?
37
-
38
40
  param_array = val.nil? ? [nil] : Array.wrap(val)
39
- return param_array.all? { |param| values.include?(param) } unless values.is_a?(Proc)
40
41
 
41
- begin
42
- param_array.all? { |param| values.call(param) }
43
- rescue StandardError => e
44
- warn "Error '#{e}' raised while validating attribute '#{attr_name}'"
45
- false
42
+ if @values_is_predicate
43
+ begin
44
+ param_array.all? { |param| @values_call.call(param) }
45
+ rescue StandardError => e
46
+ warn "Error '#{e}' raised while validating attribute '#{attr_name}'"
47
+ false
48
+ end
49
+ else
50
+ values = @values_call.call
51
+ return true if values.nil?
52
+
53
+ param_array.all? { |param| values.include?(param) }
46
54
  end
47
55
  end
48
56
 
49
57
  def required_for_root_scope?
50
- return false unless @required
58
+ return false unless required?
51
59
 
52
- scope = @scope
53
- scope = scope.parent while scope.lateral?
60
+ current_scope = scope
61
+ current_scope = current_scope.parent while current_scope.lateral?
54
62
 
55
- scope.root?
63
+ current_scope.root?
56
64
  end
57
65
  end
58
66
  end
data/lib/grape/version.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
- # The current version of Grape.
5
- VERSION = '2.4.0'
4
+ VERSION = '4.0.1'
6
5
  end
data/lib/grape/xml.rb CHANGED
@@ -1,7 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
- if defined?(::MultiXml)
4
+ # Since multi_xml 0.9.0 the canonical constant is MultiXML; MultiXml is a
5
+ # deprecated alias (removed in v1.0) that warns on use. Prefer MultiXML so
6
+ # Grape::Xml.parse doesn't trip the deprecation, falling back to the legacy
7
+ # constant and then ActiveSupport::XmlMini.
8
+ # https://github.com/sferik/multi_xml/blob/v0.9.1/CHANGELOG.md
9
+ if defined?(::MultiXML)
10
+ Xml = ::MultiXML
11
+ elsif defined?(::MultiXml)
5
12
  Xml = ::MultiXml
6
13
  else
7
14
  Xml = ::ActiveSupport::XmlMini
data/lib/grape.rb CHANGED
@@ -1,31 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'logger'
4
+ require 'monitor'
4
5
  require 'active_support'
5
- require 'active_support/concern'
6
- require 'active_support/configurable'
7
- require 'active_support/version'
8
- require 'active_support/isolated_execution_state' if ActiveSupport::VERSION::MAJOR > 6
9
- require 'active_support/core_ext/array/conversions'
10
- require 'active_support/core_ext/array/extract_options'
6
+ require 'active_support/isolated_execution_state'
7
+ require 'active_support/core_ext/array/conversions' # to_xml
11
8
  require 'active_support/core_ext/array/wrap'
12
- require 'active_support/core_ext/enumerable'
13
- require 'active_support/core_ext/hash/conversions'
9
+ require 'active_support/core_ext/hash/conversions' # to_xml
14
10
  require 'active_support/core_ext/hash/deep_merge'
15
- require 'active_support/core_ext/hash/except'
16
- require 'active_support/core_ext/hash/indifferent_access'
17
- require 'active_support/core_ext/hash/keys'
18
- require 'active_support/core_ext/hash/reverse_merge'
19
- require 'active_support/core_ext/hash/slice'
20
- require 'active_support/core_ext/module/delegation'
11
+ require 'active_support/core_ext/hash/deep_transform_values'
12
+ require 'active_support/core_ext/hash/indifferent_access' # nested_under_indifferent_access, required by HashWithIndifferentAccess.new
13
+ require 'active_support/hash_with_indifferent_access'
14
+ require 'active_support/core_ext/module/delegation' # delegate_missing_to
21
15
  require 'active_support/core_ext/object/blank'
22
16
  require 'active_support/core_ext/object/deep_dup'
23
- require 'active_support/core_ext/object/try'
24
17
  require 'active_support/core_ext/object/duplicable'
25
- require 'active_support/core_ext/string/output_safety'
26
- require 'active_support/core_ext/string/exclude'
18
+ require 'active_support/core_ext/string/inflections' # demodulize, underscore
27
19
  require 'active_support/deprecation'
28
- require 'active_support/inflector'
29
20
  require 'active_support/ordered_options'
30
21
  require 'active_support/notifications'
31
22
 
@@ -33,15 +24,15 @@ require 'English'
33
24
  require 'bigdecimal'
34
25
  require 'date'
35
26
  require 'dry-types'
27
+ require 'dry-configurable'
36
28
  require 'forwardable'
37
29
  require 'json'
38
- require 'mustermann/grape'
39
- require 'pathname'
30
+ require 'mustermann'
31
+ require 'mustermann/ast/pattern'
40
32
  require 'rack'
41
33
  require 'rack/auth/basic'
42
34
  require 'rack/builder'
43
35
  require 'rack/head'
44
- require 'set'
45
36
  require 'singleton'
46
37
  require 'zeitwerk'
47
38
 
@@ -51,16 +42,33 @@ loader.inflector.inflect(
51
42
  'dsl' => 'DSL'
52
43
  )
53
44
  railtie = "#{__dir__}/grape/railtie.rb"
54
- loader.do_not_eager_load(railtie)
45
+ # Grape::Testing is a test-environment helper -- it extends Grape::Endpoint with
46
+ # `before_each` and prepends a hook to Grape::Endpoint#run. It is documented as
47
+ # opt-in (`require 'grape/testing'`), which eager loading quietly contradicted
48
+ # by installing it in every process.
49
+ testing = "#{__dir__}/grape/testing.rb"
50
+ loader.do_not_eager_load(railtie, testing)
55
51
  loader.setup
56
52
 
57
53
  I18n.load_path << File.expand_path('grape/locale/en.yml', __dir__)
58
54
 
59
55
  module Grape
60
- include ActiveSupport::Configurable
56
+ extend Dry::Configurable
57
+
58
+ setting :param_builder, default: :hash_with_indifferent_access
59
+ setting :lint, default: false
60
+ setting :warn_on_helper_overrides, default: false
61
+ # Let an error response that cannot be rendered propagate out of the
62
+ # middleware stack instead of being answered with a failsafe 500.
63
+ setting :raise_rendering_errors, default: false
64
+
65
+ # The HTTP QUERY method (RFC 10008): a safe, idempotent request whose content
66
+ # carries the query. Rack has no constant for it yet, hence the literal.
67
+ QUERY = 'QUERY'
61
68
 
62
69
  HTTP_SUPPORTED_METHODS = [
63
70
  Rack::GET,
71
+ QUERY,
64
72
  Rack::POST,
65
73
  Rack::PUT,
66
74
  Rack::PATCH,
@@ -69,18 +77,37 @@ module Grape
69
77
  Rack::OPTIONS
70
78
  ].freeze
71
79
 
72
- def self.deprecator
73
- @deprecator ||= ActiveSupport::Deprecation.new('2.0', 'Grape')
74
- end
80
+ # Rack errors that should be rescued and wrapped as Grape::Exceptions::RequestError.
81
+ # Rack 3.1.0 introduced Rack::BadRequest as a marker module included by all bad request
82
+ # exception classes, allowing a single rescue entry to cover them all.
83
+ # Before, these errors are raised as individual exception classes.
84
+ RACK_ERRORS =
85
+ if Gem::Version.new(Rack.release) >= Gem::Version.new('3.1.0')
86
+ [EOFError, Rack::BadRequest]
87
+ else
88
+ [
89
+ EOFError,
90
+ Rack::Multipart::MultipartPartLimitError,
91
+ Rack::Multipart::MultipartTotalPartLimitError,
92
+ Rack::Utils::ParameterTypeError,
93
+ Rack::Utils::InvalidParameterError,
94
+ Rack::QueryParser::ParamsTooDeepError
95
+ ]
96
+ end.freeze
75
97
 
76
- configure do |config|
77
- config.param_builder = :hash_with_indifferent_access
78
- config.lint = false
79
- config.compile_methods!
98
+ # The deprecation horizon is the version a deprecation announces as its
99
+ # removal point, so it is the *next* major rather than the current one, and
100
+ # it is derived from VERSION rather than written out. Written out, it went
101
+ # stale: it said '2.0' from 2023 (#2353) through all of 2.x, 3.x and 4.x, so
102
+ # a method deprecated through this deprecator announced a removal version
103
+ # that had already shipped, and every custom `behavior` lambda -- how a Rails
104
+ # app consumes deprecations -- was handed the same wrong number.
105
+ def self.deprecator
106
+ @deprecator ||= ActiveSupport::Deprecation.new("#{Gem::Version.new(VERSION).segments.first + 1}.0", 'Grape')
80
107
  end
81
108
  end
82
109
 
83
110
  # https://api.rubyonrails.org/classes/ActiveSupport/Deprecation.html
84
111
  # adding Grape.deprecator to Rails App if any
85
- require 'grape/railtie' if defined?(Rails::Railtie) && ActiveSupport.gem_version >= Gem::Version.new('7.1')
112
+ require 'grape/railtie' if defined?(Rails::Railtie)
86
113
  loader.eager_load