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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5716bd65624ca07d0abd2474a843f44406de11938b3f5be4046f64a35ee13e7b
4
- data.tar.gz: 657b71bf0a3f1b2ccc7a82c80f027686c929c7a1b5d3622bd39a5cfc81471c45
3
+ metadata.gz: 41af565ee256cafc1b08a53dcdce9bf029e1503d8a368d2e946bb4bebec284a2
4
+ data.tar.gz: f7247392ea0c87346ba0915d14506852f34ced3380331905e7e9209f4b2436d7
5
5
  SHA512:
6
- metadata.gz: 5954774b2b8bc6772424debb798c8ee2f868f4d20b315a9029b08b0974b77804e1dfb041f243720b21b7506a80598ab58feec39b0083e72323b5162079505eb5
7
- data.tar.gz: 7ca723dafff67deac08f8553a67e3ffdea5c6300922584c2ae5db8a0e24114f60c9a10a83cadedaa47a0815a733d429c3064794371227c9568e5535480b3dc39
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.
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rolemodel
4
- VERSION = '2.3.2'
4
+ VERSION = '2.4.0'
5
5
  end
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.3.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