plutonium 0.33.1 → 0.34.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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/# Plutonium: The pre-alpha demo.md +4 -2
  3. data/.claude/skills/assets/SKILL.md +416 -0
  4. data/.claude/skills/connect-resource/SKILL.md +112 -0
  5. data/.claude/skills/controller/SKILL.md +302 -0
  6. data/.claude/skills/create-resource/SKILL.md +240 -0
  7. data/.claude/skills/definition/SKILL.md +218 -0
  8. data/.claude/skills/definition-actions/SKILL.md +386 -0
  9. data/.claude/skills/definition-fields/SKILL.md +474 -0
  10. data/.claude/skills/definition-query/SKILL.md +334 -0
  11. data/.claude/skills/forms/SKILL.md +439 -0
  12. data/.claude/skills/installation/SKILL.md +300 -0
  13. data/.claude/skills/interaction/SKILL.md +382 -0
  14. data/.claude/skills/model/SKILL.md +267 -0
  15. data/.claude/skills/model-features/SKILL.md +286 -0
  16. data/.claude/skills/nested-resources/SKILL.md +274 -0
  17. data/.claude/skills/package/SKILL.md +191 -0
  18. data/.claude/skills/policy/SKILL.md +352 -0
  19. data/.claude/skills/portal/SKILL.md +400 -0
  20. data/.claude/skills/resource/SKILL.md +281 -0
  21. data/.claude/skills/rodauth/SKILL.md +452 -0
  22. data/.claude/skills/views/SKILL.md +563 -0
  23. data/Appraisals +46 -4
  24. data/CHANGELOG.md +32 -1
  25. data/app/assets/plutonium.css +2 -2
  26. data/config/brakeman.ignore +239 -0
  27. data/config/initializers/action_policy.rb +1 -1
  28. data/docs/.vitepress/config.ts +132 -47
  29. data/docs/concepts/architecture.md +226 -0
  30. data/docs/concepts/auto-detection.md +254 -0
  31. data/docs/concepts/index.md +61 -0
  32. data/docs/concepts/packages-portals.md +304 -0
  33. data/docs/concepts/resources.md +224 -0
  34. data/docs/cookbook/blog.md +412 -0
  35. data/docs/cookbook/index.md +289 -0
  36. data/docs/cookbook/saas.md +481 -0
  37. data/docs/getting-started/index.md +56 -0
  38. data/docs/getting-started/installation.md +146 -0
  39. data/docs/getting-started/tutorial/01-setup.md +118 -0
  40. data/docs/getting-started/tutorial/02-first-resource.md +180 -0
  41. data/docs/getting-started/tutorial/03-authentication.md +246 -0
  42. data/docs/getting-started/tutorial/04-authorization.md +170 -0
  43. data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
  44. data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
  45. data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
  46. data/docs/getting-started/tutorial/index.md +64 -0
  47. data/docs/guides/adding-resources.md +420 -0
  48. data/docs/guides/authentication.md +551 -0
  49. data/docs/guides/authorization.md +468 -0
  50. data/docs/guides/creating-packages.md +380 -0
  51. data/docs/guides/custom-actions.md +523 -0
  52. data/docs/guides/index.md +45 -0
  53. data/docs/guides/multi-tenancy.md +302 -0
  54. data/docs/guides/nested-resources.md +411 -0
  55. data/docs/guides/search-filtering.md +266 -0
  56. data/docs/guides/theming.md +321 -0
  57. data/docs/index.md +67 -26
  58. data/docs/public/CLAUDE.md +64 -21
  59. data/docs/reference/assets/index.md +496 -0
  60. data/docs/reference/controller/index.md +363 -0
  61. data/docs/reference/definition/actions.md +400 -0
  62. data/docs/reference/definition/fields.md +350 -0
  63. data/docs/reference/definition/index.md +252 -0
  64. data/docs/reference/definition/query.md +342 -0
  65. data/docs/reference/generators/index.md +469 -0
  66. data/docs/reference/index.md +49 -0
  67. data/docs/reference/interaction/index.md +445 -0
  68. data/docs/reference/model/features.md +248 -0
  69. data/docs/reference/model/index.md +219 -0
  70. data/docs/reference/policy/index.md +385 -0
  71. data/docs/reference/portal/index.md +382 -0
  72. data/docs/reference/views/forms.md +396 -0
  73. data/docs/reference/views/index.md +479 -0
  74. data/gemfiles/rails_7.gemfile +9 -2
  75. data/gemfiles/rails_7.gemfile.lock +146 -111
  76. data/gemfiles/rails_8.0.gemfile +20 -0
  77. data/gemfiles/rails_8.0.gemfile.lock +417 -0
  78. data/gemfiles/rails_8.1.gemfile +20 -0
  79. data/gemfiles/rails_8.1.gemfile.lock +419 -0
  80. data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
  81. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
  82. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
  83. data/lib/generators/pu/pkg/portal/USAGE +65 -0
  84. data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
  85. data/lib/generators/pu/res/conn/USAGE +71 -0
  86. data/lib/generators/pu/res/model/USAGE +106 -110
  87. data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
  88. data/lib/generators/pu/res/scaffold/USAGE +85 -0
  89. data/lib/generators/pu/rodauth/install_generator.rb +2 -6
  90. data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
  91. data/lib/generators/pu/skills/sync/USAGE +14 -0
  92. data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
  93. data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
  94. data/lib/plutonium/core/controller.rb +2 -2
  95. data/lib/plutonium/interaction/base.rb +1 -0
  96. data/lib/plutonium/package/engine.rb +2 -2
  97. data/lib/plutonium/query/adhoc_block.rb +6 -2
  98. data/lib/plutonium/query/model_scope.rb +1 -1
  99. data/lib/plutonium/railtie.rb +4 -0
  100. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
  101. data/lib/plutonium/resource/query_object.rb +38 -8
  102. data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
  103. data/lib/plutonium/version.rb +1 -1
  104. data/lib/tasks/release.rake +19 -4
  105. data/package.json +1 -1
  106. metadata +76 -39
  107. data/brakeman.ignore +0 -28
  108. data/docs/api-examples.md +0 -49
  109. data/docs/guide/claude-code-guide.md +0 -74
  110. data/docs/guide/deep-dive/authorization.md +0 -189
  111. data/docs/guide/deep-dive/multitenancy.md +0 -256
  112. data/docs/guide/deep-dive/resources.md +0 -390
  113. data/docs/guide/getting-started/01-installation.md +0 -165
  114. data/docs/guide/index.md +0 -28
  115. data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
  116. data/docs/guide/introduction/02-core-concepts.md +0 -440
  117. data/docs/guide/tutorial/01-project-setup.md +0 -75
  118. data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
  119. data/docs/guide/tutorial/03-defining-resources.md +0 -90
  120. data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
  121. data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
  122. data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
  123. data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
  124. data/docs/markdown-examples.md +0 -85
  125. data/docs/modules/action.md +0 -244
  126. data/docs/modules/authentication.md +0 -236
  127. data/docs/modules/configuration.md +0 -599
  128. data/docs/modules/controller.md +0 -443
  129. data/docs/modules/core.md +0 -316
  130. data/docs/modules/definition.md +0 -1308
  131. data/docs/modules/display.md +0 -759
  132. data/docs/modules/form.md +0 -495
  133. data/docs/modules/generator.md +0 -400
  134. data/docs/modules/index.md +0 -167
  135. data/docs/modules/interaction.md +0 -642
  136. data/docs/modules/package.md +0 -151
  137. data/docs/modules/policy.md +0 -176
  138. data/docs/modules/portal.md +0 -710
  139. data/docs/modules/query.md +0 -297
  140. data/docs/modules/resource_record.md +0 -618
  141. data/docs/modules/routing.md +0 -690
  142. data/docs/modules/table.md +0 -301
  143. data/docs/modules/ui.md +0 -631
