Skip to content

Commit

Permalink
Update YARD documentation
Browse files Browse the repository at this point in the history
* Remnants from the first pass when introducing Pundit::Context.
* Further clean up the YARDOC for Authorization/Context
* Fix references to Pundit context in class methods
* Add YARDOC to the rspec helper classes
* Regroup documentation of Pundit::Context

Add some documentation to the new objects
  • Loading branch information
Burgestrand committed Oct 11, 2024
1 parent d0b723b commit 0b9b3a6
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 110 deletions.
11 changes: 6 additions & 5 deletions lib/pundit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module Pundit
SUFFIX = "Policy"

# @api private
# @private
module Generators; end

# Error that will be raised when authorization has failed
Expand Down Expand Up @@ -70,7 +71,7 @@ def self.included(base)
end

class << self
# @see [Pundit::Context#authorize]
# @see Pundit::Context#authorize
def authorize(user, record, query, policy_class: nil, cache: nil)
context = if cache
Context.new(user: user, policy_cache: cache)
Expand All @@ -81,22 +82,22 @@ def authorize(user, record, query, policy_class: nil, cache: nil)
context.authorize(record, query: query, policy_class: policy_class)
end

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

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

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

# @see [Pundit::Context#policy!]
# @see Pundit::Context#policy!
def policy!(user, *args, **kwargs, &block)
Context.new(user: user).policy!(*args, **kwargs, &block)
end
Expand Down
205 changes: 125 additions & 80 deletions lib/pundit/authorization.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# frozen_string_literal: true

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

Expand All @@ -15,47 +23,30 @@ module Authorization

protected

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

# @return [Boolean] whether authorization has been performed, i.e. whether
# one {#authorize} or {#skip_authorization} has been called
def pundit_policy_authorized?
!!@_pundit_policy_authorized
end

# @return [Boolean] whether policy scoping has been performed, i.e. whether
# one {#policy_scope} or {#skip_policy_scope} has been called
def pundit_policy_scoped?
!!@_pundit_policy_scoped
end

# Raises an error if authorization has not been performed, usually used as an
# `after_action` filter to prevent programmer error in forgetting to call
# {#authorize} or {#skip_authorization}.
# Hook method which allows customizing which user is passed to policies and
# scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
#
# @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
# @raise [AuthorizationNotPerformedError] if authorization has not been performed
# @return [void]
def verify_authorized
raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
# @see https://github.com/varvet/pundit#customize-pundit-user
# @return [Object] the user object to be used with pundit
def pundit_user
current_user
end

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

# Retrieves the policy for the given record, initializing it with the record
# and current user and finally throwing an error if the user is not
Expand All @@ -66,7 +57,9 @@ def verify_policy_scoped
# If omitted then this defaults to the Rails controller action name.
# @param policy_class [Class] the policy class we want to force use of
# @raise [NotAuthorizedError] if the given query method returned false
# @return [Object] Always returns the passed object record
# @return [record] Always returns the passed object record
# @see Pundit::Context#authorize
# @see #verify_authorized
def authorize(record, query = nil, policy_class: nil)
query ||= "#{action_name}?"

Expand All @@ -79,29 +72,45 @@ def authorize(record, query = nil, policy_class: nil)
#
# @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
# @return [void]
# @see #verify_authorized
def skip_authorization
@_pundit_policy_authorized = :skipped
end

# Allow this action not to perform policy scoping.
# @return [Boolean] wether or not authorization has been performed
# @see #authorize
# @see #skip_authorization
def pundit_policy_authorized?
!!@_pundit_policy_authorized
end

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

# Retrieves the policy scope for the given record.
# rubocop:disable Naming/MemoizedInstanceVariableName

# Cache of policies. You should not rely on this method.
#
# @see https://github.com/varvet/pundit#scopes
# @param scope [Object] the object we're retrieving the policy scope for
# @param policy_scope_class [Class] the policy scope class we want to force use of
# @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
def policy_scope(scope, policy_scope_class: nil)
@_pundit_policy_scoped = true
policy_scope_class ? policy_scope_class.new(pundit_user, scope).resolve : pundit_policy_scope(scope)
# @api private
def policies
@_pundit_policies ||= {}
end

# rubocop:enable Naming/MemoizedInstanceVariableName

# @!endgroup

# Retrieves the policy for the given record.
#
# @see https://github.com/varvet/pundit#policies
Expand All @@ -111,11 +120,78 @@ def policy(record)
pundit.policy!(record)
end

# Retrieves a set of permitted attributes from the policy by instantiating
# the policy class for the given record and calling `permitted_attributes` on
# it, or `permitted_attributes_for_{action}` if `action` is defined. It then infers
# what key the record should have in the params hash and retrieves the
# permitted attributes from the params hash under that key.
# @!group Policy Scopes

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

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

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

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

# rubocop:disable Naming/MemoizedInstanceVariableName

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

# rubocop:enable Naming/MemoizedInstanceVariableName

# @api private
def pundit_policy_scope(scope)
policy_scopes[scope] ||= pundit.policy_scope!(scope)
end
private :pundit_policy_scope

# @!endgroup

# @!group Strong Parameters

# Retrieves a set of permitted attributes from the policy.
#
# Done by instantiating the policy class for the given record and calling
# `permitted_attributes` on it, or `permitted_attributes_for_{action}` if
# `action` is defined. It then infers what key the record should have in the
# params hash and retrieves the permitted attributes from the params hash
# under that key.
#
# @see https://github.com/varvet/pundit#strong-parameters
# @param record [Object] the object we're retrieving permitted attributes for
Expand All @@ -140,37 +216,6 @@ def pundit_params_for(record)
params.require(PolicyFinder.new(record).param_key)
end

# Cache of policies. You should not rely on this method.
#
# @api private
# rubocop:disable Naming/MemoizedInstanceVariableName
def policies
@_pundit_policies ||= {}
end
# rubocop:enable Naming/MemoizedInstanceVariableName

# Cache of policy scope. You should not rely on this method.
#
# @api private
# rubocop:disable Naming/MemoizedInstanceVariableName
def policy_scopes
@_pundit_policy_scopes ||= {}
end
# rubocop:enable Naming/MemoizedInstanceVariableName

# Hook method which allows customizing which user is passed to policies and
# scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
#
# @see https://github.com/varvet/pundit#customize-pundit-user
# @return [Object] the user object to be used with pundit
def pundit_user
current_user
end

private

def pundit_policy_scope(scope)
policy_scopes[scope] ||= pundit.policy_scope!(scope)
end
# @!endgroup
end
end
4 changes: 4 additions & 0 deletions lib/pundit/cache_store/legacy_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

module Pundit
module CacheStore
# A cache store that uses only the record as a cache key, and ignores the user.
#
# The original cache mechanism used by Pundit.
#
# @api private
class LegacyStore
def initialize(hash = {})
Expand Down
6 changes: 6 additions & 0 deletions lib/pundit/cache_store/null_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

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

class << self
# @return [NullStore] the singleton instance
attr_reader :instance
end

Expand Down
Loading

0 comments on commit 0b9b3a6

Please sign in to comment.