Skip to content

Commit

Permalink
fix: skip context memoization when explicit context is provided
Browse files Browse the repository at this point in the history
Closes #266

Closes #267

Fixes #265
  • Loading branch information
palkan committed Jun 11, 2024
1 parent 2a3e962 commit ae2970b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 3 deletions.
8 changes: 5 additions & 3 deletions lib/action_policy/behaviours/policy_for.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module PolicyFor

# Returns policy instance for the record.
def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
context = context ? authorization_context.merge(context) : authorization_context
context = context ? build_authorization_context.merge(context) : authorization_context

policy_class = with || ::ActionPolicy.lookup(
record,
Expand All @@ -18,8 +18,10 @@ def policy_for(record:, with: nil, namespace: authorization_namespace, context:
policy_class&.new(record, **context)
end

def authorization_context
Kernel.raise NotImplementedError, "Please, define `authorization_context` method!"
def authorization_context = @authorization_context ||= build_authorization_context

def build_authorization_context
Kernel.raise NotImplementedError, "Please, define `build_authorization_context` method!"
end

def authorization_namespace
Expand Down
1 change: 1 addition & 0 deletions lib/action_policy/policy/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def included(base)
end

attr_reader :authorization_context
alias_method :build_authorization_context, :authorization_context

def initialize(record = nil, **params)
super(record)
Expand Down
43 changes: 43 additions & 0 deletions test/action_policy/behaviour_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,24 @@ def test_strict_namespace_authorization_policy_class
end

class TestAuthorizationContext < Minitest::Test
class AdminPolicy
include ActionPolicy::Policy::Core
include ActionPolicy::Policy::Authorization

authorize :user

def show? = user.name == "admin" || record != "admin"
end

class ChatPolicy < AdminPolicy
include ActionPolicy::Policy::Core
include ActionPolicy::Policy::Authorization

authorize :user, :account

def speak? = user.name == account
end

class ChatChannel
include ActionPolicy::Behaviour

Expand All @@ -347,9 +365,26 @@ def initialize(name)
@user = User.new(name)
end

def speak(word, to:)
# Use explicit context to skip memoization
authorize! to, to: :show?, with: AdminPolicy, context: {}

@explicit_account = to
# This will use implicit context (shouldn't be memoized)
# See https://github.com/palkan/action_policy/issues/265
authorize! to: :speak?

"I have spoken #{word} to ##{to}"
end

def account
return @explicit_account if defined?(@explicit_account)
@account = @account ? @account.succ : "a"
end

def implicit_authorization_target = self

def policy_class = ChatPolicy
end

class ChutChannel < ChatChannel
Expand All @@ -371,4 +406,12 @@ def test_authorization_context_without_memoization
assert_equal({user: User.new("guest"), account: "a"}, channel.authorization_context)
assert_equal({user: User.new("guest"), account: "b"}, channel.authorization_context)
end

def test_authorization_context_with_explicit_context
memoize_free_channel = ChutChannel.new("pilot")
assert_equal "I have spoken yohanga to #pilot", memoize_free_channel.speak("yohanga", to: "pilot")

channel = ChatChannel.new("bart")
assert_equal "I have spoken karamba to #bart", channel.speak("karamba", to: "bart")
end
end

0 comments on commit ae2970b

Please sign in to comment.