plutonium 0.62.2 → 0.63.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 (255) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +44 -0
  3. data/.claude/skills/plutonium-app/SKILL.md +3 -3
  4. data/.claude/skills/plutonium-async-interactions/SKILL.md +191 -0
  5. data/.claude/skills/plutonium-auth/SKILL.md +36 -0
  6. data/.claude/skills/plutonium-behavior/SKILL.md +121 -24
  7. data/.claude/skills/plutonium-kanban/SKILL.md +17 -3
  8. data/.claude/skills/plutonium-resource/SKILL.md +259 -12
  9. data/.claude/skills/plutonium-tenancy/SKILL.md +32 -3
  10. data/.claude/skills/plutonium-ui/SKILL.md +115 -14
  11. data/.claude/skills/plutonium-wizard/SKILL.md +73 -4
  12. data/CHANGELOG.md +67 -0
  13. data/CLAUDE.md +87 -0
  14. data/Rakefile +34 -0
  15. data/SECURITY.md +1 -1
  16. data/app/assets/plutonium.css +1 -1
  17. data/app/assets/plutonium.js +685 -102
  18. data/app/assets/plutonium.js.map +4 -4
  19. data/app/assets/plutonium.min.js +53 -53
  20. data/app/assets/plutonium.min.js.map +4 -4
  21. data/app/views/rodauth/_login_form.html.erb +13 -0
  22. data/db/migrate/async_interactions/20260817000001_create_plutonium_async_runs.rb +170 -0
  23. data/docs/.vitepress/config.ts +81 -3
  24. data/docs/.vitepress/theme/blog.data.ts +44 -0
  25. data/docs/.vitepress/theme/components/BlogIndex.vue +87 -0
  26. data/docs/.vitepress/theme/components/BlogMeta.vue +47 -0
  27. data/docs/.vitepress/theme/components/HomeFeatureTour.vue +293 -0
  28. data/docs/.vitepress/theme/components/HomeHero.vue +3 -3
  29. data/docs/.vitepress/theme/components/HomeInTheBox.vue +8 -0
  30. data/docs/.vitepress/theme/components/HomeStopWriting.vue +1 -0
  31. data/docs/.vitepress/theme/components/HomeWhyPlutonium.vue +84 -0
  32. data/docs/.vitepress/theme/index.ts +8 -4
  33. data/docs/blog/association-inputs-post-signed-ids.md +70 -0
  34. data/docs/blog/fix-the-model-not-the-policy.md +122 -0
  35. data/docs/blog/fractional-ordering-runs-out-of-room.md +67 -0
  36. data/docs/blog/half-finished-forms-are-pii.md +76 -0
  37. data/docs/blog/index.md +13 -0
  38. data/docs/blog/interactions-are-presentation-objects.md +152 -0
  39. data/docs/blog/introducing-plutonium.md +253 -0
  40. data/docs/blog/jobs-are-not-permission-snapshots.md +100 -0
  41. data/docs/blog/plutonium-and-ai-agents.md +48 -0
  42. data/docs/blog/realtime-is-one-line-and-four-dependencies.md +70 -0
  43. data/docs/blog/two-forms-one-dom-id.md +69 -0
  44. data/docs/blog/whats-new-async-kanban-wizards.md +130 -0
  45. data/docs/getting-started/tutorial/04-authorization.md +12 -3
  46. data/docs/getting-started/tutorial/06-nested-resources.md +3 -1
  47. data/docs/getting-started/tutorial/07-author-portal.md +2 -2
  48. data/docs/guides/authentication.md +73 -0
  49. data/docs/guides/authorization.md +2 -0
  50. data/docs/guides/creating-packages.md +5 -3
  51. data/docs/guides/custom-actions.md +74 -12
  52. data/docs/guides/customizing-ui.md +9 -2
  53. data/docs/guides/index.md +1 -0
  54. data/docs/guides/kanban.md +7 -5
  55. data/docs/guides/nested-resources.md +11 -1
  56. data/docs/guides/performance.md +104 -0
  57. data/docs/guides/user-invites.md +1 -1
  58. data/docs/guides/wizards.md +9 -1
  59. data/docs/index.md +3 -3
  60. data/docs/public/images/home/tour-actions.png +0 -0
  61. data/docs/public/images/home/tour-async.png +0 -0
  62. data/docs/public/images/home/tour-kanban.png +0 -0
  63. data/docs/public/images/home/tour-tenancy.png +0 -0
  64. data/docs/public/images/home/tour-wizard.png +0 -0
  65. data/docs/public/images/reference/async-progress-page.png +0 -0
  66. data/docs/public/images/reference/async-running-banner.png +0 -0
  67. data/docs/public/templates/experimental.rb +34 -0
  68. data/docs/public/templates/pluton8.rb +14 -0
  69. data/docs/reference/app/portals.md +15 -3
  70. data/docs/reference/auth/accounts.md +19 -0
  71. data/docs/reference/behavior/async-interactions.md +295 -0
  72. data/docs/reference/behavior/controllers.md +17 -4
  73. data/docs/reference/behavior/index.md +7 -1
  74. data/docs/reference/behavior/interactions.md +152 -22
  75. data/docs/reference/configuration.md +5 -0
  76. data/docs/reference/index.md +1 -0
  77. data/docs/reference/kanban/dsl.md +7 -4
  78. data/docs/reference/kanban/index.md +1 -1
  79. data/docs/reference/kanban/positioning.md +26 -4
  80. data/docs/reference/positioning.md +568 -0
  81. data/docs/reference/resource/actions.md +97 -4
  82. data/docs/reference/resource/definition.md +181 -9
  83. data/docs/reference/tenancy/invites.md +1 -1
  84. data/docs/reference/tenancy/nested-resources.md +60 -2
  85. data/docs/reference/ui/assets.md +4 -0
  86. data/docs/reference/ui/components.md +57 -4
  87. data/docs/reference/ui/displays.md +20 -10
  88. data/docs/reference/ui/index.md +1 -1
  89. data/docs/reference/wizard/dsl.md +33 -0
  90. data/docs/reference/wizard/storage-config.md +1 -0
  91. data/docs/superpowers/plans/2026-07-16-homepage-depth-upgrade.md +624 -0
  92. data/docs/superpowers/plans/2026-07-16-homepage-depth-upgrade.md.tasks.json +32 -0
  93. data/docs/superpowers/plans/2026-07-31-positioned-drag-and-drop.md +1787 -0
  94. data/docs/superpowers/plans/2026-07-31-positioned-drag-and-drop.md.tasks.json +91 -0
  95. data/docs/superpowers/plans/2026-08-17-async-interactions.md +1414 -0
  96. data/docs/superpowers/plans/2026-08-17-async-interactions.md.tasks.json +66 -0
  97. data/docs/superpowers/specs/2026-07-16-homepage-depth-upgrade-design.md +111 -0
  98. data/docs/superpowers/specs/2026-07-17-action-html-attributes-design.md +124 -0
  99. data/docs/superpowers/specs/2026-07-31-positioned-drag-and-drop-design.md +506 -0
  100. data/docs/superpowers/specs/2026-08-17-async-interactions-design.md +185 -0
  101. data/gemfiles/postgres.gemfile.lock +85 -85
  102. data/gemfiles/rails_7.gemfile.lock +322 -140
  103. data/gemfiles/rails_8.0.gemfile.lock +125 -115
  104. data/gemfiles/rails_8.1.gemfile.lock +126 -116
  105. data/lib/generators/pu/async_interactions/install_generator.rb +111 -0
  106. data/lib/generators/pu/async_interactions/templates/app/controllers/async_runs_controller.rb.tt +15 -0
  107. data/lib/generators/pu/core/typespec/typespec_generator.rb +7 -4
  108. data/lib/generators/pu/invites/install_generator.rb +3 -3
  109. data/lib/generators/pu/invites/templates/packages/invites/app/views/layouts/invites/invitation.html.erb.tt +2 -2
  110. data/lib/generators/pu/lib/plutonium_generators/concerns/mounts_engines.rb +47 -2
  111. data/lib/generators/pu/lib/plutonium_generators/concerns/resource_registration.rb +41 -0
  112. data/lib/generators/pu/lite/litestream/litestream_generator.rb +1 -1
  113. data/lib/generators/pu/lite/solid_queue/solid_queue_generator.rb +1 -1
  114. data/lib/generators/pu/res/conn/conn_generator.rb +19 -39
  115. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +4 -0
  116. data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +15 -6
  117. data/lib/generators/pu/rodauth/templates/app/rodauth/rodauth_plugin.rb.tt +7 -0
  118. data/lib/generators/pu/saas/welcome/templates/app/views/layouts/welcome.html.erb.tt +2 -2
  119. data/lib/generators/pu/wizards/install_generator.rb +78 -0
  120. data/lib/plutonium/action/base.rb +71 -9
  121. data/lib/plutonium/action/interactive.rb +9 -0
  122. data/lib/plutonium/attachments.rb +254 -0
  123. data/lib/plutonium/configuration.rb +82 -1
  124. data/lib/plutonium/core/controller.rb +50 -7
  125. data/lib/plutonium/core/controllers/authorizable.rb +16 -0
  126. data/lib/plutonium/core/controllers/entity_scoping.rb +12 -2
  127. data/lib/plutonium/definition/base.rb +51 -0
  128. data/lib/plutonium/definition/display_layout.rb +112 -0
  129. data/lib/plutonium/definition/index_views.rb +8 -7
  130. data/lib/plutonium/definition/input_aliases.rb +38 -0
  131. data/lib/plutonium/definition/page_widths.rb +65 -0
  132. data/lib/plutonium/definition/positioning.rb +126 -0
  133. data/lib/plutonium/definition/sorting.rb +17 -2
  134. data/lib/plutonium/helpers/turbo_helper.rb +7 -0
  135. data/lib/plutonium/interaction/README.md +61 -24
  136. data/lib/plutonium/interaction/async/configuration.rb +38 -0
  137. data/lib/plutonium/interaction/async/context.rb +419 -0
  138. data/lib/plutonium/interaction/async/executor.rb +422 -0
  139. data/lib/plutonium/interaction/async/job.rb +80 -0
  140. data/lib/plutonium/interaction/async/reap_job.rb +81 -0
  141. data/lib/plutonium/interaction/async/run.rb +394 -0
  142. data/lib/plutonium/interaction/async/run_definition.rb +155 -0
  143. data/lib/plutonium/interaction/async/run_policy.rb +86 -0
  144. data/lib/plutonium/interaction/base.rb +34 -7
  145. data/lib/plutonium/interaction/concerns/dispatchable.rb +518 -0
  146. data/lib/plutonium/interaction/concerns/scoping.rb +70 -9
  147. data/lib/plutonium/interaction/response/redirect.rb +11 -3
  148. data/lib/plutonium/kanban/board.rb +14 -0
  149. data/lib/plutonium/kanban/column.rb +4 -2
  150. data/lib/plutonium/kanban/dsl.rb +4 -1
  151. data/lib/plutonium/kanban/grouping.rb +9 -22
  152. data/lib/plutonium/kanban/positioning.rb +5 -65
  153. data/lib/plutonium/positioning/config.rb +94 -0
  154. data/lib/plutonium/positioning/model.rb +128 -0
  155. data/lib/plutonium/positioning.rb +25 -86
  156. data/lib/plutonium/railtie.rb +1 -0
  157. data/lib/plutonium/resource/controller.rb +118 -38
  158. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +32 -2
  159. data/lib/plutonium/resource/controllers/crud_actions.rb +30 -2
  160. data/lib/plutonium/resource/controllers/eager_loading.rb +87 -0
  161. data/lib/plutonium/resource/controllers/export_csv.rb +10 -1
  162. data/lib/plutonium/resource/controllers/kanban_actions.rb +53 -14
  163. data/lib/plutonium/resource/controllers/position_actions.rb +390 -0
  164. data/lib/plutonium/resource/controllers/presentable.rb +19 -13
  165. data/lib/plutonium/resource/controllers/queryable.rb +5 -1
  166. data/lib/plutonium/resource/controllers/wizard_actions.rb +21 -0
  167. data/lib/plutonium/resource/policy.rb +33 -0
  168. data/lib/plutonium/resource/query_object.rb +36 -0
  169. data/lib/plutonium/routing/mapper_extensions.rb +100 -8
  170. data/lib/plutonium/routing/route_set_extensions.rb +15 -1
  171. data/lib/plutonium/routing/wizard_registration.rb +4 -0
  172. data/lib/plutonium/testing/resource_policy.rb +6 -2
  173. data/lib/plutonium/ui/action_button.rb +12 -7
  174. data/lib/plutonium/ui/actions_dropdown.rb +1 -1
  175. data/lib/plutonium/ui/block.rb +21 -1
  176. data/lib/plutonium/ui/breadcrumbs.rb +187 -55
  177. data/lib/plutonium/ui/component/methods.rb +5 -0
  178. data/lib/plutonium/ui/component/positionable.rb +112 -0
  179. data/lib/plutonium/ui/component/resolves_tags.rb +57 -0
  180. data/lib/plutonium/ui/component/section.rb +185 -0
  181. data/lib/plutonium/ui/display/base.rb +13 -1
  182. data/lib/plutonium/ui/display/components/formatted_value.rb +26 -0
  183. data/lib/plutonium/ui/display/components/section.rb +18 -0
  184. data/lib/plutonium/ui/display/resource.rb +141 -22
  185. data/lib/plutonium/ui/display/theme.rb +20 -1
  186. data/lib/plutonium/ui/export_button.rb +1 -1
  187. data/lib/plutonium/ui/form/base.rb +8 -7
  188. data/lib/plutonium/ui/form/components/intl_tel_input.rb +1 -1
  189. data/lib/plutonium/ui/form/components/section.rb +7 -62
  190. data/lib/plutonium/ui/form/components/uppy.rb +12 -1
  191. data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +16 -3
  192. data/lib/plutonium/ui/form/concerns/renders_structured_inputs.rb +5 -1
  193. data/lib/plutonium/ui/form/query.rb +2 -4
  194. data/lib/plutonium/ui/form/resource.rb +92 -15
  195. data/lib/plutonium/ui/form/theme.rb +17 -0
  196. data/lib/plutonium/ui/form/wizard.rb +25 -1
  197. data/lib/plutonium/ui/grid/card.rb +79 -11
  198. data/lib/plutonium/ui/grid/resource.rb +47 -5
  199. data/lib/plutonium/ui/interaction/async/run_progress.rb +227 -0
  200. data/lib/plutonium/ui/interaction/async/running_banner.rb +65 -0
  201. data/lib/plutonium/ui/kanban/card.rb +2 -1
  202. data/lib/plutonium/ui/kanban/column.rb +12 -6
  203. data/lib/plutonium/ui/kanban/resource.rb +6 -7
  204. data/lib/plutonium/ui/layout/base.rb +10 -3
  205. data/lib/plutonium/ui/nav_grid_menu.rb +1 -0
  206. data/lib/plutonium/ui/page/base.rb +19 -0
  207. data/lib/plutonium/ui/page/edit.rb +4 -1
  208. data/lib/plutonium/ui/page/index.rb +69 -18
  209. data/lib/plutonium/ui/page/interactive_action.rb +5 -1
  210. data/lib/plutonium/ui/page/new.rb +4 -1
  211. data/lib/plutonium/ui/page/show.rb +27 -10
  212. data/lib/plutonium/ui/page/wizard.rb +10 -1
  213. data/lib/plutonium/ui/page/wizard_chooser.rb +36 -11
  214. data/lib/plutonium/ui/page_width.rb +58 -0
  215. data/lib/plutonium/ui/table/base.rb +34 -1
  216. data/lib/plutonium/ui/table/components/attachment.rb +1 -1
  217. data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +32 -8
  218. data/lib/plutonium/ui/table/components/drag_handle.rb +120 -0
  219. data/lib/plutonium/ui/table/components/filter_form.rb +1 -4
  220. data/lib/plutonium/ui/table/components/filter_pills.rb +1 -1
  221. data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +1 -1
  222. data/lib/plutonium/ui/table/resource.rb +50 -5
  223. data/lib/plutonium/ui/table/theme.rb +59 -2
  224. data/lib/plutonium/ui/wizard/review.rb +4 -2
  225. data/lib/plutonium/ui/wizard/summary_display.rb +42 -14
  226. data/lib/plutonium/version.rb +1 -1
  227. data/lib/plutonium/wizard/attachments.rb +32 -197
  228. data/lib/plutonium/wizard/base.rb +6 -1
  229. data/lib/plutonium/wizard/configuration.rb +12 -0
  230. data/lib/plutonium/wizard/controller.rb +14 -0
  231. data/lib/plutonium/wizard/driving.rb +99 -15
  232. data/lib/plutonium/wizard/dsl.rb +23 -0
  233. data/lib/plutonium/wizard/resume.rb +127 -49
  234. data/lib/plutonium/wizard/runner.rb +46 -2
  235. data/lib/plutonium/wizard/step_adapter.rb +1 -1
  236. data/lib/plutonium/wizard/sweep_job.rb +16 -0
  237. data/lib/plutonium.rb +21 -0
  238. data/lib/rodauth/features/session_isolation.rb +92 -0
  239. data/lib/rodauth/plugins.rb +1 -0
  240. data/package.json +2 -1
  241. data/plutonium.gemspec +29 -11
  242. data/src/css/components.css +89 -1
  243. data/src/css/slim_select.css +20 -0
  244. data/src/js/controllers/breadcrumbs_controller.js +112 -0
  245. data/src/js/controllers/bulk_actions_controller.js +10 -2
  246. data/src/js/controllers/kanban_controller.js +30 -21
  247. data/src/js/controllers/positioned_controller.js +452 -0
  248. data/src/js/controllers/register_controllers.js +6 -0
  249. data/src/js/controllers/resource_drop_down_controller.js +5 -0
  250. data/src/js/controllers/run_progress_controller.js +73 -0
  251. data/src/js/drag/sortable.js +186 -0
  252. data/yarn.lock +108 -63
  253. metadata +103 -16
  254. data/docs/.vitepress/theme/components/HomeAudienceSplit.vue +0 -53
  255. data/docs/.vitepress/theme/components/HomePillars.vue +0 -42
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- plutonium (0.62.1)
4
+ plutonium (0.62.2)
5
5
  action_policy (~> 0.7.0)
