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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ed31141ff3e52c81a9afce4397631ce26372a7e
4
- data.tar.gz: ee8d692843523fc91d4e9f6d28a03e4e749362b5
3
+ metadata.gz: 9042bd42bf10c567eb88dc6e830ebface1daf974
4
+ data.tar.gz: dc92016f7c8a14ddd0db49f33d0818130cc9cada
5
5
  SHA512:
6
- metadata.gz: 61463a874fb6281033cba937136a44131df14b59b1fab36532786f47f698eeaa96fbca1cdcc8235ee93b69d66e85da5230bd2c15094720c102024ee01355bc0b
7
- data.tar.gz: 48f375ae23737143a1aea7ba6494f47de0aa86548278f66dedd45333473a177f1a3704e0451777ef1b128669e68006fee952ccf10634f0f928ec28fe5942f738
6
+ metadata.gz: 049784897429ec2b7064da4217cd508d6557bdf4a3c9208e716a608d43c03b9f449a782cef01df03a1786ecb11216f2208ed576eaf1ef8ff93cd92a66ce347af
7
+ data.tar.gz: 64b6dbcd4d3e5b9e06f6b50fb1c1bfd6bd7b856d01b67f2357e46f93a0f716f600bce868b0aec0bd83d9a0e5a8141583aaf18a01a5a119766044f792c93a7467
@@ -1,3 +1,12 @@
1
- # v0.1.0 to-be-released
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
@@ -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
@@ -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
 
@@ -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
@@ -3,8 +3,8 @@ module Dry
3
3
  class Rule::Check < Rule
4
4
  alias_method :result, :predicate
5
5
 
6
- def call(*)
7
- Logic.Result(nil, result.call, self)
6
+ def call(*args)
7
+ Logic.Result(nil, result.(*args), self)
8
8
  end
9
9
 
10
10
  def type
@@ -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))
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Logic
3
- VERSION = '0.1.0'.freeze
3
+ VERSION = '0.1.1'.freeze
4
4
  end
5
5
  end
@@ -17,6 +17,8 @@ RSpec.shared_examples 'predicates' do
17
17
 
18
18
  let(:key?) { Dry::Logic::Predicates[:key?] }
19
19
 
20
+ let(:attr?) { Dry::Logic::Predicates[:attr?] }
21
+
20
22
  let(:eql?) { Dry::Logic::Predicates[:eql?] }
21
23
  end
22
24
 
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  begin
4
2
  require 'byebug'
5
3
  rescue LoadError; end
@@ -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
@@ -12,7 +12,7 @@ RSpec.describe Dry::Logic::Predicates do
12
12
  it_behaves_like 'a passing predicate'
13
13
  end
14
14
 
15
- context 'with value is not an integer' do
15
+ context 'when value is not a bool' do
16
16
  let(:arguments_list) do
17
17
  [
18
18
  [''],
@@ -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.0
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 00:00:00.000000000 Z
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