brut 0.0.29 → 0.1.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 (491) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +23 -2
  4. data/assets/LogoStop.pxd +0 -0
  5. data/assets/MetroLogo.graffle +0 -0
  6. data/assets/SocialImage.png +0 -0
  7. data/assets/SocialImage.pxd +0 -0
  8. data/brutrb.com/.vitepress/config.mjs +45 -8
  9. data/brutrb.com/.vitepress/theme/style.css +6 -5
  10. data/brutrb.com/ai.md +10 -15
  11. data/brutrb.com/assets.md +2 -9
  12. data/brutrb.com/brut-js.md +12 -2
  13. data/brutrb.com/cli.md +9 -13
  14. data/brutrb.com/components.md +118 -96
  15. data/brutrb.com/configuration.md +3 -4
  16. data/brutrb.com/css.md +2 -2
  17. data/brutrb.com/custom-element-tests.md +3 -4
  18. data/brutrb.com/database-access.md +1 -1
  19. data/brutrb.com/database-schema.md +29 -41
  20. data/brutrb.com/dev-environment.md +7 -7
  21. data/brutrb.com/dir-structure.md +120 -0
  22. data/brutrb.com/doc-conventions.md +18 -15
  23. data/brutrb.com/dx +1 -0
  24. data/brutrb.com/end-to-end-tests.md +12 -10
  25. data/brutrb.com/features.md +373 -0
  26. data/brutrb.com/flash-and-session.md +115 -131
  27. data/brutrb.com/form-constraints.md +266 -0
  28. data/brutrb.com/forms.md +140 -765
  29. data/brutrb.com/getting-started.md +10 -11
  30. data/brutrb.com/handlers.md +119 -95
  31. data/brutrb.com/hooks.md +18 -20
  32. data/brutrb.com/i18n.md +6 -4
  33. data/brutrb.com/images/LogoStop.png +0 -0
  34. data/brutrb.com/instrumentation.md +7 -10
  35. data/brutrb.com/javascript.md +14 -14
  36. data/brutrb.com/keyword-injection.md +72 -114
  37. data/brutrb.com/layouts.md +20 -52
  38. data/brutrb.com/lsp.md +1 -1
  39. data/brutrb.com/overview.md +30 -372
  40. data/brutrb.com/pages.md +119 -207
  41. data/brutrb.com/public/SocialImage.png +0 -0
  42. data/brutrb.com/public/favicon.ico +0 -0
  43. data/brutrb.com/recipes/alternate-layouts.md +32 -0
  44. data/brutrb.com/recipes/authentication.md +315 -6
  45. data/brutrb.com/recipes/blank-layouts.md +22 -0
  46. data/brutrb.com/recipes/custom-flash.md +51 -0
  47. data/brutrb.com/recipes/indexed-forms.md +149 -0
  48. data/brutrb.com/recipes/text-field-component.md +182 -0
  49. data/brutrb.com/routes.md +56 -82
  50. data/brutrb.com/security.md +0 -3
  51. data/brutrb.com/space-time-continuum.md +8 -12
  52. data/brutrb.com/tutorial.md +1 -1
  53. data/brutrb.com/why.md +19 -0
  54. data/docs/404.html +8 -3
  55. data/docs/SocialImage.png +0 -0
  56. data/docs/ai.html +11 -6
  57. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  58. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  59. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  60. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  61. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  62. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  63. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  64. data/docs/api/Brut/BackEnd.html +1 -1
  65. data/docs/api/Brut/CLI/App.html +1 -1
  66. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  67. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  68. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  69. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
  70. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  71. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  72. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  73. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  74. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  75. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  76. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  77. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  78. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  79. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  80. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  81. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  82. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
  83. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  84. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  85. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  86. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  87. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  88. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  89. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  90. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  91. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  92. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  93. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  94. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  95. data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
  96. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  97. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  98. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  99. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  100. data/docs/api/Brut/CLI/Apps.html +1 -1
  101. data/docs/api/Brut/CLI/Command.html +1 -1
  102. data/docs/api/Brut/CLI/Error.html +1 -1
  103. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  104. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  105. data/docs/api/Brut/CLI/Executor.html +1 -1
  106. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  107. data/docs/api/Brut/CLI/Options.html +1 -1
  108. data/docs/api/Brut/CLI/Output.html +1 -1
  109. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  110. data/docs/api/Brut/CLI.html +1 -1
  111. data/docs/api/Brut/FactoryBot.html +1 -1
  112. data/docs/api/Brut/Framework/App.html +1 -1
  113. data/docs/api/Brut/Framework/Config.html +1 -1
  114. data/docs/api/Brut/Framework/Container.html +1 -1
  115. data/docs/api/Brut/Framework/Error.html +1 -1
  116. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  117. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  118. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  119. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  120. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  121. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  122. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  123. data/docs/api/Brut/Framework/Errors.html +1 -1
  124. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  125. data/docs/api/Brut/Framework/MCP.html +1 -1
  126. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  127. data/docs/api/Brut/Framework.html +1 -1
  128. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  129. data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
  130. data/docs/api/Brut/FrontEnd/Component.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  132. data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
  133. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  134. data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
  135. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  136. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +1 -1
  137. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
  138. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  143. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  146. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  147. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  148. data/docs/api/Brut/FrontEnd/Form.html +9 -11
  149. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  150. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +1 -1
  151. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
  152. data/docs/api/Brut/FrontEnd/Forms/Input.html +1 -1
  153. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
  154. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1 -1
  155. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +135 -20
  156. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  157. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +135 -20
  158. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  159. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  160. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  161. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  162. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  163. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  164. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  165. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  166. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  167. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  168. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  169. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  170. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  171. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  172. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  173. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  174. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  175. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  176. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  177. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  178. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  179. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  180. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  181. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  182. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  183. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  184. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  185. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  186. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  187. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  188. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  189. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  190. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  191. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  192. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  193. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  194. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  195. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  196. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  197. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  198. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  199. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  200. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  201. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  202. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  203. data/docs/api/Brut/FrontEnd.html +1 -1
  204. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  205. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  206. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  207. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  208. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  209. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  210. data/docs/api/Brut/I18n.html +1 -1
  211. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  212. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  213. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  214. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  215. data/docs/api/Brut/Instrumentation.html +1 -1
  216. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  217. data/docs/api/Brut/SinatraHelpers.html +1 -1
  218. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  219. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  220. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  221. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  222. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  223. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  224. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  225. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  226. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  227. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  228. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  229. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  230. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  231. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  232. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  233. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  234. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  235. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  236. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  237. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  238. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  239. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  240. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  241. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  242. data/docs/api/Brut/SpecSupport.html +1 -1
  243. data/docs/api/Brut.html +1 -1
  244. data/docs/api/Clock.html +1 -1
  245. data/docs/api/RichString.html +1 -1
  246. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  247. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  248. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  249. data/docs/api/Sequel/Extensions.html +1 -1
  250. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  251. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  252. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  253. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  254. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  255. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  256. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  257. data/docs/api/Sequel/Plugins.html +1 -1
  258. data/docs/api/Sequel.html +1 -1
  259. data/docs/api/_index.html +1 -1
  260. data/docs/api/file.README.html +22 -3
  261. data/docs/api/index.html +22 -3
  262. data/docs/api/method_list.html +16 -0
  263. data/docs/api/top-level-namespace.html +1 -1
  264. data/docs/assets/LogoStop.Gb3tDhL1.png +0 -0
  265. data/docs/assets/{ai.md._6HCDL6d.js → ai.md.Cy9GWnER.js} +1 -1
  266. data/docs/assets/ai.md.Cy9GWnER.lean.js +1 -0
  267. data/docs/assets/{app.BhrfSt68.js → app.ClaS47Ru.js} +1 -1
  268. data/docs/assets/{assets.md.D3wunzLx.js → assets.md.7C3HWkga.js} +3 -3
  269. data/docs/assets/{assets.md.D3wunzLx.lean.js → assets.md.7C3HWkga.lean.js} +1 -1
  270. data/docs/assets/{brut-js.md.o2DAO2s2.js → brut-js.md.B4GYxQVw.js} +1 -1
  271. data/docs/assets/{brut-js.md.o2DAO2s2.lean.js → brut-js.md.B4GYxQVw.lean.js} +1 -1
  272. data/docs/assets/chunks/@localSearchIndexroot.Biqy1A4t.js +1 -0
  273. data/docs/assets/chunks/{VPLocalSearchBox.Dpot_2H4.js → VPLocalSearchBox.DtgDfde2.js} +1 -1
  274. data/docs/assets/chunks/{theme.N2SNVLgU.js → theme.B45bvibT.js} +2 -2
  275. data/docs/assets/{cli.md.RmeA2b0i.js → cli.md.CjsktgFz.js} +15 -20
  276. data/docs/assets/components.md.DatoNgFo.js +96 -0
  277. data/docs/assets/{components.md.CRUMdRoN.lean.js → components.md.DatoNgFo.lean.js} +1 -1
  278. data/docs/assets/{configuration.md.LG-zIBww.js → configuration.md.DeyhpqEx.js} +3 -3
  279. data/docs/assets/{css.md.DJgj2clw.js → css.md.CltvJqAa.js} +3 -3
  280. data/docs/assets/{custom-element-tests.md.BrYJQEl3.js → custom-element-tests.md.B_rbta32.js} +3 -3
  281. data/docs/assets/{database-access.md.C7l-Vuvb.js → database-access.md.gnluu54N.js} +1 -1
  282. data/docs/assets/{database-schema.md.BUjR0VS1.js → database-schema.md.CSYk6E6v.js} +6 -6
  283. data/docs/assets/{database-schema.md.BUjR0VS1.lean.js → database-schema.md.CSYk6E6v.lean.js} +1 -1
  284. data/docs/assets/dev-environment.md.BroAOLhF.js +11 -0
  285. data/docs/assets/dir-structure.md.CWir1pic.js +46 -0
  286. data/docs/assets/dir-structure.md.CWir1pic.lean.js +1 -0
  287. data/docs/assets/doc-conventions.md.BzmSrTEW.js +1 -0
  288. data/docs/assets/doc-conventions.md.BzmSrTEW.lean.js +1 -0
  289. data/docs/assets/{end-to-end-tests.md.yfQHC0b5.js → end-to-end-tests.md.DzqRpZ43.js} +5 -3
  290. data/docs/assets/end-to-end-tests.md.DzqRpZ43.lean.js +1 -0
  291. data/docs/assets/features.md.DPFXsy0z.js +154 -0
  292. data/docs/assets/features.md.DPFXsy0z.lean.js +1 -0
  293. data/docs/assets/flash-and-session.md.nPvUpnUx.js +79 -0
  294. data/docs/assets/{flash-and-session.md.BXY8RvT0.lean.js → flash-and-session.md.nPvUpnUx.lean.js} +1 -1
  295. data/docs/assets/form-constraints.md.x5tNpTTI.js +90 -0
  296. data/docs/assets/form-constraints.md.x5tNpTTI.lean.js +1 -0
  297. data/docs/assets/forms.md.C2Dizvzq.js +64 -0
  298. data/docs/assets/forms.md.C2Dizvzq.lean.js +1 -0
  299. data/docs/assets/{getting-started.md.Dj0qtZI2.js → getting-started.md.C93e0odB.js} +5 -5
  300. data/docs/assets/{getting-started.md.Dj0qtZI2.lean.js → getting-started.md.C93e0odB.lean.js} +1 -1
  301. data/docs/assets/handlers.md.Chyri6KA.js +54 -0
  302. data/docs/assets/handlers.md.Chyri6KA.lean.js +1 -0
  303. data/docs/assets/{hooks.md.C4-moMny.js → hooks.md.Jmb5VOLA.js} +4 -4
  304. data/docs/assets/{hooks.md.C4-moMny.lean.js → hooks.md.Jmb5VOLA.lean.js} +1 -1
  305. data/docs/assets/{i18n.md.Do9i1qWl.js → i18n.md.xQhiGo1G.js} +2 -2
  306. data/docs/assets/{i18n.md.Do9i1qWl.lean.js → i18n.md.xQhiGo1G.lean.js} +1 -1
  307. data/docs/assets/{index.md.CuBB-BdM.js → index.md.CAMqGBJE.js} +1 -1
  308. data/docs/assets/{index.md.CuBB-BdM.lean.js → index.md.CAMqGBJE.lean.js} +1 -1
  309. data/docs/assets/{instrumentation.md.a9Pjps4P.js → instrumentation.md.BgcaGVYH.js} +2 -2
  310. data/docs/assets/{instrumentation.md.a9Pjps4P.lean.js → instrumentation.md.BgcaGVYH.lean.js} +1 -1
  311. data/docs/assets/{javascript.md.GWbhRS51.js → javascript.md.DzrMxUmI.js} +7 -7
  312. data/docs/assets/{javascript.md.GWbhRS51.lean.js → javascript.md.DzrMxUmI.lean.js} +1 -1
  313. data/docs/assets/keyword-injection.md.95Zgh2eN.js +21 -0
  314. data/docs/assets/{keyword-injection.md.Dt2tKREs.lean.js → keyword-injection.md.95Zgh2eN.lean.js} +1 -1
  315. data/docs/assets/{layouts.md.cPnh3NId.js → layouts.md.CJGDFY-m.js} +2 -15
  316. data/docs/assets/layouts.md.CJGDFY-m.lean.js +1 -0
  317. data/docs/assets/{lsp.md.Bsu-f6VU.js → lsp.md.Dn1rIiW0.js} +1 -1
  318. data/docs/assets/{lsp.md.Bsu-f6VU.lean.js → lsp.md.Dn1rIiW0.lean.js} +1 -1
  319. data/docs/assets/overview.md.Bdq4qt3L.js +1 -0
  320. data/docs/assets/overview.md.Bdq4qt3L.lean.js +1 -0
  321. data/docs/assets/pages.md.B7Hc-i6H.js +45 -0
  322. data/docs/assets/pages.md.B7Hc-i6H.lean.js +1 -0
  323. data/docs/assets/recipes_alternate-layouts.md.BwEytl59.js +22 -0
  324. data/docs/assets/recipes_alternate-layouts.md.BwEytl59.lean.js +1 -0
  325. data/docs/assets/recipes_authentication.md.Dzvi_g69.js +156 -0
  326. data/docs/assets/recipes_authentication.md.Dzvi_g69.lean.js +1 -0
  327. data/docs/assets/recipes_blank-layouts.md.fyAUJyJR.js +15 -0
  328. data/docs/assets/recipes_blank-layouts.md.fyAUJyJR.lean.js +1 -0
  329. data/docs/assets/recipes_custom-flash.md.CrQbI5eH.js +26 -0
  330. data/docs/assets/recipes_custom-flash.md.CrQbI5eH.lean.js +1 -0
  331. data/docs/assets/recipes_indexed-forms.md.CstYyOSo.js +74 -0
  332. data/docs/assets/recipes_indexed-forms.md.CstYyOSo.lean.js +1 -0
  333. data/docs/assets/recipes_text-field-component.md.H4wLAK0Z.js +101 -0
  334. data/docs/assets/recipes_text-field-component.md.H4wLAK0Z.lean.js +1 -0
  335. data/docs/assets/routes.md.B8kfUPHU.js +21 -0
  336. data/docs/assets/{routes.md.BMM7peut.lean.js → routes.md.B8kfUPHU.lean.js} +1 -1
  337. data/docs/assets/{security.md.C668yXCi.js → security.md.C0G_AZR-.js} +1 -1
  338. data/docs/assets/{security.md.C668yXCi.lean.js → security.md.C0G_AZR-.lean.js} +1 -1
  339. data/docs/assets/space-time-continuum.md.xl44xDos.js +1 -0
  340. data/docs/assets/{space-time-continuum.md.KPUIKysQ.lean.js → space-time-continuum.md.xl44xDos.lean.js} +1 -1
  341. data/docs/assets/{style.B2o1L9eN.css → style.prAgp4yQ.css} +1 -1
  342. data/docs/assets/tutorial.md.a4a0eVOy.js +1 -0
  343. data/docs/assets/tutorial.md.a4a0eVOy.lean.js +1 -0
  344. data/docs/assets/why.md.C-hk5xgJ.js +1 -0
  345. data/docs/assets/why.md.C-hk5xgJ.lean.js +1 -0
  346. data/docs/assets.html +12 -7
  347. data/docs/brut-js/api/AjaxSubmit.html +1 -1
  348. data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
  349. data/docs/brut-js/api/Autosubmit.html +1 -1
  350. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  351. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  352. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  353. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  354. data/docs/brut-js/api/BufferedLogger.html +1 -1
  355. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  356. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  357. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  358. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  359. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  360. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  361. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  362. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  363. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  364. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  365. data/docs/brut-js/api/Form.html +1 -1
  366. data/docs/brut-js/api/Form.js.html +1 -1
  367. data/docs/brut-js/api/I18nTranslation.html +1 -1
  368. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  369. data/docs/brut-js/api/LocaleDetection.html +1 -1
  370. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  371. data/docs/brut-js/api/Logger.html +1 -1
  372. data/docs/brut-js/api/Logger.js.html +1 -1
  373. data/docs/brut-js/api/Message.html +1 -1
  374. data/docs/brut-js/api/Message.js.html +1 -1
  375. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  376. data/docs/brut-js/api/RichString.html +1 -1
  377. data/docs/brut-js/api/RichString.js.html +1 -1
  378. data/docs/brut-js/api/Tabs.html +1 -1
  379. data/docs/brut-js/api/Tabs.js.html +1 -1
  380. data/docs/brut-js/api/Tracing.html +1 -1
  381. data/docs/brut-js/api/Tracing.js.html +1 -1
  382. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  383. data/docs/brut-js/api/external-Performance.html +1 -1
  384. data/docs/brut-js/api/external-Promise.html +1 -1
  385. data/docs/brut-js/api/external-ValidityState.html +1 -1
  386. data/docs/brut-js/api/external-Window.html +1 -1
  387. data/docs/brut-js/api/external-fetch.html +1 -1
  388. data/docs/brut-js/api/global.html +1 -1
  389. data/docs/brut-js/api/index.html +1 -1
  390. data/docs/brut-js/api/index.js.html +1 -1
  391. data/docs/brut-js/api/module-testing.html +1 -1
  392. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  393. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  394. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  395. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  396. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  397. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  398. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  399. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  400. data/docs/brut-js/api/testing_index.js.html +1 -1
  401. data/docs/brut-js.html +12 -7
  402. data/docs/business-logic.html +10 -5
  403. data/docs/cli.html +26 -26
  404. data/docs/components.html +61 -64
  405. data/docs/configuration.html +13 -8
  406. data/docs/css.html +14 -9
  407. data/docs/custom-element-tests.html +14 -9
  408. data/docs/database-access.html +12 -7
  409. data/docs/database-schema.html +15 -10
  410. data/docs/deployment.html +10 -5
  411. data/docs/dev-environment.html +12 -7
  412. data/docs/dir-structure.html +74 -0
  413. data/docs/doc-conventions.html +11 -6
  414. data/docs/end-to-end-tests.html +15 -8
  415. data/docs/favicon.ico +0 -0
  416. data/docs/features.html +182 -0
  417. data/docs/flash-and-session.html +73 -82
  418. data/docs/form-constraints.html +118 -0
  419. data/docs/forms.html +57 -367
  420. data/docs/getting-started.html +15 -10
  421. data/docs/handlers.html +51 -61
  422. data/docs/hashmap.json +1 -1
  423. data/docs/hooks.html +14 -9
  424. data/docs/i18n.html +12 -7
  425. data/docs/index.html +11 -6
  426. data/docs/instrumentation.html +12 -7
  427. data/docs/javascript.html +17 -12
  428. data/docs/jobs.html +10 -5
  429. data/docs/keyword-injection.html +22 -21
  430. data/docs/layouts.html +12 -20
  431. data/docs/lsp.html +11 -6
  432. data/docs/markdown-examples.html +10 -5
  433. data/docs/middleware.html +10 -5
  434. data/docs/not-released.html +10 -5
  435. data/docs/overview.html +11 -138
  436. data/docs/pages.html +49 -121
  437. data/docs/recipes/alternate-layouts.html +50 -0
  438. data/docs/recipes/authentication.html +166 -6
  439. data/docs/recipes/blank-layouts.html +43 -0
  440. data/docs/recipes/custom-flash.html +54 -0
  441. data/docs/recipes/indexed-forms.html +102 -0
  442. data/docs/recipes/text-field-component.html +129 -0
  443. data/docs/routes.html +16 -19
  444. data/docs/security.html +11 -6
  445. data/docs/seed-data.html +10 -5
  446. data/docs/space-time-continuum.html +11 -6
  447. data/docs/tutorial.html +11 -6
  448. data/docs/unit-tests.html +10 -5
  449. data/docs/why.html +29 -0
  450. data/lib/brut/front_end/form.rb +8 -8
  451. data/lib/brut/front_end/forms/radio_button_group_input.rb +8 -1
  452. data/lib/brut/front_end/forms/select_input.rb +8 -1
  453. data/lib/brut/version.rb +1 -1
  454. data/specs/brut/front_end/forms/radio_button_group_input.spec.rb +54 -0
  455. data/specs/brut/front_end/forms/select_input.spec.rb +54 -0
  456. metadata +117 -75
  457. data/brutrb.com/public/images/logo-300.png +0 -0
  458. data/brutrb.com/public/images/logo.png +0 -0
  459. data/docs/assets/LogoStop.X8x-4riz.png +0 -0
  460. data/docs/assets/ai.md._6HCDL6d.lean.js +0 -1
  461. data/docs/assets/chunks/@localSearchIndexroot.CeRAdP1K.js +0 -1
  462. data/docs/assets/components.md.CRUMdRoN.js +0 -104
  463. data/docs/assets/dev-environment.md.GZv6xvi9.js +0 -11
  464. data/docs/assets/doc-conventions.md.-kN3Xo5C.js +0 -1
  465. data/docs/assets/doc-conventions.md.-kN3Xo5C.lean.js +0 -1
  466. data/docs/assets/end-to-end-tests.md.yfQHC0b5.lean.js +0 -1
  467. data/docs/assets/flash-and-session.md.BXY8RvT0.js +0 -93
  468. data/docs/assets/forms.md.B-koVgyw.js +0 -379
  469. data/docs/assets/forms.md.B-koVgyw.lean.js +0 -1
  470. data/docs/assets/handlers.md.089DVD3v.js +0 -69
  471. data/docs/assets/handlers.md.089DVD3v.lean.js +0 -1
  472. data/docs/assets/keyword-injection.md.Dt2tKREs.js +0 -25
  473. data/docs/assets/layouts.md.cPnh3NId.lean.js +0 -1
  474. data/docs/assets/overview.md.DVKRM8zl.js +0 -133
  475. data/docs/assets/overview.md.DVKRM8zl.lean.js +0 -1
  476. data/docs/assets/pages.md.BE3kfOc5.js +0 -122
  477. data/docs/assets/pages.md.BE3kfOc5.lean.js +0 -1
  478. data/docs/assets/recipes_authentication.md.CAsXf7hk.js +0 -1
  479. data/docs/assets/recipes_authentication.md.CAsXf7hk.lean.js +0 -1
  480. data/docs/assets/routes.md.BMM7peut.js +0 -29
  481. data/docs/assets/space-time-continuum.md.KPUIKysQ.js +0 -1
  482. data/docs/assets/tutorial.md.BnoGjrdK.js +0 -1
  483. data/docs/assets/tutorial.md.BnoGjrdK.lean.js +0 -1
  484. data/docs/images/logo-300.png +0 -0
  485. data/docs/images/logo.png +0 -0
  486. /data/docs/assets/{cli.md.RmeA2b0i.lean.js → cli.md.CjsktgFz.lean.js} +0 -0
  487. /data/docs/assets/{configuration.md.LG-zIBww.lean.js → configuration.md.DeyhpqEx.lean.js} +0 -0
  488. /data/docs/assets/{css.md.DJgj2clw.lean.js → css.md.CltvJqAa.lean.js} +0 -0
  489. /data/docs/assets/{custom-element-tests.md.BrYJQEl3.lean.js → custom-element-tests.md.B_rbta32.lean.js} +0 -0
  490. /data/docs/assets/{database-access.md.C7l-Vuvb.lean.js → database-access.md.gnluu54N.lean.js} +0 -0
  491. /data/docs/assets/{dev-environment.md.GZv6xvi9.lean.js → dev-environment.md.BroAOLhF.lean.js} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b77e4a2a30122b1805ecf5cb52a5313bb137c188364a6baac62270918cd1640
