-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathtypes.rb
150 lines (135 loc) · 4.66 KB
/
types.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# frozen_string_literal: true
require_relative 'types/build_coercer'
require_relative 'types/custom_type_coercer'
require_relative 'types/custom_type_collection_coercer'
require_relative 'types/multiple_type_coercer'
require_relative 'types/variant_collection_coercer'
require_relative 'types/json'
require_relative 'types/file'
module Grape
module Validations
# Module for code related to grape's system for
# coercion and type validation of incoming request
# parameters.
#
# Grape uses a number of tests and assertions to
# work out exactly how a parameter should be handled,
# based on the +type+ and +coerce_with+ options that
# may be supplied to {Grape::Dsl::Parameters#requires}
# and {Grape::Dsl::Parameters#optional}. The main
# entry point for this process is {Types.build_coercer}.
module Types
# Instances of this class may be used as tokens to denote that
# a parameter value could not be coerced.
class InvalidValue; end
# Types representing a single value, which are coerced.
PRIMITIVES = [
# Numerical
Integer,
Float,
BigDecimal,
Numeric,
# Date/time
Date,
DateTime,
Time,
# Misc
Grape::API::Boolean,
String,
Symbol,
Rack::Multipart::UploadedFile,
TrueClass,
FalseClass
].freeze
# Types representing data structures.
STRUCTURES = [
Hash,
Array,
Set
].freeze
# Types for which Grape provides special coercion
# and type-checking logic.
SPECIAL = {
JSON => Json,
Array[JSON] => JsonArray,
::File => File,
Rack::Multipart::UploadedFile => File
}.freeze
GROUPS = [
Array,
Hash,
JSON,
Array[JSON]
].freeze
# Is the given class a primitive type as recognized by Grape?
#
# @param type [Class] type to check
# @return [Boolean] whether or not the type is known by Grape as a valid
# type for a single value
def self.primitive?(type)
PRIMITIVES.include?(type)
end
# Is the given class a standard data structure (collection or map)
# as recognized by Grape?
#
# @param type [Class] type to check
# @return [Boolean] whether or not the type is known by Grape as a valid
# data structure type
def self.structure?(type)
STRUCTURES.include?(type)
end
# Is the declared type in fact an array of multiple allowed types?
# For example the declaration +types: [Integer,String]+ will attempt
# first to coerce given values to integer, but will also accept any
# other string.
#
# @param type [Array<Class>,Set<Class>] type (or type list!) to check
# @return [Boolean] +true+ if the given value will be treated as
# a list of types.
def self.multiple?(type)
(type.is_a?(Array) || type.is_a?(Set)) && type.size > 1
end
# Does Grape provide special coercion and validation
# routines for the given class? This does not include
# automatic handling for primitives, structures and
# otherwise recognized types. See {Types::SPECIAL}.
#
# @param type [Class] type to check
# @return [Boolean] +true+ if special routines are available
def self.special?(type)
SPECIAL.key? type
end
# Is the declared type a supported group type?
# Currently supported group types are Array, Hash, JSON, and Array[JSON]
#
# @param type [Array<Class>,Class] type to check
# @return [Boolean] +true+ if the type is a supported group type
def self.group?(type)
GROUPS.include? type
end
# A valid custom type must implement a class-level `parse` method, taking
# one String argument and returning the parsed value in its correct type.
#
# @param type [Class] type to check
# @return [Boolean] whether or not the type can be used as a custom type
def self.custom?(type)
!primitive?(type) &&
!structure?(type) &&
!multiple?(type) &&
!special?(type) &&
type.respond_to?(:parse) &&
type.method(:parse).arity == 1
end
# Is the declared type an +Array+ or +Set+ of a {#custom?} type?
#
# @param type [Array<Class>,Class] type to check
# @return [Boolean] true if +type+ is a collection of a type that implements
# its own +#parse+ method.
def self.collection_of_custom?(type)
(type.is_a?(Array) || type.is_a?(Set)) &&
type.length == 1 &&
custom?(type.first)
end
end
end
end