brut 0.0.20 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +24 -3
- data/.nvim.lua +1 -0
- data/Dockerfile.dx +12 -3
- data/Gemfile.lock +9 -7
- data/README.md +0 -7
- data/Rakefile +6 -4
- data/bin/dev +20 -0
- data/bin/docs +27 -0
- data/bin/setup +47 -1
- data/brut-css/.nvim.lua +1 -0
- data/brut-css/README.md +28 -0
- data/brut-css/bin/build +31 -0
- data/brut-css/bin/dev +1 -0
- data/brut-css/bin/docs +15 -0
- data/brut-css/bin/setup +5 -0
- data/brut-css/config/media-queries-all.css +15 -0
- data/brut-css/config/media-queries-minimal.css +5 -0
- data/brut-css/config/postcss.config.cjs +7 -0
- data/brut-css/config/pseudo-classes-all.css +9 -0
- data/brut-css/dx +1 -0
- data/brut-css/package-lock.json +3217 -0
- data/brut-css/package.json +36 -0
- data/brut-css/src/css/appearance.css +145 -0
- data/brut-css/src/css/border.css +522 -0
- data/brut-css/src/css/colors.css +3502 -0
- data/brut-css/src/css/dimensions.css +548 -0
- data/brut-css/src/css/flex.css +179 -0
- data/brut-css/src/css/index.css +13 -0
- data/brut-css/src/css/layout.css +120 -0
- data/brut-css/src/css/list.css +41 -0
- data/brut-css/src/css/positioning.css +354 -0
- data/brut-css/src/css/properties/colors.css +455 -0
- data/brut-css/src/css/properties/index.css +3 -0
- data/brut-css/src/css/properties/spacing.css +140 -0
- data/brut-css/src/css/properties/typography.css +224 -0
- data/brut-css/src/css/reset.css +107 -0
- data/brut-css/src/css/spacing.css +585 -0
- data/brut-css/src/css/typography.css +519 -0
- data/brut-css/src/css/utils.css +104 -0
- data/brut-css/src/docs/1_getting-started/1_overview.md +46 -0
- data/brut-css/src/docs/1_getting-started/2_installation.md +25 -0
- data/brut-css/src/docs/1_getting-started/3_core-concepts.md +75 -0
- data/brut-css/src/docs/1_getting-started/4_simple-example.md +132 -0
- data/brut-css/src/docs/1_getting-started/page.html.ejs +10 -0
- data/brut-css/src/docs/2_properties/page.html.ejs +71 -0
- data/brut-css/src/docs/3_classes/color-demo.html.ejs +31 -0
- data/brut-css/src/docs/3_classes/page.html.ejs +87 -0
- data/brut-css/src/docs/4_customization/1_design-system.md +36 -0
- data/brut-css/src/docs/4_customization/2_breakpoints.md +75 -0
- data/brut-css/src/docs/4_customization/3_pseudo-classes.md +74 -0
- data/brut-css/src/docs/4_customization/4_advanced-configuration.md +40 -0
- data/brut-css/src/docs/4_customization/page.html.ejs +10 -0
- data/brut-css/src/docs/docs.css +98 -0
- data/brut-css/src/docs/includes/body-and-header.html.ejs +30 -0
- data/brut-css/src/docs/includes/footer-and-rest.html.ejs +9 -0
- data/brut-css/src/docs/includes/head.html.ejs +5 -0
- data/brut-css/src/docs/includes/nav.html.ejs +10 -0
- data/brut-css/src/docs/index.html.ejs +32 -0
- data/brut-css/src/docs/prism-twilight.min.css +1 -0
- data/brut-css/src/js/Logger.js +71 -0
- data/brut-css/src/js/build.js +111 -0
- data/brut-css/src/js/cli/CLIArgError.js +7 -0
- data/brut-css/src/js/cli/Debug.js +27 -0
- data/brut-css/src/js/cli/DocsDir.js +16 -0
- data/brut-css/src/js/cli/DocsTemplateSourceDir.js +16 -0
- data/brut-css/src/js/cli/InputFile.js +31 -0
- data/brut-css/src/js/cli/MediaQueryConfigFile.js +10 -0
- data/brut-css/src/js/cli/OutputFile.js +22 -0
- data/brut-css/src/js/cli/ParsedArg.js +17 -0
- data/brut-css/src/js/cli/PathToBrutCSSRoot.js +19 -0
- data/brut-css/src/js/cli/PseudoClassConfigFile.js +11 -0
- data/brut-css/src/js/cli.js +108 -0
- data/brut-css/src/js/docGenerator.js +467 -0
- data/brut-css/src/js/mediaQueryConfigParser.js +98 -0
- data/brut-css/src/js/post-css-plugins/addMediaQueriesPlugin.js +49 -0
- data/brut-css/src/js/post-css-plugins/addPseudoClassesPlugin.js +42 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Category.js +9 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/DocState.js +185 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Documentable.js +8 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Group.js +7 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/ParsedComment.js +73 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Property.js +9 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyCategory.js +4 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyGroup.js +8 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Rule.js +12 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleCategory.js +4 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleGroup.js +8 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeRef.js +5 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeURL.js +9 -0
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin.js +49 -0
- data/brut-css/src/js/post-css-plugins/generateRootCustomPropertiesPlugin.js +45 -0
- data/brut-css/src/js/pseudoClassConfigParser.js +145 -0
- data/brut-js/.projections.json +10 -0
- data/brut-js/README.md +118 -0
- data/brut-js/bin/build +10 -0
- data/brut-js/bin/ci +5 -0
- data/brut-js/bin/setup +5 -0
- data/brut-js/docs/README.md +8 -0
- data/brut-js/docs/jsdoc-plugins/customElementTag.js +8 -0
- data/brut-js/docs/jsdoc-theme/publish.js +692 -0
- data/brut-js/docs/jsdoc-theme/static/scripts/linenumber.js +25 -0
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/Apache-License-2.0.txt +202 -0
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/lang-css.js +2 -0
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/prettify.js +28 -0
- data/brut-js/docs/jsdoc-theme/static/styles/jsdoc-default.css +327 -0
- data/brut-js/docs/jsdoc-theme/static/styles/prettify-jsdoc.css +111 -0
- data/brut-js/docs/jsdoc-theme/static/styles/prettify-tomorrow.css +132 -0
- data/brut-js/docs/jsdoc-theme/tmpl/augments.tmpl +10 -0
- data/brut-js/docs/jsdoc-theme/tmpl/container.tmpl +199 -0
- data/brut-js/docs/jsdoc-theme/tmpl/details.tmpl +143 -0
- data/brut-js/docs/jsdoc-theme/tmpl/example.tmpl +2 -0
- data/brut-js/docs/jsdoc-theme/tmpl/examples.tmpl +13 -0
- data/brut-js/docs/jsdoc-theme/tmpl/exceptions.tmpl +32 -0
- data/brut-js/docs/jsdoc-theme/tmpl/layout.tmpl +38 -0
- data/brut-js/docs/jsdoc-theme/tmpl/mainpage.tmpl +14 -0
- data/brut-js/docs/jsdoc-theme/tmpl/members.tmpl +38 -0
- data/brut-js/docs/jsdoc-theme/tmpl/method.tmpl +131 -0
- data/brut-js/docs/jsdoc-theme/tmpl/modifies.tmpl +14 -0
- data/brut-js/docs/jsdoc-theme/tmpl/params.tmpl +131 -0
- data/brut-js/docs/jsdoc-theme/tmpl/properties.tmpl +108 -0
- data/brut-js/docs/jsdoc-theme/tmpl/returns.tmpl +19 -0
- data/brut-js/docs/jsdoc-theme/tmpl/source.tmpl +8 -0
- data/brut-js/docs/jsdoc-theme/tmpl/tutorial.tmpl +19 -0
- data/brut-js/docs/jsdoc-theme/tmpl/type.tmpl +7 -0
- data/brut-js/docs/jsdoc.config.json +23 -0
- data/brut-js/docs/package-lock.json +343 -0
- data/brut-js/docs/package.json +7 -0
- data/brut-js/package-lock.json +2171 -0
- data/brut-js/package.json +32 -0
- data/brut-js/specs/AjaxSubmit.spec.js +256 -0
- data/brut-js/specs/Autosubmit.spec.js +127 -0
- data/brut-js/specs/ConfirmSubmit.spec.js +193 -0
- data/brut-js/specs/ConstraintViolationMessage.spec.js +33 -0
- data/brut-js/specs/ConstraintViolationMessages.spec.js +29 -0
- data/brut-js/specs/CopyToClipboard.spec.js +35 -0
- data/brut-js/specs/Form.spec.js +181 -0
- data/brut-js/specs/I18nTranslation.spec.js +19 -0
- data/brut-js/specs/LocaleDetection.spec.js +22 -0
- data/brut-js/specs/Message.spec.js +15 -0
- data/brut-js/specs/SpecHelper.js +23 -0
- data/brut-js/specs/Tabs.spec.js +41 -0
- data/brut-js/specs/config/asset_metadata.json +7 -0
- data/brut-js/src/AjaxSubmit.js +384 -0
- data/brut-js/src/Autosubmit.js +63 -0
- data/brut-js/src/BaseCustomElement.js +261 -0
- data/brut-js/src/ConfirmSubmit.js +116 -0
- data/brut-js/src/ConfirmationDialog.js +143 -0
- data/brut-js/src/ConstraintViolationMessage.js +125 -0
- data/brut-js/src/ConstraintViolationMessages.js +98 -0
- data/brut-js/src/CopyToClipboard.js +96 -0
- data/brut-js/src/Form.js +151 -0
- data/brut-js/src/I18nTranslation.js +61 -0
- data/brut-js/src/LocaleDetection.js +117 -0
- data/brut-js/src/Logger.js +90 -0
- data/brut-js/src/Message.js +56 -0
- data/brut-js/src/RichString.js +113 -0
- data/brut-js/src/Tabs.js +168 -0
- data/brut-js/src/Tracing.js +247 -0
- data/brut-js/src/appForTestingOnly.js +15 -0
- data/brut-js/src/index.js +130 -0
- data/brut-js/src/testing/AssetMetadata.js +35 -0
- data/brut-js/src/testing/AssetMetadataLoader.js +25 -0
- data/brut-js/src/testing/CustomElementTest.js +235 -0
- data/brut-js/src/testing/DOMCreator.js +45 -0
- data/brut-js/src/testing/index.js +48 -0
- data/brutrb.com/.vitepress/config.mjs +106 -0
- data/brutrb.com/.vitepress/plugins/jsdocLinker.js +34 -0
- data/brutrb.com/.vitepress/plugins/rdocLinker.js +18 -0
- data/brutrb.com/.vitepress/theme/custom.css +7 -0
- data/brutrb.com/.vitepress/theme/index.js +18 -0
- data/brutrb.com/.vitepress/theme/style.css +149 -0
- data/brutrb.com/ai.md +68 -0
- data/brutrb.com/assets.md +138 -0
- data/brutrb.com/bin/build +5 -0
- data/brutrb.com/bin/deploy +7 -0
- data/brutrb.com/bin/dev +5 -0
- data/brutrb.com/bin/setup +5 -0
- data/brutrb.com/brut-js.md +117 -0
- data/brutrb.com/business-logic.md +55 -0
- data/brutrb.com/cli.md +278 -0
- data/brutrb.com/components.md +243 -0
- data/brutrb.com/configuration.md +257 -0
- data/brutrb.com/css.md +103 -0
- data/brutrb.com/custom-element-tests.md +149 -0
- data/brutrb.com/database-access.md +201 -0
- data/brutrb.com/database-schema.md +312 -0
- data/brutrb.com/deployment.md +66 -0
- data/brutrb.com/dev-environment.md +179 -0
- data/brutrb.com/doc-conventions.md +39 -0
- data/brutrb.com/end-to-end-tests.md +174 -0
- data/brutrb.com/flash-and-session.md +224 -0
- data/brutrb.com/forms.md +866 -0
- data/brutrb.com/getting-started.md +66 -0
- data/brutrb.com/handlers.md +153 -0
- data/brutrb.com/hooks.md +178 -0
- data/brutrb.com/i18n.md +188 -0
- data/brutrb.com/images/Makefile +10 -0
- data/brutrb.com/images/dev-env-overview.dot +54 -0
- data/brutrb.com/images/dev-env-overview.png +0 -0
- data/brutrb.com/images/dev-env-protocol.dot +37 -0
- data/brutrb.com/images/dev-env-protocol.png +0 -0
- data/brutrb.com/images/logo-300.png +0 -0
- data/brutrb.com/images/logo.png +0 -0
- data/brutrb.com/images/overview.graffle +0 -0
- data/brutrb.com/images/overview.png +0 -0
- data/brutrb.com/images/spa.dot +19 -0
- data/brutrb.com/images/spa.png +0 -0
- data/brutrb.com/images/workspace-protocol.dot +44 -0
- data/brutrb.com/images/workspace-protocol.png +0 -0
- data/brutrb.com/index.md +36 -0
- data/brutrb.com/instrumentation.md +183 -0
- data/brutrb.com/javascript.md +122 -0
- data/brutrb.com/jobs.md +14 -0
- data/{doc-src → brutrb.com}/keyword-injection.md +122 -68
- data/brutrb.com/markdown-examples.md +85 -0
- data/brutrb.com/middleware.md +80 -0
- data/brutrb.com/not-released.md +5 -0
- data/brutrb.com/overview.md +404 -0
- data/brutrb.com/package-lock.json +2404 -0
- data/brutrb.com/package.json +11 -0
- data/brutrb.com/pages.md +378 -0
- data/brutrb.com/public/images/logo-300.png +0 -0
- data/brutrb.com/public/images/logo.png +0 -0
- data/brutrb.com/routes.md +215 -0
- data/brutrb.com/security.md +105 -0
- data/brutrb.com/seed-data.md +63 -0
- data/brutrb.com/space-time-continuum.md +85 -0
- data/brutrb.com/tutorial.md +3 -0
- data/brutrb.com/unit-tests.md +148 -0
- data/docker-compose.dx.yml +6 -3
- data/docs/404.html +21 -0
- data/docs/CNAME +1 -0
- data/docs/ai.html +24 -0
- data/docs/api/Brut/BackEnd/SeedData.html +493 -0
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +214 -0
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +125 -0
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +125 -0
- data/docs/api/Brut/BackEnd/Sidekiq.html +125 -0
- data/docs/api/Brut/BackEnd/Validators/FormValidator.html +414 -0
- data/docs/api/Brut/BackEnd/Validators.html +128 -0
- data/docs/api/Brut/BackEnd.html +132 -0
- data/docs/api/Brut/CLI/App.html +1576 -0
- data/docs/api/Brut/CLI/AppRunner.html +491 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +264 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +306 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +262 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +314 -0
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +183 -0
- data/docs/api/Brut/CLI/Apps/DB/Create.html +365 -0
- data/docs/api/Brut/CLI/Apps/DB/Drop.html +357 -0
- data/docs/api/Brut/CLI/Apps/DB/Migrate.html +383 -0
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +335 -0
- data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +329 -0
- data/docs/api/Brut/CLI/Apps/DB/Seed.html +347 -0
- data/docs/api/Brut/CLI/Apps/DB/Status.html +383 -0
- data/docs/api/Brut/CLI/Apps/DB.html +183 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +303 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +512 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +398 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +374 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +410 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +262 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +303 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +480 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +450 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +380 -0
- data/docs/api/Brut/CLI/Apps/Scaffold.html +253 -0
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +464 -0
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +407 -0
- data/docs/api/Brut/CLI/Apps/Test/JS.html +262 -0
- data/docs/api/Brut/CLI/Apps/Test/Run.html +578 -0
- data/docs/api/Brut/CLI/Apps/Test.html +253 -0
- data/docs/api/Brut/CLI/Apps.html +125 -0
- data/docs/api/Brut/CLI/Command.html +2342 -0
- data/docs/api/Brut/CLI/Error.html +139 -0
- data/docs/api/Brut/CLI/ExecutionResults/Result.html +664 -0
- data/docs/api/Brut/CLI/ExecutionResults.html +675 -0
- data/docs/api/Brut/CLI/Executor.html +430 -0
- data/docs/api/Brut/CLI/InvalidOption.html +245 -0
- data/docs/api/Brut/CLI/Options.html +753 -0
- data/docs/api/Brut/CLI/Output.html +699 -0
- data/docs/api/Brut/CLI/SystemExecError.html +451 -0
- data/docs/api/Brut/CLI.html +263 -0
- data/docs/api/Brut/FactoryBot.html +225 -0
- data/docs/api/Brut/Framework/App.html +1097 -0
- data/docs/api/Brut/Framework/Config.html +1045 -0
- data/docs/api/Brut/Framework/Container.html +1379 -0
- data/docs/api/Brut/Framework/Error.html +140 -0
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +144 -0
- data/docs/api/Brut/Framework/Errors/Bug.html +234 -0
- data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +257 -0
- data/docs/api/Brut/Framework/Errors/MissingParameter.html +273 -0
- data/docs/api/Brut/Framework/Errors/NoClassForPath.html +471 -0
- data/docs/api/Brut/Framework/Errors/NotFound.html +308 -0
- data/docs/api/Brut/Framework/Errors/NotImplemented.html +234 -0
- data/docs/api/Brut/Framework/Errors.html +328 -0
- data/docs/api/Brut/Framework/FussyTypeEnforcement.html +392 -0
- data/docs/api/Brut/Framework/MCP.html +861 -0
- data/docs/api/Brut/Framework/ProjectEnvironment.html +648 -0
- data/docs/api/Brut/Framework.html +129 -0
- data/docs/api/Brut/FrontEnd/AssetPathResolver.html +317 -0
- data/docs/api/Brut/FrontEnd/Component/Helpers.html +326 -0
- data/docs/api/Brut/FrontEnd/Component.html +365 -0
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +470 -0
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +518 -0
- data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +317 -0
- data/docs/api/Brut/FrontEnd/Components/Input.html +195 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +339 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +660 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +417 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +918 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +651 -0
- data/docs/api/Brut/FrontEnd/Components/Inputs.html +125 -0
- data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +367 -0
- data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +336 -0
- data/docs/api/Brut/FrontEnd/Components/TimeTag.html +655 -0
- data/docs/api/Brut/FrontEnd/Components/Traceparent.html +352 -0
- data/docs/api/Brut/FrontEnd/Components.html +135 -0
- data/docs/api/Brut/FrontEnd/Download.html +467 -0
- data/docs/api/Brut/FrontEnd/Flash.html +1150 -0
- data/docs/api/Brut/FrontEnd/Form.html +1157 -0
- data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +634 -0
- data/docs/api/Brut/FrontEnd/Forms/Input.html +615 -0
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +547 -0
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1318 -0
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +609 -0
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +587 -0
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +613 -0
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +582 -0
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +609 -0
- data/docs/api/Brut/FrontEnd/Forms.html +127 -0
- data/docs/api/Brut/FrontEnd/GenericResponse.html +377 -0
- data/docs/api/Brut/FrontEnd/Handler.html +442 -0
- data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +318 -0
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +336 -0
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +399 -0
- data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +354 -0
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +151 -0
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +315 -0
- data/docs/api/Brut/FrontEnd/Handlers.html +125 -0
- data/docs/api/Brut/FrontEnd/HandlingResults.html +339 -0
- data/docs/api/Brut/FrontEnd/HttpMethod.html +661 -0
- data/docs/api/Brut/FrontEnd/HttpStatus.html +496 -0
- data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +284 -0
- data/docs/api/Brut/FrontEnd/Layout.html +318 -0
- data/docs/api/Brut/FrontEnd/Middleware.html +135 -0
- data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +288 -0
- data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +292 -0
- data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +324 -0
- data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +372 -0
- data/docs/api/Brut/FrontEnd/Middlewares.html +125 -0
- data/docs/api/Brut/FrontEnd/Page.html +773 -0
- data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +797 -0
- data/docs/api/Brut/FrontEnd/Pages.html +125 -0
- data/docs/api/Brut/FrontEnd/RequestContext.html +1312 -0
- data/docs/api/Brut/FrontEnd/RouteHook.html +424 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +242 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +249 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +264 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +261 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +284 -0
- data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +252 -0
- data/docs/api/Brut/FrontEnd/RouteHooks.html +115 -0
- data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +227 -0
- data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +305 -0
- data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +324 -0
- data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +319 -0
- data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +315 -0
- data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +315 -0
- data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +327 -0
- data/docs/api/Brut/FrontEnd/Routing/Route.html +761 -0
- data/docs/api/Brut/FrontEnd/Routing.html +927 -0
- data/docs/api/Brut/FrontEnd/Session.html +1195 -0
- data/docs/api/Brut/FrontEnd.html +134 -0
- data/docs/api/Brut/I18n/BaseMethods.html +931 -0
- data/docs/api/Brut/I18n/ForBackEnd.html +302 -0
- data/docs/api/Brut/I18n/ForCLI.html +302 -0
- data/docs/api/Brut/I18n/ForHTML.html +296 -0
- data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +316 -0
- data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +930 -0
- data/docs/api/Brut/I18n.html +127 -0
- data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +435 -0
- data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +286 -0
- data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +302 -0
- data/docs/api/Brut/Instrumentation/OpenTelemetry.html +864 -0
- data/docs/api/Brut/Instrumentation.html +126 -0
- data/docs/api/Brut/SinatraHelpers/ClassMethods.html +532 -0
- data/docs/api/Brut/SinatraHelpers.html +281 -0
- data/docs/api/Brut/SpecSupport/ClockSupport.html +383 -0
- data/docs/api/Brut/SpecSupport/ComponentSupport.html +502 -0
- data/docs/api/Brut/SpecSupport/E2ETestServer.html +503 -0
- data/docs/api/Brut/SpecSupport/E2eSupport.html +142 -0
- data/docs/api/Brut/SpecSupport/EnhancedNode.html +403 -0
- data/docs/api/Brut/SpecSupport/FlashSupport.html +278 -0
- data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +401 -0
- data/docs/api/Brut/SpecSupport/GeneralSupport.html +195 -0
- data/docs/api/Brut/SpecSupport/HandlerSupport.html +160 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +553 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +439 -0
- data/docs/api/Brut/SpecSupport/Matchers.html +125 -0
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +335 -0
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +602 -0
- data/docs/api/Brut/SpecSupport/SessionSupport.html +196 -0
- data/docs/api/Brut/SpecSupport.html +129 -0
- data/docs/api/Brut.html +225 -0
- data/docs/api/Clock.html +603 -0
- data/docs/api/RichString.html +968 -0
- data/docs/api/SemanticLogger/Appender/Async.html +219 -0
- data/docs/api/Sequel/Extensions/BrutInstrumentation.html +115 -0
- data/docs/api/Sequel/Extensions/BrutMigrations.html +533 -0
- data/docs/api/Sequel/Extensions.html +117 -0
- data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +105 -0
- data/docs/api/Sequel/Plugins/CreatedAt.html +125 -0
- data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +207 -0
- data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +186 -0
- data/docs/api/Sequel/Plugins/ExternalId.html +218 -0
- data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +202 -0
- data/docs/api/Sequel/Plugins/FindBang.html +125 -0
- data/docs/api/Sequel/Plugins.html +117 -0
- data/docs/api/Sequel.html +117 -0
- data/docs/api/_index.html +1553 -0
- data/docs/api/class_list.html +54 -0
- data/docs/api/css/common.css +1 -0
- data/docs/api/css/full_list.css +58 -0
- data/docs/api/css/style.css +503 -0
- data/docs/api/file.README.html +127 -0
- data/docs/api/file_list.html +59 -0
- data/docs/api/frames.html +22 -0
- data/docs/api/index.html +127 -0
- data/docs/api/js/app.js +344 -0
- data/docs/api/js/full_list.js +242 -0
- data/docs/api/js/jquery.js +4 -0
- data/docs/api/method_list.html +3998 -0
- data/docs/api/top-level-namespace.html +112 -0
- data/docs/assets/ai.md.tZrjP9im.js +1 -0
- data/docs/assets/ai.md.tZrjP9im.lean.js +1 -0
- data/docs/assets/app.D_yaTITQ.js +1 -0
- data/docs/assets/assets.md.D3wunzLx.js +19 -0
- data/docs/assets/assets.md.D3wunzLx.lean.js +1 -0
- data/docs/assets/brut-js.md.o2DAO2s2.js +12 -0
- data/docs/assets/brut-js.md.o2DAO2s2.lean.js +1 -0
- data/docs/assets/business-logic.md.BY4hGy0m.js +1 -0
- data/docs/assets/business-logic.md.BY4hGy0m.lean.js +1 -0
- data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +1 -0
- data/docs/assets/chunks/VPLocalSearchBox.B2-ZzyTY.js +8 -0
- data/docs/assets/chunks/framework.1L-BeKqY.js +18 -0
- data/docs/assets/chunks/theme.CfGFVRvE.js +2 -0
- data/docs/assets/cli.md.RmeA2b0i.js +127 -0
- data/docs/assets/cli.md.RmeA2b0i.lean.js +1 -0
- data/docs/assets/components.md.eCttGlN-.js +104 -0
- data/docs/assets/components.md.eCttGlN-.lean.js +1 -0
- data/docs/assets/configuration.md.BRriU0cL.js +78 -0
- data/docs/assets/configuration.md.BRriU0cL.lean.js +1 -0
- data/docs/assets/css.md.DJgj2clw.js +21 -0
- data/docs/assets/css.md.DJgj2clw.lean.js +1 -0
- data/docs/assets/custom-element-tests.md.BrYJQEl3.js +69 -0
- data/docs/assets/custom-element-tests.md.BrYJQEl3.lean.js +1 -0
- data/docs/assets/database-access.md.C7l-Vuvb.js +63 -0
- data/docs/assets/database-access.md.C7l-Vuvb.lean.js +1 -0
- data/docs/assets/database-schema.md.BUjR0VS1.js +63 -0
- data/docs/assets/database-schema.md.BUjR0VS1.lean.js +1 -0
- data/docs/assets/deployment.md.Dbka4OTr.js +1 -0
- data/docs/assets/deployment.md.Dbka4OTr.lean.js +1 -0
- data/docs/assets/dev-env-overview.Gj7NWM8-.png +0 -0
- data/docs/assets/dev-env-protocol.DysDAtnz.png +0 -0
- data/docs/assets/dev-environment.md.BNc8AYiK.js +11 -0
- data/docs/assets/dev-environment.md.BNc8AYiK.lean.js +1 -0
- data/docs/assets/doc-conventions.md.DCfRXXi-.js +1 -0
- data/docs/assets/doc-conventions.md.DCfRXXi-.lean.js +1 -0
- data/docs/assets/end-to-end-tests.md.yfQHC0b5.js +26 -0
- data/docs/assets/end-to-end-tests.md.yfQHC0b5.lean.js +1 -0
- data/docs/assets/flash-and-session.md.BXY8RvT0.js +93 -0
- data/docs/assets/flash-and-session.md.BXY8RvT0.lean.js +1 -0
- data/docs/assets/forms.md.CBTYQ_Cz.js +379 -0
- data/docs/assets/forms.md.CBTYQ_Cz.lean.js +1 -0
- data/docs/assets/getting-started.md.Bz2s1Vjb.js +2 -0
- data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +1 -0
- data/docs/assets/handlers.md.089DVD3v.js +69 -0
- data/docs/assets/handlers.md.089DVD3v.lean.js +1 -0
- data/docs/assets/hooks.md.C4-moMny.js +80 -0
- data/docs/assets/hooks.md.C4-moMny.lean.js +1 -0
- data/docs/assets/i18n.md.Do9i1qWl.js +23 -0
- data/docs/assets/i18n.md.Do9i1qWl.lean.js +1 -0
- data/docs/assets/index.md.B28EwVpq.js +1 -0
- data/docs/assets/index.md.B28EwVpq.lean.js +1 -0
- data/docs/assets/instrumentation.md.CL6ax7nT.js +35 -0
- data/docs/assets/instrumentation.md.CL6ax7nT.lean.js +1 -0
- data/docs/assets/javascript.md.GWbhRS51.js +31 -0
- data/docs/assets/javascript.md.GWbhRS51.lean.js +1 -0
- data/docs/assets/jobs.md.S-2amAYp.js +1 -0
- data/docs/assets/jobs.md.S-2amAYp.lean.js +1 -0
- data/docs/assets/keyword-injection.md.Dt2tKREs.js +25 -0
- data/docs/assets/keyword-injection.md.Dt2tKREs.lean.js +1 -0
- data/docs/assets/markdown-examples.md.CCFEQO44.js +33 -0
- data/docs/assets/markdown-examples.md.CCFEQO44.lean.js +1 -0
- data/docs/assets/middleware.md.Czz_UlJN.js +20 -0
- data/docs/assets/middleware.md.Czz_UlJN.lean.js +1 -0
- data/docs/assets/not-released.md.BBy28McC.js +1 -0
- data/docs/assets/not-released.md.BBy28McC.lean.js +1 -0
- data/docs/assets/overview.Da81cB9R.png +0 -0
- data/docs/assets/overview.md.CDalkuxV.js +133 -0
- data/docs/assets/overview.md.CDalkuxV.lean.js +1 -0
- data/docs/assets/pages.md.BE3kfOc5.js +122 -0
- data/docs/assets/pages.md.BE3kfOc5.lean.js +1 -0
- data/docs/assets/routes.md.BMM7peut.js +29 -0
- data/docs/assets/routes.md.BMM7peut.lean.js +1 -0
- data/docs/assets/security.md.C668yXCi.js +1 -0
- data/docs/assets/security.md.C668yXCi.lean.js +1 -0
- data/docs/assets/seed-data.md.BvFZlqIk.js +14 -0
- data/docs/assets/seed-data.md.BvFZlqIk.lean.js +1 -0
- data/docs/assets/spa.qejUdp-5.png +0 -0
- data/docs/assets/space-time-continuum.md.KPUIKysQ.js +1 -0
- data/docs/assets/space-time-continuum.md.KPUIKysQ.lean.js +1 -0
- data/docs/assets/style.D73IYGCX.css +1 -0
- data/docs/assets/tutorial.md.BnoGjrdK.js +1 -0
- data/docs/assets/tutorial.md.BnoGjrdK.lean.js +1 -0
- data/docs/assets/unit-tests.md.DUGrnLj5.js +13 -0
- data/docs/assets/unit-tests.md.DUGrnLj5.lean.js +1 -0
- data/docs/assets/workspace-protocol.C0gXsoDb.png +0 -0
- data/docs/assets.html +42 -0
- data/docs/brut-css/brut.css +1 -0
- data/docs/brut-css/brut.max.css +22372 -0
- data/docs/brut-css/classes/appearances.html +783 -0
- data/docs/brut-css/classes/background-colors.html +3529 -0
- data/docs/brut-css/classes/border-colors.html +3529 -0
- data/docs/brut-css/classes/borders.html +2293 -0
- data/docs/brut-css/classes/dimensions.html +2581 -0
- data/docs/brut-css/classes/flex.html +917 -0
- data/docs/brut-css/classes/foreground-colors.html +3261 -0
- data/docs/brut-css/classes/junk-drawer.html +431 -0
- data/docs/brut-css/classes/layout.html +668 -0
- data/docs/brut-css/classes/lists.html +331 -0
- data/docs/brut-css/classes/positioning.html +1751 -0
- data/docs/brut-css/classes/spacings.html +2633 -0
- data/docs/brut-css/classes/typography.html +2206 -0
- data/docs/brut-css/customization/advanced-configuration.html +204 -0
- data/docs/brut-css/customization/breakpoints.html +227 -0
- data/docs/brut-css/customization/design-system.html +197 -0
- data/docs/brut-css/customization/pseudo-classes.html +228 -0
- data/docs/brut-css/docs.css +98 -0
- data/docs/brut-css/getting-started/core-concepts.html +234 -0
- data/docs/brut-css/getting-started/installation.html +190 -0
- data/docs/brut-css/getting-started/overview.html +210 -0
- data/docs/brut-css/getting-started/simple-example.html +285 -0
- data/docs/brut-css/index.html +193 -0
- data/docs/brut-css/prism-twilight.min.css +1 -0
- data/docs/brut-css/properties/colors.html +1548 -0
- data/docs/brut-css/properties/spacings.html +614 -0
- data/docs/brut-css/properties/typography.html +777 -0
- data/docs/brut-js/api/AjaxSubmit.html +374 -0
- data/docs/brut-js/api/AjaxSubmit.js.html +435 -0
- data/docs/brut-js/api/Autosubmit.html +192 -0
- data/docs/brut-js/api/Autosubmit.js.html +114 -0
- data/docs/brut-js/api/BaseCustomElement.html +1091 -0
- data/docs/brut-js/api/BaseCustomElement.js.html +312 -0
- data/docs/brut-js/api/BrutCustomElements.html +172 -0
- data/docs/brut-js/api/BufferedLogger.html +173 -0
- data/docs/brut-js/api/ConfirmSubmit.html +278 -0
- data/docs/brut-js/api/ConfirmSubmit.js.html +167 -0
- data/docs/brut-js/api/ConfirmationDialog.html +425 -0
- data/docs/brut-js/api/ConfirmationDialog.js.html +194 -0
- data/docs/brut-js/api/ConstraintViolationMessage.html +448 -0
- data/docs/brut-js/api/ConstraintViolationMessage.js.html +176 -0
- data/docs/brut-js/api/ConstraintViolationMessages.html +590 -0
- data/docs/brut-js/api/ConstraintViolationMessages.js.html +149 -0
- data/docs/brut-js/api/CopyToClipboard.html +345 -0
- data/docs/brut-js/api/CopyToClipboard.js.html +147 -0
- data/docs/brut-js/api/Form.html +294 -0
- data/docs/brut-js/api/Form.js.html +202 -0
- data/docs/brut-js/api/I18nTranslation.html +409 -0
- data/docs/brut-js/api/I18nTranslation.js.html +112 -0
- data/docs/brut-js/api/LocaleDetection.html +312 -0
- data/docs/brut-js/api/LocaleDetection.js.html +168 -0
- data/docs/brut-js/api/Logger.html +702 -0
- data/docs/brut-js/api/Logger.js.html +141 -0
- data/docs/brut-js/api/Message.html +238 -0
- data/docs/brut-js/api/Message.js.html +107 -0
- data/docs/brut-js/api/PrefixedLogger.html +369 -0
- data/docs/brut-js/api/RichString.html +1049 -0
- data/docs/brut-js/api/RichString.js.html +164 -0
- data/docs/brut-js/api/Tabs.html +295 -0
- data/docs/brut-js/api/Tabs.js.html +219 -0
- data/docs/brut-js/api/Tracing.html +277 -0
- data/docs/brut-js/api/Tracing.js.html +298 -0
- data/docs/brut-js/api/external-CustomElementRegistry.html +140 -0
- data/docs/brut-js/api/external-Performance.html +138 -0
- data/docs/brut-js/api/external-Promise.html +138 -0
- data/docs/brut-js/api/external-ValidityState.html +138 -0
- data/docs/brut-js/api/external-Window.html +233 -0
- data/docs/brut-js/api/external-fetch.html +138 -0
- data/docs/brut-js/api/global.html +400 -0
- data/docs/brut-js/api/index.html +168 -0
- data/docs/brut-js/api/index.js.html +181 -0
- data/docs/brut-js/api/module-testing.html +383 -0
- data/docs/brut-js/api/scripts/linenumber.js +25 -0
- data/docs/brut-js/api/scripts/prettify/Apache-License-2.0.txt +202 -0
- data/docs/brut-js/api/scripts/prettify/lang-css.js +2 -0
- data/docs/brut-js/api/scripts/prettify/prettify.js +28 -0
- data/docs/brut-js/api/styles/jsdoc-default.css +327 -0
- data/docs/brut-js/api/styles/prettify-jsdoc.css +111 -0
- data/docs/brut-js/api/styles/prettify-tomorrow.css +132 -0
- data/docs/brut-js/api/testing.AssetMetadata.html +172 -0
- data/docs/brut-js/api/testing.AssetMetadataLoader.html +171 -0
- data/docs/brut-js/api/testing.CustomElementTest.html +679 -0
- data/docs/brut-js/api/testing.DOMCreator.html +171 -0
- data/docs/brut-js/api/testing_AssetMetadata.js.html +86 -0
- data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +76 -0
- data/docs/brut-js/api/testing_CustomElementTest.js.html +286 -0
- data/docs/brut-js/api/testing_DOMCreator.js.html +96 -0
- data/docs/brut-js/api/testing_index.js.html +99 -0
- data/docs/brut-js.html +35 -0
- data/docs/business-logic.html +24 -0
- data/docs/cli.html +150 -0
- data/docs/components.html +127 -0
- data/docs/configuration.html +101 -0
- data/docs/css.html +44 -0
- data/docs/custom-element-tests.html +92 -0
- data/docs/database-access.html +86 -0
- data/docs/database-schema.html +86 -0
- data/docs/deployment.html +24 -0
- data/docs/dev-environment.html +34 -0
- data/docs/doc-conventions.html +24 -0
- data/docs/end-to-end-tests.html +49 -0
- data/docs/flash-and-session.html +116 -0
- data/docs/forms.html +402 -0
- data/docs/getting-started.html +25 -0
- data/docs/handlers.html +92 -0
- data/docs/hashmap.json +1 -0
- data/docs/hooks.html +103 -0
- data/docs/i18n.html +46 -0
- data/docs/images/logo-300.png +0 -0
- data/docs/images/logo.png +0 -0
- data/docs/index.html +24 -0
- data/docs/instrumentation.html +58 -0
- data/docs/javascript.html +54 -0
- data/docs/jobs.html +24 -0
- data/docs/keyword-injection.html +48 -0
- data/docs/markdown-examples.html +56 -0
- data/docs/middleware.html +43 -0
- data/docs/not-released.html +24 -0
- data/docs/overview.html +156 -0
- data/docs/pages.html +145 -0
- data/docs/routes.html +52 -0
- data/docs/security.html +24 -0
- data/docs/seed-data.html +37 -0
- data/docs/space-time-continuum.html +24 -0
- data/docs/tutorial.html +24 -0
- data/docs/unit-tests.html +36 -0
- data/docs/vp-icons.css +1 -0
- data/lib/brut/back_end/seed_data.rb +19 -2
- data/lib/brut/back_end/sidekiq/middlewares/server.rb +2 -1
- data/lib/brut/back_end/sidekiq/middlewares.rb +2 -1
- data/lib/brut/back_end/sidekiq.rb +2 -1
- data/lib/brut/back_end/validator.rb +5 -1
- data/lib/brut/back_end.rb +4 -2
- data/lib/brut/cli/app_runner.rb +1 -1
- data/lib/brut/cli/apps/test.rb +5 -0
- data/lib/brut/cli.rb +4 -3
- data/lib/brut/factory_bot.rb +0 -5
- data/lib/brut/framework/app.rb +70 -5
- data/lib/brut/framework/config.rb +5 -3
- data/lib/brut/framework/container.rb +3 -2
- data/lib/brut/framework/errors.rb +12 -4
- data/lib/brut/framework/mcp.rb +58 -1
- data/lib/brut/framework/project_environment.rb +6 -2
- data/lib/brut/framework.rb +1 -1
- data/lib/brut/front_end/component.rb +69 -71
- data/lib/brut/front_end/components/constraint_violations.rb +1 -4
- data/lib/brut/front_end/components/form_tag.rb +1 -1
- data/lib/brut/front_end/components/input.rb +3 -3
- data/lib/brut/front_end/components/inputs/csrf_token.rb +1 -1
- data/lib/brut/front_end/components/inputs/{text_field.rb → input_tag.rb} +7 -9
- data/lib/brut/front_end/components/inputs/radio_button.rb +1 -1
- data/lib/brut/front_end/components/inputs/select_tag_with_options.rb +187 -0
- data/lib/brut/front_end/components/inputs/{textarea.rb → textarea_tag.rb} +2 -2
- data/lib/brut/front_end/components/time_tag.rb +2 -1
- data/lib/brut/front_end/form.rb +4 -4
- data/lib/brut/front_end/forms/input.rb +2 -1
- data/lib/brut/front_end/forms/input_definition.rb +5 -2
- data/lib/brut/front_end/forms/radio_button_group_input.rb +2 -1
- data/lib/brut/front_end/forms/radio_button_group_input_definition.rb +2 -2
- data/lib/brut/front_end/forms/select_input.rb +2 -4
- data/lib/brut/front_end/forms/select_input_definition.rb +2 -2
- data/lib/brut/front_end/handler.rb +28 -26
- data/lib/brut/front_end/handlers/csp_reporting_handler.rb +5 -2
- data/lib/brut/front_end/handlers/instrumentation_handler.rb +8 -4
- data/lib/brut/front_end/handlers/locale_detection_handler.rb +9 -5
- data/lib/brut/front_end/handlers/missing_handler.rb +5 -2
- data/lib/brut/front_end/layout.rb +16 -0
- data/lib/brut/front_end/page.rb +52 -29
- data/lib/brut/front_end/request_context.rb +3 -2
- data/lib/brut/front_end/routing.rb +5 -1
- data/lib/brut/front_end.rb +4 -13
- data/lib/brut/i18n/base_methods.rb +167 -79
- data/lib/brut/i18n/for_back_end.rb +4 -0
- data/lib/brut/i18n/for_cli.rb +4 -0
- data/lib/brut/i18n/for_html.rb +32 -4
- data/lib/brut/i18n/http_accept_language.rb +47 -0
- data/lib/brut/instrumentation/open_telemetry.rb +36 -1
- data/lib/brut/instrumentation.rb +3 -5
- data/lib/brut/sinatra_helpers.rb +11 -3
- data/lib/brut/spec_support/component_support.rb +30 -16
- data/lib/brut/spec_support/e2e_support.rb +1 -1
- data/lib/brut/spec_support/e2e_test_server.rb +3 -0
- data/lib/brut/spec_support/general_support.rb +3 -0
- data/lib/brut/spec_support/handler_support.rb +6 -1
- data/lib/brut/spec_support/matcher.rb +1 -0
- data/lib/brut/spec_support/matchers/be_page_for.rb +1 -0
- data/lib/brut/spec_support/matchers/have_html_attribute.rb +1 -0
- data/lib/brut/spec_support/matchers/have_i18n_string.rb +2 -5
- data/lib/brut/spec_support/matchers/have_link_to.rb +1 -0
- data/lib/brut/spec_support/matchers/have_redirected_to.rb +1 -0
- data/lib/brut/spec_support/matchers/have_rendered.rb +1 -0
- data/lib/brut/spec_support/matchers/have_returned_rack_response.rb +44 -0
- data/lib/brut/spec_support.rb +1 -1
- data/lib/brut/version.rb +1 -1
- data/lib/brut.rb +5 -4
- data/lib/sequel/extensions/brut_migrations.rb +1 -1
- metadata +648 -13
- data/doc-src/architecture.md +0 -102
- data/doc-src/assets.md +0 -98
- data/doc-src/forms.md +0 -214
- data/doc-src/handlers.md +0 -83
- data/doc-src/javascript.md +0 -265
- data/doc-src/pages.md +0 -210
- data/doc-src/route-hooks.md +0 -59
- data/lib/brut/front_end/components/inputs/select.rb +0 -117
@@ -0,0 +1,149 @@
|
|
1
|
+
/**
|
2
|
+
* Customize default theme styling by overriding CSS variables:
|
3
|
+
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
|
4
|
+
*/
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Colors
|
8
|
+
*
|
9
|
+
* Each colors have exact same color scale system with 3 levels of solid
|
10
|
+
* colors with different brightness, and 1 soft color.
|
11
|
+
*
|
12
|
+
* - `XXX-1`: The most solid color used mainly for colored text. It must
|
13
|
+
* satisfy the contrast ratio against when used on top of `XXX-soft`.
|
14
|
+
*
|
15
|
+
* - `XXX-2`: The color used mainly for hover state of the button.
|
16
|
+
*
|
17
|
+
* - `XXX-3`: The color for solid background, such as bg color of the button.
|
18
|
+
* It must satisfy the contrast ratio with pure white (#ffffff) text on
|
19
|
+
* top of it.
|
20
|
+
*
|
21
|
+
* - `XXX-soft`: The color used for subtle background such as custom container
|
22
|
+
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
|
23
|
+
* on top of it.
|
24
|
+
*
|
25
|
+
* The soft color must be semi transparent alpha channel. This is crucial
|
26
|
+
* because it allows adding multiple "soft" colors on top of each other
|
27
|
+
* to create a accent, such as when having inline code block inside
|
28
|
+
* custom containers.
|
29
|
+
*
|
30
|
+
* - `default`: The color used purely for subtle indication without any
|
31
|
+
* special meanings attached to it such as bg color for menu hover state.
|
32
|
+
*
|
33
|
+
* - `brand`: Used for primary brand colors, such as link text, button with
|
34
|
+
* brand theme, etc.
|
35
|
+
*
|
36
|
+
* - `tip`: Used to indicate useful information. The default theme uses the
|
37
|
+
* brand color for this by default.
|
38
|
+
*
|
39
|
+
* - `warning`: Used to indicate warning to the users. Used in custom
|
40
|
+
* container, badges, etc.
|
41
|
+
*
|
42
|
+
* - `danger`: Used to show error, or dangerous message to the users. Used
|
43
|
+
* in custom container, badges, etc.
|
44
|
+
* -------------------------------------------------------------------------- */
|
45
|
+
|
46
|
+
:root {
|
47
|
+
--vp-c-default-1: var(--vp-c-gray-1);
|
48
|
+
--vp-c-default-2: var(--vp-c-gray-2);
|
49
|
+
--vp-c-default-3: var(--vp-c-gray-3);
|
50
|
+
--vp-c-default-soft: var(--vp-c-gray-soft);
|
51
|
+
|
52
|
+
--blue-vp-c-brand-1: #0C00A7;
|
53
|
+
--blue-vp-c-brand-2: #6357FF;
|
54
|
+
--blue-vp-c-brand-3: #1000E2;
|
55
|
+
--blue-vp-c-brand-soft: #FAF9FF;
|
56
|
+
|
57
|
+
--vp-c-brand-1: #A70C00;
|
58
|
+
--vp-c-brand-2: #FF6357;
|
59
|
+
--vp-c-brand-3: #e21000;
|
60
|
+
--vp-c-brand-soft: #FFE7E5;
|
61
|
+
|
62
|
+
--xvp-c-brand-1: var(--vp-c-indigo-1);
|
63
|
+
--xvp-c-brand-2: var(--vp-c-indigo-2);
|
64
|
+
--xvp-c-brand-3: var(--vp-c-indigo-3);
|
65
|
+
--xvp-c-brand-soft: var(--vp-c-indigo-soft);
|
66
|
+
|
67
|
+
--vp-c-tip-1: var(--vp-c-brand-1);
|
68
|
+
--vp-c-tip-2: var(--vp-c-brand-2);
|
69
|
+
--vp-c-tip-3: var(--vp-c-brand-3);
|
70
|
+
--vp-c-tip-soft: var(--vp-c-brand-soft);
|
71
|
+
|
72
|
+
--vp-c-warning-1: var(--vp-c-yellow-1);
|
73
|
+
--vp-c-warning-2: var(--vp-c-yellow-2);
|
74
|
+
--vp-c-warning-3: var(--vp-c-yellow-3);
|
75
|
+
--vp-c-warning-soft: var(--vp-c-yellow-soft);
|
76
|
+
|
77
|
+
--vp-c-danger-1: var(--vp-c-red-1);
|
78
|
+
--vp-c-danger-2: var(--vp-c-red-2);
|
79
|
+
--vp-c-danger-3: var(--vp-c-red-3);
|
80
|
+
--vp-c-danger-soft: var(--vp-c-red-soft);
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* Component: Button
|
85
|
+
* -------------------------------------------------------------------------- */
|
86
|
+
|
87
|
+
:root {
|
88
|
+
--vp-button-brand-border: transparent;
|
89
|
+
--vp-button-brand-text: var(--vp-c-white);
|
90
|
+
--vp-button-brand-bg: var(--vp-c-brand-3);
|
91
|
+
--vp-button-brand-hover-border: transparent;
|
92
|
+
--vp-button-brand-hover-text: var(--vp-c-white);
|
93
|
+
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
|
94
|
+
--vp-button-brand-active-border: transparent;
|
95
|
+
--vp-button-brand-active-text: var(--vp-c-white);
|
96
|
+
--vp-button-brand-active-bg: var(--vp-c-brand-1);
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Component: Home
|
101
|
+
* -------------------------------------------------------------------------- */
|
102
|
+
|
103
|
+
:root {
|
104
|
+
--vp-home-hero-name-color: transparent;
|
105
|
+
--vp-home-hero-name-background: -webkit-linear-gradient(
|
106
|
+
120deg,
|
107
|
+
#333333 30%,
|
108
|
+
#d14141
|
109
|
+
);
|
110
|
+
|
111
|
+
--vp-home-hero-image-background-image: linear-gradient(
|
112
|
+
-45deg,
|
113
|
+
#ffe7e5,
|
114
|
+
#a70c00
|
115
|
+
);
|
116
|
+
--vp-home-hero-image-filter: blur(44px);
|
117
|
+
}
|
118
|
+
|
119
|
+
@media (min-width: 640px) {
|
120
|
+
:root {
|
121
|
+
--vp-home-hero-image-filter: blur(56px);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
@media (min-width: 960px) {
|
126
|
+
:root {
|
127
|
+
--vp-home-hero-image-filter: blur(68px);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
/**
|
132
|
+
* Component: Custom Block
|
133
|
+
* -------------------------------------------------------------------------- */
|
134
|
+
|
135
|
+
:root {
|
136
|
+
--vp-custom-block-tip-border: transparent;
|
137
|
+
--vp-custom-block-tip-text: var(--vp-c-text-1);
|
138
|
+
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
|
139
|
+
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Component: Algolia
|
144
|
+
* -------------------------------------------------------------------------- */
|
145
|
+
|
146
|
+
.DocSearch {
|
147
|
+
--docsearch-primary-color: var(--vp-c-brand-1) !important;
|
148
|
+
}
|
149
|
+
|
data/brutrb.com/ai.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# AI Declaration and Notes
|
2
|
+
|
3
|
+
LLMs and AI tools are a current fact of life. We feel it's important to be realistic and up front about
|
4
|
+
how they affect this project.
|
5
|
+
|
6
|
+
## Levels of AI Use
|
7
|
+
|
8
|
+
I created [declare-ai.org](https://declare-ai.org/) to allow precise documentation of how AI is used in a
|
9
|
+
project. It defines four levels of AI use:
|
10
|
+
|
11
|
+
1. No involvement at all.
|
12
|
+
2. Non-create assistance, such as the completions GitHub Copilot may make while you are typing.
|
13
|
+
3. Creative assistance, where an AI generated a large amount of code that you then modified. An example would be using a tool like Cursor to write code for your review.
|
14
|
+
4. Completely Produced by an AI, for example if you used Stable Diffusion to create your icon.
|
15
|
+
|
16
|
+
## Code
|
17
|
+
|
18
|
+
The entirety of Brut's codebase could be grouped into three parts:
|
19
|
+
|
20
|
+
* The library code developers use. This is, roughly, the code in `lib`, `brut-js/src`, and `brut-css/src`.
|
21
|
+
* Tests of the library code.
|
22
|
+
* Tools used to manage the library code, such as what's in `bin/`, `brut-js/bin`, `brut-css/bin`, etc.
|
23
|
+
|
24
|
+
### Library Code Should Be Written By a Person
|
25
|
+
|
26
|
+
We want the library code written by a person, i.e. it should have level 1 AI assistance: none. As of this writing—June 17, 2025—all library code was written by a person.
|
27
|
+
|
28
|
+
If some code is level 2—non-creative assistance—that is probably fine.
|
29
|
+
|
30
|
+
### Tests Should Not Be Totally Written by AI
|
31
|
+
|
32
|
+
As of this writing—June 17, 2025—the library has no automated tests. The `adrs.cloud` app serves as the
|
33
|
+
test suite.
|
34
|
+
|
35
|
+
That said, as tests are developed, they must never be written entirely by an AI without human review (level 4). Our preference is that all tests comply with level 1 or level 2, however we are open to creative assistance for tests as might be provided by a tool like Cursor.
|
36
|
+
|
37
|
+
In any case, the author of a test should understand it and the maintainers must be able to understand and
|
38
|
+
modify all tests without the use of AI tools.
|
39
|
+
|
40
|
+
### Management Tools Should Not Be Totally Written by AI
|
41
|
+
|
42
|
+
Like tests, management tools must be comprehensible by a person and the creator of the code must
|
43
|
+
understand it. That said, some of the tools were created with level 3 AI assistance, and we expect these
|
44
|
+
tools may continue to be created this way.
|
45
|
+
|
46
|
+
Nevertheless, this code must be reviewed and understood by a person.
|
47
|
+
|
48
|
+
## Documentation
|
49
|
+
|
50
|
+
We strive to have all documentation written by a real person. None of this documentation, or the API
|
51
|
+
documentation, is produced by a machine, although spelling and grammar corrections will certainly have been suggested by automated tools.
|
52
|
+
|
53
|
+
We expect an LLM to be able to digest this documentation and source code and provide alternate analysis of how Brut works and how to use it. We hope such analysis is correct and useful, however that cannot be guaranteed, so this documentation is the second best source of truth, the source code being the best.
|
54
|
+
|
55
|
+
## Logo
|
56
|
+
|
57
|
+
The logo is level 4 - ChatGPT created it. I'd love to have a real person make a better one. I just needed
|
58
|
+
something to get this launched. Please reach out if you want to make a better one + other assets. I'm
|
59
|
+
willing to pay a real person.
|
60
|
+
|
61
|
+
## AI That Teachs You About Brut Does Not Currently Exist
|
62
|
+
|
63
|
+
Due to how LLMs work, there is naturally nothing in any model about Brut. Anything an
|
64
|
+
existing model tells you about Brut is **100% untrustworthy**. We hope to allow LLMs to consume Brut's
|
65
|
+
code, documentation, and examples, so it can be an additional source of help, but currently that is not
|
66
|
+
the case.
|
67
|
+
|
68
|
+
**Do not ask an LLM about Brut** until this part of the documentation changes.
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# Assets
|
2
|
+
|
3
|
+
As mentioned in [Javascript](/javascript) and [CSS](/css), esbuild is used to bundle JavaScript and CSS. Brut also provides support for managing images.
|
4
|
+
|
5
|
+
## JavaScript and CSS
|
6
|
+
|
7
|
+
Both JavaScript and CSS are managed largely the same way: esbuild is given `app/src/front_end/js/index.js` or
|
8
|
+
`app/src/front_end/css/index.css` and a bundle is produced.
|
9
|
+
|
10
|
+
For both JS and CSS, the bundles are *hashed*, even in development. This is to reduce differences in production
|
11
|
+
and development. The `asset_path` helper can translate the logical path (`/js/app.js` or `/css/styles.css`) into
|
12
|
+
the specific hashed path.
|
13
|
+
|
14
|
+
Sourcemaps are provided as well, for both development and production.
|
15
|
+
|
16
|
+
### What is Hashing and Why Do It?
|
17
|
+
|
18
|
+
In production, while your pages produce dynamic data, the CSS and JavaScript bundles themselves are not dynamic.
|
19
|
+
They are the same for every single request until you change them. Because of this, it's common to configure a
|
20
|
+
cache for these files. Often, that cache is a *content delivery network* or CDN.
|
21
|
+
|
22
|
+
When a page is rendered, the browser will ask for the CSS and JS bundles. The CDN will tell the browser it's
|
23
|
+
OK to cache the file, potentially for a very long time (years). On subsequent requests for those files, the
|
24
|
+
browser will re-use its cached copy, saving bandwidth and time.
|
25
|
+
|
26
|
+
A downside of this approach is when you *do* want to change something. While most CDNs allow you to invalidate
|
27
|
+
their cached values, there are many layers of caching whose behavior can be hard to control. It turns out to be
|
28
|
+
much simpler to rename the file each you change it, thus "breaking the cache".
|
29
|
+
|
30
|
+
A common way to do this is to create a hash of the file's contents and append that value to its name, so instead
|
31
|
+
of `/static/css/styles.css`, the file would `/static/css/styles-98724fhjkjk.css`. When you make a change to
|
32
|
+
your CSS, it'll get new name, say `/static/css/styles-3yjgdrjksrfdws.css`.
|
33
|
+
|
34
|
+
To keep you from having to deal with this directly, Brut's `asset_path` helper will translate a logical name like
|
35
|
+
`/css/styles.css` to the actual name, like `/static/css/styles-3yjgdrjksrfdws.css`.
|
36
|
+
|
37
|
+
### What are SourceMaps and Why Create Them?
|
38
|
+
|
39
|
+
Bundled JavaScript and CSS will have been *minified*. This means removing whitespace, line breaks and, in the
|
40
|
+
case of JavaScript, potentially changing the actual names of classes and variables. This is all to reduce the
|
41
|
+
size of the file as much as possible without changing its meaning.
|
42
|
+
|
43
|
+
In your browser's dev tools, all your CSS is one the first line of `styles.css` and every stack trace from your
|
44
|
+
JavaScript is on line 1 of `app.js`. This is not helpful for diagnosing issues.
|
45
|
+
|
46
|
+
*SourceMaps* are separate files that translate the minified files back to normal ones, so you can see a normal
|
47
|
+
stack trace with the actual line numbers of your source files.
|
48
|
+
|
49
|
+
There are many ways to create source maps and if you've used a tool like WebPack, you'll recall that many of them
|
50
|
+
don't produce usable source maps. Brut uses esbuild's facility for this, with a focus on correctness. When
|
51
|
+
you see a line number and a source file in your browser, you can be sure it's accurate.
|
52
|
+
|
53
|
+
The tradeoff is that it can take longer to produce than producing an inaccurate one. I'm not sure who wants
|
54
|
+
inaccurate source maps, but Brut does not support this. In practice, esbuild is quite fast, so it should not make
|
55
|
+
a practical difference in your day to day work.
|
56
|
+
|
57
|
+
|
58
|
+
## Fonts
|
59
|
+
|
60
|
+
Custom fonts are managed implicitly by esbuild's managing of CSS. In your CSS, you should reference fonts as
|
61
|
+
relative to the CSS file. For example, if you have the font `app/src/front_end/fonts/monaspace-xenon.ttf`, then
|
62
|
+
your `app/src/front_end/css/index.css` should look like so:
|
63
|
+
|
64
|
+
```css {3}
|
65
|
+
@font-face {
|
66
|
+
font-family: "Monaspace Xenon";
|
67
|
+
src: url("../fonts/monaspace-xenon.ttf") format("truetype");
|
68
|
+
font-display: swap;
|
69
|
+
}
|
70
|
+
```
|
71
|
+
When CSS is bundled, esbuild will copy and hash the fonts, then rewrite the CSS to reference them.
|
72
|
+
|
73
|
+
Brut does not support font management in any other way.
|
74
|
+
|
75
|
+
## Images
|
76
|
+
|
77
|
+
Brut supports managing images, and will `rsync` them from `app/src/front_end/images` to
|
78
|
+
`app/public/static/images`, where you can reference them via `/static/images/«image_name»`.
|
79
|
+
|
80
|
+
Brut does not support hashing of image names.
|
81
|
+
|
82
|
+
You can reference images like so:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
def view_template
|
86
|
+
div do
|
87
|
+
# Renders app/src/front_end/images/foo.png
|
88
|
+
img src: "/static/images/foo.png", alt: "Picture of a foo"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
## SVGs
|
94
|
+
|
95
|
+
You can place `.svg` files in `app/src/front_end/images` if you wish to use them in `<img>` tags. However, if
|
96
|
+
you place svgs in `app/src/front_end/svgs`, they can be inlined into your HTML via `inline_svg`. In this case,
|
97
|
+
there is no need for a build step, since the SVG source is included directly in your HTML. This works well
|
98
|
+
for icons.
|
99
|
+
|
100
|
+
## `favicon.ico`
|
101
|
+
|
102
|
+
`Brut::FrontEnd::Middlewares::Favicon` is configured by default to handle requests for `/favicon.ico`. It
|
103
|
+
returns a 301 to `/static/images/favicon.ico`. This means that Brut expects
|
104
|
+
`app/src/front_end/images/favicon.ico` to exist.
|
105
|
+
|
106
|
+
|
107
|
+
## All Other Assets
|
108
|
+
|
109
|
+
Brut currently does not support any other managed asset. However, you can place files in `app/public/static` and
|
110
|
+
they will be served up directly.
|
111
|
+
|
112
|
+
|
113
|
+
## Technical Notes
|
114
|
+
|
115
|
+
> [!IMPORTANT]
|
116
|
+
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
117
|
+
> internals, the source code is always more correct.
|
118
|
+
|
119
|
+
_Last Updated May 7, 2025_
|
120
|
+
|
121
|
+
`bin/build-assets` manages all of the behavior described on this page. `bin/build-assets css` and
|
122
|
+
`bin/build-assets js` modify the file `app/config/asset_metadata.json`, which stores the mappings between logic
|
123
|
+
paths and hashed paths:
|
124
|
+
|
125
|
+
```json
|
126
|
+
{
|
127
|
+
"asset_metadata": {
|
128
|
+
".js": {
|
129
|
+
"/js/app.js": "/js/app-7MMIZXTZ.js"
|
130
|
+
},
|
131
|
+
".css": {
|
132
|
+
"/css/styles.css":"/css/styles-ZAISBLGE.css"
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
```
|
137
|
+
|
138
|
+
As you can see, this format could support multiple bundles and additional file types.
|
data/brutrb.com/bin/dev
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# BrutJS
|
2
|
+
|
3
|
+
Brut includes the JavaScript library *BrutJS* which provides HTML custom elements that are useful for
|
4
|
+
progressively enhancing your HTML.
|
5
|
+
|
6
|
+
## Overview
|
7
|
+
|
8
|
+
By default, your app is set up to use BrutJS. This is done by defining all the custom elements it
|
9
|
+
provides. Their source code is included in your JavaScript bundle, but they do not do anything until you
|
10
|
+
use one of the custom elements.
|
11
|
+
|
12
|
+
Here's what `app/src/front_end/js/index.js` looks like initially:
|
13
|
+
|
14
|
+
```javascript
|
15
|
+
import { BrutCustomElements } from "brut-js"
|
16
|
+
|
17
|
+
document.addEventListener("DOMContentLoaded", () => {
|
18
|
+
BrutCustomElements.define()
|
19
|
+
})
|
20
|
+
```
|
21
|
+
|
22
|
+
Further, `Brut::FrontEnd::Component` uses Phlex's `register_element` to register all the elements so you
|
23
|
+
can use them at will in components or pages:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
def view_template
|
27
|
+
form do
|
28
|
+
input type: "text", name: "name"
|
29
|
+
brut_confirm_submit message: "Are you sure?" do
|
30
|
+
button { "Submit" }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
### Custom Elements
|
37
|
+
|
38
|
+
The JSDoc for these elements' classes should provide complete documentation, however this is an overview
|
39
|
+
of what each one does.
|
40
|
+
|
41
|
+
| Element | Purpose |
|
42
|
+
|---|---|
|
43
|
+
| `<brut-ajax-submit>` | Allows submitting a form via Ajax. Handles the use of `fetch` and all possible cases, but you still provide the logic for what to do with the response. |
|
44
|
+
| `<brut-autosubmit>` | Auto submits a form when a `<select>`'s option is chosen. |
|
45
|
+
| `<brut-confirmation-dialog>` | Enhances a `<dialog>` to make it easier to use as a generic confirmation with `<brut-confirm-submit>` |
|
46
|
+
| `<brut-confirm-submit>` | Uses `window.confirm` or your owned styled `<dialog>` to confirm a button click. |
|
47
|
+
| `<brut-cv>` | Like `<brut-message>` but specific to constraint violations, namely having additional logic for subsituting the field name in the message. |
|
48
|
+
| `<brut-cv-messages>` | Wraps `<brut-cv>` elements related to a single form input. |
|
49
|
+
| `<brut-copy-to-clipboard>` | Allows the button inside it to copy text from another element onto the clipboard. |
|
50
|
+
| `<brut-form>` | Manages client-side constraint violation UX unified with the server-side, as well as a few quality-of-life improvements for client-side violations and styling. See [Forms](/forms#forms-and-constraint-violations).|
|
51
|
+
| `<brut-i18n-translation>` | Holds the translated value for a single key in the web site visitor's locale. |
|
52
|
+
| `<brut-locale-detection>` | Sends an Ajax request to the server with the browser's reported locale and timezone. See [space-time continuum](/space-time-continuum#getting-timezone-from-the-browser) for more details. |
|
53
|
+
| `<brut-message>` | Shows a message using an [i18n](/i18n) key to dynamically pull a localized message for client-side constraint violations. |
|
54
|
+
| `<brut-tabs>` | Uses ARIA roles related to a tab control and implements it client-side. |
|
55
|
+
| `<brut-tracing>` | Sends observability data back to the server to unify a server-side request with client-side tracing.|
|
56
|
+
|
57
|
+
> [!NOTE]
|
58
|
+
> BrutJS's elements were created only to solve specific issues in the apps Brut was initially used for.
|
59
|
+
> It's hoped that more elements will be added to provide a more feature-complete set of primitives to
|
60
|
+
> create client-side enhancements.
|
61
|
+
|
62
|
+
### Creating Your Own Custom Elements
|
63
|
+
|
64
|
+
BrutJS includes a base class, [`BaseCustomElement`](/brut-js/api/BaseCustomElement.html), you can use to
|
65
|
+
create your own custom elements with a bit more help, but not too much.
|
66
|
+
|
67
|
+
The documentation for `BaseCustomElement` has an example, but here are the features you get (noting that
|
68
|
+
you aren't abandoning the web platform's API, merely gaining a few additional quality-of-life
|
69
|
+
improvements):
|
70
|
+
|
71
|
+
* The ability to add debugging statements that are disabled via markup, not commenting-out `console.log`
|
72
|
+
* Per-attribute change callbacks so you don't have to create `attributeChangedCallback` as a giant `if/else` block.
|
73
|
+
* Default implementations of `connectedCallback` and `attributeChangedCallback` that call the template method `update`, thus allowing your element to centralize its logic in one place, regardless of how a state change was triggered.
|
74
|
+
* Static `define()` method that defines your element based on its static `tagName` field. This allows richer interaction of elements, as you can do e.g. `document.querySelector(SomeOtherElement.tagName)` and better navigate changes to your code over time.
|
75
|
+
|
76
|
+
If you are familiary with the API for autonomous custom elements, `BaseCustomElement` doesn't require
|
77
|
+
learning much more. What you know already will be leveraged.
|
78
|
+
|
79
|
+
### Removing BrutJS
|
80
|
+
|
81
|
+
To remove BrutJS from your app, modify `app/src/front_end/js/index.js` to remove the `import` and call to
|
82
|
+
`define()`. You can then remove it from your `package.json`.
|
83
|
+
|
84
|
+
**Note** If you remove it like this, several features will not work, including locale detection, client-side observability, and client-side form validation UX.
|
85
|
+
|
86
|
+
## Recommnded Practices
|
87
|
+
|
88
|
+
Consider this decision tree from Alex Russell's [If Not React, Then
|
89
|
+
What?](https://infrequently.org/2024/11/if-not-react-then-what/):
|
90
|
+
|
91
|
+

|
92
|
+
|
93
|
+
This is how Brut wants you to consider your app's architecture. *Many* apps do not have long-running
|
94
|
+
sessions where visitors make lots of updates to data. Most so-called "CRUD" apps do not fall into this
|
95
|
+
category. The visitor would be better served by a traditional app with server-side HTML generation and
|
96
|
+
minimal interactivity. Visitors would also be better served with progressively enhanced features instead
|
97
|
+
of massive JS payloads that show white screens on low bandwidth/low performance devices.
|
98
|
+
|
99
|
+
Thus, Brut recommends you design your app to work in a tranditional multi-page app sort of way, then
|
100
|
+
*enhance* as needed using autonomous custom elements.
|
101
|
+
|
102
|
+
You can, of course, bring in whatever framework you like and use that in the normal way. BrutJS's custom
|
103
|
+
elements should work with any framework.
|
104
|
+
|
105
|
+
## Testing
|
106
|
+
|
107
|
+
See [Testing Custom Elements](/custom-element-tests).
|
108
|
+
|
109
|
+
## Technical Notes
|
110
|
+
|
111
|
+
> [!IMPORTANT]
|
112
|
+
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
113
|
+
> internals, the source code is always more correct.
|
114
|
+
|
115
|
+
_Last Updated June 15, 2025_
|
116
|
+
|
117
|
+
None.
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Business or Domain Logic
|
2
|
+
|
3
|
+
Since business and domain logic is not required, nor encouraged, to go in your data models, where does it go?
|
4
|
+
|
5
|
+
It can go anywhere you like - Brut is not currently prescriptive.
|
6
|
+
|
7
|
+
## Overview
|
8
|
+
|
9
|
+
It's recommended that you place classes in `app/src/back_end`. Any directory created in there will be
|
10
|
+
auto-loaded by Brut/Zeitwerk based on its rules. This means that classes in any directory in `app/src/back_end`
|
11
|
+
should not be namespaced.
|
12
|
+
|
13
|
+
For example, if you have *commands* and *queries*, you may want them in `app/src/back_end/commands` and
|
14
|
+
`app/src/back_end/queries`, respectively. In this case, `NewOrganizationCommand` would go in
|
15
|
+
`app/src/back_end/commands/new_organization_command.rb` and `AccountQuery` would go in
|
16
|
+
`app/src/back_end/queries/account_query.rb`
|
17
|
+
|
18
|
+
Or, you could could organize them into `app/src/back_end/business_logic/commands` and `app/src/back_end/business_logic/queries`. In that case, `Commands::NewOrganization` would be expected in `app/src/back_end/business_logic/commands/new_organization.rb` and `Queries::Account` would be expected in `app/src/back_end/business_logic/queries/account.rb`.
|
19
|
+
|
20
|
+
How you organize it is up to you.
|
21
|
+
|
22
|
+
## Testing
|
23
|
+
|
24
|
+
Tests can be written using RSpec in the usual way. Spec files should be in
|
25
|
+
`specs/back_end/path/to/class.spec.rb`. Note that Brut expects Spec files to end in `.spec.rb` and *not*
|
26
|
+
`_spec.rb`.
|
27
|
+
|
28
|
+
There is nothing special about the tests for your domain logic. Write them however you need to.
|
29
|
+
|
30
|
+
## Recomended Practices
|
31
|
+
|
32
|
+
This could be an entire series of books. The main recommendation is to take a light approach and don't
|
33
|
+
immediately install complex frameworks or libraries unless you know you need them.
|
34
|
+
|
35
|
+
Something somewhere in your business logic will need to interface with your front-end. The simplest way to do
|
36
|
+
that is to allow form objects to be passed into your back-end. The second simplest way is to pass form values
|
37
|
+
into your back-end.
|
38
|
+
|
39
|
+
You are strongly discouraged from having your front-end locate data models and pass those to your back-end. This
|
40
|
+
can be done while prototyping and for fast iteration on a concept, but generally you do not want to query your
|
41
|
+
database from your handlers or pages just to pass the results into a back-end class.
|
42
|
+
|
43
|
+
Exposing data models to the front-end is generally OK, as that is sometimes what you need to do.
|
44
|
+
|
45
|
+
## Technical Notes
|
46
|
+
|
47
|
+
> [!IMPORTANT]
|
48
|
+
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
49
|
+
> internals, the source code is always more correct.
|
50
|
+
|
51
|
+
_Last Updated May 7, 2025_
|
52
|
+
|
53
|
+
Creating universal abstractions from business logic is difficult. Brut is unlikely to do this. If it does, it
|
54
|
+
will be after much analysis of exsiting patterns and *only* if it helps avoid mistakes and increases developer
|
55
|
+
throughput.
|