6
6
  csv
7
7
  listen (~> 3.8)
@@ -16,7 +16,7 @@ PATH
16
16
  phlexi-menu (>= 0.4.1)
17
17
  phlexi-table (>= 0.2.0)
18
18
  rabl (~> 0.17.0)
19
- rails (>= 7.2)
19
+ rails (>= 7.2.3.1)
20
20
  redcarpet
21
21
  semantic_range (~> 3.0)
22
22
  tailwind_merge
@@ -28,31 +28,31 @@ GEM
28
28
  specs:
29
29
  action_policy (0.7.6)
30
30
  ruby-next-core (>= 1.0)
31
- action_text-trix (2.1.16)
31
+ action_text-trix (2.1.19)
32
32
  railties
33
- actioncable (8.1.2)
34
- actionpack (= 8.1.2)
35
- activesupport (= 8.1.2)
33
+ actioncable (8.1.3.1)
34
+ actionpack (= 8.1.3.1)
35
+ activesupport (= 8.1.3.1)
36
36
  nio4r (~> 2.0)
37
37
  websocket-driver (>= 0.6.1)
38
38
  zeitwerk (~> 2.6)
39
- actionmailbox (8.1.2)
40
- actionpack (= 8.1.2)
41
- activejob (= 8.1.2)
42
- activerecord (= 8.1.2)
43
- activestorage (= 8.1.2)
44
- activesupport (= 8.1.2)
39
+ actionmailbox (8.1.3.1)
40
+ actionpack (= 8.1.3.1)
41
+ activejob (= 8.1.3.1)
42
+ activerecord (= 8.1.3.1)
43
+ activestorage (= 8.1.3.1)
44
+ activesupport (= 8.1.3.1)
45
45
  mail (>= 2.8.0)
