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,25 @@
1
+ # This file is an input to Docker Compose, and serves
2
+ # as an additional set of environment variables.
3
+
4
+ # IMAGE is the name of the image to be built for running
5
+ # your app. The recommended format is ORG/REPO:TAG
6
+ #
7
+ # The ORG and REPO are based on the values used
8
+ # with mkbrut. TAG is set to mimic the value
9
+ # in the FROM directive of the Dockerfile.
10
+ # This will allow you to upgrade Ruby and run both
11
+ # versions more easily.
12
+ IMAGE=<%= organization %>/<%= app_name %>:ruby-3.4
13
+
14
+ # PROJECT_NAME is used when running Docker compose
15
+ # to help ensure the output includes your project's name.
16
+ PROJECT_NAME=<%= app_name %>
17
+
18
+ # DEFAULT_SERVICE is the name of the service (keys under
19
+ # service: in docker-compose.dx.yml) to use when
20
+ # running dx/exec and no service is specified.
21
+ # The value "app" should match that in docker-compose.dx.yml
22
+ # having the effect of dx/exec running commands inside your app's
23
+ # container.
24
+ DEFAULT_SERVICE=app
25
+ # vim: ft=bash
@@ -0,0 +1,137 @@
1
+ # shellcheck shell=bash
2
+
3
+ # These are various functions needed by all the other scripts in dx/
4
+
5
+ # Log a fatal error and exit nonzero
6
+ fatal() {
7
+ remainder=${*:2}
8
+ if [ -z "$remainder" ]; then
9
+ log "🛑" "${@}"
10
+ else
11
+ log "${@}"
12
+ fi
13
+ exit 1
14
+ }
15
+
16
+ # Log an informational message. This is preferable to simply
17
+ # using `echo` because it prepends the message with the name
18
+ # of the script so you can tell where the message came from.
19
+ log() {
20
+ emoji=$1
21
+ remainder=${*:2}
22
+ if [ -z "${NO_EMOJI}" ]; then
23
+ echo "[ ${0} ] ${*}"
24
+ else
25
+ # if remainder is empty that means no emoji was passed
26
+ if [ -z "$remainder" ]; then
27
+ echo "[ ${0} ] ${*}"
28
+ else # emoji was passed, but we ignore it
29
+ echo "[ ${0} ] ${remainder}"
30
+ fi
31
+ fi
32
+ }
33
+
34
+ # Output a debug message if BRUT_WORKSPACE_DEBUG is set
35
+ debug() {
36
+ message=$1
37
+ if [ -z "${BRUT_WORKSPACE_DEBUG}" ]; then
38
+ return
39
+ fi
40
+ log "🐛" "${message}"
41
+ }
42
+
43
+ # Output general usage information for the command and exit zero.
44
+ #
45
+ # Args:
46
+ #
47
+ # [1] the description of the command
48
+ # [2] The names of the arguments
49
+ # [3] If given, the name of the pre hook that the command responds to
50
+ # [4] If given, the name of the post hook that the command responds to
51
+ usage() {
52
+ description=$1
53
+ arg_names=$2
54
+ pre_hook=$3
55
+ post_hook=$4
56
+ echo "usage: ${0} [-h] ${arg_names}"
57
+ if [ -n "${description}" ]; then
58
+ echo
59
+ echo "DESCRIPTION"
60
+ echo " ${description}"
61
+ fi
62
+ if [ -n "${pre_hook}" ] || [ -n "${post_hook}" ]; then
63
+ echo
64
+ echo "HOOKS"
65
+ if [ -n "${pre_hook}" ]; then
66
+ echo " ${pre_hook} - if present, called before the main action"
67
+ fi
68
+ if [ -n "${post_hook}" ]; then
69
+ echo " ${post_hook} - if present, called after the main action"
70
+ fi
71
+ fi
72
+ exit 0
73
+ }
74
+
75
+ # Show usage if the command line invocation indicated that help was requested.
76
+ # This calls usage, so this will exit 0
77
+ #
78
+ # Args:
79
+ #
80
+ # [1] the description of the command
81
+ # [2] The names of the arguments
82
+ # [3] If given, the name of the pre hook that the command responds to
83
+ # [4] If given, the name of the post hook that the command responds to
84
+ usage_on_help() {
85
+ description=$1
86
+ arg_names=$2
87
+ pre_hook=$3
88
+ post_hook=$4
89
+ # These are the args passed to the invocation so this
90
+ # function can determine if the user requested help
91
+ cli_args=( "${@:5}" )
92
+
93
+ for arg in "${cli_args[@]}"; do
94
+ if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ]; then
95
+ usage "${description}" "${arg_names}" "${pre_hook}" "${post_hook}"
96
+ fi
97
+ done
98
+ }
99
+
100
+ # Ensure that a command line utility exists, exiting nonzero if not.
101
+ # This is needed to avoid assumptions about what commands a user may have
102
+ # on their computer. It attempts to provide a more useful error message
103
+ # than "command not found".
104
+ require_command() {
105
+ command_name=$1
106
+ if ! command -v "${command_name}" >/dev/null 2>&1; then
107
+ fatal "Command '${command_name}' not found - it is required for this script to run"
108
+ fi
109
+ }
110
+
111
+ # This loads dx/docker-compose.env as variables to the bash script, thus
112
+ # allowing Docker Compose and these scripts to share configuration.
113
+ load_docker_compose_env() {
114
+ . "${ENV_FILE}"
115
+ }
116
+
117
+ # Execute a hook if the file exists. Note that if the hook exits
118
+ # nonzero, the caller will need to check the return value of this function and decide
119
+ # what to do.
120
+ exec_hook_if_exists() {
121
+ script_name=$1
122
+ shift
123
+ if [ -x "${SCRIPT_DIR}"/"${script_name}" ]; then
124
+ log "🪝" "${script_name} exists - executing"
125
+ "${SCRIPT_DIR}"/"${script_name}" "${@}"
126
+ else
127
+ debug "${script_name} does not exist"
128
+ fi
129
+ }
130
+
131
+ require_command "realpath"
132
+ require_command "cat"
133
+
134
+ # Set up the location to the docker-compose.env file.
135
+ ENV_FILE=$(realpath "${SCRIPT_DIR}")/docker-compose.env
136
+
137
+ # vim: ft=bash
@@ -0,0 +1,68 @@
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
+ require_command "docker"
10
+ load_docker_compose_env
11
+
12
+ usage_description="Execute a command inside the app's container. Any command other than 'bash' will be run with bash -lc. Use -B to just run the command directly."
13
+ usage_args="[-s service] [-B] command"
14
+ usage_pre="exec.pre"
15
+ usage_on_help "${usage_description}" "${usage_args}" "${usage_pre}" "" "${@}"
16
+
17
+ SERVICE="${SERVICE_NAME:-${DEFAULT_SERVICE}}"
18
+ INCLUDE_PREFIX_FOR_NON_BASH=true
19
+ while getopts "s:B" opt "${@}"; do
20
+ case ${opt} in
21
+ s )
22
+ SERVICE="${OPTARG}"
23
+ ;;
24
+ B )
25
+ INCLUDE_PREFIX_FOR_NON_BASH=false
26
+ ;;
27
+ \? )
28
+ log "🛑" "Unknown option: ${opt}"
29
+ usage "${description}" "${usage_args}" "${usage_pre}"
30
+ ;;
31
+ : )
32
+ log "🛑" "Invalid option: ${opt} requires an argument"
33
+ usage "${description}" "${usage_args}" "${usage_pre}"
34
+ ;;
35
+ esac
36
+ done
37
+ shift $((OPTIND -1))
38
+
39
+ if [ $# -eq 0 ]; then
40
+ log "🛑" "You must provide a command e.g. bash or ls -l"
41
+ usage "${description}" "${usage_args}" "${usage_pre}"
42
+ fi
43
+
44
+ if ! exec_hook_if_exists "exec.pre"; then
45
+ log "build.pre failed"
46
+ exit 1
47
+ fi
48
+
49
+ if [ "$#" -eq 1 ] && [ "$1" = "bash" ]; then
50
+ COMMAND=(bash)
51
+ elif [ "$INCLUDE_PREFIX_FOR_NON_BASH" = "true" ]; then
52
+ COMMAND=(bash -lc "$*")
53
+ else
54
+ COMMAND=("$@")
55
+ fi
56
+
57
+ log "🚂" "Running '${COMMAND[@]}' inside container with service name '${SERVICE}'"
58
+
59
+ docker \
60
+ compose \
61
+ --file docker-compose.dx.yaml \
62
+ --project-name "${PROJECT_NAME}" \
63
+ --env-file "${ENV_FILE}" \
64
+ exec \
65
+ "${SERVICE}" \
66
+ "${COMMAND[@]}"
67
+
68
+ # vim: ft=bash
@@ -0,0 +1,19 @@
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
+ require_command "docker"
9
+ load_docker_compose_env
10
+
11
+ usage_on_help "Prune containers for this repo" "" "" "" "${@}"
12
+
13
+ for container_id in $(docker container ls -a -f "name=^${PROJECT_NAME}-.*-1$" --format="{{.ID}}"); do
14
+ log "🗑" "Removing container with id '${container_id}'"
15
+ docker container rm "${container_id}"
16
+ done
17
+ echo "🧼" "Containers removed"
18
+
19
+ # vim: ft=bash
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ # Ideally, the message below is shown after everything starts up. We can't
6
+ # achieve this using healthchecks because the interval for a healtcheck is
7
+ # also an initial delay, and we don't really want to do healthchecks on
8
+ # our DB or Redis every 2 seconds. So, we sleep just a bit to let
9
+ # the other containers start up and vomit out their output first.
10
+ sleep 2
11
+ # Output some helpful messaging when invoking `dx/start` (which itself is
12
+ # a convenience script for `docker compose up`.
13
+ #
14
+ # Adding this to work around the mild inconvenience of the `app` container's
15
+ # entrypoint generating no output.
16
+ #
17
+ cat <<-'PROMPT'
18
+
19
+
20
+
21
+ 🎉 Dev Environment Initialized! 🎉
22
+
23
+ ℹ️ To use this environment, open a new terminal and run
24
+
25
+ dx/exec bash
26
+
27
+ 🕹 Use `ctrl-c` to exit.
28
+
29
+
30
+
31
+ PROMPT
32
+
33
+ # Using `sleep infinity` instead of `tail -f /dev/null`. This may be a
34
+ # performance improvement based on the conversation on a semi-related
35
+ # StackOverflow page.
36
+ #
37
+ # @see https://stackoverflow.com/a/41655546
38
+ sleep infinity
@@ -0,0 +1,30 @@
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
+ require_command "docker"
9
+ load_docker_compose_env
10
+
11
+ usage_on_help "Starts all services, including a container in which to run your app" "" "" "" "${@}"
12
+
13
+ log "🚀" "Starting docker-compose.dx.yml"
14
+
15
+ BUILD=--build
16
+ if [ "${1}" == "--no-build" ]; then
17
+ BUILD=
18
+ fi
19
+
20
+ docker \
21
+ compose \
22
+ --file docker-compose.dx.yml \
23
+ --project-name "${PROJECT_NAME}" \
24
+ --env-file "${ENV_FILE}" \
25
+ up \
26
+ "${BUILD}" \
27
+ --timestamps \
28
+ --force-recreate
29
+
30
+ # vim: ft=bash
@@ -0,0 +1,23 @@
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
+ require_command "docker"
9
+ load_docker_compose_env
10
+
11
+ usage_on_help "Stops all services, the container in which to run your app and removes any volumes" "" "" "" "${@}"
12
+
13
+ log "🚀" "Stopping docker-compose.dx.yml"
14
+
15
+ docker \
16
+ compose \
17
+ --file docker-compose.dx.yml \
18
+ --project-name "${PROJECT_NAME}" \
19
+ --env-file "${ENV_FILE}" \
20
+ down \
21
+ --volumes
22
+
23
+ # vim: ft=bash
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "<%= app_name %>",
3
+ "type": "module",
4
+ "license": "UNLICENSED",
5
+ "dependencies": {
6
+ "brut-css": "<%= versions.brut_css_version_specifier %>",
7
+ "brut-js": "<%= versions.brut_js_version_specifier %>"
8
+ },
9
+ "devDependencies": {
10
+ "chokidar-cli": "^3.0.0",
11
+ "esbuild": "^0.20.2",
12
+ "jsdom": "^25.0.1",
13
+ "mocha": "^10.7.3",
14
+ "playwright": "1.50.1",
15
+ "typescript": "^5.8.3",
16
+ "typescript-language-server": "^4.3.4",
17
+ "vscode-langservers-extracted": "^4.10.0"
18
+ },
19
+ "notes": {
20
+ "note": "This section provides documentation on what the various information in here is for",
21
+ "type": "Needs to be 'module' so that the web components unit tests work properly",
22
+ "dependencies": {
23
+ "brut-css": "Basic CSS library to get you started. See Brut's docs for how to use or remove",
24
+ "brut-js": "Autonomous custom elements and utilities. See Brut's docs for how to use and why you should not remove"
25
+ },
26
+ "devDependencies": {
27
+ "chokidar-cli": "Used to watch files and and rebuild stuff on changes",
28
+ "esbuild": "Bundles JS and CSS - you may need this in dependencies depending on how you deploy",
29
+ "jsdom": "Used for unit testing custom elements",
30
+ "mocha": "Used for unit testing custom elements",
31
+ "playwright": "Used for end-to-end testing. This version is locked and must be the same as the one in the Gemfile for playwright-ruby-client.",
32
+ "typescript": "Needed for the JS and CSS LSP servers",
33
+ "typescript-language-server": "Provides a JS LSP server",
34
+ "vscode-langservers-extracted": "Despite the name, provides a CSS LSP server"
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,53 @@
1
+ # Mostly based on Heroku's guidance:
2
+ # https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server
3
+
4
+ # Ensures the logger is setup and availbale as early as possible
5
+ require "semantic_logger"
6
+
7
+ # workers should be set to the # of cores. Heroku
8
+ # recommends the following, with 2 as a default if you don't have a good
9
+ # reason to change it.
10
+ workers Integer(ENV["WEB_CONCURRENCY"] || 2)
11
+
12
+ # Heroku recommens 5 as a default, with the ability
13
+ # to change it as needed.
14
+ threads_count = Integer(ENV["PUMA_MAX_THREADS"] || 5)
15
+
16
+ # This actually sets the min and max # of threads.
17
+ # Heroku recommends setting them both to the thread_count
18
+ # above.
19
+ threads threads_count, threads_count
20
+
21
+ # Indicate that the app should be loaded before any
22
+ # forking or thread creation. Despite the bang,
23
+ # this doesn't actually do anything - it just sets configuration
24
+ preload_app!
25
+
26
+ # Support IPv6 by binding to host `::` instead of `0.0.0.0`
27
+ port(ENV["PORT"] || 3000, "::")
28
+
29
+ # Turn off keepalive support for better long tails response time with Router 2.0
30
+ # Remove this line when https://github.com/puma/puma/issues/3487 is closed, and the fix is released
31
+ if respond_to?(:enable_keep_alives)
32
+ enable_keep_alives(false)
33
+ end
34
+
35
+ # Commenting this out as a) we don't default DefaultRackup
36
+ # and b) I don't exactly know what it means or does.
37
+ # rackup DefaultRackup if defined?(DefaultRackup)
38
+
39
+ # Set the environment based on RACK_ENV
40
+ environment ENV["RACK_ENV"]
41
+
42
+ before_fork do
43
+ # Per http://sequel.jeremyevans.net/rdoc/files/doc/fork_safety_rdoc.html
44
+ # we must disconnect before forking. Sequel will reconnect as needed.
45
+ Sequel::DATABASES.each(&:disconnect)
46
+ end
47
+
48
+ on_worker_boot do
49
+ # Per https://logger.rocketjob.io/forking.html we want to reopen
50
+ # this to avoid issues
51
+ SemanticLogger.reopen
52
+ end
53
+
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "The home page works" do
4
+ it "shows a welcome message" do
5
+ # Instead of hard-coding URLs, you can use the
6
+ # page classes to navigate to their routing.
7
+ page.goto(HomePage.routing)
8
+
9
+ # The Brut::FrontEnd::Components::PageIdentifier component
10
+ # renders a <meta> tag that Brut's `be_page_for` will look for.
11
+ # This is a useful check that you ended up on the page you meant to.
12
+ expect(page).to be_page_for(HomePage)
13
+
14
+ # In Playwright, elements are "located" asynchronously. What is returned
15
+ # here is an object that will look for an <h1> only when an expectation
16
+ # is made on it.
17
+ h1 = page.locator("h1")
18
+
19
+ # This will actually locate the <h1> (waiting for some time for it to
20
+ # show up if it's not there), and then assert its text content.
21
+ expect(h1).to have_text("Welcome to Brut")
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ import { createTestBasedOnHTML } from "brut-js/testing"
2
+
3
+ import path from "node:path"
4
+ import fs from "node:fs"
5
+
6
+ const __dirname = import.meta.dirname
7
+
8
+ const appRoot = path.resolve(__dirname,"..","..","..","app")
9
+ const publicRoot = path.resolve(appRoot,"public")
10
+ const assetMetadataFilePath = path.resolve(appRoot,"config","asset_metadata.json")
11
+ const assetMetadata = JSON.parse(fs.readFileSync(assetMetadataFilePath))
12
+
13
+ const withHTML = (html) => {
14
+ return createTestBasedOnHTML({
15
+ html,
16
+ assetMetadata,
17
+ publicRoot
18
+ })
19
+ }
20
+
21
+ export {
22
+ withHTML,
23
+ }
24
+
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe HomePage do
4
+ it "should show the H1" do
5
+ # Page specs should evaluate the generated HTML, so
6
+ # the generate_and_parse help will accept an instaniated
7
+ # page (or components), generate its HTML, then use Nokogiri
8
+ # to parse it, return the result.
9
+ result = generate_and_parse(described_class.new)
10
+
11
+ # e! is provided by Brut::SpecSupport::EnhancedNode which
12
+ # delegates everything to the underlying Nokogiri::XML::Node
13
+ # while adding a few methods. e! requires that exactly
14
+ # one element match the given CSS selector, then returns it.
15
+ #
16
+ # Thus, this expectation will fail if:
17
+ # * there is no <h1> (and the error message would indcate this)
18
+ # * there is is more than one <h1> (and the error message would indcate this)
19
+ # * the only <h1>'s text is not exactly "Welcome to Brut"
20
+ expect(result.e!("h1").text).to eq("Welcome to Brut")
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+ RSpec.describe "factories" do
3
+ it "should be possible to create them all" do
4
+ FactoryBot.lint traits: true
5
+ end
6
+ end
7
+
@@ -0,0 +1,78 @@
1
+ ENV["RACK_ENV"] = "test"
2
+ require_relative "../app/bootstrap"
3
+ Bootstrap.new.bootstrap!
4
+
5
+ require "brut/spec_support"
6
+
7
+ require "nokogiri"
8
+ require "playwright"
9
+ require "playwright/test"
10
+ require "confidence_check/for_rspec"
11
+ require "with_clues"
12
+
13
+ require_relative "support"
14
+
15
+ RSpec.configure do |config|
16
+ # This configuration has two main parts.
17
+ #
18
+ # The first part is here: a call to Brut::SpecSupport::RSpecSetup,
19
+ # which will set configuration values required for Brut's spec
20
+ # helpers and other APIs. In generally, you do not want to change
21
+ # this configuration.
22
+ rspec_setup = Brut::SpecSupport::RSpecSetup.new(rspec_config: config)
23
+ rspec_setup.setup!
24
+
25
+ # The second part is here and is RSpec configuration that you may want
26
+ # to change. Changing the configuration below here should not break
27
+ # any of Brut's internal behavior around tests. The values
28
+ # and configuration options set are a recommended default, but you
29
+ # can certainly change it to suit your needs.
30
+
31
+
32
+ # Confidence Check allows you to wrap test expectations
33
+ # with confidence_check as a way to indicate those expectations are
34
+ # checking that the test is setup properly and not testing
35
+ # the behavior of the code under test
36
+ config.include ConfidenceCheck::ForRSpec
37
+
38
+ # With Clues allows you to wrap expectations in
39
+ # a with_clues block that will provide more details
40
+ # about the failure and make it easier to diagnose
41
+ # why a test failed.
42
+ config.include WithClues::Method
43
+ config.include FactoryBot::Syntax::Methods
44
+
45
+ config.expect_with :rspec do |expectations|
46
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
47
+ end
48
+
49
+ config.mock_with :rspec do |mocks|
50
+ mocks.verify_partial_doubles = true
51
+ end
52
+
53
+ # Not that you should be using shared contexts, but if you are, please
54
+ # see https://rubydoc.info/gems/rspec-core/3.13.5/RSpec/Core/Configuration#shared_context_metadata_behavior-instance_method
55
+ config.shared_context_metadata_behavior = :apply_to_host_groups
56
+
57
+ config.filter_run_when_matching :focus
58
+
59
+ # Can't find docs on how this path is resolved, so disabling
60
+ # config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ config.disable_monkey_patching!
63
+
64
+ config.warnings = ENV.fetch("RSPEC_WARNINGS","false") == "true"
65
+
66
+ if config.files_to_run.one?
67
+ config.default_formatter = "doc"
68
+ end
69
+
70
+ if ENV["RSPEC_PROFILE_EXAMPLES"]
71
+ config.profile_examples = ENV["RSPEC_PROFILE_EXAMPLES"].to_i
72
+ end
73
+
74
+ config.order = :random
75
+
76
+ Kernel.srand config.seed
77
+ end
78
+
@@ -0,0 +1,2 @@
1
+ module Support
2
+ end
@@ -0,0 +1,24 @@
1
+ # Allows manually triggering an exception so you can see
2
+ # the exception handling mechanism execute without waiting
3
+ # for a real exception to happen.
4
+ class TriggerExceptionHandler < AppHandler
5
+ # These arguments are query string parameters that
6
+ # can be given to the URL connected to this handler.
7
+ # See https://brutrb.com/keyword-injection.html for more details.
8
+ def initialize(message: "no message provided", status: nil, key: nil)
9
+ @message = message
10
+ @status = status
11
+ @key = key
12
+ end
13
+
14
+ def handle
15
+ if @key != Brut.container.trigger_exception_key
16
+ http_status(404)
17
+ elsif @status
18
+ http_status(@status)
19
+ else
20
+ raise @message
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,49 @@
1
+ import { BaseCustomElement } from "brut-js"
2
+
3
+ // This is very similar to a vanilla on-spec
4
+ // implementation of an autonomous custom element.
5
+ // The only difference is BaseCustomElement (which
6
+ // extends HTMLElement) provides a few convenience
7
+ // methods to make it easier to build your own custom
8
+ // elements.
9
+ class Example extends BaseCustomElement {
10
+ // tagName allows BaseCustomElement's define() method to
11
+ // define your custom element
12
+ static tagName = "<%= prefix %>-example"
13
+
14
+ static observedAttributes = [
15
+ "transform",
16
+ "show-warnings", // recognized by BaseCustomElement to allow
17
+ // for debugguing in the browser console,
18
+ // but clean consoles in production.
19
+ ]
20
+
21
+ #transform = "upper"
22
+
23
+ // Called by BaseCustomElement's attributeChangedCallback.
24
+ transformChangedCallback({newValue}) {
25
+ this.#transform = newValue
26
+ }
27
+
28
+ // Called by connectCallback and attributeChangedCallback, this
29
+ // method should make whatever changes are necessary based on the current
30
+ // state of the element.
31
+ update() {
32
+ const content = this.textContent
33
+ if (this.#transform == "upper") {
34
+ this.textContent = content.toLocaleUpperCase()
35
+ }
36
+ else if (this.#transform == "lower") {
37
+ this.textContent = content.toLocaleLowerCase()
38
+ }
39
+ else {
40
+ // Example of debugging. if show-warnings is not set, this message
41
+ // is not shown in the console. If show-warnings IS set (including
42
+ // when you override it in production using the browser's devtools)
43
+ // this message WILL be shown
44
+ this.logger.info("We only support upper or lower, but got %s",this.#transform)
45
+ }
46
+ }
47
+ }
48
+
49
+ export default Example