4
- data.tar.gz: 393ad694666d889d5672fb592e7f188e19f78f519f19fbd4a5edb1af32ef5d21
3
+ metadata.gz: ac39a29b335413475986e02cf9baa658bc44fa2e67e6dbd60dfafa7e4b9c8a2f
4
+ data.tar.gz: 155a9b104168bd18fe08fd8286576d63305d243d6025a4a1fcbcf1611611a8ed
5
5
  SHA512:
6
- metadata.gz: 769c54918d2cb9a3e40e007d954c70ef6ec4033bb1a159d616cc4680aaf5b5d650e0335d729a5acc10b0d9f96574c4dfe5ec5f563bbd82828c6878ea4a532338
7
- data.tar.gz: 510b4299c42f476f61110382afdb26c1c71ae8f6aecf16f95bebcf30bb5ac14b0817b08bb0787e3b9b541be59add9e35d4d60df78debbe0627b371aa4639ce00
6
+ metadata.gz: 6126e575575511de23bd9ffe1429f05fa017664c66f7de07fa93b000edfb4f4dfd7c619d76d02d28e69cb70bfe163c4023d7a79cc53e1e41e3a44b5882cd0fd8
7
+ data.tar.gz: d38e331cd221d1a3bbd666b83a0d3e2df5a17fd4cbdccc47da7ca0df484d8ff46fb91c2f159c9da4fefcb8c829b98a8de4a040f013676170befecc0bcc45c3d1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- brut (0.0.29)
4
+ brut (0.1.0)
5
5
  concurrent-ruby
