grape 1.6.2 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
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' }