action_policy 0.4.2 → 0.5.2

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.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +229 -171
  3. data/LICENSE.txt +1 -1
  4. data/README.md +7 -11
  5. data/lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb +62 -0
  6. data/lib/.rbnext/2.7/action_policy/i18n.rb +56 -0
  7. data/lib/.rbnext/2.7/action_policy/policy/cache.rb +101 -0
  8. data/lib/.rbnext/2.7/action_policy/policy/pre_check.rb +162 -0
  9. data/lib/.rbnext/2.7/action_policy/rspec/be_authorized_to.rb +89 -0
  10. data/lib/.rbnext/2.7/action_policy/rspec/have_authorized_scope.rb +124 -0
  11. data/lib/.rbnext/2.7/action_policy/utils/pretty_print.rb +159 -0
  12. data/lib/.rbnext/3.0/action_policy/behaviour.rb +115 -0
  13. data/lib/.rbnext/3.0/action_policy/behaviours/policy_for.rb +62 -0
  14. data/lib/.rbnext/3.0/action_policy/behaviours/scoping.rb +35 -0
  15. data/lib/.rbnext/3.0/action_policy/behaviours/thread_memoized.rb +59 -0
  16. data/lib/.rbnext/3.0/action_policy/ext/policy_cache_key.rb +72 -0
  17. data/lib/.rbnext/3.0/action_policy/policy/aliases.rb +69 -0
  18. data/lib/.rbnext/3.0/action_policy/policy/authorization.rb +87 -0
  19. data/lib/.rbnext/3.0/action_policy/policy/cache.rb +101 -0
  20. data/lib/.rbnext/3.0/action_policy/policy/core.rb +161 -0
  21. data/lib/.rbnext/3.0/action_policy/policy/defaults.rb +31 -0
  22. data/lib/.rbnext/3.0/action_policy/policy/execution_result.rb +37 -0
  23. data/lib/.rbnext/3.0/action_policy/policy/pre_check.rb +162 -0
  24. data/lib/.rbnext/3.0/action_policy/policy/reasons.rb +210 -0
  25. data/lib/.rbnext/3.0/action_policy/policy/scoping.rb +160 -0
  26. data/lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb +89 -0
  27. data/lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb +124 -0
  28. data/lib/.rbnext/3.0/action_policy/utils/pretty_print.rb +159 -0
  29. data/lib/.rbnext/3.0/action_policy/utils/suggest_message.rb +19 -0
  30. data/lib/action_policy.rb +7 -1
  31. data/lib/action_policy/behaviour.rb +22 -16
  32. data/lib/action_policy/behaviours/policy_for.rb +10 -3
  33. data/lib/action_policy/behaviours/scoping.rb +2 -1
  34. data/lib/action_policy/behaviours/thread_memoized.rb +1 -3
  35. data/lib/action_policy/ext/module_namespace.rb +1 -6
  36. data/lib/action_policy/ext/policy_cache_key.rb +15 -33
  37. data/lib/action_policy/ext/{symbol_classify.rb → symbol_camelize.rb} +6 -6
  38. data/lib/action_policy/i18n.rb +1 -1
  39. data/lib/action_policy/lookup_chain.rb +41 -21
  40. data/lib/action_policy/policy/aliases.rb +7 -12
  41. data/lib/action_policy/policy/authorization.rb +14 -17
  42. data/lib/action_policy/policy/cache.rb +34 -18
  43. data/lib/action_policy/policy/core.rb +25 -12
  44. data/lib/action_policy/policy/defaults.rb +3 -9
  45. data/lib/action_policy/policy/execution_result.rb +3 -9
  46. data/lib/action_policy/policy/pre_check.rb +19 -58
  47. data/lib/action_policy/policy/reasons.rb +32 -20
  48. data/lib/action_policy/policy/scoping.rb +5 -6
  49. data/lib/action_policy/rails/controller.rb +6 -1
  50. data/lib/action_policy/rails/ext/active_record.rb +7 -0
  51. data/lib/action_policy/rails/policy/instrumentation.rb +1 -1
  52. data/lib/action_policy/rspec/be_authorized_to.rb +5 -9
  53. data/lib/action_policy/rspec/dsl.rb +3 -3
  54. data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
  55. data/lib/action_policy/utils/pretty_print.rb +21 -24
  56. data/lib/action_policy/utils/suggest_message.rb +1 -3
  57. data/lib/action_policy/version.rb +1 -1
  58. data/lib/generators/action_policy/install/templates/{application_policy.rb → application_policy.rb.tt} +1 -1
  59. data/lib/generators/action_policy/policy/policy_generator.rb +4 -1
  60. data/lib/generators/action_policy/policy/templates/{policy.rb → policy.rb.tt} +0 -0
  61. data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
  62. data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
  63. metadata +55 -119
  64. data/.gitattributes +0 -2
  65. data/.github/FUNDING.yml +0 -1
  66. data/.github/ISSUE_TEMPLATE.md +0 -18
  67. data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
  68. data/.gitignore +0 -15
  69. data/.rubocop.yml +0 -54
  70. data/.tidelift.yml +0 -6
  71. data/.travis.yml +0 -31
  72. data/Gemfile +0 -22
  73. data/Rakefile +0 -27
  74. data/action_policy.gemspec +0 -44
  75. data/benchmarks/namespaced_lookup_cache.rb +0 -71
  76. data/bin/console +0 -14
  77. data/bin/setup +0 -8
  78. data/docs/.nojekyll +0 -0
  79. data/docs/CNAME +0 -1
  80. data/docs/README.md +0 -77
  81. data/docs/_sidebar.md +0 -27
  82. data/docs/aliases.md +0 -122
  83. data/docs/assets/docsify-search.js +0 -364
  84. data/docs/assets/docsify.min.js +0 -3
  85. data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
  86. data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
  87. data/docs/assets/images/banner.png +0 -0
  88. data/docs/assets/images/cache.png +0 -0
  89. data/docs/assets/images/cache.svg +0 -70
  90. data/docs/assets/images/layer.png +0 -0
  91. data/docs/assets/images/layer.svg +0 -35
  92. data/docs/assets/prism-ruby.min.js +0 -1
  93. data/docs/assets/styles.css +0 -347
  94. data/docs/assets/vue.min.css +0 -1
  95. data/docs/authorization_context.md +0 -92
  96. data/docs/behaviour.md +0 -113
  97. data/docs/caching.md +0 -273
  98. data/docs/controller_action_aliases.md +0 -109
  99. data/docs/custom_lookup_chain.md +0 -48
  100. data/docs/custom_policy.md +0 -53
  101. data/docs/debugging.md +0 -55
  102. data/docs/decorators.md +0 -27
  103. data/docs/favicon.ico +0 -0
  104. data/docs/graphql.md +0 -302
  105. data/docs/i18n.md +0 -44
  106. data/docs/index.html +0 -43
  107. data/docs/instrumentation.md +0 -84
  108. data/docs/lookup_chain.md +0 -17
  109. data/docs/namespaces.md +0 -77
  110. data/docs/non_rails.md +0 -28
  111. data/docs/pre_checks.md +0 -57
  112. data/docs/pundit_migration.md +0 -80
  113. data/docs/quick_start.md +0 -118
  114. data/docs/rails.md +0 -120
  115. data/docs/reasons.md +0 -120
  116. data/docs/scoping.md +0 -255
  117. data/docs/testing.md +0 -333
  118. data/docs/writing_policies.md +0 -107
  119. data/gemfiles/jruby.gemfile +0 -8
  120. data/gemfiles/rails42.gemfile +0 -8
  121. data/gemfiles/rails6.gemfile +0 -8
  122. data/gemfiles/railsmaster.gemfile +0 -6
  123. data/lib/action_policy/ext/string_match.rb +0 -14
  124. data/lib/action_policy/ext/yield_self_then.rb +0 -25
