Full Code of dry-rb/dry-logic for AI

main 3326aefba07b cached
133 files
172.7 KB
52.4k tokens
305 symbols
1 requests
Download .txt
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 [![Gem Version](https://badge.fury.io/rb/dry-logic.svg)][rubygem] [![CI Status](https://github.com/dry-rb/dry-logic/workflows/CI/badge.svg)][actions]

[![Forum](https://img.shields.io/badge/Forum-dc360f?logo=discourse&logoColor=white)][forum]
[![Chat](https://img.shields.io/badge/Chat-717cf8?logo=discord&logoColor=white)][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
Download .txt
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
Download .txt
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.

Copied to clipboard!