dry-logic 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/README.md +6 -2
  4. data/lib/dry/logic/evaluator.rb +46 -0
  5. data/lib/dry/logic/predicate.rb +3 -3
  6. data/lib/dry/logic/result.rb +26 -126
  7. data/lib/dry/logic/result/each.rb +10 -0
  8. data/lib/dry/logic/result/multi.rb +14 -0
  9. data/lib/dry/logic/result/named.rb +17 -0
  10. data/lib/dry/logic/result/set.rb +10 -0
  11. data/lib/dry/logic/result/value.rb +13 -0
  12. data/lib/dry/logic/rule.rb +14 -36
  13. data/lib/dry/logic/rule/attr.rb +3 -11
  14. data/lib/dry/logic/rule/check.rb +23 -22
  15. data/lib/dry/logic/rule/composite.rb +32 -12
  16. data/lib/dry/logic/rule/each.rb +3 -3
  17. data/lib/dry/logic/rule/key.rb +24 -5
  18. data/lib/dry/logic/rule/negation.rb +15 -0
  19. data/lib/dry/logic/rule/set.rb +9 -8
  20. data/lib/dry/logic/rule/value.rb +15 -3
  21. data/lib/dry/logic/rule_compiler.rb +8 -40
  22. data/lib/dry/logic/version.rb +1 -1
  23. data/spec/shared/predicates.rb +2 -0
  24. data/spec/spec_helper.rb +1 -0
  25. data/spec/unit/rule/attr_spec.rb +5 -5
  26. data/spec/unit/rule/check_spec.rb +26 -39
  27. data/spec/unit/rule/conjunction_spec.rb +4 -4
  28. data/spec/unit/rule/disjunction_spec.rb +3 -3
  29. data/spec/unit/rule/each_spec.rb +2 -2
  30. data/spec/unit/rule/exclusive_disjunction_spec.rb +19 -0
  31. data/spec/unit/rule/implication_spec.rb +2 -2
  32. data/spec/unit/rule/key_spec.rb +103 -9
  33. data/spec/unit/rule/set_spec.rb +7 -9
  34. data/spec/unit/rule/value_spec.rb +29 -3
  35. data/spec/unit/rule_compiler_spec.rb +21 -49
  36. metadata +12 -9
  37. data/lib/dry/logic/rule/group.rb +0 -21
  38. data/lib/dry/logic/rule/result.rb +0 -33
  39. data/rakelib/rubocop.rake +0 -18
  40. data/spec/unit/rule/group_spec.rb +0 -12
  41. data/spec/unit/rule/result_spec.rb +0 -102
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d99c501f48f756fd19ec5f5c3da47041b704e97a
4
- data.tar.gz: a8dd4c89cd7674a65b69a6d441091ba73b514bbc
3
+ metadata.gz: 556cffe1d07e756e85274f2af98b8d97245e4420
4
+ data.tar.gz: 86695fbdf83593baedb7dd73483c30f41f671280
5
5
  SHA512:
6
- metadata.gz: 68a19a8f78af8a329e47e8b089fef34fb2b9dbdd686840568b328fe755b65f9fbd7e7d9b6324aa78d68d7bba56c61f5c1a97fa51a9f7d89d39e3cc91e7c64503
7
- data.tar.gz: e5b68c7f5599a244f9918c5d2bc67b282983dc1e48cbd2deaaeabf55670dbbe5b05a27b5f5d9ce6655cf0de6f202eab50a08cb323e37e535aa0d80951ac60a31
6
+ metadata.gz: 05b63d57cfacd055926b256752698078dbca63afcf8bb03aff563cbecff5247e9dc66e999bad29cecc1fc5f37a15a5c6c76525cb1e6654f1c4915777825d5fa3
7
+ data.tar.gz: f1a65826b7049c3c5b0083b4c72feda8e911915898ff03a4d9acc5b4d6908954d9105edd36f611e535e0e0320b1f906f9ae658659a062b48ee18b564e1ab0abb
data/Gemfile CHANGED
@@ -3,7 +3,6 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :tools do
6
- gem 'rubocop'
7
6
  gem 'guard'
