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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/# Plutonium: The pre-alpha demo.md +4 -2
  3. data/.claude/skills/assets/SKILL.md +416 -0
  4. data/.claude/skills/connect-resource/SKILL.md +112 -0
  5. data/.claude/skills/controller/SKILL.md +302 -0
  6. data/.claude/skills/create-resource/SKILL.md +240 -0
  7. data/.claude/skills/definition/SKILL.md +218 -0
  8. data/.claude/skills/definition-actions/SKILL.md +386 -0
  9. data/.claude/skills/definition-fields/SKILL.md +474 -0
  10. data/.claude/skills/definition-query/SKILL.md +334 -0
  11. data/.claude/skills/forms/SKILL.md +439 -0
  12. data/.claude/skills/installation/SKILL.md +300 -0
  13. data/.claude/skills/interaction/SKILL.md +382 -0
  14. data/.claude/skills/model/SKILL.md +267 -0
  15. data/.claude/skills/model-features/SKILL.md +286 -0
  16. data/.claude/skills/nested-resources/SKILL.md +274 -0
  17. data/.claude/skills/package/SKILL.md +191 -0
  18. data/.claude/skills/policy/SKILL.md +352 -0
  19. data/.claude/skills/portal/SKILL.md +400 -0
  20. data/.claude/skills/resource/SKILL.md +281 -0
  21. data/.claude/skills/rodauth/SKILL.md +452 -0
  22. data/.claude/skills/views/SKILL.md +563 -0
  23. data/Appraisals +46 -4
  24. data/CHANGELOG.md +32 -1
  25. data/app/assets/plutonium.css +2 -2
  26. data/config/brakeman.ignore +239 -0
  27. data/config/initializers/action_policy.rb +1 -1
  28. data/docs/.vitepress/config.ts +132 -47
  29. data/docs/concepts/architecture.md +226 -0
  30. data/docs/concepts/auto-detection.md +254 -0
  31. data/docs/concepts/index.md +61 -0
  32. data/docs/concepts/packages-portals.md +304 -0
  33. data/docs/concepts/resources.md +224 -0
  34. data/docs/cookbook/blog.md +412 -0
  35. data/docs/cookbook/index.md +289 -0
  36. data/docs/cookbook/saas.md +481 -0
  37. data/docs/getting-started/index.md +56 -0
  38. data/docs/getting-started/installation.md +146 -0
  39. data/docs/getting-started/tutorial/01-setup.md +118 -0
  40. data/docs/getting-started/tutorial/02-first-resource.md +180 -0
  41. data/docs/getting-started/tutorial/03-authentication.md +246 -0
  42. data/docs/getting-started/tutorial/04-authorization.md +170 -0
  43. data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
  44. data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
  45. data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
  46. data/docs/getting-started/tutorial/index.md +64 -0
  47. data/docs/guides/adding-resources.md +420 -0
  48. data/docs/guides/authentication.md +551 -0
  49. data/docs/guides/authorization.md +468 -0
  50. data/docs/guides/creating-packages.md +380 -0
  51. data/docs/guides/custom-actions.md +523 -0
  52. data/docs/guides/index.md +45 -0
  53. data/docs/guides/multi-tenancy.md +302 -0
  54. data/docs/guides/nested-resources.md +411 -0
  55. data/docs/guides/search-filtering.md +266 -0
  56. data/docs/guides/theming.md +321 -0
  57. data/docs/index.md +67 -26
  58. data/docs/public/CLAUDE.md +64 -21
  59. data/docs/reference/assets/index.md +496 -0
  60. data/docs/reference/controller/index.md +363 -0
  61. data/docs/reference/definition/actions.md +400 -0
  62. data/docs/reference/definition/fields.md +350 -0
  63. data/docs/reference/definition/index.md +252 -0
  64. data/docs/reference/definition/query.md +342 -0
  65. data/docs/reference/generators/index.md +469 -0
  66. data/docs/reference/index.md +49 -0
  67. data/docs/reference/interaction/index.md +445 -0
  68. data/docs/reference/model/features.md +248 -0
  69. data/docs/reference/model/index.md +219 -0
  70. data/docs/reference/policy/index.md +385 -0
  71. data/docs/reference/portal/index.md +382 -0
  72. data/docs/reference/views/forms.md +396 -0
  73. data/docs/reference/views/index.md +479 -0
  74. data/gemfiles/rails_7.gemfile +9 -2
  75. data/gemfiles/rails_7.gemfile.lock +146 -111
  76. data/gemfiles/rails_8.0.gemfile +20 -0
  77. data/gemfiles/rails_8.0.gemfile.lock +417 -0
  78. data/gemfiles/rails_8.1.gemfile +20 -0
  79. data/gemfiles/rails_8.1.gemfile.lock +419 -0
  80. data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
  81. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
  82. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
  83. data/lib/generators/pu/pkg/portal/USAGE +65 -0
  84. data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
  85. data/lib/generators/pu/res/conn/USAGE +71 -0
  86. data/lib/generators/pu/res/model/USAGE +106 -110
  87. data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
  88. data/lib/generators/pu/res/scaffold/USAGE +85 -0
  89. data/lib/generators/pu/rodauth/install_generator.rb +2 -6
  90. data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
  91. data/lib/generators/pu/skills/sync/USAGE +14 -0
  92. data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
  93. data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
  94. data/lib/plutonium/core/controller.rb +2 -2
  95. data/lib/plutonium/interaction/base.rb +1 -0
  96. data/lib/plutonium/package/engine.rb +2 -2
  97. data/lib/plutonium/query/adhoc_block.rb +6 -2
  98. data/lib/plutonium/query/model_scope.rb +1 -1
  99. data/lib/plutonium/railtie.rb +4 -0
  100. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
  101. data/lib/plutonium/resource/query_object.rb +38 -8
  102. data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
  103. data/lib/plutonium/version.rb +1 -1
  104. data/lib/tasks/release.rake +19 -4
  105. data/package.json +1 -1
  106. metadata +76 -39
  107. data/brakeman.ignore +0 -28
  108. data/docs/api-examples.md +0 -49
  109. data/docs/guide/claude-code-guide.md +0 -74
  110. data/docs/guide/deep-dive/authorization.md +0 -189
  111. data/docs/guide/deep-dive/multitenancy.md +0 -256
  112. data/docs/guide/deep-dive/resources.md +0 -390
  113. data/docs/guide/getting-started/01-installation.md +0 -165
  114. data/docs/guide/index.md +0 -28
  115. data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
  116. data/docs/guide/introduction/02-core-concepts.md +0 -440
  117. data/docs/guide/tutorial/01-project-setup.md +0 -75
  118. data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
  119. data/docs/guide/tutorial/03-defining-resources.md +0 -90
  120. data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
  121. data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
  122. data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
  123. data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
  124. data/docs/markdown-examples.md +0 -85
  125. data/docs/modules/action.md +0 -244
  126. data/docs/modules/authentication.md +0 -236
  127. data/docs/modules/configuration.md +0 -599
  128. data/docs/modules/controller.md +0 -443
  129. data/docs/modules/core.md +0 -316
  130. data/docs/modules/definition.md +0 -1308
  131. data/docs/modules/display.md +0 -759
  132. data/docs/modules/form.md +0 -495
  133. data/docs/modules/generator.md +0 -400
  134. data/docs/modules/index.md +0 -167
  135. data/docs/modules/interaction.md +0 -642
  136. data/docs/modules/package.md +0 -151
  137. data/docs/modules/policy.md +0 -176
  138. data/docs/modules/portal.md +0 -710
  139. data/docs/modules/query.md +0 -297
  140. data/docs/modules/resource_record.md +0 -618
  141. data/docs/modules/routing.md +0 -690
  142. data/docs/modules/table.md +0 -301
  143. data/docs/modules/ui.md +0 -631
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09404b41e07186fad2cd1b8cac7d13dcf24365020bd421fbe1f2991a9368ac73'
4
- data.tar.gz: dfc8ca35be7e90674dd76b0a2bef5f3815adb01c36dd0bc287fe29707710f576
3
+ metadata.gz: 38de4c2a97493b6e03b185ab05f6c6990df33ad210d89612ce98ef392e6c0ae1
4
+ data.tar.gz: 1de78573407b4032d3f41fc249765028b24d8204f79d68816c4571ac55edf314
5
5
  SHA512:
