plumb 0.0.3 → 0.0.5
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 +609 -57
- data/bench/compare_parametric_schema.rb +102 -0
- data/bench/compare_parametric_struct.rb +68 -0
- data/bench/parametric_schema.rb +229 -0
- data/bench/plumb_hash.rb +109 -0
- data/examples/concurrent_downloads.rb +3 -3
- data/examples/env_config.rb +2 -2
- data/examples/event_registry.rb +127 -0
- data/examples/weekdays.rb +1 -1
- 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 +268 -0
- data/lib/plumb/build.rb +4 -3
- data/lib/plumb/composable.rb +381 -0
- 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 +59 -32
- data/lib/plumb/match_class.rb +5 -4
- 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 +27 -7
- 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 +11 -11
- 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 +30 -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 +10 -3
- data/lib/plumb/steppable.rb +0 -229
data/lib/plumb/tagged_hash.rb
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class TaggedHash
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :key, :
|
9
|
+
attr_reader :key, :children
|
10
10
|
|
11
|
-
def initialize(hash_type, key,
|
11
|
+
def initialize(hash_type, key, children)
|
12
12
|
@hash_type = hash_type
|
13
13
|
@key = Key.wrap(key)
|
14
|
-
@
|
14
|
+
@children = children
|
15
15
|
|
16
|
-
raise ArgumentError, 'all types must be HashClass' if @
|
16
|
+
raise ArgumentError, 'all types must be HashClass' if @children.size.zero? || @children.any? do |t|
|
17
17
|
!t.is_a?(HashClass)
|
18
18
|
end
|
19
|
-
raise ArgumentError, "all types must define key #{@key}" unless @
|
19
|
+
raise ArgumentError, "all types must define key #{@key}" unless @children.all? { |t| !!t.at_key(@key) }
|
20
20
|
|
21
21
|
# types are assumed to have literal values for the index field :key
|
22
|
-
@index = @
|
22
|
+
@index = @children.each.with_object({}) do |t, memo|
|
23
23
|
key_type = t.at_key(@key)
|
24
|
-
raise
|
24
|
+
raise ParseError, "key type at :#{@key} #{key_type} must be a Match type" unless key_type.is_a?(MatchClass)
|
25
25
|
|
26
|
-
memo[key_type.
|
26
|
+
memo[key_type.children[0]] = t
|
27
27
|
end
|
28
28
|
|
29
29
|
freeze
|
@@ -41,6 +41,6 @@ module Plumb
|
|
41
41
|
|
42
42
|
private
|
43
43
|
|
44
|
-
def _inspect = "TaggedHash[#{@key.inspect}, #{@
|
44
|
+
def _inspect = "TaggedHash[#{@key.inspect}, #{@children.map(&:inspect).join(', ')}]"
|
45
45
|
end
|
46
46
|
end
|
data/lib/plumb/transform.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 Transform
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :children
|
10
10
|
|
11
11
|
def initialize(target_type, callable)
|
12
12
|
@target_type = target_type
|
13
13
|
@callable = callable || Plumb::NOOP
|
14
|
+
@children = [target_type].freeze
|
14
15
|
freeze
|
15
16
|
end
|
16
17
|
|
data/lib/plumb/tuple_class.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
|
class TupleClass
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :children
|
10
10
|
|
11
|
-
def initialize(*
|
12
|
-
@
|
11
|
+
def initialize(*children)
|
12
|
+
@children = children.map { |t| Composable.wrap(t) }.freeze
|
13
13
|
freeze
|
14
14
|
end
|
15
15
|
|
@@ -21,10 +21,10 @@ module Plumb
|
|
21
21
|
|
22
22
|
def call(result)
|
23
23
|
return result.invalid(errors: 'must be an Array') unless result.value.is_a?(::Array)
|
24
|
-
return result.invalid(errors: 'must have the same size') unless result.value.size == @
|
24
|
+
return result.invalid(errors: 'must have the same size') unless result.value.size == @children.size
|
25
25
|
|
26
26
|
errors = {}
|
27
|
-
values = @
|
27
|
+
values = @children.map.with_index do |type, idx|
|
28
28
|
val = result.value[idx]
|
29
29
|
r = type.resolve(val)
|
30
30
|
errors[idx] = ["expected #{type.inspect}, got #{val.inspect}", r.errors].flatten unless r.valid?
|
@@ -39,7 +39,7 @@ module Plumb
|
|
39
39
|
private
|
40
40
|
|
41
41
|
def _inspect
|
42
|
-
"Tuple[#{@
|
42
|
+
"Tuple[#{@children.map(&:inspect).join(', ')}]"
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
data/lib/plumb/type_registry.rb
CHANGED
@@ -7,7 +7,7 @@ module Plumb
|
|
7
7
|
case obj
|
8
8
|
when Module
|
9
9
|
obj.extend TypeRegistry
|
10
|
-
when
|
10
|
+
when Composable
|
11
11
|
anc = [name, const_name].join('::')
|
12
12
|
obj.freeze.name.set(anc)
|
13
13
|
end
|
@@ -17,16 +17,19 @@ module Plumb
|
|
17
17
|
host.extend TypeRegistry
|
18
18
|
constants(false).each do |const_name|
|
19
19
|
const = const_get(const_name)
|
20
|
+
|
20
21
|
anc = [host.name, const_name].join('::')
|
21
22
|
case const
|
22
23
|
when Module
|
24
|
+
next if const.is_a?(Class)
|
25
|
+
|
23
26
|
child_mod = Module.new
|
24
27
|
child_mod.define_singleton_method(:name) do
|
25
28
|
anc
|
26
29
|
end
|
27
30
|
child_mod.send(:include, const)
|
28
31
|
host.const_set(const_name, child_mod)
|
29
|
-
when
|
32
|
+
when Composable
|
30
33
|
type = const.dup
|
31
34
|
type.freeze.name.set(anc)
|
32
35
|
host.const_set(const_name, type)
|
data/lib/plumb/types.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bigdecimal'
|
4
|
+
require 'uri'
|
5
|
+
require 'date'
|
4
6
|
|
5
7
|
module Plumb
|
6
8
|
# Define core policies
|
@@ -101,7 +103,19 @@ module Plumb
|
|
101
103
|
Types::Static[value]
|
102
104
|
end
|
103
105
|
|
104
|
-
|
106
|
+
(Types::Undefined >> val_type) | type
|
107
|
+
end
|
108
|
+
|
109
|
+
# Wrap a step execution in a rescue block.
|
110
|
+
# Expect a specific exception class, and return an invalid result if it is raised.
|
111
|
+
# Usage:
|
112
|
+
# type = Types::String.build(Date, :parse).policy(:rescue, Date::Error)
|
113
|
+
policy :rescue do |type, exception_class|
|
114
|
+
Step.new(nil, 'Rescue') do |result|
|
115
|
+
type.call(result)
|
116
|
+
rescue exception_class => e
|
117
|
+
result.invalid(errors: e.message)
|
118
|
+
end
|
105
119
|
end
|
106
120
|
|
107
121
|
# Split a string into an array. Default separator is /\s*,\s*/
|
@@ -145,6 +159,17 @@ module Plumb
|
|
145
159
|
Tuple = TupleClass.new
|
146
160
|
Hash = HashClass.new
|
147
161
|
Interface = InterfaceClass.new
|
162
|
+
Email = String[URI::MailTo::EMAIL_REGEXP].as_node(:email)
|
163
|
+
Date = Any[::Date]
|
164
|
+
|
165
|
+
module UUID
|
166
|
+
V4 = String[/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i].as_node(:uuid)
|
167
|
+
end
|
168
|
+
|
169
|
+
class Data
|
170
|
+
extend Composable
|
171
|
+
include Plumb::Attributes
|
172
|
+
end
|
148
173
|
|
149
174
|
module Lax
|
150
175
|
NUMBER_EXPR = /^\d{1,3}(?:,\d{3})*(?:\.\d+)?$/
|
@@ -185,6 +210,10 @@ module Plumb
|
|
185
210
|
Boolean = True | False
|
186
211
|
|
187
212
|
Nil = Nil | (String[BLANK_STRING] >> nil)
|
213
|
+
|
214
|
+
# Accept a Date, or a string that can be parsed into a Date
|
215
|
+
# via Date.parse
|
216
|
+
Date = Date | (String >> Any.build(::Date, :parse).policy(:rescue, ::Date::Error))
|
188
217
|
end
|
189
218
|
end
|
190
219
|
end
|
data/lib/plumb/value_class.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class ValueClass
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :children
|
10
10
|
|
11
11
|
def initialize(value = Undefined)
|
12
12
|
@value = value
|
13
|
+
@children = [value].freeze
|
13
14
|
freeze
|
14
15
|
end
|
15
16
|
|
data/lib/plumb/version.rb
CHANGED
@@ -39,5 +39,11 @@ module Plumb
|
|
39
39
|
def on_missing_handler(node, _props, method_name)
|
40
40
|
raise "No handler for #{node.inspect} with :#{method_name}"
|
41
41
|
end
|
42
|
+
|
43
|
+
def visit_children(node, props = BLANK_HASH)
|
44
|
+
node.children.reduce(props) do |acc, child|
|
45
|
+
visit(child, acc)
|
46
|
+
end
|
47
|
+
end
|
42
48
|
end
|
43
49
|
end
|
data/lib/plumb.rb
CHANGED
@@ -10,7 +10,7 @@ module Plumb
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# Register a policy with the given name and block.
|
13
|
-
# Optionally define a method on the
|
13
|
+
# Optionally define a method on the Composable method to call the policy.
|
14
14
|
# Example:
|
15
15
|
# Plumb.policy(:multiply_by, for_type: Integer, helper: true) do |step, factor, &block|
|
16
16
|
# step.transform(Integer) { |number| number * factor }
|
@@ -39,11 +39,11 @@ module Plumb
|
|
39
39
|
|
40
40
|
return self unless helper
|
41
41
|
|
42
|
-
if
|
43
|
-
raise Policies::MethodAlreadyDefinedError, "Method #{name} is already defined on
|
42
|
+
if Composable.instance_methods.include?(name)
|
43
|
+
raise Policies::MethodAlreadyDefinedError, "Method #{name} is already defined on Composable"
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
Composable.define_method(name) do |arg = Undefined, &bl|
|
47
47
|
if arg == Undefined
|
48
48
|
policy(name, &bl)
|
49
49
|
else
|
@@ -53,11 +53,15 @@ module Plumb
|
|
53
53
|
|
54
54
|
self
|
55
55
|
end
|
56
|
+
|
57
|
+
def self.decorate(type, &block)
|
58
|
+
Decorator.call(type, &block)
|
59
|
+
end
|
56
60
|
end
|
57
61
|
|
58
62
|
require 'plumb/result'
|
59
63
|
require 'plumb/type_registry'
|
60
|
-
require 'plumb/
|
64
|
+
require 'plumb/composable'
|
61
65
|
require 'plumb/any_class'
|
62
66
|
require 'plumb/step'
|
63
67
|
require 'plumb/and'
|
@@ -72,6 +76,8 @@ require 'plumb/array_class'
|
|
72
76
|
require 'plumb/stream_class'
|
73
77
|
require 'plumb/hash_class'
|
74
78
|
require 'plumb/interface_class'
|
79
|
+
require 'plumb/attributes'
|
75
80
|
require 'plumb/types'
|
76
81
|
require 'plumb/json_schema_visitor'
|
77
82
|
require 'plumb/schema'
|
83
|
+
require 'plumb/decorator'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plumb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismael Celis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|
@@ -50,17 +50,25 @@ files:
|
|
50
50
|
- LICENSE.txt
|
51
51
|
- README.md
|
52
52
|
- Rakefile
|
53
|
+
- bench/compare_parametric_schema.rb
|
54
|
+
- bench/compare_parametric_struct.rb
|
55
|
+
- bench/parametric_schema.rb
|
56
|
+
- bench/plumb_hash.rb
|
53
57
|
- examples/command_objects.rb
|
54
58
|
- examples/concurrent_downloads.rb
|
55
59
|
- examples/csv_stream.rb
|
56
60
|
- examples/env_config.rb
|
61
|
+
- examples/event_registry.rb
|
57
62
|
- examples/programmers.csv
|
58
63
|
- examples/weekdays.rb
|
59
64
|
- lib/plumb.rb
|
60
65
|
- lib/plumb/and.rb
|
61
66
|
- lib/plumb/any_class.rb
|
62
67
|
- lib/plumb/array_class.rb
|
68
|
+
- lib/plumb/attributes.rb
|
63
69
|
- lib/plumb/build.rb
|
70
|
+
- lib/plumb/composable.rb
|
71
|
+
- lib/plumb/decorator.rb
|
64
72
|
- lib/plumb/deferred.rb
|
65
73
|
- lib/plumb/hash_class.rb
|
66
74
|
- lib/plumb/hash_map.rb
|
@@ -79,7 +87,6 @@ files:
|
|
79
87
|
- lib/plumb/schema.rb
|
80
88
|
- lib/plumb/static_class.rb
|
81
89
|
- lib/plumb/step.rb
|
82
|
-
- lib/plumb/steppable.rb
|
83
90
|
- lib/plumb/stream_class.rb
|
84
91
|
- lib/plumb/tagged_hash.rb
|
85
92
|
- lib/plumb/transform.rb
|
data/lib/plumb/steppable.rb
DELETED
@@ -1,229 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'plumb/metadata_visitor'
|
4
|
-
|
5
|
-
module Plumb
|
6
|
-
class UndefinedClass
|
7
|
-
def inspect
|
8
|
-
%(Undefined)
|
9
|
-
end
|
10
|
-
|
11
|
-
def to_s = inspect
|
12
|
-
def node_name = :undefined
|
13
|
-
def empty? = true
|
14
|
-
end
|
15
|
-
|
16
|
-
TypeError = Class.new(::TypeError)
|
17
|
-
Undefined = UndefinedClass.new.freeze
|
18
|
-
|
19
|
-
BLANK_STRING = ''
|
20
|
-
BLANK_ARRAY = [].freeze
|
21
|
-
BLANK_HASH = {}.freeze
|
22
|
-
BLANK_RESULT = Result.wrap(Undefined)
|
23
|
-
NOOP = ->(result) { result }
|
24
|
-
|
25
|
-
module Callable
|
26
|
-
def metadata
|
27
|
-
MetadataVisitor.call(self)
|
28
|
-
end
|
29
|
-
|
30
|
-
def resolve(value = Undefined)
|
31
|
-
call(Result.wrap(value))
|
32
|
-
end
|
33
|
-
|
34
|
-
def parse(value = Undefined)
|
35
|
-
result = resolve(value)
|
36
|
-
raise TypeError, result.errors if result.invalid?
|
37
|
-
|
38
|
-
result.value
|
39
|
-
end
|
40
|
-
|
41
|
-
def call(result)
|
42
|
-
raise NotImplementedError, "Implement #call(Result) => Result in #{self.class}"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
module Steppable
|
47
|
-
include Callable
|
48
|
-
|
49
|
-
def self.included(base)
|
50
|
-
nname = base.name.split('::').last
|
51
|
-
nname.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
52
|
-
nname.downcase!
|
53
|
-
nname.gsub!(/_class$/, '')
|
54
|
-
nname = nname.to_sym
|
55
|
-
base.define_method(:node_name) { nname }
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.wrap(callable)
|
59
|
-
if callable.is_a?(Steppable)
|
60
|
-
callable
|
61
|
-
elsif callable.is_a?(::Hash)
|
62
|
-
HashClass.new(schema: callable)
|
63
|
-
elsif callable.respond_to?(:call)
|
64
|
-
Step.new(callable)
|
65
|
-
else
|
66
|
-
MatchClass.new(callable)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
attr_reader :name
|
71
|
-
|
72
|
-
class Name
|
73
|
-
def initialize(name)
|
74
|
-
@name = name
|
75
|
-
end
|
76
|
-
|
77
|
-
def to_s = @name
|
78
|
-
|
79
|
-
def set(n)
|
80
|
-
@name = n
|
81
|
-
self
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def freeze
|
86
|
-
return self if frozen?
|
87
|
-
|
88
|
-
@name = Name.new(_inspect)
|
89
|
-
super
|
90
|
-
end
|
91
|
-
|
92
|
-
private def _inspect = self.class.name
|
93
|
-
|
94
|
-
def inspect = name.to_s
|
95
|
-
|
96
|
-
def node_name = self.class.name.split('::').last.to_sym
|
97
|
-
|
98
|
-
def defer(definition = nil, &block)
|
99
|
-
Deferred.new(definition || block)
|
100
|
-
end
|
101
|
-
|
102
|
-
def >>(other)
|
103
|
-
And.new(self, Steppable.wrap(other))
|
104
|
-
end
|
105
|
-
|
106
|
-
def |(other)
|
107
|
-
Or.new(self, Steppable.wrap(other))
|
108
|
-
end
|
109
|
-
|
110
|
-
def transform(target_type, callable = nil, &block)
|
111
|
-
self >> Transform.new(target_type, callable || block)
|
112
|
-
end
|
113
|
-
|
114
|
-
def check(errors = 'did not pass the check', &block)
|
115
|
-
self >> MatchClass.new(block, error: errors, label: errors)
|
116
|
-
end
|
117
|
-
|
118
|
-
def meta(data = {})
|
119
|
-
self >> Metadata.new(data)
|
120
|
-
end
|
121
|
-
|
122
|
-
def not(other = self)
|
123
|
-
Not.new(other)
|
124
|
-
end
|
125
|
-
|
126
|
-
def invalid(errors: nil)
|
127
|
-
Not.new(self, errors:)
|
128
|
-
end
|
129
|
-
|
130
|
-
def value(val)
|
131
|
-
self >> ValueClass.new(val)
|
132
|
-
end
|
133
|
-
|
134
|
-
def match(*args)
|
135
|
-
self >> MatchClass.new(*args)
|
136
|
-
end
|
137
|
-
|
138
|
-
def [](val) = match(val)
|
139
|
-
|
140
|
-
class Node
|
141
|
-
include Steppable
|
142
|
-
|
143
|
-
attr_reader :node_name, :type, :attributes
|
144
|
-
|
145
|
-
def initialize(node_name, type, attributes = BLANK_HASH)
|
146
|
-
@node_name = node_name
|
147
|
-
@type = type
|
148
|
-
@attributes = attributes
|
149
|
-
freeze
|
150
|
-
end
|
151
|
-
|
152
|
-
def call(result) = type.call(result)
|
153
|
-
end
|
154
|
-
|
155
|
-
def as_node(node_name, metadata = BLANK_HASH)
|
156
|
-
Node.new(node_name, self, metadata)
|
157
|
-
end
|
158
|
-
|
159
|
-
# Register a policy for this step.
|
160
|
-
# Mode 1.a: #policy(:name, arg) a single policy with an argument
|
161
|
-
# Mode 1.b: #policy(:name) a single policy without an argument
|
162
|
-
# Mode 2: #policy(p1: value, p2: value) multiple policies with arguments
|
163
|
-
# The latter mode will be expanded to multiple #policy calls.
|
164
|
-
# @return [Step]
|
165
|
-
def policy(*args, &blk)
|
166
|
-
case args
|
167
|
-
in [::Symbol => name, *rest] # #policy(:name, arg)
|
168
|
-
types = Array(metadata[:type]).uniq
|
169
|
-
|
170
|
-
bargs = [self]
|
171
|
-
bargs << rest.first if rest.any?
|
172
|
-
block = Plumb.policies.get(types, name)
|
173
|
-
pol = block.call(*bargs, &blk)
|
174
|
-
|
175
|
-
Policy.new(name, rest.first, pol)
|
176
|
-
in [::Hash => opts] # #policy(p1: value, p2: value)
|
177
|
-
opts.reduce(self) { |step, (name, value)| step.policy(name, value) }
|
178
|
-
else
|
179
|
-
raise ArgumentError, "expected a symbol or hash, got #{args.inspect}"
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def ===(other)
|
184
|
-
case other
|
185
|
-
when Steppable
|
186
|
-
other == self
|
187
|
-
else
|
188
|
-
resolve(other).valid?
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
def build(cns, factory_method = :new, &block)
|
193
|
-
self >> Build.new(cns, factory_method:, &block)
|
194
|
-
end
|
195
|
-
|
196
|
-
def pipeline(&block)
|
197
|
-
Pipeline.new(self, &block)
|
198
|
-
end
|
199
|
-
|
200
|
-
def to_s
|
201
|
-
inspect
|
202
|
-
end
|
203
|
-
|
204
|
-
# Build a step that will invoke one or more methods on the value.
|
205
|
-
# Ex 1: Types::String.invoke(:downcase)
|
206
|
-
# Ex 2: Types::Array.invoke(:[], 1)
|
207
|
-
# Ex 3 chain of methods: Types::String.invoke([:downcase, :to_sym])
|
208
|
-
# @return [Step]
|
209
|
-
def invoke(*args, &block)
|
210
|
-
case args
|
211
|
-
in [::Symbol => method_name, *rest]
|
212
|
-
self >> Step.new(
|
213
|
-
->(result) { result.valid(result.value.public_send(method_name, *rest, &block)) },
|
214
|
-
[method_name.inspect, rest.inspect].join(' ')
|
215
|
-
)
|
216
|
-
in [Array => methods] if methods.all? { |m| m.is_a?(Symbol) }
|
217
|
-
methods.reduce(self) { |step, method| step.invoke(method) }
|
218
|
-
else
|
219
|
-
raise ArgumentError, "expected a symbol or array of symbols, got #{args.inspect}"
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
require 'plumb/deferred'
|
226
|
-
require 'plumb/transform'
|
227
|
-
require 'plumb/policy'
|
228
|
-
require 'plumb/build'
|
229
|
-
require 'plumb/metadata'
|