dry-types 0.14.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +15 -0
- data/.rubocop.yml +43 -0
- data/.travis.yml +12 -11
- data/CHANGELOG.md +115 -4
- data/Gemfile +2 -3
- data/benchmarks/hash_schemas.rb +5 -5
- data/dry-types.gemspec +1 -1
- data/lib/dry/types.rb +67 -45
- data/lib/dry/types/any.rb +11 -2
- data/lib/dry/types/array.rb +1 -4
- data/lib/dry/types/array/member.rb +2 -2
- data/lib/dry/types/builder.rb +23 -3
- data/lib/dry/types/builder_methods.rb +10 -11
- data/lib/dry/types/coercions/params.rb +2 -0
- data/lib/dry/types/compat.rb +0 -2
- data/lib/dry/types/compiler.rb +25 -33
- data/lib/dry/types/constrained.rb +5 -4
- data/lib/dry/types/constructor.rb +32 -11
- data/lib/dry/types/container.rb +2 -0
- data/lib/dry/types/core.rb +22 -12
- data/lib/dry/types/default.rb +4 -4
- data/lib/dry/types/enum.rb +10 -3
- data/lib/dry/types/errors.rb +1 -1
- data/lib/dry/types/extensions/maybe.rb +11 -1
- data/lib/dry/types/hash.rb +70 -63
- data/lib/dry/types/hash/constructor.rb +20 -0
- data/lib/dry/types/json.rb +7 -7
- data/lib/dry/types/map.rb +6 -1
- data/lib/dry/types/module.rb +115 -0
- data/lib/dry/types/{definition.rb → nominal.rb} +10 -4
- data/lib/dry/types/options.rb +2 -2
- data/lib/dry/types/params.rb +11 -11
- data/lib/dry/types/printable.rb +12 -0
- data/lib/dry/types/printer.rb +309 -0
- data/lib/dry/types/result.rb +2 -2
- data/lib/dry/types/safe.rb +4 -2
- data/lib/dry/types/schema.rb +298 -0
- data/lib/dry/types/schema/key.rb +130 -0
- data/lib/dry/types/spec/types.rb +12 -4
- data/lib/dry/types/sum.rb +14 -15
- data/lib/dry/types/version.rb +1 -1
- metadata +18 -8
- data/lib/dry/types/compat/form_types.rb +0 -27
- data/lib/dry/types/compat/int.rb +0 -14
- data/lib/dry/types/hash/schema.rb +0 -199
- data/lib/dry/types/hash/schema_builder.rb +0 -75
data/lib/dry/types/container.rb
CHANGED
data/lib/dry/types/core.rb
CHANGED
@@ -2,6 +2,7 @@ require 'dry/types/any'
|
|
2
2
|
|
3
3
|
module Dry
|
4
4
|
module Types
|
5
|
+
# Primitives with {Kernel} coercion methods
|
5
6
|
COERCIBLE = {
|
6
7
|
string: String,
|
7
8
|
integer: Integer,
|
@@ -11,6 +12,7 @@ module Dry
|
|
11
12
|
hash: ::Hash
|
12
13
|
}.freeze
|
13
14
|
|
15
|
+
# Primitives that are non-coercible through {Kernel} methods
|
14
16
|
NON_COERCIBLE = {
|
15
17
|
nil: NilClass,
|
16
18
|
symbol: Symbol,
|
@@ -23,41 +25,49 @@ module Dry
|
|
23
25
|
range: Range
|
24
26
|
}.freeze
|
25
27
|
|
28
|
+
# All built-in primitives
|
26
29
|
ALL_PRIMITIVES = COERCIBLE.merge(NON_COERCIBLE).freeze
|
27
30
|
|
31
|
+
# All built-in primitives except {NilClass}
|
28
32
|
NON_NIL = ALL_PRIMITIVES.reject { |name, _| name == :nil }.freeze
|
29
33
|
|
30
|
-
# Register
|
34
|
+
# Register generic types for {ALL_PRIMITIVES}
|
31
35
|
ALL_PRIMITIVES.each do |name, primitive|
|
32
|
-
|
36
|
+
type = Nominal[primitive].new(primitive)
|
37
|
+
register("nominal.#{name}", type)
|
33
38
|
end
|
34
39
|
|
35
|
-
# Register strict
|
40
|
+
# Register strict types for {ALL_PRIMITIVES}
|
36
41
|
ALL_PRIMITIVES.each do |name, primitive|
|
37
|
-
|
42
|
+
type = self["nominal.#{name}"].constrained(type: primitive)
|
43
|
+
register(name.to_s, type)
|
44
|
+
register("strict.#{name}", type)
|
38
45
|
end
|
39
46
|
|
40
|
-
# Register
|
47
|
+
# Register {COERCIBLE} types
|
41
48
|
COERCIBLE.each do |name, primitive|
|
42
|
-
register("coercible.#{name}", self[name
|
49
|
+
register("coercible.#{name}", self["nominal.#{name}"].constructor(Kernel.method(primitive.name)))
|
43
50
|
end
|
44
51
|
|
45
|
-
# Register
|
52
|
+
# Register optional strict {NON_NIL} types
|
46
53
|
NON_NIL.each_key do |name|
|
47
54
|
register("optional.strict.#{name}", self["strict.#{name}"].optional)
|
48
55
|
end
|
49
56
|
|
50
|
-
# Register
|
57
|
+
# Register optional {COERCIBLE} types
|
51
58
|
COERCIBLE.each_key do |name|
|
52
59
|
register("optional.coercible.#{name}", self["coercible.#{name}"].optional)
|
53
60
|
end
|
54
61
|
|
55
|
-
# Register
|
56
|
-
register("bool", self["true"] | self["false"])
|
57
|
-
|
62
|
+
# Register `:bool` since it's common and not a built-in Ruby type :(
|
63
|
+
register("nominal.bool", self["nominal.true"] | self["nominal.false"])
|
64
|
+
bool = self["strict.true"] | self["strict.false"]
|
65
|
+
register("strict.bool", bool)
|
66
|
+
register("bool", bool)
|
58
67
|
|
59
68
|
register("any", Any)
|
60
|
-
register("
|
69
|
+
register("nominal.any", Any)
|
70
|
+
register("strict.any", Any)
|
61
71
|
end
|
62
72
|
end
|
63
73
|
|
data/lib/dry/types/default.rb
CHANGED
@@ -4,12 +4,13 @@ module Dry
|
|
4
4
|
module Types
|
5
5
|
class Default
|
6
6
|
include Type
|
7
|
-
include Dry::Equalizer(:type, :options, :value)
|
8
7
|
include Decorator
|
9
8
|
include Builder
|
9
|
+
include Printable
|
10
|
+
include Dry::Equalizer(:type, :options, :value, inspect: false)
|
10
11
|
|
11
12
|
class Callable < Default
|
12
|
-
include Dry::Equalizer(:type, :options)
|
13
|
+
include Dry::Equalizer(:type, :options, inspect: false)
|
13
14
|
|
14
15
|
# Evaluates given callable
|
15
16
|
# @return [Object]
|
@@ -67,8 +68,7 @@ module Dry
|
|
67
68
|
if input.equal?(Undefined)
|
68
69
|
evaluate
|
69
70
|
else
|
70
|
-
|
71
|
-
output.nil? ? evaluate : output
|
71
|
+
Undefined.default(type[input]) { evaluate }
|
72
72
|
end
|
73
73
|
end
|
74
74
|
alias_method :[], :call
|
data/lib/dry/types/enum.rb
CHANGED
@@ -4,7 +4,7 @@ module Dry
|
|
4
4
|
module Types
|
5
5
|
class Enum
|
6
6
|
include Type
|
7
|
-
include Dry::Equalizer(:type, :options, :mapping)
|
7
|
+
include Dry::Equalizer(:type, :options, :mapping, inspect: false)
|
8
8
|
include Decorator
|
9
9
|
|
10
10
|
# @return [Array]
|
@@ -53,18 +53,25 @@ module Dry
|
|
53
53
|
|
54
54
|
# @api public
|
55
55
|
#
|
56
|
-
# @see
|
56
|
+
# @see Nominal#to_ast
|
57
57
|
def to_ast(meta: true)
|
58
58
|
[:enum, [type.to_ast(meta: meta),
|
59
59
|
mapping,
|
60
60
|
meta ? self.meta : EMPTY_HASH]]
|
61
61
|
end
|
62
62
|
|
63
|
+
# @return [String]
|
64
|
+
# @api public
|
65
|
+
def to_s
|
66
|
+
PRINTER.(self)
|
67
|
+
end
|
68
|
+
alias_method :inspect, :to_s
|
69
|
+
|
63
70
|
private
|
64
71
|
|
65
72
|
# Maps a value
|
66
73
|
#
|
67
|
-
# @
|
74
|
+
# @param [Object]
|
68
75
|
# @return [Object]
|
69
76
|
# @api private
|
70
77
|
def map_value(input)
|
data/lib/dry/types/errors.rb
CHANGED
@@ -5,7 +5,7 @@ module Dry
|
|
5
5
|
module Types
|
6
6
|
class Maybe
|
7
7
|
include Type
|
8
|
-
include Dry::Equalizer(:type, :options)
|
8
|
+
include Dry::Equalizer(:type, :options, inspect: false)
|
9
9
|
include Decorator
|
10
10
|
include Builder
|
11
11
|
include Dry::Monads::Maybe::Mixin
|
@@ -60,6 +60,16 @@ module Dry
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
class Printer
|
64
|
+
MAPPING[Maybe] = :visit_maybe
|
65
|
+
|
66
|
+
def visit_maybe(maybe)
|
67
|
+
visit(maybe.type) do |type|
|
68
|
+
yield "Maybe<#{ type }>"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
63
73
|
# Register non-coercible maybe types
|
64
74
|
NON_NIL.each_key do |name|
|
65
75
|
register("maybe.strict.#{name}", self["strict.#{name}"].maybe)
|
data/lib/dry/types/hash.rb
CHANGED
@@ -1,27 +1,26 @@
|
|
1
|
-
require 'dry/types/hash/
|
1
|
+
require 'dry/types/hash/constructor'
|
2
2
|
|
3
3
|
module Dry
|
4
4
|
module Types
|
5
|
-
class Hash <
|
6
|
-
|
7
|
-
|
8
|
-
# @
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
5
|
+
class Hash < Nominal
|
6
|
+
NOT_REQUIRED = { required: false }.freeze
|
7
|
+
|
8
|
+
# @overload schmea(type_map, meta = EMPTY_HASH)
|
9
|
+
# @param [{Symbol => Dry::Types::Nominal}] type_map
|
10
|
+
# @param [Hash] meta
|
11
|
+
# @return [Dry::Types::Schema]
|
12
|
+
# @overload schema(keys)
|
13
|
+
# @param [Array<Dry::Types::Schema::Key>] key List of schema keys
|
14
|
+
# @param [Hash] meta
|
15
|
+
# @return [Dry::Types::Schema]
|
16
|
+
def schema(keys_or_map, meta = EMPTY_HASH)
|
17
|
+
if keys_or_map.is_a?(::Array)
|
18
|
+
keys = keys_or_map
|
16
19
|
else
|
17
|
-
|
18
|
-
primitive,
|
19
|
-
**options,
|
20
|
-
member_types: member_types,
|
21
|
-
meta: meta,
|
22
|
-
hash_type: constructor
|
23
|
-
)
|
20
|
+
keys = build_keys(keys_or_map)
|
24
21
|
end
|
22
|
+
|
23
|
+
Schema.new(primitive, keys: keys, **options, meta: self.meta.merge(meta))
|
25
24
|
end
|
26
25
|
|
27
26
|
# Build a map type
|
@@ -38,43 +37,16 @@ module Dry
|
|
38
37
|
)
|
39
38
|
end
|
40
39
|
|
41
|
-
# @param [{Symbol =>
|
42
|
-
# @return [Schema]
|
43
|
-
def weak(type_map)
|
44
|
-
schema(type_map, :weak)
|
45
|
-
end
|
46
|
-
|
47
|
-
# @param [{Symbol => Definition}] type_map
|
48
|
-
# @return [Schema]
|
49
|
-
def permissive(type_map)
|
50
|
-
schema(type_map, :permissive)
|
51
|
-
end
|
52
|
-
|
53
|
-
# @param [{Symbol => Definition}] type_map
|
40
|
+
# @param [{Symbol => Nominal}] type_map
|
54
41
|
# @return [Schema]
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
# @param [{Symbol => Definition}] type_map
|
60
|
-
# @return [Schema]
|
61
|
-
def strict_with_defaults(type_map)
|
62
|
-
schema(type_map, :strict_with_defaults)
|
63
|
-
end
|
64
|
-
|
65
|
-
# @param [{Symbol => Definition}] type_map
|
66
|
-
# @return [Schema]
|
67
|
-
def symbolized(type_map)
|
68
|
-
schema(type_map, :symbolized)
|
69
|
-
end
|
70
|
-
|
71
|
-
# Build a schema from an AST
|
72
|
-
# @api private
|
73
|
-
# @param [{Symbol => Definition}] member_types
|
74
|
-
# @return [Schema]
|
75
|
-
def instantiate(member_types)
|
76
|
-
SCHEMA_BUILDER.instantiate(primitive, **options, member_types: member_types)
|
42
|
+
def weak(*)
|
43
|
+
raise "Support for old hash schemas was removed, please refer to the CHANGELOG "\
|
44
|
+
"on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
|
77
45
|
end
|
46
|
+
alias_method :permissive, :weak
|
47
|
+
alias_method :strict, :weak
|
48
|
+
alias_method :strict_with_defaults, :weak
|
49
|
+
alias_method :symbolized, :weak
|
78
50
|
|
79
51
|
# Injects a type transformation function for building schemas
|
80
52
|
# @param [#call,nil] proc
|
@@ -88,22 +60,45 @@ module Dry
|
|
88
60
|
end
|
89
61
|
|
90
62
|
handle = Dry::Types::FnContainer.register(fn)
|
91
|
-
|
63
|
+
with(type_transform_fn: handle)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @api private
|
67
|
+
def constructor_type
|
68
|
+
::Dry::Types::Hash::Constructor
|
69
|
+
end
|
70
|
+
|
71
|
+
# Whether the type transforms types of schemas created by {Dry::Types::Hash#schema}
|
72
|
+
# @return [Boolean]
|
73
|
+
# @api public
|
74
|
+
def transform_types?
|
75
|
+
!options[:type_transform_fn].nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param meta [Boolean] Whether to dump the meta to the AST
|
79
|
+
# @return [Array] An AST representation
|
80
|
+
def to_ast(meta: true)
|
81
|
+
if RUBY_VERSION >= "2.5"
|
82
|
+
opts = options.slice(:type_transform_fn)
|
83
|
+
else
|
84
|
+
opts = options.select { |k, _| k == :type_transform_fn }
|
85
|
+
end
|
86
|
+
|
87
|
+
[:hash, [opts, meta ? self.meta : EMPTY_HASH]]
|
92
88
|
end
|
93
89
|
|
94
90
|
private
|
95
91
|
|
96
92
|
# @api private
|
97
|
-
def
|
98
|
-
type_fn =
|
93
|
+
def build_keys(type_map)
|
94
|
+
type_fn = options.fetch(:type_transform_fn, Schema::NO_TRANSFORM)
|
99
95
|
type_transform = Dry::Types::FnContainer[type_fn]
|
100
96
|
|
101
|
-
type_map.
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
}
|
97
|
+
type_map.map do |map_key, type|
|
98
|
+
name, options = key_name(map_key)
|
99
|
+
key = Schema::Key.new(resolve_type(type), name, options)
|
100
|
+
type_transform.(key)
|
101
|
+
end
|
107
102
|
end
|
108
103
|
|
109
104
|
# @api private
|
@@ -113,6 +108,18 @@ module Dry
|
|
113
108
|
else type
|
114
109
|
end
|
115
110
|
end
|
111
|
+
|
112
|
+
# @api private
|
113
|
+
def key_name(key)
|
114
|
+
if key.to_s.end_with?('?')
|
115
|
+
[key.to_s.chop.to_sym, NOT_REQUIRED]
|
116
|
+
else
|
117
|
+
[key, EMPTY_HASH]
|
118
|
+
end
|
119
|
+
end
|
116
120
|
end
|
117
121
|
end
|
118
122
|
end
|
123
|
+
|
124
|
+
require 'dry/types/schema/key'
|
125
|
+
require 'dry/types/schema'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'dry/types/constructor'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Types
|
5
|
+
class Hash < Nominal
|
6
|
+
class Constructor < ::Dry::Types::Constructor
|
7
|
+
# @api private
|
8
|
+
def constructor_type
|
9
|
+
::Dry::Types::Hash::Constructor
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def composable?(value)
|
15
|
+
super && !value.is_a?(Schema::Key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/dry/types/json.rb
CHANGED
@@ -3,31 +3,31 @@ require 'dry/types/coercions/json'
|
|
3
3
|
module Dry
|
4
4
|
module Types
|
5
5
|
register('json.nil') do
|
6
|
-
self['nil'].constructor(Coercions::JSON.method(:to_nil))
|
6
|
+
self['nominal.nil'].constructor(Coercions::JSON.method(:to_nil))
|
7
7
|
end
|
8
8
|
|
9
9
|
register('json.date') do
|
10
|
-
self['date'].constructor(Coercions::JSON.method(:to_date))
|
10
|
+
self['nominal.date'].constructor(Coercions::JSON.method(:to_date))
|
11
11
|
end
|
12
12
|
|
13
13
|
register('json.date_time') do
|
14
|
-
self['date_time'].constructor(Coercions::JSON.method(:to_date_time))
|
14
|
+
self['nominal.date_time'].constructor(Coercions::JSON.method(:to_date_time))
|
15
15
|
end
|
16
16
|
|
17
17
|
register('json.time') do
|
18
|
-
self['time'].constructor(Coercions::JSON.method(:to_time))
|
18
|
+
self['nominal.time'].constructor(Coercions::JSON.method(:to_time))
|
19
19
|
end
|
20
20
|
|
21
21
|
register('json.decimal') do
|
22
|
-
self['decimal'].constructor(Coercions::JSON.method(:to_decimal))
|
22
|
+
self['nominal.decimal'].constructor(Coercions::JSON.method(:to_decimal))
|
23
23
|
end
|
24
24
|
|
25
25
|
register('json.array') do
|
26
|
-
self['array'].safe
|
26
|
+
self['nominal.array'].safe
|
27
27
|
end
|
28
28
|
|
29
29
|
register('json.hash') do
|
30
|
-
self['hash'].safe
|
30
|
+
self['nominal.hash'].safe
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/dry/types/map.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Dry
|
2
2
|
module Types
|
3
|
-
class Map <
|
3
|
+
class Map < Nominal
|
4
4
|
def initialize(_primitive, key_type: Types['any'], value_type: Types['any'], meta: EMPTY_HASH)
|
5
5
|
super(_primitive, key_type: key_type, value_type: value_type, meta: meta)
|
6
6
|
validate_options!
|
@@ -53,6 +53,11 @@ module Dry
|
|
53
53
|
meta ? self.meta : EMPTY_HASH]]
|
54
54
|
end
|
55
55
|
|
56
|
+
# @return [Boolean]
|
57
|
+
def constrained?
|
58
|
+
value_type.constrained?
|
59
|
+
end
|
60
|
+
|
56
61
|
private
|
57
62
|
|
58
63
|
def coerce(input)
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'dry/core/deprecations'
|
2
|
+
require 'dry/types/builder_methods'
|
3
|
+
|
4
|
+
module Dry
|
5
|
+
module Types
|
6
|
+
# Export types registered in a container as module constants.
|
7
|
+
# @example
|
8
|
+
# module Types
|
9
|
+
# include Dry::Types.module(:strict, :coercible, :nominal, default: :strict)
|
10
|
+
# end
|
11
|
+
# # Types.constants
|
12
|
+
# # => [:Class, :Strict, :Symbol, :Integer, :Float, :String, :Array, :Hash,
|
13
|
+
# # :Decimal, :Nil, :True, :False, :Bool, :Date, :Nominal, :DateTime, :Range,
|
14
|
+
# # :Coercible, :Time]
|
15
|
+
class Module < ::Module
|
16
|
+
def initialize(registry, *args)
|
17
|
+
@registry = registry
|
18
|
+
check_parameters(*args)
|
19
|
+
constants = type_constants(*args)
|
20
|
+
define_constants(constants)
|
21
|
+
extend(BuilderMethods)
|
22
|
+
|
23
|
+
if constants.key?(:Nominal)
|
24
|
+
singleton_class.send(:define_method, :included) do |base|
|
25
|
+
super(base)
|
26
|
+
base.instance_exec(const_get(:Nominal, false)) do |nominal|
|
27
|
+
extend Dry::Core::Deprecations[:'dry-types']
|
28
|
+
const_set(:Definition, nominal)
|
29
|
+
deprecate_constant(:Definition, message: "Nominal")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
def type_constants(*namespaces, default: Undefined, **aliases)
|
37
|
+
if namespaces.empty? && aliases.empty? && Undefined.equal?(default)
|
38
|
+
default_ns = :Strict
|
39
|
+
elsif Undefined.equal?(default)
|
40
|
+
default_ns = Undefined
|
41
|
+
else
|
42
|
+
default_ns = Inflector.camelize(default).to_sym
|
43
|
+
end
|
44
|
+
|
45
|
+
tree = registry_tree
|
46
|
+
|
47
|
+
if namespaces.empty? && aliases.empty?
|
48
|
+
modules = tree.select { |_, v| v.is_a?(::Hash) }.map(&:first)
|
49
|
+
else
|
50
|
+
modules = (namespaces + aliases.keys).map { |n| Inflector.camelize(n).to_sym }
|
51
|
+
end
|
52
|
+
|
53
|
+
tree.each_with_object({}) do |(key, value), constants|
|
54
|
+
if modules.include?(key)
|
55
|
+
name = aliases.fetch(Inflector.underscore(key).to_sym, key)
|
56
|
+
constants[name] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
constants.update(value) if key == default_ns
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @api private
|
64
|
+
def registry_tree
|
65
|
+
@registry_tree ||= @registry.keys.each_with_object({}) { |key, tree|
|
66
|
+
type = @registry[key]
|
67
|
+
*modules, const_name = key.split('.').map { |part|
|
68
|
+
Inflector.camelize(part).to_sym
|
69
|
+
}
|
70
|
+
next if modules.empty?
|
71
|
+
|
72
|
+
modules.reduce(tree) { |br, name| br[name] ||= {} }[const_name] = type
|
73
|
+
}.freeze
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# @api private
|
79
|
+
def check_parameters(*namespaces, default: Undefined, **aliases)
|
80
|
+
referenced = namespaces.dup
|
81
|
+
referenced << default unless false.equal?(default) || Undefined.equal?(default)
|
82
|
+
referenced.concat(aliases.keys)
|
83
|
+
|
84
|
+
known = @registry.keys.map { |k|
|
85
|
+
ns, *path = k.split('.')
|
86
|
+
ns.to_sym unless path.empty?
|
87
|
+
}.compact.uniq
|
88
|
+
|
89
|
+
(referenced.uniq - known).each do |name|
|
90
|
+
raise ArgumentError,
|
91
|
+
"#{ name.inspect } is not a known type namespace. "\
|
92
|
+
"Supported options are #{ known.map(&:inspect).join(', ') }"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# @api private
|
97
|
+
def define_constants(constants, mod = self)
|
98
|
+
constants.each do |name, value|
|
99
|
+
case value
|
100
|
+
when ::Hash
|
101
|
+
if mod.const_defined?(name, false)
|
102
|
+
define_constants(value, mod.const_get(name, false))
|
103
|
+
else
|
104
|
+
m = ::Module.new
|
105
|
+
mod.const_set(name, m)
|
106
|
+
define_constants(value, m)
|
107
|
+
end
|
108
|
+
else
|
109
|
+
mod.const_set(name, value)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|