6
- metadata.gz: 5732daa2516ac0120f244649d78a62a5824dc7ec976783cfc0b806b27a424372021935b099602f36348dd72a0825c994e579dbbccaae08f4e8241861bee3816c
7
- data.tar.gz: 95a474cc9f548b10c929ce170de2fc9e39886fe5f579620aa5748a2d62927c663ba270689d87345b9c75df1dd43f440ceebc61b0f840bd1d1e80d4fdb27083bb
6
+ metadata.gz: e5f97caf77d2dce2a6c8f3ac2735974a244b1387ab015ecf73ff27210bc7eb74a8c05971145f628c99cb481e55383ee8f62f42a41ac2f372597e9b3e43e4a1c5
7
+ data.tar.gz: 104fd85f23d698fdbbc6bbda701a5a22912516877761471028c93050260853f0a96461d0d80f609b0142f01155b6b5928b4403d654579baeaa9119b08258e1c8
@@ -43,8 +43,10 @@
43
43
  ```ruby
44
44
  # packages/dashboard_app/lib/engine.rb
45
45
 
46
- scope_to_entity User, strategy: :current_user
47
- # add directives above.
46
+ config.after_initialize do
47
+ scope_to_entity User, strategy: :current_user
48
+ # add directives above.
49
+ end
48
50
  ```
