brut 0.0.28 → 0.0.29

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 (369) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.projections.json +10 -0
  4. data/.rspec +3 -0
  5. data/Dockerfile.dx +32 -14
  6. data/Gemfile.lock +1 -1
  7. data/assets/Logo-Square.pxd +0 -0
  8. data/assets/LogoPylon.pxd +0 -0
  9. data/assets/LogoStop.pxd +0 -0
  10. data/assets/LogoTall.pxd +0 -0
  11. data/bin/docs +24 -2
  12. data/bin/rspec +27 -0
  13. data/bin/setup +3 -3
  14. data/brutrb.com/.vitepress/theme/custom.css +7 -0
  15. data/brutrb.com/.vitepress/theme/style.css +26 -15
  16. data/brutrb.com/deployment.md +123 -45
  17. data/brutrb.com/images/LogoPylon.png +0 -0
  18. data/brutrb.com/images/LogoSquare.png +0 -0
  19. data/brutrb.com/images/LogoStop.png +0 -0
  20. data/brutrb.com/images/LogoTall.png +0 -0
  21. data/brutrb.com/images/OverviewMetro.graffle +0 -0
  22. data/brutrb.com/images/OverviewMetro.png +0 -0
  23. data/brutrb.com/index.md +4 -3
  24. data/brutrb.com/overview.md +6 -6
  25. data/docker-compose.dx.yml +5 -2
  26. data/docs/404.html +3 -3
  27. data/docs/ai.html +4 -4
  28. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  29. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  30. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  31. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  32. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  33. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  34. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  35. data/docs/api/Brut/BackEnd.html +1 -1
  36. data/docs/api/Brut/CLI/App.html +1 -1
  37. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  38. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  39. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  40. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
  41. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  42. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  43. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  45. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  46. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  47. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  48. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  50. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  51. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  52. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  53. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
  54. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  55. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  56. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  58. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  59. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  60. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  61. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  62. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  63. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  64. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  65. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  66. data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
  67. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  68. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  69. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  70. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  71. data/docs/api/Brut/CLI/Apps.html +1 -1
  72. data/docs/api/Brut/CLI/Command.html +1 -1
  73. data/docs/api/Brut/CLI/Error.html +1 -1
  74. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  75. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  76. data/docs/api/Brut/CLI/Executor.html +1 -1
  77. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  78. data/docs/api/Brut/CLI/Options.html +1 -1
  79. data/docs/api/Brut/CLI/Output.html +1 -1
  80. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  81. data/docs/api/Brut/CLI.html +1 -1
  82. data/docs/api/Brut/FactoryBot.html +1 -1
  83. data/docs/api/Brut/Framework/App.html +1 -1
  84. data/docs/api/Brut/Framework/Config.html +1 -1
  85. data/docs/api/Brut/Framework/Container.html +1 -1
  86. data/docs/api/Brut/Framework/Error.html +1 -1
  87. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  88. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  89. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  90. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  91. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  92. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  93. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  94. data/docs/api/Brut/Framework/Errors.html +1 -1
  95. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  96. data/docs/api/Brut/Framework/MCP.html +1 -1
  97. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  98. data/docs/api/Brut/Framework.html +1 -1
  99. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  100. data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
  101. data/docs/api/Brut/FrontEnd/Component.html +1 -1
  102. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  103. data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
  104. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  105. data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
  106. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  107. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +1 -1
  108. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
  109. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +1 -1
  110. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +1 -1
  111. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  112. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  113. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  114. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  115. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  116. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  117. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  118. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  119. data/docs/api/Brut/FrontEnd/Form.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +201 -0
  122. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +535 -0
  123. data/docs/api/Brut/FrontEnd/Forms/Input.html +983 -35
  124. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
  125. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +29 -19
  126. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +9 -3
  127. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  128. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +9 -3
  129. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  130. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  132. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  133. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  134. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  135. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  136. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  137. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  138. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  141. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  142. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  143. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  144. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  146. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  147. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  148. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  149. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  150. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  151. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  152. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  153. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  154. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  155. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  156. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  157. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  158. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  159. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  160. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  161. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  162. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  163. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  164. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  165. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  166. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  167. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  168. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  169. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  170. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  171. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  172. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  173. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  174. data/docs/api/Brut/FrontEnd.html +1 -1
  175. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  176. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  177. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  178. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  179. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  180. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  181. data/docs/api/Brut/I18n.html +1 -1
  182. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  183. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  184. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  185. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  186. data/docs/api/Brut/Instrumentation.html +1 -1
  187. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  188. data/docs/api/Brut/SinatraHelpers.html +1 -1
  189. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  190. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  191. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  192. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  193. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  194. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  195. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  196. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  197. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  198. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  199. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  200. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  201. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  202. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  203. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  204. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  205. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  206. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  207. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  208. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  209. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  210. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  211. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  212. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  213. data/docs/api/Brut/SpecSupport.html +1 -1
  214. data/docs/api/Brut.html +1 -1
  215. data/docs/api/Clock.html +1 -1
  216. data/docs/api/RichString.html +1 -1
  217. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  218. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  219. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  220. data/docs/api/Sequel/Extensions.html +1 -1
  221. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  222. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  223. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  224. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  225. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  226. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  227. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  228. data/docs/api/Sequel/Plugins.html +1 -1
  229. data/docs/api/Sequel.html +1 -1
  230. data/docs/api/_index.html +15 -1
  231. data/docs/api/class_list.html +1 -1
  232. data/docs/api/css/full_list.css +2 -1
  233. data/docs/api/css/style.css +14 -13
  234. data/docs/api/file.README.html +1 -1
  235. data/docs/api/index.html +1 -1
  236. data/docs/api/method_list.html +419 -275
  237. data/docs/api/top-level-namespace.html +1 -1
  238. data/docs/assets/LogoStop.X8x-4riz.png +0 -0
  239. data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
  240. data/docs/assets/{app.BX81XO4N.js → app.BhrfSt68.js} +1 -1
  241. data/docs/assets/chunks/@localSearchIndexroot.CeRAdP1K.js +1 -0
  242. data/docs/assets/chunks/{VPLocalSearchBox.gABXcTWp.js → VPLocalSearchBox.Dpot_2H4.js} +1 -1
  243. data/docs/assets/chunks/{theme.DwUXXAL3.js → theme.N2SNVLgU.js} +2 -2
  244. data/docs/assets/{configuration.md.BGHl8oRC.js → configuration.md.LG-zIBww.js} +1 -1
  245. data/docs/assets/deployment.md.BLseERGV.js +48 -0
  246. data/docs/assets/deployment.md.BLseERGV.lean.js +1 -0
  247. data/docs/assets/{getting-started.md.Ciz82L0m.js → getting-started.md.Dj0qtZI2.js} +2 -2
  248. data/docs/assets/index.md.CuBB-BdM.js +1 -0
  249. data/docs/assets/index.md.CuBB-BdM.lean.js +1 -0
  250. data/docs/assets/{overview.md.C5wlBcR5.js → overview.md.DVKRM8zl.js} +4 -4
  251. data/docs/assets/overview.md.DVKRM8zl.lean.js +1 -0
  252. data/docs/assets/{style.D73IYGCX.css → style.B2o1L9eN.css} +1 -1
  253. data/docs/assets.html +4 -4
  254. data/docs/brut-js/api/AjaxSubmit.html +1 -1
  255. data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
  256. data/docs/brut-js/api/Autosubmit.html +1 -1
  257. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  258. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  259. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  260. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  261. data/docs/brut-js/api/BufferedLogger.html +1 -1
  262. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  263. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  264. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  265. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  266. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  267. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  268. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  269. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  270. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  271. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  272. data/docs/brut-js/api/Form.html +1 -1
  273. data/docs/brut-js/api/Form.js.html +1 -1
  274. data/docs/brut-js/api/I18nTranslation.html +1 -1
  275. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  276. data/docs/brut-js/api/LocaleDetection.html +1 -1
  277. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  278. data/docs/brut-js/api/Logger.html +1 -1
  279. data/docs/brut-js/api/Logger.js.html +1 -1
  280. data/docs/brut-js/api/Message.html +1 -1
  281. data/docs/brut-js/api/Message.js.html +1 -1
  282. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  283. data/docs/brut-js/api/RichString.html +1 -1
  284. data/docs/brut-js/api/RichString.js.html +1 -1
  285. data/docs/brut-js/api/Tabs.html +1 -1
  286. data/docs/brut-js/api/Tabs.js.html +1 -1
  287. data/docs/brut-js/api/Tracing.html +1 -1
  288. data/docs/brut-js/api/Tracing.js.html +1 -1
  289. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  290. data/docs/brut-js/api/external-Performance.html +1 -1
  291. data/docs/brut-js/api/external-Promise.html +1 -1
  292. data/docs/brut-js/api/external-ValidityState.html +1 -1
  293. data/docs/brut-js/api/external-Window.html +1 -1
  294. data/docs/brut-js/api/external-fetch.html +1 -1
  295. data/docs/brut-js/api/global.html +1 -1
  296. data/docs/brut-js/api/index.html +1 -1
  297. data/docs/brut-js/api/index.js.html +1 -1
  298. data/docs/brut-js/api/module-testing.html +1 -1
  299. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  300. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  301. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  302. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  303. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  304. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  305. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  306. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  307. data/docs/brut-js/api/testing_index.js.html +1 -1
  308. data/docs/brut-js.html +4 -4
  309. data/docs/business-logic.html +4 -4
  310. data/docs/cli.html +4 -4
  311. data/docs/components.html +4 -4
  312. data/docs/configuration.html +6 -6
  313. data/docs/css.html +4 -4
  314. data/docs/custom-element-tests.html +4 -4
  315. data/docs/database-access.html +4 -4
  316. data/docs/database-schema.html +4 -4
  317. data/docs/deployment.html +53 -6
  318. data/docs/dev-environment.html +4 -4
  319. data/docs/doc-conventions.html +4 -4
  320. data/docs/end-to-end-tests.html +4 -4
  321. data/docs/flash-and-session.html +4 -4
  322. data/docs/forms.html +4 -4
  323. data/docs/getting-started.html +7 -7
  324. data/docs/handlers.html +4 -4
  325. data/docs/hashmap.json +1 -1
  326. data/docs/hooks.html +4 -4
  327. data/docs/i18n.html +4 -4
  328. data/docs/index.html +6 -6
  329. data/docs/instrumentation.html +4 -4
  330. data/docs/javascript.html +4 -4
  331. data/docs/jobs.html +4 -4
  332. data/docs/keyword-injection.html +4 -4
  333. data/docs/layouts.html +4 -4
  334. data/docs/lsp.html +4 -4
  335. data/docs/markdown-examples.html +4 -4
  336. data/docs/middleware.html +4 -4
  337. data/docs/not-released.html +4 -4
  338. data/docs/overview.html +9 -9
  339. data/docs/pages.html +4 -4
  340. data/docs/recipes/authentication.html +4 -4
  341. data/docs/routes.html +4 -4
  342. data/docs/security.html +4 -4
  343. data/docs/seed-data.html +4 -4
  344. data/docs/space-time-continuum.html +4 -4
  345. data/docs/tutorial.html +4 -4
  346. data/docs/unit-tests.html +4 -4
  347. data/dx/bash_customizations +7 -0
  348. data/dx/build +13 -2
  349. data/dx/docker-compose.env +1 -1
  350. data/dx/exec +25 -8
  351. data/lib/brut/front_end/forms/input.rb +253 -20
  352. data/lib/brut/front_end/forms/input_definition.rb +15 -12
  353. data/lib/brut/front_end.rb +1 -0
  354. data/lib/brut/version.rb +1 -1
  355. data/specs/brut/front_end/forms/input.spec.rb +978 -0
  356. data/specs/spec_helper.rb +27 -0
  357. data/specs/support/matchers/have_constraint_violation.rb +23 -0
  358. data/specs/support/matchers.rb +5 -0
  359. data/specs/support.rb +3 -0
  360. metadata +39 -17
  361. data/docs/assets/chunks/@localSearchIndexroot.CoYzciVi.js +0 -1
  362. data/docs/assets/deployment.md.Dbka4OTr.js +0 -1
  363. data/docs/assets/deployment.md.Dbka4OTr.lean.js +0 -1
  364. data/docs/assets/index.md.B28EwVpq.js +0 -1
  365. data/docs/assets/index.md.B28EwVpq.lean.js +0 -1
  366. data/docs/assets/overview.Da81cB9R.png +0 -0
  367. data/docs/assets/overview.md.C5wlBcR5.lean.js +0 -1
  368. /data/docs/assets/{configuration.md.BGHl8oRC.lean.js → configuration.md.LG-zIBww.lean.js} +0 -0
  369. /data/docs/assets/{getting-started.md.Ciz82L0m.lean.js → getting-started.md.Dj0qtZI2.lean.js} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0212844b1dd1d69682b3b4ec4ab2a9917087b3d931916e39d9ccf0239d6a2f9c'
