skinny_controllers 0.9.1 → 0.10.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/README.md +51 -51
- data/lib/skinny_controllers.rb +0 -25
- data/lib/skinny_controllers/diet.rb +31 -8
- data/lib/skinny_controllers/lookup.rb +17 -7
- data/lib/skinny_controllers/lookup/model.rb +2 -4
- data/lib/skinny_controllers/lookup/operation.rb +3 -9
- data/lib/skinny_controllers/lookup/policy.rb +1 -6
- data/lib/skinny_controllers/operation/base.rb +15 -8
- data/lib/skinny_controllers/operation/default.rb +1 -1
- data/lib/skinny_controllers/operation/model_helpers.rb +11 -11
- data/lib/skinny_controllers/policy/base.rb +1 -8
- data/lib/skinny_controllers/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6cba81691573f9ad4648dcacb2733234c5ef4cd5
|
4
|
+
data.tar.gz: cb4d9ff187f35d0cd097da6675ed3de9f74115db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64f11e00970ccbca44700ff970e54cb92a3b0e5d107b80949d03c6f2bf98deca88774f0a39c71b2b6fd3b2b4dceeae34adccba483deef8f55a7c82a2e799ea76
|
7
|
+
data.tar.gz: 6a96956ac79c335072e0755388bb86bd40df665454e57781553090011caa90bbf36ba51a79d1a040dd2cb15d6a59f0f11d8a7448e3f4ec405c29c3d18d7a1d6f
|
data/README.md
CHANGED
@@ -34,12 +34,14 @@ gem 'skinny_controllers'
|
|
34
34
|
```
|
35
35
|
or
|
36
36
|
|
37
|
-
|
37
|
+
```bash
|
38
|
+
gem install skinny_controllers
|
39
|
+
```
|
38
40
|
|
39
41
|
|
40
42
|
## Generators
|
41
43
|
|
42
|
-
```
|
44
|
+
```bash
|
43
45
|
rails g operation event_summary
|
44
46
|
# => create app/operations/event_summary_operations.rb
|
45
47
|
|
@@ -65,18 +67,24 @@ and that's it!
|
|
65
67
|
|
66
68
|
The above does a multitude of assumptions to make sure that you can type the least amount code possible.
|
67
69
|
|
68
|
-
1. Your controller name is
|
70
|
+
1. Your controller name is the name of your _resource_.
|
69
71
|
2. Any defined policies or operations follow the formats (though they don't have to exist):
|
70
|
-
- `class #{
|
71
|
-
- `module #{
|
72
|
+
- `class #{resource_name}Policy`
|
73
|
+
- `module #{resource_name}Operations`
|
72
74
|
3. Your model responds to `find`, and `where`
|
73
|
-
4.
|
74
|
-
5. If
|
75
|
-
6. If using strong parameters, SkinnyControllers will look for `{action}_{model}_params` then `{model}_params` and then `params`. See the `strong_parameters_spec.rb` test to see an example.
|
75
|
+
4. If relying on the default / implicit operations for create and update, the params key for your model's changes much be formatted as `{ Model.name.underscore => { attributes }}`
|
76
|
+
5. If using strong parameters, SkinnyControllers will look for `{action}_{model}_params` then `{model}_params` and then `params`. See the `strong_parameters_spec.rb` test to see an example.
|
76
77
|
|
78
|
+
### Per Controller Configuration
|
77
79
|
|
80
|
+
```ruby
|
81
|
+
skinny_controllers_config model_class: AClass,
|
82
|
+
parent_class: ParentClass,
|
83
|
+
asociation_name: :association_aclasses,
|
84
|
+
model_params_key: :aclass
|
85
|
+
```
|
78
86
|
|
79
|
-
|
87
|
+
#### model_class
|
80
88
|
Lets say you have a JSON API resource that you'd like to render that has some additional/subset of data.
|
81
89
|
Maybe the model is an `Event`, and the resource an `EventSummary` (which could do some aggregation of `Event` data).
|
82
90
|
|
@@ -90,7 +98,8 @@ In `EventSummariesController`, you would make the following additions:
|
|
90
98
|
```ruby
|
91
99
|
class EventSummariesController < ApiController # or whatever your superclass is
|
92
100
|
include SkinnyControllers::Diet
|
93
|
-
|
101
|
+
|
102
|
+
skinny_controllers_config model_class: Event
|
94
103
|
|
95
104
|
def index
|
96
105
|
render json: model, each_serializer: EventSummariesSerializer
|
@@ -101,8 +110,17 @@ class EventSummariesController < ApiController # or whatever your superclass is
|
|
101
110
|
end
|
102
111
|
end
|
103
112
|
```
|
113
|
+
|
104
114
|
Note that `each_serializer` and `serializer` is not part of `SkinnyControllers`, and is part of [ActiveModel::Serializers](https://github.com/rails-api/active_model_serializers).
|
105
115
|
|
116
|
+
Also note that setting `model_class` may be required if your model is namespaced.
|
117
|
+
|
118
|
+
#### parent_class
|
119
|
+
|
120
|
+
#### association_name
|
121
|
+
|
122
|
+
#### model_params_key
|
123
|
+
|
106
124
|
|
107
125
|
### What if your model is namespaced?
|
108
126
|
|
@@ -142,13 +160,13 @@ end
|
|
142
160
|
|
143
161
|
The parameters for directly calling an operation are as follows:
|
144
162
|
|
145
|
-
| # | Parameter | Default when directly calling an operation | Implicit default
|
163
|
+
| # | Parameter | Default when directly calling an operation | Implicit default | Purpose |
|
146
164
|
|---|-------------------|--------------------------------------------|------------------------------|------------------------------------------|
|
147
165
|
| 0 | current_user | n/a | `current_user` | the user performing the action |
|
148
166
|
| 1 | controller_params | n/a | `params` | the full params hash from the controller |
|
149
167
|
| 2 | params_for_action | `controller_params` | `create_params`, `index_params`, etc | e.g.: requiring a foreign key when looking up index |
|
150
168
|
| 3 | action | `controller_params[:action]` | `action_name` | the name of the current action |
|
151
|
-
| 4 |
|
169
|
+
| 4 | options | `{}` | skinny_controllers_config options |
|
152
170
|
|
153
171
|
### For JSON-API
|
154
172
|
|
@@ -213,7 +231,13 @@ module UserOperations
|
|
213
231
|
class Create < SkinnyControllers::Operation::Base
|
214
232
|
def run
|
215
233
|
@model = model_class.new(model_params)
|
216
|
-
|
234
|
+
|
235
|
+
# raising an exception here allows the corresponding resource controller to
|
236
|
+
# `rescue_from SkinnyControllers::DeniedByPolicy` and have a uniform error
|
237
|
+
# returned to the frontend
|
238
|
+
raise SkinnyControllers::DeniedByPolicy.new('Something Horrible') unless allowed?
|
239
|
+
|
240
|
+
@model.save
|
217
241
|
return @model # or just `model`
|
218
242
|
end
|
219
243
|
end
|
@@ -225,7 +249,9 @@ end
|
|
225
249
|
module UserOperations
|
226
250
|
class Update < SkinnyControllers::Operation::Base
|
227
251
|
def run
|
228
|
-
|
252
|
+
# this throws a DeniedByPolicy exception if `allowed?` returns false
|
253
|
+
check_allowed!
|
254
|
+
|
229
255
|
model.update(model_params)
|
230
256
|
model
|
231
257
|
end
|
@@ -248,16 +274,7 @@ module UserOperations
|
|
248
274
|
end
|
249
275
|
```
|
250
276
|
|
251
|
-
|
252
|
-
```ruby
|
253
|
-
# realistically, you'd only want users to be able to access themselves
|
254
|
-
def is_accessible_to?(user)
|
255
|
-
self.id == user.id
|
256
|
-
end
|
257
|
-
```
|
258
|
-
|
259
|
-
Making a call to the destroy action on the `UsersController` will only succeed if the user trying to delete themselves. (Possibly to 'cancel their account')
|
260
|
-
|
277
|
+
NOTE: `allowed?` is `true` by default via the `SkinnyControllers.allow_by_default` option.
|
261
278
|
|
262
279
|
## Defining Policies
|
263
280
|
|
@@ -266,8 +283,8 @@ These are where you define your access logic, and how to decide if a user has ac
|
|
266
283
|
|
267
284
|
```ruby
|
268
285
|
class EventPolicy < SkinnyControllers::Policy::Base
|
269
|
-
def read?(
|
270
|
-
|
286
|
+
def read?(event = object)
|
287
|
+
event.user == user
|
271
288
|
end
|
272
289
|
end
|
273
290
|
```
|
@@ -279,26 +296,20 @@ These are snippets taking from other projects.
|
|
279
296
|
|
280
297
|
### Finding a record when the id parameter isn't passed
|
281
298
|
|
282
|
-
|
283
|
-
|
284
299
|
```ruby
|
285
300
|
module HostOperations
|
286
301
|
class Read < SkinnyControllers::Operation::Base
|
287
302
|
def run
|
288
|
-
|
303
|
+
# always allowed, never restricted
|
304
|
+
# (because there is now call to allowed?)
|
305
|
+
model
|
289
306
|
end
|
290
307
|
|
291
|
-
# Needs to be overridden, because a 'host' can be either
|
292
|
-
# an Event or an Organization.
|
293
|
-
#
|
294
308
|
# the params to this method should include the subdomain
|
295
309
|
# e.g.: { subdomain: 'swingin2015' }
|
296
310
|
def model_from_params
|
297
311
|
subdomain = params[:subdomain]
|
298
|
-
|
299
|
-
host = Event.find_by_domain(subdomain)
|
300
|
-
# if the event doesn't exist, see if we have an organization
|
301
|
-
host ||= Organization.find_by_domain(subdomain)
|
312
|
+
host = Host.find_by_subdomain(subdomain)
|
302
313
|
end
|
303
314
|
end
|
304
315
|
end
|
@@ -337,7 +348,7 @@ module MembershipRenewalOperations
|
|
337
348
|
# so, because the list is sorted by user id, then updated at,
|
338
349
|
# for each user, the first renewal will be chosen...
|
339
350
|
# and because it is descending, that means the most recent renewal
|
340
|
-
sorted_renewals.uniq{|r| r.user_id}
|
351
|
+
sorted_renewals.uniq { |r| r.user_id }
|
341
352
|
end
|
342
353
|
end
|
343
354
|
|
@@ -353,6 +364,7 @@ module UserOperations
|
|
353
364
|
class Update < SkinnyControllers::Operation::Base
|
354
365
|
def run
|
355
366
|
return unless allowed_for?(current_user)
|
367
|
+
# update with password provided by Devise
|
356
368
|
current_user.update_with_password(model_params)
|
357
369
|
current_user
|
358
370
|
end
|
@@ -398,9 +410,9 @@ describe HostOperations do
|
|
398
410
|
let(:operation){ HostOperations::Read.new(nil, { subdomain: subdomain }) }
|
399
411
|
|
400
412
|
it 'finds an event' do
|
401
|
-
|
413
|
+
host = create(:host, domain: subdomain)
|
402
414
|
model = operation.run
|
403
|
-
expect(model).to eq
|
415
|
+
expect(model).to eq host
|
404
416
|
end
|
405
417
|
#...
|
406
418
|
```
|
@@ -461,12 +473,6 @@ describe PackagePolicy do
|
|
461
473
|
|
462
474
|
## Globally Configurable Options
|
463
475
|
|
464
|
-
All of these can be set on `SkinnyControllers`,
|
465
|
-
e.g.:
|
466
|
-
```ruby
|
467
|
-
SkinnyControllers.controller_namespace = 'API'
|
468
|
-
```
|
469
|
-
|
470
476
|
The following options are available:
|
471
477
|
|
472
478
|
|Option|Default|Note|
|
@@ -476,15 +482,9 @@ The following options are available:
|
|
476
482
|
|`policy_suffix`|`'Policy'` | Default suffix for policies classes. |
|
477
483
|
|`controller_namespace`|`''`| Global Namespace for all controllers (e.g.: `'API'`) |
|
478
484
|
|`allow_by_default`| `true` | Default permission |
|
479
|
-
|`accessible_to_method`|`is_accessible_to?`| method to call an the object that the user might be able to access |
|
480
|
-
|`accessible_to_scope`| `accessible_to`| scope / class method on an object that the user might be able to access |
|
481
485
|
|`action_map`| see [skinny_controllers.rb](./lib/skinny_controllers.rb#L61)| |
|
482
486
|
|
483
487
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
488
|
-------------------------------------------------------
|
489
489
|
|
490
490
|
## How is this different from trailblazer?
|
data/lib/skinny_controllers.rb
CHANGED
@@ -32,15 +32,6 @@ module SkinnyControllers
|
|
32
32
|
|
33
33
|
POLICY_METHOD_SUFFIX = '?'
|
34
34
|
|
35
|
-
# Tells the Diet what namespace of the controller
|
36
|
-
# isn't going to be part of the model name
|
37
|
-
#
|
38
|
-
# @example
|
39
|
-
# # config/initializers/skinny_controllers.rb
|
40
|
-
# SkinnyControllers.controller_namespace = 'API'
|
41
|
-
# # 'API::' would be removed from 'API::Namespace::ObjectNamesController'
|
42
|
-
cattr_accessor :controller_namespace
|
43
|
-
|
44
35
|
# Allows integration of a search gem, like ransack.
|
45
36
|
# The scope of this proc is within an operation, so all operation
|
46
37
|
# instance variables will be available.
|
@@ -71,26 +62,10 @@ module SkinnyControllers
|
|
71
62
|
'Policy'
|
72
63
|
end
|
73
64
|
|
74
|
-
cattr_accessor :operations_namespace do
|
75
|
-
''
|
76
|
-
end
|
77
|
-
|
78
|
-
cattr_accessor :policies_namespace do
|
79
|
-
''
|
80
|
-
end
|
81
|
-
|
82
65
|
cattr_accessor :allow_by_default do
|
83
66
|
true
|
84
67
|
end
|
85
68
|
|
86
|
-
cattr_accessor :accessible_to_method do
|
87
|
-
:is_accessible_to?
|
88
|
-
end
|
89
|
-
|
90
|
-
cattr_accessor :accessible_to_scope do
|
91
|
-
:accessible_to
|
92
|
-
end
|
93
|
-
|
94
69
|
# the diet uses ActionController::Base's
|
95
70
|
# `action_name` method to get the current action.
|
96
71
|
# From that action, we map what verb we want to use for our operation
|
@@ -3,14 +3,33 @@ module SkinnyControllers
|
|
3
3
|
module Diet
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
+
ALLOWED_OPTIONS = [
|
7
|
+
:model_class, :parent_class,
|
8
|
+
:asociation_name,
|
9
|
+
:model_params_key
|
10
|
+
].freeze
|
11
|
+
|
6
12
|
included do
|
7
13
|
class << self
|
8
|
-
attr_accessor :
|
9
|
-
|
10
|
-
|
14
|
+
attr_accessor :options
|
15
|
+
|
16
|
+
def skinny_controllers_config(options = {})
|
17
|
+
@options = options.select { |o| ALLOWED_OPTIONS.include?(o) }
|
18
|
+
end
|
11
19
|
end
|
12
20
|
end
|
13
21
|
|
22
|
+
def create_operation(user:, params_for_action: nil)
|
23
|
+
operation_class.new(
|
24
|
+
user,
|
25
|
+
params,
|
26
|
+
params_for_action,
|
27
|
+
action_name,
|
28
|
+
_lookup,
|
29
|
+
_options
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
14
33
|
# TODO: what if we want multiple operations per action?
|
15
34
|
#
|
16
35
|
# @return an instance of the operation with default parameters
|
@@ -18,9 +37,9 @@ module SkinnyControllers
|
|
18
37
|
@operation ||= operation_class.new(
|
19
38
|
current_user,
|
20
39
|
params, params_for_action,
|
21
|
-
action_name,
|
40
|
+
action_name,
|
22
41
|
_lookup,
|
23
|
-
|
42
|
+
_options
|
24
43
|
)
|
25
44
|
end
|
26
45
|
|
@@ -32,11 +51,15 @@ module SkinnyControllers
|
|
32
51
|
_lookup.operation_class
|
33
52
|
end
|
34
53
|
|
54
|
+
def _options
|
55
|
+
self.class.options || {}
|
56
|
+
end
|
57
|
+
|
35
58
|
def _lookup
|
36
59
|
@_lookup ||= Lookup.from_controller(
|
37
60
|
controller_class: self.class,
|
38
61
|
verb: verb_for_action,
|
39
|
-
model_class:
|
62
|
+
model_class: _options[:model_class]
|
40
63
|
)
|
41
64
|
end
|
42
65
|
|
@@ -64,9 +87,9 @@ module SkinnyControllers
|
|
64
87
|
def params_for_action
|
65
88
|
return {} if action_name == 'destroy'
|
66
89
|
|
67
|
-
key =
|
90
|
+
key = _options[:model_params_key]
|
68
91
|
# model_class should be a class
|
69
|
-
klass =
|
92
|
+
klass = _options[:model_class]
|
70
93
|
|
71
94
|
model_key =
|
72
95
|
if key.present?
|
@@ -17,7 +17,7 @@ module SkinnyControllers
|
|
17
17
|
)
|
18
18
|
end
|
19
19
|
|
20
|
-
def from_operation(operation_class:)
|
20
|
+
def from_operation(operation_class:, model_class:)
|
21
21
|
qualified_name = operation_class.name
|
22
22
|
parts = qualified_name.split('::')
|
23
23
|
operation_name = parts[-2]
|
@@ -28,7 +28,7 @@ module SkinnyControllers
|
|
28
28
|
# namespace: parts[0..-3],
|
29
29
|
operation_name: operation_name,
|
30
30
|
operation_class: operation_class,
|
31
|
-
|
31
|
+
model_class: model_class,
|
32
32
|
namespace: qualified_name.deconstantize.deconstantize
|
33
33
|
)
|
34
34
|
end
|
@@ -87,7 +87,7 @@ module SkinnyControllers
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def model_name
|
90
|
-
@model_name ||= @model_class.try(:name) ||
|
90
|
+
@model_name ||= @model_class.try(:name) || resource_name
|
91
91
|
end
|
92
92
|
|
93
93
|
# @return [String] name of the supposed operation class
|
@@ -101,7 +101,7 @@ module SkinnyControllers
|
|
101
101
|
def namespaced_policy_name
|
102
102
|
@namespaced_policy_name ||= [
|
103
103
|
namespace,
|
104
|
-
"#{
|
104
|
+
"#{resource_name}#{SkinnyControllers.policy_suffix}"
|
105
105
|
].reject(&:blank?).join('::')
|
106
106
|
end
|
107
107
|
|
@@ -113,7 +113,7 @@ module SkinnyControllers
|
|
113
113
|
end
|
114
114
|
|
115
115
|
def operation_name
|
116
|
-
@operation_name ||= "#{
|
116
|
+
@operation_name ||= "#{resource_name}#{SkinnyControllers.operations_suffix}"
|
117
117
|
end
|
118
118
|
|
119
119
|
# @return [String] the namespace
|
@@ -123,17 +123,27 @@ module SkinnyControllers
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
+
def resource_name
|
127
|
+
@resource_name ||= resource_parts && resource_parts[-1].singularize ||
|
128
|
+
@model_class&.name ||
|
129
|
+
operation_parts && operation_parts[-1].singularize
|
130
|
+
end
|
131
|
+
|
126
132
|
# PostsController
|
127
133
|
# => Posts
|
128
134
|
#
|
129
135
|
# Api::V2::PostsController
|
130
136
|
# => Api, V2, Posts
|
131
137
|
def resource_parts
|
132
|
-
@resource_parts ||= controller_class_name
|
138
|
+
@resource_parts ||= controller_class_name&.split(/::|Controller/)
|
139
|
+
end
|
140
|
+
|
141
|
+
def operation_parts
|
142
|
+
@operation_parts ||= operation_namespace&.split(/::|Operations/)
|
133
143
|
end
|
134
144
|
|
135
145
|
def controller_class_name
|
136
|
-
@controller_class_name ||= @controller_class
|
146
|
+
@controller_class_name ||= @controller_class&.name
|
137
147
|
end
|
138
148
|
end
|
139
149
|
end
|
@@ -24,11 +24,9 @@ module SkinnyControllers
|
|
24
24
|
|
25
25
|
# Namespace::ModelOperations::Verb => Namespace::ModelOperations
|
26
26
|
namespace = operation_name.deconstantize
|
27
|
-
|
28
|
-
# nested_namespace = namespace.demodulize
|
29
|
-
nested_namespace = namespace.gsub(SkinnyControllers.operations_namespace, '')
|
27
|
+
|
30
28
|
# ModelOperations => Model
|
31
|
-
|
29
|
+
namespace.gsub(SkinnyControllers.operations_suffix, '')
|
32
30
|
end
|
33
31
|
end
|
34
32
|
end
|
@@ -35,15 +35,11 @@ module SkinnyControllers
|
|
35
35
|
|
36
36
|
# @return [Class] namespace for the default operation class
|
37
37
|
def default_operation_namespace_for(model_name)
|
38
|
-
# binding.pry
|
39
38
|
desired_namespace = namespace_from_model(model_name)
|
40
|
-
|
41
|
-
|
42
|
-
namespace_name = "#{parent_namespace}::#{desired_namespace}"
|
43
|
-
namespace = namespace_name.safe_constantize
|
39
|
+
namespace = desired_namespace.safe_constantize
|
44
40
|
|
45
41
|
unless namespace
|
46
|
-
SkinnyControllers.logger.warn("#{
|
42
|
+
SkinnyControllers.logger.warn("#{desired_namespace} not found. Creating...")
|
47
43
|
end
|
48
44
|
|
49
45
|
namespace || Namespace.create_namespace(desired_namespace)
|
@@ -61,10 +57,8 @@ module SkinnyControllers
|
|
61
57
|
# @param [String] the verb/action for the operation
|
62
58
|
# @return [String] the operation based on the model name
|
63
59
|
def name_from_model(model_name, verb)
|
64
|
-
# this namespace is '' by default
|
65
|
-
prefix = SkinnyControllers.operations_namespace
|
66
60
|
namespace = Lookup::Operation.namespace_from_model(model_name)
|
67
|
-
"#{
|
61
|
+
"#{namespace}::#{verb}"
|
68
62
|
end
|
69
63
|
end
|
70
64
|
end
|
@@ -18,8 +18,7 @@ module SkinnyControllers
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def class_name_from_model(name)
|
21
|
-
|
22
|
-
"#{parent_namespace}#{name}" + SkinnyControllers.policy_suffix
|
21
|
+
"#{name}#{SkinnyControllers.policy_suffix}"
|
23
22
|
end
|
24
23
|
|
25
24
|
def define_policy_class(name)
|
@@ -46,10 +45,6 @@ module SkinnyControllers
|
|
46
45
|
def method_name_for_operation(class_name)
|
47
46
|
class_name.demodulize.underscore + POLICY_METHOD_SUFFIX
|
48
47
|
end
|
49
|
-
|
50
|
-
def namespace
|
51
|
-
SkinnyControllers.policies_namespace
|
52
|
-
end
|
53
48
|
end
|
54
49
|
end
|
55
50
|
end
|
@@ -18,7 +18,7 @@ module SkinnyControllers
|
|
18
18
|
|
19
19
|
attr_accessor :params, :current_user, :authorized_via_parent,
|
20
20
|
:action, :params_for_action, :model_key,
|
21
|
-
:association_name,
|
21
|
+
:association_name, :options,
|
22
22
|
:_lookup
|
23
23
|
|
24
24
|
class << self
|
@@ -45,24 +45,27 @@ module SkinnyControllers
|
|
45
45
|
# @param [Hash] controller_params the params hash raw from the controller
|
46
46
|
# @param [Hash] params_for_action optional params hash, generally the result of strong parameters
|
47
47
|
# @param [string] action the current action on the controller
|
48
|
-
def initialize(current_user,
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
def initialize(current_user,
|
49
|
+
controller_params, params_for_action = nil,
|
50
|
+
action = nil,
|
51
|
+
lookup = nil,
|
52
|
+
options = {})
|
52
53
|
self.authorized_via_parent = false
|
53
54
|
self.current_user = current_user
|
54
55
|
self.action = action || controller_params[:action]
|
55
56
|
self.params = controller_params
|
56
57
|
self.params_for_action = params_for_action || controller_params
|
57
|
-
self.model_key = model_key
|
58
58
|
self._lookup = lookup
|
59
|
-
self.
|
59
|
+
self.options = options
|
60
|
+
self.model_key = options[:model_params_key]
|
61
|
+
self.association_name = options[:association_name]
|
60
62
|
end
|
61
63
|
|
62
64
|
def lookup
|
63
65
|
@lookup ||= begin
|
64
66
|
_lookup || Lookup.from_operation(
|
65
|
-
operation_class: self.class
|
67
|
+
operation_class: self.class,
|
68
|
+
model_class: options[:model_class]
|
66
69
|
)
|
67
70
|
end
|
68
71
|
end
|
@@ -104,6 +107,10 @@ module SkinnyControllers
|
|
104
107
|
allowed_for?(model)
|
105
108
|
end
|
106
109
|
|
110
|
+
def check_allowed!(*args)
|
111
|
+
raise DeniedByPolicy(*args.presence || action) unless allowed?
|
112
|
+
end
|
113
|
+
|
107
114
|
# checks the policy
|
108
115
|
def allowed_for?(object)
|
109
116
|
policy_for(object).send(policy_method_name)
|
@@ -61,7 +61,15 @@ module SkinnyControllers
|
|
61
61
|
unless @scoped_model
|
62
62
|
klass_name = scoped_params[:type]
|
63
63
|
operation_class = Lookup::Operation.operation_of(klass_name, DefaultVerbs::Read)
|
64
|
-
|
64
|
+
|
65
|
+
params = { id: scoped_params[:id] }
|
66
|
+
operation = operation_class.new(
|
67
|
+
current_user,
|
68
|
+
params, params,
|
69
|
+
'show', nil,
|
70
|
+
model_class: klass_name.safe_constantize
|
71
|
+
)
|
72
|
+
|
65
73
|
@scoped_model = operation.run
|
66
74
|
self.authorized_via_parent = !!@scoped_model
|
67
75
|
end
|
@@ -70,16 +78,7 @@ module SkinnyControllers
|
|
70
78
|
end
|
71
79
|
|
72
80
|
def model_from_params
|
73
|
-
|
74
|
-
|
75
|
-
if ar_proxy.respond_to? SkinnyControllers.accessible_to_scope
|
76
|
-
# It's better to filter in sql, than in the app, so if there is
|
77
|
-
# a way to do the filtering in active query, do that. This will help
|
78
|
-
# mitigate n+1 query scenarios
|
79
|
-
return ar_proxy.send(SkinnyControllers.accessible_to_scope, current_user)
|
80
|
-
end
|
81
|
-
|
82
|
-
ar_proxy
|
81
|
+
model_class.where(sanitized_params)
|
83
82
|
end
|
84
83
|
|
85
84
|
def model_from_named_id(key, id)
|
@@ -93,6 +92,7 @@ module SkinnyControllers
|
|
93
92
|
def model_from_scope(scope = params[:scope])
|
94
93
|
if scoped = scoped_model(scope)
|
95
94
|
association = association_name_from_object
|
95
|
+
|
96
96
|
scoped.send(association)
|
97
97
|
else
|
98
98
|
raise ActiveRecord::RecordNotFound, "Parent object of type #{scope[:type]} not accessible"
|
@@ -27,7 +27,7 @@ module SkinnyControllers
|
|
27
27
|
# TODO: this means that a destroy method, if defined,
|
28
28
|
# will never be called.... good or bad?
|
29
29
|
# should there be a difference between delete and destroy?
|
30
|
-
return send('delete?'
|
30
|
+
return send('delete?') if action == 'destroy'
|
31
31
|
|
32
32
|
# we know that these methods don't take any parameters,
|
33
33
|
# so args and block can be ignored
|
@@ -51,7 +51,6 @@ module SkinnyControllers
|
|
51
51
|
|
52
52
|
# this should be used when checking access to a single object
|
53
53
|
def read?(o = object)
|
54
|
-
return o.send(accessible_method, user) if o.respond_to?(accessible_method)
|
55
54
|
default?
|
56
55
|
end
|
57
56
|
|
@@ -72,12 +71,6 @@ module SkinnyControllers
|
|
72
71
|
accessible = object.map { |ea| read?(ea) }
|
73
72
|
accessible.all?
|
74
73
|
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def accessible_method
|
79
|
-
SkinnyControllers.accessible_to_method
|
80
|
-
end
|
81
74
|
end
|
82
75
|
end
|
83
76
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skinny_controllers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- L. Preston Sego III
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -227,7 +227,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
227
227
|
requirements:
|
228
228
|
- - ">="
|
229
229
|
- !ruby/object:Gem::Version
|
230
|
-
version: '2.
|
230
|
+
version: '2.3'
|
231
231
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
232
232
|
requirements:
|
233
233
|
- - ">="
|
@@ -238,6 +238,6 @@ rubyforge_project:
|
|
238
238
|
rubygems_version: 2.5.1
|
239
239
|
signing_key:
|
240
240
|
specification_version: 4
|
241
|
-
summary: SkinnyControllers-0.
|
241
|
+
summary: SkinnyControllers-0.10.0
|
242
242
|
test_files: []
|
243
243
|
has_rdoc:
|