rolemodel-rails 2.3.2 → 2.4.1

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: ab7ce13bbc4227eedf045d0ce216b341049445daab3856f6b1d435f9799ffb53
4
+ data.tar.gz: 5e5ce9316bd729ad8dd3bacf21d849aa274d9c870768fd6d7866f5cb2ae093bf
5
5
  SHA512:
6
- metadata.gz: 5954774b2b8bc6772424debb798c8ee2f868f4d20b315a9029b08b0974b77804e1dfb041f243720b21b7506a80598ab58feec39b0083e72323b5162079505eb5
7
- data.tar.gz: 7ca723dafff67deac08f8553a67e3ffdea5c6300922584c2ae5db8a0e24114f60c9a10a83cadedaa47a0815a733d429c3064794371227c9568e5535480b3dc39
6
+ metadata.gz: 74101aeb24869c8e84b1bb645573df9b95e97dfaf79d8c0d4d5980efafe46c64f414b9afded5511a9f4ae8bf0ee2339074e841710f08ba8314d996bd27a847db
7
+ data.tar.gz: 6438c65a802314259eaa3c0ebe8cdd94301e95f3dc5a1e82c1809f2285c219f731953da1b1bfe45ac5c877273a958b7ba5cd17f6052d4500dd12302590f194c4
data/README.md CHANGED
@@ -69,7 +69,6 @@ bin/rails g
69
69
 
70
70
  * [Core Setup](./lib/generators/rolemodel/core_setup)
71
71
  * [Github](./lib/generators/rolemodel/github)
72
- * [Semaphore](./lib/generators/rolemodel/semaphore)
73
72
  * [Heroku](./lib/generators/rolemodel/heroku)
74
73
  * [Readme](./lib/generators/rolemodel/readme)
75
74
  * [Webpack](./lib/generators/rolemodel/webpack)
@@ -219,6 +218,98 @@ DRY_RUN=true rake users:dev[_,bob@example.com]
219
218
  #=> -> 0.0000s
220
219
  ```
221
220
 
221
+ ### `Rolemodel::ResourceFor::ControllerExtension`
222
+
223
+ Provides the private `resource_for` utility method to all controller classes throughout your app. Use it in conjunction with Routing concerns.
224
+
225
+ #### The Problem It Solves
226
+
227
+ Apps accumulate resources that hang off many different parents. Good examples include `comments`, `reports`, `duplications`.
228
+
229
+ 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.
230
+
231
+ The following example illustrates a better pattern.
232
+
233
+ ##### Routing
234
+
235
+ Declare the child resource inside a route concern and pass the parent's class name as a route default using `parent_resource`:
236
+
237
+ ```ruby
238
+ concern :commentable do
239
+ resources :comments, commentable_type: parent_resource.name.classify
240
+ end
241
+
242
+ concern :reportable do
243
+ resources :generated_reports, only: %i[show create], reportable_type: parent_resource.name.classify
244
+ end
245
+
246
+ shallow do
247
+ resources :accounts do
248
+ resources :estimates, concerns: %i[commentable reportable] do
249
+ resources :widgets, concerns: %i[commentable reportable]
250
+ end
251
+ end
252
+ end
253
+ ```
254
+
255
+ ##### Controller
256
+
257
+ One controller serves every parent:
258
+
259
+ ```ruby
260
+ class CommentsController < ApplicationController
261
+ before_action :set_commentable, only: %i[index new create]
262
+ before_action :set_comment, except: %i[index new create]
263
+
264
+ def create
265
+ # ...
266
+ end
267
+
268
+ private
269
+
270
+ def set_commentable
271
+ @commentable = resource_for(:commentable_type) # pass in the symbol specified in your routing concern.
272
+ end
273
+ end
274
+ ```
275
+
276
+ 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.
277
+
278
+ `resource_for` returns the record itself, so it composes with authorization and presentation:
279
+
280
+ ```ruby
281
+ def set_resource
282
+ @resource = authorize resource_for(:resource_type)
283
+ end
284
+ ```
285
+
286
+ #### Guarding User-Supplied Types
287
+
288
+ 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:
289
+
290
+ ```ruby
291
+ REPORT_CONTEXTS = %w[Accessory Estimate PartProxy Tank].freeze
292
+
293
+ before_action :verify_context_type, :set_context, only: %i[create]
294
+
295
+ private
296
+
297
+ def verify_context_type
298
+ return if REPORT_CONTEXTS.include?(params[:context_type])
299
+
300
+ redirect_back_or_to root_url, alert: 'Invalid Request'
301
+ end
302
+ ```
303
+
304
+ 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.
305
+
306
+ #### Notes
307
+
308
+ * Raises `ActiveRecord::RecordNotFound` when the id does not resolve, which Rails renders as a 404 — the same behavior as any other `find`.
309
+ * `safe_constantize` returns `nil` for an unknown constant, producing a `NoMethodError`; allowlisting avoids that.
310
+ * The class name is demodulized when deriving the id param, so `Reporting::Tank` looks for `params[:tank_id]`.
311
+ * Included via `ActiveSupport.on_load(:action_controller_base)`, so `ActionController::API` controllers do not get it.
312
+
222
313
  ## Development
223
314
 
224
315
  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.
@@ -13,7 +13,6 @@
13
13
  * [Optics](./optics)
14
14
  * [README](./readme)
15
15
  * [SaaS](./saas)
16
- * [Semaphore](./semaphore)
17
16
  * [SimpleForm](./simple_form)
18
17
  * [Slim](./slim)
19
18
  * [SoftDestroyable](./soft_destroyable)
@@ -4,7 +4,6 @@ module Rolemodel
4
4
 
5
5
  def run_all_the_generators
6
6
  generate 'rolemodel:github'
7
- generate 'rolemodel:semaphore'
8
7
  generate 'rolemodel:heroku'
9
8
  generate 'rolemodel:readme'
10
9
  generate 'rolemodel:webpack'
@@ -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.1'
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.1
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