action_policy 0.2.4 → 0.3.0.beta1
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/.rubocop.yml +26 -64
- data/.travis.yml +13 -10
- data/CHANGELOG.md +216 -1
- data/Gemfile +7 -0
- data/LICENSE.txt +1 -1
- data/Rakefile +10 -0
- data/action_policy.gemspec +5 -3
- data/benchmarks/namespaced_lookup_cache.rb +18 -22
- data/docs/README.md +3 -3
- data/docs/_sidebar.md +4 -0
- data/docs/aliases.md +9 -5
- data/docs/authorization_context.md +59 -1
- data/docs/behaviour.md +113 -0
- data/docs/caching.md +6 -4
- data/docs/custom_policy.md +1 -2
- data/docs/debugging.md +55 -0
- data/docs/decorators.md +27 -0
- data/docs/i18n.md +41 -2
- data/docs/instrumentation.md +70 -2
- data/docs/lookup_chain.md +5 -4
- data/docs/namespaces.md +1 -1
- data/docs/non_rails.md +2 -3
- data/docs/pundit_migration.md +77 -2
- data/docs/quick_start.md +5 -5
- data/docs/rails.md +5 -2
- data/docs/reasons.md +50 -3
- data/docs/scoping.md +262 -0
- data/docs/testing.md +232 -21
- data/docs/writing_policies.md +1 -1
- data/gemfiles/jruby.gemfile +3 -0
- data/gemfiles/rails42.gemfile +3 -0
- data/gemfiles/rails6.gemfile +8 -0
- data/gemfiles/railsmaster.gemfile +1 -1
- data/lib/action_policy.rb +3 -3
- data/lib/action_policy/authorizer.rb +12 -4
- data/lib/action_policy/base.rb +2 -0
- data/lib/action_policy/behaviour.rb +14 -3
- data/lib/action_policy/behaviours/memoized.rb +1 -1
- data/lib/action_policy/behaviours/policy_for.rb +12 -3
- data/lib/action_policy/behaviours/scoping.rb +32 -0
- data/lib/action_policy/behaviours/thread_memoized.rb +1 -1
- data/lib/action_policy/ext/hash_transform_keys.rb +19 -0
- data/lib/action_policy/ext/module_namespace.rb +1 -1
- data/lib/action_policy/ext/policy_cache_key.rb +2 -1
- data/lib/action_policy/ext/proc_case_eq.rb +14 -0
- data/lib/action_policy/ext/string_constantize.rb +1 -0
- data/lib/action_policy/ext/symbol_classify.rb +22 -0
- data/lib/action_policy/i18n.rb +56 -0
- data/lib/action_policy/lookup_chain.rb +21 -3
- data/lib/action_policy/policy/cache.rb +10 -6
- data/lib/action_policy/policy/core.rb +31 -19
- data/lib/action_policy/policy/execution_result.rb +12 -0
- data/lib/action_policy/policy/pre_check.rb +2 -6
- data/lib/action_policy/policy/reasons.rb +99 -12
- data/lib/action_policy/policy/scoping.rb +165 -0
- data/lib/action_policy/rails/authorizer.rb +20 -0
- data/lib/action_policy/rails/controller.rb +4 -14
- data/lib/action_policy/rails/ext/active_record.rb +10 -0
- data/lib/action_policy/rails/policy/instrumentation.rb +24 -0
- data/lib/action_policy/rails/scope_matchers/action_controller_params.rb +19 -0
- data/lib/action_policy/rails/scope_matchers/active_record.rb +29 -0
- data/lib/action_policy/railtie.rb +29 -7
- data/lib/action_policy/rspec.rb +1 -0
- data/lib/action_policy/rspec/be_authorized_to.rb +1 -1
- data/lib/action_policy/rspec/dsl.rb +103 -0
- data/lib/action_policy/rspec/have_authorized_scope.rb +126 -0
- data/lib/action_policy/rspec/pundit_syntax.rb +1 -1
- data/lib/action_policy/test_helper.rb +69 -4
- data/lib/action_policy/testing.rb +54 -0
- data/lib/action_policy/utils/pretty_print.rb +137 -0
- data/lib/action_policy/utils/suggest_message.rb +21 -0
- data/lib/action_policy/version.rb +1 -1
- metadata +58 -11
@@ -1,17 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
=end
|
3
|
+
#
|
4
|
+
# This benchmark measures the efficiency of NamespaceCache.
|
5
|
+
#
|
6
|
+
# Run it multiple times with cache on/off to see the results:
|
7
|
+
#
|
8
|
+
# $ bundle exec ruby namespaced_lookup_cache.rb
|
9
|
+
# $ bundle exec ruby namespaced_lookup_cache.rb
|
10
|
+
# $ NO_CACHE=1 bundle exec ruby namespaced_lookup_cache.rb
|
11
|
+
# $ NO_CACHE=1 bundle exec ruby namespaced_lookup_cache.rb
|
12
|
+
#
|
15
13
|
|
16
14
|
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
17
15
|
|
@@ -37,7 +35,7 @@ end
|
|
37
35
|
a = A.new
|
38
36
|
b = B.new
|
39
37
|
|
40
|
-
if ENV[
|
38
|
+
if ENV["NO_CACHE"]
|
41
39
|
ActionPolicy::LookupChain.namespace_cache_enabled = false
|
42
40
|
end
|
43
41
|
|
@@ -60,16 +58,14 @@ Benchmark.ips do |x|
|
|
60
58
|
ActionPolicy.lookup(b, namespace: X::Y::Z)
|
61
59
|
end
|
62
60
|
|
63
|
-
x.hold!
|
61
|
+
x.hold! "temp_results"
|
64
62
|
|
65
63
|
x.compare!
|
66
64
|
end
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
no cache B: 42505.4 i/s - 4.20x slower
|
75
|
-
=end
|
66
|
+
#
|
67
|
+
# Comparison:
|
68
|
+
# cache B: 178577.4 i/s
|
69
|
+
# cache A: 173061.4 i/s - same-ish: difference falls within error
|
70
|
+
# no cache A: 97991.7 i/s - same-ish: difference falls within error
|
71
|
+
# no cache B: 42505.4 i/s - 4.20x slower
|
data/docs/README.md
CHANGED
@@ -36,11 +36,11 @@ Why did we decide to build our own authorization gem instead of using the existi
|
|
36
36
|
|
37
37
|
[Pundit][] has been our framework of choice for a long time. Being too _dead-simple_, it required a lot of hacking to fulfill business logic requirements.
|
38
38
|
|
39
|
-
These _hacks_ later
|
39
|
+
These _hacks_ later became Action Policy (initially, we even called it "Pundit, re-visited").
|
40
40
|
|
41
41
|
We also took a few ideas from [CanCanCan][]—such as [default rules and rule aliases](./aliases.md).
|
42
42
|
|
43
|
-
It is also worth noting that Action Policy (despite
|
43
|
+
It is also worth noting that Action Policy (despite having a _Railsy_ name) is designed to be **Rails-free**. On the other hand, it contains some Rails-specific extensions and seamlessly integrates into the framework.
|
44
44
|
|
45
45
|
So, what are the main reasons to consider Action Policy as your authorization tool?
|
46
46
|
|
@@ -50,7 +50,7 @@ So, what are the main reasons to consider Action Policy as your authorization to
|
|
50
50
|
|
51
51
|
- **Code Organization**: use [namespaces](./namespaces.md) to organize your policies (for example, when you have multiple authorization strategies); add [pre-checks](./pre_checks.md) to make rules more readable and better express your business-logic.
|
52
52
|
|
53
|
-
-
|
53
|
+
- **...and more**: [testability](./testing.md), [i18n](./i18n.md) integrations, [actionable errors](./reasons.md).
|
54
54
|
|
55
55
|
Learn more about the motivation behind the Action Policy and its features by watching this [RailsConf talk](https://www.youtube.com/watch?v=NVwx0DARDis).
|
56
56
|
|
data/docs/_sidebar.md
CHANGED
@@ -5,17 +5,21 @@
|
|
5
5
|
* [Non-Rails Usage](non_rails.md)
|
6
6
|
* [Testing](testing.md)
|
7
7
|
* Features
|
8
|
+
* [Authorization Behaviour](behaviour.md)
|
8
9
|
* [Policy Lookup](lookup_chain.md)
|
9
10
|
* [Authorization Context](authorization_context.md)
|
10
11
|
* [Aliases](aliases.md)
|
11
12
|
* [Pre-Checks](pre_checks.md)
|
13
|
+
* [Scoping](scoping.md)
|
12
14
|
* [Caching](caching.md)
|
13
15
|
* [Namespaces](namespaces.md)
|
14
16
|
* [Failure Reasons](reasons.md)
|
15
17
|
* [Instrumentation](instrumentation.md)
|
16
18
|
* [I18n Support](i18n.md)
|
19
|
+
* [Debugging](debugging.md)
|
17
20
|
* Tips & Tricks
|
18
21
|
* [From Pundit to Action Policy](./pundit_migration.md)
|
22
|
+
* [Dealing with Decorators](./decorators.md)
|
19
23
|
* [Controller Action Aliases](controller_action_aliases.md)
|
20
24
|
* Customize
|
21
25
|
* [Base Policy](custom_policy.md)
|
data/docs/aliases.md
CHANGED
@@ -76,11 +76,14 @@ class SuperPolicy < ApplicationPolicy
|
|
76
76
|
|
77
77
|
alias_rule :update?, :destroy?, :create?, to: :edit?
|
78
78
|
|
79
|
-
def manage
|
79
|
+
def manage?
|
80
|
+
end
|
80
81
|
|
81
|
-
def edit
|
82
|
+
def edit?
|
83
|
+
end
|
82
84
|
|
83
|
-
def index
|
85
|
+
def index?
|
86
|
+
end
|
84
87
|
end
|
85
88
|
|
86
89
|
class SubPolicy < AbstractPolicy
|
@@ -88,7 +91,8 @@ class SubPolicy < AbstractPolicy
|
|
88
91
|
|
89
92
|
alias_rule :index?, :update?, to: :manage?
|
90
93
|
|
91
|
-
def create
|
94
|
+
def create?
|
95
|
+
end
|
92
96
|
end
|
93
97
|
```
|
94
98
|
|
@@ -102,7 +106,7 @@ Authorizing against the SuperPolicy:
|
|
102
106
|
* `index?` will resolve to `index?`
|
103
107
|
* `something?` will resolve to `manage?`
|
104
108
|
|
105
|
-
Authorizing against the
|
109
|
+
Authorizing against the SubPolicy:
|
106
110
|
|
107
111
|
* `index?` will resolve to `manage?`
|
108
112
|
* `update?` will resolve to `manage?`
|
@@ -8,7 +8,7 @@ You must configure authorization context in **two places**: in the policy itself
|
|
8
8
|
|
9
9
|
By default, `ActionPolicy::Base` includes `user` as authorization context. If you don't need it, you have to [build your own base policy](custom_policy.md).
|
10
10
|
|
11
|
-
To specify additional contexts, you should use `authorize` method:
|
11
|
+
To specify additional contexts, you should use the `authorize` method:
|
12
12
|
|
13
13
|
```ruby
|
14
14
|
class ApplicationPolicy < ActionPolicy::Base
|
@@ -31,3 +31,61 @@ class ApplicationController < ActionController::Base
|
|
31
31
|
authorize :account, through: :current_account
|
32
32
|
end
|
33
33
|
```
|
34
|
+
|
35
|
+
## Nested Policies vs Contexts
|
36
|
+
|
37
|
+
See also: [action_policy#36](https://github.com/palkan/action_policy/issues/36) and [action_policy#37](https://github.com/palkan/action_policy/pull/37)
|
38
|
+
|
39
|
+
When you call another policy from the policy object (e.g. via `allowed_to?` method),
|
40
|
+
the context of the current policy is passed to the _nested_ policy.
|
41
|
+
|
42
|
+
That means that if the nested policy has a different authorization context, we won't be able
|
43
|
+
to build it (event if you configure all the required keys in the controller).
|
44
|
+
|
45
|
+
For example:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class UserPolicy < ActionPolicy::Base
|
49
|
+
authorize :user
|
50
|
+
|
51
|
+
def show?
|
52
|
+
allowed_to?(:show?, record.profile)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class ProfilePolicy < ActionPolicy::Base
|
57
|
+
authorize :user, :account
|
58
|
+
end
|
59
|
+
|
60
|
+
class ApplicationController < ActionController::Base
|
61
|
+
authorize :user, through: :current_user
|
62
|
+
authorize :account, through: :current_account
|
63
|
+
end
|
64
|
+
|
65
|
+
class UsersController < ApplicationController
|
66
|
+
def show
|
67
|
+
user = User.find(params[:id])
|
68
|
+
|
69
|
+
authorize! user #=> raises "Missing policy authorization context: account"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
That means that **all the policies that could be used together MUST share the same set of authorization contexts** (or at least the _parent_ policies contexts must be subsets of the nested policies contexts).
|
75
|
+
|
76
|
+
|
77
|
+
## Explicit context
|
78
|
+
|
79
|
+
You can override the _implicit_ authorization context (generated with `authorize` method) in-place
|
80
|
+
by passing the `context` option:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
def show
|
84
|
+
user = User.find(params[:id])
|
85
|
+
|
86
|
+
authorize! user, context: {account: user.account}
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
**NOTE:** the explictly provided context is merged with the implicit one (i.e. you can specify
|
91
|
+
only the keys you want to override).
|
data/docs/behaviour.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# Action Policy Behaviour
|
2
|
+
|
3
|
+
Action Policy provides a mixin called `ActionPolicy::Behaviour` which adds authorization methods to your classes.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
Let's make our custom _service_ object aware of authorization:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class PostUpdateAction
|
11
|
+
# First, we should include the behaviour
|
12
|
+
include ActionPolicy::Behaviour
|
13
|
+
|
14
|
+
# Secondly, provide authorization subject (performer)
|
15
|
+
authorize :user
|
16
|
+
|
17
|
+
attr_reader :user
|
18
|
+
|
19
|
+
def initialize(user)
|
20
|
+
@user = user
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(post, params)
|
24
|
+
# Now we can use authorization methods
|
25
|
+
authorize! post, to: :update?
|
26
|
+
|
27
|
+
post.update!(params)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
`ActionPolicy::Behaviour` provides `authorize` class-level method to configure [authorization context](authorization_context.md) and the instance-level methods: `authorize!`, `allowed_to?` and `authorized`:
|
33
|
+
|
34
|
+
### `authorize!`
|
35
|
+
|
36
|
+
This is a _guard-method_ which raises an `ActionPolicy::Unauthorized` exception
|
37
|
+
if authorization failed (i.e. policy rule returns false):
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# `to` is a name of the policy rule to apply
|
41
|
+
authorize! post, to: :update?
|
42
|
+
```
|
43
|
+
|
44
|
+
### `allowed_to?`
|
45
|
+
|
46
|
+
This is a _predicate_ version of `authorize!`: it returns true if authorization succeed and false otherwise:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# the first argument is the rule to apply
|
50
|
+
# the second one is the target
|
51
|
+
if allowed_to?(:edit?, post)
|
52
|
+
# ...
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
### `authorized`
|
57
|
+
|
58
|
+
See [scoping](./scoping.md) docs.
|
59
|
+
|
60
|
+
## Policy lookup
|
61
|
+
|
62
|
+
All three instance methods (`authorize!`, `allowed_to?`, `authorized`) uses the same
|
63
|
+
`policy_for` to lookup a policy class for authorization target. So, you can provide additional options to control the policy lookup process:
|
64
|
+
|
65
|
+
- Explicitly specify policy class using `with` option:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
allowed_to?(:edit?, post, with: SpecialPostPolicy)
|
69
|
+
```
|
70
|
+
|
71
|
+
- Provide a [namespace](./namespaces.md):
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
# Would try to lookup Admin::PostPolicy first
|
75
|
+
authorize! post, to: :destroy?, namespace: Admin
|
76
|
+
```
|
77
|
+
|
78
|
+
## Implicit authorization target
|
79
|
+
|
80
|
+
You can omit the authorization target for all the methods by defining an _implicit authorization target_:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
class PostActions
|
84
|
+
include ActionPolicy::Behaviour
|
85
|
+
|
86
|
+
authorize :user
|
87
|
+
|
88
|
+
attr_reader :user, :post
|
89
|
+
|
90
|
+
def initialize(user, post)
|
91
|
+
@user = user
|
92
|
+
@post = post
|
93
|
+
end
|
94
|
+
|
95
|
+
def update(params)
|
96
|
+
# post is used here implicitly as a target
|
97
|
+
authorize! to: :update
|
98
|
+
|
99
|
+
post.update!(params)
|
100
|
+
end
|
101
|
+
|
102
|
+
def destroy
|
103
|
+
# post is used here implicitly as a target
|
104
|
+
authorize! to: :destroy
|
105
|
+
|
106
|
+
post.destroy!
|
107
|
+
end
|
108
|
+
|
109
|
+
def implicit_authorization_target
|
110
|
+
post
|
111
|
+
end
|
112
|
+
end
|
113
|
+
```
|
data/docs/caching.md
CHANGED
@@ -139,10 +139,10 @@ class StagePolicy < ApplicationPolicy
|
|
139
139
|
def full_access?
|
140
140
|
!record.funnel.is_private? ||
|
141
141
|
user.permissions
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
142
|
+
.where(
|
143
|
+
funnel_id: record.funnel_id,
|
144
|
+
full_access: true
|
145
|
+
).exists?
|
146
146
|
end
|
147
147
|
end
|
148
148
|
```
|
@@ -177,6 +177,8 @@ Where `cache_namespace` is equal to `"acp:#{MAJOR_GEM_VERSION}.#{MINOR_GEM_VERSI
|
|
177
177
|
|
178
178
|
If any object does not respond to `#policy_cache_key`, we fallback to `#cache_key`. If `#cache_key` is not defined, an `ArgumentError` is raised.
|
179
179
|
|
180
|
+
**NOTE:** if your `#cache_key` method is performance-heavy (e.g. like the `ActiveRecord::Relation`'s one), we recommend to explicitly define the `#policy_cache_key` method on the corresponding class to avoid unnecessary load. See also [action_policy#55](https://github.com/palkan/action_policy/issues/55).
|
181
|
+
|
180
182
|
You can define your own `cache_key` / `cache_namespace` / `context_cache_key` methods for policy class to override this logic.
|
181
183
|
|
182
184
|
#### Invalidation
|
data/docs/custom_policy.md
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
|
5
5
|
It looks like this:
|
6
6
|
|
7
|
-
<span style="display:none;"># rubocop:disable Style/ClassAndModuleChildren</span>
|
8
7
|
|
9
8
|
```ruby
|
10
9
|
class ActionPolicy::Base
|
@@ -38,7 +37,7 @@ class ActionPolicy::Base
|
|
38
37
|
end
|
39
38
|
```
|
40
39
|
|
41
|
-
|
40
|
+
|
42
41
|
|
43
42
|
You can write your `ApplicationPolicy` from scratch instead of inheriting from `ActionPolicy::Base`
|
44
43
|
if the defaults above do not fit your needs. The only required component is `ActionPolicy::Policy::Core`:
|
data/docs/debugging.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Debug Helpers
|
2
|
+
|
3
|
+
**NOTE:** this functionality requires two additional gems to be available in the app:
|
4
|
+
- [unparser](https://github.com/mbj/unparser)
|
5
|
+
- [method_source](https://github.com/banister/method_source).
|
6
|
+
|
7
|
+
We usually describe policy rules using _boolean expressions_ (e.g. `A or (B and C)` where each of `A`, `B` and `C` is a simple boolean expression or predicate method).
|
8
|
+
|
9
|
+
When dealing with complex policies, it could be hard to figure out which predicate/check made policy to fail.
|
10
|
+
|
11
|
+
The `Policy#pp(rule)` method aims to help debug such situations.
|
12
|
+
|
13
|
+
Consider a (synthetic) example:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
def feed?
|
17
|
+
(admin? || allowed_to?(:access_feed?)) &&
|
18
|
+
(user.name == "Jack" || user.name == "Kate")
|
19
|
+
end
|
20
|
+
|
21
|
+
```
|
22
|
+
|
23
|
+
Suppose that you want to debug this rule ("Why does it return false?").
|
24
|
+
You can drop a [`binding.pry`](https://github.com/deivid-rodriguez/pry-byebug) (or `binding.irb`) right at the beginning of the method:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
def feed?
|
28
|
+
binding.pry # rubocop:disable Lint/Debugger
|
29
|
+
#...
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Now, run your code and trigger the breakpoint (i.e., run the method):
|
34
|
+
|
35
|
+
```
|
36
|
+
# now you can preview the execution of the rule using the `pp` method (defined on the policy instance)
|
37
|
+
pry> pp :feed?
|
38
|
+
MyPolicy#feed?
|
39
|
+
↳ (
|
40
|
+
admin? #=> false
|
41
|
+
OR
|
42
|
+
allowed_to?(:access_feed?) #=> true
|
43
|
+
)
|
44
|
+
AND
|
45
|
+
(
|
46
|
+
user.name == "Jack" #=> false
|
47
|
+
OR
|
48
|
+
user.name == "Kate" #=> true
|
49
|
+
)
|
50
|
+
|
51
|
+
# you can also check other rules or methods as well
|
52
|
+
pry> pp :admin?
|
53
|
+
MyPolicy#admin?
|
54
|
+
↳ user.admin? #=> false
|
55
|
+
```
|
data/docs/decorators.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Dealing with Decorators
|
2
|
+
|
3
|
+
Ref: [action_policy#7](https://github.com/palkan/action_policy/issues/7).
|
4
|
+
|
5
|
+
Since Action Policy [lookup mechanism](./lookup_chain.md) relies on the target
|
6
|
+
record's class properties (names, methods) it could break when using with _decorators_.
|
7
|
+
|
8
|
+
To make `authorize!` and other [behaviour](./behaviour.md) methods work seamlessly with decorated
|
9
|
+
objects, you might want to _enhance_ the `policy_for` method.
|
10
|
+
|
11
|
+
For example, when using the [Draper](https://github.com/drapergem/draper) gem:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
module ActionPolicy
|
15
|
+
module Draper
|
16
|
+
def policy_for(record:, **opts)
|
17
|
+
# From https://github.com/GoodMeasuresLLC/draper-cancancan/blob/master/lib/draper/cancancan.rb
|
18
|
+
record = record.model while record.is_a?(Draper::Decorator)
|
19
|
+
super(record: record, **opts)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class ApplicationController < ActionController::Base
|
25
|
+
prepend ActionPolicy::Draper
|
26
|
+
end
|
27
|
+
```
|