grape 1.6.2 → 1.7.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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -1
  3. data/CONTRIBUTING.md +30 -0
  4. data/README.md +146 -23
  5. data/UPGRADING.md +15 -0
  6. data/grape.gemspec +2 -2
  7. data/lib/grape/api/instance.rb +1 -1
  8. data/lib/grape/dry_types.rb +12 -0
  9. data/lib/grape/dsl/api.rb +0 -2
  10. data/lib/grape/dsl/callbacks.rb +0 -2
  11. data/lib/grape/dsl/configuration.rb +0 -2
  12. data/lib/grape/dsl/desc.rb +2 -16
  13. data/lib/grape/dsl/helpers.rb +0 -2
  14. data/lib/grape/dsl/inside_route.rb +34 -30
  15. data/lib/grape/dsl/middleware.rb +0 -2
  16. data/lib/grape/dsl/parameters.rb +10 -7
  17. data/lib/grape/dsl/request_response.rb +1 -3
  18. data/lib/grape/dsl/routing.rb +4 -2
  19. data/lib/grape/dsl/settings.rb +0 -2
  20. data/lib/grape/dsl/validations.rb +0 -15
  21. data/lib/grape/endpoint.rb +2 -2
  22. data/lib/grape/error_formatter/json.rb +7 -1
  23. data/lib/grape/exceptions/base.rb +3 -2
  24. data/lib/grape/exceptions/missing_group_type.rb +8 -1
  25. data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
  26. data/lib/grape/exceptions/unsupported_group_type.rb +8 -1
  27. data/lib/grape/exceptions/validation.rb +0 -4
  28. data/lib/grape/locale/en.yml +9 -8
  29. data/lib/grape/middleware/auth/dsl.rb +0 -1
  30. data/lib/grape/middleware/error.rb +2 -2
  31. data/lib/grape/middleware/stack.rb +1 -1
  32. data/lib/grape/request.rb +3 -1
  33. data/lib/grape/router/attribute_translator.rb +1 -1
  34. data/lib/grape/types/invalid_value.rb +8 -0
  35. data/lib/grape/util/cache.rb +1 -1
  36. data/lib/grape/util/json.rb +2 -0
  37. data/lib/grape/validations/attributes_doc.rb +58 -0
  38. data/lib/grape/validations/params_scope.rb +67 -41
  39. data/lib/grape/validations/types/array_coercer.rb +0 -2
  40. data/lib/grape/validations/types/dry_type_coercer.rb +3 -7
  41. data/lib/grape/validations/types/invalid_value.rb +0 -7
  42. data/lib/grape/validations/types/primitive_coercer.rb +14 -6
  43. data/lib/grape/validations/types/set_coercer.rb +0 -2
  44. data/lib/grape/validations/types.rb +98 -30
  45. data/lib/grape/validations/validators/{all_or_none.rb → all_or_none_of_validator.rb} +0 -2
  46. data/lib/grape/validations/validators/{at_least_one_of.rb → at_least_one_of_validator.rb} +0 -2
  47. data/lib/grape/validations/validators/base.rb +7 -0
  48. data/lib/grape/validations/validators/{exactly_one_of.rb → exactly_one_of_validator.rb} +0 -2
  49. data/lib/grape/validations/validators/{mutual_exclusion.rb → mutual_exclusion_validator.rb} +0 -2
  50. data/lib/grape/validations.rb +16 -12
  51. data/lib/grape/version.rb +1 -1
  52. data/lib/grape.rb +74 -28
  53. data/spec/grape/api/custom_validations_spec.rb +41 -2
  54. data/spec/grape/api/deeply_included_options_spec.rb +0 -2
  55. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -2
  56. data/spec/grape/api/documentation_spec.rb +59 -0
  57. data/spec/grape/api/inherited_helpers_spec.rb +0 -2
  58. data/spec/grape/api/instance_spec.rb +0 -1
  59. data/spec/grape/api/invalid_format_spec.rb +0 -2
  60. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -2
  61. data/spec/grape/api/nested_helpers_spec.rb +0 -2
  62. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -2
  63. data/spec/grape/api/parameters_modification_spec.rb +0 -2
  64. data/spec/grape/api/patch_method_helpers_spec.rb +0 -2
  65. data/spec/grape/api/recognize_path_spec.rb +0 -2
  66. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -2
  67. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -2
  68. data/spec/grape/api/routes_with_requirements_spec.rb +0 -2
  69. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -2
  70. data/spec/grape/api/shared_helpers_spec.rb +0 -2
  71. data/spec/grape/api_remount_spec.rb +0 -1
  72. data/spec/grape/api_spec.rb +23 -25
  73. data/spec/grape/config_spec.rb +0 -2
  74. data/spec/grape/dsl/callbacks_spec.rb +0 -2
  75. data/spec/grape/dsl/desc_spec.rb +2 -2
  76. data/spec/grape/dsl/headers_spec.rb +2 -4
  77. data/spec/grape/dsl/helpers_spec.rb +0 -2
  78. data/spec/grape/dsl/inside_route_spec.rb +10 -12
  79. data/spec/grape/dsl/logger_spec.rb +0 -2
  80. data/spec/grape/dsl/middleware_spec.rb +0 -2
  81. data/spec/grape/dsl/parameters_spec.rb +0 -2
  82. data/spec/grape/dsl/request_response_spec.rb +6 -8
  83. data/spec/grape/dsl/routing_spec.rb +1 -3
  84. data/spec/grape/dsl/settings_spec.rb +0 -2
  85. data/spec/grape/dsl/validations_spec.rb +0 -17
  86. data/spec/grape/endpoint/declared_spec.rb +2 -4
  87. data/spec/grape/endpoint_spec.rb +29 -9
  88. data/spec/grape/entity_spec.rb +0 -1
  89. data/spec/grape/exceptions/base_spec.rb +16 -2
  90. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -2
  91. data/spec/grape/exceptions/invalid_accept_header_spec.rb +3 -2
  92. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -2
  93. data/spec/grape/exceptions/invalid_response_spec.rb +0 -2
  94. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +1 -3
  95. data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
  96. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -2
  97. data/spec/grape/exceptions/missing_option_spec.rb +1 -3
  98. data/spec/grape/exceptions/unknown_options_spec.rb +0 -2
  99. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -2
  100. data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
  101. data/spec/grape/exceptions/validation_errors_spec.rb +0 -1
  102. data/spec/grape/exceptions/validation_spec.rb +1 -3
  103. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -2
  104. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -2
  105. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -2
  106. data/spec/grape/integration/global_namespace_function_spec.rb +0 -2
  107. data/spec/grape/integration/rack_sendfile_spec.rb +0 -2
  108. data/spec/grape/integration/rack_spec.rb +6 -7
  109. data/spec/grape/loading_spec.rb +0 -2
  110. data/spec/grape/middleware/auth/base_spec.rb +0 -1
  111. data/spec/grape/middleware/auth/dsl_spec.rb +0 -2
  112. data/spec/grape/middleware/auth/strategies_spec.rb +0 -2
  113. data/spec/grape/middleware/base_spec.rb +7 -7
  114. data/spec/grape/middleware/error_spec.rb +6 -1
  115. data/spec/grape/middleware/exception_spec.rb +0 -2
  116. data/spec/grape/middleware/formatter_spec.rb +6 -8
  117. data/spec/grape/middleware/globals_spec.rb +0 -2
  118. data/spec/grape/middleware/stack_spec.rb +0 -2
  119. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -2
  120. data/spec/grape/middleware/versioner/header_spec.rb +18 -4
  121. data/spec/grape/middleware/versioner/param_spec.rb +0 -2
  122. data/spec/grape/middleware/versioner/path_spec.rb +0 -2
  123. data/spec/grape/middleware/versioner_spec.rb +0 -2
  124. data/spec/grape/named_api_spec.rb +0 -2
  125. data/spec/grape/parser_spec.rb +0 -2
  126. data/spec/grape/path_spec.rb +0 -2
  127. data/spec/grape/presenters/presenter_spec.rb +0 -2
  128. data/spec/grape/request_spec.rb +0 -2
  129. data/spec/grape/util/inheritable_setting_spec.rb +0 -1
  130. data/spec/grape/util/inheritable_values_spec.rb +0 -1
  131. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -1
  132. data/spec/grape/util/stackable_values_spec.rb +0 -1
  133. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -1
  134. data/spec/grape/validations/attributes_doc_spec.rb +153 -0
  135. data/spec/grape/validations/instance_behaivour_spec.rb +0 -2
  136. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -2
  137. data/spec/grape/validations/params_scope_spec.rb +315 -86
  138. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -2
  139. data/spec/grape/validations/types/array_coercer_spec.rb +0 -2
  140. data/spec/grape/validations/types/primitive_coercer_spec.rb +20 -5
  141. data/spec/grape/validations/types/set_coercer_spec.rb +0 -2
  142. data/spec/grape/validations/types_spec.rb +28 -2
  143. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -2
  144. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -2
  145. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -2
  146. data/spec/grape/validations/validators/coerce_spec.rb +0 -2
  147. data/spec/grape/validations/validators/default_spec.rb +0 -2
  148. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -2
  149. data/spec/grape/validations/validators/except_values_spec.rb +0 -2
  150. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -2
  151. data/spec/grape/validations/validators/presence_spec.rb +0 -2
  152. data/spec/grape/validations/validators/regexp_spec.rb +0 -2
  153. data/spec/grape/validations/validators/same_as_spec.rb +0 -2
  154. data/spec/grape/validations/validators/values_spec.rb +19 -2
  155. data/spec/grape/validations_spec.rb +78 -27
  156. data/spec/integration/multi_json/json_spec.rb +0 -2
  157. data/spec/integration/multi_xml/xml_spec.rb +0 -2
  158. data/spec/spec_helper.rb +9 -4
  159. metadata +134 -122
  160. data/spec/grape/dsl/configuration_spec.rb +0 -16
  161. data/spec/grape/validations/attributes_iterator_spec.rb +0 -6
  162. data/spec/support/eager_load.rb +0 -19
  163. /data/lib/grape/validations/validators/{allow_blank.rb → allow_blank_validator.rb} +0 -0
  164. /data/lib/grape/validations/validators/{as.rb → as_validator.rb} +0 -0
  165. /data/lib/grape/validations/validators/{coerce.rb → coerce_validator.rb} +0 -0
  166. /data/lib/grape/validations/validators/{default.rb → default_validator.rb} +0 -0
  167. /data/lib/grape/validations/validators/{except_values.rb → except_values_validator.rb} +0 -0
  168. /data/lib/grape/validations/validators/{presence.rb → presence_validator.rb} +0 -0
  169. /data/lib/grape/validations/validators/{regexp.rb → regexp_validator.rb} +0 -0
  170. /data/lib/grape/validations/validators/{same_as.rb → same_as_validator.rb} +0 -0
  171. /data/lib/grape/validations/validators/{values.rb → values_validator.rb} +0 -0
