dry-logic 0.4.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +214 -53
  3. data/LICENSE +1 -1
  4. data/README.md +14 -18
  5. data/dry-logic.gemspec +28 -15
  6. data/lib/dry-logic.rb +3 -1
  7. data/lib/dry/logic.rb +4 -2
  8. data/lib/dry/logic/appliable.rb +2 -0
  9. data/lib/dry/logic/evaluator.rb +3 -1
  10. data/lib/dry/logic/operations.rb +13 -11
  11. data/lib/dry/logic/operations/abstract.rb +8 -6
  12. data/lib/dry/logic/operations/and.rb +15 -3
  13. data/lib/dry/logic/operations/attr.rb +3 -1
  14. data/lib/dry/logic/operations/binary.rb +3 -1
  15. data/lib/dry/logic/operations/check.rb +6 -4
  16. data/lib/dry/logic/operations/each.rb +4 -2
  17. data/lib/dry/logic/operations/implication.rb +4 -2
  18. data/lib/dry/logic/operations/key.rb +11 -9
  19. data/lib/dry/logic/operations/negation.rb +9 -3
  20. data/lib/dry/logic/operations/or.rb +4 -2
  21. data/lib/dry/logic/operations/set.rb +5 -3
  22. data/lib/dry/logic/operations/unary.rb +3 -1
  23. data/lib/dry/logic/operations/xor.rb +4 -2
  24. data/lib/dry/logic/operators.rb +6 -4
  25. data/lib/dry/logic/predicates.rb +72 -28
  26. data/lib/dry/logic/result.rb +4 -2
  27. data/lib/dry/logic/rule.rb +35 -30
  28. data/lib/dry/logic/rule/interface.rb +141 -0
  29. data/lib/dry/logic/rule/predicate.rb +9 -3
  30. data/lib/dry/logic/rule_compiler.rb +7 -3
  31. data/lib/dry/logic/version.rb +3 -1
  32. metadata +21 -151
  33. data/.codeclimate.yml +0 -23
  34. data/.gitignore +0 -8
  35. data/.rspec +0 -3
  36. data/.rubocop.yml +0 -16
  37. data/.rubocop_todo.yml +0 -7
  38. data/.travis.yml +0 -31
  39. data/CONTRIBUTING.md +0 -29
  40. data/Gemfile +0 -18
  41. data/Rakefile +0 -12
  42. data/examples/basic.rb +0 -14
  43. data/spec/integration/result_spec.rb +0 -59
  44. data/spec/integration/rule_spec.rb +0 -53
  45. data/spec/shared/predicates.rb +0 -57
  46. data/spec/shared/rule.rb +0 -67
  47. data/spec/spec_helper.rb +0 -34
  48. data/spec/support/mutant.rb +0 -9
  49. data/spec/unit/operations/and_spec.rb +0 -64
  50. data/spec/unit/operations/attr_spec.rb +0 -27
  51. data/spec/unit/operations/check_spec.rb +0 -49
  52. data/spec/unit/operations/each_spec.rb +0 -47
  53. data/spec/unit/operations/implication_spec.rb +0 -30
  54. data/spec/unit/operations/key_spec.rb +0 -119
  55. data/spec/unit/operations/negation_spec.rb +0 -40
  56. data/spec/unit/operations/or_spec.rb +0 -73
  57. data/spec/unit/operations/set_spec.rb +0 -41
  58. data/spec/unit/operations/xor_spec.rb +0 -61
  59. data/spec/unit/predicates/array_spec.rb +0 -41
  60. data/spec/unit/predicates/attr_spec.rb +0 -29
  61. data/spec/unit/predicates/bool_spec.rb +0 -34
  62. data/spec/unit/predicates/case_spec.rb +0 -33
  63. data/spec/unit/predicates/date_spec.rb +0 -31
  64. data/spec/unit/predicates/date_time_spec.rb +0 -31
  65. data/spec/unit/predicates/decimal_spec.rb +0 -32
  66. data/spec/unit/predicates/empty_spec.rb +0 -38
  67. data/spec/unit/predicates/eql_spec.rb +0 -21
  68. data/spec/unit/predicates/even_spec.rb +0 -31
  69. data/spec/unit/predicates/excluded_from_spec.rb +0 -35
  70. data/spec/unit/predicates/excludes_spec.rb +0 -56
  71. data/spec/unit/predicates/false_spec.rb +0 -35
  72. data/spec/unit/predicates/filled_spec.rb +0 -38
  73. data/spec/unit/predicates/float_spec.rb +0 -31
  74. data/spec/unit/predicates/format_spec.rb +0 -21
  75. data/spec/unit/predicates/gt_spec.rb +0 -40
  76. data/spec/unit/predicates/gteq_spec.rb +0 -40
  77. data/spec/unit/predicates/included_in_spec.rb +0 -35
  78. data/spec/unit/predicates/includes_spec.rb +0 -24
  79. data/spec/unit/predicates/int_spec.rb +0 -34
  80. data/spec/unit/predicates/key_spec.rb +0 -29
  81. data/spec/unit/predicates/lt_spec.rb +0 -40
  82. data/spec/unit/predicates/lteq_spec.rb +0 -40
  83. data/spec/unit/predicates/max_size_spec.rb +0 -49
  84. data/spec/unit/predicates/min_size_spec.rb +0 -49
  85. data/spec/unit/predicates/none_spec.rb +0 -28
  86. data/spec/unit/predicates/not_eql_spec.rb +0 -21
  87. data/spec/unit/predicates/number_spec.rb +0 -37
  88. data/spec/unit/predicates/odd_spec.rb +0 -31
  89. data/spec/unit/predicates/size_spec.rb +0 -55
  90. data/spec/unit/predicates/str_spec.rb +0 -32
  91. data/spec/unit/predicates/time_spec.rb +0 -31
  92. data/spec/unit/predicates/true_spec.rb +0 -35
  93. data/spec/unit/predicates/type_spec.rb +0 -35
  94. data/spec/unit/predicates_spec.rb +0 -23
  95. data/spec/unit/rule/predicate_spec.rb +0 -53
  96. data/spec/unit/rule_compiler_spec.rb +0 -127
  97. data/spec/unit/rule_spec.rb +0 -141