49
51
 
50
52
  - Demonstrate queries
@@ -0,0 +1,416 @@
1
+ ---
2
+ name: assets
3
+ description: Plutonium assets and theming - TailwindCSS configuration, custom styling, and component themes
4
+ ---
5
+
6
+ # Plutonium Assets & Theming
7
+
8
+ Plutonium uses TailwindCSS 4 for styling with a customizable theme system for components.
9
+
10
+ ## Asset Configuration
11
+
12
+ Configure assets in the initializer:
13
+
14
+ ```ruby
15
+ # config/initializers/plutonium.rb
16
+ Plutonium.configure do |config|
17
+ config.load_defaults 1.0
18
+
19
+ # Custom assets
20
+ config.assets.stylesheet = "application" # Your CSS file
21
+ config.assets.script = "application" # Your JS file
22
+ config.assets.logo = "my_logo.png" # Logo image
23
+ config.assets.favicon = "my_favicon.ico" # Favicon
24
+ end
25
+ ```
26
+
27
+ ## Setup Custom Assets
28
+
29
+ Run the assets generator to set up your own TailwindCSS build:
30
+
31
+ ```bash
32
+ rails generate pu:core:assets
33
+ ```
34
+
35
+ This:
36
+ 1. Installs required npm packages (`@radioactive-labs/plutonium`, TailwindCSS plugins)
37
+ 2. Creates `tailwind.config.js` that extends Plutonium's config
38
+ 3. Imports Plutonium CSS into your `application.tailwind.css`
39
+ 4. Registers Plutonium's Stimulus controllers
40
+ 5. Updates Plutonium config to use your assets
41
+
42
+ ## TailwindCSS Configuration
43
+
44
+ ### Generated Config
45
+
46
+ ```javascript
47
+ // tailwind.config.js
48
+ const { execSync } = require('child_process');
49
+ const plutoniumGemPath = execSync("bundle show plutonium").toString().trim();
50
+ const plutoniumTailwindConfig = require(`${plutoniumGemPath}/tailwind.options.js`)
51
+
52
+ module.exports = {
53
+ darkMode: plutoniumTailwindConfig.darkMode,
54
+ plugins: [
55
+ // Add your plugins here
56
+ ].concat(plutoniumTailwindConfig.plugins),
57
+ theme: plutoniumTailwindConfig.merge(
58
+ plutoniumTailwindConfig.theme,
59
+ {
60
+ // Your custom theme overrides
61
+ },
62
+ ),
63
+ content: [
64
+ `${__dirname}/app/**/*.{erb,haml,html,slim,rb}`,
65
+ `${__dirname}/app/javascript/**/*.js`,
66
+ `${__dirname}/packages/**/app/**/*.{erb,haml,html,slim,rb}`,
67
+ ].concat(plutoniumTailwindConfig.content),
68
+ }
69
+ ```
70
+
71
+ ### Customizing Colors
72
+
73
+ Override Plutonium's color palette:
74
+
75
+ ```javascript
76
+ // tailwind.config.js
77
+ theme: plutoniumTailwindConfig.merge(
78
+ plutoniumTailwindConfig.theme,
79
+ {
80
+ extend: {
81
+ colors: {
82
+ primary: {
83
+ 50: '#eff6ff',
84
+ 100: '#dbeafe',
85
+ 200: '#bfdbfe',
86
+ 300: '#93c5fd',
87
+ 400: '#60a5fa',
88
+ 500: '#3b82f6', // Your brand color
89
+ 600: '#2563eb',
90
+ 700: '#1d4ed8',
91
+ 800: '#1e40af',
92
+ 900: '#1e3a8a',
93
+ 950: '#172554',
94
+ },
95
+ secondary: {
96
+ // Your secondary palette
97
+ },
98
+ },
99
+ },
100
+ },
101
+ ),
102
+ ```
103
+
104
+ ### Default Color Palette
105
+
106
+ Plutonium includes these semantic colors:
107
+
108
+ | Color | Usage |
109
+ |-------|-------|
110
+ | `primary` | Primary brand color (turquoise by default) |
111
+ | `secondary` | Secondary color (navy by default) |
112
+ | `success` | Success states (green) |
113
+ | `info` | Informational states (blue) |
114
+ | `warning` | Warning states (amber) |
115
+ | `danger` | Error/danger states (red) |
116
+ | `accent` | Accent highlights (coral pink) |
117
+
118
+ ### Dark Mode
119
+
120
+ Plutonium uses `selector` strategy for dark mode:
121
+
122
+ ```javascript
123
+ darkMode: "selector"
124
+ ```
125
+
126
+ Toggle dark mode by adding/removing the `dark` class on `<html>`:
127
+
128
+ ```javascript
129
+ // Toggle dark mode
130
+ document.documentElement.classList.toggle('dark');
131
+ ```
132
+
133
+ Plutonium includes a color mode selector component that handles this automatically.
134
+
135
+ ## CSS Imports
136
+
137
+ ### Application Stylesheet
138
+
139
+ ```css
140
+ /* app/assets/stylesheets/application.tailwind.css */
141
+ @import "gem:plutonium/src/css/plutonium.css";
142
+
143
+ @import "tailwindcss";
144
+ @config '../../../tailwind.config.js';
145
+
146
+ /* Your custom styles */
147
+ ```
148
+
149
+ ### What Plutonium CSS Includes
150
+
151
+ - Core utility classes
152
+ - EasyMDE (markdown editor) styles
153
+ - Slim Select styles
154
+ - International telephone input styles
155
+ - Flatpickr (date picker) styles
156
+
157
+ ## Component Themes
158
+
159
+ Plutonium components use a theme system based on Phlexi. Each component type has a theme class with named style tokens.
160
+
161
+ ### Form Theme
162
+
163
+ ```ruby
164
+ class PostDefinition < ResourceDefinition
165
+ class Form < Form
166
+ class Theme < Plutonium::UI::Form::Theme
167
+ def self.theme
168
+ super.merge({
169
+ # Container
170
+ base: "bg-white dark:bg-gray-800 shadow-md rounded-lg p-6",
171
+ fields_wrapper: "grid grid-cols-2 gap-6",
172
+ actions_wrapper: "flex justify-end mt-6 space-x-2",
173
+
174
+ # Labels
175
+ label: "block mb-2 text-base font-bold",
176
+ invalid_label: "text-red-700 dark:text-red-500",
177
+ valid_label: "text-green-700 dark:text-green-500",
178
+ neutral_label: "text-gray-500 dark:text-gray-400",
179
+
180
+ # Inputs
181
+ input: "w-full p-2 border rounded-md shadow-sm",
182
+ invalid_input: "bg-red-50 border-red-500 text-red-900",
183
+ valid_input: "bg-green-50 border-green-500 text-green-900",
184
+ neutral_input: "border-gray-300 dark:border-gray-600",
185
+
186
+ # Hints & Errors
187
+ hint: "mt-2 text-sm text-gray-500",
188
+ error: "mt-2 text-sm text-red-600",
189
+
190
+ # Buttons
191
+ button: "px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700",
192
+ })
193
+ end
194
+ end
195
+ end
196
+ end
197
+ ```
198
+
199
+ ### Display Theme
200
+
201
+ ```ruby
202
+ class PostDefinition < ResourceDefinition
203
+ class Display < Display
204
+ class Theme < Plutonium::UI::Display::Theme
205
+ def self.theme
206
+ super.merge({
207
+ fields_wrapper: "grid grid-cols-3 gap-8",
208
+ label: "text-sm font-bold text-gray-500 mb-1",
209
+ string: "text-lg text-gray-900 dark:text-white",
210
+ link: "text-primary-600 hover:underline",
211
+ markdown: "prose dark:prose-invert max-w-none",
212
+ })
213
+ end
214
+ end
215
+ end
216
+ end
217
+ ```
218
+
219
+ ### Table Theme
220
+
221
+ ```ruby
222
+ class PostDefinition < ResourceDefinition
223
+ class Table < Table
224
+ class Theme < Plutonium::UI::Table::Theme
225
+ def self.theme
226
+ super.merge({
227
+ wrapper: "overflow-x-auto shadow-md rounded-lg",
228
+ base: "w-full text-sm text-gray-500",
229
+ header: "text-xs uppercase bg-gray-100 dark:bg-gray-700",
230
+ header_cell: "px-6 py-3",
231
+ body_row: "bg-white border-b dark:bg-gray-800",
232
+ body_cell: "px-6 py-4",
233
+ })
234
+ end
235
+ end
236
+ end
237
+ end
238
+ ```
239
+
240
+ ### Theme Keys Reference
241
+
242
+ #### Form Theme Keys
243
+
244
+ | Key | Description |
245
+ |-----|-------------|
246
+ | `base` | Form container |
247
+ | `fields_wrapper` | Grid wrapper for fields |
248
+ | `actions_wrapper` | Submit button container |
249
+ | `wrapper` | Individual field wrapper |
250
+ | `inner_wrapper` | Inner field wrapper |
251
+ | `label` | Label base styles |
252
+ | `invalid_label` | Label when field invalid |
253
+ | `valid_label` | Label when field valid |
254
+ | `neutral_label` | Label default state |
255
+ | `input` | Input base styles |
256
+ | `invalid_input` | Input when invalid |
257
+ | `valid_input` | Input when valid |
258
+ | `neutral_input` | Input default state |
259
+ | `hint` | Hint text |
260
+ | `error` | Error message |
261
+ | `button` | Submit button |
262
+ | `checkbox` | Checkbox input |
263
+ | `select` | Select dropdown |
264
+
265
+ #### Display Theme Keys
266
+
267
+ | Key | Description |
268
+ |-----|-------------|
269
+ | `fields_wrapper` | Grid wrapper |
270
+ | `label` | Field label |
271
+ | `description` | Field description |
272
+ | `string` | String values |
273
+ | `text` | Text values |
274
+ | `link` | URL links |
275
+ | `email` | Email links |
276
+ | `phone` | Phone links |
277
+ | `markdown` | Markdown content |
278
+ | `json` | JSON display |
279
+
280
+ #### Table Theme Keys
281
+
282
+ | Key | Description |
283
+ |-----|-------------|
284
+ | `wrapper` | Table container |
285
+ | `base` | Table element |
286
+ | `header` | Header row |
287
+ | `header_cell` | Header cell |
288
+ | `body_row` | Body row |
289
+ | `body_cell` | Body cell |
290
+ | `sort_icon` | Sort indicator |
291
+
292
+ ## Using Tokens in Components
293
+
294
+ ### The `tokens` Helper
295
+
296
+ Conditionally apply classes:
297
+
298
+ ```ruby
299
+ class MyComponent < Plutonium::UI::Component::Base
300
+ def initialize(active:)
301
+ @active = active
302
+ end
303
+
304
+ def view_template
305
+ div(class: tokens(
306
+ "base-class",
307
+ active?: "bg-primary-500 text-white",
308
+ inactive?: "bg-gray-200 text-gray-700"
309
+ )) {
310
+ "Content"
311
+ }
312
+ end
313
+
314
+ private
315
+
316
+ def active? = @active
317
+ def inactive? = !@active
318
+ end
319
+ ```
320
+
321
+ ### The `classes` Helper
322
+
323
+ Returns a hash suitable for splatting:
324
+
325
+ ```ruby
326
+ div(**classes("p-4", "rounded", active?: "ring-2")) { }
327
+ # => <div class="p-4 rounded ring-2">
328
+ ```
329
+
330
+ ### Conditional Tokens with Hash
331
+
332
+ ```ruby
333
+ tokens(
334
+ "base",
335
+ condition?: { then: "if-true", else: "if-false" }
336
+ )
337
+ ```
338
+
339
+ ## Stimulus Controllers
340
+
341
+ Plutonium includes Stimulus controllers. Register them in your application:
342
+
343
+ ```javascript
344
+ // app/javascript/controllers/index.js
345
+ import { application } from "./application"
346
+
347
+ import { registerControllers } from "@radioactive-labs/plutonium"
348
+ registerControllers(application)
349
+
350
+ // Your custom controllers...
351
+ ```
352
+
353
+ ### Available Controllers
354
+
355
+ - `color-mode` - Dark/light mode toggle
356
+ - `form` - Form handling (pre-submit, etc.)
357
+ - `nested-resource-form-fields` - Nested form management
358
+ - `slim-select` - Enhanced select boxes
359
+ - `flatpickr` - Date/time pickers
360
+ - `easymde` - Markdown editor
361
+ - Various UI controllers
362
+
363
+ ## Custom Stimulus Controllers
364
+
365
+ Add your own controllers alongside Plutonium's:
366
+
367
+ ```javascript
368
+ // app/javascript/controllers/custom_controller.js
369
+ import { Controller } from "@hotwired/stimulus"
370
+
371
+ export default class extends Controller {
372
+ connect() {
373
+ console.log("Custom controller connected")
374
+ }
375
+ }
376
+ ```
377
+
378
+ Register in your index:
379
+
380
+ ```javascript
381
+ import CustomController from "./custom_controller"
382
+ application.register("custom", CustomController)
383
+ ```
384
+
385
+ ## Typography
386
+
387
+ Plutonium uses Lato font by default. The layout loads it from Google Fonts.
388
+
389
+ Override in your layout:
390
+
391
+ ```ruby
392
+ class MyLayout < Plutonium::UI::Layout::ResourceLayout
393
+ def render_fonts
394
+ # Your custom fonts
395
+ link(rel: "preconnect", href: "https://fonts.googleapis.com")
396
+ link(href: "https://fonts.googleapis.com/css2?family=Inter&display=swap", rel: "stylesheet")
397
+ end
398
+ end
399
+ ```
400
+
401
+ Update Tailwind config:
402
+
403
+ ```javascript
404
+ theme: {
405
+ fontFamily: {
406
+ 'body': ['Inter', 'sans-serif'],
407
+ 'sans': ['Inter', 'sans-serif'],
408
+ }
409
+ }
410
+ ```
411
+
412
+ ## Related Skills
413
+
414
+ - `views` - Layout customization
415
+ - `forms` - Form theming
416
+ - `installation` - Initial setup
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: connect-resource
3
+ description: Connect existing resources to portals for web access
4
+ ---
5
+
6
+ # Connect Resource Skill
7
+
8
+ Use the `pu:res:conn` generator to connect resources to portals. This is required to expose resources through a portal's web interface.
9
+
10
+ ## Command Syntax
11
+
12
+ ```bash
13
+ rails g pu:res:conn RESOURCE [RESOURCE...] --dest=PORTAL_NAME
14
+ ```
15
+
16
+ **Always specify resources directly** - this avoids interactive prompts. The `--src` option is only needed for interactive mode and can be ignored.
17
+
18
+ ## Usage Patterns
19
+
20
+ ### Main App Resources (not in a package)
21
+
22
+ ```bash
23
+ rails g pu:res:conn PropertyAmenity --dest=admin_portal
24
+ rails g pu:res:conn Post Comment Tag --dest=dashboard_portal
25
+ ```
26
+
27
+ ### Namespaced Resources (from a feature package)
28
+
29
+ Use the full class name:
30
+
31
+ ```bash
32
+ rails g pu:res:conn Blogging::Post --dest=admin_portal
33
+ rails g pu:res:conn Blogging::Post Blogging::Comment --dest=admin_portal
34
+ ```
35
+
36
+ ### Multiple Resources at Once
37
+
38
+ ```bash
39
+ rails g pu:res:conn Property PropertyAmenity Unit Tenant --dest=admin_portal
40
+ ```
41
+
42
+ ## What Gets Generated
43
+
44
+ For a resource `Post` connected to `admin_portal`:
45
+
46
+ ```
47
+ packages/admin_portal/
48
+ ├── app/
49
+ │ ├── controllers/admin_portal/
50
+ │ │ └── posts_controller.rb # Portal controller
51
+ │ ├── policies/admin_portal/
52
+ │ │ └── post_policy.rb # Portal policy (if needed)
53
+ │ └── definitions/admin_portal/
54
+ │ └── post_definition.rb # Portal definition (if needed)
55
+ └── config/
56
+ └── routes.rb # Updated with register_resource
57
+ ```
58
+
59
+ ### Generated Controller
60
+
61
+ ```ruby
62
+ class AdminPortal::PostsController < ::PostsController
63
+ include AdminPortal::Concerns::Controller
64
+ end
65
+ ```
66
+
67
+ ### Generated Policy
68
+
69
+ ```ruby
70
+ class AdminPortal::PostPolicy < ::PostPolicy
71
+ include AdminPortal::ResourcePolicy
72
+
73
+ def permitted_attributes_for_create
74
+ [:title, :content, :user_id]
75
+ end
76
+
77
+ def permitted_attributes_for_read
78
+ [:title, :content, :user_id, :created_at, :updated_at]
79
+ end
80
+
81
+ def permitted_associations
82
+ %i[]
83
+ end
84
+ end
85
+ ```
86
+
87
+ ### Route Registration
88
+
89
+ ```ruby
90
+ # In packages/admin_portal/config/routes.rb
91
+ register_resource ::Post
92
+ ```
93
+
94
+ ## Typical Workflow
95
+
96
+ ```bash
97
+ # 1. Create resources (always specify --dest)
98
+ rails g pu:res:scaffold Post user:belongs_to title:string 'content:text?' --dest=main_app
99
+
100
+ # 2. Run migrations
101
+ rails db:migrate
102
+
103
+ # 3. Connect resources to portal (always specify --dest)
104
+ rails g pu:res:conn Post --dest=admin_portal
105
+ ```
106
+
107
+ ## Important Notes
108
+
109
+ 1. **Always specify resources directly** - avoids prompts, no `--src` needed
110
+ 2. **Always use the generator** - never manually connect resources
111
+ 3. **Run after migrations** - the generator reads model columns for policy attributes
112
+ 4. **Portal-specific customization** - customize the generated policy/definition per-portal