@@ -8,8 +8,11 @@ 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: authorization_context, allow_nil: false)
12
- policy_class = with || ::ActionPolicy.lookup(record, namespace: namespace, context: context, allow_nil: allow_nil)
11
+ def policy_for(record:, with: nil, namespace: authorization_namespace, context: authorization_context, allow_nil: false, default: default_authorization_policy_class)
12
+ policy_class = with || ::ActionPolicy.lookup(
13
+ record,
14
+ **{namespace, context, allow_nil, default}
15
+ )
13
16
  policy_class&.new(record, **context)
14
17
  end
15
18
 
@@ -21,6 +24,10 @@ module ActionPolicy
21
24
  # override to provide specific authorization namespace
22
25
  end
23
26
 
27
+ def default_authorization_policy_class
28
+ # override to provide a policy class use when no policy found
29
+ end
30
+
24
31
  # Override this method to provide implicit authorization target
25
32
  # that would be used in case `record` is not specified in
26
33
  # `authorize!` and `allowed_to?` call.
@@ -46,7 +53,7 @@ module ActionPolicy
46
53
 
47
54
  def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **)
48
55
  record_key = record._policy_cache_key(use_object_id: true)
49
- context_key = context.values.map { |v| v._policy_cache_key(use_object_id: true) }.join(".")
56
+ context_key = context.values.map { _1._policy_cache_key(use_object_id: true) }.join(".")
50
57
 
