plutonium 0.50.0 → 0.51.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.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium/SKILL.md +85 -102
- data/.claude/skills/plutonium-app/SKILL.md +572 -0
- data/.claude/skills/plutonium-auth/SKILL.md +163 -300
- data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
- data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
- data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
- data/.claude/skills/plutonium-testing/SKILL.md +6 -5
- data/.claude/skills/plutonium-ui/SKILL.md +900 -0
- data/CHANGELOG.md +27 -2
- data/Rakefile +2 -1
- data/app/assets/plutonium.css +1 -11
- data/app/assets/plutonium.js +1009 -1214
- data/app/assets/plutonium.js.map +3 -3
- data/app/assets/plutonium.min.js +52 -51
- data/app/assets/plutonium.min.js.map +3 -3
- data/docs/.vitepress/config.ts +37 -27
- data/docs/getting-started/index.md +22 -29
- data/docs/getting-started/installation.md +37 -80
- data/docs/getting-started/tutorial/index.md +4 -5
- data/docs/guides/adding-resources.md +66 -377
- data/docs/guides/authentication.md +94 -463
- data/docs/guides/authorization.md +124 -370
- data/docs/guides/creating-packages.md +94 -296
- data/docs/guides/custom-actions.md +121 -441
- data/docs/guides/index.md +22 -42
- data/docs/guides/multi-tenancy.md +116 -187
- data/docs/guides/nested-resources.md +103 -431
- data/docs/guides/search-filtering.md +123 -240
- data/docs/guides/testing.md +5 -4
- data/docs/guides/theming.md +157 -407
- data/docs/guides/troubleshooting.md +5 -3
- data/docs/guides/user-invites.md +106 -425
- data/docs/guides/user-profile.md +76 -243
- data/docs/index.md +1 -1
- data/docs/reference/app/generators.md +517 -0
- data/docs/reference/app/index.md +158 -0
- data/docs/reference/app/packages.md +146 -0
- data/docs/reference/app/portals.md +377 -0
- data/docs/reference/auth/accounts.md +230 -0
- data/docs/reference/auth/index.md +88 -0
- data/docs/reference/auth/profile.md +185 -0
- data/docs/reference/behavior/controllers.md +395 -0
- data/docs/reference/behavior/index.md +22 -0
- data/docs/reference/behavior/interactions.md +341 -0
- data/docs/reference/behavior/policies.md +417 -0
- data/docs/reference/index.md +56 -49
- data/docs/reference/resource/actions.md +423 -0
- data/docs/reference/resource/definition.md +508 -0
- data/docs/reference/resource/index.md +50 -0
- data/docs/reference/resource/model.md +348 -0
- data/docs/reference/resource/query.md +305 -0
- data/docs/reference/tenancy/entity-scoping.md +361 -0
- data/docs/reference/tenancy/index.md +36 -0
- data/docs/reference/tenancy/invites.md +393 -0
- data/docs/reference/tenancy/nested-resources.md +267 -0
- data/docs/reference/testing/index.md +287 -0
- data/docs/reference/ui/assets.md +400 -0
- data/docs/reference/ui/components.md +165 -0
- data/docs/reference/ui/displays.md +104 -0
- data/docs/reference/ui/forms.md +284 -0
- data/docs/reference/ui/index.md +30 -0
- data/docs/reference/ui/layouts.md +106 -0
- data/docs/reference/ui/pages.md +189 -0
- data/docs/reference/ui/tables.md +117 -0
- data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
- data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
- data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/update/update_generator.rb +0 -20
- data/lib/generators/pu/invites/install_generator.rb +1 -0
- data/lib/plutonium/definition/base.rb +1 -1
- data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
- data/lib/plutonium/helpers/turbo_helper.rb +11 -0
- data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
- data/lib/plutonium/resource/controller.rb +1 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
- data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
- data/lib/plutonium/resource/policy.rb +7 -0
- data/lib/plutonium/routing/mapper_extensions.rb +15 -0
- data/lib/plutonium/ui/component/methods.rb +4 -0
- data/lib/plutonium/ui/form/base.rb +6 -2
- data/lib/plutonium/ui/form/components/json.rb +58 -0
- data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
- data/lib/plutonium/ui/form/components/secure_association.rb +98 -22
- data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
- data/lib/plutonium/ui/form/resource.rb +0 -4
- data/lib/plutonium/ui/grid/resource.rb +1 -1
- data/lib/plutonium/ui/layout/base.rb +1 -0
- data/lib/plutonium/ui/page/base.rb +0 -7
- data/lib/plutonium/ui/page/index.rb +4 -4
- data/lib/plutonium/ui/table/resource.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium.rb +8 -0
- data/lib/tasks/release.rake +15 -1
- data/package.json +10 -10
- data/src/css/slim_select.css +4 -0
- data/src/js/controllers/slim_select_controller.js +61 -0
- data/src/js/turbo/turbo_actions.js +33 -0
- data/yarn.lock +553 -543
- metadata +44 -33
- data/.claude/skills/plutonium-assets/SKILL.md +0 -512
- data/.claude/skills/plutonium-controller/SKILL.md +0 -396
- data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
- data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
- data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
- data/.claude/skills/plutonium-forms/SKILL.md +0 -465
- data/.claude/skills/plutonium-installation/SKILL.md +0 -331
- data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
- data/.claude/skills/plutonium-invites/SKILL.md +0 -408
- data/.claude/skills/plutonium-model/SKILL.md +0 -440
- data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
- data/.claude/skills/plutonium-package/SKILL.md +0 -198
- data/.claude/skills/plutonium-policy/SKILL.md +0 -456
- data/.claude/skills/plutonium-portal/SKILL.md +0 -410
- data/.claude/skills/plutonium-views/SKILL.md +0 -651
- data/docs/reference/assets/index.md +0 -496
- data/docs/reference/controller/index.md +0 -412
- data/docs/reference/definition/actions.md +0 -462
- data/docs/reference/definition/fields.md +0 -383
- data/docs/reference/definition/index.md +0 -326
- data/docs/reference/definition/query.md +0 -351
- data/docs/reference/generators/index.md +0 -648
- data/docs/reference/interaction/index.md +0 -449
- data/docs/reference/model/features.md +0 -248
- data/docs/reference/model/index.md +0 -218
- data/docs/reference/policy/index.md +0 -456
- data/docs/reference/portal/index.md +0 -379
- data/docs/reference/views/forms.md +0 -411
- data/docs/reference/views/index.md +0 -544
|
@@ -1,450 +1,139 @@
|
|
|
1
1
|
# Adding Resources
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Add a new model to your Plutonium app: scaffold it, migrate it, connect it to a portal.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Goal
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
# Generate a resource in the main app
|
|
9
|
-
rails g pu:res:scaffold Product name:string 'price:decimal{10,2}' --dest=main_app
|
|
10
|
-
|
|
11
|
-
# Generate a resource in a feature package
|
|
12
|
-
rails g pu:res:scaffold Product name:string 'price:decimal{10,2}' --dest=inventory
|
|
13
|
-
|
|
14
|
-
# Connect to a portal
|
|
15
|
-
rails g pu:res:conn Product --dest=admin_portal
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## The Resource Generator
|
|
19
|
-
|
|
20
|
-
### Basic Usage
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
rails g pu:res:scaffold ModelName field:type field:type --dest=DESTINATION
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
**Always specify `--dest`** to avoid interactive prompts:
|
|
27
|
-
- `--dest=main_app` for resources in the main application
|
|
28
|
-
- `--dest=package_name` for resources in a feature package
|
|
29
|
-
|
|
30
|
-
### Field Types
|
|
31
|
-
|
|
32
|
-
Format: `name:type:index_type`
|
|
33
|
-
|
|
34
|
-
| Type | Example | Description |
|
|
35
|
-
|------|---------|-------------|
|
|
36
|
-
| `string` | `title:string` | Short text (required) |
|
|
37
|
-
| `'title:string?'` | `'title:string?'` | Short text (nullable) |
|
|
38
|
-
| `text` | `body:text` | Long text |
|
|
39
|
-
| `integer` | `quantity:integer` | Whole numbers |
|
|
40
|
-
| `decimal` | `'price:decimal{10,2}'` | Decimal with precision |
|
|
41
|
-
| `float` | `rating:float` | Floating point |
|
|
42
|
-
| `boolean` | `active:boolean` | True/false |
|
|
43
|
-
| `date` | `published_on:date` | Date only |
|
|
44
|
-
| `datetime` | `published_at:datetime` | Date and time |
|
|
45
|
-
| `time` | `starts_at:time` | Time only |
|
|
46
|
-
| `json` | `metadata:json` | JSON data |
|
|
47
|
-
| `jsonb` | `settings:jsonb` | JSONB (PostgreSQL) |
|
|
48
|
-
| `uuid` | `external_id:uuid` | UUID field |
|
|
49
|
-
|
|
50
|
-
### Nullable Fields
|
|
51
|
-
|
|
52
|
-
Append `?` to make a field nullable. **Quote fields with special characters**:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
'name:string?' # Nullable string
|
|
56
|
-
'description:text?' # Nullable text
|
|
57
|
-
'published_at:datetime?' # Nullable datetime
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Decimal Precision
|
|
7
|
+
Get a working list/show/new/edit/delete UI for a new model with sensible defaults, then customize as needed.
|
|
61
8
|
|
|
62
|
-
|
|
9
|
+
## Steps
|
|
63
10
|
|
|
64
|
-
|
|
65
|
-
'price:decimal{10,2}' # precision: 10, scale: 2
|
|
66
|
-
'latitude:decimal{11,8}' # precision: 11, scale: 8
|
|
67
|
-
'amount:decimal?{15,2}' # nullable with precision
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Default Values
|
|
71
|
-
|
|
72
|
-
Use `{default:value}` syntax to set default values:
|
|
11
|
+
### 1. Scaffold the resource
|
|
73
12
|
|
|
74
13
|
```bash
|
|
75
|
-
|
|
76
|
-
'active:boolean{default:true}' # Boolean default
|
|
77
|
-
'priority:integer{default:0}' # Integer default
|
|
78
|
-
'price:decimal{10,2,default:0}' # Decimal with precision and default
|
|
79
|
-
'category:string?{default:general}' # Nullable with default
|
|
14
|
+
rails g pu:res:scaffold Post user:belongs_to title:string 'content:text?' published:boolean --dest=main_app
|
|
80
15
|
```
|
|
81
16
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
# Required belongs_to
|
|
86
|
-
user:belongs_to
|
|
87
|
-
company:references # Same as belongs_to
|
|
17
|
+
Quote any field containing `?` or `{}` to prevent shell expansion.
|
|
88
18
|
|
|
89
|
-
|
|
90
|
-
'parent:belongs_to?' # Creates: null: true, optional: true
|
|
19
|
+
Full field-type syntax: [Reference › Resource › Model](/reference/resource/model). For all `pu:res:scaffold` options: [Reference › Generators](/reference/app/generators#pu-res-scaffold).
|
|
91
20
|
|
|
92
|
-
|
|
93
|
-
'author:belongs_to{class_name:User}'
|
|
94
|
-
'reviewer:belongs_to?{class_name:User}' # Nullable
|
|
21
|
+
### 2. Review the migration
|
|
95
22
|
|
|
96
|
-
|
|
97
|
-
blogging/post:belongs_to
|
|
98
|
-
```
|
|
23
|
+
Plutonium generates a basic migration. Before running it, edit `db/migrate/<timestamp>_create_posts.rb` to add:
|
|
99
24
|
|
|
100
|
-
|
|
25
|
+
- Cascade deletes (`foreign_key: {on_delete: :cascade}`)
|
|
26
|
+
- Composite indexes for tenant-scoped uniqueness
|
|
27
|
+
- Sensible defaults
|
|
101
28
|
|
|
102
|
-
|
|
29
|
+
### 3. Run the migration
|
|
103
30
|
|
|
104
31
|
```bash
|
|
105
|
-
|
|
106
|
-
email:string:uniq # Unique index
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Special Types
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
password_digest # has_secure_password
|
|
113
|
-
auth_token:token # has_secure_token (auto unique index)
|
|
114
|
-
content:rich_text # has_rich_text (Action Text)
|
|
115
|
-
avatar:attachment # has_one_attached (Active Storage)
|
|
116
|
-
photos:attachments # has_many_attached
|
|
117
|
-
price_cents:integer # has_cents (money field)
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Generator Options
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
# Skip model generation (use existing model)
|
|
124
|
-
rails g pu:res:scaffold Post --no-model --dest=main_app
|
|
125
|
-
|
|
126
|
-
# Skip migration generation
|
|
127
|
-
rails g pu:res:scaffold Post --no-migration --dest=main_app
|
|
128
|
-
|
|
129
|
-
# Both (for existing models with Plutonium::Resource::Record)
|
|
130
|
-
rails g pu:res:scaffold Post --no-model --no-migration --dest=main_app
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Generated Files
|
|
134
|
-
|
|
135
|
-
### For Main App Resources
|
|
136
|
-
|
|
137
|
-
```
|
|
138
|
-
app/
|
|
139
|
-
├── models/post.rb
|
|
140
|
-
├── controllers/posts_controller.rb
|
|
141
|
-
├── definitions/post_definition.rb
|
|
142
|
-
└── policies/post_policy.rb
|
|
143
|
-
db/migrate/xxx_create_posts.rb
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### For Packaged Resources
|
|
147
|
-
|
|
148
|
-
```
|
|
149
|
-
packages/blogging/
|
|
150
|
-
├── app/
|
|
151
|
-
│ ├── models/blogging/post.rb
|
|
152
|
-
│ ├── controllers/blogging/posts_controller.rb
|
|
153
|
-
│ ├── definitions/blogging/post_definition.rb
|
|
154
|
-
│ └── policies/blogging/post_policy.rb
|
|
155
|
-
db/migrate/xxx_create_blogging_posts.rb
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Model
|
|
159
|
-
|
|
160
|
-
```ruby
|
|
161
|
-
class Post < ResourceRecord
|
|
162
|
-
include Plutonium::Resource::Record
|
|
163
|
-
end
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Definition
|
|
167
|
-
|
|
168
|
-
```ruby
|
|
169
|
-
class PostDefinition < ResourceDefinition
|
|
170
|
-
# Fields auto-detected from model
|
|
171
|
-
end
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Policy
|
|
175
|
-
|
|
176
|
-
```ruby
|
|
177
|
-
class PostPolicy < ResourcePolicy
|
|
178
|
-
def permitted_attributes_for_create
|
|
179
|
-
%i[title content user_id]
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def permitted_attributes_for_read
|
|
183
|
-
%i[title content user_id created_at updated_at]
|
|
184
|
-
end
|
|
185
|
-
end
|
|
32
|
+
rails db:migrate
|
|
186
33
|
```
|
|
187
34
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
Resources must be connected to a portal to be accessible via the web.
|
|
191
|
-
|
|
192
|
-
### Using the Generator
|
|
35
|
+
### 4. Connect to a portal
|
|
193
36
|
|
|
194
37
|
```bash
|
|
195
38
|
rails g pu:res:conn Post --dest=admin_portal
|
|
196
39
|
```
|
|
197
40
|
|
|
198
|
-
This
|
|
199
|
-
1. Registers the resource in portal routes
|
|
200
|
-
2. Creates a portal-specific controller
|
|
201
|
-
3. Creates portal-specific policy and definition (if base versions don't exist)
|
|
41
|
+
This creates the portal-specific controller, policy, and definition, plus registers the resource in the portal's routes. Until you do this, the resource has no URL.
|
|
202
42
|
|
|
203
|
-
|
|
43
|
+
For singular resources (`/profile`, `/settings`), add `--singular`:
|
|
204
44
|
|
|
205
45
|
```bash
|
|
206
|
-
rails g pu:res:conn
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Connecting Namespaced Resources
|
|
210
|
-
|
|
211
|
-
Use the full class name for packaged resources:
|
|
212
|
-
|
|
213
|
-
```bash
|
|
214
|
-
rails g pu:res:conn Blogging::Post Blogging::Comment --dest=admin_portal
|
|
46
|
+
rails g pu:res:conn Profile --dest=customer_portal --singular
|
|
215
47
|
```
|
|
216
48
|
|
|
217
|
-
###
|
|
49
|
+
### 5. Trim the generated policy
|
|
218
50
|
|
|
219
|
-
|
|
51
|
+
The generator is liberal — it seeds `permitted_attributes_for_*` from your model columns. Open `packages/admin_portal/app/policies/admin_portal/post_policy.rb` and:
|
|
220
52
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
53
|
+
- Drop `_id` fields when the form should use the association name (e.g. `:user`, not `:user_id`).
|
|
54
|
+
- Replace `:price_cents` with `:price` if the model uses `has_cents`.
|
|
55
|
+
- Reduce to what users should actually be able to set/see.
|
|
224
56
|
|
|
225
|
-
|
|
57
|
+
See [Reference › Behavior › Policies](/reference/behavior/policies) for details.
|
|
226
58
|
|
|
227
|
-
###
|
|
59
|
+
### 6. Visit the portal
|
|
228
60
|
|
|
229
61
|
```
|
|
230
|
-
|
|
231
|
-
├── app/
|
|
232
|
-
│ ├── controllers/admin_portal/posts_controller.rb
|
|
233
|
-
│ ├── policies/admin_portal/post_policy.rb
|
|
234
|
-
│ └── definitions/admin_portal/post_definition.rb
|
|
235
|
-
└── config/routes.rb # Updated with register_resource
|
|
62
|
+
http://localhost:3000/admin/posts
|
|
236
63
|
```
|
|
237
64
|
|
|
238
|
-
|
|
65
|
+
You should see:
|
|
239
66
|
|
|
240
|
-
|
|
67
|
+
- Index page with the columns auto-detected from your model.
|
|
68
|
+
- "New" button.
|
|
69
|
+
- Show / edit / delete on each row.
|
|
241
70
|
|
|
242
|
-
|
|
243
|
-
# packages/admin_portal/app/controllers/admin_portal/posts_controller.rb
|
|
244
|
-
class AdminPortal::PostsController < ::PostsController
|
|
245
|
-
include AdminPortal::Concerns::Controller
|
|
71
|
+
## Customizing what you get
|
|
246
72
|
|
|
247
|
-
|
|
73
|
+
| Want to change | Edit | See |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| Which fields appear / how they render | The definition | [Reference › Resource › Definition](/reference/resource/definition) |
|
|
76
|
+
| Search, filters, scopes, sorting | The definition | [Reference › Resource › Query](/reference/resource/query) |
|
|
77
|
+
| Custom buttons / bulk actions | The definition + an interaction | [Reference › Resource › Actions](/reference/resource/actions) |
|
|
78
|
+
| Authorization rules | The policy | [Reference › Behavior › Policies](/reference/behavior/policies) |
|
|
79
|
+
| Redirects, params, presentation | The controller | [Reference › Behavior › Controllers](/reference/behavior/controllers) |
|
|
80
|
+
| Custom page layouts | The definition's nested page classes | [Reference › UI › Pages](/reference/ui/pages) |
|
|
248
81
|
|
|
249
|
-
|
|
250
|
-
super.tap do |post|
|
|
251
|
-
post.user = current_user
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
```
|
|
82
|
+
## Adding fields later
|
|
256
83
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
```ruby
|
|
260
|
-
# packages/admin_portal/app/definitions/admin_portal/post_definition.rb
|
|
261
|
-
class AdminPortal::PostDefinition < ::PostDefinition
|
|
262
|
-
# Add admin-only fields
|
|
263
|
-
field :internal_notes
|
|
84
|
+
Two paths:
|
|
264
85
|
|
|
265
|
-
|
|
266
|
-
field :status, as: :select, collection: %w[draft published archived]
|
|
267
|
-
end
|
|
268
|
-
```
|
|
86
|
+
**Migration only.** Add a new column with a standard Rails migration. Plutonium auto-detects it — appears in all CRUD pages.
|
|
269
87
|
|
|
270
|
-
|
|
88
|
+
**Field with custom rendering.** Add the column, then declare it in the definition:
|
|
271
89
|
|
|
272
90
|
```ruby
|
|
273
|
-
#
|
|
274
|
-
class
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
# Admins can do everything
|
|
278
|
-
def destroy?
|
|
279
|
-
true
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
def permitted_attributes_for_create
|
|
283
|
-
super + [:internal_notes]
|
|
284
|
-
end
|
|
91
|
+
# app/definitions/post_definition.rb
|
|
92
|
+
class PostDefinition < ResourceDefinition
|
|
93
|
+
input :slug, hint: "URL-friendly identifier"
|
|
285
94
|
end
|
|
286
95
|
```
|
|
287
96
|
|
|
288
|
-
##
|
|
289
|
-
|
|
290
|
-
Connect the same resource to multiple portals:
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
rails g pu:res:conn Post --dest=admin_portal
|
|
294
|
-
rails g pu:res:conn Post --dest=author_portal
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Each portal can have different customizations.
|
|
298
|
-
|
|
299
|
-
## From Existing Models
|
|
300
|
-
|
|
301
|
-
If you have existing Rails models you want to convert to Plutonium resources:
|
|
302
|
-
|
|
303
|
-
### Option 1: Model already includes Plutonium::Resource::Record
|
|
304
|
-
|
|
305
|
-
```bash
|
|
306
|
-
rails g pu:res:scaffold Post --no-model --no-migration --dest=main_app
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
This generates only the definition, policy, and controller.
|
|
310
|
-
|
|
311
|
-
### Option 2: Let the generator update the model
|
|
312
|
-
|
|
313
|
-
```bash
|
|
314
|
-
rails g pu:res:scaffold Post --dest=main_app
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
Run without attributes to auto-import fields from the model's content columns.
|
|
318
|
-
|
|
319
|
-
### Required Model Setup
|
|
97
|
+
## Converting an existing model
|
|
320
98
|
|
|
321
|
-
|
|
99
|
+
If the model already exists, skip the model generation:
|
|
322
100
|
|
|
323
101
|
```ruby
|
|
102
|
+
# 1. Include the module
|
|
324
103
|
class Post < ApplicationRecord
|
|
325
104
|
include Plutonium::Resource::Record
|
|
326
105
|
end
|
|
327
106
|
```
|
|
328
107
|
|
|
329
|
-
## Adding Fields After Creation
|
|
330
|
-
|
|
331
|
-
### 1. Create Migration
|
|
332
|
-
|
|
333
108
|
```bash
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
### 2. Update Model (if needed)
|
|
338
|
-
|
|
339
|
-
```ruby
|
|
340
|
-
class Post < ResourceRecord
|
|
341
|
-
validates :status, inclusion: { in: %w[draft published] }
|
|
342
|
-
end
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
### 3. Fields Auto-Detected
|
|
346
|
-
|
|
347
|
-
New columns automatically appear in forms. To customize:
|
|
348
|
-
|
|
349
|
-
```ruby
|
|
350
|
-
# In definition
|
|
351
|
-
field :status, as: :select, collection: %w[draft published]
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
## Migration Customizations
|
|
355
|
-
|
|
356
|
-
Always review and customize generated migrations:
|
|
357
|
-
|
|
358
|
-
### Inline Indexes (preferred)
|
|
359
|
-
|
|
360
|
-
```ruby
|
|
361
|
-
create_table :posts do |t|
|
|
362
|
-
t.belongs_to :user, null: false, foreign_key: true
|
|
363
|
-
t.string :title, null: false
|
|
364
|
-
|
|
365
|
-
t.timestamps
|
|
366
|
-
|
|
367
|
-
t.index :title
|
|
368
|
-
t.index [:user_id, :title], unique: true
|
|
369
|
-
end
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Cascade Delete
|
|
373
|
-
|
|
374
|
-
```ruby
|
|
375
|
-
t.belongs_to :user, null: false, foreign_key: {on_delete: :cascade}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
### Default Values
|
|
379
|
-
|
|
380
|
-
Default values can be set directly in the generator using `{default:value}` syntax. For expressions or complex defaults, edit the migration:
|
|
109
|
+
# 2. Scaffold the rest (skips model + migration)
|
|
110
|
+
rails g pu:res:scaffold Post --no-migration --dest=main_app
|
|
381
111
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
t.datetime :published_at, default: -> { "CURRENT_TIMESTAMP" }
|
|
112
|
+
# 3. Connect to portal
|
|
113
|
+
rails g pu:res:conn Post --dest=admin_portal
|
|
385
114
|
```
|
|
386
115
|
|
|
387
|
-
##
|
|
388
|
-
|
|
389
|
-
### Remove from Portal
|
|
390
|
-
|
|
391
|
-
1. Remove `register_resource` from portal routes
|
|
392
|
-
2. Delete portal-specific files in `packages/portal_name/app/`
|
|
393
|
-
|
|
394
|
-
### Remove Entirely
|
|
116
|
+
## Resources in feature packages
|
|
395
117
|
|
|
396
118
|
```bash
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
rm app/controllers/posts_controller.rb
|
|
400
|
-
rm app/definitions/post_definition.rb
|
|
401
|
-
rm app/policies/post_policy.rb
|
|
402
|
-
|
|
403
|
-
# Create migration to drop table
|
|
404
|
-
rails g migration DropPosts
|
|
119
|
+
rails g pu:res:scaffold Blogging::Post title:string --dest=blogging
|
|
120
|
+
rails g pu:res:conn Blogging::Post --dest=admin_portal
|
|
405
121
|
```
|
|
406
122
|
|
|
407
|
-
|
|
123
|
+
See [Creating packages](./creating-packages) for the package structure.
|
|
408
124
|
|
|
409
|
-
|
|
410
|
-
Avoids interactive prompts and makes commands reproducible.
|
|
125
|
+
## Cross-package references
|
|
411
126
|
|
|
412
|
-
### 2. Quote Special Characters
|
|
413
|
-
Fields with `?` or `{}` must be quoted to prevent shell expansion:
|
|
414
127
|
```bash
|
|
415
|
-
rails g pu:res:scaffold
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
### 3. Run Migrations Before Connecting
|
|
419
|
-
```bash
|
|
420
|
-
rails g pu:res:scaffold Post title:string --dest=main_app
|
|
421
|
-
rails db:migrate
|
|
422
|
-
rails g pu:res:conn Post --dest=admin_portal
|
|
128
|
+
rails g pu:res:scaffold Comment user:belongs_to blogging/post:belongs_to body:text --dest=comments
|
|
423
129
|
```
|
|
424
130
|
|
|
425
|
-
|
|
426
|
-
Add cascade deletes, composite indexes, and default values as needed.
|
|
427
|
-
|
|
428
|
-
## Troubleshooting
|
|
429
|
-
|
|
430
|
-
### Resource Not Found
|
|
431
|
-
|
|
432
|
-
Ensure the resource is connected to the portal with `register_resource`.
|
|
433
|
-
|
|
434
|
-
### Fields Not Showing
|
|
435
|
-
|
|
436
|
-
Check that the migration has run and the policy includes the field in `permitted_attributes_for_read`.
|
|
437
|
-
|
|
438
|
-
### Policy Denying Access
|
|
439
|
-
|
|
440
|
-
Check the policy's permission methods (`index?`, `show?`, `create?`, etc.) return `true`.
|
|
441
|
-
|
|
442
|
-
### Connection Generator Fails
|
|
443
|
-
|
|
444
|
-
Ensure migrations have run - the generator reads model columns to build policy attributes.
|
|
131
|
+
The `blogging/post` syntax expands to `Blogging::Post`.
|
|
445
132
|
|
|
446
133
|
## Related
|
|
447
134
|
|
|
448
|
-
- [
|
|
449
|
-
- [
|
|
450
|
-
- [
|
|
135
|
+
- [Reference › App › Generators](/reference/app/generators) — full generator catalog
|
|
136
|
+
- [Reference › Resource](/reference/resource/) — model + definition + query + actions
|
|
137
|
+
- [Reference › App › Portals](/reference/app/portals) — `pu:res:conn` details
|
|
138
|
+
- [Creating packages](./creating-packages) — resources in feature packages
|
|
139
|
+
- [Nested resources](./nested-resources) — parent/child relationships
|