pundit_extraextra 1.0.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 +7 -0
- data/README.md +126 -0
- data/lib/pundit_extraextra/controller_mixin.rb +10 -0
- data/lib/pundit_extraextra/helpers.rb +15 -0
- data/lib/pundit_extraextra/resource_autoload.rb +345 -0
- data/lib/pundit_extraextra/version.rb +3 -0
- data/lib/pundit_extraextra.rb +4 -0
- metadata +50 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: efe9e9d3572cd753f9834532b4e6e913ea62a839146f9f02506aed669de8ddd6
|
|
4
|
+
data.tar.gz: '0384a52b21b8329de96b08106200b80bbb3b5206997a3e5ca53562d8b7c89643'
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 75ae6276ffd31361b3924b8b5a7eb82b9e87821231cbce03155dc2790ce8d6b6444aaf01fb686b9743e6db4bc8014905f51d7b7f5477be8207328ff5d39313f0
|
|
7
|
+
data.tar.gz: 67498abe57e9e1f160f9b176e8cc388a37a434c87379acc707890ae15a69dc1a7033901c41d99ce8c0b735aae8a242d9e645cec62d8197aba77cb494de537e85
|
data/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# PunditExtra
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/pundit_extra)
|
|
4
|
+
[](https://github.com/DannyBen/pundit_extra/actions?query=workflow%3ATest)
|
|
5
|
+
[](https://codeclimate.com/github/DannyBen/pundit_extra/maintainability)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
This library borrows functionality from [CanCan(Can)][2] and adds it to [Pundit][1].
|
|
10
|
+
|
|
11
|
+
- `can?` and `cannot?` view helpers
|
|
12
|
+
- `load_resource`, `authorize_resource`, `load_and_authorize_resource` and
|
|
13
|
+
`skip_authorization` controller filters
|
|
14
|
+
|
|
15
|
+
The design intentions were:
|
|
16
|
+
|
|
17
|
+
1. To ease the transition from CanCanCan to Pundit.
|
|
18
|
+
2. To reduce boilerplate code in controller methods.
|
|
19
|
+
3. To keep things simple and intentionally avoid dealing with edge cases or
|
|
20
|
+
endless magical options you need to memorize.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
Add to your Gemfile:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
gem 'pundit_extra'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Add to your `ApplicationController`:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
class ApplicationController < ActionController::Base
|
|
36
|
+
include Pundit::Authorization
|
|
37
|
+
include PunditExtra
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## View Helpers: `can?` and `cannot?`
|
|
43
|
+
|
|
44
|
+
You can use the convenience methods `can?` and `cannot?` in any controller
|
|
45
|
+
and view.
|
|
46
|
+
|
|
47
|
+
- `if can? :assign, @task` is the same as Pundit's `policy(@task).assign?`
|
|
48
|
+
- `if can? :index, Task` is the same as Pundit's `policy(Task).index?`
|
|
49
|
+
- `if cannot? :assign, @task` is the opposite of `can?`
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Autoload and Authorize Resource
|
|
53
|
+
|
|
54
|
+
You can add these to your controllers to automatically load the resource
|
|
55
|
+
and/or authorize it.
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
class TasksController < ApplicationController
|
|
59
|
+
before_action :authenticate_user!
|
|
60
|
+
load_resource except: [:index, :create]
|
|
61
|
+
authorize_resource except: [:create]
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The `load_resource` filter will create the appropriate instance variable
|
|
66
|
+
based on the current action.
|
|
67
|
+
|
|
68
|
+
The `authorize_resource` filter will call Pundit's `authorize @model` in each
|
|
69
|
+
action.
|
|
70
|
+
|
|
71
|
+
You can use `except: :action`, or `only: :action` to limit the filter to a
|
|
72
|
+
given action or an array of actions.
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
class TasksController < ApplicationController
|
|
78
|
+
before_action :authenticate_user!
|
|
79
|
+
load_resource except: [:edit, :complete]
|
|
80
|
+
authorize_resource except: :index
|
|
81
|
+
|
|
82
|
+
def index
|
|
83
|
+
# this happens automatically
|
|
84
|
+
# @tasks = policy_scope(Task)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def show
|
|
88
|
+
# this happens automatically
|
|
89
|
+
# @task = Task.find params[:id]
|
|
90
|
+
# authorize @task
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def new
|
|
94
|
+
# this happens automatically
|
|
95
|
+
# @task = Task.new
|
|
96
|
+
# authorize @task
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def create
|
|
100
|
+
# this happens automatically
|
|
101
|
+
# @task = Task.new task_params
|
|
102
|
+
# authorize @task
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
In addition, you can use:
|
|
109
|
+
|
|
110
|
+
- `load_and_authorize_resource` which is a combination shortcut for
|
|
111
|
+
`load_resource` and `authorize_resource`
|
|
112
|
+
- `skip_authorization` which sends `skip_authorization` and
|
|
113
|
+
`skip_policy_scope` to Pundit for all (or the specified) actions.
|
|
114
|
+
|
|
115
|
+
## Credits
|
|
116
|
+
|
|
117
|
+
- [Jonas Nicklas](https://github.com/jnicklas) @ [Pundit][1]
|
|
118
|
+
- [Bryan Rite](https://github.com/bryanrite), [Ryan Bates](https://github.com/ryanb), [Richard Wilson](https://github.com/Senjai) @ [CanCanCan][2]
|
|
119
|
+
- [Tom Morgan](https://github.com/seven1m)
|
|
120
|
+
|
|
121
|
+
Thanks for building awesome stuff.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
[1]: https://github.com/elabs/pundit
|
|
126
|
+
[2]: https://github.com/CanCanCommunity/cancancan
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module PunditExtra
|
|
2
|
+
module Helpers
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.helper_method :can?, :cannot? if base.respond_to? :helper_method
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def can?(action, resource)
|
|
8
|
+
policy(resource).send :"#{action}?"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def cannot?(*args)
|
|
12
|
+
!can?(*args)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
|
|
3
|
+
module PunditExtra
|
|
4
|
+
module ResourceAutoload
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
class_attribute :resource_options
|
|
9
|
+
self.resource_options = []
|
|
10
|
+
before_action :process_resource_callbacks
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
def load_resource(resource_name_or_options = {}, options = {})
|
|
15
|
+
store_resource_options(:load, resource_name_or_options, options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def authorize_resource(resource_name_or_options = {}, options = {})
|
|
19
|
+
store_resource_options(:authorize, resource_name_or_options, options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def skip_authorization(options = {})
|
|
23
|
+
before_action :skip_authorization_and_scope, options.dup
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def load_and_authorize_resource(resource_name_or_options = {}, options = {})
|
|
27
|
+
store_resource_options(:load_and_authorize, resource_name_or_options, options)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
def store_resource_options(action, resource_name_or_options, options)
|
|
32
|
+
resource_name, options = extract_resource_name_and_options(resource_name_or_options, options)
|
|
33
|
+
self.resource_options += [{ action: action, resource_name: resource_name, options: options }]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def extract_resource_name_and_options(resource_name_or_options, options)
|
|
37
|
+
if resource_name_or_options.is_a?(Hash)
|
|
38
|
+
[nil, resource_name_or_options]
|
|
39
|
+
elsif resource_name_or_options.is_a?(Symbol) || resource_name_or_options.is_a?(String)
|
|
40
|
+
[resource_name_or_options.to_s, options]
|
|
41
|
+
else
|
|
42
|
+
[nil, options]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def process_resource_callbacks
|
|
48
|
+
self.class.resource_options.each do |resource_option|
|
|
49
|
+
next if skip_action?(resource_option[:options])
|
|
50
|
+
|
|
51
|
+
if resource_option[:action] == :load
|
|
52
|
+
load_resource(resource_option[:resource_name], resource_option[:options])
|
|
53
|
+
elsif resource_option[:action] == :authorize
|
|
54
|
+
authorize_resource(resource_option[:resource_name], resource_option[:options])
|
|
55
|
+
elsif resource_option[:action] == :load_and_authorize
|
|
56
|
+
load_resource(resource_option[:resource_name], resource_option[:options])
|
|
57
|
+
authorize_resource(resource_option[:resource_name], resource_option[:options])
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def skip_action?(options)
|
|
63
|
+
action = params[:action].to_sym
|
|
64
|
+
(options[:except] && Array(options[:except]).include?(action)) ||
|
|
65
|
+
(options[:only] && !Array(options[:only]).include?(action))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def load_resource(resource_name = nil, options = {})
|
|
69
|
+
resource_name = (resource_name || controller_name.singularize).to_s
|
|
70
|
+
instance_name = options[:instance_name] || resource_name
|
|
71
|
+
scope = resource_name.classify.constantize
|
|
72
|
+
action = params[:action]
|
|
73
|
+
varname = instance_name
|
|
74
|
+
|
|
75
|
+
# Use id_param option if provided, otherwise fallback to default pattern
|
|
76
|
+
resource_id_param = options[:id_param] || "#{resource_name}_id"
|
|
77
|
+
resource_id = params[resource_id_param] || params[:id]
|
|
78
|
+
|
|
79
|
+
if resource_name != controller_name.singularize
|
|
80
|
+
# If the resource being loaded isn't the primary resource for the controller
|
|
81
|
+
# we assume we are loading a single instance of it
|
|
82
|
+
|
|
83
|
+
if options[:through]
|
|
84
|
+
# If there's a through option, find the parent instance
|
|
85
|
+
current_instance = find_parent_instance(options[:through])
|
|
86
|
+
|
|
87
|
+
if current_instance
|
|
88
|
+
if options[:singleton]
|
|
89
|
+
# If the relationship is has_one, we load the single associated instance
|
|
90
|
+
resource = current_instance.public_send(resource_name)
|
|
91
|
+
else
|
|
92
|
+
# Otherwise, we find by resource_id or simply the first matching resource
|
|
93
|
+
resource = resource_id ? current_instance.public_send(resource_name.pluralize).find(resource_id) : current_instance.public_send(resource_name.pluralize).first
|
|
94
|
+
end
|
|
95
|
+
else
|
|
96
|
+
resource = nil
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
# Load the resource directly if no `through` option or if `resource_id_param` is provided
|
|
100
|
+
resource = scope.find(resource_id)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
raise ActiveRecord::RecordNotFound, "No valid parent instance found through #{options[:through].join(', ')}" if resource == nil
|
|
104
|
+
|
|
105
|
+
# Authorize the loaded resource for the 'show' action
|
|
106
|
+
authorize resource, "show?"
|
|
107
|
+
else
|
|
108
|
+
resource = if options[:through]
|
|
109
|
+
load_through_resource(options[:through], resource_name, resource_id, action, options)
|
|
110
|
+
else
|
|
111
|
+
load_direct_resource(scope, action, resource_id, options)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
raise ActiveRecord::RecordNotFound, "Couldn't find #{resource_name.to_s.capitalize} with #{resource_id_param} == #{resource_id}"if resource.nil?
|
|
116
|
+
|
|
117
|
+
if resource.is_a?(ActiveRecord::Relation) || resource.is_a?(Array)
|
|
118
|
+
varname = varname.to_s.pluralize
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
instance_variable_set("@#{varname}", resource)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def find_parent_instance(parents)
|
|
125
|
+
Array(parents).each do |parent|
|
|
126
|
+
parent_resource_name = parent.to_s.singularize
|
|
127
|
+
parent_instance = instance_variable_get("@#{parent_resource_name}")
|
|
128
|
+
|
|
129
|
+
return parent_instance if parent_instance
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def load_singleton_resource(current_instance, resource_name, action, options)
|
|
136
|
+
if action == 'create'
|
|
137
|
+
new_resource = resource_name.classify.constantize.new
|
|
138
|
+
new_resource.attributes = resource_attributes(new_resource, action) if new_resource.respond_to?(:attributes=)
|
|
139
|
+
new_resource
|
|
140
|
+
else
|
|
141
|
+
current_instance.public_send(resource_name)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def load_index_resource(current_instance, resource_name)
|
|
146
|
+
resource = current_instance.public_send(resource_name.pluralize)
|
|
147
|
+
policy_scope(resource)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def find_resource_by_id(current_instance, resource_name, resource_id, find_by_attribute)
|
|
151
|
+
current_instance.public_send(resource_name.pluralize).find_by(find_by_attribute => resource_id)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def create_new_resource(resource_name, action)
|
|
155
|
+
new_resource = resource_name.classify.constantize.new
|
|
156
|
+
new_resource.attributes = resource_attributes(new_resource, action)
|
|
157
|
+
new_resource
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def update_resource(current_instance, resource_name, resource_id, find_by_attribute, action)
|
|
161
|
+
resource = current_instance.public_send(resource_name.pluralize).find_by(find_by_attribute => resource_id)
|
|
162
|
+
unless record.nil?
|
|
163
|
+
authorize resource, "#{action}?"
|
|
164
|
+
resource.attributes = resource_attributes(resource, action) if resource.respond_to?(:attributes=)
|
|
165
|
+
else
|
|
166
|
+
resource = nil
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
resource
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def load_nested_resource(parent_instances, resource_name, current_instance)
|
|
173
|
+
if parent_instances.size > 1
|
|
174
|
+
query = parent_instances.inject({}) do |hash, parent_instance|
|
|
175
|
+
association_name = parent_instance.class.name.underscore.to_sym
|
|
176
|
+
hash.merge!(association_name => parent_instance)
|
|
177
|
+
end
|
|
178
|
+
resource_name.classify.constantize.find_by(query)
|
|
179
|
+
else
|
|
180
|
+
current_instance.public_send(resource_name.pluralize)
|
|
181
|
+
policy_scope(resource_name.classify.constantize)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def load_through_resource(parents, resource_name, resource_id, action, options)
|
|
186
|
+
parent_instances = Array(parents).map do |parent|
|
|
187
|
+
instance_variable_get("@#{parent.to_s.singularize}")
|
|
188
|
+
end.compact
|
|
189
|
+
|
|
190
|
+
if parent_instances.empty?
|
|
191
|
+
raise ActiveRecord::RecordNotFound, "No parent instance found for #{resource_name}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
current_instance = parent_instances.first
|
|
195
|
+
find_by_attribute = options[:find_by] || :id
|
|
196
|
+
|
|
197
|
+
resource = if options[:singleton]
|
|
198
|
+
load_singleton_resource(current_instance, resource_name, action, options)
|
|
199
|
+
elsif action == 'index' && (!resource_id && !options[:singleton])
|
|
200
|
+
load_index_resource(current_instance, resource_name)
|
|
201
|
+
elsif resource_id
|
|
202
|
+
find_resource_by_id(current_instance, resource_name, resource_id, find_by_attribute)
|
|
203
|
+
elsif action == 'create'
|
|
204
|
+
create_new_resource(resource_name, action)
|
|
205
|
+
elsif action == 'update'
|
|
206
|
+
update_resource(current_instance, resource_name, resource_id, find_by_attribute, action)
|
|
207
|
+
else
|
|
208
|
+
load_nested_resource(parent_instances, resource_name, current_instance)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
resource
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def load_direct_resource(scope, action, resource_id, options = {})
|
|
215
|
+
# Determine the attribute to find by and the parameter to use for the ID
|
|
216
|
+
# if it isn't specified we assume we're finding by the 'id' column
|
|
217
|
+
find_by_attribute = options[:find_by] || :id
|
|
218
|
+
|
|
219
|
+
if action == 'create'
|
|
220
|
+
if resource_id
|
|
221
|
+
resource = scope.find_by(find_by_attribute => resource_id) # Use the custom find_by attribute
|
|
222
|
+
else
|
|
223
|
+
new_resource = scope.new
|
|
224
|
+
new_resource.attributes = resource_attributes(new_resource, action) if new_resource.respond_to?(:attributes=)
|
|
225
|
+
resource = new_resource
|
|
226
|
+
end
|
|
227
|
+
elsif action == 'update'
|
|
228
|
+
resource = scope.find_by(find_by_attribute => resource_id) # Use the custom find_by attribute
|
|
229
|
+
unless resource.nil?
|
|
230
|
+
authorize resource, "#{action}?"
|
|
231
|
+
resource.attributes = resource_attributes(resource, action)
|
|
232
|
+
resource = resource
|
|
233
|
+
else
|
|
234
|
+
resource = nil
|
|
235
|
+
end
|
|
236
|
+
elsif action == 'index'
|
|
237
|
+
resource = policy_scope(scope) # Treat as collection for index
|
|
238
|
+
elsif resource_id
|
|
239
|
+
resource = scope.find_by(find_by_attribute => resource_id) # Use the custom find_by attribute
|
|
240
|
+
else
|
|
241
|
+
resource = policy_scope(scope) # Treat as collection for non-standard actions
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
resource
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def load_parent_resources(parents)
|
|
248
|
+
Array(parents).each do |parent|
|
|
249
|
+
parent_resource_name = parent.to_s.singularize
|
|
250
|
+
parent_id = params["#{parent_resource_name}_id"]
|
|
251
|
+
parent_instance = instance_variable_get("@#{parent_resource_name}")
|
|
252
|
+
|
|
253
|
+
unless parent_instance
|
|
254
|
+
parent_scope = parent_resource_name.classify.constantize
|
|
255
|
+
parent_instance = parent_scope.find(parent_id)
|
|
256
|
+
instance_variable_set("@#{parent_resource_name}", parent_instance)
|
|
257
|
+
authorize parent_instance, :show?
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
parent_instance
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def authorize_resource(resource_name = nil, options = {})
|
|
265
|
+
resource_name = (resource_name || controller_name.singularize).to_s
|
|
266
|
+
instance_name = (options[:instance_name] || resource_name).to_s
|
|
267
|
+
resource = instance_variable_get("@#{instance_name}") || resource_name.classify.constantize
|
|
268
|
+
|
|
269
|
+
# Determine if this is a parent resource by checking if it was listed as a `through` resource
|
|
270
|
+
is_parent_resource = self.class.resource_options.any? do |opt|
|
|
271
|
+
opt[:options][:through] && Array(opt[:options][:through]).include?(resource_name.to_sym)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
action = is_parent_resource ? :show : params[:action].to_sym
|
|
275
|
+
if resource_name != controller_name.singularize
|
|
276
|
+
action = :show
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
if resource.is_a?(Class)
|
|
280
|
+
authorize resource, "#{params[:action].to_sym}?"
|
|
281
|
+
else
|
|
282
|
+
authorize resource, "#{action}?"
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def skip_authorization_and_scope
|
|
287
|
+
action = params[:action]
|
|
288
|
+
skip_policy_scope if action == 'index'
|
|
289
|
+
skip_authorization
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def resource_name
|
|
293
|
+
controller_name.singularize
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def resource_class
|
|
297
|
+
resource_name.classify.constantize
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def resource_instance
|
|
301
|
+
instance_variable_get "@#{resource_name}"
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def resource_attributes(resource, action)
|
|
305
|
+
attributes = {}
|
|
306
|
+
|
|
307
|
+
# Get permitted attributes if they are defined
|
|
308
|
+
if has_permitted_attributes?(resource, action)
|
|
309
|
+
attributes = permitted_attributes(resource)
|
|
310
|
+
else
|
|
311
|
+
candidates = ["#{action}_params", "#{resource_name}_params"]
|
|
312
|
+
candidates.each do |candidate|
|
|
313
|
+
if respond_to?(candidate, true)
|
|
314
|
+
attributes.merge!(send(candidate)) { |key, old_val, new_val| old_val }
|
|
315
|
+
break
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Extract URL parameters that are part of the resource's attributes
|
|
321
|
+
url_param_keys = request.path_parameters.keys.map(&:to_sym)
|
|
322
|
+
|
|
323
|
+
# Remove :id from the keys to ensure it isn't included
|
|
324
|
+
url_param_keys.delete(:id)
|
|
325
|
+
|
|
326
|
+
relevant_url_params = params.slice(*url_param_keys).permit!.to_h
|
|
327
|
+
|
|
328
|
+
# Merge only the relevant URL parameters that match resource's column names
|
|
329
|
+
relevant_url_params.each do |key, value|
|
|
330
|
+
if resource.class.column_names.include?(key.to_s)
|
|
331
|
+
attributes[key.to_sym] ||= value
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
attributes
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def has_permitted_attributes?(resource, action)
|
|
339
|
+
return true if policy(resource).respond_to? :"permitted_attributes_for_#{action}"
|
|
340
|
+
return true if policy(resource).respond_to? :permitted_attributes
|
|
341
|
+
|
|
342
|
+
false
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: pundit_extraextra
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Danny Ben Shitrit
|
|
8
|
+
- Andrew Michael Fahmy
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2024-09-18 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: Add CanCanCan like load and authorize to Pundit.
|
|
15
|
+
email: andrew.michael.fahmy@gmail.com
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- README.md
|
|
21
|
+
- lib/pundit_extraextra.rb
|
|
22
|
+
- lib/pundit_extraextra/controller_mixin.rb
|
|
23
|
+
- lib/pundit_extraextra/helpers.rb
|
|
24
|
+
- lib/pundit_extraextra/resource_autoload.rb
|
|
25
|
+
- lib/pundit_extraextra/version.rb
|
|
26
|
+
homepage: https://github.com/sayre1000/pundit_extraextra
|
|
27
|
+
licenses:
|
|
28
|
+
- MIT
|
|
29
|
+
metadata:
|
|
30
|
+
rubygems_mfa_required: 'true'
|
|
31
|
+
post_install_message:
|
|
32
|
+
rdoc_options: []
|
|
33
|
+
require_paths:
|
|
34
|
+
- lib
|
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 3.0.0
|
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: '0'
|
|
45
|
+
requirements: []
|
|
46
|
+
rubygems_version: 3.3.7
|
|
47
|
+
signing_key:
|
|
48
|
+
specification_version: 4
|
|
49
|
+
summary: Additions for PunditExtra
|
|
50
|
+
test_files: []
|