51
58
  "#{namespace}/#{with}/#{context_key}/#{record_key}"
52
59
  end
@@ -17,8 +17,9 @@ module ActionPolicy
17
17
  policy ||= policy_for(record: implicit_authorization_target!, **options)
18
18
 
19
19
  type ||= authorization_scope_type_for(policy, target)
20
+ name = as
20
21
 
21
- Authorizer.scopify(target, policy, type: type, name: as, scope_options: scope_options)
22
+ Authorizer.scopify(target, policy, **{type, name, scope_options})
22
23
  end
23
24
 
24
25
  # For backward compatibility
@@ -7,9 +7,7 @@ module ActionPolicy
7
7
  class << self
8
8
  attr_writer :enabled
9
9
 
10
- def enabled?
11
- @enabled == true
12
- end
10
+ def enabled?() = @enabled == true
13
11
 
14
12
  def fetch(key)
15
13
  return yield unless enabled?
@@ -9,11 +9,6 @@ module ActionPolicy
9
9
  using ActionPolicy::Ext::StringConstantize
10
10
  end
11
11
 
12
- unless "".respond_to?(:match?)
13
- require "action_policy/ext/string_match"
14
- using ActionPolicy::Ext::StringMatch
15
- end
16
-
17
12
  module Ext
18
13
  def namespace
19
14
  return unless name&.match?(/[^^]::/)
@@ -23,7 +18,7 @@ module ActionPolicy
23
18
  end
24
19
 
25
20
  # See https://github.com/jruby/jruby/issues/5220
26
- ::Module.include(Ext) if RUBY_PLATFORM =~ /java/i
21
+ ::Module.include(Ext) if RUBY_PLATFORM.match?(/java/i)
27
22
 
28
23
  refine Module do
29
24
  include Ext
@@ -13,77 +13,59 @@ module ActionPolicy
13
13
  module ObjectExt
14
14
  def _policy_cache_key(use_object_id: false)
15
15
  return policy_cache_key if respond_to?(:policy_cache_key)
16
+ return cache_key_with_version if respond_to?(:cache_key_with_version)
16
17
  return cache_key if respond_to?(:cache_key)
17
18
 
18
- return object_id if use_object_id == true
19
+ return object_id.to_s if use_object_id == true
19
20
 
20
21
  raise ArgumentError, "object is not cacheable"
21
22
  end
22
23
  end
23
24
 
24
- # JRuby doesn't support _global_ modules refinements (see https://github.com/jruby/jruby/issues/5220)
25
- # Fallback to monkey-patching.
26
- # TODO: remove after 9.2.7.0 (See https://github.com/jruby/jruby/pull/5627)
27
- ::Object.include(ObjectExt) if RUBY_PLATFORM =~ /java/i
28
-
29
25
  refine Object do
30
26
  include ObjectExt
31
27
  end
32
28
 
33
29
  refine NilClass do
34
- def _policy_cache_key(*)
35
- ""
36
- end
30
+ def _policy_cache_key(*) = ""
37
31
  end
38
32
 
39
33
  refine TrueClass do
40
- def _policy_cache_key(*)
41
- "t"
42
- end
34
+ def _policy_cache_key(*) = "t"
43
35
  end
44
36
 
45
37
  refine FalseClass do
46
- def _policy_cache_key(*)
47
- "f"
48
- end
38
+ def _policy_cache_key(*) = "f"
49
39
  end
50
40
 
51
41
  refine String do
52
- def _policy_cache_key(*)
53
- self
54
- end
42
+ def _policy_cache_key(*) = self
55
43
  end
56
44
 
57
45
  refine Symbol do
58
- def _policy_cache_key(*)
59
- to_s
60
- end
46
+ def _policy_cache_key(*) = to_s
61
47
  end
62
48
 
63
49
  if RUBY_PLATFORM.match?(/java/i)
64
50
  refine Integer do
65
- def _policy_cache_key(*)
66
- to_s
67
- end
51
+ def _policy_cache_key(*) = to_s
68
52
  end