4
- data.tar.gz: 871a018ce32f9d1f556eef7950570c8a4dd072736be733e6a7a925e14a8c968c
3
+ metadata.gz: 3b77e4a2a30122b1805ecf5cb52a5313bb137c188364a6baac62270918cd1640
4
+ data.tar.gz: 393ad694666d889d5672fb592e7f188e19f78f519f19fbd4a5edb1af32ef5d21
5
5
  SHA512:
6
- metadata.gz: eb49d9e87e53893962c86e2a26ca54f8c82cc9a8fbbeac5e12e1d7aaa4b47cbb13de6ab9429779422a6bc075dd3f4499bf6b18accb7c7ff87943eb32afda0342
7
- data.tar.gz: c8f56cb073d99b1b3f304afe2daf3839b6b2b39ebe492bde05f00037ebf448459049560d3b32707450b65e32916366ea766d57598752026d0c025d1db63d7fe7
6
+ metadata.gz: 769c54918d2cb9a3e40e007d954c70ef6ec4033bb1a159d616cc4680aaf5b5d650e0335d729a5acc10b0d9f96574c4dfe5ec5f563bbd82828c6878ea4a532338
7
+ data.tar.gz: 510b4299c42f476f61110382afdb26c1c71ae8f6aecf16f95bebcf30bb5ac14b0817b08bb0787e3b9b541be59add9e35d4d60df78debbe0627b371aa4639ce00
data/.gitignore CHANGED
@@ -32,3 +32,9 @@
32
32
  /brut-css/dist
