dry-logic 0.1.0 → 0.1.1
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/CHANGELOG.md +10 -1
- data/lib/dry/logic/predicates.rb +12 -0
- data/lib/dry/logic/result.rb +20 -2
- data/lib/dry/logic/rule.rb +2 -0
- data/lib/dry/logic/rule/attr.rb +21 -0
- data/lib/dry/logic/rule/check.rb +2 -2
- data/lib/dry/logic/rule/result.rb +16 -0
- data/lib/dry/logic/rule_compiler.rb +10 -0
- data/lib/dry/logic/version.rb +1 -1
- data/spec/shared/predicates.rb +2 -0
- data/spec/spec_helper.rb +0 -2
- data/spec/unit/predicates/attr_spec.rb +29 -0
- data/spec/unit/predicates/bool_spec.rb +1 -1
- data/spec/unit/predicates/false_spec.rb +35 -0
- data/spec/unit/predicates/true_spec.rb +35 -0
- data/spec/unit/rule/attr_spec.rb +29 -0
- data/spec/unit/rule/result_spec.rb +33 -0
- data/spec/unit/rule_compiler_spec.rb +18 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9042bd42bf10c567eb88dc6e830ebface1daf974
|
4
|
+
data.tar.gz: dc92016f7c8a14ddd0db49f33d0818130cc9cada
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 049784897429ec2b7064da4217cd508d6557bdf4a3c9208e716a608d43c03b9f449a782cef01df03a1786ecb11216f2208ed576eaf1ef8ff93cd92a66ce347af
|
7
|
+
data.tar.gz: 64b6dbcd4d3e5b9e06f6b50fb1c1bfd6bd7b856d01b67f2357e46f93a0f716f600bce868b0aec0bd83d9a0e5a8141583aaf18a01a5a119766044f792c93a7467
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
-
# v0.1.
|
1
|
+
# v0.1.1 2016-01-18
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* `Rule::Attr` which can be applied to a data object with attr readers (SunnyMagadan)
|
6
|
+
* `Rule::Result` which can be applied to a result object (solnic)
|
7
|
+
|
8
|
+
[Compare v0.1.0...v0.1.1](https://github.com/dryrb/dry-logic/compare/v0.1.0...v0.1.1)
|
9
|
+
|
10
|
+
# v0.1.0 2016-01-11
|
2
11
|
|
3
12
|
Code extracted from dry-validation 0.4.1
|
data/lib/dry/logic/predicates.rb
CHANGED
@@ -23,6 +23,10 @@ module Dry
|
|
23
23
|
input.key?(name)
|
24
24
|
end
|
25
25
|
|
26
|
+
predicate(:attr?) do |name, input|
|
27
|
+
input.respond_to?(name)
|
28
|
+
end
|
29
|
+
|
26
30
|
predicate(:empty?) do |input|
|
27
31
|
case input
|
28
32
|
when String, Array, Hash then input.empty?
|
@@ -121,6 +125,14 @@ module Dry
|
|
121
125
|
left.eql?(right)
|
122
126
|
end
|
123
127
|
|
128
|
+
predicate(:true?) do |value|
|
129
|
+
value === true
|
130
|
+
end
|
131
|
+
|
132
|
+
predicate(:false?) do |value|
|
133
|
+
value === false
|
134
|
+
end
|
135
|
+
|
124
136
|
predicate(:format?) do |regex, input|
|
125
137
|
!regex.match(input).nil?
|
126
138
|
end
|
data/lib/dry/logic/result.rb
CHANGED
@@ -42,7 +42,7 @@ module Dry
|
|
42
42
|
@predicate_id = predicate_id
|
43
43
|
end
|
44
44
|
|
45
|
-
def call
|
45
|
+
def call(*)
|
46
46
|
Logic.Result(input, success?, rule)
|
47
47
|
end
|
48
48
|
|
@@ -56,6 +56,24 @@ module Dry
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
class Result::LazyValue < Result
|
60
|
+
def to_ary
|
61
|
+
[:input, [rule.name, input, [rule.to_ary]]]
|
62
|
+
end
|
63
|
+
alias_method :to_a, :to_ary
|
64
|
+
|
65
|
+
def input
|
66
|
+
success? ? rule.evaluate_input(@input) : @input
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Result::Wrapped < Result
|
71
|
+
def to_ary
|
72
|
+
[:input, [name, nil, [rule.to_ary]]]
|
73
|
+
end
|
74
|
+
alias_method :to_a, :to_ary
|
75
|
+
end
|
76
|
+
|
59
77
|
def initialize(input, value, rule)
|
60
78
|
@input = input
|
61
79
|
@value = value
|
@@ -63,7 +81,7 @@ module Dry
|
|
63
81
|
@name = rule.name
|
64
82
|
end
|
65
83
|
|
66
|
-
def call
|
84
|
+
def call(*)
|
67
85
|
self
|
68
86
|
end
|
69
87
|
|
data/lib/dry/logic/rule.rb
CHANGED
@@ -81,11 +81,13 @@ module Dry
|
|
81
81
|
end
|
82
82
|
|
83
83
|
require 'dry/logic/rule/key'
|
84
|
+
require 'dry/logic/rule/attr'
|
84
85
|
require 'dry/logic/rule/value'
|
85
86
|
require 'dry/logic/rule/each'
|
86
87
|
require 'dry/logic/rule/set'
|
87
88
|
require 'dry/logic/rule/composite'
|
88
89
|
require 'dry/logic/rule/check'
|
90
|
+
require 'dry/logic/rule/result'
|
89
91
|
require 'dry/logic/rule/group'
|
90
92
|
|
91
93
|
require 'dry/logic/result'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Dry
|
2
|
+
module Logic
|
3
|
+
class Rule::Attr < Rule
|
4
|
+
def self.new(name, predicate)
|
5
|
+
super(name, predicate.curry(name))
|
6
|
+
end
|
7
|
+
|
8
|
+
def type
|
9
|
+
:attr
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate_input(input)
|
13
|
+
input.public_send(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(input)
|
17
|
+
Logic::Result::LazyValue.new(input, predicate.(input), self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/dry/logic/rule/check.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Dry
|
2
|
+
module Logic
|
3
|
+
class Rule::Result < Rule
|
4
|
+
def call(input)
|
5
|
+
result = input[name]
|
6
|
+
return result unless result.success?
|
7
|
+
result_input = result.input
|
8
|
+
Result::Wrapped.new(input, predicate.(result_input), self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def type
|
12
|
+
:res
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -23,6 +23,11 @@ module Dry
|
|
23
23
|
Rule::Check.new(name, visit(predicate))
|
24
24
|
end
|
25
25
|
|
26
|
+
def visit_res(node)
|
27
|
+
name, predicate = node
|
28
|
+
Rule::Result.new(name, visit(predicate))
|
29
|
+
end
|
30
|
+
|
26
31
|
def visit_not(node)
|
27
32
|
visit(node).negation
|
28
33
|
end
|
@@ -32,6 +37,11 @@ module Dry
|
|
32
37
|
Rule::Key.new(name, visit(predicate))
|
33
38
|
end
|
34
39
|
|
40
|
+
def visit_attr(node)
|
41
|
+
name, predicate = node
|
42
|
+
Rule::Attr.new(name, visit(predicate))
|
43
|
+
end
|
44
|
+
|
35
45
|
def visit_val(node)
|
36
46
|
name, predicate = node
|
37
47
|
Rule::Value.new(name, visit(predicate))
|
data/lib/dry/logic/version.rb
CHANGED
data/spec/shared/predicates.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'dry/logic/predicates'
|
2
|
+
|
3
|
+
RSpec.describe Dry::Logic::Predicates do
|
4
|
+
describe '#attr?' do
|
5
|
+
let(:predicate_name) { :attr? }
|
6
|
+
|
7
|
+
context 'when value responds to the attr name' do
|
8
|
+
let(:arguments_list) do
|
9
|
+
[
|
10
|
+
[:name, Struct.new(:name).new('John')],
|
11
|
+
[:age, Struct.new(:age).new(18)]
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
it_behaves_like 'a passing predicate'
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'with value does not respond to the attr name' do
|
19
|
+
let(:arguments_list) do
|
20
|
+
[
|
21
|
+
[:name, Struct.new(:age).new(18)],
|
22
|
+
[:age, Struct.new(:name).new('Jill')]
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
it_behaves_like 'a failing predicate'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'dry/logic/predicates'
|
2
|
+
|
3
|
+
RSpec.describe Dry::Logic::Predicates do
|
4
|
+
describe '#false?' do
|
5
|
+
let(:predicate_name) { :false? }
|
6
|
+
|
7
|
+
context 'when value is a date' do
|
8
|
+
let(:arguments_list) do
|
9
|
+
[[false]]
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like 'a passing predicate'
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when value is not false' do
|
16
|
+
let(:arguments_list) do
|
17
|
+
[
|
18
|
+
[true],
|
19
|
+
[''],
|
20
|
+
[[]],
|
21
|
+
[{}],
|
22
|
+
[nil],
|
23
|
+
[:symbol],
|
24
|
+
[String],
|
25
|
+
[1],
|
26
|
+
[0],
|
27
|
+
['true'],
|
28
|
+
['false']
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
it_behaves_like 'a failing predicate'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'dry/logic/predicates'
|
2
|
+
|
3
|
+
RSpec.describe Dry::Logic::Predicates do
|
4
|
+
describe '#true?' do
|
5
|
+
let(:predicate_name) { :true? }
|
6
|
+
|
7
|
+
context 'when value is a date' do
|
8
|
+
let(:arguments_list) do
|
9
|
+
[[true]]
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like 'a passing predicate'
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with value is not true' do
|
16
|
+
let(:arguments_list) do
|
17
|
+
[
|
18
|
+
[false],
|
19
|
+
[''],
|
20
|
+
[[]],
|
21
|
+
[{}],
|
22
|
+
[nil],
|
23
|
+
[:symbol],
|
24
|
+
[String],
|
25
|
+
[1],
|
26
|
+
[0],
|
27
|
+
['true'],
|
28
|
+
['false']
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
it_behaves_like 'a failing predicate'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'dry/logic/rule'
|
2
|
+
|
3
|
+
RSpec.describe Dry::Logic::Rule::Attr do
|
4
|
+
include_context 'predicates'
|
5
|
+
|
6
|
+
let(:model) { Struct.new(:name) }
|
7
|
+
|
8
|
+
subject(:rule) { described_class.new(:name, attr?) }
|
9
|
+
|
10
|
+
describe '#call' do
|
11
|
+
it 'applies predicate to the value' do
|
12
|
+
expect(rule.(model.new(name: 'Jane'))).to be_success
|
13
|
+
expect(rule.(nil)).to be_failure
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#and' do
|
18
|
+
let(:other) { Dry::Logic::Rule::Value.new(:name, str?) }
|
19
|
+
|
20
|
+
it 'returns conjunction rule where value is passed to the right' do
|
21
|
+
present_and_string = rule.and(other)
|
22
|
+
|
23
|
+
expect(present_and_string.(model.new('Jane'))).to be_success
|
24
|
+
|
25
|
+
expect(present_and_string.(nil)).to be_failure
|
26
|
+
expect(present_and_string.(model.new(1))).to be_failure
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'dry/logic/rule/result'
|
2
|
+
|
3
|
+
RSpec.describe Dry::Logic::Rule::Result do
|
4
|
+
subject(:rule) { Dry::Logic::Rule::Result.new(:name, min_size?.curry(4)) }
|
5
|
+
|
6
|
+
include_context 'predicates'
|
7
|
+
|
8
|
+
let(:is_str) { Dry::Logic::Rule::Value.new(:name, str?) }
|
9
|
+
let(:is_jane) { Dry::Logic::Rule::Result.new(:name, eql?.curry('jane')) }
|
10
|
+
|
11
|
+
describe '#call' do
|
12
|
+
it 'returns result of a predicate' do
|
13
|
+
expect(rule.(name: is_str.('jane'))).to be_success
|
14
|
+
expect(rule.(name: is_str.('jan'))).to be_failure
|
15
|
+
expect(rule.(name: is_str.(nil))).to be_failure
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'evaluates input for the ast' do
|
19
|
+
expect(rule.(name: is_str.('jane')).to_ary).to eql([
|
20
|
+
:input, [
|
21
|
+
:name, nil, [[:res, [:name, [:predicate, [:min_size?, [4]]]]]]
|
22
|
+
]
|
23
|
+
])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#and' do
|
28
|
+
it 'returns result of a conjunction' do
|
29
|
+
expect(rule.and(is_jane).(name: is_str.('jane'))).to be_success
|
30
|
+
expect(rule.and(is_jane).(name: is_str.('john'))).to be_failure
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -5,6 +5,7 @@ RSpec.describe Dry::Logic::RuleCompiler, '#call' do
|
|
5
5
|
|
6
6
|
let(:predicates) {
|
7
7
|
{ key?: predicate,
|
8
|
+
attr?: predicate,
|
8
9
|
filled?: predicate,
|
9
10
|
email: val_rule.('email').curry(:filled?) }
|
10
11
|
}
|
@@ -13,8 +14,10 @@ RSpec.describe Dry::Logic::RuleCompiler, '#call' do
|
|
13
14
|
|
14
15
|
let(:key_rule) { Rule::Key.new(:email, predicate) }
|
15
16
|
let(:not_key_rule) { Rule::Key.new(:email, predicate).negation }
|
17
|
+
let(:attr_rule) { Rule::Attr.new(:email, predicate) }
|
16
18
|
let(:val_rule) { Rule::Value.new(:email, predicate) }
|
17
19
|
let(:check_rule) { Rule::Check.new(:email, predicates[:email]) }
|
20
|
+
let(:res_rule) { Rule::Result.new(:email, predicates[:email]) }
|
18
21
|
let(:and_rule) { key_rule & val_rule }
|
19
22
|
let(:or_rule) { key_rule | val_rule }
|
20
23
|
let(:xor_rule) { key_rule ^ val_rule }
|
@@ -37,6 +40,21 @@ RSpec.describe Dry::Logic::RuleCompiler, '#call' do
|
|
37
40
|
expect(rules).to eql([check_rule])
|
38
41
|
end
|
39
42
|
|
43
|
+
it 'compiles result rules' do
|
44
|
+
ast = [[:res, [:email, [:predicate, [:email, [:filled?]]]]]]
|
45
|
+
|
46
|
+
rules = compiler.(ast)
|
47
|
+
|
48
|
+
expect(rules).to eql([res_rule])
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'compiles attr rules' do
|
52
|
+
ast = [[:attr, [:email, [:predicate, [:attr?, predicate]]]]]
|
53
|
+
|
54
|
+
rules = compiler.(ast)
|
55
|
+
|
56
|
+
expect(rules).to eql([attr_rule])
|
57
|
+
end
|
40
58
|
|
41
59
|
it 'compiles negated rules' do
|
42
60
|
ast = [[:not, [:key, [:email, [:predicate, [:key?, predicate]]]]]]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-logic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-container
|
@@ -112,11 +112,13 @@ files:
|
|
112
112
|
- lib/dry/logic/predicates.rb
|
113
113
|
- lib/dry/logic/result.rb
|
114
114
|
- lib/dry/logic/rule.rb
|
115
|
+
- lib/dry/logic/rule/attr.rb
|
115
116
|
- lib/dry/logic/rule/check.rb
|
116
117
|
- lib/dry/logic/rule/composite.rb
|
117
118
|
- lib/dry/logic/rule/each.rb
|
118
119
|
- lib/dry/logic/rule/group.rb
|
119
120
|
- lib/dry/logic/rule/key.rb
|
121
|
+
- lib/dry/logic/rule/result.rb
|
120
122
|
- lib/dry/logic/rule/set.rb
|
121
123
|
- lib/dry/logic/rule/value.rb
|
122
124
|
- lib/dry/logic/rule_compiler.rb
|
@@ -125,6 +127,7 @@ files:
|
|
125
127
|
- spec/shared/predicates.rb
|
126
128
|
- spec/spec_helper.rb
|
127
129
|
- spec/unit/predicate_spec.rb
|
130
|
+
- spec/unit/predicates/attr_spec.rb
|
128
131
|
- spec/unit/predicates/bool_spec.rb
|
129
132
|
- spec/unit/predicates/date_spec.rb
|
130
133
|
- spec/unit/predicates/date_time_spec.rb
|
@@ -132,6 +135,7 @@ files:
|
|
132
135
|
- spec/unit/predicates/empty_spec.rb
|
133
136
|
- spec/unit/predicates/eql_spec.rb
|
134
137
|
- spec/unit/predicates/exclusion_spec.rb
|
138
|
+
- spec/unit/predicates/false_spec.rb
|
135
139
|
- spec/unit/predicates/filled_spec.rb
|
136
140
|
- spec/unit/predicates/float_spec.rb
|
137
141
|
- spec/unit/predicates/format_spec.rb
|
@@ -148,6 +152,8 @@ files:
|
|
148
152
|
- spec/unit/predicates/size_spec.rb
|
149
153
|
- spec/unit/predicates/str_spec.rb
|
150
154
|
- spec/unit/predicates/time_spec.rb
|
155
|
+
- spec/unit/predicates/true_spec.rb
|
156
|
+
- spec/unit/rule/attr_spec.rb
|
151
157
|
- spec/unit/rule/check_spec.rb
|
152
158
|
- spec/unit/rule/conjunction_spec.rb
|
153
159
|
- spec/unit/rule/disjunction_spec.rb
|
@@ -155,6 +161,7 @@ files:
|
|
155
161
|
- spec/unit/rule/group_spec.rb
|
156
162
|
- spec/unit/rule/implication_spec.rb
|
157
163
|
- spec/unit/rule/key_spec.rb
|
164
|
+
- spec/unit/rule/result_spec.rb
|
158
165
|
- spec/unit/rule/set_spec.rb
|
159
166
|
- spec/unit/rule/value_spec.rb
|
160
167
|
- spec/unit/rule_compiler_spec.rb
|
@@ -186,6 +193,7 @@ test_files:
|
|
186
193
|
- spec/shared/predicates.rb
|
187
194
|
- spec/spec_helper.rb
|
188
195
|
- spec/unit/predicate_spec.rb
|
196
|
+
- spec/unit/predicates/attr_spec.rb
|
189
197
|
- spec/unit/predicates/bool_spec.rb
|
190
198
|
- spec/unit/predicates/date_spec.rb
|
191
199
|
- spec/unit/predicates/date_time_spec.rb
|
@@ -193,6 +201,7 @@ test_files:
|
|
193
201
|
- spec/unit/predicates/empty_spec.rb
|
194
202
|
- spec/unit/predicates/eql_spec.rb
|
195
203
|
- spec/unit/predicates/exclusion_spec.rb
|
204
|
+
- spec/unit/predicates/false_spec.rb
|
196
205
|
- spec/unit/predicates/filled_spec.rb
|
197
206
|
- spec/unit/predicates/float_spec.rb
|
198
207
|
- spec/unit/predicates/format_spec.rb
|
@@ -209,6 +218,8 @@ test_files:
|
|
209
218
|
- spec/unit/predicates/size_spec.rb
|
210
219
|
- spec/unit/predicates/str_spec.rb
|
211
220
|
- spec/unit/predicates/time_spec.rb
|
221
|
+
- spec/unit/predicates/true_spec.rb
|
222
|
+
- spec/unit/rule/attr_spec.rb
|
212
223
|
- spec/unit/rule/check_spec.rb
|
213
224
|
- spec/unit/rule/conjunction_spec.rb
|
214
225
|
- spec/unit/rule/disjunction_spec.rb
|
@@ -216,6 +227,7 @@ test_files:
|
|
216
227
|
- spec/unit/rule/group_spec.rb
|
217
228
|
- spec/unit/rule/implication_spec.rb
|
218
229
|
- spec/unit/rule/key_spec.rb
|
230
|
+
- spec/unit/rule/result_spec.rb
|
219
231
|
- spec/unit/rule/set_spec.rb
|
220
232
|
- spec/unit/rule/value_spec.rb
|
221
233
|
- spec/unit/rule_compiler_spec.rb
|