dry-logic 0.1.0

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +16 -0
  5. data/.rubocop_todo.yml +7 -0
  6. data/.travis.yml +31 -0
  7. data/CHANGELOG.md +3 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE +20 -0
  10. data/README.md +45 -0
  11. data/Rakefile +12 -0
  12. data/dry-logic.gemspec +24 -0
  13. data/examples/basic.rb +13 -0
  14. data/lib/dry-logic.rb +1 -0
  15. data/lib/dry/logic.rb +10 -0
  16. data/lib/dry/logic/predicate.rb +35 -0
  17. data/lib/dry/logic/predicate_set.rb +23 -0
  18. data/lib/dry/logic/predicates.rb +129 -0
  19. data/lib/dry/logic/result.rb +119 -0
  20. data/lib/dry/logic/rule.rb +91 -0
  21. data/lib/dry/logic/rule/check.rb +15 -0
  22. data/lib/dry/logic/rule/composite.rb +63 -0
  23. data/lib/dry/logic/rule/each.rb +13 -0
  24. data/lib/dry/logic/rule/group.rb +21 -0
  25. data/lib/dry/logic/rule/key.rb +17 -0
  26. data/lib/dry/logic/rule/set.rb +22 -0
  27. data/lib/dry/logic/rule/value.rb +13 -0
  28. data/lib/dry/logic/rule_compiler.rb +81 -0
  29. data/lib/dry/logic/version.rb +5 -0
  30. data/rakelib/rubocop.rake +18 -0
  31. data/spec/shared/predicates.rb +41 -0
  32. data/spec/spec_helper.rb +18 -0
  33. data/spec/unit/predicate_spec.rb +27 -0
  34. data/spec/unit/predicates/bool_spec.rb +34 -0
  35. data/spec/unit/predicates/date_spec.rb +31 -0
  36. data/spec/unit/predicates/date_time_spec.rb +31 -0
  37. data/spec/unit/predicates/decimal_spec.rb +32 -0
  38. data/spec/unit/predicates/empty_spec.rb +38 -0
  39. data/spec/unit/predicates/eql_spec.rb +21 -0
  40. data/spec/unit/predicates/exclusion_spec.rb +35 -0
  41. data/spec/unit/predicates/filled_spec.rb +38 -0
  42. data/spec/unit/predicates/float_spec.rb +31 -0
  43. data/spec/unit/predicates/format_spec.rb +21 -0
  44. data/spec/unit/predicates/gt_spec.rb +40 -0
  45. data/spec/unit/predicates/gteq_spec.rb +40 -0
  46. data/spec/unit/predicates/inclusion_spec.rb +35 -0
  47. data/spec/unit/predicates/int_spec.rb +34 -0
  48. data/spec/unit/predicates/key_spec.rb +29 -0
  49. data/spec/unit/predicates/lt_spec.rb +40 -0
  50. data/spec/unit/predicates/lteq_spec.rb +40 -0
  51. data/spec/unit/predicates/max_size_spec.rb +49 -0
  52. data/spec/unit/predicates/min_size_spec.rb +49 -0
  53. data/spec/unit/predicates/none_spec.rb +28 -0
  54. data/spec/unit/predicates/size_spec.rb +55 -0
  55. data/spec/unit/predicates/str_spec.rb +32 -0
  56. data/spec/unit/predicates/time_spec.rb +31 -0
  57. data/spec/unit/rule/check_spec.rb +29 -0
  58. data/spec/unit/rule/conjunction_spec.rb +30 -0
  59. data/spec/unit/rule/disjunction_spec.rb +38 -0
  60. data/spec/unit/rule/each_spec.rb +20 -0
  61. data/spec/unit/rule/group_spec.rb +12 -0
  62. data/spec/unit/rule/implication_spec.rb +16 -0
  63. data/spec/unit/rule/key_spec.rb +27 -0
  64. data/spec/unit/rule/set_spec.rb +32 -0
  65. data/spec/unit/rule/value_spec.rb +42 -0
  66. data/spec/unit/rule_compiler_spec.rb +123 -0
  67. metadata +221 -0
