-
-
Notifications
You must be signed in to change notification settings - Fork 134
/
module.rb
129 lines (113 loc) · 3.99 KB
/
module.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
# frozen_string_literal: true
require "dry/types/builder_methods"
module Dry
module Types
# Export types registered in a container as module constants.
# @example
# module Types
# include Dry.Types(:strict, :coercible, :nominal, default: :strict)
# end
#
# Types.constants
# # => [:Class, :Strict, :Symbol, :Integer, :Float, :String, :Array, :Hash,
# # :Decimal, :Nil, :True, :False, :Bool, :Date, :Nominal, :DateTime, :Range,
# # :Coercible, :Time]
#
# @api public
class Module < ::Module
def initialize(registry, *args, **kwargs)
@registry = registry
check_parameters(*args, **kwargs)
constants = type_constants(*args, **kwargs)
define_constants(constants)
extend(BuilderMethods)
if constants.key?(:Nominal)
singleton_class.define_method(:included) do |base|
super(base)
base.instance_exec(const_get(:Nominal, false)) do |nominal|
extend Dry::Core::Deprecations[:"dry-types"]
const_set(:Definition, nominal)
deprecate_constant(:Definition, message: "Nominal")
end
end
end
end
# @api private
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def type_constants(*namespaces, default: Undefined, **aliases)
if namespaces.empty? && aliases.empty? && Undefined.equal?(default)
default_ns = :Strict
elsif Undefined.equal?(default)
default_ns = Undefined
else
default_ns = Types::Inflector.camelize(default).to_sym
end
tree = registry_tree
if namespaces.empty? && aliases.empty?
modules = tree.select { |_, v| v.is_a?(::Hash) }.map(&:first)
else
modules = (namespaces + aliases.keys).map { |n|
Types::Inflector.camelize(n).to_sym
}
end
tree.each_with_object({}) do |(key, value), constants|
if modules.include?(key)
name = aliases.fetch(Inflector.underscore(key).to_sym, key)
constants[name] = value
end
constants.update(value) if key == default_ns
end
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# @api private
def registry_tree
@registry_tree ||= @registry.keys.each_with_object({}) { |key, tree|
type = @registry[key]
*modules, const_name = key.split(".").map { |part|
Types::Inflector.camelize(part).to_sym
}
next if modules.empty?
modules.reduce(tree) { |br, name| br[name] ||= {} }[const_name] = type
}.freeze
end
private
# @api private
def check_parameters(*namespaces, default: Undefined, **aliases)
referenced = namespaces.dup
referenced << default unless false.equal?(default) || Undefined.equal?(default)
referenced.concat(aliases.keys)
known = @registry.keys.map { |k|
ns, *path = k.split(".")
ns.to_sym unless path.empty?
}.compact.uniq
unknown = (referenced.uniq - known).first
if unknown
raise ArgumentError,
"#{unknown.inspect} is not a known type namespace. "\
"Supported options are #{known.map(&:inspect).join(", ")}"
end
end
# @api private
def define_constants(constants, mod = self)
constants.each do |name, value|
case value
when ::Hash
if mod.const_defined?(name, false)
define_constants(value, mod.const_get(name, false))
else
m = ::Module.new
mod.const_set(name, m)
define_constants(value, m)
end
else
mod.const_set(name, value)
end
end
end
end
end
end