dry-logic 0.6.1 → 1.0.6

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +121 -22
  3. data/LICENSE +1 -1
  4. data/README.md +12 -14
  5. data/dry-logic.gemspec +29 -14
  6. data/lib/dry-logic.rb +2 -0
  7. data/lib/dry/logic.rb +2 -0
  8. data/lib/dry/logic/appliable.rb +2 -0
  9. data/lib/dry/logic/evaluator.rb +2 -0
  10. data/lib/dry/logic/operations.rb +2 -0
  11. data/lib/dry/logic/operations/abstract.rb +5 -3
  12. data/lib/dry/logic/operations/and.rb +3 -1
  13. data/lib/dry/logic/operations/attr.rb +2 -0
  14. data/lib/dry/logic/operations/binary.rb +2 -0
  15. data/lib/dry/logic/operations/check.rb +3 -1
  16. data/lib/dry/logic/operations/each.rb +2 -0
  17. data/lib/dry/logic/operations/implication.rb +2 -0
  18. data/lib/dry/logic/operations/key.rb +4 -2
  19. data/lib/dry/logic/operations/negation.rb +2 -0
  20. data/lib/dry/logic/operations/or.rb +2 -0
  21. data/lib/dry/logic/operations/set.rb +2 -0
  22. data/lib/dry/logic/operations/unary.rb +2 -0
  23. data/lib/dry/logic/operations/xor.rb +2 -0
  24. data/lib/dry/logic/operators.rb +2 -0
  25. data/lib/dry/logic/predicates.rb +39 -21
  26. data/lib/dry/logic/result.rb +2 -0
  27. data/lib/dry/logic/rule.rb +5 -3
  28. data/lib/dry/logic/rule/interface.rb +3 -1
  29. data/lib/dry/logic/rule/predicate.rb +2 -0
  30. data/lib/dry/logic/rule_compiler.rb +2 -0
  31. data/lib/dry/logic/version.rb +3 -1
  32. metadata +11 -128
  33. data/.codeclimate.yml +0 -15
  34. data/.gitignore +0 -8
  35. data/.rspec +0 -3
  36. data/.travis.yml +0 -31
  37. data/CONTRIBUTING.md +0 -29
  38. data/Gemfile +0 -13
  39. data/Rakefile +0 -12
  40. data/benchmarks/rule_application.rb +0 -28
  41. data/benchmarks/setup.rb +0 -11
  42. data/bin/console +0 -10
  43. data/examples/basic.rb +0 -14
  44. data/spec/integration/result_spec.rb +0 -59
  45. data/spec/integration/rule_spec.rb +0 -53
  46. data/spec/shared/predicates.rb +0 -57
  47. data/spec/shared/rule.rb +0 -72
  48. data/spec/spec_helper.rb +0 -34
  49. data/spec/support/mutant.rb +0 -9
  50. data/spec/unit/operations/and_spec.rb +0 -68
  51. data/spec/unit/operations/attr_spec.rb +0 -27
  52. data/spec/unit/operations/check_spec.rb +0 -49
  53. data/spec/unit/operations/each_spec.rb +0 -47
  54. data/spec/unit/operations/implication_spec.rb +0 -30
  55. data/spec/unit/operations/key_spec.rb +0 -133
  56. data/spec/unit/operations/negation_spec.rb +0 -49
  57. data/spec/unit/operations/or_spec.rb +0 -73
  58. data/spec/unit/operations/set_spec.rb +0 -41
  59. data/spec/unit/operations/xor_spec.rb +0 -61
  60. data/spec/unit/predicates/array_spec.rb +0 -41
  61. data/spec/unit/predicates/attr_spec.rb +0 -29
  62. data/spec/unit/predicates/bool_spec.rb +0 -34
  63. data/spec/unit/predicates/case_spec.rb +0 -33
  64. data/spec/unit/predicates/date_spec.rb +0 -31
  65. data/spec/unit/predicates/date_time_spec.rb +0 -31
  66. data/spec/unit/predicates/decimal_spec.rb +0 -32
  67. data/spec/unit/predicates/empty_spec.rb +0 -38
  68. data/spec/unit/predicates/eql_spec.rb +0 -21
  69. data/spec/unit/predicates/even_spec.rb +0 -31
  70. data/spec/unit/predicates/excluded_from_spec.rb +0 -35
  71. data/spec/unit/predicates/excludes_spec.rb +0 -56
  72. data/spec/unit/predicates/false_spec.rb +0 -35
  73. data/spec/unit/predicates/filled_spec.rb +0 -38
  74. data/spec/unit/predicates/float_spec.rb +0 -31
  75. data/spec/unit/predicates/format_spec.rb +0 -21
  76. data/spec/unit/predicates/gt_spec.rb +0 -40
  77. data/spec/unit/predicates/gteq_spec.rb +0 -40
  78. data/spec/unit/predicates/included_in_spec.rb +0 -35
  79. data/spec/unit/predicates/includes_spec.rb +0 -24
  80. data/spec/unit/predicates/int_spec.rb +0 -34
  81. data/spec/unit/predicates/key_spec.rb +0 -29
  82. data/spec/unit/predicates/lt_spec.rb +0 -40
  83. data/spec/unit/predicates/lteq_spec.rb +0 -40
  84. data/spec/unit/predicates/max_size_spec.rb +0 -49
  85. data/spec/unit/predicates/min_size_spec.rb +0 -49
  86. data/spec/unit/predicates/none_spec.rb +0 -28
  87. data/spec/unit/predicates/not_eql_spec.rb +0 -21
  88. data/spec/unit/predicates/number_spec.rb +0 -37
  89. data/spec/unit/predicates/odd_spec.rb +0 -31
  90. data/spec/unit/predicates/size_spec.rb +0 -55
  91. data/spec/unit/predicates/str_spec.rb +0 -32
  92. data/spec/unit/predicates/time_spec.rb +0 -31
  93. data/spec/unit/predicates/true_spec.rb +0 -35
  94. data/spec/unit/predicates/type_spec.rb +0 -35
  95. data/spec/unit/predicates_spec.rb +0 -23
  96. data/spec/unit/rule/predicate_spec.rb +0 -53
  97. data/spec/unit/rule_compiler_spec.rb +0 -127
  98. data/spec/unit/rule_spec.rb +0 -211