33
33
  # This is where the CSS lives temporarily while docs are being built
34
34
  /brut-css/src/docs/brut.css
35
+
36
+ # Per-developer Bash customizations that could contain secrets
37
+ /dx/bash_customizations.local
38
+
39
+ # Gems are installed here for LSP convienience
40
+ /local-gems
data/.projections.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "lib/*.rb": {
3
+ "alternate": "specs/{}.spec.rb",
4
+ "type": "source"
5
+ },
6
+ "specs/*.spec.rb": {
7
+ "alternate": "lib/{}.rb",
8
+ "type": "spec"
9
+ }
10
+ }
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ -I specs
2
+ --require spec_helper
3
+ -P "specs/**/*.spec.rb"
data/Dockerfile.dx CHANGED
@@ -10,28 +10,46 @@ RUN apt-get -y clean && \
10
10
  apt-get -y update && \
11
11
  apt-get install --quiet --yes ca-certificates curl gnupg rsync
12
12
 
13
- # Install NODE per https://github.com/nodesource/distributions?tab=readme-ov-file#using-debian-as-root-nodejs-22
14
- RUN curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh && \
15
- bash /tmp/nodesource_setup.sh && \
16
- apt-get install -y nodejs
17
-
18
- # Install Postgres client per https://www.postgresql.org/download/linux/debian/ - note that the
19
- # automated configuration doesn't work, I think because it assumes non-root.
20
- RUN apt-get -y install lsb-release && \
21
- sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' && \
22
- wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
23
- apt-get update && \
24
- apt-get -y install postgresql-client-16
25
-
26
13
  RUN echo "gem: --no-document" >> ~/.gemrc && \
27
14
  gem update --system && \
28
15
  gem install bundler
29
-
30
16
  # Need vim at all times
31
17
  ENV EDITOR=vim
32
18
  RUN apt-get install -y vim && \
33
19
  echo "set -o vi" >> /root/.bashrc
34
20
 
21
+
22
+ # Setup a non-root user
23
+
24
+ # Their user id, which ideally matches their user id on the host
25
+ ARG user_uid=10001
26
+ # Their group id, which ideally matches their group id on the host
27
+ ARG user_gid=10002
28
+
29
+ # Create the user's group ID if it does not exist
30
+ RUN getent group ${user_gid} || groupadd --gid ${user_gid} appgroup
31
+ # Create the user. Note that we put bash_customizations in both .profile and .bashrc
32
+ # to increase the chances they are used when running bash in various configurations
33
+ RUN useradd --uid ${user_uid} --gid ${user_gid} --create-home --home-dir /home/appuser appuser && \
34
+ echo ". ~/.bash_customizations" >> /home/appuser/.profile && \
35
+ echo ". ~/.bash_customizations.local" >> /home/appuser/.profile && \
36
+ echo ". ~/.bash_customizations" >> /home/appuser/.bashrc && \
37
+ echo ". ~/.bash_customizations.local" >> /home/appuser/.bashrc
38
+
39
+ COPY --chown=appuser:${user_gid} dx/show-help-in-app-container-then-wait.sh /home/appuser
40
+ COPY --chown=appuser:${user_gid} dx/bash_customizations /home/appuser/.bash_customizations
41
+ COPY --chown=appuser:${user_gid} dx/bash_customizations.local /home/appuser/.bash_customizations.local
42
+
43
+ # NOT including the group here as that will place the user's environment
44
+ # ONLY in that group and not in all the groups in which they are a part.
45
+ USER appuser
46
+
47
+ # Install NodeJS, per https://nodejs.org/en/download
48
+ RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash && \
49
+ \. "$HOME/.nvm/nvm.sh" && \
50
+ nvm install 22 && \
51
+ node -v && nvm current && npm -v
52
+
35
53
  # Node's colors are hand-crafted to always look bad and render at least some text unreadable
