plumb 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/plumb/rules.rb DELETED
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'plumb/steppable'
4
-
5
- module Plumb
6
- class Rules
7
- UnsupportedRuleError = Class.new(StandardError)
8
- UndefinedRuleError = Class.new(KeyError)
9
-
10
- class Registry
11
- RuleDef = Data.define(:name, :error_tpl, :callable, :expects) do
12
- def supports?(type)
13
- types = [type].flatten # may be an array of types for OR logic
14
- case expects
15
- when Symbol
16
- types.all? { |type| type && type.public_instance_methods.include?(expects) }
17
- when Class then types.all? { |type| type <= expects }
18
- when Object then true
19
- else raise "Unexpected expects: #{expects}"
20
- end
21
- end
22
- end
23
-
24
- Rule = Data.define(:rule_def, :arg_value, :error_str) do
25
- def self.build(rule_def, arg_value)
26
- error_str = format(rule_def.error_tpl, value: arg_value)
27
- new(rule_def, arg_value, error_str)
28
- end
29
-
30
- def node_name = :"rule_#{rule_def.name}"
31
- def name = rule_def.name
32
-
33
- def error_for(result)
34
- return nil if rule_def.callable.call(result, arg_value)
35
-
36
- error_str
37
- end
38
- end
39
-
40
- def initialize
41
- @definitions = Hash.new { |h, k| h[k] = Set.new }
42
- end
43
-
44
- def define(name, error_tpl, callable = nil, expects: Object, &block)
45
- name = name.to_sym
46
- callable ||= block
47
- @definitions[name] << RuleDef.new(name:, error_tpl:, callable:, expects:)
48
- end
49
-
50
- # Ex. size: 3, match: /foo/
51
- def resolve(rule_specs, for_type)
52
- rule_specs.map do |(name, arg_value)|
53
- rule_defs = @definitions.fetch(name.to_sym) { raise UndefinedRuleError, "no rule defined with :#{name}" }
54
- rule_def = rule_defs.find { |rd| rd.supports?(for_type) }
55
- unless rule_def
56
- raise UnsupportedRuleError, "No :#{name} rule for type #{for_type.inspect}" unless for_type.is_a?(Array)
57
-
58
- raise UnsupportedRuleError,
59
- "Can't apply :#{name} rule for types #{for_type}. All types must support the same rule implementation"
60
-
61
- end
62
-
63
- Rule.build(rule_def, arg_value)
64
- end
65
- end
66
- end
67
-
68
- include Steppable
69
-
70
- def self.registry
71
- @registry ||= Registry.new
72
- end
73
-
74
- def self.define(...)
75
- registry.define(...)
76
- end
77
-
78
- # Ex. new(size: 3, match: /foo/)
79
- attr_reader :rules
80
-
81
- def initialize(rule_specs, for_type)
82
- @rules = self.class.registry.resolve(rule_specs, for_type).freeze
83
- freeze
84
- end
85
-
86
- def call(result)
87
- errors = []
88
- err = nil
89
- @rules.each do |rule|
90
- err = rule.error_for(result)
91
- errors << err if err
92
- end
93
- return result unless errors.any?
94
-
95
- result.invalid(errors: errors.join(', '))
96
- end
97
-
98
- private def _inspect
99
- +'Rules(' << @rules.map { |r| [r.name, r.arg_value].join(': ') }.join(', ') << +')'
100
- end
101
- end
102
- end