@@ -0,0 +1,20 @@
1
+ require 'dry/logic/rule'
2
+
3
+ RSpec.describe Dry::Logic::Rule::Each do
4
+ include_context 'predicates'
5
+
6
+ subject(:address_rule) do
7
+ Dry::Logic::Rule::Each.new(:name, is_string)
8
+ end
9
+
10
+ let(:is_string) { Dry::Logic::Rule::Value.new(:name, str?) }
11
+
12
+ describe '#call' do
13
+ it 'applies its rules to all elements in the input' do
14
+ expect(address_rule.(['Address'])).to be_success
15
+
16
+ expect(address_rule.([nil, 'Address'])).to be_failure
17
+ expect(address_rule.([:Address, 'Address'])).to be_failure
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ RSpec.describe Rule::Group do
2
+ include_context 'predicates'
3
+
4
+ subject(:rule) { Rule::Group.new([:pass, :pass_confirm], eql?) }
5
+
6
+ describe '#call' do
7
+ it 'calls predicate with result values' do
8
+ expect(rule.('foo', 'foo')).to be_success
9
+ expect(rule.('foo', 'bar')).to be_failure
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ RSpec.describe Rule::Composite::Implication do
2
+ include_context 'predicates'
3
+
4
+ subject(:rule) { Rule::Composite::Implication.new(left, right) }
5
+
6
+ let(:left) { Rule::Value.new(:age, int?) }
7
+ let(:right) { Rule::Value.new(:age, gt?.curry(18)) }
8
+
9
+ describe '#call' do
10
+ it 'calls left and right' do
11
+ expect(rule.('19')).to be_success
12
+ expect(rule.(19)).to be_success
13
+ expect(rule.(18)).to be_failure
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ require 'dry/logic/rule'
2
+
3
+ RSpec.describe Dry::Logic::Rule::Key do
4
+ include_context 'predicates'
5
+
6
+ subject(:rule) { Dry::Logic::Rule::Key.new(:name, key?) }
7
+
8
+ describe '#call' do
9
+ it 'applies predicate to the value' do
10
+ expect(rule.(name: 'Jane')).to be_success
11
+ expect(rule.({})).to be_failure
12
+ end
13
+ end
14
+
15
+ describe '#and' do
16
+ let(:other) { Dry::Logic::Rule::Value.new(:name, str?) }
17
+
18
+ it 'returns conjunction rule where value is passed to the right' do
19
+ present_and_string = rule.and(other)
20
+
21
+ expect(present_and_string.(name: 'Jane')).to be_success
22
+
23
+ expect(present_and_string.({})).to be_failure
24
+ expect(present_and_string.(name: 1)).to be_failure
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ require 'dry/logic/rule'
2
+
3
+ RSpec.describe Dry::Logic::Rule::Set do
4
+ include_context 'predicates'
5
+
6
+ subject(:rule) do
7
+ Dry::Logic::Rule::Set.new(:address, [is_string, min_size.curry(6)])
8
+ end
9
+
10
+ let(:is_string) { Dry::Logic::Rule::Value.new(:name, str?) }
11
+ let(:min_size) { Dry::Logic::Rule::Value.new(:name, min_size?) }
12
+
13
+ describe '#call' do
14
+ it 'applies its rules to the input' do
15
+ expect(rule.('Address')).to be_success
16
+ expect(rule.('Addr')).to be_failure
17
+ end
18
+ end
19
+
20
+ describe '#to_ary' do
21
+ it 'returns an array representation' do
22
+ expect(rule).to match_array([
23
+ :set, [
24
+ :address, [
25
+ [:val, [:name, [:predicate, [:str?, []]]]],
26
+ [:val, [:name, [:predicate, [:min_size?, [6]]]]]
27
+ ]
28
+ ]
29
+ ])
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,42 @@
1
+ require 'dry/logic/rule'
2
+
3
+ RSpec.describe Dry::Logic::Rule::Value do
4
+ include_context 'predicates'
5
+
6
+ let(:is_nil) { Dry::Logic::Rule::Value.new(:name, none?) }
7
+
8
+ let(:is_string) { Dry::Logic::Rule::Value.new(:name, str?) }
9
+
10
+ let(:min_size) { Dry::Logic::Rule::Value.new(:name, min_size?) }
11
+
12
+ describe '#call' do
13
+ it 'returns result of a predicate' do
14
+ expect(is_string.(1)).to be_failure
15
+ expect(is_string.('1')).to be_success
16
+ end
17
+ end
18
+
19
+ describe '#and' do
20
+ it 'returns a conjunction' do
21
+ string_and_min_size = is_string.and(min_size.curry(3))
22
+
23
+ expect(string_and_min_size.('abc')).to be_success
24
+ expect(string_and_min_size.('abcd')).to be_success
25
+
26
+ expect(string_and_min_size.(1)).to be_failure
27
+ expect(string_and_min_size.('ab')).to be_failure
28
+ end
29
+ end
30
+
31
+ describe '#or' do
32
+ it 'returns a disjunction' do
33
+ nil_or_string = is_nil.or(is_string)
34
+
35
+ expect(nil_or_string.(nil)).to be_success
36
+ expect(nil_or_string.('abcd')).to be_success
37
+
38
+ expect(nil_or_string.(true)).to be_failure
39
+ expect(nil_or_string.(1)).to be_failure
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,123 @@
1
+ require 'dry/logic/rule_compiler'
2
+
3
+ RSpec.describe Dry::Logic::RuleCompiler, '#call' do
4
+ subject(:compiler) { RuleCompiler.new(predicates) }
5
+
6
+ let(:predicates) {
7
+ { key?: predicate,
8
+ filled?: predicate,
9
+ email: val_rule.('email').curry(:filled?) }
10
+ }
11
+
12
+ let(:predicate) { double(:predicate).as_null_object }
13
+
14
+ let(:key_rule) { Rule::Key.new(:email, predicate) }
15
+ let(:not_key_rule) { Rule::Key.new(:email, predicate).negation }
16
+ let(:val_rule) { Rule::Value.new(:email, predicate) }
17
+ let(:check_rule) { Rule::Check.new(:email, predicates[:email]) }
18
+ let(:and_rule) { key_rule & val_rule }
19
+ let(:or_rule) { key_rule | val_rule }
20
+ let(:xor_rule) { key_rule ^ val_rule }
21
+ let(:set_rule) { Rule::Set.new(:email, [val_rule]) }
22
+ let(:each_rule) { Rule::Each.new(:email, val_rule) }
23
+
24
+ it 'compiles key rules' do
25
+ ast = [[:key, [:email, [:predicate, [:key?, predicate]]]]]
26
+
27
+ rules = compiler.(ast)
28
+
29
+ expect(rules).to eql([key_rule])
30
+ end
31
+
32
+ it 'compiles check rules' do
33
+ ast = [[:check, [:email, [:predicate, [:email, [:filled?]]]]]]
34
+
35
+ rules = compiler.(ast)
36
+
37
+ expect(rules).to eql([check_rule])
38
+ end
39
+
40
+
41
+ it 'compiles negated rules' do
42
+ ast = [[:not, [:key, [:email, [:predicate, [:key?, predicate]]]]]]
43
+
44
+ rules = compiler.(ast)
45
+
46
+ expect(rules).to eql([not_key_rule])
47
+ end
48
+
49
+ it 'compiles conjunction rules' do
50
+ ast = [
51
+ [
52
+ :and, [
53
+ [:key, [:email, [:predicate, [:key?, []]]]],
54
+ [:val, [:email, [:predicate, [:filled?, []]]]]
55
+ ]
56
+ ]
57
+ ]
58
+
59
+ rules = compiler.(ast)
60
+
61
+ expect(rules).to eql([and_rule])
62
+ end
63
+
64
+ it 'compiles disjunction rules' do
65
+ ast = [
66
+ [
67
+ :or, [
68
+ [:key, [:email, [:predicate, [:key?, []]]]],
69
+ [:val, [:email, [:predicate, [:filled?, []]]]]
70
+ ]
71
+ ]
72
+ ]
73
+
74
+ rules = compiler.(ast)
75
+
76
+ expect(rules).to eql([or_rule])
77
+ end
78
+
79
+ it 'compiles exclusive disjunction rules' do
80
+ ast = [
81
+ [
82
+ :xor, [
83
+ [:key, [:email, [:predicate, [:key?, []]]]],
84
+ [:val, [:email, [:predicate, [:filled?, []]]]]
85
+ ]
86
+ ]
87
+ ]
88
+
89
+ rules = compiler.(ast)
90
+
91
+ expect(rules).to eql([xor_rule])
92
+ end
93
+
94
+ it 'compiles set rules' do
95
+ ast = [
96
+ [
97
+ :set, [
98
+ :email, [
99
+ [:val, [:email, [:predicate, [:filled?, []]]]]
100
+ ]
101
+ ]
102
+ ]
103
+ ]
104
+
105
+ rules = compiler.(ast)
106
+
107
+ expect(rules).to eql([set_rule])
108
+ end
109
+
110
+ it 'compiles each rules' do
111
+ ast = [
112
+ [
113
+ :each, [
114
+ :email, [:val, [:email, [:predicate, [:filled?, []]]]]
115
+ ]
116
+ ]
117
+ ]
118
+
119
+ rules = compiler.(ast)
120
+
121
+ expect(rules).to eql([each_rule])
122
+ end
123
+ end
metadata ADDED
@@ -0,0 +1,221 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dry-logic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Piotr Solnica
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-container
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.2.6
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.2.6
33
+ - !ruby/object:Gem::Dependency
34
+ name: dry-equalizer
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.2'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.2'
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description:
90
+ email:
91
+ - piotr.solnica@gmail.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - ".gitignore"
97
+ - ".rspec"
98
+ - ".rubocop.yml"
99
+ - ".rubocop_todo.yml"
100
+ - ".travis.yml"
101
+ - CHANGELOG.md
102
+ - Gemfile
103
+ - LICENSE
104
+ - README.md
105
+ - Rakefile
106
+ - dry-logic.gemspec
107
+ - examples/basic.rb
108
+ - lib/dry-logic.rb
109
+ - lib/dry/logic.rb
110
+ - lib/dry/logic/predicate.rb
111
+ - lib/dry/logic/predicate_set.rb
112
+ - lib/dry/logic/predicates.rb
113
+ - lib/dry/logic/result.rb
114
+ - lib/dry/logic/rule.rb
115
+ - lib/dry/logic/rule/check.rb
116
+ - lib/dry/logic/rule/composite.rb
117
+ - lib/dry/logic/rule/each.rb
118
+ - lib/dry/logic/rule/group.rb
119
+ - lib/dry/logic/rule/key.rb
120
+ - lib/dry/logic/rule/set.rb
121
+ - lib/dry/logic/rule/value.rb
122
+ - lib/dry/logic/rule_compiler.rb
123
+ - lib/dry/logic/version.rb
124
+ - rakelib/rubocop.rake
125
+ - spec/shared/predicates.rb
126
+ - spec/spec_helper.rb
127
+ - spec/unit/predicate_spec.rb
128
+ - spec/unit/predicates/bool_spec.rb
129
+ - spec/unit/predicates/date_spec.rb
130
+ - spec/unit/predicates/date_time_spec.rb
131
+ - spec/unit/predicates/decimal_spec.rb
132
+ - spec/unit/predicates/empty_spec.rb
133
+ - spec/unit/predicates/eql_spec.rb
134
+ - spec/unit/predicates/exclusion_spec.rb
135
+ - spec/unit/predicates/filled_spec.rb
136
+ - spec/unit/predicates/float_spec.rb
137
+ - spec/unit/predicates/format_spec.rb
138
+ - spec/unit/predicates/gt_spec.rb
139
+ - spec/unit/predicates/gteq_spec.rb
140
+ - spec/unit/predicates/inclusion_spec.rb
141
+ - spec/unit/predicates/int_spec.rb
142
+ - spec/unit/predicates/key_spec.rb
143
+ - spec/unit/predicates/lt_spec.rb
144
+ - spec/unit/predicates/lteq_spec.rb
145
+ - spec/unit/predicates/max_size_spec.rb
146
+ - spec/unit/predicates/min_size_spec.rb
147
+ - spec/unit/predicates/none_spec.rb
148
+ - spec/unit/predicates/size_spec.rb
149
+ - spec/unit/predicates/str_spec.rb
150
+ - spec/unit/predicates/time_spec.rb
151
+ - spec/unit/rule/check_spec.rb
152
+ - spec/unit/rule/conjunction_spec.rb
153
+ - spec/unit/rule/disjunction_spec.rb
154
+ - spec/unit/rule/each_spec.rb
155
+ - spec/unit/rule/group_spec.rb
156
+ - spec/unit/rule/implication_spec.rb
157
+ - spec/unit/rule/key_spec.rb
158
+ - spec/unit/rule/set_spec.rb
159
+ - spec/unit/rule/value_spec.rb
160
+ - spec/unit/rule_compiler_spec.rb
161
+ homepage: https://github.com/dryrb/dry-logic
162
+ licenses:
163
+ - MIT
164
+ metadata: {}
165
+ post_install_message:
166
+ rdoc_options: []
167
+ require_paths:
168
+ - lib
169
+ required_ruby_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ required_rubygems_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ requirements: []
180
+ rubyforge_project:
181
+ rubygems_version: 2.4.5.1
182
+ signing_key:
183
+ specification_version: 4
184
+ summary: Predicate logic with rule composition
185
+ test_files:
186
+ - spec/shared/predicates.rb
187
+ - spec/spec_helper.rb
188
+ - spec/unit/predicate_spec.rb
189
+ - spec/unit/predicates/bool_spec.rb
190
+ - spec/unit/predicates/date_spec.rb
191
+ - spec/unit/predicates/date_time_spec.rb
192
+ - spec/unit/predicates/decimal_spec.rb
193
+ - spec/unit/predicates/empty_spec.rb
194
+ - spec/unit/predicates/eql_spec.rb
195
+ - spec/unit/predicates/exclusion_spec.rb
196
+ - spec/unit/predicates/filled_spec.rb
197
+ - spec/unit/predicates/float_spec.rb
198
+ - spec/unit/predicates/format_spec.rb
199
+ - spec/unit/predicates/gt_spec.rb
200
+ - spec/unit/predicates/gteq_spec.rb
201
+ - spec/unit/predicates/inclusion_spec.rb
202
+ - spec/unit/predicates/int_spec.rb
203
+ - spec/unit/predicates/key_spec.rb
204
+ - spec/unit/predicates/lt_spec.rb
205
+ - spec/unit/predicates/lteq_spec.rb
206
+ - spec/unit/predicates/max_size_spec.rb
207
+ - spec/unit/predicates/min_size_spec.rb
208
+ - spec/unit/predicates/none_spec.rb
209
+ - spec/unit/predicates/size_spec.rb
210
+ - spec/unit/predicates/str_spec.rb
211
+ - spec/unit/predicates/time_spec.rb
212
+ - spec/unit/rule/check_spec.rb
213
+ - spec/unit/rule/conjunction_spec.rb
214
+ - spec/unit/rule/disjunction_spec.rb
215
+ - spec/unit/rule/each_spec.rb
216
+ - spec/unit/rule/group_spec.rb
217
+ - spec/unit/rule/implication_spec.rb
218
+ - spec/unit/rule/key_spec.rb
219
+ - spec/unit/rule/set_spec.rb
220
+ - spec/unit/rule/value_spec.rb
221
+ - spec/unit/rule_compiler_spec.rb