69
53
 
70
54
  refine Float do
71
- def _policy_cache_key(*)
72
- to_s
73
- end
55
+ def _policy_cache_key(*) = to_s
74
56
  end
75
57
  else
76
58
  refine Numeric do
77
- def _policy_cache_key(*)
78
- to_s
79
- end
59
+ def _policy_cache_key(*) = to_s
80
60
  end
81
61
  end
82
62
 
83
63
  refine Time do
84
- def _policy_cache_key(*)
85
- to_s
86
- end
64
+ def _policy_cache_key(*) = to_s
65
+ end
66
+
67
+ refine Module do
68
+ def _policy_cache_key(*) = name
87
69
  end
88
70
  end
89
71
  end
@@ -2,15 +2,15 @@
2
2
 
3
3
  module ActionPolicy
4
4
  module Ext
5
- # Add `classify` to Symbol
6
- module SymbolClassify
5
+ # Add `camelize` to Symbol
6
+ module SymbolCamelize
7
7
  refine Symbol do
8
- if "".respond_to?(:classify)
9
- def classify
10
- to_s.classify
8
+ if "".respond_to?(:camelize)
9
+ def camelize
10
+ to_s.camelize
11
11
  end
12
12
  else
13
- def classify
13
+ def camelize
14
14
  word = to_s.capitalize
15
15
  word.gsub!(/(?:_)([a-z\d]*)/) { $1.capitalize }
16
16
  word
@@ -21,7 +21,7 @@ module ActionPolicy
21
21
  private
22
22
 
23
23
  def candidates_for(policy_class, rule)
24
- policy_hierarchy = policy_class.ancestors.select { |klass| klass.respond_to?(:identifier) }
24
+ policy_hierarchy = policy_class.ancestors.select { _1.respond_to?(:identifier) }
25
25
  [
26
26
  *policy_hierarchy.map { |klass| :"policy.#{klass.identifier}.#{rule}" },
27
27
  :"policy.#{rule}",
@@ -12,8 +12,8 @@ module ActionPolicy
12
12
  using ActionPolicy::Ext::StringConstantize
13
13
  end
14
14
 
15
- require "action_policy/ext/symbol_classify"
16
- using ActionPolicy::Ext::SymbolClassify
15
+ require "action_policy/ext/symbol_camelize"
16
+ using ActionPolicy::Ext::SymbolCamelize
17
17
 
18
18
  require "action_policy/ext/module_namespace"
19
19
  using ActionPolicy::Ext::ModuleNamespace
@@ -24,14 +24,26 @@ module ActionPolicy
24
24
  class << self
25
25
  attr_reader :store
26
26
 
27
- def fetch(namespace, policy)
27
+ def put_if_absent(scope, namespace, policy)
28
+ local_store = store[scope][namespace]
29
+ return local_store[policy] if local_store[policy]
30
+ local_store[policy] ||= yield
31
+ end
32
+
33
+ def fetch(namespace, policy, strict:, &block)
28
34
  return yield unless LookupChain.namespace_cache_enabled?
29
- return store[namespace][policy] if store[namespace].key?(policy)
30
- store[namespace][policy] ||= yield
35
+
36
+ if strict
37
+ put_if_absent(:strict, namespace, policy, &block)
38
+ else
39
+ cached_policy = put_if_absent(:strict, namespace, policy, &block)
40
+ put_if_absent(:flexible, namespace, policy) { cached_policy }
41
+ end
31
42
  end
32
43
 
33
44
  def clear
34
- @store = Hash.new { |h, k| h[k] = {} }
45
+ hash = Hash.new { |h, k| h[k] = {} }
46
+ @store = {strict: hash, flexible: hash.clone}
35
47
  end
36
48
  end
37
49
 
@@ -53,22 +65,23 @@ module ActionPolicy
53
65
 
54
66
  private
55
67
 
56
- def lookup_within_namespace(policy_name, namespace)
57
- NamespaceCache.fetch(namespace.name, policy_name) do
68
+ def lookup_within_namespace(policy_name, namespace, strict: false)
69
+ return unless namespace
70
+ NamespaceCache.fetch(namespace.name, policy_name, strict: strict) do
58
71
  mod = namespace
59
-
60
72
  loop do
61
73
  policy = "#{mod.name}::#{policy_name}".safe_constantize
62
-
63
74
  break policy unless policy.nil?
64
-
65
75
  mod = mod.namespace
66
-
67
- break if mod.nil?
76
+ break if mod.nil? || strict
68
77
  end
69
78
  end
70
79
  end
71
80
 
81
+ def objectify_policy(policy_name, strict: false)
82
+ policy_name.safe_constantize unless strict
83
+ end
84
+
72
85
  def policy_class_name_for(record)
73
86
  return record.policy_name.to_s if record.respond_to?(:policy_name)
74
87
 
@@ -111,23 +124,30 @@ module ActionPolicy
111
124
  }
