action_policy 0.4.0 → 0.5.0
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 +4 -4
- data/CHANGELOG.md +233 -171
- data/LICENSE.txt +1 -1
- data/README.md +7 -11
- data/lib/action_policy.rb +7 -1
- data/lib/action_policy/behaviour.rb +22 -16
- data/lib/action_policy/behaviours/policy_for.rb +10 -3
- data/lib/action_policy/behaviours/scoping.rb +2 -1
- data/lib/action_policy/behaviours/thread_memoized.rb +1 -3
- data/lib/action_policy/ext/module_namespace.rb +1 -6
- data/lib/action_policy/ext/policy_cache_key.rb +15 -33
- data/lib/action_policy/ext/{symbol_classify.rb → symbol_camelize.rb} +6 -6
- data/lib/action_policy/i18n.rb +1 -1
- data/lib/action_policy/lookup_chain.rb +41 -21
- data/lib/action_policy/policy/aliases.rb +7 -12
- data/lib/action_policy/policy/authorization.rb +14 -17
- data/lib/action_policy/policy/cache.rb +34 -18
- data/lib/action_policy/policy/core.rb +25 -12
- data/lib/action_policy/policy/defaults.rb +3 -9
- data/lib/action_policy/policy/execution_result.rb +3 -9
- data/lib/action_policy/policy/pre_check.rb +19 -58
- data/lib/action_policy/policy/reasons.rb +30 -20
- data/lib/action_policy/policy/scoping.rb +5 -6
- data/lib/action_policy/rails/controller.rb +6 -1
- data/lib/action_policy/rails/ext/active_record.rb +7 -0
- data/lib/action_policy/rails/policy/instrumentation.rb +1 -1
- data/lib/action_policy/rspec/be_authorized_to.rb +5 -9
- data/lib/action_policy/rspec/dsl.rb +3 -3
- data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
- data/lib/action_policy/testing.rb +1 -1
- data/lib/action_policy/utils/pretty_print.rb +21 -24
- data/lib/action_policy/utils/suggest_message.rb +1 -3
- data/lib/action_policy/version.rb +1 -1
- data/lib/generators/action_policy/install/templates/{application_policy.rb → application_policy.rb.tt} +1 -1
- data/lib/generators/action_policy/policy/policy_generator.rb +4 -1
- data/lib/generators/action_policy/policy/templates/{policy.rb → policy.rb.tt} +0 -0
- data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
- data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
- metadata +30 -119
- data/.gitattributes +0 -2
- data/.github/FUNDING.yml +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -18
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
- data/.gitignore +0 -15
- data/.rubocop.yml +0 -54
- data/.tidelift.yml +0 -6
- data/.travis.yml +0 -31
- data/Gemfile +0 -22
- data/Rakefile +0 -27
- data/action_policy.gemspec +0 -44
- data/benchmarks/namespaced_lookup_cache.rb +0 -71
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/docs/.nojekyll +0 -0
- data/docs/CNAME +0 -1
- data/docs/README.md +0 -77
- data/docs/_sidebar.md +0 -27
- data/docs/aliases.md +0 -122
- data/docs/assets/docsify-search.js +0 -364
- data/docs/assets/docsify.min.js +0 -3
- data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
- data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
- data/docs/assets/images/banner.png +0 -0
- data/docs/assets/images/cache.png +0 -0
- data/docs/assets/images/cache.svg +0 -70
- data/docs/assets/images/layer.png +0 -0
- data/docs/assets/images/layer.svg +0 -35
- data/docs/assets/prism-ruby.min.js +0 -1
- data/docs/assets/styles.css +0 -347
- data/docs/assets/vue.min.css +0 -1
- data/docs/authorization_context.md +0 -92
- data/docs/behaviour.md +0 -113
- data/docs/caching.md +0 -273
- data/docs/controller_action_aliases.md +0 -109
- data/docs/custom_lookup_chain.md +0 -48
- data/docs/custom_policy.md +0 -53
- data/docs/debugging.md +0 -55
- data/docs/decorators.md +0 -27
- data/docs/favicon.ico +0 -0
- data/docs/graphql.md +0 -302
- data/docs/i18n.md +0 -44
- data/docs/index.html +0 -43
- data/docs/instrumentation.md +0 -84
- data/docs/lookup_chain.md +0 -17
- data/docs/namespaces.md +0 -77
- data/docs/non_rails.md +0 -28
- data/docs/pre_checks.md +0 -57
- data/docs/pundit_migration.md +0 -80
- data/docs/quick_start.md +0 -118
- data/docs/rails.md +0 -120
- data/docs/reasons.md +0 -120
- data/docs/scoping.md +0 -255
- data/docs/testing.md +0 -333
- data/docs/writing_policies.md +0 -107
- data/gemfiles/jruby.gemfile +0 -8
- data/gemfiles/rails42.gemfile +0 -8
- data/gemfiles/rails6.gemfile +0 -8
- data/gemfiles/railsmaster.gemfile +0 -6
- data/lib/action_policy/ext/string_match.rb +0 -14
- data/lib/action_policy/ext/yield_self_then.rb +0 -25
data/LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2018-
|
3
|
+
Copyright (c) 2018-2020 Vladimir Dementyev
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
[](https://badge.fury.io/rb/action_policy)
|
2
|
-
|
2
|
+

|
3
|
+

|
3
4
|
[](https://actionpolicy.evilmartians.io)
|
4
5
|
|
5
|
-
#
|
6
|
+
# Action Policy
|
6
7
|
|
7
8
|
Authorization framework for Ruby and Rails applications.
|
8
9
|
|
@@ -21,7 +22,6 @@ Composable. Extensible. Performant.
|
|
21
22
|
|
22
23
|
- RailsConf, 2018 "Access Denied" talk ([video](https://www.youtube.com/watch?v=NVwx0DARDis), [slides](https://speakerdeck.com/palkan/railsconf-2018-access-denied-the-missing-guide-to-authorization-in-rails))
|
23
24
|
|
24
|
-
|
25
25
|
## Integrations
|
26
26
|
|
27
27
|
- GraphQL Ruby ([`action_policy-graphql`](https://github.com/palkan/action_policy-graphql))
|
@@ -36,7 +36,9 @@ gem "action_policy", "~> 0.4.0"
|
|
36
36
|
|
37
37
|
And then execute:
|
38
38
|
|
39
|
-
|
39
|
+
```sh
|
40
|
+
bundle install
|
41
|
+
```
|
40
42
|
|
41
43
|
## Usage
|
42
44
|
|
@@ -89,7 +91,6 @@ end
|
|
89
91
|
|
90
92
|
\* See [Non-Rails Usage](docs/non_rails.md) on how to add `authorize!` to any Ruby project.
|
91
93
|
|
92
|
-
|
93
94
|
When authorization is successful (i.e., the corresponding rule returns `true`), nothing happens, but in case of authorization failure `ActionPolicy::Unauthorized` error is raised.
|
94
95
|
|
95
96
|
There is also an `allowed_to?` method which returns `true` or `false`, and could be used, in views, for example:
|
@@ -98,7 +99,7 @@ There is also an `allowed_to?` method which returns `true` or `false`, and could
|
|
98
99
|
<% @posts.each do |post| %>
|
99
100
|
<li><%= post.title %>
|
100
101
|
<% if allowed_to?(:edit?, post) %>
|
101
|
-
|
102
|
+
<%= link_to post, "Edit">
|
102
103
|
<% end %>
|
103
104
|
</li>
|
104
105
|
<% end %>
|
@@ -121,8 +122,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/palkan
|
|
121
122
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
122
123
|
|
123
124
|
[Documentation]: http://actionpolicy.evilmartians.io
|
124
|
-
|
125
|
-
## Security Contact
|
126
|
-
|
127
|
-
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
|
128
|
-
|
data/lib/action_policy.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "ruby-next"
|
4
|
+
|
5
|
+
require "ruby-next/language/setup"
|
6
|
+
RubyNext::Language.setup_gem_load_path(transpile: true)
|
7
|
+
|
3
8
|
# ActionPolicy is an authorization framework for Ruby/Rails applications.
|
4
9
|
#
|
5
10
|
# It provides a way to write access policies and helpers to check these policies
|
@@ -30,8 +35,9 @@ module ActionPolicy
|
|
30
35
|
attr_accessor :cache_store
|
31
36
|
|
32
37
|
# Find a policy class for a target
|
33
|
-
def lookup(target, allow_nil: false, **options)
|
38
|
+
def lookup(target, allow_nil: false, default: nil, **options)
|
34
39
|
LookupChain.call(target, **options) ||
|
40
|
+
default ||
|
35
41
|
(allow_nil ? nil : raise(NotFound, target))
|
36
42
|
end
|
37
43
|
end
|
@@ -35,12 +35,7 @@ module ActionPolicy
|
|
35
35
|
#
|
36
36
|
# Raises `ActionPolicy::Unauthorized` if check failed.
|
37
37
|
def authorize!(record = :__undef__, to:, **options)
|
38
|
-
|
39
|
-
raise ArgumentError, "Record must be specified" if record.nil?
|
40
|
-
|
41
|
-
options[:context] && (options[:context] = authorization_context.merge(options[:context]))
|
42
|
-
|
43
|
-
policy = policy_for(record: record, **options)
|
38
|
+
policy = lookup_authorization_policy(record, **options)
|
44
39
|
|
45
40
|
Authorizer.call(policy, authorization_rule_for(policy, to))
|
46
41
|
end
|
@@ -49,14 +44,17 @@ module ActionPolicy
|
|
49
44
|
#
|
50
45
|
# Returns true of false.
|
51
46
|
def allowed_to?(rule, record = :__undef__, **options)
|
52
|
-
|
53
|
-
raise ArgumentError, "Record must be specified" if record.nil?
|
47
|
+
policy = lookup_authorization_policy(record, **options)
|
54
48
|
|
55
|
-
|
49
|
+
policy.apply(authorization_rule_for(policy, rule))
|
50
|
+
end
|
56
51
|
|
57
|
-
|
52
|
+
# Returns the authorization result object after applying a specified rule to a record.
|
53
|
+
def allowance_to(rule, record = :__undef__, **options)
|
54
|
+
policy = lookup_authorization_policy(record, **options)
|
58
55
|
|
59
56
|
policy.apply(authorization_rule_for(policy, rule))
|
57
|
+
policy.result
|
60
58
|
end
|
61
59
|
|
62
60
|
def authorization_context
|
@@ -75,6 +73,15 @@ module ActionPolicy
|
|
75
73
|
policy.resolve_rule(rule)
|
76
74
|
end
|
77
75
|
|
76
|
+
def lookup_authorization_policy(record, **options) # :nodoc:
|
77
|
+
record = implicit_authorization_target! if record == :__undef__
|
78
|
+
raise ArgumentError, "Record must be specified" if record.nil?
|
79
|
+
|
80
|
+
options[:context] && (options[:context] = authorization_context.merge(options[:context]))
|
81
|
+
|
82
|
+
policy_for(record: record, **options)
|
83
|
+
end
|
84
|
+
|
78
85
|
module ClassMethods # :nodoc:
|
79
86
|
# Configure authorization context.
|
80
87
|
#
|
@@ -97,12 +104,11 @@ module ActionPolicy
|
|
97
104
|
def authorization_targets
|
98
105
|
return @authorization_targets if instance_variable_defined?(:@authorization_targets)
|
99
106
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
107
|
+
if superclass.respond_to?(:authorization_targets)
|
108
|
+
superclass.authorization_targets.dup
|
109
|
+
else
|
110
|
+
{}
|
111
|
+
end => @authorization_targets
|
106
112
|
end
|
107
113
|
end
|
108
114
|
end
|
@@ -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(
|
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 {
|
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
|
22
|
+
Authorizer.scopify(target, policy, **{type, name, scope_options})
|
22
23
|
end
|
23
24
|
|
24
25
|
# For backward compatibility
|
@@ -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
|
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
|
-
|
86
|
-
|
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 `
|
6
|
-
module
|
5
|
+
# Add `camelize` to Symbol
|
6
|
+
module SymbolCamelize
|
7
7
|
refine Symbol do
|
8
|
-
if "".respond_to?(:
|
9
|
-
def
|
10
|
-
to_s.
|
8
|
+
if "".respond_to?(:camelize)
|
9
|
+
def camelize
|
10
|
+
to_s.camelize
|
11
11
|
end
|
12
12
|
else
|
13
|
-
def
|
13
|
+
def camelize
|
14
14
|
word = to_s.capitalize
|
15
15
|
word.gsub!(/(?:_)([a-z\d]*)/) { $1.capitalize }
|
16
16
|
word
|
data/lib/action_policy/i18n.rb
CHANGED
@@ -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 {
|
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/
|
16
|
-
using ActionPolicy::Ext::
|
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
|
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
|
-
|
30
|
-
|
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
|
-
|
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
|
-
|
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.
|
118
|
-
|
119
|
-
policy_name
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|