dry-logic 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -5
- data/CHANGELOG.md +28 -0
- data/Gemfile +7 -4
- data/dry-logic.gemspec +1 -0
- data/lib/dry/logic.rb +2 -3
- data/lib/dry/logic/appliable.rb +33 -0
- data/lib/dry/logic/evaluator.rb +2 -0
- data/lib/dry/logic/operations.rb +13 -0
- data/lib/dry/logic/operations/abstract.rb +44 -0
- data/lib/dry/logic/operations/and.rb +35 -0
- data/lib/dry/logic/operations/attr.rb +17 -0
- data/lib/dry/logic/operations/binary.rb +26 -0
- data/lib/dry/logic/operations/check.rb +52 -0
- data/lib/dry/logic/operations/each.rb +32 -0
- data/lib/dry/logic/operations/implication.rb +37 -0
- data/lib/dry/logic/operations/key.rb +66 -0
- data/lib/dry/logic/operations/negation.rb +18 -0
- data/lib/dry/logic/operations/or.rb +35 -0
- data/lib/dry/logic/operations/set.rb +35 -0
- data/lib/dry/logic/operations/unary.rb +24 -0
- data/lib/dry/logic/operations/xor.rb +27 -0
- data/lib/dry/logic/operators.rb +25 -0
- data/lib/dry/logic/predicates.rb +143 -136
- data/lib/dry/logic/result.rb +76 -33
- data/lib/dry/logic/rule.rb +62 -46
- data/lib/dry/logic/rule/predicate.rb +28 -0
- data/lib/dry/logic/rule_compiler.rb +16 -17
- data/lib/dry/logic/version.rb +1 -1
- data/spec/integration/result_spec.rb +59 -0
- data/spec/integration/rule_spec.rb +53 -0
- data/spec/shared/predicates.rb +6 -0
- data/spec/shared/rule.rb +67 -0
- data/spec/spec_helper.rb +10 -3
- data/spec/support/mutant.rb +9 -0
- data/spec/unit/operations/and_spec.rb +64 -0
- data/spec/unit/operations/attr_spec.rb +27 -0
- data/spec/unit/operations/check_spec.rb +49 -0
- data/spec/unit/operations/each_spec.rb +47 -0
- data/spec/unit/operations/implication_spec.rb +30 -0
- data/spec/unit/operations/key_spec.rb +119 -0
- data/spec/unit/operations/negation_spec.rb +40 -0
- data/spec/unit/operations/or_spec.rb +73 -0
- data/spec/unit/operations/set_spec.rb +41 -0
- data/spec/unit/operations/xor_spec.rb +61 -0
- data/spec/unit/predicates_spec.rb +23 -0
- data/spec/unit/rule/predicate_spec.rb +53 -0
- data/spec/unit/rule_compiler_spec.rb +38 -38
- data/spec/unit/rule_spec.rb +94 -0
- metadata +67 -40
- data/lib/dry/logic/predicate.rb +0 -100
- data/lib/dry/logic/predicate_set.rb +0 -23
- data/lib/dry/logic/result/each.rb +0 -20
- data/lib/dry/logic/result/multi.rb +0 -14
- data/lib/dry/logic/result/named.rb +0 -17
- data/lib/dry/logic/result/set.rb +0 -10
- data/lib/dry/logic/result/value.rb +0 -17
- data/lib/dry/logic/rule/attr.rb +0 -13
- data/lib/dry/logic/rule/check.rb +0 -40
- data/lib/dry/logic/rule/composite.rb +0 -91
- data/lib/dry/logic/rule/each.rb +0 -13
- data/lib/dry/logic/rule/key.rb +0 -37
- data/lib/dry/logic/rule/negation.rb +0 -15
- data/lib/dry/logic/rule/set.rb +0 -31
- data/lib/dry/logic/rule/value.rb +0 -48
- data/spec/unit/predicate_spec.rb +0 -115
- data/spec/unit/rule/attr_spec.rb +0 -29
- data/spec/unit/rule/check_spec.rb +0 -44
- data/spec/unit/rule/conjunction_spec.rb +0 -30
- data/spec/unit/rule/disjunction_spec.rb +0 -38
- data/spec/unit/rule/each_spec.rb +0 -31
- data/spec/unit/rule/exclusive_disjunction_spec.rb +0 -19
- data/spec/unit/rule/implication_spec.rb +0 -16
- data/spec/unit/rule/key_spec.rb +0 -121
- data/spec/unit/rule/set_spec.rb +0 -30
- data/spec/unit/rule/value_spec.rb +0 -99
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65344930c4a3c0f6a085d583b71530119d1bd81a
|
4
|
+
data.tar.gz: 5ca9b9aa5819f5e8bd962b66efe47aabd865eeac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cdc353eafff80177f44585c8a82130a3f6abb1ea13d186b7da32e1bf0bafb29b2b912f543be022a8a147afe088b65a8542471865817e49ae9e3ac981051fdb1c
|
7
|
+
data.tar.gz: 8a46b78404a2edf942786c1a002be4db7f067c0a2c27190eba5caebafcec55042c76aab90fcd4a347196cec396c3c5a597298455a741c6175224d40239db309c
|
data/.travis.yml
CHANGED
@@ -5,22 +5,18 @@ bundler_args: --without console tools
|
|
5
5
|
script:
|
6
6
|
- bundle exec rake spec
|
7
7
|
rvm:
|
8
|
-
- 2.0
|
9
8
|
- 2.1
|
10
9
|
- 2.2
|
11
10
|
- 2.3.0
|
12
11
|
- rbx-2
|
13
12
|
- jruby-9000
|
14
|
-
- ruby-head
|
15
|
-
- jruby-head
|
16
13
|
env:
|
17
14
|
global:
|
18
15
|
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
19
16
|
matrix:
|
20
17
|
allow_failures:
|
21
|
-
- rvm: ruby-head
|
22
|
-
- rvm: jruby-head
|
23
18
|
- rvm: jruby-9000
|
19
|
+
- rvm: rbx-2
|
24
20
|
notifications:
|
25
21
|
email: false
|
26
22
|
webhooks:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
# v0.4.0 2016-09-21
|
2
|
+
|
3
|
+
This is a partial rewrite focused on internal clean up and major performance improvements. This is also the beginning of the work to make this library first-class rather than "just" a rule backend for dry-validation and dry-types.
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
* `Rule#[]` which applies a rule and always returns `true` or `false` (solnic)
|
8
|
+
* `Rule#bind` which returns a rule with its predicate bound to a given object (solnic)
|
9
|
+
* `Rule#eval_args` which evaluates unbound-methods-args in the context of a given object (solnic)
|
10
|
+
* `Logic.Rule` builder function (solnic)
|
11
|
+
* Nice `#inspect` on rules and operation objects (solnic)
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
* [BRAEKING] New result API (solnic)
|
16
|
+
* [BREAKING] `Predicate` is now `Rule::Predicate` (solnic)
|
17
|
+
* [BREAKING] `Rule::Conjunction` is now `Operation::And` (solnic)
|
18
|
+
* [BREAKING] `Rule::Disjunction` is now `Operation::Or` (solnic)
|
19
|
+
* [BREAKING] `Rule::ExlusiveDisjunction` is now `Operation::Xor` (solnic)
|
20
|
+
* [BREAKING] `Rule::Implication` is now `Operation::Implication` (solnic)
|
21
|
+
* [BREAKING] `Rule::Set` is now `Operation::Set` (solnic)
|
22
|
+
* [BREAKING] `Rule::Each` is now `Operation::Each` (solnic)
|
23
|
+
* [BREAKING] `Rule.new` accepts a predicate function as its first arg now (solnic)
|
24
|
+
* [BREAKING] `Rule#name` is now `Rule#id` (solnic)
|
25
|
+
* `Rule#parameters` is public now (solnic)
|
26
|
+
|
27
|
+
[Compare v0.3.0...v0.4.0](https://github.com/dryrb/dry-logic/compare/v0.3.0...v0.4.0)
|
28
|
+
|
1
29
|
# v0.3.0 2016-07-01
|
2
30
|
|
3
31
|
### Added
|
data/Gemfile
CHANGED
@@ -3,12 +3,15 @@ source 'https://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
group :test do
|
6
|
-
gem 'codeclimate-test-reporter', platform: :
|
6
|
+
gem 'codeclimate-test-reporter', platform: :mri
|
7
7
|
end
|
8
8
|
|
9
9
|
group :tools do
|
10
|
-
gem 'guard'
|
11
|
-
gem 'guard-rspec'
|
12
|
-
gem 'guard-rubocop'
|
13
10
|
gem 'byebug', platform: :mri
|
11
|
+
gem 'simplecov', platforms: :mri
|
12
|
+
|
13
|
+
unless ENV['TRAVIS']
|
14
|
+
gem 'mutant', github: 'mbj/mutant'
|
15
|
+
gem 'mutant-rspec', github: 'mbj/mutant'
|
16
|
+
end
|
14
17
|
end
|
data/dry-logic.gemspec
CHANGED
@@ -15,6 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
16
|
spec.require_paths = ['lib']
|
17
17
|
|
18
|
+
spec.add_runtime_dependency 'dry-core', '~> 0.1'
|
18
19
|
spec.add_runtime_dependency 'dry-container', '~> 0.2', '>= 0.2.6'
|
19
20
|
spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
|
20
21
|
|
data/lib/dry/logic.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'dry-equalizer'
|
2
|
-
|
3
1
|
# A collection of micro-libraries, each intended to encapsulate
|
4
2
|
# a common task in Ruby
|
5
3
|
module Dry
|
@@ -7,4 +5,5 @@ module Dry
|
|
7
5
|
end
|
8
6
|
end
|
9
7
|
|
10
|
-
require 'dry/logic/rule'
|
8
|
+
require 'dry/logic/rule/predicate'
|
9
|
+
require 'dry/logic/operations'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Dry
|
2
|
+
module Logic
|
3
|
+
module Appliable
|
4
|
+
def id
|
5
|
+
options[:id]
|
6
|
+
end
|
7
|
+
|
8
|
+
def result
|
9
|
+
options[:result]
|
10
|
+
end
|
11
|
+
|
12
|
+
def applied?
|
13
|
+
!result.nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
def success?
|
17
|
+
result.equal?(true)
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure?
|
21
|
+
!success?
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_ast
|
25
|
+
if applied? && id
|
26
|
+
[success? ? :success : :failure, [id, ast]]
|
27
|
+
else
|
28
|
+
ast
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/dry/logic/evaluator.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'dry/logic/operations/and'
|
2
|
+
require 'dry/logic/operations/or'
|
3
|
+
require 'dry/logic/operations/xor'
|
4
|
+
require 'dry/logic/operations/implication'
|
5
|
+
require 'dry/logic/operations/negation'
|
6
|
+
|
7
|
+
require 'dry/logic/operations/key'
|
8
|
+
require 'dry/logic/operations/attr'
|
9
|
+
require 'dry/logic/operations/each'
|
10
|
+
require 'dry/logic/operations/set'
|
11
|
+
require 'dry/logic/operations/check'
|
12
|
+
|
13
|
+
require 'dry/logic/operators'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'dry/core/constants'
|
2
|
+
require 'dry/equalizer'
|
3
|
+
require 'dry/logic/operators'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Logic
|
7
|
+
module Operations
|
8
|
+
class Abstract
|
9
|
+
include Core::Constants
|
10
|
+
include Dry::Equalizer(:rules, :options)
|
11
|
+
include Operators
|
12
|
+
|
13
|
+
attr_reader :rules
|
14
|
+
|
15
|
+
attr_reader :options
|
16
|
+
|
17
|
+
def initialize(*rules, **options)
|
18
|
+
@rules = rules
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def id
|
23
|
+
options[:id]
|
24
|
+
end
|
25
|
+
|
26
|
+
def curry(*args)
|
27
|
+
new(rules.map { |rule| rule.curry(*args) }, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def new(rules, **new_options)
|
31
|
+
self.class.new(*rules, options.merge(new_options))
|
32
|
+
end
|
33
|
+
|
34
|
+
def with(new_options)
|
35
|
+
new(rules, options.merge(new_options))
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_ast
|
39
|
+
ast
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'dry/logic/operations/binary'
|
2
|
+
require 'dry/logic/result'
|
3
|
+
|
4
|
+
module Dry
|
5
|
+
module Logic
|
6
|
+
module Operations
|
7
|
+
class And < Binary
|
8
|
+
def type
|
9
|
+
:and
|
10
|
+
end
|
11
|
+
alias_method :operator, :type
|
12
|
+
|
13
|
+
def call(input)
|
14
|
+
left_result = left.(input)
|
15
|
+
|
16
|
+
if left_result.success?
|
17
|
+
right_result = right.(input)
|
18
|
+
|
19
|
+
if right_result.success?
|
20
|
+
Result::SUCCESS
|
21
|
+
else
|
22
|
+
Result.new(false, id) { right_result.ast(input) }
|
23
|
+
end
|
24
|
+
else
|
25
|
+
Result.new(false, id) { [type, [left_result.to_ast, [:hint, right.ast(input)]]] }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](input)
|
30
|
+
left[input] && right[input]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'dry/logic/operations/abstract'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Logic
|
5
|
+
module Operations
|
6
|
+
class Binary < Abstract
|
7
|
+
attr_reader :left
|
8
|
+
|
9
|
+
attr_reader :right
|
10
|
+
|
11
|
+
def initialize(*rules, **options)
|
12
|
+
super
|
13
|
+
@left, @right = rules
|
14
|
+
end
|
15
|
+
|
16
|
+
def ast(input = Undefined)
|
17
|
+
[type, [left.ast(input), right.ast(input)]]
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"#{left} #{operator.to_s.upcase} #{right}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'dry/logic/operations/unary'
|
2
|
+
require 'dry/logic/evaluator'
|
3
|
+
require 'dry/logic/result'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Logic
|
7
|
+
module Operations
|
8
|
+
class Check < Unary
|
9
|
+
attr_reader :evaluator
|
10
|
+
|
11
|
+
def self.new(rule, **options)
|
12
|
+
if options[:evaluator]
|
13
|
+
super(rule, options)
|
14
|
+
else
|
15
|
+
keys = options.fetch(:keys)
|
16
|
+
evaluator = Evaluator::Set.new(keys)
|
17
|
+
|
18
|
+
super(rule, options.merge(evaluator: evaluator))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(*rules, **options)
|
23
|
+
super
|
24
|
+
@evaluator = options[:evaluator]
|
25
|
+
end
|
26
|
+
|
27
|
+
def type
|
28
|
+
:check
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(input)
|
32
|
+
*head, tail = evaluator[input]
|
33
|
+
result = rule.curry(*head).(tail)
|
34
|
+
|
35
|
+
if result.success?
|
36
|
+
Result::SUCCESS
|
37
|
+
else
|
38
|
+
Result.new(false, id) { [type, [options[:keys], result.to_ast]] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def [](input)
|
43
|
+
rule[*evaluator[input].reverse]
|
44
|
+
end
|
45
|
+
|
46
|
+
def ast(input = Undefined)
|
47
|
+
[type, [options[:keys], rule.ast(input)]]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'dry/logic/operations/unary'
|
2
|
+
require 'dry/logic/result'
|
3
|
+
|
4
|
+
module Dry
|
5
|
+
module Logic
|
6
|
+
module Operations
|
7
|
+
class Each < Unary
|
8
|
+
def type
|
9
|
+
:each
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(input)
|
13
|
+
results = input.map { |element| rule.(element) }
|
14
|
+
success = results.all?(&:success?)
|
15
|
+
|
16
|
+
Result.new(success, id) do
|
17
|
+
failures = results
|
18
|
+
.map
|
19
|
+
.with_index { |result, idx| [:key, [idx, result.ast(input[idx])]] if result.failure? }
|
20
|
+
.compact
|
21
|
+
|
22
|
+
[:set, failures]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](arr)
|
27
|
+
arr.map { |input| rule[input] }.all?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'dry/logic/operations/binary'
|
2
|
+
require 'dry/logic/result'
|
3
|
+
|
4
|
+
module Dry
|
5
|
+
module Logic
|
6
|
+
module Operations
|
7
|
+
class Implication < Binary
|
8
|
+
def type
|
9
|
+
:implication
|
10
|
+
end
|
11
|
+
|
12
|
+
def operator
|
13
|
+
:then
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(input)
|
17
|
+
left_result = left.(input)
|
18
|
+
|
19
|
+
if left_result.success?
|
20
|
+
right_result = right.(input)
|
21
|
+
Result.new(right_result.success?, id) { right_result.to_ast }
|
22
|
+
else
|
23
|
+
Result::SUCCESS
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](input)
|
28
|
+
if left[input]
|
29
|
+
right[input]
|
30
|
+
else
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'dry/logic/operations/unary'
|
2
|
+
require 'dry/logic/evaluator'
|
3
|
+
require 'dry/logic/result'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Logic
|
7
|
+
module Operations
|
8
|
+
class Key < Unary
|
9
|
+
attr_reader :evaluator
|
10
|
+
|
11
|
+
attr_reader :path
|
12
|
+
|
13
|
+
def self.new(rules, options)
|
14
|
+
if options[:evaluator]
|
15
|
+
super
|
16
|
+
else
|
17
|
+
name = options.fetch(:name)
|
18
|
+
eval = options.fetch(:evaluator, evaluator(name))
|
19
|
+
super(rules, options.merge(evaluator: eval, path: name))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.evaluator(name)
|
24
|
+
Evaluator::Key.new(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(*rules, **options)
|
28
|
+
super
|
29
|
+
@evaluator = options[:evaluator]
|
30
|
+
@path = options[:path]
|
31
|
+
end
|
32
|
+
|
33
|
+
def type
|
34
|
+
:key
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(hash)
|
38
|
+
input = evaluator[hash]
|
39
|
+
result = rule.(input)
|
40
|
+
|
41
|
+
if result.success?
|
42
|
+
Result::SUCCESS
|
43
|
+
else
|
44
|
+
Result.new(false, path) { [type, [path, result.to_ast]] }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](hash)
|
49
|
+
rule[evaluator[hash]]
|
50
|
+
end
|
51
|
+
|
52
|
+
def ast(input = nil)
|
53
|
+
if input
|
54
|
+
[type, [path, rule.ast(evaluator[input])]]
|
55
|
+
else
|
56
|
+
[type, [path, rule.ast]]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
"#{type}[#{path}](#{rule})"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|