46
- actionmailer (8.1.2)
47
- actionpack (= 8.1.2)
48
- actionview (= 8.1.2)
49
- activejob (= 8.1.2)
50
- activesupport (= 8.1.2)
46
+ actionmailer (8.1.3.1)
47
+ actionpack (= 8.1.3.1)
48
+ actionview (= 8.1.3.1)
49
+ activejob (= 8.1.3.1)
50
+ activesupport (= 8.1.3.1)
51
51
  mail (>= 2.8.0)
52
52
  rails-dom-testing (~> 2.2)
53
- actionpack (8.1.2)
54
- actionview (= 8.1.2)
55
- activesupport (= 8.1.2)
53
+ actionpack (8.1.3.1)
54
+ actionview (= 8.1.3.1)
55
+ activesupport (= 8.1.3.1)
56
56
  nokogiri (>= 1.8.5)
57
57
  rack (>= 2.2.4)
58
58
  rack-session (>= 1.0.1)
@@ -60,16 +60,16 @@ GEM
60
60
  rails-dom-testing (~> 2.2)
61
61
  rails-html-sanitizer (~> 1.6)
62
62
  useragent (~> 0.16)
63
- actiontext (8.1.2)
63
+ actiontext (8.1.3.1)
64
64
  action_text-trix (~> 2.1.15)
