Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Janitorial: Add documentation to most things #842

Merged
merged 1 commit into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ end

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

task default: :spec
1 change: 1 addition & 0 deletions lib/generators/rspec/policy_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
module Rspec
# @private
module Generators
# @private
class PolicyGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)

Expand Down
1 change: 1 addition & 0 deletions lib/generators/test_unit/policy_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
module TestUnit
# @private
module Generators
# @private
class PolicyGenerator < ::Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)

Expand Down
27 changes: 25 additions & 2 deletions lib/pundit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require "pundit/policy_finder"
require "pundit/authorization"
require "pundit/context"
require "pundit/cache_store"
require "pundit/cache_store/null_store"
require "pundit/cache_store/legacy_store"

Expand All @@ -14,6 +15,8 @@
# keep it here with compact class style definition.
class Pundit::Error < StandardError; end # rubocop:disable Style/ClassAndModuleChildren

# Hello? Yes, this is Pundit.
#
# @api public
module Pundit
# @api private
Expand All @@ -26,8 +29,24 @@ module Generators; end

# Error that will be raised when authorization has failed
class NotAuthorizedError < Error
attr_reader :query, :record, :policy

# @see #initialize
attr_reader :query
# @see #initialize
attr_reader :record
# @see #initialize
attr_reader :policy

# @overload initialize(message)
# Create an error with a simple error message.
# @param [String] message A simple error message string.
#
# @overload initialize(options)
# Create an error with the specified attributes.
# @param [Hash] options The error options.
# @option options [String] :message Optional custom error message. Will default to a generalized message.
# @option options [Symbol] :query The name of the policy method that was checked.
# @option options [Object] :record The object that was being checked with the policy.
# @option options [Class] :policy The class of policy that was used for the check.
def initialize(options = {})
if options.is_a? String
message = options
Expand Down Expand Up @@ -103,8 +122,12 @@ def policy!(user, *args, **kwargs, &block)
end
end

# Rails view helpers, to allow a slightly different view-specific
# implementation of the methods in {Pundit::Authorization}.
#
# @api private
module Helper
# @see Pundit::Authorization#pundit_policy_scope
def policy_scope(scope)
pundit_policy_scope(scope)
end
Expand Down
9 changes: 9 additions & 0 deletions lib/pundit/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ def policy_scopes

# rubocop:enable Naming/MemoizedInstanceVariableName

# This was added to allow calling `policy_scope!` without flipping the
# `pundit_policy_scoped?` flag.
#
# It's used internally by `policy_scope`, as well as from the views
# when they call `policy_scope`. It works because views get their helper
# from {Pundit::Helper}.
#
# @note This also memoizes the instance with `scope` as the key.
# @see Pundit::Helper#policy_scope
# @api private
def pundit_policy_scope(scope)
policy_scopes[scope] ||= pundit.policy_scope!(scope)
Expand Down
22 changes: 22 additions & 0 deletions lib/pundit/cache_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

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

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

# @!endgroup
end
end
3 changes: 3 additions & 0 deletions lib/pundit/cache_store/legacy_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ def initialize(hash = {})
@store = hash
end

# A cache store that uses only the record as a cache key, and ignores the user.
#
# @note `nil` results are not cached.
def fetch(user:, record:)
_ = user
@store[record] ||= yield
Expand Down
3 changes: 3 additions & 0 deletions lib/pundit/cache_store/null_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class << self
attr_reader :instance
end

# Always yields, does not cache anything.
# @yield
# @return [any] whatever the block returns.
def fetch(*, **)
yield
end
Expand Down
17 changes: 17 additions & 0 deletions lib/pundit/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def initialize(user:, policy_cache: CacheStore::NullStore.instance)
attr_reader :user

# @api private
# @see #initialize
attr_reader :policy_cache

# @!group Policies
Expand Down Expand Up @@ -134,6 +135,15 @@ def policy_scope!(scope)

# @!group Private Helpers

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

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

# Given a possibly namespaced record, return the actual record.
#
# @api private
def pundit_model(record)
record.is_a?(Array) ? record.last : record
end
Expand Down
17 changes: 16 additions & 1 deletion lib/pundit/policy_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ module Pundit
# finder.scope #=> UserPolicy::Scope
#
class PolicyFinder
# A constant applied to the end of the class name to find the policy class.
#
# @api private
SUFFIX = "Policy"

# @see #initialize
attr_reader :object

# @param object [any] the object to find policy and scope classes for
#
def initialize(object)
@object = object
end
Expand Down Expand Up @@ -76,6 +78,11 @@ def param_key # rubocop:disable Metrics/AbcSize

private

# Given an object, find the policy class name.
#
# Uses recursion to handle namespaces.
#
# @return [String, Class] the policy class, or its name.
def find(subject)
if subject.is_a?(Array)
modules = subject.dup
Expand All @@ -92,6 +99,14 @@ def find(subject)
end
end

# Given an object, find its' class name.
#
# - Supports ActiveModel.
# - Supports regular classes.
# - Supports symbols.
# - Supports object instances.
#
# @return [String, Class] the class, or its name.
def find_class_name(subject)
if subject.respond_to?(:model_name)
subject.model_name
Expand Down
2 changes: 2 additions & 0 deletions lib/pundit/rspec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
require "active_support/core_ext/array/conversions"

module Pundit
# Namespace for Pundit's RSpec integration.
module RSpec
# Namespace for Pundit's RSpec matchers.
module Matchers
extend ::RSpec::Matchers::DSL

Expand Down
1 change: 1 addition & 0 deletions lib/pundit/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

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