Showing preview only (201K chars total). Download the full file or copy to clipboard to get everything.
Repository: dry-rb/dry-logic
Branch: main
Commit: 3326aefba07b
Files: 133
Total size: 172.7 KB
Directory structure:
gitextract_njl1vvvj/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report.md
│ │ └── config.yml
│ ├── SUPPORT.md
│ └── workflows/
│ ├── ci-lint.yml
│ ├── ci.yml
│ ├── pr-comments.yml
│ ├── repo-sync-preview.yml
│ └── rubocop.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gemfile
├── Gemfile.devtools
├── LICENSE
├── README.md
├── Rakefile
├── benchmarks/
│ ├── builder.rb
│ ├── rule_application.rb
│ └── setup.rb
├── bin/
│ ├── .gitkeep
│ └── console
├── dry-logic.gemspec
├── examples/
│ └── basic.rb
├── lib/
│ ├── dry/
│ │ ├── logic/
│ │ │ ├── appliable.rb
│ │ │ ├── builder.rb
│ │ │ ├── evaluator.rb
│ │ │ ├── operations/
│ │ │ │ ├── abstract.rb
│ │ │ │ ├── and.rb
│ │ │ │ ├── attr.rb
│ │ │ │ ├── binary.rb
│ │ │ │ ├── check.rb
│ │ │ │ ├── each.rb
│ │ │ │ ├── implication.rb
│ │ │ │ ├── key.rb
│ │ │ │ ├── negation.rb
│ │ │ │ ├── or.rb
│ │ │ │ ├── set.rb
│ │ │ │ ├── unary.rb
│ │ │ │ └── xor.rb
│ │ │ ├── operators.rb
│ │ │ ├── predicates.rb
│ │ │ ├── result.rb
│ │ │ ├── rule/
│ │ │ │ ├── interface.rb
│ │ │ │ └── predicate.rb
│ │ │ ├── rule.rb
│ │ │ ├── rule_compiler.rb
│ │ │ └── version.rb
│ │ └── logic.rb
│ └── dry-logic.rb
├── repo-sync.yml
├── spec/
│ ├── integration/
│ │ ├── builder/
│ │ │ ├── operation_spec.rb
│ │ │ └── predicate_spec.rb
│ │ ├── result_spec.rb
│ │ └── rule_spec.rb
│ ├── shared/
│ │ ├── built_rule.rb
│ │ ├── operation.rb
│ │ ├── predicate.rb
│ │ ├── predicates.rb
│ │ └── rule.rb
│ ├── spec_helper.rb
│ ├── support/
│ │ ├── coverage.rb
│ │ ├── mutant.rb
│ │ ├── rspec.rb
│ │ └── warnings.rb
│ └── unit/
│ ├── builder_spec.rb
│ ├── operations/
│ │ ├── and_spec.rb
│ │ ├── attr_spec.rb
│ │ ├── check_spec.rb
│ │ ├── each_spec.rb
│ │ ├── implication_spec.rb
│ │ ├── key_spec.rb
│ │ ├── negation_spec.rb
│ │ ├── or_spec.rb
│ │ ├── set_spec.rb
│ │ └── xor_spec.rb
│ ├── predicates/
│ │ ├── array_spec.rb
│ │ ├── attr_spec.rb
│ │ ├── bool_spec.rb
│ │ ├── bytesize_spec.rb
│ │ ├── case_spec.rb
│ │ ├── date_spec.rb
│ │ ├── date_time_spec.rb
│ │ ├── decimal_spec.rb
│ │ ├── empty_spec.rb
│ │ ├── eql_spec.rb
│ │ ├── even_spec.rb
│ │ ├── excluded_from_spec.rb
│ │ ├── excludes_spec.rb
│ │ ├── false_spec.rb
│ │ ├── filled_spec.rb
│ │ ├── float_spec.rb
│ │ ├── format_spec.rb
│ │ ├── gt_spec.rb
│ │ ├── gteq_spec.rb
│ │ ├── hash_spec.rb
│ │ ├── included_in_spec.rb
│ │ ├── includes_spec.rb
│ │ ├── int_spec.rb
│ │ ├── key_spec.rb
│ │ ├── lt_spec.rb
│ │ ├── lteq_spec.rb
│ │ ├── max_bytesize_spec.rb
│ │ ├── max_size_spec.rb
│ │ ├── min_bytesize_spec.rb
│ │ ├── min_size_spec.rb
│ │ ├── none_spec.rb
│ │ ├── not_eql_spec.rb
│ │ ├── number_spec.rb
│ │ ├── odd_spec.rb
│ │ ├── respond_to_spec.rb
│ │ ├── size_spec.rb
│ │ ├── str_spec.rb
│ │ ├── time_spec.rb
│ │ ├── true_spec.rb
│ │ ├── type_spec.rb
│ │ ├── uri_spec.rb
│ │ ├── uuid_v1_spec.rb
│ │ ├── uuid_v2_spec.rb
│ │ ├── uuid_v3_spec.rb
│ │ ├── uuid_v4_spec.rb
│ │ ├── uuid_v5_spec.rb
│ │ ├── uuid_v6_spec.rb
│ │ ├── uuid_v7_spec.rb
│ │ └── uuid_v8_spec.rb
│ ├── predicates_spec.rb
│ ├── rule/
│ │ └── predicate_spec.rb
│ ├── rule_compiler_spec.rb
│ └── rule_spec.rb
└── zizmor.yml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
github: hanami
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.md
================================================
---
name: "\U0001F41B Bug report"
about: See CONTRIBUTING.md for more information
title: ''
labels: bug, help wanted
assignees: ''
---
## Describe the bug
A clear and concise description of what the bug is.
## To Reproduce
Provide detailed steps to reproduce, **an executable script would be best**.
## Expected behavior
A clear and concise description of what you expected to happen.
## My environment
- Ruby version: ...
- OS: ...
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Community support
url: https://discourse.hanamirb.org
about: Please ask and answer questions here.
================================================
FILE: .github/SUPPORT.md
================================================
## Support
If you need help with any of the Hanami, Dry or Rom libraries, feel free to ask questions on our [discussion forum](https://discourse.hanamirb.org/). This is the best place to seek help. Make sure to search for a potential solution in past threads before posting your question. Thanks! :heart:
================================================
FILE: .github/workflows/ci-lint.yml
================================================
name: CI lint
on:
push:
branches: ["main", "release-*", "ci/*"]
tags: ["v*"]
paths:
- ".github/**"
pull_request:
branches: ["main", "release-*"]
paths:
- ".github/**"
schedule:
- cron: "0 0 * * 0" # every Sunday at midnight
permissions: {}
jobs:
zizmor:
name: Run zizmor
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d
with:
advanced-security: false
================================================
FILE: .github/workflows/ci.yml
================================================
# This file is synced from hanakai-rb/repo-sync
name: CI
run-name: ${{ github.ref_type == 'tag' && format('Release {0}', github.ref_name) || 'CI' }}
on:
push:
branches: ["main", "release-*", "ci/*"]
tags: ["v*"]
pull_request:
branches: ["main", "release-*"]
schedule:
- cron: "30 4 * * *"
permissions:
contents: read
jobs:
tests:
name: Tests (Ruby ${{ matrix.ruby }})
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.optional || false }}
strategy:
fail-fast: false
matrix:
ruby:
- "4.0"
- "3.4"
- "3.3"
- "jruby"
include:
- ruby: "4.0"
coverage: "true"
env:
COVERAGE: ${{ matrix.coverage }}
steps:
- name: Checkout
uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98
with:
persist-credentials: false
- name: Install package dependencies
run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends $APT_DEPS"
- name: Set up Ruby
uses: ruby/setup-ruby@0cb964fd540e0a24c900370abf38a33466142735 # zizmor: ignore[cache-poisoning]
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run all tests
id: test
run: |
status=0
bundle exec rake || status=$?
if [ ${status} -ne 0 ] && [ "${{ matrix.optional }}" == "true" ]; then
echo "::warning::Optional matrix job failed."
echo "optional_fail=true" >> "${GITHUB_OUTPUT}"
echo "optional_fail_status=${status}" >> "${GITHUB_OUTPUT}"
exit 0 # Ignore error here to keep the green checkmark
fi
exit ${status}
- name: Create optional failure comment
if: ${{ matrix.optional && github.event.pull_request }}
uses: hanakai-rb/repo-sync/pr-comment-artifact@main
with:
name: ci-ruby-${{ matrix.ruby }}
pr-number: ${{ github.event.pull_request.number }}
comment-tag: ruby-${{ matrix.ruby }}-optional-failure
message: "ℹ️ Optional job failed: Ruby ${{ matrix.ruby }}"
mode: ${{ steps.test.outputs.optional_fail == 'true' && 'upsert' || 'delete' }}
workflow-keepalive:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- uses: liskin/gh-workflow-keepalive@7a9194bad497f0b993708eeaf10fc0a2d726eb71
release:
runs-on: ubuntu-latest
if: github.ref_type == 'tag'
needs: tests
steps:
- name: Trigger release workflow
uses: actions/github-script@450193c5abd4cdb17ba9f3ffcfe8f635c4bb6c2a
with:
github-token: ${{ secrets.RELEASE_MACHINE_DISPATCH_TOKEN }}
script: |
const tag = context.ref.replace("refs/tags/", "");
const repo = context.repo.owner + "/" + context.repo.repo;
const tagMessage = await github.rest.git.getTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag_sha: context.sha
}).then(res => res.data.message).catch(() => "");
const announce = /(skip-announce|no-announce)/i.test(tagMessage) ? "false" : "true";
await github.rest.actions.createWorkflowDispatch({
owner: "hanakai-rb",
repo: "release-machine",
workflow_id: "release.yml",
ref: "main",
inputs: { repo, tag, announce }
});
const workflowUrl = "https://github.com/hanakai-rb/release-machine/actions/workflows/release.yml";
await core.summary
.addHeading("Release Triggered")
.addRaw(`Triggered release workflow for <code>${tag}</code>`)
.addLink("View release workflow", workflowUrl)
.write();
================================================
FILE: .github/workflows/pr-comments.yml
================================================
# This file is synced from hanakai-rb/repo-sync
# Downloads comment artifacts from completed workflows and posts them to PRs. This allows source
# workflows to run with read-only permissions on fork PRs while still posting comments via this
# privileged workflow that runs in the base repo context.
#
# Comment artifacts should be generated using the `hanakai-rb/repo-sync/pr-comment-artifact@main`
# action.
name: PR comments
on: # zizmor: ignore[dangerous-triggers]
workflow_run:
workflows: ["CI"]
types:
- completed
permissions:
pull-requests: write
jobs:
post-comments:
runs-on: ubuntu-latest
permissions:
pull-requests: write
if: github.event.workflow_run.event == 'pull_request'
steps:
- name: Post comments
uses: hanakai-rb/repo-sync/pr-comments-from-artifacts@main
with:
workflow-run-id: ${{ github.event.workflow_run.id }}
================================================
FILE: .github/workflows/repo-sync-preview.yml
================================================
name: Repo-sync preview
on: # zizmor: ignore[dangerous-triggers]
workflow_run:
workflows: ["CI", "RuboCop", "CI lint"]
types: [completed]
branches:
- "ci/repo-sync-preview-*"
jobs:
report:
runs-on: ubuntu-latest
permissions: {}
if: >
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_repository.fork == false
steps:
- name: Dispatch status to repo-sync
uses: actions/github-script@450193c5abd4cdb17ba9f3ffcfe8f635c4bb6c2a
with:
github-token: ${{ secrets.REPO_SYNC_DISPATCH_TOKEN }}
script: |
const { BRANCH, REPO, WORKFLOW, STATUS, RUN_URL } = process.env;
await github.rest.actions.createWorkflowDispatch({
owner: "hanakai-rb",
repo: "repo-sync",
workflow_id: "aggregate-preview-status.yml",
ref: "main",
inputs: {
pr_number: BRANCH.replace("ci/repo-sync-preview-", ""),
repo_name: REPO,
workflow_name: WORKFLOW,
status: STATUS,
run_url: RUN_URL
}
});
env:
BRANCH: ${{ github.event.workflow_run.head_branch }}
REPO: ${{ github.repository }}
WORKFLOW: ${{ github.event.workflow_run.name }}
STATUS: ${{ github.event.workflow_run.conclusion }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
================================================
FILE: .github/workflows/rubocop.yml
================================================
# frozen_string_literal: true
# This file is synced from hanakai-rb/repo-sync
name: RuboCop
on:
push:
branches: ["main", "release-*", "ci/*"]
tags: ["v*"]
pull_request:
branches: ["main", "release-*"]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
env:
BUNDLE_ONLY: tools
steps:
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98
with:
persist-credentials: false
- name: Set up Ruby 4.0
uses: ruby/setup-ruby@0cb964fd540e0a24c900370abf38a33466142735 # zizmor: ignore[cache-poisoning]
with:
ruby-version: 4.0
bundler-cache: true
- name: Run RuboCop
run: bundle exec rubocop --parallel
================================================
FILE: .gitignore
================================================
.DS_Store
coverage
/.bundle
vendor/bundle
tmp/
pkg/
.idea/
Gemfile.lock
/.rubocop-*
================================================
FILE: .rspec
================================================
--color
--require spec_helper
--order random
--warnings
================================================
FILE: .rubocop.yml
================================================
# This file is synced from hanakai-rb/repo-sync
inherit_from:
- https://raw.githubusercontent.com/hanakai-rb/repo-sync/main/rubocop/rubocop.yml
<% if File.exist?(".rubocop.local.yml") %>
- .rubocop.local.yml
<% end %>
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Break Versioning](https://www.taoensso.com/break-versioning).
## [Unreleased]
### Changed
- Set minumum Ruby version to 3.2 (@alassek)
## [1.6.0] - 2025-01-04
### Added
- Support for more UUID formats (via #110) (@Ptico)
### Fixed
- Fix NoMethodError when trying to get AST of a Builder's result. (via #107) (@estum)
### Changed
- Set 3.1 as minimum ruby version (@flash-gordon)
[Compare v1.5.0...v1.6.0](https://github.com/dry-rb/dry-logic/compare/v1.5.0...v1.6.0)
## [1.5.0] - 2022-11-24
### Added
- `uri_rfc3986?` predicate that uses a better regexp than `uri?` (see #94 for more details) (@hieuk09)
### Changed
- Made `Predicates.respond_to?` compatible with `Object#respond_to?` (via #105) (@solnic)
- Made `Predicates.eql?` compatible with `Object#eql?` (via #106) (@solnic)
[Compare v1.4.0...v1.5.0](https://github.com/dry-rb/dry-logic/compare/v1.4.0...v1.5.0)
## [1.4.0] - 2022-11-04
### Changed
- Updated to dry-core 1.0 (@flash-gordon + @solnic)
[Compare v1.3.0...v1.4.0](https://github.com/dry-rb/dry-logic/compare/v1.3.0...v1.4.0)
## [1.3.0] - 2022-10-15
### Changed
- Use zeitwerk for auto-loading (@solnic + @flash-gordon)
[Compare v1.2.0...v1.3.0](https://github.com/dry-rb/dry-logic/compare/v1.2.0...v1.3.0)
## [1.2.0] - 2021-04-26
### Added
- Add predicate and operation builder DSL (@oleander)
[Compare v1.1.1...v1.2.0](https://github.com/dry-rb/dry-logic/compare/v1.1.1...v1.2.0)
## [1.1.1] - 2021-04-14
### Fixed
- Fixed a crash under jruby caused by arg splatting in Binary operations (@flash-gordon)
[Compare v1.1.0...v1.1.1](https://github.com/dry-rb/dry-logic/compare/v1.1.0...v1.1.1)
## [1.1.0] - 2020-12-26
### Fixed
- Nested `Check` operations no longer crash under MRI 3.0 (@oleander)
### Changed
- Switched to equalizer from dry-core (@solnic)
[Compare v1.0.8...v1.1.0](https://github.com/dry-rb/dry-logic/compare/v1.0.8...v1.1.0)
## [1.0.8] - 2020-09-28
### Fixed
- Better Ruby 3 support with fixed specialization for rules of negative arity (@flash-gordon)
[Compare v1.0.7...v1.0.8](https://github.com/dry-rb/dry-logic/compare/v1.0.7...v1.0.8)
## [1.0.7] - 2020-08-13
### Added
- A new `uri?` predicate that you can use to verify `URI` strings, ie `uri?("https", "https://dry-rb.org")` (@nerburish)
- New predicates: `uuid_v1?`, `uuid_v2?`, `uuid_v3?` and `uuid_v5?` (via #75) (@jamesbrauman)
[Compare v1.0.6...v1.0.7](https://github.com/dry-rb/dry-logic/compare/v1.0.6...v1.0.7)
## [1.0.6] - 2020-02-10
### Fixed
- Made the regexp used by `uuid_v4?` more secure (@kml)
[Compare v1.0.5...v1.0.6](https://github.com/dry-rb/dry-logic/compare/v1.0.5...v1.0.6)
## [1.0.5] - 2019-11-07
### Fixed
- Make `format?` tolerant to `nil` values. It already worked like that before, but starting Ruby 2.7 it would produce warnings. Now it won't. Don't rely on this behavior, it's only added to make tests pass in dry-schema. Use explicit type checks instead (@flash-gordon)
[Compare v1.0.4...v1.0.5](https://github.com/dry-rb/dry-logic/compare/v1.0.4...v1.0.5)
## [1.0.4] - 2019-11-06
### Fixed
- Fix keyword warnings (@flash-gordon)
[Compare v1.0.3...v1.0.4](https://github.com/dry-rb/dry-logic/compare/v1.0.3...v1.0.4)
## [1.0.3] - 2019-08-01
### Added
- `bytesize?` predicate (@bmalinconico)
- `min_bytesize?` predicate (@bmalinconico)
- `max_bytesize? predicate (@bmalinconico)
### Changed
- Min ruby version was set to `>= 2.4.0` (@flash-gordon)
[Compare v1.0.2...v1.0.3](https://github.com/dry-rb/dry-logic/compare/v1.0.2...v1.0.3)
## [1.0.2] - 2019-06-14
Re-pushed 1.0.1 after dry-schema 1.2.0 release.
[Compare v1.0.1...v1.0.2](https://github.com/dry-rb/dry-logic/compare/v1.0.1...v1.0.2)
## [1.0.1] - 2019-06-04
This release was removed from rubygems because it broke dry-schema.
### Added
- `uuid_v4?` predicate (radar)
- `respond_to?` predicate (waiting-for-dev)
[Compare v1.0.0...v1.0.1](https://github.com/dry-rb/dry-logic/compare/v1.0.0...v1.0.1)
## [1.0.0] - 2019-04-23
### Changed
- Version bump to `1.0.0` (flash-gordon)
[Compare v0.6.1...v1.0.0](https://github.com/dry-rb/dry-logic/compare/v0.6.1...v1.0.0)
## [0.6.1] - 2019-04-18
### Fixed
- Fix a regression in dry-validation 0.x for argument-less predicates (flash-gordon)
[Compare v0.6.0...v0.6.1](https://github.com/dry-rb/dry-logic/compare/v0.6.0...v0.6.1)
## [0.6.0] - 2019-04-04
### Added
- Generating hints can be disabled by building `Operations::And` with `hints: false` option set (solnic)
### Changed
- `Rule` construction has been optimized so that currying and application is multiple-times faster (flash-gordon)
[Compare v0.5.0...v0.6.0](https://github.com/dry-rb/dry-logic/compare/v0.5.0...v0.6.0)
## [0.5.0] - 2019-01-29
### Added
- `:nil?` predicate (`none?` is now an alias) (solnic)
### Fixed
- `Operation::Key#ast` will now return a correct AST with non-Undefined inputs (solnic)
[Compare v0.4.2...v0.5.0](https://github.com/dry-rb/dry-logic/compare/v0.4.2...v0.5.0)
## [0.4.2] - 2017-09-15
### Added
- New `:case?` predicate matches a value against the given object with `#===` (flash-gordon)
- New `:is?` predicate checks objects identity (using `#equal?`) (flash-gordon)
### Fixed
- A bug with using custom predicates within a standalone module in `dry-validation` (flash-gordon)
[Compare v0.4.1...v0.4.2](https://github.com/dry-rb/dry-logic/compare/v0.4.1...v0.4.2)
## [0.4.1] - 2017-01-23
### Fixed
- Warnings on MRI 2.4.0 are gone (jtippett)
### Changed
- Predicates simply reuse other predicate methods instead of referring to them via `#[]` (georgemillo)
[Compare v0.4.0...v0.4.1](https://github.com/dry-rb/dry-logic/compare/v0.4.0...v0.4.1)
## [0.4.0] - 2016-09-21
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.
### Added
- `Rule#[]` which applies a rule and always returns `true` or `false` (solnic)
- `Rule#bind` which returns a rule with its predicate bound to a given object (solnic)
- `Rule#eval_args` which evaluates unbound-methods-args in the context of a given object (solnic)
- `Logic.Rule` builder function (solnic)
- Nice `#inspect` on rules and operation objects (solnic)
### Changed
- [BRAEKING] New result API (solnic)
- [BREAKING] `Predicate` is now `Rule::Predicate` (solnic)
- [BREAKING] `Rule::Conjunction` is now `Operation::And` (solnic)
- [BREAKING] `Rule::Disjunction` is now `Operation::Or` (solnic)
- [BREAKING] `Rule::ExlusiveDisjunction` is now `Operation::Xor` (solnic)
- [BREAKING] `Rule::Implication` is now `Operation::Implication` (solnic)
- [BREAKING] `Rule::Set` is now `Operation::Set` (solnic)
- [BREAKING] `Rule::Each` is now `Operation::Each` (solnic)
- [BREAKING] `Rule.new` accepts a predicate function as its first arg now (solnic)
- [BREAKING] `Rule#name` is now `Rule#id` (solnic)
- `Rule#parameters` is public now (solnic)
[Compare v0.3.0...v0.4.0](https://github.com/dry-rb/dry-logic/compare/v0.3.0...v0.4.0)
## [0.3.0] - 2016-07-01
### Added
- `:type?` predicate imported from dry-types (solnic)
- `Rule#curry` interface (solnic)
### Changed
- Predicates AST now includes information about args (names & possible values) (fran-worley + solnic)
- Predicates raise errors when they are called with invalid arity (fran-worley + solnic)
- Rules no longer evaluate input twice when building result objects (solnic)
[Compare v0.2.3...v0.3.0](https://github.com/dry-rb/dry-logic/compare/v0.2.3...v0.3.0)
## [0.2.3] - 2016-05-11
### Added
- `not_eql?`, `includes?`, `excludes?` predicates (fran-worley)
### Changed
- Renamed `inclusion?` to `included_in?` and deprecated `inclusion?` (fran-worley)
- Renamed `exclusion?` to `excluded_from?` and deprecated `exclusion?` (fran-worley)
[Compare v0.2.2...v0.2.3](https://github.com/dry-rb/dry-logic/compare/v0.2.2...v0.2.3)
## [0.2.2] - 2016-03-30
### Added
- `number?`, `odd?`, `even?` predicates (fran-worley)
[Compare v0.2.1...v0.2.2](https://github.com/dry-rb/dry-logic/compare/v0.2.1...v0.2.2)
## [0.2.1] - 2016-03-20
### Fixed
- Result AST for `Rule::Each` correctly maps elements with eql inputs (solnic)
[Compare v0.2.0...v0.2.1](https://github.com/dry-rb/dry-logic/compare/v0.2.0...v0.2.1)
## [0.2.0] - 2016-03-11
### Changed
- Entire AST has been redefined (solnic)
[Compare v0.1.4...v0.2.0](https://github.com/dry-rb/dry-logic/compare/v0.1.4...v0.2.0)
## [0.1.4] - 2016-01-27
### Added
- Support for hash-names in `Check` and `Result` which can properly resolve input
from nested results (solnic)
[Compare v0.1.3...v0.1.4](https://github.com/dry-rb/dry-logic/compare/v0.1.3...v0.1.4)
## [0.1.3] - 2016-01-27
### Added
- Support for resolving input from `Rule::Result` (solnic)
### Changed
- `Check` and `Result` carry original input(s) (solnic)
[Compare v0.1.2...v0.1.3](https://github.com/dry-rb/dry-logic/compare/v0.1.2...v0.1.3)
## [0.1.2] - 2016-01-19
### Fixed
- `xor` returns wrapped results when used against another result-rule (solnic)
[Compare v0.1.1...v0.1.2](https://github.com/dry-rb/dry-logic/compare/v0.1.1...v0.1.2)
## [0.1.1] - 2016-01-18
### Added
- `Rule::Attr` which can be applied to a data object with attr readers (SunnyMagadan)
- `Rule::Result` which can be applied to a result object (solnic)
- `true?` and `false?` predicates (solnic)
[Compare v0.1.0...v0.1.1](https://github.com/dry-rb/dry-logic/compare/v0.1.0...v0.1.1)
## [0.1.0] - 2016-01-11
Code extracted from dry-validation 0.4.1
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We pledge to make our community welcoming, safe, and equitable for all.
We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant.
## Encouraged Behaviors
While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language.
With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including:
1. Respecting the **purpose of our community**, our activities, and our ways of gathering.
2. Engaging **kindly and honestly** with others.
3. Respecting **different viewpoints** and experiences.
4. **Taking responsibility** for our actions and contributions.
5. Gracefully giving and accepting **constructive feedback**.
6. Committing to **repairing harm** when it occurs.
7. Behaving in other ways that promote and sustain the **well-being of our community**.
## Restricted Behaviors
We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct.
1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop.
2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people.
3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits.
4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community.
5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission.
6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group.
7. Behaving in other ways that **threaten the well-being** of our community.
### Other Restrictions
1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions.
2. **Failing to credit sources.** Not properly crediting the sources of content you contribute.
3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community.
4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors.
## Reporting an Issue
Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm.
When an incident does occur, it is important to report it promptly. To report a possible violation, email conduct@hanakai.org.
Community Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution.
## Addressing and Repairing Harm
If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped.
1) Warning
1) Event: A violation involving a single incident or series of incidents.
2) Consequence: A private, written warning from the Community Moderators.
3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations.
2) Temporarily Limited Activities
1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation.
2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members.
3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over.
3) Temporary Suspension
1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation.
2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions.
3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted.
4) Permanent Ban
1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member.
2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior.
3) Repair: There is no possible repair in cases of this severity.
This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/).
Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/)
For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion).
================================================
FILE: CONTRIBUTING.md
================================================
# Issue guidelines
## Reporting bugs
If you’ve found a bug, please report an issue and describe 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.
## Reporting feature requests
Report a feature request **only after discussing it first on [our forum](https://discourse.hanamirb.org)** and having it accepted. Please provide a concise description of the feature.
## Reporting questions, support requests, ideas, concerns etc.
**Please don’t.** Use [our forum](https://discourse.hanamirb.org) instead.
# Pull request guidelines
A pull request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc.
Other requirements:
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.
2. Follow the style conventions of the surrounding code. In most cases, this is standard ruby style.
3. Add API documentation if it's a new feature.
4. Update API documentation if it changes an existing feature.
5. Bonus points for sending a PR which updates user documentation in our [site repository](https://github.com/hanakai-rb/site).
# Asking for help
If these guidelines aren't helpful, and you're stuck, please post a message on [our forum](https://discourse.dry-rb.org) or [find us in chat](https://discord.gg/KFCxDmk3JQ).
================================================
FILE: Gemfile
================================================
# frozen_string_literal: true
source "https://rubygems.org"
eval_gemfile "Gemfile.devtools"
gemspec
group :tools do
gem "benchmark-ips", platform: :mri
# gem "hotch", platform: :mri
end
================================================
FILE: Gemfile.devtools
================================================
# frozen_string_literal: true
# This file is synced from hanakai-rb/repo-sync
gem "rake", ">= 12.3.3"
group :test do
gem "simplecov", require: false, platforms: :ruby
gem "simplecov-cobertura", require: false, platforms: :ruby
gem "rexml", require: false
gem "warning"
end
group :tools do
gem "rubocop"
end
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015-2026 Hanakai team
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
<!--- This file is synced from hanakai-rb/repo-sync -->
[actions]: https://github.com/dry-rb/dry-logic/actions
[chat]: https://discord.gg/naQApPAsZB
[forum]: https://discourse.hanamirb.org
[rubygem]: https://rubygems.org/gems/dry-logic
# dry-logic [][rubygem] [][actions]
[][forum]
[][chat]
## Links
- [User documentation](https://dry-rb.org/gems/dry-logic)
- [API documentation](http://rubydoc.info/gems/dry-logic)
- [Forum](https://discourse.dry-rb.org)
## License
See `LICENSE` file.
================================================
FILE: Rakefile
================================================
#!/usr/bin/env rake
# frozen_string_literal: true
require "bundler/gem_tasks"
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
require "rspec/core"
require "rspec/core/rake_task"
task default: :spec
desc "Run all specs in spec directory"
RSpec::Core::RakeTask.new(:spec)
================================================
FILE: benchmarks/builder.rb
================================================
# frozen_string_literal: true
require "benchmark/ips"
require "dry/logic"
require "dry/logic/builder"
def regular
user_present = Dry::Logic::Rule::Predicate.new(Dry::Logic::Predicates[:key?]).curry(:user)
min_age = Dry::Logic::Rule::Predicate.new(Dry::Logic::Predicates[:gt?]).curry(18)
has_min_age = Dry::Logic::Operations::Key.new(min_age, name: %i[user age])
user_present & has_min_age
end
def builder
Dry::Logic::Builder.call do
key?(:user) & key(name: %i[user age]) { gt?(18) }
end
end
Benchmark.ips do |x|
x.report("builder") do
builder.(user: {age: 19})
builder.(user: {age: 18})
end
x.report("regular") do
regular.(user: {age: 18})
regular.(user: {age: 19})
end
x.compare!
end
================================================
FILE: benchmarks/rule_application.rb
================================================
# frozen_string_literal: true
require_relative "setup"
unless Dry::Logic::Rule.respond_to?(:build)
Dry::Logic::Rule.singleton_class.alias_method(:build, :new)
end
predicates = Dry::Logic::Predicates
type_check = Dry::Logic::Rule.build(predicates[:type?]).curry(Integer)
int_check = Dry::Logic::Rule.build(predicates[:int?])
key_check = Dry::Logic::Rule.build(predicates[:key?]).curry(:user)
with_user = { user: {} }
without_user = {}
comparison = Dry::Logic::Rule.build(predicates[:gteq?]).curry(18)
Benchmark.ips do |x|
x.report("type check - success") { type_check.(0) }
x.report("type check - failure") { type_check.("0") }
x.report("int check - success") { int_check.(0) }
x.report("int check - failure") { int_check.("0") }
x.report("key check - success") { key_check.(with_user) }
x.report("key check - failure") { key_check.(without_user) }
x.report("comparison - success") { comparison.(20) }
x.report("comparison - failure") { comparison.(17) }
x.compare!
end
================================================
FILE: benchmarks/setup.rb
================================================
# frozen_string_literal: true
require "benchmark/ips"
require "hotch"
ENV["HOTCH_VIEWER"] ||= "open"
require "dry/logic"
require "dry/logic/predicates"
def profile(&block)
Hotch(filter: "Dry", &block)
end
================================================
FILE: bin/.gitkeep
================================================
================================================
FILE: bin/console
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true
require "bundler/setup"
require "dry/logic"
require "dry/logic/predicates"
# rubocop:disable Style/MixinUsage
include Dry::Logic
# rubocop:enable Style/MixinUsage
require "irb"
IRB.start
================================================
FILE: dry-logic.gemspec
================================================
# frozen_string_literal: true
# This file is synced from hanakai-rb/repo-sync. To update it, edit repo-sync.yml.
lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "dry/logic/version"
Gem::Specification.new do |spec|
spec.name = "dry-logic"
spec.authors = ["Hanakai team"]
spec.email = ["info@hanakai.org"]
spec.license = "MIT"
spec.version = Dry::Logic::VERSION.dup
spec.summary = "Predicate logic with rule composition"
spec.description = spec.summary
spec.homepage = "https://dry-rb.org/gems/dry-logic"
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-logic.gemspec", "lib/**/*"]
spec.bindir = "exe"
spec.executables = Dir["exe/*"].map { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.extra_rdoc_files = ["README.md", "CHANGELOG.md", "LICENSE"]
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-logic/blob/main/CHANGELOG.md"
spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-logic"
spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-logic/issues"
spec.metadata["funding_uri"] = "https://github.com/sponsors/hanami"
spec.required_ruby_version = ">= 3.3"
spec.add_runtime_dependency "bigdecimal"
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
spec.add_runtime_dependency "dry-core", "~> 1.1"
spec.add_runtime_dependency "zeitwerk", "~> 2.6"
spec.add_development_dependency "bundler"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"
end
================================================
FILE: examples/basic.rb
================================================
# frozen_string_literal: true
require "dry/logic"
require "dry/logic/predicates"
# rubocop:disable Style/MixinUsage
include Dry::Logic
# rubocop:enable Style/MixinUsage
user_present = Rule::Predicate.build(Predicates[:key?]).curry(:user)
has_min_age = Operations::Key.new(Rule::Predicate.build(Predicates[:gt?]).curry(18),
name: %i[user age])
user_rule = user_present & has_min_age
puts user_rule.(user: {age: 19}).success?
puts user_rule.(user: {age: 18}).success?
================================================
FILE: lib/dry/logic/appliable.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Appliable
def id
options[:id]
end
def result
options[:result]
end
def applied?
!result.nil?
end
def success?
result.equal?(true)
end
def failure?
!success?
end
def to_ast
if applied? && id
[success? ? :success : :failure, [id, ast]]
else
ast
end
end
end
end
end
================================================
FILE: lib/dry/logic/builder.rb
================================================
# frozen_string_literal: true
require "singleton"
require "delegate"
module Dry
module Logic
module Builder
IGNORED_OPERATIONS = %i[
Abstract
Binary
Unary
].freeze
IGNORED_PREDICATES = [
:predicate
].freeze
# Predicate and operation builder
#
# @block [Proc]
# @return [Builder::Result]
# @example Check if input is zero
# is_zero = Dry::Logic::Builder.call do
# negation { lt?(0) ^ gt?(0) }
# end
#
# p is_zero.call(1) # => false
# p is_zero.call(0) # => true
# p is_zero.call(-1) # => false
def call(&)
Context.instance.call(&)
end
module_function :call
alias_method :build, :call
public :call, :build
class Context
include Dry::Logic
include Singleton
module Predicates
include Logic::Predicates
end
# @see Builder#call
def call(&)
instance_eval(&)
end
# Defines custom predicate
#
# @name [Symbol] Name of predicate
# @Context [Proc]
def predicate(name, context = nil, &block)
if singleton_class.method_defined?(name)
singleton_class.undef_method(name)
end
predicate = Rule::Predicate.new(context || block)
define_singleton_method(name) do |*args|
predicate.curry(*args)
end
end
# Defines methods for operations and predicates
def initialize
Operations.constants(false).each do |name|
next if IGNORED_OPERATIONS.include?(name)
operation = Operations.const_get(name)
define_singleton_method(name.downcase) do |*args, **kwargs, &block|
operation.new(*call(&block), *args, **kwargs)
end
end
Predicates::Methods.instance_methods(false).each do |name|
unless IGNORED_PREDICATES.include?(name)
predicate(name, Predicates[name])
end
end
end
end
end
end
end
================================================
FILE: lib/dry/logic/evaluator.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
class Evaluator
include Dry::Equalizer(:path)
attr_reader :path
class Set
include Dry::Equalizer(:evaluators)
attr_reader :evaluators
def self.new(paths)
super(paths.map { |path| Evaluator::Key.new(path) })
end
def initialize(evaluators)
@evaluators = evaluators
end
def call(input)
evaluators.map { |evaluator| evaluator[input] }
end
alias_method :[], :call
end
class Key < Evaluator
def call(input)
path.reduce(input) { |a, e| a[e] }
end
alias_method :[], :call
end
class Attr < Evaluator
def call(input)
path.reduce(input) { |a, e| a.public_send(e) }
end
alias_method :[], :call
end
def initialize(path)
@path = Array(path)
end
end
end
end
================================================
FILE: lib/dry/logic/operations/abstract.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Abstract
include Core::Constants
include ::Dry::Equalizer(:rules, :options)
include Operators
attr_reader :rules
attr_reader :options
def initialize(*rules, **options)
@rules = rules
@options = options
end
def id
options[:id]
end
def curry(*args)
new(rules.map { |rule| rule.curry(*args) }, **options)
end
def new(rules, **new_options)
self.class.new(*rules, **options, **new_options)
end
def with(new_options)
new(rules, **options, **new_options)
end
def to_ast
ast
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/and.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class And < Binary
attr_reader :hints
def initialize(*, **)
super
@hints = options.fetch(:hints, true)
end
def type
:and
end
alias_method :operator, :type
def call(input)
left_result = left.(input)
if left_result.success?
right_result = right.(input)
if right_result.success?
Result::SUCCESS
else
Result.new(false, id) { right_result.ast(input) }
end
else
Result.new(false, id) do
left_ast = left_result.to_ast
hints ? [type, [left_ast, [:hint, right.ast(input)]]] : left_ast
end
end
end
def [](input)
left[input] && right[input]
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/attr.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Attr < Key
def self.evaluator(name)
Evaluator::Attr.new(name)
end
def type
:attr
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/binary.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Binary < Abstract
attr_reader :left
attr_reader :right
def initialize(left, right, **options)
super
@left = left
@right = right
end
def ast(input = Undefined)
[type, [left.ast(input), right.ast(input)]]
end
def to_s
"#{left} #{operator.to_s.upcase} #{right}"
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/check.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Check < Unary
attr_reader :evaluator
def self.new(rule, **options)
if options[:evaluator]
super
else
keys = options.fetch(:keys)
evaluator = Evaluator::Set.new(keys)
super(rule, **options, evaluator: evaluator)
end
end
def initialize(*rules, **options)
super
@evaluator = options[:evaluator]
end
def type
:check
end
def call(input)
*head, tail = evaluator[input]
result = rule.curry(*head).(tail)
if result.success?
Result::SUCCESS
else
Result.new(false, id) { [type, [options[:keys], result.to_ast]] }
end
end
def [](input)
rule[*evaluator[input].reverse]
end
def ast(input = Undefined)
[type, [options[:keys], rule.ast(input)]]
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/each.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Each < Unary
def type
:each
end
def call(input)
results = input.map { |element| rule.(element) }
success = results.all?(&:success?)
Result.new(success, id) do
failures = results
.map
.with_index { |result, idx| [:key, [idx, result.ast(input[idx])]] if result.failure? }
.compact
[:set, failures]
end
end
def [](arr)
arr.all? { |input| rule[input] }
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/implication.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Implication < Binary
def type
:implication
end
def operator
:then
end
def call(input)
left_result = left.(input)
if left_result.success?
right_result = right.(input)
Result.new(right_result.success?, id) { right_result.to_ast }
else
Result::SUCCESS
end
end
def [](input)
if left[input]
right[input]
else
true
end
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/key.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Key < Unary
attr_reader :evaluator
attr_reader :path
def self.new(rules, **options)
if options[:evaluator]
super
else
name = options.fetch(:name)
eval = options.fetch(:evaluator, evaluator(name))
super(rules, **options, evaluator: eval, path: name)
end
end
def self.evaluator(name)
Evaluator::Key.new(name)
end
def initialize(*rules, **options)
super
@evaluator = options[:evaluator]
@path = options[:path]
end
def type
:key
end
def call(hash)
input = evaluator[hash]
result = rule.(input)
if result.success?
Result::SUCCESS
else
Result.new(false, path) { [type, [path, result.to_ast]] }
end
end
def [](hash)
rule[evaluator[hash]]
end
def ast(input = Undefined)
if input.equal?(Undefined) || !input.is_a?(Hash)
[type, [path, rule.ast]]
else
[type, [path, rule.ast(evaluator[input])]]
end
end
def to_s
"#{type}[#{path}](#{rule})"
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/negation.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Negation < Unary
def type
:not
end
def call(input)
Result.new(rule.(input).failure?, id) { ast(input) }
end
def [](input)
!rule[input]
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/or.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Or < Binary
def type
:or
end
alias_method :operator, :type
def call(input)
left_result = left.(input)
if left_result.success?
Result::SUCCESS
else
right_result = right.(input)
if right_result.success?
Result::SUCCESS
else
Result.new(false, id) { [:or, [left_result.to_ast, right_result.to_ast]] }
end
end
end
def [](input)
left[input] || right[input]
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/set.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Set < Abstract
def type
:set
end
def call(input)
results = rules.map { |rule| rule.(input) }
success = results.all?(&:success?)
Result.new(success, id) do
[type, results.select(&:failure?).map(&:to_ast)]
end
end
def [](input)
rules.map { |rule| rule[input] }.all?
end
def ast(input = Undefined)
[type, rules.map { |rule| rule.ast(input) }]
end
def to_s
"#{type}(#{rules.map(&:to_s).join(", ")})"
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/unary.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Unary < Abstract
attr_reader :rule
def initialize(*rules, **options)
super
@rule = rules.first
end
def ast(input = Undefined)
[type, rule.ast(input)]
end
def to_s
"#{type}(#{rule})"
end
end
end
end
end
================================================
FILE: lib/dry/logic/operations/xor.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operations
class Xor < Binary
def type
:xor
end
alias_method :operator, :type
def call(input)
Result.new(self[input], id) { ast(input) }
end
def [](input)
left[input] ^ right[input]
end
def ast(input = Undefined)
[type, rules.map { |rule| rule.ast(input) }]
end
end
end
end
end
================================================
FILE: lib/dry/logic/operators.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
module Operators
def and(other)
Operations::And.new(self, other)
end
alias_method :&, :and
def or(other)
Operations::Or.new(self, other)
end
alias_method :|, :or
def xor(other)
Operations::Xor.new(self, other)
end
alias_method :^, :xor
def then(other)
Operations::Implication.new(self, other)
end
alias_method :>, :then
end
end
end
================================================
FILE: lib/dry/logic/predicates.rb
================================================
# frozen_string_literal: true
require "dry/core/constants"
require "bigdecimal"
require "bigdecimal/util"
require "date"
require "uri"
module Dry
module Logic
module Predicates
include ::Dry::Core::Constants
module Methods
def self.uuid_format(version)
::Regexp.new(<<~FORMAT.chomp, ::Regexp::IGNORECASE)
\\A[0-9A-F]{8}-[0-9A-F]{4}-#{version}[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\\z
FORMAT
end
UUIDv1 = uuid_format(1)
UUIDv2 = uuid_format(2)
UUIDv3 = uuid_format(3)
UUIDv4 = uuid_format(4)
UUIDv5 = uuid_format(5)
UUIDv6 = uuid_format(6)
UUIDv7 = uuid_format(7)
UUIDv8 = uuid_format(8)
def [](name)
method(name)
end
def type?(type, input) = input.is_a?(type)
def nil?(input) = input.nil?
alias_method :none?, :nil?
def key?(name, input) = input.key?(name)
def attr?(name, input) = input.respond_to?(name)
def empty?(input)
case input
when ::String, ::Array, ::Hash then input.empty?
when nil then true
else
false
end
end
def filled?(input) = !empty?(input)
def bool?(input) = input.equal?(true) || input.equal?(false)
def date?(input) = input.is_a?(::Date)
def date_time?(input) = input.is_a?(::DateTime)
def time?(input) = input.is_a?(::Time)
def number?(input)
true if Float(input)
rescue ::ArgumentError, ::TypeError
false
end
def int?(input) = input.is_a?(::Integer)
def float?(input) = input.is_a?(::Float)
def decimal?(input) = input.is_a?(::BigDecimal)
def str?(input) = input.is_a?(::String)
def hash?(input) = input.is_a?(::Hash)
def array?(input) = input.is_a?(::Array)
def odd?(input) = input.odd?
def even?(input) = input.even?
def lt?(num, input) = input < num
def gt?(num, input) = input > num
def lteq?(num, input) = !gt?(num, input)
def gteq?(num, input) = !lt?(num, input)
def size?(size, input)
case size
when ::Integer then size.equal?(input.size)
when ::Range, ::Array then size.include?(input.size)
else
raise ::ArgumentError, "+#{size}+ is not supported type for size? predicate."
end
end
def min_size?(num, input) = input.size >= num
def max_size?(num, input) = input.size <= num
def bytesize?(size, input)
case size
when ::Integer then size.equal?(input.bytesize)
when ::Range, ::Array then size.include?(input.bytesize)
else
raise ::ArgumentError, "+#{size}+ is not supported type for bytesize? predicate."
end
end
def min_bytesize?(num, input) = input.bytesize >= num
def max_bytesize?(num, input) = input.bytesize <= num
def inclusion?(list, input)
deprecated(:inclusion?, :included_in?)
included_in?(list, input)
end
def exclusion?(list, input)
deprecated(:exclusion?, :excluded_from?)
excluded_from?(list, input)
end
def included_in?(list, input) = list.include?(input)
def excluded_from?(list, input) = !list.include?(input)
def includes?(value, input)
if input.respond_to?(:include?)
input.include?(value)
else
false
end
rescue ::TypeError
false
end
def excludes?(value, input) = !includes?(value, input)
# This overrides Object#eql? so we need to make it compatible
def eql?(left, right = Undefined)
return super(left) if right.equal?(Undefined)
left.eql?(right)
end
def is?(left, right) = left.equal?(right)
def not_eql?(left, right) = !left.eql?(right)
def true?(value) = value.equal?(true)
def false?(value) = value.equal?(false)
def format?(regex, input) = !input.nil? && regex.match?(input)
def case?(pattern, input) = pattern === input # rubocop:disable Style/CaseEquality
def uuid_v1?(input) = format?(UUIDv1, input)
def uuid_v2?(input) = format?(UUIDv2, input)
def uuid_v3?(input) = format?(UUIDv3, input)
def uuid_v4?(input) = format?(UUIDv4, input)
def uuid_v5?(input) = format?(UUIDv5, input)
def uuid_v6?(input) = format?(UUIDv6, input)
def uuid_v7?(input) = format?(UUIDv7, input)
def uuid_v8?(input) = format?(UUIDv8, input)
if defined?(::URI::RFC2396_PARSER)
def uri?(schemes, input)
uri_format = ::URI::RFC2396_PARSER.make_regexp(schemes)
format?(uri_format, input)
end
else
def uri?(schemes, input)
uri_format = ::URI::DEFAULT_PARSER.make_regexp(schemes)
format?(uri_format, input)
end
end
def uri_rfc3986?(input) = format?(::URI::RFC3986_Parser::RFC3986_URI, input)
# This overrides Object#respond_to? so we need to make it compatible
def respond_to?(method, input = Undefined)
return super if input.equal?(Undefined)
input.respond_to?(method)
end
def predicate(name, &)
define_singleton_method(name, &)
end
def deprecated(name, in_favor_of)
Core::Deprecations.warn(
"#{name} predicate is deprecated and will " \
"be removed in the next major version\n" \
"Please use #{in_favor_of} predicate instead",
tag: "dry-logic",
uplevel: 3
)
end
end
extend Methods
def self.included(other)
super
other.extend(Methods)
end
end
# rubocop:enable Metrics/ModuleLength
end
end
================================================
FILE: lib/dry/logic/result.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
class Result
SUCCESS = ::Class.new {
def success?
true
end
def failure?
false
end
}.new.freeze
attr_reader :success
attr_reader :id
attr_reader :serializer
def initialize(success, id = nil, &block)
@success = success
@id = id
@serializer = block
end
def success?
success
end
def failure?
!success?
end
def type
success? ? :success : :failure
end
def ast(input = Undefined)
serializer.(input)
end
def to_ast
if id
[type, [id, ast]]
else
ast
end
end
def to_s
visit(to_ast)
end
private
def visit(ast)
__send__(:"visit_#{ast[0]}", ast[1])
end
def visit_predicate(node)
name, args = node
if args.empty?
name.to_s
else
"#{name}(#{args.map(&:last).map(&:inspect).join(", ")})"
end
end
def visit_and(node)
left, right = node
"#{visit(left)} AND #{visit(right)}"
end
def visit_or(node)
left, right = node
"#{visit(left)} OR #{visit(right)}"
end
def visit_xor(node)
left, right = node
"#{visit(left)} XOR #{visit(right)}"
end
def visit_not(node)
"not(#{visit(node)})"
end
def visit_hint(node)
visit(node)
end
end
end
end
================================================
FILE: lib/dry/logic/rule/interface.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
class Rule
class Interface < ::Module
SPLAT = ["*rest"].freeze
attr_reader :arity
attr_reader :curried
def initialize(arity, curried)
super()
@arity = arity
@curried = curried
if !variable_arity? && curried > arity
raise ArgumentError, "wrong number of arguments (#{curried} for #{arity})"
end
define_constructor if curried?
if constant?
define_constant_application
else
define_application
end
end
def constant? = arity.zero?
def variable_arity? = arity.negative?
def curried? = !curried.zero?
def unapplied
if variable_arity?
unapplied = arity.abs - 1 - curried
if unapplied.negative?
0
else
unapplied
end
else
arity - curried
end
end
def name
if constant?
"Constant"
else
arity_str =
if variable_arity?
"Variable#{arity.abs - 1}Arity"
else
"#{arity}Arity"
end
curried_str =
if curried?
"#{curried}Curried"
else
EMPTY_STRING
end
"#{arity_str}#{curried_str}"
end
end
def define_constructor
assignment =
if curried.equal?(1)
"@arg0 = @args[0]"
else
"#{curried_args.join(", ")} = @args"
end
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
def initialize(*) # def initialize(*)
super # super
#
#{assignment} # @arg0 = @args[0]
end # end
RUBY
end
def define_constant_application
module_exec do
def call(*)
if @predicate[]
Result::SUCCESS
else
Result.new(false, id) { ast }
end
end
def [](*)
@predicate[]
end
end
end
def define_application
splat = variable_arity? ? SPLAT : EMPTY_ARRAY
parameters = (unapplied_args + splat).join(", ")
application = "@predicate[#{(curried_args + unapplied_args + splat).join(", ")}]"
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
def call(#{parameters}) # def call(input0, input1, *rest)
if #{application} # if @predicate[@arg0, @arg1, input0, input1, *rest]
Result::SUCCESS # ::Dry::Logic::Result::Success
else # else
Result.new(false, id) { ast(#{parameters}) } # ::Dry::Logic::Result.new(false, id) { ast(input0, input1, *rest) }
end # end
end # end
#
def [](#{parameters}) # def [](@arg0, @arg1, input0, input1, *rest)
#{application} # @predicate[@arg0, @arg1, input0, input1, *rest]
end # end
RUBY
end
def curried_args
@curried_args ||= ::Array.new(curried) { "@arg#{_1}" }
end
def unapplied_args
@unapplied_args ||= ::Array.new(unapplied) { "input#{_1}" }
end
end
end
end
end
================================================
FILE: lib/dry/logic/rule/predicate.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
class Rule
class Predicate < Rule
def self.specialize(arity, curried, base = Predicate)
super
end
def type
:predicate
end
def name
predicate.name
end
def to_s
if args.empty?
name.to_s
else
"#{name}(#{args.map(&:inspect).join(", ")})"
end
end
def ast(input = Undefined)
[type, [name, args_with_names(input)]]
end
alias_method :to_ast, :ast
end
end
end
end
================================================
FILE: lib/dry/logic/rule.rb
================================================
# frozen_string_literal: true
require "concurrent/map"
module Dry
module Logic
def self.Rule(*args, **options, &block)
if args.any?
Rule.build(*args, **options)
elsif block
Rule.build(block, **options)
end
end
class Rule
include Core::Constants
include ::Dry::Equalizer(:predicate, :options)
include Operators
attr_reader :predicate
attr_reader :options
attr_reader :args
attr_reader :arity
def self.interfaces
@interfaces ||= ::Concurrent::Map.new
end
def self.specialize(arity, curried, base = Rule)
base.interfaces.fetch_or_store([arity, curried]) do
interface = Interface.new(arity, curried)
klass = ::Class.new(base) { include interface }
base.const_set("#{base.name.split("::").last}#{interface.name}", klass)
klass
end
end
def self.build(predicate, args: EMPTY_ARRAY, arity: predicate.arity, **options)
specialize(arity, args.size).new(predicate, {args: args, arity: arity, **options})
end
def initialize(predicate, options = EMPTY_HASH)
@predicate = predicate
@options = options
@args = options[:args] || EMPTY_ARRAY
@arity = options[:arity] || predicate.arity
end
def type
:rule
end
def id
options[:id]
end
def curry(*new_args)
with(args: args + new_args)
end
def bind(object)
if predicate.respond_to?(:bind)
self.class.build(predicate.bind(object), **options)
else
self.class.build(
-> *args { object.instance_exec(*args, &predicate) },
**options, arity: arity, parameters: parameters
)
end
end
def eval_args(object)
with(args: args.map { |arg| arg.is_a?(UnboundMethod) ? arg.bind(object).() : arg })
end
def with(new_opts)
self.class.build(predicate, **options, **new_opts)
end
def parameters
options[:parameters] || predicate.parameters
end
def ast(input = Undefined)
[:predicate, [id, args_with_names(input)]]
end
private
def args_with_names(*input)
parameters.map(&:last).zip(args + input)
end
end
end
end
================================================
FILE: lib/dry/logic/rule_compiler.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
class RuleCompiler
attr_reader :predicates
def initialize(predicates)
@predicates = predicates
end
def call(ast)
ast.map { |node| visit(node) }
end
def visit(node)
name, nodes = node
send(:"visit_#{name}", nodes)
end
def visit_check(node)
keys, predicate = node
Operations::Check.new(visit(predicate), keys: keys)
end
def visit_not(node)
Operations::Negation.new(visit(node))
end
def visit_key(node)
name, predicate = node
Operations::Key.new(visit(predicate), name: name)
end
def visit_attr(node)
name, predicate = node
Operations::Attr.new(visit(predicate), name: name)
end
def visit_set(node)
Operations::Set.new(*call(node))
end
def visit_each(node)
Operations::Each.new(visit(node))
end
def visit_predicate(node)
name, params = node
predicate = Rule::Predicate.build(predicates[name])
if params.size > 1
args = params.map(&:last).reject { |val| val == Undefined }
predicate.curry(*args)
else
predicate
end
end
def visit_and(node)
left, right = node
visit(left).and(visit(right))
end
def visit_or(node)
left, right = node
visit(left).or(visit(right))
end
def visit_xor(node)
left, right = node
visit(left).xor(visit(right))
end
def visit_implication(node)
left, right = node
visit(left).then(visit(right))
end
end
end
end
================================================
FILE: lib/dry/logic/version.rb
================================================
# frozen_string_literal: true
module Dry
module Logic
VERSION = "1.6.0"
end
end
================================================
FILE: lib/dry/logic.rb
================================================
# frozen_string_literal: true
require "zeitwerk"
require "dry/core"
module Dry
module Logic
include Dry::Core::Constants
def self.loader
@loader ||= Zeitwerk::Loader.new.tap do |loader|
root = File.expand_path("..", __dir__)
loader.tag = "dry-logic"
loader.inflector = Zeitwerk::GemInflector.new("#{root}/dry-logic.rb")
loader.push_dir(root)
loader.ignore(
"#{root}/dry-logic.rb",
"#{root}/dry/logic/version.rb"
)
end
end
loader.setup
end
end
================================================
FILE: lib/dry-logic.rb
================================================
# frozen_string_literal: true
require "dry/logic"
================================================
FILE: repo-sync.yml
================================================
name:
gem: dry-logic
constant: Dry::Logic
github_org: dry-rb
gemspec:
authors: ["Hanakai team"]
email: ["info@hanakai.org"]
summary: "Predicate logic with rule composition"
homepage: "https://dry-rb.org/gems/dry-logic"
development_dependencies:
- bundler
- rake
- rspec
runtime_dependencies:
- [bigdecimal]
- [concurrent-ruby, "~> 1.0"]
- [dry-core, "~> 1.1"]
- [zeitwerk, "~> 2.6"]
================================================
FILE: spec/integration/builder/operation_spec.rb
================================================
# frozen_string_literal: true
require_relative "../../shared/operation"
RSpec.describe "operations" do
describe "nested" do
let(:predicate) do
Dry::Logic::Builder.call do
check keys: [:person] do
check keys: [:age] do
gt?(50) & lt?(200)
end
end
end
end
describe "success" do
subject { predicate.call({person: {age: 100}}) }
it { is_expected.to be_a_success }
end
describe "failure" do
subject { predicate.call({person: {age: 10}}) }
it { is_expected.not_to be_a_success }
end
end
describe :check do
describe "one path" do
let(:expression) do
lambda do |*|
check keys: [:age] do
gt?(50)
end
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { {age: 100} }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { {age: 10} }
let(:output) { false }
end
end
end
describe "two paths" do
let(:expression) do
lambda do |*|
check keys: %i[speed limit] do
gt?
end
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { {speed: 50, limit: 100} }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { {speed: 100, limit: 50} }
let(:output) { false }
end
end
end
end
describe :implication do
let(:expression) do
lambda do |*|
implication { [gt?(0), lt?(10)] }
end
end
describe "[true => true]" do
it_behaves_like "operation" do
let(:input) { 5 }
let(:output) { true }
end
end
describe "[true => false]" do
it_behaves_like "operation" do
let(:input) { 20 }
let(:output) { false }
end
end
describe "[false => true]" do
it_behaves_like "operation" do
let(:input) { -5 }
let(:output) { true }
end
end
end
describe :key do
let(:expression) do
lambda do |*|
key name: %i[user age] do
gt?(10)
end
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { {user: {age: 20}} }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { {user: {age: 5}} }
let(:output) { false }
end
end
end
describe :and do
let(:expression) do
lambda do |*|
even?.and(int?)
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 2 }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { 5 }
let(:output) { false }
end
end
end
describe :or do
let(:expression) do
lambda do |*|
even?.or(odd?)
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 10 }
let(:output) { true }
end
it_behaves_like "operation" do
let(:input) { 9 }
let(:output) { true }
end
end
end
describe :negation do
let(:expression) do
lambda do |*|
negation { even? }
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 9 }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { 6 }
let(:output) { false }
end
end
end
describe :set do
let(:expression) do
lambda do |*|
set { [lt?(5), gt?(2)] }
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 3 }
let(:output) { true }
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 7 }
let(:output) { false }
end
end
end
describe :each do
let(:expression) do
lambda do |*|
each { gt?(10) }
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { [20, 30] }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { [10, 20, 30] }
let(:output) { false }
end
end
end
describe :xor do
let(:expression) do
lambda do |*|
even?.xor(gt?(4))
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 5 }
let(:output) { true }
end
it_behaves_like "operation" do
let(:input) { 2 }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { 6 }
let(:output) { false }
end
end
end
# describe :attr do
# let(:expression) do
# lambda do |*|
# attr name: :age do
# gt?(50)
# end
# end
# end
#
# let(:person) { Struct.new(:age) }
#
# describe "success" do
# it_behaves_like "operation" do
# let(:input) { person.new(100) }
# let(:output) { true }
# end
# end
#
# describe "failure" do
# it_behaves_like "operation" do
# let(:input) { person.new(0) }
# let(:output) { false }
# end
# end
# end
describe "operators" do
describe :& do
let(:expression) do
lambda do |*|
even? & int?
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 2 }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { 5 }
let(:output) { false }
end
end
end
describe :^ do
let(:expression) do
lambda do |*|
even? ^ gt?(4)
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 5 }
let(:output) { true }
end
it_behaves_like "operation" do
let(:input) { 2 }
let(:output) { true }
end
end
describe "failure" do
it_behaves_like "operation" do
let(:input) { 6 }
let(:output) { false }
end
end
end
describe :> do
let(:expression) do
lambda do |*|
gt?(0) > lt?(10)
end
end
describe "[true => true]" do
it_behaves_like "operation" do
let(:input) { 5 }
let(:output) { true }
end
end
describe "[true => false]" do
it_behaves_like "operation" do
let(:input) { 20 }
let(:output) { false }
end
end
describe "[false => true]" do
it_behaves_like "operation" do
let(:input) { -5 }
let(:output) { true }
end
end
end
describe :then do
let(:expression) do
lambda do |*|
gt?(0).then(lt?(10))
end
end
describe "[true => true]" do
it_behaves_like "operation" do
let(:input) { 5 }
let(:output) { true }
end
end
describe "[true => false]" do
it_behaves_like "operation" do
let(:input) { 20 }
let(:output) { false }
end
end
describe "[false => true]" do
it_behaves_like "operation" do
let(:input) { -5 }
let(:output) { true }
end
end
end
describe :| do
let(:expression) do
lambda do |*|
even? | odd?
end
end
describe "success" do
it_behaves_like "operation" do
let(:input) { 10 }
let(:output) { true }
end
it_behaves_like "operation" do
let(:input) { 9 }
let(:output) { true }
end
end
end
end
end
================================================
FILE: spec/integration/builder/predicate_spec.rb
================================================
# frozen_string_literal: true
require_relative "../../shared/predicate"
RSpec.describe "predicates" do
let(:input) { described_class }
describe :key? do
let(:expression) { ->(*) { key?(:speed) } }
describe "success" do
let(:output) { true }
describe({speed: 100}) do
it_behaves_like "predicate"
end
end
describe "failure" do
let(:output) { false }
describe({age: 50}) do
it_behaves_like "predicate"
end
end
end
describe :format? do
let(:expression) { ->(*) { format?(/^(A|B)$/) } }
describe "success" do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { "A" }
end
it_behaves_like "predicate" do
let(:input) { "B" }
end
end
describe "failure" do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { "C" }
end
it_behaves_like "predicate" do
let(:input) { "D" }
end
end
end
describe :type? do
let(:expression) { ->(*) { type?(String) } }
describe "success" do
let(:output) { true }
describe "string" do
it_behaves_like "predicate" do
let(:input) { "string" }
end
end
end
describe "failure" do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { :symbol }
end
end
end
describe :nil? do
let(:expression) { ->(*) { nil? } }
describe "success" do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { nil }
end
end
describe "failure" do
let(:output) { false }
describe :symbol do
it_behaves_like "predicate"
end
end
end
describe :attr? do
let(:expression) { ->(*) { attr?(:name) } }
describe "success" do
let(:output) { true }
describe Struct.new(:name).new("John") do
it_behaves_like "predicate"
end
end
describe "failure" do
let(:output) { false }
describe Struct.new(:age).new(50) do
it_behaves_like "predicate"
end
end
end
describe :empty? do
let(:expression) { ->(*) { empty? } }
describe "success" do
let(:output) { true }
describe({}) do
it_behaves_like "predicate"
end
describe String do
it_behaves_like "predicate" do
let(:input) { described_class.new }
end
end
describe [] do
it_behaves_like "predicate"
end
end
describe "failure" do
let(:output) { false }
describe({key: "value"}) do
it_behaves_like "predicate"
end
describe "string" do
it_behaves_like "predicate"
end
describe nil do
it_behaves_like "predicate"
end
describe [1, 2] do
it_behaves_like "predicate"
end
end
end
describe :filled? do
let(:expression) { ->(*) { filled? } }
describe "success" do
let(:output) { true }
describe({key: "value"}) do
it_behaves_like "predicate"
end
describe "string" do
it_behaves_like "predicate"
end
describe nil do
it_behaves_like "predicate"
end
describe [1, 2] do
it_behaves_like "predicate"
end
end
describe "failure" do
let(:output) { false }
describe({}) do
it_behaves_like "predicate"
end
describe String do
it_behaves_like "predicate" do
let(:input) { described_class.new }
end
end
describe [] do
it_behaves_like "predicate"
end
end
end
describe :bool? do
let(:expression) { ->(*) { bool? } }
describe "success" do
let(:output) { true }
describe true do
it_behaves_like "predicate"
end
describe false do
it_behaves_like "predicate"
end
end
describe "failure" do
let(:output) { false }
describe :symbol do
it_behaves_like "predicate"
end
describe 5 do
it_behaves_like "predicate"
end
end
end
describe :date? do
let(:expression) { ->(*) { date? } }
describe "success" do
let(:output) { true }
describe Date.new do
it_behaves_like "predicate"
end
end
describe "failure" do
let(:output) { false }
describe :symbol do
it_behaves_like "predicate"
end
end
end
describe :date_time? do
let(:expression) { ->(*) { date_time? } }
describe "success" do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { DateTime.new }
end
end
describe "failure" do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { :symbol }
end
end
end
describe :time? do
let(:expression) { ->(*) { time? } }
describe "success" do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { Time.new }
end
end
describe "failure" do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { :symbol }
end
end
end
describe :number? do
let(:expression) { ->(*) { number? } }
describe "success" do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { -4 }
end
it_behaves_like "predicate" do
let(:input) { 10.0 }
end
it_behaves_like "predicate" do
let(:input) { 10 }
end
end
describe "failure" do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { "A-4" }
end
it_behaves_like "predicate" do
let(:input) { "A10" }
end
it_behaves_like "predicate" do
let(:input) { nil }
end
it_behaves_like "predicate" do
let(:input) { :nope }
end
end
end
describe :int? do
let(:expression) { ->(*) { int? } }
describe "success" do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { 10 }
end
end
describe "failure" do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { 10.0 }
end
end
end
describe :float? do
let(:expression) { ->(*) { float? } }
describe "success" do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { 1.0 }
end
end
describe "success" do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { 1 }
end
end
end
describe :decimal? do
let(:expression) { ->(*) { decimal? } }
describe "success" do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { BigDecimal(1) }
end
end
describe "failure" do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { 10 }
end
end
end
describe :str? do
let(:expression) { ->(*) { str? } }
describe "success" do
let(:output) { true }
describe String do
it_behaves_like "predicate" do
let(:input) { described_class.new }
end
end
end
describe "failure" do
let(:output) { false }
describe Array do
it_behaves_like "predicate" do
let(:input) { described_class.new }
end
end
end
end
describe :hash? do
let(:expression) { ->(*) { hash? } }
describe "success" do
let(:output) { true }
describe Hash do
it_behaves_like "predicate" do
let(:input) { described_class.new }
end
end
end
describe "failure" do
let(:output) { false }
describe Array do
it_behaves_like "predicate" do
let(:input) { described_class.new }
end
end
end
end
describe :array? do
let(:expression) { ->(*) { array? } }
describe "success" do
let(:output) { true }
describe Array do
it_behaves_like "predicate" do
let(:input) { described_class.new }
end
end
end
describe "failure" do
let(:output) { false }
describe Hash do
it_behaves_like "predicate" do
let(:input) { described_class.new }
end
end
end
end
describe :even? do
let(:expression) { ->(*) { even? } }
describe "success" do
let(:output) { true }
describe 10 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
describe "failure" do
let(:output) { false }
describe 5 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
end
describe :odd? do
let(:expression) { ->(*) { odd? } }
describe "success" do
let(:output) { true }
describe 5 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
describe "failure" do
let(:output) { false }
describe 10 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
end
describe :lt? do
let(:expression) { ->(*) { lt?(10) } }
describe "success" do
let(:output) { true }
describe 5 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
describe "failure" do
let(:output) { false }
describe 200 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
end
describe :gt? do
let(:expression) { ->(*) { gt?(10) } }
describe "failure" do
let(:output) { false }
describe 5 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
describe "success" do
let(:output) { true }
describe 200 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
end
describe :gteq? do
let(:expression) { ->(*) { gteq?(10) } }
describe "success" do
let(:output) { true }
describe 10 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
describe 11 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
describe "failure" do
let(:output) { false }
describe 9 do
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
end
describe :lteq? do
let(:expression) { ->(*) { lteq?(10) } }
describe "success" do
let(:output) { true }
describe 9 do
it_behaves_like "predicate"
end
end
describe "failure" do
let(:output) { false }
describe 11 do
it_behaves_like "predicate"
end
end
end
describe :size? do
describe "success" do
let(:output) { true }
describe "Integer" do
it_behaves_like "predicate" do
let(:expression) { ->(*) { size?(2) } }
let(:input) { [1, 2] }
end
end
describe "Range" do
it_behaves_like "predicate" do
let(:expression) { ->(*) { size?(1..3) } }
let(:input) { [1, 2] }
end
end
describe "Array" do
it_behaves_like "predicate" do
let(:expression) { ->(*) { size?([3]) } }
let(:input) { [1, 2, 3] }
end
end
end
describe "failure" do
let(:output) { false }
describe "Integer" do
it_behaves_like "predicate" do
let(:expression) { ->(*) { size?(2) } }
let(:input) { [1] }
end
end
describe "Range" do
it_behaves_like "predicate" do
let(:expression) { ->(*) { size?(1..3) } }
let(:input) { [1, 2, 3, 4] }
end
end
describe "Array" do
it_behaves_like "predicate" do
let(:expression) { ->(*) { size?([3]) } }
let(:input) { [1, 2] }
end
end
end
end
describe :min_size? do
let(:expression) { ->(*) { min_size?(2) } }
describe "success" do
describe [1, 2, 3] do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
describe "failure" do
describe [1] do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
end
describe :max_size? do
let(:expression) { ->(*) { max_size?(2) } }
describe "success" do
describe [1] do
let(:output) { true }
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
describe "failure" do
describe [1, 2, 3] do
let(:output) { false }
it_behaves_like "predicate" do
let(:input) { described_class }
end
end
end
end
describe :bytesize? do
describe "success" do
let(:output) { true }
describe "Integer" do
let(:expression) { ->(*) { bytesize?(1) } }
it_behaves_like "predicate" do
let(:input) { "A" }
end
end
describe "Range" do
let(:expression) { ->(*) { bytesize?(2..3) } }
it_behaves_like "predicate" do
let(:input) { "AB" }
end
end
describe "Array" do
let(:expression) { ->(*) { bytesize?([2, 3]) } }
it_behaves_like "predicate" do
let(:input) { "AB" }
end
end
end
describe "failure" do
let(:output) { false }
describe "Integer" do
let(:expression) { ->(*) { bytesize?(1) } }
it_behaves_like "predicate" do
let(:input) { "AB" }
end
end
describe "Range" do
let(:expression) { ->(*) { bytesize?(2..3) } }
it_behaves_like "predicate" do
let(:input) { "A" }
end
end
describe "Array" do
let(:expression) { ->(*) { bytesize?([2, 3]) } }
it_behaves_like "predicate" do
let(:input) { "A" }
end
end
end
end
describe :min_bytesize? do
let(:expression) { ->(*) { min_bytesize?(1) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "A" }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { "" }
end
end
end
describe :max_bytesize? do
let(:expression) { ->(*) { max_bytesize?(1) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { "AB" }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "" }
end
end
end
describe :included_in? do
let(:expression) { ->(*) { included_in?([1, 2, 3]) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { 1 }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { 4 }
end
end
end
describe :excluded_from? do
let(:expression) { ->(*) { excluded_from?([1, 2, 3]) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { 1 }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { 4 }
end
end
end
describe :includes? do
let(:expression) { ->(*) { includes?(1) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { [1, 2, 3] }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { [2, 3, 4] }
end
end
end
describe :excludes? do
describe Array do
let(:expression) { ->(*) { excludes?(1) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { described_class.new([1, 2, 3]) }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { described_class.new([2, 3, 4]) }
end
end
end
describe String do
let(:expression) { ->(*) { excludes?("A") } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { described_class.new("B") }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { described_class.new("A") }
end
end
end
end
describe "compare methods" do
describe :eql? do
let(:expression) { ->(*) { eql?(10) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { 10 }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { 20 }
end
end
end
describe :not_eql? do
let(:expression) { ->(*) { not_eql?(10) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { 10 }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { 20 }
end
end
end
end
describe :is? do
describe "success" do
it_behaves_like "predicate" do
let(:expression) { ->(*) { is?(nil) } }
let(:output) { true }
let(:input) { nil }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:expression) { ->(*) { is?(Class.new) } }
let(:output) { false }
let(:input) { Class.new }
end
end
end
describe "true? & false?" do
describe :true? do
let(:expression) { ->(*) { true? } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { true }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { false }
end
end
end
describe :false? do
let(:expression) { ->(*) { false? } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { false }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { true }
end
end
end
end
describe :case? do
describe "Range" do
let(:expression) { ->(*) { case?(5..10) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { 6 }
end
end
end
describe "Fixnum" do
let(:expression) { ->(*) { case?(Integer) } }
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { 10 }
end
end
end
end
describe "uuid" do
describe :uuid_v2?
describe :uuid_v3?
describe :uuid_v5?
describe :uuid_v1? do
let(:expression) do
->(*) { uuid_v1? }
end
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "554ef240-5433-11eb-ae93-0242ac130002" }
end
end
end
describe :uuid_v4? do
let(:expression) do
->(*) { uuid_v4? }
end
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "f2711f00-5602-45c7-ae01-c94d285592c3" }
end
end
end
end
describe :uri? do
describe :http do
let(:expression) do
->(*) { uri?(:http) }
end
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "http://google.com" }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { "https://google.com" }
end
end
end
describe [:http, :https] do
let(:expression) do
->(*) { uri?(%w[http https]) }
end
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "https://google.com" }
end
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "http://google.com" }
end
end
end
describe Regexp do
let(:expression) do
->(*) { uri?(/https?/) }
end
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "https://google.com" }
end
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "http://google.com" }
end
end
end
describe :https do
let(:expression) do
->(*) { uri?(:https) }
end
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { "https://google.com" }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { "http://google.com" }
end
end
end
end
describe :respond_to? do
let(:expression) do
->(*) { respond_to?(:awesome?) }
end
describe "success" do
it_behaves_like "predicate" do
let(:output) { true }
let(:input) { Struct.new(:awesome?).new(true) }
end
end
describe "failure" do
it_behaves_like "predicate" do
let(:output) { false }
let(:input) { Struct.new(:not_awesome?).new(true) }
end
end
end
describe :predicate do
before { extend Dry::Logic::Builder }
before(:each) do
build do
predicate :divisible_with? do |num, input|
(input % num).zero?
end
end
end
let(:expression) do
lambda do |*|
divisible_with?(10)
end
end
describe "success" do
it_behaves_like "predicate" do
let(:input) { 10 }
let(:output) { true }
end
end
describe "success" do
it_behaves_like "predicate" do
let(:input) { 3 }
let(:output) { false }
end
end
end
end
================================================
FILE: spec/integration/result_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Result do
include_context "predicates"
describe "#to_s" do
shared_examples_for "string representation" do
it "returns string representation" do
expect(rule.(input).to_s).to eql(output)
end
end
context "with a predicate" do
let(:rule) { Dry::Logic::Rule::Predicate.build(gt?, args: [18]) }
let(:input) { 17 }
let(:output) { "gt?(18, 17)" }
it_behaves_like "string representation"
end
context "with AND operation" do
let(:rule) do
Dry::Logic::Rule::Predicate.build(array?).and(Dry::Logic::Rule::Predicate.build(empty?))
end
let(:input) { "" }
let(:output) { 'array?("") AND empty?("")' }
it_behaves_like "string representation"
end
context "with OR operation" do
let(:rule) do
Dry::Logic::Rule::Predicate.build(array?).or(Dry::Logic::Rule::Predicate.build(empty?))
end
let(:input) { 123 }
let(:output) { "array?(123) OR empty?(123)" }
it_behaves_like "string representation"
end
context "with XOR operation" do
let(:rule) do
Dry::Logic::Rule::Predicate.build(array?).xor(Dry::Logic::Rule::Predicate.build(empty?))
end
let(:input) { [] }
let(:output) { "array?([]) XOR empty?([])" }
it_behaves_like "string representation"
end
context "with THEN operation" do
let(:rule) do
Dry::Logic::Rule::Predicate.build(array?).then(Dry::Logic::Rule::Predicate.build(empty?))
end
let(:input) { [1, 2, 3] }
let(:output) { "empty?([1, 2, 3])" }
it_behaves_like "string representation"
end
context "with NOT operation" do
let(:rule) { Dry::Logic::Operations::Negation.new(Dry::Logic::Rule::Predicate.build(array?)) }
let(:input) { "foo" }
let(:output) { 'not(array?("foo"))' }
it_behaves_like "string representation"
end
end
end
================================================
FILE: spec/integration/rule_spec.rb
================================================
# frozen_string_literal: true
require "dry-logic"
RSpec.describe "Rules" do
specify "defining an anonymous rule with an arbitrary predicate" do
rule = Dry::Logic.Rule { |value| value.is_a?(Integer) }
expect(rule.(1)).to be_success
expect(rule[1]).to be(true)
end
specify "defining a conjunction" do
rule = Dry::Logic.Rule(&:even?) & Dry::Logic.Rule { |v| v > 4 }
expect(rule.(3)).to be_failure
expect(rule.(4)).to be_failure
expect(rule.(5)).to be_failure
expect(rule.(6)).to be_success
end
specify "defining a disjunction" do
rule = Dry::Logic.Rule { |v| v < 4 } | Dry::Logic.Rule { |v| v > 6 }
expect(rule.(5)).to be_failure
expect(rule.(3)).to be_success
expect(rule.(7)).to be_success
end
specify "defining an implication" do
rule = Dry::Logic.Rule(&:empty?) > Dry::Logic.Rule { |v| v.is_a?(Array) }
expect(rule.("foo")).to be_success
expect(rule.([1, 2])).to be_success
expect(rule.([])).to be_success
expect(rule.("")).to be_failure
end
specify "defining an exclusive disjunction" do
rule = Dry::Logic.Rule(&:empty?) ^ Dry::Logic.Rule { |v| v.is_a?(Array) }
expect(rule.("foo")).to be_failure
expect(rule.([])).to be_failure
expect(rule.([1, 2])).to be_success
expect(rule.("")).to be_success
end
specify "defining a rule with options" do
# using &:empty? breaks the spec
rule = Dry::Logic::Rule(id: :empty?) { |value| value.empty? }
# rubocop:enable Style/SymbolProc
expect(rule.("foo")).to be_failure
expect(rule.("")).to be_success
expect(rule.ast("foo")).to eql([:predicate, [:empty?, [[:value, "foo"]]]])
end
end
================================================
FILE: spec/shared/built_rule.rb
================================================
# frozen_string_literal: true
require "dry/logic/builder"
RSpec.shared_examples "built rule" do |rule_type, *args|
subject(:built_rule) { Dry::Logic::Builder.call(&expression) }
let(:rule_type) { rule_type }
case rule_type
when :predicate
include_examples "built rule predicate", *args
else
include_examples "built rule operation", *args
end
describe "#to_ast" do
subject(:ast) { built_rule.to_ast }
it { is_expected.to include(rule_type, rule_node) }
end
end
RSpec.shared_examples "built rule predicate" do |predicate_name, *args|
it { is_expected.to be_kind_of(Dry::Logic::Rule::Predicate) }
let(:predicate_name) { predicate_name }
let(:rule_node) { [predicate_name, include(*args)] }
describe "#predicate" do
subject(:predicate) { built_rule.predicate }
describe "#name" do
subject(:name) { predicate.name }
it { is_expected.to eq(predicate_name) }
end
end
end
RSpec.shared_examples "built rule operation" do |node|
it { is_expected.to be_kind_of(Dry::Logic::Operation::Abstract) }
let(:rule_node) { node }
end
================================================
FILE: spec/shared/operation.rb
================================================
# frozen_string_literal: true
require "dry/logic/builder"
RSpec.shared_examples "operation" do
before { extend Dry::Logic::Builder }
let(:operation) { build(&expression) }
let(:args) { defined?(input) ? [input] : [] }
subject { operation.call(*args).success? }
it { is_expected.to eq(output) }
end
================================================
FILE: spec/shared/predicate.rb
================================================
# frozen_string_literal: true
require "dry/logic/builder"
# TODO: Merge with {operation}?
RSpec.shared_examples "predicate" do
before { extend Dry::Logic::Builder }
let(:predicate) { build(&expression) }
let(:args) { defined?(input) ? [input] : [] }
subject { predicate.call(*args).success? }
it { is_expected.to eq(output) }
end
================================================
FILE: spec/shared/predicates.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.shared_examples "predicates" do
let(:nil?) { Dry::Logic::Predicates[:nil?] }
let(:array?) { Dry::Logic::Predicates[:array?] }
let(:empty?) { Dry::Logic::Predicates[:empty?] }
let(:str?) { Dry::Logic::Predicates[:str?] }
let(:true?) { Dry::Logic::Predicates[:true?] }
let(:hash?) { Dry::Logic::Predicates[:hash?] }
let(:int?) { Dry::Logic::Predicates[:int?] }
let(:filled?) { Dry::Logic::Predicates[:filled?] }
let(:min_size?) { Dry::Logic::Predicates[:min_size?] }
let(:lt?) { Dry::Logic::Predicates[:lt?] }
let(:gt?) { Dry::Logic::Predicates[:gt?] }
let(:key?) { Dry::Logic::Predicates[:key?] }
let(:attr?) { Dry::Logic::Predicates[:attr?] }
let(:eql?) { Dry::Logic::Predicates[:eql?] }
let(:size?) { Dry::Logic::Predicates[:size?] }
let(:case?) { Dry::Logic::Predicates[:case?] }
let(:equal?) { Dry::Logic::Predicates[:equal?] }
end
RSpec.shared_examples "a passing predicate" do
let(:predicate) { Dry::Logic::Predicates[predicate_name] }
it do
arguments_list.each do |args|
expect(predicate.call(*args)).to be(true)
end
end
end
RSpec.shared_examples "a failing predicate" do
let(:predicate) { Dry::Logic::Predicates[predicate_name] }
it do
arguments_list.each do |args|
expect(predicate.call(*args)).to be(false)
end
end
end
================================================
FILE: spec/shared/rule.rb
================================================
# frozen_string_literal: true
RSpec.shared_examples_for Dry::Logic::Rule do
let(:arity) { 2 }
let(:predicate) { double(:predicate, arity: arity, name: predicate_name) }
let(:rule_type) { described_class }
let(:predicate_name) { :good? }
describe "#arity" do
it "returns its predicate arity" do
rule = rule_type.build(predicate)
expect(rule.arity).to be(2)
end
end
describe "#parameters" do
it "returns a list of args with their names" do
rule = rule_type.build(-> foo, bar { true }, args: [312])
expect(rule.parameters).to eql([%i[req foo], %i[req bar]])
end
end
describe "#call" do
let(:arity) { 1 }
it "returns success for valid input" do
rule = rule_type.build(predicate)
expect(predicate).to receive(:[]).with(2).and_return(true)
expect(rule.(2)).to be_success
end
it "returns failure for invalid input" do
rule = rule_type.build(predicate)
expect(predicate).to receive(:[]).with(2).and_return(false)
expect(rule.(2)).to be_failure
end
end
describe "#[]" do
let(:arity) { 1 }
it "delegates to its predicate" do
rule = rule_type.build(predicate)
expect(predicate).to receive(:[]).with(2).and_return(true)
expect(rule[2]).to be(true)
end
end
describe "#curry" do
it "returns a curried rule" do
rule = rule_type.build(predicate).curry(3)
expect(predicate).to receive(:[]).with(3, 2).and_return(true)
expect(rule.args).to eql([3])
expect(rule.(2)).to be_success
end
it "raises argument error when arity does not match" do
expect(predicate).to receive(:arity).and_return(2)
expect { rule_type.build(predicate).curry(3, 2, 1) }.to raise_error(
ArgumentError, "wrong number of arguments (3 for 2)"
)
end
end
end
================================================
FILE: spec/spec_helper.rb
================================================
# frozen_string_literal: true
require_relative "support/coverage"
require_relative "support/warnings"
begin
require "pry-byebug"
rescue LoadError;
end
require "dry/logic"
require "pathname"
SPEC_ROOT = Pathname(__dir__)
Dir[SPEC_ROOT.join("shared/**/*.rb")].each(&method(:require))
Dir[SPEC_ROOT.join("support/**/*.rb")].each(&method(:require))
RSpec.configure do |config|
config.include Module.new {
def undefined
Dry::Core::Constants::Undefined
end
}
end
================================================
FILE: spec/support/coverage.rb
================================================
# frozen_string_literal: true
# This file is synced from hanakai-rb/repo-sync
if ENV["COVERAGE"] == "true"
require "simplecov"
require "simplecov-cobertura"
SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
SimpleCov.start do
add_filter "/spec/"
enable_coverage :branch
end
end
================================================
FILE: spec/support/mutant.rb
================================================
# frozen_string_literal: true
module Mutant
class Selector
class Expression < self
def call(_subject)
integration.all_tests
end
end
end
end
================================================
FILE: spec/support/rspec.rb
================================================
# frozen_string_literal: true
# This file is synced from hanakai-rb/repo-sync
RSpec.configure do |config|
# When no filter given, search and run focused tests
config.filter_run_when_matching :focus
# Disables rspec monkey patches (no reason for their existence tbh)
config.disable_monkey_patching!
# Run ruby in verbose mode
config.warnings = true
# Collect all failing expectations automatically,
# without calling aggregate_failures everywhere
config.define_derived_metadata do |meta|
meta[:aggregate_failures] = true
end
if ENV["CI"]
# No focused specs should be committed. This ensures
# builds fail when this happens.
config.before(:each, :focus) do
raise StandardError, "You've committed a focused spec!"
end
end
end
================================================
FILE: spec/support/warnings.rb
================================================
# frozen_string_literal: true
# This file is synced from hanakai-rb/repo-sync
require "warning"
# Ignore warnings for experimental features
Warning[:experimental] = false if Warning.respond_to?(:[])
# Ignore all warnings coming from gem dependencies
Gem.path.each do |path|
Warning.ignore(//, path)
end
================================================
FILE: spec/unit/builder_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Builder do
describe "undefined methods" do
it "raises NameError" do
expect do
described_class.call { does_not_exist }
end.to raise_error(NameError, /does_not_exist/)
end
end
describe "leakage" do
context "given a module extending ::Builder" do
subject do
Module.new do
extend Dry::Logic::Builder
end
end
it { is_expected.not_to respond_to(:int?) }
it { is_expected.to respond_to(:call) }
it { is_expected.to respond_to(:build) }
end
end
describe "ast of built rule" do
let(:expression) { -> (*) { key?(:speed) } }
it_behaves_like "built rule", :predicate, :key?, [:name, :speed]
end
end
================================================
FILE: spec/unit/operations/and_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::And do
subject(:operation) { described_class.new(left, right) }
include_context "predicates"
let(:left) { Dry::Logic::Rule::Predicate.build(int?) }
let(:right) { Dry::Logic::Rule::Predicate.build(gt?).curry(18) }
describe "#call" do
it "calls left and right" do
expect(operation.(18)).to be_failure
end
end
describe "#to_ast" do
it "returns ast" do
expect(operation.to_ast).to eql(
[:and, [[:predicate, [:int?, [[:input, undefined]]]], [:predicate, [:gt?, [[:num, 18], [:input, undefined]]]]]]
)
end
it "returns result ast" do
expect(operation.("18").to_ast).to eql(
[:and, [[:predicate, [:int?, [[:input, "18"]]]], [:hint, [:predicate, [:gt?, [[:num, 18], [:input, "18"]]]]]]]
)
expect(operation.with(hints: false).("18").to_ast).to eql(
[:predicate, [:int?, [[:input, "18"]]]]
)
expect(operation.(18).to_ast).to eql(
[:predicate, [:gt?, [[:num, 18], [:input, 18]]]]
)
end
it "returns failure result ast" do
expect(operation.with(id: :age).("18").to_ast).to eql(
[:failure, [:age, [:and, [[:predicate, [:int?, [[:input, "18"]]]], [:hint, [:predicate, [:gt?, [[:num, 18], [:input, "18"]]]]]]]]]
)
expect(operation.with(id: :age).(18).to_ast).to eql(
[:failure, [:age, [:predicate, [:gt?, [[:num, 18], [:input, 18]]]]]]
)
end
end
describe "#and" do
let(:other) { Dry::Logic::Rule::Predicate.build(lt?).curry(30) }
it "creates and with the other" do
expect(operation.and(other).(31)).to be_failure
end
end
describe "#or" do
let(:other) { Dry::Logic::Rule::Predicate.build(lt?).curry(14) }
it "creates or with the other" do
expect(operation.or(other).(13)).to be_success
end
end
describe "#to_s" do
it "returns string representation" do
expect(operation.to_s).to eql("int? AND gt?(18)")
end
end
end
================================================
FILE: spec/unit/operations/attr_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Attr do
subject(:operation) do
Dry::Logic::Operations::Attr.new(Dry::Logic::Rule::Predicate.build(str?), name: :name)
end
include_context "predicates"
let(:model) { Struct.new(:name) }
describe "#call" do
it "applies predicate to the value" do
expect(operation.(model.new("Jane"))).to be_success
expect(operation.(model.new(nil))).to be_failure
end
end
describe "#and" do
let(:other) do
Dry::Logic::Operations::Attr.new(Dry::Logic::Rule::Predicate.build(min_size?).curry(3), name: :name)
end
it "returns and where value is passed to the right" do
present_and_string = operation.and(other)
expect(present_and_string.(model.new("Jane"))).to be_success
expect(present_and_string.(model.new("Ja"))).to be_failure
expect(present_and_string.(model.new(1))).to be_failure
end
end
end
================================================
FILE: spec/unit/operations/check_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Check do
include_context "predicates"
describe "#call" do
context "with 1-level nesting" do
subject(:operation) do
described_class.new(Dry::Logic::Rule::Predicate.build(eql?).curry(1), id: :compare, keys: [:num])
end
it "applies predicate to args extracted from the input" do
expect(operation.(num: 1)).to be_success
expect(operation.(num: 2)).to be_failure
end
end
context "with 2-levels nesting" do
subject(:operation) do
described_class.new(
Dry::Logic::Rule::Predicate.build(eql?), id: :compare, keys: [[:nums, :left], [:nums, :right]]
)
end
it "applies predicate to args extracted from the input" do
expect(operation.(nums: {left: 1, right: 1})).to be_success
expect(operation.(nums: {left: 1, right: 2})).to be_failure
end
it "curries args properly" do
result = operation.(nums: {left: 1, right: 2})
expect(result.to_ast).to eql(
[:failure, [:compare, [:check, [
[[:nums, :left], [:nums, :right]], [:predicate, [:eql?, [[:left, 1], [:right, 2]]]]
]]]]
)
end
end
context "with its output as input" do
let(:gt?) { Dry::Logic::Predicates[:gt?] }
let(:min) { Dry::Logic::Rule::Predicate.new(gt?).curry(18) }
let(:inner) { described_class.new(min, keys: [:age]) }
let(:outer) { described_class.new(inner, keys: [:person]) }
subject { outer.call(input) }
describe "success" do
let(:input) { {person: {age: 20}} }
it { is_expected.to be_a_success }
end
describe "failure" do
let(:input) { {person: {age: 10}} }
it { is_expected.not_to be_a_success }
end
end
end
describe "#to_ast" do
subject(:operation) do
described_class.new(Dry::Logic::Rule::Predicate.build(str?), keys: [:email])
end
it "returns ast" do
expect(operation.to_ast).to eql(
[:check, [[:email], [:predicate, [:str?, [[:input, undefined]]]]]]
)
end
end
end
================================================
FILE: spec/unit/operations/each_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Each do
subject(:operation) { described_class.new(is_string) }
include_context "predicates"
let(:is_string) { Dry::Logic::Rule::Predicate.build(str?) }
describe "#call" do
it "applies its rules to all elements in the input" do
expect(operation.(["Address"])).to be_success
expect(operation.([nil, "Address"])).to be_failure
expect(operation.([:Address, "Address"])).to be_failure
end
end
describe "#to_ast" do
it "returns ast" do
expect(operation.to_ast).to eql([:each, [:predicate, [:str?, [[:input, undefined]]]]])
end
it "returns result ast" do
expect(operation.([nil, 12, nil]).to_ast).to eql(
[:set, [
[:key, [0, [:predicate, [:str?, [[:input, nil]]]]]],
[:key, [1, [:predicate, [:str?, [[:input, 12]]]]]],
[:key, [2, [:predicate, [:str?, [[:input, nil]]]]]]
]]
)
end
it "returns failure result ast" do
expect(operation.with(id: :tags).([nil, "red", 12]).to_ast).to eql(
[:failure, [:tags, [:set, [
[:key, [0, [:predicate, [:str?, [[:input, nil]]]]]],
[:key, [2, [:predicate, [:str?, [[:input, 12]]]]]]
]]]]
)
end
end
describe "#to_s" do
it "returns string representation" do
expect(operation.to_s).to eql("each(str?)")
end
end
end
================================================
FILE: spec/unit/operations/implication_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Implication do
subject(:operation) { described_class.new(left, right) }
include_context "predicates"
let(:left) { Dry::Logic::Rule::Predicate.build(int?) }
let(:right) { Dry::Logic::Rule::Predicate.build(gt?).curry(18) }
describe "#call" do
it "calls left and right" do
expect(operation.("19")).to be_success
expect(operation.(19)).to be_success
expect(operation.(18)).to be_failure
end
end
describe "#to_ast" do
it "returns ast" do
expect(operation.to_ast).to eql(
[:implication, [[:predicate, [:int?, [[:input, undefined]]]], [:predicate, [:gt?, [[:num, 18], [:input, undefined]]]]]]
)
end
end
describe "#to_s" do
it "returns string representation" do
expect(operation.to_s).to eql("int? THEN gt?(18)")
end
end
end
================================================
FILE: spec/unit/operations/key_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Key do
subject(:operation) { described_class.new(predicate, name: :user) }
include_context "predicates"
let(:predicate) do
Dry::Logic::Rule::Predicate.build(key?).curry(:age)
end
describe "#call" do
context "with a plain predicate" do
it "returns a success for valid input" do
expect(operation.(user: {age: 18})).to be_success
end
it "returns a failure for invalid input" do
result = operation.(user: {})
expect(result).to be_failure
expect(result.to_ast).to eql(
[:failure, [:user, [:key, [:user,
[:predicate, [:key?, [[:name, :age], [:input, {}]]]]]]]]
)
end
end
context "with a set rule as predicate" do
subject(:operation) do
described_class.new(predicate, name: :address)
end
let(:predicate) do
Dry::Logic::Operations::Set.new(
Dry::Logic::Rule::Predicate.build(key?).curry(:city),
Dry::Logic::Rule::Predicate.build(key?).curry(:zipcode)
)
end
it "applies set rule to the value that passes" do
result = operation.(address: {city: "NYC", zipcode: "123"})
expect(result).to be_success
end
it "applies set rule to the value that fails" do
result = operation.(address: {city: "NYC"})
expect(result).to be_failure
expect(result.to_ast).to eql(
[:failure, [:address, [:key, [:address, [:set, [
[:predicate, [:key?, [[:name, :zipcode], [:input, {city: "NYC"}]]]]
]]]]]]
)
end
end
context "with an each rule as predicate" do
subject(:operation) do
described_class.new(predicate, name: :nums)
end
let(:predicate) do
Dry::Logic::Operations::Each.new(Dry::Logic::Rule::Predicate.build(str?))
end
it "applies each rule to the value that passses" do
result = operation.(nums: %w[1 2 3])
expect(result).to be_success
end
it "applies each rule to the value that fails" do
failure = operation.(nums: [1, "3", 3])
expect(failure).to be_failure
expect(failure.to_ast).to eql(
[:failure, [:nums, [:key, [:nums, [:set, [
[:key, [0, [:predicate, [:str?, [[:input, 1]]]]]],
[:key, [2, [:predicate, [:str?, [[:input, 3]]]]]]
]]]]]]
)
end
end
end
describe "#to_ast" do
it "returns ast" do
expect(operation.to_ast).to eql(
[:key, [:user, [:predicate, [:key?, [[:name, :age], [:input, undefined]]]]]]
)
end
end
describe "#ast" do
it "returns ast without the input" do
expect(operation.ast).to eql(
[:key, [:user, [:predicate, [:key?, [[:name, :age], [:input, undefined]]]]]]
)
end
it "returns ast with the input" do
expect(operation.ast(user: "jane")).to eql(
[:key, [:user, [:predicate, [:key?, [[:name, :age], [:input, "jane"]]]]]]
)
end
end
describe "#and" do
subject(:operation) do
described_class.new(Dry::Logic::Rule::Predicate.build(str?), name: [:user, :name])
end
let(:other) do
described_class.new(Dry::Logic::Rule::Predicate.build(filled?), name: [:user, :name])
end
it "returns and rule where value is passed to the right" do
present_and_string = operation.and(other)
expect(present_and_string.(user: {name: "Jane"})).to be_success
expect(present_and_string.(user: {})).to be_failure
expect(present_and_string.(user: {name: 1})).to be_failure
end
end
describe "#to_s" do
it "returns string representation" do
expect(operation.to_s).to eql("key[user](key?(:age))")
end
end
end
================================================
FILE: spec/unit/operations/negation_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Negation do
subject(:operation) { described_class.new(is_int) }
include_context "predicates"
let(:is_int) { Dry::Logic::Rule::Predicate.build(int?) }
describe "#call" do
it "negates its rule" do
expect(operation.("19")).to be_success
expect(operation.(17)).to be_failure
end
context "double negation" do
subject(:double_negation) { described_class.new(operation) }
it "works as rule" do
expect(double_negation.("19")).to be_failure
expect(double_negation.(17)).to be_success
end
end
end
describe "#to_ast" do
it "returns ast" do
expect(operation.to_ast).to eql(
[:not, [:predicate, [:int?, [[:input, undefined]]]]]
)
end
it "returns result ast" do
expect(operation.(17).to_ast).to eql(
[:not, [:predicate, [:int?, [[:input, 17]]]]]
)
end
it "returns result ast with an :id" do
expect(operation.with(id: :age).(17).to_ast).to eql(
[:failure, [:age, [:not, [:predicate, [:int?, [[:input, 17]]]]]]]
)
end
end
describe "#to_s" do
it "returns string representation" do
expect(operation.to_s).to eql("not(int?)")
end
end
end
================================================
FILE: spec/unit/operations/or_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Or do
subject(:operation) { described_class.new(left, right) }
include_context "predicates"
let(:left) { Dry::Logic::Rule::Predicate.build(nil?) }
let(:right) { Dry::Logic::Rule::Predicate.build(gt?).curry(18) }
let(:other) do
Dry::Logic::Rule::Predicate.build(int?) & Dry::Logic::Rule::Predicate.build(lt?).curry(14)
end
describe "#call" do
it "calls left and right" do
expect(operation.(nil)).to be_success
expect(operation.(19)).to be_success
expect(operation.(18)).to be_failure
end
end
describe "#to_ast" do
it "returns ast" do
expect(operation.to_ast).to eql(
[:or, [
[:predicate, [:nil?, [[:input, undefined]]]],
[:predicate, [:gt?, [[:num, 18], [:input, undefined]]]]
]]
)
end
it "returns result ast" do
expect(operation.(17).to_ast).to eql(
[:or, [
[:predicate, [:nil?, [[:input, 17]]]],
[:predicate, [:gt?, [[:num, 18], [:input, 17]]]]
]]
)
end
it "returns failure result ast" do
expect(operation.with(id: :age).(17).to_ast).to eql(
[:failure, [:age, [:or, [
[:predicate, [:nil?, [[:input, 17]]]],
[:predicate, [:gt?, [[:num, 18], [:input, 17]]]]
]]]]
)
end
end
describe "#and" do
it "creates and with the other" do
expect(operation.and(other).(nil)).to be_failure
expect(operation.and(other).(19)).to be_failure
expect(operation.and(other).(13)).to be_failure
expect(operation.and(other).(14)).to be_failure
end
end
describe "#or" do
it "creates or with the other" do
expect(operation.or(other).(nil)).to be_success
expect(operation.or(other).(19)).to be_success
expect(operation.or(other).(13)).to be_success
expect(operation.or(other).(14)).to be_failure
end
end
describe "#to_s" do
it "returns string representation" do
expect(operation.to_s).to eql("nil? OR gt?(18)")
end
end
end
================================================
FILE: spec/unit/operations/set_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Set do
subject(:operation) { described_class.new(is_int, gt_18) }
include_context "predicates"
let(:is_int) { Dry::Logic::Rule::Predicate.build(int?) }
let(:gt_18) { Dry::Logic::Rule::Predicate.build(gt?, args: [18]) }
describe "#call" do
it "applies all its rules to the input" do
expect(operation.(19)).to be_success
expect(operation.(17)).to be_failure
end
end
describe "#to_ast" do
it "returns ast" do
expect(operation.to_ast).to eql(
[:set, [[:predicate, [:int?, [[:input, undefined]]]], [:predicate, [:gt?, [[:num, 18], [:input, undefined]]]]]]
)
end
it "returns result ast" do
expect(operation.(17).to_ast).to eql(
[:set, [[:predicate, [:gt?, [[:num, 18], [:input, 17]]]]]]
)
end
it "returns result ast with an :id" do
expect(operation.with(id: :age).(17).to_ast).to eql(
[:failure, [:age, [:set, [[:predicate, [:gt?, [[:num, 18], [:input, 17]]]]]]]]
)
end
end
describe "#to_s" do
it "returns string representation" do
expect(operation.to_s).to eql("set(int?, gt?(18))")
end
end
end
================================================
FILE: spec/unit/operations/xor_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Operations::Xor do
subject(:operation) { described_class.new(left, right) }
include_context "predicates"
let(:left) { Dry::Logic::Rule::Predicate.build(array?) }
let(:right) { Dry::Logic::Rule::Predicate.build(empty?) }
let(:other) do
Dry::Logic::Rule::Predicate.build(str?)
end
describe "#call" do
it "calls left and right" do
expect(operation.(nil)).to be_success
expect(operation.("")).to be_success
expect(operation.([])).to be_failure
end
end
describe "#to_ast" do
it "returns ast" do
expect(operation.to_ast).to eql(
[:xor, [[:predicate, [:array?, [[:input, undefined]]]], [:predicate, [:empty?, [[:input, undefined]]]]]]
)
end
it "returns result ast" do
expect(operation.([]).to_ast).to eql(
[:xor, [[:predicate, [:array?, [[:input, []]]]], [:predicate, [:empty?, [[:input, []]]]]]]
)
end
it "returns failure result ast" do
expect(operation.with(id: :name).([]).to_ast).to eql(
[:failure, [:name, [:xor, [[:predicate, [:array?, [[:input, []]]]], [:predicate, [:empty?, [[:input, []]]]]]]]]
)
end
end
describe "#and" do
it "creates conjunction with the other" do
expect(operation.and(other).(nil)).to be_failure
expect(operation.and(other).(19)).to be_failure
expect(operation.and(other).("")).to be_success
end
end
describe "#or" do
it "creates disjunction with the other" do
expect(operation.or(other).([])).to be_failure
expect(operation.or(other).("")).to be_success
end
end
describe "#to_s" do
it "returns string representation" do
expect(operation.to_s).to eql("array? XOR empty?")
end
end
end
================================================
FILE: spec/unit/predicates/array_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#array?" do
let(:predicate_name) { :array? }
context "when value is an array" do
let(:arguments_list) do
[
[[]],
[%w[other array]],
[[123, "really", :blah]],
[[]],
[[nil]],
[[false]],
[[true]]
]
end
it_behaves_like "a passing predicate"
end
context "when value is not an array" do
let(:arguments_list) do
[
[""],
[{}],
[nil],
[:symbol],
[String],
[1],
[1.0],
[true],
[{}]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/attr_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#attr?" do
let(:predicate_name) { :attr? }
context "when value responds to the attr name" do
let(:arguments_list) do
[
[:name, Struct.new(:name).new("John")],
[:age, Struct.new(:age).new(18)]
]
end
it_behaves_like "a passing predicate"
end
context "with value does not respond to the attr name" do
let(:arguments_list) do
[
[:name, Struct.new(:age).new(18)],
[:age, Struct.new(:name).new("Jill")]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/bool_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#bool?" do
let(:predicate_name) { :bool? }
context "when value is a boolean" do
let(:arguments_list) do
[[true], [false]]
end
it_behaves_like "a passing predicate"
end
context "when value is not a bool" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil],
[:symbol],
[String],
[1],
[0],
["true"],
["false"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/bytesize_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#bytesize?" do
let(:predicate_name) { :bytesize? }
context "when value size is equal to n" do
let(:arguments_list) do
[
[4, "こa"],
[1..8, "こa"]
]
end
it_behaves_like "a passing predicate"
end
context "when value size is greater than n" do
let(:arguments_list) do
[
[3, "こa"],
[1..3, "こa"]
]
end
it_behaves_like "a failing predicate"
end
context "with value size is less than n" do
let(:arguments_list) do
[
[5, "こa"],
[5..10, "こa"]
]
end
it_behaves_like "a failing predicate"
end
context "with an unsupported size" do
it "raises an error" do
expect { Dry::Logic::Predicates[:bytesize?].call("oops", 1) }.to raise_error(ArgumentError, /oops/)
end
end
end
end
================================================
FILE: spec/unit/predicates/case_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#case?" do
let(:predicate_name) { :case? }
context "when the value matches the pattern" do
let(:arguments_list) do
[
[11, 11],
[:odd?.to_proc, 11],
[/\Af/, "foo"],
[Integer, 11]
]
end
it_behaves_like "a passing predicate"
end
context "when the value doesn't match the pattern" do
let(:arguments_list) do
[
[13, 14],
[:odd?.to_proc, 12],
[/\Af/, "bar"],
[String, 11]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/date_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#date?" do
let(:predicate_name) { :date? }
context "when value is a date" do
let(:arguments_list) do
[[Date.today]]
end
it_behaves_like "a passing predicate"
end
context "with value is not an integer" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil],
[:symbol],
[String],
[1]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/date_time_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#date_time?" do
let(:predicate_name) { :date_time? }
context "when value is a datetime" do
let(:arguments_list) do
[[DateTime.now]]
end
it_behaves_like "a passing predicate"
end
context "with value is not an integer" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil],
[:symbol],
[String],
[1]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/decimal_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#decimal?" do
let(:predicate_name) { :decimal? }
context "when value is a decimal" do
let(:arguments_list) do
[[1.2.to_d]]
end
it_behaves_like "a passing predicate"
end
context "with value is not an integer" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil],
[:symbol],
[String],
[1],
[1.0]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/empty_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#empty?" do
let(:predicate_name) { :empty? }
context "when value is empty" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil]
]
end
it_behaves_like "a passing predicate"
end
context "with value is not empty" do
let(:arguments_list) do
[
["Jill"],
[[1, 2, 3]],
[{name: "John"}],
[true],
[false],
["1"],
["0"],
[:symbol],
[String]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/eql_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates, "#eql?" do
let(:predicate_name) { :eql? }
context "when value is equal to the arg" do
let(:arguments_list) do
[%w[Foo Foo]]
end
it_behaves_like "a passing predicate"
end
context "with value is not equal to the arg" do
let(:arguments_list) do
[%w[Bar Foo]]
end
it_behaves_like "a failing predicate"
end
end
================================================
FILE: spec/unit/predicates/even_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#even?" do
let(:predicate_name) { :even? }
context "when value is an odd int" do
let(:arguments_list) do
[
[13],
[1],
[1111]
]
end
it_behaves_like "a failing predicate"
end
context "with value is an even int" do
let(:arguments_list) do
[
[0],
[2],
[2222]
]
end
it_behaves_like "a passing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/excluded_from_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#excluded_from?" do
let(:predicate_name) { :excluded_from? }
context "when value is not present in list" do
let(:arguments_list) do
[
[%w[Jill John], "Jack"],
[1..2, 0],
[1..2, 3],
[[nil, false], true]
]
end
it_behaves_like "a passing predicate"
end
context "with value is present in list" do
let(:arguments_list) do
[
[%w[Jill John], "Jill"],
[%w[Jill John], "John"],
[1..2, 1],
[1..2, 2],
[[nil, false], nil],
[[nil, false], false]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/excludes_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#excludes?" do
let(:predicate_name) { :excludes? }
context "with input excludes value" do
let(:arguments_list) do
[
["Jack", %w[Jill John]],
[0, 1..2],
[3, 1..2],
["foo", "Hello World"],
[:foo, {bar: 0}],
[true, [nil, false]]
]
end
it_behaves_like "a passing predicate"
end
context "with input of invalid type" do
let(:arguments_list) do
[
[2, 1],
[1, nil],
["foo", 1],
[1, "foo"],
[1..2, "foo"],
["foo", 1..2],
[:key, "foo"]
]
end
it_behaves_like "a passing predicate"
end
context "when input includes value" do
let(:arguments_list) do
[
["Jill", %w[Jill John]],
["John", %w[Jill John]],
[1, 1..2],
[2, 1..2],
["Hello", "Hello World"],
["World", "Hello World"],
[:bar, {bar: 0}],
[nil, [nil, false]],
[false, [nil, false]]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/false_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#false?" do
let(:predicate_name) { :false? }
context "when value is false" do
let(:arguments_list) do
[[false]]
end
it_behaves_like "a passing predicate"
end
context "when value is not false" do
let(:arguments_list) do
[
[true],
[""],
[[]],
[{}],
[nil],
[:symbol],
[String],
[1],
[0],
["true"],
["false"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/filled_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#filled?" do
let(:predicate_name) { :filled? }
context "when value is filled" do
let(:arguments_list) do
[
["Jill"],
[[1, 2, 3]],
[{name: "John"}],
[true],
[false],
["1"],
["0"],
[:symbol],
[String]
]
end
it_behaves_like "a passing predicate"
end
context "with value is not filled" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/float_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#float?" do
let(:predicate_name) { :float? }
context "when value is a float" do
let(:arguments_list) do
[[1.0]]
end
it_behaves_like "a passing predicate"
end
context "with value is not an integer" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil],
[:symbol],
[String],
[1]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/format_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates, "#format?" do
let(:predicate_name) { :format? }
context "when value matches provided regexp" do
let(:arguments_list) do
[[/^F/, "Foo"]]
end
it_behaves_like "a passing predicate"
end
context "when value does not match provided regexp" do
let(:arguments_list) do
[[/^F/, "Bar"]]
end
it_behaves_like "a failing predicate"
end
context "when input is nil" do
let(:arguments_list) do
[[/^F/, nil]]
end
it_behaves_like "a failing predicate"
end
end
================================================
FILE: spec/unit/predicates/gt_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#gt?" do
let(:predicate_name) { :gt? }
context "when value is greater than n" do
let(:arguments_list) do
[
[13, 14],
[13.37, 13.38]
]
end
it_behaves_like "a passing predicate"
end
context "when value is equal to n" do
let(:arguments_list) do
[
[13, 13],
[13.37, 13.37]
]
end
it_behaves_like "a failing predicate"
end
context "with value is less than n" do
let(:arguments_list) do
[
[13, 12],
[13.37, 13.36]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/gteq_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#gteq?" do
let(:predicate_name) { :gteq? }
context "when value is greater than n" do
let(:arguments_list) do
[
[13, 14],
[13.37, 13.38]
]
end
it_behaves_like "a passing predicate"
end
context "when value is equal to n" do
let(:arguments_list) do
[
[13, 13],
[13.37, 13.37]
]
end
it_behaves_like "a passing predicate"
end
context "with value is less than n" do
let(:arguments_list) do
[
[13, 12],
[13.37, 13.36]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/hash_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#hash?" do
let(:predicate_name) { :hash? }
context "when value is a hash" do
let(:arguments_list) do
[
[{}],
[foo: :bar],
[{}]
]
end
it_behaves_like "a passing predicate"
end
context "when value is not a hash" do
let(:arguments_list) do
[
[""],
[[]],
[nil],
[:symbol],
[String],
[1],
[1.0],
[true],
[[]],
[Hash]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/included_in_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#included_in?" do
let(:predicate_name) { :included_in? }
context "when value is present in list" do
let(:arguments_list) do
[
[%w[Jill John], "Jill"],
[%w[Jill John], "John"],
[1..2, 1],
[1..2, 2],
[[nil, false], nil],
[[nil, false], false]
]
end
it_behaves_like "a passing predicate"
end
context "with value is not present in list" do
let(:arguments_list) do
[
[%w[Jill John], "Jack"],
[1..2, 0],
[1..2, 3],
[[nil, false], true]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/includes_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates, "#is?" do
let(:predicate_name) { :is? }
let(:one) { Object.new }
let(:two) { Object.new }
context "when value is equal to the arg" do
let(:arguments_list) do
[[one, one], [:one, :one]]
end
it_behaves_like "a passing predicate"
end
context "with value is not equal to the arg" do
let(:arguments_list) do
[[one, two], [{}, {}]]
end
it_behaves_like "a failing predicate"
end
end
================================================
FILE: spec/unit/predicates/int_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#int?" do
let(:predicate_name) { :int? }
context "when value is an integer" do
let(:arguments_list) do
[
[1],
[33],
[7]
]
end
it_behaves_like "a passing predicate"
end
context "with value is not an integer" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil],
[:symbol],
[String]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/key_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#key?" do
let(:predicate_name) { :key? }
context "when key is present in value" do
let(:arguments_list) do
[
[:name, {name: "John"}],
[:age, {age: 18}]
]
end
it_behaves_like "a passing predicate"
end
context "with key is not present in value" do
let(:arguments_list) do
[
[:name, {age: 18}],
[:age, {name: "Jill"}]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/lt_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#lt?" do
let(:predicate_name) { :lt? }
context "when value is less than n" do
let(:arguments_list) do
[
[13, 12],
[13.37, 13.36]
]
end
it_behaves_like "a passing predicate"
end
context "when value is equal to n" do
let(:arguments_list) do
[
[13, 13],
[13.37, 13.37]
]
end
it_behaves_like "a failing predicate"
end
context "with value is greater than n" do
let(:arguments_list) do
[
[13, 14],
[13.37, 13.38]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/lteq_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#lteq?" do
let(:predicate_name) { :lteq? }
context "when value is less than n" do
let(:arguments_list) do
[
[13, 12],
[13.37, 13.36]
]
end
it_behaves_like "a passing predicate"
end
context "when value is equal to n" do
let(:arguments_list) do
[
[13, 13],
[13.37, 13.37]
]
end
it_behaves_like "a passing predicate"
end
context "with value is greater than n" do
let(:arguments_list) do
[
[13, 14],
[13.37, 13.38]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/max_bytesize_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#max_bytesize?" do
let(:predicate_name) { :max_bytesize? }
context "when value size is less than n" do
let(:arguments_list) do
[
[5, "こa"]
]
end
it_behaves_like "a passing predicate"
end
context "when value size is equal to n" do
let(:arguments_list) do
[
[5, "こab"]
]
end
it_behaves_like "a passing predicate"
end
context "with value size is greater than n" do
let(:arguments_list) do
[
[5, "こabc"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/max_size_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#max_size?" do
let(:predicate_name) { :max_size? }
context "when value size is less than n" do
let(:arguments_list) do
[
[3, [1, 2]],
[5, "Jill"],
[3, {1 => "st", 2 => "nd"}],
[6, 1..5]
]
end
it_behaves_like "a passing predicate"
end
context "when value size is equal to n" do
let(:arguments_list) do
[
[2, [1, 2]],
[4, "Jill"],
[2, {1 => "st", 2 => "nd"}],
[5, 1..5]
]
end
it_behaves_like "a passing predicate"
end
context "with value size is greater than n" do
let(:arguments_list) do
[
[1, [1, 2]],
[3, "Jill"],
[1, {1 => "st", 2 => "nd"}],
[4, 1..5]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/min_bytesize_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#min_bytesize?" do
let(:predicate_name) { :min_bytesize? }
context "when value size is greater than n" do
let(:arguments_list) do
[
[3, "こa"]
]
end
it_behaves_like "a passing predicate"
end
context "when value size is equal to n" do
let(:arguments_list) do
[
[5, "こab"]
]
end
it_behaves_like "a passing predicate"
end
context "with value size is less than n" do
let(:arguments_list) do
[
[5, "こ"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/min_size_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#min_size?" do
let(:predicate_name) { :min_size? }
context "when value size is greater than n" do
let(:arguments_list) do
[
[1, [1, 2]],
[3, "Jill"],
[1, {1 => "st", 2 => "nd"}],
[4, 1..5]
]
end
it_behaves_like "a passing predicate"
end
context "when value size is equal to n" do
let(:arguments_list) do
[
[2, [1, 2]],
[4, "Jill"],
[2, {1 => "st", 2 => "nd"}],
[5, 1..5]
]
end
it_behaves_like "a passing predicate"
end
context "with value size is less than n" do
let(:arguments_list) do
[
[3, [1, 2]],
[5, "Jill"],
[3, {1 => "st", 2 => "nd"}],
[6, 1..5]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/none_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#nil?" do
let(:predicate_name) { :nil? }
context "when value is nil" do
let(:arguments_list) { [[nil]] }
it_behaves_like "a passing predicate"
end
context "when value is not nil" do
let(:arguments_list) do
[
[""],
[true],
[false],
[0],
[:symbol],
[[]],
[{}],
[String]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/not_eql_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates, "#not_eql?" do
let(:predicate_name) { :not_eql? }
context "when value is equal to the arg" do
let(:arguments_list) do
[%w[Foo Foo]]
end
it_behaves_like "a failing predicate"
end
context "with value is not equal to the arg" do
let(:arguments_list) do
[%w[Bar Foo]]
end
it_behaves_like "a passing predicate"
end
end
================================================
FILE: spec/unit/predicates/number_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#number?" do
let(:predicate_name) { :number? }
context "when value is numerical" do
let(:arguments_list) do
[
["34"],
["1.000004"],
["0"],
[4],
["-15.24"],
[-3.5]
]
end
it_behaves_like "a passing predicate"
end
context "with value is not numerical" do
let(:arguments_list) do
[
[""],
["-14px"],
["10,150.00"],
[nil],
[:symbol],
[String]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/odd_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#odd?" do
let(:predicate_name) { :odd? }
context "when value is an odd int" do
let(:arguments_list) do
[
[13],
[1],
[1111]
]
end
it_behaves_like "a passing predicate"
end
context "with value is an even int" do
let(:arguments_list) do
[
[0],
[2],
[2222]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/respond_to_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#respond_to?" do
let(:predicate_name) { :respond_to? }
context "when value responds to method" do
let(:arguments_list) do
[
[:method, Object],
[:new, Hash]
]
end
it_behaves_like "a passing predicate"
end
context "when value does not respond to method" do
let(:arguments_list) do
[
[:foo, Object],
[:bar, Hash]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/size_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#size?" do
let(:predicate_name) { :size? }
context "when value size is equal to n" do
let(:arguments_list) do
[
[[2, 4, 6], "abcd"],
[4, "Jill"],
[2, {1 => "st", 2 => "nd"}],
[1..8, "qwerty"]
]
end
it_behaves_like "a passing predicate"
end
context "when value size is greater than n" do
let(:arguments_list) do
[
[[1, 2], "abc"],
[5, "Jill"],
[3, {1 => "st", 2 => "nd"}],
[1..5, "qwerty"]
]
end
it_behaves_like "a failing predicate"
end
context "with value size is less than n" do
let(:arguments_list) do
[
[[1, 2], 1],
[3, "Jill"],
[1, {1 => "st", 2 => "nd"}],
[1..5, "qwerty"]
]
end
it_behaves_like "a failing predicate"
end
context "with an unsupported size" do
it "raises an error" do
expect { Dry::Logic::Predicates[:size?].call("oops", 1) }.to raise_error(ArgumentError, /oops/)
end
end
end
end
================================================
FILE: spec/unit/predicates/str_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#str?" do
let(:predicate_name) { :str? }
context "when value is a string" do
let(:arguments_list) do
[
[""],
["John"]
]
end
it_behaves_like "a passing predicate"
end
context "with value is not a string" do
let(:arguments_list) do
[
[[]],
[{}],
[nil],
[:symbol],
[String]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/time_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#time?" do
let(:predicate_name) { :time? }
context "when value is a time" do
let(:arguments_list) do
[[Time.now]]
end
it_behaves_like "a passing predicate"
end
context "with value is not an integer" do
let(:arguments_list) do
[
[""],
[[]],
[{}],
[nil],
[:symbol],
[String],
[1]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/true_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#true?" do
let(:predicate_name) { :true? }
context "when value is true" do
let(:arguments_list) do
[[true]]
end
it_behaves_like "a passing predicate"
end
context "with value is not true" do
let(:arguments_list) do
[
[false],
[""],
[[]],
[{}],
[nil],
[:symbol],
[String],
[1],
[0],
["true"],
["false"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/type_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#type?" do
let(:predicate_name) { :type? }
context "when value has a correct type" do
let(:arguments_list) do
[[TrueClass, true]]
end
it_behaves_like "a passing predicate"
end
context "with value is not true" do
let(:arguments_list) do
[
[TrueClass, false],
[TrueClass, ""],
[TrueClass, []],
[TrueClass, {}],
[TrueClass, nil],
[TrueClass, :symbol],
[TrueClass, String],
[TrueClass, 1],
[TrueClass, 0],
[TrueClass, "true"],
[TrueClass, "false"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uri_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uri?" do
let(:predicate_name) { :uri? }
context "when value is a valid URI" do
let(:arguments_list) do
[
[nil, "https://github.com/dry-rb/dry-logic"], # without schemes param
["https", "https://github.com/dry-rb/dry-logic"], # with scheme param
[%w[http https], "https://github.com/dry-rb/dry-logic"], # with schemes array
["mailto", "mailto:myemail@host.com"], # with mailto format
["urn", "urn:isbn:0451450523"] # with URN format
]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid URI" do
let(:arguments_list) do
[
["http", "mailto:myemail@host.com"], # scheme not allowed
[%w[http https], "ftp:://myftp.com"], # scheme not allowed
["", "not-a-uri-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
describe "#uri_rfc3986?" do
let(:predicate_name) { :uri_rfc3986? }
context "when value is a valid URI" do
let(:arguments_list) do
[
["https://github.com/dry-rb/dry-logic"], # with https format
["mailto:myemail@host.com"], # with mailto format
["urn:isbn:0451450523"] # with URN format
]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid URI" do
let(:arguments_list) do
[
["not-a-uri-at-all"],
["[https://github.com/dry-rb/dry-logic]"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uuid_v1_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uuid_v1?" do
let(:predicate_name) { :uuid_v1? }
context "when value is a valid V1 UUID" do
let(:arguments_list) do
[["f2d26c57-e07c-1416-a749-57e937930e04"]]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid V1 UUID" do
let(:arguments_list) do
[
["not-a-uuid-at-all\nf2d26c57-e07c-1416-a749-57e937930e04"], # V1 with invalid prefix
["f2d26c57-e07c-1416-a749-57e937930e04\nnot-a-uuid-at-all"], # V1 with invalid suffix
["f2d26c57-e07c-3416-a749-57e937930e04"], # wrong version number (3, not 1)
["20633928-6a07-41e9-a923-1681be663d3e"], # UUID V4
["not-a-uuid-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uuid_v2_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uuid_v2?" do
let(:predicate_name) { :uuid_v2? }
context "when value is a valid V1 UUID" do
let(:arguments_list) do
[["f2d26c57-e07c-2416-a749-57e937930e04"]]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid V4 UUID" do
let(:arguments_list) do
[
["not-a-uuid-at-all\nf2d26c57-e07c-2416-a749-57e937930e04"], # V2 with invalid prefix
["f2d26c57-e07c-2416-a749-57e937930e04\nnot-a-uuid-at-all"], # V2 with invalid suffix
["f2d26c57-e07c-3416-a749-57e937930e04"], # wrong version number (3, not 2)
["20633928-6a07-11e9-a923-1681be663d3e"], # UUID V1
["not-a-uuid-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uuid_v3_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uuid_v3?" do
let(:predicate_name) { :uuid_v3? }
context "when value is a valid V3 UUID" do
let(:arguments_list) do
[["f2d26c57-e07c-3416-a749-57e937930e04"]]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid V4 UUID" do
let(:arguments_list) do
[
["not-a-uuid-at-all\nf2d26c57-e07c-3416-a749-57e937930e04"], # V3 with invalid prefix
["f2d26c57-e07c-3416-a749-57e937930e04\nnot-a-uuid-at-all"], # V3 with invalid suffix
["f2d26c57-e07c-4416-a749-57e937930e04"], # wrong version number (4, not 3)
["20633928-6a07-11e9-a923-1681be663d3e"], # UUID V1
["not-a-uuid-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uuid_v4_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uuid_v4?" do
let(:predicate_name) { :uuid_v4? }
context "when value is a valid V4 UUID" do
let(:arguments_list) do
[["f2d26c57-e07c-4416-a749-57e937930e04"]]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid V4 UUID" do
let(:arguments_list) do
[
["not-a-uuid-at-all\nf2d26c57-e07c-4416-a749-57e937930e04"], # V4 with invalid prefix
["f2d26c57-e07c-4416-a749-57e937930e04\nnot-a-uuid-at-all"], # V4 with invalid suffix
["f2d26c57-e07c-3416-a749-57e937930e04"], # wrong version number (3, not 4)
["20633928-6a07-11e9-a923-1681be663d3e"], # UUID V1
["not-a-uuid-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uuid_v5_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uuid_v5?" do
let(:predicate_name) { :uuid_v5? }
context "when value is a valid V5 UUID" do
let(:arguments_list) do
[["f2d26c57-e07c-5416-a749-57e937930e04"]]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid V5 UUID" do
let(:arguments_list) do
[
["not-a-uuid-at-all\nf2d26c57-e07c-5416-a749-57e937930e04"], # V5 with invalid prefix
["f2d26c57-e07c-5416-a749-57e937930e04\nnot-a-uuid-at-all"], # V5 with invalid suffix
["f2d26c57-e07c-3416-a749-57e937930e04"], # wrong version number (3, not 5)
["20633928-6a07-11e9-a923-1681be663d3e"], # UUID V1
["not-a-uuid-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uuid_v6_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uuid_v6?" do
let(:predicate_name) { :uuid_v6? }
context "when value is a valid V6 UUID" do
let(:arguments_list) do
[["1ec9414c-232a-6b00-b3c8-9e6bdeced846"]]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid V6 UUID" do
let(:arguments_list) do
[
["not-a-uuid-at-all\n1ec9414c-232a-6b00-b3c8-9e6bdeced846"], # V6 with invalid prefix
["1ec9414c-232a-6b00-b3c8-9e6bdeced846\nnot-a-uuid-at-all"], # V6 with invalid suffix
["1ec9414c-232a-3b00-b3c8-9e6bdeced846"], # wrong version number (3, not 6)
["20633928-6a07-11e9-a923-1681be663d3e"], # UUID V1
["not-a-uuid-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uuid_v7_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uuid_v7?" do
let(:predicate_name) { :uuid_v7? }
context "when value is a valid V7 UUID" do
let(:arguments_list) do
[["017f22e2-79b0-7cc3-98c4-dc0c0c07398f"]]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid V7 UUID" do
let(:arguments_list) do
[
["not-a-uuid-at-all\n017f22e2-79b0-7cc3-98c4-dc0c0c07398f"], # V6 with invalid prefix
["017f22e2-79b0-7cc3-98c4-dc0c0c07398f\nnot-a-uuid-at-all"], # V6 with invalid suffix
["017f22e2-79b0-4cc3-98c4-dc0c0c07398f"], # wrong version number (4, not 7)
["20633928-6a07-11e9-a923-1681be663d3e"], # UUID V1
["not-a-uuid-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates/uuid_v8_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
describe "#uuid_v8?" do
let(:predicate_name) { :uuid_v8? }
context "when value is a valid V8 UUID" do
let(:arguments_list) do
[["320c3d4d-cc00-875b-8ec9-32d5f69181c0"]]
end
it_behaves_like "a passing predicate"
end
context "with value is not a valid V8 UUID" do
let(:arguments_list) do
[
["not-a-uuid-at-all\n320c3d4d-cc00-875b-8ec9-32d5f69181c0"], # V6 with invalid prefix
["320c3d4d-cc00-875b-8ec9-32d5f69181c0\nnot-a-uuid-at-all"], # V6 with invalid suffix
["320c3d4d-cc00-475b-8ec9-32d5f69181c0"], # wrong version number (4, not 8)
["20633928-6a07-11e9-a923-1681be663d3e"], # UUID V1
["not-a-uuid-at-all"]
]
end
it_behaves_like "a failing predicate"
end
end
end
================================================
FILE: spec/unit/predicates_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/predicates"
RSpec.describe Dry::Logic::Predicates do
it "can be included in another module" do
mod = Module.new { include Dry::Logic::Predicates }
expect(mod[:key?]).to be_a(Method)
end
describe ".predicate" do
it "defines a predicate method" do
mod = Module.new {
include Dry::Logic::Predicates
predicate(:test?) do |foo|
true
end
}
expect(mod.test?("arg")).to be(true)
end
end
describe ".respond_to?" do
it "works with a just the method name" do
expect(Dry::Logic::Predicates.respond_to?(:predicate)).to be(true)
expect(Dry::Logic::Predicates.respond_to?(:not_here)).to be(false)
end
end
describe ".eql?" do
it "works with another object to compare to" do
expect(Dry::Logic::Predicates.eql?(Dry::Logic::Predicates)).to be(true)
expect(Dry::Logic::Predicates.eql?("something else")).to be(false)
end
end
end
================================================
FILE: spec/unit/rule/predicate_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Rule::Predicate do
subject(:rule) { described_class.build(predicate) }
let(:predicate) { str? }
include_context "predicates"
it_behaves_like Dry::Logic::Rule
describe "#name" do
it "returns predicate identifier" do
expect(rule.name).to be(:str?)
end
end
describe "#to_ast" do
context "without a result" do
it "returns rule ast" do
expect(rule.to_ast).to eql([:predicate, [:str?, [[:input, undefined]]]])
end
it "returns :failure with an id" do
email = rule.with(id: :email)
expect(email.(11).to_ast).to eql([:failure, [:email, [:predicate, [:str?, [[:input, 11]]]]]])
end
end
context "with a result" do
it "returns success" do
expect(rule.("foo")).to be_success
end
it "returns failure ast" do
expect(rule.(5).to_ast).to eql([:predicate, [:str?, [[:input, 5]]]])
end
end
context "with a zero-arity predicate" do
let(:predicate) {
Module.new {
def self.test?
true
end
} .method(:test?)
}
it "returns ast" do
expect(rule.to_ast).to eql([:predicate, [:test?, []]])
end
end
end
describe "#to_s" do
it "returns string representation" do
expect(rule.curry("foo").to_s).to eql('str?("foo")')
end
end
end
================================================
FILE: spec/unit/rule_compiler_spec.rb
================================================
# frozen_string_literal: true
require "dry/logic/rule_compiler"
RSpec.describe Dry::Logic::RuleCompiler, "#call" do
subject(:compiler) { described_class.new(predicates) }
let(:predicates) {
{key?: predicate,
attr?: predicate,
filled?: predicate,
gt?: predicate,
one: predicate}
}
let(:predicate) { double(:predicate, name: :test?, arity: 2).as_null_object }
let(:rule) { Dry::Logic::Rule::Predicate.build(predicate) }
let(:key_op) { Dry::Logic::Operations::Key.new(rule, name: :email) }
let(:attr_op) { Dry::Logic::Operations::Attr.new(rule, name: :email) }
let(:check_op) { Dry::Logic::Operations::Check.new(rule, keys: [:email]) }
let(:not_key_op) { Dry::Logic::Operations::Negation.new(key_op) }
let(:and_op) { key_op.curry(:email) & rule }
let(:or_op) { key_op.curry(:email) | rule }
let(:xor_op) { key_op.curry(:email) ^ rule }
let(:set_op) { Dry::Logic::Operations::Set.new(rule) }
let(:each_op) { Dry::Logic::Operations::Each.new(rule) }
it "compiles key rules" do
ast = [[:key, [:email, [:predicate, [:filled?, [[:input, undefined]]]]]]]
rules = compiler.(ast)
expect(rules).to eql([key_op])
end
it "compiles attr rules" do
ast = [[:attr, [:email, [:predicate, [:filled?, [[:input, undefined]]]]]]]
rules = compiler.(ast)
expect(rules).to eql([attr_op])
end
it "compiles check rules" do
ast = [[:check, [[:email], [:predicate, [:filled?, [[:input, undefined]]]]]]]
rules = compiler.(ast)
expect(rules).to eql([check_op])
end
it "compiles attr rules" do
ast = [[:attr, [:email, [:predicate, [:filled?, [[:input, undefined]]]]]]]
rules = compiler.(ast)
expect(rules).to eql([attr_op])
end
it "compiles negated rules" do
ast = [[:not, [:key, [:email, [:predicate, [:filled?, [[:input, undefined]]]]]]]]
rules = compiler.(ast)
expect(rules).to eql([not_key_op])
end
it "compiles and rules" do
ast = [
[
:and, [
[:key, [:email, [:predicate, [:key?, [[:name, :email], [:input, undefined]]]]]],
[:predicate, [:filled?, [[:input, undefined]]]]
]
]
]
rules = compiler.(ast)
expect(rules).to eql([and_op])
end
it "compiles or rules" do
ast = [
[
:or, [
[:key, [:email, [:predicate, [:key?, [[:name, :email], [:input, undefined]]]]]],
[:predicate, [:filled?, [[:input, undefined]]]]
]
]
]
rules = compiler.(ast)
expect(rules).to eql([or_op])
end
it "compiles exclusive or rules" do
ast = [
[
:xor, [
[:key, [:email, [:predicate, [:key?, [[:name, :email], [:input, undefined]]]]]],
[:predicate, [:filled?, [[:input, undefined]]]]
]
]
]
rules = compiler.(ast)
expect(rules).to eql([xor_op])
end
it "compiles set rules" do
ast = [[:set, [[:predicate, [:filled?, [[:input, nil]]]]]]]
rules = compiler.(ast)
expect(rules).to eql([set_op])
end
it "compiles each rules" do
ast = [[:each, [:predicate, [:filled?, [[:input, nil]]]]]]
rules = compiler.(ast)
expect(rules).to eql([each_op])
end
end
================================================
FILE: spec/unit/rule_spec.rb
================================================
# frozen_string_literal: true
RSpec.describe Dry::Logic::Rule do
subject(:rule) { described_class.build(predicate, **options) }
let(:predicate) { -> { true } }
let(:options) { {} }
let(:schema) do
Class.new do
define_method(:class, Kernel.instance_method(:class))
def respond_to_missing?(m, *)
super || m.to_s.end_with?("?")
end
def method_missing(m, *)
if m.to_s.end_with?("?")
self.class.new
else
super
end
end
def to_proc
-> value { value }
end
def arity
1
end
def parameters
[[:req, :value]]
end
end.new
end
it_behaves_like described_class
describe ".new" do
it "accepts an :id" do
expect(described_class.build(predicate, id: :check_num).id).to be(:check_num)
end
end
describe "with a function returning truthy value" do
it "is successful for valid input" do
expect(described_class.build(-> val { val }).("true")).to be_success
end
it "is not successful for invalid input" do
expect(described_class.build(-> val { val }).(nil)).to be_failure
end
end
describe "#ast" do
it "returns predicate node with :id" do
expect(described_class.build(-> value { true }).with(id: :email?).ast("oops")).to eql(
[:predicate, [:email?, [[:value, "oops"]]]]
)
end
it "returns predicate node with undefined args" do
expect(described_class.build(-> value { true }).with(id: :email?).ast).to eql(
[:predicate, [:email?, [[:value, undefined]]]]
)
end
end
describe "#type" do
it "returns rule type" do
expect(rule.type).to be(:rule)
end
end
describe "#bind" do
let(:bound) { rule.with(id: :bound).bind(object) }
context "with an unbound method" do
let(:predicate) { klass.instance_method(:test?) }
let(:klass) {
Class.new {
def test?
true
end
}
}
let(:object) { klass.new }
it "returns a new rule with its predicate bound to a specific object" do
expect(bound.()).to be_success
end
it "carries id" do
expect(bound.id).to be(:bound)
end
end
context "with an arbitrary block" do
let(:predicate) { -> value { value == expected } }
let(:object) {
Class.new {
def expected
"test"
end
} .new
}
it "returns a new with its predicate executed in the context of the provided object" do
expect(bound.("test")).to be_success
expect(bound.("oops")).to be_failure
end
it "carries id" do
expect(bound.id).to be(:bound)
end
it "stores arity" do
expect(bound.options[:arity]).to be(rule.arity)
end
it "stores parameters" do
expect(bound.options[:parameters]).to eql(rule.parameters)
end
end
context "with a schema instance" do
let(:object) { schema }
let(:predicate) { schema }
it "returns a new with its predicate executed in the context of the provided object" do
expect(bound.(true)).to be_success
expect(bound.(false)).to be_failure
end
end
end
describe "#eval_args" do
context "with an unbound method" do
let(:options) { {args: [1, klass.instance_method(:num), :foo], arity: 3} }
let(:klass) {
Class.new {
def num
7
end
}
}
let(:object) { klass.new }
it "evaluates args in the context of the provided object" do
expect(rule.eval_args(object).args).to eql([1, 7, :foo])
end
end
context "with a schema instance" do
let(:options) { {args: [1, schema, :foo], arity: 3} }
let(:object) { Object.new }
it "returns a new with its predicate executed in the context of the provided object" do
expect(rule.eval_args(object).args).to eql([1, schema, :foo])
end
end
end
describe "arity specialization" do
describe "0-arity rule" do
let(:options) { {args: [1], arity: 1} }
let(:predicate) { :odd?.to_proc }
it "generates interface with the right arity" do
expect(rule.method(:call).arity).to be_zero
expect(rule.method(:[]).arity).to be_zero
expect(rule[]).to be(true)
expect(rule.()).to be_success
end
end
describe "1-arity rule" do
let(:options) { {args: [1], arity: 2} }
let(:predicate) { -> a, b { a + b } }
it "generates interface with the right arity" do
expect(rule.method(:call).arity).to be(1)
expect(rule.method(:[]).arity).to be(1)
expect(rule[10]).to be(11)
expect(rule.(1)).to be_success
end
end
describe "currying" do
let(:options) { {args: [], arity: 2} }
let(:predicate) { -> a, b { a + b } }
let(:rule) { super().curry(1) }
it "generates correct arity on currying" do
expect(rule.method(:call).arity).to be(1)
expect(rule.method(:[]).arity).to be(1)
expect(rule[10]).to be(11)
expect(rule.(1)).to be_success
end
end
describe "arbitrary arity" do
let(:arity) { rand(1..20) }
let(:curried) { rand(arity) }
let(:options) { {args: [1] * curried, arity: arity} }
let(:predicate) { double(:predicate) }
it "generates correct arity" do
expect(rule.method(:call).arity).to be(arity - curried)
expect(rule.method(:[]).arity).to be(arity - curried)
end
end
describe "-1 arity" do
let(:options) { {args: [], arity: -1} }
it "accepts variable number of arguments" do
expect(rule.method(:call).arity).to be(-1)
expect(rule.method(:[]).arity).to be(-1)
end
end
describe "-2 arity" do
let(:options) { {args: [], arity: -2} }
it "accepts variable number of arguments" do
expect(rule.method(:call).arity).to be(-2)
expect(rule.method(:[]).arity).to be(-2)
end
context "curried 1" do
let(:options) { {args: [1], arity: -2} }
it "doesn't have required arguments" do
expect(rule.method(:call).arity).to be(-1)
expect(rule.method(:[]).arity).to be(-1)
end
end
context "curried 2" do
let(:options) { {args: [1, 2], arity: -2} }
it "doesn't have required arguments" do
expect(rule.method(:call).arity).to be(-1)
expect(rule.method(:[]).arity).to be(-1)
end
end
end
describe "constants" do
let(:options) { {args: [], arity: 0} }
it "accepts variable number of arguments" do
expect(rule.method(:call).arity).to be(-1)
expect(rule.method(:[]).arity).to be(-1)
end
end
end
end
================================================
FILE: zizmor.yml
================================================
rules:
unpinned-uses:
config:
policies:
hanakai-rb/*: ref-pin
gitextract_njl1vvvj/ ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.md │ │ └── config.yml │ ├── SUPPORT.md │ └── workflows/ │ ├── ci-lint.yml │ ├── ci.yml │ ├── pr-comments.yml │ ├── repo-sync-preview.yml │ └── rubocop.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.devtools ├── LICENSE ├── README.md ├── Rakefile ├── benchmarks/ │ ├── builder.rb │ ├── rule_application.rb │ └── setup.rb ├── bin/ │ ├── .gitkeep │ └── console ├── dry-logic.gemspec ├── examples/ │ └── basic.rb ├── lib/ │ ├── dry/ │ │ ├── logic/ │ │ │ ├── appliable.rb │ │ │ ├── builder.rb │ │ │ ├── evaluator.rb │ │ │ ├── operations/ │ │ │ │ ├── abstract.rb │ │ │ │ ├── and.rb │ │ │ │ ├── attr.rb │ │ │ │ ├── binary.rb │ │ │ │ ├── check.rb │ │ │ │ ├── each.rb │ │ │ │ ├── implication.rb │ │ │ │ ├── key.rb │ │ │ │ ├── negation.rb │ │ │ │ ├── or.rb │ │ │ │ ├── set.rb │ │ │ │ ├── unary.rb │ │ │ │ └── xor.rb │ │ │ ├── operators.rb │ │ │ ├── predicates.rb │ │ │ ├── result.rb │ │ │ ├── rule/ │ │ │ │ ├── interface.rb │ │ │ │ └── predicate.rb │ │ │ ├── rule.rb │ │ │ ├── rule_compiler.rb │ │ │ └── version.rb │ │ └── logic.rb │ └── dry-logic.rb ├── repo-sync.yml ├── spec/ │ ├── integration/ │ │ ├── builder/ │ │ │ ├── operation_spec.rb │ │ │ └── predicate_spec.rb │ │ ├── result_spec.rb │ │ └── rule_spec.rb │ ├── shared/ │ │ ├── built_rule.rb │ │ ├── operation.rb │ │ ├── predicate.rb │ │ ├── predicates.rb │ │ └── rule.rb │ ├── spec_helper.rb │ ├── support/ │ │ ├── coverage.rb │ │ ├── mutant.rb │ │ ├── rspec.rb │ │ └── warnings.rb │ └── unit/ │ ├── builder_spec.rb │ ├── operations/ │ │ ├── and_spec.rb │ │ ├── attr_spec.rb │ │ ├── check_spec.rb │ │ ├── each_spec.rb │ │ ├── implication_spec.rb │ │ ├── key_spec.rb │ │ ├── negation_spec.rb │ │ ├── or_spec.rb │ │ ├── set_spec.rb │ │ └── xor_spec.rb │ ├── predicates/ │ │ ├── array_spec.rb │ │ ├── attr_spec.rb │ │ ├── bool_spec.rb │ │ ├── bytesize_spec.rb │ │ ├── case_spec.rb │ │ ├── date_spec.rb │ │ ├── date_time_spec.rb │ │ ├── decimal_spec.rb │ │ ├── empty_spec.rb │ │ ├── eql_spec.rb │ │ ├── even_spec.rb │ │ ├── excluded_from_spec.rb │ │ ├── excludes_spec.rb │ │ ├── false_spec.rb │ │ ├── filled_spec.rb │ │ ├── float_spec.rb │ │ ├── format_spec.rb │ │ ├── gt_spec.rb │ │ ├── gteq_spec.rb │ │ ├── hash_spec.rb │ │ ├── included_in_spec.rb │ │ ├── includes_spec.rb │ │ ├── int_spec.rb │ │ ├── key_spec.rb │ │ ├── lt_spec.rb │ │ ├── lteq_spec.rb │ │ ├── max_bytesize_spec.rb │ │ ├── max_size_spec.rb │ │ ├── min_bytesize_spec.rb │ │ ├── min_size_spec.rb │ │ ├── none_spec.rb │ │ ├── not_eql_spec.rb │ │ ├── number_spec.rb │ │ ├── odd_spec.rb │ │ ├── respond_to_spec.rb │ │ ├── size_spec.rb │ │ ├── str_spec.rb │ │ ├── time_spec.rb │ │ ├── true_spec.rb │ │ ├── type_spec.rb │ │ ├── uri_spec.rb │ │ ├── uuid_v1_spec.rb │ │ ├── uuid_v2_spec.rb │ │ ├── uuid_v3_spec.rb │ │ ├── uuid_v4_spec.rb │ │ ├── uuid_v5_spec.rb │ │ ├── uuid_v6_spec.rb │ │ ├── uuid_v7_spec.rb │ │ └── uuid_v8_spec.rb │ ├── predicates_spec.rb │ ├── rule/ │ │ └── predicate_spec.rb │ ├── rule_compiler_spec.rb │ └── rule_spec.rb └── zizmor.yml
SYMBOL INDEX (305 symbols across 31 files)
FILE: benchmarks/builder.rb
function regular (line 7) | def regular
function builder (line 14) | def builder
FILE: benchmarks/setup.rb
function profile (line 10) | def profile(&block)
FILE: lib/dry/logic.rb
type Dry (line 6) | module Dry
type Logic (line 7) | module Logic
function loader (line 10) | def self.loader
FILE: lib/dry/logic/appliable.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Appliable (line 5) | module Appliable
function id (line 6) | def id
function result (line 10) | def result
function applied? (line 14) | def applied?
function success? (line 18) | def success?
function failure? (line 22) | def failure?
function to_ast (line 26) | def to_ast
FILE: lib/dry/logic/builder.rb
type Dry (line 6) | module Dry
type Logic (line 7) | module Logic
type Builder (line 8) | module Builder
function call (line 31) | def call(&)
class Context (line 38) | class Context
type Predicates (line 42) | module Predicates
method call (line 47) | def call(&)
method predicate (line 55) | def predicate(name, context = nil, &block)
method initialize (line 68) | def initialize
FILE: lib/dry/logic/evaluator.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
class Evaluator (line 5) | class Evaluator
class Set (line 10) | class Set
method new (line 15) | def self.new(paths)
method initialize (line 19) | def initialize(evaluators)
method call (line 23) | def call(input)
class Key (line 29) | class Key < Evaluator
method call (line 30) | def call(input)
class Attr (line 36) | class Attr < Evaluator
method call (line 37) | def call(input)
method initialize (line 43) | def initialize(path)
FILE: lib/dry/logic/operations/abstract.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Abstract (line 6) | class Abstract
method initialize (line 15) | def initialize(*rules, **options)
method id (line 20) | def id
method curry (line 24) | def curry(*args)
method new (line 28) | def new(rules, **new_options)
method with (line 32) | def with(new_options)
method to_ast (line 36) | def to_ast
FILE: lib/dry/logic/operations/and.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class And (line 6) | class And < Binary
method initialize (line 9) | def initialize(*, **)
method type (line 14) | def type
method call (line 19) | def call(input)
method [] (line 38) | def [](input)
FILE: lib/dry/logic/operations/attr.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Attr (line 6) | class Attr < Key
method evaluator (line 7) | def self.evaluator(name)
method type (line 11) | def type
FILE: lib/dry/logic/operations/binary.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Binary (line 6) | class Binary < Abstract
method initialize (line 11) | def initialize(left, right, **options)
method ast (line 17) | def ast(input = Undefined)
method to_s (line 21) | def to_s
FILE: lib/dry/logic/operations/check.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Check (line 6) | class Check < Unary
method new (line 9) | def self.new(rule, **options)
method initialize (line 20) | def initialize(*rules, **options)
method type (line 25) | def type
method call (line 29) | def call(input)
method [] (line 40) | def [](input)
method ast (line 44) | def ast(input = Undefined)
FILE: lib/dry/logic/operations/each.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Each (line 6) | class Each < Unary
method type (line 7) | def type
method call (line 11) | def call(input)
method [] (line 25) | def [](arr)
FILE: lib/dry/logic/operations/implication.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Implication (line 6) | class Implication < Binary
method type (line 7) | def type
method operator (line 11) | def operator
method call (line 15) | def call(input)
method [] (line 26) | def [](input)
FILE: lib/dry/logic/operations/key.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Key (line 6) | class Key < Unary
method new (line 11) | def self.new(rules, **options)
method evaluator (line 21) | def self.evaluator(name)
method initialize (line 25) | def initialize(*rules, **options)
method type (line 31) | def type
method call (line 35) | def call(hash)
method [] (line 46) | def [](hash)
method ast (line 50) | def ast(input = Undefined)
method to_s (line 58) | def to_s
FILE: lib/dry/logic/operations/negation.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Negation (line 6) | class Negation < Unary
method type (line 7) | def type
method call (line 11) | def call(input)
method [] (line 15) | def [](input)
FILE: lib/dry/logic/operations/or.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Or (line 6) | class Or < Binary
method type (line 7) | def type
method call (line 12) | def call(input)
method [] (line 28) | def [](input)
FILE: lib/dry/logic/operations/set.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Set (line 6) | class Set < Abstract
method type (line 7) | def type
method call (line 11) | def call(input)
method [] (line 20) | def [](input)
method ast (line 24) | def ast(input = Undefined)
method to_s (line 28) | def to_s
FILE: lib/dry/logic/operations/unary.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Unary (line 6) | class Unary < Abstract
method initialize (line 9) | def initialize(*rules, **options)
method ast (line 14) | def ast(input = Undefined)
method to_s (line 18) | def to_s
FILE: lib/dry/logic/operations/xor.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operations (line 5) | module Operations
class Xor (line 6) | class Xor < Binary
method type (line 7) | def type
method call (line 12) | def call(input)
method [] (line 16) | def [](input)
method ast (line 20) | def ast(input = Undefined)
FILE: lib/dry/logic/operators.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
type Operators (line 5) | module Operators
function and (line 6) | def and(other)
function or (line 11) | def or(other)
function xor (line 16) | def xor(other)
function then (line 21) | def then(other)
FILE: lib/dry/logic/predicates.rb
type Dry (line 10) | module Dry
type Logic (line 11) | module Logic
type Predicates (line 12) | module Predicates
type Methods (line 15) | module Methods
function uuid_format (line 16) | def self.uuid_format(version)
function [] (line 38) | def [](name)
function type? (line 42) | def type?(type, input) = input.is_a?(type)
function nil? (line 44) | def nil?(input) = input.nil?
function key? (line 47) | def key?(name, input) = input.key?(name)
function attr? (line 49) | def attr?(name, input) = input.respond_to?(name)
function empty? (line 51) | def empty?(input)
function filled? (line 60) | def filled?(input) = !empty?(input)
function bool? (line 62) | def bool?(input) = input.equal?(true) || input.equal?(false)
function date? (line 64) | def date?(input) = input.is_a?(::Date)
function date_time? (line 66) | def date_time?(input) = input.is_a?(::DateTime)
function time? (line 68) | def time?(input) = input.is_a?(::Time)
function number? (line 70) | def number?(input)
function int? (line 76) | def int?(input) = input.is_a?(::Integer)
function float? (line 78) | def float?(input) = input.is_a?(::Float)
function decimal? (line 80) | def decimal?(input) = input.is_a?(::BigDecimal)
function str? (line 82) | def str?(input) = input.is_a?(::String)
function hash? (line 84) | def hash?(input) = input.is_a?(::Hash)
function array? (line 86) | def array?(input) = input.is_a?(::Array)
function odd? (line 88) | def odd?(input) = input.odd?
function even? (line 90) | def even?(input) = input.even?
function lt? (line 92) | def lt?(num, input) = input < num
function gt? (line 94) | def gt?(num, input) = input > num
function lteq? (line 96) | def lteq?(num, input) = !gt?(num, input)
function gteq? (line 98) | def gteq?(num, input) = !lt?(num, input)
function size? (line 100) | def size?(size, input)
function min_size? (line 109) | def min_size?(num, input) = input.size >= num
function max_size? (line 111) | def max_size?(num, input) = input.size <= num
function bytesize? (line 113) | def bytesize?(size, input)
function min_bytesize? (line 122) | def min_bytesize?(num, input) = input.bytesize >= num
function max_bytesize? (line 124) | def max_bytesize?(num, input) = input.bytesize <= num
function inclusion? (line 126) | def inclusion?(list, input)
function exclusion? (line 131) | def exclusion?(list, input)
function included_in? (line 136) | def included_in?(list, input) = list.include?(input)
function excluded_from? (line 138) | def excluded_from?(list, input) = !list.include?(input)
function includes? (line 140) | def includes?(value, input)
function excludes? (line 150) | def excludes?(value, input) = !includes?(value, input)
function eql? (line 153) | def eql?(left, right = Undefined)
function is? (line 159) | def is?(left, right) = left.equal?(right)
function not_eql? (line 161) | def not_eql?(left, right) = !left.eql?(right)
function true? (line 163) | def true?(value) = value.equal?(true)
function false? (line 165) | def false?(value) = value.equal?(false)
function format? (line 167) | def format?(regex, input) = !input.nil? && regex.match?(input)
function case? (line 169) | def case?(pattern, input) = pattern === input
function uuid_v1? (line 171) | def uuid_v1?(input) = format?(UUIDv1, input)
function uuid_v2? (line 173) | def uuid_v2?(input) = format?(UUIDv2, input)
function uuid_v3? (line 175) | def uuid_v3?(input) = format?(UUIDv3, input)
function uuid_v4? (line 177) | def uuid_v4?(input) = format?(UUIDv4, input)
function uuid_v5? (line 179) | def uuid_v5?(input) = format?(UUIDv5, input)
function uuid_v6? (line 181) | def uuid_v6?(input) = format?(UUIDv6, input)
function uuid_v7? (line 183) | def uuid_v7?(input) = format?(UUIDv7, input)
function uuid_v8? (line 185) | def uuid_v8?(input) = format?(UUIDv8, input)
function uri? (line 188) | def uri?(schemes, input)
function uri? (line 193) | def uri?(schemes, input)
function uri_rfc3986? (line 199) | def uri_rfc3986?(input) = format?(::URI::RFC3986_Parser::RFC3986...
function respond_to? (line 202) | def respond_to?(method, input = Undefined)
function predicate (line 208) | def predicate(name, &)
function deprecated (line 212) | def deprecated(name, in_favor_of)
function included (line 225) | def self.included(other)
FILE: lib/dry/logic/result.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
class Result (line 5) | class Result
method success? (line 7) | def success?
method failure? (line 11) | def failure?
method initialize (line 22) | def initialize(success, id = nil, &block)
method success? (line 28) | def success?
method failure? (line 32) | def failure?
method type (line 36) | def type
method ast (line 40) | def ast(input = Undefined)
method to_ast (line 44) | def to_ast
method to_s (line 52) | def to_s
method visit (line 58) | def visit(ast)
method visit_predicate (line 62) | def visit_predicate(node)
method visit_and (line 72) | def visit_and(node)
method visit_or (line 77) | def visit_or(node)
method visit_xor (line 82) | def visit_xor(node)
method visit_not (line 87) | def visit_not(node)
method visit_hint (line 91) | def visit_hint(node)
FILE: lib/dry/logic/rule.rb
type Dry (line 5) | module Dry
type Logic (line 6) | module Logic
function Rule (line 7) | def self.Rule(*args, **options, &block)
method interfaces (line 28) | def self.interfaces
method specialize (line 32) | def self.specialize(arity, curried, base = Rule)
method build (line 41) | def self.build(predicate, args: EMPTY_ARRAY, arity: predicate.arit...
method initialize (line 45) | def initialize(predicate, options = EMPTY_HASH)
method type (line 52) | def type
method id (line 56) | def id
method curry (line 60) | def curry(*new_args)
method bind (line 64) | def bind(object)
method eval_args (line 75) | def eval_args(object)
method with (line 79) | def with(new_opts)
method parameters (line 83) | def parameters
method ast (line 87) | def ast(input = Undefined)
method args_with_names (line 93) | def args_with_names(*input)
class Rule (line 15) | class Rule
method interfaces (line 28) | def self.interfaces
method specialize (line 32) | def self.specialize(arity, curried, base = Rule)
method build (line 41) | def self.build(predicate, args: EMPTY_ARRAY, arity: predicate.arit...
method initialize (line 45) | def initialize(predicate, options = EMPTY_HASH)
method type (line 52) | def type
method id (line 56) | def id
method curry (line 60) | def curry(*new_args)
method bind (line 64) | def bind(object)
method eval_args (line 75) | def eval_args(object)
method with (line 79) | def with(new_opts)
method parameters (line 83) | def parameters
method ast (line 87) | def ast(input = Undefined)
method args_with_names (line 93) | def args_with_names(*input)
FILE: lib/dry/logic/rule/interface.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
class Rule (line 5) | class Rule
class Interface (line 6) | class Interface < ::Module
method initialize (line 13) | def initialize(arity, curried)
method constant? (line 32) | def constant? = arity.zero?
method variable_arity? (line 34) | def variable_arity? = arity.negative?
method curried? (line 36) | def curried? = !curried.zero?
method unapplied (line 38) | def unapplied
method name (line 52) | def name
method define_constructor (line 74) | def define_constructor
method define_constant_application (line 91) | def define_constant_application
method define_application (line 107) | def define_application
method curried_args (line 127) | def curried_args
method unapplied_args (line 131) | def unapplied_args
FILE: lib/dry/logic/rule/predicate.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
class Rule (line 5) | class Rule
class Predicate (line 6) | class Predicate < Rule
method specialize (line 7) | def self.specialize(arity, curried, base = Predicate)
method type (line 11) | def type
method name (line 15) | def name
method to_s (line 19) | def to_s
method ast (line 27) | def ast(input = Undefined)
FILE: lib/dry/logic/rule_compiler.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
class RuleCompiler (line 5) | class RuleCompiler
method initialize (line 8) | def initialize(predicates)
method call (line 12) | def call(ast)
method visit (line 16) | def visit(node)
method visit_check (line 21) | def visit_check(node)
method visit_not (line 26) | def visit_not(node)
method visit_key (line 30) | def visit_key(node)
method visit_attr (line 35) | def visit_attr(node)
method visit_set (line 40) | def visit_set(node)
method visit_each (line 44) | def visit_each(node)
method visit_predicate (line 48) | def visit_predicate(node)
method visit_and (line 60) | def visit_and(node)
method visit_or (line 65) | def visit_or(node)
method visit_xor (line 70) | def visit_xor(node)
method visit_implication (line 75) | def visit_implication(node)
FILE: lib/dry/logic/version.rb
type Dry (line 3) | module Dry
type Logic (line 4) | module Logic
FILE: spec/spec_helper.rb
function undefined (line 21) | def undefined
FILE: spec/support/mutant.rb
type Mutant (line 3) | module Mutant
class Selector (line 4) | class Selector
class Expression (line 5) | class Expression < self
method call (line 6) | def call(_subject)
FILE: spec/unit/rule/predicate_spec.rb
function test? (line 44) | def self.test?
FILE: spec/unit/rule_spec.rb
function respond_to_missing? (line 13) | def respond_to_missing?(m, *)
function method_missing (line 17) | def method_missing(m, *)
function to_proc (line 25) | def to_proc
function arity (line 29) | def arity
function parameters (line 33) | def parameters
function test? (line 84) | def test?
function expected (line 104) | def expected
function num (line 144) | def num
Condensed preview — 133 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (195K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 15,
"preview": "github: hanami\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug-report.md",
"chars": 442,
"preview": "---\nname: \"\\U0001F41B Bug report\"\nabout: See CONTRIBUTING.md for more information\ntitle: ''\nlabels: bug, help wanted\nass"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 160,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Community support\n url: https://discourse.hanamirb.org\n about"
},
{
"path": ".github/SUPPORT.md",
"chars": 306,
"preview": "## Support\n\nIf you need help with any of the Hanami, Dry or Rom libraries, feel free to ask questions on our [discussion"
},
{
"path": ".github/workflows/ci-lint.yml",
"chars": 672,
"preview": "name: CI lint\n\non:\n push:\n branches: [\"main\", \"release-*\", \"ci/*\"]\n tags: [\"v*\"]\n paths:\n - \".github/**\"\n"
},
{
"path": ".github/workflows/ci.yml",
"chars": 3897,
"preview": "# This file is synced from hanakai-rb/repo-sync\n\nname: CI\nrun-name: ${{ github.ref_type == 'tag' && format('Release {0}'"
},
{
"path": ".github/workflows/pr-comments.yml",
"chars": 912,
"preview": "# This file is synced from hanakai-rb/repo-sync\n\n# Downloads comment artifacts from completed workflows and posts them t"
},
{
"path": ".github/workflows/repo-sync-preview.yml",
"chars": 1471,
"preview": "name: Repo-sync preview\n\non: # zizmor: ignore[dangerous-triggers]\n workflow_run:\n workflows: [\"CI\", \"RuboCop\", \"CI l"
},
{
"path": ".github/workflows/rubocop.yml",
"chars": 742,
"preview": "# frozen_string_literal: true\n\n# This file is synced from hanakai-rb/repo-sync\n\nname: RuboCop\n\non:\n push:\n branches:"
},
{
"path": ".gitignore",
"chars": 84,
"preview": ".DS_Store\ncoverage\n/.bundle\nvendor/bundle\ntmp/\npkg/\n.idea/\nGemfile.lock\n/.rubocop-*\n"
},
{
"path": ".rspec",
"chars": 56,
"preview": "--color\n--require spec_helper\n--order random\n--warnings\n"
},
{
"path": ".rubocop.yml",
"chars": 227,
"preview": "# This file is synced from hanakai-rb/repo-sync\n\ninherit_from:\n - https://raw.githubusercontent.com/hanakai-rb/repo-syn"
},
{
"path": "CHANGELOG.md",
"chars": 9805,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 8490,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe pledge to make our community welcoming, safe, and equitable fo"
},
{
"path": "CONTRIBUTING.md",
"chars": 1510,
"preview": "# Issue guidelines\n\n## Reporting bugs\n\nIf you’ve found a bug, please report an issue and describe the expected behavior "
},
{
"path": "Gemfile",
"chars": 194,
"preview": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\n\neval_gemfile \"Gemfile.devtools\"\n\ngemspec\n\ngroup :tools do\n"
},
{
"path": "Gemfile.devtools",
"chars": 322,
"preview": "# frozen_string_literal: true\n\n# This file is synced from hanakai-rb/repo-sync\n\ngem \"rake\", \">= 12.3.3\"\n\ngroup :test do\n"
},
{
"path": "LICENSE",
"chars": 1084,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015-2026 Hanakai team\n\nPermission is hereby granted, free of charge, to any person"
},
{
"path": "README.md",
"chars": 781,
"preview": "<!--- This file is synced from hanakai-rb/repo-sync -->\n\n[actions]: https://github.com/dry-rb/dry-logic/actions\n[chat]: "
},
{
"path": "Rakefile",
"chars": 288,
"preview": "#!/usr/bin/env rake\n# frozen_string_literal: true\n\nrequire \"bundler/gem_tasks\"\n\n$LOAD_PATH.unshift(File.join(File.dirnam"
},
{
"path": "benchmarks/builder.rb",
"chars": 733,
"preview": "# frozen_string_literal: true\n\nrequire \"benchmark/ips\"\nrequire \"dry/logic\"\nrequire \"dry/logic/builder\"\n\ndef regular\n us"
},
{
"path": "benchmarks/rule_application.rb",
"chars": 996,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"setup\"\n\nunless Dry::Logic::Rule.respond_to?(:build)\n Dry::Logic::Rule."
},
{
"path": "benchmarks/setup.rb",
"chars": 211,
"preview": "# frozen_string_literal: true\n\nrequire \"benchmark/ips\"\nrequire \"hotch\"\nENV[\"HOTCH_VIEWER\"] ||= \"open\"\n\nrequire \"dry/logi"
},
{
"path": "bin/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "bin/console",
"chars": 240,
"preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire \"bundler/setup\"\nrequire \"dry/logic\"\nrequire \"dry/logic/predic"
},
{
"path": "dry-logic.gemspec",
"chars": 1634,
"preview": "# frozen_string_literal: true\n\n# This file is synced from hanakai-rb/repo-sync. To update it, edit repo-sync.yml.\n\nlib ="
},
{
"path": "examples/basic.rb",
"chars": 507,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic\"\nrequire \"dry/logic/predicates\"\n\n# rubocop:disable Style/MixinUsage\nin"
},
{
"path": "lib/dry/logic/appliable.rb",
"chars": 497,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Appliable\n def id\n options[:id]\n en"
},
{
"path": "lib/dry/logic/builder.rb",
"chars": 2134,
"preview": "# frozen_string_literal: true\n\nrequire \"singleton\"\nrequire \"delegate\"\n\nmodule Dry\n module Logic\n module Builder\n "
},
{
"path": "lib/dry/logic/evaluator.rb",
"chars": 957,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n class Evaluator\n include Dry::Equalizer(:path)\n\n "
},
{
"path": "lib/dry/logic/operations/abstract.rb",
"chars": 804,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Abstract\n include Core"
},
{
"path": "lib/dry/logic/operations/and.rb",
"chars": 941,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class And < Binary\n attr_rea"
},
{
"path": "lib/dry/logic/operations/attr.rb",
"chars": 257,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Attr < Key\n def self.e"
},
{
"path": "lib/dry/logic/operations/binary.rb",
"chars": 500,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Binary < Abstract\n att"
},
{
"path": "lib/dry/logic/operations/check.rb",
"chars": 1067,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Check < Unary\n attr_re"
},
{
"path": "lib/dry/logic/operations/each.rb",
"chars": 650,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Each < Unary\n def type"
},
{
"path": "lib/dry/logic/operations/implication.rb",
"chars": 654,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Implication < Binary\n "
},
{
"path": "lib/dry/logic/operations/key.rb",
"chars": 1382,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Key < Unary\n attr_read"
},
{
"path": "lib/dry/logic/operations/negation.rb",
"chars": 338,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Negation < Unary\n def "
},
{
"path": "lib/dry/logic/operations/or.rb",
"chars": 682,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Or < Binary\n def type\n"
},
{
"path": "lib/dry/logic/operations/set.rb",
"chars": 696,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Set < Abstract\n def ty"
},
{
"path": "lib/dry/logic/operations/unary.rb",
"chars": 404,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Unary < Abstract\n attr"
},
{
"path": "lib/dry/logic/operations/xor.rb",
"chars": 479,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operations\n class Xor < Binary\n def type"
},
{
"path": "lib/dry/logic/operators.rb",
"chars": 506,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n module Operators\n def and(other)\n Operations::A"
},
{
"path": "lib/dry/logic/predicates.rb",
"chars": 6009,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/core/constants\"\n\nrequire \"bigdecimal\"\nrequire \"bigdecimal/util\"\nrequire \"dat"
},
{
"path": "lib/dry/logic/result.rb",
"chars": 1586,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n class Result\n SUCCESS = ::Class.new {\n def succ"
},
{
"path": "lib/dry/logic/rule/interface.rb",
"chars": 3921,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n class Rule\n class Interface < ::Module\n SPLAT ="
},
{
"path": "lib/dry/logic/rule/predicate.rb",
"chars": 620,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n class Rule\n class Predicate < Rule\n def self.sp"
},
{
"path": "lib/dry/logic/rule.rb",
"chars": 2345,
"preview": "# frozen_string_literal: true\n\nrequire \"concurrent/map\"\n\nmodule Dry\n module Logic\n def self.Rule(*args, **options, &"
},
{
"path": "lib/dry/logic/rule_compiler.rb",
"chars": 1720,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n class RuleCompiler\n attr_reader :predicates\n\n def"
},
{
"path": "lib/dry/logic/version.rb",
"chars": 89,
"preview": "# frozen_string_literal: true\n\nmodule Dry\n module Logic\n VERSION = \"1.6.0\"\n end\nend\n"
},
{
"path": "lib/dry/logic.rb",
"chars": 547,
"preview": "# frozen_string_literal: true\n\nrequire \"zeitwerk\"\nrequire \"dry/core\"\n\nmodule Dry\n module Logic\n include Dry::Core::C"
},
{
"path": "lib/dry-logic.rb",
"chars": 51,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic\"\n"
},
{
"path": "repo-sync.yml",
"chars": 427,
"preview": "name:\n gem: dry-logic\n constant: Dry::Logic\ngithub_org: dry-rb\ngemspec:\n authors: [\"Hanakai team\"]\n email: [\"info@ha"
},
{
"path": "spec/integration/builder/operation_spec.rb",
"chars": 8243,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../../shared/operation\"\n\nRSpec.describe \"operations\" do\n describe \"nes"
},
{
"path": "spec/integration/builder/predicate_spec.rb",
"chars": 23132,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"../../shared/predicate\"\n\nRSpec.describe \"predicates\" do\n let(:input) {"
},
{
"path": "spec/integration/result_spec.rb",
"chars": 1964,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Result do\n include_context \"predicates\"\n\n describe \"#to_s\" d"
},
{
"path": "spec/integration/rule_spec.rb",
"chars": 1673,
"preview": "# frozen_string_literal: true\n\nrequire \"dry-logic\"\n\nRSpec.describe \"Rules\" do\n specify \"defining an anonymous rule with"
},
{
"path": "spec/shared/built_rule.rb",
"chars": 1092,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/builder\"\n\nRSpec.shared_examples \"built rule\" do |rule_type, *args|\n s"
},
{
"path": "spec/shared/operation.rb",
"chars": 310,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/builder\"\n\nRSpec.shared_examples \"operation\" do\n before { extend Dry::"
},
{
"path": "spec/shared/predicate.rb",
"chars": 342,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/builder\"\n\n# TODO: Merge with {operation}?\nRSpec.shared_examples \"predi"
},
{
"path": "spec/shared/predicates.rb",
"chars": 1391,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.shared_examples \"predicates\" do\n let(:nil?) { Dry:"
},
{
"path": "spec/shared/rule.rb",
"chars": 1849,
"preview": "# frozen_string_literal: true\n\nRSpec.shared_examples_for Dry::Logic::Rule do\n let(:arity) { 2 }\n let(:predicate) { dou"
},
{
"path": "spec/spec_helper.rb",
"chars": 483,
"preview": "# frozen_string_literal: true\n\nrequire_relative \"support/coverage\"\nrequire_relative \"support/warnings\"\n\nbegin\n require "
},
{
"path": "spec/support/coverage.rb",
"chars": 313,
"preview": "# frozen_string_literal: true\n\n# This file is synced from hanakai-rb/repo-sync\n\nif ENV[\"COVERAGE\"] == \"true\"\n require \""
},
{
"path": "spec/support/mutant.rb",
"chars": 173,
"preview": "# frozen_string_literal: true\n\nmodule Mutant\n class Selector\n class Expression < self\n def call(_subject)\n "
},
{
"path": "spec/support/rspec.rb",
"chars": 779,
"preview": "# frozen_string_literal: true\n\n# This file is synced from hanakai-rb/repo-sync\n\nRSpec.configure do |config|\n # When no "
},
{
"path": "spec/support/warnings.rb",
"chars": 309,
"preview": "# frozen_string_literal: true\n\n# This file is synced from hanakai-rb/repo-sync\n\nrequire \"warning\"\n\n# Ignore warnings for"
},
{
"path": "spec/unit/builder_spec.rb",
"chars": 760,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Builder do\n describe \"undefined methods\" do\n it \"raises Na"
},
{
"path": "spec/unit/operations/and_spec.rb",
"chars": 2016,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::And do\n subject(:operation) { described_class.new"
},
{
"path": "spec/unit/operations/attr_spec.rb",
"chars": 940,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Attr do\n subject(:operation) do\n Dry::Logic::O"
},
{
"path": "spec/unit/operations/check_spec.rb",
"chars": 2147,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Check do\n include_context \"predicates\"\n\n describ"
},
{
"path": "spec/unit/operations/each_spec.rb",
"chars": 1409,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Each do\n subject(:operation) { described_class.ne"
},
{
"path": "spec/unit/operations/implication_spec.rb",
"chars": 880,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Implication do\n subject(:operation) { described_c"
},
{
"path": "spec/unit/operations/key_spec.rb",
"chars": 3819,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Key do\n subject(:operation) { described_class.new"
},
{
"path": "spec/unit/operations/negation_spec.rb",
"chars": 1276,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Negation do\n subject(:operation) { described_clas"
},
{
"path": "spec/unit/operations/or_spec.rb",
"chars": 2083,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Or do\n subject(:operation) { described_class.new("
},
{
"path": "spec/unit/operations/set_spec.rb",
"chars": 1205,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Set do\n subject(:operation) { described_class.new"
},
{
"path": "spec/unit/operations/xor_spec.rb",
"chars": 1780,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Operations::Xor do\n subject(:operation) { described_class.new"
},
{
"path": "spec/unit/predicates/array_spec.rb",
"chars": 798,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#arr"
},
{
"path": "spec/unit/predicates/attr_spec.rb",
"chars": 703,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#att"
},
{
"path": "spec/unit/predicates/bool_spec.rb",
"chars": 666,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#boo"
},
{
"path": "spec/unit/predicates/bytesize_spec.rb",
"chars": 1001,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#byt"
},
{
"path": "spec/unit/predicates/case_spec.rb",
"chars": 712,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#cas"
},
{
"path": "spec/unit/predicates/date_spec.rb",
"chars": 608,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#dat"
},
{
"path": "spec/unit/predicates/date_time_spec.rb",
"chars": 624,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#dat"
},
{
"path": "spec/unit/predicates/decimal_spec.rb",
"chars": 632,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#dec"
},
{
"path": "spec/unit/predicates/empty_spec.rb",
"chars": 727,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#emp"
},
{
"path": "spec/unit/predicates/eql_spec.rb",
"chars": 458,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates, \"#eql?\" do\n let(:"
},
{
"path": "spec/unit/predicates/even_spec.rb",
"chars": 581,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#eve"
},
{
"path": "spec/unit/predicates/excluded_from_spec.rb",
"chars": 800,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#exc"
},
{
"path": "spec/unit/predicates/excludes_spec.rb",
"chars": 1254,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#exc"
},
{
"path": "spec/unit/predicates/false_spec.rb",
"chars": 673,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#fal"
},
{
"path": "spec/unit/predicates/filled_spec.rb",
"chars": 731,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#fil"
},
{
"path": "spec/unit/predicates/float_spec.rb",
"chars": 604,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#flo"
},
{
"path": "spec/unit/predicates/format_spec.rb",
"chars": 618,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates, \"#format?\" do\n le"
},
{
"path": "spec/unit/predicates/gt_spec.rb",
"chars": 777,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#gt?"
},
{
"path": "spec/unit/predicates/gteq_spec.rb",
"chars": 781,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#gte"
},
{
"path": "spec/unit/predicates/hash_spec.rb",
"chars": 708,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#has"
},
{
"path": "spec/unit/predicates/included_in_spec.rb",
"chars": 796,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#inc"
},
{
"path": "spec/unit/predicates/includes_spec.rb",
"chars": 532,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates, \"#is?\" do\n let(:p"
},
{
"path": "spec/unit/predicates/int_spec.rb",
"chars": 637,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#int"
},
{
"path": "spec/unit/predicates/key_spec.rb",
"chars": 621,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#key"
},
{
"path": "spec/unit/predicates/lt_spec.rb",
"chars": 777,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#lt?"
},
{
"path": "spec/unit/predicates/lteq_spec.rb",
"chars": 781,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#lte"
},
{
"path": "spec/unit/predicates/max_bytesize_spec.rb",
"chars": 740,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#max"
},
{
"path": "spec/unit/predicates/max_size_spec.rb",
"chars": 984,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#max"
},
{
"path": "spec/unit/predicates/min_bytesize_spec.rb",
"chars": 737,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#min"
},
{
"path": "spec/unit/predicates/min_size_spec.rb",
"chars": 984,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#min"
},
{
"path": "spec/unit/predicates/none_spec.rb",
"chars": 590,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#nil"
},
{
"path": "spec/unit/predicates/not_eql_spec.rb",
"chars": 466,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates, \"#not_eql?\" do\n l"
},
{
"path": "spec/unit/predicates/number_spec.rb",
"chars": 723,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#num"
},
{
"path": "spec/unit/predicates/odd_spec.rb",
"chars": 579,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#odd"
},
{
"path": "spec/unit/predicates/respond_to_spec.rb",
"chars": 616,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#res"
},
{
"path": "spec/unit/predicates/size_spec.rb",
"chars": 1204,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#siz"
},
{
"path": "spec/unit/predicates/str_spec.rb",
"chars": 607,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#str"
},
{
"path": "spec/unit/predicates/time_spec.rb",
"chars": 606,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#tim"
},
{
"path": "spec/unit/predicates/true_spec.rb",
"chars": 669,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#tru"
},
{
"path": "spec/unit/predicates/type_spec.rb",
"chars": 812,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#typ"
},
{
"path": "spec/unit/predicates/uri_spec.rb",
"chars": 1703,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uri"
},
{
"path": "spec/unit/predicates/uuid_v1_spec.rb",
"chars": 908,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uui"
},
{
"path": "spec/unit/predicates/uuid_v2_spec.rb",
"chars": 908,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uui"
},
{
"path": "spec/unit/predicates/uuid_v3_spec.rb",
"chars": 908,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uui"
},
{
"path": "spec/unit/predicates/uuid_v4_spec.rb",
"chars": 908,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uui"
},
{
"path": "spec/unit/predicates/uuid_v5_spec.rb",
"chars": 908,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uui"
},
{
"path": "spec/unit/predicates/uuid_v6_spec.rb",
"chars": 908,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uui"
},
{
"path": "spec/unit/predicates/uuid_v7_spec.rb",
"chars": 908,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uui"
},
{
"path": "spec/unit/predicates/uuid_v8_spec.rb",
"chars": 908,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n describe \"#uui"
},
{
"path": "spec/unit/predicates_spec.rb",
"chars": 990,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/predicates\"\n\nRSpec.describe Dry::Logic::Predicates do\n it \"can be inc"
},
{
"path": "spec/unit/rule/predicate_spec.rb",
"chars": 1407,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Rule::Predicate do\n subject(:rule) { described_class.build(pr"
},
{
"path": "spec/unit/rule_compiler_spec.rb",
"chars": 3188,
"preview": "# frozen_string_literal: true\n\nrequire \"dry/logic/rule_compiler\"\n\nRSpec.describe Dry::Logic::RuleCompiler, \"#call\" do\n "
},
{
"path": "spec/unit/rule_spec.rb",
"chars": 6843,
"preview": "# frozen_string_literal: true\n\nRSpec.describe Dry::Logic::Rule do\n subject(:rule) { described_class.build(predicate, **"
},
{
"path": "zizmor.yml",
"chars": 82,
"preview": "rules:\n unpinned-uses:\n config:\n policies:\n hanakai-rb/*: ref-pin\n"
}
]
About this extraction
This page contains the full source code of the dry-rb/dry-logic GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 133 files (172.7 KB), approximately 52.4k tokens, and a symbol index with 305 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.