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,257 @@
|
|
1
|
+
# Configuration
|
2
|
+
|
3
|
+
Brut strives to avoid configuration and flexibility. Much of Brut's behavior is convention-based, however
|
4
|
+
several aspects of Brut's behavior relate to literal values like file paths. Some of this set up is designed to
|
5
|
+
be overridden.
|
6
|
+
|
7
|
+
## Overview
|
8
|
+
|
9
|
+
Any configured value, or value otherwise intended to be abstracted from its literal value, is available via
|
10
|
+
`Brut.container`, which returns an instance of `Brut::Framework::Container`. This class uses `method_missing` to
|
11
|
+
provide direct access to configured values. It can also be used to store or override configured values.
|
12
|
+
|
13
|
+
An instance of `Brut::Framework::Config` is used to set up all of Brut's initial values. This file is a good
|
14
|
+
reference for determining the literal values of various configuration options.
|
15
|
+
|
16
|
+
### Basics of Configuration
|
17
|
+
|
18
|
+
The configuration system can be used to set literal values, or lazily-derived values. The method `store` is used
|
19
|
+
for both purposes. Lazy values are managed by calling `store` with a block.
|
20
|
+
|
21
|
+
::: code-group
|
22
|
+
|
23
|
+
```ruby [Storing Litral Values]
|
24
|
+
Brut.container.store(
|
25
|
+
"database_url",
|
26
|
+
String,
|
27
|
+
"URL to the primary database"
|
28
|
+
ENV.fetch("DATABASE_URL")
|
29
|
+
)
|
30
|
+
```
|
31
|
+
|
32
|
+
```ruby [Storing Lazy Values]
|
33
|
+
Brut.container.store(
|
34
|
+
"database_url",
|
35
|
+
String,
|
36
|
+
"URL to the primary database"
|
37
|
+
) do
|
38
|
+
# Only evaluated when Brut.container.database_url is called
|
39
|
+
ENV.fetch("DATABASE_URL")
|
40
|
+
end
|
41
|
+
```
|
42
|
+
:::
|
43
|
+
|
44
|
+
The configuration system also enables dependencies between values. For example, the Sequel connection to the database requires the URL to the database. Since `database_url` is configured seperately, `sequel_db_handle` depends on its value. It declares this by declaring a block parameter with the same name as the configuration option, `database_url` in this case.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
Brut.container.store(
|
48
|
+
"sequel_db_handle",
|
49
|
+
Object,
|
50
|
+
"Handle to the database",
|
51
|
+
) do |database_url|
|
52
|
+
Sequel.connect(database_url)
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
When `Brut.container.sequel_db_handle` is called, the block is examined. Brut sees that it has a parameter named
|
57
|
+
`"database_url"`, and will then call `Brut.container.database_url` to fetch the value and pass it in.
|
58
|
+
|
59
|
+
This reduces repetition amongst the various configuration options.
|
60
|
+
|
61
|
+
### Special Types of Configuration
|
62
|
+
|
63
|
+
Brut's configuration system enforces some rules, and may enforce more in the future. It's a strong desire that no
|
64
|
+
Brut app even boot if the configuration is not usable.
|
65
|
+
|
66
|
+
Many of Brut's configuration options are paths. To avoid having to have a bunch of `.keep` files all over the
|
67
|
+
plces, Brut allows storing an *ensured path*, which Brut will created when needed.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
Brut.container.store_ensured_path(
|
71
|
+
"images_src_dir",
|
72
|
+
"Path to where images are stored"
|
73
|
+
) do |front_end_src_dir|
|
74
|
+
front_end_src_dir / "images"
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
If your Brut app has no images at all, when `Brut.container.images_src_dir` is accessed, the path to where the
|
79
|
+
images should go will be created.
|
80
|
+
|
81
|
+
Some paths *must* exist in advance. For those, `store_required_path` can be used. It throws an error if the path
|
82
|
+
does not exist:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
Brut.container.store_required_path(
|
86
|
+
"pages_src_dir",
|
87
|
+
"Path to where page classes and templates are stored"
|
88
|
+
) do |front_end_src_dir|
|
89
|
+
front_end_src_dir / "pages"
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
### Type and Name Enforcement
|
94
|
+
|
95
|
+
You'll note that `store`, accepts a class parameter. This is mostly used for documentation, with two exceptions:
|
96
|
+
|
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
|
+
* 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`.
|
99
|
+
|
100
|
+
Brut may add more constraints or conversions over time.
|
101
|
+
|
102
|
+
### Overridable and `nil`able Values
|
103
|
+
|
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
|
+
|
106
|
+
In [Flash and Session](/flash-and-session), we discussed setting your own custom class for managing the flash.
|
107
|
+
This is possible due to how Brut defines the configuration parameter `flash_class`:
|
108
|
+
|
109
|
+
```ruby {6}
|
110
|
+
Brut.container.store(
|
111
|
+
"flash_class",
|
112
|
+
Class,
|
113
|
+
"Class to use to represent the Flash",
|
114
|
+
Brut::FrontEnd::Flash,
|
115
|
+
allow_app_override: true,
|
116
|
+
)
|
117
|
+
```
|
118
|
+
|
119
|
+
Since `allow_app_override` is true, you can call `override` in your `App`:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
Brut.container.override("flash_class",AppFlash)
|
123
|
+
```
|
124
|
+
|
125
|
+
Calling `override` for parameters where `allow_app_override` is not true results in an error. Further, calling
|
126
|
+
`store` on a previously `store`-d parameter results in an error.
|
127
|
+
|
128
|
+
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.
|
130
|
+
|
131
|
+
Some values can be `nil`. Generally, `nil` is a pain and will cause you great hardship. On occasion, it's
|
132
|
+
needed. For example, [external IDs](/database-schema#external-ids) only work if the app provides an app-wide
|
133
|
+
prefix.
|
134
|
+
|
135
|
+
Here is how Brut sets this up by default:
|
136
|
+
|
137
|
+
```ruby {5-7}
|
138
|
+
Brut.container.store(
|
139
|
+
"external_id_prefix",
|
140
|
+
String,
|
141
|
+
"String to use as a prefix for external ids in tables using the external_id feature. Nil means the feature is disabled",
|
142
|
+
nil,
|
143
|
+
allow_app_override: true,
|
144
|
+
allow_nil: true,
|
145
|
+
)
|
146
|
+
```
|
147
|
+
|
148
|
+
Brut defaults this to `nil`, meaning there is no external ID prefix to be used. Apps can opt into this behavior
|
149
|
+
by overriding the value:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
Brut.container.override("external_id_prefix","cc")
|
153
|
+
```
|
154
|
+
|
155
|
+
Conversly, apps can opt *out* of [Content Security Policy Reporting](/security). By default, Brut sets up its own
|
156
|
+
reporting hook:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
Brut.container.store(
|
160
|
+
"csp_reporting_class",
|
161
|
+
Class,
|
162
|
+
"Route Hook to use for setting the Content-Security-Policy-Report-Only header",
|
163
|
+
Brut::FrontEnd::RouteHooks::CSPNoInlineStylesOrScripts::ReportOnly,
|
164
|
+
allow_app_override: true,
|
165
|
+
allow_nil: true,
|
166
|
+
)
|
167
|
+
```
|
168
|
+
|
169
|
+
If you don't want this, you can override it and set it to `nil`:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
Brut.container.override("csp_reporting_class",nil)
|
173
|
+
```
|
174
|
+
|
175
|
+
`allow_nil` is a good indicator that Brut can handle a `nil` value for that configuration parameter—it wouldn't
|
176
|
+
allow it if couldn't.
|
177
|
+
|
178
|
+
### Setting Your Own Configuration Values
|
179
|
+
|
180
|
+
In your `App` class, you can call `store` as needed to setup configuration values relevant to your app. For
|
181
|
+
example, if you are using SendGrid to send emails, you may want to set the client up during startup:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
# app/src/app.rb
|
185
|
+
class App < Brut::Framework::App
|
186
|
+
|
187
|
+
# ...
|
188
|
+
|
189
|
+
def initialize
|
190
|
+
|
191
|
+
# ...
|
192
|
+
|
193
|
+
Brut.container.store(
|
194
|
+
"send_grid_client",
|
195
|
+
SendGrid::API,
|
196
|
+
"SendGrid API Client",
|
197
|
+
SendGrid::API.new(api_key: ENV["SENDGRID_API_KEY"])
|
198
|
+
)
|
199
|
+
|
200
|
+
# ...
|
201
|
+
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
Then, elsewhere in your app, you can call `Brut.container.send_grid_client` to access this object. Because
|
207
|
+
`allow_nil` is not specified (and therefore false), your app can confidently rely on a value being returned.
|
208
|
+
|
209
|
+
## Testing
|
210
|
+
|
211
|
+
Do not test configuration. Ideally, the configuration values for production are the same as for dev and test.
|
212
|
+
If you really cannot use the production value for something in dev or test, you can have your value depend on
|
213
|
+
`project_env`, which is a `Brut::Framework::ProjectEnvironment`:
|
214
|
+
|
215
|
+
```ruby {4-10}
|
216
|
+
Brut.container.store(
|
217
|
+
"send_grid_client",
|
218
|
+
SendGrid::API,
|
219
|
+
"SendGrid API Client") do |project_env|
|
220
|
+
if project_env.testing?
|
221
|
+
FakeSendGrid.new
|
222
|
+
else
|
223
|
+
SendGrid::API.new(api_key: ENV["SENDGRID_API_KEY"])
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
```
|
228
|
+
|
229
|
+
## Recommended Practices
|
230
|
+
|
231
|
+
Try to avoid project environment-specific behavior. The less of that you have, the more confidence you will have
|
232
|
+
that your app will boot in production.
|
233
|
+
|
234
|
+
In genreal, configure custom parameters only when you want them to be a check on app startup. For example, we
|
235
|
+
don't want our app to even start if `SENDGRID_API_KEY` is not in the environment. If we defered instantiating
|
236
|
+
`send_grid_client` until the first time we tried to send email, we wouldn't notice things were broken.
|
237
|
+
|
238
|
+
Conversely, do not place every object you could ever imagine into the container. `Brut::Framework::Container` is
|
239
|
+
not intended as a fully armed and operational dependency injection container. Perhaps someday, but not today.
|
240
|
+
|
241
|
+
Lastly, do not `store` or `override` outside of your `App` class' initializer. It will be confusing and
|
242
|
+
`override` may not work as expected.
|
243
|
+
|
244
|
+
## Technical Notes
|
245
|
+
|
246
|
+
> [!IMPORTANT]
|
247
|
+
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
248
|
+
> internals, the source code is always more correct.
|
249
|
+
|
250
|
+
_Last Updated May 7, 2025_
|
251
|
+
|
252
|
+
`Brut::Framework::Config` is created and executed in the initializer of `Brut::Framework::MCP`, which happens
|
253
|
+
before your `App`'s initializer is run. This also happens when CLI apps run. Brut tries very hard not to make
|
254
|
+
network connections inside `Brut::Framework::Config#configure!` for just this reason.
|
255
|
+
|
256
|
+
`Brut::Framework::Container` does not really check for circular dependeices, so please try to avoid making them.
|
257
|
+
If this happens, there will be a stack overflow.
|
data/brutrb.com/css.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# CSS
|
2
|
+
|
3
|
+
Brut provides basic bundling using [esbuild](https://esbuild.github.io/). You can organize your CSS across
|
4
|
+
multiple files, and you can bring in third party CSS libraries, as long as they are available from NPM (though you can always just download `.css` files manually)
|
5
|
+
|
6
|
+
## Managing Your App's CSS
|
7
|
+
|
8
|
+
All your app's CSS lives in `app/src/front_end/css`, or in modules you bring in via `package.json`. Brut
|
9
|
+
will *bundle* all of that up into a single `.css` file that is served up with your app. Brut does this by using
|
10
|
+
esbuild, a stable and standardized tool for bundling CSS.
|
11
|
+
|
12
|
+
The way esbuild works is to be given an *entry point* that requires, or transitively requires, all of your
|
13
|
+
CSS by using the standard [`@import` directive](https://developer.mozilla.org/en-US/docs/Web/CSS/@import). `app/src/front_end/css/index.css` is the entry point for your app.
|
14
|
+
|
15
|
+
> [!NOTE]
|
16
|
+
> The reason to bundle CSS instead of allowing browsers to manage the `@import`
|
17
|
+
> directives is to create a single download that is hashed for use with CDNs. This simplifies
|
18
|
+
> deployment and ensures all your CSS is available everywhere. The trade-off is that you cannot
|
19
|
+
> easily fine-tune each page's CSS.
|
20
|
+
|
21
|
+
Let's see an example. Let's say you are using the "foobar-css" CSS framework, and have placed some of your CSS in `app/src/front_end/css/pages/HomePage.css`.
|
22
|
+
|
23
|
+
First, `package.json` (in your app's root) would include `"foobar-css"`:
|
24
|
+
|
25
|
+
```json {6}
|
26
|
+
{
|
27
|
+
"name": "your-app",
|
28
|
+
"type": "module",
|
29
|
+
"license": "UNLICENSED",
|
30
|
+
"dependencies": {
|
31
|
+
"foobar-css": "^0.0.11"
|
32
|
+
},
|
33
|
+
"devDependencies": {
|
34
|
+
"chokidar-cli": "^3.0.0",
|
35
|
+
"esbuild": "^0.20.2",
|
36
|
+
"jsdom": "^25.0.1",
|
37
|
+
"mocha": "^10.7.3",
|
38
|
+
"playwright": "^1.50.1"
|
39
|
+
},
|
40
|
+
}
|
41
|
+
```
|
42
|
+
|
43
|
+
Next, `app/src/front_end/css/index.css` would import both `"foobar-css"` and `"pages/HomePage.css"`.
|
44
|
+
|
45
|
+
```javascript
|
46
|
+
@import "foobar-css/everything.css";
|
47
|
+
@import "pages/HomePage.css";
|
48
|
+
```
|
49
|
+
|
50
|
+
In the browser `@import` accepts any URL and will resolve it when the file is loaded. In Brut, when bundled using esbuild, these strings are relative paths to files in either `node_modules/` or `app/src/front_end/css/`. Unlike JavaScript imports, you don't need `"./"` to differentiate the two. Also unlike JavaScript imports, these must be the first lines of your `index.css` file or they will be ignored. All imports must come before any CSS.
|
51
|
+
|
52
|
+
## Importing Third Party CSS
|
53
|
+
|
54
|
+
Because there are so many ways to use NPM modules, it's not common to find reliable documentation on how to use a CSS library you bring in via NPM/`package.json`.
|
55
|
+
|
56
|
+
The key to figuring out what to use with `@import` is that the value is relative to `node_modules` and should an actual filename, including path and extension. You can figure this out by looking inside `node_modules` after you've done `bin/setup` (or `npm install`).
|
57
|
+
|
58
|
+
Suppose "foobar-css" has this directory structure:
|
59
|
+
|
60
|
+
```
|
61
|
+
node_modules/
|
62
|
+
foobar-css/
|
63
|
+
css/
|
64
|
+
foobar-all.css
|
65
|
+
foobar-thin.css
|
66
|
+
```
|
67
|
+
|
68
|
+
To use `foobar-thin.css`, you'd write this `@import` directive:
|
69
|
+
|
70
|
+
```css
|
71
|
+
@import "foobar-css/css/foobar-thin.css";
|
72
|
+
```
|
73
|
+
|
74
|
+
## Using Brut-CSS
|
75
|
+
|
76
|
+
By default, Brut includes a lightweight functional CSS library called "brut-css". It provides a basic design system and single-purpose classes to allow you to quickly prototype or build UIs. It is similar to TailwindCSS by far far smaller and simpler (and less powered).
|
77
|
+
|
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
|
+
|
80
|
+
To remove it, remove this line from `index.css`
|
81
|
+
|
82
|
+
```css
|
83
|
+
@import "brut-css/brut.css"; /* [!code --] */
|
84
|
+
@import "your/css/here.css";
|
85
|
+
```
|
86
|
+
|
87
|
+
## Using TailwindCSS
|
88
|
+
|
89
|
+
TBD
|
90
|
+
|
91
|
+
## Technical Notes
|
92
|
+
|
93
|
+
> [!IMPORTANT]
|
94
|
+
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
95
|
+
> internals, the source code is always more correct.
|
96
|
+
|
97
|
+
_Last Updated May 8, 2025_
|
98
|
+
|
99
|
+
Currently, Brut only supports a single entry point and bundle. This could be easily made more flexible if there
|
100
|
+
is a desire to finely tweak the CSS loaded on specific pages.
|
101
|
+
|
102
|
+
Brut also does not expose any esbuild configuration. This could be provided in the future, but for now, it is
|
103
|
+
hard-coded.
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# Testing Custom Elements
|
2
|
+
|
3
|
+
While simple custom elements can be tested as part of an [end-to-end test](/end-to-end-tests), more
|
4
|
+
complex custom elements can benefit from a unit test. Spoiler: this is not going to be pleasant.
|
5
|
+
|
6
|
+
## Overview
|
7
|
+
|
8
|
+
BrutJS provides a testing module that uses JSDom to allow you to test your custom elements. There are
|
9
|
+
downsides to JSDom, but it's the simplest way to achieve a reasonably-useful unit test.
|
10
|
+
|
11
|
+
You can use `bin/scaffold` to create a test, which will create a `.spec.js` file in `specs/front_end/js/`.
|
12
|
+
Suppose we the custom element `MyElement`:
|
13
|
+
|
14
|
+
```
|
15
|
+
> bin/scaffold custom_element_test app/src/front_end/js/MyElement.js
|
16
|
+
```
|
17
|
+
|
18
|
+
This creates `specs/front_end/js/MyElement.spec.js`:
|
19
|
+
|
20
|
+
```javascript
|
21
|
+
import { withHTML } from "brut-js/testing/index.js"
|
22
|
+
|
23
|
+
describe("<some-element>", () => {
|
24
|
+
withHTML(`
|
25
|
+
<my-element>
|
26
|
+
</my-element>
|
27
|
+
`).test("description here", ({document,window,assert}) => {
|
28
|
+
assert.fail("test goes here")
|
29
|
+
})
|
30
|
+
})
|
31
|
+
```
|
32
|
+
|
33
|
+
`withHTML` creates a JSDom-based document with the given HTML. This HTML is in effect inside the test.
|
34
|
+
Mock versions of `document` and `window` are passed to the test, and any other functions you need can be
|
35
|
+
as well, such as `assert`.
|
36
|
+
|
37
|
+
The idea is that you use the browser APIs to examine the DOM and assert the behavior of the custom element
|
38
|
+
(as opposed to interacting with the custom element's class).
|
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.
|
42
|
+
|
43
|
+
This means you'll need three tests, each with a different DOM:
|
44
|
+
|
45
|
+
```javascript
|
46
|
+
import { withHTML } from "brut-js/testing/index.js"
|
47
|
+
|
48
|
+
describe("<some-element>", () => {
|
49
|
+
withHTML(`
|
50
|
+
<my-element>
|
51
|
+
Some Text
|
52
|
+
</my-element>
|
53
|
+
`).test("lower-cases by default", ({document,window,assert}) => {
|
54
|
+
// TBD
|
55
|
+
})
|
56
|
+
|
57
|
+
withHTML(`
|
58
|
+
<my-element transform="lower">
|
59
|
+
Some Text
|
60
|
+
</my-element>
|
61
|
+
`).test("lower-cases explicitly", ({document,window,assert}) => {
|
62
|
+
// TBD
|
63
|
+
})
|
64
|
+
|
65
|
+
withHTML(`
|
66
|
+
<my-element transform="upper">
|
67
|
+
Some Text
|
68
|
+
</my-element>
|
69
|
+
`).test("upper-cases explicitly", ({document,window,assert}) => {
|
70
|
+
// TBD
|
71
|
+
})
|
72
|
+
})
|
73
|
+
```
|
74
|
+
|
75
|
+
when the function you give to `test` is executed, the DOM will have been setup, so you can rely on your
|
76
|
+
custom elements `connectedCallback` having been called. Assuming the text transformation for `my-element`
|
77
|
+
occurs in `connectedCallback`, here is how you'd test all three cases:
|
78
|
+
|
79
|
+
```javascript {9,10,18,19,27,28}
|
80
|
+
import { withHTML } from "brut-js/testing/index.js"
|
81
|
+
|
82
|
+
describe("<some-element>", () => {
|
83
|
+
withHTML(`
|
84
|
+
<my-element>
|
85
|
+
Some Text
|
86
|
+
</my-element>
|
87
|
+
`).test("lower-cases by default", ({document,window,assert}) => {
|
88
|
+
const element = document.querySelector("my-element")
|
89
|
+
assert.equal(element.textContent.trim(),"some text")
|
90
|
+
})
|
91
|
+
|
92
|
+
withHTML(`
|
93
|
+
<my-element transform="lower">
|
94
|
+
Some Text
|
95
|
+
</my-element>
|
96
|
+
`).test("lower-cases explicitly", ({document,window,assert}) => {
|
97
|
+
const element = document.querySelector("my-element")
|
98
|
+
assert.equal(element.textContent.trim(),"some text")
|
99
|
+
})
|
100
|
+
|
101
|
+
withHTML(`
|
102
|
+
<my-element transform="upper">
|
103
|
+
Some Text
|
104
|
+
</my-element>
|
105
|
+
`).test("upper-cases explicitly", ({document,window,assert}) => {
|
106
|
+
const element = document.querySelector("my-element")
|
107
|
+
assert.equal(element.textContent.trim(),"SOME TEXT")
|
108
|
+
})
|
109
|
+
})
|
110
|
+
```
|
111
|
+
|
112
|
+
You'll notice almost all of this uses the browser APIs you (should :) know and (hopefully :) love.
|
113
|
+
|
114
|
+
You can manipulate the DOM inside a test as well, and it should behave as if you are doing it in a
|
115
|
+
browser. Note that many browser APIs are synchronous, so you don't have to add `await` before every
|
116
|
+
single line of code.
|
117
|
+
|
118
|
+
Note that all of these test run under NodeJS, which is different from a browser. This means that code
|
119
|
+
like `new InputEvent()` will succeed in returning an `InputEvent`, but said object is in no way the
|
120
|
+
`InputEvent` you'd use in a browser. You must use `window.`:
|
121
|
+
|
122
|
+
```javascript
|
123
|
+
// does not work, but doesn't raise an error either
|
124
|
+
input.dispatchEvent(new InputEvent("input", {}))
|
125
|
+
|
126
|
+
// works
|
127
|
+
input.dispatchEvent(new window.InputEvent("input", {}))
|
128
|
+
```
|
129
|
+
|
130
|
+
## Recommended Practices
|
131
|
+
|
132
|
+
The custom element test library is *very* basic. Testing asychronous things like `fetch` is extremely
|
133
|
+
difficult. Your best bet is to use these tests for edge cases and error conditions.
|
134
|
+
|
135
|
+
|
136
|
+
## Technical Notes
|
137
|
+
|
138
|
+
> [!IMPORTANT]
|
139
|
+
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
140
|
+
> internals, the source code is always more correct.
|
141
|
+
|
142
|
+
_Last Updated June 13, 2025_
|
143
|
+
|
144
|
+
I will be honest with you, this part of Brut needs a lot of work and thinking-through. It's way to
|
145
|
+
DSL-tasitc for my tastes, but it does work for some needs. JSDom is not ideal and requires a lot of hoops
|
146
|
+
when using events or anything browsers support that it does not.
|
147
|
+
|
148
|
+
This is highly likely to change. My current thinking on addressing the need is to run the tests in a real
|
149
|
+
browser and to make the test setup and code more like what you'd actually write when using these elements.
|