plumb 0.0.3 → 0.0.4
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/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]
|