action_policy 0.7.7 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd9504fed879c71d0ebb7025a8bf8ced503b3ed6dfd2d41b1585db47ca2d5548
4
- data.tar.gz: 0c494eaae0d4283a604494af6f3e61326a21402e9ef98321840e755b585a0273
3
+ metadata.gz: 4a3a3b344c3b13973dae50a7a0400d1de1bc6547bddbf5870d2547b701a222f6
4
+ data.tar.gz: d9293952684089a73a27678e54b4a5c2b981117cc49b079821249c9af2d4feae
5
5
  SHA512:
6
- metadata.gz: acdb59593707dc6f0f28b6f3c75f770386a1db35662eea95ae1eb0932fc1900e8a384c10c4e88eb70e3fde853e88414e3442f4994c59a364f94e50efb6c97cf7
7
- data.tar.gz: a2deccf57e2c97b5c3dae5730ae7deecf487b49bb08cfcf488034ad3ff94e80495f3d76ad4553d76cf3f7b8f1322157c3c8f9b45179779af14212a4c9d18ce51
6
+ metadata.gz: ae6665c68d31d55c44e730624bc6dd4cf1e460f9ed26bbdda249189aec7c89258589079f2646f9c5ce9265f57668e046354cd49ae801db0bdd129937f9fab916
7
+ data.tar.gz: 9dbd344bc5f8bad378bdd40d899726a1dcf1136990a586be4a2a3db0bb9a061f56b19825387c7028743fce0e9d5d70d3497e373b40a96edf53a29f4d30bd38e9
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.7.8 (2026-11-03)
6
+
7
+ - Improve policy caching (less key calculations, fixed collisions). ([@palkan][])
8
+
9
+ - Fix `ActiveModel::MissingAttributeError` when authorizing a partially selected Active Record model from a memoized context. ([@czj][])
10
+
5
11
  ## 0.7.7 (2026-09-03)
6
12
 
7
13
  - Fix caching non-persistent Active Record objects as the same entry ([@palkan][])
