brut 0.0.29 → 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/Gemfile.lock +1 -1
- data/README.md +23 -2
- data/assets/LogoStop.pxd +0 -0
- data/assets/MetroLogo.graffle +0 -0
- data/assets/SocialImage.png +0 -0
- data/assets/SocialImage.pxd +0 -0
- data/brutrb.com/.vitepress/config.mjs +45 -8
- data/brutrb.com/.vitepress/theme/style.css +6 -5
- 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/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/LogoStop.png +0 -0
- 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 +30 -372
- 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/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 +1 -1
- data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/Input.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +135 -20
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +135 -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 +1 -1
- data/docs/api/file.README.html +22 -3
- data/docs/api/index.html +22 -3
- data/docs/api/method_list.html +16 -0
- data/docs/api/top-level-namespace.html +1 -1
- data/docs/assets/LogoStop.Gb3tDhL1.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.BhrfSt68.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.Dpot_2H4.js → VPLocalSearchBox.DtgDfde2.js} +1 -1
- data/docs/assets/chunks/{theme.N2SNVLgU.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.LG-zIBww.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/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.Dj0qtZI2.js → getting-started.md.C93e0odB.js} +5 -5
- data/docs/assets/{getting-started.md.Dj0qtZI2.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.CuBB-BdM.js → index.md.CAMqGBJE.js} +1 -1
- data/docs/assets/{index.md.CuBB-BdM.lean.js → index.md.CAMqGBJE.lean.js} +1 -1
- 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.B2o1L9eN.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 +10 -5
- 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/lib/brut/front_end/form.rb +8 -8
- 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/version.rb +1 -1
- 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
- metadata +117 -75
- data/brutrb.com/public/images/logo-300.png +0 -0
- data/brutrb.com/public/images/logo.png +0 -0
- data/docs/assets/LogoStop.X8x-4riz.png +0 -0
- data/docs/assets/ai.md._6HCDL6d.lean.js +0 -1
- data/docs/assets/chunks/@localSearchIndexroot.CeRAdP1K.js +0 -1
- data/docs/assets/components.md.CRUMdRoN.js +0 -104
- 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/keyword-injection.md.Dt2tKREs.js +0 -25
- data/docs/assets/layouts.md.cPnh3NId.lean.js +0 -1
- data/docs/assets/overview.md.DVKRM8zl.js +0 -133
- data/docs/assets/overview.md.DVKRM8zl.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.LG-zIBww.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/configuration.md
CHANGED
@@ -92,7 +92,7 @@ end
|
|
92
92
|
|
93
93
|
### Type and Name Enforcement
|
94
94
|
|
95
|
-
You'll note that `store
|
95
|
+
You'll note that `store` accepts a class parameter. This is mostly used for documentation, with two exceptions:
|
96
96
|
|
97
97
|
* If the type is `Pathname` (which is what is used by `store_ensured_path` and `store_required_path`), the configuration parameter name *must* end in `_file` or `_dir`.
|
98
98
|
* If the type is `"boolean"` or `:boolean`, the configuration parameter name *must* end in a question mark. In this case, the value itself is coerced into `true` or `false`.
|
@@ -103,8 +103,7 @@ Brut may add more constraints or conversions over time.
|
|
103
103
|
|
104
104
|
By default, Brut configuration values cannot be overridden and they cannot be `nil`. When calling `store`, `allow_app_override: true` and `allow_nil: true`, can be passed to change this behavior.
|
105
105
|
|
106
|
-
In [Flash and Session](/flash-and-session), we discussed
|
107
|
-
This is possible due to how Brut defines the configuration parameter `flash_class`:
|
106
|
+
In [Flash and Session](/flash-and-session), we discussed that you can set your own class for the flash. This is possible due to how Brut defines the configuration parameter `flash_class`:
|
108
107
|
|
109
108
|
```ruby {6}
|
110
109
|
Brut.container.store(
|
@@ -126,7 +125,7 @@ Calling `override` for parameters where `allow_app_override` is not true results
|
|
126
125
|
`store` on a previously `store`-d parameter results in an error.
|
127
126
|
|
128
127
|
The idea is to make it extremely clear what values are being set and overridden, and to avoid setting values that
|
129
|
-
don't exist.
|
128
|
+
don't exist or aren't relevant.
|
130
129
|
|
131
130
|
Some values can be `nil`. Generally, `nil` is a pain and will cause you great hardship. On occasion, it's
|
132
131
|
needed. For example, [external IDs](/database-schema#external-ids) only work if the app provides an app-wide
|
data/brutrb.com/css.md
CHANGED
@@ -42,7 +42,7 @@ First, `package.json` (in your app's root) would include `"foobar-css"`:
|
|
42
42
|
|
43
43
|
Next, `app/src/front_end/css/index.css` would import both `"foobar-css"` and `"pages/HomePage.css"`.
|
44
44
|
|
45
|
-
```
|
45
|
+
```css
|
46
46
|
@import "foobar-css/everything.css";
|
47
47
|
@import "pages/HomePage.css";
|
48
48
|
```
|
@@ -73,7 +73,7 @@ To use `foobar-thin.css`, you'd write this `@import` directive:
|
|
73
73
|
|
74
74
|
## Using Brut-CSS
|
75
75
|
|
76
|
-
By default, Brut includes a lightweight functional CSS library called
|
76
|
+
By default, Brut includes a lightweight functional CSS library called [BrutCSS](/brut-css/index.html). It provides a basic design system and single-purpose classes to allow you to quickly prototype or build UIs. It is similar to TailwindCSS but far far smaller and simpler (and less powered).
|
77
77
|
|
78
78
|
It is included so you have something to start with. You can use it by using its various classes like `bg-green-300` and `m-4`, or you can use its provided custom properties like `var(--green-300)` and `var(--sp-4)`.
|
79
79
|
|
@@ -37,8 +37,7 @@ as well, such as `assert`.
|
|
37
37
|
The idea is that you use the browser APIs to examine the DOM and assert the behavior of the custom element
|
38
38
|
(as opposed to interacting with the custom element's class).
|
39
39
|
|
40
|
-
Suppose that `my-element` transform text inside it based on the `transform` attribute. By default, it's
|
41
|
-
`lower`, but can be set to `upper` to lower case or upper case, respectively, the text inside.
|
40
|
+
Suppose that `my-element` transform text inside it based on the `transform` attribute. By default, it's `lower` (which will lower-case the text), but can be set to `upper` to upper case the text inside.
|
42
41
|
|
43
42
|
This means you'll need three tests, each with a different DOM:
|
44
43
|
|
@@ -72,7 +71,7 @@ describe("<some-element>", () => {
|
|
72
71
|
})
|
73
72
|
```
|
74
73
|
|
75
|
-
|
74
|
+
When the function you give to `test` is executed, the DOM will have been setup, so you can rely on your
|
76
75
|
custom elements `connectedCallback` having been called. Assuming the text transformation for `my-element`
|
77
76
|
occurs in `connectedCallback`, here is how you'd test all three cases:
|
78
77
|
|
@@ -109,7 +108,7 @@ describe("<some-element>", () => {
|
|
109
108
|
})
|
110
109
|
```
|
111
110
|
|
112
|
-
You'll notice almost all of this uses the browser APIs you (should
|
111
|
+
You'll notice almost all of this uses the browser APIs you (should) know and (hopefully) love.
|
113
112
|
|
114
113
|
You can manipulate the DOM inside a test as well, and it should behave as if you are doing it in a
|
115
114
|
browser. Note that many browser APIs are synchronous, so you don't have to add `await` before every
|
@@ -156,7 +156,7 @@ end
|
|
156
156
|
### Do Not Put Business Logic On Your Database Models
|
157
157
|
|
158
158
|
There's no reason to, or benefit to doing so. What you'll find is that any app of even moderate complexity will
|
159
|
-
not have a strict mapping from page to business concept to database table. Rather these things will all differ
|
159
|
+
not have a strict mapping from page to business concept to database table. Rather, these things will all differ
|
160
160
|
greatly, and each serves a different purpose.
|
161
161
|
|
162
162
|
The job of your data models—and the tables they provide access to—is to store reliable and unambiguous data.
|
@@ -1,10 +1,6 @@
|
|
1
1
|
# Database Schema / Migrations
|
2
2
|
|
3
|
-
Brut provides access to the database via the [Sequel library](https://sequel.jeremyevans.net/).
|
4
|
-
|
5
|
-
One thing to keep in mind is that Brut refers to your database layer as *database models* (notably not the un-qualified "models"). Brut treats this layer as a *model* of your database, not a model of your *domain* (though you are free to conflate the two).
|
6
|
-
|
7
|
-
This section details how to manage your database schema.
|
3
|
+
Brut provides access to the database via the [Sequel library](https://sequel.jeremyevans.net/). To manage your database schema, Brut uses Sequel's facility for this, with some of its own enhancements.
|
8
4
|
|
9
5
|
> [!NOTE]
|
10
6
|
> Brut currently only supports Postgres. Sequel supports many database systems, however Brut's extensions are
|
@@ -12,22 +8,14 @@ This section details how to manage your database schema.
|
|
12
8
|
|
13
9
|
## Overview
|
14
10
|
|
15
|
-
|
16
|
-
|
17
|
-
create the database schema from scratch—you will have to modify the existing schema to produce the schema
|
18
|
-
you want.
|
11
|
+
Your database schema is managed by a series of changes that build upon one another
|
12
|
+
called *migrations*.
|
19
13
|
|
20
|
-
For example, if you have a table `widgets` that has a `name` and `description`, to add a `status` field,
|
21
|
-
you cannot `drop table widgets` and then `create table widgets(...)` with the fields. You must instead
|
22
|
-
`alter table widgets(...)` to add the new column.
|
14
|
+
For example, if you have a table `widgets` that has a `name` and `description`, to add a `status` field, you cannot `drop table widgets` and then `create table widgets(...)` with the fields. You must instead `alter table widgets(...)` to add the new column.
|
23
15
|
|
24
16
|
Thus, each migration file is a change to the schema produced by all previous migration files.
|
25
17
|
|
26
|
-
Brut's provides this via
|
27
|
-
|
28
|
-
Schema files are located in `app/src/back_end/data_models/migrations` and are named using a timestamp-based
|
29
|
-
scheme. This means that when you create a new migration, its name will be based on the time and date you
|
30
|
-
created it, and any migrations that have not been applied will be applied in timestamp order.
|
18
|
+
Brut's provides this via Sequel. See [both](https://sequel.jeremyevans.net/rdoc/files/doc/schema_modification_rdoc.html) [docs](https://sequel.jeremyevans.net/rdoc/files/doc/migration_rdoc.html) for details on the API. Any schema modification method Sequel documents is available, however some default behavior has changed.
|
31
19
|
|
32
20
|
### Creating Migrations
|
33
21
|
|
@@ -40,6 +28,10 @@ together to form the filename:
|
|
40
28
|
app/src/back_end/data_models/migrations/20250508132646_user-accounts.rb
|
41
29
|
```
|
42
30
|
|
31
|
+
Note that the files are located in `app/src/back_end/data_models/migrations` and
|
32
|
+
have a name prefixed with a timestamp. This timestamp determins an ordering of how
|
33
|
+
the files are applied to the database.
|
34
|
+
|
43
35
|
The file is created mostly blank:
|
44
36
|
|
45
37
|
```ruby
|
@@ -49,19 +41,17 @@ Sequel.migration do
|
|
49
41
|
end
|
50
42
|
```
|
51
43
|
|
52
|
-
|
53
|
-
|
44
|
+
> [!NOTE]
|
45
|
+
> Sequels' migration API is similar in concept to Rails', but differs
|
46
|
+
> significantly in specifics. Please consult Sequel's documentation and
|
47
|
+
> don't assume Railsism will work the same way.
|
54
48
|
|
55
|
-
|
56
|
-
|
57
|
-
explanation.
|
49
|
+
Brut encourages only "up" migrations. Since Brut treats your development database
|
50
|
+
as ephemeral, there is little value to managing "down" migrations.
|
58
51
|
|
59
|
-
This is
|
60
|
-
method of the same name, automagically creates both "up" and "down" migrations, but *only* if you use the DSL. If
|
61
|
-
you use raw SQL, `change` doesn't work. But that doesn't matter for Brut (again, see *Recommended Practices*).
|
52
|
+
This is why Sequel's `change` method is not included in the scaffolded code. `change`, like Active Record's method of the same name, automagically creates both "up" and "down" migrations, but *only* if you use the DSL. If you use raw SQL, `change` doesn't work. By using only `up`, you won't have to worry about this.
|
62
53
|
|
63
|
-
Let's create
|
64
|
-
timestamp:
|
54
|
+
Let's create an accounts table that has an email field, a `deactivated_at` timestamp, and a `created_at` timestamp:
|
65
55
|
|
66
56
|
```ruby
|
67
57
|
Sequel.migration do
|
@@ -90,10 +80,6 @@ To apply this migration use `bin/db migrate`
|
|
90
80
|
> bin/db migrate
|
91
81
|
```
|
92
82
|
|
93
|
-
If you create a new migration, it will use a timestamp that is alphanumerically greater than the one we just made
|
94
|
-
and thus that migration will be applied after this one. Thus, you can rely on previous migrations having been
|
95
|
-
applied when authoring new ones.
|
96
|
-
|
97
83
|
### Managing Migrations
|
98
84
|
|
99
85
|
Sequel uses a special database table to understand which migrations have been run. This table will exist in
|
@@ -126,16 +112,19 @@ provides a more helpful error message when no records are found
|
|
126
112
|
#### External IDs
|
127
113
|
|
128
114
|
It's often useful to provide a unique identifier for a record that is not the database primary key. There are
|
129
|
-
many advantages to doing so,
|
130
|
-
|
131
|
-
|
115
|
+
many advantages to doing so, the main being that your primary and foreign keys are
|
116
|
+
considered private and for developer use only. Creating additional externalizable
|
117
|
+
unique keys is trivial, so Brut provides a way to do that.
|
132
118
|
|
133
119
|
> [!NOTE]
|
134
|
-
>
|
135
|
-
>
|
136
|
-
>
|
137
|
-
>
|
138
|
-
>
|
120
|
+
> **Primary keys** and **keys** are not the same thing. **Primary keys** are
|
121
|
+
> what is used to identify a record for the purposes of referential integrity.
|
122
|
+
> A **key** simply uniquely identifies a row or is a unique constraint on a table.
|
123
|
+
> Tables have only one primary key, but potentially many keys. Brut uses
|
124
|
+
> *synthetic* (sometimes called *surrogate*) keys as primary keys. This means
|
125
|
+
> they have no business meaning and can be safely used for foreighn keys
|
126
|
+
> and other cases without conflating them with domain concepts.
|
127
|
+
|
139
128
|
|
140
129
|
In Brut, an external ID is automatically generated by the database when a record is created. By convention, it
|
141
130
|
is prefixed with a short string representing your app and a short string representing the table, followed by a
|
@@ -190,8 +179,7 @@ This means that you can set values explicitly if you like, *and* you can change
|
|
190
179
|
|
191
180
|
### Brut Migration Changes and Enhancement
|
192
181
|
|
193
|
-
Brut attempts to set default behavior for migrations to encourage a modicum of best practices.
|
194
|
-
the changes and a brief explanation for the purpose of the change.
|
182
|
+
Brut attempts to set default behavior for migrations to encourage a modicum of best practices. These are:
|
195
183
|
|
196
184
|
* **Automatic synthetic primary key named `id` of type `int`.** You almost always want this. You can change the
|
197
185
|
primary key configuration per table if you like, but if you do nothing, you get a primary key that works for 99%
|
@@ -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`.
|