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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +85 -102
  3. data/.claude/skills/plutonium-app/SKILL.md +572 -0
  4. data/.claude/skills/plutonium-auth/SKILL.md +163 -300
  5. data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
  6. data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
  7. data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
  8. data/.claude/skills/plutonium-testing/SKILL.md +6 -5
  9. data/.claude/skills/plutonium-ui/SKILL.md +900 -0
  10. data/CHANGELOG.md +27 -2
  11. data/Rakefile +2 -1
  12. data/app/assets/plutonium.css +1 -11
  13. data/app/assets/plutonium.js +1009 -1214
  14. data/app/assets/plutonium.js.map +3 -3
  15. data/app/assets/plutonium.min.js +52 -51
  16. data/app/assets/plutonium.min.js.map +3 -3
  17. data/docs/.vitepress/config.ts +37 -27
  18. data/docs/getting-started/index.md +22 -29
  19. data/docs/getting-started/installation.md +37 -80
  20. data/docs/getting-started/tutorial/index.md +4 -5
  21. data/docs/guides/adding-resources.md +66 -377
  22. data/docs/guides/authentication.md +94 -463
  23. data/docs/guides/authorization.md +124 -370
  24. data/docs/guides/creating-packages.md +94 -296
  25. data/docs/guides/custom-actions.md +121 -441
  26. data/docs/guides/index.md +22 -42
  27. data/docs/guides/multi-tenancy.md +116 -187
  28. data/docs/guides/nested-resources.md +103 -431
  29. data/docs/guides/search-filtering.md +123 -240
  30. data/docs/guides/testing.md +5 -4
  31. data/docs/guides/theming.md +157 -407
  32. data/docs/guides/troubleshooting.md +5 -3
  33. data/docs/guides/user-invites.md +106 -425
  34. data/docs/guides/user-profile.md +76 -243
  35. data/docs/index.md +1 -1
  36. data/docs/reference/app/generators.md +517 -0
  37. data/docs/reference/app/index.md +158 -0
  38. data/docs/reference/app/packages.md +146 -0
  39. data/docs/reference/app/portals.md +377 -0
  40. data/docs/reference/auth/accounts.md +230 -0
  41. data/docs/reference/auth/index.md +88 -0
  42. data/docs/reference/auth/profile.md +185 -0
  43. data/docs/reference/behavior/controllers.md +395 -0
  44. data/docs/reference/behavior/index.md +22 -0
  45. data/docs/reference/behavior/interactions.md +341 -0
  46. data/docs/reference/behavior/policies.md +417 -0
  47. data/docs/reference/index.md +56 -49
  48. data/docs/reference/resource/actions.md +423 -0
  49. data/docs/reference/resource/definition.md +508 -0
  50. data/docs/reference/resource/index.md +50 -0
  51. data/docs/reference/resource/model.md +348 -0
  52. data/docs/reference/resource/query.md +305 -0
  53. data/docs/reference/tenancy/entity-scoping.md +361 -0
  54. data/docs/reference/tenancy/index.md +36 -0
  55. data/docs/reference/tenancy/invites.md +393 -0
  56. data/docs/reference/tenancy/nested-resources.md +267 -0
  57. data/docs/reference/testing/index.md +287 -0
  58. data/docs/reference/ui/assets.md +400 -0
  59. data/docs/reference/ui/components.md +165 -0
  60. data/docs/reference/ui/displays.md +104 -0
  61. data/docs/reference/ui/forms.md +284 -0
  62. data/docs/reference/ui/index.md +30 -0
  63. data/docs/reference/ui/layouts.md +106 -0
  64. data/docs/reference/ui/pages.md +189 -0
  65. data/docs/reference/ui/tables.md +117 -0
  66. data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
  67. data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
  68. data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
  69. data/gemfiles/rails_7.gemfile.lock +1 -1
  70. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  71. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  72. data/lib/generators/pu/core/update/update_generator.rb +0 -20
  73. data/lib/generators/pu/invites/install_generator.rb +1 -0
  74. data/lib/plutonium/definition/base.rb +1 -1
  75. data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
  76. data/lib/plutonium/helpers/turbo_helper.rb +11 -0
  77. data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
  78. data/lib/plutonium/resource/controller.rb +1 -0
  79. data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
  80. data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
  81. data/lib/plutonium/resource/policy.rb +7 -0
  82. data/lib/plutonium/routing/mapper_extensions.rb +15 -0
  83. data/lib/plutonium/ui/component/methods.rb +4 -0
  84. data/lib/plutonium/ui/form/base.rb +6 -2
  85. data/lib/plutonium/ui/form/components/json.rb +58 -0
  86. data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
  87. data/lib/plutonium/ui/form/components/secure_association.rb +98 -22
  88. data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
  89. data/lib/plutonium/ui/form/resource.rb +0 -4
  90. data/lib/plutonium/ui/grid/resource.rb +1 -1
  91. data/lib/plutonium/ui/layout/base.rb +1 -0
  92. data/lib/plutonium/ui/page/base.rb +0 -7
  93. data/lib/plutonium/ui/page/index.rb +4 -4
  94. data/lib/plutonium/ui/table/resource.rb +1 -1
  95. data/lib/plutonium/version.rb +1 -1
  96. data/lib/plutonium.rb +8 -0
  97. data/lib/tasks/release.rake +15 -1
  98. data/package.json +10 -10
  99. data/src/css/slim_select.css +4 -0
  100. data/src/js/controllers/slim_select_controller.js +61 -0
  101. data/src/js/turbo/turbo_actions.js +33 -0
  102. data/yarn.lock +553 -543
  103. metadata +44 -33
  104. data/.claude/skills/plutonium-assets/SKILL.md +0 -512
  105. data/.claude/skills/plutonium-controller/SKILL.md +0 -396
  106. data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
  107. data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
  108. data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
  109. data/.claude/skills/plutonium-forms/SKILL.md +0 -465
  110. data/.claude/skills/plutonium-installation/SKILL.md +0 -331
  111. data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
  112. data/.claude/skills/plutonium-invites/SKILL.md +0 -408
  113. data/.claude/skills/plutonium-model/SKILL.md +0 -440
  114. data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
  115. data/.claude/skills/plutonium-package/SKILL.md +0 -198
  116. data/.claude/skills/plutonium-policy/SKILL.md +0 -456
  117. data/.claude/skills/plutonium-portal/SKILL.md +0 -410
  118. data/.claude/skills/plutonium-views/SKILL.md +0 -651
  119. data/docs/reference/assets/index.md +0 -496
  120. data/docs/reference/controller/index.md +0 -412
  121. data/docs/reference/definition/actions.md +0 -462
  122. data/docs/reference/definition/fields.md +0 -383
  123. data/docs/reference/definition/index.md +0 -326
  124. data/docs/reference/definition/query.md +0 -351
  125. data/docs/reference/generators/index.md +0 -648
  126. data/docs/reference/interaction/index.md +0 -449
  127. data/docs/reference/model/features.md +0 -248
  128. data/docs/reference/model/index.md +0 -218
  129. data/docs/reference/policy/index.md +0 -456
  130. data/docs/reference/portal/index.md +0 -379
  131. data/docs/reference/views/forms.md +0 -411
  132. data/docs/reference/views/index.md +0 -544