@@ -566,3 +572,4 @@ This value is now stored in a cache (if any) instead of just the call result (`t
566
572
  [@stephannv]: https://github.com/stephannv
567
573
  [@sedubois]: https://github.com/sedubois
568
574
  [@Pagehey]: https://github.com/Pagehey
575
+ [@czj]: https://github.com/czj
@@ -8,14 +8,14 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
- context = context ? build_authorization_context.merge(context) : authorization_context
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace, cache_key: nil, resolved_context: nil)
12
+ resolved_context ||= policy_for_context(context)
13
13
 
14
14
  policy_class = with || ::ActionPolicy.lookup(
15
15
  record,
16
- namespace: namespace, context: context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
16
+ namespace: namespace, context: resolved_context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
17
17
  )
18
- policy_class&.new(record, **context)
18
+ policy_class&.new(record, **resolved_context)
19
19
  end
20
20
 
21
21
  def authorization_context ; @authorization_context ||= build_authorization_context; end
@@ -59,12 +59,16 @@ module ActionPolicy
59
59
  )
60
60
  end
61
61
 
62
- def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **__kwrest__)
62
+ def policy_for_cache_key(record:, context:, with: nil, namespace: authorization_namespace, **opts)
63
63
  record_key = record._policy_cache_key(use_object_id: true)
64
- context_key = context.values.map { |it| it._policy_cache_key(use_object_id: true) }.join(".")
64
+ context_key = context.map { |k, v| "#{k}:#{v._policy_cache_key(use_object_id: true)}" }.join(".")
65
65
 
66
66
  "#{namespace}/#{with}/#{context_key}/#{record_key}"
67
67
  end
68
+
69
+ def policy_for_context(context)
70
+ context ? build_authorization_context.merge(context) : authorization_context
71
+ end
68
72
  end
69
73
  end
70
74
  end
@@ -44,14 +44,15 @@ module ActionPolicy
44
44
  end
45
45
 
46
46
  module InstanceMethods # :nodoc:
47
- def policy_for(record:, **opts)
48
- __policy_thread_memoize__(record, **opts) { super }
47
+ def policy_for(record:, context: nil, resolved_context: nil, cache_key: nil, **opts)
48
+ resolved_context ||= policy_for_context(context)
49
+ cache_key ||= policy_for_cache_key(record: record, context: resolved_context, **opts)
50
+
51
+ __policy_thread_memoize__(cache_key) { super(record: record, **opts, resolved_context: resolved_context, cache_key: cache_key) }
49
52
  end
50
53
  end
51
54
 
52
- def __policy_thread_memoize__(record, **options)
53
- cache_key = policy_for_cache_key(record: record, **options)
54
-
55
+ def __policy_thread_memoize__(cache_key)
55
56
  ActionPolicy::PerThreadCache.fetch(cache_key) { yield }
56
57
  end
57
58
  end
@@ -10,7 +10,7 @@ module ActionPolicy
10
10
  #
11
11
  # Raises ArgumentError otherwise.
12
12
  module PolicyCacheKey # :nodoc: all
13
- module ObjectExt
13
+ refine Object do
14
14
  def _policy_cache_key(use_object_id: false)
15
15
  return policy_cache_key if respond_to?(:policy_cache_key)
16
16
  return cache_key_with_version if respond_to?(:cache_key_with_version)
@@ -22,10 +22,6 @@ module ActionPolicy
22
22
  end
23
23
  end
24
24
 
25
- refine Object do
26
- ::RubyNext::Core.import_methods(ObjectExt, binding)
27
- end
28
-
29
25
  refine NilClass do
30
26
  def _policy_cache_key(*__rest__) ; ""; end
31
27
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ module Behaviours
5
+ # Per-instance memoization for policies.
6
+ #
7
+ # Used by `policy_for` to re-use policy object for records.
8
+ #
9
+ # Example:
10
+ #
11
+ # include ActionPolicy::Behaviour
12
+ # include ActionPolicy::Memoized
13
+ #
14
+ # record = User.first
15
+ # policy = policy_for(record)
16
+ # policy2 = policy_for(record)
17
+ #
18
+ # policy.equal?(policy) #=> true
19
+ #
20
+ # policy.equal?(policy_for(record, with: CustomPolicy)) #=> false
21
+ module Memoized
22
+ class << self
23
+ def prepended(base)
24
+ base.prepend InstanceMethods
25
+ end
26
+
27
+ alias_method :included, :prepended
28
+ end
29
+
30
+ module InstanceMethods # :nodoc:
31
+ def policy_for(record:, context: nil, resolved_context: nil, cache_key: nil, **opts)
32
+ resolved_context ||= policy_for_context(context)
33
+ cache_key ||= policy_for_cache_key(record: record, context: resolved_context, **opts)
34
+ __policy_memoize__(cache_key) { super(record: record, **opts, resolved_context: resolved_context, cache_key: cache_key) }
35
+ end
36
+ end
37
+
38
+ def __policy_memoize__(cache_key)
39
+ return __policies_cache__[cache_key] if
40
+ __policies_cache__.key?(cache_key)
41
+
42
+ policy = yield
43
+
44
+ __policies_cache__[cache_key] = policy
45
+ end
46
+
47
+ def __policies_cache__
48
+ @__policies_cache__ ||= {}
49
+ end
50
+ end
51
+ end
52
+ end
@@ -8,14 +8,14 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
- context = context ? build_authorization_context.merge(context) : authorization_context
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace, cache_key: nil, resolved_context: nil)
12
+ resolved_context ||= policy_for_context(context)
13
13
 
14
14
  policy_class = with || ::ActionPolicy.lookup(
15
15
  record,
16
- namespace: namespace, context: context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
16
+ namespace: namespace, context: resolved_context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
17
17
  )
18
- policy_class&.new(record, **context)
18
+ policy_class&.new(record, **resolved_context)
19
19
  end
20
20
 
21
21
  def authorization_context = @authorization_context ||= build_authorization_context
@@ -59,12 +59,16 @@ module ActionPolicy
59
59
  )
60
60
  end
61
61
 
62
- def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **__kwrest__)
62
+ def policy_for_cache_key(record:, context:, with: nil, namespace: authorization_namespace, **opts)
63
63
  record_key = record._policy_cache_key(use_object_id: true)
64
- context_key = context.values.map { |it| it._policy_cache_key(use_object_id: true) }.join(".")
64
+ context_key = context.map { |k, v| "#{k}:#{v._policy_cache_key(use_object_id: true)}" }.join(".")
65
65
 
66
66
  "#{namespace}/#{with}/#{context_key}/#{record_key}"
67
67
  end
68
+
69
+ def policy_for_context(context)
70
+ context ? build_authorization_context.merge(context) : authorization_context
71
+ end
68
72
  end
69
73
  end
70
74
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionPolicy
4
+ module PerThreadCache # :nodoc:
5
+ CACHE_KEY = "action_policy.per_thread_cache"
6
+
7
+ class << self
8
+ attr_writer :enabled
9
+
10
+ def enabled?() = @enabled == true
11
+
12
+ def fetch(key)
13
+ return yield unless enabled?
14
+
15
+ store = (Thread.current[CACHE_KEY] ||= {})
16
+
17
+ return store[key] if store.key?(key)
18
+
19
+ store[key] = yield
20
+ end
21
+
22
+ def clear_all
23
+ Thread.current[CACHE_KEY] = {}
24
+ end
25
+ end
26
+
27
+ # Turn off by default in test env
28
+ self.enabled = !(ENV["RAILS_ENV"] == "test" || ENV["RACK_ENV"] == "test")
29
+ end
30
+
31
+ module Behaviours
32
+ # Per-thread memoization for policies.
33
+ #
34
+ # Used by `policy_for` to re-use policy object for records.
35
+ #
36
+ # NOTE: don't forget to clear thread cache with ActionPolicy::PerThreadCache.clear_all
37
+ module ThreadMemoized
38
+ class << self
39
+ def prepended(base)
40
+ base.prepend InstanceMethods
41
+ end
42
+
43
+ alias_method :included, :prepended
44
+ end
45
+
46
+ module InstanceMethods # :nodoc:
47
+ def policy_for(record:, context: nil, resolved_context: nil, cache_key: nil, **opts)
48
+ resolved_context ||= policy_for_context(context)
49
+ cache_key ||= policy_for_cache_key(record: record, context: resolved_context, **opts)
50
+
51
+ __policy_thread_memoize__(cache_key) { super(record: record, **opts, resolved_context: resolved_context, cache_key: cache_key) }
52
+ end
53
+ end
54
+
55
+ def __policy_thread_memoize__(cache_key)
56
+ ActionPolicy::PerThreadCache.fetch(cache_key) { yield }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -10,7 +10,7 @@ module ActionPolicy
10
10
  #
11
11
  # Raises ArgumentError otherwise.
12
12
  module PolicyCacheKey # :nodoc: all
13
- module ObjectExt
13
+ refine Object do
14
14
  def _policy_cache_key(use_object_id: false)
15
15
  return policy_cache_key if respond_to?(:policy_cache_key)
16
16
  return cache_key_with_version if respond_to?(:cache_key_with_version)
@@ -22,10 +22,6 @@ module ActionPolicy
22
22
  end
23
23
  end
24
24
 
25
- refine Object do
26
- import_methods ObjectExt
27
- end
28
-
29
25
  refine NilClass do
30
26
  def _policy_cache_key(*__rest__) = ""
31
27
  end
@@ -28,14 +28,14 @@ module ActionPolicy
28
28
  end
29
29
 
30
30
  module InstanceMethods # :nodoc:
31
- def policy_for(record:, **opts)
32
- __policy_memoize__(record, **opts) { super }
31
+ def policy_for(record:, context: nil, resolved_context: nil, cache_key: nil, **opts)
32
+ resolved_context ||= policy_for_context(context)
33
+ cache_key ||= policy_for_cache_key(record:, context: resolved_context, **opts)
34
+ __policy_memoize__(cache_key) { super(record:, **opts, resolved_context:, cache_key:) }
33
35
  end
34
36
  end
35
37
 
36
- def __policy_memoize__(record, **options)
37
- cache_key = policy_for_cache_key(record: record, **options)
38
-
38
+ def __policy_memoize__(cache_key)
39
39
  return __policies_cache__[cache_key] if
40
40
  __policies_cache__.key?(cache_key)
41
41
 
@@ -8,14 +8,14 @@ module ActionPolicy
8
8
  using ActionPolicy::Ext::PolicyCacheKey
9
9
 
10
10
  # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
- context = context ? build_authorization_context.merge(context) : authorization_context
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace, cache_key: nil, resolved_context: nil)
12
+ resolved_context ||= policy_for_context(context)
13
13
 
14
14
  policy_class = with || ::ActionPolicy.lookup(
15
15
  record,
16
- namespace:, context:, allow_nil:, default:, strict_namespace:
16
+ namespace:, context: resolved_context, allow_nil:, default:, strict_namespace:
17
17
  )
18
- policy_class&.new(record, **context)
18
+ policy_class&.new(record, **resolved_context)
19
19
  end
20
20
 
21
21
  def authorization_context = @authorization_context ||= build_authorization_context
@@ -59,12 +59,16 @@ module ActionPolicy
59
59
  )
60
60
  end
61
61
 
62
- def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **)
62
+ def policy_for_cache_key(record:, context:, with: nil, namespace: authorization_namespace, **opts)
63
63
  record_key = record._policy_cache_key(use_object_id: true)
64
- context_key = context.values.map { it._policy_cache_key(use_object_id: true) }.join(".")
64
+ context_key = context.map { |k, v| "#{k}:#{v._policy_cache_key(use_object_id: true)}" }.join(".")
65
65
 
66
66
  "#{namespace}/#{with}/#{context_key}/#{record_key}"
67
67
  end
68
+
69
+ def policy_for_context(context)
70
+ context ? build_authorization_context.merge(context) : authorization_context
71
+ end
68
72
  end
69
73
  end
70
74
  end
@@ -44,14 +44,15 @@ module ActionPolicy
44
44
  end
45
45
 
46
46
  module InstanceMethods # :nodoc:
47
- def policy_for(record:, **opts)
48
- __policy_thread_memoize__(record, **opts) { super }
47
+ def policy_for(record:, context: nil, resolved_context: nil, cache_key: nil, **opts)
48
+ resolved_context ||= policy_for_context(context)
49
+ cache_key ||= policy_for_cache_key(record:, context: resolved_context, **opts)
50
+
51
+ __policy_thread_memoize__(cache_key) { super(record:, **opts, resolved_context:, cache_key:) }
49
52
  end
50
53
  end
51
54
 
52
- def __policy_thread_memoize__(record, **options)
53
- cache_key = policy_for_cache_key(record: record, **options)
54
-
55
+ def __policy_thread_memoize__(cache_key)
55
56
  ActionPolicy::PerThreadCache.fetch(cache_key) { yield }
56
57
  end
57
58
  end
@@ -10,7 +10,7 @@ module ActionPolicy
10
10
  #
11
11
  # Raises ArgumentError otherwise.
12
12
  module PolicyCacheKey # :nodoc: all
13
- module ObjectExt
13
+ refine Object do
14
14
  def _policy_cache_key(use_object_id: false)
15
15
  return policy_cache_key if respond_to?(:policy_cache_key)
16
16
  return cache_key_with_version if respond_to?(:cache_key_with_version)
@@ -22,10 +22,6 @@ module ActionPolicy
22
22
  end
23
23
  end
24
24
 
25
- refine Object do
26
- import_methods ObjectExt
27
- end
28
-
29
25
  refine NilClass do
30
26
  def _policy_cache_key(*) = ""
31
27
  end
@@ -19,10 +19,10 @@ end)
19
19
  # Ensure non-persistent models are not cached as a single entry
20
20
  ActiveRecord::Base.include(Module.new do
21
21
  def policy_cache_key
22
- if persisted?
23
- cache_key_with_version
24
- else
25
- object_id.to_s
26
- end
22
+ return object_id.to_s unless persisted?
23
+ # A select leaving updated_at out makes cache_version raise MissingAttributeError
24
+ return object_id.to_s if !has_attribute?("updated_at") && self.class.attribute_types.key?("updated_at")
25
+
26
+ cache_key_with_version
27
27
  end
28
28
  end)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionPolicy
4
- VERSION = "0.7.7"
4
+ VERSION = "0.7.8"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.7.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
@@ -149,19 +149,18 @@ files:
149
149
  - lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb
150
150
  - lib/.rbnext/3.0/action_policy/utils/pretty_print.rb
151
151
  - lib/.rbnext/3.0/action_policy/utils/suggest_message.rb
152
+ - lib/.rbnext/3.1/action_policy/behaviours/memoized.rb
152
153
  - lib/.rbnext/3.1/action_policy/behaviours/policy_for.rb
153
154
  - lib/.rbnext/3.1/action_policy/behaviours/scoping.rb
155
+ - lib/.rbnext/3.1/action_policy/behaviours/thread_memoized.rb
154
156
  - lib/.rbnext/3.1/action_policy/ext/module_namespace.rb
155
- - lib/.rbnext/3.1/action_policy/ext/policy_cache_key.rb
156
157
  - lib/.rbnext/3.1/action_policy/policy/authorization.rb
157
- - lib/.rbnext/3.2/action_policy/behaviours/policy_for.rb
158
158
  - lib/.rbnext/3.2/action_policy/ext/policy_cache_key.rb
159
159
  - lib/.rbnext/3.2/action_policy/lookup_chain.rb
160
160
  - lib/.rbnext/3.2/action_policy/policy/core.rb
161
161
  - lib/.rbnext/3.2/action_policy/rspec/be_authorized_to.rb
162
162
  - lib/.rbnext/3.2/action_policy/rspec/have_authorized_scope.rb
163
163
  - lib/.rbnext/3.2/action_policy/utils/suggest_message.rb
164
- - lib/.rbnext/3.4/action_policy/behaviours/policy_for.rb
165
164
  - lib/.rbnext/3.4/action_policy/i18n.rb
166
165
  - lib/.rbnext/3.4/action_policy/policy/cache.rb
167
166
  - lib/.rbnext/3.4/action_policy/policy/pre_check.rb
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionPolicy
4
- module Ext
5
- # Adds #_policy_cache_key method to Object,
6
- # which just call #policy_cache_key or #cache_key
7
- # or #object_id (if `use_object_id` parameter is set to true).
8
- #
9
- # For other core classes returns string representation.
10
- #
11
- # Raises ArgumentError otherwise.
12
- module PolicyCacheKey # :nodoc: all
13
- module ObjectExt
14
- def _policy_cache_key(use_object_id: false)
15
- return policy_cache_key if respond_to?(:policy_cache_key)
16
- return cache_key_with_version if respond_to?(:cache_key_with_version)
17
- return cache_key if respond_to?(:cache_key)
18
-
19
- return object_id.to_s if use_object_id == true
20
-
21
- raise ArgumentError, "object is not cacheable"
22
- end
23
- end
24
-
25
- refine Object do
26
- ::RubyNext::Core.import_methods(ObjectExt, binding)
27
- end
28
-
29
- refine NilClass do
30
- def _policy_cache_key(*__rest__) = ""
31
- end
32
-
33
- refine TrueClass do
34
- def _policy_cache_key(*__rest__) = "t"
35
- end
36
-
37
- refine FalseClass do
38
- def _policy_cache_key(*__rest__) = "f"
39
- end
40
-
41
- refine String do
42
- def _policy_cache_key(*__rest__) = self
43
- end
44
-
45
- refine Symbol do
46
- def _policy_cache_key(*__rest__) = to_s
47
- end
48
-
49
- if RUBY_PLATFORM.match?(/java/i)
50
- refine Integer do
51
- def _policy_cache_key(*__rest__) = to_s
52
- end
53
-
54
- refine Float do
55
- def _policy_cache_key(*__rest__) = to_s
56
- end
57
- else
58
- refine Numeric do
59
- def _policy_cache_key(*__rest__) = to_s
60
- end
61
- end
62
-
63
- refine Time do
64
- def _policy_cache_key(*__rest__) = to_s
65
- end
66
-
67
- refine Module do
68
- def _policy_cache_key(*__rest__) = name
69
- end
70
- end
71
- end
72
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionPolicy
4
- module Behaviours
5
- # Adds `policy_for` method
6
- module PolicyFor
7
- require "action_policy/ext/policy_cache_key"
8
- using ActionPolicy::Ext::PolicyCacheKey
9
-
10
- # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
- context = context ? build_authorization_context.merge(context) : authorization_context
13
-
14
- policy_class = with || ::ActionPolicy.lookup(
15
- record,
16
- namespace:, context:, allow_nil:, default:, strict_namespace:
17
- )
18
- policy_class&.new(record, **context)
19
- end
20
-
21
- def authorization_context = @authorization_context ||= build_authorization_context
22
-
23
- def build_authorization_context
24
- Kernel.raise NotImplementedError, "Please, define `build_authorization_context` method!"
25
- end
26
-
27
- def authorization_namespace
28
- # override to provide specific authorization namespace
29
- end
30
-
31
- def default_authorization_policy_class
32
- # override to provide a policy class use when no policy found
33
- end
34
-
35
- def authorization_strict_namespace
36
- # override to provide strict namespace lookup option
37
- end
38
-
39
- # Override this method to provide implicit authorization target
40
- # that would be used in case `record` is not specified in
41
- # `authorize!` and `allowed_to?` call.
42
- #
43
- # It is also used to infer a policy for scoping (in `authorized_scope` method).
44
- def implicit_authorization_target
45
- # no-op
46
- end
47
-
48
- # Return implicit authorization target or raises an exception if it's nil
49
- def implicit_authorization_target!
50
- implicit_authorization_target || Kernel.raise(
51
- NotFound,
52
- [
53
- self,
54
- "Couldn't find implicit authorization target " \
55
- "for #{self.class}. " \
56
- "Please, provide policy class explicitly using `with` option or " \
57
- "define the `implicit_authorization_target` method."
58
- ]
59
- )
60
- end
61
-
62
- def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **__kwrest__)
63
- record_key = record._policy_cache_key(use_object_id: true)
64
- context_key = context.values.map { |it| it._policy_cache_key(use_object_id: true) }.join(".")
65
-
66
- "#{namespace}/#{with}/#{context_key}/#{record_key}"
67
- end
68
- end
69
- end
70
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionPolicy
4
- module Behaviours
5
- # Adds `policy_for` method
6
- module PolicyFor
7
- require "action_policy/ext/policy_cache_key"
8
- using ActionPolicy::Ext::PolicyCacheKey
9
-
10
- # Returns policy instance for the record.
11
- def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
12
- context = context ? build_authorization_context.merge(context) : authorization_context
13
-
14
- policy_class = with || ::ActionPolicy.lookup(
15
- record,
16
- namespace:, context:, allow_nil:, default:, strict_namespace:
17
- )
18
- policy_class&.new(record, **context)
19
- end
20
-
21
- def authorization_context = @authorization_context ||= build_authorization_context
22
-
23
- def build_authorization_context
24
- Kernel.raise NotImplementedError, "Please, define `build_authorization_context` method!"
25
- end
26
-
27
- def authorization_namespace
28
- # override to provide specific authorization namespace
29
- end
30
-
31
- def default_authorization_policy_class
32
- # override to provide a policy class use when no policy found
33
- end
34
-
35
- def authorization_strict_namespace
36
- # override to provide strict namespace lookup option
37
- end
38
-
39
- # Override this method to provide implicit authorization target
40
- # that would be used in case `record` is not specified in
41
- # `authorize!` and `allowed_to?` call.
42
- #
43
- # It is also used to infer a policy for scoping (in `authorized_scope` method).
44
- def implicit_authorization_target
45
- # no-op
46
- end
47
-
48
- # Return implicit authorization target or raises an exception if it's nil
49
- def implicit_authorization_target!
50
- implicit_authorization_target || Kernel.raise(
51
- NotFound,
52
- [
53
- self,
54
- "Couldn't find implicit authorization target " \
55
- "for #{self.class}. " \
56
- "Please, provide policy class explicitly using `with` option or " \
57
- "define the `implicit_authorization_target` method."
58
- ]
59
- )
60
- end
61
-
62
- def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **)
63
- record_key = record._policy_cache_key(use_object_id: true)
64
- context_key = context.values.map { |it| it._policy_cache_key(use_object_id: true) }.join(".")
65
-
66
- "#{namespace}/#{with}/#{context_key}/#{record_key}"
67
- end
68
- end
69
- end
70
- end