### 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 ```