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
@@ -92,7 +92,7 @@ end
92
92
 
93
93
  ### Type and Name Enforcement
94
94
 
95
- You'll note that `store`, accepts a class parameter. This is mostly used for documentation, with two exceptions:
95
+ You'll note that `store` accepts a class parameter. This is mostly used for documentation, with two exceptions:
96
96
 
97
97
  * If the type is `Pathname` (which is what is used by `store_ensured_path` and `store_required_path`), the configuration parameter name *must* end in `_file` or `_dir`.
98
98
  * If the type is `"boolean"` or `:boolean`, the configuration parameter name *must* end in a question mark. In this case, the value itself is coerced into `true` or `false`.
@@ -103,8 +103,7 @@ Brut may add more constraints or conversions over time.
103
103
 
104
104
  By default, Brut configuration values cannot be overridden and they cannot be `nil`. When calling `store`, `allow_app_override: true` and `allow_nil: true`, can be passed to change this behavior.
105
105
 
106
- In [Flash and Session](/flash-and-session), we discussed setting your own custom class for managing the flash.
107
- This is possible due to how Brut defines the configuration parameter `flash_class`:
106
+ In [Flash and Session](/flash-and-session), we discussed that you can set your own class for the flash. This is possible due to how Brut defines the configuration parameter `flash_class`:
108
107
 
109
108
  ```ruby {6}
110
109
  Brut.container.store(
@@ -126,7 +125,7 @@ Calling `override` for parameters where `allow_app_override` is not true results
126
125
  `store` on a previously `store`-d parameter results in an error.
127
126
 
128
127
  The idea is to make it extremely clear what values are being set and overridden, and to avoid setting values that
129
- don't exist.
128
+ don't exist or aren't relevant.
130
129
 
131
130
  Some values can be `nil`. Generally, `nil` is a pain and will cause you great hardship. On occasion, it's
132
131
  needed. For example, [external IDs](/database-schema#external-ids) only work if the app provides an app-wide
data/brutrb.com/css.md CHANGED
@@ -42,7 +42,7 @@ First, `package.json` (in your app's root) would include `"foobar-css"`:
42
42
 
43
43
  Next, `app/src/front_end/css/index.css` would import both `"foobar-css"` and `"pages/HomePage.css"`.
44
44
 
45
- ```javascript
45
+ ```css
46
46
  @import "foobar-css/everything.css";
47
47
  @import "pages/HomePage.css";
48
48
  ```
@@ -73,7 +73,7 @@ To use `foobar-thin.css`, you'd write this `@import` directive:
73
73
 
74
74
  ## Using Brut-CSS
75
75
 
76
- By default, Brut includes a lightweight functional CSS library called "brut-css". It provides a basic design system and single-purpose classes to allow you to quickly prototype or build UIs. It is similar to TailwindCSS by far far smaller and simpler (and less powered).
76
+ By default, Brut includes a lightweight functional CSS library called [BrutCSS](/brut-css/index.html). It provides a basic design system and single-purpose classes to allow you to quickly prototype or build UIs. It is similar to TailwindCSS but far far smaller and simpler (and less powered).
77
77
 
78
78
  It is included so you have something to start with. You can use it by using its various classes like `bg-green-300` and `m-4`, or you can use its provided custom properties like `var(--green-300)` and `var(--sp-4)`.
79
79
 
@@ -37,8 +37,7 @@ as well, such as `assert`.
37
37
  The idea is that you use the browser APIs to examine the DOM and assert the behavior of the custom element
