plumb 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +391 -52
- data/examples/concurrent_downloads.rb +3 -3
- data/examples/env_config.rb +2 -2
- data/examples/event_registry.rb +120 -0
- data/lib/plumb/and.rb +4 -3
- data/lib/plumb/any_class.rb +4 -4
- data/lib/plumb/array_class.rb +8 -5
- data/lib/plumb/attributes.rb +262 -0
- data/lib/plumb/build.rb +4 -3
- data/lib/plumb/{steppable.rb → composable.rb} +61 -28
- data/lib/plumb/decorator.rb +57 -0
- data/lib/plumb/deferred.rb +1 -1
- data/lib/plumb/hash_class.rb +19 -8
- data/lib/plumb/hash_map.rb +8 -6
- data/lib/plumb/interface_class.rb +6 -2
- data/lib/plumb/json_schema_visitor.rb +50 -32
- data/lib/plumb/match_class.rb +4 -3
- data/lib/plumb/metadata.rb +5 -1
- data/lib/plumb/metadata_visitor.rb +13 -42
- data/lib/plumb/not.rb +4 -3
- data/lib/plumb/or.rb +10 -4
- data/lib/plumb/pipeline.rb +6 -5
- data/lib/plumb/policy.rb +10 -3
- data/lib/plumb/schema.rb +11 -10
- data/lib/plumb/static_class.rb +4 -3
- data/lib/plumb/step.rb +4 -3
- data/lib/plumb/stream_class.rb +8 -7
- data/lib/plumb/tagged_hash.rb +10 -10
- data/lib/plumb/transform.rb +4 -3
- data/lib/plumb/tuple_class.rb +8 -8
- data/lib/plumb/type_registry.rb +5 -2
- data/lib/plumb/types.rb +6 -1
- data/lib/plumb/value_class.rb +4 -3
- data/lib/plumb/version.rb +1 -1
- data/lib/plumb/visitor_handlers.rb +6 -0
- data/lib/plumb.rb +11 -5
- metadata +6 -3
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plumb
|
4
|
+
# A class to help decorate all or some types in a
|
5
|
+
# type composition.
|
6
|
+
# Example:
|
7
|
+
# Type = Types::Array[Types::String | Types::Integer]
|
8
|
+
# Decorated = Plumb::Decorator.(Type) do |type|
|
9
|
+
# if type.is_a?(Plumb::ArrayClass)
|
10
|
+
# LoggerType.new(type, 'array')
|
11
|
+
# else
|
12
|
+
# type
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
class Decorator
|
16
|
+
def self.call(type, &block)
|
17
|
+
new(block).visit(type)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(block)
|
21
|
+
@block = block
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param type [Composable]
|
25
|
+
# @return [Composable]
|
26
|
+
def visit(type)
|
27
|
+
type = case type
|
28
|
+
when And
|
29
|
+
left, right = visit_children(type)
|
30
|
+
And.new(left, right)
|
31
|
+
when Or
|
32
|
+
left, right = visit_children(type)
|
33
|
+
Or.new(left, right)
|
34
|
+
when Not
|
35
|
+
child = visit_children(type).first
|
36
|
+
Not.new(child, errors: type.errors)
|
37
|
+
when Policy
|
38
|
+
child = visit_children(type).first
|
39
|
+
Policy.new(type.policy_name, type.arg, child)
|
40
|
+
else
|
41
|
+
type
|
42
|
+
end
|
43
|
+
|
44
|
+
decorate(type)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def visit_children(type)
|
50
|
+
type.children.map { |child| visit(child) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def decorate(type)
|
54
|
+
@block.call(type)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/plumb/deferred.rb
CHANGED
data/lib/plumb/hash_class.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
require 'plumb/key'
|
5
5
|
require 'plumb/static_class'
|
6
6
|
require 'plumb/hash_map'
|
@@ -8,7 +8,9 @@ require 'plumb/tagged_hash'
|
|
8
8
|
|
9
9
|
module Plumb
|
10
10
|
class HashClass
|
11
|
-
include
|
11
|
+
include Composable
|
12
|
+
|
13
|
+
NOT_A_HASH = { _: 'must be a Hash' }.freeze
|
12
14
|
|
13
15
|
attr_reader :_schema
|
14
16
|
|
@@ -31,7 +33,7 @@ module Plumb
|
|
31
33
|
in [::Hash => hash]
|
32
34
|
self.class.new(schema: _schema.merge(wrap_keys_and_values(hash)), inclusive: @inclusive)
|
33
35
|
in [key_type, value_type]
|
34
|
-
HashMap.new(
|
36
|
+
HashMap.new(Composable.wrap(key_type), Composable.wrap(value_type))
|
35
37
|
else
|
36
38
|
raise ::ArgumentError, "unexpected value to Types::Hash#schema #{args.inspect}"
|
37
39
|
end
|
@@ -44,9 +46,14 @@ module Plumb
|
|
44
46
|
# we need to keep the right-side key, because even if the key name is the same,
|
45
47
|
# it's optional flag might have changed
|
46
48
|
def +(other)
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
other_schema = case other
|
50
|
+
when HashClass then other._schema
|
51
|
+
when ::Hash then other
|
52
|
+
else
|
53
|
+
raise ArgumentError, "expected a HashClass or Hash, got #{other.class}"
|
54
|
+
end
|
55
|
+
|
56
|
+
self.class.new(schema: merge_rightmost_keys(_schema, other_schema), inclusive: @inclusive)
|
50
57
|
end
|
51
58
|
|
52
59
|
def &(other)
|
@@ -97,7 +104,7 @@ module Plumb
|
|
97
104
|
end
|
98
105
|
|
99
106
|
def call(result)
|
100
|
-
return result.invalid(errors:
|
107
|
+
return result.invalid(errors: NOT_A_HASH) unless result.value.is_a?(::Hash)
|
101
108
|
return result unless _schema.any?
|
102
109
|
|
103
110
|
input = result.value
|
@@ -121,6 +128,10 @@ module Plumb
|
|
121
128
|
errors.any? ? result.invalid(output, errors:) : result.valid(output)
|
122
129
|
end
|
123
130
|
|
131
|
+
def ==(other)
|
132
|
+
other.is_a?(self.class) && other._schema == _schema
|
133
|
+
end
|
134
|
+
|
124
135
|
private
|
125
136
|
|
126
137
|
def _inspect
|
@@ -138,7 +149,7 @@ module Plumb
|
|
138
149
|
when Callable
|
139
150
|
hash
|
140
151
|
else # leaf values
|
141
|
-
|
152
|
+
Composable.wrap(hash)
|
142
153
|
end
|
143
154
|
end
|
144
155
|
|
data/lib/plumb/hash_map.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class HashMap
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :children
|
10
10
|
|
11
11
|
def initialize(key_type, value_type)
|
12
12
|
@key_type = key_type
|
13
13
|
@value_type = value_type
|
14
|
+
@children = [key_type, value_type].freeze
|
14
15
|
freeze
|
15
16
|
end
|
16
17
|
|
@@ -35,19 +36,20 @@ module Plumb
|
|
35
36
|
end
|
36
37
|
|
37
38
|
def filtered
|
38
|
-
FilteredHashMap.new(key_type, value_type)
|
39
|
+
FilteredHashMap.new(@key_type, @value_type)
|
39
40
|
end
|
40
41
|
|
41
42
|
private def _inspect = "HashMap[#{@key_type.inspect}, #{@value_type.inspect}]"
|
42
43
|
|
43
44
|
class FilteredHashMap
|
44
|
-
include
|
45
|
+
include Composable
|
45
46
|
|
46
|
-
attr_reader :
|
47
|
+
attr_reader :children
|
47
48
|
|
48
49
|
def initialize(key_type, value_type)
|
49
50
|
@key_type = key_type
|
50
51
|
@value_type = value_type
|
52
|
+
@children = [key_type, value_type].freeze
|
51
53
|
freeze
|
52
54
|
end
|
53
55
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class InterfaceClass
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
9
|
attr_reader :method_names
|
10
10
|
|
@@ -13,6 +13,10 @@ module Plumb
|
|
13
13
|
freeze
|
14
14
|
end
|
15
15
|
|
16
|
+
def ==(other)
|
17
|
+
other.is_a?(self.class) && other.method_names == method_names
|
18
|
+
end
|
19
|
+
|
16
20
|
def of(*args)
|
17
21
|
case args
|
18
22
|
in Array => symbols if symbols.all? { |s| s.is_a?(::Symbol) }
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'date'
|
3
4
|
require 'plumb/visitor_handlers'
|
4
5
|
|
5
6
|
module Plumb
|
@@ -23,11 +24,15 @@ module Plumb
|
|
23
24
|
MAX_ITEMS = 'maxItems'
|
24
25
|
MIN_LENGTH = 'minLength'
|
25
26
|
MAX_LENGTH = 'maxLength'
|
27
|
+
ENVELOPE = {
|
28
|
+
'$schema' => 'https://json-schema.org/draft-08/schema#'
|
29
|
+
}.freeze
|
26
30
|
|
27
|
-
def self.call(node)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
+
def self.call(node, root: true)
|
32
|
+
data = new.visit(node)
|
33
|
+
return data unless root
|
34
|
+
|
35
|
+
ENVELOPE.merge(data)
|
31
36
|
end
|
32
37
|
|
33
38
|
private def stringify_keys(hash) = hash.transform_keys(&:to_s)
|
@@ -37,7 +42,7 @@ module Plumb
|
|
37
42
|
end
|
38
43
|
|
39
44
|
on(:pipeline) do |node, props|
|
40
|
-
|
45
|
+
visit_children(node, props)
|
41
46
|
end
|
42
47
|
|
43
48
|
on(:step) do |node, props|
|
@@ -58,9 +63,12 @@ module Plumb
|
|
58
63
|
)
|
59
64
|
end
|
60
65
|
|
66
|
+
on(:data) do |node, props|
|
67
|
+
visit_name :hash, node._schema, props
|
68
|
+
end
|
69
|
+
|
61
70
|
on(:and) do |node, props|
|
62
|
-
left = visit(
|
63
|
-
right = visit(node.right)
|
71
|
+
left, right = node.children.map { |c| visit(c) }
|
64
72
|
type = right[TYPE] || left[TYPE]
|
65
73
|
props = props.merge(left).merge(right)
|
66
74
|
props = props.merge(TYPE => type) if type
|
@@ -69,11 +77,10 @@ module Plumb
|
|
69
77
|
|
70
78
|
# A "default" value is usually an "or" of expected_value | (undefined >> static_value)
|
71
79
|
on(:or) do |node, props|
|
72
|
-
left = visit(
|
73
|
-
|
74
|
-
any_of = [left, right].uniq
|
80
|
+
left, right = node.children.map { |c| visit(c) }
|
81
|
+
any_of = [left, right].uniq.filter(&:any?)
|
75
82
|
if any_of.size == 1
|
76
|
-
props.merge(
|
83
|
+
props.merge(any_of.first)
|
77
84
|
elsif any_of.size == 2 && (defidx = any_of.index { |p| p.key?(DEFAULT) })
|
78
85
|
val = any_of[defidx.zero? ? 1 : 0]
|
79
86
|
props.merge(val).merge(DEFAULT => any_of[defidx][DEFAULT])
|
@@ -83,22 +90,23 @@ module Plumb
|
|
83
90
|
end
|
84
91
|
|
85
92
|
on(:not) do |node, props|
|
86
|
-
props.merge(NOT =>
|
93
|
+
props.merge(NOT => visit_children(node))
|
87
94
|
end
|
88
95
|
|
89
96
|
on(:value) do |node, props|
|
90
|
-
|
97
|
+
value = node.children.first
|
98
|
+
props = case value
|
91
99
|
when ::String, ::Symbol, ::Numeric
|
92
|
-
props.merge(CONST =>
|
100
|
+
props.merge(CONST => value)
|
93
101
|
else
|
94
102
|
props
|
95
103
|
end
|
96
104
|
|
97
|
-
visit(
|
105
|
+
visit(value, props)
|
98
106
|
end
|
99
107
|
|
100
108
|
on(:transform) do |node, props|
|
101
|
-
|
109
|
+
visit_children(node, props)
|
102
110
|
end
|
103
111
|
|
104
112
|
on(:undefined) do |_node, props|
|
@@ -108,18 +116,19 @@ module Plumb
|
|
108
116
|
on(:static) do |node, props|
|
109
117
|
# Set const AND default
|
110
118
|
# to emulate static values
|
111
|
-
|
119
|
+
value = node.children.first
|
120
|
+
props = case value
|
112
121
|
when ::String, ::Symbol, ::Numeric
|
113
|
-
props.merge(CONST =>
|
122
|
+
props.merge(CONST => value, DEFAULT => value)
|
114
123
|
else
|
115
124
|
props
|
116
125
|
end
|
117
126
|
|
118
|
-
visit(
|
127
|
+
visit(value, props)
|
119
128
|
end
|
120
129
|
|
121
130
|
on(:policy) do |node, props|
|
122
|
-
props =
|
131
|
+
props = visit_children(node, props)
|
123
132
|
method_name = :"visit_#{node.policy_name}_policy"
|
124
133
|
if respond_to?(method_name)
|
125
134
|
send(method_name, node, props)
|
@@ -168,14 +177,15 @@ module Plumb
|
|
168
177
|
|
169
178
|
on(:match) do |node, props|
|
170
179
|
# Set const if primitive
|
171
|
-
|
180
|
+
matcher = node.children.first
|
181
|
+
props = case matcher
|
172
182
|
when ::String, ::Symbol, ::Numeric
|
173
|
-
props.merge(CONST =>
|
183
|
+
props.merge(CONST => matcher)
|
174
184
|
else
|
175
185
|
props
|
176
186
|
end
|
177
187
|
|
178
|
-
visit(
|
188
|
+
visit(matcher, props)
|
179
189
|
end
|
180
190
|
|
181
191
|
on(:boolean) do |_node, props|
|
@@ -228,6 +238,14 @@ module Plumb
|
|
228
238
|
props.merge(opts)
|
229
239
|
end
|
230
240
|
|
241
|
+
on(::Time) do |_node, props|
|
242
|
+
props.merge(TYPE => 'string', 'format' => 'date-time')
|
243
|
+
end
|
244
|
+
|
245
|
+
on(::Date) do |_node, props|
|
246
|
+
props.merge(TYPE => 'string', 'format' => 'date')
|
247
|
+
end
|
248
|
+
|
231
249
|
on(::Hash) do |_node, props|
|
232
250
|
props.merge(TYPE => 'object')
|
233
251
|
end
|
@@ -245,7 +263,7 @@ module Plumb
|
|
245
263
|
{
|
246
264
|
TYPE => 'object',
|
247
265
|
'patternProperties' => {
|
248
|
-
'.*' => visit(node.
|
266
|
+
'.*' => visit(node.children[1])
|
249
267
|
}
|
250
268
|
}
|
251
269
|
end
|
@@ -254,27 +272,27 @@ module Plumb
|
|
254
272
|
{
|
255
273
|
TYPE => 'object',
|
256
274
|
'patternProperties' => {
|
257
|
-
'.*' => visit(node.
|
275
|
+
'.*' => visit(node.children[1])
|
258
276
|
}
|
259
277
|
}
|
260
278
|
end
|
261
279
|
|
262
280
|
on(:build) do |node, props|
|
263
|
-
|
281
|
+
visit_children(node, props)
|
264
282
|
end
|
265
283
|
|
266
284
|
on(:array) do |node, _props|
|
267
|
-
|
268
|
-
{ TYPE => 'array', ITEMS =>
|
285
|
+
items_props = visit_children(node)
|
286
|
+
{ TYPE => 'array', ITEMS => items_props }
|
269
287
|
end
|
270
288
|
|
271
289
|
on(:stream) do |node, _props|
|
272
|
-
|
273
|
-
{ TYPE => 'array', ITEMS =>
|
290
|
+
items_props = visit_children(node)
|
291
|
+
{ TYPE => 'array', ITEMS => items_props }
|
274
292
|
end
|
275
293
|
|
276
294
|
on(:tuple) do |node, _props|
|
277
|
-
items = node.
|
295
|
+
items = node.children.map { |t| visit(t) }
|
278
296
|
{ TYPE => 'array', 'prefixItems' => items }
|
279
297
|
end
|
280
298
|
|
@@ -286,7 +304,7 @@ module Plumb
|
|
286
304
|
}
|
287
305
|
|
288
306
|
key = node.key.to_s
|
289
|
-
children = node.
|
307
|
+
children = node.children.map { |c| visit(c) }
|
290
308
|
key_enum = children.map { |c| c[PROPERTIES][key][CONST] }
|
291
309
|
key_type = children.map { |c| c[PROPERTIES][key][TYPE] }
|
292
310
|
required << key
|
data/lib/plumb/match_class.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class MatchClass
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :children
|
10
10
|
|
11
11
|
def initialize(matcher = Undefined, error: nil, label: nil)
|
12
12
|
raise TypeError 'matcher must respond to #===' unless matcher.respond_to?(:===)
|
@@ -14,6 +14,7 @@ module Plumb
|
|
14
14
|
@matcher = matcher
|
15
15
|
@error = error.nil? ? build_error(matcher) : (error % matcher)
|
16
16
|
@label = matcher.is_a?(Class) ? matcher.inspect : "Match(#{label || @matcher.inspect})"
|
17
|
+
@children = [matcher].freeze
|
17
18
|
freeze
|
18
19
|
end
|
19
20
|
|
data/lib/plumb/metadata.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Plumb
|
4
4
|
class Metadata
|
5
|
-
include
|
5
|
+
include Composable
|
6
6
|
|
7
7
|
attr_reader :metadata
|
8
8
|
|
@@ -11,6 +11,10 @@ module Plumb
|
|
11
11
|
freeze
|
12
12
|
end
|
13
13
|
|
14
|
+
def ==(other)
|
15
|
+
other.is_a?(self.class) && @metadata == other.metadata
|
16
|
+
end
|
17
|
+
|
14
18
|
def call(result) = result
|
15
19
|
|
16
20
|
private def _inspect = "Metadata[#{@metadata.inspect}]"
|
@@ -10,23 +10,14 @@ module Plumb
|
|
10
10
|
new.visit(node)
|
11
11
|
end
|
12
12
|
|
13
|
-
def on_missing_handler(node, props,
|
13
|
+
def on_missing_handler(node, props, _method_name)
|
14
14
|
return props.merge(type: node) if node.instance_of?(Class)
|
15
15
|
|
16
|
-
|
17
|
-
props
|
18
|
-
end
|
19
|
-
|
20
|
-
on(:undefined) do |_node, props|
|
21
|
-
props
|
22
|
-
end
|
16
|
+
return props unless node.respond_to?(:children)
|
23
17
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
on(:pipeline) do |node, props|
|
29
|
-
visit(node.type, props)
|
18
|
+
node.children.reduce(props) do |acc, child|
|
19
|
+
visit(child, acc)
|
20
|
+
end
|
30
21
|
end
|
31
22
|
|
32
23
|
on(:step) do |node, props|
|
@@ -42,17 +33,12 @@ module Plumb
|
|
42
33
|
props.merge(match: node, type:)
|
43
34
|
end
|
44
35
|
|
45
|
-
on(:match) do |node, props|
|
46
|
-
visit(node.matcher, props)
|
47
|
-
end
|
48
|
-
|
49
36
|
on(:hash) do |_node, props|
|
50
37
|
props.merge(type: Hash)
|
51
38
|
end
|
52
39
|
|
53
40
|
on(:and) do |node, props|
|
54
|
-
left = visit(
|
55
|
-
right = visit(node.right)
|
41
|
+
left, right = node.children.map { |child| visit(child) }
|
56
42
|
type = right[:type] || left[:type]
|
57
43
|
props = props.merge(left).merge(right)
|
58
44
|
props = props.merge(type:) if type
|
@@ -60,7 +46,7 @@ module Plumb
|
|
60
46
|
end
|
61
47
|
|
62
48
|
on(:or) do |node, props|
|
63
|
-
child_metas =
|
49
|
+
child_metas = node.children.map { |child| visit(child) }
|
64
50
|
types = child_metas.map { |child| child[:type] }.flatten.compact
|
65
51
|
types = types.first if types.size == 1
|
66
52
|
child_metas.reduce(props) do |acc, child|
|
@@ -68,27 +54,16 @@ module Plumb
|
|
68
54
|
end.merge(type: types)
|
69
55
|
end
|
70
56
|
|
71
|
-
on(:value) do |node, props|
|
72
|
-
visit(node.value, props)
|
73
|
-
end
|
74
|
-
|
75
|
-
on(:transform) do |node, props|
|
76
|
-
props.merge(type: node.target_type)
|
77
|
-
end
|
78
|
-
|
79
57
|
on(:static) do |node, props|
|
80
|
-
|
81
|
-
|
58
|
+
value = node.children[0]
|
59
|
+
type = value.is_a?(Class) ? value : value.class
|
60
|
+
props.merge(static: value, type:)
|
82
61
|
end
|
83
62
|
|
84
63
|
on(:policy) do |node, props|
|
85
|
-
visit(node.
|
86
|
-
|
87
|
-
|
88
|
-
on(:rules) do |node, props|
|
89
|
-
node.rules.reduce(props) do |acc, rule|
|
90
|
-
acc.merge(rule.name => rule.arg_value)
|
91
|
-
end
|
64
|
+
props = visit(node.children[0], props)
|
65
|
+
props = props.merge(node.policy_name => node.arg) unless node.arg == Plumb::Undefined
|
66
|
+
props
|
92
67
|
end
|
93
68
|
|
94
69
|
on(:boolean) do |_node, props|
|
@@ -103,10 +78,6 @@ module Plumb
|
|
103
78
|
props.merge(type: Hash)
|
104
79
|
end
|
105
80
|
|
106
|
-
on(:build) do |node, props|
|
107
|
-
visit(node.type, props)
|
108
|
-
end
|
109
|
-
|
110
81
|
on(:array) do |_node, props|
|
111
82
|
props.merge(type: Array)
|
112
83
|
end
|
data/lib/plumb/not.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class Not
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :children, :errors
|
10
10
|
|
11
11
|
def initialize(step, errors: nil)
|
12
12
|
@step = step
|
13
13
|
@errors = errors
|
14
|
+
@children = [step].freeze
|
14
15
|
freeze
|
15
16
|
end
|
16
17
|
|
data/lib/plumb/or.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class Or
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :children
|
10
10
|
|
11
11
|
def initialize(left, right)
|
12
12
|
@left = left
|
13
13
|
@right = right
|
14
|
+
@children = [left, right].freeze
|
14
15
|
freeze
|
15
16
|
end
|
16
17
|
|
@@ -23,7 +24,12 @@ module Plumb
|
|
23
24
|
return left_result if left_result.valid?
|
24
25
|
|
25
26
|
right_result = @right.call(result)
|
26
|
-
right_result.valid?
|
27
|
+
if right_result.valid?
|
28
|
+
right_result
|
29
|
+
else
|
30
|
+
right_result.invalid(errors: [left_result.errors,
|
31
|
+
right_result.errors].flatten)
|
32
|
+
end
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
data/lib/plumb/pipeline.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class Pipeline
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
9
|
class AroundStep
|
10
|
-
include
|
10
|
+
include Composable
|
11
11
|
|
12
12
|
def initialize(step, block)
|
13
13
|
@step = step
|
@@ -19,10 +19,11 @@ module Plumb
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
attr_reader :
|
22
|
+
attr_reader :children
|
23
23
|
|
24
24
|
def initialize(type = Types::Any, &setup)
|
25
25
|
@type = type
|
26
|
+
@children = [type].freeze
|
26
27
|
@around_blocks = []
|
27
28
|
return unless block_given?
|
28
29
|
|
@@ -38,7 +39,7 @@ module Plumb
|
|
38
39
|
callable ||= block
|
39
40
|
unless is_a_step?(callable)
|
40
41
|
raise ArgumentError,
|
41
|
-
|
42
|
+
"#step expects an interface #call(Result) Result, but got #{callable.inspect}"
|
42
43
|
end
|
43
44
|
|
44
45
|
callable = @around_blocks.reduce(callable) { |cl, bl| AroundStep.new(cl, bl) } if @around_blocks.any?
|
data/lib/plumb/policy.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
# Wrap a policy composition ("step") in a Policy object.
|
7
7
|
# So that visitors such as JSONSchema and Metadata visitors
|
8
8
|
# can define dedicated handlers for policies, if they need to.
|
9
9
|
class Policy
|
10
|
-
include
|
10
|
+
include Composable
|
11
11
|
|
12
|
-
attr_reader :policy_name, :arg, :
|
12
|
+
attr_reader :policy_name, :arg, :children
|
13
13
|
|
14
14
|
# @param policy_name [Symbol]
|
15
15
|
# @param arg [Object, nil] the argument to the policy, if any.
|
@@ -18,9 +18,16 @@ module Plumb
|
|
18
18
|
@policy_name = policy_name
|
19
19
|
@arg = arg
|
20
20
|
@step = step
|
21
|
+
@children = [step].freeze
|
21
22
|
freeze
|
22
23
|
end
|
23
24
|
|
25
|
+
def ==(other)
|
26
|
+
other.is_a?(self.class) &&
|
27
|
+
policy_name == other.policy_name &&
|
28
|
+
arg == other.arg
|
29
|
+
end
|
30
|
+
|
24
31
|
# The standard Step interface.
|
25
32
|
# @param result [Result::Valid]
|
26
33
|
# @return [Result::Valid, Result::Invalid]
|