8
7
  gem 'guard-rspec'
9
8
  gem 'guard-rubocop'
data/README.md CHANGED
@@ -28,8 +28,12 @@ require 'dry/logic/predicates'
28
28
 
29
29
  include Dry::Logic
30
30
 
31
- user_present = Rule::Key.new(:user, Predicates[:key?])
32
- has_min_age = Rule::Key.new(:age, Predicates[:key?]) & Rule::Value.new(:age, Predicates[:gt?].curry(18))
31
+ user_present = Rule::Key.new(Predicates[:key?], name: :user)
32
+
33
+ has_min_age = Rule::Key.new(
34
+ Predicates[:key?]) & Rule::Value.new(:age, Predicates[:gt?].curry(18),
35
+ name: :age
36
+ )
33
37
 
34
38
  user_rule = user_present & has_min_age
35
39
 
@@ -0,0 +1,46 @@
1
+ module Dry
2
+ module Logic
3
+ class Evaluator
4
+ include Dry::Equalizer(:path)
5
+
6
+ attr_reader :path
7
+
8
+ class Set
9
+ include Dry::Equalizer(:evaluators)
10
+
11
+ attr_reader :evaluators
12
+
13
+ def self.new(paths)
14
+ super(paths.map { |path| Evaluator::Key.new(path) })
15
+ end
16
+
17
+ def initialize(evaluators)
18
+ @evaluators = evaluators
19
+ end
20
+
21
+ def call(input)
22
+ evaluators.map { |evaluator| evaluator[input] }
23
+ end
24
+ alias_method :[], :call
25
+ end
26
+
27
+ class Key < Evaluator
28
+ def call(input)
29
+ path.reduce(input) { |a, e| a[e] }
30
+ end
31
+ alias_method :[], :call
32
+ end
33
+
34
+ class Attr < Evaluator
35
+ def call(input)
36
+ path.reduce(input) { |a, e| a.public_send(e) }
37
+ end
38
+ alias_method :[], :call
39
+ end
40
+
41
+ def initialize(path)
42
+ @path = Array(path)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -8,7 +8,7 @@ module Dry
8
8
  end
9
9
 
10
10
  class Predicate
11
- include Dry::Equalizer(:id)
11
+ include Dry::Equalizer(:id, :args)
12
12
 
13
13
  attr_reader :id, :args, :fn
14
14
 
@@ -26,10 +26,10 @@ module Dry
26
26
  self.class.new(id, *args, &fn.curry.(*args))
27
27
  end
28
28
 
29
- def to_ary
29
+ def to_ast
30
30
  [:predicate, [id, args]]
31
31
  end
32
- alias_method :to_a, :to_ary
32
+ alias_method :to_a, :to_ast
33
33
  end
34
34
  end
35
35
  end
@@ -1,155 +1,55 @@
1
1
  module Dry
2
2
  module Logic
3
- def self.Result(input, value, rule)
4
- case value
5
- when Result
6
- value.class.new(value.input, value.success?, rule)
7
- when Array
8
- Result::Set.new(input, value, rule)
9
- else
10
- Result::Value.new(input, value, rule)
11
- end
3
+ def self.Result(response, rule, input)
4
+ Result[rule].new(response, rule, input)
12
5
  end
13
6
 
14
7
  class Result
15
8
  include Dry::Equalizer(:success?, :input, :rule)
16
9
 
17
- attr_reader :input, :value, :rule, :name
18
-
19
- class Result::Set < Result
20
- def success?
21
- value.all?(&:success?)
22
- end
23
-
24
- def to_ary
25
- indices = value.map { |v| v.failure? ? value.index(v) : nil }.compact
26
- [:input, [name, input, value.values_at(*indices).map(&:to_ary)]]
27
- end
28
- end
29
-
30
- class Result::Value < Result
31
- def to_ary
32
- [:input, [name, input, [rule.to_ary]]]
33
- end
34
- alias_method :to_a, :to_ary
35
- end
36
-
37
- class Result::LazyValue < Result
38
- def to_ary
39
- [:input, [rule.name, input, [rule.to_ary]]]
40
- end
41
- alias_method :to_a, :to_ary
10
+ attr_reader :input, :rule, :response, :success
42
11
 