data/.codeclimate.yml DELETED
@@ -1,23 +0,0 @@
1
- engines:
2
- rubocop:
3
- enabled: true
4
- checks:
5
- Rubocop/Metrics/LineLength:
6
- enabled: true
7
- max: 100
8
- Rubocop/Style/Documentation:
9
- enabled: false
10
- Rubocop/Lint/HandleExceptions:
11
- enabled: true
12
- exclude:
13
- - rakelib/*.rake
14
- Rubocop/Style/FileName:
15
- enabled: true
16
- exclude:
17
- - 'lib/dry-logic.rb'
18
- ratings:
19
- paths:
20
- - lib/**/*.rb
21
- exclude_paths:
22
- - spec/**/*
23
- - examples/**/*
data/.gitignore DELETED
@@ -1,8 +0,0 @@
1
- .DS_Store
2
- coverage
3
- /.bundle
4
- vendor/bundle
5
- bin/
6
- tmp/
7
- .idea/
8
- Gemfile.lock
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --color
2
- --require spec_helper
3
- --order random
data/.rubocop.yml DELETED
@@ -1,16 +0,0 @@
1
- # Generated by `rubocop --auto-gen-config`
2
- inherit_from: .rubocop_todo.yml
3
-
4
- Metrics/LineLength:
5
- Max: 100
6
-
7
- Style/Documentation:
8
- Enabled: false
9
-
10
- Lint/HandleExceptions:
11
- Exclude:
12
- - rakelib/*.rake
13
-
14
- Style/FileName:
15
- Exclude:
16
- - lib/dry-logic.rb
data/.rubocop_todo.yml DELETED
@@ -1,7 +0,0 @@
1
- # This configuration was generated by
2
- # `rubocop --auto-gen-config`
3
- # on 2015-10-30 01:32:46 +0000 using RuboCop version 0.34.2.
4
- # The point is for the user to remove these configuration records
5
- # one by one as the offenses are removed from the code base.
6
- # Note that changes in the inspected code, or installation of new
7
- # versions of RuboCop, may require this file to be generated again.
data/.travis.yml DELETED
@@ -1,31 +0,0 @@
1
- language: ruby
2
- dist: trusty
3
- sudo: false
4
- cache: bundler
5
- bundler_args: --without console tools
6
- script:
7
- - bundle exec rake spec
8
- after_success:
9
- - '[ -d coverage ] && bundle exec codeclimate-test-reporter'
10
- rvm:
11
- - 2.1.10
12
- - 2.2.7
13
- - 2.3.4
14
- - 2.4.1
15
- - jruby-9.1.10.0
16
- - ruby-head
17
- env:
18
- global:
19
- - JRUBY_OPTS='--dev -J-Xmx1024M'
20
- - COVERAGE=true
21
- matrix:
22
- allow_failures:
23
- - rvm: ruby-head
24
- notifications:
25
- email: false
26
- webhooks:
27
- urls:
28
- - https://webhooks.gitter.im/e/19098b4253a72c9796db
29
- on_success: change # options: [always|never|change] default: always
30
- on_failure: always # options: [always|never|change] default: always
31
- on_start: false # default: false
data/CONTRIBUTING.md DELETED
@@ -1,29 +0,0 @@
1
- # Issue Guidelines
2
-
3
- ## Reporting bugs
4
-
5
- If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated.
6
-
7
- ## Reporting feature requests
8
-
9
- Report a feature request **only after discussing it first on [discuss.dry-rb.org](https://discuss.dry-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discussion thread, and instead summarize what was discussed.
10
-
11
- ## Reporting questions, support requests, ideas, concerns etc.
12
-
13
- **PLEASE DON'T** - use [discuss.dry-rb.org](http://discuss.dry-rb.org) instead.
14
-
15
- # Pull Request Guidelines
16
-
17
- A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc.
18
-
19
- Other requirements:
20
-
21
- 1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue.
22
- 2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style.
23
- 3) Add API documentation if it's a new feature
24
- 4) Update API documentation if it changes an existing feature
25
- 5) Bonus points for sending a PR to [github.com/dry-rb/dry-rb.org](github.com/dry-rb/dry-rb.org) which updates user documentation and guides
26
-
27
- # Asking for help
28
-
29
- If these guidelines aren't helpful, and you're stuck, please post a message on [discuss.dry-rb.org](https://discuss.dry-rb.org).
data/Gemfile DELETED
@@ -1,18 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- group :test do
6
- gem 'codeclimate-test-reporter', platform: :mri
7
- gem 'simplecov', require: false, platform: :mri
8
- end
9
-
10
- group :tools do
11
- gem 'rubocop'
12
- gem 'byebug', platform: :mri
13
-
14
- unless ENV['TRAVIS']
15
- gem 'mutant', github: 'mbj/mutant'
16
- gem 'mutant-rspec', github: 'mbj/mutant'
17
- end
18
- end
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- #!/usr/bin/env rake
2
- require 'bundler/gem_tasks'
3
-
4
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
5
-
6
- require 'rspec/core'
7
- require 'rspec/core/rake_task'
8
-
9
- task default: :spec
10
-
11
- desc 'Run all specs in spec directory'
12
- RSpec::Core::RakeTask.new(:spec)
data/examples/basic.rb DELETED
@@ -1,14 +0,0 @@
1
- require 'dry/logic'
2
- require 'dry/logic/predicates'
3
-
4
- include Dry::Logic
5
-
6
- user_present = Rule::Predicate.new(Predicates[:key?]).curry(:user)
7
-
8
- has_min_age = Operations::Key.new(Rule::Predicate.new(Predicates[:gt?]).curry(18), name: [:user, :age])
9
-
10
- user_rule = user_present & has_min_age
11
-
12
- puts user_rule.(user: { age: 19 }).success?
13
-
14
- puts user_rule.(user: { age: 18 }).success?
@@ -1,59 +0,0 @@
1
- RSpec.describe Result do
2
- include_context 'predicates'
3
-
4
- describe '#to_s' do
5
- shared_examples_for 'string representation' do
6
- it 'returns string representation' do
7
- expect(rule.(input).to_s).to eql(output)
8
- end
9
- end
10
-
11
- context 'with a predicate' do
12
- let(:rule) { Rule::Predicate.new(gt?, args: [18]) }
13
- let(:input) { 17 }
14
- let(:output) { 'gt?(18, 17)' }
15
-
16
- it_behaves_like 'string representation'
17
- end
18
-
19
- context 'with AND operation' do
20
- let(:rule) { Rule::Predicate.new(array?).and(Rule::Predicate.new(empty?)) }
21
- let(:input) { '' }
22
- let(:output) { 'array?("") AND empty?("")' }
23
-
24
- it_behaves_like 'string representation'
25
- end
26
-
27
- context 'with OR operation' do
28
- let(:rule) { Rule::Predicate.new(array?).or(Rule::Predicate.new(empty?)) }
29
- let(:input) { 123 }
30
- let(:output) { 'array?(123) OR empty?(123)' }
31
-
32
- it_behaves_like 'string representation'
33
- end
34
-
35
- context 'with XOR operation' do
36
- let(:rule) { Rule::Predicate.new(array?).xor(Rule::Predicate.new(empty?)) }
37
- let(:input) { [] }
38
- let(:output) { 'array?([]) XOR empty?([])' }
39
-
40
- it_behaves_like 'string representation'
41
- end
42
-
43
- context 'with THEN operation' do
44
- let(:rule) { Rule::Predicate.new(array?).then(Rule::Predicate.new(empty?)) }
45
- let(:input) { [1, 2, 3] }
46
- let(:output) { 'empty?([1, 2, 3])' }
47
-
48
- it_behaves_like 'string representation'
49
- end
50
-
51
- context 'with NOT operation' do
52
- let(:rule) { Operations::Negation.new(Rule::Predicate.new(array?)) }
53
- let(:input) { 'foo' }
54
- let(:output) { 'not(array?("foo"))' }
55
-
56
- it_behaves_like 'string representation'
57
- end
58
- end
59
- end
@@ -1,53 +0,0 @@
1
- require 'dry-logic'
2
-
3
- RSpec.describe 'Rules' do
4
- specify 'defining an anonymous rule with an arbitrary predicate' do
5
- rule = Dry::Logic.Rule { |value| value.is_a?(Integer) }
6
-
7
- expect(rule.(1)).to be_success
8
- expect(rule[1]).to be(true)
9
- end
10
-
11
- specify 'defining a conjunction' do
12
- rule = Dry::Logic.Rule(&:even?) & Dry::Logic.Rule { |v| v > 4 }
13
-
14
- expect(rule.(3)).to be_failure
15
- expect(rule.(4)).to be_failure
16
- expect(rule.(5)).to be_failure
17
- expect(rule.(6)).to be_success
18
- end
19
-
20
- specify 'defining a disjunction' do
21
- rule = Dry::Logic.Rule { |v| v < 4 } | Dry::Logic.Rule { |v| v > 6 }
22
-
23
- expect(rule.(5)).to be_failure
24
- expect(rule.(3)).to be_success
25
- expect(rule.(7)).to be_success
26
- end
27
-
28
- specify 'defining an implication' do
29
- rule = Dry::Logic.Rule(&:empty?) > Dry::Logic.Rule { |v| v.is_a?(Array) }
30
-
31
- expect(rule.('foo')).to be_success
32
- expect(rule.([1, 2])).to be_success
33
- expect(rule.([])).to be_success
34
- expect(rule.('')).to be_failure
35
- end
36
-
37
- specify 'defining an exclusive disjunction' do
38
- rule = Dry::Logic.Rule(&:empty?) ^ Dry::Logic.Rule { |v| v.is_a?(Array) }
39
-
40
- expect(rule.('foo')).to be_failure
41
- expect(rule.([])).to be_failure
42
- expect(rule.([1, 2])).to be_success
43
- expect(rule.('')).to be_success
44
- end
45
-
46
- specify 'defining a rule with options' do
47
- rule = Dry::Logic::Rule(id: :empty?) { |value| value.empty? }
48
-
49
- expect(rule.('foo')).to be_failure
50
- expect(rule.('')).to be_success
51
- expect(rule.ast('foo')).to eql([:predicate, [:empty?, [[:value, 'foo']]]])
52
- end
53
- end
@@ -1,57 +0,0 @@
1
- require 'dry/logic/predicates'
2
-
3
- RSpec.shared_examples 'predicates' do
4
- let(:none?) { Dry::Logic::Predicates[:none?] }
5
-
6
- let(:array?) { Dry::Logic::Predicates[:array?] }
7
-
8
- let(:empty?) { Dry::Logic::Predicates[:empty?] }
9
-
10
- let(:str?) { Dry::Logic::Predicates[:str?] }
11
-
12
- let(:true?) { Dry::Logic::Predicates[:true?] }
13
-
14
- let(:hash?) { Dry::Logic::Predicates[:hash?] }
15
-
16
- let(:int?) { Dry::Logic::Predicates[:int?] }
17
-
18
- let(:filled?) { Dry::Logic::Predicates[:filled?] }
19
-
20
- let(:min_size?) { Dry::Logic::Predicates[:min_size?] }
21
-
22
- let(:lt?) { Dry::Logic::Predicates[:lt?] }
23
-
24
- let(:gt?) { Dry::Logic::Predicates[:gt?] }
25
-
26
- let(:key?) { Dry::Logic::Predicates[:key?] }
27
-
28
- let(:attr?) { Dry::Logic::Predicates[:attr?] }
29
-
30
- let(:eql?) { Dry::Logic::Predicates[:eql?] }
31
-
32
- let(:size?) { Dry::Logic::Predicates[:size?] }
33
-
34
- let(:case?) { Dry::Logic::Predicates[:case?] }
35
-
36
- let(:equal?) { Dry::Logic::Predicates[:equal?] }
37
- end
38
-
39
- RSpec.shared_examples 'a passing predicate' do
40
- let(:predicate) { Dry::Logic::Predicates[predicate_name] }
41
-
42
- it do
43
- arguments_list.each do |args|
44
- expect(predicate.call(*args)).to be(true)
45
- end
46
- end
47
- end
48
-
49
- RSpec.shared_examples 'a failing predicate' do
50
- let(:predicate) { Dry::Logic::Predicates[predicate_name] }
51
-
52
- it do
53
- arguments_list.each do |args|
54
- expect(predicate.call(*args)).to be(false)
55
- end
56
- end
57
- end
data/spec/shared/rule.rb DELETED
@@ -1,67 +0,0 @@
1
- shared_examples_for Dry::Logic::Rule do
2
- let(:predicate) { double(:predicate, arity: 2, name: predicate_name) }
3
- let(:rule_type) { described_class }
4
- let(:predicate_name) { :good? }
5
-
6
- describe '#arity' do
7
- it 'returns its predicate arity' do
8
- rule = rule_type.new(predicate)
9
-
10
- expect(rule.arity).to be(2)
11
- end
12
- end
13
-
14
- describe '#parameters' do
15
- it 'returns a list of args with their names' do
16
- rule = rule_type.new(-> foo, bar { true }, args: [312])
17
-
18
- expect(rule.parameters).to eql([[:req, :foo], [:req, :bar]])
19
- end
20
- end
21
-
22
- describe '#call' do
23
- it 'returns success for valid input' do
24
- rule = rule_type.new(predicate)
25
-
26
- expect(predicate).to receive(:[]).with(2).and_return(true)
27
-
28
- expect(rule.(2)).to be_success
29
- end
30
-
31
- it 'returns failure for invalid input' do
32
- rule = rule_type.new(predicate)
33
-
34
- expect(predicate).to receive(:[]).with(2).and_return(false)
35
-
36
- expect(rule.(2)).to be_failure
37
- end
38
- end
39
-
40
- describe '#[]' do
41
- it 'delegates to its predicate' do
42
- rule = rule_type.new(predicate)
43
-
44
- expect(predicate).to receive(:[]).with(2).and_return(true)
45
- expect(rule[2]).to be(true)
46
- end
47
- end
48
-
49
- describe '#curry' do
50
- it 'returns a curried rule' do
51
- rule = rule_type.new(predicate).curry(3)
52
-
53
- expect(predicate).to receive(:[]).with(3, 2).and_return(true)
54
- expect(rule.args).to eql([3])
55
-
56
- expect(rule.(2)).to be_success
57
- end
58
-
59
- it 'raises argument error when arity does not match' do
60
- expect(predicate).to receive(:arity).and_return(2)
61
-
62
- expect { rule_type.new(predicate).curry(3, 2, 1) }.to raise_error(
63
- ArgumentError, 'wrong number of arguments (3 for 2)'
64
- )
65
- end
66
- end
67
- end
data/spec/spec_helper.rb DELETED
@@ -1,34 +0,0 @@
1
- if RUBY_ENGINE == 'ruby' && ENV['COVERAGE'] == 'true'
2
- require 'yaml'
3
- rubies = YAML.load(File.read(File.join(__dir__, '..', '.travis.yml')))['rvm']
4
- latest_mri = rubies.select { |v| v =~ /\A\d+\.\d+.\d+\z/ }.max
5
-
6
- if RUBY_VERSION == latest_mri
7
- require 'simplecov'
8
- SimpleCov.start do
9
- add_filter '/spec/'
10
- end
11
- end
12
- end
13
-
14
- begin
15
- require 'byebug'
16
- rescue LoadError; end
17
-
18
- require 'dry-logic'
19
- require 'dry/core/constants'
20
- require 'pathname'
21
-
22
- SPEC_ROOT = Pathname(__dir__)
23
-
24
- Dir[SPEC_ROOT.join('shared/**/*.rb')].each(&method(:require))
25
- Dir[SPEC_ROOT.join('support/**/*.rb')].each(&method(:require))
26
-
27
- include Dry::Logic
28
- include Dry::Core::Constants
29
-
30
- RSpec.configure do |config|
31
- config.disable_monkey_patching!
32
-
33
- config.warnings = true
34
- end