plutonium 0.23.4 → 0.23.5
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/app/assets/plutonium.css +2 -2
- data/config/initializers/sqlite_json_alias.rb +1 -1
- data/docs/.vitepress/config.ts +60 -19
- data/docs/guide/cursor-rules.md +75 -0
- data/docs/guide/deep-dive/authorization.md +189 -0
- data/docs/guide/{getting-started → deep-dive}/resources.md +137 -0
- data/docs/guide/getting-started/{installation.md → 01-installation.md} +0 -105
- data/docs/guide/index.md +28 -0
- data/docs/guide/introduction/02-core-concepts.md +440 -0
- data/docs/guide/tutorial/01-project-setup.md +75 -0
- data/docs/guide/tutorial/02-creating-a-feature-package.md +45 -0
- data/docs/guide/tutorial/03-defining-resources.md +90 -0
- data/docs/guide/tutorial/04-creating-a-portal.md +101 -0
- data/docs/guide/tutorial/05-customizing-the-ui.md +128 -0
- data/docs/guide/tutorial/06-adding-custom-actions.md +101 -0
- data/docs/guide/tutorial/07-implementing-authorization.md +90 -0
- data/docs/index.md +24 -31
- data/docs/modules/action.md +190 -0
- data/docs/modules/authentication.md +236 -0
- data/docs/modules/configuration.md +599 -0
- data/docs/modules/controller.md +398 -0
- data/docs/modules/core.md +316 -0
- data/docs/modules/definition.md +876 -0
- data/docs/modules/display.md +759 -0
- data/docs/modules/form.md +605 -0
- data/docs/modules/generator.md +288 -0
- data/docs/modules/index.md +167 -0
- data/docs/modules/interaction.md +470 -0
- data/docs/modules/package.md +151 -0
- data/docs/modules/policy.md +176 -0
- data/docs/modules/portal.md +710 -0
- data/docs/modules/query.md +287 -0
- data/docs/modules/resource_record.md +618 -0
- data/docs/modules/routing.md +641 -0
- data/docs/modules/table.md +293 -0
- data/docs/modules/ui.md +631 -0
- data/docs/public/plutonium.mdc +667 -0
- data/lib/generators/pu/core/assets/assets_generator.rb +0 -5
- data/lib/plutonium/ui/display/resource.rb +7 -2
- data/lib/plutonium/ui/table/resource.rb +8 -3
- data/lib/plutonium/version.rb +1 -1
- metadata +36 -9
- data/docs/guide/getting-started/authorization.md +0 -296
- data/docs/guide/getting-started/core-concepts.md +0 -432
- data/docs/guide/getting-started/index.md +0 -21
- data/docs/guide/tutorial.md +0 -401
- /data/docs/guide/{what-is-plutonium.md → introduction/01-what-is-plutonium.md} +0 -0
@@ -0,0 +1,641 @@
|
|
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
|
+
### Context-Aware URL Generation
|
377
|
+
|
378
|
+
In nested controller contexts, URLs automatically include proper context:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
class PostsController < ApplicationController
|
382
|
+
include Plutonium::Resource::Controller
|
383
|
+
|
384
|
+
def show
|
385
|
+
# When accessed via /users/123/nested_posts/456
|
386
|
+
|
387
|
+
# These automatically include the user context:
|
388
|
+
resource_url_for(Post) # => "/users/123/nested_posts"
|
389
|
+
resource_url_for(@post, action: :edit) # => "/users/123/nested_posts/456/edit"
|
390
|
+
|
391
|
+
# Parent is automatically detected:
|
392
|
+
current_parent # => User.find(123)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
```
|
396
|
+
|
397
|
+
## Advanced Routing Patterns
|
398
|
+
|
399
|
+
### Multiple Engine Mounting
|
400
|
+
|
401
|
+
Different engines can have different routing strategies:
|
402
|
+
|
403
|
+
```ruby
|
404
|
+
# config/routes.rb
|
405
|
+
Rails.application.routes.draw do
|
406
|
+
# Admin portal with organization scoping
|
407
|
+
constraints Rodauth::Rails.authenticate(:admin) do
|
408
|
+
mount AdminPortal::Engine, at: "/admin"
|
409
|
+
end
|
410
|
+
|
411
|
+
# Customer portal with account scoping
|
412
|
+
constraints Rodauth::Rails.authenticate(:customer) do
|
413
|
+
mount CustomerPortal::Engine, at: "/app"
|
414
|
+
end
|
415
|
+
|
416
|
+
# Public portal with no scoping or authentication
|
417
|
+
mount PublicPortal::Engine, at: "/"
|
418
|
+
end
|
419
|
+
```
|
420
|
+
|
421
|
+
### Route Constraints and Conditions
|
422
|
+
|
423
|
+
```ruby
|
424
|
+
Rails.application.routes.draw do
|
425
|
+
# Subdomain-based portal mounting
|
426
|
+
constraints subdomain: 'admin' do
|
427
|
+
mount AdminPortal::Engine, at: "/"
|
428
|
+
end
|
429
|
+
|
430
|
+
# Feature flag-based mounting
|
431
|
+
constraints ->(request) { FeatureFlag.enabled?(:beta_portal) } do
|
432
|
+
mount BetaPortal::Engine, at: "/beta"
|
433
|
+
end
|
434
|
+
|
435
|
+
# IP-based constraints for admin access
|
436
|
+
constraints ip: /192\.168\.1\.\d+/ do
|
437
|
+
mount AdminPortal::Engine, at: "/secure-admin"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
```
|
441
|
+
|
442
|
+
### Route Generation Lifecycle
|
443
|
+
|
444
|
+
Understanding how Plutonium generates routes helps with debugging:
|
445
|
+
|
446
|
+
**1. Registration Phase:**
|
447
|
+
```ruby
|
448
|
+
register_resource Post
|
449
|
+
# - Resource is registered with the engine
|
450
|
+
# - Route configuration is created and stored
|
451
|
+
# - Concern name is generated (posts_routes)
|
452
|
+
```
|
453
|
+
|
454
|
+
**2. Route Definition Phase:**
|
455
|
+
```ruby
|
456
|
+
concern :posts_routes do
|
457
|
+
resources :posts, controller: "posts", concerns: [:interactive_resource_actions] do
|
458
|
+
# Nested routes for has_many associations
|
459
|
+
resources "nested_comments", controller: "comments"
|
460
|
+
end
|
461
|
+
end
|
462
|
+
```
|
463
|
+
|
464
|
+
**3. Route Materialization Phase:**
|
465
|
+
```ruby
|
466
|
+
scope :organization_id, as: :organization_id do
|
467
|
+
concerns :posts_routes, :comments_routes, :users_routes
|
468
|
+
end
|
469
|
+
# - All registered concerns are materialized within appropriate scope
|
470
|
+
# - Entity scoping parameters are applied
|
471
|
+
# - Final route table is generated
|
472
|
+
```
|
473
|
+
|
474
|
+
## Debugging and Troubleshooting
|
475
|
+
|
476
|
+
### Inspecting Generated Routes
|
477
|
+
|
478
|
+
```ruby
|
479
|
+
# View all routes for an engine
|
480
|
+
AdminPortal::Engine.routes.routes.each do |route|
|
481
|
+
puts "#{route.verb.ljust(6)} #{route.path.spec}"
|
482
|
+
end
|
483
|
+
|
484
|
+
# View registered resources
|
485
|
+
AdminPortal::Engine.resource_register.resources
|
486
|
+
# => [User, Post, Comment]
|
487
|
+
|
488
|
+
# View route configurations
|
489
|
+
AdminPortal::Engine.routes.resource_route_config_lookup
|
490
|
+
# => { "posts" => {...}, "users" => {...} }
|
491
|
+
|
492
|
+
# Check available route helpers
|
493
|
+
AdminPortal::Engine.routes.url_helpers.methods.grep(/path|url/)
|
494
|
+
```
|
495
|
+
|
496
|
+
### Common Issues and Solutions
|
497
|
+
|
498
|
+
**Missing Nested Routes:**
|
499
|
+
```ruby
|
500
|
+
# Ensure the association exists
|
501
|
+
User.reflect_on_association(:posts) # Should not be nil
|
502
|
+
|
503
|
+
# Check association route discovery
|
504
|
+
User.has_many_association_routes # Should include "posts"
|
505
|
+
```
|
506
|
+
|
507
|
+
**Incorrect Entity Scoping:**
|
508
|
+
```ruby
|
509
|
+
# Verify engine configuration
|
510
|
+
AdminPortal::Engine.scoped_to_entity? # => true
|
511
|
+
AdminPortal::Engine.scoped_entity_class # => Organization
|
512
|
+
AdminPortal::Engine.scoped_entity_strategy # => :path
|
513
|
+
```
|
514
|
+
|
515
|
+
**Interactive Action Routes Missing:**
|
516
|
+
```ruby
|
517
|
+
# Ensure action is defined in resource definition
|
518
|
+
PostDefinition.new.defined_actions.keys # Should include your action
|
519
|
+
```
|
520
|
+
|
521
|
+
**Route Helper Not Found:**
|
522
|
+
```ruby
|
523
|
+
# Include the engine's route helpers
|
524
|
+
include AdminPortal::Engine.routes.url_helpers
|
525
|
+
|
526
|
+
# Test URL generation
|
527
|
+
posts_path # => "/posts" or "/organizations/:organization_id/posts"
|
528
|
+
```
|
529
|
+
|
530
|
+
## Best Practices
|
531
|
+
|
532
|
+
### Route Organization
|
533
|
+
|
534
|
+
**Register Resources Logically:**
|
535
|
+
```ruby
|
536
|
+
# ✅ Good - logical grouping
|
537
|
+
AdminPortal::Engine.routes.draw do
|
538
|
+
# Core entities first
|
539
|
+
register_resource Organization
|
540
|
+
register_resource User
|
541
|
+
|
542
|
+
# Business domain resources
|
543
|
+
register_resource Project
|
544
|
+
register_resource Task
|
545
|
+
|
546
|
+
# Supporting resources
|
547
|
+
register_resource Comment
|
548
|
+
register_resource Attachment
|
549
|
+
end
|
550
|
+
```
|
551
|
+
|
552
|
+
**Leverage Entity Scoping:**
|
553
|
+
```ruby
|
554
|
+
# ✅ Good - consistent scoping strategy
|
555
|
+
class AdminPortal::Engine < Rails::Engine
|
556
|
+
scope_to_entity Organization, strategy: :path
|
557
|
+
end
|
558
|
+
|
559
|
+
# All resources automatically scoped to organization
|
560
|
+
# Consistent URL structure: /:organization_id/resources
|
561
|
+
```
|
562
|
+
|
563
|
+
### Security Considerations
|
564
|
+
|
565
|
+
```ruby
|
566
|
+
# ✅ Good - proper scoping for multi-tenancy
|
567
|
+
scope_to_entity Organization, strategy: :path
|
568
|
+
|
569
|
+
# ✅ Good - route-level authentication
|
570
|
+
constraints Rodauth::Rails.authenticate(:admin) do
|
571
|
+
mount AdminPortal::Engine, at: "/admin"
|
572
|
+
end
|
573
|
+
|
574
|
+
# ✅ Good - controller-level authorization
|
575
|
+
class PostsController < ApplicationController
|
576
|
+
include Plutonium::Resource::Controller
|
577
|
+
|
578
|
+
private
|
579
|
+
|
580
|
+
def current_authorized_scope
|
581
|
+
super.where(organization: current_scoped_entity)
|
582
|
+
end
|
583
|
+
end
|
584
|
+
```
|
585
|
+
|
586
|
+
## Integration with Other Modules
|
587
|
+
|
588
|
+
### With Resource Module
|
589
|
+
|
590
|
+
Routes automatically integrate with resource definitions:
|
591
|
+
|
592
|
+
```ruby
|
593
|
+
class PostDefinition < Plutonium::Resource::Definition
|
594
|
+
# These create interactive action routes automatically
|
595
|
+
action :publish, interaction: PublishPostInteraction
|
596
|
+
action :archive, interaction: ArchivePostInteraction
|
597
|
+
end
|
598
|
+
```
|
599
|
+
|
600
|
+
### With Portal Module
|
601
|
+
|
602
|
+
Portals provide routing contexts and scoping:
|
603
|
+
|
604
|
+
```ruby
|
605
|
+
module AdminPortal
|
606
|
+
class Engine < Rails::Engine
|
607
|
+
include Plutonium::Portal::Engine
|
608
|
+
|
609
|
+
# This affects all routes in this portal
|
610
|
+
scope_to_entity Organization, strategy: :path
|
611
|
+
end
|
612
|
+
end
|
613
|
+
```
|
614
|
+
|
615
|
+
### With Authentication Module
|
616
|
+
|
617
|
+
Routes can be protected by authentication constraints:
|
618
|
+
|
619
|
+
```ruby
|
620
|
+
Rails.application.routes.draw do
|
621
|
+
# Only authenticated admins can access admin routes
|
622
|
+
constraints Rodauth::Rails.authenticate(:admin) do
|
623
|
+
mount AdminPortal::Engine, at: "/admin"
|
624
|
+
end
|
625
|
+
|
626
|
+
# Customer authentication for customer portal
|
627
|
+
constraints Rodauth::Rails.authenticate(:customer) do
|
628
|
+
mount CustomerPortal::Engine, at: "/app"
|
629
|
+
end
|
630
|
+
end
|
631
|
+
```
|
632
|
+
|
633
|
+
## Related Modules
|
634
|
+
|
635
|
+
The Routing module works seamlessly with other Plutonium components:
|
636
|
+
|
637
|
+
- **[Controller](./controller.md)**: HTTP request handling and URL generation methods
|
638
|
+
- **[Resource Record](./resource_record.md)**: Resource definitions that drive route generation
|
639
|
+
- **[Portal](./portal.md)**: Multi-tenant portal functionality and route scoping
|
640
|
+
- **[Action](./action.md)**: Interactive actions that create dynamic routes
|
641
|
+
- **[Authentication](./authentication.md)**: Route protection and authentication constraints
|