6
6
  i18n
7
7
  irb
data/README.md CHANGED
@@ -3,6 +3,8 @@
3
3
  Brut is a way to make web apps with Ruby, captializing on the knowledge you have—HTTP, HTML, CSS, JavaScript, SQL—without requiring
4
4
  *too* much extra stuff to learn.
5
5
 
6
+ ![Brut Logo in the style of the Washington DC Metro. It has the metro brown background with all text in white Helvetica. Centered at the top is "BrutRB". Below that, in the style of metro stops is "Ruby", next to a red dot with "RB" in it, "HTML/CSS/JS" next to an orange dot with "WP" in it, "Phlex" next to a blue dot with "PL" in it, and "RSpec" next to a green dot with "RS" in it.](brutrb.com/images/LogoStop.png)
7
+
6
8
  ## Installation
7
9
 
8
10
  Add this line to your application's Gemfile:
@@ -19,6 +21,26 @@ Or install it yourself as:
19
21
 
20
22
  $ gem install brut
21
23
 
24
+ ## Getting Started
25
+
26
+ [Please note there is extensive documentation](https://brutrb.com), however to get started, you can use [mkbrut](https://github.com/thirdtank/mkrbut):
27
+
28
+ ```
29
+ docker run --pull always \
30
+ -v "$PWD":"$PWD" \
31
+ -w "$PWD"
32
+ -it \
33
+ thirdtank/mkbrut \
34
+ mkbrut your-new-app
35
+ ```
36
+
37
+ If you have Ruby 3.4 installed somewhere, you can use this via RubyGems as well:
38
+
39
+ ```
40
+ > gem install mkbrut
41
+ > mkbrut your-new-app
42
+ ```
43
+
22
44
  ## Developing
23
45
 
24
46
  The dev environment is managed by Docker and you are encouraged to use this. It's set up so you can edit your code on your computer
@@ -45,6 +67,5 @@ with your editor, but all commands are run inside Docker, which should be more c
45
67
 
46
68
  dx/exec bin/setup --no-credentials
47
69
 
48
- The `--no-credentials` means that you will not be able to push to GitHub or RubyGems from within the Docker container. This
49
- ability is only needed by maintainers to push new versions of the gem. You can push to GitHub from your computer.
70
+ The `--no-credentials` means that you will not be able to push to GitHub or RubyGems from within the Docker container. This ability is only needed by maintainers to push new versions of the gem. You can push to GitHub from your computer.
50
71
 
data/assets/LogoStop.pxd CHANGED
Binary file
Binary file
Binary file
Binary file
@@ -6,6 +6,33 @@ import jsdocLinker from './plugins/jsdocLinker'
6
6
  export default defineConfig({
7
7
  title: "Brut RB",
8
8
  description: "Documentation for the Brut.RB web framework.",
9
+ head: [
10
+ ["link", { rel: "icon", href: "/favicon.ico" }],
11
+ [
12
+ "meta", {
13
+ property: "og:title",
14
+ content: "BrutRB Documentation"
15
+ }
16
+ ],
17
+ [
18
+ "meta", {
19
+ property: "og:type",
20
+ content: "website"
21
+ }
22
+ ],
23
+ [
24
+ "meta", {
25
+ property: "og:image",
26
+ content: "https://github.com/thirdtank/brut/blob/main/assets/SocialImage.png?raw=true"
27
+ }
28
+ ],
29
+ ["script", {
30
+ defer: "",
31
+ "data-domain": "brutrb.com",
32
+ src: "https://plausible.io/js/script.js"
33
+ }
34
+ ],
35
+ ],
9
36
  themeConfig: {
10
37
  // https://vitepress.dev/reference/default-theme-config
11
38
  search: {
@@ -27,10 +54,11 @@ export default defineConfig({
27
54
  items: [
28
55
  { text: "Getting Started", link: "/getting-started" },
29
56
  { text: "Concepts", link: "/overview" },
30
- { text: "Documentation Conventions", link: "/doc-conventions" },
31
- { text: "Tutorial", link: "/tutorial" },
57
+ { text: "Features", link: "/features" },
58
+ { text: "Directory Structure", link: "/dir-structure" },
32
59
  { text: "Dev Environment", link: "/dev-environment" },
33
- { text: "AI Declaration", link: "/ai" },
60
+ { text: "Tutorial", link: "/tutorial" },
61
+ { text: "Documentation Conventions", link: "/doc-conventions" },
34
62
  ]
35
63
  },
36
64
  {
@@ -41,6 +69,7 @@ export default defineConfig({
41
69
  { text: "Pages", link: "/pages" },
42
70
  { text: "Layouts", link: "/layouts" },
43
71
  { text: "Forms", link: "/forms" },
72
+ { text: "Form Constraints", link: "/form-constraints" },
44
73
  { text: "Handlers and Actions", link: "/handlers" },
45
74
  { text: "Components", link: "/components" },
46
75
  { text: "Flash and Session", link: "/flash-and-session" },
@@ -98,12 +127,20 @@ export default defineConfig({
98
127
  collapsed: true,
99
128
  items: [
100
129
  { text: "Authentication", link: "/recipes/authentication" },
101
- { text: "Form Validations", link: "/recipes/form-validations" },
102
- { text: "Database Migrations", link: "/recipes/database-migrations" },
103
- { text: "Ajax Form Submission", link: "/recipes/ajax-form" },
104
- { text: "Custom Telemetry", link: "/recipes/telemetry" },
105
- { text: "CLI App/Task", link: "/recipes/cli-app" },
130
+ { text: "Alternate Layouts", link: "/recipes/alternate-layouts" },
131
+ { text: "Blank Layouts", link: "/recipes/blank-layouts" },
132
+ { text: "Custom Flash Class", link: "/recipes/custom-flash" },
133
+ { text: "Indexed Form Elements", link: "/recipes/indexed-forms" },
134
+ { text: "Text Field Component", link: "/recipes/text-field-component" },
106
135
  ],
136
+ },
137
+ {
138
+ text: "Meta",
139
+ collapsed: false,
140
+ items: [
141
+ { text: "Why?!", link: "/why" },
142
+ { text: "AI Declaration", link: "/ai" },
143
+ ]
107
144
  }
108
145
  ],
109
146
 
@@ -44,6 +44,7 @@
44
44
  * -------------------------------------------------------------------------- */
45
45
 
46
46
  :root {
47
+ --vp-c-text-1: #362400;
47
48
  --vp-c-default-1: var(--vp-c-gray-1);
48
49
  --vp-c-default-2: var(--vp-c-gray-2);
49
50
  --vp-c-default-3: var(--vp-c-gray-3);
@@ -54,9 +55,9 @@
54
55
  --blue-vp-c-brand-3: #4C0606;
55
56
  --blue-vp-c-brand-soft: #FFF9F9;
56
57
 
57
- --vp-c-brand-1: #362400;
58
- --vp-c-brand-2: #F1A100;
59
- --vp-c-brand-3: #281B00;
58
+ --vp-c-brand-1: #001842;
59
+ --vp-c-brand-2: #6B84B1;
60
+ --vp-c-brand-3: #0443B1;
60
61
  --vp-c-brand-soft: #FFF1D6;
61
62
 
62
63
 
@@ -115,8 +116,8 @@
115
116
  --vp-home-hero-name-color: transparent;
116
117
  --vp-home-hero-name-background: -webkit-linear-gradient(
117
118
  120deg,
118
- #333333 30%,
119
- #d14141
119
+ #D67402 20%,
120
+ #11AC0E
120
121
  );
121
122
 
122
123
  --vp-home-hero-image-background-image: linear-gradient(
data/brutrb.com/ai.md CHANGED
@@ -52,22 +52,17 @@ documentation, is produced by a machine, although spelling and grammar correctio
52
52
 
53
53
  We expect an LLM to be able to digest this documentation and source code and provide alternate analysis of how Brut works and how to use it. We hope such analysis is correct and useful, however that cannot be guaranteed, so this documentation is the second best source of truth, the source code being the best.
54
54
 
55
- ## Logo
55
+ ## Logos
56
56
 
57
- The logo is level 4 - ChatGPT created it. I'd love to have a real person make a better one. I just needed
58
- something to get this launched. Please reach out if you want to make a better one + other assets. I'm
59
- willing to pay a real person.
57
+ While the various logos look like someone typed "make me a logo in the style of the Washington, DC Metro", if you look closely, you will see the telltale sings that they were made by a person…a person who is not a professional designer.
60
58
 
61
- ## AI Completions Should Be Viewed with Skepticism
59
+ As such, these are all Level 1.
62
60
 
63
- Due to how LLMs work, there is naturally nothing in any model about Brut. Anything an
64
- existing model tells you about Brut is **100% untrustworthy**. We hope to allow LLMs to consume Brut's
65
- code, documentation, and examples, so it can be an additional source of help, but currently that is not
66
- the case.
61
+ ## AI Information about Brut Should Be Viewed with Skepticism
67
62
 
68
- **Do not ask an LLM about Brut** until this part of the documentation changes.
69
-
70
- For completion-based AI suggestions, **view them with skepticism**. In my
71
- experience, completions from e.g. GitHub CoPilot work OK when replicating a pattern
72
- in the file you are editing, but suggestions in a freshly-opened file tend to be
73
- entirely imaginary or Rails-based. Beware.
63
+ * **Answers from an LLM about Brut are likely incorrect.** LLMs will certainly not
64
+ have been trained on information about Brut, since it is new.
65
+ * **Code Completions of Brut Code are suspect.** I have observed that e.g. GitHub
66
+ CoPilot is capable for properly completing Brut code if there is enough context, but
67
+ it is not capable of, say, creating a component from scratch. **Review all Code
68
+ Completions Carefully**.
data/brutrb.com/assets.md CHANGED
@@ -46,14 +46,7 @@ JavaScript is on line 1 of `app.js`. This is not helpful for diagnosing issues.
46
46
  *SourceMaps* are separate files that translate the minified files back to normal ones, so you can see a normal
47
47
  stack trace with the actual line numbers of your source files.
48
48
 
49
- There are many ways to create source maps and if you've used a tool like WebPack, you'll recall that many of them
50
- don't produce usable source maps. Brut uses esbuild's facility for this, with a focus on correctness. When
51
- you see a line number and a source file in your browser, you can be sure it's accurate.
52
-
53
- The tradeoff is that it can take longer to produce than producing an inaccurate one. I'm not sure who wants
54
- inaccurate source maps, but Brut does not support this. In practice, esbuild is quite fast, so it should not make
55
- a practical difference in your day to day work.
56
-
49
+ Brut's configuration of esbuild is to produce sourcemaps.
57
50
 
58
51
  ## Fonts
59
52
 
@@ -93,7 +86,7 @@ end
93
86
  ## SVGs
94
87
 
95
88
  You can place `.svg` files in `app/src/front_end/images` if you wish to use them in `<img>` tags. However, if
96
- you place svgs in `app/src/front_end/svgs`, they can be inlined into your HTML via `inline_svg`. In this case,
89
+ you place svgs in `app/src/front_end/svgs`, they can be inlined into your HTML via `inline_svg`, provided by `Brut::FrontEnd::Component::Helpers` and included in all components and pages. In this case,
97
90
  there is no need for a build step, since the SVG source is included directly in your HTML. This works well
98
91
  for icons.
99
92
 
@@ -73,7 +73,7 @@ improvements):
73
73
  * Default implementations of `connectedCallback` and `attributeChangedCallback` that call the template method `update`, thus allowing your element to centralize its logic in one place, regardless of how a state change was triggered.
74
74
  * Static `define()` method that defines your element based on its static `tagName` field. This allows richer interaction of elements, as you can do e.g. `document.querySelector(SomeOtherElement.tagName)` and better navigate changes to your code over time.
75
75
 
76
- If you are familiary with the API for autonomous custom elements, `BaseCustomElement` doesn't require
76
+ If you are familiar with the API for autonomous custom elements, `BaseCustomElement` doesn't require
77
77
  learning much more. What you know already will be leveraged.
78
78
 
79
79
  ### Removing BrutJS
@@ -81,10 +81,20 @@ learning much more. What you know already will be leveraged.
81
81
  To remove BrutJS from your app, modify `app/src/front_end/js/index.js` to remove the `import` and call to
82
82
  `define()`. You can then remove it from your `package.json`.
83
83
 
84
- **Note** If you remove it like this, several features will not work, including locale detection, client-side observability, and client-side form validation UX.
84
+ > [!NOTE]
85
+ > If you remove it like this, several features will not work, including locale detection, client-side observability, and client-side form validation UX.
85
86
 
86
87
  ## Recommnded Practices
87
88
 
89
+ ### Leaving BrutJS In Your App
90
+
91
+ BrutJS provides useful tools unrelated to single-page apps, or reactivity, or
92
+ whatever else you might be concerned with in your client-side code. These features
93
+ can work alongside whatever framework you want to use. Leave them in unless they are
94
+ causing a specific problem.
95
+
96
+ ### You Probably Don't Need a Single-Page App
97
+
88
98
  Consider this decision tree from Alex Russell's [If Not React, Then
89
99
  What?](https://infrequently.org/2024/11/if-not-react-then-what/):
90
100
 
data/brutrb.com/cli.md CHANGED
@@ -117,7 +117,7 @@ All of this means that the bulk of CLI-specific code you will write is specifyin
117
117
 
118
118
  Every CLI app is a class that extends `Brut::CLI::App`. This class should contain one inner class for each subcommand. Those classes should extend `Brut::CLI::App`.
119
119
 
120
- Inside your `Brut::CLI::App` class, you can call a few class methods to declare aspects of the UI. In particular, `opts` returns the `OptionParser` in play that you can use to declare global options. Unlike `OptionParser`'s `on` method, Brut's does not require providing a block. Brut will store the runtime options in a hash (see below).
120
+ Inside your `Brut::CLI::App` class, you can call a few class methods to declare aspects of the UI. In particular, `opts` returns the `OptionParser` in play that you can use to declare global options. Unlike `OptionParser`'s `on` method, Brut's does not require providing a block. Brut will store the runtime options in an object (see below).
121
121
 
122
122
  ```ruby
123
123
  class MyAppCLI < Brut::CLI::App
@@ -132,7 +132,7 @@ This code means your app's global options are `--dry-run`, which will not accept
132
132
 
133
133
  Declaring subcommands provides a similar API. Let's say our app has a "status" subcommand, and a "run" subcommand.
134
134
 
135
- ```ruby
135
+ ```ruby {7-11,13-16}
136
136
  class MyAppCLI < Brut::CLI::App
137
137
  description "My awesome command line app"
138
138
 
@@ -189,7 +189,7 @@ end
189
189
 
190
190
  ### Implementing `execute`
191
191
 
192
- Once `execute` is called, your app's internals will have been setup and bootstrapped. That means all you data models can access the database, and any other setup will have ocurred. Generally, `execute` can then have whatever code makes sense.
192
+ Once `execute` is called, your app's internals will have been setup and bootstrapped. That means all your data models can access the database, and any other setup will have ocurred. Generally, `execute` can then have whatever code makes sense.
193
193
 
194
194
  That said, `execute` has access to a few values to understand the command line invocation and to support testing.
195
195
 
@@ -220,23 +220,19 @@ use that affect its behavior.
220
220
 
221
221
  Currently, Brut doesn't provide a way to create this file, but it's relatively straightforward. It's almost entirely boilerplate except for your class:
222
222
 
223
- ```ruby {14}
223
+ ```ruby {9}
224
224
  #!/usr/bin/env ruby
225
225
 
226
226
  require "bundler"
227
227
  Bundler.require
228
228
  require "pathname"
229
-
230
- require "brut/cli"
231
-
232
- APP_PATH = File.join(File.dirname($0),"..","app","src")
233
- $: << APP_PATH
234
- require "cli/my_app"
229
+ require "brut/cli/apps/db"
235
230
 
236
231
  exit Brut::CLI.app(
237
- MyAppCLI,
238
- project_root: Pathname($0).dirname / ".."
239
- )
232
+ Brut::CLI::Apps::DB,
233
+ project_root: Pathname($0).dirname / ".."
234
+ )
235
+
240
236
  ```
241
237
 
242
238
  ## Testing
@@ -12,136 +12,159 @@ Components in Brut are Phlex Components: a class that can hold data and use that
12
12
 
13
13
  ### Simple Component
14
14
 
15
- For example, suppose you want a re-usable button component whose HTML would look like so:
16
15
 
17
- ```html
18
- <button class="button button__small button__red">
19
- Delete Files
20
- </button>
21
- ```
16
+ For example, suppose you want a re-usable button that can be gray, green, or red, and have an optional `formaction`.
22
17
 
23
- Your button can be large (the default) or small, and could be gray (default), green, or red. Your button could also have an optional `formaction` attribute. Let's also suppose the label could be a string or embedded HTML.
24
18
 
25
- Your constructor would need to accept the size, color, label, and formaction.
26
-
27
- You can create a scaffold via `bin/scaffold`:
19
+ You can create a component with `bin/scaffold component`:
28
20
 
29
21
  ```
30
- > bin/scaffold component Button
22
+ bin/scaffold component button
23
+ # => app/src/front_end/components/button_component.rb
24
+ # => specs/front_end/components/button_component.spec.rb
31
25
  ```
32
26
 
33
- You'll first implement the initializer, like so:
27
+ Component inititalizers are called by you when you use them, so you can define it
28
+ how you like. Brut uses keyword arguments by convention.
34
29
 
35
30
  ```ruby
36
- # app/src/front_end/components/button.rb
37
- class Button < AppComponent
38
- def initialize(
39
- size: :large,
40
- color: :gray,
41
- formaction: nil,
42
- label: :use_block
43
- )
44
- @size = size
31
+ # app/src/front_end/components/button_component.rb
32
+ class ButtonComponent < AppComponent
33
+ def initialize(color: :gray,
34
+ formaction: nil)
45
35
  @color = color
46
36
  @formaction = formaction
47
- @label = label
48
37
  end
49
38
  end
50
39
  ```
51
40
 
52
- This initializer is rather simplistic. You may want to validate the values here to prevent the construction of an invalid component.
53
-
54
- Now, implement `view_template`. This method will receive a block if one is given when the component is used. We'll see an example in a minute.
41
+ Since it's a Phlex component, implement `view_template` to generate the HTML you
42
+ like. Our `view_template` will `yield` so the button's contents can be controlled
43
+ by the caller. Note that the CSS here is [BrutCSS](/brut-css/index.html), but it
44
+ can be anything you are using in your oapp.
55
45
 
56
46
  ```ruby
57
- # app/src/front_end/components/button.rb
58
- class Button < AppComponent
47
+ # app/src/front_end/components/button_component.rb
48
+ class ButtonComponent < AppComponent
59
49
 
60
50
  # ...
61
51
 
62
52
  def view_template
63
53
  attributes = {
64
54
  class: [
65
- "button",
66
- "button__#{@size}",
67
- "button__#@{color}",
55
+ "tc", # centered text
56
+ "br-3", # border radius @ 3rd step of scale
57
+ "bn", # no border
58
+ "f-3", # font size @ 3rd step of scale
59
+ "ph-4", # horizontal padding @ 4th step of scale
60
+ "pv-2", # vertical padding @ 2nd step of scale
61
+ "bg-#{@color}-800", # background is second lighest of scale
62
+ "#{@color}-300", # text is third darkest of scale
68
63
  ],
69
- formaction: @formaction,
64
+ formaction: @formaction
70
65
  }
71
66
 
72
67
  button(**attributes) do
73
- if @label == :use_block
74
- yield
75
- else
76
- @label
77
- end
68
+ yield
78
69
  end
79
70
  end
80
71
  end
81
72
  ```
82
73
 
83
- If you've never used Phlex before, it's refreshingly straightforward:
74
+ Here are two examples of how you'd use this component and the HTML that would be
75
+ generated:
76
+
77
+ ::: code-group
78
+
79
+ ```ruby
80
+ render ButtonComponent(color: :green) do
81
+ "Click Here"
82
+ end
83
+ ```
84
+
85
+ ```html
86
+ <button class="tc br-3 bn f-3 ph-4 pv-2 bg-green-800 green-300">
87
+ Click Here
88
+ <button>
89
+ ```
84
90
 
85
- * There's a method for each HTML element.
86
- * The method's parameters produce attributes in the HTML that is generated.
87
- * If a parameter's value is an array (like `class:`), the values are joined with strings to form the atttribute's value in HTML.
88
- * If the element can have inner content, whatever happens inside a yielded block becomes that inner content.
91
+ :::
89
92
 
90
- To use this component, we can call `render` in either the `view_template` of another component or the `page_template` of a [page](/pages):
93
+ ::: code-group
91
94
 
92
95
  ```ruby
93
- def view_template
94
- form do
95
- render Button.new(label: "Submit")
96
- render Button.new(label: "Nevermind", size: :small, color: :red)
97
- render Button.new(color: :green) do
98
- img(src: "/images/ok.png", alt: "OK icon")
99
- end
100
- end
96
+ render ButtonComponent(color: :red, formaction: DeleteWidget.routing) do
97
+ "Delete Widget"
101
98
  end
102
99
  ```
103
100
 
104
- Note that the block passed to `render` is the block available when `yield` is called inside `view_template`.
101
+ ```html
102
+ <button class="tc br-3 bn f-3 ph-4 pv-2 bg-red-800 green-300"
103
+ formaction="/delete_widget">
104
+ Delete Widget
105
+ <button>
106
+ ```
107
+ :::
105
108
 
106
- There are two special types of components beyond what we have just seen. *Global* Components and *Page private* components.
109
+ One issue with components is that you must pass them all their initializer arguments
110
+ to use them. This means that if your component needs access to, say, the session,
111
+ any page or component that uses your component must also require the session to
112
+ be passed in.
113
+
114
+ Brut provides a partial solution to this called *global components*.
107
115
 
108
116
  ### Global Components
109
117
 
110
- As we saw above, creating a component is just like creating any Ruby class: you call `.new` on it. If you create a component that uses request-level data, such as the flash or session, it would mean that any page or component that used *that* component would need to accept the flash or session as a parameter to its initializer, even if it it was otherwise not needed.
118
+ A global component can be created by Brut using [keyword
119
+ injection](/keyword-injection). This means that, in our example above, a page that
120
+ uses your component does not need to be given the session. It can have Brut inject
121
+ it.
111
122
 
112
- In those cases, `global_component` can be used to leverage [keyword injection](/keyword-injection) to have Brut create the component. That way, its initializer's parameters don't need to be passed into the page or component using the global component.
123
+ This provides a partial solution to so-called "prop drilling".
113
124
 
114
- Suppose we had a component to display the flash:
125
+ In [the features overview](/features), we saw a basic component for rendering a
126
+ flash:
115
127
 
116
128
  ```ruby
117
- class FlashMessage < AppComponent
118
-
129
+ # components/flash_component.rb
130
+ class FlashComponent < AppComponent
119
131
  def initialize(flash:)
120
- @flash = flash
132
+ if flash.notice?
133
+ @message_key = flash.notice
134
+ @role = :info
135
+ elsif flash.alert?
136
+ @message_key = flash.alert
137
+ @role = :alert
138
+ end
121
139
  end
122
140
 
141
+ def any_message? = !@message_key.nil?
142
+
123
143
  def view_template
124
- if @flash.notice?
125
- div(role: "status") { @flash.notice }
126
- elsif @flash.alert?
127
- div(role: "alert") { @flash.alert }
144
+ if any_message?
145
+ div(role: @role) do
146
+ t([ :flash, @message_key ])
147
+ end
128
148
  end
129
149
  end
130
150
  end
131
151
  ```
132
152
 
133
- To use it without having to instantiate it, call `global_component` with the component's class:
153
+ Instead of requiring each user of this component to manually inject the flash, we
154
+ can call `global_component`, provided by `Brut::FrontEnd::Component::Helpers`, which
155
+ is included in all pages and components.
134
156
 
135
157
  ```ruby
136
- class HomePage < AppPage
137
- def page_template
138
- header do
139
- global_component(FlashMessage) # note: render not required
140
- end
158
+ def view_template
159
+ header do
160
+ global_component(FlashComponent)
141
161
  end
142
162
  end
143
163
  ```
144
164
 
165
+ Components used in layouts will tend to be global components, to avoid creating odd
166
+ dependencies between pages.
167
+
145
168
  > [!IMPORTANT]
146
169
  > Brut currently requires an all-or-nothing approach to global components. Either
147
170
  > the component can be injected with all its initializer parameters or it must
@@ -157,9 +180,19 @@ Often, components are helpful to simplifying a page's template or managing re-us
157
180
 
158
181
  Brut provides a way to create a *page private* component that exists as an inner class of a page. It's not truly private, since it's still a Ruby class anyone can use, but it's form and source location communicate intent.
159
182
 
160
- Suppose our `HomePage` has a list of widgets on it, but we want each widget's HTML managed by a separate component:
183
+ They can be created with `bin/scaffold`:
161
184
 
162
- ```ruby
185
+ ```
186
+ bin/scaffold component --page HomePage Widget
187
+ # => app/src/front_end/page/home_page/widget_component.rb
188
+ # => specs/front_end/page/home_page/widget_component.spec.rb
189
+ ```
190
+
191
+ The class will be an inner class of `HomePage` in this example, `HomePage::WidgetComponent`. You build them and use them like normal:
192
+
193
+ ::: code-group
194
+
195
+ ```ruby [Page]
163
196
  class HomePage < AppPage
164
197
  def page_template
165
198
  header do
@@ -176,16 +209,7 @@ class HomePage < AppPage
176
209
  end
177
210
  ```
178
211
 
179
- `HomePage::WidgetListItem` can be created like so:
180
-
181
- ```
182
- bin/scaffold component --page=HomePage WidgetListItem
183
- ```
184
-
185
- This will create `app/src/front_end/pages/home_page/widget_list_item.rb`, which you can then implement like a normal component:
186
-
187
- ```ruby
188
- # app/src/front_end/pages/home_page/widget_list_item.rb
212
+ ```ruby [Page Private Component]
189
213
  class HomePage::WidgetListItem < AppComponent
190
214
  def initialize(widget:)
191
215
  @widget = widget
@@ -200,27 +224,25 @@ class HomePage::WidgetListItem < AppComponent
200
224
  end
201
225
  ```
202
226
 
203
- The only special thing about a page private component is that it can access the page's I18n translations. As we'll discussion in [I18n](/i18n), The `t` method will try to locate translations based on the page on which `t` is called. A page private component will also trigger this behavior, but a normal component will not.
204
-
205
- For example, the following code will look for `pages.HomePage.status.«status»` when generated the `<h3>`:
227
+ :::
206
228
 
207
- ```ruby {10}
208
- # app/src/front_end/pages/home_page/widget_list_item.rb
209
- class HomePage::WidgetListItem < AppComponent
210
- def initialize(widget:)
211
- @widget = widget
212
- end
229
+ The main difference between a page-private component and a normal component's
230
+ behavior is how [I18n](/i18n) strings are resolved. In short, given this:
213
231
 
214
- def view_template
215
- li do
216
- h2 { @widget.name }
217
- h3 { t([ :status, @widget.status ]) }
218
- p { @widget.description }
219
- end
220
- end
232
+ ```ruby
233
+ p do
234
+ t(:hello)
221
235
  end
222
236
  ```
223
237
 
238
+ In a normal component named `WidgetComponent`, the keys searched for translations
239
+ would be `"components.WidgetComponent.hello"` and `"hello"` . For the page-private
240
+ component `HomePage::WidgetComponent`, the keys searched would be
241
+ `"pages.HomePage.hello"` and `"hello"`. This means that page private components can
242
+ access a page's translations.
243
+
244
+
245
+
224
246
  ## Testing
225
247
 
226
248
  Test widgets exactly as you would [pages](/pages#testing). The only difference is that components always render HTML and have no `before_generate` concept.