brut 0.0.21 → 0.0.23
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/brutrb.com/keyword-injection.md +237 -0
- 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/cli/app_runner.rb +1 -1
- data/lib/brut/cli/apps/test.rb +5 -0
- data/lib/brut/framework/mcp.rb +0 -4
- data/lib/brut/front_end/component.rb +59 -84
- 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} +6 -8
- 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/page.rb +4 -6
- data/lib/brut/front_end/request_context.rb +3 -2
- data/lib/brut/i18n/base_methods.rb +136 -82
- data/lib/brut/i18n/for_back_end.rb +1 -0
- data/lib/brut/i18n/for_cli.rb +1 -0
- data/lib/brut/i18n/for_html.rb +32 -4
- data/lib/brut/instrumentation/open_telemetry.rb +12 -2
- data/lib/brut/sinatra_helpers.rb +10 -3
- data/lib/brut/spec_support/component_support.rb +18 -18
- data/lib/brut/spec_support/e2e_test_server.rb +3 -0
- data/lib/brut/version.rb +1 -1
- data/lib/sequel/extensions/brut_migrations.rb +12 -9
- metadata +647 -5
- data/lib/brut/front_end/components/inputs/select.rb +0 -117
@@ -28,7 +28,6 @@ end
|
|
28
28
|
#
|
29
29
|
# Becuase this is a Phlex component, you must implement `view_template` and make calls to Phlex's API to create
|
30
30
|
# the markup for your component.
|
31
|
-
#
|
32
31
|
class Brut::FrontEnd::Component < Phlex::HTML
|
33
32
|
|
34
33
|
include Brut::Framework::Errors
|
@@ -51,96 +50,72 @@ class Brut::FrontEnd::Component < Phlex::HTML
|
|
51
50
|
register_element :brut_tabs
|
52
51
|
register_element :brut_tracing
|
53
52
|
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# Include a {Brut::FrontEnd::Components::FormTag} in your markup.
|
76
|
-
def form_tag(**args, &block)
|
77
|
-
render Brut::FrontEnd::Components::FormTag.new(**args,&block)
|
78
|
-
end
|
79
|
-
|
80
|
-
# Include a component in your markup that you would like Brut to instantiate.
|
81
|
-
# This will use keyword injection to create the component, which means that if the component
|
82
|
-
# doesn't require any data from this component, you do not need to pass through those values.
|
83
|
-
# For example, you may have a component that renders the flash message. To avoid requiring your component to
|
84
|
-
# be passed the flash, a global component can be injected with it from Brut.
|
85
|
-
def global_component(component_klass)
|
86
|
-
render Brut::FrontEnd::RequestContext.inject(component_klass)
|
87
|
-
end
|
53
|
+
# Module for the various "free methods" available to all components.
|
54
|
+
# Generally, we don't want to build up mega-classes with lots of modules, but
|
55
|
+
# this provides a nice, singular namespace to document the helpers as apart
|
56
|
+
# from the various methods of the component.
|
57
|
+
module Helpers
|
58
|
+
# Render an inline an SVG that is part of your app. **Note** this does not
|
59
|
+
# return the SVG's contents, but it renders it into the current Phlex
|
60
|
+
# context.
|
61
|
+
#
|
62
|
+
# @param [String] svg path to the SVG file, relative to where SVGs are
|
63
|
+
# stored, which is `app/src/front_end/svgs` or where `Brut.container.svg_locator` is
|
64
|
+
# looking
|
65
|
+
#
|
66
|
+
# @see Brut::FrontEnd::InlineSvgLocator
|
67
|
+
def inline_svg(svg)
|
68
|
+
Brut.container.svg_locator.locate(svg).then { |svg_file|
|
69
|
+
File.read(svg_file)
|
70
|
+
}.then { |svg_content|
|
71
|
+
raw(safe(svg_content))
|
72
|
+
}
|
73
|
+
end
|
88
74
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
)
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
# Create an HTML input tag for the given input of a form. This is a convieniece method
|
103
|
-
# that calls {Brut::FrontEnd::Components::Inputs::TextField.for_form_input}.
|
104
|
-
def input_tag(form:, input_name:, index: nil, **html_attributes)
|
105
|
-
render(
|
106
|
-
Brut::FrontEnd::Components::Inputs::TextField.for_form_input(
|
107
|
-
form:,
|
108
|
-
input_name:,
|
109
|
-
index:,
|
110
|
-
html_attributes:)
|
111
|
-
)
|
75
|
+
# Return a component that you would like Brut to instantiate.
|
76
|
+
# This will use keyword injection to create the component, which means that if the component
|
77
|
+
# doesn't require any data from this component, you do not need to pass through those values.
|
78
|
+
# For example, you may have a component that renders the flash message. To avoid requiring your component to
|
79
|
+
# be passed the flash, a global component can be injected with it from Brut.
|
80
|
+
#
|
81
|
+
# @return [Object] instance of `component_klass`, as created by Brut. This will
|
82
|
+
# not render the component.
|
83
|
+
def global_component(component_klass)
|
84
|
+
Brut::FrontEnd::RequestContext.inject(component_klass)
|
85
|
+
end
|
112
86
|
end
|
87
|
+
include Helpers
|
113
88
|
|
114
89
|
# The name of this component, used for debugging and other purposes. Do not
|
115
90
|
# override this.
|
116
91
|
def self.component_name = self.name
|
117
|
-
|
118
|
-
#
|
92
|
+
# Convenience method to get the component name. This just calls the class
|
93
|
+
# method {.component_name}.
|
119
94
|
def component_name = self.class.component_name
|
120
95
|
|
121
|
-
#
|
122
|
-
#
|
123
|
-
|
124
|
-
#
|
125
|
-
# @raise If this component is not nested inside a page
|
126
|
-
# @see Brut::I18n::BaseMethods#t
|
127
|
-
def page_name
|
128
|
-
@page_name ||= begin
|
129
|
-
page = self.class.name.split(/::/).reduce(Module) { |accumulator,class_path_part|
|
130
|
-
if accumulator.ancestors.include?(Brut::FrontEnd::Page)
|
131
|
-
accumulator
|
132
|
-
else
|
133
|
-
accumulator.const_get(class_path_part)
|
134
|
-
end
|
135
|
-
}
|
136
|
-
if page.ancestors.include?(Brut::FrontEnd::Page)
|
137
|
-
page.name
|
138
|
-
elsif page.respond_to?(:page_name)
|
139
|
-
page.page_name
|
140
|
-
else
|
141
|
-
raise "#{self.class} is not nested inside a page, so #page_name should not have been called"
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
96
|
+
# True if this component is page private.
|
97
|
+
# @!visibility private
|
98
|
+
def page_private? = !!self.containing_page_class
|
145
99
|
|
100
|
+
# Returns the {Brut::FrontEnd::Page.page_name} of the page containing this component,
|
101
|
+
# if it is {#page_private?}. Do not call if it's not.
|
102
|
+
# @!visibility private
|
103
|
+
def containing_page_name = self.containing_page_class.page_name
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def containing_page_class
|
108
|
+
page_class = self.class.name.split(/::/).reduce(Module) { |accumulator,class_path_part|
|
109
|
+
if accumulator.ancestors.include?(Brut::FrontEnd::Page)
|
110
|
+
accumulator
|
111
|
+
else
|
112
|
+
accumulator.const_get(class_path_part)
|
113
|
+
end
|
114
|
+
}
|
115
|
+
if page_class.ancestors.include?(Brut::FrontEnd::Page)
|
116
|
+
page_class
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
end
|
146
121
|
end
|
@@ -14,10 +14,7 @@
|
|
14
14
|
# </brut-cv-messages>
|
15
15
|
# ```
|
16
16
|
#
|
17
|
-
# Note that if you are using `<brut-form>` then `<brut-cv>` elements will be inserted into the `<brut-cv-messages>` element, however
|
18
|
-
# they will not have the `server-side` attribute.
|
19
|
-
#
|
20
|
-
# You will most commonly use this component via {Brut::FrontEnd::Component#constraint_violations}.
|
17
|
+
# Note that if you are using `<brut-form>` then `<brut-cv>` elements will be inserted into the `<brut-cv-messages>` element, however they will not have the `server-side` attribute.
|
21
18
|
class Brut::FrontEnd::Components::ConstraintViolations < Brut::FrontEnd::Component
|
22
19
|
# Create a new ConstraintViolations component
|
23
20
|
#
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Represents a `<form>` HTML element that includes a CSRF token as needed.
|
1
|
+
# Represents a `<form>` HTML element that includes a CSRF token as needed.
|
2
2
|
class Brut::FrontEnd::Components::FormTag < Brut::FrontEnd::Component
|
3
3
|
# Creates the form surrounding the contents of the block yielded to it. If the form's action is a POST, it will include a CSRF token.
|
4
4
|
# If the form's action is GET, it will not.
|
@@ -2,10 +2,10 @@ module Brut::FrontEnd::Components
|
|
2
2
|
|
3
3
|
# Holds components designed to render HTML `<input>` and other form components.
|
4
4
|
module Inputs
|
5
|
-
autoload(:
|
5
|
+
autoload(:InputTag,"brut/front_end/components/inputs/input_tag")
|
6
6
|
autoload(:RadioButton,"brut/front_end/components/inputs/radio_button")
|
7
|
-
autoload(:
|
8
|
-
autoload(:
|
7
|
+
autoload(:SelectTagWithOptions,"brut/front_end/components/inputs/select_tag_with_options")
|
8
|
+
autoload(:TextareaTag,"brut/front_end/components/inputs/textarea_tag")
|
9
9
|
autoload(:CsrfToken,"brut/front_end/components/inputs/csrf_token")
|
10
10
|
end
|
11
11
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Renders a hidden field for a form that contains the current CSRF token. You only need
|
2
|
-
# to use this directly if you are building a form without {Brut::FrontEnd::
|
2
|
+
# to use this directly if you are building a form without {Brut::FrontEnd::Components::FormTag}
|
3
3
|
class Brut::FrontEnd::Components::Inputs::CsrfToken < Brut::FrontEnd::Components::Input
|
4
4
|
def initialize(csrf_token:)
|
5
5
|
@csrf_token = csrf_token
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Generates an HTML `<input>` field.
|
2
|
-
class Brut::FrontEnd::Components::Inputs::
|
2
|
+
class Brut::FrontEnd::Components::Inputs::InputTag < Brut::FrontEnd::Components::Input
|
3
3
|
# Creates the appropriate input for the given {Brut::FrontEnd::Form} and input name.
|
4
4
|
# Generally, you want to use this method over the initializer.
|
5
5
|
#
|
@@ -7,10 +7,10 @@ class Brut::FrontEnd::Components::Inputs::TextField < Brut::FrontEnd::Components
|
|
7
7
|
# @param [String] input_name the name of the input, which should be a member of `form`
|
8
8
|
# @param [Integer] index if this input is part of an array, this is the index into that array. This is used to get the input's value.
|
9
9
|
# @param [Hash] html_attributes any additional HTML attributes to include on the `<input>` element.
|
10
|
-
def self.for_form_input(form:, input_name:, index: nil, html_attributes
|
10
|
+
def self.for_form_input(form:, input_name:, index: nil, **html_attributes)
|
11
|
+
input = form.input(input_name, index:)
|
11
12
|
default_html_attributes = {}
|
12
13
|
html_attributes = html_attributes.map { |key,value| [ key.to_sym, value ] }.to_h
|
13
|
-
input = form.input(input_name, index:)
|
14
14
|
|
15
15
|
default_html_attributes[:required] = input.required
|
16
16
|
default_html_attributes[:pattern] = input.pattern
|
@@ -45,13 +45,11 @@ class Brut::FrontEnd::Components::Inputs::TextField < Brut::FrontEnd::Components
|
|
45
45
|
end
|
46
46
|
if !form.new? && !input.valid?
|
47
47
|
default_html_attributes["data-invalid"] = true
|
48
|
-
input.validity_state.each do |constraint
|
49
|
-
|
50
|
-
default_html_attributes["data-#{constraint}"] = true
|
51
|
-
end
|
48
|
+
input.validity_state.each do |constraint|
|
49
|
+
default_html_attributes["data-#{constraint}"] = true
|
52
50
|
end
|
53
51
|
end
|
54
|
-
Brut::FrontEnd::Components::Inputs::
|
52
|
+
Brut::FrontEnd::Components::Inputs::InputTag.new(default_html_attributes.merge(html_attributes))
|
55
53
|
end
|
56
54
|
|
57
55
|
def invalid? = @attributes["data-invalid"] == true
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# internal to the {Brut::FrontEnd::Form} treat the radio button group as a single input with
|
4
4
|
# a single name and value. When it comes time to generate HTML, this class is used
|
5
5
|
# to generate a single radio button from a group.
|
6
|
-
class Brut::FrontEnd::Components::Inputs::RadioButton < Brut::FrontEnd::Components::Inputs::
|
6
|
+
class Brut::FrontEnd::Components::Inputs::RadioButton < Brut::FrontEnd::Components::Inputs::InputTag
|
7
7
|
# Creates a radio button that is part of a radio button group. You should call this
|
8
8
|
# method once for each radio button in the group.
|
9
9
|
#
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# Renders an HTML `<select>`.
|
2
|
+
class Brut::FrontEnd::Components::Inputs::SelectTagWithOptions < Brut::FrontEnd::Components::Input
|
3
|
+
# Creates the appropriate select input for the given {Brut::FrontEnd::Form} and input name.
|
4
|
+
# Generally, you want to use this method over the initializer.
|
5
|
+
#
|
6
|
+
# @param [Brut::FrontEnd::Form} form The form that is being rendered.
|
7
|
+
# This method will consult this class to understand the requirements
|
8
|
+
# on this select so its HTML is generated correctly.
|
9
|
+
# @param [String] input_name the name of the input, which should be a member of `form`
|
10
|
+
# @param [Array<Object>] options An array of objects represented what is being selected.
|
11
|
+
# These can be any object and are ideally whatever domain object or
|
12
|
+
# data type you want on the backend to represent this selection.
|
13
|
+
# @param [Symbol|String] value_attribute the name of an attribute to determine an option's value.
|
14
|
+
# This will be called on each element of `options` to get the value used for the `<option>`'s
|
15
|
+
# `value` attribute. The value returned by `value_attribute` should be unique amongst the
|
16
|
+
# `options` provided *and* be distinct from whatever `value` is used for `include_blank`.
|
17
|
+
# @param [Symbol|String] option_text_attribute the name of an attribute to determine the text for an option.
|
18
|
+
# This will be called on each element of `options` to get the value used for the `<option>`'s
|
19
|
+
# text content. The value returned by `option_text_attribute` need not be unique, though if it
|
20
|
+
# is not unique, it will certainly be confusing.
|
21
|
+
# @param [Integer] index if this input is part of an array, this is the index into that array.
|
22
|
+
# This is used to get the input's value.
|
23
|
+
# @param [Hash] html_attributes any additional HTML attributes to include on the `<select>` element.
|
24
|
+
# @param [false|true|Hash] include_blank configure how and if to include a blank element in the select.
|
25
|
+
# If this is false, there will be no blank element. If it's `true`, there will be one with
|
26
|
+
# no value or text. If this is a `Hash` it must contain a `value:` key and a `text_content:` key
|
27
|
+
# to be used as the `value` attribute and option text content, respectively.
|
28
|
+
#
|
29
|
+
# @return [Brut::FrontEnd::Components::Inputs::SelectTagWithOptions] the select input ready to be placed into a view.
|
30
|
+
def self.for_form_input(form:,
|
31
|
+
input_name:,
|
32
|
+
options:,
|
33
|
+
include_blank: false,
|
34
|
+
value_attribute:,
|
35
|
+
option_text_attribute:,
|
36
|
+
index: nil,
|
37
|
+
html_attributes: {})
|
38
|
+
html_attributes = html_attributes.map { |key,value| [ key.to_sym, value ] }.to_h
|
39
|
+
default_html_attributes = {}
|
40
|
+
index ||= 0
|
41
|
+
input = form.input(input_name, index:)
|
42
|
+
default_html_attributes[:required] = input.required
|
43
|
+
if !form.new? && !input.valid?
|
44
|
+
default_html_attributes["data-invalid"] = true
|
45
|
+
input.validity_state.each do |constraint,violated|
|
46
|
+
if violated
|
47
|
+
default_html_attributes["data-#{constraint}"] = true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
name = if input.array?
|
52
|
+
"#{input.name}[]"
|
53
|
+
else
|
54
|
+
input.name
|
55
|
+
end
|
56
|
+
|
57
|
+
Brut::FrontEnd::Components::Inputs::SelectTagWithOptions.new(
|
58
|
+
name: name,
|
59
|
+
options:,
|
60
|
+
selected_value: input.value,
|
61
|
+
value_attribute:,
|
62
|
+
option_text_attribute:,
|
63
|
+
include_blank:,
|
64
|
+
html_attributes: default_html_attributes.merge(html_attributes)
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Create the element.
|
69
|
+
#
|
70
|
+
# @param [String] name the name of the input
|
71
|
+
# @param [Array<Object>] options An array of objects represented what is being selected.
|
72
|
+
# These can be any object and are ideally whatever domain object or
|
73
|
+
# data type you want on the backend to represent this selection.
|
74
|
+
# @param [Symbol|String] value_attribute the name of an attribute to determine an option's value.
|
75
|
+
# This will be called on each element of `options` to get the value used for the `<option>`'s
|
76
|
+
# `value` attribute. The value returned by `value_attribute` should be unique amongst the
|
77
|
+
# `options` provided *and* be distinct from whatever `value` is used for `include_blank`.
|
78
|
+
# @param [String] selected_value the value of the selected option. Note that this is the *value*
|
79
|
+
# of the selected option, not the selected option itself. To set the selected value
|
80
|
+
# based on a selected option, omit this and use `selected_option`
|
81
|
+
# @param [String] selected_option the selected option. Note that `value_attribute` will be called
|
82
|
+
# on this to determine the selected value to use when generating HTML. Also note that
|
83
|
+
# this object must be in `options` or an exeception is raised.
|
84
|
+
# @param [Symbol|String] option_text_attribute the name of an attribute to determine the text for an option.
|
85
|
+
# This will be called on each element of `options` to get the value used for the `<option>`'s
|
86
|
+
# text content. The value returned by `option_text_attribute` need not be unique, though if it
|
87
|
+
# is not unique, it will certainly be confusing.
|
88
|
+
# @param [Hash] html_attributes any additional HTML attributes to include on the `<select>` element.
|
89
|
+
# @param [false|true|Hash] include_blank configure how and if to include a blank element in the select.
|
90
|
+
# If this is false, there will be no blank element. If it's `true`, there will be one with
|
91
|
+
# no value or text. If this is a `Hash` it must contain a `value:` key and a `text_content:` key
|
92
|
+
# to be used as the `value` attribute and option text content, respectively.
|
93
|
+
#
|
94
|
+
# @raise ArgumentError if `selected_option` is present, but not in `options` or if `selected_value` is
|
95
|
+
# present, but no option's value for `value_attribute` is that value.
|
96
|
+
#
|
97
|
+
# XXX: Why does this not ask the form for the selected_value?
|
98
|
+
# XXX: This doesn't do well when values are strings
|
99
|
+
def initialize(name:,
|
100
|
+
options:,
|
101
|
+
value_attribute:,
|
102
|
+
selected_value: nil,
|
103
|
+
selected_option: nil,
|
104
|
+
option_text_attribute:,
|
105
|
+
include_blank: false,
|
106
|
+
html_attributes:)
|
107
|
+
@options = options
|
108
|
+
@include_blank = IncludeBlank.from_param(include_blank)
|
109
|
+
@value_attribute = value_attribute
|
110
|
+
@option_text_attribute = option_text_attribute
|
111
|
+
@html_attributes = html_attributes
|
112
|
+
@html_attributes[:name] = name
|
113
|
+
|
114
|
+
if selected_value.nil?
|
115
|
+
if selected_option.nil?
|
116
|
+
@selected_value = nil # explicitly nothing is selected
|
117
|
+
else
|
118
|
+
option = options.detect { |option|
|
119
|
+
option.send(@value_attribute) == selected_option.send(@value_attribute)
|
120
|
+
}
|
121
|
+
if option.nil?
|
122
|
+
raise ArgumentError, "selected_option #{selected_option} (with #{value_attribute} '#{selected_option.send(value_attribute)}') was not found in options"
|
123
|
+
end
|
124
|
+
@selected_value = option.send(@value_attribute)
|
125
|
+
end
|
126
|
+
else
|
127
|
+
if selected_value.kind_of?(Array)
|
128
|
+
raise "WTF: #{name}"
|
129
|
+
end
|
130
|
+
option = options.detect { |option|
|
131
|
+
selected_value == option.send(@value_attribute)
|
132
|
+
}
|
133
|
+
if option.nil?
|
134
|
+
raise ArgumentError, "selected_value #{selected_value} was not the value for #{value_attribute} on any of the options: #{options.map { |option| option.send(value_attribute) }.join(', ')}"
|
135
|
+
end
|
136
|
+
@selected_value = option.send(@value_attribute)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def view_template
|
141
|
+
select(**@html_attributes) {
|
142
|
+
if @include_blank
|
143
|
+
option(**@include_blank.option_attributes) {
|
144
|
+
@include_blank.text_content
|
145
|
+
}
|
146
|
+
end
|
147
|
+
options = @options.each do |option|
|
148
|
+
value = option.send(@value_attribute)
|
149
|
+
option_attributes = { value: value }
|
150
|
+
if value == @selected_value
|
151
|
+
option_attributes[:selected] = true
|
152
|
+
end
|
153
|
+
option(**option_attributes) {
|
154
|
+
option.send(@option_text_attribute)
|
155
|
+
}
|
156
|
+
end
|
157
|
+
}
|
158
|
+
end
|
159
|
+
private
|
160
|
+
|
161
|
+
# @!visibility private
|
162
|
+
class IncludeBlank
|
163
|
+
attr_reader :text_content, :option_attributes
|
164
|
+
def self.from_param(include_blank)
|
165
|
+
if !include_blank
|
166
|
+
return nil
|
167
|
+
else
|
168
|
+
self.new(include_blank)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
def initialize(include_blank)
|
172
|
+
if include_blank == true
|
173
|
+
@text_content = ""
|
174
|
+
@option_attributes = {}
|
175
|
+
elsif include_blank.kind_of?(Hash)
|
176
|
+
if include_blank.key?(:value) && include_blank.key?(:text_content)
|
177
|
+
@text_content = include_blank[:text_content]
|
178
|
+
@option_attributes = { value: include_blank[:value] }
|
179
|
+
else
|
180
|
+
raise ArgumentError, "when include_blank: is a Hash, it must include both :value and :text_content as keys. Got: #{include_blank.keys.join(", ")}"
|
181
|
+
end
|
182
|
+
else
|
183
|
+
raise ArgumentError,"include_blank: was a #{include_blank.class}. It should be true, false, nil, or a Hash"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Generates an HTML `<textarea>` field.
|
2
|
-
class Brut::FrontEnd::Components::Inputs::
|
2
|
+
class Brut::FrontEnd::Components::Inputs::TextareaTag < Brut::FrontEnd::Components::Input
|
3
3
|
# Creates the appropriate textarea for the given {Brut::FrontEnd::Form} and input name.
|
4
4
|
# Generally, you want to use this method over the initializer.
|
5
5
|
#
|
@@ -35,7 +35,7 @@ class Brut::FrontEnd::Components::Inputs::Textarea < Brut::FrontEnd::Components:
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
value = input.value
|
38
|
-
Brut::FrontEnd::Components::Inputs::
|
38
|
+
Brut::FrontEnd::Components::Inputs::TextareaTag.new(default_html_attributes.merge(html_attributes), value)
|
39
39
|
end
|
40
40
|
# Create an instance
|
41
41
|
#
|
@@ -1,4 +1,5 @@
|
|
1
|
-
# Renders a date or timestamp accessibly, using the `<time>` element.
|
1
|
+
# Renders a date or timestamp accessibly, using the `<time>` element.
|
2
|
+
# This will account for the current request's time zone. See {Clock}.
|
2
3
|
class Brut::FrontEnd::Components::TimeTag < Brut::FrontEnd::Component
|
3
4
|
include Brut::I18n::ForHTML
|
4
5
|
# Creates the component
|
data/lib/brut/front_end/form.rb
CHANGED
@@ -68,12 +68,12 @@ class Brut::FrontEnd::Form
|
|
68
68
|
@inputs = self.class.input_definitions.map { |name,input_definition|
|
69
69
|
value = @params[name] || @params[name.to_sym]
|
70
70
|
inputs = if value.kind_of?(Array)
|
71
|
-
value.map { |one_value|
|
72
|
-
input_definition.make_input(value: one_value)
|
71
|
+
value.map.with_index { |one_value, index|
|
72
|
+
input_definition.make_input(value: one_value, index:)
|
73
73
|
}
|
74
74
|
else
|
75
75
|
[
|
76
|
-
input_definition.make_input(value:)
|
76
|
+
input_definition.make_input(value:, index: nil)
|
77
77
|
]
|
78
78
|
end
|
79
79
|
|
@@ -98,7 +98,7 @@ class Brut::FrontEnd::Form
|
|
98
98
|
input = inputs[index]
|
99
99
|
if input.nil?
|
100
100
|
input_definition = self.class.input_definitions.fetch(input_name.to_s)
|
101
|
-
input = input_definition.make_input(value:"")
|
101
|
+
input = input_definition.make_input(value:"", index:)
|
102
102
|
inputs[index] = input
|
103
103
|
end
|
104
104
|
input
|
@@ -13,9 +13,10 @@ class Brut::FrontEnd::Forms::Input
|
|
13
13
|
# Create the input with the given definition and value
|
14
14
|
# @param [Brut::FrontEnd::Forms::InputDefinition] input_definition
|
15
15
|
# @param [String] value
|
16
|
-
def initialize(input_definition:, value:)
|
16
|
+
def initialize(input_definition:, value:, index:)
|
17
17
|
@input_definition = input_definition
|
18
18
|
@validity_state = Brut::FrontEnd::Forms::ValidityState.new
|
19
|
+
@index = index
|
19
20
|
self.value=(value)
|
20
21
|
end
|
21
22
|
|
@@ -109,7 +109,10 @@ class Brut::FrontEnd::Forms::InputDefinition
|
|
109
109
|
|
110
110
|
|
111
111
|
# Create an Input based on this definition, initializing it with the given value.
|
112
|
-
|
113
|
-
|
112
|
+
# @param [String] value the value to give this input initially.
|
113
|
+
# @param [Integer] index the index of this input, if it is part of an array of
|
114
|
+
# inputs. `nil` is allowed only if the input definition is not for an array.
|
115
|
+
def make_input(value:, index:)
|
116
|
+
Brut::FrontEnd::Forms::Input.new(input_definition: self, value:, index:)
|
114
117
|
end
|
115
118
|
end
|
@@ -8,9 +8,10 @@ class Brut::FrontEnd::Forms::RadioButtonGroupInput
|
|
8
8
|
attr_reader :validity_state
|
9
9
|
|
10
10
|
# (see Brut::FrontEnd::Forms::Input#initialize)
|
11
|
-
def initialize(input_definition:, value:)
|
11
|
+
def initialize(input_definition:, value:, index:)
|
12
12
|
@input_definition = input_definition
|
13
13
|
@validity_state = Brut::FrontEnd::Forms::ValidityState.new
|
14
|
+
@index = index
|
14
15
|
if input_definition.array?
|
15
16
|
value ||= []
|
16
17
|
end
|
@@ -23,7 +23,7 @@ class Brut::FrontEnd::Forms::RadioButtonGroupInputDefinition
|
|
23
23
|
def array? = @array
|
24
24
|
|
25
25
|
# Create an Input based on this defitition, initializing it with the given value.
|
26
|
-
def make_input(value:)
|
27
|
-
Brut::FrontEnd::Forms::RadioButtonGroupInput.new(input_definition: self, value:
|
26
|
+
def make_input(value:, index:)
|
27
|
+
Brut::FrontEnd::Forms::RadioButtonGroupInput.new(input_definition: self, value:, index:)
|
28
28
|
end
|
29
29
|
end
|
@@ -9,12 +9,10 @@ class Brut::FrontEnd::Forms::SelectInput
|
|
9
9
|
attr_reader :validity_state
|
10
10
|
|
11
11
|
# (see Brut::FrontEnd::Forms::Input#initialize)
|
12
|
-
def initialize(input_definition:, value:)
|
12
|
+
def initialize(input_definition:, value:, index:)
|
13
13
|
@input_definition = input_definition
|
14
14
|
@validity_state = Brut::FrontEnd::Forms::ValidityState.new
|
15
|
-
|
16
|
-
value ||= []
|
17
|
-
end
|
15
|
+
@index = index
|
18
16
|
self.value=(value)
|
19
17
|
end
|
20
18
|
|
@@ -21,7 +21,7 @@ class Brut::FrontEnd::Forms::SelectInputDefinition
|
|
21
21
|
def array? = @array
|
22
22
|
|
23
23
|
# Create an Input based on this defitition, initializing it with the given value.
|
24
|
-
def make_input(value:)
|
25
|
-
Brut::FrontEnd::Forms::SelectInput.new(input_definition: self, value:
|
24
|
+
def make_input(value:, index:)
|
25
|
+
Brut::FrontEnd::Forms::SelectInput.new(input_definition: self, value:, index:)
|
26
26
|
end
|
27
27
|
end
|
@@ -1,12 +1,24 @@
|
|
1
1
|
module Brut::FrontEnd
|
2
|
-
# A handler responds to all HTTP requests other than those that render a page.
|
3
|
-
#
|
4
|
-
#
|
2
|
+
# A handler responds to all HTTP requests other than those that render a page.
|
3
|
+
# Like a page, the handler is initialized with any of the data it needs. The {#handle} method will
|
4
|
+
# be called to perform whatever action is needed, and its return value will determine what
|
5
|
+
# ther esponse will be.
|
5
6
|
#
|
6
|
-
#
|
7
|
-
#
|
7
|
+
# To create a handler, after defining a route or form,
|
8
|
+
# subclass this class (or, more likely, your app's `AppHandler`) and
|
9
|
+
# provide an initializer that accepts keyword arguments. The names of these arguments will be used
|
10
|
+
# to locate the values that Brut will pass in when creating your page object. If your handler
|
11
|
+
# is for a form, be sure to include the `form:` keyword argument.
|
8
12
|
#
|
9
|
-
#
|
13
|
+
# Consult Brut's documentation on keyword injection to know what values you may use and how values are located.
|
14
|
+
#
|
15
|
+
# Then, implement {#handle} to perform whatever logic is needed to handle the request.
|
16
|
+
#
|
17
|
+
# You may also define `before_handle` which will be called before {#handle} to potentially abort
|
18
|
+
# the request. This is mostly useful if you have a base class for some of your handlers and want to
|
19
|
+
# share cross-cutting logic.
|
20
|
+
#
|
21
|
+
# Note that the public API for handlers is {#handle!}, which is what you should call in a test.
|
10
22
|
class Handler
|
11
23
|
include Brut::FrontEnd::HandlingResults
|
12
24
|
include Brut::Framework::Errors
|
@@ -27,32 +39,22 @@ module Brut::FrontEnd
|
|
27
39
|
abstract_method!
|
28
40
|
end
|
29
41
|
|
42
|
+
# Override this to performa any checks before {#handle} is called. This should
|
43
|
+
# return `nil` if {#handle} should proceed to be called. Generally, you don't need to override
|
44
|
+
# this as {#handle} can include the logic. Where this is useful is to share cross-cutting logic
|
45
|
+
# across other handlers.
|
46
|
+
#
|
47
|
+
# @return [URI|Brut::FrontEnd::Component,Array,Brut::FrontEnd::HttpStatus,Brut::FrontEnd::Download] See
|
48
|
+
# {#handle} for what each return value means.
|
49
|
+
def before_handle = nil
|
50
|
+
|
30
51
|
# Called by Brut to handle the request. Do not override this. If your handler responds to `before_handle` that is called with the
|
31
52
|
# same args as you have defined for {#handle}. If `before_handle` returns anything other than `nil`, that value is returned and
|
32
53
|
# should be one of the values documented in {#handle}. If `before_handle` returns `nil`, {#handle} is called and whatever it
|
33
54
|
# returns is returned here.
|
34
55
|
def handle!(**args)
|
35
56
|
result = nil
|
36
|
-
|
37
|
-
before_handle_args = self.method(:before_handle).parameters.map { |(type,name)|
|
38
|
-
if type == :keyreq
|
39
|
-
if args.key?(name)
|
40
|
-
[ name, args[name] ]
|
41
|
-
else
|
42
|
-
raise ArgumentError,"before_handle requires keyword arg '#{name}' but `handle` did not receive it. It must"
|
43
|
-
end
|
44
|
-
elsif type == :key
|
45
|
-
if args.key?(name)
|
46
|
-
[ name, args[name] ]
|
47
|
-
else
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
else
|
51
|
-
raise ArgumentError,"before_handle must only have keyword args. Got '#{name}' of type '#{type}'"
|
52
|
-
end
|
53
|
-
}.compact.to_h
|
54
|
-
result = self.before_handle(**before_handle_args)
|
55
|
-
end
|
57
|
+
result = self.before_handle
|
56
58
|
if result.nil?
|
57
59
|
result = self.handle(**args)
|
58
60
|
end
|