plutonium 0.26.8 → 0.26.9
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/config/initializers/sqlite_alias.rb +3 -2
- data/docs/.vitepress/config.ts +1 -1
- data/docs/guide/{cursor-rules.md → claude-code-guide.md} +14 -15
- data/docs/guide/tutorial/01-project-setup.md +1 -1
- data/docs/guide/tutorial/04-creating-a-portal.md +1 -1
- data/docs/modules/controller.md +45 -0
- data/docs/modules/interaction.md +2 -2
- data/docs/public/CLAUDE.md +535 -0
- data/lib/plutonium/core/controller.rb +2 -0
- data/lib/plutonium/interaction/README.md +1 -1
- data/lib/plutonium/resource/controllers/crud_actions.rb +7 -7
- data/lib/plutonium/resource/controllers/interactive_actions.rb +8 -8
- data/lib/plutonium/version.rb +1 -1
- metadata +4 -4
- data/docs/public/plutonium.mdc +0 -565
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plutonium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.26.
|
4
|
+
version: 0.26.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Froelich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -495,7 +495,7 @@ files:
|
|
495
495
|
- docs/.vitepress/theme/custom.css
|
496
496
|
- docs/.vitepress/theme/index.ts
|
497
497
|
- docs/api-examples.md
|
498
|
-
- docs/guide/
|
498
|
+
- docs/guide/claude-code-guide.md
|
499
499
|
- docs/guide/deep-dive/authorization.md
|
500
500
|
- docs/guide/deep-dive/multitenancy.md
|
501
501
|
- docs/guide/deep-dive/resources.md
|
@@ -531,13 +531,13 @@ files:
|
|
531
531
|
- docs/modules/routing.md
|
532
532
|
- docs/modules/table.md
|
533
533
|
- docs/modules/ui.md
|
534
|
+
- docs/public/CLAUDE.md
|
534
535
|
- docs/public/android-chrome-192x192.png
|
535
536
|
- docs/public/android-chrome-512x512.png
|
536
537
|
- docs/public/apple-touch-icon.png
|
537
538
|
- docs/public/favicon-16x16.png
|
538
539
|
- docs/public/favicon-32x32.png
|
539
540
|
- docs/public/favicon.ico
|
540
|
-
- docs/public/plutonium.mdc
|
541
541
|
- docs/public/plutonium.png
|
542
542
|
- docs/public/site.webmanifest
|
543
543
|
- docs/public/templates/base.rb
|
data/docs/public/plutonium.mdc
DELETED
@@ -1,565 +0,0 @@
|
|
1
|
-
---
|
2
|
-
description: Expert Plutonium framework guidelines for AI-assisted Rails development with auto-detection, resource architecture, component-based UI, and RAD patterns
|
3
|
-
globs:
|
4
|
-
alwaysApply: true
|
5
|
-
---
|
6
|
-
You are an expert Ruby on Rails developer specializing in the Plutonium framework. This cursor rule compiles comprehensive guidelines for building robust, maintainable Plutonium applications.
|
7
|
-
|
8
|
-
# Plutonium Framework Development Guidelines
|
9
|
-
|
10
|
-
## Framework Overview
|
11
|
-
|
12
|
-
Plutonium is a Rapid Application Development (RAD) toolkit that extends Rails with powerful conventions, patterns, and tools. It provides:
|
13
|
-
- Resource-oriented architecture with declarative definitions
|
14
|
-
- Modular packaging system (Feature Packages and Portal Packages)
|
15
|
-
- Built-in authentication, authorization, and multi-tenancy
|
16
|
-
- Component-based UI system built on Phlex
|
17
|
-
- Business logic encapsulation through Interactions
|
18
|
-
- Query objects for filtering and searching
|
19
|
-
|
20
|
-
## Project Structure & Setup
|
21
|
-
|
22
|
-
### Initial Setup
|
23
|
-
- Use Ruby 3.2.2+ and Rails 7.1+
|
24
|
-
- Create new apps with: `rails new app_name -a propshaft -j esbuild -c tailwind -m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb`
|
25
|
-
- For existing apps: `bin/rails app:template LOCATION=https://radioactive-labs.github.io/plutonium-core/templates/base.rb`
|
26
|
-
- Run `rails generate pu:core:install` after adding the gem
|
27
|
-
|
28
|
-
### Directory Structure
|
29
|
-
```
|
30
|
-
app/
|
31
|
-
├── controllers/
|
32
|
-
│ ├── plutonium_controller.rb # Base controller
|
33
|
-
│ └── resource_controller.rb # Base for resources
|
34
|
-
├── definitions/ # Resource definitions
|
35
|
-
├── interactions/ # Business logic
|
36
|
-
├── models/
|
37
|
-
└── policies/ # Authorization policies
|
38
|
-
config/
|
39
|
-
├── initializers/plutonium.rb # Main configuration
|
40
|
-
└── packages.rb # Package registration
|
41
|
-
packages/ # Modular features
|
42
|
-
```
|
43
|
-
|
44
|
-
## Resource Architecture
|
45
|
-
|
46
|
-
### Resource Components
|
47
|
-
Every resource consists of 4 core components:
|
48
|
-
1. **Model** - ActiveRecord model with `include Plutonium::Resource::Record`
|
49
|
-
2. **Definition** - Declarative UI and behavior configuration
|
50
|
-
3. **Policy** - Authorization and access control
|
51
|
-
4. **Controller** - Auto-generated CRUD operations
|
52
|
-
|
53
|
-
### Creating Resources
|
54
|
-
```bash
|
55
|
-
# Scaffold a complete resource
|
56
|
-
rails generate pu:res:scaffold Post user:belongs_to title:string content:text 'published_at:datetime?'
|
57
|
-
|
58
|
-
# Individual components
|
59
|
-
rails generate pu:res:model Post title:string content:text
|
60
|
-
rails generate pu:res:definition Post
|
61
|
-
rails generate pu:res:policy Post
|
62
|
-
```
|
63
|
-
|
64
|
-
### Resource Models
|
65
|
-
```ruby
|
66
|
-
class Post < ApplicationRecord
|
67
|
-
include Plutonium::Resource::Record
|
68
|
-
|
69
|
-
belongs_to :user
|
70
|
-
has_many :comments, dependent: :destroy
|
71
|
-
|
72
|
-
validates :title, :content, presence: true
|
73
|
-
|
74
|
-
scope :published, -> { where.not(published_at: nil) }
|
75
|
-
scope :drafts, -> { where(published_at: nil) }
|
76
|
-
end
|
77
|
-
```
|
78
|
-
|
79
|
-
### Resource Definitions
|
80
|
-
```ruby
|
81
|
-
class PostDefinition < Plutonium::Resource::Definition
|
82
|
-
# Field declarations are OPTIONAL - all attributes are auto-detected from the model
|
83
|
-
# You only need to declare fields when overriding auto-detected behavior
|
84
|
-
|
85
|
-
# These would be auto-detected from your Post model:
|
86
|
-
# - :title (string column) → :string field
|
87
|
-
# - :content (text column) → :text field
|
88
|
-
# - :published_at (datetime column) → :datetime field
|
89
|
-
# - :author (belongs_to association) → :association field
|
90
|
-
|
91
|
-
# Only declare fields when you want to override defaults:
|
92
|
-
field :content, as: :rich_text # Override text → rich_text
|
93
|
-
field :status, as: :select, choices: %w[draft published archived]
|
94
|
-
|
95
|
-
# Display customization (show/index pages)
|
96
|
-
display :title, as: :string
|
97
|
-
display :content, as: :markdown
|
98
|
-
display :status, as: :string
|
99
|
-
display :author, as: :association
|
100
|
-
|
101
|
-
# Conditional displays (for table columns and show pages)
|
102
|
-
# Use for COSMETIC/STATE-BASED logic only, NOT authorization (use policies for that)
|
103
|
-
display :debug_info, condition: -> { Rails.env.development? }
|
104
|
-
display :internal_notes, condition: -> { object.status == 'draft' }
|
105
|
-
display :parent_category, condition: -> { current_parent.present? }
|
106
|
-
|
107
|
-
# Custom display with block (for complex rendering)
|
108
|
-
display :custom_field do |field|
|
109
|
-
# Return component instances directly in definition blocks (no 'render' needed)
|
110
|
-
CustomComponent.new(value: field.value)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Advanced custom display with phlexi_tag (use sparingly)
|
114
|
-
display :status, as: :phlexi_tag, with: ->(value, attrs) {
|
115
|
-
# SGML methods available here because proc is evaluated in rendering context
|
116
|
-
span(class: "badge badge-#{value}") { value.humanize }
|
117
|
-
}
|
118
|
-
|
119
|
-
# Input configuration (new/edit forms)
|
120
|
-
input :title, placeholder: "Enter post title" # No need for as: :string (auto-detected)
|
121
|
-
input :content, as: :rich_text
|
122
|
-
input :category, as: :select, choices: %w[Tech Business Lifestyle]
|
123
|
-
|
124
|
-
# New: Custom component classes
|
125
|
-
input :color_picker, as: ColorPickerComponent
|
126
|
-
display :status_badge, as: StatusBadgeComponent
|
127
|
-
|
128
|
-
# Input blocks for custom logic (must use form builder methods)
|
129
|
-
input :birth_date do |f|
|
130
|
-
case object.age_category
|
131
|
-
when 'adult'
|
132
|
-
f.date_tag(min: 18.years.ago.to_date)
|
133
|
-
else
|
134
|
-
f.date_tag
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# Conditional inputs (for forms only)
|
139
|
-
# Use for COSMETIC/STATE-BASED logic only, NOT authorization (use policies for that)
|
140
|
-
input :debug_info, condition: -> { Rails.env.development? }
|
141
|
-
input :internal_notes, condition: -> { object.status == 'draft' }
|
142
|
-
input :parent_category, condition: -> { current_parent.present? }
|
143
|
-
input :team_projects, condition: -> { current_user.team_lead? }
|
144
|
-
|
145
|
-
# Search functionality
|
146
|
-
search do |scope, query|
|
147
|
-
scope.where("title ILIKE ? OR content ILIKE ?", "%#{query}%", "%#{query}%")
|
148
|
-
end
|
149
|
-
|
150
|
-
# Filters
|
151
|
-
filter :published, with: Plutonium::Query::Filters::Text, predicate: :eq
|
152
|
-
filter :category, with: Plutonium::Query::Filters::Text, predicate: :contains
|
153
|
-
filter :title, with: Plutonium::Query::Filters::Text, predicate: :contains
|
154
|
-
|
155
|
-
# Scopes (appear as buttons)
|
156
|
-
scope :published
|
157
|
-
scope :drafts
|
158
|
-
scope :recent, -> { where('created_at > ?', 1.week.ago) }
|
159
|
-
|
160
|
-
# Custom actions
|
161
|
-
action :publish, interaction: PublishPostInteraction, icon: Phlex::TablerIcons::Send
|
162
|
-
action :archive, interaction: ArchivePostInteraction, color: :warning
|
163
|
-
|
164
|
-
# Page customization
|
165
|
-
index_page_title "All Posts"
|
166
|
-
show_page_title "Post Details"
|
167
|
-
end
|
168
|
-
```
|
169
|
-
|
170
|
-
### Available Field Types (Auto-Detected from Model)
|
171
|
-
- Text: `:string`, `:text`, `:rich_text`, `:markdown`
|
172
|
-
- Numeric: `:number`, `:integer`, `:decimal`
|
173
|
-
- Boolean: `:boolean`
|
174
|
-
- Date/Time: `:date`, `:datetime`, `:time`
|
175
|
-
- Selection: `:select`, `:radio_buttons`, `:check_boxes`
|
176
|
-
- Files: `:file` (with `multiple: true` for multiple files)
|
177
|
-
- Associations: `:association`
|
178
|
-
- Special: `:hidden`, `:email`, `:url`, `:color`
|
179
|
-
|
180
|
-
### Conditional Rendering Guidelines
|
181
|
-
|
182
|
-
**IMPORTANT**: Use conditions for COSMETIC/STATE-BASED logic only, NOT for authorization.
|
183
|
-
|
184
|
-
#### ✅ Appropriate Uses
|
185
|
-
- **Environment-based**: Show debug info in development only
|
186
|
-
- **Object state-based**: Show fields based on record status
|
187
|
-
- **Context-based**: Show fields based on parent presence
|
188
|
-
- **Dynamic behavior**: Show dependent fields conditionally
|
189
|
-
|
190
|
-
```ruby
|
191
|
-
# Environment-based visibility
|
192
|
-
display :debug_info, condition: -> { Rails.env.development? }
|
193
|
-
|
194
|
-
# Object state-based visibility
|
195
|
-
display :approval_date, condition: -> { object.approved? }
|
196
|
-
input :rejection_reason, condition: -> { object.rejected? }
|
197
|
-
|
198
|
-
# Parent context-based visibility
|
199
|
-
display :parent_category, condition: -> { current_parent.present? }
|
200
|
-
|
201
|
-
# Dynamic form behavior
|
202
|
-
input :end_date, condition: -> { object.start_date.present? }
|
203
|
-
```
|
204
|
-
|
205
|
-
#### ❌ Inappropriate Uses
|
206
|
-
Authorization logic belongs in policies, not conditions:
|
207
|
-
|
208
|
-
```ruby
|
209
|
-
# DON'T use conditions for role-based authorization
|
210
|
-
display :admin_notes, condition: -> { current_user&.admin? } # Use policy instead
|
211
|
-
input :sensitive_data, condition: -> { current_user&.can_edit_sensitive? } # Use policy instead
|
212
|
-
```
|
213
|
-
|
214
|
-
### Available Configuration Options
|
215
|
-
|
216
|
-
#### Field Options
|
217
|
-
```ruby
|
218
|
-
field :name, as: :string, class: "custom-class", wrapper: {class: "field-wrapper"}
|
219
|
-
```
|
220
|
-
|
221
|
-
#### Input Options
|
222
|
-
```ruby
|
223
|
-
input :title,
|
224
|
-
as: :string,
|
225
|
-
placeholder: "Enter title",
|
226
|
-
required: true,
|
227
|
-
class: "custom-input",
|
228
|
-
wrapper: {class: "input-wrapper"},
|
229
|
-
data: {controller: "custom"},
|
230
|
-
condition: -> { object.status == 'draft' } # Cosmetic condition only
|
231
|
-
```
|
232
|
-
|
233
|
-
#### Display Options
|
234
|
-
```ruby
|
235
|
-
display :content,
|
236
|
-
as: :markdown,
|
237
|
-
class: "prose",
|
238
|
-
wrapper: {class: "content-wrapper"},
|
239
|
-
condition: -> { Rails.env.development? } # Cosmetic condition only
|
240
|
-
```
|
241
|
-
|
242
|
-
#### Choices Options (for selects)
|
243
|
-
```ruby
|
244
|
-
# Static choices
|
245
|
-
input :category, as: :select, choices: %w[Tech Business Lifestyle]
|
246
|
-
|
247
|
-
# Dynamic choices require block form
|
248
|
-
input :author do |f|
|
249
|
-
choices = User.active.pluck(:name, :id)
|
250
|
-
f.select_tag choices: choices
|
251
|
-
end
|
252
|
-
|
253
|
-
# Dynamic choices with access to context
|
254
|
-
input :team_members do |f|
|
255
|
-
choices = current_user.organization.users.active.pluck(:name, :id)
|
256
|
-
f.select_tag choices: choices
|
257
|
-
end
|
258
|
-
|
259
|
-
# Dynamic choices based on object state
|
260
|
-
input :related_posts do |f|
|
261
|
-
choices = if object.persisted?
|
262
|
-
Post.where.not(id: object.id).published.pluck(:title, :id)
|
263
|
-
else
|
264
|
-
[]
|
265
|
-
end
|
266
|
-
f.select_tag choices: choices
|
267
|
-
end
|
268
|
-
```
|
269
|
-
|
270
|
-
#### File Upload Options
|
271
|
-
```ruby
|
272
|
-
input :avatar, as: :file, multiple: false
|
273
|
-
input :documents, as: :file, multiple: true
|
274
|
-
# Note: Advanced file options like allowed_file_types and max_file_size
|
275
|
-
# are not currently supported by the framework
|
276
|
-
```
|
277
|
-
|
278
|
-
### Resource Policies
|
279
|
-
|
280
|
-
Policies control authorization and data access. They define who can perform what actions and what data users can see.
|
281
|
-
|
282
|
-
```ruby
|
283
|
-
class PostPolicy < Plutonium::Resource::Policy
|
284
|
-
# CRUD permissions
|
285
|
-
def index?
|
286
|
-
true # Everyone can view list
|
287
|
-
end
|
288
|
-
|
289
|
-
def show?
|
290
|
-
record.published? || record.author == user || user.admin?
|
291
|
-
end
|
292
|
-
|
293
|
-
def create?
|
294
|
-
user.present?
|
295
|
-
end
|
296
|
-
|
297
|
-
def update?
|
298
|
-
record.author == user || user.admin?
|
299
|
-
end
|
300
|
-
|
301
|
-
def destroy?
|
302
|
-
user.admin?
|
303
|
-
end
|
304
|
-
|
305
|
-
# Custom action permissions
|
306
|
-
def publish?
|
307
|
-
update? && record.draft?
|
308
|
-
end
|
309
|
-
|
310
|
-
# Attribute permissions - control which fields are accessible
|
311
|
-
def permitted_attributes_for_read
|
312
|
-
attrs = [:title, :content, :category, :published_at]
|
313
|
-
attrs << :internal_notes if user.admin?
|
314
|
-
attrs
|
315
|
-
end
|
316
|
-
|
317
|
-
def permitted_attributes_for_create
|
318
|
-
[:title, :content, :category]
|
319
|
-
end
|
320
|
-
|
321
|
-
def permitted_attributes_for_update
|
322
|
-
attrs = permitted_attributes_for_create
|
323
|
-
attrs << :slug if user.admin?
|
324
|
-
attrs
|
325
|
-
end
|
326
|
-
|
327
|
-
# Collection scoping - filter what records users can see
|
328
|
-
relation_scope do |relation|
|
329
|
-
relation = super(relation) # Apply entity scoping first
|
330
|
-
|
331
|
-
if user.admin?
|
332
|
-
relation
|
333
|
-
else
|
334
|
-
# Users see only their posts or published posts
|
335
|
-
relation.where(author: user).or(relation.where(published: true))
|
336
|
-
end
|
337
|
-
end
|
338
|
-
|
339
|
-
# Association permissions
|
340
|
-
def permitted_associations
|
341
|
-
[:comments, :author, :tags]
|
342
|
-
end
|
343
|
-
end
|
344
|
-
```
|
345
|
-
|
346
|
-
## Business Logic with Interactions
|
347
|
-
|
348
|
-
### Interaction Structure
|
349
|
-
```ruby
|
350
|
-
class PublishPostInteraction < Plutonium::Resource::Interaction
|
351
|
-
# Metadata for UI
|
352
|
-
presents label: "Publish Post",
|
353
|
-
icon: Phlex::TablerIcons::Send,
|
354
|
-
description: "Make this post visible to readers"
|
355
|
-
|
356
|
-
# Attributes (form inputs)
|
357
|
-
attribute :resource, class: Post
|
358
|
-
attribute :publish_date, :datetime, default: -> { Time.current }
|
359
|
-
attribute :notify_subscribers, :boolean, default: true
|
360
|
-
|
361
|
-
# Validations
|
362
|
-
validates :resource, presence: true
|
363
|
-
validates :publish_date, presence: true
|
364
|
-
|
365
|
-
private
|
366
|
-
|
367
|
-
def execute
|
368
|
-
# Business logic
|
369
|
-
resource.transaction do
|
370
|
-
resource.update!(
|
371
|
-
published_at: publish_date,
|
372
|
-
status: 'published'
|
373
|
-
)
|
374
|
-
|
375
|
-
# Side effects
|
376
|
-
NotifySubscribersJob.perform_later(resource) if notify_subscribers
|
377
|
-
|
378
|
-
# Return successful outcome
|
379
|
-
succeed(resource)
|
380
|
-
.with_message("Post published successfully!")
|
381
|
-
.with_redirect_response(resource_path(resource))
|
382
|
-
end
|
383
|
-
rescue => error
|
384
|
-
# Return failure outcome
|
385
|
-
failed(error.message)
|
386
|
-
end
|
387
|
-
end
|
388
|
-
```
|
389
|
-
|
390
|
-
### Interaction Types (Auto-detected)
|
391
|
-
- **Record Actions**: Have `attribute :resource` - work on single records
|
392
|
-
- **Bulk Actions**: Have `attribute :resources` - work on multiple records
|
393
|
-
- **Resource Actions**: Have neither - work at resource level (imports, etc.)
|
394
|
-
|
395
|
-
### Interaction Execution Patterns
|
396
|
-
- **Immediate**: Only `resource`/`resources` attribute → executes on click
|
397
|
-
- **Modal**: Additional attributes → shows form modal first
|
398
|
-
|
399
|
-
## Package Architecture
|
400
|
-
|
401
|
-
### Feature Packages
|
402
|
-
Contain business logic and domain models:
|
403
|
-
```bash
|
404
|
-
rails generate pu:pkg:package blogging
|
405
|
-
```
|
406
|
-
|
407
|
-
Structure:
|
408
|
-
```
|
409
|
-
packages/blogging/
|
410
|
-
├── app/
|
411
|
-
│ ├── models/blogging/
|
412
|
-
│ ├── definitions/blogging/
|
413
|
-
│ ├── policies/blogging/
|
414
|
-
│ ├── interactions/blogging/
|
415
|
-
│ └── controllers/blogging/
|
416
|
-
└── lib/engine.rb
|
417
|
-
```
|
418
|
-
|
419
|
-
### Portal Packages
|
420
|
-
Provide web interfaces with routing and authentication:
|
421
|
-
```bash
|
422
|
-
rails generate pu:pkg:portal admin
|
423
|
-
```
|
424
|
-
|
425
|
-
Portal Engine:
|
426
|
-
```ruby
|
427
|
-
module AdminPortal
|
428
|
-
class Engine < ::Rails::Engine
|
429
|
-
include Plutonium::Portal::Engine
|
430
|
-
|
431
|
-
# Multi-tenancy
|
432
|
-
scope_to_entity Organization, strategy: :path
|
433
|
-
end
|
434
|
-
end
|
435
|
-
```
|
436
|
-
|
437
|
-
Portal Routes:
|
438
|
-
```ruby
|
439
|
-
AdminPortal::Engine.routes.draw do
|
440
|
-
register_resource Blog::Post
|
441
|
-
register_resource Blog::Comment
|
442
|
-
register_resource User
|
443
|
-
end
|
444
|
-
```
|
445
|
-
|
446
|
-
Portal Controller:
|
447
|
-
```ruby
|
448
|
-
module AdminPortal
|
449
|
-
module Concerns
|
450
|
-
module Controller
|
451
|
-
extend ActiveSupport::Concern
|
452
|
-
include Plutonium::Portal::Controller
|
453
|
-
include Plutonium::Auth::Rodauth(:admin)
|
454
|
-
|
455
|
-
included do
|
456
|
-
before_action :ensure_admin_access
|
457
|
-
layout "admin_portal"
|
458
|
-
end
|
459
|
-
|
460
|
-
private
|
461
|
-
|
462
|
-
def ensure_admin_access
|
463
|
-
redirect_to root_path unless current_user&.admin?
|
464
|
-
end
|
465
|
-
end
|
466
|
-
end
|
467
|
-
end
|
468
|
-
```
|
469
|
-
|
470
|
-
## Authentication & Authorization
|
471
|
-
|
472
|
-
### Rodauth Setup
|
473
|
-
```bash
|
474
|
-
# Install Rodauth
|
475
|
-
rails generate pu:rodauth:install
|
476
|
-
|
477
|
-
# Create user account
|
478
|
-
rails generate pu:rodauth:account user
|
479
|
-
|
480
|
-
# Create admin account with MFA
|
481
|
-
rails generate pu:rodauth:account admin --no-defaults \
|
482
|
-
--login --logout --remember --lockout \
|
483
|
-
--create-account --verify-account --close-account \
|
484
|
-
--change-password --reset-password --reset-password-notify \
|
485
|
-
--active-sessions --password-grace-period --otp \
|
486
|
-
--recovery-codes --audit-logging --internal-request
|
487
|
-
```
|
488
|
-
|
489
|
-
### Authentication Integration
|
490
|
-
```ruby
|
491
|
-
# In portal controller concerns
|
492
|
-
include Plutonium::Auth::Rodauth(:user) # For user authentication
|
493
|
-
include Plutonium::Auth::Rodauth(:admin) # For admin authentication
|
494
|
-
include Plutonium::Auth::Public # For public access
|
495
|
-
```
|
496
|
-
|
497
|
-
## Routing & Custom Routes
|
498
|
-
|
499
|
-
### Resource Registration
|
500
|
-
```ruby
|
501
|
-
# Basic resource registration
|
502
|
-
register_resource Post
|
503
|
-
|
504
|
-
# Custom routes with member/collection actions
|
505
|
-
register_resource Post do
|
506
|
-
member do
|
507
|
-
get :publish # /posts/1/publish
|
508
|
-
post :archive # /posts/1/archive
|
509
|
-
end
|
510
|
-
|
511
|
-
collection do
|
512
|
-
get :search # /posts/search
|
513
|
-
end
|
514
|
-
end
|
515
|
-
```
|
516
|
-
|
517
|
-
### Common Routing Errors
|
518
|
-
|
519
|
-
#### "Unresolvable Association" Error
|
520
|
-
When adding custom routes inside `register_resource` blocks, Plutonium may interpret them as nested resources and fail with:
|
521
|
-
```
|
522
|
-
RuntimeError (Could not resolve the association between 'Model' and 'Model')
|
523
|
-
```
|
524
|
-
|
525
|
-
**Problem**: Routes like this create nested resource interpretation:
|
526
|
-
```ruby
|
527
|
-
register_resource ::UniversalFlow::Definition do
|
528
|
-
get "editor", to: "universal_flow/definitions#editor"
|
529
|
-
end
|
530
|
-
```
|
531
|
-
|
532
|
-
**Solutions**:
|
533
|
-
|
534
|
-
1. **Add custom scope to model (Recommended)**:
|
535
|
-
```ruby
|
536
|
-
class UniversalFlow::Definition < ApplicationRecord
|
537
|
-
scope :associated_with_universal_flow_definition, ->(universal_flow_definition) {
|
538
|
-
all # or specific logic for your use case
|
539
|
-
}
|
540
|
-
end
|
541
|
-
```
|
542
|
-
|
543
|
-
2. **Use member/collection routes**:
|
544
|
-
```ruby
|
545
|
-
register_resource ::UniversalFlow::Definition do
|
546
|
-
member do
|
547
|
-
get :editor
|
548
|
-
end
|
549
|
-
end
|
550
|
-
```
|
551
|
-
|
552
|
-
3. **Create separate controller**:
|
553
|
-
```ruby
|
554
|
-
# In routes file
|
555
|
-
get 'universal_flow/editor/:id', to: 'universal_flow/editor#show'
|
556
|
-
|
557
|
-
# Create dedicated controller
|
558
|
-
class UniversalFlow::EditorController < CustomerPortal::ResourceController
|
559
|
-
def show
|
560
|
-
@definition = UniversalFlow::Definition.find(params[:id])
|
561
|
-
authorize_current! @definition
|
562
|
-
# ... custom logic
|
563
|
-
end
|
564
|
-
end
|
565
|
-
```
|