Skip to content

javierav/pundit-before

Repository files navigation

Pundit::Before

CI

Adds before hook to pundit policy classes to resolve things like varvet/pundit#474. Inspired by action_policy pre-checks.

Installation

Add this line to your application's Gemfile:

gem "pundit-before"

And then execute:

bundle install

Usage

Use allow! inside callback method or block to return true without evaluating edit? method defined in policy.

class UserPolicy < ApplicationPolicy
  include Pundit::Before

  before :check_admin

  def edit?
    false
  end

  private

  def check_admin
    allow! if user.admin?
  end
end

UserPolicy.new(User.new(admin: true), record).edit?  # => true
UserPolicy.new(User.new(admin: false), record).edit? # => false

Use deny! inside callback method or block to return false without evaluating edit? method defined in policy.

class UserPolicy < ApplicationPolicy
  include Pundit::Before

  before :check_admin

  def edit?
    true
  end

  private

  def check_admin
    deny! unless user.admin?
  end
end

UserPolicy.new(User.new(admin: true), record).edit?  # => true
UserPolicy.new(User.new(admin: false), record).edit? # => false

Internally before hook is implemented as ActiveSupport::Callbacks, so the callback chain will halt if do any call to allow! or deny! method. It's similar as Rails controller action filters works.

block form

class UserPolicy < ApplicationPolicy
  include Pundit::Before

  before do
    allow! if user.admin?
  end

  def edit?
    false
  end
end

skip before hook

class UserPolicy < ApplicationPolicy
  include Pundit::Before

  before :check_admin

  def edit?
    false
  end

  private

  def check_admin
    allow! if user.admin?
  end
end

class OperatorPolicy < UserPolicy
  skip_before :check_admin
end

UserPolicy.new(User.new(admin: true), record).edit?     # => true
OperatorPolicy.new(User.new(admin: true), record).edit? # => false

using only modifier

class UserPolicy < ApplicationPolicy
  include Pundit::Before

  before :check_admin, only: :update?

  def edit?
    false
  end

  private

  def check_admin
    allow! if user.admin?
  end
end

UserPolicy.new(User.new(admin: true), record).edit? # => false

using except modifier

class UserPolicy < ApplicationPolicy
  include Pundit::Before

  before :check_admin, except: :edit?

  def edit?
    false
  end

  def destroy?
    false
  end

  private

  def check_admin
    allow! if user.admin?
  end
end

UserPolicy.new(User.new(admin: true), record).edit?    # => false
UserPolicy.new(User.new(admin: true), record).destroy? # => true

calling multiple methods

class UserPolicy < BasePolicy
  before :check_presence, :check_admin

  def edit?
    false
  end

  private

  def check_presence
    deny! unless user.present?
  end

  def check_admin
    allow! if user.admin?
  end
end

UserPolicy.new(nil, record).edit?                    # => false
UserPolicy.new(User.new(admin: false), record).edit? # => false
UserPolicy.new(User.new(admin: true), record).edit?  # => true

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bin/rake install.

To release a new version, update the version number in version.rb, and then run bin/rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

License

Copyright © 2023 Javier Aranda. Released under the terms of the MIT license.