File: types.rb

package info (click to toggle)
ruby-grape 1.6.2-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 2,156 kB
  • sloc: ruby: 25,265; makefile: 7
file content (148 lines) | stat: -rw-r--r-- 4,659 bytes parent folder | download
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
# 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'
require_relative 'types/invalid_value'

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
      # 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,
        TrueClass,
        FalseClass
      ].freeze

      # Types representing data structures.
      STRUCTURES = [
        Hash,
        Array,
        Set
      ].freeze

      # Special custom types provided by Grape.
      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) &&
          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) || special?(type.first))
      end

      def self.map_special(type)
        SPECIAL.fetch(type, type)
      end
    end
  end
end