@@ -1,690 +0,0 @@
1
- ---
2
- title: Routing Module
3
- ---
4
-
5
- # Routing Module
6
-
7
- Plutonium's routing system transforms the way you think about Rails routing. Instead of manually defining dozens of routes, you simply register your resources and Plutonium automatically generates comprehensive routing structures including CRUD operations, nested associations, interactive actions, and multi-tenant scoping.
8
-
9
- ::: tip
10
- The Routing module is located in `lib/plutonium/routing/` and seamlessly extends Rails' built-in routing system.
11
- :::
12
-
13
- ## The Routing Revolution
14
-
15
- Traditional Rails routing requires you to manually define every route, leading to repetitive, error-prone route files. Plutonium's approach is radically different:
16
-
17
- **Traditional Rails Approach:**
18
- ```ruby
19
- # Lots of manual route definition
20
- resources :posts do
21
- member do
22
- post :publish
23
- post :archive
24
- end
25
-
26
- resources :comments, except: [:new, :edit]
27
- end
28
-
29
- resources :users do
30
- resources :posts, controller: 'users/posts'
31
- resources :comments, controller: 'users/comments'
32
- end
33
- ```
34
-
35
- **Plutonium Approach:**
36
- ```ruby
37
- # Simple, declarative registration
38
- register_resource Post
39
- register_resource Comment
40
- register_resource User
41
-
42
- # Plutonium automatically generates:
43
- # - All CRUD routes
44
- # - Nested association routes
45
- # - Interactive action routes
46
- # - Multi-tenant scoped routes
47
- ```
48
-
49
- ## Core Routing Principles
50
-
51
- Plutonium's routing system is built on four fundamental concepts:
52
-
53
- - **Declarative Registration**: Register resources instead of defining individual routes
54
- - **Intelligent Generation**: Routes are created based on your model associations and definitions
55
- - **Entity Scoping**: Automatic multi-tenant routing with parameter injection
56
- - **Interactive Actions**: Dynamic routes for business operations and user interactions
57
-
58
- ## Resource Registration: The Foundation
59
-
60
- ### Basic Resource Registration
61
-
62
- The heart of Plutonium routing is the `register_resource` method:
63
-
64
- ```ruby
65
- # packages/admin_portal/config/routes.rb
66
- AdminPortal::Engine.routes.draw do
67
- root to: "dashboard#index"
68
-
69
- # Register your resources - that's it!
70
- register_resource User
71
- register_resource Post
72
- register_resource Comment
73
- end
74
- ```
75
-
76
- ### What Registration Creates
77
-
78
- When you register a single resource, Plutonium automatically generates:
79
-
80
- ```ruby
81
- register_resource Post
82
-
83
- # Standard CRUD routes:
84
- # GET /posts # index - list all posts
85
- # GET /posts/new # new - form for creating posts
86
- # POST /posts # create - handle post creation
87
- # GET /posts/:id # show - display specific post
88
- # GET /posts/:id/edit # edit - form for editing posts
89
- # PATCH /posts/:id # update - handle post updates
90
- # PUT /posts/:id # update - alternative update method
91
- # DELETE /posts/:id # destroy - delete posts
92
-
93
- # Interactive action routes:
94
- # GET /posts/resource_actions/:action # Resource-level operations
95
- # POST /posts/resource_actions/:action # Execute resource operations
96
- # GET /posts/:id/record_actions/:action # Individual record operations
97
- # POST /posts/:id/record_actions/:action # Execute record operations
98
- # GET /posts/bulk_actions/:action # Bulk operations on multiple records
99
- # POST /posts/bulk_actions/:action # Execute bulk operations
100
-
101
- # Nested association routes (if Post has_many :comments):
102
- # GET /posts/:post_id/nested_comments # Comments belonging to a post
103
- # GET /posts/:post_id/nested_comments/:id # Specific comment in context
104
- ```
105
-
106
- ### Advanced Registration Options
107
-
108
- #### Singular Resources
109
-
110
- For resources that don't need collection routes:
111
-
112
- ```ruby
113
- register_resource Profile, singular: true
114
-
115
- # Generates singular routes:
116
- # GET /profile # show
117
- # GET /profile/new # new
118
- # POST /profile # create
119
- # GET /profile/edit # edit
120
- # PATCH /profile # update
121
- # DELETE /profile # destroy
122
- ```
123
-
124
- #### Custom Routes with Blocks
125
-
126
- Add custom routes alongside the standard ones:
127
-
128
- ```ruby
129
- register_resource Post do
130
- # Member routes (operate on specific posts)
131
- member do
132
- get :publish # GET /posts/1/publish
133
- post :archive # POST /posts/1/archive
134
- patch :featured # PATCH /posts/1/featured
135
- end
136
-
137
- # Collection routes (operate on post collection)
138
- collection do
139
- get :search # GET /posts/search
140
- get :recent # GET /posts/recent
141
- post :bulk_update # POST /posts/bulk_update
142
- end
143
-
144
- # Nested resources for complex relationships
145
- resources :comments, only: [:index, :show]
146
-
147
- # Alternative syntax for single routes
148
- get :preview, on: :member # GET /posts/1/preview
149
- end
150
- ```
151
-
152
- **Handling Custom Routes in Controllers:**
153
- ```ruby
154
- class PostsController < ApplicationController
155
- include Plutonium::Resource::Controller
156
-
157
- # Custom member actions
158
- def publish
159
- authorize_current!(resource_record!)
160
- resource_record!.update!(published: true)
161
- redirect_to resource_url_for(resource_record!), success: "Post published!"
162
- end
163
-
164
- def archive
165
- authorize_current!(resource_record!)
166
- resource_record!.update!(archived: true)
167
- redirect_to resource_url_for(resource_class), success: "Post archived!"
168
- end
169
-
170
- # Custom collection actions
171
- def search
172
- authorize_current!(resource_class)
173
- @query = params[:q]
174
- @posts = resource_scope.where("title ILIKE ?", "%#{@query}%")
175
- render :index
176
- end
177
- end
178
- ```
179
-
180
- ## Automatic Nested Resource Generation
181
-
182
- One of Plutonium's most powerful features is automatic nested route generation based on your ActiveRecord associations.
183
-
184
- ### How Association-Based Routing Works
185
-
186
- ```ruby
187
- # Define your model associations
188
- class User < ApplicationRecord
189
- include Plutonium::Resource::Record
190
-
191
- has_many :posts
192
- has_many :comments
193
- has_many :projects
194
- end
195
-
196
- class Post < ApplicationRecord
197
- include Plutonium::Resource::Record
198
-
199
- belongs_to :user
200
- has_many :comments
201
- end
202
-
203
- # Register resources normally
204
- AdminPortal::Engine.routes.draw do
205
- register_resource User
206
- register_resource Post
207
- register_resource Comment
208
- end
209
- ```
210
-
211
- **Plutonium automatically generates nested routes:**
212
- ```ruby
213
- # User's nested resources:
214
- # GET /users/:user_id/nested_posts # User's posts
215
- # GET /users/:user_id/nested_posts/:id # Specific post by user
216
- # GET /users/:user_id/nested_comments # User's comments
217
- # GET /users/:user_id/nested_projects # User's projects
218
-
219
- # Post's nested resources:
220
- # GET /posts/:post_id/nested_comments # Post's comments
221
- # GET /posts/:post_id/nested_comments/:id # Specific comment on post
222
- ```
223
-
224
- ### Nested Route Naming Convention
225
-
226
- Nested routes use the `nested_#{resource_name}` pattern to avoid conflicts:
227
-
228
- - **Standard route**: `/posts` → `PostsController#index`
229
- - **Nested route**: `/users/:user_id/nested_posts` → `PostsController#index` (with `current_parent`)
230
-
231
- ### Automatic Parent Resolution
232
-
233
- Controllers automatically handle parent relationships in nested contexts:
234
-
235
- ```ruby
236
- class PostsController < ApplicationController
237
- include Plutonium::Resource::Controller
238
-
239
- def index
240
- # When accessed via /users/123/nested_posts
241
- current_parent # => User.find(123) - automatically resolved
242
- parent_route_param # => :user_id
243
- parent_input_param # => :user (the belongs_to association name)
244
-
245
- # Parameters are automatically merged for creation
246
- resource_params # => includes user: current_parent
247
-
248
- # URLs automatically include parent context
249
- resource_url_for(Post) # => "/users/123/nested_posts"
250
- resource_url_for(@post) # => "/users/123/nested_posts/456"
251
- end
252
- end
253
- ```
254
-
255
- ## Entity Scoping: Multi-Tenant Routing
256
-
257
- Entity scoping automatically transforms your routes to support multi-tenancy, where all data is scoped to a parent entity like Organization or Account.
258
-
259
- ### Path-Based Scoping
260
-
261
- The most common approach uses URL path parameters:
262
-
263
- ```ruby
264
- # Engine configuration
265
- class AdminPortal::Engine < Rails::Engine
266
- include Plutonium::Portal::Engine
267
-
268
- scope_to_entity Organization, strategy: :path
269
- end
270
- ```
271
-
272
- **Route Transformation:**
273
- ```ruby
274
- # Without scoping:
275
- # GET /posts
276
- # GET /posts/:id
277
-
278
- # With path scoping:
279
- # GET /:organization_id/posts
280
- # GET /:organization_id/posts/:id
281
- ```
282
-
283
- ### Custom Scoping Strategies
284
-
285
- For more sophisticated multi-tenancy patterns:
286
-
287
- ```ruby
288
- # Subdomain-based scoping
289
- scope_to_entity Organization, strategy: :current_organization
290
-
291
- # Custom parameter key
292
- scope_to_entity Organization,
293
- strategy: :path,
294
- param_key: :org_slug
295
-
296
- # Routes become: GET /:org_slug/posts
297
- ```
298
-
299
- **Required Controller Implementation:**
300
- ```ruby
301
- module AdminPortal::Concerns::Controller
302
- private
303
-
304
- # Method name MUST match the strategy name exactly
305
- def current_organization
306
- @current_organization ||= Organization.find_by!(subdomain: request.subdomain)
307
- rescue ActiveRecord::RecordNotFound
308
- redirect_to root_path, error: "Invalid organization"
309
- end
310
- end
311
- ```
312
-
313
- ### Entity Scoping with Nested Routes
314
-
315
- Scoping applies to both standard and nested routes:
316
-
317
- ```ruby
318
- scope_to_entity Organization, strategy: :path
319
-
320
- # Standard scoped routes:
321
- # GET /:organization_id/users
322
- # GET /:organization_id/posts
323
-
324
- # Nested scoped routes:
325
- # GET /:organization_id/users/:user_id/nested_posts
326
- # GET /:organization_id/posts/:post_id/nested_comments
327
- ```
328
-
329
- ## Smart URL Generation
330
-
331
- Plutonium provides intelligent URL generation that handles scoping, nesting, and context automatically.
332
-
333
- ### The `resource_url_for` Method
334
-
335
- This is your go-to method for generating resource URLs:
336
-
337
- ```ruby
338
- # Basic usage
339
- resource_url_for(User) # => "/users"
340
- resource_url_for(@user) # => "/users/123"
341
- resource_url_for(@user, action: :edit) # => "/users/123/edit"
342
-
343
- # With entity scoping
344
- resource_url_for(@user) # => "/organizations/456/users/123"
345
-
346
- # Nested resources
347
- resource_url_for(Post, parent: @user) # => "/users/123/nested_posts"
348
- resource_url_for(@post, parent: @user) # => "/users/123/nested_posts/789"
349
-
350
- # Override parent context
351
- resource_url_for(@post, parent: nil) # => "/posts/789"
352
-
353
- # Different actions
354
- resource_url_for(@post, action: :edit, parent: @user)
355
- # => "/users/123/nested_posts/789/edit"
356
- ```
357
-
358
- ### Interactive Action URLs
359
-
360
- Special URL generation for interactive actions:
361
-
362
- ```ruby
363
- # Record-level actions (operate on specific records)
364
- record_action_url(@post, :publish)
365
- # => "/posts/123/record_actions/publish"
366
-
367
- # Resource-level actions (operate on the resource class)
368
- resource_action_url(Post, :import)
369
- # => "/posts/resource_actions/import"
370
-
371
- # Bulk actions (operate on multiple records)
372
- bulk_action_url(Post, :archive, ids: [1, 2, 3])
373
- # => "/posts/bulk_actions/archive?ids[]=1&ids[]=2&ids[]=3"
374
- ```
375
-
376
- ### Dynamic URL Generation for Actions
377
-
378
- For actions that need context-aware URL generation, use `RouteOptions` with custom `url_resolver`:
379
-
380
- ```ruby
381
- # In a resource definition
382
- class ProjectDefinition < Plutonium::Resource::Definition
383
- # Dynamic parent-child navigation
384
- action :create_deployment,
385
- label: "Create Deployment",
386
- icon: Phlex::TablerIcons::Rocket,
387
- record_action: true,
388
- route_options: Plutonium::Action::RouteOptions.new(
389
- url_resolver: ->(subject) {
390
- resource_url_for(UniversalFlow::Deployment, action: :new, parent: subject)
391
- }
392
- )
393
-
394
- # Conditional routing based on permissions
395
- action :manage_settings,
396
- label: "Settings",
397
- resource_action: true,
398
- route_options: Plutonium::Action::RouteOptions.new(
399
- url_resolver: ->(subject) {
400
- if current_user.admin?
401
- admin_project_settings_path(subject)
402
- else
403
- project_settings_path(subject)
404
- end
405
- }
406
- )
407
-
408
- # External system integration
409
- action :view_in_external_system,
410
- label: "View Externally",
411
- record_action: true,
412
- route_options: Plutonium::Action::RouteOptions.new(
413
- url_resolver: ->(subject) {
414
- "https://external-system.com/projects/#{subject.external_id}"
415
- }
416
- )
417
- end
418
- ```
419
-
420
- The `url_resolver` lambda receives:
421
- - **For record actions**: The current record instance
422
- - **For resource actions**: The resource class
423
- - **For bulk actions**: The resource class (with selected IDs available in params)
424
-
425
- ### Context-Aware URL Generation
426
-
427
- In nested controller contexts, URLs automatically include proper context:
428
-
429
- ```ruby
430
- class PostsController < ApplicationController
431
- include Plutonium::Resource::Controller
432
-
433
- def show
434
- # When accessed via /users/123/nested_posts/456
435
-
436
- # These automatically include the user context:
437
- resource_url_for(Post) # => "/users/123/nested_posts"
438
- resource_url_for(@post, action: :edit) # => "/users/123/nested_posts/456/edit"
439
-
440
- # Parent is automatically detected:
441
- current_parent # => User.find(123)
442
- end
443
- end
444
- ```
445
-
446
- ## Advanced Routing Patterns
447
-
448
- ### Multiple Engine Mounting
449
-
450
- Different engines can have different routing strategies:
451
-
452
- ```ruby
453
- # config/routes.rb
454
- Rails.application.routes.draw do
455
- # Admin portal with organization scoping
456
- constraints Rodauth::Rails.authenticate(:admin) do
457
- mount AdminPortal::Engine, at: "/admin"
458
- end
459
-
460
- # Customer portal with account scoping
461
- constraints Rodauth::Rails.authenticate(:customer) do
462
- mount CustomerPortal::Engine, at: "/app"
463
- end
464
-
465
- # Public portal with no scoping or authentication
466
- mount PublicPortal::Engine, at: "/"
467
- end
468
- ```
469
-
470
- ### Route Constraints and Conditions
471
-
472
- ```ruby
473
- Rails.application.routes.draw do
474
- # Subdomain-based portal mounting
475
- constraints subdomain: 'admin' do
476
- mount AdminPortal::Engine, at: "/"
477
- end
478
-
479
- # Feature flag-based mounting
480
- constraints ->(request) { FeatureFlag.enabled?(:beta_portal) } do
481
- mount BetaPortal::Engine, at: "/beta"
482
- end
483
-
484
- # IP-based constraints for admin access
485
- constraints ip: /192\.168\.1\.\d+/ do
486
- mount AdminPortal::Engine, at: "/secure-admin"
487
- end
488
- end
489
- ```
490
-
491
- ### Route Generation Lifecycle
492
-
493
- Understanding how Plutonium generates routes helps with debugging:
494
-
495
- **1. Registration Phase:**
496
- ```ruby
497
- register_resource Post
498
- # - Resource is registered with the engine
499
- # - Route configuration is created and stored
500
- # - Concern name is generated (posts_routes)
501
- ```
502
-
503
- **2. Route Definition Phase:**
504
- ```ruby
505
- concern :posts_routes do
506
- resources :posts, controller: "posts", concerns: [:interactive_resource_actions] do
507
- # Nested routes for has_many associations
508
- resources "nested_comments", controller: "comments"
509
- end
510
- end
511
- ```
512
-
513
- **3. Route Materialization Phase:**
514
- ```ruby
515
- scope :organization_id, as: :organization_id do
516
- concerns :posts_routes, :comments_routes, :users_routes
517
- end
518
- # - All registered concerns are materialized within appropriate scope
519
- # - Entity scoping parameters are applied
520
- # - Final route table is generated
521
- ```
522
-
523
- ## Debugging and Troubleshooting
524
-
525
- ### Inspecting Generated Routes
526
-
527
- ```ruby
528
- # View all routes for an engine
529
- AdminPortal::Engine.routes.routes.each do |route|
530
- puts "#{route.verb.ljust(6)} #{route.path.spec}"
531
- end
532
-
533
- # View registered resources
534
- AdminPortal::Engine.resource_register.resources
535
- # => [User, Post, Comment]
536
-
537
- # View route configurations
538
- AdminPortal::Engine.routes.resource_route_config_lookup
539
- # => { "posts" => {...}, "users" => {...} }
540
-
541
- # Check available route helpers
542
- AdminPortal::Engine.routes.url_helpers.methods.grep(/path|url/)
543
- ```
544
-
545
- ### Common Issues and Solutions
546
-
547
- **Missing Nested Routes:**
548
- ```ruby
549
- # Ensure the association exists
550
- User.reflect_on_association(:posts) # Should not be nil
551
-
552
- # Check association route discovery
553
- User.has_many_association_routes # Should include "posts"
554
- ```
555
-
556
- **Incorrect Entity Scoping:**
557
- ```ruby
558
- # Verify engine configuration
559
- AdminPortal::Engine.scoped_to_entity? # => true
560
- AdminPortal::Engine.scoped_entity_class # => Organization
561
- AdminPortal::Engine.scoped_entity_strategy # => :path
562
- ```
563
-
564
- **Interactive Action Routes Missing:**
565
- ```ruby
566
- # Ensure action is defined in resource definition
567
- PostDefinition.new.defined_actions.keys # Should include your action
568
- ```
569
-
570
- **Route Helper Not Found:**
571
- ```ruby
572
- # Include the engine's route helpers
573
- include AdminPortal::Engine.routes.url_helpers
574
-
575
- # Test URL generation
576
- posts_path # => "/posts" or "/organizations/:organization_id/posts"
577
- ```
578
-
579
- ## Best Practices
580
-
581
- ### Route Organization
582
-
583
- **Register Resources Logically:**
584
- ```ruby
585
- # ✅ Good - logical grouping
586
- AdminPortal::Engine.routes.draw do
587
- # Core entities first
588
- register_resource Organization
589
- register_resource User
590
-
591
- # Business domain resources
592
- register_resource Project
593
- register_resource Task
594
-
595
- # Supporting resources
596
- register_resource Comment
597
- register_resource Attachment
598
- end
599
- ```
600
-
601
- **Leverage Entity Scoping:**
602
- ```ruby
603
- # ✅ Good - consistent scoping strategy
604
- class AdminPortal::Engine < Rails::Engine
605
- scope_to_entity Organization, strategy: :path
606
- end
607
-
608
- # All resources automatically scoped to organization
609
- # Consistent URL structure: /:organization_id/resources
610
- ```
611
-
612
- ### Security Considerations
613
-
614
- ```ruby
615
- # ✅ Good - proper scoping for multi-tenancy
616
- scope_to_entity Organization, strategy: :path
617
-
618
- # ✅ Good - route-level authentication
619
- constraints Rodauth::Rails.authenticate(:admin) do
620
- mount AdminPortal::Engine, at: "/admin"
621
- end
622
-
623
- # ✅ Good - controller-level authorization
624
- class PostsController < ApplicationController
625
- include Plutonium::Resource::Controller
626
-
627
- private
628
-
629
- def current_authorized_scope
630
- super.where(organization: current_scoped_entity)
631
- end
632
- end
633
- ```
634
-
635
- ## Integration with Other Modules
636
-
637
- ### With Resource Module
638
-
639
- Routes automatically integrate with resource definitions:
640
-
641
- ```ruby
642
- class PostDefinition < Plutonium::Resource::Definition
643
- # These create interactive action routes automatically
644
- action :publish, interaction: PublishPostInteraction
645
- action :archive, interaction: ArchivePostInteraction
646
- end
647
- ```
648
-
649
- ### With Portal Module
650
-
651
- Portals provide routing contexts and scoping:
652
-
653
- ```ruby
654
- module AdminPortal
655
- class Engine < Rails::Engine
656
- include Plutonium::Portal::Engine
657
-
658
- # This affects all routes in this portal
659
- scope_to_entity Organization, strategy: :path
660
- end
661
- end
662
- ```
663
-
664
- ### With Authentication Module
665
-
666
- Routes can be protected by authentication constraints:
667
-
668
- ```ruby
669
- Rails.application.routes.draw do
670
- # Only authenticated admins can access admin routes
671
- constraints Rodauth::Rails.authenticate(:admin) do
672
- mount AdminPortal::Engine, at: "/admin"
673
- end
674
-
675
- # Customer authentication for customer portal
676
- constraints Rodauth::Rails.authenticate(:customer) do
677
- mount CustomerPortal::Engine, at: "/app"
678
- end
679
- end
680
- ```
681
-
682
- ## Related Modules
683
-
684
- The Routing module works seamlessly with other Plutonium components:
685
-
686
- - **[Controller](./controller.md)**: HTTP request handling and URL generation methods
687
- - **[Resource Record](./resource_record.md)**: Resource definitions that drive route generation
688
- - **[Portal](./portal.md)**: Multi-tenant portal functionality and route scoping
689
- - **[Action](./action.md)**: Interactive actions that create dynamic routes
690
- - **[Authentication](./authentication.md)**: Route protection and authentication constraints