65
- actionpack (= 8.1.2)
66
- activerecord (= 8.1.2)
67
- activestorage (= 8.1.2)
68
- activesupport (= 8.1.2)
65
+ actionpack (= 8.1.3.1)
66
+ activerecord (= 8.1.3.1)
67
+ activestorage (= 8.1.3.1)
68
+ activesupport (= 8.1.3.1)
69
69
  globalid (>= 0.6.0)
70
70
  nokogiri (>= 1.8.5)
71
- actionview (8.1.2)
72
- activesupport (= 8.1.2)
71
+ actionview (8.1.3.1)
72
+ activesupport (= 8.1.3.1)
73
73
  builder (~> 3.1)
74
74
  erubi (~> 1.11)
75
75
  rails-dom-testing (~> 2.2)
@@ -79,22 +79,22 @@ GEM
79
79
  activesupport
80
80
  railties
81
81
  shrine
82
- activejob (8.1.2)
83
- activesupport (= 8.1.2)
82
+ activejob (8.1.3.1)
83
+ activesupport (= 8.1.3.1)
84
84
  globalid (>= 0.3.6)
85
- activemodel (8.1.2)
86
- activesupport (= 8.1.2)
87
- activerecord (8.1.2)
88
- activemodel (= 8.1.2)
89
- activesupport (= 8.1.2)
85
+ activemodel (8.1.3.1)
86
+ activesupport (= 8.1.3.1)
87
+ activerecord (8.1.3.1)
88
+ activemodel (= 8.1.3.1)
89
+ activesupport (= 8.1.3.1)
90
90
  timeout (>= 0.4.0)
91
- activestorage (8.1.2)
92
- actionpack (= 8.1.2)
93
- activejob (= 8.1.2)
94
- activerecord (= 8.1.2)
95
- activesupport (= 8.1.2)
91
+ activestorage (8.1.3.1)
92
+ actionpack (= 8.1.3.1)
93
+ activejob (= 8.1.3.1)
94
+ activerecord (= 8.1.3.1)
95
+ activesupport (= 8.1.3.1)
96
96
  marcel (~> 1.0)
97
- activesupport (8.1.2)
97
+ activesupport (8.1.3.1)
98
98
  base64
99
99
  bigdecimal
