dry-logic 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -5
  3. data/CHANGELOG.md +28 -0
  4. data/Gemfile +7 -4
  5. data/dry-logic.gemspec +1 -0
  6. data/lib/dry/logic.rb +2 -3
  7. data/lib/dry/logic/appliable.rb +33 -0
  8. data/lib/dry/logic/evaluator.rb +2 -0
  9. data/lib/dry/logic/operations.rb +13 -0
  10. data/lib/dry/logic/operations/abstract.rb +44 -0
  11. data/lib/dry/logic/operations/and.rb +35 -0
  12. data/lib/dry/logic/operations/attr.rb +17 -0
  13. data/lib/dry/logic/operations/binary.rb +26 -0
  14. data/lib/dry/logic/operations/check.rb +52 -0
  15. data/lib/dry/logic/operations/each.rb +32 -0
  16. data/lib/dry/logic/operations/implication.rb +37 -0
  17. data/lib/dry/logic/operations/key.rb +66 -0
  18. data/lib/dry/logic/operations/negation.rb +18 -0
  19. data/lib/dry/logic/operations/or.rb +35 -0
  20. data/lib/dry/logic/operations/set.rb +35 -0
  21. data/lib/dry/logic/operations/unary.rb +24 -0
  22. data/lib/dry/logic/operations/xor.rb +27 -0
  23. data/lib/dry/logic/operators.rb +25 -0
  24. data/lib/dry/logic/predicates.rb +143 -136
  25. data/lib/dry/logic/result.rb +76 -33
  26. data/lib/dry/logic/rule.rb +62 -46
  27. data/lib/dry/logic/rule/predicate.rb +28 -0
  28. data/lib/dry/logic/rule_compiler.rb +16 -17
  29. data/lib/dry/logic/version.rb +1 -1
  30. data/spec/integration/result_spec.rb +59 -0
  31. data/spec/integration/rule_spec.rb +53 -0
  32. data/spec/shared/predicates.rb +6 -0
  33. data/spec/shared/rule.rb +67 -0
  34. data/spec/spec_helper.rb +10 -3
  35. data/spec/support/mutant.rb +9 -0
  36. data/spec/unit/operations/and_spec.rb +64 -0
  37. data/spec/unit/operations/attr_spec.rb +27 -0
  38. data/spec/unit/operations/check_spec.rb +49 -0
  39. data/spec/unit/operations/each_spec.rb +47 -0
  40. data/spec/unit/operations/implication_spec.rb +30 -0
  41. data/spec/unit/operations/key_spec.rb +119 -0
  42. data/spec/unit/operations/negation_spec.rb +40 -0
  43. data/spec/unit/operations/or_spec.rb +73 -0
  44. data/spec/unit/operations/set_spec.rb +41 -0
  45. data/spec/unit/operations/xor_spec.rb +61 -0
  46. data/spec/unit/predicates_spec.rb +23 -0
  47. data/spec/unit/rule/predicate_spec.rb +53 -0
  48. data/spec/unit/rule_compiler_spec.rb +38 -38
  49. data/spec/unit/rule_spec.rb +94 -0
  50. metadata +67 -40
  51. data/lib/dry/logic/predicate.rb +0 -100
  52. data/lib/dry/logic/predicate_set.rb +0 -23
  53. data/lib/dry/logic/result/each.rb +0 -20
  54. data/lib/dry/logic/result/multi.rb +0 -14
  55. data/lib/dry/logic/result/named.rb +0 -17
  56. data/lib/dry/logic/result/set.rb +0 -10
  57. data/lib/dry/logic/result/value.rb +0 -17
  58. data/lib/dry/logic/rule/attr.rb +0 -13
  59. data/lib/dry/logic/rule/check.rb +0 -40
  60. data/lib/dry/logic/rule/composite.rb +0 -91
  61. data/lib/dry/logic/rule/each.rb +0 -13
  62. data/lib/dry/logic/rule/key.rb +0 -37
  63. data/lib/dry/logic/rule/negation.rb +0 -15
  64. data/lib/dry/logic/rule/set.rb +0 -31
  65. data/lib/dry/logic/rule/value.rb +0 -48
  66. data/spec/unit/predicate_spec.rb +0 -115
  67. data/spec/unit/rule/attr_spec.rb +0 -29
  68. data/spec/unit/rule/check_spec.rb +0 -44
  69. data/spec/unit/rule/conjunction_spec.rb +0 -30
  70. data/spec/unit/rule/disjunction_spec.rb +0 -38
  71. data/spec/unit/rule/each_spec.rb +0 -31
  72. data/spec/unit/rule/exclusive_disjunction_spec.rb +0 -19
  73. data/spec/unit/rule/implication_spec.rb +0 -16
  74. data/spec/unit/rule/key_spec.rb +0 -121
  75. data/spec/unit/rule/set_spec.rb +0 -30
  76. data/spec/unit/rule/value_spec.rb +0 -99
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ea86d568127098e2777868066c2026dbc380eb9
4
- data.tar.gz: 5553735b89ab2ef6a1c5bdd3f24874bab46c2581
3
+ metadata.gz: 65344930c4a3c0f6a085d583b71530119d1bd81a
4
+ data.tar.gz: 5ca9b9aa5819f5e8bd962b66efe47aabd865eeac
5
5
  SHA512:
6
- metadata.gz: c943b1c67b55618b3e3fa1e982e371614a3e34c0ec2ca7457c775e755335a9579f52434d50545b411bf053f3c8e5a72209bdc86a58d7f13abef521df92b1fde5
7
- data.tar.gz: 87598a0a72b1196d966464b1a731746922613fa8eb72cfa3d002ecfe3dee917502126bcfb2e506ae67c958335a6343b05db131e7012d1a0c64d74320d4e3d2d6
6
+ metadata.gz: cdc353eafff80177f44585c8a82130a3f6abb1ea13d186b7da32e1bf0bafb29b2b912f543be022a8a147afe088b65a8542471865817e49ae9e3ac981051fdb1c
7
+ data.tar.gz: 8a46b78404a2edf942786c1a002be4db7f067c0a2c27190eba5caebafcec55042c76aab90fcd4a347196cec396c3c5a597298455a741c6175224d40239db309c
@@ -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:
@@ -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: :rbx
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
@@ -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
 
@@ -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
@@ -1,3 +1,5 @@
1
+ require 'dry/equalizer'
2
+
1
3
  module Dry
2
4
  module Logic
3
5
  class Evaluator
@@ -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,17 @@
1
+ require 'dry/logic/operations/key'
2
+
3
+ module Dry
4
+ module Logic
5
+ module Operations
6
+ class Attr < Key
7
+ def self.evaluator(name)
8
+ Evaluator::Attr.new(name)
9
+ end
10
+
11
+ def type
12
+ :attr
13
+ end
14
+ end
15
+ end
16
+ end
17
+ 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