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
@@ -0,0 +1,563 @@
1
+ ---
2
+ name: views
3
+ description: Customizing Plutonium views - pages, forms, displays, tables, and layouts using Phlex
4
+ ---
5
+
6
+ # Plutonium Views
7
+
8
+ Plutonium uses [Phlex](https://www.phlex.fun/) for all view components. This provides a Ruby-first approach to building HTML with full IDE support and type safety.
9
+
10
+ ## Architecture Overview
11
+
12
+ ```
13
+ Definition
14
+ ├── IndexPage → renders Table
15
+ ├── ShowPage → renders Display
16
+ ├── NewPage → renders Form
17
+ ├── EditPage → renders Form
18
+ └── InteractiveActionPage → renders Form
19
+ ```
20
+
21
+ Each definition has nested classes you can override:
22
+
23
+ ```ruby
24
+ class PostDefinition < ResourceDefinition
25
+ class IndexPage < IndexPage; end
26
+ class ShowPage < ShowPage; end
27
+ class NewPage < NewPage; end
28
+ class EditPage < EditPage; end
29
+ class Form < Form; end
30
+ class Table < Table; end
31
+ class Display < Display; end
32
+ end
33
+ ```
34
+
35
+ ## Page Customization
36
+
37
+ ### Page Configuration
38
+
39
+ Set page titles and descriptions in definitions:
40
+
41
+ ```ruby
42
+ class PostDefinition < ResourceDefinition
43
+ # Static titles
44
+ index_page_title "Blog Posts"
45
+ index_page_description "Manage all published articles"
46
+
47
+ show_page_title "Article Details"
48
+ new_page_title "Write New Article"
49
+ edit_page_title "Edit Article"
50
+
51
+ # Control breadcrumbs
52
+ breadcrumbs true # Global default
53
+ index_page_breadcrumbs false # Per-page override
54
+ show_page_breadcrumbs true
55
+ end
56
+ ```
57
+
58
+ ### Custom Page Class
59
+
60
+ Override page rendering by subclassing:
61
+
62
+ ```ruby
63
+ class PostDefinition < ResourceDefinition
64
+ class ShowPage < ShowPage
65
+ private
66
+
67
+ # Custom title logic
68
+ def page_title
69
+ "#{object.title} - #{object.author.name}"
70
+ end
71
+
72
+ # Add content before the main area
73
+ def render_before_content
74
+ div(class: "alert alert-info") {
75
+ "This post has #{object.comments.count} comments"
76
+ }
77
+ end
78
+
79
+ # Add content after
80
+ def render_after_content
81
+ render RelatedPostsComponent.new(post: object)
82
+ end
83
+
84
+ # Override the toolbar
85
+ def render_toolbar
86
+ div(class: "flex gap-2") {
87
+ button(class: "btn") { "Preview" }
88
+ button(class: "btn btn-primary") { "Publish" }
89
+ }
90
+ end
91
+ end
92
+ end
93
+ ```
94
+
95
+ ### Page Hooks
96
+
97
+ All pages inherit these customization hooks:
98
+
99
+ | Hook | Purpose |
100
+ |------|---------|
101
+ | `render_before_header` | Before entire header section |
102
+ | `render_after_header` | After entire header section |
103
+ | `render_before_breadcrumbs` | Before breadcrumbs |
104
+ | `render_after_breadcrumbs` | After breadcrumbs |
105
+ | `render_before_page_header` | Before title/actions |
106
+ | `render_after_page_header` | After title/actions |
107
+ | `render_before_toolbar` | Before toolbar |
108
+ | `render_after_toolbar` | After toolbar |
109
+ | `render_before_content` | Before main content |
110
+ | `render_after_content` | After main content |
111
+ | `render_before_footer` | Before footer |
112
+ | `render_after_footer` | After footer |
113
+
114
+ ### Custom View Files
115
+
116
+ For complete control, create custom ERB view files that replace the default entirely.
117
+
118
+ **File locations:**
119
+
120
+ ```
121
+ # Main app (for a PostsController)
122
+ app/views/posts/index.html.erb
123
+ app/views/posts/show.html.erb
124
+ app/views/posts/new.html.erb
125
+ app/views/posts/edit.html.erb
126
+
127
+ # Portal-specific
128
+ packages/admin_portal/app/views/admin_portal/posts/show.html.erb
129
+ ```
130
+
131
+ **Default view structure:**
132
+
133
+ The default views simply render the page class:
134
+
135
+ ```erb
136
+ <%# app/views/resource/show.html.erb %>
137
+ <%= render current_definition.show_page_class.new %>
138
+ ```
139
+
140
+ **Custom view example:**
141
+
142
+ ```erb
143
+ <%# app/views/posts/show.html.erb %>
144
+ <div class="max-w-4xl mx-auto">
145
+ <article class="prose lg:prose-xl">
146
+ <h1><%= resource_record!.title %></h1>
147
+ <div class="meta text-gray-500">
148
+ By <%= resource_record!.author.name %> on <%= resource_record!.created_at.strftime("%B %d, %Y") %>
149
+ </div>
150
+ <%= raw resource_record!.content %>
151
+ </article>
152
+
153
+ <div class="mt-8">
154
+ <%= link_to "Edit", resource_url_for(resource_record!, action: :edit), class: "btn" %>
155
+ <%= link_to "Back", resource_url_for(Post), class: "btn" %>
156
+ </div>
157
+ </div>
158
+ ```
159
+
160
+ **Mixing approaches:**
161
+
162
+ Render the default page with additions:
163
+
164
+ ```erb
165
+ <%# app/views/posts/show.html.erb %>
166
+ <div class="announcement-banner">
167
+ Special announcement here
168
+ </div>
169
+
170
+ <%= render current_definition.show_page_class.new %>
171
+
172
+ <div class="related-posts">
173
+ <%= render partial: "related_posts", locals: { post: resource_record! } %>
174
+ </div>
175
+ ```
176
+
177
+ ## Form Customization
178
+
179
+ ### Custom Form Template
180
+
181
+ Override how fields are rendered:
182
+
183
+ ```ruby
184
+ class PostDefinition < ResourceDefinition
185
+ class Form < Form
186
+ def form_template
187
+ # Custom layout with sections
188
+ div(class: "grid grid-cols-2 gap-6") {
189
+ div {
190
+ h3(class: "text-lg font-medium") { "Basic Info" }
191
+ render_resource_field :title
192
+ render_resource_field :slug
193
+ }
194
+
195
+ div {
196
+ h3(class: "text-lg font-medium") { "Content" }
197
+ render_resource_field :content
198
+ }
199
+ }
200
+
201
+ div(class: "mt-6") {
202
+ h3(class: "text-lg font-medium") { "Publishing" }
203
+ render_resource_field :published_at
204
+ render_resource_field :category
205
+ }
206
+
207
+ render_actions
208
+ end
209
+ end
210
+ end
211
+ ```
212
+
213
+ ### Form Methods
214
+
215
+ | Method | Purpose |
216
+ |--------|---------|
217
+ | `render_fields` | Render all permitted fields |
218
+ | `render_resource_field(name)` | Render a single field |
219
+ | `render_actions` | Render submit buttons |
220
+ | `record` | The form object (alias: `object`) |
221
+ | `resource_fields` | List of permitted field names |
222
+ | `resource_definition` | The definition instance |
223
+
224
+ ## Display Customization
225
+
226
+ ### Custom Display Template
227
+
228
+ Override the show page detail rendering:
229
+
230
+ ```ruby
231
+ class PostDefinition < ResourceDefinition
232
+ class Display < Display
233
+ def display_template
234
+ # Hero section
235
+ div(class: "bg-gradient-to-r from-blue-500 to-purple-600 p-8 rounded-lg text-white mb-6") {
236
+ h1(class: "text-3xl font-bold") { object.title }
237
+ p(class: "mt-2 opacity-90") { object.excerpt }
238
+ }
239
+
240
+ # Main content
241
+ Block do
242
+ fields_wrapper do
243
+ render_resource_field :author
244
+ render_resource_field :published_at
245
+ render_resource_field :category
246
+ end
247
+ end
248
+
249
+ # Full-width content
250
+ Block do
251
+ div(class: "prose max-w-none") {
252
+ raw object.content
253
+ }
254
+ end
255
+
256
+ # Associations (tabs)
257
+ render_associations if present_associations?
258
+ end
259
+ end
260
+ end
261
+ ```
262
+
263
+ ### Display Methods
264
+
265
+ | Method | Purpose |
266
+ |--------|---------|
267
+ | `render_fields` | Render all permitted fields in a block |
268
+ | `render_resource_field(name)` | Render single field |
269
+ | `render_associations` | Render association tabs |
270
+ | `object` | The record being displayed |
271
+ | `resource_fields` | List of permitted field names |
272
+ | `resource_associations` | List of permitted associations |
273
+
274
+ ## Table Customization
275
+
276
+ ### Custom Table Rendering
277
+
278
+ Override list page table:
279
+
280
+ ```ruby
281
+ class PostDefinition < ResourceDefinition
282
+ class Table < Table
283
+ def view_template
284
+ render_search_bar
285
+ render_scopes_bar
286
+
287
+ if collection.empty?
288
+ render_empty_card
289
+ else
290
+ # Custom card grid instead of table
291
+ div(class: "grid grid-cols-3 gap-4") {
292
+ collection.each do |post|
293
+ render PostCardComponent.new(post:)
294
+ end
295
+ }
296
+ end
297
+
298
+ render_footer
299
+ end
300
+ end
301
+ end
302
+ ```
303
+
304
+ ### Table Methods
305
+
306
+ | Method | Purpose |
307
+ |--------|---------|
308
+ | `render_search_bar` | Search input |
309
+ | `render_scopes_bar` | Scope tabs |
310
+ | `render_table` | Default table |
311
+ | `render_empty_card` | Empty state |
312
+ | `render_footer` | Pagination |
313
+ | `collection` | The paginated records |
314
+ | `resource_fields` | Column field names |
315
+
316
+ ## Component Kit
317
+
318
+ Plutonium provides shorthand methods for common components:
319
+
320
+ ```ruby
321
+ class MyPage < Plutonium::UI::Page::Base
322
+ def render_content
323
+ # These are automatically rendered
324
+ PageHeader(title: "Dashboard")
325
+
326
+ Panel(class: "mt-4") {
327
+ p { "Content here" }
328
+ }
329
+
330
+ Block {
331
+ TabList(items: tabs)
332
+ }
333
+
334
+ EmptyCard("No items found")
335
+
336
+ ActionButton(action, url: "/posts/new")
337
+ end
338
+ end
339
+ ```
340
+
341
+ Available kit methods:
342
+ - `Breadcrumbs()`
343
+ - `PageHeader(title:, description:, actions:)`
344
+ - `Panel(**attrs)`
345
+ - `Block(**attrs)`
346
+ - `TabList(items:)`
347
+ - `EmptyCard(message)`
348
+ - `ActionButton(action, url:)`
349
+ - `DynaFrameHost()` / `DynaFrameContent()`
350
+ - `TableSearchBar()`
351
+ - `TableScopesBar()`
352
+ - `TableInfo(pagy)`
353
+ - `TablePagination(pagy)`
354
+
355
+ ## Custom Components
356
+
357
+ ### Creating a Phlex Component
358
+
359
+ ```ruby
360
+ # app/components/post_card_component.rb
361
+ class PostCardComponent < Plutonium::UI::Component::Base
362
+ def initialize(post:)
363
+ @post = post
364
+ end
365
+
366
+ def view_template
367
+ div(class: "bg-white rounded-lg shadow p-4") {
368
+ h3(class: "font-bold") { @post.title }
369
+ p(class: "text-gray-600 mt-2") { @post.excerpt }
370
+
371
+ div(class: "mt-4 flex justify-between items-center") {
372
+ span(class: "text-sm text-gray-500") { @post.published_at&.strftime("%B %d, %Y") }
373
+ a(href: resource_url_for(@post), class: "text-blue-600") { "Read more" }
374
+ }
375
+ }
376
+ end
377
+ end
378
+ ```
379
+
380
+ ### Using in Definitions
381
+
382
+ Reference components in field definitions:
383
+
384
+ ```ruby
385
+ class PostDefinition < ResourceDefinition
386
+ # Custom display component
387
+ display :status, as: StatusBadgeComponent
388
+
389
+ # Custom input component
390
+ input :color, as: ColorPickerComponent
391
+
392
+ # Block with component
393
+ display :metrics do |field|
394
+ MetricsChartComponent.new(data: field.value)
395
+ end
396
+ end
397
+ ```
398
+
399
+ ## Layout Customization
400
+
401
+ ### Eject Layout
402
+
403
+ Copy the layout template to your project:
404
+
405
+ ```bash
406
+ rails generate pu:eject:layout
407
+ ```
408
+
409
+ This copies `layouts/resource.html.erb` to your portal.
410
+
411
+ ### Custom Layout Class
412
+
413
+ Override the Phlex layout:
414
+
415
+ ```ruby
416
+ # packages/admin_portal/app/views/layouts/admin_portal/resource_layout.rb
417
+ module AdminPortal
418
+ class ResourceLayout < Plutonium::UI::Layout::ResourceLayout
419
+ private
420
+
421
+ # Custom body classes
422
+ def body_attributes
423
+ {class: "antialiased bg-slate-100 dark:bg-slate-900"}
424
+ end
425
+
426
+ # Add custom header content
427
+ def render_before_main
428
+ super
429
+ render AnnouncementBanner.new if Announcement.active.any?
430
+ end
431
+
432
+ # Custom scripts
433
+ def render_body_scripts
434
+ super
435
+ script(src: "/custom-analytics.js")
436
+ end
437
+ end
438
+ end
439
+ ```
440
+
441
+ ### Layout Hooks
442
+
443
+ | Hook | Purpose |
444
+ |------|---------|
445
+ | `render_before_main` | Before main content area |
446
+ | `render_after_main` | After main (modals, etc.) |
447
+ | `render_before_content` | Inside main, before content |
448
+ | `render_after_content` | Inside main, after content |
449
+ | `render_flash` | Flash messages |
450
+ | `render_head` | HTML head section |
451
+ | `render_title` | Page title tag |
452
+ | `render_metatags` | Meta tags |
453
+ | `render_assets` | CSS/JS assets |
454
+ | `render_body_scripts` | Scripts at end of body |
455
+
456
+ ## Available Context
457
+
458
+ Both ERB views and Phlex components have access to the same context.
459
+
460
+ ### Resource Methods
461
+
462
+ | Method | Description |
463
+ |--------|-------------|
464
+ | `resource_class` | The model class (e.g., `Post`) |
465
+ | `resource_record!` | Current record (raises if not found) |
466
+ | `resource_record?` | Current record (nil if not found) |
467
+ | `current_parent` | Parent record for nested routes |
468
+ | `current_scoped_entity` | Entity for multi-tenant portals |
469
+
470
+ ### Definition & Policy
471
+
472
+ | Method | Description |
473
+ |--------|-------------|
474
+ | `current_definition` | Definition instance for current resource |
475
+ | `current_policy` | Policy instance for current record |
476
+ | `current_authorized_scope` | Scoped collection user can access |
477
+
478
+ ### Authentication
479
+
480
+ | Method | Description |
481
+ |--------|-------------|
482
+ | `current_user` | Authenticated user (if using Rodauth) |
483
+
484
+ ### URL Helpers
485
+
486
+ | Method | Description |
487
+ |--------|-------------|
488
+ | `resource_url_for(record)` | URL for a record |
489
+ | `resource_url_for(record, action: :edit)` | Action URL for record |
490
+ | `resource_url_for(Model)` | Index URL for model |
491
+ | `resource_url_for(Model, action: :new)` | New URL for model |
492
+ | `resource_url_for(record, parent: parent)` | Nested resource URL |
493
+
494
+ ### Display Helpers
495
+
496
+ | Method | Description |
497
+ |--------|-------------|
498
+ | `display_name_of(record)` | Human-readable name for record |
499
+ | `resource_name(klass)` | Singular model name |
500
+ | `resource_name_plural(klass)` | Plural model name |
501
+
502
+ ### In Phlex Components
503
+
504
+ ```ruby
505
+ class MyComponent < Plutonium::UI::Component::Base
506
+ def view_template
507
+ # All the above methods work directly
508
+ current_user
509
+ resource_record!
510
+ resource_url_for(@post)
511
+
512
+ # Rails helpers via helpers proxy
513
+ helpers.link_to(...)
514
+ helpers.image_tag(...)
515
+ helpers.number_to_currency(...)
516
+ end
517
+ end
518
+ ```
519
+
520
+ ### In ERB Views
521
+
522
+ ```erb
523
+ <%# All methods available directly %>
524
+ <%= resource_record!.title %>
525
+ <%= current_user.name %>
526
+ <%= link_to "Edit", resource_url_for(resource_record!, action: :edit) %>
527
+
528
+ <%# Render Phlex components %>
529
+ <%= render current_definition.show_page_class.new %>
530
+ <%= render MyCustomComponent.new(post: resource_record!) %>
531
+ ```
532
+
533
+ ## Portal-Specific Views
534
+
535
+ Each portal can have its own view overrides:
536
+
537
+ ```ruby
538
+ # Base definition
539
+ class PostDefinition < ResourceDefinition
540
+ class ShowPage < ShowPage
541
+ # Default behavior
542
+ end
543
+ end
544
+
545
+ # Admin portal override
546
+ class AdminPortal::PostDefinition < ::PostDefinition
547
+ class ShowPage < ShowPage # Inherits from ::PostDefinition::ShowPage
548
+ def render_after_content
549
+ super
550
+ render AdminOnlySection.new(post: object)
551
+ end
552
+ end
553
+ end
554
+ ```
555
+
556
+ ## Related Skills
557
+
558
+ - `forms` - Custom form templates and field builders
559
+ - `assets` - TailwindCSS and component theming
560
+ - `definition-fields` - Field/input/display configuration
561
+ - `definition-actions` - Action buttons and interactions
562
+ - `controller` - Presentation hooks (`present_parent?`, etc.)
563
+ - `portal` - Portal-specific customization
data/Appraisals CHANGED
@@ -1,11 +1,53 @@
1
1
  appraise "rails-7" do
2
- gem "rails", "~> 7.1.3", ">= 7.1.3.4"
3
- # gem "sprockets-rails"
4
- gem "sqlite3", "~> 1.4"
2
+ gem "rails", "~> 7.2"
3
+ gem "sqlite3"
5
4
  gem "puma", ">= 5.0"
6
5
  gem "importmap-rails"
7
6
  gem "turbo-rails"
8
7
  gem "stimulus-rails"
8
+ gem "propshaft"
9
+ # Rodauth dependencies
10
+ gem "rodauth-rails"
11
+ gem "sequel-activerecord_connection"
12
+ gem "tilt"
13
+ gem "bcrypt"
14
+ gem "rotp"
15
+ gem "rqrcode"
16
+ gem "tzinfo-data", platforms: %i[windows jruby]
17
+ end
18
+
19
+ appraise "rails-8.0" do
20
+ gem "rails", "~> 8.0.0"
21
+ gem "sqlite3"
22
+ gem "puma", ">= 5.0"
23
+ gem "importmap-rails"
24
+ gem "turbo-rails"
25
+ gem "stimulus-rails"
26
+ gem "propshaft"
27
+ # Rodauth dependencies
28
+ gem "rodauth-rails"
29
+ gem "sequel-activerecord_connection"
30
+ gem "tilt"
31
+ gem "bcrypt"
32
+ gem "rotp"
33
+ gem "rqrcode"
34
+ gem "tzinfo-data", platforms: %i[windows jruby]
35
+ end
36
+
37
+ appraise "rails-8.1" do
38
+ gem "rails", "~> 8.1.0"
39
+ gem "sqlite3"
40
+ gem "puma", ">= 5.0"
41
+ gem "importmap-rails"
42
+ gem "turbo-rails"
43
+ gem "stimulus-rails"
44
+ gem "propshaft"
45
+ # Rodauth dependencies
46
+ gem "rodauth-rails"
47
+ gem "sequel-activerecord_connection"
48
+ gem "tilt"
49
+ gem "bcrypt"
50
+ gem "rotp"
51
+ gem "rqrcode"
9
52
  gem "tzinfo-data", platforms: %i[windows jruby]
10
- # gem "bootsnap", require: false
11
53
  end
data/CHANGELOG.md CHANGED
@@ -1,8 +1,39 @@
1
- ## [0.33.1] - 2026-01-14
1
+ ## [0.34.0] - 2026-01-18
2
+
3
+ ### 🚀 Features
4
+
5
+ - *(generators)* Configure default_url_options via RAILS_DEFAULT_URL env var
6
+ - *(generators)* Add pu:skills:sync to install Claude Code skills
7
+ - *(query)* Execute adhoc blocks in controller context
8
+ - *(railtie)* Map ActionPolicy::Unauthorized to 403 Forbidden
9
+ - *(query)* Add default scope support for resource queries
2
10
 
3
11
  ### 🐛 Bug Fixes
4
12
 
5
13
  - *(controllers)* Handle turbo_stream format in CRUD and interactive actions
14
+ - *(generators)* Support nullable syntax with type options
15
+ - *(generators)* Prevent overwriting existing url_options configuration
16
+ - *(generators)* Normalize reference names before comparing namespaces
17
+ - *(package)* Include engine migrations in programmatic migration paths
18
+ - *(release)* Handle missing version tag in next_version task
19
+
20
+ ### 📚 Documentation
21
+
22
+ - Add Claude Code skills and improve generator documentation
23
+ - Add definition skills and update module documentation
24
+ - Add comprehensive Claude Code skills for resources
25
+ - Add package and portal skills
26
+ - Add interaction skill for business logic actions
27
+ - *(skills)* Add new Claude Code skills and enhance existing ones
28
+
29
+ ### 🎨 Styling
30
+
31
+ - Standardrb linting
32
+
33
+ ### ⚙️ Miscellaneous Tasks
34
+
35
+ - Overhaul documentation structure and test infrastructure
36
+ - Move brakeman config to config/ and update ignore list
6
37
  ## [0.33.0] - 2026-01-12
7
38
 
8
39
  ### ◀️ Revert