|
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' |
11 |
| - |
12 | 3 | module Grape
|
13 | 4 | module Validations
|
14 | 5 | # Module for code related to grape's system for
|
@@ -143,6 +134,89 @@ def self.collection_of_custom?(type)
|
143 | 134 | def self.map_special(type)
|
144 | 135 | SPECIAL.fetch(type, type)
|
145 | 136 | end
|
| 137 | + |
| 138 | + # Chooses the best coercer for the given type. For example, if the type |
| 139 | + # is Integer, it will return a coercer which will be able to coerce a value |
| 140 | + # to the integer. |
| 141 | + # |
| 142 | + # There are a few very special coercers which might be returned. |
| 143 | + # |
| 144 | + # +Grape::Types::MultipleTypeCoercer+ is a coercer which is returned when |
| 145 | + # the given type implies values in an array with different types. |
| 146 | + # For example, +[Integer, String]+ allows integer and string values in |
| 147 | + # an array. |
| 148 | + # |
| 149 | + # +Grape::Types::CustomTypeCoercer+ is a coercer which is returned when |
| 150 | + # a method is specified by a user with +coerce_with+ option or the user |
| 151 | + # specifies a custom type which implements requirments of |
| 152 | + # +Grape::Types::CustomTypeCoercer+. |
| 153 | + # |
| 154 | + # +Grape::Types::CustomTypeCollectionCoercer+ is a very similar to the |
| 155 | + # previous one, but it expects an array or set of values having a custom |
| 156 | + # type implemented by the user. |
| 157 | + # |
| 158 | + # There is also a group of custom types implemented by Grape, check |
| 159 | + # +Grape::Validations::Types::SPECIAL+ to get the full list. |
| 160 | + # |
| 161 | + # @param type [Class] the type to which input strings |
| 162 | + # should be coerced |
| 163 | + # @param method [Class,#call] the coercion method to use |
| 164 | + # @return [Object] object to be used |
| 165 | + # for coercion and type validation |
| 166 | + def self.build_coercer(type, method: nil, strict: false) |
| 167 | + cache_instance(type, method, strict) do |
| 168 | + create_coercer_instance(type, method, strict) |
| 169 | + end |
| 170 | + end |
| 171 | + |
| 172 | + def self.create_coercer_instance(type, method, strict) |
| 173 | + # Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!! |
| 174 | + type = Types.map_special(type) |
| 175 | + |
| 176 | + # Use a special coercer for multiply-typed parameters. |
| 177 | + if Types.multiple?(type) |
| 178 | + MultipleTypeCoercer.new(type, method) |
| 179 | + |
| 180 | + # Use a special coercer for custom types and coercion methods. |
| 181 | + elsif method || Types.custom?(type) |
| 182 | + CustomTypeCoercer.new(type, method) |
| 183 | + |
| 184 | + # Special coercer for collections of types that implement a parse method. |
| 185 | + # CustomTypeCoercer (above) already handles such types when an explicit coercion |
| 186 | + # method is supplied. |
| 187 | + elsif Types.collection_of_custom?(type) |
| 188 | + Types::CustomTypeCollectionCoercer.new( |
| 189 | + Types.map_special(type.first), type.is_a?(Set) |
| 190 | + ) |
| 191 | + else |
| 192 | + DryTypeCoercer.coercer_instance_for(type, strict) |
| 193 | + end |
| 194 | + end |
| 195 | + |
| 196 | + def self.cache_instance(type, method, strict, &_block) |
| 197 | + key = cache_key(type, method, strict) |
| 198 | + |
| 199 | + return @__cache[key] if @__cache.key?(key) |
| 200 | + |
| 201 | + instance = yield |
| 202 | + |
| 203 | + @__cache_write_lock.synchronize do |
| 204 | + @__cache[key] = instance |
| 205 | + end |
| 206 | + |
| 207 | + instance |
| 208 | + end |
| 209 | + |
| 210 | + def self.cache_key(type, method, strict) |
| 211 | + [type, method, strict].each_with_object(+'_') do |val, memo| |
| 212 | + next if val.nil? |
| 213 | + |
| 214 | + memo << '_' << val.to_s |
| 215 | + end |
| 216 | + end |
| 217 | + |
| 218 | + instance_variable_set(:@__cache, {}) |
| 219 | + instance_variable_set(:@__cache_write_lock, Mutex.new) |
146 | 220 | end
|
147 | 221 | end
|
148 | 222 | end
|
0 commit comments