rolemodel-rails 2.3.2 → 2.4.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 +92 -0
- data/lib/rolemodel/engine.rb +7 -0
- data/lib/rolemodel/resource_for/controller_extension.rb +15 -0
- data/lib/rolemodel/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 41af565ee256cafc1b08a53dcdce9bf029e1503d8a368d2e946bb4bebec284a2
|
|
4
|
+
data.tar.gz: f7247392ea0c87346ba0915d14506852f34ced3380331905e7e9209f4b2436d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f5fe95adeb421eb88bf8c04e9630b6c688403237fd09b68b4185391254430d12161cd3de74cceabda0b11680a56ee2848fba273b2070a3bc36b7a5ce3701b29d
|
|
7
|
+
data.tar.gz: 89343467c51120526e7ef218d3037fdeb920b75ab846ba5c09a45464897e6484db0532d70d66bf9a3d3477d3d2c13bf34197fe432fd980ff47ad1d6ca50ef058
|
data/README.md
CHANGED
|
@@ -219,6 +219,98 @@ DRY_RUN=true rake users:dev[_,bob@example.com]
|
|
|
219
219
|
#=> -> 0.0000s
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
+
### `Rolemodel::ResourceFor::ControllerExtension`
|
|
223
|
+
|
|
224
|
+
Provides the private `resource_for` utility method to all controller classes throughout your app. Use it in conjunction with Routing concerns.
|
|
225
|
+
|
|
226
|
+
#### The Problem It Solves
|
|
227
|
+
|
|
228
|
+
Apps accumulate resources that hang off many different parents. Good examples include `comments`, `reports`, `duplications`.
|
|
229
|
+
|
|
230
|
+
The naive approach involves many namespaced controllers that all do the same thing but with a different parent resource, or adding several non-restful actions to each parent controller.
|
|
231
|
+
|
|
232
|
+
The following example illustrates a better pattern.
|
|
233
|
+
|
|
234
|
+
##### Routing
|
|
235
|
+
|
|
236
|
+
Declare the child resource inside a route concern and pass the parent's class name as a route default using `parent_resource`:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
concern :commentable do
|
|
240
|
+
resources :comments, commentable_type: parent_resource.name.classify
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
concern :reportable do
|
|
244
|
+
resources :generated_reports, only: %i[show create], reportable_type: parent_resource.name.classify
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
shallow do
|
|
248
|
+
resources :accounts do
|
|
249
|
+
resources :estimates, concerns: %i[commentable reportable] do
|
|
250
|
+
resources :widgets, concerns: %i[commentable reportable]
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
##### Controller
|
|
257
|
+
|
|
258
|
+
One controller serves every parent:
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
class CommentsController < ApplicationController
|
|
262
|
+
before_action :set_commentable, only: %i[index new create]
|
|
263
|
+
before_action :set_comment, except: %i[index new create]
|
|
264
|
+
|
|
265
|
+
def create
|
|
266
|
+
# ...
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
private
|
|
270
|
+
|
|
271
|
+
def set_commentable
|
|
272
|
+
@commentable = resource_for(:commentable_type) # pass in the symbol specified in your routing concern.
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Under `shallow: true`, only the collection actions (`index`, `new`, `create`) carry the parent id — hence the `only:`/`except:` split above. Member actions find the child directly by `params[:id]` and reach the parent through its own association.
|
|
278
|
+
|
|
279
|
+
`resource_for` returns the record itself, so it composes with authorization and presentation:
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
def set_resource
|
|
283
|
+
@resource = authorize resource_for(:resource_type)
|
|
284
|
+
end
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### Guarding User-Supplied Types
|
|
288
|
+
|
|
289
|
+
Route defaults are merged into `params` last, so a route-supplied type cannot be overridden by a query string or request body. If a type ever arrives from user input instead, allowlist it before calling `resource_for` — `safe_constantize` will happily resolve any constant in the app:
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
REPORT_CONTEXTS = %w[Accessory Estimate PartProxy Tank].freeze
|
|
293
|
+
|
|
294
|
+
before_action :verify_context_type, :set_context, only: %i[create]
|
|
295
|
+
|
|
296
|
+
private
|
|
297
|
+
|
|
298
|
+
def verify_context_type
|
|
299
|
+
return if REPORT_CONTEXTS.include?(params[:context_type])
|
|
300
|
+
|
|
301
|
+
redirect_back_or_to root_url, alert: 'Invalid Request'
|
|
302
|
+
end
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Doing this even for route-supplied types is cheap insurance: it documents which parents the controller actually supports and fails loudly when a new route wires up a parent the controller cannot handle.
|
|
306
|
+
|
|
307
|
+
#### Notes
|
|
308
|
+
|
|
309
|
+
* Raises `ActiveRecord::RecordNotFound` when the id does not resolve, which Rails renders as a 404 — the same behavior as any other `find`.
|
|
310
|
+
* `safe_constantize` returns `nil` for an unknown constant, producing a `NoMethodError`; allowlisting avoids that.
|
|
311
|
+
* The class name is demodulized when deriving the id param, so `Reporting::Tank` looks for `params[:tank_id]`.
|
|
312
|
+
* Included via `ActiveSupport.on_load(:action_controller_base)`, so `ActionController::API` controllers do not get it.
|
|
313
|
+
|
|
222
314
|
## Development
|
|
223
315
|
|
|
224
316
|
Install the versions of Node and Ruby specified in `.node-version` and `.ruby-version` on your machine. https://asdf-vm.com/ is a great tool for managing language versions. Then run `corepack enable` to activate the Yarn 4+ version pinned by each project's `packageManager` field.
|
data/lib/rolemodel/engine.rb
CHANGED
|
@@ -3,9 +3,16 @@
|
|
|
3
3
|
module Rolemodel
|
|
4
4
|
class Engine < ::Rails::Engine
|
|
5
5
|
require_relative 'generator_base'
|
|
6
|
+
require_relative 'resource_for/controller_extension'
|
|
6
7
|
|
|
7
8
|
generators do
|
|
8
9
|
require 'generators/rolemodel/all_generator'
|
|
9
10
|
end
|
|
11
|
+
|
|
12
|
+
initializer 'rolemodel.action_controller' do
|
|
13
|
+
ActiveSupport.on_load(:action_controller_base) do
|
|
14
|
+
include Rolemodel::ResourceFor::ControllerExtension
|
|
15
|
+
end
|
|
16
|
+
end
|
|
10
17
|
end
|
|
11
18
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rolemodel
|
|
4
|
+
module ResourceFor
|
|
5
|
+
module ControllerExtension
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def resource_for(type_symbol)
|
|
11
|
+
params[type_symbol].safe_constantize.find(params[params[type_symbol].foreign_key])
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/rolemodel/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rolemodel-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- RoleModel Software Inc
|
|
@@ -436,6 +436,7 @@ files:
|
|
|
436
436
|
- lib/rolemodel/optics/phosphor_icon_builder.rb
|
|
437
437
|
- lib/rolemodel/optics/tabler_icon_builder.rb
|
|
438
438
|
- lib/rolemodel/replace_content_helper.rb
|
|
439
|
+
- lib/rolemodel/resource_for/controller_extension.rb
|
|
439
440
|
- lib/rolemodel/utility.rb
|
|
440
441
|
- lib/rolemodel/utility/task_tools.rb
|
|
441
442
|
- lib/rolemodel/version.rb
|