Full Code of varvet/pundit for AI

main 06318683c960 cached
92 files
132.4 KB
37.0k tokens
218 symbols
1 requests
Download .txt
Repository: varvet/pundit
Branch: main
Commit: 06318683c960
Files: 92
Total size: 132.4 KB

Directory structure:
gitextract_wfsck2t6/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   └── gem_release_template.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── main.yml
│       └── push_gem.yml
├── .gitignore
├── .rubocop_ignore_git.yml
├── .standard.yml
├── .yardopts
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── SECURITY.md
├── bin/
│   ├── console
│   └── setup
├── config/
│   └── rubocop-rspec.yml
├── lib/
│   ├── generators/
│   │   ├── pundit/
│   │   │   ├── install/
│   │   │   │   ├── USAGE
│   │   │   │   ├── install_generator.rb
│   │   │   │   └── templates/
│   │   │   │       └── application_policy.rb.tt
│   │   │   └── policy/
│   │   │       ├── USAGE
│   │   │       ├── policy_generator.rb
│   │   │       └── templates/
│   │   │           └── policy.rb.tt
│   │   ├── rspec/
│   │   │   ├── policy_generator.rb
│   │   │   └── templates/
│   │   │       └── policy_spec.rb.tt
│   │   └── test_unit/
│   │       ├── policy_generator.rb
│   │       └── templates/
│   │           └── policy_test.rb.tt
│   ├── pundit/
│   │   ├── authorization.rb
│   │   ├── cache_store/
│   │   │   ├── legacy_store.rb
│   │   │   └── null_store.rb
│   │   ├── cache_store.rb
│   │   ├── context.rb
│   │   ├── error.rb
│   │   ├── helper.rb
│   │   ├── policy_finder.rb
│   │   ├── railtie.rb
│   │   ├── rspec.rb
│   │   └── version.rb
│   └── pundit.rb
├── pundit.gemspec
└── spec/
    ├── authorization_spec.rb
    ├── generators_spec.rb
    ├── policies/
    │   └── post_policy_spec.rb
    ├── policy_finder_spec.rb
    ├── pundit/
    │   └── helper_spec.rb
    ├── pundit_spec.rb
    ├── rspec_dsl_spec.rb
    ├── spec_helper.rb
    └── support/
        ├── lib/
        │   ├── controller.rb
        │   ├── custom_cache.rb
        │   └── instance_tracking.rb
        ├── models/
        │   ├── article.rb
        │   ├── article_tag.rb
        │   ├── artificial_blog.rb
        │   ├── blog.rb
        │   ├── comment.rb
        │   ├── comment_four_five_six.rb
        │   ├── comment_scope.rb
        │   ├── comments_relation.rb
        │   ├── customer/
        │   │   └── post.rb
        │   ├── default_scope_contains_error.rb
        │   ├── dummy_current_user.rb
        │   ├── foo.rb
        │   ├── post.rb
        │   ├── post_four_five_six.rb
        │   ├── project_one_two_three/
        │   │   ├── avatar_four_five_six.rb
        │   │   └── tag_four_five_six.rb
        │   └── wiki.rb
        └── policies/
            ├── article_tag_other_name_policy.rb
            ├── base_policy.rb
            ├── blog_policy.rb
            ├── comment_policy.rb
            ├── criteria_policy.rb
            ├── default_scope_contains_error_policy.rb
            ├── dummy_current_user_policy.rb
            ├── nil_class_policy.rb
            ├── post_policy.rb
            ├── project/
            │   ├── admin/
            │   │   └── comment_policy.rb
            │   ├── comment_policy.rb
            │   ├── criteria_policy.rb
            │   └── post_policy.rb
            ├── project_one_two_three/
            │   ├── avatar_four_five_six_policy.rb
            │   ├── comment_four_five_six_policy.rb
            │   ├── criteria_four_five_six_policy.rb
            │   ├── post_four_five_six_policy.rb
            │   └── tag_four_five_six_policy.rb
            ├── publication_policy.rb
            └── wiki_policy.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a bug report to report a problem
title: ''
labels: problem
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps or runnable code to reproduce the problem.

**Expected behavior**
A clear and concise description of what you expected to happen.

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea
title: ''
labels: ['feature request']
assignees: ''
---

**Please consider**
- Could this feature break backwards-compatibility?
- Could this feature benefit the many who use Pundit?
- Could this feature be useful in _most_ projects that use Pundit?
- Would this feature require Rails?
- Am I open to creating a Pull Request with the necessary changes?

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of how you'd like to approach solving the problem.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context. Ex. if you've solved this problem in your own projects already, how that worked, and why the feature should be moved and maintained in Pundit instead.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE/gem_release_template.md
================================================
## To do

- [ ] Make changes:
  - [ ] Bump `Pundit::VERSION` in `lib/pundit/version.rb`.
  - [ ] Update `CHANGELOG.md`.
