plumb 0.0.18 → 0.2.0.beta.2

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +887 -64
  3. data/bench/compare_dry_schema.rb +79 -0
  4. data/bench/compare_dry_types.rb +37 -0
  5. data/bench/compare_parametric_schema.rb +2 -80
  6. data/bench/dry_schema_hash.rb +103 -0
  7. data/bench/dry_types_hash.rb +125 -0
  8. data/bench/json_schema_profile.rb +107 -0
  9. data/bench/plumb_hash.rb +17 -11
  10. data/bench/results_allocations.rb +137 -0
  11. data/bench/sample_data.rb +78 -0
  12. data/examples/command_objects.rb +1 -1
  13. data/examples/concurrent_downloads.rb +16 -9
  14. data/examples/event_registry.rb +6 -1
  15. data/examples/weekdays.rb +1 -1
  16. data/lib/plumb/and.rb +63 -6
  17. data/lib/plumb/any_class.rb +12 -2
  18. data/lib/plumb/array_class.rb +133 -25
  19. data/lib/plumb/attribute_value_match.rb +41 -1
  20. data/lib/plumb/attributes.rb +59 -19
  21. data/lib/plumb/codec.rb +886 -0
  22. data/lib/plumb/composable.rb +451 -39
  23. data/lib/plumb/conjunction.rb +50 -0
  24. data/lib/plumb/constraint.rb +234 -0
  25. data/lib/plumb/covariant_fusion.rb +46 -0
  26. data/lib/plumb/decorator.rb +12 -22
  27. data/lib/plumb/deferred.rb +13 -5
  28. data/lib/plumb/disjunction.rb +112 -0
  29. data/lib/plumb/encoder.rb +207 -0
  30. data/lib/plumb/function.rb +347 -0
  31. data/lib/plumb/hash_class.rb +339 -32
  32. data/lib/plumb/hash_map.rb +58 -14
  33. data/lib/plumb/implementation.rb +247 -0
  34. data/lib/plumb/interface_class.rb +21 -2
  35. data/lib/plumb/intersection.rb +47 -0
  36. data/lib/plumb/json_schema_visitor.rb +255 -36
  37. data/lib/plumb/key.rb +63 -13
  38. data/lib/plumb/mermaid_visitor.rb +129 -0
  39. data/lib/plumb/metadata.rb +10 -1
  40. data/lib/plumb/metadata_visitor.rb +36 -34
  41. data/lib/plumb/never_class.rb +38 -0
  42. data/lib/plumb/node_mapper.rb +97 -0
  43. data/lib/plumb/not.rb +34 -2
  44. data/lib/plumb/optimizer.rb +444 -0
  45. data/lib/plumb/or.rb +26 -29
  46. data/lib/plumb/pipeline.rb +99 -11
  47. data/lib/plumb/policy.rb +17 -4
  48. data/lib/plumb/range_class.rb +46 -0
  49. data/lib/plumb/relation.rb +57 -0
  50. data/lib/plumb/result.rb +55 -23
  51. data/lib/plumb/semantic_matcher.rb +393 -0
  52. data/lib/plumb/static_class.rb +20 -1
  53. data/lib/plumb/stream_class.rb +28 -6
  54. data/lib/plumb/subtyping.rb +461 -0
  55. data/lib/plumb/tagged_hash.rb +45 -4
  56. data/lib/plumb/tuple_class.rb +21 -4
  57. data/lib/plumb/type_cache.rb +41 -0
  58. data/lib/plumb/type_registry.rb +71 -0
  59. data/lib/plumb/typed_step.rb +67 -0
  60. data/lib/plumb/types.rb +44 -43
  61. data/lib/plumb/union.rb +30 -0
  62. data/lib/plumb/value_class.rb +20 -1
  63. data/lib/plumb/version.rb +1 -1
  64. data/lib/plumb/visitor_handlers.rb +20 -4
  65. data/lib/plumb.rb +90 -3
  66. metadata +30 -8
  67. data/lib/plumb/build.rb +0 -22
  68. data/lib/plumb/match_class.rb +0 -42
  69. data/lib/plumb/schema.rb +0 -195
  70. data/lib/plumb/step.rb +0 -27
  71. data/lib/plumb/transform.rb +0 -26
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plumb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.18
4
+ version: 0.2.0.beta.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismael Celis
@@ -49,10 +49,17 @@ files:
49
49
  - LICENSE.txt
