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
|
# frozen_string_literal: true
module Dry
module Types
# Common API for building type objects in a convenient way
#
# rubocop:disable Naming/MethodName
#
# @api public
module BuilderMethods
# @api private
def included(base)
super
base.extend(BuilderMethods)
end
# Build an array type.
#
# Shortcut for Array#of.
#
# @example
# Types::Strings = Types.Array(Types::String)
#
# @param [Dry::Types::Type] type
#
# @return [Dry::Types::Array]
def Array(type)
Strict(::Array).of(type)
end
# Build a hash schema
#
# @param [Hash{Symbol => Dry::Types::Type}] type_map
#
# @return [Dry::Types::Array]
def Hash(type_map)
Strict(::Hash).schema(type_map)
end
# Build a type which values are instances of a given class
# Values are checked using `is_a?` call
#
# @example
# Types::Error = Types.Instance(StandardError)
# Types::Error = Types.Strict(StandardError)
# Types.Strict(Integer) == Types::Strict::Int # => true
#
# @param [Class,Module] klass Class or module
#
# @return [Dry::Types::Type]
def Instance(klass)
Nominal(klass).constrained(type: klass)
end
alias_method :Strict, :Instance
# Build a type with a single value
# The equality check done with `eql?`
#
# @param [Object] value
#
# @return [Dry::Types::Type]
def Value(value)
Nominal(value.class).constrained(eql: value)
end
# Build a type with a single value
# The equality check done with `equal?`
#
# @param [Object] object
#
# @return [Dry::Types::Type]
def Constant(object)
Nominal(object.class).constrained(is: object)
end
# Build a constructor type
# If no constructor block given it uses .new method
#
# @param [Class] klass
# @param [#call,nil] cons Value constructor
# @param [#call,nil] block Value constructor
#
# @return [Dry::Types::Type]
def Constructor(klass, cons = nil, &block)
if klass.is_a?(Type)
if cons || block
klass.constructor(cons || block)
else
klass
end
else
Nominal(klass).constructor(cons || block || klass.method(:new))
end
end
# Build a nominal type
#
# @param [Class] klass
#
# @return [Dry::Types::Type]
def Nominal(klass)
if klass <= ::Array
Array.new(klass)
elsif klass <= ::Hash
Hash.new(klass)
else
Nominal.new(klass)
end
end
# Build a map type
#
# @example
# Types::IntMap = Types.Map(Types::Strict::Integer, 'any')
# Types::IntStringMap = Types.Map(Types::Strict::Integer, Types::Strict::String)
#
# @param [Type] key_type Key type
# @param [Type] value_type Value type
#
# @return [Dry::Types::Map]
def Map(key_type, value_type)
Nominal(::Hash).map(key_type, value_type)
end
# Builds a constrained nominal type accepting any value that
# responds to given methods
#
# @example
# Types::Callable = Types.Interface(:call)
# Types::Contact = Types.Interface(:name, :address)
#
# @param methods [Array<String, Symbol>] Method names
#
# @return [Dry::Types::Contrained]
def Interface(*methods)
methods.reduce(Types['nominal.any']) do |type, method|
type.constrained(respond_to: method)
end
end
end
end
end
|