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
@@ -0,0 +1,535 @@
|
|
1
|
+
# Plutonium Framework Development Guide
|
2
|
+
|
3
|
+
This guide helps AI agents understand and build Plutonium applications effectively. Plutonium is a Rails RAD framework that extends Rails conventions with application-level concepts.
|
4
|
+
|
5
|
+
## Quick Start
|
6
|
+
|
7
|
+
### New Application
|
8
|
+
```bash
|
9
|
+
rails new app_name -a propshaft -j esbuild -c tailwind \
|
10
|
+
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
11
|
+
```
|
12
|
+
|
13
|
+
### Essential Commands
|
14
|
+
```bash
|
15
|
+
# Setup auth
|
16
|
+
rails generate pu:rodauth:install
|
17
|
+
rails generate pu:rodauth:account user
|
18
|
+
|
19
|
+
# Create feature + portal
|
20
|
+
rails generate pu:pkg:package blog_management
|
21
|
+
rails generate pu:pkg:portal admin_portal
|
22
|
+
|
23
|
+
# Create complete resource
|
24
|
+
rails generate pu:res:scaffold Post user:belongs_to title:string content:text --dest=blog_management
|
25
|
+
|
26
|
+
# Connect to portal
|
27
|
+
rails generate pu:res:conn BlogManagement::Post --dest=admin_portal
|
28
|
+
```
|
29
|
+
|
30
|
+
### Start Building
|
31
|
+
```bash
|
32
|
+
rails db:migrate
|
33
|
+
bin/dev # Visit http://localhost:3000
|
34
|
+
```
|
35
|
+
|
36
|
+
## Core Concepts
|
37
|
+
|
38
|
+
### 1. Architecture
|
39
|
+
- **Packages**: Modular organization using Rails engines
|
40
|
+
- **Feature Packages**: Contain business logic (models, interactions, policies)
|
41
|
+
- **Portal Packages**: Provide web interfaces with authentication
|
42
|
+
- **Resources**: Complete CRUD entities with models, definitions, policies, controllers
|
43
|
+
- **Entity Scoping**: Built-in multi-tenancy support
|
44
|
+
|
45
|
+
### 2. Key Components
|
46
|
+
- **Models**: ActiveRecord + `Plutonium::Resource::Record` for enhanced functionality
|
47
|
+
- **Definitions**: Declarative UI configuration (how fields render)
|
48
|
+
- **Policies**: Authorization control (what users can access)
|
49
|
+
- **Interactions**: Business logic encapsulation (what actions do)
|
50
|
+
- **Controllers**: Auto-generated CRUD with customization points
|
51
|
+
|
52
|
+
## Essential Generators
|
53
|
+
|
54
|
+
### Project Setup
|
55
|
+
```bash
|
56
|
+
# New app with Plutonium template
|
57
|
+
rails new app_name -a propshaft -j esbuild -c tailwind \
|
58
|
+
-m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
59
|
+
|
60
|
+
# Add to existing app
|
61
|
+
rails generate pu:core:install
|
62
|
+
```
|
63
|
+
|
64
|
+
### Authentication Setup (Optional)
|
65
|
+
|
66
|
+
Authentication is optional in Plutonium. You can build apps without auth using `Plutonium::Auth::Public`. When you do need authentication:
|
67
|
+
|
68
|
+
#### Basic Setup Workflow
|
69
|
+
```bash
|
70
|
+
# 1. Install Rodauth infrastructure
|
71
|
+
rails generate pu:rodauth:install
|
72
|
+
|
73
|
+
# 2. Create account types as needed
|
74
|
+
rails generate pu:rodauth:account user
|
75
|
+
|
76
|
+
# 3. Run migrations
|
77
|
+
rails db:migrate
|
78
|
+
|
79
|
+
# 4. Connect auth to portals (see Portal Integration below)
|
80
|
+
```
|
81
|
+
|
82
|
+
#### Account Type Options
|
83
|
+
```bash
|
84
|
+
# Standard user accounts
|
85
|
+
rails generate pu:rodauth:account user
|
86
|
+
|
87
|
+
# Enhanced admin accounts with MFA and security features
|
88
|
+
rails generate pu:rodauth:admin admin
|
89
|
+
|
90
|
+
# Multi-tenant customer accounts with entity scoping
|
91
|
+
rails generate pu:rodauth:customer Customer --entity=Organization
|
92
|
+
|
93
|
+
# Custom feature set (specify exact features needed)
|
94
|
+
rails generate pu:rodauth:account api_user \
|
95
|
+
--no-defaults --login --logout --jwt --verify-account
|
96
|
+
```
|
97
|
+
|
98
|
+
#### Portal Integration
|
99
|
+
```ruby
|
100
|
+
# For authenticated portals - add to controller concerns:
|
101
|
+
include Plutonium::Auth::Rodauth(:user) # Links to user auth
|
102
|
+
include Plutonium::Auth::Rodauth(:admin) # Links to admin auth
|
103
|
+
|
104
|
+
# For public portals - no authentication required:
|
105
|
+
include Plutonium::Auth::Public
|
106
|
+
|
107
|
+
# This provides: current_user, logout_url helpers in controllers/views
|
108
|
+
```
|
109
|
+
|
110
|
+
#### Available Auth Features
|
111
|
+
- **Core**: `login`, `logout`, `create_account`, `verify_account`, `reset_password`, `change_password`
|
112
|
+
- **Security**: `remember`, `lockout`, `active_sessions`, `audit_logging`
|
113
|
+
- **MFA**: `otp`, `recovery_codes`, `sms_codes`, `webauthn`
|
114
|
+
- **Advanced**: `password_grace_period`, `single_session`, `jwt`, `internal_request`
|
115
|
+
|
116
|
+
### Package Generators
|
117
|
+
```bash
|
118
|
+
# Create feature package for business logic
|
119
|
+
rails generate pu:pkg:package blog_management
|
120
|
+
|
121
|
+
# Create portal package for web interface
|
122
|
+
rails generate pu:pkg:portal admin_dashboard
|
123
|
+
```
|
124
|
+
|
125
|
+
### Resource Generators (Most Important)
|
126
|
+
```bash
|
127
|
+
# Complete resource scaffold (preferred) - use --dest for package
|
128
|
+
rails generate pu:res:scaffold Post user:belongs_to title:string content:text 'published_at:datetime?' --dest=blogging
|
129
|
+
|
130
|
+
# Referencing namespaced models in associations
|
131
|
+
rails generate pu:res:scaffold Comment user:belongs_to blogging/post:belongs_to body:text --dest=comments
|
132
|
+
rails generate pu:res:scaffold Order customer:belongs_to inventory/product:belongs_to quantity:integer --dest=commerce
|
133
|
+
|
134
|
+
# Model only - use --dest for package
|
135
|
+
rails generate pu:res:model Article title:string body:text author:belongs_to --dest=blogging
|
136
|
+
rails generate pu:res:model Review user:belongs_to inventory/product:belongs_to rating:integer --dest=reviews
|
137
|
+
|
138
|
+
# CRITICAL: Use connection generator to expose resources in portals
|
139
|
+
# Always use the generator - NEVER manually connect resources
|
140
|
+
|
141
|
+
# Use full namespaced path (package_name/model_name format)
|
142
|
+
rails generate pu:res:conn blog_management/post blog_management/comment --dest=admin_portal
|
143
|
+
|
144
|
+
# Interactive mode (will prompt for resource and portal selection)
|
145
|
+
rails generate pu:res:conn
|
146
|
+
|
147
|
+
# The generator handles all routing, controller, and policy connections automatically
|
148
|
+
```
|
149
|
+
|
150
|
+
### Other Useful Generators
|
151
|
+
```bash
|
152
|
+
# Entity for multi-tenancy
|
153
|
+
rails generate pu:res:entity Organization --auth-account=Customer
|
154
|
+
|
155
|
+
# Eject components for customization
|
156
|
+
rails generate pu:eject:layout --dest=admin_portal
|
157
|
+
rails generate pu:eject:shell --dest=admin_portal
|
158
|
+
|
159
|
+
# Development tools
|
160
|
+
rails generate pu:gem:annotated # Model annotations
|
161
|
+
rails generate pu:gem:standard # Ruby Standard linter
|
162
|
+
rails generate pu:gem:dotenv # Environment variables
|
163
|
+
```
|
164
|
+
|
165
|
+
## File Structure Patterns
|
166
|
+
|
167
|
+
### Feature Package Structure
|
168
|
+
```
|
169
|
+
packages/blog_management/
|
170
|
+
├── lib/
|
171
|
+
│ └── engine.rb # Package engine
|
172
|
+
└── app/
|
173
|
+
├── models/blog_management/
|
174
|
+
│ ├── post.rb # Business models
|
175
|
+
│ └── resource_record.rb # Base class
|
176
|
+
├── policies/blog_management/
|
177
|
+
│ ├── post_policy.rb # Authorization rules
|
178
|
+
│ └── resource_policy.rb # Base policy
|
179
|
+
├── definitions/blog_management/
|
180
|
+
│ ├── post_definition.rb # UI configuration
|
181
|
+
│ └── resource_definition.rb # Base definition
|
182
|
+
├── interactions/blog_management/
|
183
|
+
│ └── post_interactions/
|
184
|
+
│ └── publish.rb # Business logic
|
185
|
+
└── controllers/blog_management/
|
186
|
+
└── posts_controller.rb # Optional custom controller
|
187
|
+
```
|
188
|
+
|
189
|
+
### Portal Package Structure
|
190
|
+
```
|
191
|
+
packages/admin_portal/
|
192
|
+
├── lib/
|
193
|
+
│ └── engine.rb # Portal engine
|
194
|
+
├── config/
|
195
|
+
│ └── routes.rb # Portal routes
|
196
|
+
└── app/
|
197
|
+
├── controllers/admin_portal/
|
198
|
+
│ ├── concerns/controller.rb # Auth integration
|
199
|
+
│ ├── dashboard_controller.rb # Dashboard
|
200
|
+
│ ├── plutonium_controller.rb # Base controller
|
201
|
+
│ └── resource_controller.rb # Resource base
|
202
|
+
├── policies/admin_portal/
|
203
|
+
│ └── resource_policy.rb # Portal-specific policies
|
204
|
+
└── definitions/admin_portal/
|
205
|
+
└── resource_definition.rb # Portal-specific definitions
|
206
|
+
```
|
207
|
+
|
208
|
+
## Development Patterns
|
209
|
+
|
210
|
+
### 1. Resource Definition (UI Configuration)
|
211
|
+
|
212
|
+
**CRITICAL**: Plutonium automatically detects all fields from your model - database columns, associations, and Active Storage attachments. Definitions should only contain customizations, not field declarations.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
class PostDefinition < Plutonium::Resource::Definition
|
216
|
+
# NO NEED to declare fields - they're auto-detected from the model
|
217
|
+
# Only add customizations below when you want to change default behavior
|
218
|
+
|
219
|
+
# Form inputs (only override when changing auto-detected behavior)
|
220
|
+
input :content, as: :rich_text
|
221
|
+
input :published_at, as: :date
|
222
|
+
input :category, as: :select, choices: %w[Tech Business]
|
223
|
+
|
224
|
+
# Display formatting (only override when changing auto-detected behavior)
|
225
|
+
display :content, as: :markdown
|
226
|
+
display :published_at, as: :datetime
|
227
|
+
|
228
|
+
# Table columns (only override when changing auto-detected behavior)
|
229
|
+
column :published_at, as: :datetime
|
230
|
+
|
231
|
+
# Search functionality
|
232
|
+
search do |scope, query|
|
233
|
+
scope.where("title ILIKE ? OR content ILIKE ?", "%#{query}%", "%#{query}%")
|
234
|
+
end
|
235
|
+
|
236
|
+
# Filters (currently only Text filter available)
|
237
|
+
filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
|
238
|
+
|
239
|
+
# Scopes (named scopes defined on the model. they that appear as filter buttons)
|
240
|
+
scope :published
|
241
|
+
scope :drafts
|
242
|
+
|
243
|
+
# Custom actions
|
244
|
+
action :publish, interaction: PostInteractions::Publish
|
245
|
+
action :archive, interaction: PostInteractions::Archive
|
246
|
+
end
|
247
|
+
```
|
248
|
+
|
249
|
+
### 2. Policy Configuration (Authorization)
|
250
|
+
```ruby
|
251
|
+
class PostPolicy < Plutonium::Resource::Policy
|
252
|
+
# Basic permissions (required - secure by default)
|
253
|
+
def create?
|
254
|
+
user.present?
|
255
|
+
end
|
256
|
+
|
257
|
+
def update?
|
258
|
+
record.user_id == user.id || user.admin?
|
259
|
+
end
|
260
|
+
|
261
|
+
def destroy?
|
262
|
+
user.admin?
|
263
|
+
end
|
264
|
+
|
265
|
+
# Custom action permissions
|
266
|
+
def publish?
|
267
|
+
update? && record.published_at.nil?
|
268
|
+
end
|
269
|
+
|
270
|
+
# Attribute permissions (what fields are visible/editable)
|
271
|
+
def permitted_attributes_for_read
|
272
|
+
attrs = [:title, :content, :published_at, :created_at]
|
273
|
+
attrs << :admin_notes if user.admin?
|
274
|
+
attrs
|
275
|
+
end
|
276
|
+
|
277
|
+
def permitted_attributes_for_create
|
278
|
+
[:title, :content, :user_id]
|
279
|
+
end
|
280
|
+
|
281
|
+
def permitted_attributes_for_update
|
282
|
+
permitted_attributes_for_create
|
283
|
+
end
|
284
|
+
|
285
|
+
# Data scoping (what records are visible)
|
286
|
+
relation_scope do |scope|
|
287
|
+
scope = super(scope) # Important: call super for entity scoping
|
288
|
+
|
289
|
+
if user.admin?
|
290
|
+
scope
|
291
|
+
else
|
292
|
+
scope.where(user: user).or(scope.where(published: true))
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
### 3. Interaction Implementation (Business Logic)
|
299
|
+
```ruby
|
300
|
+
module PostInteractions
|
301
|
+
class Publish < Plutonium::Resource::Interaction
|
302
|
+
# Define what this interaction accepts
|
303
|
+
attribute :resource, class: "Post"
|
304
|
+
attribute :published_at, :datetime, default: -> { Time.current }
|
305
|
+
|
306
|
+
# UI presentation
|
307
|
+
presents label: "Publish Post",
|
308
|
+
icon: Phlex::TablerIcons::Send,
|
309
|
+
description: "Make this post public"
|
310
|
+
|
311
|
+
# Validations
|
312
|
+
validates :resource, presence: true
|
313
|
+
|
314
|
+
private
|
315
|
+
|
316
|
+
# Business logic
|
317
|
+
def execute
|
318
|
+
if resource.update(published_at: published_at, status: 'published')
|
319
|
+
succeed(resource)
|
320
|
+
.with_message("Post published successfully")
|
321
|
+
.with_redirect_response(resource_url_for(resource))
|
322
|
+
else
|
323
|
+
failed(resource.errors)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
```
|
329
|
+
|
330
|
+
### 4. Model Setup
|
331
|
+
```ruby
|
332
|
+
class Post < BlogManagement::ResourceRecord
|
333
|
+
# Associations
|
334
|
+
belongs_to :user
|
335
|
+
has_many :comments, dependent: :destroy
|
336
|
+
has_one_attached :featured_image
|
337
|
+
|
338
|
+
# Validations
|
339
|
+
validates :title, presence: true
|
340
|
+
validates :content, presence: true
|
341
|
+
|
342
|
+
# Enums
|
343
|
+
enum status: { draft: 0, published: 1, archived: 2 }
|
344
|
+
|
345
|
+
# Scopes
|
346
|
+
scope :published, -> { where.not(published_at: nil) }
|
347
|
+
scope :recent, -> { order(created_at: :desc) }
|
348
|
+
|
349
|
+
# Monetary fields (if needed)
|
350
|
+
has_cents :price_cents
|
351
|
+
|
352
|
+
# Custom path parameters (class methods, not instance methods)
|
353
|
+
# path_parameter :username # Uses username in URLs
|
354
|
+
# dynamic_path_parameter :title # Creates SEO URLs like "1-my-title"
|
355
|
+
|
356
|
+
# Custom labeling (optional)
|
357
|
+
def to_label
|
358
|
+
title.presence || "Post ##{id}"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# Example with cross-package associations
|
363
|
+
class Comment < Comments::ResourceRecord
|
364
|
+
belongs_to :user
|
365
|
+
belongs_to :post, class_name: "Blogging::Post" # Cross-package reference
|
366
|
+
|
367
|
+
validates :body, presence: true
|
368
|
+
end
|
369
|
+
```
|
370
|
+
|
371
|
+
## Best Practices
|
372
|
+
|
373
|
+
### 1. File Organization
|
374
|
+
- Use packages to organize related features
|
375
|
+
- Keep business logic in feature packages
|
376
|
+
- Use portal packages for different user interfaces
|
377
|
+
- Follow namespacing conventions strictly
|
378
|
+
|
379
|
+
### 2. Security First
|
380
|
+
- Always define explicit permissions in policies
|
381
|
+
- Use relation_scope for data access control
|
382
|
+
- Leverage entity scoping for multi-tenancy
|
383
|
+
- Test authorization thoroughly
|
384
|
+
|
385
|
+
### 3. Generator Usage
|
386
|
+
- Start with `pu:res:scaffold` for complete resources
|
387
|
+
- Use `--dest=package_name` to specify target package
|
388
|
+
- **CRITICAL**: Use `pu:res:conn` generator to connect resources to portals - never manually connect
|
389
|
+
- Definitions only need overrides - auto-detection handles defaults
|
390
|
+
|
391
|
+
### 4. UI Customization
|
392
|
+
- Policies control WHAT (authorization)
|
393
|
+
- Definitions control HOW (presentation) - **fields are auto-detected, only add customizations**
|
394
|
+
- Interactions control business logic
|
395
|
+
- Trust auto-detection, customize only when needed
|
396
|
+
|
397
|
+
### 5. Common Field Types
|
398
|
+
**Input Types**: `:string`, `:text`, `:rich_text`, `:email`, `:url`, `:tel`, `:password`, `:number`, `:boolean`, `:date`, `:datetime`, `:select`, `:file`, `:uppy`, `:association`
|
399
|
+
|
400
|
+
**Display Types**: `:string`, `:text`, `:markdown`, `:email`, `:url`, `:boolean`, `:date`, `:datetime`, `:association`, `:attachment`
|
401
|
+
|
402
|
+
**Action Options**: `category: :primary/:secondary/:danger`, `position: 10`, `record_action: true`, `collection_record_action: true`, `resource_action: true`, `bulk_action: true`, `confirmation: "message"`, `icon: Phlex::TablerIcons::IconName`
|
403
|
+
|
404
|
+
## Migration Tips
|
405
|
+
|
406
|
+
### Database Setup
|
407
|
+
- Use standard Rails migration conventions
|
408
|
+
- Always inline indexes and constraints in create_table blocks
|
409
|
+
- Use nullable fields with `'field:type?'` syntax
|
410
|
+
- Reference namespaced models: `package_name/model:belongs_to`
|
411
|
+
- Leverage Rails associations (`belongs_to`, `has_many`, etc.)
|
412
|
+
|
413
|
+
### Cross-Package Associations
|
414
|
+
```bash
|
415
|
+
# When generating models that reference other packages
|
416
|
+
rails generate pu:res:scaffold Comment user:belongs_to blogging/post:belongs_to body:text --dest=comments
|
417
|
+
|
418
|
+
# This creates the correct association:
|
419
|
+
# belongs_to :post, class_name: "Blogging::Post"
|
420
|
+
```
|
421
|
+
|
422
|
+
### Entity Scoping (Multi-Tenancy) Setup
|
423
|
+
|
424
|
+
Plutonium provides powerful multi-tenancy through Entity Scoping, which automatically isolates data by tenant.
|
425
|
+
|
426
|
+
#### 1. Configure Portal Engine
|
427
|
+
```ruby
|
428
|
+
# In packages/admin_portal/lib/engine.rb
|
429
|
+
scope_to_entity Organization, strategy: :path # URLs: /organizations/:organization_id/posts
|
430
|
+
|
431
|
+
# Custom strategy (subdomain-based)
|
432
|
+
scope_to_entity Organization, strategy: :current_organization # URLs: /posts on acme.app.com
|
433
|
+
|
434
|
+
# Custom parameter name
|
435
|
+
scope_to_entity Client, strategy: :path, param_key: :client_slug # URLs: /clients/:client_slug/posts
|
436
|
+
```
|
437
|
+
|
438
|
+
#### 2. Implement Custom Strategy Methods
|
439
|
+
```ruby
|
440
|
+
# In packages/customer_portal/app/controllers/customer_portal/concerns/controller.rb
|
441
|
+
private
|
442
|
+
|
443
|
+
def current_organization
|
444
|
+
@current_organization ||= begin
|
445
|
+
organization = Organization.find_by!(subdomain: request.subdomain)
|
446
|
+
|
447
|
+
# CRITICAL: Verify user has access to this organization
|
448
|
+
unless current_user.organizations.include?(organization)
|
449
|
+
raise ActionPolicy::Unauthorized, "Access denied to organization"
|
450
|
+
end
|
451
|
+
|
452
|
+
organization
|
453
|
+
end
|
454
|
+
rescue ActiveRecord::RecordNotFound
|
455
|
+
redirect_to root_path, error: "Invalid organization subdomain"
|
456
|
+
end
|
457
|
+
```
|
458
|
+
|
459
|
+
#### 3. Model Association Setup
|
460
|
+
```ruby
|
461
|
+
# Direct association (preferred)
|
462
|
+
class Post < ApplicationRecord
|
463
|
+
belongs_to :organization # Direct link
|
464
|
+
end
|
465
|
+
|
466
|
+
# Indirect association (automatic chain discovery)
|
467
|
+
class Comment < ApplicationRecord
|
468
|
+
belongs_to :post
|
469
|
+
has_one :organization, through: :post # Chain: Comment -> Post -> Organization
|
470
|
+
end
|
471
|
+
|
472
|
+
# Custom scope for complex relationships
|
473
|
+
class Invoice < ApplicationRecord
|
474
|
+
belongs_to :customer
|
475
|
+
|
476
|
+
scope :associated_with_organization, ->(organization) do
|
477
|
+
joins(customer: :organization_memberships)
|
478
|
+
.where(organization_memberships: { organization_id: organization.id })
|
479
|
+
end
|
480
|
+
end
|
481
|
+
```
|
482
|
+
|
483
|
+
#### 4. Policy Integration
|
484
|
+
```ruby
|
485
|
+
class PostPolicy < Plutonium::Resource::Policy
|
486
|
+
authorize :entity_scope, allow_nil: true # Access to current tenant
|
487
|
+
|
488
|
+
def update?
|
489
|
+
# Ensure record belongs to current tenant AND user can edit
|
490
|
+
record.organization == entity_scope && record.author == user
|
491
|
+
end
|
492
|
+
|
493
|
+
relation_scope do |relation|
|
494
|
+
relation = super(relation) # Apply entity scoping first
|
495
|
+
|
496
|
+
# Add additional tenant-aware filtering
|
497
|
+
user.admin? ? relation : relation.where(published: true)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
```
|
501
|
+
|
502
|
+
## Quick Reference Commands
|
503
|
+
|
504
|
+
```bash
|
505
|
+
# Essential workflow
|
506
|
+
rails generate pu:pkg:package feature_name # Create feature package
|
507
|
+
rails generate pu:res:scaffold Resource --dest=feature_name # Create complete resource
|
508
|
+
rails generate pu:pkg:portal portal_name # Create portal
|
509
|
+
rails generate pu:res:conn Feature::Resource --dest=portal # CRITICAL: Always use generator to connect
|
510
|
+
|
511
|
+
# Authentication
|
512
|
+
rails generate pu:rodauth:install # Install auth system
|
513
|
+
rails generate pu:rodauth:account user # Create user account
|
514
|
+
rails generate pu:rodauth:admin admin # Create admin account
|
515
|
+
|
516
|
+
# Database
|
517
|
+
rails db:migrate # Run migrations
|
518
|
+
rails db:seed # Seed data
|
519
|
+
|
520
|
+
# Development
|
521
|
+
rails server # Start server
|
522
|
+
rails console # Rails console
|
523
|
+
rails runner "code" # Run code (prefer over console)
|
524
|
+
```
|
525
|
+
|
526
|
+
## Troubleshooting
|
527
|
+
|
528
|
+
### Common Issues
|
529
|
+
- **Missing permissions**: Check policy methods return true
|
530
|
+
- **Fields not showing**: Verify policy permits attributes
|
531
|
+
- **Actions not visible**: Ensure action policy method exists
|
532
|
+
- **Routing errors**: Check portal routes registration
|
533
|
+
- **Package not loading**: Verify engine is properly configured
|
534
|
+
|
535
|
+
This guide provides the foundation for building robust Plutonium applications with proper separation of concerns, security, and maintainability.
|
@@ -139,7 +139,7 @@ class ApplicationController < ActionController::Base
|
|
139
139
|
end
|
140
140
|
else
|
141
141
|
outcome.messages.each { |msg, type| flash.now[type] = msg }
|
142
|
-
render json: { errors: outcome.errors }, status: :
|
142
|
+
render json: { errors: outcome.errors }, status: :unprocessable_content
|
143
143
|
end
|
144
144
|
end
|
145
145
|
end
|
@@ -47,7 +47,7 @@ module Plutonium
|
|
47
47
|
|
48
48
|
respond_to do |format|
|
49
49
|
if params[:pre_submit]
|
50
|
-
format.html { render :new, status: :
|
50
|
+
format.html { render :new, status: :unprocessable_content }
|
51
51
|
elsif resource_record!.save
|
52
52
|
format.html do
|
53
53
|
redirect_to redirect_url_after_submit,
|
@@ -59,10 +59,10 @@ module Plutonium
|
|
59
59
|
location: redirect_url_after_submit
|
60
60
|
end
|
61
61
|
else
|
62
|
-
format.html { render :new, status: :
|
62
|
+
format.html { render :new, status: :unprocessable_content }
|
63
63
|
format.any do
|
64
64
|
@errors = resource_record!.errors
|
65
|
-
render "errors", status: :
|
65
|
+
render "errors", status: :unprocessable_content
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -87,7 +87,7 @@ module Plutonium
|
|
87
87
|
|
88
88
|
respond_to do |format|
|
89
89
|
if params[:pre_submit]
|
90
|
-
format.html { render :edit, status: :
|
90
|
+
format.html { render :edit, status: :unprocessable_content }
|
91
91
|
elsif resource_record!.save
|
92
92
|
format.html do
|
93
93
|
redirect_to redirect_url_after_submit,
|
@@ -99,10 +99,10 @@ module Plutonium
|
|
99
99
|
render :show, status: :ok, location: redirect_url_after_submit
|
100
100
|
end
|
101
101
|
else
|
102
|
-
format.html { render :edit, status: :
|
102
|
+
format.html { render :edit, status: :unprocessable_content }
|
103
103
|
format.any do
|
104
104
|
@errors = resource_record!.errors
|
105
|
-
render "errors", status: :
|
105
|
+
render "errors", status: :unprocessable_content
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -133,7 +133,7 @@ module Plutonium
|
|
133
133
|
:existing_references,
|
134
134
|
message: "is referenced by other records"
|
135
135
|
|
136
|
-
render "errors", status: :
|
136
|
+
render "errors", status: :unprocessable_content
|
137
137
|
end
|
138
138
|
end
|
139
139
|
end
|
@@ -41,7 +41,7 @@ module Plutonium
|
|
41
41
|
if params[:pre_submit]
|
42
42
|
respond_to do |format|
|
43
43
|
format.html do
|
44
|
-
render :interactive_record_action, status: :
|
44
|
+
render :interactive_record_action, status: :unprocessable_content
|
45
45
|
end
|
46
46
|
end
|
47
47
|
return
|
@@ -65,12 +65,12 @@ module Plutonium
|
|
65
65
|
end
|
66
66
|
else
|
67
67
|
format.html do
|
68
|
-
render :interactive_record_action, status: :
|
68
|
+
render :interactive_record_action, status: :unprocessable_content
|
69
69
|
end
|
70
70
|
|
71
71
|
format.any do
|
72
72
|
@errors = @interaction.errors
|
73
|
-
render "errors", status: :
|
73
|
+
render "errors", status: :unprocessable_content
|
74
74
|
end
|
75
75
|
|
76
76
|
end
|
@@ -98,7 +98,7 @@ module Plutonium
|
|
98
98
|
if params[:pre_submit]
|
99
99
|
respond_to do |format|
|
100
100
|
format.html do
|
101
|
-
render :interactive_resource_action, status: :
|
101
|
+
render :interactive_resource_action, status: :unprocessable_content
|
102
102
|
end
|
103
103
|
end
|
104
104
|
return
|
@@ -122,12 +122,12 @@ module Plutonium
|
|
122
122
|
end
|
123
123
|
else
|
124
124
|
format.html do
|
125
|
-
render :interactive_resource_action, status: :
|
125
|
+
render :interactive_resource_action, status: :unprocessable_content
|
126
126
|
end
|
127
127
|
|
128
128
|
format.any do
|
129
129
|
@errors = @interaction.errors
|
130
|
-
render "errors", status: :
|
130
|
+
render "errors", status: :unprocessable_content
|
131
131
|
end
|
132
132
|
|
133
133
|
end
|
@@ -171,11 +171,11 @@ module Plutonium
|
|
171
171
|
# end
|
172
172
|
# else
|
173
173
|
# format.html do
|
174
|
-
# render :interactive_bulk_action, status: :
|
174
|
+
# render :interactive_bulk_action, status: :unprocessable_content
|
175
175
|
# end
|
176
176
|
# format.any do
|
177
177
|
# @errors = @interaction.errors
|
178
|
-
# render "errors", status: :
|
178
|
+
# render "errors", status: :unprocessable_content
|
179
179
|
# end
|
180
180
|
# end
|
181
181
|
# end
|
data/lib/plutonium/version.rb
CHANGED