50
50
  - README.md
51
51
  - Rakefile
52
+ - bench/compare_dry_schema.rb
53
+ - bench/compare_dry_types.rb
52
54
  - bench/compare_parametric_schema.rb
53
55
  - bench/compare_parametric_struct.rb
56
+ - bench/dry_schema_hash.rb
57
+ - bench/dry_types_hash.rb
58
+ - bench/json_schema_profile.rb
54
59
  - bench/parametric_schema.rb
55
60
  - bench/plumb_hash.rb
61
+ - bench/results_allocations.rb
62
+ - bench/sample_data.rb
56
63
  - examples/command_objects.rb
57
64
  - examples/concurrent_downloads.rb
58
65
  - examples/csv_stream.rb
@@ -66,33 +73,48 @@ files:
66
73
  - lib/plumb/array_class.rb
67
74
  - lib/plumb/attribute_value_match.rb
68
75
  - lib/plumb/attributes.rb
69
- - lib/plumb/build.rb
76
+ - lib/plumb/codec.rb
70
77
  - lib/plumb/composable.rb
78
+ - lib/plumb/conjunction.rb
79
+ - lib/plumb/constraint.rb
80
+ - lib/plumb/covariant_fusion.rb
71
81
  - lib/plumb/decorator.rb
72
82
  - lib/plumb/deferred.rb
83
+ - lib/plumb/disjunction.rb
84
+ - lib/plumb/encoder.rb
85
+ - lib/plumb/function.rb
73
86
  - lib/plumb/hash_class.rb
74
87
  - lib/plumb/hash_map.rb
88
+ - lib/plumb/implementation.rb
75
89
  - lib/plumb/interface_class.rb
90
+ - lib/plumb/intersection.rb
76
91
  - lib/plumb/json_schema_visitor.rb
77
92
  - lib/plumb/key.rb
78
- - lib/plumb/match_class.rb
93
+ - lib/plumb/mermaid_visitor.rb
79
94
  - lib/plumb/metadata.rb
80
95
  - lib/plumb/metadata_visitor.rb
96
+ - lib/plumb/never_class.rb
97
+ - lib/plumb/node_mapper.rb
81
98
  - lib/plumb/not.rb
99
+ - lib/plumb/optimizer.rb
82
100
  - lib/plumb/or.rb
83
101
  - lib/plumb/pipeline.rb
84
102
  - lib/plumb/policies.rb
85
103
  - lib/plumb/policy.rb
104
+ - lib/plumb/range_class.rb
105
+ - lib/plumb/relation.rb
86
106
  - lib/plumb/result.rb
87
- - lib/plumb/schema.rb
107
+ - lib/plumb/semantic_matcher.rb
88
108
  - lib/plumb/static_class.rb
89
- - lib/plumb/step.rb
90
109
  - lib/plumb/stream_class.rb
110
+ - lib/plumb/subtyping.rb
91
111
  - lib/plumb/tagged_hash.rb
92
- - lib/plumb/transform.rb
93
112
  - lib/plumb/tuple_class.rb
113
+ - lib/plumb/type_cache.rb
94
114
  - lib/plumb/type_registry.rb
115
+ - lib/plumb/typed_step.rb
95
116
  - lib/plumb/types.rb
117
+ - lib/plumb/union.rb
96
118
  - lib/plumb/value_class.rb
97
119
  - lib/plumb/version.rb
98
120
  - lib/plumb/visitor_handlers.rb
@@ -108,14 +130,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
130
  requirements:
109
131
  - - ">="
110
132
  - !ruby/object:Gem::Version
111
- version: 3.0.0
133
+ version: 3.3.0
112
134
  required_rubygems_version: !ruby/object:Gem::Requirement
113
135
  requirements:
114
136
  - - ">="
115
137
  - !ruby/object:Gem::Version
116
138
  version: '0'
117
139
  requirements: []
