brut 0.4.0 → 0.8.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 (568) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/CHANGELOG.md +36 -1
  4. data/Dockerfile.dx +19 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +19 -0
  7. data/assets/MetroIcon.graffle +0 -0
  8. data/assets/YouTubeThumb.pxd +0 -0
  9. data/bin/build +86 -0
  10. data/bin/ci +36 -0
  11. data/bin/docs +39 -9
  12. data/bin/publish +61 -0
  13. data/bin/setup +6 -0
  14. data/brut-css/bin/build +19 -0
  15. data/brut-css/bin/ci +19 -0
  16. data/brut-css/bin/docs +19 -0
  17. data/brut-css/bin/publish +21 -0
  18. data/brut-css/bin/setup +1 -0
  19. data/brut-css/package-lock.json +2 -2
  20. data/brut-css/package.json +1 -1
  21. data/brut-css/src/css/flex.css +1 -1
  22. data/brut-css/src/docs/includes/body-and-header.html.ejs +1 -1
  23. data/brut-js/bin/build +15 -6
  24. data/brut-js/bin/docs +25 -0
  25. data/brut-js/bin/publish +21 -0
  26. data/brut-js/bin/setup +1 -0
  27. data/brut-js/dx +1 -0
  28. data/brut-js/package-lock.json +2 -2
  29. data/brut-js/package.json +1 -1
  30. data/brut-js/specs/AjaxSubmit.spec.js +100 -7
  31. data/brut-js/src/AjaxSubmit.js +76 -40
  32. data/brut.gemspec +2 -2
  33. data/brutrb.com/adrs.md +1 -0
  34. data/brutrb.com/bin/setup +1 -0
  35. data/brutrb.com/getting-started.md +3 -0
  36. data/brutrb.com/overview.md +6 -0
  37. data/brutrb.com/tutorial.md +7 -3
  38. data/docs/404.html +2 -2
  39. data/docs/adrs.html +5 -5
  40. data/docs/ai.html +3 -3
  41. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  42. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  43. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  44. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  45. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  46. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  47. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  48. data/docs/api/Brut/BackEnd.html +1 -1
  49. data/docs/api/Brut/CLI/App.html +1 -1
  50. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  51. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  52. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  53. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
  54. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  55. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  56. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  58. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  59. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  60. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  61. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  62. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  63. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  64. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  65. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  66. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
  67. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  68. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  69. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  70. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  71. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  72. data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +2 -2
  73. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  74. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  75. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  76. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  77. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  78. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  79. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  80. data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
  81. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  82. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  83. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  84. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  85. data/docs/api/Brut/CLI/Apps.html +1 -1
  86. data/docs/api/Brut/CLI/Command.html +1 -1
  87. data/docs/api/Brut/CLI/Error.html +1 -1
  88. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  89. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  90. data/docs/api/Brut/CLI/Executor.html +1 -1
  91. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  92. data/docs/api/Brut/CLI/Options.html +1 -1
  93. data/docs/api/Brut/CLI/Output.html +1 -1
  94. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  95. data/docs/api/Brut/CLI.html +1 -1
  96. data/docs/api/Brut/FactoryBot.html +1 -1
  97. data/docs/api/Brut/Framework/App.html +1 -1
  98. data/docs/api/Brut/Framework/Config.html +1 -1
  99. data/docs/api/Brut/Framework/Container.html +1 -1
  100. data/docs/api/Brut/Framework/Error.html +1 -1
  101. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  102. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  103. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  104. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  105. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  106. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  107. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  108. data/docs/api/Brut/Framework/Errors.html +1 -1
  109. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  110. data/docs/api/Brut/Framework/MCP.html +1 -1
  111. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  112. data/docs/api/Brut/Framework.html +1 -1
  113. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  114. data/docs/api/Brut/FrontEnd/Component/Helpers.html +86 -2
  115. data/docs/api/Brut/FrontEnd/Component.html +78 -9
  116. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +3 -3
  117. data/docs/api/Brut/FrontEnd/Components/FormTag.html +3 -3
  118. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +3 -3
  119. data/docs/api/Brut/FrontEnd/Components/Input.html +3 -3
  120. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +3 -3
  121. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +3 -3
  122. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +3 -3
  123. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +3 -3
  124. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +3 -3
  125. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  126. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +3 -3
  127. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +3 -3
  128. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +3 -3
  129. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +3 -3
  130. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  132. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  133. data/docs/api/Brut/FrontEnd/Form.html +1 -1
  134. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  135. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +1 -1
  136. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
  137. data/docs/api/Brut/FrontEnd/Forms/Input.html +1 -1
  138. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +1 -1
  143. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  146. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  147. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  148. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  149. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  150. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  151. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  152. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  153. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  154. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  155. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  156. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  157. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  158. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  159. data/docs/api/Brut/FrontEnd/Layout.html +3 -3
  160. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  161. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  162. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  163. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  164. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  165. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  166. data/docs/api/Brut/FrontEnd/Page.html +31 -23
  167. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +3 -3
  168. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  169. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  170. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  171. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  172. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  173. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  174. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  175. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  176. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  177. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  178. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  179. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  180. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  181. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  182. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  183. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  184. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  185. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  186. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  187. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  188. data/docs/api/Brut/FrontEnd.html +1 -1
  189. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  190. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  191. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  192. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  193. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  194. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  195. data/docs/api/Brut/I18n.html +1 -1
  196. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  197. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  198. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  199. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  200. data/docs/api/Brut/Instrumentation.html +1 -1
  201. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +13 -11
  202. data/docs/api/Brut/SinatraHelpers.html +1 -1
  203. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  204. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  205. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  206. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  207. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  208. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  209. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  210. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  211. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  212. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  213. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  214. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  215. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  216. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  217. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  218. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  219. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  220. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  221. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  222. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  223. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  224. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  225. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  226. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  227. data/docs/api/Brut/SpecSupport.html +1 -1
  228. data/docs/api/Brut.html +1 -1
  229. data/docs/api/Clock.html +1 -1
  230. data/docs/api/ModuleName.html +1 -1
  231. data/docs/api/RichString.html +1 -1
  232. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  233. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  234. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  235. data/docs/api/Sequel/Extensions.html +1 -1
  236. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  237. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  238. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  239. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  240. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  241. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  242. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  243. data/docs/api/Sequel/Plugins.html +1 -1
  244. data/docs/api/Sequel.html +1 -1
  245. data/docs/api/_index.html +1 -1
  246. data/docs/api/file.README.html +1 -1
  247. data/docs/api/index.html +1 -1
  248. data/docs/api/method_list.html +92 -76
  249. data/docs/api/top-level-namespace.html +1 -1
  250. data/docs/assets/{adrs.md.JRxZ5uYE.js → adrs.md.BxjHi9-8.js} +1 -1
  251. data/docs/assets/adrs.md.BxjHi9-8.lean.js +1 -0
  252. data/docs/assets/{app.AkS4fHzI.js → app.DyQLb4Ot.js} +1 -1
  253. data/docs/assets/chunks/@localSearchIndexroot.CmtZyrFA.js +1 -0
  254. data/docs/assets/chunks/{VPLocalSearchBox.By6un1FD.js → VPLocalSearchBox.T1iA-eJx.js} +1 -1
  255. data/docs/assets/chunks/{theme.DwiUPdci.js → theme.ChwsbWjK.js} +2 -2
  256. data/docs/assets/{components.md.BfeWD9sX.js → components.md.DHh-NwKs.js} +3 -3
  257. data/docs/assets/{configuration.md.DoSBNc0H.js → configuration.md.D8Wz3oJU.js} +1 -1
  258. data/docs/assets/{forms.md.DFveP5g_.js → forms.md.BRE85eju.js} +1 -1
  259. data/docs/assets/{getting-started.md.DZWjCVC0.js → getting-started.md.2ioiTe-B.js} +6 -3
  260. data/docs/assets/{getting-started.md.DZWjCVC0.lean.js → getting-started.md.2ioiTe-B.lean.js} +1 -1
  261. data/docs/assets/overview.md.DlKiRRG_.js +1 -0
  262. data/docs/assets/overview.md.DlKiRRG_.lean.js +1 -0
  263. data/docs/assets/tutorial.md.BIb7XT6j.js +1 -0
  264. data/docs/assets/tutorial.md.BIb7XT6j.lean.js +1 -0
  265. data/docs/assets.html +3 -3
  266. data/docs/brut-css/brut.max.css +1 -1
  267. data/docs/brut-css/classes/appearances.html +1 -1
  268. data/docs/brut-css/classes/background-colors.html +1 -1
  269. data/docs/brut-css/classes/border-colors.html +1 -1
  270. data/docs/brut-css/classes/borders.html +1 -1
  271. data/docs/brut-css/classes/dimensions.html +1 -1
  272. data/docs/brut-css/classes/flex.html +3 -3
  273. data/docs/brut-css/classes/foreground-colors.html +1 -1
  274. data/docs/brut-css/classes/junk-drawer.html +1 -1
  275. data/docs/brut-css/classes/layout.html +1 -1
  276. data/docs/brut-css/classes/lists.html +1 -1
  277. data/docs/brut-css/classes/positioning.html +1 -1
  278. data/docs/brut-css/classes/spacings.html +1 -1
  279. data/docs/brut-css/classes/typography.html +1 -1
  280. data/docs/brut-css/customization/advanced-configuration.html +1 -1
  281. data/docs/brut-css/customization/breakpoints.html +1 -1
  282. data/docs/brut-css/customization/design-system.html +1 -1
  283. data/docs/brut-css/customization/pseudo-classes.html +1 -1
  284. data/docs/brut-css/getting-started/core-concepts.html +1 -1
  285. data/docs/brut-css/getting-started/installation.html +1 -1
  286. data/docs/brut-css/getting-started/overview.html +1 -1
  287. data/docs/brut-css/getting-started/simple-example.html +1 -1
  288. data/docs/brut-css/index.html +1 -1
  289. data/docs/brut-css/properties/colors.html +1 -1
  290. data/docs/brut-css/properties/spacings.html +1 -1
  291. data/docs/brut-css/properties/typography.html +1 -1
  292. data/docs/brut-js/api/AjaxSubmit.html +56 -7
  293. data/docs/brut-js/api/AjaxSubmit.js.html +77 -41
  294. data/docs/brut-js/api/Autosubmit.html +1 -1
  295. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  296. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  297. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  298. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  299. data/docs/brut-js/api/BufferedLogger.html +1 -1
  300. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  301. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  302. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  303. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  304. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  305. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  306. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  307. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  308. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  309. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  310. data/docs/brut-js/api/Form.html +1 -1
  311. data/docs/brut-js/api/Form.js.html +1 -1
  312. data/docs/brut-js/api/I18nTranslation.html +1 -1
  313. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  314. data/docs/brut-js/api/LocaleDetection.html +1 -1
  315. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  316. data/docs/brut-js/api/Logger.html +1 -1
  317. data/docs/brut-js/api/Logger.js.html +1 -1
  318. data/docs/brut-js/api/Message.html +1 -1
  319. data/docs/brut-js/api/Message.js.html +1 -1
  320. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  321. data/docs/brut-js/api/RichString.html +1 -1
  322. data/docs/brut-js/api/RichString.js.html +1 -1
  323. data/docs/brut-js/api/Tabs.html +1 -1
  324. data/docs/brut-js/api/Tabs.js.html +1 -1
  325. data/docs/brut-js/api/Tracing.html +1 -1
  326. data/docs/brut-js/api/Tracing.js.html +1 -1
  327. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  328. data/docs/brut-js/api/external-Performance.html +1 -1
  329. data/docs/brut-js/api/external-Promise.html +1 -1
  330. data/docs/brut-js/api/external-ValidityState.html +1 -1
  331. data/docs/brut-js/api/external-Window.html +1 -1
  332. data/docs/brut-js/api/external-fetch.html +1 -1
  333. data/docs/brut-js/api/global.html +1 -1
  334. data/docs/brut-js/api/index.html +1 -1
  335. data/docs/brut-js/api/index.js.html +1 -1
  336. data/docs/brut-js/api/module-testing.html +1 -1
  337. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  338. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  339. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  340. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  341. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  342. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  343. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  344. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  345. data/docs/brut-js/api/testing_index.js.html +1 -1
  346. data/docs/brut-js.html +3 -3
  347. data/docs/business-logic.html +3 -3
  348. data/docs/cli.html +3 -3
  349. data/docs/components.html +7 -7
  350. data/docs/configuration.html +5 -5
  351. data/docs/css.html +3 -3
  352. data/docs/custom-element-tests.html +3 -3
  353. data/docs/database-access.html +3 -3
  354. data/docs/database-schema.html +3 -3
  355. data/docs/deployment.html +3 -3
  356. data/docs/dev-environment.html +3 -3
  357. data/docs/dir-structure.html +3 -3
  358. data/docs/doc-conventions.html +3 -3
  359. data/docs/end-to-end-tests.html +3 -3
  360. data/docs/features.html +3 -3
  361. data/docs/flash-and-session.html +3 -3
  362. data/docs/form-constraints.html +3 -3
  363. data/docs/forms.html +5 -5
  364. data/docs/getting-started.html +9 -6
  365. data/docs/handlers.html +3 -3
  366. data/docs/hashmap.json +1 -1
  367. data/docs/hooks.html +3 -3
  368. data/docs/i18n.html +3 -3
  369. data/docs/index.html +3 -3
  370. data/docs/instrumentation.html +3 -3
  371. data/docs/javascript.html +3 -3
  372. data/docs/jobs.html +3 -3
  373. data/docs/keyword-injection.html +3 -3
  374. data/docs/layouts.html +3 -3
  375. data/docs/lsp.html +3 -3
  376. data/docs/markdown-examples.html +3 -3
  377. data/docs/middleware.html +3 -3
  378. data/docs/overview.html +5 -5
  379. data/docs/pages.html +3 -3
  380. data/docs/recipes/alternate-layouts.html +3 -3
  381. data/docs/recipes/authentication.html +3 -3
  382. data/docs/recipes/blank-layouts.html +3 -3
  383. data/docs/recipes/custom-flash.html +3 -3
  384. data/docs/recipes/indexed-forms.html +3 -3
  385. data/docs/recipes/migrations.html +3 -3
  386. data/docs/recipes/text-field-component.html +3 -3
  387. data/docs/roadmap.html +3 -3
  388. data/docs/routes.html +3 -3
  389. data/docs/security.html +3 -3
  390. data/docs/seed-data.html +3 -3
  391. data/docs/space-time-continuum.html +3 -3
  392. data/docs/tutorial.html +5 -5
  393. data/docs/unit-tests.html +3 -3
  394. data/docs/why.html +3 -3
  395. data/lib/brut/cli/apps/scaffold.rb +1 -1
  396. data/lib/brut/framework/mcp.rb +1 -1
  397. data/lib/brut/front_end/component.rb +19 -0
  398. data/lib/brut/front_end/components/form_tag.rb +2 -2
  399. data/lib/brut/front_end/page.rb +11 -7
  400. data/lib/brut/sinatra_helpers.rb +1 -0
  401. data/lib/brut/version.rb +1 -1
  402. data/mkbrut/.gitignore +16 -0
  403. data/mkbrut/CODE_OF_CONDUCT.txt +100 -0
  404. data/mkbrut/Gemfile +3 -0
  405. data/mkbrut/Gemfile.lock +19 -0
  406. data/mkbrut/LICENSE.txt +370 -0
  407. data/mkbrut/README.md +145 -0
  408. data/mkbrut/Rakefile +2 -0
  409. data/mkbrut/bin/build +36 -0
  410. data/mkbrut/bin/ci +19 -0
  411. data/mkbrut/bin/docs +19 -0
  412. data/mkbrut/bin/publish +129 -0
  413. data/mkbrut/bin/rake +16 -0
  414. data/mkbrut/bin/setup +30 -0
  415. data/mkbrut/brut-welcome.png +0 -0
  416. data/mkbrut/deploy/.dockerignore +2 -0
  417. data/mkbrut/deploy/Dockerfile +25 -0
  418. data/mkbrut/exe/mkbrut +5 -0
  419. data/mkbrut/lib/mkbrut/app.rb +79 -0
  420. data/mkbrut/lib/mkbrut/app_id.rb +8 -0
  421. data/mkbrut/lib/mkbrut/app_name.rb +29 -0
  422. data/mkbrut/lib/mkbrut/app_options.rb +36 -0
  423. data/mkbrut/lib/mkbrut/base.rb +57 -0
  424. data/mkbrut/lib/mkbrut/cli.rb +107 -0
  425. data/mkbrut/lib/mkbrut/erb_binding_delegate.rb +20 -0
  426. data/mkbrut/lib/mkbrut/internet_identifier.rb +32 -0
  427. data/mkbrut/lib/mkbrut/invalid_identifier.rb +4 -0
  428. data/mkbrut/lib/mkbrut/ops/add_css_import.rb +42 -0
  429. data/mkbrut/lib/mkbrut/ops/add_i18n_message.rb +74 -0
  430. data/mkbrut/lib/mkbrut/ops/add_method.rb +48 -0
  431. data/mkbrut/lib/mkbrut/ops/append_to_file.rb +20 -0
  432. data/mkbrut/lib/mkbrut/ops/base_op.rb +21 -0
  433. data/mkbrut/lib/mkbrut/ops/copy_file.rb +12 -0
  434. data/mkbrut/lib/mkbrut/ops/insert_code_in_method.rb +58 -0
  435. data/mkbrut/lib/mkbrut/ops/insert_route.rb +52 -0
  436. data/mkbrut/lib/mkbrut/ops/mkdir.rb +13 -0
  437. data/mkbrut/lib/mkbrut/ops/prism_parsing_op.rb +70 -0
  438. data/mkbrut/lib/mkbrut/ops/render_template.rb +26 -0
  439. data/mkbrut/lib/mkbrut/ops/skip_file.rb +10 -0
  440. data/mkbrut/lib/mkbrut/ops.rb +16 -0
  441. data/mkbrut/lib/mkbrut/organization.rb +5 -0
  442. data/mkbrut/lib/mkbrut/prefix.rb +26 -0
  443. data/mkbrut/lib/mkbrut/prefixed_io.rb +16 -0
  444. data/mkbrut/lib/mkbrut/segments/bare_bones.rb +185 -0
  445. data/mkbrut/lib/mkbrut/segments/demo.rb +121 -0
  446. data/mkbrut/lib/mkbrut/segments/heroku.rb +30 -0
  447. data/mkbrut/lib/mkbrut/segments/sidekiq.rb +3 -0
  448. data/mkbrut/lib/mkbrut/segments.rb +8 -0
  449. data/mkbrut/lib/mkbrut/version.rb +3 -0
  450. data/mkbrut/lib/mkbrut/versions.rb +13 -0
  451. data/mkbrut/lib/mkbrut.rb +18 -0
  452. data/mkbrut/mkbrut.gemspec +32 -0
  453. data/mkbrut/templates/Base/.dockerignore +25 -0
  454. data/mkbrut/templates/Base/.env.development.erb +60 -0
  455. data/mkbrut/templates/Base/.env.test.erb +8 -0
  456. data/mkbrut/templates/Base/.gitignore +31 -0
  457. data/mkbrut/templates/Base/.projections.json +59 -0
  458. data/mkbrut/templates/Base/Dockerfile.dx +205 -0
  459. data/mkbrut/templates/Base/Gemfile.erb +53 -0
  460. data/mkbrut/templates/Base/Procfile.development +5 -0
  461. data/mkbrut/templates/Base/Procfile.test +1 -0
  462. data/mkbrut/templates/Base/README.md +4 -0
  463. data/mkbrut/templates/Base/README.md.erb +40 -0
  464. data/mkbrut/templates/Base/app/bootstrap.rb +61 -0
  465. data/mkbrut/templates/Base/app/config/i18n/en/1_defaults.rb +128 -0
  466. data/mkbrut/templates/Base/app/config/i18n/en/2_app.rb +24 -0
  467. data/mkbrut/templates/Base/app/public/static/manifest.json.erb +33 -0
  468. data/mkbrut/templates/Base/app/src/app.rb.erb +37 -0
  469. data/mkbrut/templates/Base/app/src/back_end/data_models/app_data_model.rb +5 -0
  470. data/mkbrut/templates/Base/app/src/back_end/data_models/db.rb +19 -0
  471. data/mkbrut/templates/Base/app/src/back_end/data_models/migrations/20240101130000_citext.rb +6 -0
  472. data/mkbrut/templates/Base/app/src/back_end/data_models/seed/seed_data.rb +9 -0
  473. data/mkbrut/templates/Base/app/src/front_end/components/app_component.rb +8 -0
  474. data/mkbrut/templates/Base/app/src/front_end/components/custom_element_registration.rb.erb +7 -0
  475. data/mkbrut/templates/Base/app/src/front_end/css/index.css +2 -0
  476. data/mkbrut/templates/Base/app/src/front_end/css/svgs.css +12 -0
  477. data/mkbrut/templates/Base/app/src/front_end/forms/app_form.rb +4 -0
  478. data/mkbrut/templates/Base/app/src/front_end/handlers/app_handler.rb +4 -0
  479. data/mkbrut/templates/Base/app/src/front_end/images/LogoPylon.png +0 -0
  480. data/mkbrut/templates/Base/app/src/front_end/images/LogoTransit.png +0 -0
  481. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-120x120.png +0 -0
  482. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-152x152.png +0 -0
  483. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-167x167.png +0 -0
  484. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-180x180.png +0 -0
  485. data/mkbrut/templates/Base/app/src/front_end/images/favicon.ico +0 -0
  486. data/mkbrut/templates/Base/app/src/front_end/images/icon.png +0 -0
  487. data/mkbrut/templates/Base/app/src/front_end/images/mkicons.sh +6 -0
  488. data/mkbrut/templates/Base/app/src/front_end/js/index.js +6 -0
  489. data/mkbrut/templates/Base/app/src/front_end/layouts/default_layout.rb.erb +73 -0
  490. data/mkbrut/templates/Base/app/src/front_end/pages/app_page.rb +11 -0
  491. data/mkbrut/templates/Base/app/src/front_end/pages/home_page.rb +62 -0
  492. data/mkbrut/templates/Base/app/src/front_end/support/app_session.rb +6 -0
  493. data/mkbrut/templates/Base/app/src/front_end/svgs/README.md +5 -0
  494. data/mkbrut/templates/Base/app/src/front_end/svgs/comment-button.svg +59 -0
  495. data/mkbrut/templates/Base/bin/README.md.erb +5 -0
  496. data/mkbrut/templates/Base/bin/build-assets +7 -0
  497. data/mkbrut/templates/Base/bin/ci +39 -0
  498. data/mkbrut/templates/Base/bin/console +31 -0
  499. data/mkbrut/templates/Base/bin/db +9 -0
  500. data/mkbrut/templates/Base/bin/dbconsole +51 -0
  501. data/mkbrut/templates/Base/bin/dev +25 -0
  502. data/mkbrut/templates/Base/bin/release +26 -0
  503. data/mkbrut/templates/Base/bin/run +86 -0
  504. data/mkbrut/templates/Base/bin/scaffold +9 -0
  505. data/mkbrut/templates/Base/bin/setup +256 -0
  506. data/mkbrut/templates/Base/bin/startup-message +65 -0
  507. data/mkbrut/templates/Base/bin/test +9 -0
  508. data/mkbrut/templates/Base/bin/test-server +29 -0
  509. data/mkbrut/templates/Base/bin/watch-and-build-assets +37 -0
  510. data/mkbrut/templates/Base/config.ru +16 -0
  511. data/mkbrut/templates/Base/docker-compose.dx.yml +92 -0
  512. data/mkbrut/templates/Base/dx/README.md +28 -0
  513. data/mkbrut/templates/Base/dx/bash_customizations +12 -0
  514. data/mkbrut/templates/Base/dx/bash_customizations.local +8 -0
  515. data/mkbrut/templates/Base/dx/build +107 -0
  516. data/mkbrut/templates/Base/dx/docker-compose.env.erb +25 -0
  517. data/mkbrut/templates/Base/dx/dx.sh.lib +137 -0
  518. data/mkbrut/templates/Base/dx/exec +68 -0
  519. data/mkbrut/templates/Base/dx/prune +19 -0
  520. data/mkbrut/templates/Base/dx/show-help-in-app-container-then-wait.sh +38 -0
  521. data/mkbrut/templates/Base/dx/start +30 -0
  522. data/mkbrut/templates/Base/dx/stop +23 -0
  523. data/mkbrut/templates/Base/package.json.erb +37 -0
  524. data/mkbrut/templates/Base/puma.config.rb +53 -0
  525. data/mkbrut/templates/Base/specs/e2e/home_page.spec.rb.erb +23 -0
  526. data/mkbrut/templates/Base/specs/front_end/js/SpecHelper.js +24 -0
  527. data/mkbrut/templates/Base/specs/front_end/pages/home_page.spec.rb +22 -0
  528. data/mkbrut/templates/Base/specs/lint_factories.spec.rb +7 -0
  529. data/mkbrut/templates/Base/specs/spec_helper.rb +78 -0
  530. data/mkbrut/templates/Base/specs/support.rb +2 -0
  531. data/mkbrut/templates/segments/BareBones/app/src/front_end/handlers/trigger_exception_handler.rb +24 -0
  532. data/mkbrut/templates/segments/BareBones/app/src/front_end/js/Example.js.erb +49 -0
  533. data/mkbrut/templates/segments/BareBones/specs/front_end/handlers/trigger_exception_handler.spec.rb +41 -0
  534. data/mkbrut/templates/segments/BareBones/specs/front_end/js/Example.spec.js.erb +38 -0
  535. data/mkbrut/templates/segments/Demo/app/src/back_end/data_models/db/guestbook_message.rb +3 -0
  536. data/mkbrut/templates/segments/Demo/app/src/back_end/data_models/migrations/20250628194124_guestbook.rb +14 -0
  537. data/mkbrut/templates/segments/Demo/app/src/front_end/components/flash_component.rb +36 -0
  538. data/mkbrut/templates/segments/Demo/app/src/front_end/css/constraint-violations.css +18 -0
  539. data/mkbrut/templates/segments/Demo/app/src/front_end/css/fonts.css +19 -0
  540. data/mkbrut/templates/segments/Demo/app/src/front_end/fonts/monaspace-xenon.ttf +0 -0
  541. data/mkbrut/templates/segments/Demo/app/src/front_end/forms/guestbook_message_form.rb +4 -0
  542. data/mkbrut/templates/segments/Demo/app/src/front_end/handlers/guestbook_message_handler.rb +64 -0
  543. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/guestbook_page/message_component.rb +41 -0
  544. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/guestbook_page.rb +43 -0
  545. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/new_guestbook_message_page.rb +64 -0
  546. data/mkbrut/templates/segments/Demo/specs/back_end/data_models/db/guestbook_message.spec.rb +5 -0
  547. data/mkbrut/templates/segments/Demo/specs/e2e/guest_message.spec.rb +54 -0
  548. data/mkbrut/templates/segments/Demo/specs/factories/db/guestbook_message.factory.rb +7 -0
  549. data/mkbrut/templates/segments/Demo/specs/front_end/components/flash_component.spec.rb +5 -0
  550. data/mkbrut/templates/segments/Demo/specs/front_end/handlers/guestbook_message_handler.spec.rb +122 -0
  551. data/mkbrut/templates/segments/Demo/specs/front_end/pages/guestbook_page/message_component.spec.rb +5 -0
  552. data/mkbrut/templates/segments/Demo/specs/front_end/pages/guestbook_page.spec.rb +52 -0
  553. data/mkbrut/templates/segments/Demo/specs/front_end/pages/new_guestbook_message_page.spec.rb +5 -0
  554. data/mkbrut/templates/segments/Heroku/bin/deploy +11 -0
  555. data/mkbrut/templates/segments/Heroku/deploy/Dockerfile +125 -0
  556. data/mkbrut/templates/segments/Heroku/deploy/docker-entrypoint +15 -0
  557. data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +26 -0
  558. metadata +188 -24
  559. data/brut-js/CHANGELOG.md +0 -5
  560. data/docs/assets/adrs.md.JRxZ5uYE.lean.js +0 -1
  561. data/docs/assets/chunks/@localSearchIndexroot.C9FqcQxD.js +0 -1
  562. data/docs/assets/overview.md.iMnwLO4x.js +0 -1
  563. data/docs/assets/overview.md.iMnwLO4x.lean.js +0 -1
  564. data/docs/assets/tutorial.md.BYXj4cOu.js +0 -1
  565. data/docs/assets/tutorial.md.BYXj4cOu.lean.js +0 -1
  566. /data/docs/assets/{components.md.BfeWD9sX.lean.js → components.md.DHh-NwKs.lean.js} +0 -0
  567. /data/docs/assets/{configuration.md.DoSBNc0H.lean.js → configuration.md.D8Wz3oJU.lean.js} +0 -0
  568. /data/docs/assets/{forms.md.DFveP5g_.lean.js → forms.md.BRE85eju.lean.js} +0 -0
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require "fileutils"
5
+ require "open3"
6
+ require "optparse"
7
+ require "pathname"
8
+
9
+ # This is intended to run inside the Workspace (i.e. Docker container) to
10
+ # set up the Foundation, thus enabling development. This script should:
11
+ #
12
+ # a) Rely only on Ruby and its standard library, since it runs
13
+ # before any gems are installed
14
+ # b) Be idempotent, with the ability to run in a CI server
15
+ # c) Be organized with the setup method first, as this is the main chunk
16
+ # of logic, with all other supporting methods following
17
+ #
18
+ # NOTE: It is not clear yet how Brut will manage this file for you, especially
19
+ # given that you will need to add to it based on your own setup needs.
20
+ #
21
+ # Take care to only add code and not change what is here.
22
+
23
+ def setup(update_gems:,update_node:)
24
+ if update_gems
25
+ log "Updating gems"
26
+ system! "bundle update"
27
+ else
28
+ log "Installing gems"
29
+ # Only do bundle install if the much-faster
30
+ # bundle check indicates we need to
31
+ system! "bundle check --no-color || bundle install --no-color --quiet"
32
+ end
33
+
34
+ log "Installing puma binstubs"
35
+ system! "bundle binstub puma"
36
+
37
+ log "Installing rspec binstubs"
38
+ system! "bundle binstub rspec-core"
39
+
40
+ # https://github.com/ddollar/foreman/wiki/Don't-Bundle-Foreman
41
+ log "Installing foreman"
42
+ system! "gem install foreman"
43
+
44
+ # Installs Shopify's LSP. You can remove this if you don't want that,
45
+ # but this is here to show where to do it vs requiring you to read
46
+ # documentation.
47
+ log "Installing Ruby LSP"
48
+ system! "gem install ruby-lsp"
49
+
50
+ if update_node
51
+ log "Updating Node Modules"
52
+ system! "npm --no-color --no-progress update"
53
+ else
54
+ log "Installing Node Modules"
55
+ system! "npm --no-color --no-progress install"
56
+ end
57
+
58
+ # In theory, this was installed when the Workspace was setup, but
59
+ # it doesn't alwaysd work propertly, so this ensures
60
+ # Chromium is set up. See comments in Dockerfile.dx as to why
61
+ # Chromium and not Chrome.
62
+ log "Re-installing playwright/chromium if needed"
63
+ system! "node_modules/.bin/playwright install chromium"
64
+
65
+ log "Ensuring tmp dir exists"
66
+ system! "mkdir -p tmp"
67
+
68
+ setup_dot_env_local
69
+
70
+ log "Setting up databases"
71
+
72
+ log "Re-creating development database"
73
+ system! "bin/db rebuild --env=development"
74
+
75
+ log "Re-creating test database"
76
+ system! "bin/db rebuild --env=test"
77
+
78
+ log "Loading seed data into the development database"
79
+ system! "bin/db seed --env=development"
80
+
81
+ log "All set up."
82
+ help
83
+ end
84
+
85
+ def setup_dot_env_local()
86
+
87
+ dot_env = ROOT_DIR / ".env.development"
88
+ if !File.exist?(dot_env)
89
+ raise "Problem: '#{dot_env}' does not exist"
90
+ end
91
+
92
+ log "Checking #{dot_env} for required local environment variables"
93
+ previous_comment = ""
94
+ dot_env_need_local = {}
95
+ File.read(dot_env).split(/\n/).each do |line|
96
+ if line =~ /^#(.*)$/
97
+ previous_comment << line
98
+ elsif line =~ /^LOCAL:([^=]+)$/
99
+ dot_env_need_local[$1] = previous_comment
100
+ previous_comment = ""
101
+ elsif line =~ /^([^=])+/
102
+ previous_comment = ""
103
+ end
104
+ end
105
+
106
+ dot_env_local = ROOT_DIR / ".env.development.local"
107
+ log "Ensuring '#{dot_env_local}' exists"
108
+ if !File.exist?(dot_env_local)
109
+ log "Creating '#{dot_env_local}'"
110
+ FileUtils.touch dot_env_local
111
+ end
112
+
113
+ log "Checking #{dot_env_local} for values for required local environment variables"
114
+ local_contents = File.read(dot_env_local).split(/\n/).map { |line|
115
+ if line =~ /^([^=]+)=(.*)$/
116
+ var = $1
117
+ val = $2
118
+ if dot_env_need_local[var] && val.to_s != ""
119
+ log "Value for '#{var}' is good"
120
+ dot_env_need_local.delete(var)
121
+ end
122
+ end
123
+ line
124
+ }.compact
125
+
126
+ dot_env_need_local.each do |var,comment|
127
+ log "A value is needed locally for '#{var}'"
128
+ log ""
129
+ log comment
130
+ log ""
131
+ puts "Enter the value below and hit Return"
132
+ value = gets
133
+ local_contents << "#{var}=#{value}"
134
+ log "Got it, thanks"
135
+ end
136
+
137
+ log "Updating '#{dot_env_local}'"
138
+ File.open(dot_env_local,"w") do |file|
139
+ local_contents.each do |line|
140
+ file.puts line
141
+ end
142
+ end
143
+
144
+ log "Checking that #{dot_env_local} is being ignored"
145
+ gitignore = ROOT_DIR / ".gitignore"
146
+ if File.exist?(gitignore)
147
+ lines = File.read(gitignore).split(/\n/)
148
+ if lines.include?("/#{dot_env_local.basename}")
149
+ log "It's there!"
150
+ else
151
+ log "It's missing. Adding..."
152
+ lines << ""
153
+ lines << "# This contains actual secrets and should not be checked in"
154
+ lines << "/#{dot_env_local.basename}"
155
+ File.open(gitignore,"w") do |file|
156
+ lines.each do |line|
157
+ file.puts line
158
+ end
159
+ end
160
+ end
161
+ else
162
+ log "No #{gitignore}, so make sure you DO NOT CHECK #{dot_env_local} IN!!!"
163
+ end
164
+ end
165
+
166
+ # We don't want the setup method to have to do all this error
167
+ # checking, and we also want to explicitly log what we are
168
+ # executing. Thus, we use this method instead of Kernel#system
169
+ def system!(*args)
170
+ if ENV["BRUT_BIN_KIT_DEBUG"] == "true"
171
+ log "Executing #{args}"
172
+ out,err,status = Open3.capture3(*args)
173
+ if status.success?
174
+ log "#{args} succeeded"
175
+ else
176
+ log "#{args} failed"
177
+ log "STDOUT:"
178
+ $stdout.puts out
179
+ log "STDERR:"
180
+ $stderr.puts err
181
+ abort
182
+ end
183
+ else
184
+ log "Executing #{args}"
185
+ if system(*args)
186
+ log "#{args} succeeded"
187
+ else
188
+ log "#{args} failed"
189
+ abort
190
+ end
191
+ end
192
+ end
193
+
194
+ # It's helpful to know what messages came from this
195
+ # script, so we'll use log instead of `puts`
196
+ def log(message)
197
+ puts "[ #{$0} ] #{message}"
198
+ end
199
+
200
+ ROOT_DIR = ((Pathname(__dir__) / ".." ).expand_path)
201
+
202
+ def help(option_parser=nil)
203
+ if option_parser
204
+ puts option_parser
205
+ puts
206
+ puts "OTHER USEFUL COMMANDS"
207
+ else
208
+ puts
209
+ puts "USEFUL COMMANDS"
210
+ end
211
+ puts ""
212
+ puts " bin/dev"
213
+ puts " # run app locally, rebuilding and reloading as needed"
214
+ puts ""
215
+ puts " bin/ci"
216
+ puts " # runs all tests and checks as CI would"
217
+ puts ""
218
+ puts " bin/console"
219
+ puts " # get an IRB console with the app loaded"
220
+ puts ""
221
+ puts " bin/db"
222
+ puts " # interact with the DB for migrations, information, etc"
223
+ puts ""
224
+ puts " bin/dbconsole"
225
+ puts " # get a PSQL session to the database"
226
+ puts ""
227
+ puts " bin/scaffold"
228
+ puts " # Create various structures in your app, like pages or forms"
229
+ puts ""
230
+ puts " bin/setup help"
231
+ puts " # show this help"
232
+ puts ""
233
+ end
234
+
235
+ options = {
236
+ update_gems: false,
237
+ update_node: false,
238
+ }
239
+ option_parser = OptionParser.new do |opts|
240
+ opts.banner = "Usage: bin/setup [options]\n\n Set up the Foundation, allowing for development and testing of the app\n\nOPTIONS\n"
241
+ opts.on("--update[=TYPE]","Update gems or modules to get the latest versions consistent with Gemfile or package.json. TYPE can be 'all', 'gems', or 'node'. If TYPE is omitted, 'all' is assumed.") do |value|
242
+ options[:update_gems] = value.nil? || value == "gems"
243
+ options[:update_node] = value.nil? || value == "node"
244
+ end
245
+ opts.on("-h", "--help", "Display this help message") do
246
+ help(opts)
247
+ exit
248
+ end
249
+ end
250
+ option_parser.parse!
251
+
252
+ if ARGV[0] == "help"
253
+ help(option_parser)
254
+ else
255
+ setup(**options)
256
+ end
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require "yaml"
5
+ require "optparse"
6
+
7
+ option_parser = OptionParser.new do |opts|
8
+ opts.banner = "Usage: bin/startup-message\n\n Outputs a message about where the dev server is running\n\nENVIRONMENT VARIABLES\n\n PORT - the port configured for bin/run"
9
+ end
10
+ option_parser.parse!
11
+
12
+ docker_compose_file = Pathname.new(__FILE__).dirname / ".." / "docker-compose.dx.yml"
13
+
14
+ port_config = nil
15
+ error = nil
16
+
17
+ if docker_compose_file.exist?
18
+ docker_compose = YAML.load_file(docker_compose_file)
19
+
20
+ docker_port = begin
21
+ ENV.fetch("PORT")
22
+ rescue KeyError
23
+ $stderr.puts "ERROR: The PORT environment variable is not set."
24
+ $stderr.puts " Please set it to the port your app is running on."
25
+ exit 1
26
+ end
27
+
28
+ ports_config = docker_compose.dig("services", "app", "ports")
29
+ error = nil
30
+ if ports_config
31
+ port_config = ports_config.detect { |port_mapping|
32
+ host_port, container_port = port_mapping.split(":")
33
+ container_port.to_s == docker_port
34
+ }
35
+ if !port_config
36
+ error = "#{docker_compose_file} does not expose the port #{docker_port} for the 'app' service."
37
+ end
38
+ else
39
+ error = "#{docker_compose_file} does not contain a 'ports' section for the 'app' service."
40
+ end
41
+ else
42
+ error = "#{docker_compose_file} does not exist. This is assumed to be in placefor your dev environment"
43
+ end
44
+ if !error
45
+ sleep 2 # allow all other initial output from bin/dev to happen first
46
+
47
+ host_port = port_config.split(":")[0]
48
+
49
+ url = "http://localhost:#{host_port}"
50
+
51
+ $stdout.puts "Your app is now running at"
52
+ $stdout.puts
53
+ $stdout.puts " #{url}"
54
+ $stdout.puts
55
+ $stdout.flush # ensure this output happens immediately
56
+ else
57
+ $stderr.puts "WARN: #{$0} could not figure out what port the app is exposed on"
58
+ $stderr.puts
59
+ $stderr.puts " #{error}"
60
+ $stderr.puts
61
+ $stderr.puts " This won't stop your app from running, but it does mean"
62
+ $stderr.puts " there is some issue with your dev environment"
63
+ $stderr.flush # ensure this error output happens immediately
64
+ end
65
+ sleep
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler"
4
+ Bundler.require
5
+ require "pathname"
6
+
7
+ require "brut/cli/apps/test"
8
+ exit Brut::CLI.app(Brut::CLI::Apps::Test,project_root: Pathname($0).dirname / "..")
9
+
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+ SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
5
+ ROOT_DIR="${SCRIPT_DIR}/.."
6
+
7
+ usage() {
8
+ echo "Usage: $0"
9
+ echo
10
+ echo " Run the app in the test environment, suitable for end-to-end tests"
11
+ echo " This will build all assets first, but not rebuild or reload after that"
12
+ echo
13
+ }
14
+
15
+ for arg in "$@"; do
16
+ if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ] || [ "${arg}" = "help" ]; then
17
+ usage
18
+ exit 0
19
+ fi
20
+ done
21
+
22
+ RACK_ENV="test"
23
+ export RACK_ENV
24
+ echo "[ bin/test-server ] Building assets"
25
+ "${SCRIPT_DIR}"/build-assets
26
+
27
+ echo "[ bin/test-server ] Starting server"
28
+ PORT=6503 bin/run &
29
+ wait
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This script exists to ensure that chokidar actually terminates when foreman stops all the processes.
4
+ # For whatever reason, if Procfile has the npx chokidar… invocation below, these processes are not
5
+ # stopped. Putting them into this script addresses that. Cool.
6
+
7
+ set -e
8
+
9
+ if [ -z "${1}" ] ; then
10
+ echo "[ $0 ] error: asset type required. Must be css, js, or images"
11
+ exit 65
12
+ fi
13
+ usage() {
14
+ echo "usage: $0 asset_type"
15
+ echo
16
+ echo " Sets up a watching/rebuild command for the given asset. Useful in dev only"
17
+ echo
18
+ echo " asset_types:"
19
+ echo
20
+ echo " - css"
21
+ echo " - js"
22
+ echo " - images"
23
+ echo
24
+ }
25
+ for arg in "${@}"; do
26
+ if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ] || [ "${arg}" = "help" ]; then
27
+ usage
28
+ exit 0
29
+ fi
30
+ done
31
+
32
+ asset_type=$1
33
+ watch_dir="app/src/front_end/${asset_type}/**/*"
34
+ build_command="bin/build-assets ${asset_type}"
35
+
36
+ export SHELL # chokidar needs this for reasons unknown to me
37
+ exec npx chokidar --initial --polling --command "${build_command}" "${watch_dir}"
@@ -0,0 +1,16 @@
1
+ require_relative "app/bootstrap"
2
+ bootstrap = Bootstrap.new.bootstrap!
3
+
4
+ app = Rack::Builder.app do
5
+ use Rack::Session::Cookie,
6
+ key: "rack.session",
7
+ path: "/",
8
+ expire_after: 31_536_000,
9
+ same_site: :lax, # this allows links from other domains to send our cookies to us,
10
+ # but only if such links are direct/obvious to the user.
11
+ secret: ENV.fetch("SESSION_SECRET")
12
+
13
+ run bootstrap.rack_app
14
+ end
15
+ run app
16
+
@@ -0,0 +1,92 @@
1
+ # This file is heavily documented to explain what is going on and why.
2
+ # If you are reading this, however, you now own this file and can
3
+ # change it how you like. Just be sure you understand how it works in the
4
+ # context of Dockerfile.dx and the files in dx/,
5
+ # which are referred to as the Workspace
6
+ #
7
+ # This file is used to run several Docker containers, based on images, together
8
+ # in a shared private network. All containers run by this file will be able to see
9
+ # each other over a network, but the only access your host (computer) will have to
10
+ # these services is via explicitly exposed ports.
11
+
12
+ services:
13
+ # 'app' is the service where all your dev tools will run. The tests will
14
+ # run in here as will the dev server.
15
+ #
16
+ # You interact with this container via `dx/exec`
17
+ app:
18
+ # IMAGE is defined in dx/docker-compose.env and is the name of an image
19
+ # built LOCALLY (never pushed to DockerHub) that has all the tools needed
20
+ # for developing your app installed into it.
21
+ image: ${IMAGE}
22
+ # This is magic that makes the Docker container stop much more quickly when
23
+ # you hit Ctrl-C.
24
+ init: true
25
+ # There are two types of volumes (drives) mounted.
26
+ volumes:
27
+ # This volume is your source code. Its source and target are the same
28
+ # so that stuff like language servers can work.
29
+ - type: bind
30
+ source: ${PWD}
31
+ target: ${PWD}
32
+ consistency: "consistent"
33
+ # This allows docker build commands run inside Docker, referred to
34
+ # as "docker out of docker". Essentially, Docker binaries installed into
35
+ # this container will access your host (you computer)'s Docker install.
36
+ # This is to allow it to build the production images.
37
+ - type: bind
38
+ source: "/var/run/docker.sock"
39
+ target: "/var/run/docker.sock"
40
+ # This is what is started up when you run `dx/start`. It just
41
+ # waits forever keeping the container running.
42
+ command: /home/appuser/show-help-in-app-container-then-wait.sh
43
+ # The working directory for any command, this is the same as your
44
+ # project root.
45
+ working_dir: ${PWD}
46
+ # This sets environment variables
47
+ environment:
48
+ # Because we'll install RubyGems inside our project (mostly to allow
49
+ # language servers to work), configuration in bash_customizations relies
50
+ # on this value being set
51
+ PROJECT_ROOT: ${PWD}
52
+ # This exposes ports from this running container to your localhost.
53
+ # In this case, we expose 6502, where the app runs, and 6503, where
54
+ # the test server runs.
55
+ #
56
+ # To change this, the port before the colon is where the app
57
+ # will be available on your localhost. The port AFTER the colon is where
58
+ # the app runs in the container. So: change the port before the colon, and
59
+ # don't change the one after.
60
+ #
61
+ # * 6502 - dev
62
+ # * 6503 - test server (run by E2E tests)
63
+ ports:
64
+ - "6502:6502"
65
+ - "6503:6503"
66
+ # This "service" runs Postgres. The service name ("postgres") is the host
67
+ # on which the service is available to other containers, namely from the
68
+ # app container. You cannot connect to thise service from your host (computer)
69
+ # directly without additional configuration.
70
+ postgres:
71
+ # This image is the name of the image from DockerHub that will be pulled
72
+ # down to run Postgres.
73
+ image: postgres:16.4
74
+ pull_policy: "missing"
75
+ environment:
76
+ # Postgres will not work without being told the password. The most
77
+ # direct way to do that is via this environment variable.
78
+ POSTGRES_PASSWORD: postgres
79
+ # This runes otel-deskop-viewer, which will receive OpenTelemetry traces
80
+ # from your app. You can connect to this to observe those traces.
81
+ otel-desktop-viewer:
82
+ # The otel-desktop-viewer maintainers do not maintain a Docker
83
+ # image of the app. I have made one available on my DockerHub account.
84
+ # If you want to build your own, see
85
+ # https://github.com/CtrlSpice/otel-desktop-viewer?tab=readme-ov-file#via-docker
86
+ # When you build that image, change the value for image: below to the image
87
+ # name you chose.
88
+ image: davetron5000/otel-desktop-viewer:alpine-3
89
+ # This runs internally on port 8000 but will be available
90
+ # on port 6504 of your machine.
91
+ ports:
92
+ - "6504:8000"
@@ -0,0 +1,28 @@
1
+ # Workspace Commands
2
+
3
+ These commands manage the Workspace, AKA the part of the development environment that
4
+ runs on your computer (sometimes called the *host*).
5
+
6
+ These are all written in Bash as that is the only environment that can be relied upon to exist on any operating system.
7
+
8
+ These are designed to be somewhat agnostic of your app and Brut may update some of
9
+ these files if changes are needed. All app-specific configuration is consolidated
10
+ into just a few files.
11
+
12
+ These files are owned by Brut and you should avoid editing them:
13
+
14
+ * `bash_customizations` - Bash configuration
15
+ * `build` - Builds the development docker image
16
+ * `dx.sh.lib` - Shared bash functions
17
+ * `exec` - Run commands inside a container
18
+ * `prune` - Remove unused containers
19
+ * `README.md` - This file
20
+ * `show-help-in-app-container-then-wait.sh` - Runs inside the app container to keep
21
+ the container up
22
+ * `start` - starts the Workspace/dev environment
23
+ * `stop` - stops the Workspace/dev environment
24
+
25
+ These files contain project-specific information and Brut will not change them:
26
+
27
+ * `docker-compose.env` - Configuration values.
28
+ * `bash_customizations.local` - Per-developer bash configuration, **project specific**, **developer specific**, **do not check into version control**.
@@ -0,0 +1,12 @@
1
+ # This sets up RubyGems and Bundler so they install
2
+ # all gems inside this project's root and not into the system
3
+ # location. This allows LSP servers to work more easily.
4
+ if [ ! -n "$PROJECT_ROOT" ]; then
5
+ echo "[ bash_customizations ] WARNING: PROJECT_ROOT is not set - this will break RubyGems"
6
+ fi
7
+ export GEM_HOME=${PROJECT_ROOT}/local-gems/gem-home
8
+ export PATH=${PATH}:${GEM_HOME}/bin
9
+ # This sets up the Node version so we don't have to do it before every. single.
10
+ # shell. invocation.
11
+ . ~/.nvm/nvm.sh
12
+ nvm use default
@@ -0,0 +1,8 @@
1
+ # Insert developer-specifilc Bash customizations here.
2
+ # This file is not checked into version control, so you
3
+ # are safe to export EDITOR=vim and avoid any guff from
4
+ # co-workers.
5
+
6
+ # Sets up a multi-line prompt since the working directory
7
+ # may be very deep. Customize or change at your leisure.
8
+ PS1='\[\e[35m\]docker-container\[\e[0m\] - \[\e[37m\]\w\n\[\e[0m\]> '
@@ -0,0 +1,107 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
6
+
7
+ . "${SCRIPT_DIR}/dx.sh.lib"
8
+
9
+ read_custom_build_args() {
10
+ build_args_file="${SCRIPT_DIR}/build.args"
11
+ if [ -e "${build_args_file}" ]; then
12
+ for arg in `grep -v '^#' "${build_args_file}"`; do
13
+ BUILD_ARGS+=(--build-arg ${arg})
14
+ done
15
+ fi
16
+ }
17
+
18
+ setup_local_user_build_args() {
19
+ require_command "id"
20
+ require_command "uname"
21
+ user_uid=$(id -u)
22
+ user_gid=$(id -g)
23
+ docker_gid=
24
+ sadly_user_must_be_added_to_root=
25
+ OS=$(uname)
26
+ if [ "${OS}" == "Darwin" ] ; then
27
+ docker_gid=$(stat -f %g /var/run/docker.sock)
28
+ sadly_user_must_be_added_to_root="0,"
29
+ else
30
+ if [ "${OS}" == "Linux" ] ; then
31
+ docker_gid=$(stat -c %g /var/run/docker.sock)
32
+ else
33
+ log "Could not determine OS, which is needed to know how to invoke stat to figure out the group id of /var/run/docker.sock"
34
+ log "Docker commands will not work"
35
+ fi
36
+ fi
37
+ echo user_uid=${user_uid} >> "${SCRIPT_DIR}"/build.args
38
+ echo user_gid=${user_gid} >> "${SCRIPT_DIR}"/build.args
39
+ echo docker_gid=${docker_gid} >> "${SCRIPT_DIR}"/build.args
40
+ echo sadly_user_must_be_added_to_root=${sadly_user_must_be_added_to_root} >> "${SCRIPT_DIR}"/build.args
41
+ }
42
+
43
+ initialize_build_args() {
44
+ echo "# This is generated - see build.pre" > "${SCRIPT_DIR}"/build.args
45
+ }
46
+
47
+ setup_playright_build_args() {
48
+ require_command "grep"
49
+ require_command "sed"
50
+
51
+ if [ ! -e "${SCRIPT_DIR}"/Gemfile.lock ]; then
52
+ log "Could not find Gemfile.lock, which is needed to determine the playwright-ruby-client version"
53
+ log "Assuming your app is brand-new, this should be OK"
54
+ echo "# When this file was created, there was no Gemfile.lock, so" >> "${SCRIPT_DIR}"/build.args
55
+ echo "# it was not possible to determine which version of Playright was set up." >> "${SCRIPT_DIR}"/build.args
56
+ echo "# Once you've build your app and installed gems, you are " >> "${SCRIPT_DIR}"/build.args
57
+ echo "# encouraged to re-run \`dx/build\` to address this issue." >> "${SCRIPT_DIR}"/build.args
58
+ echo PLAYWRIGHT_VERSION=latest >> "${SCRIPT_DIR}"/build.args
59
+ else
60
+ PLAYWRIGHT_VERSION=$(grep playwright-ruby-client Gemfile.lock | grep '(' | sed 's/^.*(//' | sed 's/).*$//' | grep -v ^=)
61
+ if [ -z "${PLAYWRIGHT_VERSION}" ]; then
62
+ log "Could not find precise version of playwright-ruby-client from Gemfile.lock"
63
+ log "This means that your playwright-ruby-client version and playwright NPM modules may be out of sync and may not work"
64
+ echo "# When this file was created, Gemfile.lock did not" >> "${SCRIPT_DIR}"/build.args
65
+ echo "# contain playwrith-ruby-client. This means it" >> "${SCRIPT_DIR}"/build.args
66
+ echo "# it was not possible to determine which version of" >> "${SCRIPT_DIR}"/build.args
67
+ echo "# Playright was set up. If you aren't using Playwright," >> "${SCRIPT_DIR}"/build.args
68
+ echo "# that's fine, this won't cause issues" >> "${SCRIPT_DIR}"/build.args
69
+ echo "# If you ARE using Playwright, something may be wrong with your setup" >> "${SCRIPT_DIR}"/build.args
70
+ echo PLAYWRIGHT_VERSION=latest >> "${SCRIPT_DIR}"/build.args
71
+ else
72
+ echo PLAYWRIGHT_VERSION=${PLAYWRIGHT_VERSION} >> "${SCRIPT_DIR}"/build.args
73
+ fi
74
+ fi
75
+ }
76
+
77
+ require_command "docker"
78
+ load_docker_compose_env
79
+
80
+ usage_on_help "Builds the Docker image based on the Dockerfile" "" "build.pre" "build.post" "${@}"
81
+
82
+ if ! exec_hook_if_exists "build.pre" Dockerfile.dx "${IMAGE}"; then
83
+ log "build.pre failed"
84
+ exit 1
85
+ fi
86
+ BUILD_ARGS=()
87
+
88
+ initialize_build_args
89
+ setup_local_user_build_args
90
+ setup_playright_build_args
91
+ read_custom_build_args
92
+
93
+ docker build \
94
+ --file Dockerfile.dx \
95
+ --tag "${IMAGE}" \
96
+ ${BUILD_ARGS[@]} \
97
+ ./
98
+
99
+ if ! exec_hook_if_exists "build.post" Dockerfile.dx "${IMAGE}"; then
100
+ log "build.pre failed"
101
+ exit 1
102
+ fi
103
+
104
+ log "🌈" "Your Docker image has been built tagged '${IMAGE}'"
105
+ log "🔄" "You can now run dx/start to start it up, though you may need to stop it first with Ctrl-C"
106
+
107
+ # vim: ft=bash