112
125
 
113
126
  # Infer from passed symbol
114
- SYMBOL_LOOKUP = ->(record, namespace: nil, **) {
127
+ SYMBOL_LOOKUP = ->(record, namespace: nil, strict_namespace: false, **) {
115
128
  next unless record.is_a?(Symbol)
116
129
 
117
- policy_name = "#{record.classify}Policy"
118
- if namespace.nil?
119
- policy_name.safe_constantize
120
- else
121
- lookup_within_namespace(policy_name, namespace)
122
- end
130
+ policy_name = "#{record.camelize}Policy"
131
+ lookup_within_namespace(policy_name, namespace, strict: strict_namespace) ||
132
+ objectify_policy(policy_name, strict: strict_namespace)
133
+ }
134
+
135
+ # (Optional) Infer using String#classify if available
136
+ CLASSIFY_SYMBOL_LOOKUP = ->(record, namespace: nil, strict_namespace: false, **) {
137
+ next unless record.is_a?(Symbol)
138
+
139
+ policy_name = "#{record.to_s.classify}Policy"
140
+ lookup_within_namespace(policy_name, namespace, strict: strict_namespace) ||
141
+ objectify_policy(policy_name, strict: strict_namespace)
123
142
  }
124
143
 
125
144
  self.chain = [
126
145
  SYMBOL_LOOKUP,
146
+ (CLASSIFY_SYMBOL_LOOKUP if String.method_defined?(:classify)),
127
147
  INSTANCE_POLICY_CLASS,
128
148
  CLASS_POLICY_CLASS,
129
149
  NAMESPACE_LOOKUP,
130
150
  INFER_FROM_CLASS
131
- ]
151
+ ].compact
132
152
  end
133
153
  end
@@ -46,23 +46,18 @@ module ActionPolicy
46
46
  end
47
47
  end
48
48
 
49
- def lookup_alias(rule)
50
- rules_aliases[rule]
51
- end
49
+ def lookup_alias(rule) = rules_aliases[rule]
52
50
 
53
- def lookup_default_rule
54
- rules_aliases[DEFAULT]
55
- end
51
+ def lookup_default_rule() = rules_aliases[DEFAULT]
56
52
 
57
53
  def rules_aliases
58
54
  return @rules_aliases if instance_variable_defined?(:@rules_aliases)
59
55
 
60
- @rules_aliases =
61
- if superclass.respond_to?(:rules_aliases)
62
- superclass.rules_aliases.dup
63
- else
64
- {}
65
- end
56
+ if superclass.respond_to?(:rules_aliases)
57
+ superclass.rules_aliases.dup
58
+ else
59
+ {}
60
+ end => @rules_aliases
66
61
  end
67
62
 
68
63
  def method_added(name)
@@ -43,21 +43,17 @@ module ActionPolicy
43
43
 
44
44
  attr_reader :authorization_context
45
45
 
46
- def initialize(*args, **params)
47
- super(*args)
46
+ def initialize(record = nil, **params)
47
+ super(record)
48
48
 
49
49
  @authorization_context = {}
50
50
 
51
51
  self.class.authorization_targets.each do |id, opts|
52
- if opts[:optional] == true
53
- val = params.fetch(id, nil)
54
- else
55
- raise AuthorizationContextMissing, id unless params.key?(id)
52
+ raise AuthorizationContextMissing, id unless params.key?(id) || opts[:optional]
56
53
 
57
- val = params.fetch(id)
54
+ val = params.fetch(id, nil)
58
55
 
59
- raise AuthorizationContextMissing, id if val.nil? && opts[:allow_nil] != true
60
- end
56
+ raise AuthorizationContextMissing, id if val.nil? && opts[:allow_nil] != true
61
57
 
62
58
  authorization_context[id] = instance_variable_set("@#{id}", val)
63
59
  end
@@ -66,9 +62,11 @@ module ActionPolicy
66
62
  end
67
63
 
68
64
  module ClassMethods # :nodoc:
69
- def authorize(*ids, **opts)
65
+ def authorize(*ids, allow_nil: false, optional: false)
66
+ allow_nil ||= optional
67
+
70
68
  ids.each do |id|
71
- authorization_targets[id] = opts
69
+ authorization_targets[id] = {allow_nil, optional}
72
70
  end
73
71
 
74
72
  attr_reader(*ids)
@@ -77,12 +75,11 @@ module ActionPolicy
77
75
  def authorization_targets
78
76
  return @authorization_targets if instance_variable_defined?(:@authorization_targets)
79
77
 
80
- @authorization_targets =
81
- if superclass.respond_to?(:authorization_targets)
82
- superclass.authorization_targets.dup
83
- else
84
- {}
85
- end
78
+ if superclass.respond_to?(:authorization_targets)
79
+ superclass.authorization_targets.dup
80
+ else
81
+ {}
82
+ end => @authorization_targets
86
83
  end
87
84
  end
88
85
  end
@@ -3,16 +3,13 @@
3
3
  require "action_policy/version"
4
4
 
5
5
  module ActionPolicy # :nodoc:
6
+ using RubyNext
7
+
6
8
  # By default cache namespace (or prefix) contains major and minor version of the gem
7
9
  CACHE_NAMESPACE = "acp:#{ActionPolicy::VERSION.split(".").take(2).join(".")}"
8
10
 
9
- require "action_policy/ext/yield_self_then"
10
11
  require "action_policy/ext/policy_cache_key"
11
12
 
12
- unless "".respond_to?(:then)
13
- require "action_policy/ext/yield_self_then"
14
- using ActionPolicy::Ext::YieldSelfThen
15
- end
16
13
  using ActionPolicy::Ext::PolicyCacheKey
17
14
 
18
15
  module Policy
@@ -26,22 +23,31 @@ module ActionPolicy # :nodoc:
26
23
  end
27
24
  end
28
25
 
29
- def cache_namespace
30
- ActionPolicy::CACHE_NAMESPACE
26
+ def cache_namespace() = ActionPolicy::CACHE_NAMESPACE
27
+
28
+ def cache_key(*parts)
29
+ [
30
+ cache_namespace,
31
+ *parts
32
+ ].map { _1._policy_cache_key }.join("/")
31
33
  end
32
34
 
33
- def cache_key(rule)
34
- "#{cache_namespace}/#{context_cache_key}/" \
35
- "#{record._policy_cache_key}/#{self.class.name}/#{rule}"
35
+ def rule_cache_key(rule)
36
+ cache_key(
37
+ context_cache_key,
38
+ record,
39
+ self.class,
40
+ rule
41
+ )
36
42
  end
37
43
 
38
44
  def context_cache_key
39
- authorization_context.map { |_k, v| v._policy_cache_key.to_s }.join("/")
45
+ authorization_context.map { _2._policy_cache_key.to_s }.join("/")
40
46
  end
41
47
 
42
48
  def apply_with_cache(rule)
43
49
  options = self.class.cached_rules.fetch(rule)
44
- key = cache_key(rule)
50
+ key = rule_cache_key(rule)
45
51
 
46
52
  ActionPolicy.cache_store.then do |store|
47
53
  @result = store.read(key)
@@ -62,6 +68,17 @@ module ActionPolicy # :nodoc:
62
68
  apply_with_cache(rule) { super }
63
69
  end
64
70
 
71
+ def cache(*parts, **options)
72
+ key = cache_key(*parts)
73
+ ActionPolicy.cache_store.then do |store|
74
+ res = store.read(key)
75
+ next res unless res.nil?
76
+ res = yield
77
+ store.write(key, res, options)
78
+ res
79
+ end
80
+ end
81
+
65
82
  module ClassMethods # :nodoc:
66
83
  def cache(*rules, **options)
67
84
  rules.each do |rule|
@@ -72,12 +89,11 @@ module ActionPolicy # :nodoc:
72
89
  def cached_rules
73
90
  return @cached_rules if instance_variable_defined?(:@cached_rules)
74
91
 
75
- @cached_rules =
76
- if superclass.respond_to?(:cached_rules)
77
- superclass.cached_rules.dup
78
- else
79
- {}
80
- end
92
+ if superclass.respond_to?(:cached_rules)
93
+ superclass.cached_rules.dup
94
+ else
95
+ {}
96
+ end => @cached_rules
81
97
  end
82
98
  end
83
99
  end