100
100
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -107,18 +107,21 @@ GEM
107
107
  securerandom (>= 0.3)
108
108
  tzinfo (~> 2.0, >= 2.0.5)
109
109
  uri (>= 0.13.1)
110
+ acts_as_list (1.2.6)
111
+ activerecord (>= 6.1)
112
+ activesupport (>= 6.1)
110
113
  addressable (2.9.0)
111
114
  public_suffix (>= 2.0.2, < 8.0)
112
- ansi (1.5.0)
115
+ ansi (1.6.0)
113
116
  appraisal (2.5.0)
114
117
  bundler
115
118
  rake
116
119
  thor (>= 0.14.0)
117
120
  ast (2.4.3)
118
121
  base64 (0.3.0)
119
- bcrypt (3.1.21)
120
- bigdecimal (4.0.1)
121
- brakeman (8.0.4)
122
+ bcrypt (3.1.22)
123
+ bigdecimal (4.1.2)
124
+ brakeman (8.0.5)
122
125
  racc
123
126
  builder (3.3.0)
124
127
  bundle-audit (0.2.0)
@@ -140,65 +143,66 @@ GEM
140
143
  activesupport (>= 3.0.0)
141
144
  railties (>= 3.0.0)
142
145
  thor (>= 0.14.6)
143
- concurrent-ruby (1.3.6)
146
+ concurrent-ruby (1.3.8)
144
147
  connection_pool (3.0.2)
145
148
  content_disposition (1.0.0)
146
- crass (1.0.6)
147
- csv (3.3.5)
149
+ crass (1.0.7)
150
+ csv (3.3.6)
148
151
  date (3.5.1)
149
152
  down (5.6.0)
150
153
  addressable (~> 2.8)
151
154
  base64 (~> 0.3)
152
155
  drb (2.2.3)
153
- erb (6.0.2)
156
+ erb (6.0.6)
154
157
  erubi (1.13.1)
155
- ffi (1.17.3-arm64-darwin)
158
+ ffi (1.17.4-arm64-darwin)
159
+ ffi (1.17.4-x86_64-linux-gnu)
156
160
  fiber-local (1.1.0)
157
161
  fiber-storage
158
162
  fiber-storage (1.0.1)
159
- globalid (1.3.0)
163
+ globalid (1.4.0)
160
164
  activesupport (>= 6.1)
161
- i18n (1.14.8)
165
+ i18n (1.15.2)
162
166
  concurrent-ruby (~> 1.0)
163
167
  importmap-rails (2.2.3)
164
168
  actionpack (>= 6.0.0)
165
169
  activesupport (>= 6.0.0)
166
170
  railties (>= 6.0.0)
167
171
  io-console (0.8.2)
168
- irb (1.17.0)
172
+ irb (1.18.0)
169
173
  pp (>= 0.6.0)
170
174
  prism (>= 1.3.0)
171
175
  rdoc (>= 4.0.0)
172
176
  reline (>= 0.4.2)
173
- json (2.19.1)
174
- language_server-protocol (3.17.0.5)
177
+ json (2.21.2)
178
+ language_server-protocol (3.17.0.6)
175
179
  lint_roller (1.1.0)
176
180
  listen (3.10.0)
177
181
  logger
178
182
  rb-fsevent (~> 0.10, >= 0.10.3)
179
183
  rb-inotify (~> 0.9, >= 0.9.10)
180
184
  logger (1.7.0)
181
- loofah (2.25.0)
185
+ loofah (2.25.2)
182
186
  crass (~> 1.0.2)
183
187
  nokogiri (>= 1.12.0)
184
- mail (2.9.0)
188
+ mail (2.9.1)
185
189
  logger
186
190
  mini_mime (>= 0.1.1)
187
191
  net-imap
188
192
  net-pop
189
193
  net-smtp
190
- marcel (1.1.0)
194
+ marcel (1.2.1)
191
195
  matrix (0.4.3)
192
196
  mini_mime (1.1.5)
193
- minitest (6.0.2)
197
+ minitest (6.0.6)
194
198
  drb (~> 2.0)
195
199
  prism (~> 1.5)
196
- minitest-reporters (1.7.1)
200
+ minitest-reporters (1.8.0)
197
201
  ansi
198
202
  builder
199
- minitest (>= 5.0)
203
+ minitest (>= 5.0, < 7)
200
204
  ruby-progressbar
201
- net-imap (0.6.3)
205
+ net-imap (0.6.6)
202
206
  date
203
207
  net-protocol
204
208
  net-pop (0.1.2)
@@ -208,14 +212,16 @@ GEM
208
212
  net-smtp (0.5.1)
209
213
  net-protocol
210
214
  nio4r (2.7.5)
211
- nokogiri (1.19.1-arm64-darwin)
215
+ nokogiri (1.19.4-arm64-darwin)
216
+ racc (~> 1.4)
217
+ nokogiri (1.19.4-x86_64-linux-gnu)
212
218
  racc (~> 1.4)
213
- pagy (43.3.3)
219
+ pagy (43.6.1)
214
220
  json
215
221
  uri
216
222
  yaml
217
- parallel (1.27.0)
218
- parser (3.3.10.2)
223
+ parallel (2.1.0)
224
+ parser (3.3.12.0)
219
225
  ast (~> 2.4.1)
220
226
  racc
221
227
  pastel (0.8.0)
@@ -255,55 +261,52 @@ GEM
255
261
  phlexi-display
256
262
  phlexi-field (~> 0.2.0)
257
263
  zeitwerk
258
- pp (0.6.3)
264
+ pp (0.6.4)
259
265
  prettyprint
260
266
  prettyprint (0.2.0)
261
267
  prism (1.9.0)
262
- propshaft (1.3.1)
268
+ propshaft (1.3.2)
263
269
  actionpack (>= 7.0.0)
264
270
  activesupport (>= 7.0.0)
265
271
  rack
266
- psych (5.3.1)
267
- date
268
- stringio
269
272
  public_suffix (7.0.5)
270
- puma (7.2.0)
273
+ puma (8.0.2)
271
274
  nio4r (~> 2.0)