38
38
  (as opposed to interacting with the custom element's class).
39
39
 
40
- Suppose that `my-element` transform text inside it based on the `transform` attribute. By default, it's
41
- `lower`, but can be set to `upper` to lower case or upper case, respectively, the text inside.
40
+ Suppose that `my-element` transform text inside it based on the `transform` attribute. By default, it's `lower` (which will lower-case the text), but can be set to `upper` to upper case the text inside.
42
41
 
43
42
  This means you'll need three tests, each with a different DOM:
44
43
 
@@ -72,7 +71,7 @@ describe("<some-element>", () => {
72
71
  })
73
72
  ```
74
73
 
75
- when the function you give to `test` is executed, the DOM will have been setup, so you can rely on your
74
+ When the function you give to `test` is executed, the DOM will have been setup, so you can rely on your
76
75
  custom elements `connectedCallback` having been called. Assuming the text transformation for `my-element`
77
76
  occurs in `connectedCallback`, here is how you'd test all three cases:
78
77
 
@@ -109,7 +108,7 @@ describe("<some-element>", () => {
109
108
  })
110
109
  ```
111
110
 
112
- You'll notice almost all of this uses the browser APIs you (should :) know and (hopefully :) love.
111
+ You'll notice almost all of this uses the browser APIs you (should) know and (hopefully) love.
113
112
 
114
113
  You can manipulate the DOM inside a test as well, and it should behave as if you are doing it in a
115
114
  browser. Note that many browser APIs are synchronous, so you don't have to add `await` before every
@@ -156,7 +156,7 @@ end
156
156
  ### Do Not Put Business Logic On Your Database Models
157
157
 
158
158
  There's no reason to, or benefit to doing so. What you'll find is that any app of even moderate complexity will
159
- not have a strict mapping from page to business concept to database table. Rather these things will all differ
159
+ not have a strict mapping from page to business concept to database table. Rather, these things will all differ
160
160
  greatly, and each serves a different purpose.
161
161
 
162
162
  The job of your data models—and the tables they provide access to—is to store reliable and unambiguous data.
@@ -1,10 +1,6 @@
1
1
  # Database Schema / Migrations
2
2
 
3
- Brut provides access to the database via the [Sequel library](https://sequel.jeremyevans.net/). Sequel is fully featured and provides a lot of ways of interacting with and managing your database. Brut includes several plugins and extensions to provide opinionated default behavior or additional features.
4
-
5
- One thing to keep in mind is that Brut refers to your database layer as *database models* (notably not the un-qualified "models"). Brut treats this layer as a *model* of your database, not a model of your *domain* (though you are free to conflate the two).
6
-
7
- This section details how to manage your database schema.
3
+ Brut provides access to the database via the [Sequel library](https://sequel.jeremyevans.net/). To manage your database schema, Brut uses Sequel's facility for this, with some of its own enhancements.
8
4
 
9
5
  > [!NOTE]
10
6
  > Brut currently only supports Postgres. Sequel supports many database systems, however Brut's extensions are
@@ -12,22 +8,14 @@ This section details how to manage your database schema.
12
8
 
13
9
  ## Overview
14
10
 
15
- Brut uses *migrations* to control and manage the schema of your database. Migrations are changes to the
16
- schema that depend on the changes before them. In a running production database, you will not be able to
17
- create the database schema from scratch—you will have to modify the existing schema to produce the schema
18
- you want.
11
+ Your database schema is managed by a series of changes that build upon one another
12
+ called *migrations*.
19
13
 
20
- For example, if you have a table `widgets` that has a `name` and `description`, to add a `status` field,
21
- you cannot `drop table widgets` and then `create table widgets(...)` with the fields. You must instead
22
- `alter table widgets(...)` to add the new column.
14
+ For example, if you have a table `widgets` that has a `name` and `description`, to add a `status` field, you cannot `drop table widgets` and then `create table widgets(...)` with the fields. You must instead `alter table widgets(...)` to add the new column.
23
15
 
24
16
  Thus, each migration file is a change to the schema produced by all previous migration files.
25
17
 
26
- Brut's provides this via Sequels. See [both](https://sequel.jeremyevans.net/rdoc/files/doc/schema_modification_rdoc.html) [docs](https://sequel.jeremyevans.net/rdoc/files/doc/migration_rdoc.html) for details on the API. Any schema modification method Sequel documents is available, however some default behavior has changed.
27
-
28
- Schema files are located in `app/src/back_end/data_models/migrations` and are named using a timestamp-based
29
- scheme. This means that when you create a new migration, its name will be based on the time and date you
30
- created it, and any migrations that have not been applied will be applied in timestamp order.
18
+ Brut's provides this via Sequel. See [both](https://sequel.jeremyevans.net/rdoc/files/doc/schema_modification_rdoc.html) [docs](https://sequel.jeremyevans.net/rdoc/files/doc/migration_rdoc.html) for details on the API. Any schema modification method Sequel documents is available, however some default behavior has changed.
31
19
 
32
20
  ### Creating Migrations
33
21
 
@@ -40,6 +28,10 @@ together to form the filename:
40
28
  app/src/back_end/data_models/migrations/20250508132646_user-accounts.rb
41
29
  ```
42
30
 
31
+ Note that the files are located in `app/src/back_end/data_models/migrations` and
32
+ have a name prefixed with a timestamp. This timestamp determins an ordering of how
33
+ the files are applied to the database.
34
+
43
35
  The file is created mostly blank:
44
36
 
45
37
  ```ruby
@@ -49,19 +41,17 @@ Sequel.migration do
49
41
  end
50
42
  ```
51
43
 
52
- Sequels' migration system is similar to Active Record's/Rails' in design and spirit, but the API is different.
53
- Please consult the documentation and don't assume Active Record's DSL will work. It will not.
44
+ > [!NOTE]
45
+ > Sequels' migration API is similar in concept to Rails', but differs
46
+ > significantly in specifics. Please consult Sequel's documentation and
47
+ > don't assume Railsism will work the same way.
54
48
 
55
- One thing to note is that Brut encourages the creation of only "up" migrations. That is, migrations that change a
56
- database. "Down" migrations, which revert a change, are discouraged. See *Recommended Practices* for a detailed
57
- explanation.
49
+ Brut encourages only "up" migrations. Since Brut treats your development database
50
+ as ephemeral, there is little value to managing "down" migrations.
58
51
 
59
- This is also why Sequel's `change` method is not included in the scaffolded code. `change`, like Active Record's
60
- method of the same name, automagically creates both "up" and "down" migrations, but *only* if you use the DSL. If
61
- you use raw SQL, `change` doesn't work. But that doesn't matter for Brut (again, see *Recommended Practices*).
52
+ This is why Sequel's `change` method is not included in the scaffolded code. `change`, like Active Record's method of the same name, automagically creates both "up" and "down" migrations, but *only* if you use the DSL. If you use raw SQL, `change` doesn't work. By using only `up`, you won't have to worry about this.
62
53
 
63
- Let's create a user accounts table that has an email field, a `deactivated_at` timestamp, and a `created_at`
64
- timestamp:
54
+ Let's create an accounts table that has an email field, a `deactivated_at` timestamp, and a `created_at` timestamp:
65
55
 
66
56
  ```ruby
67
57
  Sequel.migration do
@@ -90,10 +80,6 @@ To apply this migration use `bin/db migrate`
90
80
  > bin/db migrate
91
81
  ```
92
82
 
93
- If you create a new migration, it will use a timestamp that is alphanumerically greater than the one we just made
94
- and thus that migration will be applied after this one. Thus, you can rely on previous migrations having been
95
- applied when authoring new ones.
96
-
97
83
  ### Managing Migrations
98
84
 
99
85
  Sequel uses a special database table to understand which migrations have been run. This table will exist in
@@ -126,16 +112,19 @@ provides a more helpful error message when no records are found
126
112
  #### External IDs
127
113
 
128
114
  It's often useful to provide a unique identifier for a record that is not the database primary key. There are
129
- many advantages to doing so, but the core value Brut has regarding this is that database primary and foreign keys
130
- are considered private and internal and for developer use only. It is trivial to produce externalizable keys, as
131
- you'll see, so there's no reason to expose primary keys.
115
+ many advantages to doing so, the main being that your primary and foreign keys are
116
+ considered private and for developer use only. Creating additional externalizable
117
+ unique keys is trivial, so Brut provides a way to do that.
132
118
 
133
119
  > [!NOTE]
134
- > Take care to differentiate the terms *primary key* and *key*. In relational database literature, and thus
135
- > in Brut, a *key* is any value that uniquely identifies a record. `email` in the `accounts` table above
136
- > is a key. The *primary key* is single source of truth for identifying records internally in the database.
137
- > Thus, it can be used as a *foreign key* to create relationships between tables. Brut further implements
138
- > this as a *surrogate* or *synthetic* key, which means the value itself has no business or domain meaning.
120
+ > **Primary keys** and **keys** are not the same thing. **Primary keys** are
121
+ > what is used to identify a record for the purposes of referential integrity.
122
+ > A **key** simply uniquely identifies a row or is a unique constraint on a table.
123
+ > Tables have only one primary key, but potentially many keys. Brut uses
124
+ > *synthetic* (sometimes called *surrogate*) keys as primary keys. This means
125
+ > they have no business meaning and can be safely used for foreighn keys
126
+ > and other cases without conflating them with domain concepts.
127
+
139
128
 
140
129
  In Brut, an external ID is automatically generated by the database when a record is created. By convention, it
141
130
  is prefixed with a short string representing your app and a short string representing the table, followed by a
@@ -190,8 +179,7 @@ This means that you can set values explicitly if you like, *and* you can change
190
179
 
191
180
  ### Brut Migration Changes and Enhancement
192
181
 
193
- Brut attempts to set default behavior for migrations to encourage a modicum of best practices. This lists out
194
- the changes and a brief explanation for the purpose of the change.
182
+ Brut attempts to set default behavior for migrations to encourage a modicum of best practices. These are:
195
183
 
196
184
  * **Automatic synthetic primary key named `id` of type `int`.** You almost always want this. You can change the
197
185
  primary key configuration per table if you like, but if you do nothing, you get a primary key that works for 99%
@@ -1,6 +1,6 @@
1
1
  # Dev Environment
2
2
 
3
- Brut provides sophisticatd tooling to manage your dev environment
3
+ Brut provides sophisticated tooling to manage your dev environment
4
4
 
5
5
  ## Overview
6
6
 
@@ -41,7 +41,7 @@ Your editor and version control system run on your computer.
41
41
  These are the commands you will use to manage the *foundational core*, which is the Docker containers and
42
42
  their contents.
43
43
 
44
- A few brief terminoloy notes if you aren't familiar with Docker:
44
+ A few brief terminology notes if you aren't familiar with Docker:
45
45
 
46
46
  * A Docker *container* is akin to a virtual machine. On Linux this isn't strictly true, but conceptually, you can think of this like a virtual computer.
47
47
  * A Docker *image* is what you use to start a container. This is akin to a disk image you might use to
@@ -52,7 +52,7 @@ A few verbs to provide additional help:
52
52
 
53
53
  * One *builds* a Docker image from a Dockerfile.
54
54
  * One *starts* a Docker container from an image.
55
- * One *stops* a Docker conatiner when it's no longer needed.
55
+ * One *stops* a Docker container when it's no longer needed.
56
56
 
57
57
 
58
58
  | App | Purpose |
@@ -68,7 +68,7 @@ The workflow for the foundational core is shown in this diagram.
68
68
 
69
69
  In words:
70
70
 
71
- 1. You build the images based on the latest instrutions via `dx/build`.
71
+ 1. You build the images based on the latest instructions via `dx/build`.
72
72
  2. You start up the environment with `dx/start`.
73
73
  3. You then use `dx/exec` to execute commands from the Workspace (see below).
74
74
  4. When you are done working for the day, `dx/stop` shuts everything down.
@@ -90,7 +90,7 @@ you full documentation about what the command and subcommands do.
90
90
  | | `drop` | Drop the database if it exists |
91
91
  | | `migrate` | Apply any outstanding migrations to the database |
92
92
  | | `new_migration` | Create a new migration file |
93
- | | `rebuild` | Drop, re-create, and run migrations, effecitvely rebuilding the entire database |
93
+ | | `rebuild` | Drop, re-create, and run migrations, effectively rebuilding the entire database |
94
94
  | | `seed` | Load seed data into the database |
95
95
  | | `status` | Check the status of the database and migrations |
96
96
  | <code style="white-space: nowrap">bin/dbconsole</code> | None | Starts up a `psql` session to your database |
@@ -138,7 +138,7 @@ While you are free to set up mise or rbenv or whatever to run all this on your c
138
138
  working is currently not supported nor encouraged. For now, Brut will focus on the Docker-based approach.
139
139
 
140
140
  The primary reason is that it's a tightly controlled environment that is almost
141
- entirely scriptable, but does not require devs to abandon their preffered editor.
141
+ entirely scriptable, but does not require devs to abandon their preferred editor.
142
142
  Environment manager-based approaches tend to be more fussy and require documentation
143
143
  to ensure they are set up.
144
144
 
@@ -174,7 +174,7 @@ exit Brut::CLI.app(
174
174
  )
175
175
  ```
176
176
 
177
- These files have a lot of duplication, but should be relatively stable.
177
+ These files have some duplication, but should be relatively stable.
178
178
 
179
179
  This means that Brut-provided CLIs *will* be updated when you update Brut. Compare this to the files in
180
180
  `dx/` which are entire Bash scripts that will not be updated when Brut is updated.
@@ -0,0 +1,120 @@
1
+ # Directory Structure
2
+
3
+ ```
4
+ .
5
+ ├── app
6
+ │   ├── config
7
+ │   │   └── i18n
8
+ │   │   └── en
9
+ │   ├── public
10
+ │   │   ├── css
11
+ │   │   ├── js
12
+ │   │   └── static
13
+ │   │   └── images
14
+ │   └── src
15
+ │   ├── back_end
16
+ │   │   ├── data_models
17
+ │   │      ├── db
18
+ │   │      ├── migrations
19
+ │   │      └── seed
20
+ │   ├── cli
21
+ │   └── front_end
22
+ │   ├── components
23
+ │   ├── css
24
+ │   ├── fonts
25
+ │   ├── forms
26
+ │   ├── handlers
27
+ │   ├── images
28
+ │   ├── js
29
+ │   ├── layouts
30
+ │   ├── pages
31
+ │   ├── route_hooks
32
+ │   ├── support
33
+ │   └── svgs
34
+ ├── bin
35
+ ├── deploy
36
+ ├── dx
37
+ └── specs
38
+ ├── back_end
39
+ │   ├── data_models
40
+ │      └── db
41
+ ├── e2e
42
+ ├── factories
43
+ │   └── db
44
+ └── front_end
45
+ ├── components
46
+ ├── handlers
47
+ ├── js
48
+ ├── pages
49
+ └── support
50
+ ```
51
+
52
+ ## Top Level
53
+
54
+ | Directory | Purpose |
55
+ |-----------|---------|
56
+ | `app/` | Contains all configuration and source code specific to your app |
57
+ | `bin/` | Contains tasks and other CLIs to do development of your app, such as `bin/test` |
58
+ | `dx/` | Contains scripts to manage your development environment |
59
+ | `specs/` | Contains all tests |
60
+
61
+ ## Inside `app`/
62
+
63
+ | Directory | Purpose |
64
+ |-----------|---------|
65
+ | `bootstrap.rb` | A ruby file that sets up your app and ensures everything is `require`d in the right way. |
66
+ | `config/` | Configuration for your app, such as localizations and translations. Brut tries very hard to make sure there is no YAML in here at all. YAML is not good for you. |
67
+ | `public/` | Root of public assets served by the app. |
68
+ | `src/` | All source code for your app |
69
+
70
+ Inside `app/src`
71
+
72
+ | Directory | Purpose |
73
+ |-----------|---------|
74
+ | `app.rb` | The core of your app, mostly configuration, such as routes, hooks, middleware, etc. |
75
+ | `back_end/` | Back end classes for your app including database schema, DB models, seed data, and your domain logic |
76
+ | `cli/` | Any CLIs or tasks for your app |
77
+ | `front_end/` | The front-end for your app, including pages, components, forms, handlers, JavaScript, and assets |
78
+
79
+ Inside `app/src/back_end`
80
+
81
+ | Directory | Purpose |
82
+ |-----------|---------|
83
+ | `data_models/app_data_model.rb` | Base class for all DB model classes |
84
+ | `data_models/db` | DB model classes |
85
+ | `data_models/db.rb` | Namespace module for DB model classes |
86
+ | `data_models/migrations` | Database schema migrations |
87
+ | `data_models/seed` | Seed data used for local development |
88
+
89
+ Inside `app/src/front_end`
90
+
91
+ |Directory | Purpose |
92
+ |----------------|---------|
93
+ | `components/` | Component classes |
94
+ | `css/` | CSS, managed by esbuild and `bin/build-assets` |
95
+ | `fonts/` | Custom fonts, managed by esbuild and `bin/build-assets` |
96
+ | `forms/` | Form classes |
97
+ | `handlers/` | Handler classes |
98
+ | `images/` | Images, copied to `app/public` by `bin/build-assets` |
99
+ | `js/` | JavaScript, managed by esbuild and `bin/build-assets` |
100
+ | `layouts/` | Layout classes |
101
+ | `middlewares/` | Rack Middleware, if any |
102
+ | `pages/` | Page classes |
103
+ | `route_hooks/` | Route hooks, if any |
104
+ | `support/` | General support classes/junk drawer. |
105
+ | `svgs/` | SVGs you want to render inline |
106
+
107
+ ## Inside `specs/`
108
+
109
+ `specs/` is intended to mirror `app/src`, but has a few extra directories:
110
+
111
+
112
+ |Directory | Purpose |
113
+ |----------------|---------|
114
+ | `specs/back_end` | tests for all back-end code, organized the same as `app/src/back_end` |
115
+ | `specs/back_end/data_models/db` | tests for all DB classes, if needed |
116
+ | `specs/e2e` | End-to-end tests, organized however you like |
117
+ | `specs/factories` | Root of all factories for FactoryBot. You can create subdirectories here for non-DB classes you may want to be able to create |
118
+ | `specs/factories/db` | Factories to create DB records |
119
+ | `specs/front_end` | tests for all front-end code, organized the same as `app/src/front_end` |
120
+ | `specs/js`| *JavaScript* code to test any autonomous custom elements you have created |
@@ -2,32 +2,35 @@
2
2
 
3
3
  ## Terminology
4
4
 
5
- Brut attempts to use existing terminology where possible, particularly where that technology applies to the web platform. For example, there is not a thing called "CSS variables", rather the term is "custom properties". HTML entities are *elements* or *tags* that have *attributes*. As another example, HTML doesn't have *validations*, rather it as *constraints*, which can be *violated*.
5
+ Brut attempts to use existing terminology where possible, particularly where that technology applies to the web platform. For example, there is not a thing called "CSS variables", rather the term is "custom properties".
6
6
 
7
- When speaking about Ruby, we prefer the term *initializer* over constructor, *parameters* over arguments, and *methods* over messages. We also prefer *tests* over specs, however test files *are* located in `specs/` and named `*.spec.rb` to be consistent with RSpec's nomenclature. We prefer *end-to-end* or *e2e* tests instead of browser tests or request specs.
7
+ Here are some common exampels:
8
8
 
9
- Further, Brut doesn't render HTML, it *generates* it. The browser renders the HTML for the website's visitor. One exception is Phlex, which uses the term *render* to mean "generate HTML to an internal buffer that will be delivered to the client later". Thus, you will need to call Phlex's `render` method from time to time, even though it is not rendering HTML but helping to generate it.
10
9
 
11
- Lastly, the documentation tries to talk about the person accessing a website as a "vistor" not a "user". Though the "user" nomenclature is near-ossified in software development, we feel "visitor" is more apt.
10
+ - HTML entities are **elements** or **tags**
11
+ - HTML elements have **attributes**.
12
+ - Forms don't have validations, they have **constraints** which are **violated** by invalid data.
13
+ - Ruby classes don't have constructors, they have **initializers**.
14
+ - Invoking behavior on a Ruby object is **calling a method**, not sending a message.
15
+ - Despite being in `specs/`, the files in there are **tests**, not specifications or
16
+ -specs".
17
+ - Tests that use a browser are **end to end** or **e2e** tests.
18
+ - HTML is not rendered, but **generated**. The browser renders the HTML sent to it by the server, along with the CSS.
19
+ - Your app or site doesn't have users, it has **visitors**.
12
20
 
13
21
  ## Structure of These Documents
14
22
 
15
23
  Each page here documents on aspect of Brut, called a *module*, and these pages are organized along four sections:
16
24
 
17
- * **Overview** - provides detailed information on how this part of Brut works, with minimal examples to orient you to
18
- the terms and design of that module. Links to reference documentation are provided inline as needed.
19
- * **Testing** - information about how to write tests for the code in this module. For example, in [Pages](/pages), we detail how you are intended to test page classes.
20
- * **Recommended Practices** - this section outlines what we believe is the best way to use the module, along with
21
- justifications for the recommended approach. While you are free to ignore this advice, it's often useful to
22
- understand the intention of the authors.
23
- * **Technical Notes** - where appropriate, technical details about how or why the module works the way it does
24
- are provided. This section should be marked with a date to allow you to understand the recency of the
25
- information. It may not always be up to date, but this can help further clarify what is happening under the
26
- covers and why.
25
+ * **Overview** - What the module does, how it works, and a brief example.
26
+ * **Testing** - How to test the code you write in this module.
27
+ * **Recommended Practices** - Opinions from the creators about how best to think about the code in this module.
28
+ * **Technical Notes** - details about the technical implementations that may be
29
+ useful as context.
27
30
 
28
31
  ## Names of the Library and Associated Modules
29
32
 
30
- This framework is called "Brut" though may be called "BrutRB". It lives at `brutrb.com`.
33
+ This framework is called "Brut" though may be called "BrutRB" or "brut-rp". It lives at `brutrb.com`. Never use "brutRB", "brut_rb", etc.
31
34
 
32
35
  The JavaScript library is called "BrutJS", but is `brut-js` in code or the filesystem. "Brut-JS" is wrong, as is `brut_js`.
33
36
 
data/brutrb.com/dx ADDED
@@ -0,0 +1 @@
1
+ ../dx
@@ -55,7 +55,7 @@ RSpec.describe "logging into the website" do
55
55
  end
56
56
  ```
57
57
 
58
- `playwright-ruby-client` provides excellent documentation on how it has adapter Playwright's API for use
58
+ `playwright-ruby-client` provides excellent documentation on how it has adapted Playwright's API for use
59
59
  in Ruby.
60
60
 
61
61
  ### Test Setup
@@ -70,9 +70,15 @@ jobs, but rather assert the effects those jobs will have. Redis is flushed betwe
70
70
 
71
71
  Inside your test, `t` is available to produce translations. You can also access all your page and handler classes, so you can (and should) use `.routing`, e.g. `DashboardPage.routing`, to generate or access routes for your app.
72
72
 
73
- You can set `e2e_timeout` on any test to override the default amount of time Playwright will wait for a
73
+ You can set the `e2e_timeout` metadata on any test to override the default amount of time Playwright will wait for a
74
74
  locator to locate an element. The default is 5 seconds.
75
- `, to generate or access routes for your app.
75
+
76
+ ```ruby
77
+ RSpec.describe "Test for login", e2e_timeout: 10 do
78
+ # ...
79
+ end
80
+ ```
81
+
76
82
  You can also configure behavior with environment variables:
77
83
 
78
84
  | Variable | Default | Purpose |
@@ -122,12 +128,9 @@ The main Playwright documentation encourages you to locate elements by "accessib
122
128
  indirect ways of finding elements. In practice, this is error prone and tedious. Determining the
123
129
  accessible name of an element is not always easy.
124
130
 
125
- We recommend you assess your app's accssibility in another way than trying to do it while performing
126
- end-to-end tests. Instead, locate elements with CSS selectors—this is what you'd use to debug your app so
127
- it makes sense as a testing technique.
131
+ We recommend you assess your app's accessibility in another way than trying to do it while performing end-to-end tests. Instead, locate elements with CSS selectors—this is what you'd use to debug your app so it makes sense as a testing technique.
128
132
 
129
- Insulating your end-to-end tests from markup changes does not produce significant savinsg and can make
130
- tests more difficult to write.
133
+ Insulating your end-to-end tests from markup changes does not produce significant savings and can make tests more difficult to write.
131
134
 
132
135
  ### Testing Must Inform your HTML
133
136
 
@@ -170,5 +173,4 @@ make sure it's still needed.
170
173
 
171
174
  _Last Updated June 13, 2025_
172
175
 
173
- The test server is run bin `bin/test-server`, which is why Sidekiq will be running when your app is
174
- running for an e2e test.
176
+ The test server is run via `bin/test-server`.