brut 0.0.27 → 0.0.28
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/Gemfile.lock +1 -1
- data/brutrb.com/.vitepress/config.mjs +1 -0
- data/brutrb.com/dev-environment.md +6 -5
- data/brutrb.com/doc-conventions.md +1 -1
- data/brutrb.com/getting-started.md +64 -28
- data/brutrb.com/instrumentation.md +12 -0
- data/brutrb.com/layouts.md +130 -0
- data/docs/404.html +2 -2
- data/docs/ai.html +5 -5
- 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 +38 -13
- data/docs/api/Brut/CLI/AppRunner.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Create.html +2 -2
- data/docs/api/Brut/CLI/Apps/DB/Drop.html +2 -2
- data/docs/api/Brut/CLI/Apps/DB/Migrate.html +9 -3
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +11 -11
- data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +2 -2
- data/docs/api/Brut/CLI/Apps/DB/Seed.html +2 -2
- data/docs/api/Brut/CLI/Apps/DB/Status.html +12 -12
- data/docs/api/Brut/CLI/Apps/DB.html +1 -1
- data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +270 -0
- data/docs/api/Brut/CLI/Apps/DeployBase.html +257 -0
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +585 -0
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +196 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +12 -10
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +8 -8
- data/docs/api/Brut/CLI/Apps/Test/JS.html +9 -9
- data/docs/api/Brut/CLI/Apps/Test/Run.html +18 -18
- data/docs/api/Brut/CLI/Apps/Test.html +1 -1
- data/docs/api/Brut/CLI/Apps.html +2 -2
- data/docs/api/Brut/CLI/Command.html +113 -28
- 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 +169 -38
- data/docs/api/Brut/CLI/InvalidOption.html +1 -1
- data/docs/api/Brut/CLI/Options.html +68 -19
- 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 +110 -29
- data/docs/api/Brut/Framework/Error.html +1 -1
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +89 -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 +31 -8
- 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 +36 -26
- data/docs/api/Brut/FrontEnd/Component.html +7 -7
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +37 -29
- 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 +20 -117
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +25 -23
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +73 -380
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +22 -138
- 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 +23 -2
- 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 +1 -1
- data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +24 -68
- data/docs/api/Brut/FrontEnd/Forms/Input.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +4 -4
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +72 -22
- 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 +6 -2
- 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 +20 -20
- 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 +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +142 -0
- data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +142 -0
- data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +155 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +55 -25
- data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +149 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +46 -19
- data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +149 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +149 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +165 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +158 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +156 -0
- data/docs/api/Brut/SpecSupport/Matchers.html +2 -2
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +24 -24
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +55 -20
- 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 +5 -1
- data/docs/api/Sequel/Extensions/BrutMigrations.html +36 -28
- 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/SpecSupport/Matchers/BeABug.html +143 -0
- data/docs/api/_index.html +92 -1
- data/docs/api/class_list.html +1 -1
- data/docs/api/file.README.html +1 -1
- data/docs/api/index.html +1 -1
- data/docs/api/method_list.html +392 -336
- data/docs/api/top-level-namespace.html +1 -1
- data/docs/assets/{ai.md.tZrjP9im.js → ai.md._6HCDL6d.js} +1 -1
- data/docs/assets/ai.md._6HCDL6d.lean.js +1 -0
- data/docs/assets/{app.D_yaTITQ.js → app.BX81XO4N.js} +1 -1
- data/docs/assets/chunks/@localSearchIndexroot.CoYzciVi.js +1 -0
- data/docs/assets/chunks/{VPLocalSearchBox.B2-ZzyTY.js → VPLocalSearchBox.gABXcTWp.js} +1 -1
- data/docs/assets/chunks/{theme.CfGFVRvE.js → theme.DwUXXAL3.js} +2 -2
- data/docs/assets/{components.md.eCttGlN-.js → components.md.CRUMdRoN.js} +1 -1
- data/docs/assets/{configuration.md.BRriU0cL.js → configuration.md.BGHl8oRC.js} +1 -1
- data/docs/assets/{dev-environment.md.BNc8AYiK.js → dev-environment.md.GZv6xvi9.js} +1 -1
- data/docs/assets/doc-conventions.md.-kN3Xo5C.js +1 -0
- data/docs/assets/{doc-conventions.md.DCfRXXi-.lean.js → doc-conventions.md.-kN3Xo5C.lean.js} +1 -1
- data/docs/assets/{forms.md.CBTYQ_Cz.js → forms.md.B-koVgyw.js} +23 -23
- data/docs/assets/{forms.md.CBTYQ_Cz.lean.js → forms.md.B-koVgyw.lean.js} +1 -1
- data/docs/assets/getting-started.md.Ciz82L0m.js +25 -0
- data/docs/assets/getting-started.md.Ciz82L0m.lean.js +1 -0
- data/docs/assets/{instrumentation.md.CL6ax7nT.js → instrumentation.md.a9Pjps4P.js} +2 -2
- data/docs/assets/{instrumentation.md.CL6ax7nT.lean.js → instrumentation.md.a9Pjps4P.lean.js} +1 -1
- data/docs/assets/layouts.md.cPnh3NId.js +51 -0
- data/docs/assets/layouts.md.cPnh3NId.lean.js +1 -0
- data/docs/assets/lsp.md.Bsu-f6VU.js +1 -0
- data/docs/assets/lsp.md.Bsu-f6VU.lean.js +1 -0
- data/docs/assets/{overview.md.CDalkuxV.js → overview.md.C5wlBcR5.js} +3 -3
- data/docs/assets/recipes_authentication.md.CAsXf7hk.js +1 -0
- data/docs/assets/recipes_authentication.md.CAsXf7hk.lean.js +1 -0
- data/docs/assets.html +4 -4
- 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 +4 -4
- data/docs/business-logic.html +4 -4
- data/docs/cli.html +4 -4
- data/docs/components.html +6 -6
- data/docs/configuration.html +5 -5
- data/docs/css.html +4 -4
- data/docs/custom-element-tests.html +4 -4
- data/docs/database-access.html +4 -4
- data/docs/database-schema.html +4 -4
- data/docs/deployment.html +4 -4
- data/docs/dev-environment.html +5 -5
- data/docs/doc-conventions.html +5 -5
- data/docs/end-to-end-tests.html +4 -4
- data/docs/flash-and-session.html +4 -4
- data/docs/forms.html +27 -27
- data/docs/getting-started.html +29 -6
- data/docs/handlers.html +4 -4
- data/docs/hashmap.json +1 -1
- data/docs/hooks.html +4 -4
- data/docs/i18n.html +4 -4
- data/docs/index.html +3 -3
- data/docs/instrumentation.html +5 -5
- data/docs/javascript.html +4 -4
- data/docs/jobs.html +4 -4
- data/docs/keyword-injection.html +4 -4
- data/docs/layouts.html +74 -0
- data/docs/lsp.html +24 -0
- data/docs/markdown-examples.html +4 -4
- data/docs/middleware.html +4 -4
- data/docs/not-released.html +4 -4
- data/docs/overview.html +8 -8
- data/docs/pages.html +5 -5
- data/docs/recipes/authentication.html +24 -0
- data/docs/routes.html +4 -4
- data/docs/security.html +4 -4
- data/docs/seed-data.html +4 -4
- data/docs/space-time-continuum.html +4 -4
- data/docs/tutorial.html +4 -4
- data/docs/unit-tests.html +4 -4
- data/lib/brut/cli/app.rb +7 -2
- data/lib/brut/cli/apps/deploy_base.rb +86 -0
- data/lib/brut/cli/apps/heroku_container_based_deploy.rb +194 -0
- data/lib/brut/cli/command.rb +7 -13
- data/lib/brut/cli/executor.rb +31 -5
- data/lib/brut/cli/options.rb +4 -0
- data/lib/brut/cli.rb +4 -3
- data/lib/brut/framework/container.rb +25 -7
- data/lib/brut/framework/errors/abstract_method.rb +7 -0
- data/lib/brut/framework/errors.rb +4 -2
- data/lib/brut/front_end/middlewares/reload_app.rb +2 -0
- data/lib/brut/spec_support/rspec_setup.rb +42 -2
- data/lib/brut/version.rb +1 -1
- data/lib/sequel/extensions/brut_instrumentation.rb +4 -0
- metadata +49 -23
- data/docs/assets/ai.md.tZrjP9im.lean.js +0 -1
- data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +0 -1
- data/docs/assets/doc-conventions.md.DCfRXXi-.js +0 -1
- data/docs/assets/getting-started.md.Bz2s1Vjb.js +0 -2
- data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +0 -1
- /data/docs/assets/{components.md.eCttGlN-.lean.js → components.md.CRUMdRoN.lean.js} +0 -0
- /data/docs/assets/{configuration.md.BRriU0cL.lean.js → configuration.md.BGHl8oRC.lean.js} +0 -0
- /data/docs/assets/{dev-environment.md.BNc8AYiK.lean.js → dev-environment.md.GZv6xvi9.lean.js} +0 -0
- /data/docs/assets/{overview.md.CDalkuxV.lean.js → overview.md.C5wlBcR5.lean.js} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0212844b1dd1d69682b3b4ec4ab2a9917087b3d931916e39d9ccf0239d6a2f9c'
|
4
|
+
data.tar.gz: 871a018ce32f9d1f556eef7950570c8a4dd072736be733e6a7a925e14a8c968c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb49d9e87e53893962c86e2a26ca54f8c82cc9a8fbbeac5e12e1d7aaa4b47cbb13de6ab9429779422a6bc075dd3f4499bf6b18accb7c7ff87943eb32afda0342
|
7
|
+
data.tar.gz: c8f56cb073d99b1b3f304afe2daf3839b6b2b39ebe492bde05f00037ebf448459049560d3b32707450b65e32916366ea766d57598752026d0c025d1db63d7fe7
|
data/Gemfile.lock
CHANGED
@@ -39,6 +39,7 @@ export default defineConfig({
|
|
39
39
|
items: [
|
40
40
|
{ text: "Routes", link: "/routes" },
|
41
41
|
{ text: "Pages", link: "/pages" },
|
42
|
+
{ text: "Layouts", link: "/layouts" },
|
42
43
|
{ text: "Forms", link: "/forms" },
|
43
44
|
{ text: "Handlers and Actions", link: "/handlers" },
|
44
45
|
{ text: "Components", link: "/components" },
|
@@ -137,16 +137,17 @@ that the versions of these command line apps provided when you set up your app a
|
|
137
137
|
While you are free to set up mise or rbenv or whatever to run all this on your computer, this way of
|
138
138
|
working is currently not supported nor encouraged. For now, Brut will focus on the Docker-based approach.
|
139
139
|
|
140
|
-
|
141
|
-
|
140
|
+
The primary reason is that it's a tightly controlled environment that is almost
|
141
|
+
entirely scriptable, but does not require devs to abandon their preffered editor.
|
142
|
+
Environment manager-based approaches tend to be more fussy and require documentation
|
143
|
+
to ensure they are set up.
|
142
144
|
|
143
|
-
|
145
|
+
Keep in mind a few things when adding your own automation:
|
144
146
|
|
145
147
|
* The *Foundational Core* is bootstrapped in a degenerate environment without reliable tools beyond Bash.
|
146
148
|
This is why it's almost entirely written in Bash, since it's available everywhere and relatively stable.
|
147
149
|
* The *Workspace* **can and should** rely on the languages and third party modules that are part of your
|
148
|
-
app.
|
149
|
-
particular Ruby gem having been installed.
|
150
|
+
app. The only exception is `bin/setup`, since it installs third party modules. As such, it should work entirely based on Ruby and its standard library.
|
150
151
|
|
151
152
|
## Technical Notes
|
152
153
|
|
@@ -6,7 +6,7 @@ Brut attempts to use existing terminology where possible, particularly where tha
|
|
6
6
|
|
7
7
|
When speaking about Ruby, we prefer the term *initializer* over constructor, *parameters* over arguments, and *methods* over messages. We also prefer *tests* over specs, however test files *are* located in `specs/` and named `*.spec.rb` to be consistent with RSpec's nomenclature. We prefer *end-to-end* or *e2e* tests instead of browser tests or request specs.
|
8
8
|
|
9
|
-
Further, Brut doesn't render HTML, it *generates* it. The browser renders the HTML for the website's visitor.
|
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
10
|
|
11
11
|
Lastly, the documentation tries to talk about the person accessing a website as a "vistor" not a "user". Though the "user" nomenclature is near-ossified in software development, we feel "visitor" is more apt.
|
12
12
|
|
@@ -1,21 +1,26 @@
|
|
1
1
|
# Getting Started
|
2
2
|
|
3
3
|
Brut is developed alongside a separate gem called `mkbrut`, which allows you to
|
4
|
-
create a new Brut app. It will set up
|
4
|
+
create a new Brut app. It will set up your dev environment as well.
|
5
5
|
|
6
6
|
## Get `mkbrut`
|
7
7
|
|
8
|
-
|
9
|
-
RubyGems:
|
8
|
+
The simplest way to use `mkbrut` is to use an existing [Docker image](https://hub.docker.com/repository/docker/thirdtank/mkbrut/general). You don't have to install or configure Ruby:
|
10
9
|
|
11
10
|
```
|
12
|
-
|
11
|
+
docker run \
|
12
|
+
-v "$PWD":"$PWD" \
|
13
|
+
-w "$PWD" \
|
14
|
+
-it \
|
15
|
+
thirdtank/mkbrut \
|
16
|
+
mkbrut my-new-app
|
13
17
|
```
|
14
18
|
|
15
|
-
If
|
19
|
+
If you already have Ruby 3.4 installed, you can install `mkbrut` directly:
|
16
20
|
|
17
21
|
```
|
18
|
-
|
22
|
+
> gem install mkbrut
|
23
|
+
> mkbrut my-new-app
|
19
24
|
```
|
20
25
|
|
21
26
|
## Init Your App
|
@@ -23,16 +28,44 @@ docker pull XXXX
|
|
23
28
|
A Brut app just needs a name, which will be used to derive a few more useful values.
|
24
29
|
For now:
|
25
30
|
|
31
|
+
::: code-group
|
32
|
+
|
33
|
+
``` [Docker-based]
|
34
|
+
docker run \
|
35
|
+
-v "$PWD":"$PWD" \
|
36
|
+
-w "$PWD" \
|
37
|
+
-it \
|
38
|
+
thirdtank/mkbrut \
|
39
|
+
mkbrut my-new-app
|
26
40
|
```
|
41
|
+
|
42
|
+
``` [RubyGems-based]
|
27
43
|
mkbrut my-new-app
|
28
44
|
```
|
29
45
|
|
30
|
-
|
46
|
+
:::
|
47
|
+
|
48
|
+
This will create your new app, along with some demo routes, components, handlers, and tests. If this is your first time using Brut, we recommend you examine these demo components.
|
49
|
+
|
50
|
+
To create your app without the demo components:
|
31
51
|
|
52
|
+
::: code-group
|
53
|
+
|
54
|
+
``` [Docker-based]
|
55
|
+
docker run \
|
56
|
+
-v "$PWD":"$PWD" \
|
57
|
+
-w "$PWD" \
|
58
|
+
-it \
|
59
|
+
thirdtank/mkbrut \
|
60
|
+
mkbrut my-new-app --no-demo
|
32
61
|
```
|
33
|
-
|
62
|
+
|
63
|
+
``` [RubyGems-based]
|
64
|
+
mkbrut my-new-app --no-demo
|
34
65
|
```
|
35
66
|
|
67
|
+
:::
|
68
|
+
|
36
69
|
## Start Your Dev Environment
|
37
70
|
|
38
71
|
Brut includes a dev environment based on Docker. It uses Docker compose to run a
|
@@ -50,49 +83,52 @@ Docker container for local observability via OpenTelemetry.
|
|
50
83
|
```
|
51
84
|
> dx/start
|
52
85
|
```
|
53
|
-
4.
|
86
|
+
4. Now, "log in" to the container where your app and its tests will run:
|
54
87
|
|
55
88
|
```
|
56
|
-
> dx/exec
|
89
|
+
> dx/exec login
|
57
90
|
```
|
58
91
|
|
59
|
-
|
92
|
+
5. Set everything up:
|
60
93
|
|
61
94
|
```
|
62
|
-
> dx/exec bash
|
63
95
|
inside-container> bin/setup
|
64
96
|
```
|
65
97
|
|
66
|
-
Now, you're ready to go
|
98
|
+
Now, you're ready to go. See [Dev Environemnt](/dev-environment) for details on how
|
99
|
+
this all works.
|
67
100
|
|
68
101
|
## Run the App
|
69
102
|
|
70
103
|
```
|
71
|
-
>
|
72
|
-
```
|
73
|
-
|
74
|
-
OR
|
75
|
-
|
76
|
-
```
|
77
|
-
> dx/exec bash
|
78
|
-
> bin/dev
|
104
|
+
inside-container> bin/dev
|
79
105
|
```
|
80
106
|
|
81
107
|
You can now visit your app at `localhost:6502`
|
82
108
|
|
83
|
-
|
109
|
+
You can make changes and see them when you reload. Open up `app/src/front_end/pages/home_page.rb` *in your editor running on your computer* and change the `h1` to look like so:
|
84
110
|
|
85
|
-
|
111
|
+
```ruby {6}
|
112
|
+
class HomePage < AppPage
|
113
|
+
def page_template
|
114
|
+
div(class: "flex flex-column items-center justify-center h-80vh") do
|
115
|
+
img(src: "/static/images/icon.png", class: "h-50")
|
116
|
+
h1(class: "ff-sans ma-0 lh-title f-5") do
|
117
|
+
"Welcome to My New App!"
|
118
|
+
end
|
86
119
|
|
120
|
+
# ...
|
87
121
|
```
|
88
|
-
> dx/exec bin/ci
|
89
|
-
```
|
90
122
|
|
91
|
-
|
123
|
+
When you reload your browser, you'll see your change
|
124
|
+
|
125
|
+
## Run the Tests
|
126
|
+
|
127
|
+
There are a few tests you can run, as well as some checks that you aren't using
|
128
|
+
RubyGems with security vulnerabilities. Run it all now with `bin/ci`:
|
92
129
|
|
93
130
|
```
|
94
|
-
>
|
95
|
-
> bin/ci
|
131
|
+
inside-container> bin/dev
|
96
132
|
```
|
97
133
|
|
98
134
|
## Now Build The Rest of Your App 🦉
|
@@ -66,6 +66,17 @@ Here is a non-exhaustive list of what Brut automatically instruments:
|
|
66
66
|
* Ignored parameters on all form submissions
|
67
67
|
* How long reloading takes in development
|
68
68
|
* CSP reporting results
|
69
|
+
* SQL Statements
|
70
|
+
|
71
|
+
> [!WARNING]
|
72
|
+
> `Sequel::Extensions::BrutInstrumentation` sets up telemetry for
|
73
|
+
> Sequel, and it does it in a relatively simplistic way. The result
|
74
|
+
> is that *all* SQL statements are part of the telemetry, including
|
75
|
+
> the actual values inserted or used in `WHERE` clauses.
|
76
|
+
> While you should not be putting sensitive data into your database,
|
77
|
+
> be warned that this is happening. There are plans to improve this
|
78
|
+
> to be more flexible and reduce the Schance of sensitive data
|
79
|
+
> being sent in traces.
|
69
80
|
|
70
81
|
### Adding Your Own Instrumentation
|
71
82
|
|
@@ -175,6 +186,7 @@ specific issues.
|
|
175
186
|
|
176
187
|
_Last Updated June 12, 2025_
|
177
188
|
|
189
|
+
|
178
190
|
Brut does not have plans to support non-OTel instrumentation, nor does it have plans to provide hooks to
|
179
191
|
use proprietary formats. This could change of course.
|
180
192
|
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# Layouts
|
2
|
+
|
3
|
+
Brut supports *layouts*, which are a way to centralizing common HTML amongst many
|
4
|
+
different pages. Conceptually, they are the same as a Rails layout. Technically, they are a Phlex component designed to render a page from a `yield` block.
|
5
|
+
|
6
|
+
## Overview
|
7
|
+
|
8
|
+
Your app should include `app/src/front_end/layouts/default_layout.rb`. The name
|
9
|
+
"default" is special, in that all pages will use this layout by default.
|
10
|
+
|
11
|
+
Since a layout is a Phlex component, its HTML is generated from `view_template`, and
|
12
|
+
it is expected to have exactly one `yield`, where the page's content will be
|
13
|
+
inserted.
|
14
|
+
|
15
|
+
```ruby {33}
|
16
|
+
class DefaultLayout < Brut::FrontEnd::Layout
|
17
|
+
include Brut::FrontEnd::Components
|
18
|
+
|
19
|
+
def initialize(page_name:)
|
20
|
+
@page_name = page_name
|
21
|
+
end
|
22
|
+
|
23
|
+
def view_template
|
24
|
+
doctype
|
25
|
+
html(lang: "en") do
|
26
|
+
head do
|
27
|
+
meta(charset: "utf-8")
|
28
|
+
meta(content: "width=device-width,initial-scale=1", name:"viewport")
|
29
|
+
meta(content: "website", property:"og:type")
|
30
|
+
link(rel: "manifest", href: "/static/manifest.json")
|
31
|
+
link(rel: "preload", as: "style", href: asset_path("/css/styles.css"))
|
32
|
+
link(rel: "stylesheet", href: asset_path("/css/styles.css"))
|
33
|
+
script(defer: true, src: asset_path("/js/app.js"))
|
34
|
+
title { app_name }
|
35
|
+
PageIdentifier(@page_name)
|
36
|
+
I18nTranslations("cv.fe")
|
37
|
+
I18nTranslations("cv.this_field")
|
38
|
+
Traceparent()
|
39
|
+
render(
|
40
|
+
Brut::FrontEnd::RequestContext.inject(
|
41
|
+
Brut::FrontEnd::Components::LocaleDetection
|
42
|
+
)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
body do
|
46
|
+
brut_tracing url: "/__brut/instrumentation", show_warnings: true
|
47
|
+
main class: @page_name do
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
### Maintaining Layouts
|
57
|
+
|
58
|
+
You are free to manage this how you like, however a few components inside the
|
59
|
+
`<head>` and `<body>` that are important to keep:
|
60
|
+
|
61
|
+
* `Brut::FrontEnd::Components::PageIdentifier` includes a `<meta>` tag with the page's name in it, which is handy for managing your end-to-end tests.
|
62
|
+
* `Brut::FrontEnd::Components::I18nTranslations` includes translatsion for common client-side constraint violations. See [Forms](/forms), [I18n](/i18n), and [JavaScript](/javascript) for more details on how this is used.
|
63
|
+
* `Brut::FrontEnd::Components::Traceparent` ensures that the OpenTelemetry *traceparent* is available so when client-side telemetry is reported back to the server, it can be connected to the request that initiated it.
|
64
|
+
* The `<brut-tracing>` element collects the client-side telemetry and sends it back
|
65
|
+
to the server.
|
66
|
+
|
67
|
+
### Creating Alternate Layouts
|
68
|
+
|
69
|
+
The way each page knows to use `DefaultLayout` is due to the `layout` method of
|
70
|
+
`Brut::FrontEnd::Page`, which returns `"default"`. The return value of `layout` is
|
71
|
+
used to figure out the name of the layout class.
|
72
|
+
|
73
|
+
You can set up your own by overriding `layout`:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
class MyOtherPage < AppPage
|
77
|
+
def layout = "other_design"
|
78
|
+
|
79
|
+
# ...
|
80
|
+
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
Brut will expect the class `OtherDesignLayout` to exist and provide HTML. Based on
|
85
|
+
Zeitwerk's conventions, that class should be in
|
86
|
+
`app/src/front_end/layouts/other_design_layout.rb`.
|
87
|
+
|
88
|
+
### No Layout
|
89
|
+
|
90
|
+
If you don't want a layout, you are encouraged to creat a blank layout, for example:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
class BlankLayout < Brut::FrontEnd::Layout
|
94
|
+
def view_template
|
95
|
+
yield
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# use like so:
|
100
|
+
|
101
|
+
def layout = "blank"
|
102
|
+
```
|
103
|
+
|
104
|
+
## Testing
|
105
|
+
|
106
|
+
You generally don't test a layout, aside from end-to-end tests. If your layout
|
107
|
+
needs complex logic, you are encouraged to extract that to a
|
108
|
+
[component](/components) and test that instead.
|
109
|
+
|
110
|
+
## Recommended Practices
|
111
|
+
|
112
|
+
Keep your layouts as simple as you can.
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
## Technical Notes
|
117
|
+
|
118
|
+
> [!IMPORTANT]
|
119
|
+
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
120
|
+
> internals, the source code is always more correct.
|
121
|
+
|
122
|
+
_Last Updated July 1, 2025_
|
123
|
+
|
124
|
+
Layouts work due to the implementation of the method `view_template` in `Brut::FrontEnd::Page`. This is why a page class must provide `page_template` instead.
|
125
|
+
|
126
|
+
While you could override `view_template` in your page to provide a "blank layout",
|
127
|
+
this is discouraged, as the use of `view_template` should be considered a
|
128
|
+
private implementation detail.
|
129
|
+
|
130
|
+
|
data/docs/404.html
CHANGED
@@ -9,13 +9,13 @@
|
|
9
9
|
<link rel="preload stylesheet" href="/assets/style.D73IYGCX.css" as="style">
|
10
10
|
<link rel="preload stylesheet" href="/vp-icons.css" as="style">
|
11
11
|
|
12
|
-
<script type="module" src="/assets/app.
|
12
|
+
<script type="module" src="/assets/app.BX81XO4N.js"></script>
|
13
13
|
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
|
14
14
|
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
|
15
15
|
</head>
|
16
16
|
<body>
|
17
17
|
<div id="app"></div>
|
18
|
-
<script>window.__VP_HASH_MAP__=JSON.parse("{\"ai.md\":\"
|
18
|
+
<script>window.__VP_HASH_MAP__=JSON.parse("{\"ai.md\":\"_6HCDL6d\",\"assets.md\":\"D3wunzLx\",\"brut-js.md\":\"o2DAO2s2\",\"business-logic.md\":\"BY4hGy0m\",\"cli.md\":\"RmeA2b0i\",\"components.md\":\"CRUMdRoN\",\"configuration.md\":\"BGHl8oRC\",\"css.md\":\"DJgj2clw\",\"custom-element-tests.md\":\"BrYJQEl3\",\"database-access.md\":\"C7l-Vuvb\",\"database-schema.md\":\"BUjR0VS1\",\"deployment.md\":\"Dbka4OTr\",\"dev-environment.md\":\"GZv6xvi9\",\"doc-conventions.md\":\"-kN3Xo5C\",\"end-to-end-tests.md\":\"yfQHC0b5\",\"flash-and-session.md\":\"BXY8RvT0\",\"forms.md\":\"B-koVgyw\",\"getting-started.md\":\"Ciz82L0m\",\"handlers.md\":\"089DVD3v\",\"hooks.md\":\"C4-moMny\",\"i18n.md\":\"Do9i1qWl\",\"index.md\":\"B28EwVpq\",\"instrumentation.md\":\"a9Pjps4P\",\"javascript.md\":\"GWbhRS51\",\"jobs.md\":\"S-2amAYp\",\"keyword-injection.md\":\"Dt2tKREs\",\"layouts.md\":\"cPnh3NId\",\"lsp.md\":\"Bsu-f6VU\",\"markdown-examples.md\":\"CCFEQO44\",\"middleware.md\":\"Czz_UlJN\",\"not-released.md\":\"BBy28McC\",\"overview.md\":\"C5wlBcR5\",\"pages.md\":\"BE3kfOc5\",\"recipes_authentication.md\":\"CAsXf7hk\",\"routes.md\":\"BMM7peut\",\"security.md\":\"C668yXCi\",\"seed-data.md\":\"BvFZlqIk\",\"space-time-continuum.md\":\"KPUIKysQ\",\"tutorial.md\":\"BnoGjrdK\",\"unit-tests.md\":\"DUGrnLj5\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Brut RB\",\"description\":\"Documentation for the Brut.RB web framework.\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"search\":{\"provider\":\"local\"},\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Overview\",\"link\":\"/overview\"},{\"text\":\"Brut API\",\"link\":\"/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutCSS\",\"link\":\"/brut-css/index.html\",\"target\":\"_self\"}],\"outline\":[2,3],\"sidebar\":[{\"text\":\"Overview\",\"collapsed\":false,\"items\":[{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Concepts\",\"link\":\"/overview\"},{\"text\":\"Documentation Conventions\",\"link\":\"/doc-conventions\"},{\"text\":\"Tutorial\",\"link\":\"/tutorial\"},{\"text\":\"Dev Environment\",\"link\":\"/dev-environment\"},{\"text\":\"AI Declaration\",\"link\":\"/ai\"}]},{\"text\":\"Front-End\",\"collapsed\":false,\"items\":[{\"text\":\"Routes\",\"link\":\"/routes\"},{\"text\":\"Pages\",\"link\":\"/pages\"},{\"text\":\"Layouts\",\"link\":\"/layouts\"},{\"text\":\"Forms\",\"link\":\"/forms\"},{\"text\":\"Handlers and Actions\",\"link\":\"/handlers\"},{\"text\":\"Components\",\"link\":\"/components\"},{\"text\":\"Flash and Session\",\"link\":\"/flash-and-session\"},{\"text\":\"Space/Time Continuum\",\"link\":\"/space-time-continuum\"},{\"text\":\"JavaScript\",\"link\":\"/javascript\"},{\"text\":\"CSS\",\"link\":\"/css\"},{\"text\":\"Assets\",\"link\":\"/assets\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js\"}]},{\"text\":\"Back-End\",\"collapsed\":false,\"items\":[{\"text\":\"Database Schema\",\"link\":\"/database-schema\"},{\"text\":\"Database Access\",\"link\":\"/database-access\"},{\"text\":\"Seed Data\",\"link\":\"/seed-data\"},{\"text\":\"Jobs\",\"link\":\"/jobs\"},{\"text\":\"Business Logic\",\"link\":\"/business-logic\"}]},{\"text\":\"Framework\",\"collapsed\":false,\"items\":[{\"text\":\"Configuration\",\"link\":\"/configuration\"},{\"text\":\"Keyword Injection\",\"link\":\"/keyword-injection\"},{\"text\":\"I18n\",\"link\":\"/i18n\"},{\"text\":\"CLI / Tasks\",\"link\":\"/cli\"},{\"text\":\"Deployment\",\"link\":\"/deployment\"}]},{\"text\":\"Testing\",\"collapsed\":false,\"items\":[{\"text\":\"Unit Tests\",\"link\":\"/unit-tests\"},{\"text\":\"End-to-End Tests\",\"link\":\"/end-to-end-tests\"},{\"text\":\"Testing Custom Elements\",\"link\":\"/custom-element-tests\"}]},{\"text\":\"Advanced Topics\",\"collapsed\":true,\"items\":[{\"text\":\"Route Hooks\",\"link\":\"/hooks\"},{\"text\":\"Middleware\",\"link\":\"/middleware\"},{\"text\":\"Instrumentation\",\"link\":\"/instrumentation\"},{\"text\":\"Security\",\"link\":\"/security\"},{\"text\":\"LSP Support\",\"link\":\"/lsp\"}]},{\"text\":\"Recipes\",\"collapsed\":true,\"items\":[{\"text\":\"Authentication\",\"link\":\"/recipes/authentication\"},{\"text\":\"Form Validations\",\"link\":\"/recipes/form-validations\"},{\"text\":\"Database Migrations\",\"link\":\"/recipes/database-migrations\"},{\"text\":\"Ajax Form Submission\",\"link\":\"/recipes/ajax-form\"},{\"text\":\"Custom Telemetry\",\"link\":\"/recipes/telemetry\"},{\"text\":\"CLI App/Task\",\"link\":\"/recipes/cli-app\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/thirdtank/brut\"}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
|
19
19
|
|
20
20
|
</body>
|
21
21
|
</html>
|