272
275
  rabl (0.17.0)
273
276
  activesupport (>= 2.3.14)
274
277
  racc (1.8.1)
275
- rack (3.2.5)
276
- rack-session (2.1.1)
278
+ rack (3.2.6)
279
+ rack-session (2.1.2)
277
280
  base64 (>= 0.1.0)
278
281
  rack (>= 3.0.0)
279
282
  rack-test (2.2.0)
280
283
  rack (>= 1.3)
281
284
  rackup (2.3.1)
282
285
  rack (>= 3)
283
- rails (8.1.2)
284
- actioncable (= 8.1.2)
285
- actionmailbox (= 8.1.2)
286
- actionmailer (= 8.1.2)
287
- actionpack (= 8.1.2)
288
- actiontext (= 8.1.2)
289
- actionview (= 8.1.2)
290
- activejob (= 8.1.2)
291
- activemodel (= 8.1.2)
292
- activerecord (= 8.1.2)
293
- activestorage (= 8.1.2)
294
- activesupport (= 8.1.2)
286
+ rails (8.1.3.1)
287
+ actioncable (= 8.1.3.1)
288
+ actionmailbox (= 8.1.3.1)
289
+ actionmailer (= 8.1.3.1)
290
+ actionpack (= 8.1.3.1)
291
+ actiontext (= 8.1.3.1)
292
+ actionview (= 8.1.3.1)
293
+ activejob (= 8.1.3.1)
294
+ activemodel (= 8.1.3.1)
295
+ activerecord (= 8.1.3.1)
296
+ activestorage (= 8.1.3.1)
297
+ activesupport (= 8.1.3.1)
295
298
  bundler (>= 1.15.0)
296
- railties (= 8.1.2)
299
+ railties (= 8.1.3.1)
297
300
  rails-dom-testing (2.3.0)
298
301
  activesupport (>= 5.0.0)
299
302
  minitest
300
303
  nokogiri (>= 1.6)
301
- rails-html-sanitizer (1.7.0)
302
- loofah (~> 2.25)
304
+ rails-html-sanitizer (1.7.1)
305
+ loofah (~> 2.25, >= 2.25.2)
303
306
  nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
304
- railties (8.1.2)
305
- actionpack (= 8.1.2)
306
- activesupport (= 8.1.2)
307
+ railties (8.1.3.1)
308
+ actionpack (= 8.1.3.1)
309
+ activesupport (= 8.1.3.1)
307
310
  irb (~> 1.13)
308
311
  rackup (>= 1.0.0)
309
312
  rake (>= 12.2)
@@ -311,31 +314,36 @@ GEM
311
314
  tsort (>= 0.2)
312
315
  zeitwerk (~> 2.6)
313
316
  rainbow (3.1.1)
314
- rake (13.3.1)
317
+ rake (13.4.2)
315
318
  rb-fsevent (0.11.2)
316
319
  rb-inotify (0.11.1)
317
320
  ffi (~> 1.0)
318
- rdoc (7.2.0)
321
+ rbs (4.1.1)
322
+ logger
323
+ prism (>= 1.6.0)
324
+ tsort
325
+ rdoc (8.0.0)
319
326
  erb
320
- psych (>= 4.0.0)
327
+ prism (>= 1.6.0)
328
+ rbs (>= 4.0.0)
321
329
  tsort
322
330
  redcarpet (3.6.1)
323
331
  refract (1.1.0)
324
332
  prism
325
333
  zeitwerk
326
- regexp_parser (2.11.3)
334
+ regexp_parser (2.12.0)
327
335
  reline (0.6.3)
328
336
  io-console (~> 0.5)
329
337
  rexml (3.4.4)
330
- roda (3.102.0)
338
+ roda (3.106.0)
331
339
  rack
332
- rodauth (2.42.0)
340
+ rodauth (2.45.0)
333
341
  roda (>= 2.6.0)
334
342
  sequel (>= 4)
335
- rodauth-model (0.4.0)
343
+ rodauth-model (0.5.0)
336
344
  rodauth (~> 2.28)
337
- rodauth-rails (2.1.2)
338
- railties (>= 5.1)
345
+ rodauth-rails (2.2.0)
346
+ railties (>= 5.2)
339
347
  roda (~> 3.76)
340
348
  rodauth (~> 2.36)
341
349
  rodauth-model (~> 0.2)
@@ -344,49 +352,50 @@ GEM
344
352
  chunky_png (~> 1.0)
345
353
  rqrcode_core (~> 2.0)
346
354
  rqrcode_core (2.1.0)
347
- rubocop (1.84.2)
355
+ rubocop (1.88.2)
348
356
  json (~> 2.3)
349
357
  language_server-protocol (~> 3.17.0.2)
350
358
  lint_roller (~> 1.1.0)
351
- parallel (~> 1.10)
359
+ parallel (>= 1.10)
352
360
  parser (>= 3.3.0.2)
353
361
  rainbow (>= 2.2.2, < 4.0)
354
362
  regexp_parser (>= 2.9.3, < 3.0)
355
363
  rubocop-ast (>= 1.49.0, < 2.0)
356
364
  ruby-progressbar (~> 1.7)
357
365
  unicode-display_width (>= 2.4.0, < 4.0)
358
- rubocop-ast (1.49.1)
366
+ rubocop-ast (1.50.0)
359
367
  parser (>= 3.3.7.2)
360
368
  prism (~> 1.7)
361
369
  rubocop-performance (1.26.1)
362
370
  lint_roller (~> 1.1)
363
371
  rubocop (>= 1.75.0, < 2.0)
364
372
  rubocop-ast (>= 1.47.1, < 2.0)
365
- ruby-next-core (1.2.0)
373
+ ruby-next-core (1.2.1)
366
374
  ruby-progressbar (1.13.0)
367
- rubyzip (3.2.2)
375
+ rubyzip (3.4.1)
368
376
  securerandom (0.4.1)