118
- rubygems_version: 4.0.8
140
+ rubygems_version: 4.0.10
119
141
  specification_version: 4
120
142
  summary: Data validation and transformation library.
121
143
  test_files: []
data/lib/plumb/build.rb DELETED
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'plumb/composable'
4
-
5
- module Plumb
6
- class Build
7
- include Composable
8
-
9
- attr_reader :children
10
-
11
- def initialize(type, factory_method: :new, &block)
12
- @type = type
13
- @block = block || ->(value) { type.send(factory_method, value) }
14
- @children = [type].freeze
15
- freeze
16
- end
17
-
18
- def call(result) = result.valid(@block.call(result.value))
19
-
20
- private def _inspect = "Build[#{@type.inspect}]"
21
- end
22
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'plumb/composable'
4
-
5
- module Plumb
6
- class MatchClass
7
- include Composable
8
-
9
- attr_reader :children
10
-
11
- def initialize(matcher = Undefined, error: nil, label: nil)
12
- raise ParseError 'matcher must respond to #===' unless matcher.respond_to?(:===)
13
-
14
- @matcher = matcher
15
- @error = error.nil? ? build_error(matcher) : (error % matcher)
16
- @label = matcher.is_a?(Class) ? matcher.inspect : "Match(#{label || @matcher.inspect})"
17
- @children = [matcher].freeze
18
- freeze
19
- end
20
-
21
- def call(result)
22
- @matcher === result.value ? result : result.invalid(errors: @error)
23
- end
24
-
25
- private
26
-
27
- def _inspect = @label
28
-
29
- def build_error(matcher)
30
- case matcher
31
- when Class # A class primitive, ex. String, Integer, etc.
32
- "Must be a #{matcher}"
33
- when ::String, ::Symbol, ::Numeric, ::TrueClass, ::FalseClass, ::NilClass, ::Array, ::Hash
34
- "Must be equal to #{matcher}"
35
- when ::Range
36
- "Must be within #{matcher}"
37
- else
38
- "Must match #{matcher.inspect}"
39
- end
40
- end
41
- end
42
- end
data/lib/plumb/schema.rb DELETED
@@ -1,195 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'forwardable'
4
- require 'plumb/json_schema_visitor'
5
-
6
- module Plumb
7
- class Schema
8
- include Composable
9
-
10
- def self.wrap(sch = nil, &block)
11
- raise ArgumentError, 'expected a block or a schema' if sch.nil? && !block_given?
12
-
13
- if sch
14
- raise ArgumentError, 'expected a Composable' unless sch.is_a?(Composable)
15
-
16
- return sch
17
- end
18
-
19
- new(&block)
20
- end
21
-
22
- attr_reader :fields
23
-
24
- def initialize(hash = Types::Hash, &block)
25
- @pipeline = Types::Any
26
- @before = Types::Any
27
- @after = Types::Any
28
- @_hash = hash
29
- @fields = @_hash._schema.each.with_object(SymbolAccessHash.new({})) do |(k, v), memo|
30
- memo[k] = Field.new(k, v)
31
- end
32
-
33
- setup(&block) if block_given?
34
-
35
- finish
36
- end
37
-
38
- def inspect
39
- "#{self.class}#{fields.keys.inspect}"
40
- end
41
-
42
- def before(callable = nil, &block)
43
- @before >>= callable || block
44
- self
45
- end
46
-
47
- def after(callable = nil, &block)
48
- @after >>= callable || block
49
- self
50
- end
51
-
52
- def to_json_schema
53
- _hash.to_json_schema(root: true)
54
- end
55
-
56
- def call(result)
57
- @pipeline.call(result)
58
- end
59
-
60
- private def setup(&block)
61
- case block.arity
62
- when 1
63
- yield self
64
- when 0
65
- instance_eval(&block)
66
- else
67
- raise ::ArgumentError, "#{self.class} expects a block with 0 or 1 argument, but got #{block.arity}"
68
- end
69
- @_hash = Types::Hash.schema(@fields.transform_values(&:_type))
70
- self
71
- end
72
-
73
- private def finish
74
- @pipeline = @before.freeze >> @_hash.freeze >> @after.freeze
75
- freeze
76
- end
77
-
78
- def field(key, type = nil, &block)
79
- key = Key.new(key.to_sym)
80
- @fields[key] = Field.new(key, type, &block)
81
- end
82
-
83
- def field?(key, type = nil, &block)
84
- key = Key.new(key.to_sym, optional: true)
85
- @fields[key] = Field.new(key, type, &block)
86
- end
87
-
88
- def +(other)
89
- self.class.new(_hash + other._hash)
90
- end
91
-
92
- def &(other)
93
- self.class.new(_hash & other._hash)
94
- end
95
-
96
- def merge(other = nil, &block)
97
- other = self.class.wrap(other, &block)
98
- self + other
99
- end
100
-
101
- protected
102
-
103
- attr_reader :_hash
104
-
105
- class SymbolAccessHash < SimpleDelegator
106
- def [](key)
107
- __getobj__[Key.wrap(key)]
108
- end
109
- end
110
-
111
- class Field
112
- include Callable
113
-
114
- attr_reader :_type, :key
115
-
116
- def initialize(key, type = nil, &block)
117
- @key = key.to_sym
118
- @_type = case type
119
- when ArrayClass, Array
120
- block_given? ? ArrayClass.new(element_type: Schema.new(&block)) : type
121
- when nil
122
- block_given? ? Schema.new(&block) : Types::Any
123
- when Composable
124
- type
125
- when Class
126
- if type == Array && block_given?
127
- ArrayClass.new(element_type: Schema.new(&block))
128
- else
129
- Types::Any[type]
130
- end
131
- else
132
- raise ArgumentError, "expected a Plumb type, but got #{type.inspect}"
133
- end
134
- end
135
-
136
- def call(result) = _type.call(result)
137
-
138
- def default(v, &block)
139
- @_type = @_type.default(v, &block)
140
- self
141
- end
142
-
143
- def metadata(data = Undefined)
144
- if data == Undefined
145
- @_type.metadata
146
- else
147
- @_type = @_type.metadata(data)
148
- end
149
- end
150
-
151
- def options(opts)
152
- @_type = @_type.options(opts)
153
- self
154
- end
155
-
156
- def nullable
157
- @_type = @_type.nullable
158
- self
159
- end
160
-
161
- def present
162
- @_type = @_type.present
163
- self
164
- end
165
-
166
- def required
167
- @_type = Types::Undefined.invalid(errors: 'is required') >> @_type
168
- self
169
- end
170
-
171
- def match(matcher)
172
- @_type = @_type.match(matcher)
173
- self
174
- end
175
-
176
- def policy(...)
177
- @_type = @_type.policy(...)
178
- self
179
- end
180
-
181
- def where(...)
182
- @_type = @_type.where(...)
183
- self
184
- end
185
-
186
- def inspect
187
- "#{self.class}[#{@_type.inspect}]"
188
- end
189
-
190
- private
191
-
192
- attr_reader :registry
193
- end
194
- end
195
- end
data/lib/plumb/step.rb DELETED
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'plumb/composable'
4
-
5
- module Plumb
6
- class Step
7
- include Composable
8
-
9
- attr_reader :_metadata, :children
10
-
11
- def initialize(callable = nil, inspect = nil, &block)
12
- @_metadata = callable.respond_to?(:metadata) ? callable.metadata : BLANK_HASH
13
- @callable = callable || block
14
- @children = [@callable].freeze
15
- @inspect = inspect || @callable.inspect
16
- freeze
17
- end
18
-
19
- def call(result)
20
- @callable.call(result)
21
- end
22
-
23
- private
24
-
25
- def _inspect = "Step[#{@inspect}]"
26
- end
27
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'plumb/composable'
4
-
5
- module Plumb
6
- class Transform
7
- include Composable
8
-
9
- attr_reader :children
10
-
11
- def initialize(target_type, callable)
12
- @target_type = target_type
13
- @callable = callable || Plumb::NOOP
14
- @children = [target_type].freeze
15
- freeze
16
- end
17
-
18
- def call(result)
19
- result.valid(@callable.call(result.value))
20
- end
21
-
22
- private
23
-
24
- def _inspect = "->(#{@target_type.inspect})"
25
- end
26
- end