@@ -18,19 +18,15 @@ module Grape
18
18
  # https://dry-rb.org/gems/dry-types/1.2/built-in-types/
19
19
  class DryTypeCoercer
20
20
  class << self
21
- # Registers a collection coercer which could be found by a type,
22
- # see +collection_coercer_for+ method below. This method is meant for inheritors.
23
- def register_collection(type)
24
- DryTypeCoercer.collection_coercers[type] = self
25
- end
26
-
27
21
  # Returns a collection coercer which corresponds to a given type.
28
22
  # Example:
29
23
  #
30
24
  # collection_coercer_for(Array)
31
25
  # #=> Grape::Validations::Types::ArrayCoercer
32
26
  def collection_coercer_for(type)
33
- collection_coercers[type]
27
+ collection_coercers.fetch(type) do
28
+ DryTypeCoercer.collection_coercers[type] = Grape::Validations::Types.const_get("#{type.name.camelize}Coercer")
29
+ end
34
30
  end
35
31
 
36
32
  # Returns an instance of a coercer for a given type
@@ -15,10 +15,3 @@ module Grape
15
15
  end
16
16
  end
17
17
  end
18
-
19
- # only exists to make it shorter for external use
20
- module Grape
21
- module Types
22
- InvalidValue = Class.new(Grape::Validations::Types::InvalidValue)
23
- end
24
- end
@@ -12,6 +12,9 @@ module Grape
12
12
  MAPPING = {
13
13
  Grape::API::Boolean => DryTypes::Params::Bool,
14
14
  BigDecimal => DryTypes::Params::Decimal,
15
+ Numeric => DryTypes::Params::Integer | DryTypes::Params::Float | DryTypes::Params::Decimal,
16
+ TrueClass => DryTypes::Params::Bool.constrained(eql: true),
17
+ FalseClass => DryTypes::Params::Bool.constrained(eql: false),
15
18
 
16
19
  # unfortunately, a +Params+ scope doesn't contain String
17
20
  String => DryTypes::Coercible::String
@@ -19,7 +22,10 @@ module Grape
19
22
 
20
23
  STRICT_MAPPING = {
21
24
  Grape::API::Boolean => DryTypes::Strict::Bool,
22
- BigDecimal => DryTypes::Strict::Decimal
25
+ BigDecimal => DryTypes::Strict::Decimal,
26
+ Numeric => DryTypes::Strict::Integer | DryTypes::Strict::Float | DryTypes::Strict::Decimal,
27
+ TrueClass => DryTypes::Strict::Bool.constrained(eql: true),
28
+ FalseClass => DryTypes::Strict::Bool.constrained(eql: false)
23
29
  }.freeze
24
30
 
25
31
  def initialize(type, strict = false)
@@ -27,11 +33,13 @@ module Grape
27
33
 
28
34
  @type = type
29
35
 
30
- @coercer = if strict
31
- STRICT_MAPPING.fetch(type) { scope.const_get(type.name) }
32
- else
33
- MAPPING.fetch(type) { scope.const_get(type.name) }
34
- end
36
+ @coercer = (strict ? STRICT_MAPPING : MAPPING).fetch(type) do
37
+ scope.const_get(type.name, false)
38
+ rescue NameError
39
+ raise ArgumentError, "type #{type} should support coercion via `[]`" unless type.respond_to?(:[])
40
+
41
+ type
42
+ end
35
43
  end
36
44
 
37
45
  def call(val)
@@ -9,8 +9,6 @@ module Grape
9
9
  # Takes the given array and converts it to a set. Every element of the set
10
10
  # is also coerced.
11
11
  class SetCoercer < ArrayCoercer
12
- register_collection Set
13
-
14
12
  def initialize(type, strict = false)
15
13
  super
16
14
 
@@ -1,13 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'types/build_coercer'
4
- require_relative 'types/custom_type_coercer'
5
- require_relative 'types/custom_type_collection_coercer'
6
- require_relative 'types/multiple_type_coercer'
7
- require_relative 'types/variant_collection_coercer'
8
- require_relative 'types/json'
9
- require_relative 'types/file'
10
- require_relative 'types/invalid_value'
3
+ require 'grape/validations/types/json'
4
+ require 'grape/validations/types/file'
11
5
 
12
6
  module Grape
13
7
  module Validations
@@ -22,7 +16,8 @@ module Grape
22
16
  # and {Grape::Dsl::Parameters#optional}. The main
23
17
  # entry point for this process is {Types.build_coercer}.
24
18
  module Types
25
- # Types representing a single value, which are coerced.
19
+ module_function
20
+
26
21
  PRIMITIVES = [
27
22
  # Numerical
28
23
  Integer,
@@ -44,33 +39,23 @@ module Grape
44
39
  ].freeze
45
40
 
46
41
  # Types representing data structures.
47
- STRUCTURES = [
48
- Hash,
49
- Array,
50
- Set
51
- ].freeze
42
+ STRUCTURES = [Hash, Array, Set].freeze
52
43
 
53
- # Special custom types provided by Grape.
54
44
  SPECIAL = {
55
- JSON => Json,
45
+ ::JSON => Json,
56
46
  Array[JSON] => JsonArray,
57
47
  ::File => File,
58
48
  Rack::Multipart::UploadedFile => File
59
49
  }.freeze
60
50
 
61
- GROUPS = [
62
- Array,
63
- Hash,
64
- JSON,
65
- Array[JSON]
66
- ].freeze
51
+ GROUPS = [Array, Hash, JSON, Array[JSON]].freeze
67
52
 
68
53
  # Is the given class a primitive type as recognized by Grape?
69
54
  #
70
55
  # @param type [Class] type to check
71
56
  # @return [Boolean] whether or not the type is known by Grape as a valid
72
57
  # type for a single value
73
- def self.primitive?(type)
58
+ def primitive?(type)
74
59
  PRIMITIVES.include?(type)
75
60
  end
76
61
 
@@ -80,7 +65,7 @@ module Grape
80
65
  # @param type [Class] type to check
81
66
  # @return [Boolean] whether or not the type is known by Grape as a valid
82
67
  # data structure type
83
- def self.structure?(type)
68
+ def structure?(type)
84
69
  STRUCTURES.include?(type)
85
70
  end
86
71
 
@@ -92,7 +77,7 @@ module Grape
92
77
  # @param type [Array<Class>,Set<Class>] type (or type list!) to check
93
78
  # @return [Boolean] +true+ if the given value will be treated as
94
79
  # a list of types.
95
- def self.multiple?(type)
80
+ def multiple?(type)
96
81
  (type.is_a?(Array) || type.is_a?(Set)) && type.size > 1
97
82
  end
98
83
 
@@ -103,7 +88,7 @@ module Grape
103
88
  #
104
89
  # @param type [Class] type to check
105
90
  # @return [Boolean] +true+ if special routines are available
106
- def self.special?(type)
91
+ def special?(type)
107
92
  SPECIAL.key? type
108
93
  end
109
94
 
@@ -112,7 +97,7 @@ module Grape
112
97
  #
113
98
  # @param type [Array<Class>,Class] type to check
114
99
  # @return [Boolean] +true+ if the type is a supported group type
115
- def self.group?(type)
100
+ def group?(type)
116
101
  GROUPS.include? type
117
102
  end
118
103
 
@@ -121,7 +106,7 @@ module Grape
121
106
  #
122
107
  # @param type [Class] type to check
123
108
  # @return [Boolean] whether or not the type can be used as a custom type
124
- def self.custom?(type)
109
+ def custom?(type)
125
110
  !primitive?(type) &&
126
111
  !structure?(type) &&
127
112
  !multiple?(type) &&
@@ -134,15 +119,98 @@ module Grape
134
119
  # @param type [Array<Class>,Class] type to check
135
120
  # @return [Boolean] true if +type+ is a collection of a type that implements
136
121
  # its own +#parse+ method.
137
- def self.collection_of_custom?(type)
122
+ def collection_of_custom?(type)
138
123
  (type.is_a?(Array) || type.is_a?(Set)) &&
139
124
  type.length == 1 &&
140
125
  (custom?(type.first) || special?(type.first))
141
126
  end
142
127
 
143
- def self.map_special(type)
128
+ def map_special(type)
144
129
  SPECIAL.fetch(type, type)
145
130
  end
131
+
132
+ # Chooses the best coercer for the given type. For example, if the type
133
+ # is Integer, it will return a coercer which will be able to coerce a value
134
+ # to the integer.
135
+ #
136
+ # There are a few very special coercers which might be returned.
137
+ #
138
+ # +Grape::Types::MultipleTypeCoercer+ is a coercer which is returned when
139
+ # the given type implies values in an array with different types.
140
+ # For example, +[Integer, String]+ allows integer and string values in
141
+ # an array.
142
+ #
143
+ # +Grape::Types::CustomTypeCoercer+ is a coercer which is returned when
144
+ # a method is specified by a user with +coerce_with+ option or the user
145
+ # specifies a custom type which implements requirments of
146
+ # +Grape::Types::CustomTypeCoercer+.
147
+ #
148
+ # +Grape::Types::CustomTypeCollectionCoercer+ is a very similar to the
149
+ # previous one, but it expects an array or set of values having a custom
150
+ # type implemented by the user.
151
+ #
152
+ # There is also a group of custom types implemented by Grape, check
153
+ # +Grape::Validations::Types::SPECIAL+ to get the full list.
154
+ #
155
+ # @param type [Class] the type to which input strings
156
+ # should be coerced
157
+ # @param method [Class,#call] the coercion method to use
158
+ # @return [Object] object to be used
159
+ # for coercion and type validation
160
+ def build_coercer(type, method: nil, strict: false)
161
+ cache_instance(type, method, strict) do
162
+ create_coercer_instance(type, method, strict)
163
+ end
164
+ end
165
+
166
+ def create_coercer_instance(type, method, strict)
167
+ # Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!!
168
+ type = Types.map_special(type)
169
+
170
+ # Use a special coercer for multiply-typed parameters.
171
+ if Types.multiple?(type)
172
+ MultipleTypeCoercer.new(type, method)
173
+
174
+ # Use a special coercer for custom types and coercion methods.
175
+ elsif method || Types.custom?(type)
176
+ CustomTypeCoercer.new(type, method)
177
+
178
+ # Special coercer for collections of types that implement a parse method.
179
+ # CustomTypeCoercer (above) already handles such types when an explicit coercion
180
+ # method is supplied.
181
+ elsif Types.collection_of_custom?(type)
182
+ Types::CustomTypeCollectionCoercer.new(
183
+ Types.map_special(type.first), type.is_a?(Set)
184
+ )
185
+ else
186
+ DryTypeCoercer.coercer_instance_for(type, strict)
187
+ end
188
+ end
189
+
190
+ def cache_instance(type, method, strict, &_block)
191
+ key = cache_key(type, method, strict)
192
+
193
+ return @__cache[key] if @__cache.key?(key)
194
+
195
+ instance = yield
196
+
197
+ @__cache_write_lock.synchronize do
198
+ @__cache[key] = instance
199
+ end
200
+
201
+ instance
202
+ end
203
+
204
+ def cache_key(type, method, strict)
205
+ [type, method, strict].each_with_object(+'_') do |val, memo|
206
+ next if val.nil?
207
+
208
+ memo << '_' << val.to_s
209
+ end
210
+ end
211
+
212
+ instance_variable_set(:@__cache, {})
213
+ instance_variable_set(:@__cache_write_lock, Mutex.new)
146
214
  end
147
215
  end
148
216
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/validations/validators/multiple_params_base'
4
-
5
3
  module Grape
6
4
  module Validations
7
5
  module Validators
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/validations/validators/multiple_params_base'
4
-
5
3
  module Grape
6
4
  module Validations
7
5
  module Validators
@@ -93,3 +93,10 @@ module Grape
93
93
  end
94
94
  end
95
95
  end
96
+
97
+ Grape::Validations::Base = Class.new(Grape::Validations::Validators::Base) do
98
+ def initialize(*)
99
+ super
100
+ warn '[DEPRECATION] `Grape::Validations::Base` is deprecated. Use `Grape::Validations::Validators::Base` instead.'
101
+ end
102
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/validations/validators/multiple_params_base'
4
-
5
3
  module Grape
6
4
  module Validations
7
5
  module Validators
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/validations/validators/multiple_params_base'
4
-
5
3
  module Grape
6
4
  module Validations
7
5
  module Validators
@@ -1,30 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/validations/attributes_iterator'
4
- require 'grape/validations/single_attribute_iterator'
5
- require 'grape/validations/multiple_attributes_iterator'
6
- require 'grape/validations/params_scope'
7
- require 'grape/validations/types'
8
-
9
3
  module Grape
10
4
  # Registry to store and locate known Validators.
11
5
  module Validations
12
- class << self
13
- attr_accessor :validators
14
- end
6
+ module_function
15
7
 
16
- self.validators = {}
8
+ def validators
9
+ @validators ||= {}
10
+ end
17
11
 
18
12
  # Register a new validator, so it can be used to validate parameters.
19
13
  # @param short_name [String] all lower-case, no spaces
20
14
  # @param klass [Class] the validator class. Should inherit from
21
15
  # Validations::Base.
22
- def self.register_validator(short_name, klass)
16
+ def register_validator(short_name, klass)
23
17
  validators[short_name] = klass
24
18
  end
25
19
 
26
- def self.deregister_validator(short_name)
20
+ def deregister_validator(short_name)
27
21
  validators.delete(short_name)
28
22
  end
23
+
24
+ # Find a validator and if not found will try to load it
25
+ def require_validator(short_name)
26
+ str_name = short_name.to_s
27
+ validators.fetch(str_name) do
28
+ Grape::Validations::Validators.const_get("#{str_name.camelize}Validator")
29
+ end
30
+ rescue NameError
31
+ raise Grape::Exceptions::UnknownValidator.new(short_name)
32
+ end
29
33
  end
30
34
  end
data/lib/grape/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Grape
4
4
  # The current version of Grape.
5
- VERSION = '1.6.2'
5
+ VERSION = '1.7.1'
6
6
  end
data/lib/grape.rb CHANGED
@@ -7,19 +7,22 @@ require 'rack/accept'
7
7
  require 'rack/auth/basic'
8
8
  require 'rack/auth/digest/md5'
9
9
  require 'set'
10
+ require 'bigdecimal'
11
+ require 'date'
10
12
  require 'active_support'
13
+ require 'active_support/concern'
11
14
  require 'active_support/version'
12
15
  require 'active_support/isolated_execution_state' if ActiveSupport::VERSION::MAJOR > 6
13
- require 'active_support/core_ext/hash/indifferent_access'
14
- require 'active_support/core_ext/object/blank'
16
+ require 'active_support/core_ext/array/conversions'
15
17
  require 'active_support/core_ext/array/extract_options'
16
18
  require 'active_support/core_ext/array/wrap'
17
- require 'active_support/core_ext/array/conversions'
19
+ require 'active_support/core_ext/hash/conversions'
18
20
  require 'active_support/core_ext/hash/deep_merge'
19
- require 'active_support/core_ext/hash/reverse_merge'
20
21
  require 'active_support/core_ext/hash/except'
22
+ require 'active_support/core_ext/hash/indifferent_access'
23
+ require 'active_support/core_ext/hash/reverse_merge'
21
24
  require 'active_support/core_ext/hash/slice'
22
- require 'active_support/core_ext/hash/conversions'
25
+ require 'active_support/core_ext/object/blank'
23
26
  require 'active_support/dependencies/autoload'
24
27
  require 'active_support/notifications'
25
28
  require 'i18n'
@@ -45,6 +48,7 @@ module Grape
45
48
  autoload :Env, 'grape/util/env'
46
49
  autoload :Json, 'grape/util/json'
47
50
  autoload :Xml, 'grape/util/xml'
51
+ autoload :DryTypes
48
52
  end
49
53
 
50
54
  module Http
@@ -71,14 +75,17 @@ module Grape
71
75
  autoload :UnknownParameter
72
76
  autoload :InvalidWithOptionForRepresent
73
77
  autoload :IncompatibleOptionValues
74
- autoload :MissingGroupTypeError, 'grape/exceptions/missing_group_type'
75
- autoload :UnsupportedGroupTypeError, 'grape/exceptions/unsupported_group_type'
78
+ autoload :MissingGroupType
79
+ autoload :UnsupportedGroupType
76
80
  autoload :InvalidMessageBody
77
81
  autoload :InvalidAcceptHeader
78
82
  autoload :InvalidVersionHeader
79
83
  autoload :MethodNotAllowed
80
84
  autoload :InvalidResponse
81
85
  autoload :EmptyMessageBody
86
+ autoload :TooManyMultipartFiles
87
+ autoload :MissingGroupTypeError, 'grape/exceptions/missing_group_type'
88
+ autoload :UnsupportedGroupTypeError, 'grape/exceptions/unsupported_group_type'
82
89
  end
83
90
  end
84
91
 
@@ -218,6 +225,66 @@ module Grape
218
225
  autoload :StreamResponse
219
226
  end
220
227
  end
228
+
229
+ module Validations
230
+ extend ::ActiveSupport::Autoload
231
+
232
+ eager_autoload do
233
+ autoload :AttributesIterator
234
+ autoload :MultipleAttributesIterator
235
+ autoload :SingleAttributeIterator
236
+ autoload :Types
237
+ autoload :ParamsScope
238
+ autoload :ValidatorFactory
239
+ autoload :Base, 'grape/validations/validators/base'
240
+ end
241
+
242
+ module Types
243
+ extend ::ActiveSupport::Autoload
244
+
245
+ eager_autoload do
246
+ autoload :InvalidValue
247
+ autoload :DryTypeCoercer
248
+ autoload :ArrayCoercer
249
+ autoload :SetCoercer
250
+ autoload :PrimitiveCoercer
251
+ autoload :CustomTypeCoercer
252
+ autoload :CustomTypeCollectionCoercer
253
+ autoload :MultipleTypeCoercer
254
+ autoload :VariantCollectionCoercer
255
+ end
256
+ end
257
+
258
+ module Validators
259
+ extend ::ActiveSupport::Autoload
260
+
261
+ eager_autoload do
262
+ autoload :Base
263
+ autoload :MultipleParamsBase
264
+ autoload :AllOrNoneOfValidator
265
+ autoload :AllowBlankValidator
266
+ autoload :AsValidator
267
+ autoload :AtLeastOneOfValidator
268
+ autoload :CoerceValidator
269
+ autoload :DefaultValidator
270
+ autoload :ExactlyOneOfValidator
271
+ autoload :ExceptValuesValidator
272
+ autoload :MutualExclusionValidator
273
+ autoload :PresenceValidator
274
+ autoload :RegexpValidator
275
+ autoload :SameAsValidator
276
+ autoload :ValuesValidator
277
+ end
278
+ end
279
+ end
280
+
281
+ module Types
282
+ extend ::ActiveSupport::Autoload
283
+
284
+ eager_autoload do
285
+ autoload :InvalidValue
286
+ end
287
+ end
221
288
  end
222
289
 
223
290
  require 'grape/config'
@@ -227,25 +294,4 @@ require 'grape/util/lazy_value'
227
294
  require 'grape/util/lazy_block'
228
295
  require 'grape/util/endpoint_configuration'
229
296
 
230
- require 'grape/validations/validators/base'
231
- require 'grape/validations/attributes_iterator'
232
- require 'grape/validations/single_attribute_iterator'
233
- require 'grape/validations/multiple_attributes_iterator'
234
- require 'grape/validations/validators/allow_blank'
235
- require 'grape/validations/validators/as'
236
- require 'grape/validations/validators/at_least_one_of'
237
- require 'grape/validations/validators/coerce'
238
- require 'grape/validations/validators/default'
239
- require 'grape/validations/validators/exactly_one_of'
240
- require 'grape/validations/validators/mutual_exclusion'
241
- require 'grape/validations/validators/presence'
242
- require 'grape/validations/validators/regexp'
243
- require 'grape/validations/validators/same_as'
244
- require 'grape/validations/validators/values'
245
- require 'grape/validations/validators/except_values'
246
- require 'grape/validations/params_scope'
247
- require 'grape/validations/validators/all_or_none'
248
- require 'grape/validations/types'
249
- require 'grape/validations/validator_factory'
250
-
251
297
  require 'grape/version'
@@ -1,8 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
4
-
5
3
  describe Grape::Validations do
4
+ context 'deprecated Grape::Validations::Base' do
5
+ subject do
6
+ Class.new(Grape::API) do
7
+ params do
8
+ requires :text, validator_with_old_base: true
9
+ end
10
+ get do
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:validator_with_old_base) do
16
+ Class.new(Grape::Validations::Base) do
17
+ def validate_param!(_attr_name, _params)
18
+ true
19
+ end
20
+ end
21
+ end
22
+
23
+ before do
24
+ described_class.register_validator('validator_with_old_base', validator_with_old_base)
25
+ allow(Warning).to receive(:warn)
26
+ end
27
+
28
+ after do
29
+ described_class.deregister_validator('validator_with_old_base')
30
+ end
31
+
32
+ def app
33
+ subject
34
+ end
35
+
36
+ it 'puts a deprecation warning' do
37
+ expect(Warning).to receive(:warn) do |message|
38
+ expect(message).to include('`Grape::Validations::Base` is deprecated')
39
+ end
40
+
41
+ get '/'
42
+ end
43
+ end
44
+
6
45
  context 'using a custom length validator' do
