dry-logic 1.0.5 → 1.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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +122 -26
  3. data/LICENSE +1 -1
  4. data/README.md +12 -14
  5. data/dry-logic.gemspec +26 -20
  6. data/lib/dry-logic.rb +1 -1
  7. data/lib/dry/logic.rb +2 -2
  8. data/lib/dry/logic/evaluator.rb +1 -1
  9. data/lib/dry/logic/operations.rb +11 -11
  10. data/lib/dry/logic/operations/abstract.rb +3 -3
  11. data/lib/dry/logic/operations/and.rb +3 -3
  12. data/lib/dry/logic/operations/attr.rb +1 -1
  13. data/lib/dry/logic/operations/binary.rb +4 -3
  14. data/lib/dry/logic/operations/check.rb +4 -4
  15. data/lib/dry/logic/operations/each.rb +2 -2
  16. data/lib/dry/logic/operations/implication.rb +2 -2
  17. data/lib/dry/logic/operations/key.rb +3 -3
  18. data/lib/dry/logic/operations/negation.rb +2 -2
  19. data/lib/dry/logic/operations/or.rb +2 -2
  20. data/lib/dry/logic/operations/set.rb +3 -3
  21. data/lib/dry/logic/operations/unary.rb +1 -1
  22. data/lib/dry/logic/operations/xor.rb +2 -2
  23. data/lib/dry/logic/operators.rb +4 -4
  24. data/lib/dry/logic/predicates.rb +32 -7
  25. data/lib/dry/logic/result.rb +2 -2
  26. data/lib/dry/logic/rule.rb +8 -8
  27. data/lib/dry/logic/rule/interface.rb +32 -37
  28. data/lib/dry/logic/rule/predicate.rb +3 -3
  29. data/lib/dry/logic/rule_compiler.rb +3 -3
  30. data/lib/dry/logic/version.rb +1 -1
  31. metadata +17 -166
  32. data/.codeclimate.yml +0 -12
  33. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
  34. data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
  35. data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
  36. data/.github/workflows/ci.yml +0 -70
  37. data/.github/workflows/docsite.yml +0 -34
  38. data/.github/workflows/sync_configs.yml +0 -34
  39. data/.gitignore +0 -9
  40. data/.rspec +0 -4
  41. data/.rubocop.yml +0 -89
  42. data/CODE_OF_CONDUCT.md +0 -13
  43. data/CONTRIBUTING.md +0 -29
  44. data/Gemfile +0 -16
  45. data/Rakefile +0 -14
  46. data/benchmarks/rule_application.rb +0 -30
  47. data/benchmarks/setup.rb +0 -13
  48. data/bin/console +0 -11
  49. data/docsite/source/index.html.md +0 -54
  50. data/docsite/source/operations.html.md +0 -62
  51. data/docsite/source/predicates.html.md +0 -102
  52. data/examples/basic.rb +0 -16
  53. data/spec/integration/result_spec.rb +0 -61
  54. data/spec/integration/rule_spec.rb +0 -55
  55. data/spec/shared/predicates.rb +0 -59
  56. data/spec/shared/rule.rb +0 -74
  57. data/spec/spec_helper.rb +0 -30
  58. data/spec/support/mutant.rb +0 -11
  59. data/spec/unit/operations/and_spec.rb +0 -70
  60. data/spec/unit/operations/attr_spec.rb +0 -29
  61. data/spec/unit/operations/check_spec.rb +0 -51
  62. data/spec/unit/operations/each_spec.rb +0 -49
  63. data/spec/unit/operations/implication_spec.rb +0 -32
  64. data/spec/unit/operations/key_spec.rb +0 -135
  65. data/spec/unit/operations/negation_spec.rb +0 -51
  66. data/spec/unit/operations/or_spec.rb +0 -75
  67. data/spec/unit/operations/set_spec.rb +0 -43
  68. data/spec/unit/operations/xor_spec.rb +0 -63
  69. data/spec/unit/predicates/array_spec.rb +0 -43
  70. data/spec/unit/predicates/attr_spec.rb +0 -31
  71. data/spec/unit/predicates/bool_spec.rb +0 -36
  72. data/spec/unit/predicates/bytesize_spec.rb +0 -48
  73. data/spec/unit/predicates/case_spec.rb +0 -35
  74. data/spec/unit/predicates/date_spec.rb +0 -33
  75. data/spec/unit/predicates/date_time_spec.rb +0 -33
  76. data/spec/unit/predicates/decimal_spec.rb +0 -34
  77. data/spec/unit/predicates/empty_spec.rb +0 -40
  78. data/spec/unit/predicates/eql_spec.rb +0 -23
  79. data/spec/unit/predicates/even_spec.rb +0 -33
  80. data/spec/unit/predicates/excluded_from_spec.rb +0 -37
  81. data/spec/unit/predicates/excludes_spec.rb +0 -58
  82. data/spec/unit/predicates/false_spec.rb +0 -37
  83. data/spec/unit/predicates/filled_spec.rb +0 -40
  84. data/spec/unit/predicates/float_spec.rb +0 -33
  85. data/spec/unit/predicates/format_spec.rb +0 -31
  86. data/spec/unit/predicates/gt_spec.rb +0 -42
  87. data/spec/unit/predicates/gteq_spec.rb +0 -42
  88. data/spec/unit/predicates/hash_spec.rb +0 -40
  89. data/spec/unit/predicates/included_in_spec.rb +0 -37
  90. data/spec/unit/predicates/includes_spec.rb +0 -24
  91. data/spec/unit/predicates/int_spec.rb +0 -36
  92. data/spec/unit/predicates/key_spec.rb +0 -31
  93. data/spec/unit/predicates/lt_spec.rb +0 -42
  94. data/spec/unit/predicates/lteq_spec.rb +0 -42
  95. data/spec/unit/predicates/max_bytesize_spec.rb +0 -39
  96. data/spec/unit/predicates/max_size_spec.rb +0 -51
  97. data/spec/unit/predicates/min_bytesize_spec.rb +0 -39
  98. data/spec/unit/predicates/min_size_spec.rb +0 -51
  99. data/spec/unit/predicates/none_spec.rb +0 -30
  100. data/spec/unit/predicates/not_eql_spec.rb +0 -23
  101. data/spec/unit/predicates/number_spec.rb +0 -39
  102. data/spec/unit/predicates/odd_spec.rb +0 -33
  103. data/spec/unit/predicates/respond_to_spec.rb +0 -31
  104. data/spec/unit/predicates/size_spec.rb +0 -57
  105. data/spec/unit/predicates/str_spec.rb +0 -34
  106. data/spec/unit/predicates/time_spec.rb +0 -33
  107. data/spec/unit/predicates/true_spec.rb +0 -37
  108. data/spec/unit/predicates/type_spec.rb +0 -37
  109. data/spec/unit/predicates/uuid_v4_spec.rb +0 -29
  110. data/spec/unit/predicates_spec.rb +0 -25
  111. data/spec/unit/rule/predicate_spec.rb +0 -55
  112. data/spec/unit/rule_compiler_spec.rb +0 -129
  113. data/spec/unit/rule_spec.rb +0 -213
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Operations::Each do
4
- subject(:operation) { Operations::Each.new(is_string) }
5
-
6
- include_context 'predicates'
7
-
8
- let(:is_string) { Rule::Predicate.build(str?) }
9
-
10
- describe '#call' do
11
- it 'applies its rules to all elements in the input' do
12
- expect(operation.(['Address'])).to be_success
13
-
14
- expect(operation.([nil, 'Address'])).to be_failure
15
- expect(operation.([:Address, 'Address'])).to be_failure
16
- end
17
- end
18
-
19
- describe '#to_ast' do
20
- it 'returns ast' do
21
- expect(operation.to_ast).to eql([:each, [:predicate, [:str?, [[:input, Undefined]]]]])
22
- end
23
-
24
- it 'returns result ast' do
25
- expect(operation.([nil, 12, nil]).to_ast).to eql(
26
- [:set, [
27
- [:key, [0, [:predicate, [:str?, [[:input, nil]]]]]],
28
- [:key, [1, [:predicate, [:str?, [[:input, 12]]]]]],
29
- [:key, [2, [:predicate, [:str?, [[:input, nil]]]]]]
30
- ]]
31
- )
32
- end
33
-
34
- it 'returns failure result ast' do
35
- expect(operation.with(id: :tags).([nil, 'red', 12]).to_ast).to eql(
36
- [:failure, [:tags, [:set, [
37
- [:key, [0, [:predicate, [:str?, [[:input, nil]]]]]],
38
- [:key, [2, [:predicate, [:str?, [[:input, 12]]]]]]
39
- ]]]]
40
- )
41
- end
42
- end
43
-
44
- describe '#to_s' do
45
- it 'returns string representation' do
46
- expect(operation.to_s).to eql('each(str?)')
47
- end
48
- end
49
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Operations::Implication do
4
- subject(:operation) { Operations::Implication.new(left, right) }
5
-
6
- include_context 'predicates'
7
-
8
- let(:left) { Rule::Predicate.build(int?) }
9
- let(:right) { Rule::Predicate.build(gt?).curry(18) }
10
-
11
- describe '#call' do
12
- it 'calls left and right' do
13
- expect(operation.('19')).to be_success
14
- expect(operation.(19)).to be_success
15
- expect(operation.(18)).to be_failure
16
- end
17
- end
18
-
19
- describe '#to_ast' do
20
- it 'returns ast' do
21
- expect(operation.to_ast).to eql(
22
- [:implication, [[:predicate, [:int?, [[:input, Undefined]]]], [:predicate, [:gt?, [[:num, 18], [:input, Undefined]]]]]]
23
- )
24
- end
25
- end
26
-
27
- describe '#to_s' do
28
- it 'returns string representation' do
29
- expect(operation.to_s).to eql('int? THEN gt?(18)')
30
- end
31
- end
32
- end
@@ -1,135 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Operations::Key do
4
- subject(:operation) { Operations::Key.new(predicate, name: :user) }
5
-
6
- include_context 'predicates'
7
-
8
- let(:predicate) do
9
- Rule::Predicate.build(key?).curry(:age)
10
- end
11
-
12
- describe '#call' do
13
- context 'with a plain predicate' do
14
- it 'returns a success for valid input' do
15
- expect(operation.(user: { age: 18 })).to be_success
16
- end
17
-
18
- it 'returns a failure for invalid input' do
19
- result = operation.(user: {})
20
-
21
- expect(result).to be_failure
22
-
23
- expect(result.to_ast).to eql(
24
- [:failure, [:user, [:key, [:user,
25
- [:predicate, [:key?, [[:name, :age], [:input, {}]]]]
26
- ]]]]
27
- )
28
- end
29
- end
30
-
31
- context 'with a set rule as predicate' do
32
- subject(:operation) do
33
- Operations::Key.new(predicate, name: :address)
34
- end
35
-
36
- let(:predicate) do
37
- Operations::Set.new(Rule::Predicate.build(key?).curry(:city), Rule::Predicate.build(key?).curry(:zipcode))
38
- end
39
-
40
- it 'applies set rule to the value that passes' do
41
- result = operation.(address: { city: 'NYC', zipcode: '123' })
42
-
43
- expect(result).to be_success
44
- end
45
-
46
- it 'applies set rule to the value that fails' do
47
- result = operation.(address: { city: 'NYC' })
48
-
49
- expect(result).to be_failure
50
-
51
- expect(result.to_ast).to eql(
52
- [:failure, [:address, [:key, [:address, [:set, [
53
- [:predicate, [:key?, [[:name, :zipcode], [:input, { city: 'NYC' }]]]]
54
- ]]]]]]
55
- )
56
- end
57
- end
58
-
59
- context 'with an each rule as predicate' do
60
- subject(:operation) do
61
- Operations::Key.new(predicate, name: :nums)
62
- end
63
-
64
- let(:predicate) do
65
- Operations::Each.new(Rule::Predicate.build(str?))
66
- end
67
-
68
- it 'applies each rule to the value that passses' do
69
- result = operation.(nums: %w(1 2 3))
70
-
71
- expect(result).to be_success
72
- end
73
-
74
- it 'applies each rule to the value that fails' do
75
- failure = operation.(nums: [1, '3', 3])
76
-
77
- expect(failure).to be_failure
78
-
79
- expect(failure.to_ast).to eql(
80
- [:failure, [:nums, [:key, [:nums, [:set, [
81
- [:key, [0, [:predicate, [:str?, [[:input, 1]]]]]],
82
- [:key, [2, [:predicate, [:str?, [[:input, 3]]]]]]
83
- ]]]]]]
84
- )
85
- end
86
- end
87
- end
88
-
89
- describe '#to_ast' do
90
- it 'returns ast' do
91
- expect(operation.to_ast).to eql(
92
- [:key, [:user, [:predicate, [:key?, [[:name, :age], [:input, Undefined]]]]]]
93
- )
94
- end
95
- end
96
-
97
- describe '#ast' do
98
- it 'returns ast without the input' do
99
- expect(operation.ast).to eql(
100
- [:key, [:user, [:predicate, [:key?, [[:name, :age], [:input, Undefined]]]]]]
101
- )
102
- end
103
-
104
- it 'returns ast with the input' do
105
- expect(operation.ast(user: 'jane')).to eql(
106
- [:key, [:user, [:predicate, [:key?, [[:name, :age], [:input, 'jane']]]]]]
107
- )
108
- end
109
- end
110
-
111
- describe '#and' do
112
- subject(:operation) do
113
- Operations::Key.new(Rule::Predicate.build(str?), name: [:user, :name])
114
- end
115
-
116
- let(:other) do
117
- Operations::Key.new(Rule::Predicate.build(filled?), name: [:user, :name])
118
- end
119
-
120
- it 'returns and rule where value is passed to the right' do
121
- present_and_string = operation.and(other)
122
-
123
- expect(present_and_string.(user: { name: 'Jane' })).to be_success
124
-
125
- expect(present_and_string.(user: {})).to be_failure
126
- expect(present_and_string.(user: { name: 1 })).to be_failure
127
- end
128
- end
129
-
130
- describe '#to_s' do
131
- it 'returns string representation' do
132
- expect(operation.to_s).to eql('key[user](key?(:age))')
133
- end
134
- end
135
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Operations::Negation do
4
- subject(:operation) { Operations::Negation.new(is_int) }
5
-
6
- include_context 'predicates'
7
-
8
- let(:is_int) { Rule::Predicate.build(int?) }
9
-
10
- describe '#call' do
11
- it 'negates its rule' do
12
- expect(operation.('19')).to be_success
13
- expect(operation.(17)).to be_failure
14
- end
15
-
16
- context 'double negation' do
17
- subject(:double_negation) { Operations::Negation.new(operation) }
18
-
19
- it 'works as rule' do
20
- expect(double_negation.('19')).to be_failure
21
- expect(double_negation.(17)).to be_success
22
- end
23
- end
24
- end
25
-
26
- describe '#to_ast' do
27
- it 'returns ast' do
28
- expect(operation.to_ast).to eql(
29
- [:not, [:predicate, [:int?, [[:input, Undefined]]]]]
30
- )
31
- end
32
-
33
- it 'returns result ast' do
34
- expect(operation.(17).to_ast).to eql(
35
- [:not, [:predicate, [:int?, [[:input, 17]]]]]
36
- )
37
- end
38
-
39
- it 'returns result ast with an :id' do
40
- expect(operation.with(id: :age).(17).to_ast).to eql(
41
- [:failure, [:age, [:not, [:predicate, [:int?, [[:input, 17]]]]]]]
42
- )
43
- end
44
- end
45
-
46
- describe '#to_s' do
47
- it 'returns string representation' do
48
- expect(operation.to_s).to eql('not(int?)')
49
- end
50
- end
51
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Operations::Or do
4
- subject(:operation) { Operations::Or.new(left, right) }
5
-
6
- include_context 'predicates'
7
-
8
- let(:left) { Rule::Predicate.build(nil?) }
9
- let(:right) { Rule::Predicate.build(gt?).curry(18) }
10
-
11
- let(:other) do
12
- Rule::Predicate.build(int?) & Rule::Predicate.build(lt?).curry(14)
13
- end
14
-
15
- describe '#call' do
16
- it 'calls left and right' do
17
- expect(operation.(nil)).to be_success
18
- expect(operation.(19)).to be_success
19
- expect(operation.(18)).to be_failure
20
- end
21
- end
22
-
23
- describe '#to_ast' do
24
- it 'returns ast' do
25
- expect(operation.to_ast).to eql(
26
- [:or, [
27
- [:predicate, [:nil?, [[:input, Undefined]]]],
28
- [:predicate, [:gt?, [[:num, 18], [:input, Undefined]]]]]
29
- ]
30
- )
31
- end
32
-
33
- it 'returns result ast' do
34
- expect(operation.(17).to_ast).to eql(
35
- [:or, [
36
- [:predicate, [:nil?, [[:input, 17]]]],
37
- [:predicate, [:gt?, [[:num, 18], [:input, 17]]]]]
38
- ]
39
- )
40
- end
41
-
42
- it 'returns failure result ast' do
43
- expect(operation.with(id: :age).(17).to_ast).to eql(
44
- [:failure, [:age, [:or, [
45
- [:predicate, [:nil?, [[:input, 17]]]],
46
- [:predicate, [:gt?, [[:num, 18], [:input, 17]]]]]
47
- ]]]
48
- )
49
- end
50
- end
51
-
52
- describe '#and' do
53
- it 'creates and with the other' do
54
- expect(operation.and(other).(nil)).to be_failure
55
- expect(operation.and(other).(19)).to be_failure
56
- expect(operation.and(other).(13)).to be_failure
57
- expect(operation.and(other).(14)).to be_failure
58
- end
59
- end
60
-
61
- describe '#or' do
62
- it 'creates or with the other' do
63
- expect(operation.or(other).(nil)).to be_success
64
- expect(operation.or(other).(19)).to be_success
65
- expect(operation.or(other).(13)).to be_success
66
- expect(operation.or(other).(14)).to be_failure
67
- end
68
- end
69
-
70
- describe '#to_s' do
71
- it 'returns string representation' do
72
- expect(operation.to_s).to eql('nil? OR gt?(18)')
73
- end
74
- end
75
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Operations::Set do
4
- subject(:operation) { Operations::Set.new(is_int, gt_18) }
5
-
6
- include_context 'predicates'
7
-
8
- let(:is_int) { Rule::Predicate.build(int?) }
9
- let(:gt_18) { Rule::Predicate.build(gt?, args: [18]) }
10
-
11
- describe '#call' do
12
- it 'applies all its rules to the input' do
13
- expect(operation.(19)).to be_success
14
- expect(operation.(17)).to be_failure
15
- end
16
- end
17
-
18
- describe '#to_ast' do
19
- it 'returns ast' do
20
- expect(operation.to_ast).to eql(
21
- [:set, [[:predicate, [:int?, [[:input, Undefined]]]], [:predicate, [:gt?, [[:num, 18], [:input, Undefined]]]]]]
22
- )
23
- end
24
-
25
- it 'returns result ast' do
26
- expect(operation.(17).to_ast).to eql(
27
- [:set, [[:predicate, [:gt?, [[:num, 18], [:input, 17]]]]]]
28
- )
29
- end
30
-
31
- it 'returns result ast with an :id' do
32
- expect(operation.with(id: :age).(17).to_ast).to eql(
33
- [:failure, [:age, [:set, [[:predicate, [:gt?, [[:num, 18], [:input, 17]]]]]]]]
34
- )
35
- end
36
- end
37
-
38
- describe '#to_s' do
39
- it 'returns string representation' do
40
- expect(operation.to_s).to eql('set(int?, gt?(18))')
41
- end
42
- end
43
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Operations::Xor do
4
- subject(:operation) { Operations::Xor.new(left, right) }
5
-
6
- include_context 'predicates'
7
-
8
- let(:left) { Rule::Predicate.build(array?) }
9
- let(:right) { Rule::Predicate.build(empty?) }
10
-
11
- let(:other) do
12
- Rule::Predicate.build(str?)
13
- end
14
-
15
- describe '#call' do
16
- it 'calls left and right' do
17
- expect(operation.(nil)).to be_success
18
- expect(operation.('')).to be_success
19
- expect(operation.([])).to be_failure
20
- end
21
- end
22
-
23
- describe '#to_ast' do
24
- it 'returns ast' do
25
- expect(operation.to_ast).to eql(
26
- [:xor, [[:predicate, [:array?, [[:input, Undefined]]]], [:predicate, [:empty?, [[:input, Undefined]]]]]]
27
- )
28
- end
29
-
30
- it 'returns result ast' do
31
- expect(operation.([]).to_ast).to eql(
32
- [:xor, [[:predicate, [:array?, [[:input, []]]]], [:predicate, [:empty?, [[:input, []]]]]]]
33
- )
34
- end
35
-
36
- it 'returns failure result ast' do
37
- expect(operation.with(id: :name).([]).to_ast).to eql(
38
- [:failure, [:name, [:xor, [[:predicate, [:array?, [[:input, []]]]], [:predicate, [:empty?, [[:input, []]]]]]]]]
39
- )
40
- end
41
- end
42
-
43
- describe '#and' do
44
- it 'creates conjunction with the other' do
45
- expect(operation.and(other).(nil)).to be_failure
46
- expect(operation.and(other).(19)).to be_failure
47
- expect(operation.and(other).('')).to be_success
48
- end
49
- end
50
-
51
- describe '#or' do
52
- it 'creates disjunction with the other' do
53
- expect(operation.or(other).([])).to be_failure
54
- expect(operation.or(other).('')).to be_success
55
- end
56
- end
57
-
58
- describe '#to_s' do
59
- it 'returns string representation' do
60
- expect(operation.to_s).to eql('array? XOR empty?')
61
- end
62
- end
63
- end