dry-logic 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +2 -5
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
- data/.github/ISSUE_TEMPLATE/---bug-report.md +34 -0
- data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
- data/.github/workflows/ci.yml +70 -0
- data/.github/workflows/docsite.yml +34 -0
- data/.github/workflows/sync_configs.yml +34 -0
- data/.gitignore +1 -1
- data/.rspec +1 -0
- data/.rubocop.yml +89 -0
- data/CHANGELOG.md +14 -8
- data/CODE_OF_CONDUCT.md +13 -0
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +1 -0
- data/LICENSE +1 -1
- data/README.md +6 -6
- data/docsite/source/index.html.md +54 -0
- data/docsite/source/operations.html.md +62 -0
- data/docsite/source/predicates.html.md +102 -0
- data/dry-logic.gemspec +5 -0
- data/lib/dry/logic/operations/abstract.rb +3 -3
- data/lib/dry/logic/operations/and.rb +1 -1
- data/lib/dry/logic/operations/check.rb +1 -1
- data/lib/dry/logic/operations/key.rb +2 -2
- data/lib/dry/logic/rule.rb +3 -3
- data/lib/dry/logic/version.rb +1 -1
- data/spec/spec_helper.rb +4 -10
- data/spec/unit/rule_spec.rb +2 -2
- metadata +17 -5
- data/.travis.yml +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a7700d85d1b92e3f0ddb2625b7c01e420101b33ae39ee12d1dcc342d22471d0
|
4
|
+
data.tar.gz: 5324d7fa8ef310acdfe0de371ab7c7ac28fee9454a8a339a5cfad3170fc1527e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89917d5c3c0b0d2f988db144b66d898606a8a2aae27f92c681bdb99646601236e63a08bce58a07bf6e50bad27c3458b89c3a3aaa73745266c83fe6008852da02
|
7
|
+
data.tar.gz: 7d3635146104a74a58f60ee2b819ae61a568fd936b3abe0a93eeb3e908b7658c0613bdcda9c5cbb423d2434039ccdcdc221fd316ea6dec15029396d2888e3d5b
|
data/.codeclimate.yml
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
---
|
2
|
+
name: "\U0001F41B Bug report"
|
3
|
+
about: See CONTRIBUTING.md for more information
|
4
|
+
title: ''
|
5
|
+
labels: bug
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
**Before you submit this: WE ONLY ACCEPT BUG REPORTS AND FEATURE REQUESTS**
|
11
|
+
|
12
|
+
For more information see [our contribution guidelines](https://github.com/rom-rb/rom/blob/master/CONTRIBUTING.md)
|
13
|
+
|
14
|
+
**Before you report**
|
15
|
+
|
16
|
+
:warning: If you have a problem related to a schema, please **report it under [dry-schema issues](https://github.com/dry-rb/dry-schema/issues/new?assignees=&labels=bug&template=---bug-report.md&title=)** instead.
|
17
|
+
|
18
|
+
**Describe the bug**
|
19
|
+
|
20
|
+
A clear and concise description of what the bug is.
|
21
|
+
|
22
|
+
**To Reproduce**
|
23
|
+
|
24
|
+
Provide detailed steps to reproduce, an executable script would be best.
|
25
|
+
|
26
|
+
**Expected behavior**
|
27
|
+
|
28
|
+
A clear and concise description of what you expected to happen.
|
29
|
+
|
30
|
+
**Your environment**
|
31
|
+
|
32
|
+
- Affects my production application: **YES/NO**
|
33
|
+
- Ruby version: ...
|
34
|
+
- OS: ...
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
name: "\U0001F6E0 Feature request"
|
3
|
+
about: See CONTRIBUTING.md for more information
|
4
|
+
title: ''
|
5
|
+
labels: feature
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
Summary of what the feature is supposed to do.
|
11
|
+
|
12
|
+
## Examples
|
13
|
+
|
14
|
+
Code examples showing how the feature could be used.
|
15
|
+
|
16
|
+
## Resources
|
17
|
+
|
18
|
+
Additional information, like a link to the discussion forum thread where the feature was discussed etc.
|
@@ -0,0 +1,70 @@
|
|
1
|
+
name: ci
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
paths:
|
6
|
+
- .github/workflows/ci.yml
|
7
|
+
- lib/**
|
8
|
+
- spec/**
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
tests-mri:
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
strategy:
|
14
|
+
fail-fast: false
|
15
|
+
matrix:
|
16
|
+
ruby: ["2.6.x", "2.5.x", "2.4.x"]
|
17
|
+
include:
|
18
|
+
- ruby: "2.6.x"
|
19
|
+
coverage: "true"
|
20
|
+
steps:
|
21
|
+
- uses: actions/checkout@v1
|
22
|
+
- name: Set up Ruby
|
23
|
+
uses: actions/setup-ruby@v1
|
24
|
+
with:
|
25
|
+
ruby-version: ${{matrix.ruby}}
|
26
|
+
- name: Download test reporter
|
27
|
+
if: "matrix.coverage == 'true'"
|
28
|
+
run: |
|
29
|
+
mkdir -p tmp/
|
30
|
+
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./tmp/cc-test-reporter
|
31
|
+
chmod +x ./tmp/cc-test-reporter
|
32
|
+
./tmp/cc-test-reporter before-build
|
33
|
+
- name: Run all tests
|
34
|
+
env:
|
35
|
+
COVERAGE: ${{matrix.coverage}}
|
36
|
+
run: |
|
37
|
+
gem install bundler
|
38
|
+
bundle install --jobs 4 --retry 3 --without tools docs
|
39
|
+
bundle exec rake
|
40
|
+
- name: Send coverage results
|
41
|
+
if: "matrix.coverage == 'true'"
|
42
|
+
env:
|
43
|
+
CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}
|
44
|
+
GIT_COMMIT_SHA: ${{github.sha}}
|
45
|
+
GIT_BRANCH: ${{github.ref}}
|
46
|
+
GIT_COMMITTED_AT: ${{github.event.head_commit.timestamp}}
|
47
|
+
run: |
|
48
|
+
GIT_BRANCH=`ruby -e "puts ENV['GITHUB_REF'].split('/', 3).last"` \
|
49
|
+
GIT_COMMITTED_AT=`ruby -r time -e "puts Time.iso8601(ENV['GIT_COMMITTED_AT']).to_i"` \
|
50
|
+
./tmp/cc-test-reporter after-build
|
51
|
+
|
52
|
+
tests-others:
|
53
|
+
runs-on: ubuntu-latest
|
54
|
+
strategy:
|
55
|
+
fail-fast: false
|
56
|
+
matrix:
|
57
|
+
image: ["jruby:9.2.8", "ruby:rc"]
|
58
|
+
container:
|
59
|
+
image: ${{matrix.image}}
|
60
|
+
steps:
|
61
|
+
- uses: actions/checkout@v1
|
62
|
+
- name: Install git
|
63
|
+
run: |
|
64
|
+
apt-get update
|
65
|
+
apt-get install -y --no-install-recommends git
|
66
|
+
- name: Run all tests
|
67
|
+
run: |
|
68
|
+
gem install bundler
|
69
|
+
bundle install --jobs 4 --retry 3 --without tools docs
|
70
|
+
bundle exec rspec
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# this file is managed by dry-rb/devtools project
|
2
|
+
|
3
|
+
name: docsite
|
4
|
+
|
5
|
+
on:
|
6
|
+
push:
|
7
|
+
paths:
|
8
|
+
- docsite/**
|
9
|
+
- .github/workflows/docsite.yml
|
10
|
+
branches:
|
11
|
+
- master
|
12
|
+
- release-**
|
13
|
+
tags:
|
14
|
+
|
15
|
+
jobs:
|
16
|
+
update-docs:
|
17
|
+
runs-on: ubuntu-latest
|
18
|
+
steps:
|
19
|
+
- uses: actions/checkout@v1
|
20
|
+
- name: Set up Ruby
|
21
|
+
uses: actions/setup-ruby@v1
|
22
|
+
with:
|
23
|
+
ruby-version: "2.6.x"
|
24
|
+
- name: Install dependencies
|
25
|
+
run: |
|
26
|
+
gem install bundler
|
27
|
+
bundle install --jobs 4 --retry 3 --without benchmarks sql
|
28
|
+
- name: Symlink ossy
|
29
|
+
run: mkdir -p bin && ln -sf "$(bundle show ossy)/bin/ossy" bin/ossy
|
30
|
+
- name: Trigger dry-rb.org deploy
|
31
|
+
env:
|
32
|
+
GITHUB_LOGIN: dry-bot
|
33
|
+
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
34
|
+
run: bin/ossy github workflow dry-rb/dry-rb.org ci
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# this file is managed by dry-rb/devtools project
|
2
|
+
|
3
|
+
name: sync_configs
|
4
|
+
|
5
|
+
on:
|
6
|
+
repository_dispatch:
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
sync-configs:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
if: github.event.action == 'sync_configs'
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v1
|
14
|
+
- name: Update configuration files from devtools
|
15
|
+
env:
|
16
|
+
GITHUB_LOGIN: dry-bot
|
17
|
+
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
18
|
+
run: |
|
19
|
+
git clone https://github.com/dry-rb/devtools.git tmp/devtools
|
20
|
+
|
21
|
+
if [ -f ".github/workflows/custom_ci.yml" ]; then
|
22
|
+
rsync -av --exclude '.github/workflows/ci.yml' tmp/devtools/shared/ . ;
|
23
|
+
else
|
24
|
+
rsync -av tmp/devtools/shared/ . ;
|
25
|
+
fi
|
26
|
+
|
27
|
+
git config --local user.email "dry-bot@dry-rb.org"
|
28
|
+
git config --local user.name "dry-bot"
|
29
|
+
git add -A
|
30
|
+
git commit -m "[devtools] config sync" || echo "nothing changed"
|
31
|
+
- name: Push changes
|
32
|
+
uses: ad-m/github-push-action@master
|
33
|
+
with:
|
34
|
+
github_token: ${{ secrets.GH_PAT }}
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# this file is managed by dry-rb/devtools project
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
TargetRubyVersion: 2.4
|
5
|
+
|
6
|
+
Style/EachWithObject:
|
7
|
+
Enabled: false
|
8
|
+
|
9
|
+
Style/StringLiterals:
|
10
|
+
Enabled: true
|
11
|
+
EnforcedStyle: single_quotes
|
12
|
+
|
13
|
+
Style/Alias:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Style/LambdaCall:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Style/StabbyLambdaParentheses:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/FormatString:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/Documentation:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Layout/SpaceInLambdaLiteral:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Layout/MultilineMethodCallIndentation:
|
32
|
+
Enabled: true
|
33
|
+
EnforcedStyle: indented
|
34
|
+
|
35
|
+
Metrics/LineLength:
|
36
|
+
Max: 100
|
37
|
+
|
38
|
+
Metrics/MethodLength:
|
39
|
+
Max: 22
|
40
|
+
|
41
|
+
Metrics/ClassLength:
|
42
|
+
Max: 150
|
43
|
+
|
44
|
+
Metrics/AbcSize:
|
45
|
+
Max: 20
|
46
|
+
|
47
|
+
Metrics/BlockLength:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Metrics/CyclomaticComplexity:
|
51
|
+
Enabled: true
|
52
|
+
Max: 10
|
53
|
+
|
54
|
+
Lint/BooleanSymbol:
|
55
|
+
Enabled: false
|
56
|
+
|
57
|
+
Style/AccessModifierDeclarations:
|
58
|
+
Enabled: false
|
59
|
+
|
60
|
+
Style/BlockDelimiters:
|
61
|
+
Enabled: false
|
62
|
+
|
63
|
+
Layout/IndentFirstArrayElement:
|
64
|
+
EnforcedStyle: consistent
|
65
|
+
|
66
|
+
Style/ClassAndModuleChildren:
|
67
|
+
Exclude:
|
68
|
+
- "spec/**/*_spec.rb"
|
69
|
+
|
70
|
+
Lint/HandleExceptions:
|
71
|
+
Exclude:
|
72
|
+
- "spec/spec_helper.rb"
|
73
|
+
|
74
|
+
Naming/FileName:
|
75
|
+
Exclude:
|
76
|
+
- "lib/dry-*.rb"
|
77
|
+
|
78
|
+
Style/SymbolArray:
|
79
|
+
Exclude:
|
80
|
+
- "spec/**/*_spec.rb"
|
81
|
+
|
82
|
+
Style/ConditionalAssignment:
|
83
|
+
Enabled: false
|
84
|
+
|
85
|
+
Naming/MethodName:
|
86
|
+
Enabled: false
|
87
|
+
|
88
|
+
Style/AsciiComments:
|
89
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,14 +1,20 @@
|
|
1
|
+
# v1.0.4 2019-11-06
|
2
|
+
|
3
|
+
- Fix keyword warnings (@flash-gordon)
|
4
|
+
|
5
|
+
[Compare v1.0.3...v1.0.4](https://github.com/dry-rb/dry-logic/compare/v1.0.3...v1.0.4)
|
6
|
+
|
1
7
|
# v1.0.3 2019-08-01
|
2
8
|
|
3
9
|
### Added
|
4
10
|
|
5
|
-
|
6
|
-
|
7
|
-
|
11
|
+
- `bytesize?` predicate (@bmalinconico)
|
12
|
+
- `min_bytesize?` predicate (@bmalinconico)
|
13
|
+
- `max_bytesize? predicate (@bmalinconico)
|
8
14
|
|
9
15
|
### Changed
|
10
16
|
|
11
|
-
|
17
|
+
- Min ruby version was set to `>= 2.4.0` (@flash-gordon)
|
12
18
|
|
13
19
|
[Compare v1.0.2...v1.0.3](https://github.com/dry-rb/dry-logic/compare/v1.0.2...v1.0.3)
|
14
20
|
|
@@ -31,13 +37,13 @@ This release was removed from rubygems because it broke dry-schema.
|
|
31
37
|
|
32
38
|
# v1.0.0 2019-04-23
|
33
39
|
|
34
|
-
|
40
|
+
- Version bump to `1.0.0` (flash-gordon)
|
35
41
|
|
36
42
|
[Compare v0.6.1...v1.0.0](https://github.com/dry-rb/dry-logic/compare/v0.6.1...v1.0.0)
|
37
43
|
|
38
44
|
# v0.6.1 2019-04-18
|
39
45
|
|
40
|
-
|
46
|
+
- Fix a regression in dry-validation 0.x for argument-less predicates (flash-gordon)
|
41
47
|
|
42
48
|
[Compare v0.6.0...v0.6.1](https://github.com/dry-rb/dry-logic/compare/v0.6.0...v0.6.1)
|
43
49
|
|
@@ -45,11 +51,11 @@ This release was removed from rubygems because it broke dry-schema.
|
|
45
51
|
|
46
52
|
### Added
|
47
53
|
|
48
|
-
|
54
|
+
- Generating hints can be disabled by building `Operations::And` with `hints: false` option set (solnic)
|
49
55
|
|
50
56
|
### Changed
|
51
57
|
|
52
|
-
|
58
|
+
- `Rule` construction has been optimized so that currying and application is multiple-times faster (flash-gordon)
|
53
59
|
|
54
60
|
[Compare v0.5.0...v0.6.0](https://github.com/dry-rb/dry-logic/compare/v0.5.0...v0.6.0)
|
55
61
|
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.4.0, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct)
|
data/CONTRIBUTING.md
CHANGED
@@ -10,7 +10,7 @@ Report a feature request **only after discussing it first on [discourse.dry-rb.o
|
|
10
10
|
|
11
11
|
## Reporting questions, support requests, ideas, concerns etc.
|
12
12
|
|
13
|
-
**PLEASE DON'T** - use [discourse.dry-rb.org](
|
13
|
+
**PLEASE DON'T** - use [discourse.dry-rb.org](http://discourse.dry-rb.org) instead.
|
14
14
|
|
15
15
|
# Pull Request Guidelines
|
16
16
|
|
@@ -26,4 +26,4 @@ Other requirements:
|
|
26
26
|
|
27
27
|
# Asking for help
|
28
28
|
|
29
|
-
If these guidelines aren't helpful, and you're stuck, please post a message on [discourse.dry-rb.org](https://discourse.dry-rb.org).
|
29
|
+
If these guidelines aren't helpful, and you're stuck, please post a message on [discourse.dry-rb.org](https://discourse.dry-rb.org) or join [our chat](https://dry-rb.zulipchat.com).
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/dry-logic
|
2
|
-
[
|
2
|
+
[ci]: https://github.com/dry-rb/dry-logic/actions?query=workflow%3Aci
|
3
3
|
[codeclimate]: https://codeclimate.com/github/dry-rb/dry-logic
|
4
4
|
[chat]: https://dry-rb.zulipchat.com
|
5
5
|
[inchpages]: http://inch-ci.org/github/dry-rb/dry-logic
|
@@ -7,20 +7,20 @@
|
|
7
7
|
# dry-logic [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat]
|
8
8
|
|
9
9
|
[![Gem Version](https://badge.fury.io/rb/dry-logic.svg)][gem]
|
10
|
-
[![Build Status](https://
|
10
|
+
[![Build Status](https://github.com/dry-rb/dry-logic/workflows/ci/badge.svg)][ci]
|
11
11
|
[![Code Climate](https://codeclimate.com/github/dry-rb/dry-logic/badges/gpa.svg)][codeclimate]
|
12
12
|
[![Test Coverage](https://codeclimate.com/github/dry-rb/dry-logic/badges/coverage.svg)][codeclimate]
|
13
13
|
[![Inline docs](http://inch-ci.org/github/dry-rb/dry-logic.svg?branch=master)][inchpages]
|
14
14
|
|
15
15
|
Predicate logic and rule composition used by:
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
- [dry-types](https://github.com/dry-rb/dry-types) for constrained types
|
18
|
+
- [dry-schema](https://github.com/dry-rb/dry-schema) and [dry-validation](https://github.com/dry-rb/dry-validation) for composing validation rules
|
19
|
+
- your project...?
|
20
20
|
|
21
21
|
## Links
|
22
22
|
|
23
|
-
|
23
|
+
- [Documentation](http://dry-rb.org/gems/dry-logic)
|
24
24
|
|
25
25
|
## Contributing
|
26
26
|
|
@@ -0,0 +1,54 @@
|
|
1
|
+
---
|
2
|
+
title: Introduction
|
3
|
+
description: Predicate logic with composable rules
|
4
|
+
layout: gem-single
|
5
|
+
type: gem
|
6
|
+
name: dry-logic
|
7
|
+
sections:
|
8
|
+
- predicates
|
9
|
+
- operations
|
10
|
+
---
|
11
|
+
|
12
|
+
Predicate logic and rule composition used by:
|
13
|
+
|
14
|
+
* [dry-types](https://github.com/dry-rb/dry-types) for constrained types
|
15
|
+
* [dry-validation](https://github.com/dry-rb/dry-validation) for composing validation rules
|
16
|
+
* your project...?
|
17
|
+
|
18
|
+
## Synopsis
|
19
|
+
|
20
|
+
``` ruby
|
21
|
+
require 'dry/logic'
|
22
|
+
require 'dry/logic/predicates'
|
23
|
+
|
24
|
+
include Dry::Logic
|
25
|
+
|
26
|
+
# Rule::Predicate will only apply its predicate to its input, that’s all
|
27
|
+
|
28
|
+
# require input to have the key :user
|
29
|
+
user_present = Rule::Predicate.new(Predicates[:key?]).curry(:user)
|
30
|
+
# curry allows us to prepare predicates with args, without the input
|
31
|
+
|
32
|
+
# require value to be greater than 18
|
33
|
+
min_18 = Rule::Predicate.new(Predicates[:gt?]).curry(18)
|
34
|
+
|
35
|
+
# use the min_18 predicate on the the value of user[:age]
|
36
|
+
has_min_age = Operations::Key.new(min_18, name: [:user, :age])
|
37
|
+
|
38
|
+
user_rule = user_present & has_min_age
|
39
|
+
|
40
|
+
user_rule.(user: { age: 19 }).success?
|
41
|
+
# => true
|
42
|
+
|
43
|
+
user_rule.(user: { age: 18 }).success?
|
44
|
+
# => false
|
45
|
+
|
46
|
+
user_rule.(user: { age: 'seventeen' })
|
47
|
+
# => ArgumentError: comparison of String with 18 failed
|
48
|
+
|
49
|
+
user_rule.(user: { })
|
50
|
+
# => NoMethodError: undefined method `>' for nil:NilClass
|
51
|
+
|
52
|
+
user_rule.({}).success?
|
53
|
+
# => false
|
54
|
+
```
|
@@ -0,0 +1,62 @@
|
|
1
|
+
---
|
2
|
+
title: Operations
|
3
|
+
layout: gem-single
|
4
|
+
name: dry-logic
|
5
|
+
---
|
6
|
+
|
7
|
+
Dry-logic uses operations to interact with the input passed to the different rules.
|
8
|
+
|
9
|
+
``` ruby
|
10
|
+
require 'dry/logic'
|
11
|
+
require 'dry/logic/predicates'
|
12
|
+
|
13
|
+
include Dry::Logic
|
14
|
+
|
15
|
+
user_present = Rule::Predicate.new(Predicates[:key?]).curry(:user)
|
16
|
+
|
17
|
+
min_18 = Rule::Predicate.new(Predicates[:gt?]).curry(18)
|
18
|
+
|
19
|
+
# Here Operations::Key and Rule::Predicate are use to compose and logic based on the value of a given key e.g [:user, :age]
|
20
|
+
has_min_age = Operations::Key.new(min_18, name: [:user, :age])
|
21
|
+
# => #<Dry::Logic::Operations::Key rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#gt?> options={:args=>[18]}>] options={:name=>[:user, :age], :evaluator=>#<Dry::Logic::Evaluator::Key path=[:user, :age]>, :path=>[:user, :age]}>
|
22
|
+
|
23
|
+
# Thanks to the composable structure of the library we can use multiple Rules and Operations to create custom logic
|
24
|
+
user_rule = user_present & has_min_age
|
25
|
+
|
26
|
+
user_rule.(user: { age: 19 }).success?
|
27
|
+
# => true
|
28
|
+
```
|
29
|
+
|
30
|
+
* Built-in:
|
31
|
+
- `and`
|
32
|
+
- `or`
|
33
|
+
- `key`
|
34
|
+
- `attr`
|
35
|
+
- `binary`
|
36
|
+
- `check`
|
37
|
+
- `each`
|
38
|
+
- `implication`
|
39
|
+
- `negation`
|
40
|
+
- `set`
|
41
|
+
- `xor`
|
42
|
+
|
43
|
+
Another example, lets create the `all?` method from the `Enumerable` module.
|
44
|
+
|
45
|
+
``` ruby
|
46
|
+
require 'dry/logic'
|
47
|
+
require 'dry/logic/predicates'
|
48
|
+
|
49
|
+
include Dry::Logic
|
50
|
+
|
51
|
+
def all?(value)
|
52
|
+
Operations::Each.new(Rule::Predicate.new(Predicates[:gt?]).curry(value))
|
53
|
+
end
|
54
|
+
|
55
|
+
all_6 = all?(6)
|
56
|
+
|
57
|
+
all_6.([6,7,8,9]).success?
|
58
|
+
# => true
|
59
|
+
|
60
|
+
all_6.([1,2,3,4]).success?
|
61
|
+
# => false
|
62
|
+
```
|
@@ -0,0 +1,102 @@
|
|
1
|
+
---
|
2
|
+
title: Predicates
|
3
|
+
layout: gem-single
|
4
|
+
name: dry-logic
|
5
|
+
---
|
6
|
+
|
7
|
+
Dry-logic comes with a lot predicates to compose multiple rules:
|
8
|
+
|
9
|
+
``` ruby
|
10
|
+
require 'dry/logic'
|
11
|
+
require 'dry/logic/predicates'
|
12
|
+
|
13
|
+
include Dry::Logic
|
14
|
+
```
|
15
|
+
|
16
|
+
Now you can access all built-in predicates:
|
17
|
+
|
18
|
+
``` ruby
|
19
|
+
Predicates[:key?]
|
20
|
+
# => #<Method: Module(Dry::Logic::Predicates::Methods)#key?>
|
21
|
+
```
|
22
|
+
|
23
|
+
In the end predicates return true or false.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
Predicates[:key?].(:name, {name: 'John'})
|
27
|
+
# => true
|
28
|
+
```
|
29
|
+
|
30
|
+
* Built-in:
|
31
|
+
- `type?`
|
32
|
+
- `none?`
|
33
|
+
- `key?`
|
34
|
+
- `attr?`
|
35
|
+
- `empty?`
|
36
|
+
- `filled?`
|
37
|
+
- `bool?`
|
38
|
+
- `date?`
|
39
|
+
- `date_time?`
|
40
|
+
- `time?`
|
41
|
+
- `number?`
|
42
|
+
- `int?`
|
43
|
+
- `float?`
|
44
|
+
- `decimal?`
|
45
|
+
- `str?`
|
46
|
+
- `hash?`
|
47
|
+
- `array?`
|
48
|
+
- `odd?`
|
49
|
+
- `even?`
|
50
|
+
- `lt?`
|
51
|
+
- `gt?`
|
52
|
+
- `lteq?`
|
53
|
+
- `gteq?`
|
54
|
+
- `size?`
|
55
|
+
- `min_size?`
|
56
|
+
- `max_size?`
|
57
|
+
- `bytesize?`
|
58
|
+
- `min_bytesize?`
|
59
|
+
- `max_bytesize?`
|
60
|
+
- `inclusion?`
|
61
|
+
- `exclusion?`
|
62
|
+
- `included_in?`
|
63
|
+
- `excluded_from?`
|
64
|
+
- `includes?`
|
65
|
+
- `excludes?`
|
66
|
+
- `eql?`
|
67
|
+
- `not_eql?`
|
68
|
+
- `is?`
|
69
|
+
- `case?`
|
70
|
+
- `true?`
|
71
|
+
- `false?`
|
72
|
+
- `format?`
|
73
|
+
- `respond_to?`
|
74
|
+
- `predicate`
|
75
|
+
- `uuid_v4?`
|
76
|
+
|
77
|
+
With predicates you can build more composable and complex operations:
|
78
|
+
For example, let's say we want to check that a given input is a hash and has a specify key.
|
79
|
+
|
80
|
+
``` ruby
|
81
|
+
require 'dry/logic'
|
82
|
+
require 'dry/logic/predicates'
|
83
|
+
|
84
|
+
include Dry::Logic
|
85
|
+
|
86
|
+
is_hash = Rule::Predicate.new(Predicates[:type?]).curry(Hash)
|
87
|
+
# => #<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[:hash]}>
|
88
|
+
name_key = Rule::Predicate.new(Predicates[:key?]).curry(:name)
|
89
|
+
# => #<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#key?> options={:args=>[:name]}>
|
90
|
+
|
91
|
+
hash_with_key = is_hash & name_key
|
92
|
+
# => #<Dry::Logic::Operations::And rules=[#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[:hash]}>, #<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#key?> options={:args=>[:name]}>] options={}>
|
93
|
+
|
94
|
+
hash_with_key.(name: 'John').success?
|
95
|
+
# => true
|
96
|
+
|
97
|
+
hash_with_key.(not_valid: 'John').success?
|
98
|
+
# => false
|
99
|
+
|
100
|
+
hash_with_key.([1,2]).success?
|
101
|
+
# => false
|
102
|
+
```
|
data/dry-logic.gemspec
CHANGED
@@ -15,6 +15,11 @@ 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
|
spec.required_ruby_version = '>= 2.4.0'
|
18
|
+
|
19
|
+
spec.metadata = {
|
20
|
+
'source_code_uri' => 'https://github.com/dry-rb/dry-logic',
|
21
|
+
'changelog_uri' => 'https://github.com/dry-rb/dry-logic/blob/master/CHANGELOG.md'
|
22
|
+
}
|
18
23
|
|
19
24
|
spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
20
25
|
spec.add_runtime_dependency 'dry-core', '~> 0.2'
|
@@ -26,15 +26,15 @@ module Dry
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def curry(*args)
|
29
|
-
new(rules.map { |rule| rule.curry(*args) }, options)
|
29
|
+
new(rules.map { |rule| rule.curry(*args) }, **options)
|
30
30
|
end
|
31
31
|
|
32
32
|
def new(rules, **new_options)
|
33
|
-
self.class.new(*rules, options
|
33
|
+
self.class.new(*rules, **options, **new_options)
|
34
34
|
end
|
35
35
|
|
36
36
|
def with(new_options)
|
37
|
-
new(rules, options
|
37
|
+
new(rules, **options, **new_options)
|
38
38
|
end
|
39
39
|
|
40
40
|
def to_ast
|
@@ -12,13 +12,13 @@ module Dry
|
|
12
12
|
|
13
13
|
attr_reader :path
|
14
14
|
|
15
|
-
def self.new(rules, options)
|
15
|
+
def self.new(rules, **options)
|
16
16
|
if options[:evaluator]
|
17
17
|
super
|
18
18
|
else
|
19
19
|
name = options.fetch(:name)
|
20
20
|
eval = options.fetch(:evaluator, evaluator(name))
|
21
|
-
super(rules, options
|
21
|
+
super(rules, **options, evaluator: eval, path: name)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
data/lib/dry/logic/rule.rb
CHANGED
@@ -68,11 +68,11 @@ module Dry
|
|
68
68
|
|
69
69
|
def bind(object)
|
70
70
|
if predicate.respond_to?(:bind)
|
71
|
-
self.class.build(predicate.bind(object), options)
|
71
|
+
self.class.build(predicate.bind(object), **options)
|
72
72
|
else
|
73
73
|
self.class.build(
|
74
74
|
-> *args { object.instance_exec(*args, &predicate) },
|
75
|
-
options
|
75
|
+
**options, arity: arity, parameters: parameters
|
76
76
|
)
|
77
77
|
end
|
78
78
|
end
|
@@ -82,7 +82,7 @@ module Dry
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def with(new_opts)
|
85
|
-
self.class.build(predicate, options
|
85
|
+
self.class.build(predicate, **options, **new_opts)
|
86
86
|
end
|
87
87
|
|
88
88
|
def parameters
|
data/lib/dry/logic/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,15 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
if
|
4
|
-
require '
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
if RUBY_VERSION == latest_mri
|
9
|
-
require 'simplecov'
|
10
|
-
SimpleCov.start do
|
11
|
-
add_filter '/spec/'
|
12
|
-
end
|
3
|
+
if ENV['COVERAGE'] == 'true'
|
4
|
+
require 'simplecov'
|
5
|
+
SimpleCov.start do
|
6
|
+
add_filter '/spec/'
|
13
7
|
end
|
14
8
|
end
|
15
9
|
|
data/spec/unit/rule_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RSpec.describe Dry::Logic::Rule do
|
4
|
-
subject(:rule) { Rule.build(predicate, options) }
|
4
|
+
subject(:rule) { Rule.build(predicate, **options) }
|
5
5
|
|
6
6
|
let(:predicate) { -> { true } }
|
7
7
|
let(:options) { {} }
|
@@ -204,7 +204,7 @@ RSpec.describe Dry::Logic::Rule do
|
|
204
204
|
describe 'constants' do
|
205
205
|
let(:options) { { args: [], arity: 0 } }
|
206
206
|
|
207
|
-
|
207
|
+
it 'accepts variable number of arguments' do
|
208
208
|
expect(rule.method(:call).arity).to be(-1)
|
209
209
|
expect(rule.method(:[]).arity).to be(-1)
|
210
210
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-logic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -102,10 +102,17 @@ extensions: []
|
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
104
|
- ".codeclimate.yml"
|
105
|
+
- ".github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md"
|
106
|
+
- ".github/ISSUE_TEMPLATE/---bug-report.md"
|
107
|
+
- ".github/ISSUE_TEMPLATE/---feature-request.md"
|
108
|
+
- ".github/workflows/ci.yml"
|
109
|
+
- ".github/workflows/docsite.yml"
|
110
|
+
- ".github/workflows/sync_configs.yml"
|
105
111
|
- ".gitignore"
|
106
112
|
- ".rspec"
|
107
|
-
- ".
|
113
|
+
- ".rubocop.yml"
|
108
114
|
- CHANGELOG.md
|
115
|
+
- CODE_OF_CONDUCT.md
|
109
116
|
- CONTRIBUTING.md
|
110
117
|
- Gemfile
|
111
118
|
- LICENSE
|
@@ -114,6 +121,9 @@ files:
|
|
114
121
|
- benchmarks/rule_application.rb
|
115
122
|
- benchmarks/setup.rb
|
116
123
|
- bin/console
|
124
|
+
- docsite/source/index.html.md
|
125
|
+
- docsite/source/operations.html.md
|
126
|
+
- docsite/source/predicates.html.md
|
117
127
|
- dry-logic.gemspec
|
118
128
|
- examples/basic.rb
|
119
129
|
- lib/dry-logic.rb
|
@@ -206,7 +216,9 @@ files:
|
|
206
216
|
homepage: https://github.com/dry-rb/dry-logic
|
207
217
|
licenses:
|
208
218
|
- MIT
|
209
|
-
metadata:
|
219
|
+
metadata:
|
220
|
+
source_code_uri: https://github.com/dry-rb/dry-logic
|
221
|
+
changelog_uri: https://github.com/dry-rb/dry-logic/blob/master/CHANGELOG.md
|
210
222
|
post_install_message:
|
211
223
|
rdoc_options: []
|
212
224
|
require_paths:
|
@@ -222,7 +234,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
234
|
- !ruby/object:Gem::Version
|
223
235
|
version: '0'
|
224
236
|
requirements: []
|
225
|
-
rubygems_version: 3.0.
|
237
|
+
rubygems_version: 3.0.6
|
226
238
|
signing_key:
|
227
239
|
specification_version: 4
|
228
240
|
summary: Predicate logic with rule composition
|
data/.travis.yml
DELETED
@@ -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.3
|
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
|