Skip to content

Commit 224402f

Browse files
authored
Autoload Types (#2209)
1 parent efc25f2 commit 224402f

9 files changed

+121
-125
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* [#2202](https://github.com/ruby-grape/grape/pull/2202): Fix random mock spec error - [@ericproulx](https://github.com/ericproulx).
1616
* [#2203](https://github.com/ruby-grape/grape/pull/2203): Add rubocop-rspec - [@ericproulx](https://github.com/ericproulx).
1717
* [#2207](https://github.com/ruby-grape/grape/pull/2207): Autoload Validations/Validators - [@ericproulx](https://github.com/ericproulx).
18+
* [#2209](https://github.com/ruby-grape/grape/pull/2209): Autoload Validations/Types - [@ericproulx](https://github.com/ericproulx).
1819
* Your contribution here.
1920

2021
### 1.6.0 (2021/10/04)

lib/grape.rb

+19
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ module Grape
4545
autoload :Env, 'grape/util/env'
4646
autoload :Json, 'grape/util/json'
4747
autoload :Xml, 'grape/util/xml'
48+
autoload :DryTypes
4849
end
4950

5051
module Http
@@ -222,6 +223,24 @@ module ServeStream
222223
module Validations
223224
extend ::ActiveSupport::Autoload
224225

226+
module Types
227+
extend ::ActiveSupport::Autoload
228+
229+
eager_autoload do
230+
autoload :InvalidValue
231+
autoload :File
232+
autoload :Json
233+
autoload :DryTypeCoercer
234+
autoload :ArrayCoercer
235+
autoload :SetCoercer
236+
autoload :PrimitiveCoercer
237+
autoload :CustomTypeCoercer
238+
autoload :CustomTypeCollectionCoercer
239+
autoload :MultipleTypeCoercer
240+
autoload :VariantCollectionCoercer
241+
end
242+
end
243+
225244
eager_autoload do
226245
autoload :AttributesIterator
227246
autoload :MultipleAttributesIterator

lib/grape/dry_types.rb

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
require 'dry-types'
4+
5+
module Grape
6+
module DryTypes
7+
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
8+
# a container in this case. Check documentation for more information
9+
# https://dry-rb.org/gems/dry-types/1.2/getting-started/
10+
include Dry.Types()
11+
end
12+
end

lib/grape/validations/types.rb

+83-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
# frozen_string_literal: true
22

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-
123
module Grape
134
module Validations
145
# Module for code related to grape's system for
@@ -143,6 +134,89 @@ def self.collection_of_custom?(type)
143134
def self.map_special(type)
144135
SPECIAL.fetch(type, type)
145136
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)
146220
end
147221
end
148222
end

lib/grape/validations/types/array_coercer.rb

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# frozen_string_literal: true
22

3-
require_relative 'dry_type_coercer'
4-
53
module Grape
64
module Validations
75
module Types

lib/grape/validations/types/build_coercer.rb

-94
This file was deleted.

lib/grape/validations/types/dry_type_coercer.rb

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
# frozen_string_literal: true
22

3-
require 'dry-types'
4-
5-
module DryTypes
6-
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
7-
# a container in this case. Check documentation for more information
8-
# https://dry-rb.org/gems/dry-types/1.2/getting-started/
9-
include Dry.Types()
10-
end
11-
123
module Grape
134
module Validations
145
module Types
@@ -52,7 +43,7 @@ def collection_coercers
5243
def initialize(type, strict = false)
5344
@type = type
5445
@strict = strict
55-
@scope = strict ? DryTypes::Strict : DryTypes::Params
46+
@scope = strict ? Grape::DryTypes::Strict : Grape::DryTypes::Params
5647
end
5748

5849
# Coerces the given value to a type which was specified during

lib/grape/validations/types/primitive_coercer.rb

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# frozen_string_literal: true
22

3-
require_relative 'dry_type_coercer'
4-
53
module Grape
64
module Validations
75
module Types
@@ -10,16 +8,16 @@ module Types
108
# that it has the proper type.
119
class PrimitiveCoercer < DryTypeCoercer
1210
MAPPING = {
13-
Grape::API::Boolean => DryTypes::Params::Bool,
14-
BigDecimal => DryTypes::Params::Decimal,
11+
Grape::API::Boolean => Grape::DryTypes::Params::Bool,
12+
BigDecimal => Grape::DryTypes::Params::Decimal,
1513

1614
# unfortunately, a +Params+ scope doesn't contain String
17-
String => DryTypes::Coercible::String
15+
String => Grape::DryTypes::Coercible::String
1816
}.freeze
1917

2018
STRICT_MAPPING = {
21-
Grape::API::Boolean => DryTypes::Strict::Bool,
22-
BigDecimal => DryTypes::Strict::Decimal
19+
Grape::API::Boolean => Grape::DryTypes::Strict::Bool,
20+
BigDecimal => Grape::DryTypes::Strict::Decimal
2321
}.freeze
2422

2523
def initialize(type, strict = false)

lib/grape/validations/types/set_coercer.rb

-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# frozen_string_literal: true
22

3-
require 'set'
4-
require_relative 'array_coercer'
5-
63
module Grape
74
module Validations
85
module Types

0 commit comments

Comments
 (0)