brut 0.0.28 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/.projections.json +10 -0
- data/.rspec +3 -0
- data/Dockerfile.dx +32 -14
- data/Gemfile.lock +1 -1
- data/README.md +23 -2
- data/assets/Logo-Square.pxd +0 -0
- data/assets/LogoPylon.pxd +0 -0
- data/assets/LogoStop.pxd +0 -0
- data/assets/LogoTall.pxd +0 -0
- data/assets/MetroLogo.graffle +0 -0
- data/assets/SocialImage.png +0 -0
- data/assets/SocialImage.pxd +0 -0
- data/bin/docs +24 -2
- data/bin/rspec +27 -0
- data/bin/setup +3 -3
- data/brutrb.com/.vitepress/config.mjs +45 -8
- data/brutrb.com/.vitepress/theme/custom.css +7 -0
- data/brutrb.com/.vitepress/theme/style.css +29 -17
- data/brutrb.com/ai.md +10 -15
- data/brutrb.com/assets.md +2 -9
- data/brutrb.com/brut-js.md +12 -2
- data/brutrb.com/cli.md +9 -13
- data/brutrb.com/components.md +118 -96
- data/brutrb.com/configuration.md +3 -4
- data/brutrb.com/css.md +2 -2
- data/brutrb.com/custom-element-tests.md +3 -4
- data/brutrb.com/database-access.md +1 -1
- data/brutrb.com/database-schema.md +29 -41
- data/brutrb.com/deployment.md +123 -45
- data/brutrb.com/dev-environment.md +7 -7
- data/brutrb.com/dir-structure.md +120 -0
- data/brutrb.com/doc-conventions.md +18 -15
- data/brutrb.com/dx +1 -0
- data/brutrb.com/end-to-end-tests.md +12 -10
- data/brutrb.com/features.md +373 -0
- data/brutrb.com/flash-and-session.md +115 -131
- data/brutrb.com/form-constraints.md +266 -0
- data/brutrb.com/forms.md +140 -765
- data/brutrb.com/getting-started.md +10 -11
- data/brutrb.com/handlers.md +119 -95
- data/brutrb.com/hooks.md +18 -20
- data/brutrb.com/i18n.md +6 -4
- data/brutrb.com/images/LogoPylon.png +0 -0
- data/brutrb.com/images/LogoSquare.png +0 -0
- data/brutrb.com/images/LogoStop.png +0 -0
- data/brutrb.com/images/LogoTall.png +0 -0
- data/brutrb.com/images/OverviewMetro.graffle +0 -0
- data/brutrb.com/images/OverviewMetro.png +0 -0
- data/brutrb.com/index.md +4 -3
- data/brutrb.com/instrumentation.md +7 -10
- data/brutrb.com/javascript.md +14 -14
- data/brutrb.com/keyword-injection.md +72 -114
- data/brutrb.com/layouts.md +20 -52
- data/brutrb.com/lsp.md +1 -1
- data/brutrb.com/overview.md +35 -377
- data/brutrb.com/pages.md +119 -207
- data/brutrb.com/public/SocialImage.png +0 -0
- data/brutrb.com/public/favicon.ico +0 -0
- data/brutrb.com/recipes/alternate-layouts.md +32 -0
- data/brutrb.com/recipes/authentication.md +315 -6
- data/brutrb.com/recipes/blank-layouts.md +22 -0
- data/brutrb.com/recipes/custom-flash.md +51 -0
- data/brutrb.com/recipes/indexed-forms.md +149 -0
- data/brutrb.com/recipes/text-field-component.md +182 -0
- data/brutrb.com/routes.md +56 -82
- data/brutrb.com/security.md +0 -3
- data/brutrb.com/space-time-continuum.md +8 -12
- data/brutrb.com/tutorial.md +1 -1
- data/brutrb.com/why.md +19 -0
- data/docker-compose.dx.yml +5 -2
- data/docs/404.html +8 -3
- data/docs/SocialImage.png +0 -0
- data/docs/ai.html +11 -6
- data/docs/api/Brut/BackEnd/SeedData.html +1 -1
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
- data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
- data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
- data/docs/api/Brut/BackEnd/Validators.html +1 -1
- data/docs/api/Brut/BackEnd.html +1 -1
- data/docs/api/Brut/CLI/App.html +1 -1
- data/docs/api/Brut/CLI/AppRunner.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB.html +1 -1
- data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
- data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test.html +1 -1
- data/docs/api/Brut/CLI/Apps.html +1 -1
- data/docs/api/Brut/CLI/Command.html +1 -1
- data/docs/api/Brut/CLI/Error.html +1 -1
- data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
- data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
- data/docs/api/Brut/CLI/Executor.html +1 -1
- data/docs/api/Brut/CLI/InvalidOption.html +1 -1
- data/docs/api/Brut/CLI/Options.html +1 -1
- data/docs/api/Brut/CLI/Output.html +1 -1
- data/docs/api/Brut/CLI/SystemExecError.html +1 -1
- data/docs/api/Brut/CLI.html +1 -1
- data/docs/api/Brut/FactoryBot.html +1 -1
- data/docs/api/Brut/Framework/App.html +1 -1
- data/docs/api/Brut/Framework/Config.html +1 -1
- data/docs/api/Brut/Framework/Container.html +1 -1
- data/docs/api/Brut/Framework/Error.html +1 -1
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
- data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
- data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
- data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
- data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
- data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
- data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
- data/docs/api/Brut/Framework/Errors.html +1 -1
- data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
- data/docs/api/Brut/Framework/MCP.html +1 -1
- data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
- data/docs/api/Brut/Framework.html +1 -1
- data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
- data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
- data/docs/api/Brut/FrontEnd/Component.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
- data/docs/api/Brut/FrontEnd/Components.html +1 -1
- data/docs/api/Brut/FrontEnd/Download.html +1 -1
- data/docs/api/Brut/FrontEnd/Flash.html +1 -1
- data/docs/api/Brut/FrontEnd/Form.html +9 -11
- data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +201 -0
- data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +535 -0
- data/docs/api/Brut/FrontEnd/Forms/Input.html +983 -35
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +29 -19
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +141 -20
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +141 -20
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms.html +1 -1
- data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
- data/docs/api/Brut/FrontEnd/Handler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
- data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
- data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
- data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
- data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
- data/docs/api/Brut/FrontEnd/Layout.html +1 -1
- data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
- data/docs/api/Brut/FrontEnd/Page.html +1 -1
- data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
- data/docs/api/Brut/FrontEnd/Pages.html +1 -1
- data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing.html +1 -1
- data/docs/api/Brut/FrontEnd/Session.html +1 -1
- data/docs/api/Brut/FrontEnd.html +1 -1
- data/docs/api/Brut/I18n/BaseMethods.html +1 -1
- data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
- data/docs/api/Brut/I18n/ForCLI.html +1 -1
- data/docs/api/Brut/I18n/ForHTML.html +1 -1
- data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
- data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
- data/docs/api/Brut/I18n.html +1 -1
- data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
- data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
- data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
- data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
- data/docs/api/Brut/Instrumentation.html +1 -1
- data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
- data/docs/api/Brut/SinatraHelpers.html +1 -1
- data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
- data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
- data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
- data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
- data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
- data/docs/api/Brut/SpecSupport.html +1 -1
- data/docs/api/Brut.html +1 -1
- data/docs/api/Clock.html +1 -1
- data/docs/api/RichString.html +1 -1
- data/docs/api/SemanticLogger/Appender/Async.html +1 -1
- data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
- data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
- data/docs/api/Sequel/Extensions.html +1 -1
- data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
- data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
- data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
- data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
- data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
- data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
- data/docs/api/Sequel/Plugins/FindBang.html +1 -1
- data/docs/api/Sequel/Plugins.html +1 -1
- data/docs/api/Sequel.html +1 -1
- data/docs/api/_index.html +15 -1
- data/docs/api/class_list.html +1 -1
- data/docs/api/css/full_list.css +2 -1
- data/docs/api/css/style.css +14 -13
- data/docs/api/file.README.html +22 -3
- data/docs/api/index.html +22 -3
- data/docs/api/method_list.html +435 -275
- data/docs/api/top-level-namespace.html +1 -1
- data/docs/assets/LogoStop.Gb3tDhL1.png +0 -0
- data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
- data/docs/assets/{ai.md._6HCDL6d.js → ai.md.Cy9GWnER.js} +1 -1
- data/docs/assets/ai.md.Cy9GWnER.lean.js +1 -0
- data/docs/assets/{app.BX81XO4N.js → app.ClaS47Ru.js} +1 -1
- data/docs/assets/{assets.md.D3wunzLx.js → assets.md.7C3HWkga.js} +3 -3
- data/docs/assets/{assets.md.D3wunzLx.lean.js → assets.md.7C3HWkga.lean.js} +1 -1
- data/docs/assets/{brut-js.md.o2DAO2s2.js → brut-js.md.B4GYxQVw.js} +1 -1
- data/docs/assets/{brut-js.md.o2DAO2s2.lean.js → brut-js.md.B4GYxQVw.lean.js} +1 -1
- data/docs/assets/chunks/@localSearchIndexroot.Biqy1A4t.js +1 -0
- data/docs/assets/chunks/{VPLocalSearchBox.gABXcTWp.js → VPLocalSearchBox.DtgDfde2.js} +1 -1
- data/docs/assets/chunks/{theme.DwUXXAL3.js → theme.B45bvibT.js} +2 -2
- data/docs/assets/{cli.md.RmeA2b0i.js → cli.md.CjsktgFz.js} +15 -20
- data/docs/assets/components.md.DatoNgFo.js +96 -0
- data/docs/assets/{components.md.CRUMdRoN.lean.js → components.md.DatoNgFo.lean.js} +1 -1
- data/docs/assets/{configuration.md.BGHl8oRC.js → configuration.md.DeyhpqEx.js} +3 -3
- data/docs/assets/{css.md.DJgj2clw.js → css.md.CltvJqAa.js} +3 -3
- data/docs/assets/{custom-element-tests.md.BrYJQEl3.js → custom-element-tests.md.B_rbta32.js} +3 -3
- data/docs/assets/{database-access.md.C7l-Vuvb.js → database-access.md.gnluu54N.js} +1 -1
- data/docs/assets/{database-schema.md.BUjR0VS1.js → database-schema.md.CSYk6E6v.js} +6 -6
- data/docs/assets/{database-schema.md.BUjR0VS1.lean.js → database-schema.md.CSYk6E6v.lean.js} +1 -1
- data/docs/assets/deployment.md.BLseERGV.js +48 -0
- data/docs/assets/deployment.md.BLseERGV.lean.js +1 -0
- data/docs/assets/dev-environment.md.BroAOLhF.js +11 -0
- data/docs/assets/dir-structure.md.CWir1pic.js +46 -0
- data/docs/assets/dir-structure.md.CWir1pic.lean.js +1 -0
- data/docs/assets/doc-conventions.md.BzmSrTEW.js +1 -0
- data/docs/assets/doc-conventions.md.BzmSrTEW.lean.js +1 -0
- data/docs/assets/{end-to-end-tests.md.yfQHC0b5.js → end-to-end-tests.md.DzqRpZ43.js} +5 -3
- data/docs/assets/end-to-end-tests.md.DzqRpZ43.lean.js +1 -0
- data/docs/assets/features.md.DPFXsy0z.js +154 -0
- data/docs/assets/features.md.DPFXsy0z.lean.js +1 -0
- data/docs/assets/flash-and-session.md.nPvUpnUx.js +79 -0
- data/docs/assets/{flash-and-session.md.BXY8RvT0.lean.js → flash-and-session.md.nPvUpnUx.lean.js} +1 -1
- data/docs/assets/form-constraints.md.x5tNpTTI.js +90 -0
- data/docs/assets/form-constraints.md.x5tNpTTI.lean.js +1 -0
- data/docs/assets/forms.md.C2Dizvzq.js +64 -0
- data/docs/assets/forms.md.C2Dizvzq.lean.js +1 -0
- data/docs/assets/{getting-started.md.Ciz82L0m.js → getting-started.md.C93e0odB.js} +5 -5
- data/docs/assets/{getting-started.md.Ciz82L0m.lean.js → getting-started.md.C93e0odB.lean.js} +1 -1
- data/docs/assets/handlers.md.Chyri6KA.js +54 -0
- data/docs/assets/handlers.md.Chyri6KA.lean.js +1 -0
- data/docs/assets/{hooks.md.C4-moMny.js → hooks.md.Jmb5VOLA.js} +4 -4
- data/docs/assets/{hooks.md.C4-moMny.lean.js → hooks.md.Jmb5VOLA.lean.js} +1 -1
- data/docs/assets/{i18n.md.Do9i1qWl.js → i18n.md.xQhiGo1G.js} +2 -2
- data/docs/assets/{i18n.md.Do9i1qWl.lean.js → i18n.md.xQhiGo1G.lean.js} +1 -1
- data/docs/assets/index.md.CAMqGBJE.js +1 -0
- data/docs/assets/index.md.CAMqGBJE.lean.js +1 -0
- data/docs/assets/{instrumentation.md.a9Pjps4P.js → instrumentation.md.BgcaGVYH.js} +2 -2
- data/docs/assets/{instrumentation.md.a9Pjps4P.lean.js → instrumentation.md.BgcaGVYH.lean.js} +1 -1
- data/docs/assets/{javascript.md.GWbhRS51.js → javascript.md.DzrMxUmI.js} +7 -7
- data/docs/assets/{javascript.md.GWbhRS51.lean.js → javascript.md.DzrMxUmI.lean.js} +1 -1
- data/docs/assets/keyword-injection.md.95Zgh2eN.js +21 -0
- data/docs/assets/{keyword-injection.md.Dt2tKREs.lean.js → keyword-injection.md.95Zgh2eN.lean.js} +1 -1
- data/docs/assets/{layouts.md.cPnh3NId.js → layouts.md.CJGDFY-m.js} +2 -15
- data/docs/assets/layouts.md.CJGDFY-m.lean.js +1 -0
- data/docs/assets/{lsp.md.Bsu-f6VU.js → lsp.md.Dn1rIiW0.js} +1 -1
- data/docs/assets/{lsp.md.Bsu-f6VU.lean.js → lsp.md.Dn1rIiW0.lean.js} +1 -1
- data/docs/assets/overview.md.Bdq4qt3L.js +1 -0
- data/docs/assets/overview.md.Bdq4qt3L.lean.js +1 -0
- data/docs/assets/pages.md.B7Hc-i6H.js +45 -0
- data/docs/assets/pages.md.B7Hc-i6H.lean.js +1 -0
- data/docs/assets/recipes_alternate-layouts.md.BwEytl59.js +22 -0
- data/docs/assets/recipes_alternate-layouts.md.BwEytl59.lean.js +1 -0
- data/docs/assets/recipes_authentication.md.Dzvi_g69.js +156 -0
- data/docs/assets/recipes_authentication.md.Dzvi_g69.lean.js +1 -0
- data/docs/assets/recipes_blank-layouts.md.fyAUJyJR.js +15 -0
- data/docs/assets/recipes_blank-layouts.md.fyAUJyJR.lean.js +1 -0
- data/docs/assets/recipes_custom-flash.md.CrQbI5eH.js +26 -0
- data/docs/assets/recipes_custom-flash.md.CrQbI5eH.lean.js +1 -0
- data/docs/assets/recipes_indexed-forms.md.CstYyOSo.js +74 -0
- data/docs/assets/recipes_indexed-forms.md.CstYyOSo.lean.js +1 -0
- data/docs/assets/recipes_text-field-component.md.H4wLAK0Z.js +101 -0
- data/docs/assets/recipes_text-field-component.md.H4wLAK0Z.lean.js +1 -0
- data/docs/assets/routes.md.B8kfUPHU.js +21 -0
- data/docs/assets/{routes.md.BMM7peut.lean.js → routes.md.B8kfUPHU.lean.js} +1 -1
- data/docs/assets/{security.md.C668yXCi.js → security.md.C0G_AZR-.js} +1 -1
- data/docs/assets/{security.md.C668yXCi.lean.js → security.md.C0G_AZR-.lean.js} +1 -1
- data/docs/assets/space-time-continuum.md.xl44xDos.js +1 -0
- data/docs/assets/{space-time-continuum.md.KPUIKysQ.lean.js → space-time-continuum.md.xl44xDos.lean.js} +1 -1
- data/docs/assets/{style.D73IYGCX.css → style.prAgp4yQ.css} +1 -1
- data/docs/assets/tutorial.md.a4a0eVOy.js +1 -0
- data/docs/assets/tutorial.md.a4a0eVOy.lean.js +1 -0
- data/docs/assets/why.md.C-hk5xgJ.js +1 -0
- data/docs/assets/why.md.C-hk5xgJ.lean.js +1 -0
- data/docs/assets.html +12 -7
- data/docs/brut-js/api/AjaxSubmit.html +1 -1
- data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
- data/docs/brut-js/api/Autosubmit.html +1 -1
- data/docs/brut-js/api/Autosubmit.js.html +1 -1
- data/docs/brut-js/api/BaseCustomElement.html +1 -1
- data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
- data/docs/brut-js/api/BrutCustomElements.html +1 -1
- data/docs/brut-js/api/BufferedLogger.html +1 -1
- data/docs/brut-js/api/ConfirmSubmit.html +1 -1
- data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
- data/docs/brut-js/api/ConfirmationDialog.html +1 -1
- data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
- data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
- data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
- data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
- data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
- data/docs/brut-js/api/CopyToClipboard.html +1 -1
- data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
- data/docs/brut-js/api/Form.html +1 -1
- data/docs/brut-js/api/Form.js.html +1 -1
- data/docs/brut-js/api/I18nTranslation.html +1 -1
- data/docs/brut-js/api/I18nTranslation.js.html +1 -1
- data/docs/brut-js/api/LocaleDetection.html +1 -1
- data/docs/brut-js/api/LocaleDetection.js.html +1 -1
- data/docs/brut-js/api/Logger.html +1 -1
- data/docs/brut-js/api/Logger.js.html +1 -1
- data/docs/brut-js/api/Message.html +1 -1
- data/docs/brut-js/api/Message.js.html +1 -1
- data/docs/brut-js/api/PrefixedLogger.html +1 -1
- data/docs/brut-js/api/RichString.html +1 -1
- data/docs/brut-js/api/RichString.js.html +1 -1
- data/docs/brut-js/api/Tabs.html +1 -1
- data/docs/brut-js/api/Tabs.js.html +1 -1
- data/docs/brut-js/api/Tracing.html +1 -1
- data/docs/brut-js/api/Tracing.js.html +1 -1
- data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
- data/docs/brut-js/api/external-Performance.html +1 -1
- data/docs/brut-js/api/external-Promise.html +1 -1
- data/docs/brut-js/api/external-ValidityState.html +1 -1
- data/docs/brut-js/api/external-Window.html +1 -1
- data/docs/brut-js/api/external-fetch.html +1 -1
- data/docs/brut-js/api/global.html +1 -1
- data/docs/brut-js/api/index.html +1 -1
- data/docs/brut-js/api/index.js.html +1 -1
- data/docs/brut-js/api/module-testing.html +1 -1
- data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
- data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
- data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
- data/docs/brut-js/api/testing.DOMCreator.html +1 -1
- data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
- data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
- data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
- data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
- data/docs/brut-js/api/testing_index.js.html +1 -1
- data/docs/brut-js.html +12 -7
- data/docs/business-logic.html +10 -5
- data/docs/cli.html +26 -26
- data/docs/components.html +61 -64
- data/docs/configuration.html +13 -8
- data/docs/css.html +14 -9
- data/docs/custom-element-tests.html +14 -9
- data/docs/database-access.html +12 -7
- data/docs/database-schema.html +15 -10
- data/docs/deployment.html +58 -6
- data/docs/dev-environment.html +12 -7
- data/docs/dir-structure.html +74 -0
- data/docs/doc-conventions.html +11 -6
- data/docs/end-to-end-tests.html +15 -8
- data/docs/favicon.ico +0 -0
- data/docs/features.html +182 -0
- data/docs/flash-and-session.html +73 -82
- data/docs/form-constraints.html +118 -0
- data/docs/forms.html +57 -367
- data/docs/getting-started.html +15 -10
- data/docs/handlers.html +51 -61
- data/docs/hashmap.json +1 -1
- data/docs/hooks.html +14 -9
- data/docs/i18n.html +12 -7
- data/docs/index.html +11 -6
- data/docs/instrumentation.html +12 -7
- data/docs/javascript.html +17 -12
- data/docs/jobs.html +10 -5
- data/docs/keyword-injection.html +22 -21
- data/docs/layouts.html +12 -20
- data/docs/lsp.html +11 -6
- data/docs/markdown-examples.html +10 -5
- data/docs/middleware.html +10 -5
- data/docs/not-released.html +10 -5
- data/docs/overview.html +11 -138
- data/docs/pages.html +49 -121
- data/docs/recipes/alternate-layouts.html +50 -0
- data/docs/recipes/authentication.html +166 -6
- data/docs/recipes/blank-layouts.html +43 -0
- data/docs/recipes/custom-flash.html +54 -0
- data/docs/recipes/indexed-forms.html +102 -0
- data/docs/recipes/text-field-component.html +129 -0
- data/docs/routes.html +16 -19
- data/docs/security.html +11 -6
- data/docs/seed-data.html +10 -5
- data/docs/space-time-continuum.html +11 -6
- data/docs/tutorial.html +11 -6
- data/docs/unit-tests.html +10 -5
- data/docs/why.html +29 -0
- data/dx/bash_customizations +7 -0
- data/dx/build +13 -2
- data/dx/docker-compose.env +1 -1
- data/dx/exec +25 -8
- data/lib/brut/front_end/form.rb +8 -8
- data/lib/brut/front_end/forms/input.rb +253 -20
- data/lib/brut/front_end/forms/input_definition.rb +15 -12
- data/lib/brut/front_end/forms/radio_button_group_input.rb +8 -1
- data/lib/brut/front_end/forms/select_input.rb +8 -1
- data/lib/brut/front_end.rb +1 -0
- data/lib/brut/version.rb +1 -1
- data/specs/brut/front_end/forms/input.spec.rb +978 -0
- data/specs/brut/front_end/forms/radio_button_group_input.spec.rb +54 -0
- data/specs/brut/front_end/forms/select_input.spec.rb +54 -0
- data/specs/spec_helper.rb +27 -0
- data/specs/support/matchers/have_constraint_violation.rb +23 -0
- data/specs/support/matchers.rb +5 -0
- data/specs/support.rb +3 -0
- metadata +141 -77
- data/brutrb.com/public/images/logo-300.png +0 -0
- data/brutrb.com/public/images/logo.png +0 -0
- data/docs/assets/ai.md._6HCDL6d.lean.js +0 -1
- data/docs/assets/chunks/@localSearchIndexroot.CoYzciVi.js +0 -1
- data/docs/assets/components.md.CRUMdRoN.js +0 -104
- data/docs/assets/deployment.md.Dbka4OTr.js +0 -1
- data/docs/assets/deployment.md.Dbka4OTr.lean.js +0 -1
- data/docs/assets/dev-environment.md.GZv6xvi9.js +0 -11
- data/docs/assets/doc-conventions.md.-kN3Xo5C.js +0 -1
- data/docs/assets/doc-conventions.md.-kN3Xo5C.lean.js +0 -1
- data/docs/assets/end-to-end-tests.md.yfQHC0b5.lean.js +0 -1
- data/docs/assets/flash-and-session.md.BXY8RvT0.js +0 -93
- data/docs/assets/forms.md.B-koVgyw.js +0 -379
- data/docs/assets/forms.md.B-koVgyw.lean.js +0 -1
- data/docs/assets/handlers.md.089DVD3v.js +0 -69
- data/docs/assets/handlers.md.089DVD3v.lean.js +0 -1
- data/docs/assets/index.md.B28EwVpq.js +0 -1
- data/docs/assets/index.md.B28EwVpq.lean.js +0 -1
- data/docs/assets/keyword-injection.md.Dt2tKREs.js +0 -25
- data/docs/assets/layouts.md.cPnh3NId.lean.js +0 -1
- data/docs/assets/overview.Da81cB9R.png +0 -0
- data/docs/assets/overview.md.C5wlBcR5.js +0 -133
- data/docs/assets/overview.md.C5wlBcR5.lean.js +0 -1
- data/docs/assets/pages.md.BE3kfOc5.js +0 -122
- data/docs/assets/pages.md.BE3kfOc5.lean.js +0 -1
- data/docs/assets/recipes_authentication.md.CAsXf7hk.js +0 -1
- data/docs/assets/recipes_authentication.md.CAsXf7hk.lean.js +0 -1
- data/docs/assets/routes.md.BMM7peut.js +0 -29
- data/docs/assets/space-time-continuum.md.KPUIKysQ.js +0 -1
- data/docs/assets/tutorial.md.BnoGjrdK.js +0 -1
- data/docs/assets/tutorial.md.BnoGjrdK.lean.js +0 -1
- data/docs/images/logo-300.png +0 -0
- data/docs/images/logo.png +0 -0
- /data/docs/assets/{cli.md.RmeA2b0i.lean.js → cli.md.CjsktgFz.lean.js} +0 -0
- /data/docs/assets/{configuration.md.BGHl8oRC.lean.js → configuration.md.DeyhpqEx.lean.js} +0 -0
- /data/docs/assets/{css.md.DJgj2clw.lean.js → css.md.CltvJqAa.lean.js} +0 -0
- /data/docs/assets/{custom-element-tests.md.BrYJQEl3.lean.js → custom-element-tests.md.B_rbta32.lean.js} +0 -0
- /data/docs/assets/{database-access.md.C7l-Vuvb.lean.js → database-access.md.gnluu54N.lean.js} +0 -0
- /data/docs/assets/{dev-environment.md.GZv6xvi9.lean.js → dev-environment.md.BroAOLhF.lean.js} +0 -0
data/brutrb.com/deployment.md
CHANGED
@@ -1,66 +1,144 @@
|
|
1
1
|
# Deployment
|
2
2
|
|
3
3
|
Brut apps are Rack apps, so they can be deployed in conventional
|
4
|
-
ways.
|
5
|
-
development are inteded to work for production as well.
|
4
|
+
ways.
|
6
5
|
|
7
6
|
## Overview
|
8
7
|
|
9
|
-
|
10
|
-
simple solution for all deployment setups, so this document will
|
11
|
-
outline considerations when setting up deployment.
|
8
|
+
There are just too many ways to deploy. Brut attempts to address this by adhering to [12-factor principles](https://12factor.net). Brut also tries not to create artifacts like `Procfile` or `Dockerfile` that would conflict with the artifacts you'd need to manage deployment.
|
12
9
|
|
13
|
-
|
14
|
-
at `deploy/Dockerfile`, which is the foundation of a `Dockerfile`
|
15
|
-
you can use. In particular, it shows you the commands needed to
|
16
|
-
setup and run the app in production:
|
10
|
+
That said, Brut includes first-class support for deploying to Heroku using containers. More options will be included as necessary, either through direct support in code/tooling, or documentation here.
|
17
11
|
|
18
|
-
|
19
|
-
as whatever is needed for NodeJS and Postgres, the Brut-specific
|
20
|
-
parts look like so:
|
12
|
+
### Heroku Container-based Deployment
|
21
13
|
|
22
|
-
|
23
|
-
2. Install Node modules with `npm clean-install`
|
24
|
-
3. Build all assets with `bin/build-assets` (this will bundle all
|
25
|
-
CSS and Javascript, plus copy over any other [assets](/assets)
|
26
|
-
to the locations from where the Brut app will serve them)
|
27
|
-
4. Run the app with `bin/run`
|
14
|
+
When creating your Brut app with `mkbrut`, the Heroku segment can be used to create files and scripts for a [Heroku container-based deployment](https://devcenter.heroku.com/articles/container-registry-and-runtime).
|
28
15
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
16
|
+
| File | Purpose | Notes |
|
17
|
+
|------|---------|-------|
|
18
|
+
| `bin/deploy` | Script to use to perform the deployment | This wraps `HerokuContainerBasedDeploy` in `Brut::CLI::Apps` |
|
19
|
+
| `deploy/Dockerfile` | Template `Dockerfile` used to create a `Dockerfile` for each process type | Heroku requires each process (web, worker, release, etc.) to have its own `Dockerfile` and own image |
|
20
|
+
| `deploy/heroku_config.rb` | Class that exports optional processes | By default, your app has a web and release process. `HerokuConfig` can export others, like Sidekiq |
|
21
|
+
| `deploy/docker-entrypoint` | The [`ENTRYPOINT`](https://docs.docker.com/reference/dockerfile/#entrypoint) for production Docker images, which is set up to use jemalloc | You can modify or remove this as needed |
|
22
|
+
|
23
|
+
How to deploy:
|
24
|
+
|
25
|
+
1. Auth to Heroku from inside your dev container:
|
26
|
+
|
27
|
+
```
|
28
|
+
your-computer> dx/exec bash
|
29
|
+
devcontainer> heroku auth:login
|
30
|
+
# You will need to copy/paste the URL to log in
|
31
|
+
devcontainer> heroku container:login
|
32
|
+
```
|
33
|
+
|
34
|
+
2. Create your app using the container stack:
|
35
|
+
|
36
|
+
```
|
37
|
+
> heroku create --stack container -a «your heroku app name»
|
38
|
+
```
|
39
|
+
3. Ensure your app's source code is all checked in, there are no uncommitted or unadded files, and you have pushed to main.
|
40
|
+
4. `bin/deploy`
|
41
|
+
|
42
|
+
This will generate a `Dockerfile` for each process (by default, `Dockerfile.web` and `Dockerfile.release`), build images, push those images to Heroku, and ask Heroku to release them.
|
43
|
+
|
44
|
+
Debugging Tips:
|
45
|
+
|
46
|
+
* Keep in mind it's hard to make general deployment tools. You are expected to understand your deployment and be capable of deploying an arbitrary Rack app manually. Brut's tooling automates what you need to know.
|
47
|
+
* `bin/deploy` runs the `deploy` subcommand, so `bin/deploy help deploy` can provide
|
48
|
+
some options for debugging issues:
|
49
|
+
|
50
|
+
```
|
51
|
+
devcontainer> bin/deploy help deploy
|
52
|
+
Usage: bin/deploy [global options] deploy [command options]
|
53
|
+
|
54
|
+
Build images, push them to Heroku, and deploy them
|
55
|
+
|
56
|
+
Manages a deploy process based on using Heroku's Container Registry. See
|
57
|
+
|
58
|
+
https://devcenter.heroku.com/articles/container-registry-and-runtime
|
59
|
+
|
60
|
+
for details. You are assumed to understand this.
|
61
|
+
This command will make the process somewhat easier.
|
62
|
+
|
63
|
+
This will use deploy/Dockerfile as a template to create
|
64
|
+
one Dockerfile for each process you want to run in Heroku.
|
65
|
+
deploy/heroku_config.rb is where the processes and their
|
66
|
+
commands are configured.
|
67
|
+
|
68
|
+
The release phase is included automatically, based on bin/release.
|
69
|
+
|
70
|
+
GLOBAL OPTIONS
|
71
|
+
|
72
|
+
-h, --help Get help
|
73
|
+
--log-level=LEVEL Set log level. Allowed values: debug,
|
74
|
+
info, warn, error, fatal. Default 'fatal'
|
75
|
+
--verbose Set log level to 'debug', which will produce
|
76
|
+
maximum output
|
77
|
+
|
78
|
+
ENVIRONMENT VARIABLES
|
79
|
+
|
80
|
+
BRUT_CLI_RAISE_ON_ERROR - if set, shows backtrace on errors
|
81
|
+
LOG_LEVEL - log level if --log-level or --verbose is omitted
|
82
|
+
|
83
|
+
|
84
|
+
COMMAND OPTIONS
|
85
|
+
|
86
|
+
--platform=PLATFORM Override default platform. Can be any Docker
|
87
|
+
platform.
|
88
|
+
--[no-]dry-run Print the commands that would be run and
|
89
|
+
don't actually do anything. Implies --skip-checks
|
90
|
+
--[no-]skip-checks Skip checks for code having been
|
91
|
+
committed and pushed
|
92
|
+
--[no-]deploy After images are pushed, actually deploy them
|
93
|
+
--[no-]push After images are created, push them
|
94
|
+
to Heroku's registry. If false,
|
95
|
+
implies --no-deploy
|
96
|
+
```
|
97
|
+
* Try building images first: `bin/deploy deploy --no-push --skip-checks`
|
98
|
+
* It's possible to run the images locally. If you are on Apple Silicon, you'll
|
99
|
+
need to set --platform:
|
100
|
+
|
101
|
+
* `bin/deploy deploy --no-push --skip-checks --platform linux/arm64`
|
102
|
+
* Create `docker-compose.yml` for your image and any other services e.g. databases
|
103
|
+
* Set required environment variables in `docker-compose.yml`
|
104
|
+
* Start up Docker compose and poke around
|
105
|
+
|
106
|
+
You'll need to have a better understanding of Docker to do this, however if you
|
107
|
+
are deploying with Docker, this is an understanding you hopefully already have.
|
108
|
+
|
109
|
+
|
110
|
+
### Other Mechanisms for Deployment
|
111
|
+
|
112
|
+
As a Rack app, other deployments should be possible. To make the app work, you'll need to make sure a few things are dealt with:
|
113
|
+
|
114
|
+
* `RACK_ENV` **must** be `"production"`
|
115
|
+
* `bin/build-assets` will build all assets by default. This must either be done on production servers or done ahead of time and the results packaged with the app.
|
116
|
+
* `bin/build-assets` outputs files in `app/public` and `app/config`. Those files are used at runtime. Brut **will not** initiate the build of any assets.
|
117
|
+
* If you are going to build assets on production servers, you *must* included developer tooling. This means NodeJS, all modules in `package.json` and all RubyGems in `Gemfile`.
|
118
|
+
|
119
|
+
The `deploy/Dockerfile` created by `mkbrut --segment-heroku` is not very Heroku-specific and could serve as a reference.
|
33
120
|
|
34
121
|
## Testing
|
35
122
|
|
36
|
-
|
37
|
-
them locally to see how they work. You will need to have local
|
38
|
-
versions of all infrastructure (database, Redis, etc.), but if
|
39
|
-
these work locally, there is a high chance they work in
|
40
|
-
production.
|
123
|
+
Testing deployments is a bit out of scope, but in general:
|
41
124
|
|
42
|
-
|
43
|
-
|
125
|
+
* A container-based deployment can theoretically be run on your computer as a test.
|
126
|
+
* Non-production, but production-like environments can be used to validate production configurations.
|
127
|
+
* You own the means of production…not Brut.
|
44
128
|
|
45
129
|
## Recommended Practices
|
46
130
|
|
47
|
-
|
48
|
-
|
49
|
-
|
131
|
+
* Avoid a lot of code that checks `Brut.container.project_env`. Try to consolidate all prod/test/dev differences in environment variables.
|
132
|
+
* Have a way to get a shell into your production environment for debugging.
|
133
|
+
* Brut doesn't log much, but if you remove the `OTEL_*` environment variables, Brut will log OTel telemetry to the console, which may be useful.
|
134
|
+
* Setting `OTEL_LOG_LEVEL=debug` is advised if the app isn't starting or you aren't seeing any telemetry or logging
|
50
135
|
|
51
|
-
|
52
|
-
of surprises. That all being said, here are some recommendations:
|
136
|
+
## Technical Notes
|
53
137
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
card you charge and refund.
|
58
|
-
* Configure observability so you know what your app is doing at
|
59
|
-
all times.
|
60
|
-
* Configure a URL that, when accessed, produces an error. This
|
61
|
-
allows you to check your error reporting system.
|
62
|
-
* Create a page somewhere that shows the git SHA of your
|
63
|
-
deployment, or some other unique, unambiguous version number. This
|
64
|
-
will clarify what version of the code is actually running.
|
138
|
+
> [!IMPORTANT]
|
139
|
+
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
140
|
+
> internals, the source code is always more correct.
|
65
141
|
|
142
|
+
_Last Updated July 3, 2025_
|
66
143
|
|
144
|
+
None at this time.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Dev Environment
|
2
2
|
|
3
|
-
Brut provides
|
3
|
+
Brut provides sophisticated tooling to manage your dev environment
|
4
4
|
|
5
5
|
## Overview
|
6
6
|
|
@@ -41,7 +41,7 @@ Your editor and version control system run on your computer.
|
|
41
41
|
These are the commands you will use to manage the *foundational core*, which is the Docker containers and
|
42
42
|
their contents.
|
43
43
|
|
44
|
-
A few brief
|
44
|
+
A few brief terminology notes if you aren't familiar with Docker:
|
45
45
|
|
46
46
|
* A Docker *container* is akin to a virtual machine. On Linux this isn't strictly true, but conceptually, you can think of this like a virtual computer.
|
47
47
|
* A Docker *image* is what you use to start a container. This is akin to a disk image you might use to
|
@@ -52,7 +52,7 @@ A few verbs to provide additional help:
|
|
52
52
|
|
53
53
|
* One *builds* a Docker image from a Dockerfile.
|
54
54
|
* One *starts* a Docker container from an image.
|
55
|
-
* One *stops* a Docker
|
55
|
+
* One *stops* a Docker container when it's no longer needed.
|
56
56
|
|
57
57
|
|
58
58
|
| App | Purpose |
|
@@ -68,7 +68,7 @@ The workflow for the foundational core is shown in this diagram.
|
|
68
68
|
|
69
69
|
In words:
|
70
70
|
|
71
|
-
1. You build the images based on the latest
|
71
|
+
1. You build the images based on the latest instructions via `dx/build`.
|
72
72
|
2. You start up the environment with `dx/start`.
|
73
73
|
3. You then use `dx/exec` to execute commands from the Workspace (see below).
|
74
74
|
4. When you are done working for the day, `dx/stop` shuts everything down.
|
@@ -90,7 +90,7 @@ you full documentation about what the command and subcommands do.
|
|
90
90
|
| | `drop` | Drop the database if it exists |
|
91
91
|
| | `migrate` | Apply any outstanding migrations to the database |
|
92
92
|
| | `new_migration` | Create a new migration file |
|
93
|
-
| | `rebuild` | Drop, re-create, and run migrations,
|
93
|
+
| | `rebuild` | Drop, re-create, and run migrations, effectively rebuilding the entire database |
|
94
94
|
| | `seed` | Load seed data into the database |
|
95
95
|
| | `status` | Check the status of the database and migrations |
|
96
96
|
| <code style="white-space: nowrap">bin/dbconsole</code> | None | Starts up a `psql` session to your database |
|
@@ -138,7 +138,7 @@ While you are free to set up mise or rbenv or whatever to run all this on your c
|
|
138
138
|
working is currently not supported nor encouraged. For now, Brut will focus on the Docker-based approach.
|
139
139
|
|
140
140
|
The primary reason is that it's a tightly controlled environment that is almost
|
141
|
-
entirely scriptable, but does not require devs to abandon their
|
141
|
+
entirely scriptable, but does not require devs to abandon their preferred editor.
|
142
142
|
Environment manager-based approaches tend to be more fussy and require documentation
|
143
143
|
to ensure they are set up.
|
144
144
|
|
@@ -174,7 +174,7 @@ exit Brut::CLI.app(
|
|
174
174
|
)
|
175
175
|
```
|
176
176
|
|
177
|
-
These files have
|
177
|
+
These files have some duplication, but should be relatively stable.
|
178
178
|
|
179
179
|
This means that Brut-provided CLIs *will* be updated when you update Brut. Compare this to the files in
|
180
180
|
`dx/` which are entire Bash scripts that will not be updated when Brut is updated.
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# Directory Structure
|
2
|
+
|
3
|
+
```
|
4
|
+
.
|
5
|
+
├── app
|
6
|
+
│ ├── config
|
7
|
+
│ │ └── i18n
|
8
|
+
│ │ └── en
|
9
|
+
│ ├── public
|
10
|
+
│ │ ├── css
|
11
|
+
│ │ ├── js
|
12
|
+
│ │ └── static
|
13
|
+
│ │ └── images
|
14
|
+
│ └── src
|
15
|
+
│ ├── back_end
|
16
|
+
│ │ ├── data_models
|
17
|
+
│ │ ├── db
|
18
|
+
│ │ ├── migrations
|
19
|
+
│ │ └── seed
|
20
|
+
│ ├── cli
|
21
|
+
│ └── front_end
|
22
|
+
│ ├── components
|
23
|
+
│ ├── css
|
24
|
+
│ ├── fonts
|
25
|
+
│ ├── forms
|
26
|
+
│ ├── handlers
|
27
|
+
│ ├── images
|
28
|
+
│ ├── js
|
29
|
+
│ ├── layouts
|
30
|
+
│ ├── pages
|
31
|
+
│ ├── route_hooks
|
32
|
+
│ ├── support
|
33
|
+
│ └── svgs
|
34
|
+
├── bin
|
35
|
+
├── deploy
|
36
|
+
├── dx
|
37
|
+
└── specs
|
38
|
+
├── back_end
|
39
|
+
│ ├── data_models
|
40
|
+
│ └── db
|
41
|
+
├── e2e
|
42
|
+
├── factories
|
43
|
+
│ └── db
|
44
|
+
└── front_end
|
45
|
+
├── components
|
46
|
+
├── handlers
|
47
|
+
├── js
|
48
|
+
├── pages
|
49
|
+
└── support
|
50
|
+
```
|
51
|
+
|
52
|
+
## Top Level
|
53
|
+
|
54
|
+
| Directory | Purpose |
|
55
|
+
|-----------|---------|
|
56
|
+
| `app/` | Contains all configuration and source code specific to your app |
|
57
|
+
| `bin/` | Contains tasks and other CLIs to do development of your app, such as `bin/test` |
|
58
|
+
| `dx/` | Contains scripts to manage your development environment |
|
59
|
+
| `specs/` | Contains all tests |
|
60
|
+
|
61
|
+
## Inside `app`/
|
62
|
+
|
63
|
+
| Directory | Purpose |
|
64
|
+
|-----------|---------|
|
65
|
+
| `bootstrap.rb` | A ruby file that sets up your app and ensures everything is `require`d in the right way. |
|
66
|
+
| `config/` | Configuration for your app, such as localizations and translations. Brut tries very hard to make sure there is no YAML in here at all. YAML is not good for you. |
|
67
|
+
| `public/` | Root of public assets served by the app. |
|
68
|
+
| `src/` | All source code for your app |
|
69
|
+
|
70
|
+
Inside `app/src`
|
71
|
+
|
72
|
+
| Directory | Purpose |
|
73
|
+
|-----------|---------|
|
74
|
+
| `app.rb` | The core of your app, mostly configuration, such as routes, hooks, middleware, etc. |
|
75
|
+
| `back_end/` | Back end classes for your app including database schema, DB models, seed data, and your domain logic |
|
76
|
+
| `cli/` | Any CLIs or tasks for your app |
|
77
|
+
| `front_end/` | The front-end for your app, including pages, components, forms, handlers, JavaScript, and assets |
|
78
|
+
|
79
|
+
Inside `app/src/back_end`
|
80
|
+
|
81
|
+
| Directory | Purpose |
|
82
|
+
|-----------|---------|
|
83
|
+
| `data_models/app_data_model.rb` | Base class for all DB model classes |
|
84
|
+
| `data_models/db` | DB model classes |
|
85
|
+
| `data_models/db.rb` | Namespace module for DB model classes |
|
86
|
+
| `data_models/migrations` | Database schema migrations |
|
87
|
+
| `data_models/seed` | Seed data used for local development |
|
88
|
+
|
89
|
+
Inside `app/src/front_end`
|
90
|
+
|
91
|
+
|Directory | Purpose |
|
92
|
+
|----------------|---------|
|
93
|
+
| `components/` | Component classes |
|
94
|
+
| `css/` | CSS, managed by esbuild and `bin/build-assets` |
|
95
|
+
| `fonts/` | Custom fonts, managed by esbuild and `bin/build-assets` |
|
96
|
+
| `forms/` | Form classes |
|
97
|
+
| `handlers/` | Handler classes |
|
98
|
+
| `images/` | Images, copied to `app/public` by `bin/build-assets` |
|
99
|
+
| `js/` | JavaScript, managed by esbuild and `bin/build-assets` |
|
100
|
+
| `layouts/` | Layout classes |
|
101
|
+
| `middlewares/` | Rack Middleware, if any |
|
102
|
+
| `pages/` | Page classes |
|
103
|
+
| `route_hooks/` | Route hooks, if any |
|
104
|
+
| `support/` | General support classes/junk drawer. |
|
105
|
+
| `svgs/` | SVGs you want to render inline |
|
106
|
+
|
107
|
+
## Inside `specs/`
|
108
|
+
|
109
|
+
`specs/` is intended to mirror `app/src`, but has a few extra directories:
|
110
|
+
|
111
|
+
|
112
|
+
|Directory | Purpose |
|
113
|
+
|----------------|---------|
|
114
|
+
| `specs/back_end` | tests for all back-end code, organized the same as `app/src/back_end` |
|
115
|
+
| `specs/back_end/data_models/db` | tests for all DB classes, if needed |
|
116
|
+
| `specs/e2e` | End-to-end tests, organized however you like |
|
117
|
+
| `specs/factories` | Root of all factories for FactoryBot. You can create subdirectories here for non-DB classes you may want to be able to create |
|
118
|
+
| `specs/factories/db` | Factories to create DB records |
|
119
|
+
| `specs/front_end` | tests for all front-end code, organized the same as `app/src/front_end` |
|
120
|
+
| `specs/js`| *JavaScript* code to test any autonomous custom elements you have created |
|
@@ -2,32 +2,35 @@
|
|
2
2
|
|
3
3
|
## Terminology
|
4
4
|
|
5
|
-
Brut attempts to use existing terminology where possible, particularly where that technology applies to the web platform. For example, there is not a thing called "CSS variables", rather the term is "custom properties".
|
5
|
+
Brut attempts to use existing terminology where possible, particularly where that technology applies to the web platform. For example, there is not a thing called "CSS variables", rather the term is "custom properties".
|
6
6
|
|
7
|
-
|
7
|
+
Here are some common exampels:
|
8
8
|
|
9
|
-
Further, Brut doesn't render HTML, it *generates* it. The browser renders the HTML for the website's visitor. One exception is Phlex, which uses the term *render* to mean "generate HTML to an internal buffer that will be delivered to the client later". Thus, you will need to call Phlex's `render` method from time to time, even though it is not rendering HTML but helping to generate it.
|
10
9
|
|
11
|
-
|
10
|
+
- HTML entities are **elements** or **tags**
|
11
|
+
- HTML elements have **attributes**.
|
12
|
+
- Forms don't have validations, they have **constraints** which are **violated** by invalid data.
|
13
|
+
- Ruby classes don't have constructors, they have **initializers**.
|
14
|
+
- Invoking behavior on a Ruby object is **calling a method**, not sending a message.
|
15
|
+
- Despite being in `specs/`, the files in there are **tests**, not specifications or
|
16
|
+
-specs".
|
17
|
+
- Tests that use a browser are **end to end** or **e2e** tests.
|
18
|
+
- HTML is not rendered, but **generated**. The browser renders the HTML sent to it by the server, along with the CSS.
|
19
|
+
- Your app or site doesn't have users, it has **visitors**.
|
12
20
|
|
13
21
|
## Structure of These Documents
|
14
22
|
|
15
23
|
Each page here documents on aspect of Brut, called a *module*, and these pages are organized along four sections:
|
16
24
|
|
17
|
-
* **Overview** -
|
18
|
-
|
19
|
-
* **
|
20
|
-
* **
|
21
|
-
|
22
|
-
understand the intention of the authors.
|
23
|
-
* **Technical Notes** - where appropriate, technical details about how or why the module works the way it does
|
24
|
-
are provided. This section should be marked with a date to allow you to understand the recency of the
|
25
|
-
information. It may not always be up to date, but this can help further clarify what is happening under the
|
26
|
-
covers and why.
|
25
|
+
* **Overview** - What the module does, how it works, and a brief example.
|
26
|
+
* **Testing** - How to test the code you write in this module.
|
27
|
+
* **Recommended Practices** - Opinions from the creators about how best to think about the code in this module.
|
28
|
+
* **Technical Notes** - details about the technical implementations that may be
|
29
|
+
useful as context.
|
27
30
|
|
28
31
|
## Names of the Library and Associated Modules
|
29
32
|
|
30
|
-
This framework is called "Brut" though may be called "BrutRB". It lives at `brutrb.com`.
|
33
|
+
This framework is called "Brut" though may be called "BrutRB" or "brut-rp". It lives at `brutrb.com`. Never use "brutRB", "brut_rb", etc.
|
31
34
|
|
32
35
|
The JavaScript library is called "BrutJS", but is `brut-js` in code or the filesystem. "Brut-JS" is wrong, as is `brut_js`.
|
33
36
|
|
data/brutrb.com/dx
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
../dx
|
@@ -55,7 +55,7 @@ RSpec.describe "logging into the website" do
|
|
55
55
|
end
|
56
56
|
```
|
57
57
|
|
58
|
-
`playwright-ruby-client` provides excellent documentation on how it has
|
58
|
+
`playwright-ruby-client` provides excellent documentation on how it has adapted Playwright's API for use
|
59
59
|
in Ruby.
|
60
60
|
|
61
61
|
### Test Setup
|
@@ -70,9 +70,15 @@ jobs, but rather assert the effects those jobs will have. Redis is flushed betwe
|
|
70
70
|
|
71
71
|
Inside your test, `t` is available to produce translations. You can also access all your page and handler classes, so you can (and should) use `.routing`, e.g. `DashboardPage.routing`, to generate or access routes for your app.
|
72
72
|
|
73
|
-
You can set `e2e_timeout` on any test to override the default amount of time Playwright will wait for a
|
73
|
+
You can set the `e2e_timeout` metadata on any test to override the default amount of time Playwright will wait for a
|
74
74
|
locator to locate an element. The default is 5 seconds.
|
75
|
-
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
RSpec.describe "Test for login", e2e_timeout: 10 do
|
78
|
+
# ...
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
76
82
|
You can also configure behavior with environment variables:
|
77
83
|
|
78
84
|
| Variable | Default | Purpose |
|
@@ -122,12 +128,9 @@ The main Playwright documentation encourages you to locate elements by "accessib
|
|
122
128
|
indirect ways of finding elements. In practice, this is error prone and tedious. Determining the
|
123
129
|
accessible name of an element is not always easy.
|
124
130
|
|
125
|
-
We recommend you assess your app's
|
126
|
-
end-to-end tests. Instead, locate elements with CSS selectors—this is what you'd use to debug your app so
|
127
|
-
it makes sense as a testing technique.
|
131
|
+
We recommend you assess your app's accessibility in another way than trying to do it while performing end-to-end tests. Instead, locate elements with CSS selectors—this is what you'd use to debug your app so it makes sense as a testing technique.
|
128
132
|
|
129
|
-
Insulating your end-to-end tests from markup changes does not produce significant
|
130
|
-
tests more difficult to write.
|
133
|
+
Insulating your end-to-end tests from markup changes does not produce significant savings and can make tests more difficult to write.
|
131
134
|
|
132
135
|
### Testing Must Inform your HTML
|
133
136
|
|
@@ -170,5 +173,4 @@ make sure it's still needed.
|
|
170
173
|
|
171
174
|
_Last Updated June 13, 2025_
|
172
175
|
|
173
|
-
The test server is run
|
174
|
-
running for an e2e test.
|
176
|
+
The test server is run via `bin/test-server`.
|