Module: Grape::Validations::Types

Defined in:
lib/grape/validations/types.rb,
lib/grape/validations/types/file.rb,
lib/grape/validations/types/json.rb,
lib/grape/validations/types/build_coercer.rb,
lib/grape/validations/types/custom_type_coercer.rb,
lib/grape/validations/types/multiple_type_coercer.rb,
lib/grape/validations/types/variant_collection_coercer.rb

Overview

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 Dsl::Parameters#requires and Dsl::Parameters#optional. The main entry point for this process is Types.build_coercer.

Defined Under Namespace

Classes: CustomTypeCoercer, File, InvalidValue, Json, JsonArray, MultipleTypeCoercer, VariantCollectionCoercer

Constant Summary collapse

PRIMITIVES =

Types representing a single value, which are coerced through Virtus or special logic in Grape.

[
  # Numerical
  Integer,
  Float,
  BigDecimal,
  Numeric,

  # Date/time
  Date,
  DateTime,
  Time,

  # Misc
  Virtus::Attribute::Boolean,
  String,
  Symbol,
  Rack::Multipart::UploadedFile
].freeze
STRUCTURES =

Types representing data structures.

[
  Hash,
  Array,
  Set
].freeze
SPECIAL =

Types for which Grape provides special coercion and type-checking logic.

{
  JSON => Json,
  Array[JSON] => JsonArray,
  ::File => File,
  Rack::Multipart::UploadedFile => File
}.freeze
GROUPS =
[
  Array,
  Hash,
  JSON,
  Array[JSON]
].freeze

Class Method Summary collapse

Class Method Details

.build_coercer(type, method = nil) ⇒ Virtus::Attribute

Work out the +Virtus::Attribute+ object to use for coercing strings to the given +type+. Coercion +method+ will be inferred if none is supplied.

If a +Virtus::Attribute+ object already built with +Virtus::Attribute.build+ is supplied as the +type+ it will be returned and +method+ will be ignored.

See CustomTypeCoercer for further details about coercion and type-checking inference.



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
# File 'lib/grape/validations/types/build_coercer.rb', line 22

def self.build_coercer(type, method = nil)
  # Accept pre-rolled virtus attributes without interference
  return type if type.is_a? Virtus::Attribute

  converter_options = {
    nullify_blank: true
  }
  conversion_type = if method == JSON
                      Object
                      # because we want just parsed JSON content:
                      # if type is Array and data is `"{}"`
                      # result will be [] because Virtus converts hashes
                      # to arrays
                    else
                      type
                    end

  # Use a special coercer for multiply-typed parameters.
  if Types.multiple?(type)
    converter_options[:coercer] = Types::MultipleTypeCoercer.new(type, method)
    conversion_type = Object

  # Use a special coercer for custom types and coercion methods.
  elsif method || Types.custom?(type)
    converter_options[:coercer] = Types::CustomTypeCoercer.new(type, method)

  # Grape swaps in its own Virtus::Attribute implementations
  # for certain special types that merit first-class support
  # (but not if a custom coercion method has been supplied).
  elsif Types.special?(type)
    conversion_type = Types::SPECIAL[type]
  end

  # Virtus will infer coercion and validation rules
  # for many common ruby types.
  Virtus::Attribute.build(conversion_type, converter_options)
end

.custom?(type) ⇒ Boolean

A valid custom type must implement a class-level parse method, taking one String argument and returning the parsed value in its correct type.



149
150
151
152
153
154
155
156
157
# File 'lib/grape/validations/types.rb', line 149

def self.custom?(type)
  !primitive?(type) &&
    !structure?(type) &&
    !multiple?(type) &&
    !recognized?(type) &&
    !special?(type) &&
    type.respond_to?(:parse) &&
    type.method(:parse).arity == 1
end

.group?(type) ⇒ Boolean

Is the declared type a supported group type? Currently supported group types are Array, Hash, JSON, and Array[JSON]



141
142
143
# File 'lib/grape/validations/types.rb', line 141

def self.group?(type)
  GROUPS.include? type
end

.multiple?(type) ⇒ Boolean

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.



102
103
104
# File 'lib/grape/validations/types.rb', line 102

def self.multiple?(type)
  (type.is_a?(Array) || type.is_a?(Set)) && type.size > 1
end

.primitive?(type) ⇒ Boolean

Is the given class a primitive type as recognized by Grape?



78
79
80
# File 'lib/grape/validations/types.rb', line 78

def self.primitive?(type)
  PRIMITIVES.include?(type)
end

.recognized?(type) ⇒ Boolean

Does the given class implement a type system that Grape (i.e. the underlying virtus attribute system) supports out-of-the-box? Currently supported are +axiom-types+ and +virtus+.

The type will be passed to +Virtus::Attribute.build+, and the resulting attribute object will be expected to respond correctly to +coerce+ and +value_coerced?+.



117
118
119
120
121
122
123
# File 'lib/grape/validations/types.rb', line 117

def self.recognized?(type)
  return false if type.is_a?(Array) || type.is_a?(Set)

  type.is_a?(Virtus::Attribute) ||
    type.ancestors.include?(Axiom::Types::Type) ||
    type.include?(Virtus::Model::Core)
end

.special?(type) ⇒ Boolean

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 SPECIAL.



132
133
134
# File 'lib/grape/validations/types.rb', line 132

def self.special?(type)
  SPECIAL.key? type
end

.structure?(type) ⇒ Boolean

Note:

This method does not yet consider 'complex types', which inherit Virtus.model.

Is the given class a standard data structure (collection or map) as recognized by Grape?



90
91
92
# File 'lib/grape/validations/types.rb', line 90

def self.structure?(type)
  STRUCTURES.include?(type)
end