plutonium 0.33.1 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/# Plutonium: The pre-alpha demo.md +4 -2
- data/.claude/skills/assets/SKILL.md +416 -0
- data/.claude/skills/connect-resource/SKILL.md +112 -0
- data/.claude/skills/controller/SKILL.md +302 -0
- data/.claude/skills/create-resource/SKILL.md +240 -0
- data/.claude/skills/definition/SKILL.md +218 -0
- data/.claude/skills/definition-actions/SKILL.md +386 -0
- data/.claude/skills/definition-fields/SKILL.md +474 -0
- data/.claude/skills/definition-query/SKILL.md +334 -0
- data/.claude/skills/forms/SKILL.md +439 -0
- data/.claude/skills/installation/SKILL.md +300 -0
- data/.claude/skills/interaction/SKILL.md +382 -0
- data/.claude/skills/model/SKILL.md +267 -0
- data/.claude/skills/model-features/SKILL.md +286 -0
- data/.claude/skills/nested-resources/SKILL.md +274 -0
- data/.claude/skills/package/SKILL.md +191 -0
- data/.claude/skills/policy/SKILL.md +352 -0
- data/.claude/skills/portal/SKILL.md +400 -0
- data/.claude/skills/resource/SKILL.md +281 -0
- data/.claude/skills/rodauth/SKILL.md +452 -0
- data/.claude/skills/views/SKILL.md +563 -0
- data/Appraisals +46 -4
- data/CHANGELOG.md +32 -1
- data/app/assets/plutonium.css +2 -2
- data/config/brakeman.ignore +239 -0
- data/config/initializers/action_policy.rb +1 -1
- data/docs/.vitepress/config.ts +132 -47
- data/docs/concepts/architecture.md +226 -0
- data/docs/concepts/auto-detection.md +254 -0
- data/docs/concepts/index.md +61 -0
- data/docs/concepts/packages-portals.md +304 -0
- data/docs/concepts/resources.md +224 -0
- data/docs/cookbook/blog.md +412 -0
- data/docs/cookbook/index.md +289 -0
- data/docs/cookbook/saas.md +481 -0
- data/docs/getting-started/index.md +56 -0
- data/docs/getting-started/installation.md +146 -0
- data/docs/getting-started/tutorial/01-setup.md +118 -0
- data/docs/getting-started/tutorial/02-first-resource.md +180 -0
- data/docs/getting-started/tutorial/03-authentication.md +246 -0
- data/docs/getting-started/tutorial/04-authorization.md +170 -0
- data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
- data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
- data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
- data/docs/getting-started/tutorial/index.md +64 -0
- data/docs/guides/adding-resources.md +420 -0
- data/docs/guides/authentication.md +551 -0
- data/docs/guides/authorization.md +468 -0
- data/docs/guides/creating-packages.md +380 -0
- data/docs/guides/custom-actions.md +523 -0
- data/docs/guides/index.md +45 -0
- data/docs/guides/multi-tenancy.md +302 -0
- data/docs/guides/nested-resources.md +411 -0
- data/docs/guides/search-filtering.md +266 -0
- data/docs/guides/theming.md +321 -0
- data/docs/index.md +67 -26
- data/docs/public/CLAUDE.md +64 -21
- data/docs/reference/assets/index.md +496 -0
- data/docs/reference/controller/index.md +363 -0
- data/docs/reference/definition/actions.md +400 -0
- data/docs/reference/definition/fields.md +350 -0
- data/docs/reference/definition/index.md +252 -0
- data/docs/reference/definition/query.md +342 -0
- data/docs/reference/generators/index.md +469 -0
- data/docs/reference/index.md +49 -0
- data/docs/reference/interaction/index.md +445 -0
- data/docs/reference/model/features.md +248 -0
- data/docs/reference/model/index.md +219 -0
- data/docs/reference/policy/index.md +385 -0
- data/docs/reference/portal/index.md +382 -0
- data/docs/reference/views/forms.md +396 -0
- data/docs/reference/views/index.md +479 -0
- data/gemfiles/rails_7.gemfile +9 -2
- data/gemfiles/rails_7.gemfile.lock +146 -111
- data/gemfiles/rails_8.0.gemfile +20 -0
- data/gemfiles/rails_8.0.gemfile.lock +417 -0
- data/gemfiles/rails_8.1.gemfile +20 -0
- data/gemfiles/rails_8.1.gemfile.lock +419 -0
- data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
- data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
- data/lib/generators/pu/pkg/portal/USAGE +65 -0
- data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
- data/lib/generators/pu/res/conn/USAGE +71 -0
- data/lib/generators/pu/res/model/USAGE +106 -110
- data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
- data/lib/generators/pu/res/scaffold/USAGE +85 -0
- data/lib/generators/pu/rodauth/install_generator.rb +2 -6
- data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
- data/lib/generators/pu/skills/sync/USAGE +14 -0
- data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
- data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
- data/lib/plutonium/core/controller.rb +2 -2
- data/lib/plutonium/interaction/base.rb +1 -0
- data/lib/plutonium/package/engine.rb +2 -2
- data/lib/plutonium/query/adhoc_block.rb +6 -2
- data/lib/plutonium/query/model_scope.rb +1 -1
- data/lib/plutonium/railtie.rb +4 -0
- data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
- data/lib/plutonium/resource/query_object.rb +38 -8
- data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
- data/lib/plutonium/version.rb +1 -1
- data/lib/tasks/release.rake +19 -4
- data/package.json +1 -1
- metadata +76 -39
- data/brakeman.ignore +0 -28
- data/docs/api-examples.md +0 -49
- data/docs/guide/claude-code-guide.md +0 -74
- data/docs/guide/deep-dive/authorization.md +0 -189
- data/docs/guide/deep-dive/multitenancy.md +0 -256
- data/docs/guide/deep-dive/resources.md +0 -390
- data/docs/guide/getting-started/01-installation.md +0 -165
- data/docs/guide/index.md +0 -28
- data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
- data/docs/guide/introduction/02-core-concepts.md +0 -440
- data/docs/guide/tutorial/01-project-setup.md +0 -75
- data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
- data/docs/guide/tutorial/03-defining-resources.md +0 -90
- data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
- data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
- data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
- data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
- data/docs/markdown-examples.md +0 -85
- data/docs/modules/action.md +0 -244
- data/docs/modules/authentication.md +0 -236
- data/docs/modules/configuration.md +0 -599
- data/docs/modules/controller.md +0 -443
- data/docs/modules/core.md +0 -316
- data/docs/modules/definition.md +0 -1308
- data/docs/modules/display.md +0 -759
- data/docs/modules/form.md +0 -495
- data/docs/modules/generator.md +0 -400
- data/docs/modules/index.md +0 -167
- data/docs/modules/interaction.md +0 -642
- data/docs/modules/package.md +0 -151
- data/docs/modules/policy.md +0 -176
- data/docs/modules/portal.md +0 -710
- data/docs/modules/query.md +0 -297
- data/docs/modules/resource_record.md +0 -618
- data/docs/modules/routing.md +0 -690
- data/docs/modules/table.md +0 -301
- data/docs/modules/ui.md +0 -631
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: controller
|
|
3
|
+
description: Plutonium resource controllers - CRUD actions, customization, and integration
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Plutonium Controllers
|
|
7
|
+
|
|
8
|
+
Controllers in Plutonium provide full CRUD functionality out of the box. You rarely need to customize them - definitions handle most UI configuration and policies handle authorization.
|
|
9
|
+
|
|
10
|
+
## Base Classes
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
# app/controllers/resource_controller.rb (generated during install)
|
|
14
|
+
class ResourceController < ApplicationController
|
|
15
|
+
include Plutonium::Resource::Controller
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# app/controllers/posts_controller.rb (generated per resource)
|
|
19
|
+
class PostsController < ::ResourceController
|
|
20
|
+
# Empty - all CRUD actions inherited
|
|
21
|
+
end
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## What You Get for Free
|
|
25
|
+
|
|
26
|
+
Every resource controller automatically provides:
|
|
27
|
+
|
|
28
|
+
| Action | Route | Purpose |
|
|
29
|
+
|--------|-------|---------|
|
|
30
|
+
| `index` | GET /posts | List with pagination, search, filters, sorting |
|
|
31
|
+
| `show` | GET /posts/:id | Display single record |
|
|
32
|
+
| `new` | GET /posts/new | New record form |
|
|
33
|
+
| `create` | POST /posts | Create record |
|
|
34
|
+
| `edit` | GET /posts/:id/edit | Edit record form |
|
|
35
|
+
| `update` | PATCH /posts/:id | Update record |
|
|
36
|
+
| `destroy` | DELETE /posts/:id | Delete record |
|
|
37
|
+
|
|
38
|
+
Plus interactive action routes for custom operations defined in definitions.
|
|
39
|
+
|
|
40
|
+
## When to Customize
|
|
41
|
+
|
|
42
|
+
**Use Definitions for:**
|
|
43
|
+
- Field configuration (inputs, displays, columns)
|
|
44
|
+
- Search, filters, scopes, sorting
|
|
45
|
+
- Actions (interactive operations)
|
|
46
|
+
- Form customization
|
|
47
|
+
|
|
48
|
+
**Customize Controller for:**
|
|
49
|
+
- Custom redirect logic
|
|
50
|
+
- Special parameter processing
|
|
51
|
+
- Non-standard authorization flows
|
|
52
|
+
- External integrations
|
|
53
|
+
- Response format changes
|
|
54
|
+
|
|
55
|
+
## Override Hooks
|
|
56
|
+
|
|
57
|
+
All customization is done by overriding private methods:
|
|
58
|
+
|
|
59
|
+
### Redirect Hooks
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
class PostsController < ::ResourceController
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# Where to go after create/update: "show" (default), "edit", "new", "index"
|
|
66
|
+
def preferred_action_after_submit
|
|
67
|
+
"edit"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Custom URL after create/update (overrides preferred_action_after_submit)
|
|
71
|
+
def redirect_url_after_submit
|
|
72
|
+
posts_path
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Custom URL after destroy
|
|
76
|
+
def redirect_url_after_destroy
|
|
77
|
+
posts_path
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Parameter Hooks
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
class PostsController < ::ResourceController
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
# Modify params before create/update
|
|
89
|
+
def resource_params
|
|
90
|
+
params = super
|
|
91
|
+
params[:tags] = params[:tags].split(",") if params[:tags].is_a?(String)
|
|
92
|
+
params
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Query Hooks
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
class PostsController < ::ResourceController
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
# Customize the index query
|
|
104
|
+
def filtered_resource_collection
|
|
105
|
+
base = current_authorized_scope
|
|
106
|
+
base = base.featured if params[:featured]
|
|
107
|
+
current_query_object.apply(base, raw_resource_query_params)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Presentation Hooks
|
|
113
|
+
|
|
114
|
+
Control whether parent/entity fields appear in forms and displays:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
class PostsController < ::ResourceController
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
# Show parent field in displays (default: false)
|
|
121
|
+
def present_parent?
|
|
122
|
+
true
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Include parent field in forms (default: same as present_parent?)
|
|
126
|
+
def submit_parent?
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Show scoped entity in displays (default: false)
|
|
131
|
+
def present_scoped_entity?
|
|
132
|
+
true
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Include scoped entity in forms (default: same as present_scoped_entity?)
|
|
136
|
+
def submit_scoped_entity?
|
|
137
|
+
true
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Custom Actions
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
class PostsController < ::ResourceController
|
|
146
|
+
def publish
|
|
147
|
+
authorize_current!(resource_record!, to: :publish?)
|
|
148
|
+
resource_record!.update!(published: true)
|
|
149
|
+
redirect_to resource_url_for(resource_record!), notice: "Published!"
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Note: For most custom operations, use Interactive Actions in definitions instead.
|
|
155
|
+
|
|
156
|
+
## Key Methods
|
|
157
|
+
|
|
158
|
+
### Resource Access
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
resource_class # The model class (e.g., Post)
|
|
162
|
+
resource_record! # Current record (raises if not found)
|
|
163
|
+
resource_record? # Current record (nil if not found)
|
|
164
|
+
resource_params # Permitted params for create/update
|
|
165
|
+
current_parent # Parent record for nested routes
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Authorization
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
authorize_current!(record, to: :action?) # Check permission
|
|
172
|
+
current_policy # Policy for current resource
|
|
173
|
+
permitted_attributes # Allowed attributes for action
|
|
174
|
+
current_authorized_scope # Scoped records user can access
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Definition Access
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
current_definition # Definition for current resource
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### UI Building
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
build_form # Build form component
|
|
187
|
+
build_detail # Build show/detail component
|
|
188
|
+
build_collection # Build table component
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### URL Generation
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
resource_url_for(@post) # URL for record
|
|
195
|
+
resource_url_for(@post, action: :edit) # Edit URL
|
|
196
|
+
resource_url_for(Post) # Index URL
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Nested Resources
|
|
200
|
+
|
|
201
|
+
Parent records are automatically resolved:
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
# Route: /users/:user_id/posts/:id
|
|
205
|
+
class PostsController < ::ResourceController
|
|
206
|
+
# current_parent returns the User
|
|
207
|
+
# resource_record! returns the Post scoped to that User
|
|
208
|
+
end
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Parent fields are automatically excluded from forms/displays. Override with presentation hooks (see above).
|
|
212
|
+
|
|
213
|
+
## Entity Scoping (Multi-tenancy)
|
|
214
|
+
|
|
215
|
+
When a portal is scoped to an entity:
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
# packages/admin_portal/lib/engine.rb
|
|
219
|
+
module AdminPortal
|
|
220
|
+
class Engine < Rails::Engine
|
|
221
|
+
include Plutonium::Portal::Engine
|
|
222
|
+
|
|
223
|
+
config.after_initialize do
|
|
224
|
+
scope_to_entity Organization, strategy: :path
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Controllers automatically:
|
|
231
|
+
- Scope all queries to the entity
|
|
232
|
+
- Exclude entity field from forms
|
|
233
|
+
- Provide `current_scoped_entity` method
|
|
234
|
+
|
|
235
|
+
## Authorization Verification
|
|
236
|
+
|
|
237
|
+
Controllers verify authorization was performed:
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
# These run after every action
|
|
241
|
+
verify_authorize_current # Ensures authorize_current! was called
|
|
242
|
+
verify_current_authorized_scope # Ensures scope was loaded (except new/create)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
To skip verification for custom actions:
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
class PostsController < ::ResourceController
|
|
249
|
+
skip_verify_authorize_current only: [:custom_action]
|
|
250
|
+
|
|
251
|
+
def custom_action
|
|
252
|
+
# Handle authorization manually
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Response Formats
|
|
258
|
+
|
|
259
|
+
Controllers respond to multiple formats:
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
def show
|
|
263
|
+
# Responds to:
|
|
264
|
+
# - HTML (default)
|
|
265
|
+
# - JSON (via RABL templates)
|
|
266
|
+
# - Turbo Stream (for Hotwire)
|
|
267
|
+
end
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Portal-Specific Controllers
|
|
271
|
+
|
|
272
|
+
Each portal can have its own controller override:
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
# packages/admin_portal/app/controllers/admin_portal/posts_controller.rb
|
|
276
|
+
module AdminPortal
|
|
277
|
+
class PostsController < ResourceController
|
|
278
|
+
private
|
|
279
|
+
|
|
280
|
+
def preferred_action_after_submit
|
|
281
|
+
"index" # Admin prefers list view
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Best Practices
|
|
288
|
+
|
|
289
|
+
1. **Keep controllers thin** - Use definitions for UI, policies for auth, interactions for logic
|
|
290
|
+
2. **Don't override CRUD actions** - Customize via hooks (`resource_params`, `redirect_url_after_submit`)
|
|
291
|
+
3. **Use interactive actions** - For custom operations, define in definition with interaction
|
|
292
|
+
4. **Let authorization work** - Don't skip verification without good reason
|
|
293
|
+
5. **Trust the framework** - Most customization belongs in definitions or policies
|
|
294
|
+
|
|
295
|
+
## Related Skills
|
|
296
|
+
|
|
297
|
+
- `resource` - How controllers fit in the resource architecture
|
|
298
|
+
- `policy` - Authorization (used by controllers)
|
|
299
|
+
- `definition-actions` - Interactive actions (preferred over custom controller actions)
|
|
300
|
+
- `views` - Custom page, form, display, and table classes
|
|
301
|
+
- `nested-resources` - Parent/child routes and scoping
|
|
302
|
+
- `model` - Resource models
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-resource
|
|
3
|
+
description: Generate Plutonium resources with models, migrations, controllers, policies, and definitions
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Resource Skill
|
|
7
|
+
|
|
8
|
+
Use the `pu:res:scaffold` generator to create complete resources in Plutonium applications.
|
|
9
|
+
|
|
10
|
+
## Command Syntax
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
rails g pu:res:scaffold MODEL_NAME \
|
|
14
|
+
field1:type \
|
|
15
|
+
field2:type \
|
|
16
|
+
--dest=DESTINATION
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**IMPORTANT**: Always specify `--dest` to avoid interactive prompts:
|
|
20
|
+
- `--dest=main_app` for resources in the main application
|
|
21
|
+
- `--dest=package_name` for resources in a feature package
|
|
22
|
+
|
|
23
|
+
**IMPORTANT**: Quote fields containing `?` or `{}` to prevent shell expansion:
|
|
24
|
+
```bash
|
|
25
|
+
'field:type?' # Nullable - must quote
|
|
26
|
+
'field:decimal{10,2}' # Options - must quote
|
|
27
|
+
'field:decimal?{10,2}' # Both - must quote
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## From Existing Models
|
|
31
|
+
|
|
32
|
+
For existing Rails projects with models you want to convert to Plutonium resources:
|
|
33
|
+
|
|
34
|
+
### Option 1: Model already includes Plutonium::Resource::Record
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
rails g pu:res:scaffold Post --no-migration --dest=main_app
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This generates only the definition, policy, and controller - leaving your model unchanged.
|
|
41
|
+
|
|
42
|
+
### Option 2: Let the generator update the model
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
rails g pu:res:scaffold Post --dest=main_app
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Run without attributes to auto-import fields from `model.content_columns`. This regenerates the model file, so review changes carefully.
|
|
49
|
+
|
|
50
|
+
### Don't forget to include the module
|
|
51
|
+
|
|
52
|
+
Your model must include `Plutonium::Resource::Record` (directly or via inheritance):
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
class Post < ApplicationRecord
|
|
56
|
+
include Plutonium::Resource::Record
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Or inherit from a base class
|
|
60
|
+
class Post < ResourceRecord
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Field Type Syntax
|
|
65
|
+
|
|
66
|
+
Format: `name:type:index_type`
|
|
67
|
+
|
|
68
|
+
### Basic Types
|
|
69
|
+
|
|
70
|
+
| Syntax | Result |
|
|
71
|
+
|--------|--------|
|
|
72
|
+
| `name:string` | Required string |
|
|
73
|
+
| `'name:string?'` | Nullable string |
|
|
74
|
+
| `age:integer` | Required integer |
|
|
75
|
+
| `'age:integer?'` | Nullable integer |
|
|
76
|
+
| `active:boolean` | Required boolean |
|
|
77
|
+
| `'active:boolean?'` | Nullable boolean |
|
|
78
|
+
| `content:text` | Required text |
|
|
79
|
+
| `'content:text?'` | Nullable text |
|
|
80
|
+
| `birth_date:date` | Required date |
|
|
81
|
+
| `'anniversary:date?'` | Nullable date |
|
|
82
|
+
| `starts_at:datetime` | Required datetime |
|
|
83
|
+
| `'ends_at:datetime?'` | Nullable datetime |
|
|
84
|
+
| `alarm_time:time` | Required time |
|
|
85
|
+
| `'reminder_time:time?'` | Nullable time |
|
|
86
|
+
|
|
87
|
+
### Decimal with Precision
|
|
88
|
+
|
|
89
|
+
The `{precision,scale}` syntax **only works for decimal types**:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
'latitude:decimal{11,8}' # precision: 11, scale: 8
|
|
93
|
+
'amount:decimal{10,2}' # precision: 10, scale: 2
|
|
94
|
+
'latitude:decimal?{11,8}' # nullable with precision
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Note**: For default values on other types (boolean, integer, etc.), edit the migration manually.
|
|
98
|
+
|
|
99
|
+
### References/Associations
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
company:belongs_to # Required foreign key
|
|
103
|
+
'parent:belongs_to?' # Nullable (null: true + optional: true)
|
|
104
|
+
user:references # Same as belongs_to
|
|
105
|
+
blogging/post:belongs_to # Cross-package reference
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Nullable references generate:
|
|
109
|
+
- Migration: `null: true`
|
|
110
|
+
- Model: `belongs_to :parent, optional: true`
|
|
111
|
+
|
|
112
|
+
### Index Types (third segment)
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
email:string:index # Regular index
|
|
116
|
+
email:string:uniq # Unique index
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Special Types
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
password_digest # has_secure_password
|
|
123
|
+
auth_token:token # has_secure_token (auto unique index)
|
|
124
|
+
content:rich_text # has_rich_text
|
|
125
|
+
avatar:attachment # has_one_attached
|
|
126
|
+
photos:attachments # has_many_attached
|
|
127
|
+
price_cents:integer # has_cents (money field)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Token fields automatically get a unique index in the migration.
|
|
131
|
+
|
|
132
|
+
## Generator Options
|
|
133
|
+
|
|
134
|
+
- `--dest=DESTINATION` - Target destination (**always required** to avoid prompts)
|
|
135
|
+
- `main_app` for main application resources
|
|
136
|
+
- `package_name` for feature package resources
|
|
137
|
+
- `--no-model` - Skip model generation (keeps existing model)
|
|
138
|
+
- `--no-migration` - Skip migration generation (use with `--no-model` for existing models)
|
|
139
|
+
|
|
140
|
+
## What Gets Generated
|
|
141
|
+
|
|
142
|
+
For **main_app** resources:
|
|
143
|
+
1. **Model** - `app/models/model_name.rb`
|
|
144
|
+
2. **Migration** - `db/migrate/xxx_create_model_names.rb`
|
|
145
|
+
3. **Controller** - `app/controllers/model_names_controller.rb`
|
|
146
|
+
4. **Policy** - `app/policies/model_name_policy.rb`
|
|
147
|
+
5. **Definition** - `app/definitions/model_name_definition.rb`
|
|
148
|
+
|
|
149
|
+
For **packaged** resources:
|
|
150
|
+
1. **Model** - `app/models/package_name/model_name.rb`
|
|
151
|
+
2. **Migration** - `db/migrate/xxx_create_package_name_model_names.rb`
|
|
152
|
+
3. **Controller** - `packages/package_name/app/controllers/package_name/model_names_controller.rb`
|
|
153
|
+
4. **Policy** - `packages/package_name/app/policies/package_name/model_name_policy.rb`
|
|
154
|
+
5. **Definition** - `packages/package_name/app/definitions/package_name/model_name_definition.rb`
|
|
155
|
+
|
|
156
|
+
## Migration Customizations
|
|
157
|
+
|
|
158
|
+
The generator creates basic migrations. **Always review and customize** the migration before running:
|
|
159
|
+
|
|
160
|
+
### Inline Indexes (preferred)
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
create_table :model_names do |t|
|
|
164
|
+
t.belongs_to :parent, null: false, foreign_key: true
|
|
165
|
+
t.string :name, null: false
|
|
166
|
+
|
|
167
|
+
t.timestamps
|
|
168
|
+
|
|
169
|
+
t.index :name
|
|
170
|
+
t.index [:parent_id, :name], unique: true
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Cascade Delete
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
t.belongs_to :parent, null: false, foreign_key: {on_delete: :cascade}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Default Values
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
t.boolean :is_active, default: true
|
|
184
|
+
t.integer :status, default: 0
|
|
185
|
+
t.integer :count, null: true, default: 0
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Examples
|
|
189
|
+
|
|
190
|
+
### Main App Resource
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
rails g pu:res:scaffold Post \
|
|
194
|
+
user:belongs_to \
|
|
195
|
+
title:string \
|
|
196
|
+
'content:text?' \
|
|
197
|
+
'published_at:datetime?' \
|
|
198
|
+
--dest=main_app
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Resource with Precision and Indexes
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
rails g pu:res:scaffold Property \
|
|
205
|
+
company:belongs_to \
|
|
206
|
+
code:string:uniq \
|
|
207
|
+
'latitude:decimal{11,8}' \
|
|
208
|
+
'longitude:decimal?{11,8}' \
|
|
209
|
+
'value:decimal?{15,2}' \
|
|
210
|
+
'notes:text?' \
|
|
211
|
+
--dest=main_app
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Optional Association
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
rails g pu:res:scaffold Comment \
|
|
218
|
+
user:belongs_to \
|
|
219
|
+
'parent:belongs_to?' \
|
|
220
|
+
body:text \
|
|
221
|
+
--dest=blogging
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Cross-Package Reference
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
rails g pu:res:scaffold Comment \
|
|
228
|
+
user:belongs_to \
|
|
229
|
+
blogging/post:belongs_to \
|
|
230
|
+
body:text \
|
|
231
|
+
--dest=comments
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## After Generation
|
|
235
|
+
|
|
236
|
+
1. **Review and customize the migration** (add cascade delete, defaults, composite indexes)
|
|
237
|
+
2. Run `rails db:migrate`
|
|
238
|
+
3. Connect resource to portal: `rails g pu:res:conn Post --dest=admin_portal`
|
|
239
|
+
4. Customize policy permissions as needed
|
|
240
|
+
5. Add definition customizations for UI behavior
|