dry-types 0.13.2 → 1.5.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/CHANGELOG.md +763 -233
- data/LICENSE +17 -17
- data/README.md +15 -13
- data/dry-types.gemspec +28 -28
- data/lib/dry-types.rb +3 -1
- data/lib/dry/types.rb +156 -76
- data/lib/dry/types/any.rb +32 -12
- data/lib/dry/types/array.rb +19 -6
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +75 -16
- data/lib/dry/types/builder.rb +131 -15
- data/lib/dry/types/builder_methods.rb +49 -20
- data/lib/dry/types/coercions.rb +76 -22
- data/lib/dry/types/coercions/json.rb +43 -7
- data/lib/dry/types/coercions/params.rb +118 -31
- data/lib/dry/types/compat.rb +0 -2
- data/lib/dry/types/compiler.rb +56 -41
- data/lib/dry/types/constrained.rb +81 -32
- data/lib/dry/types/constrained/coercible.rb +36 -6
- data/lib/dry/types/constraints.rb +18 -4
- data/lib/dry/types/constructor.rb +127 -54
- data/lib/dry/types/constructor/function.rb +216 -0
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/container.rb +7 -0
- data/lib/dry/types/core.rb +54 -21
- data/lib/dry/types/decorator.rb +38 -17
- data/lib/dry/types/default.rb +61 -16
- data/lib/dry/types/enum.rb +43 -20
- data/lib/dry/types/errors.rb +75 -9
- data/lib/dry/types/extensions.rb +7 -1
- data/lib/dry/types/extensions/maybe.rb +74 -16
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/fn_container.rb +6 -1
- data/lib/dry/types/hash.rb +86 -67
- data/lib/dry/types/hash/constructor.rb +33 -0
- data/lib/dry/types/inflector.rb +3 -1
- data/lib/dry/types/json.rb +18 -16
- data/lib/dry/types/lax.rb +75 -0
- data/lib/dry/types/map.rb +76 -33
- data/lib/dry/types/meta.rb +51 -0
- data/lib/dry/types/module.rb +120 -0
- data/lib/dry/types/nominal.rb +210 -0
- data/lib/dry/types/options.rb +13 -26
- data/lib/dry/types/params.rb +39 -25
- data/lib/dry/types/predicate_inferrer.rb +238 -0
- data/lib/dry/types/predicate_registry.rb +34 -0
- data/lib/dry/types/primitive_inferrer.rb +97 -0
- data/lib/dry/types/printable.rb +16 -0
- data/lib/dry/types/printer.rb +315 -0
- data/lib/dry/types/result.rb +29 -3
- data/lib/dry/types/schema.rb +408 -0
- data/lib/dry/types/schema/key.rb +156 -0
- data/lib/dry/types/spec/types.rb +103 -33
- data/lib/dry/types/sum.rb +84 -35
- data/lib/dry/types/type.rb +49 -0
- data/lib/dry/types/version.rb +3 -1
- metadata +68 -79
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.travis.yml +0 -29
- data/.yardopts +0 -5
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -24
- data/Rakefile +0 -20
- data/benchmarks/hash_schemas.rb +0 -51
- data/lib/dry/types/compat/form_types.rb +0 -27
- data/lib/dry/types/compat/int.rb +0 -14
- data/lib/dry/types/definition.rb +0 -113
- data/lib/dry/types/hash/schema.rb +0 -199
- data/lib/dry/types/hash/schema_builder.rb +0 -75
- data/lib/dry/types/safe.rb +0 -59
data/lib/dry/types/compat.rb
CHANGED
data/lib/dry/types/compiler.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
|
1
5
|
module Dry
|
2
6
|
module Types
|
7
|
+
# @api private
|
3
8
|
class Compiler
|
9
|
+
extend ::Dry::Core::Deprecations[:'dry-types']
|
10
|
+
|
4
11
|
attr_reader :registry
|
5
12
|
|
6
13
|
def initialize(registry)
|
@@ -13,33 +20,34 @@ module Dry
|
|
13
20
|
|
14
21
|
def visit(node)
|
15
22
|
type, body = node
|
16
|
-
send(:"visit_#{
|
23
|
+
send(:"visit_#{type}", body)
|
17
24
|
end
|
18
25
|
|
19
26
|
def visit_constrained(node)
|
20
|
-
|
21
|
-
|
27
|
+
nominal, rule = node
|
28
|
+
type = visit(nominal)
|
29
|
+
type.constrained_type.new(type, rule: visit_rule(rule))
|
22
30
|
end
|
23
31
|
|
24
32
|
def visit_constructor(node)
|
25
|
-
|
26
|
-
|
27
|
-
primitive
|
28
|
-
Types::Constructor.new(primitive, meta: meta, fn: fn)
|
33
|
+
nominal, fn = node
|
34
|
+
primitive = visit(nominal)
|
35
|
+
primitive.constructor(compile_fn(fn))
|
29
36
|
end
|
30
37
|
|
31
|
-
def
|
32
|
-
|
33
|
-
Types::Safe.new(visit(ast), meta: meta)
|
38
|
+
def visit_lax(node)
|
39
|
+
Types::Lax.new(visit(node))
|
34
40
|
end
|
41
|
+
deprecate(:visit_safe, :visit_lax)
|
35
42
|
|
36
|
-
def
|
43
|
+
def visit_nominal(node)
|
37
44
|
type, meta = node
|
45
|
+
nominal_name = "nominal.#{Types.identifier(type)}"
|
38
46
|
|
39
|
-
if registry.registered?(
|
40
|
-
registry[
|
47
|
+
if registry.registered?(nominal_name)
|
48
|
+
registry[nominal_name].meta(meta)
|
41
49
|
else
|
42
|
-
|
50
|
+
Nominal.new(type, meta: meta)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
@@ -55,65 +63,72 @@ module Dry
|
|
55
63
|
def visit_array(node)
|
56
64
|
member, meta = node
|
57
65
|
member = member.is_a?(Class) ? member : visit(member)
|
58
|
-
registry[
|
66
|
+
registry["nominal.array"].of(member).meta(meta)
|
59
67
|
end
|
60
68
|
|
61
69
|
def visit_hash(node)
|
62
|
-
|
63
|
-
|
70
|
+
opts, meta = node
|
71
|
+
registry["nominal.hash"].with(**opts, meta: meta)
|
64
72
|
end
|
65
73
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
74
|
+
def visit_schema(node)
|
75
|
+
keys, options, meta = node
|
76
|
+
registry["nominal.hash"].schema(keys.map { |key| visit(key) }).with(**options, meta: meta)
|
69
77
|
end
|
70
78
|
|
71
79
|
def visit_json_hash(node)
|
72
|
-
|
73
|
-
|
80
|
+
keys, meta = node
|
81
|
+
registry["json.hash"].schema(keys.map { |key| visit(key) }, meta)
|
74
82
|
end
|
75
83
|
|
76
84
|
def visit_json_array(node)
|
77
85
|
member, meta = node
|
78
|
-
registry[
|
86
|
+
registry["json.array"].of(visit(member)).meta(meta)
|
79
87
|
end
|
80
88
|
|
81
89
|
def visit_params_hash(node)
|
82
|
-
|
83
|
-
|
90
|
+
keys, meta = node
|
91
|
+
registry["params.hash"].schema(keys.map { |key| visit(key) }, meta)
|
84
92
|
end
|
85
93
|
|
86
94
|
def visit_params_array(node)
|
87
95
|
member, meta = node
|
88
|
-
registry[
|
96
|
+
registry["params.array"].of(visit(member)).meta(meta)
|
89
97
|
end
|
90
98
|
|
91
|
-
def
|
92
|
-
name, type = node
|
93
|
-
|
99
|
+
def visit_key(node)
|
100
|
+
name, required, type = node
|
101
|
+
Schema::Key.new(visit(type), name, required: required)
|
94
102
|
end
|
95
103
|
|
96
104
|
def visit_enum(node)
|
97
|
-
type, mapping
|
98
|
-
Enum.new(visit(type), mapping: mapping
|
105
|
+
type, mapping = node
|
106
|
+
Enum.new(visit(type), mapping: mapping)
|
99
107
|
end
|
100
108
|
|
101
109
|
def visit_map(node)
|
102
110
|
key_type, value_type, meta = node
|
103
|
-
registry[
|
111
|
+
registry["nominal.hash"].map(visit(key_type), visit(value_type)).meta(meta)
|
104
112
|
end
|
105
113
|
|
106
|
-
def
|
107
|
-
registry[
|
108
|
-
schema.map { |key| visit(key) }.reduce({}, :update),
|
109
|
-
constructor
|
110
|
-
)
|
114
|
+
def visit_any(meta)
|
115
|
+
registry["any"].meta(meta)
|
111
116
|
end
|
112
117
|
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
118
|
+
def compile_fn(fn)
|
119
|
+
type, *node = fn
|
120
|
+
|
121
|
+
case type
|
122
|
+
when :id
|
123
|
+
Dry::Types::FnContainer[node.fetch(0)]
|
124
|
+
when :callable
|
125
|
+
node.fetch(0)
|
126
|
+
when :method
|
127
|
+
target, method = node
|
128
|
+
target.method(method)
|
129
|
+
else
|
130
|
+
raise ArgumentError, "Cannot build callable from #{fn.inspect}"
|
131
|
+
end
|
117
132
|
end
|
118
133
|
end
|
119
134
|
end
|
@@ -1,93 +1,142 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/types/decorator"
|
5
|
+
require "dry/types/constraints"
|
6
|
+
require "dry/types/constrained/coercible"
|
4
7
|
|
5
8
|
module Dry
|
6
9
|
module Types
|
10
|
+
# Constrained types apply rules to the input
|
11
|
+
#
|
12
|
+
# @api public
|
7
13
|
class Constrained
|
8
14
|
include Type
|
9
|
-
include Dry::Equalizer(:type, :options, :rule, :meta)
|
10
15
|
include Decorator
|
11
16
|
include Builder
|
17
|
+
include Printable
|
18
|
+
include Dry::Equalizer(:type, :rule, inspect: false, immutable: true)
|
12
19
|
|
13
20
|
# @return [Dry::Logic::Rule]
|
14
21
|
attr_reader :rule
|
15
22
|
|
16
23
|
# @param [Type] type
|
24
|
+
#
|
17
25
|
# @param [Hash] options
|
18
|
-
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def initialize(type, **options)
|
19
29
|
super
|
20
30
|
@rule = options.fetch(:rule)
|
21
31
|
end
|
22
32
|
|
23
|
-
# @param [Object] input
|
24
33
|
# @return [Object]
|
25
|
-
#
|
26
|
-
|
27
|
-
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
def call_unsafe(input)
|
37
|
+
result = rule.(input)
|
38
|
+
|
39
|
+
if result.success?
|
40
|
+
type.call_unsafe(input)
|
41
|
+
else
|
28
42
|
raise ConstraintError.new(result, input)
|
29
|
-
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Object]
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
def call_safe(input, &block)
|
50
|
+
if rule[input]
|
51
|
+
type.call_safe(input, &block)
|
52
|
+
else
|
53
|
+
yield
|
54
|
+
end
|
30
55
|
end
|
31
|
-
|
32
|
-
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# @
|
37
|
-
#
|
38
|
-
#
|
56
|
+
|
57
|
+
# Safe coercion attempt. It is similar to #call with a
|
58
|
+
# block given but returns a Result instance with metadata
|
59
|
+
# about errors (if any).
|
60
|
+
#
|
61
|
+
# @overload try(input)
|
62
|
+
# @param [Object] input
|
63
|
+
# @return [Logic::Result]
|
64
|
+
#
|
65
|
+
# @overload try(input)
|
66
|
+
# @param [Object] input
|
67
|
+
# @yieldparam [Failure] failure
|
68
|
+
# @yieldreturn [Object]
|
69
|
+
# @return [Object]
|
70
|
+
#
|
71
|
+
# @api public
|
39
72
|
def try(input, &block)
|
40
73
|
result = rule.(input)
|
41
74
|
|
42
75
|
if result.success?
|
43
76
|
type.try(input, &block)
|
44
77
|
else
|
45
|
-
failure = failure(input, result)
|
46
|
-
|
78
|
+
failure = failure(input, ConstraintError.new(result, input))
|
79
|
+
block_given? ? yield(failure) : failure
|
47
80
|
end
|
48
81
|
end
|
49
82
|
|
50
|
-
# @param [Object] value
|
51
|
-
# @return [Boolean]
|
52
|
-
def valid?(value)
|
53
|
-
rule.(value).success? && type.valid?(value)
|
54
|
-
end
|
55
|
-
|
56
83
|
# @param [Hash] options
|
57
84
|
# The options hash provided to {Types.Rule} and combined
|
58
85
|
# using {&} with previous {#rule}
|
86
|
+
#
|
59
87
|
# @return [Constrained]
|
88
|
+
#
|
60
89
|
# @see Dry::Logic::Operators#and
|
90
|
+
#
|
91
|
+
# @api public
|
61
92
|
def constrained(options)
|
62
93
|
with(rule: rule & Types.Rule(options))
|
63
94
|
end
|
64
95
|
|
65
96
|
# @return [true]
|
97
|
+
#
|
98
|
+
# @api public
|
66
99
|
def constrained?
|
67
100
|
true
|
68
101
|
end
|
69
102
|
|
70
103
|
# @param [Object] value
|
104
|
+
#
|
71
105
|
# @return [Boolean]
|
106
|
+
#
|
107
|
+
# @api public
|
72
108
|
def ===(value)
|
73
109
|
valid?(value)
|
74
110
|
end
|
75
111
|
|
76
|
-
#
|
112
|
+
# Build lax type. Constraints are not applicable to lax types hence unwrapping
|
77
113
|
#
|
78
|
-
# @
|
114
|
+
# @return [Lax]
|
115
|
+
# @api public
|
116
|
+
def lax
|
117
|
+
type.lax
|
118
|
+
end
|
119
|
+
|
120
|
+
# @see Nominal#to_ast
|
121
|
+
# @api public
|
79
122
|
def to_ast(meta: true)
|
80
|
-
[:constrained, [type.to_ast(meta: meta),
|
81
|
-
|
82
|
-
|
123
|
+
[:constrained, [type.to_ast(meta: meta), rule.to_ast]]
|
124
|
+
end
|
125
|
+
|
126
|
+
# @api private
|
127
|
+
def constructor_type
|
128
|
+
type.constructor_type
|
83
129
|
end
|
84
130
|
|
85
131
|
private
|
86
132
|
|
87
133
|
# @param [Object] response
|
134
|
+
#
|
88
135
|
# @return [Boolean]
|
136
|
+
#
|
137
|
+
# @api private
|
89
138
|
def decorate?(response)
|
90
|
-
super || response.
|
139
|
+
super || response.is_a?(Constructor)
|
91
140
|
end
|
92
141
|
end
|
93
142
|
end
|
@@ -1,12 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
3
5
|
class Constrained
|
6
|
+
# Common coercion-related API for constrained types
|
7
|
+
#
|
8
|
+
# @api public
|
4
9
|
class Coercible < Constrained
|
5
|
-
# @
|
6
|
-
#
|
7
|
-
# @
|
8
|
-
|
9
|
-
|
10
|
+
# @return [Object]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def call_unsafe(input)
|
14
|
+
coerced = type.call_unsafe(input)
|
15
|
+
result = rule.(coerced)
|
16
|
+
|
17
|
+
if result.success?
|
18
|
+
coerced
|
19
|
+
else
|
20
|
+
raise ConstraintError.new(result, input)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Object]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
def call_safe(input)
|
28
|
+
coerced = type.call_safe(input) { return yield }
|
29
|
+
|
30
|
+
if rule[coerced]
|
31
|
+
coerced
|
32
|
+
else
|
33
|
+
yield(coerced)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @see Dry::Types::Constrained#try
|
38
|
+
#
|
39
|
+
# @api public
|
10
40
|
def try(input, &block)
|
11
41
|
result = type.try(input)
|
12
42
|
|
@@ -16,7 +46,7 @@ module Dry
|
|
16
46
|
if validation.success?
|
17
47
|
result
|
18
48
|
else
|
19
|
-
failure = failure(result.input, validation)
|
49
|
+
failure = failure(result.input, ConstraintError.new(validation, input))
|
20
50
|
block ? yield(failure) : failure
|
21
51
|
end
|
22
52
|
else
|
@@ -1,18 +1,32 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logic/rule_compiler"
|
4
|
+
require "dry/logic/predicates"
|
5
|
+
require "dry/logic/rule/predicate"
|
4
6
|
|
5
7
|
module Dry
|
8
|
+
# Helper methods for constraint types
|
9
|
+
#
|
10
|
+
# @api public
|
6
11
|
module Types
|
7
12
|
# @param [Hash] options
|
13
|
+
#
|
8
14
|
# @return [Dry::Logic::Rule]
|
15
|
+
#
|
16
|
+
# @api public
|
9
17
|
def self.Rule(options)
|
10
18
|
rule_compiler.(
|
11
|
-
options.map { |key, val|
|
19
|
+
options.map { |key, val|
|
20
|
+
Logic::Rule::Predicate.build(
|
21
|
+
Logic::Predicates[:"#{key}?"]
|
22
|
+
).curry(val).to_ast
|
23
|
+
}
|
12
24
|
).reduce(:and)
|
13
25
|
end
|
14
26
|
|
15
27
|
# @return [Dry::Logic::RuleCompiler]
|
28
|
+
#
|
29
|
+
# @api private
|
16
30
|
def self.rule_compiler
|
17
31
|
@rule_compiler ||= Logic::RuleCompiler.new(Logic::Predicates)
|
18
32
|
end
|
@@ -1,10 +1,18 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/types/fn_container"
|
5
|
+
require "dry/types/constructor/function"
|
6
|
+
require "dry/types/constructor/wrapper"
|
3
7
|
|
4
8
|
module Dry
|
5
9
|
module Types
|
6
|
-
|
7
|
-
|
10
|
+
# Constructor types apply a function to the input that is supposed to return
|
11
|
+
# a new value. Coercion is a common use case for constructor types.
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
class Constructor < Nominal
|
15
|
+
include Dry::Equalizer(:type, :options, inspect: false, immutable: true)
|
8
16
|
|
9
17
|
# @return [#call]
|
10
18
|
attr_reader :fn
|
@@ -12,114 +20,179 @@ module Dry
|
|
12
20
|
# @return [Type]
|
13
21
|
attr_reader :type
|
14
22
|
|
15
|
-
undef :constrained
|
23
|
+
undef :constrained?, :meta, :optional?, :primitive, :default?, :name
|
16
24
|
|
17
25
|
# @param [Builder, Object] input
|
18
26
|
# @param [Hash] options
|
19
27
|
# @param [#call, nil] block
|
20
|
-
|
21
|
-
|
22
|
-
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def self.new(input, **options, &block)
|
31
|
+
type = input.is_a?(Builder) ? input : Nominal.new(input)
|
32
|
+
super(type, **options, fn: Function[options.fetch(:fn, block)])
|
23
33
|
end
|
24
34
|
|
25
|
-
# @param [
|
35
|
+
# @param [Builder, Object] input
|
26
36
|
# @param [Hash] options
|
27
37
|
# @param [#call, nil] block
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
raise ArgumentError, 'Missing constructor block' if fn.nil?
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
def self.[](type, fn:, **options)
|
41
|
+
function = Function[fn]
|
33
42
|
|
34
|
-
|
43
|
+
if function.wrapper?
|
44
|
+
wrapper_type.new(type, fn: function, **options)
|
45
|
+
else
|
46
|
+
new(type, fn: function, **options)
|
47
|
+
end
|
35
48
|
end
|
36
49
|
|
37
|
-
# @
|
38
|
-
def
|
39
|
-
|
50
|
+
# @api private
|
51
|
+
def self.wrapper_type
|
52
|
+
@wrapper_type ||= begin
|
53
|
+
if self < Wrapper
|
54
|
+
self
|
55
|
+
else
|
56
|
+
const_set(:Wrapping, ::Class.new(self).include(Wrapper))
|
57
|
+
end
|
58
|
+
end
|
40
59
|
end
|
41
60
|
|
42
|
-
#
|
43
|
-
|
44
|
-
|
61
|
+
# Instantiate a new constructor type instance
|
62
|
+
#
|
63
|
+
# @param [Type] type
|
64
|
+
# @param [Function] fn
|
65
|
+
# @param [Hash] options
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def initialize(type, fn: nil, **options)
|
69
|
+
@type = type
|
70
|
+
@fn = fn
|
71
|
+
|
72
|
+
super(type, **options, fn: fn)
|
45
73
|
end
|
46
74
|
|
47
|
-
|
48
|
-
|
75
|
+
# @return [Object]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
def call_safe(input)
|
79
|
+
coerced = fn.(input) { |output = input| return yield(output) }
|
80
|
+
type.call_safe(coerced) { |output = coerced| yield(output) }
|
49
81
|
end
|
50
82
|
|
51
|
-
# @param [Object] input
|
52
83
|
# @return [Object]
|
53
|
-
|
54
|
-
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
def call_unsafe(input)
|
87
|
+
type.call_unsafe(fn.(input))
|
55
88
|
end
|
56
|
-
alias_method :[], :call
|
57
89
|
|
58
90
|
# @param [Object] input
|
59
91
|
# @param [#call,nil] block
|
92
|
+
#
|
60
93
|
# @return [Logic::Result, Types::Result]
|
61
94
|
# @return [Object] if block given and try fails
|
95
|
+
#
|
96
|
+
# @api public
|
62
97
|
def try(input, &block)
|
63
|
-
|
64
|
-
rescue
|
65
|
-
failure(input, e
|
98
|
+
value = fn.(input)
|
99
|
+
rescue CoercionError => e
|
100
|
+
failure = failure(input, e)
|
101
|
+
block_given? ? yield(failure) : failure
|
102
|
+
else
|
103
|
+
type.try(value, &block)
|
66
104
|
end
|
67
105
|
|
106
|
+
# Build a new constructor by appending a block to the coercion function
|
107
|
+
#
|
68
108
|
# @param [#call, nil] new_fn
|
69
109
|
# @param [Hash] options
|
70
110
|
# @param [#call, nil] block
|
111
|
+
#
|
71
112
|
# @return [Constructor]
|
113
|
+
#
|
114
|
+
# @api public
|
72
115
|
def constructor(new_fn = nil, **options, &block)
|
73
|
-
|
74
|
-
right = fn
|
75
|
-
|
76
|
-
with(options.merge(fn: -> input { left[right[input]] }))
|
77
|
-
end
|
116
|
+
next_fn = Function[new_fn || block]
|
78
117
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
118
|
+
if next_fn.wrapper?
|
119
|
+
self.class.wrapper_type.new(with(**options), fn: next_fn)
|
120
|
+
else
|
121
|
+
with(**options, fn: fn >> next_fn)
|
122
|
+
end
|
83
123
|
end
|
84
|
-
alias_method
|
124
|
+
alias_method :append, :constructor
|
125
|
+
alias_method :>>, :constructor
|
85
126
|
|
86
127
|
# @return [Class]
|
128
|
+
#
|
129
|
+
# @api private
|
87
130
|
def constrained_type
|
88
131
|
Constrained::Coercible
|
89
132
|
end
|
90
133
|
|
91
|
-
# @
|
134
|
+
# @see Nominal#to_ast
|
92
135
|
#
|
93
|
-
# @
|
136
|
+
# @api public
|
94
137
|
def to_ast(meta: true)
|
95
|
-
[:constructor, [type.to_ast(meta: meta),
|
96
|
-
register_fn(fn),
|
97
|
-
meta ? self.meta : EMPTY_HASH]]
|
138
|
+
[:constructor, [type.to_ast(meta: meta), fn.to_ast]]
|
98
139
|
end
|
99
140
|
|
100
|
-
|
141
|
+
# Build a new constructor by prepending a block to the coercion function
|
142
|
+
#
|
143
|
+
# @param [#call, nil] new_fn
|
144
|
+
# @param [Hash] options
|
145
|
+
# @param [#call, nil] block
|
146
|
+
#
|
147
|
+
# @return [Constructor]
|
148
|
+
#
|
149
|
+
# @api public
|
150
|
+
def prepend(new_fn = nil, **options, &block)
|
151
|
+
with(**options, fn: fn << (new_fn || block))
|
152
|
+
end
|
153
|
+
alias_method :<<, :prepend
|
154
|
+
|
155
|
+
# Build a lax type
|
156
|
+
#
|
157
|
+
# @return [Lax]
|
158
|
+
# @api public
|
159
|
+
def lax
|
160
|
+
Lax.new(constructor_type[type.lax, **options])
|
161
|
+
end
|
101
162
|
|
102
|
-
|
103
|
-
|
163
|
+
# Wrap the type with a proc
|
164
|
+
#
|
165
|
+
# @return [Proc]
|
166
|
+
#
|
167
|
+
# @api public
|
168
|
+
def to_proc
|
169
|
+
proc { |value| self.(value) }
|
104
170
|
end
|
105
171
|
|
172
|
+
private
|
173
|
+
|
106
174
|
# @param [Symbol] meth
|
107
175
|
# @param [Boolean] include_private
|
108
176
|
# @return [Boolean]
|
177
|
+
#
|
178
|
+
# @api private
|
109
179
|
def respond_to_missing?(meth, include_private = false)
|
110
180
|
super || type.respond_to?(meth)
|
111
181
|
end
|
112
182
|
|
113
183
|
# Delegates missing methods to {#type}
|
114
|
-
#
|
184
|
+
#
|
185
|
+
# @param [Symbol] method
|
115
186
|
# @param [Array] args
|
116
187
|
# @param [#call, nil] block
|
117
|
-
|
118
|
-
|
119
|
-
|
188
|
+
#
|
189
|
+
# @api private
|
190
|
+
def method_missing(method, *args, &block)
|
191
|
+
if type.respond_to?(method)
|
192
|
+
response = type.public_send(method, *args, &block)
|
120
193
|
|
121
|
-
if response.
|
122
|
-
|
194
|
+
if response.is_a?(Type) && type.class.equal?(response.class)
|
195
|
+
response.constructor_type[response, **options]
|
123
196
|
else
|
124
197
|
response
|
125
198
|
end
|