36
54
  # no matter what your setup. Cool.
37
55
  ENV NODE_DISABLE_COLORS=1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- brut (0.0.28)
4
+ brut (0.0.29)
5
5
  concurrent-ruby
6
6
  i18n
7
7
  irb
Binary file
Binary file
Binary file
Binary file
data/bin/docs CHANGED
@@ -14,14 +14,36 @@ OptionParser.new do |opts|
14
14
  end
15
15
  end.parse!
16
16
 
17
- public_dir = (Pathname(__FILE__).dirname / ".." / "brutrb.com" / "public" ).expand_path.to_s
18
- docs_dir = public_dir + "/api"
17
+ public_dir = (Pathname(__FILE__).dirname / ".." / "brutrb.com" / "public" ).expand_path
18
+ docs_dir = public_dir / "api"
19
19
  brutjs_dir = (Pathname(__FILE__).dirname / ".." / "brut-js" ).expand_path.to_s
20
20
 
21
21
  system(
22
22
  "bundle exec yard doc -o '#{docs_dir}' -m markdown -M rdiscount --backtrace"
23
23
  )
24
24
 
25
+ puts "Hacking CSS"
26
+ [
27
+ docs_dir / "css" / "style.css",
28
+ docs_dir / "css" / "full_list.css"
29
+ ].each do |css_file_to_hack|
30
+ lines = File.read(css_file_to_hack).split(/\n/)
31
+ File.open(css_file_to_hack, "w") do |f|
32
+ lines.each do |line|
33
+ if line =~ /\"Lucida Sans"/
34
+ f.puts line.gsub(/\"Lucida Sans\"/, '"Helvetica Neue", "Lucida Sans"')
35
+ elsif line =~ / Monaco,/
36
+ f.puts line.gsub(/ Monaco,/, '"Courier New", Monaco,')
37
+ elsif line =~ /font-family: monospace;/
38
+ f.puts line.gsub(/font-family: monospace;/, 'font-family: "Courier New", monospace;')
39
+ else
40
+ f.puts line
41
+ end
42
+ end
43
+ f.puts "code { font-family: 'Courier New', monospace; font-weight: 600; }"
44
+ end
45
+ end
46
+
25
47
  FileUtils.chdir brutjs_dir do
26
48
  system("bin/build")
27
49
  end
data/bin/rspec ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/setup CHANGED
@@ -83,7 +83,7 @@ def setup(update_gems:,setup_credentials:)
83
83
  system! "ssh-add #{key_file}"
84
84
  end
85
85
 
86
- known_hosts_dest = Pathname("/") / "root" / ".ssh" / "known_hosts"
86
+ known_hosts_dest = Pathname("/") / "home" / "appuser" / ".ssh" / "known_hosts"
87
87
  if known_hosts_dest.exist?
88
88
  log "#{known_hosts_dest} exists, your ssh key should work with GitHub"
89
89
  else
@@ -117,7 +117,7 @@ def setup(update_gems:,setup_credentials:)
117
117
  end
118
118
  log "Your ssh key looks good"
119
119
 
120
- gem_credentials_dest = Pathname("/") / "root" / ".gem" / "credentials"
120
+ gem_credentials_dest = Pathname("/") / "home" / "appuser" / ".gem" / "credentials"
121
121
  if gem_credentials_dest.exist?
122
122
  log "Gem credentials look good"
123
123
  else
@@ -139,7 +139,7 @@ def setup(update_gems:,setup_credentials:)
139
139
  end
140
140
  end
141
141
 
142
- npm_credentials_dest = Pathname("/") / "root" / ".npmrc"
142
+ npm_credentials_dest = Pathname("/") / "home" / "appuser" / ".npmrc"
143
143
  if npm_credentials_dest.exist?
144
144
  log "NPM credentials look good"
145
145
  else
@@ -5,3 +5,10 @@
5
5
  .image-bg {
6
6
  display: none;
7
7
  }
8
+ td > code {
9
+ white-space: nowrap;
10
+ }
11
+
12
+ .VPHero img, .VPHome img {
13
+ border-radius: 0.5rem;
14
+ }
@@ -51,13 +51,14 @@
51
51
 
52
52
  --blue-vp-c-brand-1: #0C00A7;
53
53
  --blue-vp-c-brand-2: #6357FF;
54
- --blue-vp-c-brand-3: #1000E2;
55
- --blue-vp-c-brand-soft: #FAF9FF;
54
+ --blue-vp-c-brand-3: #4C0606;
55
+ --blue-vp-c-brand-soft: #FFF9F9;
56
+
57
+ --vp-c-brand-1: #362400;
58
+ --vp-c-brand-2: #F1A100;
59
+ --vp-c-brand-3: #281B00;
60
+ --vp-c-brand-soft: #FFF1D6;
56
61
 
57
- --vp-c-brand-1: #A70C00;
58
- --vp-c-brand-2: #FF6357;
59
- --vp-c-brand-3: #e21000;
60
- --vp-c-brand-soft: #FFE7E5;
61
62
 
62
63
  --xvp-c-brand-1: var(--vp-c-indigo-1);
63
64
  --xvp-c-brand-2: var(--vp-c-indigo-2);
@@ -69,15 +70,25 @@
69
70
  --vp-c-tip-3: var(--vp-c-brand-3);
70
71
  --vp-c-tip-soft: var(--vp-c-brand-soft);
71
72
 
72
- --vp-c-warning-1: var(--vp-c-yellow-1);
73
- --vp-c-warning-2: var(--vp-c-yellow-2);
74
- --vp-c-warning-3: var(--vp-c-yellow-3);
75
- --vp-c-warning-soft: var(--vp-c-yellow-soft);
76
-
77
- --vp-c-danger-1: var(--vp-c-red-1);
78
- --vp-c-danger-2: var(--vp-c-red-2);
79
- --vp-c-danger-3: var(--vp-c-red-3);
80
- --vp-c-danger-soft: var(--vp-c-red-soft);
73
+ --xvp-c-warning-1: var(--vp-c-yellow-1);
74
+ --xvp-c-warning-2: var(--vp-c-yellow-2);
75
+ --xvp-c-warning-3: var(--vp-c-yellow-3);
76
+ --xvp-c-warning-soft: var(--vp-c-yellow-soft);
77
+
78
+ --vp-c-warning-1: #9E5601;
79
+ --vp-c-warning-2: #FDAF53;
80
+ --vp-c-warning-3: #5E3301;
81
+ --vp-c-warning-soft: #FFF3E5;
82
+
83
+ --vp-c-danger-1: #AC0E0E;
84
+ --vp-c-danger-2: #F14E4E;
85
+ --vp-c-danger-3: #4C0606;
86
+ --vp-c-danger-soft: #FFF9F9;
87
+
88
+ --xvp-c-danger-1: var(--vp-c-red-1);
89
+ --xvp-c-danger-2: var(--vp-c-red-2);
90
+ --xvp-c-danger-3: var(--vp-c-red-3);
91
+ --xvp-c-danger-soft: var(--vp-c-red-soft);
81
92
  }
