brut 0.17.0 → 0.18.1
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/exe/brut +34 -0
- data/lib/brut/cli/apps/build_assets.rb +78 -48
- data/lib/brut/cli/apps/db.rb +168 -202
- data/lib/brut/cli/apps/deploy.rb +291 -0
- data/lib/brut/cli/apps/heroku_container_based_deploy.rb +6 -0
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/add_segment.rb +5 -5
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/add_segment_options.rb +1 -1
- data/lib/brut/cli/apps/new/app.rb +240 -0
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/app_id.rb +1 -1
- data/lib/brut/cli/apps/new/app_name.rb +29 -0
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/base.rb +9 -6
- data/lib/brut/cli/apps/new/erb_binding_delegate.rb +23 -0
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/internet_identifier.rb +5 -5
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/invalid_identifier.rb +1 -1
- data/{mkbrut/lib/mkbrut/app.rb → lib/brut/cli/apps/new/old_app.rb} +8 -11
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/add_css_import.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/add_i18n_message.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/add_method.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/append_to_file.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/base_op.rb +3 -3
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/copy_file.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/insert_code_in_method.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/insert_into_file.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/insert_route.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/mkdir.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/prism_parsing_op.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/render_template.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/skip_file.rb +1 -1
- data/lib/brut/cli/apps/new/ops.rb +17 -0
- data/lib/brut/cli/apps/new/organization.rb +5 -0
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/prefix.rb +1 -1
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/segments/bare_bones.rb +12 -11
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/segments/demo.rb +16 -15
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/segments/heroku.rb +9 -5
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/segments/sidekiq.rb +44 -21
- data/lib/brut/cli/apps/new/segments.rb +8 -0
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/versions.rb +3 -3
- data/lib/brut/cli/apps/new.rb +26 -0
- data/lib/brut/cli/apps/scaffold.rb +150 -141
- data/lib/brut/cli/apps/test.rb +92 -68
- data/lib/brut/cli/commands/base_command.rb +174 -0
- data/lib/brut/cli/commands/compound_command.rb +29 -0
- data/lib/brut/cli/commands/execution_context.rb +32 -0
- data/lib/brut/cli/commands/help.rb +26 -0
- data/lib/brut/cli/commands/output_error.rb +13 -0
- data/lib/brut/cli/commands/raise_error.rb +11 -0
- data/lib/brut/cli/commands.rb +8 -0
- data/lib/brut/cli/execute_result.rb +39 -0
- data/lib/brut/cli/executor.rb +9 -4
- data/lib/brut/cli/output.rb +13 -0
- data/lib/brut/cli/parsed_command_line.rb +143 -0
- data/lib/brut/cli/runner.rb +124 -0
- data/lib/brut/cli.rb +7 -29
- data/lib/brut/framework/container.rb +1 -1
- data/lib/brut/framework/mcp.rb +59 -13
- data/lib/brut/framework/project_environment.rb +3 -1
- data/lib/brut/junk_drawer.rb +3 -1
- data/lib/brut/spec_support/cli_command_support.rb +45 -0
- data/lib/brut/spec_support/e2e_test_server.rb +3 -0
- data/lib/brut/spec_support/general_support.rb +1 -1
- data/lib/brut/spec_support/matchers/have_executed.rb +35 -0
- data/lib/brut/spec_support/rspec_setup.rb +4 -8
- data/lib/brut/spec_support.rb +1 -0
- data/lib/brut/tui/markup_string.rb +2 -0
- data/lib/brut/tui/script/events/command_std_out.rb +3 -2
- data/lib/brut/tui/script/exec_step.rb +11 -4
- data/lib/brut/tui/script/puts_subscriber.rb +4 -4
- data/lib/brut/tui/script.rb +7 -3
- data/lib/brut/tui/terminal_theme.rb +15 -11
- data/lib/brut/version.rb +1 -1
- data/templates/Base/.env.development.local +2 -0
- data/templates/Base/bin/ci +42 -0
- data/{mkbrut/templates → templates}/Base/bin/release +2 -2
- data/templates/Base/bin/setup +174 -0
- data/{mkbrut/templates → templates}/Base/bin/watch-and-build-assets +1 -1
- data/{mkbrut/templates → templates}/Base/dx/docker-compose.env.erb +1 -1
- data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/css/fonts.css +1 -1
- data/{mkbrut/templates → templates}/segments/Heroku/deploy/Dockerfile +2 -2
- data/templates/segments/Heroku/deploy/docker_config.rb +30 -0
- metadata +190 -1055
- data/.gitignore +0 -61
- data/.projections.json +0 -10
- data/CHANGELOG.md +0 -172
- data/CODE_OF_CONDUCT.txt +0 -99
- data/Dockerfile.dx +0 -82
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -246
- data/LICENSE.txt +0 -370
- data/README.md +0 -90
- data/Rakefile +0 -25
- data/assets/Logo-Square.pxd +0 -0
- data/assets/LogoPylon.pxd +0 -0
- data/assets/LogoStop.pxd +0 -0
- data/assets/LogoTall.pxd +0 -0
- data/assets/MetroIcon.graffle +0 -0
- data/assets/MetroLogo.graffle +0 -0
- data/assets/SocialImage.png +0 -0
- data/assets/SocialImage.pxd +0 -0
- data/assets/YouTubeThumb.pxd +0 -0
- data/bin/bin_kit.rb +0 -51
- data/bin/build +0 -86
- data/bin/ci +0 -40
- data/bin/dev +0 -20
- data/bin/docs +0 -86
- data/bin/generate-and-run-rubocop +0 -52
- data/bin/new-version +0 -8
- data/bin/publish +0 -61
- data/bin/rake +0 -27
- data/bin/rspec +0 -27
- data/bin/rubocop +0 -27
- data/bin/setup +0 -252
- data/bin/test +0 -18
- data/brut-css/.nvim.lua +0 -1
- data/brut-css/README.md +0 -28
- data/brut-css/bin/build +0 -50
- data/brut-css/bin/ci +0 -19
- data/brut-css/bin/dev +0 -1
- data/brut-css/bin/docs +0 -34
- data/brut-css/bin/publish +0 -21
- data/brut-css/bin/setup +0 -6
- data/brut-css/config/media-queries-all.css +0 -15
- data/brut-css/config/media-queries-minimal.css +0 -5
- data/brut-css/config/postcss.config.cjs +0 -7
- data/brut-css/config/pseudo-classes-all.css +0 -9
- data/brut-css/dx +0 -1
- data/brut-css/package-lock.json +0 -3165
- data/brut-css/package.json +0 -36
- data/brut-css/src/css/appearance.css +0 -145
- data/brut-css/src/css/border.css +0 -522
- data/brut-css/src/css/colors.css +0 -3502
- data/brut-css/src/css/dimensions.css +0 -548
- data/brut-css/src/css/flex.css +0 -179
- data/brut-css/src/css/index.css +0 -13
- data/brut-css/src/css/layout.css +0 -120
- data/brut-css/src/css/list.css +0 -41
- data/brut-css/src/css/positioning.css +0 -354
- data/brut-css/src/css/properties/colors.css +0 -455
- data/brut-css/src/css/properties/index.css +0 -3
- data/brut-css/src/css/properties/spacing.css +0 -140
- data/brut-css/src/css/properties/typography.css +0 -224
- data/brut-css/src/css/reset.css +0 -107
- data/brut-css/src/css/spacing.css +0 -585
- data/brut-css/src/css/typography.css +0 -519
- data/brut-css/src/css/utils.css +0 -104
- data/brut-css/src/docs/1_getting-started/1_overview.md +0 -46
- data/brut-css/src/docs/1_getting-started/2_installation.md +0 -25
- data/brut-css/src/docs/1_getting-started/3_core-concepts.md +0 -75
- data/brut-css/src/docs/1_getting-started/4_simple-example.md +0 -132
- data/brut-css/src/docs/1_getting-started/page.html.ejs +0 -10
- data/brut-css/src/docs/2_properties/page.html.ejs +0 -71
- data/brut-css/src/docs/3_classes/color-demo.html.ejs +0 -31
- data/brut-css/src/docs/3_classes/page.html.ejs +0 -87
- data/brut-css/src/docs/4_customization/1_design-system.md +0 -36
- data/brut-css/src/docs/4_customization/2_breakpoints.md +0 -75
- data/brut-css/src/docs/4_customization/3_pseudo-classes.md +0 -74
- data/brut-css/src/docs/4_customization/4_advanced-configuration.md +0 -40
- data/brut-css/src/docs/4_customization/page.html.ejs +0 -10
- data/brut-css/src/docs/docs.css +0 -98
- data/brut-css/src/docs/includes/body-and-header.html.ejs +0 -30
- data/brut-css/src/docs/includes/footer-and-rest.html.ejs +0 -9
- data/brut-css/src/docs/includes/head.html.ejs +0 -5
- data/brut-css/src/docs/includes/nav.html.ejs +0 -10
- data/brut-css/src/docs/index.html.ejs +0 -32
- data/brut-css/src/docs/prism-twilight.min.css +0 -1
- data/brut-css/src/js/Logger.js +0 -71
- data/brut-css/src/js/build.js +0 -111
- data/brut-css/src/js/cli/CLIArgError.js +0 -7
- data/brut-css/src/js/cli/Debug.js +0 -27
- data/brut-css/src/js/cli/DocsDir.js +0 -16
- data/brut-css/src/js/cli/DocsTemplateSourceDir.js +0 -16
- data/brut-css/src/js/cli/InputFile.js +0 -31
- data/brut-css/src/js/cli/MediaQueryConfigFile.js +0 -10
- data/brut-css/src/js/cli/OutputFile.js +0 -22
- data/brut-css/src/js/cli/ParsedArg.js +0 -17
- data/brut-css/src/js/cli/PathToBrutCSSRoot.js +0 -19
- data/brut-css/src/js/cli/PseudoClassConfigFile.js +0 -11
- data/brut-css/src/js/cli.js +0 -108
- data/brut-css/src/js/docGenerator.js +0 -467
- data/brut-css/src/js/mediaQueryConfigParser.js +0 -98
- data/brut-css/src/js/post-css-plugins/addMediaQueriesPlugin.js +0 -49
- data/brut-css/src/js/post-css-plugins/addPseudoClassesPlugin.js +0 -42
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Category.js +0 -9
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/DocState.js +0 -185
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Documentable.js +0 -8
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Group.js +0 -7
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/ParsedComment.js +0 -73
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Property.js +0 -9
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyCategory.js +0 -4
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyGroup.js +0 -8
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Rule.js +0 -12
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleCategory.js +0 -4
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleGroup.js +0 -8
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeRef.js +0 -5
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeURL.js +0 -9
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin.js +0 -49
- data/brut-css/src/js/post-css-plugins/generateRootCustomPropertiesPlugin.js +0 -45
- data/brut-css/src/js/pseudoClassConfigParser.js +0 -145
- data/brut-js/.projections.json +0 -10
- data/brut-js/README.md +0 -118
- data/brut-js/bin/build +0 -19
- data/brut-js/bin/ci +0 -5
- data/brut-js/bin/docs +0 -25
- data/brut-js/bin/publish +0 -21
- data/brut-js/bin/setup +0 -6
- data/brut-js/docs/README.md +0 -8
- data/brut-js/docs/jsdoc-plugins/customElementTag.js +0 -8
- data/brut-js/docs/jsdoc-theme/publish.js +0 -692
- data/brut-js/docs/jsdoc-theme/static/scripts/linenumber.js +0 -25
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/Apache-License-2.0.txt +0 -202
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/lang-css.js +0 -2
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/prettify.js +0 -28
- data/brut-js/docs/jsdoc-theme/static/styles/jsdoc-default.css +0 -327
- data/brut-js/docs/jsdoc-theme/static/styles/prettify-jsdoc.css +0 -111
- data/brut-js/docs/jsdoc-theme/static/styles/prettify-tomorrow.css +0 -132
- data/brut-js/docs/jsdoc-theme/tmpl/augments.tmpl +0 -10
- data/brut-js/docs/jsdoc-theme/tmpl/container.tmpl +0 -199
- data/brut-js/docs/jsdoc-theme/tmpl/details.tmpl +0 -143
- data/brut-js/docs/jsdoc-theme/tmpl/example.tmpl +0 -2
- data/brut-js/docs/jsdoc-theme/tmpl/examples.tmpl +0 -13
- data/brut-js/docs/jsdoc-theme/tmpl/exceptions.tmpl +0 -32
- data/brut-js/docs/jsdoc-theme/tmpl/layout.tmpl +0 -38
- data/brut-js/docs/jsdoc-theme/tmpl/mainpage.tmpl +0 -14
- data/brut-js/docs/jsdoc-theme/tmpl/members.tmpl +0 -38
- data/brut-js/docs/jsdoc-theme/tmpl/method.tmpl +0 -131
- data/brut-js/docs/jsdoc-theme/tmpl/modifies.tmpl +0 -14
- data/brut-js/docs/jsdoc-theme/tmpl/params.tmpl +0 -131
- data/brut-js/docs/jsdoc-theme/tmpl/properties.tmpl +0 -108
- data/brut-js/docs/jsdoc-theme/tmpl/returns.tmpl +0 -19
- data/brut-js/docs/jsdoc-theme/tmpl/source.tmpl +0 -8
- data/brut-js/docs/jsdoc-theme/tmpl/tutorial.tmpl +0 -19
- data/brut-js/docs/jsdoc-theme/tmpl/type.tmpl +0 -7
- data/brut-js/docs/jsdoc.config.json +0 -23
- data/brut-js/docs/package-lock.json +0 -343
- data/brut-js/docs/package.json +0 -7
- data/brut-js/dx +0 -1
- data/brut-js/package-lock.json +0 -2210
- data/brut-js/package.json +0 -36
- data/brut-js/specs/AjaxSubmit.spec.js +0 -453
- data/brut-js/specs/Autosubmit.spec.js +0 -127
- data/brut-js/specs/ConfirmSubmit.spec.js +0 -224
- data/brut-js/specs/ConstraintViolationMessage.spec.js +0 -33
- data/brut-js/specs/ConstraintViolationMessages.spec.js +0 -32
- data/brut-js/specs/CopyToClipboard.spec.js +0 -35
- data/brut-js/specs/Form.spec.js +0 -137
- data/brut-js/specs/I18nTranslation.spec.js +0 -19
- data/brut-js/specs/LocaleDetection.spec.js +0 -22
- data/brut-js/specs/Message.spec.js +0 -15
- data/brut-js/specs/SpecHelper.js +0 -23
- data/brut-js/specs/Tabs.spec.js +0 -41
- data/brut-js/specs/Toast.spec.js +0 -34
- data/brut-js/specs/config/asset_metadata.json +0 -7
- data/brut-js/src/AjaxSubmit.js +0 -499
- data/brut-js/src/Autosubmit.js +0 -63
- data/brut-js/src/BaseCustomElement.js +0 -261
- data/brut-js/src/ConfirmSubmit.js +0 -137
- data/brut-js/src/ConfirmationDialog.js +0 -143
- data/brut-js/src/ConstraintViolationMessage.js +0 -140
- data/brut-js/src/ConstraintViolationMessages.js +0 -98
- data/brut-js/src/CopyToClipboard.js +0 -96
- data/brut-js/src/Form.js +0 -147
- data/brut-js/src/I18nTranslation.js +0 -64
- data/brut-js/src/LocaleDetection.js +0 -117
- data/brut-js/src/Logger.js +0 -90
- data/brut-js/src/Message.js +0 -62
- data/brut-js/src/RichString.js +0 -116
- data/brut-js/src/Tabs.js +0 -168
- data/brut-js/src/Toast.js +0 -102
- data/brut-js/src/Tracing.js +0 -247
- data/brut-js/src/appForTestingOnly.js +0 -15
- data/brut-js/src/index.js +0 -133
- data/brut-js/src/testing/AssetMetadata.js +0 -35
- data/brut-js/src/testing/AssetMetadataLoader.js +0 -25
- data/brut-js/src/testing/CustomElementTest.js +0 -235
- data/brut-js/src/testing/DOMCreator.js +0 -45
- data/brut-js/src/testing/index.js +0 -48
- data/brut.gemspec +0 -73
- data/brutrb.com/.vitepress/config.mjs +0 -164
- data/brutrb.com/.vitepress/plugins/jsdocLinker.js +0 -34
- data/brutrb.com/.vitepress/plugins/rdocLinker.js +0 -18
- data/brutrb.com/.vitepress/theme/custom.css +0 -14
- data/brutrb.com/.vitepress/theme/index.js +0 -18
- data/brutrb.com/.vitepress/theme/style.css +0 -139
- data/brutrb.com/adrs.md +0 -16
- data/brutrb.com/ai.md +0 -68
- data/brutrb.com/assets.md +0 -131
- data/brutrb.com/bin/build +0 -5
- data/brutrb.com/bin/deploy +0 -7
- data/brutrb.com/bin/dev +0 -5
- data/brutrb.com/bin/setup +0 -6
- data/brutrb.com/brut-js.md +0 -128
- data/brutrb.com/business-logic.md +0 -55
- data/brutrb.com/cli.md +0 -274
- data/brutrb.com/components.md +0 -265
- data/brutrb.com/configuration.md +0 -256
- data/brutrb.com/css.md +0 -103
- data/brutrb.com/custom-element-tests.md +0 -148
- data/brutrb.com/database-access.md +0 -201
- data/brutrb.com/database-schema.md +0 -320
- data/brutrb.com/deployment.md +0 -158
- data/brutrb.com/dev-environment.md +0 -186
- data/brutrb.com/dir-structure.md +0 -120
- data/brutrb.com/doc-conventions.md +0 -41
- data/brutrb.com/dx +0 -1
- data/brutrb.com/end-to-end-tests.md +0 -176
- data/brutrb.com/features.md +0 -373
- data/brutrb.com/flash-and-session.md +0 -208
- data/brutrb.com/form-constraints.md +0 -266
- data/brutrb.com/forms.md +0 -238
- data/brutrb.com/getting-started.md +0 -142
- data/brutrb.com/handlers.md +0 -177
- data/brutrb.com/hooks.md +0 -176
- data/brutrb.com/i18n.md +0 -190
- data/brutrb.com/images/DevEnvironment.graffle +0 -0
- data/brutrb.com/images/DevEnvironment.png +0 -0
- data/brutrb.com/images/LogoSquare.png +0 -0
- data/brutrb.com/images/LogoStop.png +0 -0
- data/brutrb.com/images/LogoTall.png +0 -0
- data/brutrb.com/images/Makefile +0 -10
- data/brutrb.com/images/OverviewMetro.graffle +0 -0
- data/brutrb.com/images/OverviewMetro.png +0 -0
- data/brutrb.com/images/dev-env-overview.dot +0 -54
- data/brutrb.com/images/dev-env-overview.png +0 -0
- data/brutrb.com/images/dev-env-protocol.dot +0 -37
- data/brutrb.com/images/dev-env-protocol.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 +0 -19
- data/brutrb.com/images/spa.png +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-dialog-browser-element-styled.png +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-dialog-browser-element.png +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-dialog-browser.png +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-flow.graffle +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-flow.png +0 -0
- data/brutrb.com/images/tutorial/basic-form-with-violations.png +0 -0
- data/brutrb.com/images/tutorial/basic-form.png +0 -0
- data/brutrb.com/images/tutorial/initial-home-page.png +0 -0
- data/brutrb.com/images/tutorial/new-post-editor.png +0 -0
- data/brutrb.com/images/tutorial/new-post-home-page.png +0 -0
- data/brutrb.com/images/tutorial/styled-form-with-server-side-violations.png +0 -0
- data/brutrb.com/images/tutorial/styled-form-with-violations.png +0 -0
- data/brutrb.com/images/tutorial/styled-home-page-with-posts.png +0 -0
- data/brutrb.com/images/tutorial/styled-home-page.png +0 -0
- data/brutrb.com/images/tutorial/welcome-to-brut.png +0 -0
- data/brutrb.com/images/workspace-protocol.dot +0 -44
- data/brutrb.com/images/workspace-protocol.png +0 -0
- data/brutrb.com/index.md +0 -34
- data/brutrb.com/instrumentation.md +0 -331
- data/brutrb.com/javascript.md +0 -122
- data/brutrb.com/jobs.md +0 -114
- data/brutrb.com/keyword-injection.md +0 -195
- data/brutrb.com/layouts.md +0 -156
- data/brutrb.com/lsp.md +0 -23
- data/brutrb.com/markdown-examples.md +0 -85
- data/brutrb.com/middleware.md +0 -80
- data/brutrb.com/overview.md +0 -68
- data/brutrb.com/package-lock.json +0 -2451
- data/brutrb.com/package.json +0 -11
- data/brutrb.com/pages.md +0 -290
- data/brutrb.com/public/SocialImage.png +0 -0
- data/brutrb.com/public/favicon.ico +0 -0
- data/brutrb.com/recipes/alternate-layouts.md +0 -32
- data/brutrb.com/recipes/authentication.md +0 -336
- data/brutrb.com/recipes/custom-flash.md +0 -51
- data/brutrb.com/recipes/dev-env-secrets.md +0 -87
- data/brutrb.com/recipes/form-errors.md +0 -148
- data/brutrb.com/recipes/indexed-forms.md +0 -149
- data/brutrb.com/recipes/migrations.md +0 -210
- data/brutrb.com/recipes/text-field-component.md +0 -182
- data/brutrb.com/roadmap.md +0 -52
- data/brutrb.com/routes.md +0 -189
- data/brutrb.com/security.md +0 -102
- data/brutrb.com/seed-data.md +0 -63
- data/brutrb.com/space-time-continuum.md +0 -81
- data/brutrb.com/tutorial.md +0 -138
- data/brutrb.com/tutorials/01-intro.md +0 -1654
- data/brutrb.com/tutorials/02-dialog.md +0 -569
- data/brutrb.com/unit-tests.md +0 -148
- data/brutrb.com/why.md +0 -19
- data/docker-compose.dx.yml +0 -25
- data/docs/404.html +0 -26
- data/docs/CNAME +0 -1
- data/docs/SocialImage.png +0 -0
- data/docs/adrs.html +0 -29
- data/docs/ai.html +0 -29
- data/docs/api/Brut/BackEnd/SeedData.html +0 -493
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +0 -214
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +0 -125
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +0 -125
- data/docs/api/Brut/BackEnd/Sidekiq.html +0 -125
- data/docs/api/Brut/BackEnd/Validators/FormValidator.html +0 -414
- data/docs/api/Brut/BackEnd/Validators.html +0 -128
- data/docs/api/Brut/BackEnd.html +0 -132
- data/docs/api/Brut/CLI/App.html +0 -1601
- data/docs/api/Brut/CLI/AppRunner.html +0 -491
- data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +0 -264
- data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +0 -306
- data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +0 -262
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +0 -314
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +0 -183
- data/docs/api/Brut/CLI/Apps/DB/Create.html +0 -365
- data/docs/api/Brut/CLI/Apps/DB/Drop.html +0 -357
- data/docs/api/Brut/CLI/Apps/DB/Migrate.html +0 -389
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +0 -339
- data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +0 -329
- data/docs/api/Brut/CLI/Apps/DB/Seed.html +0 -347
- data/docs/api/Brut/CLI/Apps/DB/Status.html +0 -383
- data/docs/api/Brut/CLI/Apps/DB.html +0 -183
- data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +0 -270
- data/docs/api/Brut/CLI/Apps/DeployBase.html +0 -257
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +0 -587
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +0 -196
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +0 -303
- data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +0 -508
- data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +0 -398
- data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +0 -374
- data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +0 -384
- data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +0 -410
- data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +0 -262
- data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +0 -303
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +0 -480
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +0 -450
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +0 -380
- data/docs/api/Brut/CLI/Apps/Scaffold.html +0 -253
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +0 -474
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +0 -407
- data/docs/api/Brut/CLI/Apps/Test/JS.html +0 -262
- data/docs/api/Brut/CLI/Apps/Test/Run.html +0 -578
- data/docs/api/Brut/CLI/Apps/Test.html +0 -253
- data/docs/api/Brut/CLI/Apps.html +0 -125
- data/docs/api/Brut/CLI/Command.html +0 -2425
- data/docs/api/Brut/CLI/Error.html +0 -139
- data/docs/api/Brut/CLI/ExecutionResults/Result.html +0 -664
- data/docs/api/Brut/CLI/ExecutionResults.html +0 -675
- data/docs/api/Brut/CLI/Executor.html +0 -561
- data/docs/api/Brut/CLI/InvalidOption.html +0 -245
- data/docs/api/Brut/CLI/Options.html +0 -880
- data/docs/api/Brut/CLI/Output.html +0 -699
- data/docs/api/Brut/CLI/SystemExecError.html +0 -451
- data/docs/api/Brut/CLI.html +0 -263
- data/docs/api/Brut/FactoryBot.html +0 -225
- data/docs/api/Brut/Framework/App.html +0 -1097
- data/docs/api/Brut/Framework/Config.html +0 -1071
- data/docs/api/Brut/Framework/Container.html +0 -1464
- data/docs/api/Brut/Framework/Error.html +0 -140
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +0 -232
- data/docs/api/Brut/Framework/Errors/Bug.html +0 -234
- data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +0 -257
- data/docs/api/Brut/Framework/Errors/MissingParameter.html +0 -273
- data/docs/api/Brut/Framework/Errors/NoClassForPath.html +0 -471
- data/docs/api/Brut/Framework/Errors/NotFound.html +0 -308
- data/docs/api/Brut/Framework/Errors/NotImplemented.html +0 -234
- data/docs/api/Brut/Framework/Errors.html +0 -351
- data/docs/api/Brut/Framework/FussyTypeEnforcement.html +0 -392
- data/docs/api/Brut/Framework/MCP.html +0 -871
- data/docs/api/Brut/Framework/ProjectEnvironment.html +0 -648
- data/docs/api/Brut/Framework.html +0 -129
- data/docs/api/Brut/FrontEnd/AssetPathResolver.html +0 -317
- data/docs/api/Brut/FrontEnd/Component/Helpers.html +0 -420
- data/docs/api/Brut/FrontEnd/Component.html +0 -434
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +0 -491
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +0 -526
- data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +0 -313
- data/docs/api/Brut/FrontEnd/Components/Input.html +0 -195
- data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +0 -447
- data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +0 -339
- data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +0 -568
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +0 -419
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +0 -610
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +0 -534
- data/docs/api/Brut/FrontEnd/Components/Inputs.html +0 -125
- data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +0 -367
- data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +0 -355
- data/docs/api/Brut/FrontEnd/Components/TimeTag.html +0 -655
- data/docs/api/Brut/FrontEnd/Components/Traceparent.html +0 -352
- data/docs/api/Brut/FrontEnd/Components.html +0 -156
- data/docs/api/Brut/FrontEnd/CsrfProtector.html +0 -250
- data/docs/api/Brut/FrontEnd/Download.html +0 -467
- data/docs/api/Brut/FrontEnd/Flash.html +0 -1150
- data/docs/api/Brut/FrontEnd/Form.html +0 -1227
- data/docs/api/Brut/FrontEnd/Forms/Button.html +0 -331
- data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +0 -537
- data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +0 -590
- data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +0 -201
- data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +0 -535
- data/docs/api/Brut/FrontEnd/Forms/Input.html +0 -1567
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +0 -635
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +0 -1336
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +0 -730
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +0 -587
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +0 -734
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +0 -582
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +0 -659
- data/docs/api/Brut/FrontEnd/Forms.html +0 -127
- data/docs/api/Brut/FrontEnd/GenericResponse.html +0 -377
- data/docs/api/Brut/FrontEnd/Handler.html +0 -442
- data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +0 -318
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +0 -336
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +0 -399
- data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +0 -354
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +0 -151
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +0 -315
- data/docs/api/Brut/FrontEnd/Handlers.html +0 -125
- data/docs/api/Brut/FrontEnd/HandlingResults.html +0 -339
- data/docs/api/Brut/FrontEnd/HttpMethod.html +0 -661
- data/docs/api/Brut/FrontEnd/HttpStatus.html +0 -496
- data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +0 -284
- data/docs/api/Brut/FrontEnd/Layout.html +0 -486
- data/docs/api/Brut/FrontEnd/Middleware.html +0 -135
- data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +0 -288
- data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +0 -292
- data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +0 -324
- data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +0 -376
- data/docs/api/Brut/FrontEnd/Middlewares.html +0 -125
- data/docs/api/Brut/FrontEnd/Page.html +0 -781
- data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +0 -797
- data/docs/api/Brut/FrontEnd/Pages.html +0 -125
- data/docs/api/Brut/FrontEnd/RequestContext.html +0 -1312
- data/docs/api/Brut/FrontEnd/RouteHook.html +0 -424
- data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +0 -242
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +0 -249
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +0 -264
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +0 -261
- data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +0 -284
- data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +0 -252
- data/docs/api/Brut/FrontEnd/RouteHooks.html +0 -115
- data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +0 -227
- data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +0 -305
- data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +0 -324
- data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +0 -319
- data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +0 -315
- data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +0 -315
- data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +0 -327
- data/docs/api/Brut/FrontEnd/Routing/Route.html +0 -761
- data/docs/api/Brut/FrontEnd/Routing.html +0 -927
- data/docs/api/Brut/FrontEnd/Session.html +0 -1195
- data/docs/api/Brut/FrontEnd.html +0 -134
- data/docs/api/Brut/I18n/BaseMethods.html +0 -931
- data/docs/api/Brut/I18n/ForBackEnd.html +0 -302
- data/docs/api/Brut/I18n/ForCLI.html +0 -302
- data/docs/api/Brut/I18n/ForHTML.html +0 -296
- data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +0 -316
- data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +0 -930
- data/docs/api/Brut/I18n.html +0 -127
- data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +0 -435
- data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +0 -596
- data/docs/api/Brut/Instrumentation/Methods.html +0 -173
- data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +0 -286
- data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +0 -302
- data/docs/api/Brut/Instrumentation/OpenTelemetry.html +0 -866
- data/docs/api/Brut/Instrumentation.html +0 -128
- data/docs/api/Brut/RubocopConfig.html +0 -237
- data/docs/api/Brut/SinatraHelpers/ClassMethods.html +0 -534
- data/docs/api/Brut/SinatraHelpers.html +0 -281
- data/docs/api/Brut/SpecSupport/ClockSupport.html +0 -383
- data/docs/api/Brut/SpecSupport/ComponentSupport.html +0 -496
- data/docs/api/Brut/SpecSupport/E2ETestServer.html +0 -503
- data/docs/api/Brut/SpecSupport/E2eSupport.html +0 -142
- data/docs/api/Brut/SpecSupport/EnhancedNode.html +0 -403
- data/docs/api/Brut/SpecSupport/FlashSupport.html +0 -278
- data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +0 -401
- data/docs/api/Brut/SpecSupport/GeneralSupport.html +0 -195
- data/docs/api/Brut/SpecSupport/HandlerSupport.html +0 -160
- data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +0 -142
- data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +0 -142
- data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +0 -155
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +0 -583
- data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +0 -149
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +0 -466
- data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +0 -149
- data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +0 -149
- data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +0 -165
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +0 -158
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +0 -156
- data/docs/api/Brut/SpecSupport/Matchers.html +0 -125
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +0 -335
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +0 -637
- data/docs/api/Brut/SpecSupport/SessionSupport.html +0 -196
- data/docs/api/Brut/SpecSupport.html +0 -129
- data/docs/api/Brut/TUI/AnsiEscapeCode/Mod.html +0 -409
- data/docs/api/Brut/TUI/AnsiEscapeCode.html +0 -426
- data/docs/api/Brut/TUI/EventLoop/Deque.html +0 -531
- data/docs/api/Brut/TUI/EventLoop.html +0 -676
- data/docs/api/Brut/TUI/Events/BaseEvent.html +0 -449
- data/docs/api/Brut/TUI/Events/EventBus.html +0 -485
- data/docs/api/Brut/TUI/Events/EventLoopStarted.html +0 -211
- data/docs/api/Brut/TUI/Events/Exception.html +0 -523
- data/docs/api/Brut/TUI/Events/Tick.html +0 -294
- data/docs/api/Brut/TUI/Events.html +0 -131
- data/docs/api/Brut/TUI/MarkupString.html +0 -537
- data/docs/api/Brut/TUI/Script/BlockStep.html +0 -300
- data/docs/api/Brut/TUI/Script/Events/CommandExecutionFailed.html +0 -252
- data/docs/api/Brut/TUI/Script/Events/CommandExecutionSucceeded.html +0 -163
- data/docs/api/Brut/TUI/Script/Events/CommandStdErr.html +0 -163
- data/docs/api/Brut/TUI/Script/Events/CommandStdOut.html +0 -300
- data/docs/api/Brut/TUI/Script/Events/ExecutingCommand.html +0 -298
- data/docs/api/Brut/TUI/Script/Events/Message.html +0 -345
- data/docs/api/Brut/TUI/Script/Events/PhaseCompleted.html +0 -229
- data/docs/api/Brut/TUI/Script/Events/PhaseStarted.html +0 -350
- data/docs/api/Brut/TUI/Script/Events/ScriptCompleted.html +0 -282
- data/docs/api/Brut/TUI/Script/Events/ScriptStarted.html +0 -343
- data/docs/api/Brut/TUI/Script/Events/StepCompleted.html +0 -163
- data/docs/api/Brut/TUI/Script/Events/StepStarted.html +0 -346
- data/docs/api/Brut/TUI/Script/Events.html +0 -115
- data/docs/api/Brut/TUI/Script/ExecStep/ProcessStatusFailed.html +0 -210
- data/docs/api/Brut/TUI/Script/ExecStep.html +0 -493
- data/docs/api/Brut/TUI/Script/LoggingSubscriber.html +0 -914
- data/docs/api/Brut/TUI/Script/PutsSubscriber.html +0 -783
- data/docs/api/Brut/TUI/Script/Step.html +0 -313
- data/docs/api/Brut/TUI/Script.html +0 -1250
- data/docs/api/Brut/TUI/Terminal.html +0 -593
- data/docs/api/Brut/TUI/TerminalTheme.html +0 -1403
- data/docs/api/Brut/TUI/Themes/Dark.html +0 -706
- data/docs/api/Brut/TUI/Themes/Light.html +0 -804
- data/docs/api/Brut/TUI/Themes/None.html +0 -218
- data/docs/api/Brut/TUI/Themes.html +0 -115
- data/docs/api/Brut/TUI.html +0 -129
- data/docs/api/Brut.html +0 -341
- data/docs/api/Clock.html +0 -603
- data/docs/api/ModuleName.html +0 -595
- data/docs/api/RichString.html +0 -775
- data/docs/api/SemanticLogger/Appender/Async.html +0 -219
- data/docs/api/Sequel/Extensions/BrutInstrumentation.html +0 -119
- data/docs/api/Sequel/Extensions/BrutMigrations.html +0 -541
- data/docs/api/Sequel/Extensions.html +0 -117
- data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +0 -105
- data/docs/api/Sequel/Plugins/CreatedAt.html +0 -125
- data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +0 -207
- data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +0 -186
- data/docs/api/Sequel/Plugins/ExternalId.html +0 -218
- data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +0 -202
- data/docs/api/Sequel/Plugins/FindBang.html +0 -125
- data/docs/api/Sequel/Plugins.html +0 -117
- data/docs/api/Sequel.html +0 -117
- data/docs/api/SpecSupport/Matchers/BeABug.html +0 -143
- data/docs/api/_index.html +0 -1964
- data/docs/api/class_list.html +0 -54
- data/docs/api/css/common.css +0 -1
- data/docs/api/css/full_list.css +0 -59
- data/docs/api/css/style.css +0 -504
- data/docs/api/file.README.html +0 -172
- data/docs/api/file_list.html +0 -59
- data/docs/api/frames.html +0 -22
- data/docs/api/index.html +0 -172
- data/docs/api/js/app.js +0 -344
- data/docs/api/js/full_list.js +0 -242
- data/docs/api/js/jquery.js +0 -4
- data/docs/api/method_list.html +0 -5542
- data/docs/api/top-level-namespace.html +0 -112
- data/docs/assets/02-confirmation-dialog-browser-element-styled.3NEGM20-.png +0 -0
- data/docs/assets/02-confirmation-dialog-browser-element.DPsf0xUW.png +0 -0
- data/docs/assets/02-confirmation-dialog-browser.DH8ALFO4.png +0 -0
- data/docs/assets/02-confirmation-flow.D9gZ0S5U.png +0 -0
- data/docs/assets/DevEnvironment.DaFcVfwP.png +0 -0
- data/docs/assets/LogoStop.Gb3tDhL1.png +0 -0
- data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
- data/docs/assets/adrs.md.YglbWtQe.js +0 -1
- data/docs/assets/adrs.md.YglbWtQe.lean.js +0 -1
- data/docs/assets/ai.md.ChLnvDAX.js +0 -1
- data/docs/assets/ai.md.ChLnvDAX.lean.js +0 -1
- data/docs/assets/app.B8jAEB7R.js +0 -1
- data/docs/assets/assets.md.BEF6Oz6K.js +0 -19
- data/docs/assets/assets.md.BEF6Oz6K.lean.js +0 -1
- data/docs/assets/basic-form-with-violations.Cv6Y9-Q_.png +0 -0
- data/docs/assets/basic-form.DbHnu0oW.png +0 -0
- data/docs/assets/brut-js.md.BMz0X1Rz.js +0 -12
- data/docs/assets/brut-js.md.BMz0X1Rz.lean.js +0 -1
- data/docs/assets/business-logic.md.DbuaOYGU.js +0 -1
- data/docs/assets/business-logic.md.DbuaOYGU.lean.js +0 -1
- data/docs/assets/chunks/@localSearchIndexroot.DJ8mocCj.js +0 -1
- data/docs/assets/chunks/VPLocalSearchBox.gF-Po_fz.js +0 -8
- data/docs/assets/chunks/framework.C4nOkCZI.js +0 -18
- data/docs/assets/chunks/theme.BjPAOJkz.js +0 -2
- data/docs/assets/cli.md.DDMar_51.js +0 -122
- data/docs/assets/cli.md.DDMar_51.lean.js +0 -1
- data/docs/assets/components.md.Ber8UBM0.js +0 -96
- data/docs/assets/components.md.Ber8UBM0.lean.js +0 -1
- data/docs/assets/configuration.md.DrJ6YVoZ.js +0 -78
- data/docs/assets/configuration.md.DrJ6YVoZ.lean.js +0 -1
- data/docs/assets/css.md.K5rOCOQY.js +0 -21
- data/docs/assets/css.md.K5rOCOQY.lean.js +0 -1
- data/docs/assets/custom-element-tests.md.DiLe-eFw.js +0 -69
- data/docs/assets/custom-element-tests.md.DiLe-eFw.lean.js +0 -1
- data/docs/assets/database-access.md.Dc8l2Plf.js +0 -63
- data/docs/assets/database-access.md.Dc8l2Plf.lean.js +0 -1
- data/docs/assets/database-schema.md.BJ_JhXmO.js +0 -70
- data/docs/assets/database-schema.md.BJ_JhXmO.lean.js +0 -1
- data/docs/assets/deployment.md.CHTx2eTR.js +0 -55
- data/docs/assets/deployment.md.CHTx2eTR.lean.js +0 -1
- data/docs/assets/dev-env-protocol.DysDAtnz.png +0 -0
- data/docs/assets/dev-environment.md.B1S9p5ZK.js +0 -16
- data/docs/assets/dev-environment.md.B1S9p5ZK.lean.js +0 -1
- data/docs/assets/dir-structure.md.D1T2kGwj.js +0 -46
- data/docs/assets/dir-structure.md.D1T2kGwj.lean.js +0 -1
- data/docs/assets/doc-conventions.md.CDnWaEFg.js +0 -1
- data/docs/assets/doc-conventions.md.CDnWaEFg.lean.js +0 -1
- data/docs/assets/end-to-end-tests.md.BJJdNDYL.js +0 -28
- data/docs/assets/end-to-end-tests.md.BJJdNDYL.lean.js +0 -1
- data/docs/assets/features.md.BDWxnyNO.js +0 -154
- data/docs/assets/features.md.BDWxnyNO.lean.js +0 -1
- data/docs/assets/flash-and-session.md.CUsMxoNl.js +0 -79
- data/docs/assets/flash-and-session.md.CUsMxoNl.lean.js +0 -1
- data/docs/assets/form-constraints.md.KlfXSKm2.js +0 -90
- data/docs/assets/form-constraints.md.KlfXSKm2.lean.js +0 -1
- data/docs/assets/forms.md.RK0zkhm0.js +0 -64
- data/docs/assets/forms.md.RK0zkhm0.lean.js +0 -1
- data/docs/assets/getting-started.md.CGJ44juQ.js +0 -31
- data/docs/assets/getting-started.md.CGJ44juQ.lean.js +0 -1
- data/docs/assets/handlers.md.C5tUwmmo.js +0 -54
- data/docs/assets/handlers.md.C5tUwmmo.lean.js +0 -1
- data/docs/assets/hooks.md.CoiYCKRc.js +0 -80
- data/docs/assets/hooks.md.CoiYCKRc.lean.js +0 -1
- data/docs/assets/i18n.md.DxkCKhUw.js +0 -23
- data/docs/assets/i18n.md.DxkCKhUw.lean.js +0 -1
- data/docs/assets/index.md.DnphWyQd.js +0 -1
- data/docs/assets/index.md.DnphWyQd.lean.js +0 -1
- data/docs/assets/initial-home-page.DNIaYmgP.png +0 -0
- data/docs/assets/instrumentation.md.BcxjC4jd.js +0 -90
- data/docs/assets/instrumentation.md.BcxjC4jd.lean.js +0 -1
- data/docs/assets/javascript.md.D6fxhaQb.js +0 -31
- data/docs/assets/javascript.md.D6fxhaQb.lean.js +0 -1
- data/docs/assets/jobs.md.Bi3qb3v6.js +0 -25
- data/docs/assets/jobs.md.Bi3qb3v6.lean.js +0 -1
- data/docs/assets/keyword-injection.md.CqLnnzIz.js +0 -21
- data/docs/assets/keyword-injection.md.CqLnnzIz.lean.js +0 -1
- data/docs/assets/layouts.md.HEbeK7Jr.js +0 -68
- data/docs/assets/layouts.md.HEbeK7Jr.lean.js +0 -1
- data/docs/assets/lsp.md.bE9dW8n9.js +0 -1
- data/docs/assets/lsp.md.bE9dW8n9.lean.js +0 -1
- data/docs/assets/markdown-examples.md.BPmtHlc-.js +0 -33
- data/docs/assets/markdown-examples.md.BPmtHlc-.lean.js +0 -1
- data/docs/assets/middleware.md.BhOIsg59.js +0 -20
- data/docs/assets/middleware.md.BhOIsg59.lean.js +0 -1
- data/docs/assets/new-post-editor.DrHr-5oh.png +0 -0
- data/docs/assets/new-post-home-page.Bm34lyMg.png +0 -0
- data/docs/assets/overview.md.BpWAgPFH.js +0 -1
- data/docs/assets/overview.md.BpWAgPFH.lean.js +0 -1
- data/docs/assets/pages.md.B3sQXpEd.js +0 -45
- data/docs/assets/pages.md.B3sQXpEd.lean.js +0 -1
- data/docs/assets/recipes_alternate-layouts.md.C1QzVkA7.js +0 -22
- data/docs/assets/recipes_alternate-layouts.md.C1QzVkA7.lean.js +0 -1
- data/docs/assets/recipes_authentication.md.CyvoIW82.js +0 -157
- data/docs/assets/recipes_authentication.md.CyvoIW82.lean.js +0 -1
- data/docs/assets/recipes_custom-flash.md.6gFqf2uL.js +0 -26
- data/docs/assets/recipes_custom-flash.md.6gFqf2uL.lean.js +0 -1
- data/docs/assets/recipes_dev-env-secrets.md.DC_jVY9U.js +0 -12
- data/docs/assets/recipes_dev-env-secrets.md.DC_jVY9U.lean.js +0 -1
- data/docs/assets/recipes_form-errors.md.B5ptSzMO.js +0 -66
- data/docs/assets/recipes_form-errors.md.B5ptSzMO.lean.js +0 -1
- data/docs/assets/recipes_indexed-forms.md.BYYQGW2C.js +0 -74
- data/docs/assets/recipes_indexed-forms.md.BYYQGW2C.lean.js +0 -1
- data/docs/assets/recipes_migrations.md.Cid7-3cu.js +0 -97
- data/docs/assets/recipes_migrations.md.Cid7-3cu.lean.js +0 -1
- data/docs/assets/recipes_text-field-component.md.VhOsCtKI.js +0 -101
- data/docs/assets/recipes_text-field-component.md.VhOsCtKI.lean.js +0 -1
- data/docs/assets/roadmap.md.DqC1Y7Zt.js +0 -1
- data/docs/assets/roadmap.md.DqC1Y7Zt.lean.js +0 -1
- data/docs/assets/routes.md.C1dgIBtD.js +0 -21
- data/docs/assets/routes.md.C1dgIBtD.lean.js +0 -1
- data/docs/assets/security.md.Jn4SY1uK.js +0 -1
- data/docs/assets/security.md.Jn4SY1uK.lean.js +0 -1
- data/docs/assets/seed-data.md.UZW0WxYN.js +0 -14
- data/docs/assets/seed-data.md.UZW0WxYN.lean.js +0 -1
- data/docs/assets/spa.qejUdp-5.png +0 -0
- data/docs/assets/space-time-continuum.md.D9rYGDFH.js +0 -1
- data/docs/assets/space-time-continuum.md.D9rYGDFH.lean.js +0 -1
- data/docs/assets/style.B1z60PPQ.css +0 -1
- data/docs/assets/styled-form-with-server-side-violations.Bjxd8Dpv.png +0 -0
- data/docs/assets/styled-form-with-violations.Bv_sa9tg.png +0 -0
- data/docs/assets/styled-home-page-with-posts.Dd4kG89D.png +0 -0
- data/docs/assets/styled-home-page.BzdI7dWz.png +0 -0
- data/docs/assets/tutorial.md.BX6f6l00.js +0 -27
- data/docs/assets/tutorial.md.BX6f6l00.lean.js +0 -1
- data/docs/assets/tutorials_01-intro.md.CzZ3kpF_.js +0 -708
- data/docs/assets/tutorials_01-intro.md.CzZ3kpF_.lean.js +0 -1
- data/docs/assets/tutorials_02-dialog.md.DE5WfCXI.js +0 -274
- data/docs/assets/tutorials_02-dialog.md.DE5WfCXI.lean.js +0 -1
- data/docs/assets/unit-tests.md.vDsdBbO_.js +0 -13
- data/docs/assets/unit-tests.md.vDsdBbO_.lean.js +0 -1
- data/docs/assets/welcome-to-brut.VSWzl17-.png +0 -0
- data/docs/assets/why.md.4WpxdrQ2.js +0 -1
- data/docs/assets/why.md.4WpxdrQ2.lean.js +0 -1
- data/docs/assets/workspace-protocol.C0gXsoDb.png +0 -0
- data/docs/assets.html +0 -47
- data/docs/brut-css/brut.css +0 -1
- data/docs/brut-css/brut.max.css +0 -22372
- data/docs/brut-css/classes/appearances.html +0 -783
- data/docs/brut-css/classes/background-colors.html +0 -3529
- data/docs/brut-css/classes/border-colors.html +0 -3529
- data/docs/brut-css/classes/borders.html +0 -2293
- data/docs/brut-css/classes/dimensions.html +0 -2581
- data/docs/brut-css/classes/flex.html +0 -917
- data/docs/brut-css/classes/foreground-colors.html +0 -3261
- data/docs/brut-css/classes/junk-drawer.html +0 -431
- data/docs/brut-css/classes/layout.html +0 -668
- data/docs/brut-css/classes/lists.html +0 -331
- data/docs/brut-css/classes/positioning.html +0 -1751
- data/docs/brut-css/classes/spacings.html +0 -2633
- data/docs/brut-css/classes/typography.html +0 -2206
- data/docs/brut-css/customization/advanced-configuration.html +0 -204
- data/docs/brut-css/customization/breakpoints.html +0 -227
- data/docs/brut-css/customization/design-system.html +0 -197
- data/docs/brut-css/customization/pseudo-classes.html +0 -228
- data/docs/brut-css/docs.css +0 -98
- data/docs/brut-css/getting-started/core-concepts.html +0 -234
- data/docs/brut-css/getting-started/installation.html +0 -190
- data/docs/brut-css/getting-started/overview.html +0 -210
- data/docs/brut-css/getting-started/simple-example.html +0 -285
- data/docs/brut-css/index.html +0 -193
- data/docs/brut-css/prism-twilight.min.css +0 -1
- data/docs/brut-css/properties/colors.html +0 -1548
- data/docs/brut-css/properties/spacings.html +0 -614
- data/docs/brut-css/properties/typography.html +0 -777
- data/docs/brut-js/api/AjaxSubmit.html +0 -452
- data/docs/brut-js/api/AjaxSubmit.js.html +0 -550
- data/docs/brut-js/api/Autosubmit.html +0 -192
- data/docs/brut-js/api/Autosubmit.js.html +0 -114
- data/docs/brut-js/api/BaseCustomElement.html +0 -1091
- data/docs/brut-js/api/BaseCustomElement.js.html +0 -312
- data/docs/brut-js/api/BrutCustomElements.html +0 -172
- data/docs/brut-js/api/BufferedLogger.html +0 -173
- data/docs/brut-js/api/ConfirmSubmit.html +0 -286
- data/docs/brut-js/api/ConfirmSubmit.js.html +0 -188
- data/docs/brut-js/api/ConfirmationDialog.html +0 -425
- data/docs/brut-js/api/ConfirmationDialog.js.html +0 -194
- data/docs/brut-js/api/ConstraintViolationMessage.html +0 -498
- data/docs/brut-js/api/ConstraintViolationMessage.js.html +0 -191
- data/docs/brut-js/api/ConstraintViolationMessages.html +0 -590
- data/docs/brut-js/api/ConstraintViolationMessages.js.html +0 -149
- data/docs/brut-js/api/CopyToClipboard.html +0 -345
- data/docs/brut-js/api/CopyToClipboard.js.html +0 -147
- data/docs/brut-js/api/Form.html +0 -291
- data/docs/brut-js/api/Form.js.html +0 -198
- data/docs/brut-js/api/I18nTranslation.html +0 -409
- data/docs/brut-js/api/I18nTranslation.js.html +0 -115
- data/docs/brut-js/api/LocaleDetection.html +0 -312
- data/docs/brut-js/api/LocaleDetection.js.html +0 -168
- data/docs/brut-js/api/Logger.html +0 -702
- data/docs/brut-js/api/Logger.js.html +0 -141
- data/docs/brut-js/api/Message.html +0 -238
- data/docs/brut-js/api/Message.js.html +0 -113
- data/docs/brut-js/api/PrefixedLogger.html +0 -369
- data/docs/brut-js/api/RichString.html +0 -1049
- data/docs/brut-js/api/RichString.js.html +0 -167
- data/docs/brut-js/api/Tabs.html +0 -295
- data/docs/brut-js/api/Tabs.js.html +0 -219
- data/docs/brut-js/api/Toast.html +0 -270
- data/docs/brut-js/api/Toast.js.html +0 -153
- data/docs/brut-js/api/Tracing.html +0 -277
- data/docs/brut-js/api/Tracing.js.html +0 -298
- data/docs/brut-js/api/external-CustomElementRegistry.html +0 -140
- data/docs/brut-js/api/external-Performance.html +0 -138
- data/docs/brut-js/api/external-Promise.html +0 -138
- data/docs/brut-js/api/external-ValidityState.html +0 -138
- data/docs/brut-js/api/external-Window.html +0 -233
- data/docs/brut-js/api/external-fetch.html +0 -138
- data/docs/brut-js/api/global.html +0 -400
- data/docs/brut-js/api/index.html +0 -168
- data/docs/brut-js/api/index.js.html +0 -184
- data/docs/brut-js/api/module-testing.html +0 -383
- data/docs/brut-js/api/scripts/linenumber.js +0 -25
- data/docs/brut-js/api/scripts/prettify/Apache-License-2.0.txt +0 -202
- data/docs/brut-js/api/scripts/prettify/lang-css.js +0 -2
- data/docs/brut-js/api/scripts/prettify/prettify.js +0 -28
- data/docs/brut-js/api/styles/jsdoc-default.css +0 -327
- data/docs/brut-js/api/styles/prettify-jsdoc.css +0 -111
- data/docs/brut-js/api/styles/prettify-tomorrow.css +0 -132
- data/docs/brut-js/api/testing.AssetMetadata.html +0 -172
- data/docs/brut-js/api/testing.AssetMetadataLoader.html +0 -171
- data/docs/brut-js/api/testing.CustomElementTest.html +0 -679
- data/docs/brut-js/api/testing.DOMCreator.html +0 -171
- data/docs/brut-js/api/testing_AssetMetadata.js.html +0 -86
- data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +0 -76
- data/docs/brut-js/api/testing_CustomElementTest.js.html +0 -286
- data/docs/brut-js/api/testing_DOMCreator.js.html +0 -96
- data/docs/brut-js/api/testing_index.js.html +0 -99
- data/docs/brut-js.html +0 -40
- data/docs/business-logic.html +0 -29
- data/docs/cli.html +0 -150
- data/docs/components.html +0 -124
- data/docs/configuration.html +0 -106
- data/docs/css.html +0 -49
- data/docs/custom-element-tests.html +0 -97
- data/docs/database-access.html +0 -91
- data/docs/database-schema.html +0 -98
- data/docs/deployment.html +0 -83
- data/docs/dev-environment.html +0 -44
- data/docs/dir-structure.html +0 -74
- data/docs/doc-conventions.html +0 -29
- data/docs/end-to-end-tests.html +0 -56
- data/docs/favicon.ico +0 -0
- data/docs/features.html +0 -182
- data/docs/flash-and-session.html +0 -107
- data/docs/form-constraints.html +0 -118
- data/docs/forms.html +0 -92
- data/docs/getting-started.html +0 -59
- data/docs/handlers.html +0 -82
- data/docs/hashmap.json +0 -1
- data/docs/hooks.html +0 -108
- data/docs/i18n.html +0 -51
- data/docs/index.html +0 -29
- data/docs/instrumentation.html +0 -118
- data/docs/javascript.html +0 -59
- data/docs/jobs.html +0 -53
- data/docs/keyword-injection.html +0 -49
- data/docs/layouts.html +0 -96
- data/docs/lsp.html +0 -29
- data/docs/markdown-examples.html +0 -61
- data/docs/middleware.html +0 -48
- data/docs/overview.html +0 -29
- data/docs/pages.html +0 -73
- data/docs/recipes/alternate-layouts.html +0 -50
- data/docs/recipes/authentication.html +0 -185
- data/docs/recipes/custom-flash.html +0 -54
- data/docs/recipes/dev-env-secrets.html +0 -40
- data/docs/recipes/form-errors.html +0 -94
- data/docs/recipes/indexed-forms.html +0 -102
- data/docs/recipes/migrations.html +0 -125
- data/docs/recipes/text-field-component.html +0 -129
- data/docs/roadmap.html +0 -29
- data/docs/routes.html +0 -49
- data/docs/security.html +0 -29
- data/docs/seed-data.html +0 -42
- data/docs/space-time-continuum.html +0 -29
- data/docs/tutorial.html +0 -55
- data/docs/tutorials/01-intro.html +0 -736
- data/docs/tutorials/02-dialog.html +0 -302
- data/docs/unit-tests.html +0 -41
- data/docs/vp-icons.css +0 -1
- data/docs/why.html +0 -29
- data/docs-todo.md +0 -32
- data/dx/bash_customizations +0 -6
- data/dx/build +0 -73
- data/dx/build.pre +0 -15
- data/dx/docker-compose.env +0 -22
- data/dx/dx.sh.lib +0 -24
- data/dx/exec +0 -75
- data/dx/setupkit.sh.lib +0 -144
- data/dx/show-help-in-app-container-then-wait.sh +0 -38
- data/lib/brut/cli/app.rb +0 -238
- data/lib/brut/cli/app_runner.rb +0 -252
- data/lib/brut/cli/command.rb +0 -258
- data/lib/brut/cli/execution_results.rb +0 -119
- data/lib/brut/front_end/layouts/_internal.html.erb +0 -68
- data/lib/brut/front_end/pages/_missing_page.html.erb +0 -17
- data/mkbrut/.gitignore +0 -16
- data/mkbrut/CODE_OF_CONDUCT.txt +0 -100
- data/mkbrut/Gemfile +0 -3
- data/mkbrut/Gemfile.lock +0 -20
- data/mkbrut/LICENSE.txt +0 -370
- data/mkbrut/README.md +0 -145
- data/mkbrut/Rakefile +0 -2
- data/mkbrut/bin/build +0 -36
- data/mkbrut/bin/ci +0 -19
- data/mkbrut/bin/docs +0 -19
- data/mkbrut/bin/publish +0 -129
- data/mkbrut/bin/rake +0 -16
- data/mkbrut/bin/setup +0 -30
- data/mkbrut/brut-welcome.png +0 -0
- data/mkbrut/deploy/.dockerignore +0 -2
- data/mkbrut/deploy/Dockerfile +0 -25
- data/mkbrut/dx +0 -1
- data/mkbrut/exe/mkbrut +0 -5
- data/mkbrut/lib/mkbrut/app_name.rb +0 -29
- data/mkbrut/lib/mkbrut/app_options.rb +0 -36
- data/mkbrut/lib/mkbrut/cli.rb +0 -189
- data/mkbrut/lib/mkbrut/erb_binding_delegate.rb +0 -20
- data/mkbrut/lib/mkbrut/ops.rb +0 -17
- data/mkbrut/lib/mkbrut/organization.rb +0 -5
- data/mkbrut/lib/mkbrut/segments.rb +0 -8
- data/mkbrut/lib/mkbrut/version.rb +0 -3
- data/mkbrut/lib/mkbrut.rb +0 -20
- data/mkbrut/mkbrut.gemspec +0 -34
- data/mkbrut/templates/Base/app/src/front_end/images/LogoPylon.png +0 -0
- data/mkbrut/templates/Base/bin/build-assets +0 -7
- data/mkbrut/templates/Base/bin/ci +0 -39
- data/mkbrut/templates/Base/bin/db +0 -9
- data/mkbrut/templates/Base/bin/scaffold +0 -9
- data/mkbrut/templates/Base/bin/setup +0 -287
- data/mkbrut/templates/Base/bin/test +0 -9
- data/mkbrut/templates/Base/bin/test-server +0 -29
- data/mkbrut/templates/Base/dx/prune +0 -19
- data/mkbrut/templates/Base/dx/start +0 -30
- data/mkbrut/templates/Base/dx/stop +0 -23
- data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +0 -27
- data/specs/brut/front_end/forms/input.spec.rb +0 -978
- data/specs/brut/front_end/forms/radio_button_group_input.spec.rb +0 -54
- data/specs/brut/front_end/forms/select_input.spec.rb +0 -54
- data/specs/brut/instrumentation/methods.spec.rb +0 -399
- data/specs/brut/junk_drawer.spec.rb +0 -79
- data/specs/brut/tui/ansi_escape_code.spec.rb +0 -30
- data/specs/brut/tui/event_loop.spec.rb +0 -70
- data/specs/brut/tui/events/base_event.spec.rb +0 -26
- data/specs/brut/tui/events/event_bus.spec.rb +0 -141
- data/specs/brut/tui/events/exception.spec.rb +0 -19
- data/specs/brut/tui/events/test_event.rb +0 -5
- data/specs/spec_helper.rb +0 -31
- data/specs/support/matchers/have_constraint_violation.rb +0 -23
- data/specs/support/matchers.rb +0 -5
- data/specs/support.rb +0 -3
- /data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/prefixed_io.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/.dockerignore +0 -0
- /data/{mkbrut/templates → templates}/Base/.env.development.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/.env.test.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/.gitignore +0 -0
- /data/{mkbrut/templates → templates}/Base/.projections.json +0 -0
- /data/{mkbrut/templates → templates}/Base/Dockerfile.dx +0 -0
- /data/{mkbrut/templates → templates}/Base/Gemfile.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/Procfile.development +0 -0
- /data/{mkbrut/templates → templates}/Base/Procfile.test +0 -0
- /data/{mkbrut/templates → templates}/Base/README.md +0 -0
- /data/{mkbrut/templates → templates}/Base/README.md.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/bootstrap.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/config/i18n/en/1_defaults.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/config/i18n/en/2_app.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/public/static/manifest.json.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/app.rb.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/app_data_model.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/db.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/migrations/20240101130000_citext.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/seed/seed_data.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/components/app_component.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/components/custom_element_registration.rb.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/css/index.css +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/css/svgs.css +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/forms/app_form.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/handlers/app_handler.rb +0 -0
- /data/{brutrb.com → templates/Base/app/src/front_end}/images/LogoPylon.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/LogoTransit.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-120x120.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-152x152.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-167x167.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-180x180.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/favicon.ico +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/icon.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/mkicons.sh +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/js/index.js +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/layouts/blank_layout.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/layouts/default_layout.rb.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/pages/app_page.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/pages/home_page.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/support/app_session.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/svgs/README.md +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/svgs/comment-button.svg +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/README.md.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/console +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/dbconsole +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/dev +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/run +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/run.run +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/startup-message +0 -0
- /data/{mkbrut/templates → templates}/Base/config.ru +0 -0
- /data/{mkbrut/templates → templates}/Base/docker-compose.dx.yml +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/README.md +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/bash_customizations +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/bash_customizations.local +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/build +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/dx.sh.lib +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/exec +0 -0
- /data/{dx → templates/Base/dx}/prune +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/show-help-in-app-container-then-wait.sh +0 -0
- /data/{dx → templates/Base/dx}/start +0 -0
- /data/{dx → templates/Base/dx}/stop +0 -0
- /data/{mkbrut/templates → templates}/Base/package.json.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/puma.config.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/e2e/home_page.spec.rb.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/front_end/js/SpecHelper.js +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/front_end/pages/home_page.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/lint_factories.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/spec_helper.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/support.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/BareBones/app/src/front_end/handlers/trigger_exception_handler.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/BareBones/app/src/front_end/js/Example.js.erb +0 -0
- /data/{mkbrut/templates → templates}/segments/BareBones/specs/front_end/handlers/trigger_exception_handler.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/BareBones/specs/front_end/js/Example.spec.js.erb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/back_end/data_models/db/guestbook_message.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/back_end/data_models/migrations/20250628194124_guestbook.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/components/flash_component.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/css/constraint-violations.css +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/fonts/monaspace-xenon.ttf +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/forms/guestbook_message_form.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/handlers/guestbook_message_handler.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/guestbook_page/message_component.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/guestbook_page.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/new_guestbook_message_page.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/back_end/data_models/db/guestbook_message.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/e2e/guest_message.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/factories/db/guestbook_message.factory.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/components/flash_component.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/handlers/guestbook_message_handler.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/guestbook_page/message_component.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/guestbook_page.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/new_guestbook_message_page.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Heroku/bin/deploy +0 -0
- /data/{mkbrut/templates → templates}/segments/Heroku/deploy/docker-entrypoint +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/boot_sidekiq.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/config/sidekiq.yml +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/jobs/app_job.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/jobs/example_job.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/segments/sidekiq_segment.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/bin/run.sidekiq +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/specs/back_end/jobs/example_job.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/specs/integration/sidekiq_works.spec.rb +0 -0
data/brutrb.com/package.json
DELETED
data/brutrb.com/pages.md
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
# Pages
|
|
2
|
-
|
|
3
|
-
A core abstraction of Brut is the core concept of the web: the web page.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
To create a web page, you'll need:
|
|
8
|
-
|
|
9
|
-
* A [Route](/routes) using `page`.
|
|
10
|
-
* A class in `app/src/front_end/pages/` that extends `Brut::FrontEnd::Page`, named [conventionally](/routes#class-naming-conventions) (though in reality, your page willextend `AppPage` in `app/src/front_end/pages/app_page.rb`, which extends `Brut::FrontEnd::Page`).
|
|
11
|
-
* [Optional, but recommended] A test in `specs/front_end/pages`.
|
|
12
|
-
|
|
13
|
-
You can create all this with `bin/scaffold`, which accepts the route you want:
|
|
14
|
-
|
|
15
|
-
```shell
|
|
16
|
-
> bin/scaffold page /new_widgets
|
|
17
|
-
# => app/src/front_end/pages/new_widgets_page.rb
|
|
18
|
-
# => specs/front_end/pages/new_widgets_page.spec.rb
|
|
19
|
-
# => add `page "/new_widgets"` to app/src/app.rb
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
or
|
|
23
|
-
|
|
24
|
-
```shell
|
|
25
|
-
> bin/scaffold page /widget/:id
|
|
26
|
-
# => app/src/front_end/pages/widget_by_id_page.rb
|
|
27
|
-
# => specs/front_end/pages/widget_by_id_page.spec.rb
|
|
28
|
-
# => add `page "/widget/:id"` to app/src/app.rb
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
You can also perform these steps manually.
|
|
32
|
-
|
|
33
|
-
> [!WARNING]
|
|
34
|
-
> Adding a `page` route without the corresponding class may not always
|
|
35
|
-
> work, since Brut may try to load the class. Brut does its best
|
|
36
|
-
> to avoid problems, but you should create your route and classes
|
|
37
|
-
> all at once
|
|
38
|
-
|
|
39
|
-
> [!IMPORTANT]
|
|
40
|
-
> Brut cannot currently reload new routes, so you must
|
|
41
|
-
> restart your dev server when you modify or add routes.
|
|
42
|
-
|
|
43
|
-
### Creating a Page
|
|
44
|
-
|
|
45
|
-
Pages need a `page_template` method that contains calls to Phlex, which will produce
|
|
46
|
-
the page's HTML.
|
|
47
|
-
|
|
48
|
-
If you have not used Phlex before, it's relatively straightfoward. For each HTML
|
|
49
|
-
tag that exists, Phlex provides a method. So, for `<div>`, Phlex provides `div`.
|
|
50
|
-
|
|
51
|
-
Each method accepts parameters which are converted into attributes. Methods can
|
|
52
|
-
also accept blocks that can be used to add more HTML by calling more of Phlex's API.
|
|
53
|
-
|
|
54
|
-
```ruby
|
|
55
|
-
class DashboardPage < AppPage
|
|
56
|
-
def page_template
|
|
57
|
-
header do
|
|
58
|
-
h1 { "Welcome to My App!" }
|
|
59
|
-
time { Date.today }
|
|
60
|
-
end
|
|
61
|
-
main do
|
|
62
|
-
p(class: "body-text") do
|
|
63
|
-
"This is my awesome app! I hope you stay awhile!"
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
By default, this page will be rendered inside `DefaultLayout`, located in
|
|
71
|
-
`app/src/front_end/layouts/default_layout.rb` and discussed in [the layouts
|
|
72
|
-
module](/layouts). The HTML this page will generate, that would then be inserted
|
|
73
|
-
into the layout's HTML, looks like so:
|
|
74
|
-
|
|
75
|
-
```html
|
|
76
|
-
<header>
|
|
77
|
-
<h1>Welcome to My App!</h1>
|
|
78
|
-
<time>2025-07-05</time>
|
|
79
|
-
</header>
|
|
80
|
-
<main>
|
|
81
|
-
<p class="body-text">
|
|
82
|
-
This is my awesome app! I hope you stay awhile!"
|
|
83
|
-
</p>
|
|
84
|
-
</main>
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Accessing Data in a Page
|
|
88
|
-
|
|
89
|
-
Building static pages is fine, but not really why we use web app libraries. Your
|
|
90
|
-
page is a normal class, so you can create instance variables and methods, which can
|
|
91
|
-
do whatever you need.
|
|
92
|
-
|
|
93
|
-
That being said, the initializer is called by Brut and can be given special
|
|
94
|
-
arguments. For example, if your route has as placeholder, e.g. `/widgets/:id`, then
|
|
95
|
-
your initializer will be given the value of `:id` if its initializer has a keyword
|
|
96
|
-
argument named `id:`:
|
|
97
|
-
|
|
98
|
-
```ruby
|
|
99
|
-
def initialize(id:)
|
|
100
|
-
end
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Query string parameters are also avaiable this way, but your page can access a wide
|
|
104
|
-
variety of request-level information simply by declaring a keyword argument to its
|
|
105
|
-
initializer.
|
|
106
|
-
|
|
107
|
-
This mechanism is called [keyword injection](/keyword-injection) and is available to many class you create, including pages.
|
|
108
|
-
|
|
109
|
-
Here is a list of what is available:
|
|
110
|
-
|
|
111
|
-
| Keyword Argument | Type | Description |
|
|
112
|
-
|-------|------|-------------|
|
|
113
|
-
`session:` | `Brut::FrontEnd::Session` (or your app's subclass) | The current session, even if it's empty. See [Flash and Session](/flash-and-session)|
|
|
114
|
-
`flash:` | `Brut::FrontEnd::Flash` (or your app's subclass) | The current flash, even if it's empty. See [Flash and Session](/flash-and-session) |
|
|
115
|
-
`xhr:` | `true` or `false` | true if this was an Ajax request|
|
|
116
|
-
`csrf_token:` | `String`| The current CSRF token. |
|
|
117
|
-
`clock:` | `Clock` | Used when you need to access the current date and time, potentially accounting for time zones. See [Space/Time Continuum](/space-time-continuum)|
|
|
118
|
-
`http_*` | `String` or `nil` | Any parameter that starts with `http_` is assumed to be for an HTTP header. For example, `http_accept_language` would be given the value for the "Accept-Language" header. See [HTTP Headers](/keyword-injection#http-headers) |
|
|
119
|
-
`rack_request_*` | `String` or `nil` | Any parameter that starts with `rack_request_` is assumed to be for a value from the `Rack::Request`. For example, `rack_request_id` would provide the `ip` value from `Rack::Request` |
|
|
120
|
-
`env:` | `Hash` | The Rack env. You are discouraged from using this directly in your pages, but if you need it, it's available. |
|
|
121
|
-
Placeholders | `String` | Any placeholder value from the route definition |
|
|
122
|
-
Any query string paramter | `String` | the value given is always a string.
|
|
123
|
-
Any object placed into the request context | `Object` | Values you place into the request context. See below for an example.
|
|
124
|
-
|
|
125
|
-
Thus, if `Admin::WidgetsByIdPage` responds to the `detail_level` query string parameter, needs access to the current time, wants to
|
|
126
|
-
check a value from the session, and responded to the completely made-up header "X-Be-Nice", the initializer would look like so:
|
|
127
|
-
|
|
128
|
-
```ruby
|
|
129
|
-
def initialize(id:,
|
|
130
|
-
session:,
|
|
131
|
-
clock:,
|
|
132
|
-
http_x_be_nice:,
|
|
133
|
-
detail_level: nil)
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
> [!CAUTION]
|
|
137
|
-
> Keyword arguments for query string parameters **must** have default values
|
|
138
|
-
> or Brut will be unable to instantiate your page class when they are omitted.
|
|
139
|
-
> We recommended that **no other keywords arguments have defaults** to ensure your
|
|
140
|
-
> pages aren't created with `nil` values.
|
|
141
|
-
|
|
142
|
-
> [!NOTE]
|
|
143
|
-
> Omitting a default for an HTTP header is OK, but you should know what the behavior is. See [the HTTP Headers
|
|
144
|
-
> section](/keyword-injection#http-headers) for details.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
### Page Hooks
|
|
148
|
-
|
|
149
|
-
Occasionally, you want to prevent a page from rendering after the visitor has been routed to it. A common reason for this could be a lack of authorization by that visitor to view the page.
|
|
150
|
-
|
|
151
|
-
`before_generate` achieves this. It's called after construction, so has access to
|
|
152
|
-
any injected values, and its return value tells Brut what should happen:
|
|
153
|
-
|
|
154
|
-
* `URI` - the visitor will be redirected to the given URI. Instead of creating a `URI`, you may use the method `redirect_to`, which
|
|
155
|
-
accepts a page and its parameters.
|
|
156
|
-
* `Brut::FrontEnd::HttpStatus` - the page will not be rendered and this status will be returned. You may use `http_status` to create
|
|
157
|
-
an `HttpStatus` from a number.
|
|
158
|
-
* `Brut::FrontEnd::GenericResponse` - a typed wrapper around the standard Rack response.
|
|
159
|
-
* Anything else - page rendering will proceed as usual.
|
|
160
|
-
|
|
161
|
-
## Testing
|
|
162
|
-
|
|
163
|
-
See [Unit Testing](/unit-tests) for some basic assumptions and configuration available for all Brut unit tests.
|
|
164
|
-
|
|
165
|
-
Although pages are Plain Old Ruby Objects, you likely want to test the HTML they
|
|
166
|
-
generate. Brut provides convenience methods to do this based on Nokogiri.
|
|
167
|
-
|
|
168
|
-
### Generating a Response
|
|
169
|
-
|
|
170
|
-
* If your page has no before hook, or you aren't testing that, call `generate_and_parse(page_instance)`. This returns a `Brut::SpecSupport::EnhancedNode`, which is a delegate to Nokogiri's `Nokogiri::XML::Node` (see below for why this exists)
|
|
171
|
-
* If you want to assert behavior of the before hook, call `generate_result`, which
|
|
172
|
-
will return whatever the page's internal `handle!` method called.
|
|
173
|
-
will use one of these matchers on the result:
|
|
174
|
-
|
|
175
|
-
### Asserting Results
|
|
176
|
-
|
|
177
|
-
When using `generate_and_parse`, you have access to all of Nokogiri, however
|
|
178
|
-
`Brut::SpecSupport::EnhancedNode` provides two methods to simplify your test:
|
|
179
|
-
|
|
180
|
-
```ruby
|
|
181
|
-
it "should work" do
|
|
182
|
-
result = generate_and_parse(described_class.new)
|
|
183
|
-
|
|
184
|
-
expect(result.e!("h1").text).to include("Welcome")
|
|
185
|
-
expect(result.e("h2")).to eq(nil)
|
|
186
|
-
end
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
* `e!` returns the node matching the given CSS selector, failing the test if there
|
|
190
|
-
is not exactly one matching node.
|
|
191
|
-
* `e` (no bang) returns the node matching the given CSS selector, or `nil` if none matched. If there is more than one match, the test fails.
|
|
192
|
-
|
|
193
|
-
When using `generate_result`, you will want to use one of two special purpose
|
|
194
|
-
matchers:
|
|
195
|
-
|
|
196
|
-
```ruby
|
|
197
|
-
it "redirects" do
|
|
198
|
-
result = generate_result(described_class.new)
|
|
199
|
-
expect(result).to have_redirected_to(AuthPage)
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
it "404's" do
|
|
203
|
-
result = generate_result(described_class.new)
|
|
204
|
-
expect(result).to have_returned_http_status(404)
|
|
205
|
-
end
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
- `have_redirected_to` to check that a redirect happened to the URI you set (see `Brut::SpecSupport::Matchers::HaveRedirectedTo`)
|
|
209
|
-
- `have_returned_http_status` to check that a given HTTP response was returned (see `Brut::SpecSupport::Matchers::HaveReturnedHttpStatus`)
|
|
210
|
-
|
|
211
|
-
Beyond this, you can use Nokogiri as usual to navigate the DOM that's generated and
|
|
212
|
-
make assertions. A few additional matchers to help are:
|
|
213
|
-
|
|
214
|
-
- `be_routing_for` - expect a URI to be a routing for a certain page or
|
|
215
|
-
page/parameter combination. See `Brut::SpecSupport::Matchers::BeRoutingFor`.
|
|
216
|
-
- `have_html_attribute` - check that a node has an attribute or an attribute with a
|
|
217
|
-
specific value. See `Brut::SpecSupport::Matchers::HaveHTMLAttribute`.
|
|
218
|
-
- `have_i18n_string` - check that a node's text has a string from your [I18n](/i18n)
|
|
219
|
-
configuration. See `Brut::SpecSupport::Matchers::HaveI18nString`.
|
|
220
|
-
|
|
221
|
-
## Recommended Practices
|
|
222
|
-
|
|
223
|
-
### Instance variables (ivars) are fine.
|
|
224
|
-
|
|
225
|
-
Since `page_template` is a method of your class, it has access to your instance variables (ivars). Feel free to
|
|
226
|
-
use them directly. Only create `attr_reader` implementations if a subclass should be expected to override
|
|
227
|
-
something or you want something lazily evaluated. Make them private. Your page's API is just the method `page_template`.
|
|
228
|
-
|
|
229
|
-
### Don't set ivars in `before_generate`
|
|
230
|
-
|
|
231
|
-
It's Ruby and you can do whatever you want, but your page class will be easier to understand and test if you set up necessary state in
|
|
232
|
-
your initializer. Memoization is fine, but don't have your `before_generate` set up additional state if you can avoid it. As we'll see
|
|
233
|
-
below, you won't need to use `before_generate` as a failsafe check on authorization.
|
|
234
|
-
|
|
235
|
-
### Leverage Keyword Injection
|
|
236
|
-
|
|
237
|
-
The list of available data for injection above will always be available to your page, with the exception of query string parameters. The real power comes when you learn how to [inject your own data](/keyword-injection#injecting-custom-data) into the request context.
|
|
238
|
-
|
|
239
|
-
A great example of this is in the [recipe for keywords and auth](/recipes/authentication), which results in a much simpler and less error-prone way to prevent unauthorized access to pages when compared to how you might do it in Rails.
|
|
240
|
-
|
|
241
|
-
### In Tests, It's Fine to Locate Elements Via CSS Selectors
|
|
242
|
-
|
|
243
|
-
Your page's job is to produce HTML. To check if it's doing that, it makes sense to manipulate that HTML using standard, battle-tested
|
|
244
|
-
techniques like CSS selectors. This creates consonance between your in-browser debugging and your test suite.
|
|
245
|
-
|
|
246
|
-
It also makes it much more obvious what's wrong if something is not where you expect it to be.
|
|
247
|
-
|
|
248
|
-
### That Said, Avoid Test-Specific Attributes or Classes
|
|
249
|
-
|
|
250
|
-
When you have a lot of `<div>` elements, it can be tempting to use attributes like `data-testid` on the elements you want to find in
|
|
251
|
-
your tests. You can often avoid this if you use semantic markup and proper ARIA roles. For example, a Flash message is likely
|
|
252
|
-
something you'd put in a `role="status"` or `role="alert"`, so you don't need `data-flash` or `class="flash"` in order to find it in a
|
|
253
|
-
test.
|
|
254
|
-
|
|
255
|
-
Custom Elements can also be helpful here, as that may be how you choose to manage your client-side behavior.
|
|
256
|
-
|
|
257
|
-
## Technical Notes
|
|
258
|
-
|
|
259
|
-
> [!IMPORTANT]
|
|
260
|
-
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
|
261
|
-
> internals, the source code is always more correct.
|
|
262
|
-
|
|
263
|
-
_Last Updated May 4, 2025_
|
|
264
|
-
|
|
265
|
-
### Page Internal API
|
|
266
|
-
|
|
267
|
-
A Page's core API is the method `handle!`, which can return an HTML-safe string, `URI`, or Rack response.
|
|
268
|
-
Developers should avoid overriding this method, as it also handles the logic related to calling `before_generate`
|
|
269
|
-
as well as the logic required to make layouts work.
|
|
270
|
-
|
|
271
|
-
This is why we recommend using `Brut::SpecSupport::ComponentSupport#generate_and_parse` or `Brut::SpecSupport::ComponentSupport#generate_result` in a tests. *They* call `handle!`, thus ensuring your `before_generate` method will be called and that your page class will behave in a test the way it would in production.
|
|
272
|
-
|
|
273
|
-
### Layouts
|
|
274
|
-
|
|
275
|
-
Pages do not have to have a layout. You can override Phlex's `view_template` and produce HTML that will not be
|
|
276
|
-
wrapped in any Layout. It may be a better idea to create a `BlankLayout` class to avoid this, but it's up to
|
|
277
|
-
you.
|
|
278
|
-
|
|
279
|
-
### Helpers in Templates
|
|
280
|
-
|
|
281
|
-
`Brut::FrontEnd::Page` is a subclass of `Brut::FrontEnd::Component`, so all your pages will have access to the helpers included there. This is how, for example, `t` can be called to perform translations.
|
|
282
|
-
|
|
283
|
-
Note that Brut does *not* include `Brut::FrontEnd::Components` (pluralized). You
|
|
284
|
-
can include that in `AppPage` to access Brut's builtin components as a Phlex kit.
|
|
285
|
-
|
|
286
|
-
### So You Don't Like Phlex?
|
|
287
|
-
|
|
288
|
-
Brut did initially use ERB, but the initial Brut-powered apps ended up having an all-too-common mess of HTML, Ruby, and angle brackets. It really sucked. Phlex seems pretty solid and is a very lightweight abstraction over HTML. It keeps everything in Ruby, but still maintains consonance to what you see in your browser.
|
|
289
|
-
|
|
290
|
-
Support for ERB, Slim, or HAML, is not planned ever.
|
|
Binary file
|
|
Binary file
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# Alternate Layouts
|
|
2
|
-
|
|
3
|
-
To create an alternate layout, your page can override `layout` to return a string.
|
|
4
|
-
That string will be camel-cased and preped to `Layout` to form a class that is
|
|
5
|
-
expected to exist and provide the layout. That class must extend
|
|
6
|
-
`Brut::FrontEnd::Layout`.
|
|
7
|
-
|
|
8
|
-
```ruby
|
|
9
|
-
class MyOtherPage < AppPage
|
|
10
|
-
def layout = "other_design"
|
|
11
|
-
|
|
12
|
-
# ...
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
class OtherDesignLayout < Brut::FrontEnd::Layout
|
|
17
|
-
def view_template
|
|
18
|
-
doctype
|
|
19
|
-
html do
|
|
20
|
-
head do
|
|
21
|
-
link(rel: "preload", as: "style", href: asset_path("/css/other-styles.css"))
|
|
22
|
-
link(rel: "stylesheet", href: asset_path("/css/other-styles.css"))
|
|
23
|
-
script(defer: true, src: asset_path("/js/app.js"))
|
|
24
|
-
end
|
|
25
|
-
body do
|
|
26
|
-
yield
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
```
|
|
32
|
-
|
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
# Authentication Example
|
|
2
|
-
|
|
3
|
-
It's impossible to account for all types of authentication you may want to use, but
|
|
4
|
-
this recipe will demonstrate all the moving parts:
|
|
5
|
-
|
|
6
|
-
* How to require authentication for some pages
|
|
7
|
-
* How to design pages that require authentication
|
|
8
|
-
* How to manage the signed-in user in code
|
|
9
|
-
|
|
10
|
-
## Feature
|
|
11
|
-
|
|
12
|
-
* Visitors can log in with an email, that is assumed to have been inserted previously (no passwords or signup, just to simplify the recipe)
|
|
13
|
-
* Visitors can access the home page without logging in
|
|
14
|
-
* Visitors cannot access the dashboard page without logging in
|
|
15
|
-
|
|
16
|
-
## Recipe
|
|
17
|
-
|
|
18
|
-
### Set up Database and Seed Data
|
|
19
|
-
|
|
20
|
-
First, we'll make a database table called `accounts` that will have an email field
|
|
21
|
-
and a password hash field.
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
bin/db new-migration accounts
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
This will create a file in `app/src/back_end/data_models/migrations`. We'll edit it
|
|
28
|
-
to create a new table called `accounts`:
|
|
29
|
-
|
|
30
|
-
```ruby
|
|
31
|
-
Sequel.migration do
|
|
32
|
-
up do
|
|
33
|
-
create_table :accounts, comment: "People or systems who can access this system", external_id: true do
|
|
34
|
-
column :email, :text, unique: true
|
|
35
|
-
column :deactivated_at, :timestamptz, null: true
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
We'll also create `app/src/back_end/data_models/db/account.rb`:
|
|
42
|
-
|
|
43
|
-
```ruby
|
|
44
|
-
class DB::Account < AppDataModel
|
|
45
|
-
has_external_id :a3 # !IMPORTANT: Make sure this is unique amongst your DB models
|
|
46
|
-
end
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Next, we'll create a factory for it in `specs/factories/db/account.factory.rb`:
|
|
50
|
-
|
|
51
|
-
```ruby
|
|
52
|
-
require "bcrypt"
|
|
53
|
-
FactoryBot.define do
|
|
54
|
-
factory :account, class: "DB::Account" do
|
|
55
|
-
email { Faker::Internet.unique.email }
|
|
56
|
-
trait :inactive do
|
|
57
|
-
deactivated_at { Time.now }
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Next, we'll make seed data in `app/src/back_end/data_models/seed/app_seed_data.rb`
|
|
64
|
-
|
|
65
|
-
```ruby
|
|
66
|
-
require "brut/back_end/seed_data"
|
|
67
|
-
class AppSeedData < Brut::BackEnd::SeedData
|
|
68
|
-
include FactoryBot::Syntax::Methods
|
|
69
|
-
def seed!
|
|
70
|
-
create(:account, email: "pat@example.com")
|
|
71
|
-
create(:account, :inactive, email: "chris@example.com")
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Now, let's apply this to the database and load the seed data:
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
> bin/db migrate
|
|
80
|
-
> bin/db migrate -e test
|
|
81
|
-
> bin/db seed
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Create a Login Page
|
|
85
|
-
|
|
86
|
-
To make this UI work, we'll need a login page and a dashboard page.
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
> bin/scaffold page /login
|
|
90
|
-
> bin/scaffold page /dashboard
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
We'll also need a login form:
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
> bin/scaffold form /login
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
We'll add a link on the HomePage to log in:
|
|
100
|
-
|
|
101
|
-
```ruby
|
|
102
|
-
# app/src/front_end/pages/home_page.rb
|
|
103
|
-
class HomePage < AppPage
|
|
104
|
-
def page_template
|
|
105
|
-
h1 { "Welcome!" }
|
|
106
|
-
a(href: LoginPage.routing) {
|
|
107
|
-
"Log in"
|
|
108
|
-
}
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Before building the login page, we'll need the form. It'll just have one field:
|
|
114
|
-
email:
|
|
115
|
-
|
|
116
|
-
```ruby
|
|
117
|
-
# app/src/front_end/forms/login_form.rb
|
|
118
|
-
class LoginForm < AppForm
|
|
119
|
-
input :email # Brut will make this type=email and required
|
|
120
|
-
end
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Now, we can create the login page:
|
|
124
|
-
|
|
125
|
-
```ruby
|
|
126
|
-
# app/src/front_end/pages/login_page.rb
|
|
127
|
-
class LoginPage < AppPage
|
|
128
|
-
|
|
129
|
-
include Brut::FrontEnd::Components
|
|
130
|
-
|
|
131
|
-
# An existing form can be passed in, so that this
|
|
132
|
-
# page can be shown with form errors from a previous
|
|
133
|
-
# login attempt
|
|
134
|
-
def initialize(form: nil)
|
|
135
|
-
@form = form || LoginForm.new
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def page_template
|
|
139
|
-
h1 { "Login, please!" }
|
|
140
|
-
brut_form do
|
|
141
|
-
FormTag(for: @form) do
|
|
142
|
-
label do
|
|
143
|
-
Inputs::InputTag(form: @form, input_name: :email)
|
|
144
|
-
div { "Email" }
|
|
145
|
-
ConstraintViolations(form: @form, input_name: :email)
|
|
146
|
-
end
|
|
147
|
-
button do
|
|
148
|
-
"Login"
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Let's style the constraint violations in `app/src/front_end/css/index.css`:
|
|
157
|
-
|
|
158
|
-
```css
|
|
159
|
-
/* app/src/front_end/css/index.css */
|
|
160
|
-
brut-cv {
|
|
161
|
-
display: none;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
brut-cv[server-side],
|
|
165
|
-
brut-form[submitted-invalid] brut-cv {
|
|
166
|
-
display: block;
|
|
167
|
-
color var(--red-300);
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Now, you can click on "Login", and you should see a client-side error message.
|
|
172
|
-
|
|
173
|
-
### Handle Logins
|
|
174
|
-
|
|
175
|
-
Now, we'll build out the login handler. An email must exist and be active to be
|
|
176
|
-
allowed in.
|
|
177
|
-
|
|
178
|
-
```ruby
|
|
179
|
-
# app/src/front_end/handlers/login_handler.rb
|
|
180
|
-
class LoginHandler < AppHandler
|
|
181
|
-
def initialize(form:, session:, flash:)
|
|
182
|
-
@form = form
|
|
183
|
-
@session = session
|
|
184
|
-
@flash = flash
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
def handle
|
|
188
|
-
if !@form.constraint_violations? # no client-side issues
|
|
189
|
-
account = DB::Account.find(email: @form.email, deactivated_at: nil)
|
|
190
|
-
if !account
|
|
191
|
-
@form.server_side_constraint_violation(
|
|
192
|
-
input_name: :email,
|
|
193
|
-
key: :no_such_account
|
|
194
|
-
)
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
if @form.constraint_violations?
|
|
198
|
-
LoginPage.new(form: @form)
|
|
199
|
-
else
|
|
200
|
-
@session.login!(account:)
|
|
201
|
-
redirect_to(DashboardPage)
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Hopefully, this logic is straightforward. We'll need to allow `AppSession` to
|
|
208
|
-
implement `login!`. We'll also need to have it fetch the `DB::Account` from the
|
|
209
|
-
session, we'll add that, too.
|
|
210
|
-
|
|
211
|
-
```ruby
|
|
212
|
-
# app/src/front_end/support/app_session.rb
|
|
213
|
-
class AppSession < Brut::FrontEnd::Session
|
|
214
|
-
def login!(account:)
|
|
215
|
-
self[:account_id] = account.id
|
|
216
|
-
end
|
|
217
|
-
def account
|
|
218
|
-
DB::Account.find(id: self[:account_id])
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Now, we can build the dashboard page to greet them. Instead of injecting the
|
|
224
|
-
session, however, we're going to inject the account as `current_account:`. We'll
|
|
225
|
-
set this up in a minute.
|
|
226
|
-
|
|
227
|
-
```ruby
|
|
228
|
-
# app/src/front_end/pages/dashboard_page.rb
|
|
229
|
-
class DashboardPage < AppPage
|
|
230
|
-
def initialize(current_account:)
|
|
231
|
-
@current_account = current_account
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def page_template
|
|
235
|
-
h1 { "Dashboard" }
|
|
236
|
-
h2 { "Hello #{@current_account.email}!" }
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Injecting the Current Account
|
|
242
|
-
|
|
243
|
-
We want the current account to be in the `Brut::FrontEnd::RequestContext` if the
|
|
244
|
-
visitor is logged in. We'll do that in a route hook.
|
|
245
|
-
|
|
246
|
-
First, we'll declare it in `App`:
|
|
247
|
-
|
|
248
|
-
```ruby
|
|
249
|
-
# app/src/app.rb
|
|
250
|
-
class App < Brut::Framework::App
|
|
251
|
-
|
|
252
|
-
# ...
|
|
253
|
-
|
|
254
|
-
before :SetupCurrentAccount
|
|
255
|
-
|
|
256
|
-
# ...
|
|
257
|
-
end
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
Now, we can build the `SetupCurrentAccount` route hook. Since it'll run after
|
|
261
|
-
`Brut::FrontEnd::RouteHooks::SetupRequestContext`, we can assume a `RequestContext`
|
|
262
|
-
will be available for injection. The session will be, too, of course:
|
|
263
|
-
|
|
264
|
-
```ruby
|
|
265
|
-
# app/src/front_end/hooks/setup_current_account.rb
|
|
266
|
-
class SetupCurrentAccount < Brut::FrontEnd::RouteHook
|
|
267
|
-
def before(request_context:, session:)
|
|
268
|
-
logged_in = !!session.account
|
|
269
|
-
# NOTE: we do not insert nil. Either insert a value or don't insert.
|
|
270
|
-
if logged_in
|
|
271
|
-
request_context[:current_account] = session.account
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
At this point, the code we've written should work. The only problem is that anyone
|
|
278
|
-
can access the Dashboard page. Granted, doing so without being logged in will cause
|
|
279
|
-
an error, but we don't want that.
|
|
280
|
-
|
|
281
|
-
### Requiring Login
|
|
282
|
-
|
|
283
|
-
To require login, we'll add to the `SetupCurrentAccount` hook we created. We want to
|
|
284
|
-
allow access to the login page as well as any Brut-owned paths. If a logged-out
|
|
285
|
-
user access a restricted page, we'll redirect them to the login page.
|
|
286
|
-
|
|
287
|
-
```ruby
|
|
288
|
-
# app/src/front_end/hooks/setup_current_account.rb
|
|
289
|
-
class SetupCurrentAccount < Brut::FrontEnd::RouteHook
|
|
290
|
-
def before(request_context:, session:)
|
|
291
|
-
logged_in = !!session.account
|
|
292
|
-
if logged_in
|
|
293
|
-
request_context[:current_account] = session.account
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
is_login_page = request.path_info.match(/#{Regexp.escape(LoginPage.routing)}/
|
|
297
|
-
is_brut_owned_path = env["brut.owned_path"]
|
|
298
|
-
|
|
299
|
-
path_requires_login = !is_login_page &&
|
|
300
|
-
!is_brut_owned_path
|
|
301
|
-
|
|
302
|
-
if !logged_in && path_requires_login
|
|
303
|
-
redirect_to(LoginPage)
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
end
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
And that's it! The visitor should be redirected if they aren't logged in, but
|
|
310
|
-
should be allowed to restricted pages like the dashboard page if they are.
|
|
311
|
-
|
|
312
|
-
### You Don't Need Page Hooks for This
|
|
313
|
-
|
|
314
|
-
Implementing something like this in Rails would usually involve similar code to what
|
|
315
|
-
we just did, but pages requiring login would have some sort of `before_action`:
|
|
316
|
-
|
|
317
|
-
```ruby{2}
|
|
318
|
-
class WidgetsController < ApplicationController
|
|
319
|
-
before_action :require_login!
|
|
320
|
-
|
|
321
|
-
# ...
|
|
322
|
-
end
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
This could be shared in a parent page, but you essentially have to remember to do this on every page that requires login (or do the opposite - allow specific pages to be accessed without logging in).
|
|
326
|
-
|
|
327
|
-
In Rails, this is a good practice, because even though your views won't route a
|
|
328
|
-
logged-out visitor to a logged-in page, URL hacking or bugs could result in an
|
|
329
|
-
attempt to do so. You need the failsafe.
|
|
330
|
-
|
|
331
|
-
In Brut, the very definition of the page's class includes the requirement for the
|
|
332
|
-
`current_account`. The page cannot be instantiated without it.
|
|
333
|
-
|
|
334
|
-
Thus, there is no need for a failsafe. `SetupCurrentAccount` handles checking the
|
|
335
|
-
routes, and that's it. If someone hacks a URL or a bug in the code sends a
|
|
336
|
-
logged-out visitor to the dashboard page, Brut literally cannot handle the request, since the `current_account` will be missing.
|