dry-logic 0.6.1 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
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