- [ ] Open pull request 🚀 and merge it.
- [ ] Run [push gem](https://github.com/varvet/pundit/actions/workflows/push_gem.yml) GitHub Action.
- [ ] Make an announcement in [Pundit discussions](https://github.com/varvet/pundit/discussions/categories/announcements)

================================================
FILE: .github/pull_request_template.md
================================================
## To do

- [ ] I have read the [contributing guidelines](https://github.com/varvet/pundit/contribute).
- [ ] I have added relevant tests.
- [ ] I have adjusted relevant documentation.
- [ ] I have made sure the individual commits are meaningful.
- [ ] I have added relevant lines to the CHANGELOG.

PS: Thank you for contributing to Pundit ❤️


================================================
FILE: .github/workflows/main.yml
================================================
name: Main

on:
  push:
    branches: ["main"]
  pull_request:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  matrix-test:
    runs-on: ubuntu-latest
    continue-on-error: ${{ matrix.allow-failure || false }}
    strategy:
      fail-fast: false
      matrix:
        ruby-version:
          - "3.2"
          - "3.3"
          - "3.4"
          - "4.0"
          - "jruby-9.4"
          - "jruby"
        include: # HEAD-versions
          - ruby-version: "head"
            allow-failure: true
          - ruby-version: "jruby-head"
            allow-failure: true
          - ruby-version: "truffleruby-head"
            allow-failure: true

    steps:
      - uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          rubygems: latest
          ruby-version: ${{ matrix.ruby-version }}
          bundler-cache: true
      - name: Run tests
        run: bundle exec rspec

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          rubygems: latest
          ruby-version: "ruby"
          bundler-cache: true
      - name: Run tests
        run: bundle exec rspec
        env:
          COVERAGE: 1
      - name: Upload coverage results
        uses: actions/upload-artifact@v4
        with:
          include-hidden-files: true
          name: coverage-results
          path: coverage
          retention-days: 1

  linting:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          rubygems: default
          ruby-version: "ruby"
          bundler-cache: false
      - run: bundle install
      - name: Run standard
        run: bundle exec standardrb

  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          rubygems: default
          ruby-version: "ruby"
          bundler-cache: false
      - run: bundle install
      - run: rake yard

  required-checks:
    runs-on: ubuntu-latest
    if: ${{ always() }}
    needs:
      - test
      - matrix-test
      - docs
      - linting
    steps:
      - name: failure
        if: ${{ failure() || contains(needs.*.result, 'failure') }}
        run: exit 1
      - name: success
        run: exit 0


================================================
FILE: .github/workflows/push_gem.yml
================================================
name: Push Gem

on:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  push:
    if: github.repository == 'varvet/pundit'
    runs-on: ubuntu-latest

    permissions:
      contents: write
      id-token: write

    steps:
      # Set up
      - name: Harden Runner
        uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
        with:
          egress-policy: audit

      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
      - name: Set up Ruby
        uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0
        with:
          bundler-cache: true
          ruby-version: ruby

      # Release
      - uses: rubygems/release-gem@612653d273a73bdae1df8453e090060bb4db5f31 # v1+ unreleased


================================================
FILE: .gitignore
================================================
*.gem
*.rbc
.bundle
.config
.coverage
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp


================================================
FILE: .rubocop_ignore_git.yml
================================================
# This is here so we can keep YAML syntax highlight in the main file.
AllCops:
  Exclude:
    - "lib/generators/**/templates/**/*"
    <% `git status --ignored --porcelain`.lines.grep(/^!! /).each do |path| %>
    - <%= path.sub(/^!! /, '').sub(/\/$/, '/**/*') %>
    <% end %>


================================================
FILE: .standard.yml
================================================
parallel: true
ruby_version: 3.2

extend_config:
  - .rubocop_ignore_git.yml


================================================
FILE: .yardopts
================================================
--no-private --private --protected --hide-void-return --markup markdown --fail-on-warning


================================================
FILE: CHANGELOG.md
================================================
# Pundit

## Unreleased

- Add support for `params.expect` using `expected_parameters` and `expected_parameters_for`. [#855](https://github.com/varvet/pundit/pull/855)

### Fixed
- Update for rspec 4 breaking changes [#873](https://github.com/varvet/pundit/issues/873)

## 2.5.2 (2025-09-24)

### Fixed
- Added `config/rubocop-rspec.yml` back from accidentally being excluded [#866](https://github.com/varvet/pundit/issues/866)

## 2.5.1 (2025-09-12)

### Fixed
- Requiring only `pundit/rspec` no longer raises an error in Active Support [#857](https://github.com/varvet/pundit/issues/857)

## 2.5.0 (2025-03-03)

### Added

- Add `Pundit::Authorization#pundit_reset!` hook to reset the policy and policy scope cache. [#830](https://github.com/varvet/pundit/issues/830)
- Add links to gemspec. [#845](https://github.com/varvet/pundit/issues/845)
- Register policies directories for Rails 8 code statistics [#833](https://github.com/varvet/pundit/issues/833)
- Added an example for how to use pundit with Rails 8 authentication generator [#850](https://github.com/varvet/pundit/issues/850)

### Changed

- Deprecated `Pundit::SUFFIX`, moved it to `Pundit::PolicyFinder::SUFFIX` [#835](https://github.com/varvet/pundit/issues/835)
- Explicitly require less of `active_support` [#837](https://github.com/varvet/pundit/issues/837)
- Using `permit` matcher without a surrouding `permissions` block now raises a useful error. [#836](https://github.com/varvet/pundit/issues/836)

### Fixed

- Using a hash as custom cache in `Pundit.authorize` now works as documented. [#838](https://github.com/varvet/pundit/issues/838)

## 2.4.0 (2024-08-26)

### Changed

- Improve the `NotAuthorizedError` message to include the policy class.
  Furthermore, in the case where the record passed is a class instead of an instance, the class name is given. [#812](https://github.com/varvet/pundit/issues/812)

### Added

- Add customizable permit matcher description [#806](https://github.com/varvet/pundit/issues/806)
- Add support for filter_run_when_matching :focus with permissions helper. [#820](https://github.com/varvet/pundit/issues/820)

## 2.3.2 (2024-05-08)

- Refactor: First pass of Pundit::Context [#797](https://github.com/varvet/pundit/issues/797)

### Changed

- Update `ApplicationPolicy` generator to qualify the `Scope` class name [#792](https://github.com/varvet/pundit/issues/792)
- Policy generator uses `NoMethodError` to indicate `#resolve` is not implemented [#776](https://github.com/varvet/pundit/issues/776)

## Deprecated

- Dropped support for Ruby 3.0 [#796](https://github.com/varvet/pundit/issues/796)

## 2.3.1 (2023-07-17)

### Fixed

- Use `Kernel.warn` instead of `ActiveSupport::Deprecation.warn` for deprecations [#764](https://github.com/varvet/pundit/issues/764)
- Policy generator now works on Ruby 3.2 [#754](https://github.com/varvet/pundit/issues/754)

## 2.3.0 (2022-12-19)

### Added

- add support for rubocop-rspec syntax extensions [#745](https://github.com/varvet/pundit/issues/745)

## 2.2.0 (2022-02-11)

### Fixed

- Using `policy_class` and a namespaced record now passes only the record when instantiating the policy. (#697, #689, #694, #666)

### Changed

- Require users to explicitly define Scope#resolve in generated policies (#711, #722)

### Deprecated

- Deprecate `include Pundit` in favor of `include Pundit::Authorization` [#621](https://github.com/varvet/pundit/issues/621)

## 2.1.1 (2021-08-13)

Friday 13th-release!

Careful! The bugfix below [#626](https://github.com/varvet/pundit/issues/626) could break existing code. If you rely on the
return value for `authorize` and namespaced policies you might need to do some
changes.

### Fixed

- `.authorize` and `#authorize` return the instance, even for namespaced
  policies [#626](https://github.com/varvet/pundit/issues/626)

### Changed

- Generate application scope with `protected` attr_readers. [#616](https://github.com/varvet/pundit/issues/616)

### Removed

- Dropped support for Ruby end-of-life versions: 2.1 and 2.2. [#604](https://github.com/varvet/pundit/issues/604)
- Dropped support for Ruby end-of-life versions: 2.3 [#633](https://github.com/varvet/pundit/issues/633)
- Dropped support for Ruby end-of-life versions: 2.4, 2.5 and JRuby 9.1 [#676](https://github.com/varvet/pundit/issues/676)
- Dropped support for RSpec 2 [#615](https://github.com/varvet/pundit/issues/615)

## 2.1.0 (2019-08-14)

### Fixed

- Avoid name clashes with the Error class. [#590](https://github.com/varvet/pundit/issues/590)

### Changed

- Return a safer default NotAuthorizedError message. [#583](https://github.com/varvet/pundit/issues/583)

## 2.0.1 (2019-01-18)

### Breaking changes

None

### Other changes

- Improve exception handling for `#policy_scope` and `#policy_scope!`. [#550](https://github.com/varvet/pundit/issues/550)
- Add `:policy` metadata to RSpec template. [#566](https://github.com/varvet/pundit/issues/566)

## 2.0.0 (2018-07-21)

No changes since beta1

## 2.0.0.beta1 (2018-07-04)

### Breaking changes

- Only pass last element of "namespace array" to policy and scope. [#529](https://github.com/varvet/pundit/issues/529)
- Raise `InvalidConstructorError` if a policy or policy scope with an invalid constructor is called. [#462](https://github.com/varvet/pundit/issues/462)
- Return passed object from `#authorize` method to make chaining possible. [#385](https://github.com/varvet/pundit/issues/385)

### Other changes

- Add `policy_class` option to `authorize` to be able to override the policy. [#441](https://github.com/varvet/pundit/issues/441)
- Add `policy_scope_class` option to `authorize` to be able to override the policy scope. [#441](https://github.com/varvet/pundit/issues/441)
- Fix `param_key` issue when passed an array. [#529](https://github.com/varvet/pundit/issues/529)
- Allow specification of a `NilClassPolicy`. [#525](https://github.com/varvet/pundit/issues/525)
- Make sure `policy_class` override is called when passed an array. [#475](https://github.com/varvet/pundit/issues/475)

- Use `action_name` instead of `params[:action]`. [#419](https://github.com/varvet/pundit/issues/419)
- Add `pundit_params_for` method to make it easy to customize params fetching. [#502](https://github.com/varvet/pundit/issues/502)

## 1.1.0 (2016-01-14)

- Can retrieve policies via an array of symbols/objects.
- Add autodetection of param key to `permitted_attributes` helper.
- Hide some methods which should not be actions.
- Permitted attributes should be expanded.
- Generator uses `RSpec.describe` according to modern best practices.

## 1.0.1 (2015-05-27)

- Fixed a regression where NotAuthorizedError could not be ininitialized with a string.
- Use `camelize` instead of `classify` for symbol policies to prevent weird pluralizations.

## 1.0.0 (2015-04-19)

- Caches policy scopes and policies.
- Explicitly setting the policy for the controller via `controller.policy = foo` has been removed. Instead use `controller.policies[record] = foo`.
- Explicitly setting the policy scope for the controller via `controller.policy_policy = foo` has been removed. Instead use `controller.policy_scopes[scope] = foo`.
- Add `permitted_attributes` helper to fetch attributes from policy.
- Add `pundit_policy_authorized?` and `pundit_policy_scoped?` methods.
- Instance variables are prefixed to avoid collisions.
- Add `Pundit.authorize` method.
- Add `skip_authorization` and `skip_policy_scope` helpers.
- Better errors when checking multiple permissions in RSpec tests.
- Better errors in case `nil` is passed to `policy` or `policy_scope`.
- Use `inspect` when printing object for better errors.
- Dropped official support for Ruby 1.9.3

## 0.3.0 (2014-08-22)

- Extend the default `ApplicationPolicy` with an `ApplicationPolicy::Scope` [#120](https://github.com/varvet/pundit/issues/120)
- Fix RSpec 3 deprecation warnings for built-in matchers [#162](https://github.com/varvet/pundit/issues/162)
- Generate blank policy spec/test files for Rspec/MiniTest/Test::Unit in Rails [#138](https://github.com/varvet/pundit/issues/138)

## 0.2.3 (2014-04-06)

- Customizable error messages: `#query`, `#record` and `#policy` methods on `Pundit::NotAuthorizedError` [#114](https://github.com/varvet/pundit/issues/114)
- Raise a different `Pundit::AuthorizationNotPerformedError` when `authorize` call is expected in controller action but missing [#109](https://github.com/varvet/pundit/issues/109)
- Update Rspec matchers for Rspec 3 [#124](https://github.com/varvet/pundit/issues/124)

## 0.2.2 (2014-02-07)

- Customize the user to be passed into policies: `pundit_user` [#42](https://github.com/varvet/pundit/issues/42)


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Code of Conduct

As contributors and maintainers of this project, we pledge to respect all
people who contribute through reporting issues, posting feature requests,
updating documentation, submitting pull requests or patches, and other
activities.

We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, age, or religion.

Examples of unacceptable behavior by participants include the use of sexual
language or imagery, derogatory comments or personal attacks, trolling, public
or private harassment, insults, or other unprofessional conduct.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct. Project maintainers who do not
follow the Code of Conduct may be removed from the project team.

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by opening an issue or contacting one or more of the project
maintainers.

This Code of Conduct is adapted from the [Contributor
Covenant](http:contributor-covenant.org), version 1.0.0, available at
[https://contributor-covenant.org/version/1/0/0/](https://contributor-covenant.org/version/1/0/0/)


================================================
FILE: CONTRIBUTING.md
================================================
## Security issues

If you have found a security related issue, please do not file an issue on GitHub or send a PR addressing the issue. Refer to [SECURITY.md](./SECURITY.md) for instructions.

## Reporting issues

Please try to answer the following questions in your bug report:

- What did you do?
- What did you expect to happen?
- What happened instead?

Make sure to include as much relevant information as possible. Ruby version,
Pundit version, OS version and any stack traces you have are very valuable.

## Pull Requests

- **Add tests!** Your patch won't be accepted if it doesn't have tests.

- **Document any change in behaviour**. Make sure the README and any other
  relevant documentation are kept up-to-date.

- **Create topic branches**. Please don't ask us to pull from your main branch.

- **One pull request per feature**. If you want to do more than one thing, send
  multiple pull requests.

- **Send coherent history**. Make sure each individual commit in your pull
  request is meaningful. If you had to make multiple intermediate commits while
  developing, please squash them before sending them to us.
- **Update the CHANGELOG.** Don't forget to add your new changes to the CHANGELOG.


================================================
FILE: Gemfile
================================================
# frozen_string_literal: true

source "https://rubygems.org"

gemspec

# Rails-related - for testing purposes
gem "actionpack", ">= 3.0.0" # Used to test strong parameters
gem "activemodel", ">= 3.0.0" # Used to test ActiveModel::Naming
gem "railties", ">= 3.0.0" # Used to test generators

# Testing
gem "rspec", ">= 3.0.0"
gem "simplecov", ">= 0.17.0"

# Development tools
gem "bundler"
gem "rake"
gem "standard"
gem "yard"
gem "zeitwerk"

# Affects us on JRuby 9.3.15.
#
# @see https://github.com/rails/rails/issues/54260
gem "logger"


================================================
FILE: LICENSE.txt
================================================
Copyright (c) 2019 Jonas Nicklas, Varvet AB

MIT License

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
================================================
# Pundit

[![Main](https://github.com/varvet/pundit/actions/workflows/main.yml/badge.svg)](https://github.com/varvet/pundit/actions/workflows/main.yml)
[![Inline docs](https://inch-ci.org/github/varvet/pundit.svg?branch=main)](https://inch-ci.org/github/varvet/pundit)
[![Gem Version](https://badge.fury.io/rb/pundit.svg)](https://badge.fury.io/rb/pundit)

Pundit provides a set of helpers which guide you in leveraging regular Ruby
classes and object oriented design patterns to build a straightforward, robust, and
scalable authorization system.

## Links:

- [API documentation for the most recent version](https://www.rubydoc.info/gems/pundit)
- [Source Code](https://github.com/varvet/pundit)
- [Contributing](https://github.com/varvet/pundit/blob/main/CONTRIBUTING.md)
- [Code of Conduct](https://github.com/varvet/pundit/blob/main/CODE_OF_CONDUCT.md)

<strong>Sponsored by:</strong> <a href="https://www.varvet.com">Varvet<br><br><img src="https://github.com/varvet/pundit/assets/99166/aa9efa0a-6903-4037-abee-1824edc57f1a" alt="Varvet logo" height="120"></div>

## Installation

> **Please note** that the README on GitHub is accurate with the _latest code on GitHub_. You are most likely using a released version of Pundit, so please refer to the [documentation for the latest released version of Pundit](https://www.rubydoc.info/gems/pundit).

``` sh
bundle add pundit
```

Include `Pundit::Authorization` in your application controller:

``` ruby
class ApplicationController < ActionController::Base
  include Pundit::Authorization
end
```

Optionally, you can run the generator, which will set up an application policy
with some useful defaults for you:

``` sh
rails g pundit:install
```

After generating your application policy, restart the Rails server so that Rails
can pick up any classes in the new `app/policies/` directory.

## Policies

Pundit is focused around the notion of policy classes. We suggest that you put
these classes in `app/policies`. This is an example that allows updating a post
if the user is an admin, or if the post is unpublished:

``` ruby
class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin? || !post.published?
  end
end
```

As you can see, this is a plain Ruby class. Pundit makes the following
assumptions about this class:

- The class has the same name as some kind of model class, only suffixed
  with the word "Policy".
- The first argument is a user. In your controller, Pundit will call the
  `current_user` method to retrieve what to send into this argument
- The second argument is some kind of model object, whose authorization
  you want to check. This does not need to be an ActiveRecord or even
  an ActiveModel object, it can be anything really.
- The class implements some kind of query method, in this case `update?`.
  Usually, this will map to the name of a particular controller action.

That's it really.

Usually you'll want to inherit from the application policy created by the
generator, or set up your own base class to inherit from:

``` ruby
class PostPolicy < ApplicationPolicy
  def update?
    user.admin? or not record.published?
  end
end
```

In the generated `ApplicationPolicy`, the model object is called `record`.

Supposing that you have an instance of class `Post`, Pundit now lets you do
this in your controller:

``` ruby
def update
  @post = Post.find(params[:id])
  authorize @post
  if @post.update(post_params)
    redirect_to @post
  else
    render :edit
  end
end
```

The authorize method automatically infers that `Post` will have a matching
`PostPolicy` class, and instantiates this class, handing in the current user
and the given record. It then infers from the action name, that it should call
`update?` on this instance of the policy. In this case, you can imagine that
`authorize` would have done something like this:

``` ruby
unless PostPolicy.new(current_user, @post).update?
  raise Pundit::NotAuthorizedError, "not allowed to PostPolicy#update? this Post"
end
```

You can pass a second argument to `authorize` if the name of the permission you
want to check doesn't match the action name. For example:

``` ruby
def publish
  @post = Post.find(params[:id])
  authorize @post, :update?
  @post.publish!
  redirect_to @post
end
```

You can pass an argument to override the policy class if necessary. For example:

```ruby
def create
  @publication = find_publication # assume this method returns any model that behaves like a publication
  # @publication.class => Post
  authorize @publication, policy_class: PublicationPolicy
  @publication.publish!
  redirect_to @publication
end
```

If you don't have an instance for the first argument to `authorize`, then you can pass
the class. For example:

Policy:
```ruby
class PostPolicy < ApplicationPolicy
  def admin_list?
    user.admin?
  end
end
```

Controller:
```ruby
def admin_list
  authorize Post # we don't have a particular post to authorize
  # Rest of controller action
end
```

`authorize` returns the instance passed to it, so you can chain it like this:

Controller:
```ruby
def show
  @user = authorize User.find(params[:id])
end
```

You can easily get a hold of an instance of the policy through the `policy`
method in both the view and controller. This is especially useful for
conditionally showing links or buttons in the view:

``` erb
<% if policy(@post).update? %>
  <%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
```
## Headless policies

Given there is a policy without a corresponding model / ruby class,
you can retrieve it by passing a symbol.

```ruby
# app/policies/dashboard_policy.rb
class DashboardPolicy
  attr_reader :user

  # `_record` in this example will be :dashboard
  def initialize(user, _record)
    @user = user
  end

  def show?
    user.admin?
  end
end
```

Note that the headless policy still needs to accept two arguments. The
second argument will be the symbol `:dashboard` in this case, which
is what is passed as the record to `authorize` below.

```ruby
# In controllers
def show
  authorize :dashboard, :show?
  ...
end
```

```erb
# In views
<% if policy(:dashboard).show? %>
  <%= link_to 'Dashboard', dashboard_path %>
<% end %>
```

## Scopes

Often, you will want to have some kind of view listing records which a
particular user has access to. When using Pundit, you are expected to
define a class called a policy scope. It can look something like this:

``` ruby
class PostPolicy < ApplicationPolicy
  class Scope
    def initialize(user, scope)
      @user  = user
      @scope = scope
    end

    def resolve
      if user.admin?
        scope.all
      else
        scope.where(published: true)
      end
    end

    private

    attr_reader :user, :scope
  end

  def update?
    user.admin? or not record.published?
  end
end
```

Pundit makes the following assumptions about this class:

- The class has the name `Scope` and is nested under the policy class.
- The first argument is a user. In your controller, Pundit will call the
  `current_user` method to retrieve what to send into this argument.
- The second argument is a scope of some kind on which to perform some kind of
  query. It will usually be an ActiveRecord class or a
  `ActiveRecord::Relation`, but it could be something else entirely.
- Instances of this class respond to the method `resolve`, which should return
  some kind of result which can be iterated over. For ActiveRecord classes,
  this would usually be an `ActiveRecord::Relation`.

You'll probably want to inherit from the application policy scope generated by the
generator, or create your own base class to inherit from:

``` ruby
class PostPolicy < ApplicationPolicy
  class Scope < ApplicationPolicy::Scope
    def resolve
      if user.admin?
        scope.all
      else
        scope.where(published: true)
      end
    end
  end

  def update?
    user.admin? or not record.published?
  end
end
```

You can now use this class from your controller via the `policy_scope` method:

``` ruby
def index
  @posts = policy_scope(Post)
end

def show
  @post = policy_scope(Post).find(params[:id])
end
```

Like with the authorize method, you can also override the policy scope class:

``` ruby
def index
  # publication_class => Post
  @publications = policy_scope(publication_class, policy_scope_class: PublicationPolicy::Scope)
end
```

In this case it is a shortcut for doing:

``` ruby
def index
  @publications = PublicationPolicy::Scope.new(current_user, Post).resolve
end
```

You can, and are encouraged to, use this method in views:

``` erb
<% policy_scope(@user.posts).each do |post| %>
  <p><%= link_to post.title, post_path(post) %></p>
<% end %>
```

## Ensuring policies and scopes are used

When you are developing an application with Pundit it can be easy to forget to
authorize some action. People are forgetful after all. Since Pundit encourages
you to add the `authorize` call manually to each controller action, it's really
easy to miss one.

Thankfully, Pundit has a handy feature which reminds you in case you forget.
Pundit tracks whether you have called `authorize` anywhere in your controller
action. Pundit also adds a method to your controllers called
`verify_authorized`. This method will raise an exception if `authorize` has not
yet been called. You should run this method in an `after_action` hook to ensure
that you haven't forgotten to authorize the action. For example:

``` ruby
class ApplicationController < ActionController::Base
  include Pundit::Authorization
  after_action :verify_authorized
end
```

Likewise, Pundit also adds `verify_policy_scoped` to your controller. This
will raise an exception similar to `verify_authorized`. However, it tracks
if `policy_scope` is used instead of `authorize`. This is mostly useful for
controller actions like `index` which find collections with a scope and don't
authorize individual instances.

``` ruby
class ApplicationController < ActionController::Base
  include Pundit::Authorization
  after_action :verify_pundit_authorization

  def verify_pundit_authorization
    if action_name == "index"
      verify_policy_scoped
    else
      verify_authorized
    end
  end
end
```

**This verification mechanism only exists to aid you while developing your
application, so you don't forget to call `authorize`. It is not some kind of
failsafe mechanism or authorization mechanism. You should be able to remove
these filters without affecting how your app works in any way.**

Some people have found this feature confusing, while many others
find it extremely helpful. If you fall into the category of people who find it
confusing then you do not need to use it. Pundit will work fine without
using `verify_authorized` and `verify_policy_scoped`.

### Conditional verification

If you're using `verify_authorized` in your controllers but need to
conditionally bypass verification, you can use `skip_authorization`. For
bypassing `verify_policy_scoped`, use `skip_policy_scope`. These are useful
in circumstances where you don't want to disable verification for the
entire action, but have some cases where you intend to not authorize.

```ruby
def show
  record = Record.find_by(attribute: "value")
  if record.present?
    authorize record
  else
    skip_authorization
  end
end
```

## Manually specifying policy classes

Sometimes you might want to explicitly declare which policy to use for a given
class, instead of letting Pundit infer it. This can be done like so:

``` ruby
class Post
  def self.policy_class
    PostablePolicy
  end
end
```

Alternatively, you can declare an instance method:

``` ruby
class Post
  def policy_class
    PostablePolicy
  end
end
```

## Plain old Ruby

Pundit is a very small library on purpose, and it doesn't do anything you can't do yourself. There's no secret sauce here. It does as little as possible, and then gets out of your way.

With the few but powerful helpers available in Pundit, you have the power to build a well structured, fully working authorization system without using any special DSLs or funky syntax.

Remember that all of the policy and scope classes are plain Ruby classes, which means you can use the same mechanisms you always use to DRY things up. Encapsulate a set of permissions into a module and include them in multiple policies. Use `alias_method` to make some permissions behave the same as others. Inherit from a base set of permissions. Use metaprogramming if you really have to.

## Generator

Use the supplied generator to generate policies:

``` sh
rails g pundit:policy post
```

## Closed systems

In many applications, only logged in users are really able to do anything. If
you're building such a system, it can be kind of cumbersome to check that the
user in a policy isn't `nil` for every single permission. Aside from policies,
you can add this check to the base class for scopes.

We suggest that you define a filter that redirects unauthenticated users to the
login page. As a secondary defence, if you've defined an ApplicationPolicy, it
might be a good idea to raise an exception if somehow an unauthenticated user
got through. This way you can fail more gracefully.

``` ruby
class ApplicationPolicy
  def initialize(user, record)
    raise Pundit::NotAuthorizedError, "must be logged in" unless user
    @user   = user
    @record = record
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      raise Pundit::NotAuthorizedError, "must be logged in" unless user
      @user = user
      @scope = scope
    end
  end
end
```

## NilClassPolicy

To support a [null object pattern](https://en.wikipedia.org/wiki/Null_Object_pattern)
you may find that you want to implement a `NilClassPolicy`. This might be useful
where you want to extend your ApplicationPolicy to allow some tolerance of, for
example, associations which might be `nil`.

```ruby
class NilClassPolicy < ApplicationPolicy
  class Scope < ApplicationPolicy::Scope
    def resolve
      raise Pundit::NotDefinedError, "Cannot scope NilClass"
    end
  end

  def show?
    false # Nobody can see nothing
  end
end
```

## Rescuing a denied Authorization in Rails

Pundit raises a `Pundit::NotAuthorizedError` you can
[rescue_from](https://guides.rubyonrails.org/action_controller_overview.html#rescue-from)
in your `ApplicationController`. You can customize the `user_not_authorized`
method in every controller.

```ruby
class ApplicationController < ActionController::Base
  include Pundit::Authorization

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private

  def user_not_authorized
    flash[:alert] = "You are not authorized to perform this action."
    redirect_back_or_to(root_path)
  end
end
```

Alternatively, you can globally handle Pundit::NotAuthorizedError's by having rails handle them as a 403 error and serving a 403 error page. Add the following to application.rb:

```config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden```

## Creating custom error messages

`NotAuthorizedError`s provide information on what query (e.g. `:create?`), what
record (e.g. an instance of `Post`), and what policy (e.g. an instance of
`PostPolicy`) caused the error to be raised.

One way to use these `query`, `record`, and `policy` properties is to connect
them with `I18n` to generate error messages. Here's how you might go about doing
that.

```ruby
class ApplicationController < ActionController::Base
 rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

 private

 def user_not_authorized(exception)
   policy_name = exception.policy.class.to_s.underscore

   flash[:error] = t "#{policy_name}.#{exception.query}", scope: "pundit", default: :default
   redirect_back_or_to(root_path)
 end
end
```

```yaml
en:
 pundit:
   default: 'You cannot perform this action.'
   post_policy:
     update?: 'You cannot edit this post!'
     create?: 'You cannot create posts!'
```

This is an example. Pundit is agnostic as to how you implement your error messaging.

## Manually retrieving policies and scopes

Sometimes you want to retrieve a policy for a record outside the controller or
view. For example when you delegate permissions from one policy to another.

You can easily retrieve policies and scopes like this:

``` ruby
Pundit.policy!(user, post)
Pundit.policy(user, post)

Pundit.policy_scope!(user, Post)
Pundit.policy_scope(user, Post)
```

The bang methods will raise an exception if the policy does not exist, whereas
those without the bang will return nil.

## Customize Pundit user

On occasion, your controller may be unable to access `current_user`, or the method that should be invoked by Pundit may not be `current_user`. To address this, you can define a method in your controller named `pundit_user`.

```ruby
def pundit_user
  User.find_by_other_means
end
```

For instance, Rails 8 includes a built-in [authentication generator](https://github.com/rails/rails/tree/8-0-stable/railties/lib/rails/generators/rails/authentication). If you choose to use it, the currently logged-in user is accessed via `Current.user` instead of `current_user`.

To ensure compatibility with Pundit, define a `pundit_user` method in `application_controller.rb` (or another suitable location) as follows:

```ruby
def pundit_user
  Current.user
end
```

### Handling User Switching in Pundit

When switching users in your application, it's important to reset the Pundit user context to ensure that authorization policies are applied correctly for the new user. Pundit caches the user context, so failing to reset it could result in incorrect permissions being applied.

To handle user switching, you can use the following pattern in your controller:

```ruby
class ApplicationController
  include Pundit::Authorization

  def switch_user_to(user)
    terminate_session if authenticated?
    start_new_session_for user
    pundit_reset!
  end
end
```

Make sure to invoke `pundit_reset!` whenever changing the user. This ensures the cached authorization context is reset, preventing any incorrect permissions from being applied.

## Policy Namespacing

In some cases it might be helpful to have multiple policies that serve different contexts for a resource. A prime example of this is the case where User policies differ from Admin policies. To authorize with a namespaced policy, pass the namespace into the `authorize` helper in an array:

```ruby
authorize(post)                   # => will look for a PostPolicy
authorize([:admin, post])         # => will look for an Admin::PostPolicy
authorize([:foo, :bar, post])     # => will look for a Foo::Bar::PostPolicy

policy_scope(Post)                # => will look for a PostPolicy::Scope
policy_scope([:admin, Post])      # => will look for an Admin::PostPolicy::Scope
policy_scope([:foo, :bar, Post])  # => will look for a Foo::Bar::PostPolicy::Scope
```

If you are using namespaced policies for something like Admin areas, we recommend defining a `pundit_namespace` hook in your `ApplicationController` and overriding it in namespaced controllers:

```ruby
class ApplicationController < ActionController::Base
  include Pundit::Authorization

  private

  def pundit_namespace(record) = record

  def authorize(record, ...) = super(pundit_namespace(record), ...)
  def policy_scope(scope, ...) = super(pundit_namespace(scope), ...)
end

class AdminController < ApplicationController
  private

  # Override the pundit namespace in Admin.
  def pundit_namespace(record) = [:admin, record]
end

class Admin::PostController < AdminController
  def index
    policy_scope(Post)
  end

  def show
    post = authorize Post.find(params[:id])
  end
end
```

## Additional context

Pundit strongly encourages you to model your application in such a way that the
only context you need for authorization is a user object and a domain model that
you want to check authorization for. If you find yourself needing more context than
that, consider whether you are authorizing the right domain model, maybe another
domain model (or a wrapper around multiple domain models) can provide the context
you need.

Pundit does not allow you to pass additional arguments to policies for precisely
this reason.

However, in very rare cases, you might need to authorize based on more context than just
the currently authenticated user. Suppose for example that authorization is dependent
on IP address in addition to the authenticated user. In that case, one option is to
create a special class which wraps up both user and IP and passes it to the policy.

``` ruby
class UserContext < Data.define(:user, :ip)
end

class ApplicationController
  include Pundit::Authorization

  def pundit_user = UserContext.new(current_user, request.ip)
end
```

## Strong parameters

In Rails, [mass-assignment protection is handled in the controller](https://guides.rubyonrails.org/action_controller_overview.html#strong-parameters). With Pundit you can control which attributes a user has access to update via your policies. You can set up an `expected_attributes_for_action(action_name)` method in your policy like this:

```ruby
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
  def expected_attributes_for_action(_action_name)
    if user.admin? || user.owner_of?(post)
      [:title, :body, :tag_list]
    else
      [:tag_list]
    end
  end
end
```

You can now retrieve these attributes from the policy:

```ruby
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def update
    @post = Post.find(params[:id])
    if @post.update(post_params)
      redirect_to @post
    else
      render :edit
    end
  end

  private

  def post_params
    params.expect(policy(@post).expected_attributes_for_action(action_name))
  end
end
```

However, this is a bit cumbersome, so Pundit provides a convenient helper method with `#expected_attributes`:

```ruby
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def update
    @post = Post.find(params[:id])
    if @post.update(expected_attributes(@post))
      redirect_to @post
    else
      render :edit
    end
  end
end
```

### Permitted Parameters

Pundit still support the old `params.require.permit()` style of permitting attributes, although `params.expect()` is preferred.

If you need to fetch parameters based on namespaces different from the suggested one, override the below method, in your controller, and return an instance of `ActionController::Parameters`.

```ruby
def pundit_params_for(record)
  params.require(pundit_param_key(record))
end
```

For example:

```ruby
# If you don't want to use require
def pundit_params_for(record)
  params.fetch(pundit_param_key(record), {})
end

# If you are using something like the JSON API spec
def pundit_params_for(_record)
  params.fetch(:data, {}).fetch(:attributes, {})
end
```

## RSpec

### Policy Specs

> [!TIP]
> An alternative approach to Pundit policy specs is scoping them to a user context as outlined in this
[excellent post](https://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/) and implemented in the third party [pundit-matchers](https://github.com/punditcommunity/pundit-matchers) gem.

Pundit includes a mini-DSL for writing expressive tests for your policies in RSpec.
Require `pundit/rspec` in your `spec_helper.rb`:

``` ruby
require "pundit/rspec"
```

Then put your policy specs in `spec/policies`, and make them look somewhat like this:

``` ruby
describe PostPolicy do
  subject { described_class }

  permissions :update?, :edit? do
    it "denies access if post is published" do
      expect(subject).not_to permit(User.new(admin: false), Post.new(published: true))
    end

    it "grants access if post is published and user is an admin" do
      expect(subject).to permit(User.new(admin: true), Post.new(published: true))
    end

    it "grants access if post is unpublished" do
      expect(subject).to permit(User.new(admin: false), Post.new(published: false))
    end
  end
end
```

### Custom matcher description

By default rspec includes an inspected `user` and `record` in the matcher description, which might become overly verbose:

```
PostPolicy
  update? and show?
    is expected to permit #<User:0x0000000104aefd80> and #<Post:0x0000000104aef8d0 @user=#<User:0x0000000104aefd80>>
```

You can override the default description with a static string, or a block:

```ruby
# static alternative: Pundit::RSpec::Matchers.description = "permit the user"
Pundit::RSpec::Matchers.description = ->(user, record) do
  "permit user with role #{user.role} to access record with ID #{record.id}"
end
```

Which would make for a less chatty output:

```
PostPolicy
  update? and show?
    is expected to permit user with role admin to access record with ID 130
```

### Focus Support

If your RSpec config has `filter_run_when_matching :focus`, you may tag the `permissions` helper like so:

```
permissions :show?, :focus do
```

### Scope Specs

Pundit does not provide a DSL for testing scopes. Test them like you would a regular Ruby class!

### Linting with RuboCop RSpec

When you lint your RSpec spec files with `rubocop-rspec`, it will fail to properly detect RSpec constructs that Pundit defines, `permissions`.
Make sure to use `rubocop-rspec` 2.0 or newer and add the following to your `.rubocop.yml`:

```yaml
inherit_gem:
  pundit: config/rubocop-rspec.yml
```

# External Resources

- [RailsApps Example Application: Pundit and Devise](https://github.com/RailsApps/rails-devise-pundit)
- [Migrating to Pundit from CanCan](https://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/)
- [Testing Pundit Policies with RSpec](https://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/)
- [Testing Pundit with Minitest](https://github.com/varvet/pundit/issues/204#issuecomment-60166450)
- [Using Pundit outside of a Rails controller](https://github.com/varvet/pundit/pull/136)
- [Straightforward Rails Authorization with Pundit](https://www.sitepoint.com/straightforward-rails-authorization-with-pundit/)

## Other implementations

- [Flask-Pundit](https://github.com/anurag90x/flask-pundit) (Python) is a [Flask](https://flask.pocoo.org/) extension "heavily inspired by" Pundit

# License

Licensed under the MIT license, see the separate LICENSE.txt file.


================================================
FILE: Rakefile
================================================
# frozen_string_literal: true

require "rubygems"
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require "yard"
require "rubocop/rake_task"

RuboCop::RakeTask.new

desc "Run all examples"
RSpec::Core::RakeTask.new(:spec)

YARD::Rake::YardocTask.new do |t|
  t.files = ["lib/**/*.rb"]
  t.stats_options = ["--list-undoc"]
end

task default: :spec


================================================
FILE: SECURITY.md
================================================
# Security Policy

Please do not file an issue on GitHub, or send a PR addressing the issue.

## Supported versions

Most recent major version only.

## Reporting a vulnerability

Contact one of the maintainers directly:

* [@Burgestrand](https://github.com/Burgestrand)
* [@dgmstuart](https://github.com/dgmstuart)
* [@varvet](https://github.com/varvet)

You can report vulnerabilities on GitHub too: https://github.com/varvet/pundit/security

Thank you!


================================================
FILE: bin/console
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"
require "pundit"

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

require "irb"
IRB.start(__FILE__)


================================================
FILE: bin/setup
================================================
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here


================================================
FILE: config/rubocop-rspec.yml
================================================
RSpec:
  Language:
    ExampleGroups:
      Regular:
        - permissions


================================================
FILE: lib/generators/pundit/install/USAGE
================================================
Description:
    Generates an application policy as a starting point for your application.


================================================
FILE: lib/generators/pundit/install/install_generator.rb
================================================
# frozen_string_literal: true

module Pundit
  # @private
  module Generators
    # @private
    class InstallGenerator < ::Rails::Generators::Base
      source_root File.expand_path("templates", __dir__)

      def copy_application_policy
        template "application_policy.rb.tt", "app/policies/application_policy.rb"
      end
    end
  end
end


================================================
FILE: lib/generators/pundit/install/templates/application_policy.rb.tt
================================================
# frozen_string_literal: true

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def show?
    false
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  class Scope
    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      raise NoMethodError, "You must define #resolve in #{self.class}"
    end

    private

    attr_reader :user, :scope
  end
end


================================================
FILE: lib/generators/pundit/policy/USAGE
================================================
Description:
    Generates a policy for a model with the given name.

Example:
    rails generate pundit:policy user

    This will create:
        app/policies/user_policy.rb


================================================
FILE: lib/generators/pundit/policy/policy_generator.rb
================================================
# frozen_string_literal: true

module Pundit
  # @private
  module Generators
    # @private
    class PolicyGenerator < ::Rails::Generators::NamedBase
      source_root File.expand_path("templates", __dir__)

      def create_policy
        template "policy.rb.tt", File.join("app/policies", class_path, "#{file_name}_policy.rb")
      end

      hook_for :test_framework
    end
  end
end


================================================
FILE: lib/generators/pundit/policy/templates/policy.rb.tt
================================================
<% module_namespacing do -%>
class <%= class_name %>Policy < ApplicationPolicy
  # NOTE: Up to Pundit v2.3.1, the inheritance was declared as
  # `Scope < Scope` rather than `Scope < ApplicationPolicy::Scope`.
  # In most cases the behavior will be identical, but if updating existing
  # code, beware of possible changes to the ancestors:
  # https://gist.github.com/Burgestrand/4b4bc22f31c8a95c425fc0e30d7ef1f5

  class Scope < ApplicationPolicy::Scope
    # NOTE: Be explicit about which records you allow access to!
    # def resolve
    #   scope.all
    # end
  end
end
<% end -%>


================================================
FILE: lib/generators/rspec/policy_generator.rb
================================================
# frozen_string_literal: true

# @private
module Rspec
  # @private
  module Generators
    # @private
    class PolicyGenerator < ::Rails::Generators::NamedBase
      source_root File.expand_path("templates", __dir__)

      def create_policy_spec
        template "policy_spec.rb.tt", File.join("spec/policies", class_path, "#{file_name}_policy_spec.rb")
      end
    end
  end
end


================================================
FILE: lib/generators/rspec/templates/policy_spec.rb.tt
================================================
require '<%= File.exist?('spec/rails_helper.rb') ? 'rails_helper' : 'spec_helper' %>'

RSpec.describe <%= class_name %>Policy, type: :policy do
  let(:user) { User.new }

  subject { described_class }

  permissions ".scope" do
    pending "add some examples to (or delete) #{__FILE__}"
  end

  permissions :show? do
    pending "add some examples to (or delete) #{__FILE__}"
  end

  permissions :create? do
    pending "add some examples to (or delete) #{__FILE__}"
  end

  permissions :update? do
    pending "add some examples to (or delete) #{__FILE__}"
  end

  permissions :destroy? do
    pending "add some examples to (or delete) #{__FILE__}"
  end
end


================================================
FILE: lib/generators/test_unit/policy_generator.rb
================================================
# frozen_string_literal: true

# @private
module TestUnit
  # @private
  module Generators
    # @private
    class PolicyGenerator < ::Rails::Generators::NamedBase
      source_root File.expand_path("templates", __dir__)

      def create_policy_test
        template "policy_test.rb.tt", File.join("test/policies", class_path, "#{file_name}_policy_test.rb")
      end
    end
  end
end


================================================
FILE: lib/generators/test_unit/templates/policy_test.rb.tt
================================================
require 'test_helper'

class <%= class_name %>PolicyTest < ActiveSupport::TestCase
  def test_scope
  end

  def test_show
  end

  def test_create
  end

  def test_update
  end

  def test_destroy
  end
end


================================================
FILE: lib/pundit/authorization.rb
================================================
# frozen_string_literal: true

module Pundit
  # Pundit DSL to include in your controllers to provide authorization helpers.
  #
  # @example
  #   class ApplicationController < ActionController::Base
  #     include Pundit::Authorization
  #   end
  # @see #pundit
  # @api public
  # @since v2.2.0
  module Authorization
    extend ActiveSupport::Concern

    included do
      helper Helper if respond_to?(:helper)
      if respond_to?(:helper_method)
        helper_method :policy
        helper_method :pundit_policy_scope
        helper_method :pundit_user
      end
    end

    protected

    # An instance of {Pundit::Context} initialized with the current user.
    #
    # @note this method is memoized and will return the same instance during the request.
    # @api public
    # @return [Pundit::Context]
    # @see #pundit_user
    # @see #policies
    # @since v2.3.2
    def pundit
      @pundit ||= Pundit::Context.new(
        user: pundit_user,
        policy_cache: Pundit::CacheStore::LegacyStore.new(policies)
      )
    end

    # Hook method which allows customizing which user is passed to policies and
    # scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
    #
    # @note Make sure to call `pundit_reset!` if this changes during a request.
    # @see https://github.com/varvet/pundit#customize-pundit-user
    # @see #pundit
    # @see #pundit_reset!
    # @return [Object] the user object to be used with pundit
    # @since v0.2.2
    def pundit_user
      current_user
    end

    # Clears the cached Pundit authorization data.
    #
    # This method should be called when the pundit_user is changed,
    # such as during user switching, to ensure that stale authorization
    # data is not used. Pundit caches authorization policies and scopes
    # for the pundit_user, so calling this method will reset those
    # caches and ensure that the next authorization checks are performed
    # with the correct context for the new pundit_user.
    #
    # @return [void]
    # @since v2.5.0
    def pundit_reset!
      @pundit = nil
      @_pundit_policies = nil
      @_pundit_policy_scopes = nil
      @_pundit_policy_authorized = nil
      @_pundit_policy_scoped = nil
    end

    # @!group Policies

    # Retrieves the policy for the given record, initializing it with the record
    # and current user and finally throwing an error if the user is not
    # authorized to perform the given action.
    #
    # @param record [Object, Array] the object we're checking permissions of
    # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
    #   If omitted then this defaults to the Rails controller action name.
    # @param policy_class [Class] the policy class we want to force use of
    # @raise [NotAuthorizedError] if the given query method returned false
    # @return [record] Always returns the passed object record
    # @see Pundit::Context#authorize
    # @see #verify_authorized
    # @since v0.1.0
    def authorize(record, query = nil, policy_class: nil)
      query ||= "#{action_name}?"

      @_pundit_policy_authorized = true

      pundit.authorize(record, query: query, policy_class: policy_class)
    end

    # Allow this action not to perform authorization.
    #
    # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
    # @return [void]
    # @see #verify_authorized
    # @since v1.0.0
    def skip_authorization
      @_pundit_policy_authorized = :skipped
    end

    # @return [Boolean] wether or not authorization has been performed
    # @see #authorize
    # @see #skip_authorization
    # @since v1.0.0
    def pundit_policy_authorized?
      !!@_pundit_policy_authorized
    end

    # Raises an error if authorization has not been performed.
    #
    # Usually used as an `after_action` filter to prevent programmer error in
    # forgetting to call {#authorize} or {#skip_authorization}.
    #
    # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
    # @raise [AuthorizationNotPerformedError] if authorization has not been performed
    # @return [void]
    # @see #authorize
    # @see #skip_authorization
    # @since v0.1.0
    def verify_authorized
      raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
    end

    # rubocop:disable Naming/MemoizedInstanceVariableName

    # Cache of policies. You should not rely on this method.
    #
    # @api private
    # @since v1.0.0
    def policies
      @_pundit_policies ||= {}
    end

    # rubocop:enable Naming/MemoizedInstanceVariableName

    # @!endgroup

    # Retrieves the policy for the given record.
    #
    # @see https://github.com/varvet/pundit#policies
    # @param record [Object] the object we're retrieving the policy for
    # @return [Object] instance of policy class with query methods
    # @since v0.1.0
    def policy(record)
      pundit.policy!(record)
    end

    # @!group Policy Scopes

    # Retrieves the policy scope for the given record.
    #
    # @see https://github.com/varvet/pundit#scopes
    # @param scope [Object] the object we're retrieving the policy scope for
    # @param policy_scope_class [#resolve] the policy scope class we want to force use of
    # @return [#resolve, nil] instance of scope class which can resolve to a scope
    # @since v0.1.0
    def policy_scope(scope, policy_scope_class: nil)
      @_pundit_policy_scoped = true
      policy_scope_class ? policy_scope_class.new(pundit_user, scope).resolve : pundit_policy_scope(scope)
    end

    # Allow this action not to perform policy scoping.
    #
    # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
    # @return [void]
    # @see #verify_policy_scoped
    # @since v1.0.0
    def skip_policy_scope
      @_pundit_policy_scoped = :skipped
    end

    # @return [Boolean] wether or not policy scoping has been performed
    # @see #policy_scope
    # @see #skip_policy_scope
    # @since v1.0.0
    def pundit_policy_scoped?
      !!@_pundit_policy_scoped
    end

    # Raises an error if policy scoping has not been performed.
    #
    # Usually used as an `after_action` filter to prevent programmer error in
    # forgetting to call {#policy_scope} or {#skip_policy_scope} in index
    # actions.
    #
    # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
    # @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
    # @return [void]
    # @see #policy_scope
    # @see #skip_policy_scope
    # @since v0.2.1
    def verify_policy_scoped
      raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
    end

    # rubocop:disable Naming/MemoizedInstanceVariableName

    # Cache of policy scope. You should not rely on this method.
    #
    # @api private
    # @since v1.0.0
    def policy_scopes
      @_pundit_policy_scopes ||= {}
    end

    # rubocop:enable Naming/MemoizedInstanceVariableName

    # This was added to allow calling `policy_scope!` without flipping the
    # `pundit_policy_scoped?` flag.
    #
    # It's used internally by `policy_scope`, as well as from the views
    # when they call `policy_scope`. It works because views get their helper
    # from {Pundit::Helper}.
    #
    # @note This also memoizes the instance with `scope` as the key.
    # @see Pundit::Helper#policy_scope
    # @api private
    # @since v1.0.0
    def pundit_policy_scope(scope)
      policy_scopes[scope] ||= pundit.policy_scope!(scope)
    end
    private :pundit_policy_scope

    # @!endgroup

    # @!group Strong Parameters

    # Retrieves a set of expected attributes from the policy.
    #
    # @example
    #   if @post.update(expected_attributes(@post))
    #     redirect_to @post
    #   else
    #     render :edit
    #   end
    #
    # @see https://github.com/varvet/pundit#strong-parameters
    # @see https://guides.rubyonrails.org/action_controller_overview.html#expect
    # @param record [Object] the object we're retrieving expected attributes for
    # @param action [Symbol, String] the name of the action being performed on the record (e.g. `update`).
    #   If omitted then this defaults to the Rails controller action name.
    # @param param_key [String] the key that the record would have in the params hash
    # @return [Hash{String => Object}] the expected attributes
    # @since v2.6.0
    def expected_attributes(record, action: action_name, param_key: pundit_param_key(record))
      policy = policy(record)
      params.expect(param_key => policy.expected_attributes_for_action(action))
    end

    # @note This is provided as a hook for overrides.
    # @param record [Object]
    # @return [String] the key that the record would have in the params hash
    # @since v2.6.0
    def pundit_param_key(record)
      PolicyFinder.new(record).param_key
    end

    # Retrieves a set of permitted attributes from the policy.
    #
    # Done by instantiating the policy class for the given record and calling
    # `permitted_attributes` on it, or `permitted_attributes_for_{action}` if
    # `action` is defined. It then infers what key the record should have in the
    # params hash and retrieves the permitted attributes from the params hash
    # under that key.
    #
    # @see https://github.com/varvet/pundit#strong-parameters
    # @param record [Object] the object we're retrieving permitted attributes for
    # @param action [Symbol, String] the name of the action being performed on the record (e.g. `update`).
    #   If omitted then this defaults to the Rails controller action name.
    # @return [Hash{String => Object}] the permitted attributes
    # @since v1.0.0
    def permitted_attributes(record, action = action_name)
      policy = policy(record)
      method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
        "permitted_attributes_for_#{action}"
      else
        "permitted_attributes"
      end
      pundit_params_for(record).permit(*policy.public_send(method_name))
    end

    # Retrieves the params for the given record.
    #
    # @param record [Object] the object we're retrieving params for
    # @return [ActionController::Parameters] the params
    # @since v2.0.0
    def pundit_params_for(record)
      params.require(pundit_param_key(record))
    end

    # @!endgroup
  end
end


================================================
FILE: lib/pundit/cache_store/legacy_store.rb
================================================
# frozen_string_literal: true

module Pundit
  module CacheStore
    # A cache store that uses only the record as a cache key, and ignores the user.
    #
    # The original cache mechanism used by Pundit.
    #
    # @api private
    # @since v2.3.2
    class LegacyStore
      # @since v2.3.2
      def initialize(hash = {})
        @store = hash
      end

      # A cache store that uses only the record as a cache key, and ignores the user.
      #
      # @note `nil` results are not cached.
      # @since v2.3.2
      def fetch(user:, record:)
        _ = user
        @store[record] ||= yield
      end
    end
  end
end


================================================
FILE: lib/pundit/cache_store/null_store.rb
================================================
# frozen_string_literal: true

module Pundit
  module CacheStore
    # A cache store that does not cache anything.
    #
    # Use `NullStore.instance` to get the singleton instance, it is thread-safe.
    #
    # @see Pundit::Context#initialize
    # @api private
    # @since v2.3.2
    class NullStore
      @instance = new

      class << self
        # @since v2.3.2
        # @return [NullStore] the singleton instance
        attr_reader :instance
      end

      # Always yields, does not cache anything.
      # @yield
      # @return [any] whatever the block returns.
      # @since v2.3.2
      def fetch(*, **)
        yield
      end
    end
  end
end


================================================
FILE: lib/pundit/cache_store.rb
================================================
# frozen_string_literal: true

module Pundit
  # Namespace for cache store implementations.
  #
  # Cache stores are used to cache policy lookups, so you get the same policy
  # instance for the same record.
  # @since v2.3.2
  module CacheStore
    # @!group Cache Store Interface

    # @!method fetch(user:, record:, &block)
    #   Looks up a stored policy or generate a new one.
    #
    #   @since v2.3.2
    #   @note This is a method template, but the method does not exist in this module.
    #   @param user [Object]  the user that initiated the action
    #   @param record [Object] the object being accessed
    #   @param block [Proc] the block to execute if missing
    #   @return [Object] the policy

    # @!endgroup
  end
end


================================================
FILE: lib/pundit/context.rb
================================================
# frozen_string_literal: true

module Pundit
  # {Pundit::Context} is intended to be created once per request and user, and
  # it is then used to perform authorization checks throughout the request.
  #
  # @example Using Sinatra
  #   helpers do
  #     def current_user = ...
  #
  #     def pundit
  #       @pundit ||= Pundit::Context.new(user: current_user)
  #     end
  #   end
  #
  #   get "/posts/:id" do |id|
  #     pundit.authorize(Post.find(id), query: :show?)
  #   end
  #
  # @example Using [Roda](https://roda.jeremyevans.net/index.html)
  #   route do |r|
  #     context = Pundit::Context.new(user:)
  #
  #     r.get "posts", Integer do |id|
  #       context.authorize(Post.find(id), query: :show?)
  #     end
  #   end
  #
  # @since v2.3.2
  class Context
    # @see Pundit::Authorization#pundit
    # @param user later passed to policies and scopes
    # @param policy_cache [#fetch] cache store for policies (see e.g. {CacheStore::NullStore})
    # @since v2.3.2
    def initialize(user:, policy_cache: CacheStore::NullStore.instance)
      @user = user
      @policy_cache = policy_cache
    end

    # @api public
    # @see #initialize
    # @since v2.3.2
    attr_reader :user

    # @api private
    # @see #initialize
    # @since v2.3.2
    attr_reader :policy_cache

    # @!group Policies

    # Retrieves the policy for the given record, initializing it with the
    # record and user and finally throwing an error if the user is not
    # authorized to perform the given action.
    #
    # @param possibly_namespaced_record [Object, Array] the object we're checking permissions of
    # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
    # @param policy_class [Class] the policy class we want to force use of
    # @raise [NotAuthorizedError] if the given query method returned false
    # @return [Object] Always returns the passed object record
    # @since v2.3.2
    def authorize(possibly_namespaced_record, query:, policy_class:)
      record = pundit_model(possibly_namespaced_record)
      policy = if policy_class
        policy_class.new(user, record)
      else
        policy!(possibly_namespaced_record)
      end

      raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)

      record
    end

    # Retrieves the policy for the given record.
    #
    # @see https://github.com/varvet/pundit#policies
    # @param record [Object] the object we're retrieving the policy for
    # @raise [InvalidConstructorError] if the policy constructor called incorrectly
    # @return [Object, nil] instance of policy class with query methods
    # @since v2.3.2
    def policy(record)
      cached_find(record, &:policy)
    end

    # Retrieves the policy for the given record, or raises if not found.
    #
    # @see https://github.com/varvet/pundit#policies
    # @param record [Object] the object we're retrieving the policy for
    # @raise [NotDefinedError] if the policy cannot be found
    # @raise [InvalidConstructorError] if the policy constructor called incorrectly
    # @return [Object] instance of policy class with query methods
    # @since v2.3.2
    def policy!(record)
      cached_find(record, &:policy!)
    end

    # @!endgroup

    # @!group Scopes

    # Retrieves the policy scope for the given record.
    #
    # @see https://github.com/varvet/pundit#scopes
    # @param scope [Object] the object we're retrieving the policy scope for
    # @raise [InvalidConstructorError] if the policy constructor called incorrectly
    # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
    # @since v2.3.2
    def policy_scope(scope)
      policy_scope_class = policy_finder(scope).scope
      return unless policy_scope_class

      begin
        policy_scope = policy_scope_class.new(user, pundit_model(scope))
      rescue ArgumentError
        raise InvalidConstructorError, "Invalid #<#{policy_scope_class}> constructor is called"
      end

      policy_scope.resolve
    end

    # Retrieves the policy scope for the given record. Raises if not found.
    #
    # @see https://github.com/varvet/pundit#scopes
    # @param scope [Object] the object we're retrieving the policy scope for
    # @raise [NotDefinedError] if the policy scope cannot be found
    # @raise [InvalidConstructorError] if the policy constructor called incorrectly
    # @return [Scope{#resolve}] instance of scope class which can resolve to a scope
    # @since v2.3.2
    def policy_scope!(scope)
      policy_scope_class = policy_finder(scope).scope!

      begin
        policy_scope = policy_scope_class.new(user, pundit_model(scope))
      rescue ArgumentError
        raise InvalidConstructorError, "Invalid #<#{policy_scope_class}> constructor is called"
      end

      policy_scope.resolve
    end

    # @!endgroup

    private

    # @!group Private Helpers

    # Finds a cached policy for the given record, or yields to find one.
    #
    # @api private
    # @param record [Object] the object we're retrieving the policy for
    # @yield a policy finder if no policy was cached
    # @yieldparam [PolicyFinder] policy_finder
    # @yieldreturn [#new(user, model)]
    # @return [Policy, nil] an instantiated policy
    # @raise [InvalidConstructorError] if policy can't be instantated
    # @since v2.3.2
    def cached_find(record)
      policy_cache.fetch(user: user, record: record) do
        klass = yield policy_finder(record)
        next unless klass

        model = pundit_model(record)

        begin
          klass.new(user, model)
        rescue ArgumentError
          raise InvalidConstructorError, "Invalid #<#{klass}> constructor is called"
        end
      end
    end

    # Return a policy finder for the given record.
    #
    # @api private
    # @return [PolicyFinder]
    # @since v2.3.2
    def policy_finder(record)
      PolicyFinder.new(record)
    end

    # Given a possibly namespaced record, return the actual record.
    #
    # @api private
    # @since v2.3.2
    def pundit_model(record)
      record.is_a?(Array) ? record.last : record
    end
  end
end


================================================
FILE: lib/pundit/error.rb
================================================
# frozen_string_literal: true

module Pundit
  # @api private
  # @since v1.0.0
  # To avoid name clashes with common Error naming when mixing in Pundit,
  # keep it here with compact class style definition.
  class Error < StandardError; end

  # Error that will be raised when authorization has failed
  # @since v0.1.0
  class NotAuthorizedError < Error
    # @see #initialize
    # @since v0.2.3
    attr_reader :query
    # @see #initialize
    # @since v0.2.3
    attr_reader :record
    # @see #initialize
    # @since v0.2.3
    attr_reader :policy

    # @since v1.0.0
    #
    # @overload initialize(message)
    #   Create an error with a simple error message.
    #   @param [String] message A simple error message string.
    #
    # @overload initialize(options)
    #   Create an error with the specified attributes.
    #   @param [Hash] options The error options.
    #   @option options [String] :message Optional custom error message. Will default to a generalized message.
    #   @option options [Symbol] :query The name of the policy method that was checked.
    #   @option options [Object] :record The object that was being checked with the policy.
    #   @option options [Class] :policy The class of policy that was used for the check.
    def initialize(options = {})
      if options.is_a? String
        message = options
      else
        @query = options[:query]
        @record = options[:record]
        @policy = options[:policy]

        message = options.fetch(:message) do
          record_name = record.is_a?(Class) ? record.to_s : "this #{record.class}"
          "not allowed to #{policy.class}##{query} #{record_name}"
        end
      end

      super(message)
    end
  end

  # Error that will be raised if a policy or policy scope constructor is not called correctly.
  # @since v2.0.0
  class InvalidConstructorError < Error; end

  # Error that will be raised if a controller action has not called the
  # `authorize` or `skip_authorization` methods.
  # @since v0.2.3
  class AuthorizationNotPerformedError < Error; end

  # Error that will be raised if a controller action has not called the
  # `policy_scope` or `skip_policy_scope` methods.
  # @since v0.3.0
  class PolicyScopingNotPerformedError < AuthorizationNotPerformedError; end

  # Error that will be raised if a policy or policy scope is not defined.
  # @since v0.1.0
  class NotDefinedError < Error; end
end


================================================
FILE: lib/pundit/helper.rb
================================================
# frozen_string_literal: true

module Pundit
  # Rails view helpers, to allow a slightly different view-specific
  # implementation of the methods in {Pundit::Authorization}.
  #
  # @api private
  # @since v1.0.0
  module Helper
    # @see Pundit::Authorization#pundit_policy_scope
    # @since v1.0.0
    def policy_scope(scope)
      pundit_policy_scope(scope)
    end
  end
end


================================================
FILE: lib/pundit/policy_finder.rb
================================================
# frozen_string_literal: true

# String#safe_constantize, String#demodulize, String#underscore, String#camelize
require "active_support/core_ext/string/inflections"

module Pundit
  # Finds policy and scope classes for given object.
  # @since v0.1.0
  # @api public
  # @example
  #   user = User.find(params[:id])
  #   finder = PolicyFinder.new(user)
  #   finder.policy #=> UserPolicy
  #   finder.scope #=> UserPolicy::Scope
  #
  class PolicyFinder
    # A constant applied to the end of the class name to find the policy class.
    #
    # @api private
    # @since v2.5.0
    SUFFIX = "Policy"

    # @see #initialize
    # @since v0.1.0
    attr_reader :object

    # @param object [any] the object to find policy and scope classes for
    # @since v0.1.0
    def initialize(object)
      @object = object
    end

    # @return [nil, Scope{#resolve}] scope class which can resolve to a scope
    # @see https://github.com/varvet/pundit#scopes
    # @example
    #   scope = finder.scope #=> UserPolicy::Scope
    #   scope.resolve #=> <#ActiveRecord::Relation ...>
    #
    # @since v0.1.0
    def scope
      "#{policy}::Scope".safe_constantize
    end

    # @return [nil, Class] policy class with query methods
    # @see https://github.com/varvet/pundit#policies
    # @example
    #   policy = finder.policy #=> UserPolicy
    #   policy.show? #=> true
    #   policy.update? #=> false
    #
    # @since v0.1.0
    def policy
      klass = find(object)
      klass.is_a?(String) ? klass.safe_constantize : klass
    end

    # @return [Scope{#resolve}] scope class which can resolve to a scope
    # @raise [NotDefinedError] if scope could not be determined
    #
    # @since v0.1.0
    def scope!
      scope or raise NotDefinedError, "unable to find scope `#{find(object)}::Scope` for `#{object.inspect}`"
    end

    # @return [Class] policy class with query methods
    # @raise [NotDefinedError] if policy could not be determined
    #
    # @since v0.1.0
    def policy!
      policy or raise NotDefinedError, "unable to find policy `#{find(object)}` for `#{object.inspect}`"
    end

    # @return [String] the name of the key this object would have in a params hash
    #
    # @since v1.1.0
    def param_key # rubocop:disable Metrics/AbcSize
      model = object.is_a?(Array) ? object.last : object

      if model.respond_to?(:model_name)
        model.model_name.param_key.to_s
      elsif model.is_a?(Class)
        model.to_s.demodulize.underscore
      else
        model.class.to_s.demodulize.underscore
      end
    end

    private

    # Given an object, find the policy class name.
    #
    # Uses recursion to handle namespaces.
    #
    # @return [String, Class] the policy class, or its name.
    # @since v0.2.0
    def find(subject)
      if subject.is_a?(Array)
        modules = subject.dup
        last = modules.pop
        context = modules.map { |x| find_class_name(x) }.join("::")
        [context, find(last)].join("::")
      elsif subject.respond_to?(:policy_class)
        subject.policy_class
      elsif subject.class.respond_to?(:policy_class)
        subject.class.policy_class
      else
        klass = find_class_name(subject)
        "#{klass}#{SUFFIX}"
      end
    end

    # Given an object, find its' class name.
    #
    # - Supports ActiveModel.
    # - Supports regular classes.
    # - Supports symbols.
    # - Supports object instances.
    #
    # @return [String, Class] the class, or its name.
    # @since v1.1.0
    def find_class_name(subject)
      if subject.respond_to?(:model_name)
        subject.model_name
      elsif subject.class.respond_to?(:model_name)
        subject.class.model_name
      elsif subject.is_a?(Class)
        subject
      elsif subject.is_a?(Symbol)
        subject.to_s.camelize
      else
        subject.class
      end
    end
  end
end


================================================
FILE: lib/pundit/railtie.rb
================================================
# frozen_string_literal: true

module Pundit
  # @since v2.5.0
  class Railtie < Rails::Railtie
    if Rails.version.to_f >= 8.0
      initializer "pundit.stats_directories" do
        require "rails/code_statistics"

        if Rails.root.join("app/policies").directory?
          Rails::CodeStatistics.register_directory("Policies", "app/policies")
        end

        if Rails.root.join("test/policies").directory?
          Rails::CodeStatistics.register_directory("Policy tests", "test/policies", test_directory: true)
        end
      end
    end
  end
end


================================================
FILE: lib/pundit/rspec.rb
================================================
# frozen_string_literal: true

require "pundit"
# Array#to_sentence
require "active_support/core_ext/array/conversions"

module Pundit
  # Namespace for Pundit's RSpec integration.
  # @since v0.1.0
  module RSpec
    # Namespace for Pundit's RSpec matchers.
    module Matchers
      extend ::RSpec::Matchers::DSL

      # @!method description=(description)
      class << self
        # Used to build a suitable description for the Pundit `permit` matcher.
        # @api public
        # @param value [String, Proc]
        # @example
        #   Pundit::RSpec::Matchers.description = ->(user, record) do
        #     "permit user with role #{user.role} to access record with ID #{record.id}"
        #   end
        attr_writer :description

        # Used to retrieve a suitable description for the Pundit `permit` matcher.
        # @api private
        # @private
        def description(user, record)
          return @description.call(user, record) if defined?(@description) && @description.respond_to?(:call)

          @description
        end
      end

      # rubocop:disable Metrics/BlockLength
      matcher :permit do |user, record|
        match_proc = lambda do |policy|
          @violating_permissions = permissions.find_all do |permission|
            !policy.new(user, record).public_send(permission)
          end
          @violating_permissions.empty?
        end

        match_when_negated_proc = lambda do |policy|
          @violating_permissions = permissions.find_all do |permission|
            policy.new(user, record).public_send(permission)
          end
          @violating_permissions.empty?
        end

        failure_message_proc = lambda do |policy|
          "Expected #{policy} to grant #{permissions.to_sentence} on " \
          "#{record} but #{@violating_permissions.to_sentence} #{was_or_were} not granted"
        end

        failure_message_when_negated_proc = lambda do |policy|
          "Expected #{policy} not to grant #{permissions.to_sentence} on " \
          "#{record} but #{@violating_permissions.to_sentence} #{was_or_were} granted"
        end

        def was_or_were
          if @violating_permissions.count > 1
            "were"
          else
            "was"
          end
        end

        description do
          Pundit::RSpec::Matchers.description(user, record) || super()
        end

        if respond_to?(:match_when_negated)
          match(&match_proc)
          match_when_negated(&match_when_negated_proc)
          failure_message(&failure_message_proc)
          failure_message_when_negated(&failure_message_when_negated_proc)
        else
          # :nocov:
          # Compatibility with RSpec < 3.0, released 2014-06-01.
          match_for_should(&match_proc)
          match_for_should_not(&match_when_negated_proc)
          failure_message_for_should(&failure_message_proc)
          failure_message_for_should_not(&failure_message_when_negated_proc)
          # :nocov:
        end

        if ::RSpec.respond_to?(:current_example)
          def current_example
            ::RSpec.current_example
          end
        else
          # :nocov:
          # Compatibility with RSpec < 3.0, released 2014-06-01.
          def current_example
            example
          end
          # :nocov:
        end

        def permissions
          current_example.metadata.fetch(:permissions) do
            raise KeyError, <<~ERROR.strip
              No permissions in example metadata, did you forget to wrap with `permissions :show?, ...`?
            ERROR
          end
        end
      end
      # rubocop:enable Metrics/BlockLength
    end

    # Mixed in to all policy example groups to provide a DSL.
    module DSL
      # @example
      #   describe PostPolicy do
      #     permissions :show?, :update? do
      #       it { is_expected.to permit(user, own_post) }
      #     end
      #   end
      #
      # @example focused example group
      #   describe PostPolicy do
      #     permissions :show?, :update?, :focus do
      #       it { is_expected.to permit(user, own_post) }
      #     end
      #   end
      #
      # @param list [Symbol, Array<Symbol>] a permission to describe
      # @return [void]
      def permissions(*list, &block)
        metadata = {permissions: list, caller: caller}

        if list.last == :focus
          list.pop
          metadata[:focus] = true
        end

        description = list.to_sentence
        describe(description, metadata) { instance_eval(&block) }
      end
    end

    # Mixed in to all policy example groups.
    #
    # @private not useful
    module PolicyExampleGroup
      include Pundit::RSpec::Matchers

      def self.included(base)
        base.metadata[:type] = :policy
        base.extend Pundit::RSpec::DSL
        super
      end
    end
  end
end

RSpec.configure do |config|
  config.include(Pundit::RSpec::PolicyExampleGroup, file_path: %r{spec/policies})
  config.include(Pundit::RSpec::PolicyExampleGroup, type: :policy)
end


================================================
FILE: lib/pundit/version.rb
================================================
# frozen_string_literal: true

module Pundit
  # The current version of Pundit.
  VERSION = "2.5.2"
end


================================================
FILE: lib/pundit.rb
================================================
# frozen_string_literal: true

require "active_support"

require "pundit/version"
require "pundit/error"
require "pundit/policy_finder"
require "pundit/context"
require "pundit/authorization"
require "pundit/helper"
require "pundit/cache_store"
require "pundit/cache_store/null_store"
require "pundit/cache_store/legacy_store"

# :nocov:
require "pundit/railtie" if defined?(Rails)
# :nocov:

# Hello? Yes, this is Pundit.
#
# @api public
module Pundit
  # @api private
  # @since v1.0.0
  # @deprecated See {Pundit::PolicyFinder}
  SUFFIX = Pundit::PolicyFinder::SUFFIX

  # @api private
  # @private
  # @since v0.1.0
  module Generators; end

  def self.included(base)
    location = caller_locations(1, 1).first
    warn <<~WARNING
      'include Pundit' is deprecated. Please use 'include Pundit::Authorization' instead.
       (called from #{location.label} at #{location.path}:#{location.lineno})
    WARNING
    base.include Authorization
  end

  class << self
    # @see Pundit::Context#authorize
    # @since v1.0.0
    def authorize(user, record, query, policy_class: nil, cache: nil)
      context = if cache
        policy_cache = CacheStore::LegacyStore.new(cache)
        Context.new(user: user, policy_cache: policy_cache)
      else
        Context.new(user: user)
      end

      context.authorize(record, query: query, policy_class: policy_class)
    end

    # @see Pundit::Context#policy_scope
    # @since v0.1.0
    def policy_scope(user, *args, **kwargs, &block)
      Context.new(user: user).policy_scope(*args, **kwargs, &block)
    end

    # @see Pundit::Context#policy_scope!
    # @since v0.1.0
    def policy_scope!(user, *args, **kwargs, &block)
      Context.new(user: user).policy_scope!(*args, **kwargs, &block)
    end

    # @see Pundit::Context#policy
    # @since v0.1.0
    def policy(user, *args, **kwargs, &block)
      Context.new(user: user).policy(*args, **kwargs, &block)
    end

    # @see Pundit::Context#policy!
    # @since v0.1.0
    def policy!(user, *args, **kwargs, &block)
      Context.new(user: user).policy!(*args, **kwargs, &block)
    end
  end
end


================================================
FILE: pundit.gemspec
================================================
# frozen_string_literal: true

require_relative "lib/pundit/version"

Gem::Specification.new do |gem|
  gem.name = "pundit"
  gem.version = Pundit::VERSION
  gem.authors = ["Jonas Nicklas", "Varvet AB"]
  gem.email = ["jonas.nicklas@gmail.com", "info@varvet.com"]
  gem.description = "Object oriented authorization for Rails applications"
  gem.summary = "OO authorization for Rails"
  gem.homepage = "https://github.com/varvet/pundit"
  gem.license = "MIT"

  Dir.chdir(__dir__) do
    gem.files = `git ls-files -z`.split("\x0").select do |f|
      f.start_with?("lib/", "README", "SECURITY", "LICENSE", "CHANGELOG", "CONTRIBUTING", "config/rubocop-rspec.yml")
    end
  end
  gem.require_paths = ["lib"]

  gem.metadata = {
    "rubygems_mfa_required" => "true",
    "bug_tracker_uri" => "https://github.com/varvet/pundit/issues",
    "changelog_uri" => "https://github.com/varvet/pundit/blob/main/CHANGELOG.md",
    "documentation_uri" => "https://github.com/varvet/pundit/blob/main/README.md",
    "homepage_uri" => "https://github.com/varvet/pundit",
    "source_code_uri" => "https://github.com/varvet/pundit"
  }

  gem.add_dependency "activesupport", ">= 3.0.0"
end


================================================
FILE: spec/authorization_spec.rb
================================================
# frozen_string_literal: true

require "spec_helper"
require "action_controller"

RSpec.describe Pundit::Authorization do
  def to_params(*args, **kwargs, &block)
    ActionController::Parameters.new(*args, **kwargs, &block)
  end

  let(:controller) { Controller.new(user, "update", to_params({})) }
  let(:user) { double("user") }
  let(:post) { Post.new(user) }
  let(:comment) { Comment.new }
  let(:article) { Article.new }
  let(:article_tag) { ArticleTag.new }
  let(:wiki) { Wiki.new }

  describe "#verify_authorized" do
    it "does nothing when authorized" do
      controller.authorize(post)
      controller.verify_authorized
    end

    it "raises an exception when not authorized" do
      expect { controller.verify_authorized }.to raise_error(Pundit::AuthorizationNotPerformedError)
    end
  end

  describe "#verify_policy_scoped" do
    it "does nothing when policy_scope is used" do
      controller.policy_scope(Post)
      controller.verify_policy_scoped
    end

    it "raises an exception when policy_scope is not used" do
      expect { controller.verify_policy_scoped }.to raise_error(Pundit::PolicyScopingNotPerformedError)
    end
  end

  describe "#pundit_policy_authorized?" do
    it "is true when authorized" do
      controller.authorize(post)
      expect(controller.pundit_policy_authorized?).to be true
    end

    it "is false when not authorized" do
      expect(controller.pundit_policy_authorized?).to be false
    end
  end

  describe "#pundit_policy_scoped?" do
    it "is true when policy_scope is used" do
      controller.policy_scope(Post)
      expect(controller.pundit_policy_scoped?).to be true
    end

    it "is false when policy scope is not used" do
      expect(controller.pundit_policy_scoped?).to be false
    end
  end

  describe "#authorize" do
    it "infers the policy name and authorizes based on it" do
      expect(controller.authorize(post)).to be_truthy
    end

    it "returns the record on successful authorization" do
      expect(controller.authorize(post)).to eq(post)
    end

    it "returns the record when passed record with namespace " do
      expect(controller.authorize([:project, comment], :update?)).to eq(comment)
    end

    it "returns the record when passed record with nested namespace " do
      expect(controller.authorize([:project, :admin, comment], :update?)).to eq(comment)
    end

    it "returns the policy name symbol when passed record with headless policy" do
      expect(controller.authorize(:publication, :create?)).to eq(:publication)
    end

    it "returns the class when passed record not a particular instance" do
      expect(controller.authorize(Post, :show?)).to eq(Post)
    end

    it "can be given a different permission to check" do
      expect(controller.authorize(post, :show?)).to be_truthy
      expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
    end

    it "can be given a different policy class" do
      expect(controller.authorize(post, :create?, policy_class: PublicationPolicy)).to be_truthy
    end

    it "works with anonymous class policies" do
      expect(controller.authorize(article_tag, :show?)).to be_truthy
      expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
    end

    it "throws an exception when the permission check fails" do
      expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
    end

    it "throws an exception when a policy cannot be found" do
      expect { controller.authorize(Article) }.to raise_error(Pundit::NotDefinedError)
    end

    it "caches the policy" do
      expect(controller.policies[post]).to be_nil
      controller.authorize(post)
      expect(controller.policies[post]).not_to be_nil
    end

    it "raises an error when the given record is nil" do
      expect { controller.authorize(nil, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
    end

    it "raises an error with a invalid policy constructor" do
      expect { controller.authorize(wiki, :destroy?) }.to raise_error(Pundit::InvalidConstructorError)
    end
  end

  describe "#skip_authorization" do
    it "disables authorization verification" do
      controller.skip_authorization
      expect { controller.verify_authorized }.not_to raise_error
    end
  end

  describe "#skip_policy_scope" do
    it "disables policy scope verification" do
      controller.skip_policy_scope
      expect { controller.verify_policy_scoped }.not_to raise_error
    end
  end

  describe "#pundit_user" do
    it "returns the same thing as current_user" do
      expect(controller.pundit_user).to eq controller.current_user
    end
  end

  describe "#policy" do
    it "returns an instantiated policy" do
      policy = controller.policy(post)
      expect(policy.user).to eq user
      expect(policy.post).to eq post
    end

    it "throws an exception if the given policy can't be found" do
      expect { controller.policy(article) }.to raise_error(Pundit::NotDefinedError)
    end

    it "raises an error with a invalid policy constructor" do
      expect { controller.policy(wiki) }.to raise_error(Pundit::InvalidConstructorError)
    end

    it "allows policy to be injected" do
      new_policy = double
      controller.policies[post] = new_policy

      expect(controller.policy(post)).to eq new_policy
    end
  end

  describe "#policy_scope" do
    it "returns an instantiated policy scope" do
      expect(controller.policy_scope(Post)).to eq :published
    end

    it "allows policy scope class to be overridden" do
      expect(controller.policy_scope(Post, policy_scope_class: PublicationPolicy::Scope)).to eq :published
    end

    it "throws an exception if the given policy can't be found" do
      expect { controller.policy_scope(Article) }.to raise_error(Pundit::NotDefinedError)
    end

    it "raises an error with a invalid policy scope constructor" do
      expect { controller.policy_scope(Wiki) }.to raise_error(Pundit::InvalidConstructorError)
    end

    it "allows policy_scope to be injected" do
      new_scope = double
      controller.policy_scopes[Post] = new_scope

      expect(controller.policy_scope(Post)).to eq new_scope
    end
  end

  describe "#permitted_attributes" do
    it "checks policy for permitted attributes" do
      params = to_params(
        post: {
          title: "Hello",
          votes: 5,
          admin: true
        }
      )

      action = "update"

      expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq(
        "title" => "Hello",
        "votes" => 5
      )
      expect(Controller.new(double, action, params).permitted_attributes(post).to_h).to eq("votes" => 5)
    end

    it "checks policy for permitted attributes for record of a ActiveModel type" do
      customer_post = Customer::Post.new(user)
      params = to_params(
        customer_post: {
          title: "Hello",
          votes: 5,
          admin: true
        }
      )

      action = "update"

      expect(Controller.new(user, action, params).permitted_attributes(customer_post).to_h).to eq(
        "title" => "Hello",
        "votes" => 5
      )
      expect(Controller.new(double, action, params).permitted_attributes(customer_post).to_h).to eq(
        "votes" => 5
      )
    end

    it "goes through the policy cache" do
      params = to_params(post: {title: "Hello"})
      user = double
      post = Post.new(user)
      controller = Controller.new(user, "update", params)

      expect do
        expect(controller.permitted_attributes(post)).to be_truthy
        expect(controller.permitted_attributes(post)).to be_truthy
      end.to change { PostPolicy.instances }.by(1)
    end
  end

  describe "#permitted_attributes_for_action" do
    it "is checked if it is defined in the policy" do
      params = to_params(
        post: {
          title: "Hello",
          body: "blah",
          votes: 5,
          admin: true
        }
      )

      action = "revise"
      expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq("body" => "blah")
    end

    it "can be explicitly set" do
      params = to_params(
        post: {
          title: "Hello",
          body: "blah",
          votes: 5,
          admin: true
        }
      )

      action = "update"
      expect(Controller.new(user, action, params).permitted_attributes(post, :revise).to_h).to eq("body" => "blah")
    end
  end

  if ActionController::Parameters.method_defined?(:expect)
    describe "#expected_attributes" do
      it "checks policy for expected attributes" do
        params = to_params(
          post: {
            title: "Hello",
            votes: 5,
            admin: true
          }
        )

        action = "update"

        expect(Controller.new(user, action, params).expected_attributes(post).to_h).to eq(
          "title" => "Hello",
          "votes" => 5
        )
        expect(Controller.new(double, action, params).expected_attributes(post).to_h).to eq("votes" => 5)
      end

      it "checks policy for expected attributes for record of a ActiveModel type" do
        customer_post = Customer::Post.new(user)
        params = to_params(
          customer_post: {
            title: "Hello",
            votes: 5,
            admin: true
          }
        )

        action = "update"

        expect(Controller.new(user, action, params).expected_attributes(customer_post).to_h).to eq(
          "title" => "Hello",
          "votes" => 5
        )
        expect(Controller.new(double, action, params).expected_attributes(customer_post).to_h).to eq(
          "votes" => 5
        )
      end

      it "goes through the policy cache" do
        params = to_params(post: {title: "Hello"})
        user = double
        post = Post.new(user)
        controller = Controller.new(user, "update", params)

        expect do
          expect(controller.expected_attributes(post)).to be_truthy
          expect(controller.expected_attributes(post)).to be_truthy
        end.to change { PostPolicy.instances }.by(1)
      end
    end

    context "action-specific expected attributes" do
      it "is checked if it is defined in the policy" do
        params = to_params(
          post: {
            title: "Hello",
            body: "blah",
            votes: 5,
            admin: true
          }
        )

        action = "revise"
        expect(Controller.new(user, action, params).expected_attributes(post).to_h).to eq("body" => "blah")
      end

      it "can be explicitly set" do
        params = to_params(
          post: {
            title: "Hello",
            body: "blah",
            votes: 5,
            admin: true
          }
        )

        action = "update"
        controller = Controller.new(user, action, params)
        expect(controller.expected_attributes(post, action: :revise).to_h).to eq("body" => "blah")
      end
    end

    it "can be retrieved with an explicit param key" do
      params = to_params(admin_post: {title: "Hello"})

      action = "update"
      controller = Controller.new(user, action, params)
      expect(controller.expected_attributes(post, param_key: "admin_post").to_h).to eq("title" => "Hello")
    end
  end

  describe "#pundit_reset!" do
    it "allows authorize to react to a user change" do
      expect(controller.authorize(post)).to be_truthy

      controller.current_user = double
      controller.pundit_reset!
      expect { controller.authorize(post) }.to raise_error(Pundit::NotAuthorizedError)
    end

    it "allows policy to react to a user change" do
      expect(controller.policy(DummyCurrentUser).user).to be user

      new_user = double("new user")
      controller.current_user = new_user
      controller.pundit_reset!
      expect(controller.policy(DummyCurrentUser).user).to be new_user
    end

    it "allows policy scope to react to a user change" do
      expect(controller.policy_scope(DummyCurrentUser)).to be user

      new_user = double("new user")
      controller.current_user = new_user
      controller.pundit_reset!
      expect(controller.policy_scope(DummyCurrentUser)).to be new_user
    end

    it "resets the pundit context" do
      expect(controller.pundit.user).to be(user)

      new_user = double
      controller.current_user = new_user
      expect { controller.pundit_reset! }.to change { controller.pundit.user }.from(user).to(new_user)
    end

    it "clears pundit_policy_authorized? flag" do
      expect(controller.pundit_policy_authorized?).to be false

      controller.skip_authorization
      expect(controller.pundit_policy_authorized?).to be true

      controller.pundit_reset!
      expect(controller.pundit_policy_authorized?).to be false
    end

    it "clears pundit_policy_scoped? flag" do
      expect(controller.pundit_policy_scoped?).to be false

      controller.skip_policy_scope
      expect(controller.pundit_policy_scoped?).to be true

      controller.pundit_reset!
      expect(controller.pundit_policy_scoped?).to be false
    end
  end
end


================================================
FILE: spec/generators_spec.rb
================================================
# frozen_string_literal: true

require "spec_helper"
require "tmpdir"

require "rails/generators"
require "generators/pundit/install/install_generator"
require "generators/pundit/policy/policy_generator"

RSpec.describe "generators" do
  before(:all) do
    @tmpdir = Dir.mktmpdir

    Dir.chdir(@tmpdir) do
      Pundit::Generators::InstallGenerator.new([], {quiet: true}).invoke_all
      Pundit::Generators::PolicyGenerator.new(%w[Widget], {quiet: true}).invoke_all

      require "./app/policies/application_policy"
      require "./app/policies/widget_policy"
    end
  end

  after(:all) do
    FileUtils.remove_entry(@tmpdir)
  end

  describe "WidgetPolicy", type: :policy do
    permissions :index?, :show?, :create?, :new?, :update?, :edit?, :destroy? do
      it "has safe defaults" do
        expect(WidgetPolicy).not_to permit(double("User"), double("Widget"))
      end
    end

    describe "WidgetPolicy::Scope" do
      describe "#resolve" do
        it "raises a descriptive error" do
          scope = WidgetPolicy::Scope.new(double("User"), double("User.all"))
          expect { scope.resolve }.to raise_error(NoMethodError, /WidgetPolicy::Scope/)
        end
      end
    end
  end
end


================================================
FILE: spec/policies/post_policy_spec.rb
================================================
# frozen_string_literal: true

require "spec_helper"

RSpec.describe PostPolicy do
  let(:user) { double }
  let(:own_post) { double(user: user) }
  let(:other_post) { double(user: double) }
  subject { described_class }

  permissions :update?, :show? do
    it "is successful when all permissions match" do
      is_expected.to permit(user, own_post)
    end

    it "fails when any permissions do not match" do
      expect do
        is_expected.to permit(user, other_post)
      end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
    end

    it "uses the default description if not overridden" do
      expect(permit(user, own_post).description).to eq("permit #{user.inspect} and #{own_post.inspect}")
    end

    context "when the matcher description is overridden" do
      after do
        Pundit::RSpec::Matchers.description = nil
      end

      it "sets a custom matcher description with a Proc" do
        allow(user).to receive(:role).and_return("default_role")
        allow(own_post).to receive(:id).and_return(1)

        Pundit::RSpec::Matchers.description = lambda { |user, record|
          "permit user with role #{user.role} to access record with ID #{record.id}"
        }

        description = permit(user, own_post).description
        expect(description).to eq("permit user with role default_role to access record with ID 1")
      end

      it "sets a custom matcher description with a string" do
        Pundit::RSpec::Matchers.description = "permit user"
        expect(permit(user, own_post).description).to eq("permit user")
      end
    end
  end
end


================================================
FILE: spec/policy_finder_spec.rb
================================================
# frozen_string_literal: true

require "spec_helper"

RSpec.describe Pundit::PolicyFinder do
  let(:user) { double }
  let(:post) { Post.new(user) }
  let(:comment) { CommentFourFiveSix.new }
  let(:article) { Article.new }

  describe "SUFFIX" do
    specify { expect(described_class::SUFFIX).to eq "Policy" }
    specify { expect(Pundit::SUFFIX).to eq(described_class::SUFFIX) }
  end

  describe "#scope" do
    subject { described_class.new(post) }

    it "returns a policy scope" do
      expect(subject.scope).to eq PostPolicy::Scope
    end

    context "policy is nil" do
      it "returns nil" do
        allow(subject).to receive(:policy).and_return nil
        expect(subject.scope).to eq nil
      end
    end
  end

  describe "#policy" do
    context "with an instance" do
      it "returns the associated policy" do
        object = described_class.new(post)

        expect(object.policy).to eq PostPolicy
      end
    end

    context "with an array of symbols" do
      it "returns the associated namespaced policy" do
        object = described_class.new(%i[project post])

        expect(object.policy).to eq Project::PostPolicy
      end
    end

    context "with an array of a symbol and an instance" do
      it "returns the associated namespaced policy" do
        object = described_class.new([:project, post])

        expect(object.policy).to eq Project::PostPolicy
      end
    end

    context "with an array of a symbol and a class with a specified policy class" do
      it "returns the associated namespaced policy" do
        object = described_class.new([:project, Customer::Post])

        expect(object.policy).to eq Project::PostPolicy
      end
    end

    context "with an array of a symbol and a class with a specified model name" do
      it "returns the associated namespaced policy" do
        object = described_class.new([:project, CommentsRelation])

        expect(object.policy).to eq Project::CommentPolicy
      end
    end

    context "with a class" do
      it "returns the associated policy" do
        object = described_class.new(Post)

        expect(object.policy).to eq PostPolicy
      end
    end

    context "with a class which has a specified policy class" do
      it "returns the associated policy" do
        object = described_class.new(Customer::Post)

        expect(object.policy).to eq PostPolicy
      end
    end

    context "with an instance which has a specified policy class" do
      it "returns the associated policy" do
        object = described_class.new(Customer::Post.new(user))

        expect(object.policy).to eq PostPolicy
      end
    end

    context "with a class which has a specified model name" do
      it "returns the associated policy" do
        object = described_class.new(CommentsRelation)

        expect(object.policy).to eq CommentPolicy
      end
    end

    context "with an instance which has a specified policy class" do
      it "returns the associated policy" do
        object = described_class.new(CommentsRelation.new)

        expect(object.policy).to eq CommentPolicy
      end
    end

    context "with nil" do
      it "returns a NilClassPolicy" do
        object = described_class.new(nil)

        expect(object.policy).to eq NilClassPolicy
      end
    end

    context "with a class that doesn't have an associated policy" do
      it "returns nil" do
        object = described_class.new(Foo)

        expect(object.policy).to eq nil
      end
    end
  end

  describe "#scope!" do
    context "@object is nil" do
      subject { described_class.new(nil) }

      it "returns the NilClass policy's scope class" do
        expect(subject.scope!).to eq NilClassPolicy::Scope
      end
    end

    context "@object is defined" do
      subject { described_class.new(post) }

      it "returns the scope" do
        expect(subject.scope!).to eq PostPolicy::Scope
      end
    end
  end

  describe "#param_key" do
    context "object responds to model_name" do
      subject { described_class.new(comment) }

      it "returns the param_key" do
        expect(subject.object).to respond_to(:model_name)
        expect(subject.param_key).to eq "comment_four_five_six"
      end
    end

    context "object is a class" do
      subject { described_class.new(Article) }

      it "returns the param_key" do
        expect(subject.object).not_to respond_to(:model_name)
        expect(subject.object).to be_a Class
        expect(subject.param_key).to eq "article"
      end
    end

    context "object is an instance of a class" do
      subject { described_class.new(article) }

      it "returns the param_key" do
        expect(subject.object).not_to respond_to(:model_name)
        expect(subject.object).not_to be_a Class
        expect(subject.object).to be_an_instance_of Article

        expect(subject.param_key).to eq "article"
      end
    end

    context "object is an array" do
      subject { described_class.new([:project, article]) }

      it "returns the param_key for the last element of the array" do
        expect(subject.object).not_to respond_to(:model_name)
        expect(subject.object).not_to be_a Class
        expect(subject.object).to be_an_instance_of Array

        expect(subject.param_key).to eq "article"
      end
    end
  end
end


================================================
FILE: spec/pundit/helper_spec.rb
================================================
# frozen_string_literal: true

require "spec_helper"

RSpec.describe Pundit::Helper do
  let(:user) { double }
  let(:controller) { Controller.new(user, "update", double) }
  let(:view) { Controller::View.new(controller) }

  describe "#policy_scope" do
    it "doesn't flip pundit_policy_scoped?" do
      scoped = view.policy_scope(Post)

      expect(scoped).to be(Post.published)
      expect(controller).not_to be_pundit_policy_scoped
    end
  end
end


================================================
FILE: spec/pundit_spec.rb
================================================
# frozen_string_literal: true

require "spec_helper"

RSpec.describe Pundit do
  let(:user) { double }
  let(:post) { Post.new(user) }
  let(:customer_post) { Customer::Post.new(user) }
  let(:post_four_five_six) { PostFourFiveSix.new(user) }
  let(:comment) { Comment.new }
  let(:comment_four_five_six) { CommentFourFiveSix.new }
  let(:article) { Article.new }
  let(:artificial_blog) { ArtificialBlog.new }
  let(:article_tag) { ArticleTag.new }
  let(:comments_relation) { CommentsRelation.new(empty: false) }
  let(:empty_comments_relation) { CommentsRelation.new(empty: true) }
  let(:tag_four_five_six) { ProjectOneTwoThree::TagFourFiveSix.new(user) }
  let(:avatar_four_five_six) { ProjectOneTwoThree::AvatarFourFiveSix.new }
  let(:wiki) { Wiki.new }

  describe ".authorize" do
    it "infers the policy and authorizes based on it" do
      expect(Pundit.authorize(user, post, :update?)).to be_truthy
    end

    it "returns the record on successful authorization" do
      expect(Pundit.authorize(user, post, :update?)).to eq(post)
    end

    it "returns the record when passed record with namespace " do
      expect(Pundit.authorize(user, [:project, comment], :update?)).to eq(comment)
    end

    it "returns the record when passed record with nested namespace " do
      expect(Pundit.authorize(user, [:project, :admin, comment], :update?)).to eq(comment)
    end

    it "returns the policy name symbol when passed record with headless policy" do
      expect(Pundit.authorize(user, :publication, :create?)).to eq(:publication)
    end

    it "returns the class when passed record not a particular instance" do
      expect(Pundit.authorize(user, Post, :show?)).to eq(Post)
    end

    it "works with anonymous class policies" do
      expect(Pundit.authorize(user, article_tag, :show?)).to be_truthy
      expect { Pundit.authorize(user, article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
    end

    it "raises an error with the policy, query and record" do
      # rubocop:disable Style/MultilineBlockChain
      expect do
        Pundit.authorize(user, post, :destroy?)
      end.to raise_error(Pundit::NotAuthorizedError, "not allowed to PostPolicy#destroy? this Post") do |error|
        expect(error.query).to eq :destroy?
        expect(error.record).to eq post
        expect(error.policy).to have_attributes(
          user: user,
          record: post
        )
        expect(error.policy).to be_a(PostPolicy)
      end
      # rubocop:enable Style/MultilineBlockChain
    end

    it "raises an error with the policy, query and record when the record is namespaced" do
      # rubocop:disable Style/MultilineBlockChain
      expect do
        Pundit.authorize(user, [:project, :admin, comment], :destroy?)
      end.to raise_error(Pundit::NotAuthorizedError,
        "not allowed to Project::Admin::CommentPolicy#destroy? this Comment") do |error|
        expect(error.query).to eq :destroy?
        expect(error.record).to eq comment
        expect(error.policy).to have_attributes(
          user: user,
          record: comment
        )
        expect(error.policy).to be_a(Project::Admin::CommentPolicy)
      end
      # rubocop:enable Style/MultilineBlockChain
    end

    it "raises an error with the policy, query and the class name when a Class is given" do
      # rubocop:disable Style/MultilineBlockChain
      expect do
        Pundit.authorize(user, Post, :destroy?)
      end.to raise_error(Pundit::NotAuthorizedError, "not allowed to PostPolicy#destroy? Post") do |error|
        expect(error.query).to eq :destroy?
        expect(error.record).to eq Post
        expect(error.policy).to have_attributes(
          user: user,
          record: Post
        )
        expect(error.policy).to be_a(PostPolicy)
      end
      # rubocop:enable Style/MultilineBlockChain
    end

    it "raises an error with a invalid policy constructor" do
      expect do
        Pundit.authorize(user, wiki, :update?)
      end.to raise_error(Pundit::InvalidConstructorError, "Invalid #<WikiPolicy> constructor is called")
    end

    context "when passed a policy class" do
      it "uses the passed policy class" do
        expect(Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy)).to be_truthy
      end

      # This is documenting past behaviour.
      it "doesn't cache the policy class" do
        cache = {}

        expect do
          Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy, cache: cache)
          Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy, cache: cache)
        end.to change { PublicationPolicy.instances }.by(2)
      end
    end

    context "when passed a policy class while simultaenously passing a namespace" do
      it "uses the passed policy class" do
        expect(PublicationPolicy).to receive(:new).with(user, comment).and_call_original
        expect(Pundit.authorize(user, [:project, comment], :create?, policy_class: PublicationPolicy)).to be_truthy
      end
    end

    context "when passed an explicit cache" do
      it "uses the hash assignment interface on the cache" do
        custom_cache = CustomCache.new

        Pundit.authorize(user, post, :update?, cache: custom_cache)

        expect(custom_cache.to_h).to match({
          post => kind_of(PostPolicy)
        })
      end
    end
  end

  describe ".policy_scope" do
    it "returns an instantiated policy scope given a plain model class" do
      expect(Pundit.policy_scope(user, Post)).to eq :published
    end

    it "returns an instantiated policy scope given an active model class" do
      expect(Pundit.policy_scope(user, Comment)).to eq CommentScope.new(Comment)
    end

    it "returns an instantiated policy scope given an active record relation" do
      expect(Pundit.policy_scope(user, comments_relation)).to eq CommentScope.new(comments_relation)
    end

    it "returns an instantiated policy scope given an empty active record relation" do
      expect(Pundit.policy_scope(user, empty_comments_relation)).to eq CommentScope.new(empty_comments_relation)
    end

    it "returns an instantiated policy scope given an array of a symbol and plain model class" do
      expect(Pundit.policy_scope(user, [:project, Post])).to eq :read
    end

    it "returns an instantiated policy scope given an array of a symbol and active model class" do
      expect(Pundit.policy_scope(user, [:project, Comment])).to eq Comment
    end

    it "returns nil if the given policy scope can't be found" do
      expect(Pundit.policy_scope(user, Article)).to be_nil
    end

    it "raises an exception if nil object given" do
      expect { Pundit.policy_scope(user, nil) }.to raise_error(Pundit::NotDefinedError)
    end

    it "raises an error with a invalid policy scope constructor" do
      expect do
        Pundit.policy_scope(user, Wiki)
      end.to raise_error(Pundit::InvalidConstructorError, "Invalid #<WikiPolicy::Scope> constructor is called")
    end

    it "raises an original error with a policy scope that contains error" do
      expect do
        Pundit.policy_scope(user, DefaultScopeContainsError)
      end.to raise_error(RuntimeError, "This is an arbitrary error that should bubble up")
    end
  end

  describe ".policy_scope!" do
    it "returns an instantiated policy scope given a plain model class" do
      expect(Pundit.policy_scope!(user, Post)).to eq :published
    end

    it "returns an instantiated policy scope given an active model class" do
      expect(Pundit.policy_scope!(user, Comment)).to eq CommentScope.new(Comment)
    end

    it "throws an exception if the given policy scope can't be found" do
      expect { Pundit.policy_scope!(user, Article) }.to raise_error(Pundit::NotDefinedError)
    end

    it "throws an exception if the given policy scope can't be found" do
      expect { Pundit.policy_scope!(user, ArticleTag) }.to raise_error(Pundit::NotDefinedError)
    end

    it "throws an exception if the given policy scope is nil" do
      expect do
        Pundit.policy_scope!(user, nil)
      end.to raise_error(Pundit::NotDefinedError, "Cannot scope NilClass")
    end

    it "returns an instantiated policy scope given an array of a symbol and plain model class" do
      expect(Pundit.policy_scope!(user, [:project, Post])).to eq :read
    end

    it "returns an instantiated policy scope given an array of a symbol and active model class" do
      expect(Pundit.policy_scope!(user, [:project, Comment])).to eq Comment
    end

    it "raises an error with a invalid policy scope constructor" do
      expect do
        Pundit.policy_scope(user, Wiki)
      end.to raise_error(Pundit::InvalidConstructorError, "Invalid #<WikiPolicy::Scope> constructor is called")
    end
  end

  describe ".policy" do
    it "returns an instantiated policy given a plain model instance" do
      policy = Pundit.policy(user, post)
      expect(policy.user).to eq user
      expect(policy.post).to eq post
    end

    it "returns an instantiated policy given an active model instance" do
      policy = Pundit.policy(user, comment)
      expect(policy.user).to eq user
      expect(policy.comment).to eq comment
    end

    it "returns an instantiated policy given a plain model class" do
      policy = Pundit.policy(user, Post)
      expect(policy.user).to eq user
      expect(policy.post).to eq Post
    end

    it "returns an instantiated policy given an active model class" do
      policy = Pundit.policy(user, Comment)
      expect(policy.user).to eq user
      expect(policy.comment).to eq Comment
    end

    it "returns an instantiated policy given a symbol" do
      policy = Pundit.policy(user, :criteria)
      expect(policy.class).to eq CriteriaPolicy
      expect(policy.user).to eq user
      expect(policy.criteria).to eq :criteria
    end

    it "returns an instantiated policy given an array of symbols" do
      policy = Pundit.policy(user, %i[project criteria])
      expect(policy.class).to eq Project::CriteriaPolicy
      expect(policy.user).to eq user
      expect(policy.criteria).to eq :criteria
    end

    it "returns an instantiated policy given an array of a symbol and plain model instance" do
      policy = Pundit.policy(user, [:project, post])
      expect(policy.class).to eq Project::PostPolicy
      expect(policy.user).to eq user
      expect(policy.post).to eq post
    end

    it "returns an instantiated policy given an array of a symbol and a model instance with policy_class override" do
      policy = Pundit.policy(user, [:project, customer_post])
      expect(policy.class).to eq Project::PostPolicy
      expect(policy.user).to eq user
      expect(policy.post).to eq customer_post
    end

    it "returns an instantiated policy given an array of a symbol and an active model instance" do
      policy = Pundit.policy(user, [:project, comment])
      expect(policy.class).to eq Project::CommentPolicy
      expect(policy.user).to eq user
      expect(policy.comment).to eq comment
    end

    it "returns an instantiated policy given an array of a symbol and a plain model class" do
      policy = Pundit.policy(user, [:project, Post])
      expect(policy.class).to eq Project::PostPolicy
      expect(policy.user).to eq user
      expect(policy.post).to eq Post
    end

    it "raises an error with a invalid policy constructor" do
      expect do
        Pundit.policy(user, Wiki)
      end.to raise_error(Pundit::InvalidConstructorError, "Invalid #<WikiPolicy> constructor is called")
    end

    it "returns an instantiated policy given an array of a symbol and an active model class" do
      policy = Pundit.policy(user, [:project, Comment])
      expect(policy.class).to eq Project::CommentPolicy
      expect(policy.user).to eq user
      expect(policy.comment).to eq Comment
    end

    it "returns an instantiated policy given an array of a symbol and a class with policy_class override" do
      policy = Pundit.policy(user, [:project, Customer::Post])
      expect(policy.class).to eq Project::PostPolicy
      expect(policy.user).to eq user
      expect(policy.post).to eq Customer::Post
    end

    it "returns correct policy class for an array of a multi-word symbols" do
      policy = Pundit.policy(user, %i[project_one_two_three criteria_four_five_six])
      expect(policy.class).to eq ProjectOneTwoThree::CriteriaFourFiveSixPolicy
    end

    it "returns correct policy class for an array of a multi-word symbol and a multi-word plain model instance" do
      policy = Pundit.policy(user, [:project_one_two_three, post_four_five_six])
      expect(policy.class).to eq ProjectOneTwoThree::PostFourFiveSixPolicy
    end

    it "returns correct policy class for an array of a multi-word symbol and a multi-word active model instance" do
      policy = Pundit.policy(user, [:project_one_two_three, comment_four_five_six])
      expect(policy.class).to eq ProjectOneTwoThree::CommentFourFiveSixPolicy
    end

    it "returns correct policy class for an array of a multi-word symbol and a multi-word plain model class" do
      policy = Pundit.policy(user, [:project_one_two_three, PostFourFiveSix])
      expect(policy.class).to eq ProjectOneTwoThree::PostFourFiveSixPolicy
    end

    it "returns correct policy class for an array of a multi-word symbol and a multi-word active model class" do
      policy = Pundit.policy(user, [:project_one_two_three, CommentFourFiveSix])
      expect(policy.class).to eq ProjectOneTwoThree::CommentFourFiveSixPolicy
    end

    it "returns correct policy class for a multi-word scoped plain model class" do
      policy = Pundit.policy(user, ProjectOneTwoThree::TagFourFiveSix)
      expect(policy.class).to eq ProjectOneTwoThree::TagFourFiveSixPolicy
    end

    it "returns correct policy class for a multi-word scoped plain model instance" do
      policy = Pundit.policy(user, tag_four_five_six)
      expect(policy.class).to eq ProjectOneTwoThree::TagFourFiveSixPolicy
    end

    it "returns correct policy class for a multi-word scoped active model class" do
      policy = Pundit.policy(user, ProjectOneTwoThree::AvatarFourFiveSix)
      expect(policy.class).to eq ProjectOneTwoThree::AvatarFourFiveSixPolicy
    end

    it "returns correct policy class for a multi-word scoped active model instance" do
      policy = Pundit.policy(user, avatar_four_five_six)
      expect(policy.class).to eq ProjectOneTwoThree::AvatarFourFiveSixPolicy
    end

    it "returns nil if the given policy can't be found" do
      expect(Pundit.policy(user, article)).to be_nil
      expect(Pundit.policy(user, Article)).to be_nil
    end

    it "returns the specified NilClassPolicy for nil" do
      expect(Pundit.policy(user, nil)).to be_a NilClassPolicy
    end

    describe "with .policy_class set on the model" do
      it "returns an instantiated policy given a plain model instance" do
        policy = Pundit.policy(user, artificial_blog)
        expect(policy.user).to eq user
        expect(policy.blog).to eq artificial_blog
      end

      it "returns an instantiated policy given a plain model class" do
        policy = Pundit.policy(user, ArtificialBlog)
        expect(policy.user).to eq user
        expect(policy.blog).to eq ArtificialBlog
      end

      it "returns an instantiated policy given a plain model instance providing an anonymous class" do
        policy = Pundit.policy(user, article_tag)
        expect(policy.user).to eq user
        expect(policy.tag).to eq article_tag
      end

      it "returns an instantiated policy given a plain model class providing an anonymous class" do
        policy = Pundit.policy(user, ArticleTag)
        expect(policy.user).to eq user
        expect(policy.tag).to eq ArticleTag
      end
    end
  end

  describe ".policy!" do
    it "returns an instantiated policy given a plain model instance" do
      policy = Pundit.policy!(user, post)
      expect(policy.user).to eq user
      expect(policy.post).to eq post
    end

    it "returns an instantiated policy given an active model instance" do
      policy = Pundit.policy!(user, comment)
      expect(policy.user).to eq user
      expect(policy.comment).to eq comment
    end

    it "returns an instantiated policy given a plain model class" do
      policy = Pundit.policy!(user, Post)
      expect(policy.user).to eq user
      expect(policy.post).to eq Post
    end

    it "returns an instantiated policy given an active model class" do
      policy = Pundit.policy!(user, Comment)
      expect(policy.user).to eq user
      expect(policy.comment).to eq Comment
    end

    it "returns an instantiated policy given a symbol" do
      policy = Pundit.policy!(user, :criteria)
      expect(policy.class).to eq CriteriaPolicy
      expect(policy.user).to eq user
      expect(policy.criteria).to eq :criteria
    end

    it "returns an instantiated policy given an array of symbols" do
      policy = Pundit.policy!(user, %i[project criteria])
      expect(policy.class).to eq Project::CriteriaPolicy
      expect(policy.user).to eq user
      expect(policy.criteria).to eq :criteria
    end

    it "throws an exception if the given policy can't be found" do
      expect { Pundit.policy!(user, article) }.to raise_error(Pundit::NotDefinedError)
      expect { Pundit.policy!(user, Article) }.to raise_error(Pundit::NotDefinedError)
    end

    it "returns the specified NilClassPolicy for nil" do
      expect(Pundit.policy!(user, nil)).to be_a NilClassPolicy
    end

    it "raises an error with a invalid policy constructor" do
      expect do
        Pundit.policy(user, Wiki)
      end.to raise_error(Pundit::InvalidConstructorError, "Invalid #<WikiPolicy> constructor is called")
    end
  end

  describe ".included" do
    it "includes Authorization module" do
      klass = Class.new

      expect do
        klass.include Pundit
      end.to output.to_stderr

      expect(klass).to include Pundit::Authorization
    end

    it "warns about deprecation" do
      klass = Class.new
      expect do
        klass.include Pundit
      end.to output(a_string_starting_with("'include Pundit' is deprecated")).to_stderr
    end
  end

  describe "Pundit::NotAuthorizedError" do
    it "can be initialized with a string as message" do
      error = Pundit::NotAuthorizedError.new("must be logged in")
      expect(error.message).to eq "must be logged in"
    end
  end
end


================================================
FILE: spec/rspec_dsl_spec.rb
================================================
# frozen_string_literal: true

require "spec_helper"

RSpec.describe "Pundit RSpec DSL", type: :policy do
  let(:fake_rspec) do
    double = class_double(RSpec::Core::ExampleGroup)
    double.extend(::Pundit::RSpec::DSL)
    double
  end
  let(:block) { proc { "block content" } }

  let(:user) { double }
  let(:other_user) { double }
  let(:post) { Post.new(user) }
  let(:policy) { PostPolicy }

  it "calls describe with the correct metadata and without :focus" do
    expected_metadata = {permissions: %i[item1 item2], caller: instance_of(Array)}
    expect(fake_rspec).to receive(:describe).with("item1 and item2", match(expected_metadata)) do |&block|
      expect(block.call).to eq("block content")
    end

    fake_rspec.permissions(:item1, :item2, &block)
  end

  it "calls describe with the correct metadata and with :focus" do
    expected_metadata = {permissions: %i[item1 item2], caller: instance_of(Array), focus: true}
    expect(fake_rspec).to receive(:describe).with("item1 and item2", match(expected_metadata)) do |&block|
      expect(block.call).to eq("block content")
    end

    fake_rspec.permissions(:item1, :item2, :focus, &block)
  end

  describe "#permit" do
    context "when not appropriately wrapped in permissions" do
      it "raises a descriptive error" do
        expect do
          expect(policy).to permit(user, post)
        end.to raise_error(KeyError, <<~MSG.strip)
          No permissions in example metadata, did you forget to wrap with `permissions :show?, ...`?
        MSG
      end
    end

    permissions :edit?, :update? do
      it "succeeds when action is permitted" do
        expect(policy).to permit(user, post)
      end

      context "when it fails" do
        it "fails with a descriptive error message" do
          expect do
            expect(policy).to permit(other_user, post)
          end.to raise_error(RSpec::Expectations::ExpectationNotMetError, <<~MSG.strip)
            Expected PostPolicy to grant edit? and update? on Post but edit? and update? were not granted
          MSG
        end
      end

      context "when negated" do
        it "succeeds when action is not permitted" do
          expect(policy).not_to permit(other_user, post)
        end

        context "when it fails" do
          it "fails with a descriptive error message" do
            expect do
              expect(policy).not_to permit(user, post)
            end.to raise_error(RSpec::Expectations::ExpectationNotMetError, <<~MSG.strip)
              Expected PostPolicy not to grant edit? and update? on Post but edit? and update? were granted
            MSG
          end
        end
      end
    end
  end
end


================================================
FILE: spec/spec_helper.rb
================================================
# frozen_string_literal: true

if ENV["COVERAGE"]
  require "simplecov"
  require "simplecov_json_formatter"

  SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new([
    SimpleCov::Formatter::HTMLFormatter,
    SimpleCov::Formatter::JSONFormatter
  ])

  SimpleCov.start do
    enable_coverage :branch
    primary_coverage :branch
  end

  SimpleCov.minimum_coverage_by_file line: 100, branch: 100
end

# @see https://github.com/rails/rails/issues/54260
require "logger" if RUBY_ENGINE == "jruby" && RUBY_ENGINE_VERSION.start_with?("9.3")

require "pundit"
require "pundit/rspec"
require "active_model/naming"

# Load all supporting files: models, policies, etc.
require "zeitwerk"
loader = Zeitwerk::Loader.new
loader.push_dir(File.expand_path("support/models", __dir__))
loader.push_dir(File.expand_path("support/policies", __dir__))
loader.push_dir(File.expand_path("support/lib", __dir__))
loader.setup
loader.eager_load


================================================
FILE: spec/support/lib/controller.rb
================================================
# frozen_string_literal: true

class Controller
  attr_accessor :current_user
  attr_reader :action_name, :params

  class View
    def initialize(controller)
      @controller = controller
    end

    attr_reader :controller
  end

  class << self
    def helper(mod)
      View.include(mod)
    end

    def helper_method(method)
      View.class_eval <<-RUBY, __FILE__, __LINE__ + 1
        def #{method}(*args, **kwargs, &block)
          controller.send(:#{method}, *args, **kwargs, &block)
        end
      RUBY
    end
  end

  include Pundit::Authorization

  # Mark protected methods public so they may be called in test
  public(*Pundit::Authorization.protected_instance_methods)

  def initialize(current_user, action_name, params)
    @current_user = current_user
    @action_name = action_name
    @params = params
  end
end


================================================
FILE: spec/support/lib/custom_cache.rb
================================================
# frozen_string_literal: true

class CustomCache
  def initialize
    @store = {}
  end

  def to_h
    @store
  end

  def [](key)
    @store[key]
  end

  def []=(key, value)
    @store[key] = value
  end
end


================================================
FILE: spec/support/lib/instance_tracking.rb
================================================
# frozen_string_literal: true

module InstanceTracking
  module ClassMethods
    def instances
      @instances || 0
    end

    attr_writer :instances
  end

  def self.prepended(other)
    other.extend(ClassMethods)
  end

  def initialize(*args, **kwargs, &block)
    self.class.instances += 1
    super
  end
end


================================================
FILE: spec/support/models/article.rb
================================================
# frozen_string_literal: true

class Article
end


================================================
FILE: spec/support/models/article_tag.rb
================================================
# frozen_string_literal: true

class ArticleTag
  def self.policy_class
    ArticleTagOtherNamePolicy
  end
end


================================================
FILE: spec/support/models/artificial_blog.rb
================================================
# frozen_string_literal: true

class ArtificialBlog < Blog
  def self.policy_class
    BlogPolicy
  end
end


================================================
FILE: spec/support/models/blog.rb
================================================
# frozen_string_literal: true

class Blog
end


================================================
FILE: spec/support/models/comment.rb
================================================
# frozen_string_literal: true

class Comment
  extend ActiveModel::Naming
end


================================================
FILE: spec/support/models/comment_four_five_six.rb
================================================
# frozen_string_literal: true

class CommentFourFiveSix
  extend ActiveModel::Naming
end


================================================
FILE: spec/support/models/comment_scope.rb
================================================
# frozen_string_literal: true

class CommentScope
  attr_reader :original_object

  def initialize(original_object)
    @original_object = original_object
  end

  def ==(other)
    original_object == other.original_object
  end
end


================================================
FILE: spec/support/models/comments_relation.rb
================================================
# frozen_string_literal: true

class CommentsRelation
  def initialize(empty: false)
    @empty = empty
  end

  def self.model_name
    Comment.model_name
  end
end


================================================
FILE: spec/support/models/customer/post.rb
================================================
# frozen_string_literal: true

module Customer
  class Post < ::Post
    extend ActiveModel::Naming

    def self.policy_class
      PostPolicy
    end
  end
end


================================================
FILE: spec/support/models/default_scope_contains_error.rb
================================================
# frozen_string_literal: true

class DefaultScopeContainsError
  def self.all
  end
end


================================================
FILE: spec/support/models/dummy_current_user.rb
================================================
# frozen_string_literal: true

class DummyCurrentUser
end


================================================
FILE: spec/support/models/foo.rb
================================================
# frozen_string_literal: true

class Foo
end


================================================
FILE: spec/support/models/post.rb
================================================
# frozen_string_literal: true

class Post
  def initialize(user = nil)
    @user = user
  end

  attr_reader :user

  def self.published
    :published
  end

  def self.read
    :read
  end

  def to_s
    "Post"
  end
end


================================================
FILE: spec/support/models/post_four_five_six.rb
================================================
# frozen_string_literal: true

class PostFourFiveSix
  def initialize(user)
    @user = user
  end

  attr_reader(:user)
end


================================================
FILE: spec/support/models/project_one_two_three/avatar_four_five_six.rb
================================================
# frozen_string_literal: true

module ProjectOneTwoThree
  class AvatarFourFiveSix
    extend ActiveModel::Naming
  end
end


================================================
FILE: spec/support/models/project_one_two_three/tag_four_five_six.rb
================================================
# frozen_string_literal: true

module ProjectOneTwoThree
  class TagFourFiveSix
    def initialize(user)
      @user = user
    end

    attr_reader(:user)
  end
end


================================================
FILE: spec/support/models/wiki.rb
================================================
# frozen_string_literal: true

class Wiki
end


================================================
FILE: spec/support/policies/article_tag_other_name_policy.rb
================================================
# frozen_string_literal: true

class ArticleTagOtherNamePolicy < BasePolicy
  def show?
    true
  end

  def destroy?
    false
  end

  alias_method :tag, :record
end


================================================
FILE: spec/support/policies/base_policy.rb
================================================
# frozen_string_literal: true

class BasePolicy
  prepend InstanceTracking

  class BaseScope
    prepend InstanceTracking

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    attr_reader :user, :scope
  end

  def initialize(user, record)
    @user = user
    @record = record
  end

  attr_reader :user, :record
end


================================================
FILE: spec/support/policies/blog_policy.rb
================================================
# frozen_string_literal: true

class BlogPolicy < BasePolicy
  alias_method :blog, :record
end


================================================
FILE: spec/support/policies/comment_policy.rb
================================================
# frozen_string_literal: true

class CommentPolicy < BasePolicy
  class Scope < BaseScope
    def resolve
      CommentScope.new(scope)
    end
  end

  alias_method :comment, :record
end


================================================
FILE: spec/support/policies/criteria_policy.rb
================================================
# frozen_string_literal: true

class CriteriaPolicy < BasePolicy
  alias_method :criteria, :record
end


================================================
FILE: spec/support/policies/default_scope_contains_error_policy.rb
================================================
# frozen_string_literal: true

class DefaultScopeContainsErrorPolicy < BasePolicy
  class Scope < BaseScope
    def resolve
      # deliberate wrong usage of the method
      raise "This is an arbitrary error that should bubble up"
    end
  end
end


================================================
FILE: spec/support/policies/dummy_current_user_policy.rb
================================================
# frozen_string_literal: true

class DummyCurrentUserPolicy < BasePolicy
  class Scope < BasePolicy::BaseScope
    def resolve
      user
    end
  end
end


================================================
FILE: spec/support/policies/nil_class_policy.rb
================================================
# frozen_string_literal: true

class NilClassPolicy < BasePolicy
  class Scope
    def initialize(*)
      raise Pundit::NotDefinedError, "Cannot scope NilClass"
    end
  end

  def destroy?
    false
  end
end


================================================
FILE: spec/support/policies/post_policy.rb
================================================
# frozen_string_literal: true

class PostPolicy < BasePolicy
  class Scope < BaseScope
    def resolve
      scope.published
    end
  end

  alias_method :post, :record

  def update?
    post.user == user
  end
  alias_method :edit?, :update?

  def destroy?
    false
  end

  def show?
    true
  end

  def permitted_attributes
    if post.user == user
      %i[title votes]
    else
      [:votes]
    end
  end

  def permitted_attributes_for_revise
    [:body]
  end

  def expected_attributes_for_action(action_name)
    case action_name.to_sym
    when :revise
      [:body]
    else
      if post.user == user
        %i[title votes]
      else
        [:votes]
      end
    end
  end
end


================================================
FILE: spec/support/policies/project/admin/comment_policy.rb
================================================
# frozen_string_literal: true

module Project
  module Admin
    class CommentPolicy < BasePolicy
      def update?
        true
      end

      def destroy?
        false
      end
    end
  end
end


================================================
FILE: spec/support/policies/project/comment_policy.rb
================================================
# frozen_string_literal: true

module Project
  class CommentPolicy < BasePolicy
    class Scope < BaseScope
      def resolve
        scope
      end
    end

    def update?
      true
    end

    alias_method :comment, :record
  end
end


================================================
FILE: spec/support/policies/project/criteria_policy.rb
================================================
# frozen_string_literal: true

module Project
  class CriteriaPolicy < BasePolicy
    alias_method :criteria, :record
  end
end


================================================
FILE: spec/support/policies/project/post_policy.rb
================================================
# frozen_string_literal: true

module Project
  class PostPolicy < BasePolicy
    class Scope < BaseScope
      def resolve
        scope.read
      end
    end

    alias_method :post, :record
  end
end


================================================
FILE: spec/support/policies/project_one_two_three/avatar_four_five_six_policy.rb
================================================
# frozen_string_literal: true

module ProjectOneTwoThree
  class AvatarFourFiveSixPolicy < BasePolicy
  end
end


================================================
FILE: spec/support/policies/project_one_two_three/comment_four_five_six_policy.rb
================================================
# frozen_string_literal: true

module ProjectOneTwoThree
  class CommentFourFiveSixPolicy < BasePolicy
  end
end


================================================
FILE: spec/support/policies/project_one_two_three/criteria_four_five_six_policy.rb
================================================
# frozen_string_literal: true

module ProjectOneTwoThree
  class CriteriaFourFiveSixPolicy < BasePolicy
  end
end


================================================
FILE: spec/support/policies/project_one_two_three/post_four_five_six_policy.rb
================================================
# frozen_string_literal: true

module ProjectOneTwoThree
  class PostFourFiveSixPolicy < BasePolicy
  end
end


================================================
FILE: spec/support/policies/project_one_two_three/tag_four_five_six_policy.rb
================================================
# frozen_string_literal: true

module ProjectOneTwoThree
  class TagFourFiveSixPolicy < BasePolicy
  end
end


================================================
FILE: spec/support/policies/publication_policy.rb
================================================
# frozen_string_literal: true

class PublicationPolicy < BasePolicy
  class Scope < BaseScope
    def resolve
      scope.published
    end
  end

  def create?
    true
  end
end


================================================
FILE: spec/support/policies/wiki_policy.rb
================================================
# frozen_string_literal: true

class WikiPolicy
  class Scope
    # deliberate typo method
    def initalize
    end
  end
end
Download .txt
gitextract_wfsck2t6/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   └── gem_release_template.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── main.yml
│       └── push_gem.yml
├── .gitignore
├── .rubocop_ignore_git.yml
├── .standard.yml
├── .yardopts
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── SECURITY.md
├── bin/
│   ├── console
│   └── setup
├── config/
│   └── rubocop-rspec.yml
├── lib/
│   ├── generators/
│   │   ├── pundit/
│   │   │   ├── install/
│   │   │   │   ├── USAGE
│   │   │   │   ├── install_generator.rb
│   │   │   │   └── templates/
│   │   │   │       └── application_policy.rb.tt
│   │   │   └── policy/
│   │   │       ├── USAGE
│   │   │       ├── policy_generator.rb
│   │   │       └── templates/
│   │   │           └── policy.rb.tt
│   │   ├── rspec/
│   │   │   ├── policy_generator.rb
│   │   │   └── templates/
│   │   │       └── policy_spec.rb.tt
│   │   └── test_unit/
│   │       ├── policy_generator.rb
│   │       └── templates/
│   │           └── policy_test.rb.tt
│   ├── pundit/
│   │   ├── authorization.rb
│   │   ├── cache_store/
│   │   │   ├── legacy_store.rb
│   │   │   └── null_store.rb
│   │   ├── cache_store.rb
│   │   ├── context.rb
│   │   ├── error.rb
│   │   ├── helper.rb
│   │   ├── policy_finder.rb
│   │   ├── railtie.rb
│   │   ├── rspec.rb
│   │   └── version.rb
│   └── pundit.rb
├── pundit.gemspec
└── spec/
    ├── authorization_spec.rb
    ├── generators_spec.rb
    ├── policies/
    │   └── post_policy_spec.rb
    ├── policy_finder_spec.rb
    ├── pundit/
    │   └── helper_spec.rb
    ├── pundit_spec.rb
    ├── rspec_dsl_spec.rb
    ├── spec_helper.rb
    └── support/
        ├── lib/
        │   ├── controller.rb
        │   ├── custom_cache.rb
        │   └── instance_tracking.rb
        ├── models/
        │   ├── article.rb
        │   ├── article_tag.rb
        │   ├── artificial_blog.rb
        │   ├── blog.rb
        │   ├── comment.rb
        │   ├── comment_four_five_six.rb
        │   ├── comment_scope.rb
        │   ├── comments_relation.rb
        │   ├── customer/
        │   │   └── post.rb
        │   ├── default_scope_contains_error.rb
        │   ├── dummy_current_user.rb
        │   ├── foo.rb
        │   ├── post.rb
        │   ├── post_four_five_six.rb
        │   ├── project_one_two_three/
        │   │   ├── avatar_four_five_six.rb
        │   │   └── tag_four_five_six.rb
        │   └── wiki.rb
        └── policies/
            ├── article_tag_other_name_policy.rb
            ├── base_policy.rb
            ├── blog_policy.rb
            ├── comment_policy.rb
            ├── criteria_policy.rb
            ├── default_scope_contains_error_policy.rb
            ├── dummy_current_user_policy.rb
            ├── nil_class_policy.rb
            ├── post_policy.rb
            ├── project/
            │   ├── admin/
            │   │   └── comment_policy.rb
            │   ├── comment_policy.rb
            │   ├── criteria_policy.rb
            │   └── post_policy.rb
            ├── project_one_two_three/
            │   ├── avatar_four_five_six_policy.rb
            │   ├── comment_four_five_six_policy.rb
            │   ├── criteria_four_five_six_policy.rb
            │   ├── post_four_five_six_policy.rb
            │   └── tag_four_five_six_policy.rb
            ├── publication_policy.rb
            └── wiki_policy.rb
Download .txt
SYMBOL INDEX (218 symbols across 57 files)

FILE: lib/generators/pundit/install/install_generator.rb
  type Pundit (line 3) | module Pundit
    type Generators (line 5) | module Generators
      class InstallGenerator (line 7) | class InstallGenerator < ::Rails::Generators::Base
        method copy_application_policy (line 10) | def copy_application_policy

FILE: lib/generators/pundit/policy/policy_generator.rb
  type Pundit (line 3) | module Pundit
    type Generators (line 5) | module Generators
      class PolicyGenerator (line 7) | class PolicyGenerator < ::Rails::Generators::NamedBase
        method create_policy (line 10) | def create_policy

FILE: lib/generators/rspec/policy_generator.rb
  type Rspec (line 4) | module Rspec
    type Generators (line 6) | module Generators
      class PolicyGenerator (line 8) | class PolicyGenerator < ::Rails::Generators::NamedBase
        method create_policy_spec (line 11) | def create_policy_spec

FILE: lib/generators/test_unit/policy_generator.rb
  type TestUnit (line 4) | module TestUnit
    type Generators (line 6) | module Generators
      class PolicyGenerator (line 8) | class PolicyGenerator < ::Rails::Generators::NamedBase
        method create_policy_test (line 11) | def create_policy_test

FILE: lib/pundit.rb
  type Pundit (line 22) | module Pundit
    type Generators (line 31) | module Generators; end
    function included (line 33) | def self.included(base)
    function authorize (line 45) | def authorize(user, record, query, policy_class: nil, cache: nil)
    function policy_scope (line 58) | def policy_scope(user, *args, **kwargs, &block)
    function policy_scope! (line 64) | def policy_scope!(user, *args, **kwargs, &block)
    function policy (line 70) | def policy(user, *args, **kwargs, &block)
    function policy! (line 76) | def policy!(user, *args, **kwargs, &block)

FILE: lib/pundit/authorization.rb
  type Pundit (line 3) | module Pundit
    type Authorization (line 13) | module Authorization
      function pundit (line 35) | def pundit
      function pundit_user (line 51) | def pundit_user
      function pundit_reset! (line 66) | def pundit_reset!
      function authorize (line 89) | def authorize(record, query = nil, policy_class: nil)
      function skip_authorization (line 103) | def skip_authorization
      function pundit_policy_authorized? (line 111) | def pundit_policy_authorized?
      function verify_authorized (line 126) | def verify_authorized
      function policies (line 136) | def policies
      function policy (line 150) | def policy(record)
      function policy_scope (line 163) | def policy_scope(scope, policy_scope_class: nil)
      function skip_policy_scope (line 174) | def skip_policy_scope
      function pundit_policy_scoped? (line 182) | def pundit_policy_scoped?
      function verify_policy_scoped (line 198) | def verify_policy_scoped
      function policy_scopes (line 208) | def policy_scopes
      function pundit_policy_scope (line 225) | def pundit_policy_scope(scope)
      function expected_attributes (line 251) | def expected_attributes(record, action: action_name, param_key: pund...
      function pundit_param_key (line 260) | def pundit_param_key(record)
      function permitted_attributes (line 278) | def permitted_attributes(record, action = action_name)
      function pundit_params_for (line 293) | def pundit_params_for(record)

FILE: lib/pundit/cache_store.rb
  type Pundit (line 3) | module Pundit
    type CacheStore (line 9) | module CacheStore

FILE: lib/pundit/cache_store/legacy_store.rb
  type Pundit (line 3) | module Pundit
    type CacheStore (line 4) | module CacheStore
      class LegacyStore (line 11) | class LegacyStore
        method initialize (line 13) | def initialize(hash = {})
        method fetch (line 21) | def fetch(user:, record:)

FILE: lib/pundit/cache_store/null_store.rb
  type Pundit (line 3) | module Pundit
    type CacheStore (line 4) | module CacheStore
      class NullStore (line 12) | class NullStore
        method fetch (line 25) | def fetch(*, **)

FILE: lib/pundit/context.rb
  type Pundit (line 3) | module Pundit
    class Context (line 30) | class Context
      method initialize (line 35) | def initialize(user:, policy_cache: CacheStore::NullStore.instance)
      method authorize (line 62) | def authorize(possibly_namespaced_record, query:, policy_class:)
      method policy (line 82) | def policy(record)
      method policy! (line 94) | def policy!(record)
      method policy_scope (line 109) | def policy_scope(scope)
      method policy_scope! (line 130) | def policy_scope!(scope)
      method cached_find (line 158) | def cached_find(record)
      method policy_finder (line 178) | def policy_finder(record)
      method pundit_model (line 186) | def pundit_model(record)

FILE: lib/pundit/error.rb
  type Pundit (line 3) | module Pundit
    class Error (line 8) | class Error < StandardError; end
    class NotAuthorizedError (line 12) | class NotAuthorizedError < Error
      method initialize (line 36) | def initialize(options = {})
    class InvalidConstructorError (line 56) | class InvalidConstructorError < Error; end
    class AuthorizationNotPerformedError (line 61) | class AuthorizationNotPerformedError < Error; end
    class PolicyScopingNotPerformedError (line 66) | class PolicyScopingNotPerformedError < AuthorizationNotPerformedError;...
    class NotDefinedError (line 70) | class NotDefinedError < Error; end

FILE: lib/pundit/helper.rb
  type Pundit (line 3) | module Pundit
    type Helper (line 9) | module Helper
      function policy_scope (line 12) | def policy_scope(scope)

FILE: lib/pundit/policy_finder.rb
  type Pundit (line 6) | module Pundit
    class PolicyFinder (line 16) | class PolicyFinder
      method initialize (line 29) | def initialize(object)
      method scope (line 40) | def scope
      method policy (line 52) | def policy
      method scope! (line 61) | def scope!
      method policy! (line 69) | def policy!
      method param_key (line 76) | def param_key # rubocop:disable Metrics/AbcSize
      method find (line 96) | def find(subject)
      method find_class_name (line 121) | def find_class_name(subject)

FILE: lib/pundit/railtie.rb
  type Pundit (line 3) | module Pundit
    class Railtie (line 5) | class Railtie < Rails::Railtie

FILE: lib/pundit/rspec.rb
  type Pundit (line 7) | module Pundit
    type RSpec (line 10) | module RSpec
      type Matchers (line 12) | module Matchers
        function description (line 29) | def description(user, record)
        function was_or_were (line 62) | def was_or_were
        function current_example (line 90) | def current_example
        function current_example (line 96) | def current_example
        function permissions (line 102) | def permissions
      type DSL (line 114) | module DSL
        function permissions (line 131) | def permissions(*list, &block)
      type PolicyExampleGroup (line 147) | module PolicyExampleGroup
        function included (line 150) | def self.included(base)

FILE: lib/pundit/version.rb
  type Pundit (line 3) | module Pundit

FILE: spec/authorization_spec.rb
  function to_params (line 7) | def to_params(*args, **kwargs, &block)

FILE: spec/support/lib/controller.rb
  class Controller (line 3) | class Controller
    class View (line 7) | class View
      method initialize (line 8) | def initialize(controller)
    method helper (line 16) | def helper(mod)
    method helper_method (line 20) | def helper_method(method)
    method initialize (line 34) | def initialize(current_user, action_name, params)

FILE: spec/support/lib/custom_cache.rb
  class CustomCache (line 3) | class CustomCache
    method initialize (line 4) | def initialize
    method to_h (line 8) | def to_h
    method [] (line 12) | def [](key)
    method []= (line 16) | def []=(key, value)

FILE: spec/support/lib/instance_tracking.rb
  type InstanceTracking (line 3) | module InstanceTracking
    type ClassMethods (line 4) | module ClassMethods
      function instances (line 5) | def instances
    function prepended (line 12) | def self.prepended(other)
    function initialize (line 16) | def initialize(*args, **kwargs, &block)

FILE: spec/support/models/article.rb
  class Article (line 3) | class Article

FILE: spec/support/models/article_tag.rb
  class ArticleTag (line 3) | class ArticleTag
    method policy_class (line 4) | def self.policy_class

FILE: spec/support/models/artificial_blog.rb
  class ArtificialBlog (line 3) | class ArtificialBlog < Blog
    method policy_class (line 4) | def self.policy_class

FILE: spec/support/models/blog.rb
  class Blog (line 3) | class Blog

FILE: spec/support/models/comment.rb
  class Comment (line 3) | class Comment

FILE: spec/support/models/comment_four_five_six.rb
  class CommentFourFiveSix (line 3) | class CommentFourFiveSix

FILE: spec/support/models/comment_scope.rb
  class CommentScope (line 3) | class CommentScope
    method initialize (line 6) | def initialize(original_object)
    method == (line 10) | def ==(other)

FILE: spec/support/models/comments_relation.rb
  class CommentsRelation (line 3) | class CommentsRelation
    method initialize (line 4) | def initialize(empty: false)
    method model_name (line 8) | def self.model_name

FILE: spec/support/models/customer/post.rb
  type Customer (line 3) | module Customer
    class Post (line 4) | class Post < ::Post
      method policy_class (line 7) | def self.policy_class

FILE: spec/support/models/default_scope_contains_error.rb
  class DefaultScopeContainsError (line 3) | class DefaultScopeContainsError
    method all (line 4) | def self.all

FILE: spec/support/models/dummy_current_user.rb
  class DummyCurrentUser (line 3) | class DummyCurrentUser

FILE: spec/support/models/foo.rb
  class Foo (line 3) | class Foo

FILE: spec/support/models/post.rb
  class Post (line 3) | class Post
    method initialize (line 4) | def initialize(user = nil)
    method published (line 10) | def self.published
    method read (line 14) | def self.read
    method to_s (line 18) | def to_s

FILE: spec/support/models/post_four_five_six.rb
  class PostFourFiveSix (line 3) | class PostFourFiveSix
    method initialize (line 4) | def initialize(user)

FILE: spec/support/models/project_one_two_three/avatar_four_five_six.rb
  type ProjectOneTwoThree (line 3) | module ProjectOneTwoThree
    class AvatarFourFiveSix (line 4) | class AvatarFourFiveSix

FILE: spec/support/models/project_one_two_three/tag_four_five_six.rb
  type ProjectOneTwoThree (line 3) | module ProjectOneTwoThree
    class TagFourFiveSix (line 4) | class TagFourFiveSix
      method initialize (line 5) | def initialize(user)

FILE: spec/support/models/wiki.rb
  class Wiki (line 3) | class Wiki

FILE: spec/support/policies/article_tag_other_name_policy.rb
  class ArticleTagOtherNamePolicy (line 3) | class ArticleTagOtherNamePolicy < BasePolicy
    method show? (line 4) | def show?
    method destroy? (line 8) | def destroy?

FILE: spec/support/policies/base_policy.rb
  class BasePolicy (line 3) | class BasePolicy
    class BaseScope (line 6) | class BaseScope
      method initialize (line 9) | def initialize(user, scope)
    method initialize (line 17) | def initialize(user, record)

FILE: spec/support/policies/blog_policy.rb
  class BlogPolicy (line 3) | class BlogPolicy < BasePolicy

FILE: spec/support/policies/comment_policy.rb
  class CommentPolicy (line 3) | class CommentPolicy < BasePolicy
    class Scope (line 4) | class Scope < BaseScope
      method resolve (line 5) | def resolve

FILE: spec/support/policies/criteria_policy.rb
  class CriteriaPolicy (line 3) | class CriteriaPolicy < BasePolicy

FILE: spec/support/policies/default_scope_contains_error_policy.rb
  class DefaultScopeContainsErrorPolicy (line 3) | class DefaultScopeContainsErrorPolicy < BasePolicy
    class Scope (line 4) | class Scope < BaseScope
      method resolve (line 5) | def resolve

FILE: spec/support/policies/dummy_current_user_policy.rb
  class DummyCurrentUserPolicy (line 3) | class DummyCurrentUserPolicy < BasePolicy
    class Scope (line 4) | class Scope < BasePolicy::BaseScope
      method resolve (line 5) | def resolve

FILE: spec/support/policies/nil_class_policy.rb
  class NilClassPolicy (line 3) | class NilClassPolicy < BasePolicy
    class Scope (line 4) | class Scope
      method initialize (line 5) | def initialize(*)
    method destroy? (line 10) | def destroy?

FILE: spec/support/policies/post_policy.rb
  class PostPolicy (line 3) | class PostPolicy < BasePolicy
    class Scope (line 4) | class Scope < BaseScope
      method resolve (line 5) | def resolve
    method update? (line 12) | def update?
    method destroy? (line 17) | def destroy?
    method show? (line 21) | def show?
    method permitted_attributes (line 25) | def permitted_attributes
    method permitted_attributes_for_revise (line 33) | def permitted_attributes_for_revise
    method expected_attributes_for_action (line 37) | def expected_attributes_for_action(action_name)

FILE: spec/support/policies/project/admin/comment_policy.rb
  type Project (line 3) | module Project
    type Admin (line 4) | module Admin
      class CommentPolicy (line 5) | class CommentPolicy < BasePolicy
        method update? (line 6) | def update?
        method destroy? (line 10) | def destroy?

FILE: spec/support/policies/project/comment_policy.rb
  type Project (line 3) | module Project
    class CommentPolicy (line 4) | class CommentPolicy < BasePolicy
      class Scope (line 5) | class Scope < BaseScope
        method resolve (line 6) | def resolve
      method update? (line 11) | def update?

FILE: spec/support/policies/project/criteria_policy.rb
  type Project (line 3) | module Project
    class CriteriaPolicy (line 4) | class CriteriaPolicy < BasePolicy

FILE: spec/support/policies/project/post_policy.rb
  type Project (line 3) | module Project
    class PostPolicy (line 4) | class PostPolicy < BasePolicy
      class Scope (line 5) | class Scope < BaseScope
        method resolve (line 6) | def resolve

FILE: spec/support/policies/project_one_two_three/avatar_four_five_six_policy.rb
  type ProjectOneTwoThree (line 3) | module ProjectOneTwoThree
    class AvatarFourFiveSixPolicy (line 4) | class AvatarFourFiveSixPolicy < BasePolicy

FILE: spec/support/policies/project_one_two_three/comment_four_five_six_policy.rb
  type ProjectOneTwoThree (line 3) | module ProjectOneTwoThree
    class CommentFourFiveSixPolicy (line 4) | class CommentFourFiveSixPolicy < BasePolicy

FILE: spec/support/policies/project_one_two_three/criteria_four_five_six_policy.rb
  type ProjectOneTwoThree (line 3) | module ProjectOneTwoThree
    class CriteriaFourFiveSixPolicy (line 4) | class CriteriaFourFiveSixPolicy < BasePolicy

FILE: spec/support/policies/project_one_two_three/post_four_five_six_policy.rb
  type ProjectOneTwoThree (line 3) | module ProjectOneTwoThree
    class PostFourFiveSixPolicy (line 4) | class PostFourFiveSixPolicy < BasePolicy

FILE: spec/support/policies/project_one_two_three/tag_four_five_six_policy.rb
  type ProjectOneTwoThree (line 3) | module ProjectOneTwoThree
    class TagFourFiveSixPolicy (line 4) | class TagFourFiveSixPolicy < BasePolicy

FILE: spec/support/policies/publication_policy.rb
  class PublicationPolicy (line 3) | class PublicationPolicy < BasePolicy
    class Scope (line 4) | class Scope < BaseScope
      method resolve (line 5) | def resolve
    method create? (line 10) | def create?

FILE: spec/support/policies/wiki_policy.rb
  class WikiPolicy (line 3) | class WikiPolicy
    class Scope (line 4) | class Scope
      method initalize (line 6) | def initalize
Condensed preview — 92 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (147K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 411,
    "preview": "---\nname: Bug report\nabout: Create a bug report to report a problem\ntitle: ''\nlabels: problem\nassignees: ''\n\n---\n\n**Desc"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 1019,
    "preview": "---\nname: Feature request\nabout: Suggest an idea\ntitle: ''\nlabels: ['feature request']\nassignees: ''\n---\n\n**Please consi"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/gem_release_template.md",
    "chars": 382,
    "preview": "## To do\n\n- [ ] Make changes:\n  - [ ] Bump `Pundit::VERSION` in `lib/pundit/version.rb`.\n  - [ ] Update `CHANGELOG.md`.\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 344,
    "preview": "## To do\n\n- [ ] I have read the [contributing guidelines](https://github.com/varvet/pundit/contribute).\n- [ ] I have add"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 2429,
    "preview": "name: Main\n\non:\n  push:\n    branches: [\"main\"]\n  pull_request:\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs"
  },
  {
    "path": ".github/workflows/push_gem.yml",
    "chars": 786,
    "preview": "name: Push Gem\n\non:\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  push:\n    if: github.repository == 'var"
  },
  {
    "path": ".gitignore",
    "chars": 164,
    "preview": "*.gem\n*.rbc\n.bundle\n.config\n.coverage\n.yardoc\nGemfile.lock\nInstalledFiles\n_yardoc\ncoverage\ndoc/\nlib/bundler/man\npkg\nrdoc"
  },
  {
    "path": ".rubocop_ignore_git.yml",
    "chars": 278,
    "preview": "# This is here so we can keep YAML syntax highlight in the main file.\nAllCops:\n  Exclude:\n    - \"lib/generators/**/templ"
  },
  {
    "path": ".standard.yml",
    "chars": 77,
    "preview": "parallel: true\nruby_version: 3.2\n\nextend_config:\n  - .rubocop_ignore_git.yml\n"
  },
  {
    "path": ".yardopts",
    "chars": 90,
    "preview": "--no-private --private --protected --hide-void-return --markup markdown --fail-on-warning\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 8658,
    "preview": "# Pundit\n\n## Unreleased\n\n- Add support for `params.expect` using `expected_parameters` and `expected_parameters_for`. [#"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 1423,
    "preview": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, we pledge to respect all\npeople who cont"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1212,
    "preview": "## Security issues\n\nIf you have found a security related issue, please do not file an issue on GitHub or send a PR addre"
  },
  {
    "path": "Gemfile",
    "chars": 538,
    "preview": "# frozen_string_literal: true\n\nsource \"https://rubygems.org\"\n\ngemspec\n\n# Rails-related - for testing purposes\ngem \"actio"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1081,
    "preview": "Copyright (c) 2019 Jonas Nicklas, Varvet AB\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "README.md",
    "chars": 26384,
    "preview": "# Pundit\n\n[![Main](https://github.com/varvet/pundit/actions/workflows/main.yml/badge.svg)](https://github.com/varvet/pun"
  },
  {
    "path": "Rakefile",
    "chars": 358,
    "preview": "# frozen_string_literal: true\n\nrequire \"rubygems\"\nrequire \"bundler/gem_tasks\"\nrequire \"rspec/core/rake_task\"\nrequire \"ya"
  },
  {
    "path": "SECURITY.md",
    "chars": 456,
    "preview": "# Security Policy\n\nPlease do not file an issue on GitHub, or send a PR addressing the issue.\n\n## Supported versions\n\nMos"
  },
  {
    "path": "bin/console",
    "chars": 280,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire \"bundler/setup\"\nrequire \"pundit\"\n\n# You can add fixtures and/"
  },
  {
    "path": "bin/setup",
    "chars": 131,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\nIFS=$'\\n\\t'\nset -vx\n\nbundle install\n\n# Do any other automated setup that you need "
  },
  {
    "path": "config/rubocop-rspec.yml",
    "chars": 75,
    "preview": "RSpec:\n  Language:\n    ExampleGroups:\n      Regular:\n        - permissions\n"
  },
  {
    "path": "lib/generators/pundit/install/USAGE",
    "chars": 91,
    "preview": "Description:\n    Generates an application policy as a starting point for your application.\n"
  },
  {
    "path": "lib/generators/pundit/install/install_generator.rb",
    "chars": 350,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # @private\n  module Generators\n    # @private\n    class InstallGenerator "
  },
  {
    "path": "lib/generators/pundit/install/templates/application_policy.rb.tt",
    "chars": 619,
    "preview": "# frozen_string_literal: true\n\nclass ApplicationPolicy\n  attr_reader :user, :record\n\n  def initialize(user, record)\n    "
  },
  {
    "path": "lib/generators/pundit/policy/USAGE",
    "chars": 176,
    "preview": "Description:\n    Generates a policy for a model with the given name.\n\nExample:\n    rails generate pundit:policy user\n\n  "
  },
  {
    "path": "lib/generators/pundit/policy/policy_generator.rb",
    "chars": 391,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # @private\n  module Generators\n    # @private\n    class PolicyGenerator <"
  },
  {
    "path": "lib/generators/pundit/policy/templates/policy.rb.tt",
    "chars": 587,
    "preview": "<% module_namespacing do -%>\nclass <%= class_name %>Policy < ApplicationPolicy\n  # NOTE: Up to Pundit v2.3.1, the inheri"
  },
  {
    "path": "lib/generators/rspec/policy_generator.rb",
    "chars": 385,
    "preview": "# frozen_string_literal: true\n\n# @private\nmodule Rspec\n  # @private\n  module Generators\n    # @private\n    class PolicyG"
  },
  {
    "path": "lib/generators/rspec/templates/policy_spec.rb.tt",
    "chars": 664,
    "preview": "require '<%= File.exist?('spec/rails_helper.rb') ? 'rails_helper' : 'spec_helper' %>'\n\nRSpec.describe <%= class_name %>P"
  },
  {
    "path": "lib/generators/test_unit/policy_generator.rb",
    "chars": 388,
    "preview": "# frozen_string_literal: true\n\n# @private\nmodule TestUnit\n  # @private\n  module Generators\n    # @private\n    class Poli"
  },
  {
    "path": "lib/generators/test_unit/templates/policy_test.rb.tt",
    "chars": 209,
    "preview": "require 'test_helper'\n\nclass <%= class_name %>PolicyTest < ActiveSupport::TestCase\n  def test_scope\n  end\n\n  def test_sh"
  },
  {
    "path": "lib/pundit/authorization.rb",
    "chars": 10405,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # Pundit DSL to include in your controllers to provide authorization help"
  },
  {
    "path": "lib/pundit/cache_store/legacy_store.rb",
    "chars": 630,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  module CacheStore\n    # A cache store that uses only the record as a cach"
  },
  {
    "path": "lib/pundit/cache_store/null_store.rb",
    "chars": 666,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  module CacheStore\n    # A cache store that does not cache anything.\n    #"
  },
  {
    "path": "lib/pundit/cache_store.rb",
    "chars": 745,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # Namespace for cache store implementations.\n  #\n  # Cache stores are use"
  },
  {
    "path": "lib/pundit/context.rb",
    "chars": 6181,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # {Pundit::Context} is intended to be created once per request and user, "
  },
  {
    "path": "lib/pundit/error.rb",
    "chars": 2424,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # @api private\n  # @since v1.0.0\n  # To avoid name clashes with common Er"
  },
  {
    "path": "lib/pundit/helper.rb",
    "chars": 382,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # Rails view helpers, to allow a slightly different view-specific\n  # imp"
  },
  {
    "path": "lib/pundit/policy_finder.rb",
    "chars": 3855,
    "preview": "# frozen_string_literal: true\n\n# String#safe_constantize, String#demodulize, String#underscore, String#camelize\nrequire "
  },
  {
    "path": "lib/pundit/railtie.rb",
    "chars": 565,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # @since v2.5.0\n  class Railtie < Rails::Railtie\n    if Rails.version.to_"
  },
  {
    "path": "lib/pundit/rspec.rb",
    "chars": 5018,
    "preview": "# frozen_string_literal: true\n\nrequire \"pundit\"\n# Array#to_sentence\nrequire \"active_support/core_ext/array/conversions\"\n"
  },
  {
    "path": "lib/pundit/version.rb",
    "chars": 104,
    "preview": "# frozen_string_literal: true\n\nmodule Pundit\n  # The current version of Pundit.\n  VERSION = \"2.5.2\"\nend\n"
  },
  {
    "path": "lib/pundit.rb",
    "chars": 2112,
    "preview": "# frozen_string_literal: true\n\nrequire \"active_support\"\n\nrequire \"pundit/version\"\nrequire \"pundit/error\"\nrequire \"pundit"
  },
  {
    "path": "pundit.gemspec",
    "chars": 1174,
    "preview": "# frozen_string_literal: true\n\nrequire_relative \"lib/pundit/version\"\n\nGem::Specification.new do |gem|\n  gem.name = \"pund"
  },
  {
    "path": "spec/authorization_spec.rb",
    "chars": 13115,
    "preview": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"action_controller\"\n\nRSpec.describe Pundit::Authorization d"
  },
  {
    "path": "spec/generators_spec.rb",
    "chars": 1209,
    "preview": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\nrequire \"tmpdir\"\n\nrequire \"rails/generators\"\nrequire \"generators/pu"
  },
  {
    "path": "spec/policies/post_policy_spec.rb",
    "chars": 1601,
    "preview": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe PostPolicy do\n  let(:user) { double }\n  let(:own_po"
  },
  {
    "path": "spec/policy_finder_spec.rb",
    "chars": 5301,
    "preview": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Pundit::PolicyFinder do\n  let(:user) { double }\n  l"
  },
  {
    "path": "spec/pundit/helper_spec.rb",
    "chars": 458,
    "preview": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Pundit::Helper do\n  let(:user) { double }\n  let(:co"
  },
  {
    "path": "spec/pundit_spec.rb",
    "chars": 18418,
    "preview": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe Pundit do\n  let(:user) { double }\n  let(:post) { Po"
  },
  {
    "path": "spec/rspec_dsl_spec.rb",
    "chars": 2670,
    "preview": "# frozen_string_literal: true\n\nrequire \"spec_helper\"\n\nRSpec.describe \"Pundit RSpec DSL\", type: :policy do\n  let(:fake_rs"
  },
  {
    "path": "spec/spec_helper.rb",
    "chars": 937,
    "preview": "# frozen_string_literal: true\n\nif ENV[\"COVERAGE\"]\n  require \"simplecov\"\n  require \"simplecov_json_formatter\"\n\n  SimpleCo"
  },
  {
    "path": "spec/support/lib/controller.rb",
    "chars": 840,
    "preview": "# frozen_string_literal: true\n\nclass Controller\n  attr_accessor :current_user\n  attr_reader :action_name, :params\n\n  cla"
  },
  {
    "path": "spec/support/lib/custom_cache.rb",
    "chars": 211,
    "preview": "# frozen_string_literal: true\n\nclass CustomCache\n  def initialize\n    @store = {}\n  end\n\n  def to_h\n    @store\n  end\n\n  "
  },
  {
    "path": "spec/support/lib/instance_tracking.rb",
    "chars": 318,
    "preview": "# frozen_string_literal: true\n\nmodule InstanceTracking\n  module ClassMethods\n    def instances\n      @instances || 0\n   "
  },
  {
    "path": "spec/support/models/article.rb",
    "chars": 49,
    "preview": "# frozen_string_literal: true\n\nclass Article\nend\n"
  },
  {
    "path": "spec/support/models/article_tag.rb",
    "chars": 112,
    "preview": "# frozen_string_literal: true\n\nclass ArticleTag\n  def self.policy_class\n    ArticleTagOtherNamePolicy\n  end\nend\n"
  },
  {
    "path": "spec/support/models/artificial_blog.rb",
    "chars": 108,
    "preview": "# frozen_string_literal: true\n\nclass ArtificialBlog < Blog\n  def self.policy_class\n    BlogPolicy\n  end\nend\n"
  },
  {
    "path": "spec/support/models/blog.rb",
    "chars": 46,
    "preview": "# frozen_string_literal: true\n\nclass Blog\nend\n"
  },
  {
    "path": "spec/support/models/comment.rb",
    "chars": 78,
    "preview": "# frozen_string_literal: true\n\nclass Comment\n  extend ActiveModel::Naming\nend\n"
  },
  {
    "path": "spec/support/models/comment_four_five_six.rb",
    "chars": 89,
    "preview": "# frozen_string_literal: true\n\nclass CommentFourFiveSix\n  extend ActiveModel::Naming\nend\n"
  },
  {
    "path": "spec/support/models/comment_scope.rb",
    "chars": 233,
    "preview": "# frozen_string_literal: true\n\nclass CommentScope\n  attr_reader :original_object\n\n  def initialize(original_object)\n    "
  },
  {
    "path": "spec/support/models/comments_relation.rb",
    "chars": 166,
    "preview": "# frozen_string_literal: true\n\nclass CommentsRelation\n  def initialize(empty: false)\n    @empty = empty\n  end\n\n  def sel"
  },
  {
    "path": "spec/support/models/customer/post.rb",
    "chars": 162,
    "preview": "# frozen_string_literal: true\n\nmodule Customer\n  class Post < ::Post\n    extend ActiveModel::Naming\n\n    def self.policy"
  },
  {
    "path": "spec/support/models/default_scope_contains_error.rb",
    "chars": 88,
    "preview": "# frozen_string_literal: true\n\nclass DefaultScopeContainsError\n  def self.all\n  end\nend\n"
  },
  {
    "path": "spec/support/models/dummy_current_user.rb",
    "chars": 58,
    "preview": "# frozen_string_literal: true\n\nclass DummyCurrentUser\nend\n"
  },
  {
    "path": "spec/support/models/foo.rb",
    "chars": 45,
    "preview": "# frozen_string_literal: true\n\nclass Foo\nend\n"
  },
  {
    "path": "spec/support/models/post.rb",
    "chars": 224,
    "preview": "# frozen_string_literal: true\n\nclass Post\n  def initialize(user = nil)\n    @user = user\n  end\n\n  attr_reader :user\n\n  de"
  },
  {
    "path": "spec/support/models/post_four_five_six.rb",
    "chars": 125,
    "preview": "# frozen_string_literal: true\n\nclass PostFourFiveSix\n  def initialize(user)\n    @user = user\n  end\n\n  attr_reader(:user)"
  },
  {
    "path": "spec/support/models/project_one_two_three/avatar_four_five_six.rb",
    "chars": 124,
    "preview": "# frozen_string_literal: true\n\nmodule ProjectOneTwoThree\n  class AvatarFourFiveSix\n    extend ActiveModel::Naming\n  end\n"
  },
  {
    "path": "spec/support/models/project_one_two_three/tag_four_five_six.rb",
    "chars": 166,
    "preview": "# frozen_string_literal: true\n\nmodule ProjectOneTwoThree\n  class TagFourFiveSix\n    def initialize(user)\n      @user = u"
  },
  {
    "path": "spec/support/models/wiki.rb",
    "chars": 46,
    "preview": "# frozen_string_literal: true\n\nclass Wiki\nend\n"
  },
  {
    "path": "spec/support/policies/article_tag_other_name_policy.rb",
    "chars": 169,
    "preview": "# frozen_string_literal: true\n\nclass ArticleTagOtherNamePolicy < BasePolicy\n  def show?\n    true\n  end\n\n  def destroy?\n "
  },
  {
    "path": "spec/support/policies/base_policy.rb",
    "chars": 351,
    "preview": "# frozen_string_literal: true\n\nclass BasePolicy\n  prepend InstanceTracking\n\n  class BaseScope\n    prepend InstanceTracki"
  },
  {
    "path": "spec/support/policies/blog_policy.rb",
    "chars": 95,
    "preview": "# frozen_string_literal: true\n\nclass BlogPolicy < BasePolicy\n  alias_method :blog, :record\nend\n"
  },
  {
    "path": "spec/support/policies/comment_policy.rb",
    "chars": 188,
    "preview": "# frozen_string_literal: true\n\nclass CommentPolicy < BasePolicy\n  class Scope < BaseScope\n    def resolve\n      CommentS"
  },
  {
    "path": "spec/support/policies/criteria_policy.rb",
    "chars": 103,
    "preview": "# frozen_string_literal: true\n\nclass CriteriaPolicy < BasePolicy\n  alias_method :criteria, :record\nend\n"
  },
  {
    "path": "spec/support/policies/default_scope_contains_error_policy.rb",
    "chars": 250,
    "preview": "# frozen_string_literal: true\n\nclass DefaultScopeContainsErrorPolicy < BasePolicy\n  class Scope < BaseScope\n    def reso"
  },
  {
    "path": "spec/support/policies/dummy_current_user_policy.rb",
    "chars": 156,
    "preview": "# frozen_string_literal: true\n\nclass DummyCurrentUserPolicy < BasePolicy\n  class Scope < BasePolicy::BaseScope\n    def r"
  },
  {
    "path": "spec/support/policies/nil_class_policy.rb",
    "chars": 212,
    "preview": "# frozen_string_literal: true\n\nclass NilClassPolicy < BasePolicy\n  class Scope\n    def initialize(*)\n      raise Pundit:"
  },
  {
    "path": "spec/support/policies/post_policy.rb",
    "chars": 701,
    "preview": "# frozen_string_literal: true\n\nclass PostPolicy < BasePolicy\n  class Scope < BaseScope\n    def resolve\n      scope.publi"
  },
  {
    "path": "spec/support/policies/project/admin/comment_policy.rb",
    "chars": 201,
    "preview": "# frozen_string_literal: true\n\nmodule Project\n  module Admin\n    class CommentPolicy < BasePolicy\n      def update?\n    "
  },
  {
    "path": "spec/support/policies/project/comment_policy.rb",
    "chars": 241,
    "preview": "# frozen_string_literal: true\n\nmodule Project\n  class CommentPolicy < BasePolicy\n    class Scope < BaseScope\n      def r"
  },
  {
    "path": "spec/support/policies/project/criteria_policy.rb",
    "chars": 128,
    "preview": "# frozen_string_literal: true\n\nmodule Project\n  class CriteriaPolicy < BasePolicy\n    alias_method :criteria, :record\n  "
  },
  {
    "path": "spec/support/policies/project/post_policy.rb",
    "chars": 204,
    "preview": "# frozen_string_literal: true\n\nmodule Project\n  class PostPolicy < BasePolicy\n    class Scope < BaseScope\n      def reso"
  },
  {
    "path": "spec/support/policies/project_one_two_three/avatar_four_five_six_policy.rb",
    "chars": 112,
    "preview": "# frozen_string_literal: true\n\nmodule ProjectOneTwoThree\n  class AvatarFourFiveSixPolicy < BasePolicy\n  end\nend\n"
  },
  {
    "path": "spec/support/policies/project_one_two_three/comment_four_five_six_policy.rb",
    "chars": 113,
    "preview": "# frozen_string_literal: true\n\nmodule ProjectOneTwoThree\n  class CommentFourFiveSixPolicy < BasePolicy\n  end\nend\n"
  },
  {
    "path": "spec/support/policies/project_one_two_three/criteria_four_five_six_policy.rb",
    "chars": 114,
    "preview": "# frozen_string_literal: true\n\nmodule ProjectOneTwoThree\n  class CriteriaFourFiveSixPolicy < BasePolicy\n  end\nend\n"
  },
  {
    "path": "spec/support/policies/project_one_two_three/post_four_five_six_policy.rb",
    "chars": 110,
    "preview": "# frozen_string_literal: true\n\nmodule ProjectOneTwoThree\n  class PostFourFiveSixPolicy < BasePolicy\n  end\nend\n"
  },
  {
    "path": "spec/support/policies/project_one_two_three/tag_four_five_six_policy.rb",
    "chars": 109,
    "preview": "# frozen_string_literal: true\n\nmodule ProjectOneTwoThree\n  class TagFourFiveSixPolicy < BasePolicy\n  end\nend\n"
  },
  {
    "path": "spec/support/policies/publication_policy.rb",
    "chars": 180,
    "preview": "# frozen_string_literal: true\n\nclass PublicationPolicy < BasePolicy\n  class Scope < BaseScope\n    def resolve\n      scop"
  },
  {
    "path": "spec/support/policies/wiki_policy.rb",
    "chars": 127,
    "preview": "# frozen_string_literal: true\n\nclass WikiPolicy\n  class Scope\n    # deliberate typo method\n    def initalize\n    end\n  e"
  }
]

About this extraction

This page contains the full source code of the varvet/pundit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 92 files (132.4 KB), approximately 37.0k tokens, and a symbol index with 218 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!