Module: WebFunction::Type

Defined in:
lib/web_function/type.rb,
lib/web_function/type/any.rb,
lib/web_function/type/base.rb,
lib/web_function/type/union.rb,
lib/web_function/type/array_of.rb

Defined Under Namespace

Classes: Any, ArrayOf, Base, Union

Constant Summary collapse

ALLOWED_REFINEMENTS =
{
  "number" => %w[u32 u64 i32 i64 f32 f64 timestamp].freeze,
  "string" => %w[date time datetime uuid base64 email phone url uri ipv4 ipv6 hostname].freeze,
}.freeze
REFINEMENT_VALIDATORS =

Value-level validators for each refinement, keyed by refinement name. Refinement names are unique across base types, so a flat table is enough. Must stay in sync with ALLOWED_REFINEMENTS.

{
  "u32" => ->(v) { v.is_a?(Integer) && v.between?(0, 0xFFFFFFFF) },
  "u64" => ->(v) { v.is_a?(Integer) && v.between?(0, 0xFFFFFFFFFFFFFFFF) },
  "i32" => ->(v) { v.is_a?(Integer) && v.between?(-0x80000000, 0x7FFFFFFF) },
  "i64" => ->(v) { v.is_a?(Integer) && v.between?(-0x8000000000000000, 0x7FFFFFFFFFFFFFFF) },
  "f32" => ->(v) { v.is_a?(Numeric) && v.to_f.finite? && v.to_f.abs <= 3.4028235e38 },
  "f64" => ->(v) { v.is_a?(Numeric) && v.to_f.finite? },
  "timestamp" => ->(v) { v.is_a?(Integer) && v >= 0 },
  "date" => ->(v) { v.match?(/\A\d{4}-\d{2}-\d{2}\z/) },
  "time" => ->(v) { v.match?(/\A\d{2}:\d{2}:\d{2}(\.\d+)?\z/) },
  "datetime" => ->(v) { v.match?(/\A\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?\z/) },
  "uuid" => ->(v) { v.match?(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/) },
  "base64" => ->(v) { (v.length % 4).zero? && v.match?(%r{\A[A-Za-z0-9+/]*={0,2}\z}) },
  "email" => ->(v) { v.match?(/\A[^@\s]+@[^@\s]+\.[^@\s]+\z/) },
  "phone" => ->(v) { v.match?(/\A\+[1-9]\d{1,14}\z/) },
  "url" => ->(v) { (uri = URI.parse(v)).is_a?(URI::HTTP) && !uri.host.nil? rescue false },
  "uri" => ->(v) { !URI.parse(v).scheme.nil? rescue false },
  "ipv4" => ->(v) { IPAddr.new(v).ipv4? rescue false },
  "ipv6" => ->(v) { IPAddr.new(v).ipv6? rescue false },
  "hostname" => lambda do |v|
    v.match?(%r{\A(?=.{1,253}\z)([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z})
  end,
}.freeze

Class Method Summary collapse

Class Method Details

.anyObject



136
137
138
# File 'lib/web_function/type.rb', line 136

def self.any
  Any.new
end

.array(of = any) ⇒ Object



114
115
116
# File 'lib/web_function/type.rb', line 114

def self.array(of = any)
  ArrayOf.new(of)
end

.base(type) ⇒ Object



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
# File 'lib/web_function/type.rb', line 71

def self.base(type)
  base_type, refinement = type.split(".", 2)

  case base_type
  when "string"
    if refinement && !ALLOWED_REFINEMENTS["string"].include?(refinement)
      return Type.string
    end

    Type.string(refinement)
  when "number"
    if refinement && !ALLOWED_REFINEMENTS["number"].include?(refinement)
      return Type.number
    end

    Type.number(refinement)
  when "object"
    Type.object(refinement)
  when "array"
    Type.array
  when "boolean"
    Type.boolean
  when "null"
    Type.null
  when "any"
    Type.any
  else
    nil
  end
end

.booleanObject



118
119
120
# File 'lib/web_function/type.rb', line 118

def self.boolean
  Base.new(base_type: "boolean")
end

.detect(raw) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/web_function/type.rb', line 54

def self.detect(raw)
  case raw
  when String
    Type.base(raw)
  when Array
    types = raw.map { |type| Type.detect(type) }.compact

    if types.empty?
      return Type.array(Type.any)
    end

    Type.array(Type.union(types))
  else
    return nil
  end
end

.nullObject



122
123
124
# File 'lib/web_function/type.rb', line 122

def self.null
  Base.new(base_type: "null")
end

.number(refinement = nil) ⇒ Object



106
107
108
# File 'lib/web_function/type.rb', line 106

def self.number(refinement = nil)
  Base.new(base_type: "number", refinement: refinement)
end

.object(refinement = nil) ⇒ Object



110
111
112
# File 'lib/web_function/type.rb', line 110

def self.object(refinement = nil)
  Base.new(base_type: "object", refinement: refinement)
end

.parse(raw) ⇒ Object



44
45
46
47
48
49
50
51
52
# File 'lib/web_function/type.rb', line 44

def self.parse(raw)
  types = [*raw].map { |type| Type.detect(type) }.compact

  if types.empty?
    return Type.any
  end

  Type.union(types)
end

.string(refinement = nil) ⇒ Object



102
103
104
# File 'lib/web_function/type.rb', line 102

def self.string(refinement = nil)
  Base.new(base_type: "string", refinement: refinement)
end

.union(types) ⇒ Object



126
127
128
129
130
131
132
133
134
# File 'lib/web_function/type.rb', line 126

def self.union(types)
  types = types.uniq

  if types.length > 1
    Union.new(types)
  else
    types.first
  end
end