action_policy 0.4.2 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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