369
- selenium-webdriver (4.43.0)
377
+ selenium-webdriver (4.46.0)
370
378
  base64 (~> 0.2)
371
379
  logger (~> 1.4)
372
380
  rexml (~> 3.2, >= 3.2.5)
373
381
  rubyzip (>= 1.2.2, < 4.0)
374
382
  websocket (~> 1.0)
375
383
  semantic_range (3.1.1)
376
- sequel (5.102.0)
384
+ sequel (5.107.0)
377
385
  bigdecimal
378
386
  sequel-activerecord_connection (2.0.1)
379
387
  activerecord (>= 5.1)
380
388
  sequel (~> 5.38)
381
- shrine (3.7.1)
389
+ shrine (3.9.0)
382
390
  content_disposition (~> 1.0)
383
391
  down (~> 5.1)
384
392
  sin_lru_redux (2.5.2)
385
- sqlite3 (2.9.1-arm64-darwin)
386
- standard (1.54.0)
393
+ sqlite3 (2.9.5-arm64-darwin)
394
+ sqlite3 (2.9.5-x86_64-linux-gnu)
395
+ standard (1.56.0)
387
396
  language_server-protocol (~> 3.17.0.2)
388
397
  lint_roller (~> 1.0)
389
- rubocop (~> 1.84.0)
398
+ rubocop (~> 1.88.0)
390
399
  standard-custom (~> 1.0.0)
391
400
  standard-performance (~> 1.8)
392
401
  standard-custom (1.0.2)
@@ -397,11 +406,10 @@ GEM
397
406
  rubocop-performance (~> 1.26.0)
398
407
  stimulus-rails (1.3.4)
399
408
  railties (>= 6.0.0)
400
- stringio (3.2.0)
401
- tailwind_merge (1.4.0)
409
+ tailwind_merge (1.5.2)
402
410
  sin_lru_redux (~> 2.5)
403
411
  thor (1.5.0)
404
- tilt (2.7.0)
412
+ tilt (2.8.0)
405
413
  timeout (0.6.1)
406
414
  tsort (0.2.0)
407
415
  tty-color (0.6.0)
@@ -425,7 +433,7 @@ GEM
425
433
  uri (1.1.1)
426
434
  useragent (0.16.11)
427
435
  websocket (1.2.11)
428
- websocket-driver (0.8.0)
436
+ websocket-driver (0.8.2)
429
437
  base64
430
438
  websocket-extensions (>= 0.1.0)
431
439
  websocket-extensions (0.1.5)
@@ -433,13 +441,15 @@ GEM
433
441
  xpath (3.2.0)
434
442
  nokogiri (~> 1.8)
435
443
  yaml (0.4.0)
436
- zeitwerk (2.7.5)
444
+ zeitwerk (2.8.3)
437
445
 
438
446
  PLATFORMS
439
447
  arm64-darwin
448
+ x86_64-linux
440
449
 
441
450
  DEPENDENCIES
442
451
  active_shrine
452
+ acts_as_list
443
453
  appraisal
444
454
  bcrypt
445
455
  brakeman
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../lib/plutonium_generators"
4
+
5
+ module Pu
6
+ module AsyncInteractions
7
+ class InstallGenerator < Rails::Generators::Base
8
+ include PlutoniumGenerators::Generator
9
+ include PlutoniumGenerators::Concerns::ResourceRegistration
10
+ include PlutoniumGenerators::Concerns::ConfiguresRecurring
11
+
12
+ source_root File.expand_path("templates", __dir__)
13
+
14
+ desc(
15
+ "Enable async interactions, schedule the reaper, and connect the run resource to a portal\n\n" \
16
+ "e.g. rails g pu:async_interactions:install --dest=admin_portal\n" \
17
+ " rails g pu:async_interactions:install --skip-portal # app-wide only"
18
+ )
19
+
20
+ class_option :schedule, type: :string, default: "every 15 minutes",
21
+ desc: "Cron-style schedule for the ReapJob recurring task"
22
+
23
+ class_option :skip_portal, type: :boolean, default: false,
24
+ desc: "Enable the subsystem without connecting the run resource to a portal"
25
+
26
+ def start
27
+ enable_async_interactions
28
+ connect_portal unless options[:skip_portal]
29
+ schedule_reap_job
30
+ migration_reminder
31
+ rescue => e
32
+ exception "#{self.class} failed:", e
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :app_namespace
38
+
39
+ # The flag gates the MIGRATION as well as the behaviour: while it is off
40
+ # the runs migration path is never registered, so plutonium_async_runs does
41
+ # not exist and dispatching raises NotEnabledError.
42
+ def enable_async_interactions
43
+ configure_plutonium "config.async_interactions.enabled = true"
44
+ end
45
+
46
+ # The half that needs somewhere to route to.
47
+ #
48
+ # Skippable because a fresh app has no portals yet — only main_app, which
49
+ # is the wrong home for a run resource in an app that is about to grow
50
+ # portals. So an installer running before any portal exists (a project
51
+ # template, say) can turn the subsystem on and leave connecting for later,
52
+ # when there is a portal worth naming.
53
+ def connect_portal
54
+ @app_namespace = portal_option(:dest, prompt: "Select destination portal").camelize
55
+
56
+ template "app/controllers/async_runs_controller.rb", controller_path
57
+ register_resource_in_routes(routes_path, "Plutonium::Interaction::Async::Run")
58
+ end
59
+
60
+ # Not run for you: the migration path is registered from the initializer at
61
+ # BOOT, and this process booted before the flag was written.
62
+ def migration_reminder
63
+ log :info, "async interactions enabled — run `rails db:migrate` to create plutonium_async_runs"
64
+ return unless options[:skip_portal]
65
+
66
+ log :info, "connect the run resource to a portal later: " \
67
+ "rails g pu:async_interactions:install --dest=<portal>"
68
+ end
69
+
70
+ # main_app is a valid destination (see PackageSelector#available_portals),
71
+ # and it has neither a packages/ tree nor a Concerns::Controller to include.
72
+ def main_app? = app_namespace == "MainApp"
73
+
74
+ def controller_path
75
+ return "app/controllers/async_runs_controller.rb" if main_app?
76
+
77
+ "packages/#{package_namespace}/app/controllers/#{package_namespace}/async_runs_controller.rb"
78
+ end
79
+
80
+ def routes_path
81
+ return "config/routes.rb" if main_app?
82
+
83
+ "packages/#{package_namespace}/config/routes.rb"
84
+ end
85
+
86
+ def package_namespace
87
+ app_namespace.underscore
88
+ end
89
+
90
+ # ReapJob is app-wide, not per-portal — idempotent via add_recurring_tasks,
91
+ # so running this generator against a second portal is a harmless no-op here.
92
+ def schedule_reap_job
93
+ if gem_in_bundle?("solid_queue")
94
+ unless add_recurring_tasks(reap_job_task_yaml, marker: "reap_stalled_async_runs")
95
+ log :skip, "ReapJob not scheduled (config/recurring.yml missing or already scheduled)"
96
+ end
97
+ else
98
+ log :info, "solid_queue not found — schedule Plutonium::Interaction::Async::ReapJob yourself (see docs)"
99
+ end
100
+ end
101
+
102
+ def reap_job_task_yaml
103
+ <<~YAML
104
+ reap_stalled_async_runs:
105
+ class: Plutonium::Interaction::Async::ReapJob
106
+ schedule: "#{options[:schedule]}"
107
+ YAML
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,15 @@
1
+ <%- if main_app? -%>
2
+ class AsyncRunsController < ::ResourceController
3
+ # The controller's name doesn't match Run's real, namespaced class
4
+ # (Plutonium::Interaction::Async::Run), so inference can't find it on its own.
5
+ controller_for ::Plutonium::Interaction::Async::Run
6
+ end
7
+ <%- else -%>
8
+ class <%= app_namespace %>::AsyncRunsController < ::<%= app_namespace %>::ResourceController
9
+ # The controller's name doesn't match Run's real, namespaced class
10
+ # (Plutonium::Interaction::Async::Run), so inference can't find it on its own.
11
+ controller_for ::Plutonium::Interaction::Async::Run
12
+
13
+ include <%= app_namespace %>::Concerns::Controller
14
+ end
15
+ <%- end -%>
@@ -209,7 +209,9 @@ module Pu
209
209
 
