dry-types 0.13.2 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|