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