dry-types 0.12.3 → 0.14.1
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/.travis.yml +7 -6
- data/CHANGELOG.md +249 -105
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +0 -2
- data/README.md +0 -2
- data/Rakefile +4 -2
- data/benchmarks/hash_schemas.rb +2 -2
- data/dry-types.gemspec +8 -6
- data/lib/dry/types/array/member.rb +6 -2
- data/lib/dry/types/array.rb +1 -3
- data/lib/dry/types/builder.rb +9 -2
- data/lib/dry/types/builder_methods.rb +22 -1
- data/lib/dry/types/coercions/{form.rb → params.rb} +1 -1
- data/lib/dry/types/compat/form_types.rb +27 -0
- data/lib/dry/types/compat/int.rb +14 -0
- data/lib/dry/types/compat.rb +2 -0
- data/lib/dry/types/compiler.rb +30 -7
- data/lib/dry/types/constructor.rb +13 -5
- data/lib/dry/types/core.rb +4 -3
- data/lib/dry/types/decorator.rb +11 -1
- data/lib/dry/types/default.rb +14 -3
- data/lib/dry/types/definition.rb +3 -13
- data/lib/dry/types/enum.rb +42 -14
- data/lib/dry/types/errors.rb +6 -2
- data/lib/dry/types/extensions/maybe.rb +18 -28
- data/lib/dry/types/fn_container.rb +9 -8
- data/lib/dry/types/hash/schema.rb +99 -180
- data/lib/dry/types/hash/schema_builder.rb +75 -0
- data/lib/dry/types/hash.rb +83 -28
- data/lib/dry/types/inflector.rb +7 -0
- data/lib/dry/types/map.rb +93 -0
- data/lib/dry/types/options.rb +3 -3
- data/lib/dry/types/params.rb +53 -0
- data/lib/dry/types/safe.rb +3 -2
- data/lib/dry/types/spec/types.rb +10 -0
- data/lib/dry/types/sum.rb +12 -6
- data/lib/dry/types/version.rb +1 -1
- data/lib/dry/types.rb +12 -18
- metadata +44 -50
- data/lib/dry/types/form.rb +0 -53
data/README.md
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/dry-types
|
2
2
|
[travis]: https://travis-ci.org/dry-rb/dry-types
|
3
|
-
[gemnasium]: https://gemnasium.com/dry-rb/dry-types
|
4
3
|
[codeclimate]: https://codeclimate.com/github/dry-rb/dry-types
|
5
4
|
[coveralls]: https://coveralls.io/r/dry-rb/dry-types
|
6
5
|
[inchpages]: http://inch-ci.org/github/dry-rb/dry-types
|
@@ -9,7 +8,6 @@
|
|
9
8
|
|
10
9
|
[][gem]
|
11
10
|
[][travis]
|
12
|
-
[][gemnasium]
|
13
11
|
[][codeclimate]
|
14
12
|
[][codeclimate]
|
15
13
|
[][inchpages]
|
data/Rakefile
CHANGED
@@ -4,11 +4,13 @@ require "rspec/core/rake_task"
|
|
4
4
|
task :run_specs do
|
5
5
|
require 'rspec/core'
|
6
6
|
|
7
|
-
RSpec::Core::Runner.run(['spec/dry'])
|
7
|
+
types_result = RSpec::Core::Runner.run(['spec/dry'])
|
8
8
|
RSpec.clear_examples
|
9
9
|
|
10
10
|
Dry::Types.load_extensions(:maybe)
|
11
|
-
RSpec::Core::Runner.run(['spec'])
|
11
|
+
ext_result = RSpec::Core::Runner.run(['spec'])
|
12
|
+
|
13
|
+
exit [types_result, ext_result].max
|
12
14
|
end
|
13
15
|
|
14
16
|
task default: :run_specs
|
data/benchmarks/hash_schemas.rb
CHANGED
@@ -7,8 +7,8 @@ module SchemaBench
|
|
7
7
|
def self.hash_schema(type)
|
8
8
|
Dry::Types['hash'].public_send(type,
|
9
9
|
email: Dry::Types['string'],
|
10
|
-
age: Dry::Types['
|
11
|
-
admin: Dry::Types['
|
10
|
+
age: Dry::Types['params.integer'],
|
11
|
+
admin: Dry::Types['params.bool'],
|
12
12
|
address: Dry::Types['hash'].public_send(type,
|
13
13
|
city: Dry::Types['string'],
|
14
14
|
street: Dry::Types['string']
|
data/dry-types.gemspec
CHANGED
@@ -17,6 +17,9 @@ Gem::Specification.new do |spec|
|
|
17
17
|
# delete this section to allow pushing this gem to any host.
|
18
18
|
if spec.respond_to?(:metadata)
|
19
19
|
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
20
|
+
spec.metadata['changelog_uri'] = "https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
|
21
|
+
spec.metadata['source_code_uri'] = "https://github.com/dry-rb/dry-types"
|
22
|
+
spec.metadata['bug_tracker_uri'] = "https://github.com/dry-rb/dry-types/issues"
|
20
23
|
else
|
21
24
|
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
25
|
end
|
@@ -25,17 +28,16 @@ Gem::Specification.new do |spec|
|
|
25
28
|
spec.bindir = "exe"
|
26
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
30
|
spec.require_paths = ["lib"]
|
28
|
-
spec.required_ruby_version = ">= 2.
|
31
|
+
spec.required_ruby_version = ">= 2.3.0"
|
29
32
|
|
30
33
|
spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
31
|
-
spec.add_runtime_dependency 'dry-core', '~> 0.
|
34
|
+
spec.add_runtime_dependency 'dry-core', '~> 0.4', '>= 0.4.4'
|
35
|
+
spec.add_runtime_dependency 'dry-inflector', '~> 0.1', '>= 0.1.2'
|
32
36
|
spec.add_runtime_dependency 'dry-container', '~> 0.3'
|
33
37
|
spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
|
34
|
-
spec.add_runtime_dependency 'dry-
|
35
|
-
spec.add_runtime_dependency 'dry-logic', '~> 0.4', '>= 0.4.2'
|
36
|
-
spec.add_runtime_dependency 'inflecto', '~> 0.0.0', '>= 0.0.2'
|
38
|
+
spec.add_runtime_dependency 'dry-logic', '~> 0.5', '>= 0.5'
|
37
39
|
|
38
|
-
spec.add_development_dependency "bundler"
|
40
|
+
spec.add_development_dependency "bundler"
|
39
41
|
spec.add_development_dependency "rake", "~> 11.0"
|
40
42
|
spec.add_development_dependency "rspec", "~> 3.3"
|
41
43
|
spec.add_development_dependency 'dry-monads', '~> 0.2'
|
@@ -34,7 +34,7 @@ module Dry
|
|
34
34
|
# @return [Result,Logic::Result]
|
35
35
|
def try(input, &block)
|
36
36
|
if input.is_a?(::Array)
|
37
|
-
result = call(input, :try)
|
37
|
+
result = call(input, :try).reject { |r| r.input.equal?(Undefined) }
|
38
38
|
output = result.map(&:input)
|
39
39
|
|
40
40
|
if result.all?(&:success?)
|
@@ -53,7 +53,11 @@ module Dry
|
|
53
53
|
#
|
54
54
|
# @see Definition#to_ast
|
55
55
|
def to_ast(meta: true)
|
56
|
-
|
56
|
+
if member.respond_to?(:to_ast)
|
57
|
+
[:array, [member.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
|
58
|
+
else
|
59
|
+
[:array, [member, meta ? self.meta : EMPTY_HASH]]
|
60
|
+
end
|
57
61
|
end
|
58
62
|
end
|
59
63
|
end
|
data/lib/dry/types/array.rb
CHANGED
@@ -11,14 +11,12 @@ module Dry
|
|
11
11
|
def of(type)
|
12
12
|
member =
|
13
13
|
case type
|
14
|
-
when String
|
14
|
+
when String then Types[type]
|
15
15
|
else type
|
16
16
|
end
|
17
17
|
|
18
18
|
Array::Member.new(primitive, **options, member: member)
|
19
19
|
end
|
20
|
-
|
21
|
-
deprecate :member, :of
|
22
20
|
end
|
23
21
|
end
|
24
22
|
end
|
data/lib/dry/types/builder.rb
CHANGED
@@ -31,7 +31,7 @@ module Dry
|
|
31
31
|
# @raise [ConstraintError]
|
32
32
|
# @return [Default]
|
33
33
|
def default(input = Undefined, &block)
|
34
|
-
value = input
|
34
|
+
value = input.equal?(Undefined) ? block : input
|
35
35
|
|
36
36
|
if value.is_a?(Proc) || valid?(value)
|
37
37
|
Default[value].new(self, value)
|
@@ -43,7 +43,14 @@ module Dry
|
|
43
43
|
# @param [Array] values
|
44
44
|
# @return [Enum]
|
45
45
|
def enum(*values)
|
46
|
-
|
46
|
+
mapping =
|
47
|
+
if values.length == 1 && values[0].is_a?(::Hash)
|
48
|
+
values[0]
|
49
|
+
else
|
50
|
+
::Hash[values.zip(values)]
|
51
|
+
end
|
52
|
+
|
53
|
+
Enum.new(constrained(included_in: mapping.keys), mapping: mapping)
|
47
54
|
end
|
48
55
|
|
49
56
|
# @return [Safe]
|
@@ -34,6 +34,11 @@ module Dry
|
|
34
34
|
# Build a type which values are instances of a given class
|
35
35
|
# Values are checked using `is_a?` call
|
36
36
|
#
|
37
|
+
# @example
|
38
|
+
# Types::Error = Types.Instance(StandardError)
|
39
|
+
# Types::Error = Types.Strict(StandardError)
|
40
|
+
# Types.Strict(Integer) == Types::Strict::Int # => true
|
41
|
+
#
|
37
42
|
# @param [Class,Module] klass Class or module
|
38
43
|
#
|
39
44
|
# @return [Dry::Types::Type]
|
@@ -41,6 +46,7 @@ module Dry
|
|
41
46
|
def Instance(klass)
|
42
47
|
Definition.new(klass).constrained(type: klass)
|
43
48
|
end
|
49
|
+
alias_method :Strict, :Instance
|
44
50
|
|
45
51
|
# Build a type with a single value
|
46
52
|
# The equality check done with `eql?`
|
@@ -77,7 +83,7 @@ module Dry
|
|
77
83
|
Definition.new(klass).constructor(cons || block || klass.method(:new))
|
78
84
|
end
|
79
85
|
|
80
|
-
# Build a
|
86
|
+
# Build a definition type
|
81
87
|
#
|
82
88
|
# @param [Class] klass
|
83
89
|
#
|
@@ -86,6 +92,21 @@ module Dry
|
|
86
92
|
def Definition(klass)
|
87
93
|
Definition.new(klass)
|
88
94
|
end
|
95
|
+
|
96
|
+
# Build a map type
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# Types::IntMap = Types.Map(Types::Strict::Integer, 'any')
|
100
|
+
# Types::IntStringMap = Types.Map(Types::Strict::Integer, Types::Strict::String)
|
101
|
+
#
|
102
|
+
# @param [Type] key_type Key type
|
103
|
+
# @param [Type] value_type Value type
|
104
|
+
#
|
105
|
+
# @return [Dry::Types::Map]
|
106
|
+
# @api public
|
107
|
+
def Map(key_type, value_type)
|
108
|
+
Types['hash'].map(key_type, value_type)
|
109
|
+
end
|
89
110
|
end
|
90
111
|
end
|
91
112
|
end
|
@@ -4,7 +4,7 @@ require 'bigdecimal/util'
|
|
4
4
|
module Dry
|
5
5
|
module Types
|
6
6
|
module Coercions
|
7
|
-
module
|
7
|
+
module Params
|
8
8
|
TRUE_VALUES = %w[1 on On ON t true True TRUE T y yes Yes YES Y].freeze
|
9
9
|
FALSE_VALUES = %w[0 off Off OFF f false False FALSE F n no No NO N].freeze
|
10
10
|
BOOLEAN_MAP = ::Hash[TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])].freeze
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'dry/core/deprecations'
|
2
|
+
|
3
|
+
Dry::Core::Deprecations.warn('Form types were renamed to Params', tag: :'dry-types')
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Types
|
7
|
+
container.keys.grep(/^params\./).each do |key|
|
8
|
+
next if key.start_with?('params.int')
|
9
|
+
register(key.sub('params.', 'form.'), container[key])
|
10
|
+
end
|
11
|
+
|
12
|
+
register('form.int', self['params.integer'])
|
13
|
+
register('form.integer', self['params.integer'])
|
14
|
+
|
15
|
+
class Compiler
|
16
|
+
def visit_form_hash(node)
|
17
|
+
schema, meta = node
|
18
|
+
merge_with('params.hash', :symbolized, schema).meta(meta)
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit_form_array(node)
|
22
|
+
member, meta = node
|
23
|
+
registry['params.array'].of(visit(member)).meta(meta)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'dry/core/deprecations'
|
2
|
+
|
3
|
+
Dry::Core::Deprecations.warn('Int type was renamed to Integer', tag: :'dry-types')
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Types
|
7
|
+
register('int', self['integer'])
|
8
|
+
register('strict.int', self['strict.integer'])
|
9
|
+
register('coercible.int', self['coercible.integer'])
|
10
|
+
register('optional.strict.int', self['optional.strict.integer'])
|
11
|
+
register('optional.coercible.int', self['optional.coercible.integer'])
|
12
|
+
register('params.int', self['params.integer'])
|
13
|
+
end
|
14
|
+
end
|
data/lib/dry/types/compiler.rb
CHANGED
@@ -54,7 +54,8 @@ module Dry
|
|
54
54
|
|
55
55
|
def visit_array(node)
|
56
56
|
member, meta = node
|
57
|
-
|
57
|
+
member = member.is_a?(Class) ? member : visit(member)
|
58
|
+
registry['array'].of(member).meta(meta)
|
58
59
|
end
|
59
60
|
|
60
61
|
def visit_hash(node)
|
@@ -62,6 +63,11 @@ module Dry
|
|
62
63
|
merge_with('hash', constructor, schema).meta(meta)
|
63
64
|
end
|
64
65
|
|
66
|
+
def visit_hash_schema(node)
|
67
|
+
schema, meta = node
|
68
|
+
merge_with_schema('hash', schema).meta(meta)
|
69
|
+
end
|
70
|
+
|
65
71
|
def visit_json_hash(node)
|
66
72
|
schema, meta = node
|
67
73
|
merge_with('json.hash', :symbolized, schema).meta(meta)
|
@@ -72,14 +78,14 @@ module Dry
|
|
72
78
|
registry['json.array'].of(visit(member)).meta(meta)
|
73
79
|
end
|
74
80
|
|
75
|
-
def
|
81
|
+
def visit_params_hash(node)
|
76
82
|
schema, meta = node
|
77
|
-
merge_with('
|
83
|
+
merge_with('params.hash', :symbolized, schema).meta(meta)
|
78
84
|
end
|
79
85
|
|
80
|
-
def
|
86
|
+
def visit_params_array(node)
|
81
87
|
member, meta = node
|
82
|
-
registry['
|
88
|
+
registry['params.array'].of(visit(member)).meta(meta)
|
83
89
|
end
|
84
90
|
|
85
91
|
def visit_member(node)
|
@@ -87,9 +93,26 @@ module Dry
|
|
87
93
|
{ name => visit(type) }
|
88
94
|
end
|
89
95
|
|
96
|
+
def visit_enum(node)
|
97
|
+
type, mapping, meta = node
|
98
|
+
Enum.new(visit(type), mapping: mapping, meta: meta)
|
99
|
+
end
|
100
|
+
|
101
|
+
def visit_map(node)
|
102
|
+
key_type, value_type, meta = node
|
103
|
+
registry['hash'].map(visit(key_type), visit(value_type)).meta(meta)
|
104
|
+
end
|
105
|
+
|
90
106
|
def merge_with(hash_id, constructor, schema)
|
91
|
-
registry[hash_id].
|
92
|
-
|
107
|
+
registry[hash_id].schema(
|
108
|
+
schema.map { |key| visit(key) }.reduce({}, :update),
|
109
|
+
constructor
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
def merge_with_schema(hash_id, schema)
|
114
|
+
registry[hash_id].instantiate(
|
115
|
+
schema.map { |key| visit(key) }.reduce({}, :update)
|
93
116
|
)
|
94
117
|
end
|
95
118
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'dry/types/
|
1
|
+
require 'dry/types/fn_container'
|
2
2
|
|
3
3
|
module Dry
|
4
4
|
module Types
|
@@ -16,15 +16,15 @@ module Dry
|
|
16
16
|
# @param [Builder, Object] input
|
17
17
|
# @param [Hash] options
|
18
18
|
# @param [#call, nil] block
|
19
|
-
def self.new(input, options
|
19
|
+
def self.new(input, **options, &block)
|
20
20
|
type = input.is_a?(Builder) ? input : Definition.new(input)
|
21
|
-
super(type, options, &block)
|
21
|
+
super(type, **options, &block)
|
22
22
|
end
|
23
23
|
|
24
24
|
# @param [Type] type
|
25
25
|
# @param [Hash] options
|
26
26
|
# @param [#call, nil] block
|
27
|
-
def initialize(type, options
|
27
|
+
def initialize(type, **options, &block)
|
28
28
|
@type = type
|
29
29
|
@fn = options.fetch(:fn, block)
|
30
30
|
|
@@ -43,6 +43,10 @@ module Dry
|
|
43
43
|
type.name
|
44
44
|
end
|
45
45
|
|
46
|
+
def default?
|
47
|
+
type.default?
|
48
|
+
end
|
49
|
+
|
46
50
|
# @param [Object] input
|
47
51
|
# @return [Object]
|
48
52
|
def call(input)
|
@@ -74,7 +78,11 @@ module Dry
|
|
74
78
|
# @param [Object] value
|
75
79
|
# @return [Boolean]
|
76
80
|
def valid?(value)
|
77
|
-
|
81
|
+
constructed_value = fn[value]
|
82
|
+
rescue NoMethodError, TypeError, ArgumentError
|
83
|
+
false
|
84
|
+
else
|
85
|
+
type.valid?(constructed_value)
|
78
86
|
end
|
79
87
|
alias_method :===, :valid?
|
80
88
|
|
data/lib/dry/types/core.rb
CHANGED
@@ -4,7 +4,7 @@ module Dry
|
|
4
4
|
module Types
|
5
5
|
COERCIBLE = {
|
6
6
|
string: String,
|
7
|
-
|
7
|
+
integer: Integer,
|
8
8
|
float: Float,
|
9
9
|
decimal: BigDecimal,
|
10
10
|
array: ::Array,
|
@@ -19,7 +19,8 @@ module Dry
|
|
19
19
|
false: FalseClass,
|
20
20
|
date: Date,
|
21
21
|
date_time: DateTime,
|
22
|
-
time: Time
|
22
|
+
time: Time,
|
23
|
+
range: Range
|
23
24
|
}.freeze
|
24
25
|
|
25
26
|
ALL_PRIMITIVES = COERCIBLE.merge(NON_COERCIBLE).freeze
|
@@ -61,5 +62,5 @@ module Dry
|
|
61
62
|
end
|
62
63
|
|
63
64
|
require 'dry/types/coercions'
|
64
|
-
require 'dry/types/
|
65
|
+
require 'dry/types/params'
|
65
66
|
require 'dry/types/json'
|
data/lib/dry/types/decorator.rb
CHANGED
@@ -39,6 +39,11 @@ module Dry
|
|
39
39
|
type.constrained?
|
40
40
|
end
|
41
41
|
|
42
|
+
# @return [Sum]
|
43
|
+
def optional
|
44
|
+
Types['strict.nil'] | self
|
45
|
+
end
|
46
|
+
|
42
47
|
# @param [Symbol] meth
|
43
48
|
# @param [Boolean] include_private
|
44
49
|
# @return [Boolean]
|
@@ -63,7 +68,7 @@ module Dry
|
|
63
68
|
response = type.__send__(meth, *args, &block)
|
64
69
|
|
65
70
|
if decorate?(response)
|
66
|
-
|
71
|
+
__new__(response)
|
67
72
|
else
|
68
73
|
response
|
69
74
|
end
|
@@ -71,6 +76,11 @@ module Dry
|
|
71
76
|
super
|
72
77
|
end
|
73
78
|
end
|
79
|
+
|
80
|
+
# Replace underlying type
|
81
|
+
def __new__(type)
|
82
|
+
self.class.new(type, options)
|
83
|
+
end
|
74
84
|
end
|
75
85
|
end
|
76
86
|
end
|
data/lib/dry/types/default.rb
CHANGED
@@ -35,7 +35,7 @@ module Dry
|
|
35
35
|
|
36
36
|
# @param [Type] type
|
37
37
|
# @param [Object] value
|
38
|
-
def initialize(type, value,
|
38
|
+
def initialize(type, value, **options)
|
39
39
|
super
|
40
40
|
@value = value
|
41
41
|
end
|
@@ -57,10 +57,14 @@ module Dry
|
|
57
57
|
success(call(input))
|
58
58
|
end
|
59
59
|
|
60
|
+
def valid?(value = Undefined)
|
61
|
+
value.equal?(Undefined) || super
|
62
|
+
end
|
63
|
+
|
60
64
|
# @param [Object] input
|
61
65
|
# @return [Object] value passed through {#type} or {#default} value
|
62
|
-
def call(input)
|
63
|
-
if input.
|
66
|
+
def call(input = Undefined)
|
67
|
+
if input.equal?(Undefined)
|
64
68
|
evaluate
|
65
69
|
else
|
66
70
|
output = type[input]
|
@@ -68,6 +72,13 @@ module Dry
|
|
68
72
|
end
|
69
73
|
end
|
70
74
|
alias_method :[], :call
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Replace underlying type
|
79
|
+
def __new__(type)
|
80
|
+
self.class.new(type, value, options)
|
81
|
+
end
|
71
82
|
end
|
72
83
|
end
|
73
84
|
end
|
data/lib/dry/types/definition.rb
CHANGED
@@ -6,12 +6,9 @@ module Dry
|
|
6
6
|
module Types
|
7
7
|
class Definition
|
8
8
|
include Type
|
9
|
-
include Dry::Equalizer(:primitive, :options, :meta)
|
10
9
|
include Options
|
11
10
|
include Builder
|
12
|
-
|
13
|
-
# @return [Hash]
|
14
|
-
attr_reader :options
|
11
|
+
include Dry::Equalizer(:primitive, :options, :meta)
|
15
12
|
|
16
13
|
# @return [Class]
|
17
14
|
attr_reader :primitive
|
@@ -30,7 +27,7 @@ module Dry
|
|
30
27
|
|
31
28
|
# @param [Type,Class] primitive
|
32
29
|
# @param [Hash] options
|
33
|
-
def initialize(primitive, options
|
30
|
+
def initialize(primitive, **options)
|
34
31
|
super
|
35
32
|
@primitive = primitive
|
36
33
|
freeze
|
@@ -84,20 +81,12 @@ module Dry
|
|
84
81
|
Result::Success.new(input)
|
85
82
|
end
|
86
83
|
|
87
|
-
|
88
84
|
# @param (see Failure#initialize)
|
89
85
|
# @return [Result::Failure]
|
90
86
|
def failure(input, error)
|
91
87
|
Result::Failure.new(input, error)
|
92
88
|
end
|
93
89
|
|
94
|
-
# @param [Object] klass class of the result instance
|
95
|
-
# @param [Array] args arguments for the +klass#initialize+ method
|
96
|
-
# @return [Object] new instance of the given +klass+
|
97
|
-
def result(klass, *args)
|
98
|
-
klass.new(*args)
|
99
|
-
end
|
100
|
-
|
101
90
|
# Checks whether value is of a #primitive class
|
102
91
|
# @param [Object] value
|
103
92
|
# @return [Boolean]
|
@@ -121,3 +110,4 @@ end
|
|
121
110
|
|
122
111
|
require 'dry/types/array'
|
123
112
|
require 'dry/types/hash'
|
113
|
+
require 'dry/types/map'
|
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, :
|
7
|
+
include Dry::Equalizer(:type, :options, :mapping)
|
8
8
|
include Decorator
|
9
9
|
|
10
10
|
# @return [Array]
|
@@ -13,40 +13,68 @@ module Dry
|
|
13
13
|
# @return [Hash]
|
14
14
|
attr_reader :mapping
|
15
15
|
|
16
|
+
# @return [Hash]
|
17
|
+
attr_reader :inverted_mapping
|
18
|
+
|
16
19
|
# @param [Type] type
|
17
20
|
# @param [Hash] options
|
18
21
|
# @option options [Array] :values
|
19
22
|
def initialize(type, options)
|
20
23
|
super
|
21
|
-
@
|
22
|
-
@values.
|
23
|
-
@
|
24
|
+
@mapping = options.fetch(:mapping).freeze
|
25
|
+
@values = @mapping.keys.freeze
|
26
|
+
@inverted_mapping = @mapping.invert.freeze
|
27
|
+
freeze
|
24
28
|
end
|
25
29
|
|
26
30
|
# @param [Object] input
|
27
31
|
# @return [Object]
|
28
|
-
def call(input)
|
29
|
-
|
30
|
-
if values.include?(input)
|
31
|
-
input
|
32
|
-
elsif mapping.key?(input)
|
33
|
-
mapping[input]
|
34
|
-
end
|
35
|
-
|
36
|
-
type[value || input]
|
32
|
+
def call(input = Undefined)
|
33
|
+
type[map_value(input)]
|
37
34
|
end
|
38
35
|
alias_method :[], :call
|
39
36
|
|
37
|
+
# @param [Object] input
|
38
|
+
# @yieldparam [Failure] failure
|
39
|
+
# @yieldreturn [Result]
|
40
|
+
# @return [Logic::Result]
|
41
|
+
# @return [Object] if coercion fails and a block is given
|
42
|
+
def try(input)
|
43
|
+
super(map_value(input))
|
44
|
+
end
|
45
|
+
|
40
46
|
def default(*)
|
41
47
|
raise '.enum(*values).default(value) is not supported. Call '\
|
42
48
|
'.default(value).enum(*values) instead'
|
43
49
|
end
|
44
50
|
|
51
|
+
# Check whether a value is in the enum
|
52
|
+
alias_method :include?, :valid?
|
53
|
+
|
45
54
|
# @api public
|
46
55
|
#
|
47
56
|
# @see Definition#to_ast
|
48
57
|
def to_ast(meta: true)
|
49
|
-
[:enum, [type.to_ast(meta: meta),
|
58
|
+
[:enum, [type.to_ast(meta: meta),
|
59
|
+
mapping,
|
60
|
+
meta ? self.meta : EMPTY_HASH]]
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Maps a value
|
66
|
+
#
|
67
|
+
# @params [Object]
|
68
|
+
# @return [Object]
|
69
|
+
# @api private
|
70
|
+
def map_value(input)
|
71
|
+
if input.equal?(Undefined)
|
72
|
+
type.call
|
73
|
+
elsif mapping.key?(input)
|
74
|
+
input
|
75
|
+
else
|
76
|
+
inverted_mapping.fetch(input, input)
|
77
|
+
end
|
50
78
|
end
|
51
79
|
end
|
52
80
|
end
|
data/lib/dry/types/errors.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module Dry
|
2
2
|
module Types
|
3
|
-
extend Dry::
|
3
|
+
extend Dry::Core::ClassAttributes
|
4
4
|
|
5
5
|
# @!attribute [r] namespace
|
6
6
|
# @return [Container{String => Definition}]
|
7
|
-
|
7
|
+
defines :namespace
|
8
|
+
|
9
|
+
namespace self
|
8
10
|
|
9
11
|
class SchemaError < TypeError
|
10
12
|
# @param [String,Symbol] key
|
@@ -15,6 +17,8 @@ module Dry
|
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
20
|
+
MapError = Class.new(TypeError)
|
21
|
+
|
18
22
|
SchemaKeyError = Class.new(KeyError)
|
19
23
|
private_constant(:SchemaKeyError)
|
20
24
|
|