82
93
 
83
94
  /**
@@ -1,66 +1,144 @@
1
1
  # Deployment
2
2
 
3
3
  Brut apps are Rack apps, so they can be deployed in conventional
4
- ways. Brut apps are 12-factor apps and the scripts used for
5
- development are inteded to work for production as well.
4
+ ways.
6
5
 
7
6
  ## Overview
8
7
 
9
- Everyone deploys apps in different ways. Brut can't provide a
10
- simple solution for all deployment setups, so this document will
11
- outline considerations when setting up deployment.
8
+ There are just too many ways to deploy. Brut attempts to address this by adhering to [12-factor principles](https://12factor.net). Brut also tries not to create artifacts like `Procfile` or `Dockerfile` that would conflict with the artifacts you'd need to manage deployment.
12
9
 
13
- The most direct way to understand what needs to happen is to look
14
- at `deploy/Dockerfile`, which is the foundation of a `Dockerfile`
15
- you can use. In particular, it shows you the commands needed to
16
- setup and run the app in production:
10
+ That said, Brut includes first-class support for deploying to Heroku using containers. More options will be included as necessary, either through direct support in code/tooling, or documentation here.
17
11
 
18
- Beyond installing system software to run any Ruby web app, as well
19
- as whatever is needed for NodeJS and Postgres, the Brut-specific
20
- parts look like so:
12
+ ### Heroku Container-based Deployment
21
13
 
22
- 1. Install Ruby Gems with `bundle install`
23
- 2. Install Node modules with `npm clean-install`
24
- 3. Build all assets with `bin/build-assets` (this will bundle all
25
- CSS and Javascript, plus copy over any other [assets](/assets)
26
- to the locations from where the Brut app will serve them)
27
- 4. Run the app with `bin/run`
14
+ When creating your Brut app with `mkbrut`, the Heroku segment can be used to create files and scripts for a [Heroku container-based deployment](https://devcenter.heroku.com/articles/container-registry-and-runtime).
28
15
 
29
- Your Brut app also includes `bin/release` which is a script
30
- intended to run in the production environment after the code has
31
- been deployed, but before the app starts up. By default, it
32
- applies any needed migrations to the database.
16
+ | File | Purpose | Notes |
17
+ |------|---------|-------|
18
+ | `bin/deploy` | Script to use to perform the deployment | This wraps `HerokuContainerBasedDeploy` in `Brut::CLI::Apps` |
19
+ | `deploy/Dockerfile` | Template `Dockerfile` used to create a `Dockerfile` for each process type | Heroku requires each process (web, worker, release, etc.) to have its own `Dockerfile` and own image |
20
+ | `deploy/heroku_config.rb` | Class that exports optional processes | By default, your app has a web and release process. `HerokuConfig` can export others, like Sidekiq |
21
+ | `deploy/docker-entrypoint` | The [`ENTRYPOINT`](https://docs.docker.com/reference/dockerfile/#entrypoint) for production Docker images, which is set up to use jemalloc | You can modify or remove this as needed |
22
+
23
+ How to deploy:
24
+
25
+ 1. Auth to Heroku from inside your dev container:
26
+
27
+ ```
28
+ your-computer> dx/exec bash
29
+ devcontainer> heroku auth:login
30
+ # You will need to copy/paste the URL to log in
31
+ devcontainer> heroku container:login
32
+ ```
33
+
34
+ 2. Create your app using the container stack:
35
+
36
+ ```
37
+ > heroku create --stack container -a «your heroku app name»
38
+ ```
39
+ 3. Ensure your app's source code is all checked in, there are no uncommitted or unadded files, and you have pushed to main.
40
+ 4. `bin/deploy`
41
+
42
+ This will generate a `Dockerfile` for each process (by default, `Dockerfile.web` and `Dockerfile.release`), build images, push those images to Heroku, and ask Heroku to release them.
43
+
44
+ Debugging Tips:
45
+
46
+ * Keep in mind it's hard to make general deployment tools. You are expected to understand your deployment and be capable of deploying an arbitrary Rack app manually. Brut's tooling automates what you need to know.
47
+ * `bin/deploy` runs the `deploy` subcommand, so `bin/deploy help deploy` can provide
48
+ some options for debugging issues:
49
+
50
+ ```
51
+ devcontainer> bin/deploy help deploy
52
+ Usage: bin/deploy [global options] deploy [command options]
53
+
54
+ Build images, push them to Heroku, and deploy them
55
+
56
+ Manages a deploy process based on using Heroku's Container Registry. See
57
+
58
+ https://devcenter.heroku.com/articles/container-registry-and-runtime
59
+
60
+ for details. You are assumed to understand this.
61
+ This command will make the process somewhat easier.
62
+
63
+ This will use deploy/Dockerfile as a template to create
64
+ one Dockerfile for each process you want to run in Heroku.
65
+ deploy/heroku_config.rb is where the processes and their
66
+ commands are configured.
67
+
68
+ The release phase is included automatically, based on bin/release.
69
+
70
+ GLOBAL OPTIONS
71
+
72
+ -h, --help Get help
73
+ --log-level=LEVEL Set log level. Allowed values: debug,
74
+ info, warn, error, fatal. Default 'fatal'
75
+ --verbose Set log level to 'debug', which will produce
76
+ maximum output
77
+
78
+ ENVIRONMENT VARIABLES
79
+
80
+ BRUT_CLI_RAISE_ON_ERROR - if set, shows backtrace on errors
81
+ LOG_LEVEL - log level if --log-level or --verbose is omitted
82
+
83
+
84
+ COMMAND OPTIONS
85
+
86
+ --platform=PLATFORM Override default platform. Can be any Docker
87
+ platform.
88
+ --[no-]dry-run Print the commands that would be run and
89
+ don't actually do anything. Implies --skip-checks
90
+ --[no-]skip-checks Skip checks for code having been
91
+ committed and pushed
92
+ --[no-]deploy After images are pushed, actually deploy them
93
+ --[no-]push After images are created, push them
94
+ to Heroku's registry. If false,
95
+ implies --no-deploy
96
+ ```
97
+ * Try building images first: `bin/deploy deploy --no-push --skip-checks`
98
+ * It's possible to run the images locally. If you are on Apple Silicon, you'll
99
+ need to set --platform:
100
+
101
+ * `bin/deploy deploy --no-push --skip-checks --platform linux/arm64`
102
+ * Create `docker-compose.yml` for your image and any other services e.g. databases
103
+ * Set required environment variables in `docker-compose.yml`
104
+ * Start up Docker compose and poke around
105
+
106
+ You'll need to have a better understanding of Docker to do this, however if you
107
+ are deploying with Docker, this is an understanding you hopefully already have.
108
+
109
+
110
+ ### Other Mechanisms for Deployment
111
+
112
+ As a Rack app, other deployments should be possible. To make the app work, you'll need to make sure a few things are dealt with:
113
+
114
+ * `RACK_ENV` **must** be `"production"`
115
+ * `bin/build-assets` will build all assets by default. This must either be done on production servers or done ahead of time and the results packaged with the app.
116
+ * `bin/build-assets` outputs files in `app/public` and `app/config`. Those files are used at runtime. Brut **will not** initiate the build of any assets.
117
+ * If you are going to build assets on production servers, you *must* included developer tooling. This means NodeJS, all modules in `package.json` and all RubyGems in `Gemfile`.
118
+
119
+ The `deploy/Dockerfile` created by `mkbrut --segment-heroku` is not very Heroku-specific and could serve as a reference.
33
120
 
34
121
  ## Testing
35
122
 
36
- If you are using Docker, you can create the `Dockerfile`s and run
37
- them locally to see how they work. You will need to have local
38
- versions of all infrastructure (database, Redis, etc.), but if
39
- these work locally, there is a high chance they work in
40
- production.
123
+ Testing deployments is a bit out of scope, but in general:
41
124
 
42
- If you are not using Docker, you will need to apply various
43
- techniques that are beyond the scope of this documentation.
125
+ * A container-based deployment can theoretically be run on your computer as a test.
126
+ * Non-production, but production-like environments can be used to validate production configurations.
127
+ * You own the means of production…not Brut.
44
128
 
45
129
  ## Recommended Practices
46
130
 
47
- Brut goes to great lengths to avoid environment-specific code.
48
- Much of Brut's behavior works the same in dev as it does in
49
- production. For example, assets are hashed in all environments.
131
+ * Avoid a lot of code that checks `Brut.container.project_env`. Try to consolidate all prod/test/dev differences in environment variables.
132
+ * Have a way to get a shell into your production environment for debugging.
133
+ * Brut doesn't log much, but if you remove the `OTEL_*` environment variables, Brut will log OTel telemetry to the console, which may be useful.
134
+ * Setting `OTEL_LOG_LEVEL=debug` is advised if the app isn't starting or you aren't seeing any telemetry or logging
50
135
 
51
- Assuming your code does the same thing, there should be a minium
52
- of surprises. That all being said, here are some recommendations:
136
+ ## Technical Notes
53
137
 
54
- * Create a way to interact with external services in a testing
55
- capacity. For example, ensure you have a test user with a known
56
- email address and trigger an email to them. Or a company credit
57
- card you charge and refund.
58
- * Configure observability so you know what your app is doing at
59
- all times.
60
- * Configure a URL that, when accessed, produces an error. This
61
- allows you to check your error reporting system.
62
- * Create a page somewhere that shows the git SHA of your
63
- deployment, or some other unique, unambiguous version number. This
64
- will clarify what version of the code is actually running.
138
+ > [!IMPORTANT]
139
+ > Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
140
+ > internals, the source code is always more correct.
65
141
 
142
+ _Last Updated July 3, 2025_
66
143
 
144
+ None at this time.
Binary file
Binary file
Binary file
Binary file
Binary file
data/brutrb.com/index.md CHANGED
@@ -6,8 +6,8 @@ hero:
6
6
  name: "Brut RB"
7
7
  text: "Raw Ruby Web Apps"
8
8
  tagline: Standards-based, No-nonsense, HTML-first, Low Ceremony
9
- image:
10
- src: /images/logo.png
9
+ ximage:
10
+ src: /images/LogoTall.png
11
11
  alt: "A Ruby gemstone embedded into a concrete, brutalist building"
12
12
  actions:
13
13
  - theme: brand
@@ -20,7 +20,7 @@ hero:
20
20
  text: Conceptual Overview
21
21
  link: /overview
22
22
 
23
- features:
23
+ xfeatures:
24
24
  - title: Standards-Based
25
25
  icon: 📄
26
26
  details: "Brut leverages HTML, HTTP, SQL, and the Ruby standard library to let you write apps using standards you already know…or could quickly learn"
@@ -34,3 +34,4 @@ features:
34
34
  icon: 🏘️
35
35
  details: "Sequel, Phlex, I18n, RSpec. They do it best"
36
36
  ---
37
+ ![logo](/images/LogoStop.png)
@@ -24,18 +24,18 @@ the *server side*, however it exists to provide a user interface of some sort.
24
24
  * *Back End* is the code that deals with everything else, such as accessing a database, executing business logic, or managing background jobs.
25
25
 
26
26
 
27
- ![Architectural Overview](/images/overview.png)
27
+ ![Architectural Overview](/images/OverviewMetro.png)
28
28
 
29
+ * **Visitor** is someone visiting your web site or app.
29
30
  * **Browser** is, well, a web browser
30
31
  * [**Pages**](/pages) generate web pages, which is what happens when a browser's UI navigates to a URL.
32
+ * [**Forms**](/forms) describe the inputs of an HTML `<form>` element, and hold a form's submitted data for server-side processing. Browser submit forms to the server.
31
33
  * [**Components**](/components) generate HTML fragments and are used to generate the HTML of a page or for re-use across pages.
32
- * [**Forms**](/forms) describe the inputs of an HTML `<form>` element, and hold a form's submitted data for server-side
33
- processing.
34
34
  * [**Handlers**](/handlers) receive non-GET HTTP requests from the browser, notably form submissions.
35
- * [**JS**](/javascript) and [**Assets**](/assets) (including [CSS](/css)) are bundled on the server and sent to the client.
35
+ * [**JavaScript**](/javascript) and [**Assets**](/assets) (including [CSS](/css)) are bundled on the server and sent to the client.
36
+ * [**Domain Logic**](/business-logic) as where your business and domain logic lives and can be implemented however you like.
36
37
  * [**DB Models**](/database-access) are objects that provide access to your database.
37
- * [**Domain Logic**](/business-logic) as where your business and domain logic lives and can be implemented
38
- however you like.
38
+ * **Relational Database** is your database, where data is stored.
39
39
 
40
40
  ## Brut is Not a Resource-Oriented MVC Framework.
41
41
 
@@ -8,11 +8,14 @@ services:
8
8
  source: ${PWD}
9
9
  target: ${PWD}
10
10
  consistency: "consistent"
11
+ # This allows access to Git configuration so Git commands work
11
12
  - type: bind
12
13
  source: ${GIT_CONFIG}
13
- target: "/root/.gitconfig"
14
- entrypoint: /root/show-help-in-app-container-then-wait.sh
14
+ target: "/home/appuser/.gitconfig"
15
+ command: /home/appuser/show-help-in-app-container-then-wait.sh
15
16
  working_dir: ${PWD}
17
+ environment:
18
+ PROJECT_ROOT: ${PWD}
16
19
  ports:
17
20
  - "8086:8086"
18
21
  - "8087:8087"
data/docs/404.html CHANGED
@@ -6,16 +6,16 @@
6
6
  <title>404 | Brut RB</title>
7
7
  <meta name="description" content="Not Found">
8
8
  <meta name="generator" content="VitePress v1.6.3">
9
- <link rel="preload stylesheet" href="/assets/style.D73IYGCX.css" as="style">
9
+ <link rel="preload stylesheet" href="/assets/style.B2o1L9eN.css" as="style">
10
10
  <link rel="preload stylesheet" href="/vp-icons.css" as="style">
11
11
 
12
- <script type="module" src="/assets/app.BX81XO4N.js"></script>
12
+ <script type="module" src="/assets/app.BhrfSt68.js"></script>
13
13
  <script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
14
14
  <script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
15
15
  </head>
16
16
  <body>
17
17
  <div id="app"></div>
18
- <script>window.__VP_HASH_MAP__=JSON.parse("{\"ai.md\":\"_6HCDL6d\",\"assets.md\":\"D3wunzLx\",\"brut-js.md\":\"o2DAO2s2\",\"business-logic.md\":\"BY4hGy0m\",\"cli.md\":\"RmeA2b0i\",\"components.md\":\"CRUMdRoN\",\"configuration.md\":\"BGHl8oRC\",\"css.md\":\"DJgj2clw\",\"custom-element-tests.md\":\"BrYJQEl3\",\"database-access.md\":\"C7l-Vuvb\",\"database-schema.md\":\"BUjR0VS1\",\"deployment.md\":\"Dbka4OTr\",\"dev-environment.md\":\"GZv6xvi9\",\"doc-conventions.md\":\"-kN3Xo5C\",\"end-to-end-tests.md\":\"yfQHC0b5\",\"flash-and-session.md\":\"BXY8RvT0\",\"forms.md\":\"B-koVgyw\",\"getting-started.md\":\"Ciz82L0m\",\"handlers.md\":\"089DVD3v\",\"hooks.md\":\"C4-moMny\",\"i18n.md\":\"Do9i1qWl\",\"index.md\":\"B28EwVpq\",\"instrumentation.md\":\"a9Pjps4P\",\"javascript.md\":\"GWbhRS51\",\"jobs.md\":\"S-2amAYp\",\"keyword-injection.md\":\"Dt2tKREs\",\"layouts.md\":\"cPnh3NId\",\"lsp.md\":\"Bsu-f6VU\",\"markdown-examples.md\":\"CCFEQO44\",\"middleware.md\":\"Czz_UlJN\",\"not-released.md\":\"BBy28McC\",\"overview.md\":\"C5wlBcR5\",\"pages.md\":\"BE3kfOc5\",\"recipes_authentication.md\":\"CAsXf7hk\",\"routes.md\":\"BMM7peut\",\"security.md\":\"C668yXCi\",\"seed-data.md\":\"BvFZlqIk\",\"space-time-continuum.md\":\"KPUIKysQ\",\"tutorial.md\":\"BnoGjrdK\",\"unit-tests.md\":\"DUGrnLj5\"}");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\":\"Documentation Conventions\",\"link\":\"/doc-conventions\"},{\"text\":\"Tutorial\",\"link\":\"/tutorial\"},{\"text\":\"Dev Environment\",\"link\":\"/dev-environment\"},{\"text\":\"AI Declaration\",\"link\":\"/ai\"}]},{\"text\":\"Front-End\",\"collapsed\":false,\"items\":[{\"text\":\"Routes\",\"link\":\"/routes\"},{\"text\":\"Pages\",\"link\":\"/pages\"},{\"text\":\"Layouts\",\"link\":\"/layouts\"},{\"text\":\"Forms\",\"link\":\"/forms\"},{\"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\":\"Authentication\",\"link\":\"/recipes/authentication\"},{\"text\":\"Form Validations\",\"link\":\"/recipes/form-validations\"},{\"text\":\"Database Migrations\",\"link\":\"/recipes/database-migrations\"},{\"text\":\"Ajax Form Submission\",\"link\":\"/recipes/ajax-form\"},{\"text\":\"Custom Telemetry\",\"link\":\"/recipes/telemetry\"},{\"text\":\"CLI App/Task\",\"link\":\"/recipes/cli-app\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/thirdtank/brut\"}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
18
+ <script>window.__VP_HASH_MAP__=JSON.parse("{\"ai.md\":\"_6HCDL6d\",\"assets.md\":\"D3wunzLx\",\"brut-js.md\":\"o2DAO2s2\",\"business-logic.md\":\"BY4hGy0m\",\"cli.md\":\"RmeA2b0i\",\"components.md\":\"CRUMdRoN\",\"configuration.md\":\"LG-zIBww\",\"css.md\":\"DJgj2clw\",\"custom-element-tests.md\":\"BrYJQEl3\",\"database-access.md\":\"C7l-Vuvb\",\"database-schema.md\":\"BUjR0VS1\",\"deployment.md\":\"BLseERGV\",\"dev-environment.md\":\"GZv6xvi9\",\"doc-conventions.md\":\"-kN3Xo5C\",\"end-to-end-tests.md\":\"yfQHC0b5\",\"flash-and-session.md\":\"BXY8RvT0\",\"forms.md\":\"B-koVgyw\",\"getting-started.md\":\"Dj0qtZI2\",\"handlers.md\":\"089DVD3v\",\"hooks.md\":\"C4-moMny\",\"i18n.md\":\"Do9i1qWl\",\"index.md\":\"CuBB-BdM\",\"instrumentation.md\":\"a9Pjps4P\",\"javascript.md\":\"GWbhRS51\",\"jobs.md\":\"S-2amAYp\",\"keyword-injection.md\":\"Dt2tKREs\",\"layouts.md\":\"cPnh3NId\",\"lsp.md\":\"Bsu-f6VU\",\"markdown-examples.md\":\"CCFEQO44\",\"middleware.md\":\"Czz_UlJN\",\"not-released.md\":\"BBy28McC\",\"overview.md\":\"DVKRM8zl\",\"pages.md\":\"BE3kfOc5\",\"recipes_authentication.md\":\"CAsXf7hk\",\"routes.md\":\"BMM7peut\",\"security.md\":\"C668yXCi\",\"seed-data.md\":\"BvFZlqIk\",\"space-time-continuum.md\":\"KPUIKysQ\",\"tutorial.md\":\"BnoGjrdK\",\"unit-tests.md\":\"DUGrnLj5\"}");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\":\"Documentation Conventions\",\"link\":\"/doc-conventions\"},{\"text\":\"Tutorial\",\"link\":\"/tutorial\"},{\"text\":\"Dev Environment\",\"link\":\"/dev-environment\"},{\"text\":\"AI Declaration\",\"link\":\"/ai\"}]},{\"text\":\"Front-End\",\"collapsed\":false,\"items\":[{\"text\":\"Routes\",\"link\":\"/routes\"},{\"text\":\"Pages\",\"link\":\"/pages\"},{\"text\":\"Layouts\",\"link\":\"/layouts\"},{\"text\":\"Forms\",\"link\":\"/forms\"},{\"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\":\"Authentication\",\"link\":\"/recipes/authentication\"},{\"text\":\"Form Validations\",\"link\":\"/recipes/form-validations\"},{\"text\":\"Database Migrations\",\"link\":\"/recipes/database-migrations\"},{\"text\":\"Ajax Form Submission\",\"link\":\"/recipes/ajax-form\"},{\"text\":\"Custom Telemetry\",\"link\":\"/recipes/telemetry\"},{\"text\":\"CLI App/Task\",\"link\":\"/recipes/cli-app\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/thirdtank/brut\"}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
19
19
 
20
20
  </body>
21
21
  </html>