43
- def input
44
- success? ? rule.evaluate_input(@input) : @input
12
+ def self.[](type)
13
+ case type
14
+ when Rule::Each then Result::Each
15
+ when Rule::Set then Result::Set
16
+ when Rule::Key, Rule::Attr, Rule::Check then Result::Named
17
+ else Result::Value
45
18
  end
46
19
  end
47
20
 
48
- class Result::Wrapped < Result::Value
49
- def to_ary
50
- [:input, [rule.name, rule.evaluate_input(input), [rule.to_ary]]]
51
- end
52
- alias_method :to_a, :to_ary
53
-
54
- def wrapped?
55
- true
56
- end
57
- end
58
-
59
- class Result::Verified < Result
60
- attr_reader :predicate_id
61
-
62
- def initialize(result, predicate_id)
63
- @input = result.input
64
- @value = result.value
65
- @rule = result.rule
66
- @name = result.name
67
- @predicate_id = predicate_id
68
- end
69
-
70
- def call(*)
71
- Logic.Result(input, success?, rule)
72
- end
73
-
74
- def to_ary
75
- [:input, [name, input, [rule.to_ary]]]
76
- end
77
- alias_method :to_a, :to_ary
78
-
79
- def success?
80
- rule.predicate_id == predicate_id
81
- end
82
- end
83
-
84
- def initialize(input, value, rule)
85
- @input = input
86
- @value = value
21
+ def initialize(response, rule, input)
22
+ @response = response
23
+ @success = response.respond_to?(:success?) ? response.success? : response
87
24
  @rule = rule
88
- @name = rule.name
25
+ @input = input
89
26
  end
90
27
 
91
- def call(*)
92
- self
28
+ def [](name)
29
+ response[name] if response.respond_to?(:[])
93
30
  end
94
31
 
95
- def curry(predicate_id = nil)
96
- if predicate_id
97
- Result::Verified.new(self, predicate_id)
98
- else
99
- self
100
- end
32
+ def name
33
+ nil
101
34
  end
102
35
 
103
36
  def negated
104
- self.class.new(input, !value, rule)
105
- end
106
-
107
- def then(other)
108
- if success?
109
- other.(input)
110
- else
111
- Logic.Result(input, true, rule)
112
- end
113
- end
114
-
115
- def and(other)
116
- if success?
117
- other.(input)
118
- else
119
- self
120
- end
121
- end
122
-
123
- def or(other)
124
- if success?
125
- self
126
- else
127
- other.(input)
128
- end
129
- end
130
-
131
- def xor(other)
132
- other_result = other.(input)
133
- value = success? ^ other_result.success?
134
-
135
- if other_result.wrapped?
136
- Result::Wrapped.new(input, value, rule)
137
- else
138
- Logic.Result(other_result.input, value, rule)
139
- end
37
+ self.class.new(!success, rule, input)
140
38
  end
141
39
 
142
40
  def success?
143
- @value
41
+ @success
144
42
  end
145
43
 
146
44
  def failure?
147
- ! success?
148
- end
149
-
150
- def wrapped?
151
- false
45
+ !success?
152
46
  end
153
47
  end
154
48
  end
155
49
  end