@@ -1,360 +0,0 @@
1
- ---
2
- name: plutonium-nested-resources
3
- description: Use BEFORE configuring parent/child resource relationships, nested routes, or scoped URL generation with resource_url_for(parent:).
4
- ---
5
-
6
- # Nested Resources
7
-
8
- ## 🚨 Critical (read first)
9
- - **Use `pu:res:scaffold` + `pu:res:conn`** for both parent and child. Nested routes are generated from the `belongs_to` + association on the parent — no manual route wiring.
10
- - **Parent scoping beats entity scoping.** When a parent is present, `default_relation_scope` scopes via the parent, not via `entity_scope`. Don't double-scope in the policy.
11
- - **Plutonium supports one level of nesting.** Grandparent → parent → child nested routes are NOT supported. Use top-level routes for deeper relationships.
12
- - **Named custom routes only.** When adding member/collection routes on a nested resource, always pass `as:` — otherwise `resource_url_for` will fail.
13
- - **Related skills:** `plutonium-entity-scoping` (how parent scoping interacts with entity scoping), `plutonium-policy` (parent scoping in `relation_scope`), `plutonium-controller` (presentation hooks), `plutonium-portal` (route registration).
14
-
15
- **Always use generators** to create both parent and child resources, then connect them to portals:
16
- ```bash
17
- rails g pu:res:scaffold Company name:string --dest=main_app
18
- rails g pu:res:scaffold Property company:belongs_to name:string --dest=main_app
19
- rails g pu:res:conn Company Property --dest=admin_portal
20
- ```
21
-
22
- Plutonium automatically creates nested routes for `has_many` and `has_one` associations, scopes queries to the parent, and handles URL generation.
23
-
24
- ## How It Works
25
-
26
- When you register resources with parent-child relationships:
27
-
28
- ```ruby
29
- # In portal routes
30
- register_resource ::Company
31
- register_resource ::Property # has belongs_to :company
32
- register_resource ::CompanyProfile # has belongs_to :company (has_one on Company)
33
- ```
34
-
35
- Plutonium automatically creates nested routes with a `nested_` prefix:
36
- - `/companies/:company_id/nested_properties` - Properties scoped to company (has_many)
37
- - `/companies/:company_id/nested_properties/new` - New property for company
38
- - `/companies/:company_id/nested_properties/:id` - Property in company context
39
- - `/companies/:company_id/nested_company_profile` - Singular profile (has_one)
40
- - `/companies/:company_id/nested_company_profile/new` - New profile for company
41
-
42
- The `nested_` prefix prevents route conflicts when the same resource is registered both as a top-level and nested resource.
43
-
44
- ## Automatic Behavior
45
-
46
- When accessing nested routes, Plutonium automatically:
47
-
48
- 1. **Resolves the parent** via `current_parent`
49
- 2. **Scopes queries** to only show records belonging to parent
50
- 3. **Assigns parent** to new records on create
51
- 4. **Hides parent field** in forms (already determined by URL)
52
- 5. **Authorizes parent access** before proceeding
53
-
54
- ## Controller Methods
55
-
56
- ### current_parent
57
-
58
- Returns the parent record from the URL:
59
-
60
- ```ruby
61
- # URL: /companies/123/properties
62
- current_parent # => Company.find(123)
63
- ```
64
-
65
- ### parent_route_param
66
-
67
- The URL parameter containing the parent ID:
68
-
69
- ```ruby
70
- parent_route_param # => :company_id
71
- ```
72
-
73
- ### parent_input_param
74
-
75
- The association name on the child model:
76
-
77
- ```ruby
78
- parent_input_param # => :company
79
- ```
80
-
81
- ## Presentation Hooks
82
-
83
- Control whether parent field appears in views/forms:
84
-
85
- ```ruby
86
- class PropertiesController < ResourceController
87
- private
88
-
89
- # Show parent field in displays (default: false)
90
- def present_parent?
91
- true
92
- end
93
-
94
- # Allow changing parent in forms (default: same as present_parent?)
95
- def submit_parent?
96
- false # Parent is set from URL, don't allow changing
97
- end
98
- end
99
- ```
100
-
101
- ## Query Scoping
102
-
103
- Collections are automatically scoped to the parent via policies. The policy receives `parent` and `parent_association` context:
104
-
105
- ```ruby
106
- class PropertyPolicy < ResourcePolicy
107
- # parent: the parent record (e.g., Company instance)
108
- # parent_association: the association name (e.g., :properties)
109
-
110
- relation_scope do |relation|
111
- relation = super(relation) # Applies parent scoping automatically
112
- relation
113
- end
114
- end
115
- ```
116
-
117
- ### How Parent Scoping Works
118
-
119
- For **has_many** associations, scoping uses the association directly:
120
- ```ruby
121
- # parent.properties => Company#properties
122
- parent.send(parent_association)
123
- ```
124
-
125
- For **has_one** associations, scoping uses a where clause:
126
- ```ruby
127
- # Property.where(company_id: company.id) with limit
128
- relation.where(foreign_key => parent.id)
129
- ```
130
-
131
- ### Parent vs Entity Scope
132
-
133
- When a parent is present, parent scoping takes precedence over entity scoping:
134
-
135
- ```ruby
136
- # With parent: scopes via parent association
137
- # Without parent: falls back to entity_scope (multi-tenancy)
138
- ```
139
-
140
- This prevents double-scoping - the parent was already authorized and entity-scoped during its own authorization.
141
-
142
- ### Custom Association Scope
143
-
144
- For complex relationships, define a custom scope:
145
-
146
- ```ruby
147
- class Property < ResourceRecord
148
- scope :associated_with_organization, ->(org) {
149
- joins(:company).where(companies: { organization_id: org.id })
150
- }
151
- end
152
- ```
153
-
154
- ## URL Generation
155
-
156
- Use `resource_url_for` with the `parent:` option:
157
-
158
- ```ruby
159
- # Child collection (has_many)
160
- resource_url_for(Property, parent: company)
161
- # => /companies/123/nested_properties
162
-
163
- # Child record
164
- resource_url_for(property, parent: company)
165
- # => /companies/123/nested_properties/456
166
-
167
- # New child form
168
- resource_url_for(Property, action: :new, parent: company)
169
- # => /companies/123/nested_properties/new
170
-
171
- # Edit child
172
- resource_url_for(property, action: :edit, parent: company)
173
- # => /companies/123/nested_properties/456/edit
174
-
175
- # Singular resource (has_one)
176
- resource_url_for(company_profile, parent: company)
177
- # => /companies/123/nested_company_profile
178
-
179
- resource_url_for(CompanyProfile, action: :new, parent: company)
180
- # => /companies/123/nested_company_profile/new
181
-
182
- # Interactions (composes with parent — see plutonium-interaction skill)
183
- resource_url_for(property, parent: company, interaction: :archive)
184
- # => /companies/123/nested_properties/456/record_actions/archive
185
-
186
- resource_url_for(Property, parent: company, interaction: :import)
187
- # => /companies/123/nested_properties/resource_actions/import
188
-
189
- resource_url_for(Property, parent: company, interaction: :bulk_delete, ids: [1, 2])
190
- # => /companies/123/nested_properties/bulk_actions/bulk_delete?ids[]=1&ids[]=2
191
- ```
192
-
193
- ### Cross-Package URL Generation
194
-
195
- Generate URLs for resources in a different package:
196
-
197
- ```ruby
198
- # From AdminPortal, generate URL to CustomerPortal resource
199
- resource_url_for(property, parent: company, package: CustomerPortal)
200
- ```
201
-
202
- ## Association Panels
203
-
204
- On the parent's show page, child resources are displayed via association panels:
205
-
206
- ```ruby
207
- class CompanyPolicy < ResourcePolicy
208
- def permitted_associations
209
- %i[properties contacts] # Shows panels for these
210
- end
211
- end
212
- ```
213
-
214
- The panel loads children via the nested route automatically.
215
-
216
- ## Authorization
217
-
218
- ### Parent Authorization
219
-
220
- The parent is authorized for `:read?` before `current_parent` returns:
221
-
222
- ```ruby
223
- def current_parent
224
- # ... resolution logic ...
225
- authorize! parent, to: :read?
226
- parent
227
- end
228
- ```
229
-
230
- ### Policy Context
231
-
232
- The parent is passed to child policies as `entity_scope`:
233
-
234
- ```ruby
235
- class PropertyPolicy < ResourcePolicy
236
- def create?
237
- # entity_scope is the parent company
238
- entity_scope.present? && user.member_of?(entity_scope)
239
- end
240
-
241
- def read?
242
- entity_scope.present? && record.company == entity_scope
243
- end
244
- end
245
- ```
246
-
247
- ## Parameter Handling
248
-
249
- Parent is automatically injected into resource params:
250
-
251
- ```ruby
252
- # When creating a property under /companies/123/properties
253
- resource_params
254
- # => { name: "...", company: <Company:123>, company_id: 123 }
255
- ```
256
-
257
- You don't need to include hidden fields for the parent in forms.
258
-
259
- ## has_one Associations
260
-
261
- Plutonium supports both `has_many` and `has_one` associations:
262
-
263
- ```ruby
264
- class Company < ResourceRecord
265
- has_many :properties # Plural routes
266
- has_one :company_profile # Singular routes
267
- end
268
- ```
269
-
270
- Routes generated:
271
- - `has_many`: `/companies/:id/nested_properties` (plural, with `:id` param)
272
- - `has_one`: `/companies/:id/nested_company_profile` (singular, no `:id` param)
273
-
274
- For has_one associations:
275
- - Index redirects to show (or new if no record exists)
276
- - Only one record can exist per parent
277
- - Forms don't show parent field (determined by URL)
278
-
279
- ## Nesting Limitations
280
-
281
- Plutonium supports **one level of nesting**:
282
-
283
- - ✅ `/companies/:company_id/nested_properties` (parent → child)
284
- - ❌ `/companies/:company_id/nested_properties/:property_id/nested_units` (grandparent → parent → child)
285
-
286
- ## Common Patterns
287
-
288
- ### Scoped Uniqueness
289
-
290
- Validate uniqueness within parent:
291
-
292
- ```ruby
293
- class Property < ResourceRecord
294
- belongs_to :company
295
- validates :code, uniqueness: { scope: :company_id }
296
- end
297
- ```
298
-
299
- ### Conditional Parent Display
300
-
301
- Show parent only in certain contexts:
302
-
303
- ```ruby
304
- class PropertiesController < ResourceController
305
- private
306
-
307
- def present_parent?
308
- # Show parent when accessed standalone, hide when nested
309
- current_parent.nil?
310
- end
311
- end
312
- ```
313
-
314
- ### Custom Parent Resolution
315
-
316
- Override parent lookup:
317
-
318
- ```ruby
319
- class PropertiesController < ResourceController
320
- private
321
-
322
- def current_parent
323
- @current_parent ||= Company.friendly.find(params[:company_id])
324
- end
325
- end
326
- ```
327
-
328
- ### Breadcrumbs
329
-
330
- Breadcrumbs automatically include the parent:
331
-
332
- ```
333
- Companies > Acme Corp > Properties > Property #123
334
- ```
335
-
336
- ## Route Registration with Custom Routes
337
-
338
- Add custom member/collection routes to nested resources:
339
-
340
- ```ruby
341
- register_resource ::Property do
342
- member do
343
- get :analytics, as: :analytics
344
- post :archive, as: :archive
345
- end
346
- end
347
- ```
348
-
349
- **Important:** Always use the `as:` option to name custom routes. This ensures `resource_url_for` can generate correct URLs for nested resources. Without named routes, URL generation will fail.
350
-
351
- Generates nested routes:
352
- - `/companies/:company_id/nested_properties/:id/analytics`
353
- - `/companies/:company_id/nested_properties/:id/archive`
354
-
355
- ## Related Skills
356
-
357
- - `plutonium-portal` - Route registration
358
- - `plutonium-policy` - Authorization and scoping
359
- - `plutonium-controller` - Presentation hooks
360
- - `plutonium-model` - associated_with scope
@@ -1,198 +0,0 @@
1
- ---
2
- name: plutonium-package
3
- description: Use BEFORE creating a feature package or portal package via pu:pkg:package / pu:pkg:portal, or organizing a Plutonium app into modular engines.
4
- ---
5
-
6
- # Plutonium Packages
7
-
8
- ## 🚨 Critical (read first)
9
- - **Use the generators.** `pu:pkg:package` for feature packages, `pu:pkg:portal` for portal packages — never hand-write engine files or directory structures.
10
- - **Feature vs portal is a hard split.** Feature packages hold models/policies/definitions/interactions; portal packages hold controllers/views/routes/auth. Don't mix.
11
- - **Package classes are auto-namespaced** (`packages/blogging/app/models/blogging/post.rb` → `Blogging::Post`). Don't fight the namespacing.
12
- - **Cross-package resource references** use the full namespace: `rails g pu:res:conn Blogging::Post --dest=admin_portal`.
13
- - **Related skills:** `plutonium-portal` (portal-specific features), `plutonium-create-resource` (creating resources in packages), `plutonium-installation` (package loading).
14
-
15
- Packages are specialized Rails engines for organizing code. There are two types:
16
-
17
- | Type | Purpose | Generator |
18
- |------|---------|-----------|
19
- | **Feature** | Business logic (models, policies, interactions) | `rails g pu:pkg:package NAME` |
20
- | **Portal** | Web interface (controllers, views, auth) | `rails g pu:pkg:portal NAME` |
21
-
22
- ## Feature Packages
23
-
24
- Contain domain logic without web interface:
25
-
26
- ```bash
27
- rails g pu:pkg:package blogging
28
- ```
29
-
30
- ### Structure
31
-
32
- ```
33
- packages/blogging/
34
- ├── app/
35
- │ ├── models/blogging/
36
- │ │ ├── post.rb
37
- │ │ └── comment.rb
38
- │ ├── definitions/blogging/
39
- │ │ ├── post_definition.rb
40
- │ │ └── comment_definition.rb
41
- │ ├── policies/blogging/
42
- │ │ ├── post_policy.rb
43
- │ │ └── comment_policy.rb
44
- │ └── interactions/blogging/
45
- │ └── publish_post_interaction.rb
46
- ├── db/migrate/
47
- └── lib/
48
- └── engine.rb
49
- ```
50
-
51
- ### Engine
52
-
53
- ```ruby
54
- module Blogging
55
- class Engine < Rails::Engine
56
- include Plutonium::Package::Engine
57
- end
58
- end
59
- ```
60
-
61
- ### Namespacing
62
-
63
- All classes are auto-namespaced:
64
- - `app/models/blogging/post.rb` → `Blogging::Post`
65
- - `app/policies/blogging/post_policy.rb` → `Blogging::PostPolicy`
66
-
67
- ## Portal Packages
68
-
69
- Provide web interfaces for specific user types:
70
-
71
- ```bash
72
- rails g pu:pkg:portal admin
73
- rails g pu:pkg:portal dashboard
74
- ```
75
-
76
- ### Structure
77
-
78
- ```
79
- packages/admin_portal/
80
- ├── app/
81
- │ ├── controllers/admin_portal/
82
- │ │ ├── concerns/controller.rb
83
- │ │ ├── dashboard_controller.rb
84
- │ │ ├── plutonium_controller.rb
85
- │ │ └── resource_controller.rb
86
- │ ├── definitions/admin_portal/ # Portal-specific overrides
87
- │ ├── policies/admin_portal/ # Portal-specific overrides
88
- │ └── views/
89
- │ └── layouts/admin_portal.html.erb
90
- ├── config/
91
- │ └── routes.rb
92
- └── lib/
93
- └── engine.rb
94
- ```
95
-
96
- ### Engine
97
-
98
- ```ruby
99
- module AdminPortal
100
- class Engine < Rails::Engine
101
- include Plutonium::Portal::Engine
102
-
103
- config.after_initialize do
104
- # Optional: multi-tenancy
105
- scope_to_entity Organization, strategy: :path
106
- end
107
- end
108
- end
109
- ```
110
-
111
- See `plutonium-portal` skill for portal-specific features.
112
-
113
- ## Package Loading
114
-
115
- Packages are loaded via `config/packages.rb`:
116
-
117
- ```ruby
118
- # config/packages.rb (generated during install)
119
- Dir.glob(File.expand_path("../packages/**/lib/engine.rb", __dir__)) do |package|
120
- load package
121
- end
122
- ```
123
-
124
- This is required in `config/application.rb`.
125
-
126
- ## Creating Resources in Packages
127
-
128
- ```bash
129
- # In main app
130
- rails g pu:res:scaffold Post title:string --dest=main_app
131
-
132
- # In feature package
133
- rails g pu:res:scaffold Blogging::Post title:string --dest=blogging
134
- ```
135
-
136
- ## Connecting Resources to Portals
137
-
138
- Resources must be connected to portals to be accessible:
139
-
140
- ```bash
141
- rails g pu:res:conn Post --dest=admin_portal
142
- rails g pu:res:conn Blogging::Post --dest=admin_portal
143
- ```
144
-
145
- This creates:
146
- - Portal-specific controller
147
- - Portal-specific policy (optional)
148
- - Portal-specific definition (optional)
149
- - Route registration
150
-
151
- ## When to Use Each Type
152
-
153
- ### Feature Packages
154
-
155
- Use for:
156
- - Domain-specific models and logic
157
- - Reusable business functionality
158
- - Shared code across portals
159
-
160
- Examples: `blogging`, `billing`, `inventory`, `user_management`
161
-
162
- ### Portal Packages
163
-
164
- Use for:
165
- - User-facing interfaces
166
- - Role-specific access (admin, customer, public)
167
- - Different authentication requirements
168
-
169
- Examples: `admin_portal`, `dashboard_portal`, `public_portal`, `api_portal`
170
-
171
- ## Typical Architecture
172
-
173
- ```
174
- packages/
175
- ├── blogging/ # Feature: blog functionality
176
- │ └── models, definitions, policies
177
- ├── billing/ # Feature: payment/invoicing
178
- │ └── models, definitions, policies
179
- ├── admin_portal/ # Portal: admin interface
180
- │ └── controllers, views, routes
181
- └── dashboard_portal/ # Portal: user dashboard
182
- └── controllers, views, routes
183
- ```
184
-
185
- ## Migration Integration
186
-
187
- Package migrations are automatically integrated:
188
-
189
- ```bash
190
- rails db:migrate # Runs migrations from all packages
191
- ```
192
-
193
- ## Related Skills
194
-
195
- - `plutonium-portal` - Portal-specific features (auth, entity scoping, routes)
196
- - `plutonium` - Resource architecture overview
197
- - `plutonium-portal` - Connecting resources to portals
198
- - `plutonium-create-resource` - Creating resources