plumb 0.0.2 → 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 +636 -129
- data/examples/concurrent_downloads.rb +3 -3
- data/examples/env_config.rb +122 -0
- data/examples/event_registry.rb +120 -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 +262 -0
- data/lib/plumb/build.rb +4 -3
- data/lib/plumb/{steppable.rb → composable.rb} +85 -67
- data/lib/plumb/decorator.rb +57 -0
- data/lib/plumb/deferred.rb +1 -1
- data/lib/plumb/hash_class.rb +20 -11
- data/lib/plumb/hash_map.rb +8 -6
- data/lib/plumb/interface_class.rb +6 -2
- data/lib/plumb/json_schema_visitor.rb +97 -36
- data/lib/plumb/match_class.rb +7 -7
- data/lib/plumb/metadata.rb +5 -1
- data/lib/plumb/metadata_visitor.rb +18 -38
- data/lib/plumb/not.rb +4 -3
- data/lib/plumb/or.rb +10 -4
- data/lib/plumb/pipeline.rb +6 -5
- data/lib/plumb/policies.rb +81 -0
- data/lib/plumb/policy.rb +38 -0
- data/lib/plumb/schema.rb +13 -12
- 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 +119 -23
- data/lib/plumb/value_class.rb +4 -3
- data/lib/plumb/version.rb +1 -1
- data/lib/plumb/visitor_handlers.rb +12 -1
- data/lib/plumb.rb +59 -2
- metadata +12 -7
- data/lib/plumb/rules.rb +0 -102
data/lib/plumb/schema.rb
CHANGED
@@ -5,13 +5,13 @@ require 'plumb/json_schema_visitor'
|
|
5
5
|
|
6
6
|
module Plumb
|
7
7
|
class Schema
|
8
|
-
include
|
8
|
+
include Composable
|
9
9
|
|
10
10
|
def self.wrap(sch = nil, &block)
|
11
11
|
raise ArgumentError, 'expected a block or a schema' if sch.nil? && !block_given?
|
12
12
|
|
13
13
|
if sch
|
14
|
-
raise ArgumentError, 'expected a
|
14
|
+
raise ArgumentError, 'expected a Composable' unless sch.is_a?(Composable)
|
15
15
|
|
16
16
|
return sch
|
17
17
|
end
|
@@ -49,8 +49,8 @@ module Plumb
|
|
49
49
|
self
|
50
50
|
end
|
51
51
|
|
52
|
-
def
|
53
|
-
|
52
|
+
def to_json_schema
|
53
|
+
_hash.to_json_schema(root: true)
|
54
54
|
end
|
55
55
|
|
56
56
|
def call(result)
|
@@ -120,7 +120,7 @@ module Plumb
|
|
120
120
|
block_given? ? ArrayClass.new(element_type: Schema.new(&block)) : type
|
121
121
|
when nil
|
122
122
|
block_given? ? Schema.new(&block) : Types::Any
|
123
|
-
when
|
123
|
+
when Composable
|
124
124
|
type
|
125
125
|
when Class
|
126
126
|
if type == Array && block_given?
|
@@ -140,13 +140,14 @@ module Plumb
|
|
140
140
|
self
|
141
141
|
end
|
142
142
|
|
143
|
-
def
|
144
|
-
|
145
|
-
|
143
|
+
def metadata(data = Undefined)
|
144
|
+
if data == Undefined
|
145
|
+
@_type.metadata
|
146
|
+
else
|
147
|
+
@_type = @_type.metadata(data)
|
148
|
+
end
|
146
149
|
end
|
147
150
|
|
148
|
-
def metadata = @_type.metadata
|
149
|
-
|
150
151
|
def options(opts)
|
151
152
|
@_type = @_type.options(opts)
|
152
153
|
self
|
@@ -172,8 +173,8 @@ module Plumb
|
|
172
173
|
self
|
173
174
|
end
|
174
175
|
|
175
|
-
def
|
176
|
-
@_type = @_type.
|
176
|
+
def policy(...)
|
177
|
+
@_type = @_type.policy(...)
|
177
178
|
self
|
178
179
|
end
|
179
180
|
|
data/lib/plumb/static_class.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'plumb/
|
3
|
+
require 'plumb/composable'
|
4
4
|
|
5
5
|
module Plumb
|
6
6
|
class StaticClass
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :children
|
10
10
|
|
11
11
|
def initialize(value = Undefined)
|
12
12
|
raise ArgumentError, 'value must be frozen' unless value.frozen?
|
13
13
|
|
14
14
|
@value = value
|
15
|
+
@children = [value].freeze
|
15
16
|
freeze
|
16
17
|
end
|
17
18
|
|
data/lib/plumb/step.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 Step
|
7
|
-
include
|
7
|
+
include Composable
|
8
8
|
|
9
|
-
attr_reader :_metadata
|
9
|
+
attr_reader :_metadata, :children
|
10
10
|
|
11
11
|
def initialize(callable = nil, inspect = nil, &block)
|
12
12
|
@_metadata = callable.respond_to?(:metadata) ? callable.metadata : BLANK_HASH
|
13
13
|
@callable = callable || block
|
14
|
+
@children = [@callable].freeze
|
14
15
|
@inspect = inspect || @callable.inspect
|
15
16
|
freeze
|
16
17
|
end
|
data/lib/plumb/stream_class.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'thread'
|
4
|
-
require 'plumb/
|
4
|
+
require 'plumb/composable'
|
5
5
|
|
6
6
|
module Plumb
|
7
7
|
# A stream that validates each element.
|
@@ -15,18 +15,19 @@ module Plumb
|
|
15
15
|
# result.value # => ['name', 10]
|
16
16
|
# end
|
17
17
|
class StreamClass
|
18
|
-
include
|
18
|
+
include Composable
|
19
19
|
|
20
|
-
attr_reader :
|
20
|
+
attr_reader :children
|
21
21
|
|
22
|
-
# @option element_type [
|
22
|
+
# @option element_type [Composable] the type of the elements in the stream
|
23
23
|
def initialize(element_type: Types::Any)
|
24
|
-
@element_type =
|
24
|
+
@element_type = Composable.wrap(element_type)
|
25
|
+
@children = [@element_type].freeze
|
25
26
|
freeze
|
26
27
|
end
|
27
28
|
|
28
29
|
# return a new Stream definition.
|
29
|
-
# @param element_type [
|
30
|
+
# @param element_type [Composable] the type of the elements in the stream
|
30
31
|
def [](element_type)
|
31
32
|
self.class.new(element_type:)
|
32
33
|
end
|
@@ -39,7 +40,7 @@ module Plumb
|
|
39
40
|
|
40
41
|
enum = Enumerator.new do |y|
|
41
42
|
result.value.each do |e|
|
42
|
-
y << element_type.resolve(e)
|
43
|
+
y << @element_type.resolve(e)
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
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
24
|
raise TypeError, "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
@@ -3,25 +3,127 @@
|
|
3
3
|
require 'bigdecimal'
|
4
4
|
|
5
5
|
module Plumb
|
6
|
-
|
7
|
-
|
6
|
+
# Define core policies
|
7
|
+
# Allowed options for an array type.
|
8
|
+
# It validates that each element is in the options array.
|
9
|
+
# Usage:
|
10
|
+
# type = Types::Array.options(['a', 'b'])
|
11
|
+
policy :options, helper: true, for_type: ::Array do |type, opts|
|
12
|
+
type.check("must be included in #{opts.inspect}") do |v|
|
13
|
+
v.all? { |val| opts.include?(val) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Generic options policy for all other types.
|
18
|
+
# Usage:
|
19
|
+
# type = Types::String.options(['a', 'b'])
|
20
|
+
policy :options do |type, opts|
|
21
|
+
type.check("must be included in #{opts.inspect}") do |v|
|
22
|
+
opts.include?(v)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validate that array elements are NOT in the options array.
|
27
|
+
# Usage:
|
28
|
+
# type = Types::Array.policy(excluded_from: ['a', 'b'])
|
29
|
+
policy :excluded_from, for_type: ::Array do |type, opts|
|
30
|
+
type.check("must not be included in #{opts.inspect}") do |v|
|
31
|
+
v.none? { |val| opts.include?(val) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Usage:
|
36
|
+
# type = Types::String.policy(excluded_from: ['a', 'b'])
|
37
|
+
policy :excluded_from do |type, opts|
|
38
|
+
type.check("must not be included in #{opts.inspect}") do |v|
|
39
|
+
!opts.include?(v)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Validate #size against a number or any object that responds to #===.
|
44
|
+
# This works with any type that repsonds to #size.
|
45
|
+
# Usage:
|
46
|
+
# type = Types::String.policy(size: 10)
|
47
|
+
# type = Types::Integer.policy(size: 1..10)
|
48
|
+
# type = Types::Array.policy(size: 1..)
|
49
|
+
# type = Types::Any[Set].policy(size: 1..)
|
50
|
+
policy :size, for_type: :size do |type, size|
|
51
|
+
type.check("must be of size #{size}") { |v| size === v.size }
|
8
52
|
end
|
9
|
-
|
10
|
-
|
53
|
+
|
54
|
+
# Validate that an object is not #empty? nor #nil?
|
55
|
+
# Usage:
|
56
|
+
# Types::String.present
|
57
|
+
# Types::Array.present
|
58
|
+
policy :present, helper: true do |type, *_args|
|
59
|
+
type.check('must be present') do |v|
|
60
|
+
if v.respond_to?(:empty?)
|
61
|
+
!v.empty?
|
62
|
+
else
|
63
|
+
!v.nil?
|
64
|
+
end
|
65
|
+
end
|
11
66
|
end
|
12
|
-
|
13
|
-
|
67
|
+
|
68
|
+
# Allow nil values for a type.
|
69
|
+
# Usage:
|
70
|
+
# nullable_int = Types::Integer.nullable
|
71
|
+
# nullable_int.parse(nil) # => nil
|
72
|
+
# nullable_int.parse(10) # => 10
|
73
|
+
# nullable_int.parse('nope') # => error: not an Integer
|
74
|
+
policy :nullable, helper: true do |type, *_args|
|
75
|
+
Types::Nil | type
|
14
76
|
end
|
15
|
-
|
16
|
-
|
77
|
+
|
78
|
+
# Validate that a value responds to a method
|
79
|
+
# Usage:
|
80
|
+
# type = Types::Any.policy(respond_to: :upcase)
|
81
|
+
# type = Types::Any.policy(respond_to: [:upcase, :strip])
|
82
|
+
policy :respond_to do |type, method_names|
|
83
|
+
type.check("must respond to #{method_names.inspect}") do |value|
|
84
|
+
Array(method_names).all? { |m| value.respond_to?(m) }
|
85
|
+
end
|
17
86
|
end
|
18
|
-
|
19
|
-
|
87
|
+
|
88
|
+
# Return a default value if the input value is Undefined (ie key not present in a Hash).
|
89
|
+
# Usage:
|
90
|
+
# type = Types::String.default('default')
|
91
|
+
# type.parse(Undefined) # => 'default'
|
92
|
+
# type.parse('yes') # => 'yes'
|
93
|
+
#
|
94
|
+
# Works with a block too:
|
95
|
+
# date = Type::Any[Date].default { Date.today }
|
96
|
+
#
|
97
|
+
policy :default, helper: true do |type, value = Undefined, &block|
|
98
|
+
val_type = if value == Undefined
|
99
|
+
Step.new(->(result) { result.valid(block.call) }, 'default proc')
|
100
|
+
else
|
101
|
+
Types::Static[value]
|
102
|
+
end
|
103
|
+
|
104
|
+
(Types::Undefined >> val_type) | type
|
20
105
|
end
|
21
|
-
|
22
|
-
|
106
|
+
|
107
|
+
# Split a string into an array. Default separator is /\s*,\s*/
|
108
|
+
# Usage:
|
109
|
+
# type = Types::String.split
|
110
|
+
# type.parse('a,b,c') # => ['a', 'b', 'c']
|
111
|
+
#
|
112
|
+
# Custom separator:
|
113
|
+
# type = Types::String.split(';')
|
114
|
+
module SplitPolicy
|
115
|
+
DEFAULT_SEPARATOR = /\s*,\s*/
|
116
|
+
|
117
|
+
def self.call(type, separator = DEFAULT_SEPARATOR)
|
118
|
+
type.invoke(:split, separator) >> Types::Array[String]
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.for_type = ::String
|
122
|
+
def self.helper = false
|
23
123
|
end
|
24
124
|
|
125
|
+
policy :split, SplitPolicy
|
126
|
+
|
25
127
|
module Types
|
26
128
|
extend TypeRegistry
|
27
129
|
|
@@ -43,17 +145,11 @@ module Plumb
|
|
43
145
|
Tuple = TupleClass.new
|
44
146
|
Hash = HashClass.new
|
45
147
|
Interface = InterfaceClass.new
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
| Hash.value(BLANK_HASH) \
|
52
|
-
| Array.value(BLANK_ARRAY)
|
53
|
-
)
|
54
|
-
|
55
|
-
Present = Blank.invalid(errors: 'must be present')
|
56
|
-
Split = String.transform(::String) { |v| v.split(/\s*,\s*/) }
|
148
|
+
|
149
|
+
class Data
|
150
|
+
extend Composable
|
151
|
+
include Plumb::Attributes
|
152
|
+
end
|
57
153
|
|
58
154
|
module Lax
|
59
155
|
NUMBER_EXPR = /^\d{1,3}(?:,\d{3})*(?:\.\d+)?$/
|
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
@@ -23,7 +23,12 @@ module Plumb
|
|
23
23
|
else
|
24
24
|
:"#{(node.is_a?(::Class) ? node : node.class)}_class"
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
|
+
visit_name(method_name, node, props)
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_name(method_name, node, props = BLANK_HASH)
|
31
|
+
method_name = :"visit_#{method_name}"
|
27
32
|
if respond_to?(method_name)
|
28
33
|
send(method_name, node, props)
|
29
34
|
else
|
@@ -34,5 +39,11 @@ module Plumb
|
|
34
39
|
def on_missing_handler(node, _props, method_name)
|
35
40
|
raise "No handler for #{node.inspect} with :#{method_name}"
|
36
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
|
37
48
|
end
|
38
49
|
end
|
data/lib/plumb.rb
CHANGED
@@ -1,16 +1,71 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'plumb/policies'
|
4
|
+
|
3
5
|
module Plumb
|
6
|
+
@policies = Policies.new
|
7
|
+
|
8
|
+
def self.policies
|
9
|
+
@policies
|
10
|
+
end
|
11
|
+
|
12
|
+
# Register a policy with the given name and block.
|
13
|
+
# Optionally define a method on the Composable method to call the policy.
|
14
|
+
# Example:
|
15
|
+
# Plumb.policy(:multiply_by, for_type: Integer, helper: true) do |step, factor, &block|
|
16
|
+
# step.transform(Integer) { |number| number * factor }
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# type = Types::Integer.multiply_by(2)
|
20
|
+
# type.parse(10) # => 20
|
21
|
+
#
|
22
|
+
# @param name [Symbol] the name of the policy
|
23
|
+
# @param opts [Hash] options for the policy
|
24
|
+
# @yield [Step, Object, &block] the step (type), policy argument, and policy block, if any.
|
25
|
+
def self.policy(name, opts = {}, &block)
|
26
|
+
name = name.to_sym
|
27
|
+
if opts.is_a?(Hash) && block_given?
|
28
|
+
for_type = opts[:for_type] || Object
|
29
|
+
helper = opts[:helper] || false
|
30
|
+
elsif opts.respond_to?(:call) && opts.respond_to?(:for_type) && opts.respond_to?(:helper)
|
31
|
+
for_type = opts.for_type
|
32
|
+
helper = opts.helper
|
33
|
+
block = opts.method(:call)
|
34
|
+
else
|
35
|
+
raise ArgumentError, 'Expected a block or a hash with :for_type and :helper keys'
|
36
|
+
end
|
37
|
+
|
38
|
+
policies.register(for_type, name, block)
|
39
|
+
|
40
|
+
return self unless helper
|
41
|
+
|
42
|
+
if Composable.instance_methods.include?(name)
|
43
|
+
raise Policies::MethodAlreadyDefinedError, "Method #{name} is already defined on Composable"
|
44
|
+
end
|
45
|
+
|
46
|
+
Composable.define_method(name) do |arg = Undefined, &bl|
|
47
|
+
if arg == Undefined
|
48
|
+
policy(name, &bl)
|
49
|
+
else
|
50
|
+
policy(name, arg, &bl)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.decorate(type, &block)
|
58
|
+
Decorator.call(type, &block)
|
59
|
+
end
|
4
60
|
end
|
5
61
|
|
6
62
|
require 'plumb/result'
|
7
63
|
require 'plumb/type_registry'
|
8
|
-
require 'plumb/
|
64
|
+
require 'plumb/composable'
|
9
65
|
require 'plumb/any_class'
|
10
66
|
require 'plumb/step'
|
11
67
|
require 'plumb/and'
|
12
68
|
require 'plumb/pipeline'
|
13
|
-
require 'plumb/rules'
|
14
69
|
require 'plumb/static_class'
|
15
70
|
require 'plumb/value_class'
|
16
71
|
require 'plumb/match_class'
|
@@ -21,6 +76,8 @@ require 'plumb/array_class'
|
|
21
76
|
require 'plumb/stream_class'
|
22
77
|
require 'plumb/hash_class'
|
23
78
|
require 'plumb/interface_class'
|
79
|
+
require 'plumb/attributes'
|
24
80
|
require 'plumb/types'
|
25
81
|
require 'plumb/json_schema_visitor'
|
26
82
|
require 'plumb/schema'
|
83
|
+
require 'plumb/decorator'
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
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.4
|
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-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bigdecimal
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: concurrent-ruby
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -53,13 +53,18 @@ files:
|
|
53
53
|
- examples/command_objects.rb
|
54
54
|
- examples/concurrent_downloads.rb
|
55
55
|
- examples/csv_stream.rb
|
56
|
+
- examples/env_config.rb
|
57
|
+
- examples/event_registry.rb
|
56
58
|
- examples/programmers.csv
|
57
59
|
- examples/weekdays.rb
|
58
60
|
- lib/plumb.rb
|
59
61
|
- lib/plumb/and.rb
|
60
62
|
- lib/plumb/any_class.rb
|
61
63
|
- lib/plumb/array_class.rb
|
64
|
+
- lib/plumb/attributes.rb
|
62
65
|
- lib/plumb/build.rb
|
66
|
+
- lib/plumb/composable.rb
|
67
|
+
- lib/plumb/decorator.rb
|
63
68
|
- lib/plumb/deferred.rb
|
64
69
|
- lib/plumb/hash_class.rb
|
65
70
|
- lib/plumb/hash_map.rb
|
@@ -72,12 +77,12 @@ files:
|
|
72
77
|
- lib/plumb/not.rb
|
73
78
|
- lib/plumb/or.rb
|
74
79
|
- lib/plumb/pipeline.rb
|
80
|
+
- lib/plumb/policies.rb
|
81
|
+
- lib/plumb/policy.rb
|
75
82
|
- lib/plumb/result.rb
|
76
|
-
- lib/plumb/rules.rb
|
77
83
|
- lib/plumb/schema.rb
|
78
84
|
- lib/plumb/static_class.rb
|
79
85
|
- lib/plumb/step.rb
|
80
|
-
- lib/plumb/steppable.rb
|
81
86
|
- lib/plumb/stream_class.rb
|
82
87
|
- lib/plumb/tagged_hash.rb
|
83
88
|
- lib/plumb/transform.rb
|
@@ -87,7 +92,7 @@ files:
|
|
87
92
|
- lib/plumb/value_class.rb
|
88
93
|
- lib/plumb/version.rb
|
89
94
|
- lib/plumb/visitor_handlers.rb
|
90
|
-
homepage:
|
95
|
+
homepage: https://github.com/ismasan/plumb
|
91
96
|
licenses:
|
92
97
|
- MIT
|
93
98
|
metadata: {}
|