### Complete Authorization Workflow Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
A full example demonstrating authorization checks, updates, and destruction of a post within a try-catch block for handling `Pundit::NotAuthorizedError`.
```ruby
# In a script or background job
user = User.find(1)
post = Post.find(1)
begin
# Check permission and raise if not authorized
Pundit.authorize(user, post, :update?)
# Update the post
post.update(title: "New Title")
# Get the policy for additional checks
policy = Pundit.policy(user, post)
if policy.destroy?
post.destroy
end
rescue Pundit::NotAuthorizedError => e
puts "Authorization failed: #{e.message}"
# Handle error
end
```
--------------------------------
### Permit Matcher Usage Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
Use the `permit` matcher to verify that a policy grants specified permissions to a user for a record. This example tests the `update?` permission.
```ruby
describe PostPolicy do
let(:user) { User.new(admin: true) }
let(:post) { Post.new }
permissions :update? do
it { is_expected.to permit(user, post) }
end
end
```
--------------------------------
### Permissions DSL Method Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
The `permissions` helper method organizes related permission checks within a describe block. This example shows testing for both allowed and disallowed actions.
```ruby
describe PostPolicy do
let(:user) { User.new }
let(:post) { Post.new }
permissions :show? do
it { is_expected.to permit(user, post) }
end
permissions :update?, :destroy? do
it { is_expected.not_to permit(user, post) }
end
end
```
--------------------------------
### Pundit::Context Initialization Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Context.md
Demonstrates how to create a Pundit::Context instance, both automatically in Rails and manually in other frameworks.
```ruby
# In a Rails controller (automatic)
context = Pundit::Context.new(user: current_user)
# In Sinatra or other frameworks
context = Pundit::Context.new(user: current_user)
post = Post.find(params[:id])
context.authorize(post, query: :show?)
```
--------------------------------
### Install Pundit Gem
Source: https://github.com/varvet/pundit/blob/main/_autodocs/README.md
Add the Pundit gem to your Gemfile and run bundle install. Then, generate the application policy.
```ruby
# Gemfile
gem 'pundit'
# Terminal
bundle add pundit
# Generate application policy
rails generate pundit:install
```
--------------------------------
### Complete Pundit RSpec Policy Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
A comprehensive example demonstrating Pundit RSpec integration, including setup with factories, testing public posts, own posts, and admin privileges.
```ruby
require 'spec_helper'
RSpec.describe PostPolicy do
let(:user) { create(:user) }
let(:admin) { create(:user, :admin) }
let(:own_post) { create(:post, user: user) }
let(:other_post) { create(:post, user: create(:user)) }
describe "public posts" do
let(:public_post) { create(:post, published: true) }
permissions :show? do
it { is_expected.to permit(user, public_post) }
it { is_expected.to permit(admin, public_post) }
it { is_expected.to permit(nil, public_post) }
end
end
describe "own posts" do
permissions :show?, :update?, :destroy? do
it { is_expected.to permit(user, own_post) }
end
permissions :destroy? do
it { is_expected.not_to permit(user, other_post) }
end
end
describe "admin privileges" do
permissions :destroy? do
it { is_expected.to permit(admin, other_post) }
end
end
end
```
--------------------------------
### Custom Cache Store Implementation
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Example of how to implement a custom cache store that can be used with Pundit.
```APIDOC
## Custom Cache Store Implementation
Implement a custom cache store for specific caching needs:
```ruby
class UserAwareCacheStore
def initialize
@cache = {}
end
def fetch(user:, record:)
key = [user.id, record.id]
@cache[key] ||= yield
end
end
# Usage
cache = UserAwareCacheStore.new
context = Pundit::Context.new(
user: user,
policy_cache: cache
)
```
```
--------------------------------
### Permit Matcher Negation Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
Use `is_expected.not_to permit` to test that permissions are NOT granted to a user for a record.
```ruby
permissions :delete? do
it { is_expected.not_to permit(guest_user, admin_post) }
end
```
--------------------------------
### Testing Policies: RSpec Setup
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Basic RSpec setup for testing Pundit policies. This includes configuring the test environment to use Pundit.
```ruby
```
--------------------------------
### NullStore Fetch Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Illustrates the behavior of NullStore's fetch method. It always executes the provided block and returns its result, without performing any caching.
```ruby
store = Pundit::CacheStore::NullStore.instance
result1 = store.fetch(user: user, record: post) { "policy1" }
result2 = store.fetch(user: user, record: post) { "policy2" }
# result1 != result2 (different instances, no caching)
```
--------------------------------
### Cache Store Interface Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Demonstrates a typical implementation of the fetch method for a custom cache store. It uses a composite key of user and record to store and retrieve policy instances.
```ruby
def fetch(user:, record:)
key = [user, record]
@cache[key] ||= yield
end
```
--------------------------------
### Policy Class Structure Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/README.md
A simplified policy class demonstrating the `update?` permission check, comparing the record's author with the current user.
```ruby
class PostPolicy
def update?
record.author == user
end
end
```
--------------------------------
### RSpec Policy Specification Example
Source: https://github.com/varvet/pundit/blob/main/README.md
Write expressive RSpec tests for Pundit policies using the provided mini-DSL. This example tests update and edit permissions for a PostPolicy.
```ruby
describe PostPolicy do
subject { described_class }
permissions :update?, :edit? do
it "denies access if post is published"
it "grants access if post is published and user is an admin"
it "grants access if post is unpublished"
end
end
```
--------------------------------
### RSpec Policy Testing Examples
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
RSpec examples for testing `PostPolicy`, including tests for public posts and draft posts, using `permissions` and `is_expected.to permit`.
```ruby
require 'spec_helper'
RSpec.describe PostPolicy do
let(:user) { create(:user) }
let(:other_user) { create(:user) }
let(:admin) { create(:user, :admin) }
describe "public posts" do
let(:post) { create(:post, published: true) }
permissions :show? do
it { is_expected.to permit(user, post) }
it { is_expected.to permit(other_user, post) }
it { is_expected.to permit(nil, post) }
end
end
describe "draft posts" do
let(:draft) { create(:post, published: false, author: user) }
permissions :show? do
it { is_expected.to permit(user, draft) }
it { is_expected.not_to permit(other_user, draft) }
end
permissions :update? do
it { is_expected.to permit(user, draft) }
it { is_expected.not_to permit(other_user, draft) }
it { is_expected.to permit(admin, draft) }
end
end
end
```
--------------------------------
### Add Pundit Gem to Bundle
Source: https://github.com/varvet/pundit/blob/main/README.md
Install the Pundit gem using Bundler. This command adds the gem to your application's Gemfile and installs it.
```sh
bundle add pundit
```
--------------------------------
### Controller Authorization Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Helper.md
Shows the correct pattern for authorizing a record in the controller before rendering the view. The view can then safely assume that authorization has already occurred.
```ruby
# Controller
def show
@post = Post.find(params[:id])
authorize @post # Ensure user can view
end
# View assumes authorization already happened
<%= @post.title %>
```
--------------------------------
### LegacyStore Fetch Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Shows how LegacyStore fetches policies. It caches based solely on the record, which can lead to issues if the same record is accessed by different users, as demonstrated by the object_id comparison.
```ruby
cache = {}
store = Pundit::CacheStore::LegacyStore.new(cache)
# First call - yields
policy1 = store.fetch(user: user, record: post) { PostPolicy.new(user, post) }
# Second call - returns cached (same instance)
policy2 = store.fetch(user: different_user, record: post) { PostPolicy.new(different_user, post) }
# policy1.object_id == policy2.object_id (same instance, WRONG!)
```
--------------------------------
### Direct Authorization Check in Console
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Provides examples for directly authorizing a user and record, or retrieving the policy object in the Rails console.
```ruby
# In console
user = User.first
post = Post.first
Pundit.authorize(user, post, :update?) # Raises if not allowed
policy = Pundit.policy(user, post)
policy.update? # true/false
```
--------------------------------
### Add Pundit Gem and Generate Policy
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Add the Pundit gem to your Gemfile and generate the application policy. This is the initial setup step for using Pundit.
```bash
# Add to Gemfile
bundle add pundit
# Generate application policy
rails generate pundit:install
```
--------------------------------
### Initialize Pundit Context with Custom Cache
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Demonstrates how to initialize Pundit::Context with a custom cache store, specifically LegacyStore in this example. This allows for tailored caching strategies.
```ruby
cache = Pundit::CacheStore::LegacyStore.new({})
context = Pundit::Context.new(user: user, policy_cache: cache)
```
--------------------------------
### RSpec Matcher Description
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
Demonstrates how to get a descriptive string for a matcher result. Use this to understand the outcome of a permission check.
```ruby
matcher_result = permit(user, record)
puts matcher_result.description
```
--------------------------------
### Authorizing a Class Instance
Source: https://github.com/varvet/pundit/blob/main/README.md
Example of authorizing a class when no specific instance is available, typically used for actions that apply to the collection rather than a single record.
```ruby
def admin_list
authorize Post # we don't have a particular post to authorize
# Rest of controller action
end
```
--------------------------------
### Get Policy Object or Raise
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Context.md
Use `Pundit.policy!` to retrieve the policy object or raise `Pundit::NotDefinedError` if the policy is not defined.
```ruby
def self.policy!(user, *args, **kwargs, &block)
```
```
--------------------------------
### Custom Cache Store Implementation
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Provides an example of a custom cache store, UserAwareCacheStore, that uses a composite key of user ID and record ID for more precise caching. It demonstrates initialization and usage within Pundit::Context.
```ruby
class UserAwareCacheStore
def initialize
@cache = {}
end
def fetch(user:, record:)
key = [user.id, record.id]
@cache[key] ||= yield
end
end
# Usage
cache = UserAwareCacheStore.new
context = Pundit::Context.new(
user: user,
policy_cache: cache
)
```
--------------------------------
### Standard Pundit Setup in Application Controller
Source: https://github.com/varvet/pundit/blob/main/_autodocs/errors.md
Provides a common pattern for setting up Pundit's authorization and policy scoping verification filters in a base `ApplicationController`.
```ruby
class ApplicationController < ActionController::Base
include Pundit::Authorization
after_action :verify_authorized
after_action :verify_policy_scoped, only: [:index]
end
```
--------------------------------
### Authorize in Sinatra
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Example of using `Pundit.authorize` within a Sinatra application's helper methods to protect routes.
```ruby
# Sinatra
helpers do
def current_user
# User lookup
end
end
get "/posts/:id" do
Pundit.authorize(current_user, Post.find(params[:id]), :show?)
"Post content"
end
```
--------------------------------
### Manual Authorization Check Example
Source: https://github.com/varvet/pundit/blob/main/README.md
Illustrates the internal logic that Pundit's `authorize` method might perform, explicitly checking authorization and raising `Pundit::NotAuthorizedError` if unauthorized.
```ruby
unless PostPolicy.new(current_user, @post).update?
raise Pundit::NotAuthorizedError, "not allowed to PostPolicy#update? this Post"
end
```
--------------------------------
### Authorization Check Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/README.md
Perform an explicit authorization check for a specific action on a record. Raises `NotAuthorizedError` if the user is not permitted.
```ruby
authorize @post, :update?
```
--------------------------------
### Complex Logic Query Method
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
An example of a complex query method combining multiple conditions for authorization, including author, editor, and admin roles.
```ruby
def update?
# Author can always edit
return true if record.author == user
# Editors can edit published posts
return true if user.editor? && record.published?
# Admins can edit anything
return true if user.admin?
false
end
```
--------------------------------
### Custom Policy Resolution
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Example demonstrating how PolicyFinder uses a custom `policy_class` method defined on the object or its class for policy resolution.
```ruby
# Model with custom policy
class Publication
def self.policy_class
PublicationPolicy
end
end
finder = Pundit::PolicyFinder.new(Publication.new)
finder.policy # => PublicationPolicy
```
--------------------------------
### Permit Matcher with Multiple Permissions
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
The `permit` matcher can check multiple permissions specified in the `permissions` block metadata. This example checks both `:show?` and `:update?`.
```ruby
describe PostPolicy do
let(:user) { User.create(admin: true) }
let(:post) { Post.create(published: true) }
permissions :show?, :update? do
# Checks both :show? and :update? on the policy
it { is_expected.to permit(user, post) }
end
permissions :destroy? do
it { is_expected.not_to permit(user, post) }
end
end
```
--------------------------------
### Basic Post Policy Example
Source: https://github.com/varvet/pundit/blob/main/README.md
Defines a policy for a Post model, allowing updates if the user is an admin or the post is not published. Assumes the user object has an `admin?` method and the post object has a `published?` method.
```ruby
class PostPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def update?
user.admin? || !post.published?
end
end
```
--------------------------------
### Strong Parameters Definition Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/README.md
Defines permitted attributes for updating a post, with different sets of attributes allowed for administrators versus regular users.
```ruby
class PostPolicy
def permitted_attributes
user.admin? ? [:title, :body, :published] : [:title, :body]
end
end
```
--------------------------------
### Controller Actions with Permitted Attributes
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
Example controller actions demonstrating the use of `permitted_attributes` to update a post based on its ID.
```ruby
def update
post = Post.find(params[:id])
post.update(permitted_attributes(post, action: :update))
end
def publish
post = Post.find(params[:id])
post.update(permitted_attributes(post, action: :publish))
end
```
--------------------------------
### Fixing Constructor Errors in Pundit Policies
Source: https://github.com/varvet/pundit/blob/main/_autodocs/errors.md
Illustrates correct ways to define constructors for Pundit policies. It shows examples of missing parameters, accepting user and record, and accepting a splat for flexibility.
```ruby
# WRONG: Missing parameters
class PostPolicy
def initialize(user)
@user = user
end
end
# CORRECT: Accept user and record
class PostPolicy
def initialize(user, record)
@user = user
@record = record
end
end
# CORRECT: Accept splat for flexibility
class PostPolicy
def initialize(user, record, *_)
@user = user
@record = record
end
end
```
--------------------------------
### Get Policy Instance Using Module Method
Source: https://github.com/varvet/pundit/blob/main/_autodocs/README.md
Access the policy instance directly using the `Pundit.policy` module method, providing the user and the record.
```ruby
policy = Pundit.policy(user, post)
```
--------------------------------
### Initialize LegacyStore Cache
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Demonstrates initializing a LegacyStore with a provided hash and then using it to set up Pundit::Context. This store uses only the record as a cache key.
```ruby
cache = {}
store = Pundit::CacheStore::LegacyStore.new(cache)
context = Pundit::Context.new(
user: user,
policy_cache: store
)
```
--------------------------------
### PolicyFinder with Classes
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Demonstrates initializing PolicyFinder with a class to find its corresponding policy class.
```ruby
finder = Pundit::PolicyFinder.new(Post)
finder.policy # => PostPolicy
```
--------------------------------
### Get Policy Scope
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Context.md
Use `Pundit.policy_scope` to get a scoped collection based on the user's policies. Returns `nil` if no scope is found.
```ruby
def self.policy_scope(user, *args, **kwargs, &block)
```
```
```ruby
posts = Pundit.policy_scope(user, Post.all)
```
--------------------------------
### Manual Policy Scope Instantiation
Source: https://github.com/varvet/pundit/blob/main/README.md
Illustrates the manual way to instantiate and resolve a policy scope, equivalent to using `policy_scope` with a specified `policy_scope_class`. This is useful for understanding the underlying mechanism.
```ruby
def index
@publications = PublicationPolicy::Scope.new(current_user, Post).resolve
end
```
--------------------------------
### LegacyStore Constructor
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Initializes a LegacyStore with an optional backing hash. This store uses only the record as a cache key, ignoring the user.
```APIDOC
## LegacyStore
A cache store that uses only the record as a cache key, ignoring the user. This is the original cache mechanism used by Pundit.
**Deprecated:** This implementation ignores the user in the cache key, which can cause incorrect behavior when the same record is accessed by different users. Use with caution or implement a custom cache store.
### Constructor
```ruby
def initialize(hash = {})
```
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| hash | Hash | {} | The backing hash for the cache. |
**Example:**
```ruby
cache = {}
store = Pundit::CacheStore::LegacyStore.new(cache)
context = Pundit::Context.new(
user: user,
policy_cache: store
)
```
```
--------------------------------
### Post Policy Inheriting from ApplicationPolicy
Source: https://github.com/varvet/pundit/blob/main/README.md
An example of a Post policy that inherits from ApplicationPolicy. It uses `record` for the model object and `user` for the current user, with a slightly different syntax for the update check.
```ruby
class PostPolicy < ApplicationPolicy
def update?
user.admin? or not record.published?
end
end
```
--------------------------------
### Get Pundit Version
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Access the current version of the Pundit gem.
```ruby
Pundit::VERSION # => "2.5.2"
```
--------------------------------
### Performance Tips: Scopes and Eager Loading
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Demonstrates efficient data retrieval using database-level filtering with scopes and eager loading to prevent N+1 queries.
```ruby
# Good: Database query
class Scope
def resolve
scope.where(published: true).includes(:author)
end
end
# Bad: N+1 queries
class Scope
def resolve
scope.select { |post| post.published? }
end
end
```
--------------------------------
### Get Policy Class
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Retrieve the policy class for the object. Returns nil if no policy is found.
```ruby
finder = Pundit::PolicyFinder.new(post)
policy_class = finder.policy # => PostPolicy
finder = Pundit::PolicyFinder.new(:article)
policy_class = finder.policy # => ArticlePolicy
# If not found, returns nil
finder = Pundit::PolicyFinder.new(non_existent_object)
policy_class = finder.policy # => nil
```
--------------------------------
### PolicyFinder with Model Instances
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Demonstrates initializing PolicyFinder with a model instance to find its corresponding policy class.
```ruby
post = Post.new
finder = Pundit::PolicyFinder.new(post)
finder.policy # => PostPolicy
```
--------------------------------
### Retrieve Permitted Attributes from Policy
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Authorization.md
Use `#permitted_attributes` to get allowed parameters from a policy, supporting action-specific definitions.
```ruby
def permitted_attributes(record, action = action_name)
end
```
```ruby
def update
@post = Post.find(params[:id])
authorize @post
if @post.update(permitted_attributes(@post))
redirect_to @post
else
render :edit
end
end
# Policy defines permitted attributes per action:
class PostPolicy < ApplicationPolicy
def permitted_attributes
[:title, :body, :tags]
end
def permitted_attributes_for_publish
[:published_at]
end
end
```
--------------------------------
### Initialize NullStore Cache
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Shows how to initialize Pundit::Context with the NullStore, which disables caching. This is useful for testing or when caching is not desired.
```ruby
context = Pundit::Context.new(
user: user,
policy_cache: Pundit::CacheStore::NullStore.instance
)
```
--------------------------------
### Equivalence: Module Authorize vs. Context Authorize
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Demonstrates that module-level `Pundit.authorize` is a convenience wrapper equivalent to using `Pundit::Context.authorize`.
```ruby
# These are equivalent:
Pundit.authorize(user, post, :update?)
context = Pundit::Context.new(user: user)
context.authorize(post, query: :update?, policy_class: nil)
```
--------------------------------
### Permitted Attributes for Strong Parameters
Source: https://github.com/varvet/pundit/blob/main/_autodocs/README.md
Get a hash of permitted attributes for updating a record, useful for strong parameters.
```ruby
post.update(permitted_attributes(@post))
```
--------------------------------
### Testing for NotAuthorizedError
Source: https://github.com/varvet/pundit/blob/main/_autodocs/errors.md
Shows an RSpec example for testing that a `Pundit::NotAuthorizedError` is raised when a user lacks permission for an action.
```ruby
RSpec.describe PostsController do
describe "PUT #update" do
it "raises NotAuthorizedError when user lacks permission" do
post = create(:post)
expect {
put :update, params: { id: post, post: { title: "New" } }
}.to raise_error(Pundit::NotAuthorizedError)
end
end
end
```
--------------------------------
### Authorize in Roda
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Example of using `Pundit.authorize` within a Roda application's route definition to protect endpoints.
```ruby
# Roda
route do |r|
r.get "posts", Integer do |id|
Pundit.authorize(current_user, Post.find(id), :show?)
"Post content"
end
end
```
--------------------------------
### Define User and Record in Policy
Source: https://github.com/varvet/pundit/blob/main/_autodocs/configuration.md
Initialize a policy with user and record instance variables. This is a common pattern for accessing these objects within policy methods.
```ruby
class PostPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def update?
record.author == user || user.admin?
end
end
```
```ruby
class PostPolicy
def initialize(user, post)
@user = user
@post = post
end
alias_method :post, :record
def update?
post.author == user
end
end
```
--------------------------------
### Using Policy Scope in Controller
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
Demonstrates how to use the `policy_scope` helper in a controller to apply the defined scope to a collection.
```ruby
class PostsController < ApplicationController
def index
@posts = policy_scope(Post.all)
# Returns published posts for regular users, all posts for admins
end
end
```
--------------------------------
### Automatic Inclusion with type: :policy Tag
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
RSpec integration is automatically included for examples tagged with `type: :policy`.
```ruby
describe PostPolicy, type: :policy do
# RSpec integration available
end
```
--------------------------------
### Automatic RSpec Integration Setup
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
RSpec integration is automatically included for files matching `spec/policies` or tagged with `type: :policy`.
```ruby
# Automatically included for spec/policies/**/*_spec.rb files
describe PostPolicy do
# RSpec integration is available
end
```
--------------------------------
### Get Policy Scope or Raise
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Context.md
Use `Pundit.policy_scope!` to retrieve a scoped collection or raise `Pundit::NotDefinedError` if the scope is not defined.
```ruby
def self.policy_scope!(user, *args, **kwargs, &block)
```
```
--------------------------------
### Basic Policy Class Structure
Source: https://github.com/varvet/pundit/blob/main/_autodocs/configuration.md
Define a policy class with an initializer that accepts a user and a record, and includes query methods returning booleans.
```ruby
class PostPolicy
def initialize(user, record)
@user = user
@record = record
end
# Query methods - return boolean
def show?
true
end
def update?
@record.user == @user
end
def destroy?
@user.admin?
end
end
```
--------------------------------
### Get Parameter Key
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Determine the key name for the object in a Rails params hash, used for methods like permitted_attributes.
```ruby
finder = Pundit::PolicyFinder.new(post)
finder.param_key # => "post"
finder = Pundit::PolicyFinder.new(Blog::Article)
finder.param_key # => "article"
finder = Pundit::PolicyFinder.new([:admin, user])
finder.param_key # => "user"
finder = Pundit::PolicyFinder.new(:publication)
finder.param_key # => "publication"
```
--------------------------------
### PolicyFinder with Symbols
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Demonstrates initializing PolicyFinder with a symbol to find its corresponding policy class.
```ruby
finder = Pundit::PolicyFinder.new(:publication)
finder.policy # => PublicationPolicy
```
--------------------------------
### Controller Action for Headless Policy
Source: https://github.com/varvet/pundit/blob/main/README.md
Demonstrates how to authorize a headless policy in a controller by passing a symbol representing the concept to the `authorize` method.
```ruby
# In controllers
def show
authorize :dashboard, :show?
...
end
```
--------------------------------
### Roda Context Integration
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Context.md
Example of integrating Pundit::Context within a Roda application's routes for authorization checks.
```ruby
# Roda example
route do |r|
context = Pundit::Context.new(user: current_user)
r.get "posts", Integer do |id|
context.authorize(Post.find(id), query: :show?)
end
end
```
--------------------------------
### Controller Callbacks for Authorization
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Sets up controller callbacks to automatically verify authorization after actions.
```ruby
class ApplicationController < ActionController::Base
after_action :verify_authorized, except: [:index, :show]
after_action :verify_policy_scoped, only: [:index]
end
```
--------------------------------
### Equivalence: Module Policy vs. Context Policy
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Shows that module-level `Pundit.policy` is equivalent to using `Pundit::Context.policy`.
```ruby
# These are equivalent:
policy = Pundit.policy(user, post)
context = Pundit::Context.new(user: user)
policy = context.policy(post)
```
--------------------------------
### Get Memoized Pundit Context
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Authorization.md
The protected `#pundit` method returns a memoized `Pundit::Context` instance used internally by Pundit.
```ruby
def pundit
end
```
--------------------------------
### Get Policy Class (with Error)
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Retrieve the policy class for the object, raising NotDefinedError if not found. Use this when a policy is expected to exist.
```ruby
finder = Pundit::PolicyFinder.new(post)
begin
policy_class = finder.policy! # => PostPolicy
rescue Pundit::NotDefinedError
puts "No policy found for post!"
end
```
--------------------------------
### Query Methods Naming Convention
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
Illustrates the standard naming convention for query methods (permissions) in a policy class, such as `show?`, `create?`, `update?`, and `destroy?`.
```ruby
class PostPolicy
def show?
# Can user view this post?
end
def create?
# Can user create posts?
end
def update?
# Can user update this post?
end
def destroy?
# Can user delete this post?
end
def publish?
# Can user publish this post?
end
end
```
--------------------------------
### Context-Aware Policy Initialization
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
A `PostPolicy` that accepts an optional `context` hash during initialization to influence authorization decisions, such as checking for a locked record.
```ruby
class PostPolicy
def initialize(user, record, context = {})
@user = user
@record = record
@context = context
end
def update?
return false unless @user
return true if @user.admin?
return true if @record.author == @user && !@context[:locked]
false
end
end
# Usage (if using custom Context initialization)
policy = PostPolicy.new(user, post, locked: true)
```
--------------------------------
### Creating a Missing Policy
Source: https://github.com/varvet/pundit/blob/main/_autodocs/errors.md
Shows how to define a policy class to resolve a Pundit::NotDefinedError.
```ruby
# Create the policy to fix NotDefinedError
class PostPolicy
def initialize(user, record)
@user = user
@record = record
end
def create?
user.admin?
end
def show?
true
end
# No error now
end
```
--------------------------------
### Sinatra Context Integration
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Context.md
Example of integrating Pundit::Context within a Sinatra application's helpers to provide authorization methods.
```ruby
# Sinatra example
helpers do
def current_user
# User lookup logic
end
def pundit
@pundit ||= Pundit::Context.new(user: current_user)
end
end
get "/posts/:id" do |id|
pundit.authorize(Post.find(id), query: :show?)
# Authorization passed, continue...
end
```
--------------------------------
### Get Policy Object
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Context.md
Use `Pundit.policy` to retrieve the policy object for a given user and record. This method forwards arguments to `Context#policy`.
```ruby
def self.policy(user, *args, **kwargs, &block)
```
```
```ruby
policy = Pundit.policy(user, post)
```
--------------------------------
### Get Scope Class (with Error)
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Retrieve the policy scope class for the object, raising NotDefinedError if not found. Use this when a scope is expected to exist.
```ruby
finder = Pundit::PolicyFinder.new(post)
begin
scope_class = finder.scope! # => PostPolicy::Scope
rescue Pundit::NotDefinedError
puts "No scope defined for posts!"
end
```
--------------------------------
### Get Policy Instance for Record
Source: https://github.com/varvet/pundit/blob/main/_autodocs/README.md
Retrieve the policy instance associated with a record using the `policy` helper. Returns `nil` if no policy is found.
```ruby
policy = policy(@post)
```
--------------------------------
### Handling Missing Policies Gracefully
Source: https://github.com/varvet/pundit/blob/main/_autodocs/errors.md
Illustrates a method to safely retrieve a policy, returning nil if not found, and falling back to a default policy.
```ruby
def policy(record)
finder = Pundit::PolicyFinder.new(record)
policy = finder.policy # Returns nil if not found
if policy.nil?
# Use default policy or handle gracefully
DefaultPolicy.new(current_user, record)
else
policy.new(current_user, record)
end
end
```
--------------------------------
### Scope Class Example
Source: https://github.com/varvet/pundit/blob/main/_autodocs/README.md
A scope class that filters a collection to only include published posts. This is used to ensure users only see relevant data.
```ruby
class PostPolicy
class Scope
def resolve
scope.where(published: true) # Only published posts
end
end
end
```
--------------------------------
### NullStore#instance
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Returns the singleton instance of NullStore. This store does not cache anything and always executes the provided block.
```APIDOC
## NullStore
A cache store that does not cache anything. It always executes the block and returns the result.
### `#instance`
Returns the singleton instance of NullStore. Thread-safe.
```ruby
def self.instance
```
**Returns:** NullStore
**Example:**
```ruby
cache = Pundit::CacheStore::NullStore.instance
policy = cache.fetch(user: user, record: post) { create_policy }
```
```
--------------------------------
### Common Mistake: Missing Constructor Arguments
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
Illustrates the incorrect way to define a policy constructor missing the `record` argument and the correct way, which accepts both `user` and `record`.
```ruby
# WRONG: Missing record parameter
class PostPolicy
def initialize(user)
@user = user
end
end
# CORRECT: Accept both user and record
class PostPolicy
def initialize(user, record)
@user = user
@record = record
end
end
```
--------------------------------
### Get NullStore Singleton Instance
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Retrieves the singleton instance of NullStore. This method is thread-safe and ensures only one instance of NullStore is used throughout the application.
```ruby
def self.instance
```
--------------------------------
### Custom pundit_params_for without require
Source: https://github.com/varvet/pundit/blob/main/README.md
Example of overriding `pundit_params_for` to use `fetch` instead of `require`, providing a default empty hash if the key is missing.
```ruby
# If you don't want to use require
def pundit_params_for(record)
params.fetch(pundit_param_key(record), {})
end
```
--------------------------------
### Custom Permit Matcher Description Configuration
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/RSpec.md
Configure a custom description for the `permit` matcher using a string or a Proc for dynamic descriptions. Remember to reset to default in an after hook.
```ruby
Pundit::RSpec::Matchers.description = "permit user to perform action on record"
# Or with a Proc for dynamic descriptions:
Pundit::RSpec::Matchers.description = lambda { |user, record|
"permit user with role #{user.role} to access record ##{record.id}"
}
# In after hook, reset to default
after do
Pundit::RSpec::Matchers.description = nil
end
```
--------------------------------
### Troubleshooting: Pundit::NotDefinedError
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Provides a solution for the 'unable to find policy' error by creating the necessary policy file and basic methods.
```ruby
Error: Pundit::NotDefinedError: unable to find policy `PostPolicy`
# Solution: Create the policy file
# app/policies/post_policy.rb
class PostPolicy
def initialize(user, record)
@user = user
@record = record
end
def show?
true
end
end
```
--------------------------------
### Correct Controller Authorization for UX
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Helper.md
Demonstrates the correct approach to authorization for UX enhancements, where the controller handles security checks and the view relies on this.
```ruby
# CORRECT: Authorize in controller
def admin_dashboard
authorize :admin_panel, :view?
render 'admin/dashboard'
end
```
--------------------------------
### Controller Action Using authorize
Source: https://github.com/varvet/pundit/blob/main/README.md
Demonstrates a controller action that finds a post and uses the `authorize` method to check if the current user can update it. The `authorize` method infers the policy and action.
```ruby
def update
@post = Post.find(params[:id])
authorize @post
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
```
--------------------------------
### Legacy Cache Store Constructor
Source: https://github.com/varvet/pundit/blob/main/_autodocs/types.md
Initializes the LegacyStore with an optional hash. This store uses only the record as a cache key and is deprecated.
```ruby
def initialize(hash = {})
```
--------------------------------
### Accessing User and Record in Policy
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Illustrates how to access the current user and the record being authorized within a policy class.
```ruby
class PostPolicy
def show?
puts "User: #{user.inspect}"
puts "Record: #{record.inspect}"
true
end
end
```
--------------------------------
### Get Policy Instance or Raise Error
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Retrieves the policy for a record. This method raises `Pundit::NotDefinedError` if the policy cannot be found. Use this when a policy is expected to exist.
```ruby
begin
policy = Pundit.policy!(user, post)
rescue Pundit::NotDefinedError
puts "Policy not found"
end
```
--------------------------------
### Initialize PolicyFinder
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Instantiate PolicyFinder with a model instance, class, symbol, or array of namespace segments.
```ruby
finder = Pundit::PolicyFinder.new(post)
finder = Pundit::PolicyFinder.new(Post) # Class
finder = Pundit::PolicyFinder.new(:publication) # Symbol
finder = Pundit::PolicyFinder.new([:admin, post]) # Namespace
```
--------------------------------
### Render Posts with Local Options
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Helper.md
Demonstrates rendering a collection of posts and passing local variables to the partial for conditional display of admin options.
```erb
<%= render @posts, locals: { show_admin_options: true } %>
```
```erb
<%= post.title %>
<%= post.body %>
<% if show_admin_options && policy(post).update? %>
<%= link_to 'Edit', edit_post_path(post) %>
<%= link_to 'Delete', post_path(post), method: :delete %>
<% end %>
```
--------------------------------
### Get Policy Instance
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Retrieves the policy for a record. This method does not raise an error if the policy is not found, returning `nil` instead. Useful for checking permissions conditionally.
```ruby
policy = Pundit.policy(user, post)
if policy
if policy.update?
# Allow update
end
else
# No policy found
end
```
--------------------------------
### Get Policy Suffix
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Retrieve the default suffix appended to class names to find policy classes. Note: `Pundit::PolicyFinder::SUFFIX` is the recommended alternative.
```ruby
Pundit::SUFFIX # => "Policy"
```
--------------------------------
### Usage in Context
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/CacheStore.md
Demonstrates how to pass cache stores to Pundit::Context during initialization.
```APIDOC
## Usage in Context
Cache stores are passed to `Pundit::Context` at initialization:
```ruby
# No caching (default)
context = Pundit::Context.new(user: user)
# With custom cache
cache = Pundit::CacheStore::LegacyStore.new({})
context = Pundit::Context.new(user: user, policy_cache: cache)
```
```
--------------------------------
### Custom pundit_params_for for JSON API spec
Source: https://github.com/varvet/pundit/blob/main/README.md
Example of overriding `pundit_params_for` to handle parameter fetching according to the JSON API specification, targeting the `attributes` hash.
```ruby
# If you are using something like the JSON API spec
def pundit_params_for(_record)
params.fetch(:data, {}).fetch(:attributes, {})
end
```
--------------------------------
### I18n Configuration for Pundit Errors
Source: https://github.com/varvet/pundit/blob/main/README.md
Example YAML configuration for internationalizing Pundit error messages, mapping policy names and queries to specific user-facing strings.
```yaml
en:
pundit:
default: 'You cannot perform this action.'
post_policy:
update?: 'You cannot edit this post!'
create?: 'You cannot create posts!'
```
--------------------------------
### Scope Collections: Basic Scope
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Define a `Scope` class within your policy to control which records are returned by `policy_scope`. This example scopes posts to only published ones.
```ruby
class PostPolicy
class Scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
@scope.where(published: true)
end
end
end
# In controller
@posts = policy_scope(Post.all)
```
--------------------------------
### View Helper for Headless Policy
Source: https://github.com/varvet/pundit/blob/main/README.md
Shows how to use the `policy` helper in a view to check authorization for a headless policy, using a symbol to represent the concept.
```erb
# In views
<% if policy(:dashboard).show? %>
<%= link_to 'Dashboard', dashboard_path %>
<% end %>
```
--------------------------------
### Complex Scoping Logic
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
An example of a complex `Scope` class that resolves collections based on user roles (admin, moderator, author) with different filtering criteria.
```ruby
class PostPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
case @user.role
when 'admin'
@scope.all
when 'moderator'
@scope.where.not(status: 'spam')
when 'author'
@scope.where(author_id: @user.id).or(
@scope.where(published: true)
)
else
@scope.where(published: true)
end
end
end
end
```
--------------------------------
### Minimal Policy Structure
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
Defines a basic policy class with an initializer and query methods for viewing, updating, and destroying a post.
```ruby
class PostPolicy
def initialize(user, post)
@user = user
@post = post
end
def show?
true # Everyone can view posts
end
def update?
@post.author == @user # Only author can edit
end
def destroy?
@user.admin? # Only admins can delete
end
end
```
--------------------------------
### Policy Scope Resolution
Source: https://github.com/varvet/pundit/blob/main/_autodocs/types.md
Example of implementing the `resolve` method within a policy's Scope class to filter a collection. This method should return the filtered scope.
```ruby
class RecordPolicy
class Scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
# Return filtered collection
@scope.where(public: true)
end
end
end
```
--------------------------------
### Get Scope Class
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Retrieve the policy scope class for the object. Returns nil if no scope is found. The scope class should respond to #initialize(user, record) and #resolve.
```ruby
finder = Pundit::PolicyFinder.new(post)
scope_class = finder.scope # => PostPolicy::Scope
# Use the scope
scope = scope_class.new(user, Post.all)
scoped_posts = scope.resolve # Returns filtered posts
```
--------------------------------
### Base ApplicationPolicy Class
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
Defines a base `ApplicationPolicy` with common authorization logic for user, record, and scope initialization. Includes default implementations for actions like `show?`, `create?`, `update?`, and `destroy?`.
```ruby
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def show?
true
end
def create?
user.present?
end
def new?
create?
end
def update?
user.present? && (record.user == user || user.admin?)
end
def edit?
update?
end
def destroy?
user.admin?
end
class Scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
@scope
end
end
end
```
--------------------------------
### Customizing NotAuthorizedError Messages
Source: https://github.com/varvet/pundit/blob/main/README.md
Connect Pundit's NotAuthorizedError properties with I18n to generate dynamic error messages. This example shows how to implement a rescue_from method in ApplicationController.
```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
```
--------------------------------
### Policy Method: Allow Everyone
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Implement a policy method that always returns true to grant access to all users. Use this for public-facing actions.
```ruby
def show?
true
end
```
--------------------------------
### Using policy_scope in ERB Views
Source: https://github.com/varvet/pundit/blob/main/README.md
Provides an example of how to use the `policy_scope` method directly within an ERB view to iterate over records that have been filtered by their respective policy scopes.
```erb
<% policy_scope(@user.posts).each do |post| %>
<%= link_to post.title, post_path(post) %>
<% end %>
```
--------------------------------
### Scope Collections: With Eager Loading
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Enhance the scope resolution by including eager loading for associated records to optimize performance. This example eager loads `author` and `comments`.
```ruby
class Scope
def resolve
@scope.includes(:author, :comments)
.where(published: true)
end
end
```
--------------------------------
### Policy with Aliases
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/Policies.md
Demonstrates using `alias_method` to create a more convenient alias for the record within the policy.
```ruby
class PostPolicy
def initialize(user, post)
@user = user
@post = post
end
alias_method :post, :record
def update?
post.author == user
end
def destroy?
false
end
end
```
--------------------------------
### Get Scoped Collection or Raise Error
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/MainModule.md
Retrieves a scoped collection for a user and record. This method raises `Pundit::NotDefinedError` if the scope cannot be found. Use this when a scope is expected to exist.
```ruby
begin
posts = Pundit.policy_scope!(user, Post.all)
posts.each { |post| puts post.title }
rescue Pundit::NotDefinedError
puts "Scope not defined for Post"
end
```
--------------------------------
### Find Policy Without Raising Error
Source: https://github.com/varvet/pundit/blob/main/_autodocs/api-reference/PolicyFinder.md
Demonstrates how to safely find a policy class for a given object. If no policy is defined, it returns nil, allowing for fallback to default behavior.
```ruby
finder = Pundit::PolicyFinder.new(user)
policy = finder.policy
if policy
# Policy exists, use it
else
# No policy defined, use default behavior
end
```
--------------------------------
### Create a Post Policy
Source: https://github.com/varvet/pundit/blob/main/_autodocs/QUICK-START.md
Define a policy for the Post model, specifying authorization rules for actions like `show`, `create`, `update`, and `destroy`, as well as a `Scope` for collection authorization. This policy determines who can perform what actions on posts.
```ruby
# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
def show?
record.published? || record.author == user
end
def create?
user.present?
end
def update?
record.author == user
end
def destroy?
user.admin?
end
class Scope < ApplicationPolicy::Scope
def resolve
if user.admin?
scope.all
else
scope.where(published: true)
end
end
end
end
```