brut 0.9.2 → 0.10.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 (350) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +1 -1
  4. data/brut-css/package-lock.json +2 -2
  5. data/brut-css/package.json +1 -1
  6. data/brut-js/package-lock.json +2 -2
  7. data/brut-js/package.json +1 -1
  8. data/brut-js/specs/ConstraintViolationMessages.spec.js +4 -1
  9. data/brut-js/specs/Form.spec.js +2 -46
  10. data/brut-js/src/ConstraintViolationMessage.js +17 -2
  11. data/brut-js/src/Form.js +19 -23
  12. data/brutrb.com/.vitepress/config.mjs +1 -0
  13. data/brutrb.com/form-constraints.md +6 -6
  14. data/brutrb.com/recipes/form-errors.md +148 -0
  15. data/docs/404.html +2 -2
  16. data/docs/adrs.html +3 -3
  17. data/docs/ai.html +3 -3
  18. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  19. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  20. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  21. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  22. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  23. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  24. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  25. data/docs/api/Brut/BackEnd.html +1 -1
  26. data/docs/api/Brut/CLI/App.html +1 -1
  27. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  28. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  29. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  30. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
  31. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  32. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  33. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  34. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  35. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  36. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  37. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  38. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  39. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  40. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  41. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  42. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  43. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  45. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  46. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  47. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  48. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +1 -1
  50. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  51. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  52. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  53. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  54. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  55. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  56. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
  58. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  59. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  60. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  61. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  62. data/docs/api/Brut/CLI/Apps.html +1 -1
  63. data/docs/api/Brut/CLI/Command.html +1 -1
  64. data/docs/api/Brut/CLI/Error.html +1 -1
  65. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  66. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  67. data/docs/api/Brut/CLI/Executor.html +1 -1
  68. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  69. data/docs/api/Brut/CLI/Options.html +1 -1
  70. data/docs/api/Brut/CLI/Output.html +1 -1
  71. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  72. data/docs/api/Brut/CLI.html +1 -1
  73. data/docs/api/Brut/FactoryBot.html +1 -1
  74. data/docs/api/Brut/Framework/App.html +1 -1
  75. data/docs/api/Brut/Framework/Config.html +1 -1
  76. data/docs/api/Brut/Framework/Container.html +1 -1
  77. data/docs/api/Brut/Framework/Error.html +1 -1
  78. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  79. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  80. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  81. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  82. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  83. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  84. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  85. data/docs/api/Brut/Framework/Errors.html +1 -1
  86. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  87. data/docs/api/Brut/Framework/MCP.html +1 -1
  88. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  89. data/docs/api/Brut/Framework.html +1 -1
  90. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  91. data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
  92. data/docs/api/Brut/FrontEnd/Component.html +1 -1
  93. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  94. data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
  95. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  96. data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
  97. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  98. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +1 -1
  99. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
  100. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +1 -1
  101. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +1 -1
  102. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  103. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  104. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  105. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  106. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  107. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  108. data/docs/api/Brut/FrontEnd/CsrfProtector.html +1 -1
  109. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  110. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  111. data/docs/api/Brut/FrontEnd/Form.html +1 -1
  112. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  113. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +1 -1
  114. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
  115. data/docs/api/Brut/FrontEnd/Forms/Input.html +1 -1
  116. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
  117. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1 -1
  118. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +1 -1
  119. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  122. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  123. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  124. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  125. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  126. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  127. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  128. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  129. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  130. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  132. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  133. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  134. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  135. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  136. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  137. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  138. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  143. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  146. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  147. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  148. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  149. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  150. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  151. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  152. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  153. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  154. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  155. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  156. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  157. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  158. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  159. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  160. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  161. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  162. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  163. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  164. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  165. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  166. data/docs/api/Brut/FrontEnd.html +1 -1
  167. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  168. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  169. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  170. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  171. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  172. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  173. data/docs/api/Brut/I18n.html +1 -1
  174. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  175. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  176. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  177. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  178. data/docs/api/Brut/Instrumentation.html +1 -1
  179. data/docs/api/Brut/RubocopConfig.html +1 -1
  180. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  181. data/docs/api/Brut/SinatraHelpers.html +1 -1
  182. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  183. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  184. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  185. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  186. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  187. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  188. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  189. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  190. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  191. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  192. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  193. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  194. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  195. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  196. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  197. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  198. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  199. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  200. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  201. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  202. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  203. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  204. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  205. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  206. data/docs/api/Brut/SpecSupport.html +1 -1
  207. data/docs/api/Brut.html +1 -1
  208. data/docs/api/Clock.html +1 -1
  209. data/docs/api/ModuleName.html +1 -1
  210. data/docs/api/RichString.html +1 -1
  211. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  212. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  213. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  214. data/docs/api/Sequel/Extensions.html +1 -1
  215. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  216. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  217. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  218. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  219. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  220. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  221. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  222. data/docs/api/Sequel/Plugins.html +1 -1
  223. data/docs/api/Sequel.html +1 -1
  224. data/docs/api/_index.html +1 -1
  225. data/docs/api/file.README.html +1 -1
  226. data/docs/api/index.html +1 -1
  227. data/docs/api/top-level-namespace.html +1 -1
  228. data/docs/assets/{app.DmIaHRc4.js → app.vjGWMSnJ.js} +1 -1
  229. data/docs/assets/chunks/@localSearchIndexroot.Dn1xGMv_.js +1 -0
  230. data/docs/assets/chunks/{VPLocalSearchBox.C2QeRbCY.js → VPLocalSearchBox.C-ymMW2k.js} +1 -1
  231. data/docs/assets/chunks/{theme.CFKmBRX5.js → theme.pJUosGlI.js} +2 -2
  232. data/docs/assets/{components.md.BBHib1uV.js → components.md.B543a3Lm.js} +3 -3
  233. data/docs/assets/{configuration.md.CJ0MmIps.js → configuration.md.-foE_iVv.js} +1 -1
  234. data/docs/assets/{forms.md.D5jFZIJz.js → forms.md.D5-2rgHh.js} +1 -1
  235. data/docs/assets/{getting-started.md.abLTnd_n.js → getting-started.md.Cd4XSZb_.js} +3 -3
  236. data/docs/assets/{getting-started.md.abLTnd_n.lean.js → getting-started.md.Cd4XSZb_.lean.js} +1 -1
  237. data/docs/assets.html +3 -3
  238. data/docs/brut-js/api/AjaxSubmit.html +1 -1
  239. data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
  240. data/docs/brut-js/api/Autosubmit.html +1 -1
  241. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  242. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  243. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  244. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  245. data/docs/brut-js/api/BufferedLogger.html +1 -1
  246. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  247. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  248. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  249. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  250. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  251. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  252. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  253. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  254. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  255. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  256. data/docs/brut-js/api/Form.html +1 -1
  257. data/docs/brut-js/api/Form.js.html +1 -1
  258. data/docs/brut-js/api/I18nTranslation.html +1 -1
  259. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  260. data/docs/brut-js/api/LocaleDetection.html +1 -1
  261. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  262. data/docs/brut-js/api/Logger.html +1 -1
  263. data/docs/brut-js/api/Logger.js.html +1 -1
  264. data/docs/brut-js/api/Message.html +1 -1
  265. data/docs/brut-js/api/Message.js.html +1 -1
  266. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  267. data/docs/brut-js/api/RichString.html +1 -1
  268. data/docs/brut-js/api/RichString.js.html +1 -1
  269. data/docs/brut-js/api/Tabs.html +1 -1
  270. data/docs/brut-js/api/Tabs.js.html +1 -1
  271. data/docs/brut-js/api/Tracing.html +1 -1
  272. data/docs/brut-js/api/Tracing.js.html +1 -1
  273. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  274. data/docs/brut-js/api/external-Performance.html +1 -1
  275. data/docs/brut-js/api/external-Promise.html +1 -1
  276. data/docs/brut-js/api/external-ValidityState.html +1 -1
  277. data/docs/brut-js/api/external-Window.html +1 -1
  278. data/docs/brut-js/api/external-fetch.html +1 -1
  279. data/docs/brut-js/api/global.html +1 -1
  280. data/docs/brut-js/api/index.html +1 -1
  281. data/docs/brut-js/api/index.js.html +1 -1
  282. data/docs/brut-js/api/module-testing.html +1 -1
  283. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  284. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  285. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  286. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  287. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  288. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  289. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  290. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  291. data/docs/brut-js/api/testing_index.js.html +1 -1
  292. data/docs/brut-js.html +3 -3
  293. data/docs/business-logic.html +3 -3
  294. data/docs/cli.html +3 -3
  295. data/docs/components.html +7 -7
  296. data/docs/configuration.html +5 -5
  297. data/docs/css.html +3 -3
  298. data/docs/custom-element-tests.html +3 -3
  299. data/docs/database-access.html +3 -3
  300. data/docs/database-schema.html +3 -3
  301. data/docs/deployment.html +3 -3
  302. data/docs/dev-environment.html +3 -3
  303. data/docs/dir-structure.html +3 -3
  304. data/docs/doc-conventions.html +3 -3
  305. data/docs/end-to-end-tests.html +3 -3
  306. data/docs/features.html +3 -3
  307. data/docs/flash-and-session.html +3 -3
  308. data/docs/form-constraints.html +3 -3
  309. data/docs/forms.html +5 -5
  310. data/docs/getting-started.html +6 -6
  311. data/docs/handlers.html +3 -3
  312. data/docs/hashmap.json +1 -1
  313. data/docs/hooks.html +3 -3
  314. data/docs/i18n.html +3 -3
  315. data/docs/index.html +3 -3
  316. data/docs/instrumentation.html +3 -3
  317. data/docs/javascript.html +3 -3
  318. data/docs/jobs.html +3 -3
  319. data/docs/keyword-injection.html +3 -3
  320. data/docs/layouts.html +3 -3
  321. data/docs/lsp.html +3 -3
  322. data/docs/markdown-examples.html +3 -3
  323. data/docs/middleware.html +3 -3
  324. data/docs/overview.html +3 -3
  325. data/docs/pages.html +3 -3
  326. data/docs/recipes/alternate-layouts.html +3 -3
  327. data/docs/recipes/authentication.html +3 -3
  328. data/docs/recipes/blank-layouts.html +3 -3
  329. data/docs/recipes/custom-flash.html +3 -3
  330. data/docs/recipes/indexed-forms.html +3 -3
  331. data/docs/recipes/migrations.html +3 -3
  332. data/docs/recipes/text-field-component.html +3 -3
  333. data/docs/roadmap.html +3 -3
  334. data/docs/routes.html +3 -3
  335. data/docs/security.html +3 -3
  336. data/docs/seed-data.html +3 -3
  337. data/docs/space-time-continuum.html +3 -3
  338. data/docs/tutorial.html +3 -3
  339. data/docs/unit-tests.html +3 -3
  340. data/docs/why.html +3 -3
  341. data/lib/brut/front_end/components/constraint_violations.rb +23 -10
  342. data/lib/brut/version.rb +1 -1
  343. data/mkbrut/Gemfile.lock +1 -1
  344. data/mkbrut/lib/mkbrut/version.rb +1 -1
  345. metadata +14 -14
  346. data/.nvim.lua +0 -1
  347. data/docs/assets/chunks/@localSearchIndexroot.D0RGp49A.js +0 -1
  348. /data/docs/assets/{components.md.BBHib1uV.lean.js → components.md.B543a3Lm.lean.js} +0 -0
  349. /data/docs/assets/{configuration.md.CJ0MmIps.lean.js → configuration.md.-foE_iVv.lean.js} +0 -0
  350. /data/docs/assets/{forms.md.D5jFZIJz.lean.js → forms.md.D5-2rgHh.lean.js} +0 -0