50
+
51
+ require 'dry/logic/result/value'
52
+ require 'dry/logic/result/named'
53
+ require 'dry/logic/result/multi'
54
+ require 'dry/logic/result/each'
55
+ require 'dry/logic/result/set'
@@ -0,0 +1,10 @@
1
+ module Dry
2
+ module Logic
3
+ class Result::Each < Result::Multi
4
+ def to_ast
5
+ failed_rules = failures.map { |el| [:el, [success.index(el), el.to_ast]] }
6
+ [:result, [rule.evaluate(input), [:each, failed_rules]]]
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Dry
2
+ module Logic
3
+ class Result::Multi < Result
4
+ def success?
5
+ success.all?(&:success?)
6
+ end
7
+
8
+ def failures
9
+ indices = success.map { |v| v.failure? ? success.index(v) : nil }.compact
10
+ success.values_at(*indices)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module Dry
2
+ module Logic
3
+ class Result::Named < Result::Value
4
+ def name
5
+ rule.name
6
+ end
7
+
8
+ def to_ast
9
+ if response.respond_to?(:to_ast) && !response.is_a?(Result)
10
+ response.to_ast
11
+ else
12
+ [:input, [rule.name, super]]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ module Dry
2
+ module Logic
3
+ class Result::Set < Result::Multi
4
+ def to_ast
5
+ failed_rules = failures.map { |el| el.to_ast }
6
+ [:result, [rule.evaluate(input), [:set, failed_rules]]]
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module Dry
2
+ module Logic
3
+ class Result::Value < Result
4
+ def to_ast
5
+ if response.respond_to?(:to_ast)
6
+ response.to_ast
7
+ else
8
+ [:result, [rule.evaluate(input), rule.to_ast]]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,31 +1,15 @@
1
1
  module Dry
2
2
  module Logic
3
3
  class Rule
4
- include Dry::Equalizer(:name, :predicate)
4
+ include Dry::Equalizer(:predicate, :options)
5
5
 
6
- attr_reader :name, :predicate
6
+ attr_reader :predicate
7
7
 
8
- class Negation < Rule
9
- include Dry::Equalizer(:rule)
8
+ attr_reader :options
10
9
 
11
- attr_reader :rule
12
-
13
- def initialize(rule)
14
- @rule = rule
15
- end
16
-
17
- def call(*args)
18
- rule.(*args).negated
19
- end
20
-
21
- def to_ary
22
- [:not, rule.to_ary]
23
- end
24
- end
25
-
26
- def initialize(name, predicate)
27
- @name = name
10
+ def initialize(predicate, options = {})
28
11
  @predicate = predicate
12
+ @options = options
29
13
  end
30
14
 
31
15
  def predicate_id
@@ -33,18 +17,9 @@ module Dry
33
17
  end
34
18
 
35
19
  def type
36
- :rule
37
- end
38
-
39
- def call(*args)
40
- Logic.Result(args, predicate.call, self)
20
+ raise NotImplementedError
41
21
  end
42
22
 
43
- def to_ary
44
- [type, [name, predicate.to_ary]]
45
- end
46
- alias_method :to_a, :to_ary
47
-
48
23
  def and(other)
49
24
  Conjunction.new(self, other)
50
25
  end
@@ -70,24 +45,27 @@ module Dry
70
45
  end
71
46
 
72
47
  def new(predicate)
73
- self.class.new(name, predicate)
48
+ self.class.new(predicate, options)
74
49
  end
75
50
 
76
51
  def curry(*args)
77
- self.class.new(name, predicate.curry(*args))
52
+ self.class.new(predicate.curry(*args), options)
53
+ end
54
+
55
+ def each?
56
+ predicate.is_a?(Rule::Each)
78
57
  end
79
58
  end
80
59
  end
81
60
  end
82
61
 
62
+ require 'dry/logic/rule/value'
83
63
  require 'dry/logic/rule/key'
84
64
  require 'dry/logic/rule/attr'
85
- require 'dry/logic/rule/value'
86
65
  require 'dry/logic/rule/each'
87
66
  require 'dry/logic/rule/set'
88
67
  require 'dry/logic/rule/composite'
68
+ require 'dry/logic/rule/negation'
89
69
  require 'dry/logic/rule/check'
90
- require 'dry/logic/rule/result'
91
- require 'dry/logic/rule/group'
92
70
 
93
71
  require 'dry/logic/result'