210
210
  {
211
211
  name: name.to_s,
212
- as: input_config[:as]&.to_s,
212
+ # Only an alias `as:` names a type here; a component-class `as:` says
213
+ # nothing about the wire format, so it contributes nothing.
214
+ as: Plutonium::Definition::InputAliases.resolve(input_config[:as])&.to_s,
213
215
  required: !input_config[:optional],
214
216
  type: determine_input_type(name, input_config, column, assoc, resource),
215
217
  typespec_type: determine_typespec_input_type(name, input_config, column, assoc, resource),
@@ -222,7 +224,8 @@ module Pu
222
224
  end
223
225
 
224
226
  def determine_input_type(name, config, column, assoc, resource)
225
- return config[:as].to_s if config[:as]
227
+ input_alias = Plutonium::Definition::InputAliases.resolve(config[:as])
228
+ return input_alias.to_s if input_alias
226
229
  return "association" if assoc
227
230
  return "enum" if resource.defined_enums.key?(name.to_s)
228
231
  return column.type.to_s if column
@@ -242,8 +245,8 @@ module Pu
242
245
  # Use column type
243
246
  return TYPE_MAPPING[column.type] || "string" if column
244
247
 
245
- # Infer from as: option
246
- AS_TYPE_MAPPING[config[:as]&.to_sym] || "string"
248
+ # Infer from as: option (nil for a component-class as:, which maps to nothing)
249
+ AS_TYPE_MAPPING[Plutonium::Definition::InputAliases.resolve(config[:as])] || "string"
247
250
  end
248
251
 
249
252
  def generate_typespec_files
@@ -329,7 +329,7 @@ module Pu
329
329
  new_block = <<~RUBY.chomp
330
330
  #{indent}after_login do
331
331
  #{indent} #{existing_code}
332
- #{indent} session[:after_welcome_redirect] = session.delete(:login_redirect)
332
+ #{indent} session[:after_welcome_redirect] = session.delete(login_redirect_session_key)
333
333
  #{indent}end
334
334
  RUBY
335
335
 
@@ -345,7 +345,7 @@ module Pu
345
345
  new_block = <<~RUBY.chomp
346
346
  #{indent}after_login do
347
347
  #{block_content}
348
- #{indent} session[:after_welcome_redirect] = session.delete(:login_redirect)
348
+ #{indent} session[:after_welcome_redirect] = session.delete(login_redirect_session_key)
349
349
  #{end_indent}end
350
350
  RUBY
351
351
 
@@ -357,7 +357,7 @@ module Pu
357
357
 
358
358
  # ==> User Invites - Move captured path to session for WelcomeController
359
359
  after_login do
360
- session[:after_welcome_redirect] = session.delete(:login_redirect)
360
+ session[:after_welcome_redirect] = session.delete(login_redirect_session_key)
361
361
  end
362
362
  RUBY
363
363
 
@@ -13,8 +13,8 @@
13
13
  <%%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
14
14
  <%% end %>
15
15
  </head>
16
- <body class="antialiased min-h-screen bg-[var(--pu-body)]">
17
- <main class="p-4 min-h-screen flex flex-col items-center justify-center gap-2 px-6 py-8 mx-auto lg:py-0">
16
+ <body class="antialiased pu-min-h-viewport bg-[var(--pu-body)]">
17
+ <main class="p-4 pu-min-h-viewport flex flex-col items-center justify-center gap-2 px-6 py-8 mx-auto lg:py-0">
18
18
  <%% if flash.any? %>
19
19
  <%% flash.each do |type, message| %>
20
20
  <div class="fixed z-50 top-16 inset-x-0 mx-auto flex items-center w-full max-w-md p-4 rounded-[var(--pu-radius-lg)]"