@@ -1,15 +0,0 @@
1
- version: "2"
2
-
3
- prepare:
4
- fetch:
5
- - url: "https://raw.githubusercontent.com/dry-rb/devtools/master/.rubocop.yml"
6
- path: ".rubocop.yml"
7
-
8
- exclude_patterns:
9
- - "benchmarks/"
10
- - "examples/"
11
- - "spec/"
12
-
13
- plugins:
14
- rubocop:
15
- enabled: true
data/.gitignore DELETED
@@ -1,8 +0,0 @@
1
- .DS_Store
2
- coverage
3
- /.bundle
4
- vendor/bundle
5
- tmp/
6
- .idea/
7
- Gemfile.lock
8
- .rubocop.yml
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --color
2
- --require spec_helper
3
- --order random
@@ -1,31 +0,0 @@
1
- language: ruby
2
- cache: bundler
3
- bundler_args: --without benchmarks tools
4
- before_script:
5
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
6
- - chmod +x ./cc-test-reporter
7
- - ./cc-test-reporter before-build
8
- after_script:
9
- - "[ -d coverage ] && ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT"
10
- script:
11
- - bundle exec rake
12
- rvm:
13
- - 2.6.2
14
- - 2.5.5
15
- - 2.4.6
16
- - jruby-9.2.7.0
17
- - truffleruby
18
- env:
19
- global:
20
- - COVERAGE=true
21
- matrix:
22
- allow_failures:
23
- - rvm: truffleruby
24
- notifications:
25
- email: false
26
- webhooks:
27
- urls:
28
- - https://dry-rb.zulipchat.com/api/v1/external/travis?api_key=SY8LLz6fShd4TJeLDXEdT0eBEgRqT2lv&stream=notifications&topic=ci
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
@@ -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,13 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- group :test do
6
- gem 'simplecov', require: false, platform: :mri
7
- end
8
-
9
- group :tools do
10
- gem 'pry-byebug', platform: :mri
11
- gem 'benchmark-ips', platform: :mri
12
- gem 'hotch', platform: :mri
13
- 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)
@@ -1,28 +0,0 @@
1
- require_relative 'setup'
2
-
3
- unless Dry::Logic::Rule.respond_to?(:build)
4
- Dry::Logic::Rule.singleton_class.alias_method(:build, :new)
5
- end
6
-
7
- predicates = Dry::Logic::Predicates
8
-
9
- type_check = Dry::Logic::Rule.build(predicates[:type?]).curry(Integer)
10
- int_check = Dry::Logic::Rule.build(predicates[:int?])
11
- key_check = Dry::Logic::Rule.build(predicates[:key?]).curry(:user)
12
- with_user = { user: {} }
13
- without_user = {}
14
-
15
- comparison = Dry::Logic::Rule.build(predicates[:gteq?]).curry(18)
16
-
17
- Benchmark.ips do |x|
18
- x.report("type check - success") { type_check.(0) }
19
- x.report("type check - failure") { type_check.('0') }
20
- x.report("int check - success") { int_check.(0) }
21
- x.report("int check - failure") { int_check.('0') }
22
- x.report("key check - success") { key_check.(with_user) }
23
- x.report("key check - failure") { key_check.(without_user) }
24
- x.report("comparison - success") { comparison.(20) }
25
- x.report("comparison - failure") { comparison.(17) }
26
-
27
- x.compare!
28
- end
@@ -1,11 +0,0 @@
1
- require 'benchmark/ips'
2
- require 'hotch'
3
- ENV['HOTCH_VIEWER'] ||= 'open'
4
-
5
- require 'dry/logic'
6
- require 'dry/logic/predicates'
7
-
8
- def profile(&block)
9
- Hotch(filter: 'Dry', &block)
10
- end
11
-
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require 'dry/logic'
5
- require 'dry/logic/predicates'
6
-
7
- include Dry::Logic
8
-
9
- require "irb"
10
- IRB.start
@@ -1,14 +0,0 @@
1
- require 'dry/logic'
2
- require 'dry/logic/predicates'
3
-
4
- include Dry::Logic
5
-
6
- user_present = Rule::Predicate.build(Predicates[:key?]).curry(:user)
7
-
8
- has_min_age = Operations::Key.new(Rule::Predicate.build(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.build(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.build(array?).and(Rule::Predicate.build(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.build(array?).or(Rule::Predicate.build(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.build(array?).xor(Rule::Predicate.build(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.build(array?).then(Rule::Predicate.build(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.build(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(:nil?) { Dry::Logic::Predicates[:nil?] }
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
@@ -1,72 +0,0 @@
1
- shared_examples_for Dry::Logic::Rule do
2
- let(:arity) { 2 }
3
- let(:predicate) { double(:predicate, arity: arity, name: predicate_name) }
4
- let(:rule_type) { described_class }
5
- let(:predicate_name) { :good? }
6
-
7
- describe '#arity' do
8
- it 'returns its predicate arity' do
9
- rule = rule_type.build(predicate)
10
-
11
- expect(rule.arity).to be(2)
12
- end
13
- end
14
-
15
- describe '#parameters' do
16
- it 'returns a list of args with their names' do
17
- rule = rule_type.build(-> foo, bar { true }, args: [312])
18
-
19
- expect(rule.parameters).to eql([[:req, :foo], [:req, :bar]])
20
- end
21
- end
22
-
23
- describe '#call' do
24
- let(:arity) { 1 }
25
-
26
- it 'returns success for valid input' do
27
- rule = rule_type.build(predicate)
28
-
29
- expect(predicate).to receive(:[]).with(2).and_return(true)
30
-
31
- expect(rule.(2)).to be_success
32
- end
33
-
34
- it 'returns failure for invalid input' do
35
- rule = rule_type.build(predicate)
36
-
37
- expect(predicate).to receive(:[]).with(2).and_return(false)
38
-
39
- expect(rule.(2)).to be_failure
40
- end
41
- end
42
-
43
- describe '#[]' do
44
- let(:arity) { 1 }
45
-
46
- it 'delegates to its predicate' do
47
- rule = rule_type.build(predicate)
48
-
49
- expect(predicate).to receive(:[]).with(2).and_return(true)
50
- expect(rule[2]).to be(true)
51
- end
52
- end
53
-
54
- describe '#curry' do
55
- it 'returns a curried rule' do
56
- rule = rule_type.build(predicate).curry(3)
57
-
58
- expect(predicate).to receive(:[]).with(3, 2).and_return(true)
59
- expect(rule.args).to eql([3])
60
-
61
- expect(rule.(2)).to be_success
62
- end
63
-
64
- it 'raises argument error when arity does not match' do
65
- expect(predicate).to receive(:arity).and_return(2)
66
-
67
- expect { rule_type.build(predicate).curry(3, 2, 1) }.to raise_error(
68
- ArgumentError, 'wrong number of arguments (3 for 2)'
69
- )
70
- end
71
- end
72
- end
@@ -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 'pry-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