7
46
  subject do
8
47
  Class.new(Grape::API) do
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
4
-
5
3
  module DeeplyIncludedOptionsSpec
6
4
  module Defaults
7
5
  extend ActiveSupport::Concern
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
4
-
5
3
  describe Grape::API::Instance do
6
4
  describe 'boolean constant' do
7
5
  module DefinesBooleanInstanceSpec
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Grape::API do
6
+ subject { Class.new(described_class) }
7
+
8
+ let(:app) { subject }
9
+
10
+ context 'an endpoint with documentation' do
11
+ it 'documents parameters' do
12
+ subject.params do
13
+ requires 'price', type: Float, desc: 'Sales price'
14
+ end
15
+ subject.get '/'
16
+
17
+ expect(subject.routes.first.params['price']).to eq(required: true,
18
+ type: 'Float',
19
+ desc: 'Sales price')
20
+ end
21
+
22
+ it 'allows documentation with a hash' do
23
+ documentation = { example: 'Joe' }
24
+
25
+ subject.params do
26
+ requires 'first_name', documentation: documentation
27
+ end
28
+ subject.get '/'
29
+
30
+ expect(subject.routes.first.params['first_name'][:documentation]).to eq(documentation)
31
+ end
32
+ end
33
+
34
+ context 'an endpoint without documentation' do
35
+ before do
36
+ subject.do_not_document!
37
+
38
+ subject.params do
39
+ requires :city, type: String, desc: 'Should be ignored'
40
+ optional :postal_code, type: Integer
41
+ end
42
+ subject.post '/' do
43
+ declared(params).to_json
44
+ end
45
+ end
46
+
47
+ it 'does not document parameters for the endpoint' do
48
+ expect(subject.routes.first.params).to eq({})
49
+ end
50
+
51
+ it 'still declares params internally' do
52
+ data = { city: 'Berlin', postal_code: 10_115 }
53
+
54
+ post '/', data
55
+
56
+ expect(last_response.body).to eq(data.to_json)
57
+ end
58
+ end
59
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
4
-
5
3
  describe Grape::API::Helpers do
6
4
  let(:user) { 'Miguel Caneo' }
7
5
  let(:id) { '42' }