data/docs/why.html CHANGED
@@ -9,8 +9,8 @@
9
9
  <link rel="preload stylesheet" href="/assets/style.B1z60PPQ.css" as="style">
10
10
  <link rel="preload stylesheet" href="/vp-icons.css" as="style">
11
11
 
12
- <script type="module" src="/assets/app.DmIaHRc4.js"></script>
13
- <link rel="modulepreload" href="/assets/chunks/theme.CFKmBRX5.js">
12
+ <script type="module" src="/assets/app.vjGWMSnJ.js"></script>
13
+ <link rel="modulepreload" href="/assets/chunks/theme.pJUosGlI.js">
14
14
  <link rel="modulepreload" href="/assets/chunks/framework.1L-BeKqY.js">
15
15
  <link rel="modulepreload" href="/assets/why.md.C-hk5xgJ.lean.js">
16
16
  <link rel="icon" href="/favicon.ico">
@@ -23,7 +23,7 @@
23
23
  </head>
24
24
  <body>
25
25
  <div id="app"><div class="Layout" data-v-d8b57b2d><!--[--><!--]--><!--[--><span tabindex="-1" data-v-fcbfc0e0></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-fcbfc0e0>Skip to content</a><!--]--><!----><header class="VPNav" data-v-d8b57b2d data-v-7ad780c2><div class="VPNavBar" data-v-7ad780c2 data-v-9fd4d1dd><div class="wrapper" data-v-9fd4d1dd><div class="container" data-v-9fd4d1dd><div class="title" data-v-9fd4d1dd><div class="VPNavBarTitle has-sidebar" data-v-9fd4d1dd data-v-9f43907a><a class="title" href="/" data-v-9f43907a><!--[--><!--]--><!----><span data-v-9f43907a>Brut RB</span><!--[--><!--]--></a></div></div><div class="content" data-v-9fd4d1dd><div class="content-body" data-v-9fd4d1dd><!--[--><!--]--><div class="VPNavBarSearch search" data-v-9fd4d1dd><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-9fd4d1dd data-v-afb2845e><span id="main-nav-aria-label" class="visually-hidden" data-v-afb2845e> Main Navigation </span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Home</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/getting-started.html" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Getting Started</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/overview.html" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Overview</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/api/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Brut API</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/brut-js/api/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>BrutJS</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/brut-css/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>BrutCSS</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-9fd4d1dd data-v-3f90c1a5><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-3f90c1a5 data-v-be9742d9 data-v-b4ccac88><span class="check" data-v-b4ccac88><span class="icon" data-v-b4ccac88><!--[--><span class="vpi-sun sun" data-v-be9742d9></span><span class="vpi-moon moon" data-v-be9742d9></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-9fd4d1dd data-v-ef6192dc data-v-e71e869c><!--[--><a class="VPSocialLink no-icon" href="https://github.com/thirdtank/brut" aria-label="github" target="_blank" rel="noopener" data-v-e71e869c data-v-60a9a2d3><span class="vpi-social-github"></span></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-9fd4d1dd data-v-f953d92f data-v-bfe7971f><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-bfe7971f><span class="vpi-more-horizontal icon" data-v-bfe7971f></span></button><div class="menu" data-v-bfe7971f><div class="VPMenu" data-v-bfe7971f data-v-20ed86d6><!----><!--[--><!--[--><!----><div class="group" data-v-f953d92f><div class="item appearance" data-v-f953d92f><p class="label" data-v-f953d92f>Appearance</p><div class="appearance-action" data-v-f953d92f><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-f953d92f data-v-be9742d9 data-v-b4ccac88><span class="check" data-v-b4ccac88><span class="icon" data-v-b4ccac88><!--[--><span class="vpi-sun sun" data-v-be9742d9></span><span class="vpi-moon moon" data-v-be9742d9></span><!--]--></span></span></button></div></div></div><div class="group" data-v-f953d92f><div class="item social-links" data-v-f953d92f><div class="VPSocialLinks social-links-list" data-v-f953d92f data-v-e71e869c><!--[--><a class="VPSocialLink no-icon" href="https://github.com/thirdtank/brut" aria-label="github" target="_blank" rel="noopener" data-v-e71e869c data-v-60a9a2d3><span class="vpi-social-github"></span></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-9fd4d1dd data-v-6bee1efd><span class="container" data-v-6bee1efd><span class="top" data-v-6bee1efd></span><span class="middle" data-v-6bee1efd></span><span class="bottom" data-v-6bee1efd></span></span></button></div></div></div></div><div class="divider" data-v-9fd4d1dd><div class="divider-line" data-v-9fd4d1dd></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-d8b57b2d data-v-2488c25a><div class="container" data-v-2488c25a><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-2488c25a><span class="vpi-align-left menu-icon" data-v-2488c25a></span><span class="menu-text" data-v-2488c25a>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-2488c25a data-v-6b867909><button data-v-6b867909>Return to top</button><!----></div></div></div><aside class="VPSidebar" data-v-d8b57b2d data-v-42c4c606><div class="curtain" data-v-42c4c606></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-42c4c606><span class="visually-hidden" id="sidebar-aria-label" data-v-42c4c606> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Overview</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/getting-started.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Getting Started</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/overview.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Concepts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/features.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Features</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/dir-structure.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Directory Structure</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/dev-environment.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Dev Environment</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/tutorial.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Tutorial</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/doc-conventions.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Documentation Conventions</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Front-End</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/routes.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Routes</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/pages.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Pages</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/layouts.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Layouts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/forms.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Forms</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/form-constraints.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Form Constraints</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/handlers.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Handlers and Actions</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/components.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Components</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/flash-and-session.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Flash and Session</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/space-time-continuum.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Space/Time Continuum</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/javascript.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>JavaScript</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/css.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>CSS</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/assets.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Assets</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/brut-js.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>BrutJS</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Back-End</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/database-schema.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Database Schema</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/database-access.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Database Access</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/seed-data.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Seed Data</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/jobs.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Jobs</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/business-logic.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Business Logic</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Framework</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/configuration.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Configuration</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/keyword-injection.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Keyword Injection</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/i18n.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>I18n</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/cli.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>CLI / Tasks</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/deployment.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Deployment</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Testing</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/unit-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Unit Tests</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/end-to-end-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>End-to-End Tests</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/custom-element-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Testing Custom Elements</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible collapsed" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Advanced Topics</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/hooks.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Route Hooks</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/middleware.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Middleware</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/instrumentation.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Instrumentation</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/security.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Security</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/lsp.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>LSP Support</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible collapsed" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Recipes</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/migrations.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Migration Basics</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/authentication.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Authentication</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/alternate-layouts.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Alternate Layouts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/blank-layouts.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Blank Layouts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/custom-flash.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Custom Flash Class</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/indexed-forms.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Indexed Form Elements</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/text-field-component.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Text Field Component</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible has-active" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Meta</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/why.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Why?!</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/adrs.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>ADRs</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/roadmap.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Roadmap to 1.0</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/ai.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>AI Declaration</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-d8b57b2d data-v-9a6c75ad><div class="VPDoc has-sidebar has-aside" data-v-9a6c75ad data-v-e6f2a212><!--[--><!--]--><div class="container" data-v-e6f2a212><div class="aside" data-v-e6f2a212><div class="aside-curtain" data-v-e6f2a212></div><div class="aside-container" data-v-e6f2a212><div class="aside-content" data-v-e6f2a212><div class="VPDocAside" data-v-e6f2a212 data-v-cb998dce><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-cb998dce data-v-f610f197><div class="content" data-v-f610f197><div class="outline-marker" data-v-f610f197></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-f610f197>On this page</div><ul class="VPDocOutlineItem root" data-v-f610f197 data-v-53c99d69><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-cb998dce></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-e6f2a212><div class="content-container" data-v-e6f2a212><!--[--><!--]--><main class="main" data-v-e6f2a212><div style="position:relative;" class="vp-doc _why" data-v-e6f2a212><div><h1 id="why-does-brut-exist" tabindex="-1">Why Does Brut Exist? <a class="header-anchor" href="#why-does-brut-exist" aria-label="Permalink to &quot;Why Does Brut Exist?&quot;">​</a></h1><p>I love writing Ruby, but grew tired of writing Rails. Rails is great, and has been great to me over the years. I&#39;ve written a lot of books about it! But the churn and increasing configuration burden made me think: what if we had another way to build web apps in Ruby?</p><p>What if it was totally different, but still focused on being straightforward and simple? What if it had <em>fewer</em> abstractions, <em>less</em> configuration, and not as much <em>stuff</em>?</p><p>My thinking is, you need to know HTML, JavaScript, CSS, SQL, Ruby, HTTP, and a few other things to make a web app. What if we tried to limit the additional abstractions you&#39;d have to learn?</p><p>That&#39;s what Brut is trying to be. Straightfoward, direct abstractions or translations of stuff you already know. The raw web…or at least as raw as it can be.</p></div></div></main><footer class="VPDocFooter" data-v-e6f2a212 data-v-1bcd8184><!--[--><!--]--><!----><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-1bcd8184><span class="visually-hidden" id="doc-footer-aria-label" data-v-1bcd8184>Pager</span><div class="pager" data-v-1bcd8184><a class="VPLink link pager-link prev" href="/recipes/text-field-component.html" data-v-1bcd8184><!--[--><span class="desc" data-v-1bcd8184>Previous page</span><span class="title" data-v-1bcd8184>Text Field Component</span><!--]--></a></div><div class="pager" data-v-1bcd8184><a class="VPLink link pager-link next" href="/adrs.html" data-v-1bcd8184><!--[--><span class="desc" data-v-1bcd8184>Next page</span><span class="title" data-v-1bcd8184>ADRs</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
26
- <script>window.__VP_HASH_MAP__=JSON.parse("{\"adrs.md\":\"BxjHi9-8\",\"ai.md\":\"Cy9GWnER\",\"assets.md\":\"7C3HWkga\",\"brut-js.md\":\"B4GYxQVw\",\"business-logic.md\":\"BY4hGy0m\",\"cli.md\":\"CjsktgFz\",\"components.md\":\"BBHib1uV\",\"configuration.md\":\"CJ0MmIps\",\"css.md\":\"CltvJqAa\",\"custom-element-tests.md\":\"B_rbta32\",\"database-access.md\":\"gnluu54N\",\"database-schema.md\":\"LpmBPVEU\",\"deployment.md\":\"BLseERGV\",\"dev-environment.md\":\"DRH2D2-O\",\"dir-structure.md\":\"CWir1pic\",\"doc-conventions.md\":\"DOkAuXlt\",\"end-to-end-tests.md\":\"DzqRpZ43\",\"features.md\":\"DPFXsy0z\",\"flash-and-session.md\":\"nPvUpnUx\",\"form-constraints.md\":\"DK5adCgM\",\"forms.md\":\"D5jFZIJz\",\"getting-started.md\":\"abLTnd_n\",\"handlers.md\":\"h84MMB1R\",\"hooks.md\":\"Jmb5VOLA\",\"i18n.md\":\"BAm9t9JJ\",\"index.md\":\"Bn9e0sRJ\",\"instrumentation.md\":\"BgcaGVYH\",\"javascript.md\":\"DzrMxUmI\",\"jobs.md\":\"S-2amAYp\",\"keyword-injection.md\":\"95Zgh2eN\",\"layouts.md\":\"CVGl9xIO\",\"lsp.md\":\"Dn1rIiW0\",\"markdown-examples.md\":\"CCFEQO44\",\"middleware.md\":\"Czz_UlJN\",\"overview.md\":\"DlKiRRG_\",\"pages.md\":\"B7Hc-i6H\",\"recipes_alternate-layouts.md\":\"BwEytl59\",\"recipes_authentication.md\":\"Dzvi_g69\",\"recipes_blank-layouts.md\":\"fyAUJyJR\",\"recipes_custom-flash.md\":\"CrQbI5eH\",\"recipes_indexed-forms.md\":\"CstYyOSo\",\"recipes_migrations.md\":\"CTcnWDJF\",\"recipes_text-field-component.md\":\"H4wLAK0Z\",\"roadmap.md\":\"C6PRi0DX\",\"routes.md\":\"BD6y2i-f\",\"security.md\":\"C0G_AZR-\",\"seed-data.md\":\"BvFZlqIk\",\"space-time-continuum.md\":\"xl44xDos\",\"tutorial.md\":\"C4zR5XPG\",\"unit-tests.md\":\"DUGrnLj5\",\"why.md\":\"C-hk5xgJ\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Brut RB\",\"description\":\"Documentation for the Brut.RB web framework.\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"search\":{\"provider\":\"local\"},\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Overview\",\"link\":\"/overview\"},{\"text\":\"Brut API\",\"link\":\"/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutCSS\",\"link\":\"/brut-css/index.html\",\"target\":\"_self\"}],\"outline\":[2,3],\"sidebar\":[{\"text\":\"Overview\",\"collapsed\":false,\"items\":[{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Concepts\",\"link\":\"/overview\"},{\"text\":\"Features\",\"link\":\"/features\"},{\"text\":\"Directory Structure\",\"link\":\"/dir-structure\"},{\"text\":\"Dev Environment\",\"link\":\"/dev-environment\"},{\"text\":\"Tutorial\",\"link\":\"/tutorial\"},{\"text\":\"Documentation Conventions\",\"link\":\"/doc-conventions\"}]},{\"text\":\"Front-End\",\"collapsed\":false,\"items\":[{\"text\":\"Routes\",\"link\":\"/routes\"},{\"text\":\"Pages\",\"link\":\"/pages\"},{\"text\":\"Layouts\",\"link\":\"/layouts\"},{\"text\":\"Forms\",\"link\":\"/forms\"},{\"text\":\"Form Constraints\",\"link\":\"/form-constraints\"},{\"text\":\"Handlers and Actions\",\"link\":\"/handlers\"},{\"text\":\"Components\",\"link\":\"/components\"},{\"text\":\"Flash and Session\",\"link\":\"/flash-and-session\"},{\"text\":\"Space/Time Continuum\",\"link\":\"/space-time-continuum\"},{\"text\":\"JavaScript\",\"link\":\"/javascript\"},{\"text\":\"CSS\",\"link\":\"/css\"},{\"text\":\"Assets\",\"link\":\"/assets\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js\"}]},{\"text\":\"Back-End\",\"collapsed\":false,\"items\":[{\"text\":\"Database Schema\",\"link\":\"/database-schema\"},{\"text\":\"Database Access\",\"link\":\"/database-access\"},{\"text\":\"Seed Data\",\"link\":\"/seed-data\"},{\"text\":\"Jobs\",\"link\":\"/jobs\"},{\"text\":\"Business Logic\",\"link\":\"/business-logic\"}]},{\"text\":\"Framework\",\"collapsed\":false,\"items\":[{\"text\":\"Configuration\",\"link\":\"/configuration\"},{\"text\":\"Keyword Injection\",\"link\":\"/keyword-injection\"},{\"text\":\"I18n\",\"link\":\"/i18n\"},{\"text\":\"CLI / Tasks\",\"link\":\"/cli\"},{\"text\":\"Deployment\",\"link\":\"/deployment\"}]},{\"text\":\"Testing\",\"collapsed\":false,\"items\":[{\"text\":\"Unit Tests\",\"link\":\"/unit-tests\"},{\"text\":\"End-to-End Tests\",\"link\":\"/end-to-end-tests\"},{\"text\":\"Testing Custom Elements\",\"link\":\"/custom-element-tests\"}]},{\"text\":\"Advanced Topics\",\"collapsed\":true,\"items\":[{\"text\":\"Route Hooks\",\"link\":\"/hooks\"},{\"text\":\"Middleware\",\"link\":\"/middleware\"},{\"text\":\"Instrumentation\",\"link\":\"/instrumentation\"},{\"text\":\"Security\",\"link\":\"/security\"},{\"text\":\"LSP Support\",\"link\":\"/lsp\"}]},{\"text\":\"Recipes\",\"collapsed\":true,\"items\":[{\"text\":\"Migration Basics\",\"link\":\"/recipes/migrations\"},{\"text\":\"Authentication\",\"link\":\"/recipes/authentication\"},{\"text\":\"Alternate Layouts\",\"link\":\"/recipes/alternate-layouts\"},{\"text\":\"Blank Layouts\",\"link\":\"/recipes/blank-layouts\"},{\"text\":\"Custom Flash Class\",\"link\":\"/recipes/custom-flash\"},{\"text\":\"Indexed Form Elements\",\"link\":\"/recipes/indexed-forms\"},{\"text\":\"Text Field Component\",\"link\":\"/recipes/text-field-component\"}]},{\"text\":\"Meta\",\"collapsed\":false,\"items\":[{\"text\":\"Why?!\",\"link\":\"/why\"},{\"text\":\"ADRs\",\"link\":\"/adrs\"},{\"text\":\"Roadmap to 1.0\",\"link\":\"/roadmap\"},{\"text\":\"AI Declaration\",\"link\":\"/ai\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/thirdtank/brut\"}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
26
+ <script>window.__VP_HASH_MAP__=JSON.parse("{\"adrs.md\":\"BxjHi9-8\",\"ai.md\":\"Cy9GWnER\",\"assets.md\":\"7C3HWkga\",\"brut-js.md\":\"B4GYxQVw\",\"business-logic.md\":\"BY4hGy0m\",\"cli.md\":\"CjsktgFz\",\"components.md\":\"B543a3Lm\",\"configuration.md\":\"-foE_iVv\",\"css.md\":\"CltvJqAa\",\"custom-element-tests.md\":\"B_rbta32\",\"database-access.md\":\"gnluu54N\",\"database-schema.md\":\"LpmBPVEU\",\"deployment.md\":\"BLseERGV\",\"dev-environment.md\":\"DRH2D2-O\",\"dir-structure.md\":\"CWir1pic\",\"doc-conventions.md\":\"DOkAuXlt\",\"end-to-end-tests.md\":\"DzqRpZ43\",\"features.md\":\"DPFXsy0z\",\"flash-and-session.md\":\"nPvUpnUx\",\"form-constraints.md\":\"DK5adCgM\",\"forms.md\":\"D5-2rgHh\",\"getting-started.md\":\"Cd4XSZb_\",\"handlers.md\":\"h84MMB1R\",\"hooks.md\":\"Jmb5VOLA\",\"i18n.md\":\"BAm9t9JJ\",\"index.md\":\"Bn9e0sRJ\",\"instrumentation.md\":\"BgcaGVYH\",\"javascript.md\":\"DzrMxUmI\",\"jobs.md\":\"S-2amAYp\",\"keyword-injection.md\":\"95Zgh2eN\",\"layouts.md\":\"CVGl9xIO\",\"lsp.md\":\"Dn1rIiW0\",\"markdown-examples.md\":\"CCFEQO44\",\"middleware.md\":\"Czz_UlJN\",\"overview.md\":\"DlKiRRG_\",\"pages.md\":\"B7Hc-i6H\",\"recipes_alternate-layouts.md\":\"BwEytl59\",\"recipes_authentication.md\":\"Dzvi_g69\",\"recipes_blank-layouts.md\":\"fyAUJyJR\",\"recipes_custom-flash.md\":\"CrQbI5eH\",\"recipes_indexed-forms.md\":\"CstYyOSo\",\"recipes_migrations.md\":\"CTcnWDJF\",\"recipes_text-field-component.md\":\"H4wLAK0Z\",\"roadmap.md\":\"C6PRi0DX\",\"routes.md\":\"BD6y2i-f\",\"security.md\":\"C0G_AZR-\",\"seed-data.md\":\"BvFZlqIk\",\"space-time-continuum.md\":\"xl44xDos\",\"tutorial.md\":\"C4zR5XPG\",\"unit-tests.md\":\"DUGrnLj5\",\"why.md\":\"C-hk5xgJ\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Brut RB\",\"description\":\"Documentation for the Brut.RB web framework.\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"search\":{\"provider\":\"local\"},\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Overview\",\"link\":\"/overview\"},{\"text\":\"Brut API\",\"link\":\"/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutCSS\",\"link\":\"/brut-css/index.html\",\"target\":\"_self\"}],\"outline\":[2,3],\"sidebar\":[{\"text\":\"Overview\",\"collapsed\":false,\"items\":[{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Concepts\",\"link\":\"/overview\"},{\"text\":\"Features\",\"link\":\"/features\"},{\"text\":\"Directory Structure\",\"link\":\"/dir-structure\"},{\"text\":\"Dev Environment\",\"link\":\"/dev-environment\"},{\"text\":\"Tutorial\",\"link\":\"/tutorial\"},{\"text\":\"Documentation Conventions\",\"link\":\"/doc-conventions\"}]},{\"text\":\"Front-End\",\"collapsed\":false,\"items\":[{\"text\":\"Routes\",\"link\":\"/routes\"},{\"text\":\"Pages\",\"link\":\"/pages\"},{\"text\":\"Layouts\",\"link\":\"/layouts\"},{\"text\":\"Forms\",\"link\":\"/forms\"},{\"text\":\"Form Constraints\",\"link\":\"/form-constraints\"},{\"text\":\"Handlers and Actions\",\"link\":\"/handlers\"},{\"text\":\"Components\",\"link\":\"/components\"},{\"text\":\"Flash and Session\",\"link\":\"/flash-and-session\"},{\"text\":\"Space/Time Continuum\",\"link\":\"/space-time-continuum\"},{\"text\":\"JavaScript\",\"link\":\"/javascript\"},{\"text\":\"CSS\",\"link\":\"/css\"},{\"text\":\"Assets\",\"link\":\"/assets\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js\"}]},{\"text\":\"Back-End\",\"collapsed\":false,\"items\":[{\"text\":\"Database Schema\",\"link\":\"/database-schema\"},{\"text\":\"Database Access\",\"link\":\"/database-access\"},{\"text\":\"Seed Data\",\"link\":\"/seed-data\"},{\"text\":\"Jobs\",\"link\":\"/jobs\"},{\"text\":\"Business Logic\",\"link\":\"/business-logic\"}]},{\"text\":\"Framework\",\"collapsed\":false,\"items\":[{\"text\":\"Configuration\",\"link\":\"/configuration\"},{\"text\":\"Keyword Injection\",\"link\":\"/keyword-injection\"},{\"text\":\"I18n\",\"link\":\"/i18n\"},{\"text\":\"CLI / Tasks\",\"link\":\"/cli\"},{\"text\":\"Deployment\",\"link\":\"/deployment\"}]},{\"text\":\"Testing\",\"collapsed\":false,\"items\":[{\"text\":\"Unit Tests\",\"link\":\"/unit-tests\"},{\"text\":\"End-to-End Tests\",\"link\":\"/end-to-end-tests\"},{\"text\":\"Testing Custom Elements\",\"link\":\"/custom-element-tests\"}]},{\"text\":\"Advanced Topics\",\"collapsed\":true,\"items\":[{\"text\":\"Route Hooks\",\"link\":\"/hooks\"},{\"text\":\"Middleware\",\"link\":\"/middleware\"},{\"text\":\"Instrumentation\",\"link\":\"/instrumentation\"},{\"text\":\"Security\",\"link\":\"/security\"},{\"text\":\"LSP Support\",\"link\":\"/lsp\"}]},{\"text\":\"Recipes\",\"collapsed\":true,\"items\":[{\"text\":\"Migration Basics\",\"link\":\"/recipes/migrations\"},{\"text\":\"Authentication\",\"link\":\"/recipes/authentication\"},{\"text\":\"Alternate Layouts\",\"link\":\"/recipes/alternate-layouts\"},{\"text\":\"Blank Layouts\",\"link\":\"/recipes/blank-layouts\"},{\"text\":\"Custom Flash Class\",\"link\":\"/recipes/custom-flash\"},{\"text\":\"Indexed Form Elements\",\"link\":\"/recipes/indexed-forms\"},{\"text\":\"Text Field Component\",\"link\":\"/recipes/text-field-component\"}]},{\"text\":\"Meta\",\"collapsed\":false,\"items\":[{\"text\":\"Why?!\",\"link\":\"/why\"},{\"text\":\"ADRs\",\"link\":\"/adrs\"},{\"text\":\"Roadmap to 1.0\",\"link\":\"/roadmap\"},{\"text\":\"AI Declaration\",\"link\":\"/ai\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/thirdtank/brut\"}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
27
27
 
28
28
  </body>
29
29
  </html>
@@ -1,20 +1,26 @@
1
- # Renders the custom elements used to manage both client- and server-side constraint violations via the `<brut-cv-messages>` and `<brut-cv>` tags. Each constraint violation on the input's {Brut::FrontEnd::Forms::ValidityState} will generate a `<brut-cv server-side>` tag that will contain the I18n translation of the violation's {Brut::FrontEnd::Forms::ConstraintViolation#key} prefixed with `"cv.ss"`.
1
+ # Renders the custom elements used to manage both client- and server-side constraint violations via the `<brut-cv-messages>` and `<brut-cv>` tags. Each constraint violation on the input's {Brut::FrontEnd::Forms::ValidityState} will generate a `<brut-cv server-generated>` tag that will contain the I18n translation of the violation's {Brut::FrontEnd::Forms::ConstraintViolation#key} prefixed with `"cv.cs"` or `"cv.ss"`.
2
2
  #
3
3
  # The general form of this component will be:
4
4
  #
5
5
  # ```html
6
6
  # <brut-cv-messages input-name="«input_name»">
7
- # <brut-cv server-side>
7
+ # <brut-cv server-generated client-side>
8
8
  # «message»
9
9
  # </brut-cv>
10
- # <brut-cv server-side>
10
+ # <brut-cv server-generated server-side>
11
11
  # «message»
12
12
  # </brut-cv>
13
13
  # <!- ... ->
14
14
  # </brut-cv-messages>
15
15
  # ```
16
16
  #
17
- # Note that if you are using `<brut-form>` then `<brut-cv>` elements will be inserted into the `<brut-cv-messages>` element, however they will not have the `server-side` attribute.
17
+ # Notes:
18
+ #
19
+ # * If the form is considered #{Brut::FrontEnd::Form#new?}, then the client-side constraint violations
20
+ # will not be generated. This is to prevent a fresh form from being generated with a bunch of
21
+ # errors already present.
22
+ # * If using `<brut-form>`, the `<brut-cv-messages>` element this generates will be where it inserts
23
+ # client side constraint violations.
18
24
  class Brut::FrontEnd::Components::ConstraintViolations < Brut::FrontEnd::Component
19
25
  # Create a new ConstraintViolations component
20
26
  #
@@ -38,17 +44,24 @@ class Brut::FrontEnd::Components::ConstraintViolations < Brut::FrontEnd::Compone
38
44
  }.merge(@html_attributes)
39
45
 
40
46
  message_html_attributes = {
41
- "server-side": true,
47
+ "server-generated": true,
42
48
  }.merge(@message_html_attributes)
43
49
 
44
50
  brut_cv_messages(**html_attributes) do
45
- @form.input(@input_name, index: @index).validity_state.select { |constraint|
46
- !constraint.client_side?
47
- }.each do |constraint|
48
- brut_cv(**message_html_attributes) do
49
- t("cv.ss.#{constraint}", **constraint.context)
51
+ @form.input(@input_name, index: @index).validity_state.each do |constraint|
52
+ if constraint.client_side?
53
+ if !@form.new?
54
+ brut_cv(**message_html_attributes, client_side: true) do
55
+ t("cv.cs.#{constraint}", **constraint.context)
56
+ end
57
+ end
58
+ else
59
+ brut_cv(**message_html_attributes, server_side: true) do
60
+ t("cv.ss.#{constraint}", **constraint.context)
61
+ end
50
62
  end
51
63
  end
52
64
  end
53
65
  end
66
+
54
67
  end
data/lib/brut/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Brut
2
2
  # @!visibility private
3
- VERSION = "0.9.2"
3
+ VERSION = "0.10.0"
4
4
  end
data/mkbrut/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mkbrut (0.9.2)
4
+ mkbrut (0.10.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,3 +1,3 @@
1
1
  module MKBrut
2
- VERSION = "0.9.2"
2
+ VERSION = "0.10.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brut
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Bryant Copeland
@@ -423,7 +423,6 @@ extensions: []
423
423
  extra_rdoc_files: []
424
424
  files:
425
425
  - ".gitignore"
426
- - ".nvim.lua"
427
426
  - ".projections.json"
428
427
  - CHANGELOG.md
429
428
  - CODE_OF_CONDUCT.txt
@@ -701,6 +700,7 @@ files:
701
700
  - brutrb.com/recipes/authentication.md
702
701
  - brutrb.com/recipes/blank-layouts.md
703
702
  - brutrb.com/recipes/custom-flash.md
703
+ - brutrb.com/recipes/form-errors.md
704
704
  - brutrb.com/recipes/indexed-forms.md
705
705
  - brutrb.com/recipes/migrations.md
706
706
  - brutrb.com/recipes/text-field-component.md
@@ -948,7 +948,7 @@ files:
948
948
  - docs/assets/adrs.md.BxjHi9-8.lean.js
949
949
  - docs/assets/ai.md.Cy9GWnER.js
950
950
  - docs/assets/ai.md.Cy9GWnER.lean.js
951
- - docs/assets/app.DmIaHRc4.js
951
+ - docs/assets/app.vjGWMSnJ.js
952
952
  - docs/assets/assets.md.7C3HWkga.js
953
953
  - docs/assets/assets.md.7C3HWkga.lean.js
954
954
  - docs/assets/basic-form-with-violations.Cv6Y9-Q_.png
@@ -957,16 +957,16 @@ files:
957
957
  - docs/assets/brut-js.md.B4GYxQVw.lean.js
958
958
  - docs/assets/business-logic.md.BY4hGy0m.js
959
959
  - docs/assets/business-logic.md.BY4hGy0m.lean.js
960
- - docs/assets/chunks/@localSearchIndexroot.D0RGp49A.js
961
- - docs/assets/chunks/VPLocalSearchBox.C2QeRbCY.js
960
+ - docs/assets/chunks/@localSearchIndexroot.Dn1xGMv_.js
961
+ - docs/assets/chunks/VPLocalSearchBox.C-ymMW2k.js
962
962
  - docs/assets/chunks/framework.1L-BeKqY.js
963
- - docs/assets/chunks/theme.CFKmBRX5.js
963
+ - docs/assets/chunks/theme.pJUosGlI.js
964
964
  - docs/assets/cli.md.CjsktgFz.js
965
965
  - docs/assets/cli.md.CjsktgFz.lean.js
966
- - docs/assets/components.md.BBHib1uV.js
967
- - docs/assets/components.md.BBHib1uV.lean.js
968
- - docs/assets/configuration.md.CJ0MmIps.js
969
- - docs/assets/configuration.md.CJ0MmIps.lean.js
966
+ - docs/assets/components.md.B543a3Lm.js
967
+ - docs/assets/components.md.B543a3Lm.lean.js
968
+ - docs/assets/configuration.md.-foE_iVv.js
969
+ - docs/assets/configuration.md.-foE_iVv.lean.js
970
970
  - docs/assets/css.md.CltvJqAa.js
971
971
  - docs/assets/css.md.CltvJqAa.lean.js
972
972
  - docs/assets/custom-element-tests.md.B_rbta32.js
@@ -992,10 +992,10 @@ files:
992
992
  - docs/assets/flash-and-session.md.nPvUpnUx.lean.js
993
993
  - docs/assets/form-constraints.md.DK5adCgM.js
994
994
  - docs/assets/form-constraints.md.DK5adCgM.lean.js
995
- - docs/assets/forms.md.D5jFZIJz.js
996
- - docs/assets/forms.md.D5jFZIJz.lean.js
997
- - docs/assets/getting-started.md.abLTnd_n.js
998
- - docs/assets/getting-started.md.abLTnd_n.lean.js
995
+ - docs/assets/forms.md.D5-2rgHh.js
996
+ - docs/assets/forms.md.D5-2rgHh.lean.js
997
+ - docs/assets/getting-started.md.Cd4XSZb_.js
998
+ - docs/assets/getting-started.md.Cd4XSZb_.lean.js
999
999
  - docs/assets/handlers.md.h84MMB1R.js
1000
1000
  - docs/assets/handlers.md.h84MMB1R.lean.js
1001
1001
  - docs/assets/hooks.md.Jmb5VOLA.js
data/.nvim.lua DELETED
@@ -1 +0,0 @@
1
- useLSP = true