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
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
# Testing Custom Elements
|
|
2
|
-
|
|
3
|
-
While simple custom elements can be tested as part of an [end-to-end test](/end-to-end-tests), more
|
|
4
|
-
complex custom elements can benefit from a unit test. Spoiler: this is not going to be pleasant.
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
BrutJS provides a testing module that uses JSDom to allow you to test your custom elements. There are
|
|
9
|
-
downsides to JSDom, but it's the simplest way to achieve a reasonably-useful unit test.
|
|
10
|
-
|
|
11
|
-
You can use `bin/scaffold` to create a test, which will create a `.spec.js` file in `specs/front_end/js/`.
|
|
12
|
-
Suppose we the custom element `MyElement`:
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
> bin/scaffold custom_element_test app/src/front_end/js/MyElement.js
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
This creates `specs/front_end/js/MyElement.spec.js`:
|
|
19
|
-
|
|
20
|
-
```javascript
|
|
21
|
-
import { withHTML } from "brut-js/testing/index.js"
|
|
22
|
-
|
|
23
|
-
describe("<some-element>", () => {
|
|
24
|
-
withHTML(`
|
|
25
|
-
<my-element>
|
|
26
|
-
</my-element>
|
|
27
|
-
`).test("description here", ({document,window,assert}) => {
|
|
28
|
-
assert.fail("test goes here")
|
|
29
|
-
})
|
|
30
|
-
})
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
`withHTML` creates a JSDom-based document with the given HTML. This HTML is in effect inside the test.
|
|
34
|
-
Mock versions of `document` and `window` are passed to the test, and any other functions you need can be
|
|
35
|
-
as well, such as `assert`.
|
|
36
|
-
|
|
37
|
-
The idea is that you use the browser APIs to examine the DOM and assert the behavior of the custom element
|
|
38
|
-
(as opposed to interacting with the custom element's class).
|
|
39
|
-
|
|
40
|
-
Suppose that `my-element` transform text inside it based on the `transform` attribute. By default, it's `lower` (which will lower-case the text), but can be set to `upper` to upper case the text inside.
|
|
41
|
-
|
|
42
|
-
This means you'll need three tests, each with a different DOM:
|
|
43
|
-
|
|
44
|
-
```javascript
|
|
45
|
-
import { withHTML } from "brut-js/testing/index.js"
|
|
46
|
-
|
|
47
|
-
describe("<some-element>", () => {
|
|
48
|
-
withHTML(`
|
|
49
|
-
<my-element>
|
|
50
|
-
Some Text
|
|
51
|
-
</my-element>
|
|
52
|
-
`).test("lower-cases by default", ({document,window,assert}) => {
|
|
53
|
-
// TBD
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
withHTML(`
|
|
57
|
-
<my-element transform="lower">
|
|
58
|
-
Some Text
|
|
59
|
-
</my-element>
|
|
60
|
-
`).test("lower-cases explicitly", ({document,window,assert}) => {
|
|
61
|
-
// TBD
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
withHTML(`
|
|
65
|
-
<my-element transform="upper">
|
|
66
|
-
Some Text
|
|
67
|
-
</my-element>
|
|
68
|
-
`).test("upper-cases explicitly", ({document,window,assert}) => {
|
|
69
|
-
// TBD
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
When the function you give to `test` is executed, the DOM will have been setup, so you can rely on your
|
|
75
|
-
custom elements `connectedCallback` having been called. Assuming the text transformation for `my-element`
|
|
76
|
-
occurs in `connectedCallback`, here is how you'd test all three cases:
|
|
77
|
-
|
|
78
|
-
```javascript {9,10,18,19,27,28}
|
|
79
|
-
import { withHTML } from "brut-js/testing/index.js"
|
|
80
|
-
|
|
81
|
-
describe("<some-element>", () => {
|
|
82
|
-
withHTML(`
|
|
83
|
-
<my-element>
|
|
84
|
-
Some Text
|
|
85
|
-
</my-element>
|
|
86
|
-
`).test("lower-cases by default", ({document,window,assert}) => {
|
|
87
|
-
const element = document.querySelector("my-element")
|
|
88
|
-
assert.equal(element.textContent.trim(),"some text")
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
withHTML(`
|
|
92
|
-
<my-element transform="lower">
|
|
93
|
-
Some Text
|
|
94
|
-
</my-element>
|
|
95
|
-
`).test("lower-cases explicitly", ({document,window,assert}) => {
|
|
96
|
-
const element = document.querySelector("my-element")
|
|
97
|
-
assert.equal(element.textContent.trim(),"some text")
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
withHTML(`
|
|
101
|
-
<my-element transform="upper">
|
|
102
|
-
Some Text
|
|
103
|
-
</my-element>
|
|
104
|
-
`).test("upper-cases explicitly", ({document,window,assert}) => {
|
|
105
|
-
const element = document.querySelector("my-element")
|
|
106
|
-
assert.equal(element.textContent.trim(),"SOME TEXT")
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
You'll notice almost all of this uses the browser APIs you (should) know and (hopefully) love.
|
|
112
|
-
|
|
113
|
-
You can manipulate the DOM inside a test as well, and it should behave as if you are doing it in a
|
|
114
|
-
browser. Note that many browser APIs are synchronous, so you don't have to add `await` before every
|
|
115
|
-
single line of code.
|
|
116
|
-
|
|
117
|
-
Note that all of these test run under NodeJS, which is different from a browser. This means that code
|
|
118
|
-
like `new InputEvent()` will succeed in returning an `InputEvent`, but said object is in no way the
|
|
119
|
-
`InputEvent` you'd use in a browser. You must use `window.`:
|
|
120
|
-
|
|
121
|
-
```javascript
|
|
122
|
-
// does not work, but doesn't raise an error either
|
|
123
|
-
input.dispatchEvent(new InputEvent("input", {}))
|
|
124
|
-
|
|
125
|
-
// works
|
|
126
|
-
input.dispatchEvent(new window.InputEvent("input", {}))
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
## Recommended Practices
|
|
130
|
-
|
|
131
|
-
The custom element test library is *very* basic. Testing asychronous things like `fetch` is extremely
|
|
132
|
-
difficult. Your best bet is to use these tests for edge cases and error conditions.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
## Technical Notes
|
|
136
|
-
|
|
137
|
-
> [!IMPORTANT]
|
|
138
|
-
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
|
139
|
-
> internals, the source code is always more correct.
|
|
140
|
-
|
|
141
|
-
_Last Updated June 13, 2025_
|
|
142
|
-
|
|
143
|
-
I will be honest with you, this part of Brut needs a lot of work and thinking-through. It's way to
|
|
144
|
-
DSL-tasitc for my tastes, but it does work for some needs. JSDom is not ideal and requires a lot of hoops
|
|
145
|
-
when using events or anything browsers support that it does not.
|
|
146
|
-
|
|
147
|
-
This is highly likely to change. My current thinking on addressing the need is to run the tests in a real
|
|
148
|
-
browser and to make the test setup and code more like what you'd actually write when using these elements.
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
# Database Access / Data Models
|
|
2
|
-
|
|
3
|
-
Brut provides access to the database via the [Sequel library](https://sequel.jeremyevans.net/). Sequel is fully featured and provides a lot of ways of interacting with and managing your database. Brut includes several plugins and extensions to provide opinionated default behavior or additional features.
|
|
4
|
-
|
|
5
|
-
One thing to keep in mind is that Brut refers to your database layer as *database models* (notably not the un-qualified "models"). Brut treats this layer as a *model* of your database, not a model of your *domain* (though you are free to conflate the two).
|
|
6
|
-
|
|
7
|
-
This section details how to access data in your database.
|
|
8
|
-
|
|
9
|
-
> [!NOTE]
|
|
10
|
-
> Brut currently only supports Postgres. Sequel supports many database systems, however Brut's extensions are
|
|
11
|
-
> currently geared toward Postgres only.
|
|
12
|
-
|
|
13
|
-
## Overview
|
|
14
|
-
|
|
15
|
-
Accessing your database in Brut uses Sequel's `Sequel::Model`. A base class called `AppDataModel` exists in your
|
|
16
|
-
app from which all other data models extend:
|
|
17
|
-
|
|
18
|
-
```ruby
|
|
19
|
-
# app/src/back_end/data_models/app_data_model.rb
|
|
20
|
-
AppDataModel = Class.new(Sequel::Model)
|
|
21
|
-
class AppDataModel
|
|
22
|
-
# You can insert your own shared methods here
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# app/src/back_end/data_models/db/account.rb
|
|
26
|
-
class DB::Account < AppDataModel
|
|
27
|
-
end
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
All data models are in the `DB` namespace. This clearly identifies a model as a model of your database and
|
|
31
|
-
not your domain.
|
|
32
|
-
|
|
33
|
-
Inside a data model, you can use all of Sequel's API. In particular, you will want to use its API for
|
|
34
|
-
[associations](https://sequel.jeremyevans.net/rdoc/files/doc/association_basics_rdoc.html) so that you can create
|
|
35
|
-
relationships between models.
|
|
36
|
-
|
|
37
|
-
In your business logic or front-end code, you can access your data using these models:
|
|
38
|
-
|
|
39
|
-
```ruby
|
|
40
|
-
account = DB::Account.find(email: form.email)
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
> [!IMPORTANT]
|
|
44
|
-
> Sequel's `Sequel::Model` is different from Active Record, especially when it comes to associations.
|
|
45
|
-
> `account.organizations` would return an `Array` of `DB::Organization` records, all fetched from the database.
|
|
46
|
-
> `account.organizations_dataset` would return a active-relation style object to allow stacking
|
|
47
|
-
> quieries. **Please** familiarize yourself with Sequel's API.
|
|
48
|
-
|
|
49
|
-
## Testing
|
|
50
|
-
|
|
51
|
-
Testing, as it applies to data models, is made up of two parts: managing test *data* and testing the models
|
|
52
|
-
themselves.
|
|
53
|
-
|
|
54
|
-
### Test Data is Managed with FactoryBot
|
|
55
|
-
|
|
56
|
-
Brut apps come with [FactoryBot](https://github.com/thoughtbot/factory_bot) installed, and this is how you should
|
|
57
|
-
create test (and seed) data.
|
|
58
|
-
|
|
59
|
-
Factories for data models live in `specs/factories/db`. Because data models are in the `DB` namespace, you will
|
|
60
|
-
need to explicitly state the `class:` in the factory, but otherwise, you can use FactoryBot in a conventional
|
|
61
|
-
way. [Faker](https://github.com/faker-ruby/faker) is also installed to allow you to create realistic and
|
|
62
|
-
randomized data.
|
|
63
|
-
|
|
64
|
-
Here is a factory for our hypothetical account:
|
|
65
|
-
|
|
66
|
-
```ruby
|
|
67
|
-
# specs/factories/db/account.factory.rb
|
|
68
|
-
FactoryBot.define do
|
|
69
|
-
factory :account, class: "DB::Account" do
|
|
70
|
-
email { Faker::Internet.unique.email }
|
|
71
|
-
organization
|
|
72
|
-
|
|
73
|
-
trait :inactive do
|
|
74
|
-
deactivated_at { Time.now }
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
The `spec_support.rb` file generated when you created your Brut app should ensure that `FactoryBot::Syntax::Methods` is included in all specs, meaning you can do `create(:account)` to create an instance of `DB::Account`.
|
|
81
|
-
|
|
82
|
-
See [Unit Tests](/unit-tests) for more details on testing and Factory Bot setup.
|
|
83
|
-
|
|
84
|
-
### Testing Your Data Models
|
|
85
|
-
|
|
86
|
-
In general, you don't want to test the configuration in your data models. For example, testing that
|
|
87
|
-
`account.organization = organization` works is largely pointless, since this is provided by Sequel.
|
|
88
|
-
|
|
89
|
-
That said, if you have complex or unusual database constraints, having a test for them can be valuable.
|
|
90
|
-
|
|
91
|
-
Suppose our `DB::Account` has the following check constraint that requires an email end with `@example.com`:
|
|
92
|
-
|
|
93
|
-
```ruby {13-16}
|
|
94
|
-
Sequel.migration do
|
|
95
|
-
up do
|
|
96
|
-
create_table :accounts,
|
|
97
|
-
comment: "People or systems who can access this system",
|
|
98
|
-
external_id: true do
|
|
99
|
-
|
|
100
|
-
column :email, :text, unique: true
|
|
101
|
-
foreign_key :organization_id, :organizations
|
|
102
|
-
column :deactivated_at, :timestamptz, null: true
|
|
103
|
-
|
|
104
|
-
key [:email, :organization_id]
|
|
105
|
-
|
|
106
|
-
constraint(
|
|
107
|
-
:email_must_be_domain,
|
|
108
|
-
"email ~* '@example.com$'"
|
|
109
|
-
)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
To test this, you would try to write invalid data into the database an ensure the expected exception is raised:
|
|
116
|
-
|
|
117
|
-
```ruby {11}
|
|
118
|
-
# specs/back_end/data_models/db/account.spec.rb
|
|
119
|
-
require "spec_helper"
|
|
120
|
-
RSpec.describe DB::Account do
|
|
121
|
-
describe "email" do
|
|
122
|
-
it "must end in @example.com" do
|
|
123
|
-
expect {
|
|
124
|
-
DB::Account.create(
|
|
125
|
-
email: "pat@example.net",
|
|
126
|
-
organization: create(:organization)
|
|
127
|
-
)
|
|
128
|
-
}.to raise_error(Sequel::CheckConstraintViolation)
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
If you don't want to be overly coupled to Sequel's exceptions, you can also assert on the message Postgres will
|
|
135
|
-
produce, which would include the name of the violated constraint:
|
|
136
|
-
|
|
137
|
-
```ruby {11}
|
|
138
|
-
# specs/back_end/data_models/db/account.spec.rb
|
|
139
|
-
require "spec_helper"
|
|
140
|
-
RSpec.describe DB::Account do
|
|
141
|
-
describe "email" do
|
|
142
|
-
it "must end in @example.com" do
|
|
143
|
-
expect {
|
|
144
|
-
DB::Account.create(
|
|
145
|
-
email: "pat@example.net",
|
|
146
|
-
organization: create(:organization)
|
|
147
|
-
)
|
|
148
|
-
}.to raise_error(/email_must_be_domain/)
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Recommended Practices
|
|
155
|
-
|
|
156
|
-
### Do Not Put Business Logic On Your Database Models
|
|
157
|
-
|
|
158
|
-
There's no reason to, or benefit to doing so. What you'll find is that any app of even moderate complexity will
|
|
159
|
-
not have a strict mapping from page to business concept to database table. Rather, these things will all differ
|
|
160
|
-
greatly, and each serves a different purpose.
|
|
161
|
-
|
|
162
|
-
The job of your data models—and the tables they provide access to—is to store reliable and unambiguous data.
|
|
163
|
-
Their job is to ensure there is no bad data such that when you ask the database a question, you get a reliable
|
|
164
|
-
and correct answer.
|
|
165
|
-
|
|
166
|
-
Your views and business logic do not have this exact same job.
|
|
167
|
-
|
|
168
|
-
As such, your models should only contain:
|
|
169
|
-
|
|
170
|
-
* configuration to allow navigating the database.
|
|
171
|
-
* methods to manage type conversions between your types and the strings or numbers required in the database
|
|
172
|
-
* methods to query the data based on data definitions (not business logic).
|
|
173
|
-
|
|
174
|
-
Business logic and data models *do* overlap at times, so there is some judgement in maintaining a clear
|
|
175
|
-
separation of concerns. One way to manage this is to always put all logic elsewhere until you see a pattern of
|
|
176
|
-
re-use that leads you to extract that logic to a data model.
|
|
177
|
-
|
|
178
|
-
### Do Not Use Validations on Models Unless There is No Other Choice
|
|
179
|
-
|
|
180
|
-
Sequel provides a validation layer for use on models. You should not generally use this, since a) data integrity
|
|
181
|
-
is baked into your database design, and b) user interactions and constraints are part of the front-end.
|
|
182
|
-
|
|
183
|
-
That said, there are times when you have data constraints that cannot be modeled in the database. In that case,
|
|
184
|
-
a validation on the data model is better than nothing. Since all data access for your app should go through your
|
|
185
|
-
data models, a validation on a data model has a high chance of being checked.
|
|
186
|
-
|
|
187
|
-
> [!NOTE]
|
|
188
|
-
> Since any process, app, or tool can manipulate your database, model-based validations won't be
|
|
189
|
-
> in effect, and therefore won't be applied. This is why you design your schema to avoid invalid
|
|
190
|
-
> data wherever possible.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
## Technical Notes
|
|
194
|
-
|
|
195
|
-
> [!IMPORTANT]
|
|
196
|
-
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
|
197
|
-
> internals, the source code is always more correct.
|
|
198
|
-
|
|
199
|
-
_Last Updated May 8, 2025_
|
|
200
|
-
|
|
201
|
-
None at this time
|
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
# Database Schema / Migrations
|
|
2
|
-
|
|
3
|
-
Brut provides access to the database via the [Sequel library](https://sequel.jeremyevans.net/). To manage your database schema, Brut uses Sequel's facility for this, with some of its own enhancements.
|
|
4
|
-
|
|
5
|
-
> [!NOTE]
|
|
6
|
-
> Brut currently only supports Postgres. Sequel supports many database systems, however Brut's extensions are
|
|
7
|
-
> currently geared toward Postgres only.
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
|
|
11
|
-
Your database schema is managed by a series of changes that build upon one another
|
|
12
|
-
called *migrations*.
|
|
13
|
-
|
|
14
|
-
For example, if you have a table `widgets` that has a `name` and `description`, to add a `status` field, you cannot `drop table widgets` and then `create table widgets(...)` with the fields. You must instead `alter table widgets(...)` to add the new column.
|
|
15
|
-
|
|
16
|
-
Thus, each migration file is a change to the schema produced by all previous migration files.
|
|
17
|
-
|
|
18
|
-
Brut's provides this via Sequel. See [both](https://sequel.jeremyevans.net/rdoc/files/doc/schema_modification_rdoc.html) [docs](https://sequel.jeremyevans.net/rdoc/files/doc/migration_rdoc.html) for details on the API. Any schema modification method Sequel documents is available, however some default behavior has changed.
|
|
19
|
-
|
|
20
|
-
### Creating Migrations
|
|
21
|
-
|
|
22
|
-
To create a migration, use `bin/db new-migration`. It accepts any number of arguments that will be joined together to form the filename:
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
> bin/db new-migration user accounts
|
|
26
|
-
[ bin/db ] Migration created:
|
|
27
|
-
app/src/back_end/data_models/migrations/20250508132646_user-accounts.rb
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
If you will be creating [database models](/database-access) as well, you may find it
|
|
31
|
-
easier to use `bin/scaffold db_model`, which will create an empty database model
|
|
32
|
-
class, empty test, and empty factory, along with an outline of your migration:
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
bin/scaffold db_model widget
|
|
36
|
-
[ bin/scaffold ] Executing ["bin/db new_migration create_widget"]
|
|
37
|
-
[ bin/db ] Migration created:
|
|
38
|
-
app/src/back_end/data_models/migrations/20250712182257_create_widgets.rb
|
|
39
|
-
[ bin/scaffold ] ["bin/db new_migration create_widgets"] succeeded
|
|
40
|
-
[ bin/scaffold ] Creating DB::Foo in app/src/back_end/data_models/db/widget.rb
|
|
41
|
-
[ bin/scaffold ] Creating spec for DB::Foo in specs/back_end/data_models/db/widget.spec.rb
|
|
42
|
-
[ bin/scaffold ] Creating factory for DB::Foo in specs/factories/db/widget.factory.rb
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
> [!IMPORTANT]
|
|
46
|
-
> Brut doesn't do pluralization logic. Although Sequel does do some, you should
|
|
47
|
-
> not refer to your database model plurally. If you were do run `bin/scaffold
|
|
48
|
-
> db_model widgets`, you'd create the class `DB::Widgets`, which would not work.
|
|
49
|
-
> Be aware.
|
|
50
|
-
|
|
51
|
-
Note that the files are located in `app/src/back_end/data_models/migrations` and
|
|
52
|
-
have a name prefixed with a timestamp. This timestamp determins an ordering of how
|
|
53
|
-
the files are applied to the database.
|
|
54
|
-
|
|
55
|
-
The file is created mostly blank:
|
|
56
|
-
|
|
57
|
-
```ruby
|
|
58
|
-
Sequel.migration do
|
|
59
|
-
up do
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
> [!NOTE]
|
|
65
|
-
> Sequels' migration API is similar in concept to Rails', but differs
|
|
66
|
-
> significantly in specifics. Please consult Sequel's documentation and
|
|
67
|
-
> don't assume Railsism will work the same way.
|
|
68
|
-
|
|
69
|
-
Brut encourages only "up" migrations. Since Brut treats your development database
|
|
70
|
-
as ephemeral, there is little value to managing "down" migrations.
|
|
71
|
-
|
|
72
|
-
This is why Sequel's `change` method is not included in the scaffolded code. `change`, like Active Record's method of the same name, automagically creates both "up" and "down" migrations, but *only* if you use the DSL. If you use raw SQL, `change` doesn't work. By using only `up`, you won't have to worry about this.
|
|
73
|
-
|
|
74
|
-
Let's create an accounts table that has an email field, a `deactivated_at` timestamp, and a `created_at` timestamp:
|
|
75
|
-
|
|
76
|
-
```ruby
|
|
77
|
-
Sequel.migration do
|
|
78
|
-
up do
|
|
79
|
-
create_table :accounts,
|
|
80
|
-
comment: "People or systems who can access this system" do
|
|
81
|
-
|
|
82
|
-
column :email, :text, unique: true
|
|
83
|
-
column :deactivated_at, :timestamptz, null: true
|
|
84
|
-
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
A few notes that aren't obvious without knowing about Brut's extensions:
|
|
91
|
-
|
|
92
|
-
* `comment:` is required. You must provide documentation about what table is for
|
|
93
|
-
* The table has a primary key named `id` of type `int` that is a serial.
|
|
94
|
-
* `created_at` is created by default, with time `timestamptz` (AKA `timestamp with time zone`, see [Space/Time Continuum](/space-time-continuum)).
|
|
95
|
-
* `email` is not null by default. `deactivated_at` *is* null because it's specified as such.
|
|
96
|
-
|
|
97
|
-
To apply this migration use `bin/db migrate`
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
> bin/db migrate
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Managing Migrations
|
|
104
|
-
|
|
105
|
-
Sequel uses a special database table to understand which migrations have been run. This table will exist in
|
|
106
|
-
production and prevent you from applying migrations twice or skipping a migration.
|
|
107
|
-
|
|
108
|
-
Note that managing a production database in this way requires knowledge of both your database system and the data
|
|
109
|
-
itself. Brut can only provide so much to make this process manageable. You should consult [Strong Migrations'
|
|
110
|
-
README](https://github.com/ankane/strong_migrations?tab=readme-ov-file) and learn it deeply. Although it's
|
|
111
|
-
targeted at Rails developers, the information here applies to any database management system.
|
|
112
|
-
|
|
113
|
-
### Brut Extensions and Changes in Sequel's Behavior
|
|
114
|
-
|
|
115
|
-
Brut includes the following standard plugins and extensions:
|
|
116
|
-
|
|
117
|
-
* [`pg_array`](https://sequel.jeremyevans.net/rdoc-plugins/files/lib/sequel/extensions/pg_array_rb.html)
|
|
118
|
-
* [`pg_json`](https://sequel.jeremyevans.net/rdoc-plugins/files/lib/sequel/extensions/pg_json_rb.html)
|
|
119
|
-
* [`table_select`](https://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/TableSelect.html), which
|
|
120
|
-
changes queries to prepend `*` with the table name, e.g. `select accounts.*` instead of `select *`.
|
|
121
|
-
* [`skip_saving_columns`](https://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/SkipSavingColumns.html) which will skip saving columns that the database generates.
|
|
122
|
-
|
|
123
|
-
Brut also provides the following plugins and behavior changes:
|
|
124
|
-
|
|
125
|
-
* `Sequel::Extensions::BrutInstrumentation`, which adds OpenTelemetry instrumentation to Sequel (see [Instrumentation](/instrumentation)).
|
|
126
|
-
* `Sequel::Plugins::FindBang`, which adds `find!` to all models. This wraps Sequel's `first!` method, but
|
|
127
|
-
provides a more helpful error message when no records are found
|
|
128
|
-
* `Sequel::Plugins::CreatedAt`, which automatically sets `created_at` when a record is created.
|
|
129
|
-
* `Sequel::Plugins::ExternalId`, which adds support for external IDs (see below)
|
|
130
|
-
* `Sequel::Extensions::BrutMigrations`, which enhances the migrations API (see below)
|
|
131
|
-
|
|
132
|
-
#### External IDs
|
|
133
|
-
|
|
134
|
-
It's often useful to provide a unique identifier for a record that is not the database primary key. There are
|
|
135
|
-
many advantages to doing so, the main being that your primary and foreign keys are
|
|
136
|
-
considered private and for developer use only. Creating additional externalizable
|
|
137
|
-
unique keys is trivial, so Brut provides a way to do that.
|
|
138
|
-
|
|
139
|
-
> [!NOTE]
|
|
140
|
-
> **Primary keys** and **keys** are not the same thing. **Primary keys** are
|
|
141
|
-
> what is used to identify a record for the purposes of referential integrity.
|
|
142
|
-
> A **key** simply uniquely identifies a row or is a unique constraint on a table.
|
|
143
|
-
> Tables have only one primary key, but potentially many keys. Brut uses
|
|
144
|
-
> *synthetic* (sometimes called *surrogate*) keys as primary keys. This means
|
|
145
|
-
> they have no business meaning and can be safely used for foreign keys
|
|
146
|
-
> and other cases without conflating them with domain concepts.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
In Brut, an external ID is automatically generated by the database when a record is created. By convention, it
|
|
150
|
-
is prefixed with a short string representing your app and a short string representing the table, followed by a
|
|
151
|
-
unique hash.
|
|
152
|
-
|
|
153
|
-
For example, if our app's prefix is, say, "my" (for "my app"), and the accounts table's prefix is "ac" (for "accounts"), an external ID might look like `myac_3457238947239487`. This double-prefixing is extremely useful when sharing these values with the outside world. You can immediately identify an ID from your app *and* know what sort of thing it refers to.
|
|
154
|
-
|
|
155
|
-
To use external IDs in Brut, you must do three things:
|
|
156
|
-
|
|
157
|
-
1. You must set your external ID prefix in `app/src/app.rb`. This should have been done when you created your
|
|
158
|
-
Brut app, but it looks like so:
|
|
159
|
-
|
|
160
|
-
```ruby {5}
|
|
161
|
-
class App < Brut::App
|
|
162
|
-
# ...
|
|
163
|
-
def initialize
|
|
164
|
-
# ...
|
|
165
|
-
Brut.container.override("external_id_prefix","my")
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# ...
|
|
169
|
-
end
|
|
170
|
-
```
|
|
171
|
-
2. When creating the table in a migration, use `external_id: true`:
|
|
172
|
-
```ruby {5}
|
|
173
|
-
Sequel.migration do
|
|
174
|
-
up do
|
|
175
|
-
create_table :accounts,
|
|
176
|
-
comment: "People or systems who can access this system",
|
|
177
|
-
external_id: true do
|
|
178
|
-
|
|
179
|
-
column :email, :text, unique: true
|
|
180
|
-
column :deactivated_at, :timestamptz, null: true
|
|
181
|
-
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
```
|
|
186
|
-
3. In your [data model class](/database-access), use `has_external_id` to specify the prefix for this table:
|
|
187
|
-
|
|
188
|
-
```
|
|
189
|
-
class DB::Account < AppDataModel
|
|
190
|
-
has_external_id :ac
|
|
191
|
-
|
|
192
|
-
# ...
|
|
193
|
-
end
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Brut creates the external ID using Ruby code as part of Sequel's lifecycle hooks. It's only set a) on creation, and b) if there is no value provided when creating the record.
|
|
197
|
-
|
|
198
|
-
This means that you can set values explicitly if you like, *and* you can change them later. This is useful if you shared the value with someone you didn't mean to. Because these external IDs aren't use for referential integrity/foreign keys, they can be changed at any time, as long as the value is unique (which will be enforced by the database).
|
|
199
|
-
|
|
200
|
-
### Brut Migration Changes and Enhancement
|
|
201
|
-
|
|
202
|
-
Brut attempts to set default behavior for migrations to encourage a modicum of best practices. These are:
|
|
203
|
-
|
|
204
|
-
* **Automatic synthetic primary key named `id` of type `int`.** You almost always want this. You can change the
|
|
205
|
-
primary key configuration per table if you like, but if you do nothing, you get a primary key that works for 99%
|
|
206
|
-
of your needs.
|
|
207
|
-
* **Automatic `created_at` of type `timestamptz`.** It's a good practice to store the date a record was created. This can help with debugging and provide a reliable sort key for data that otherwise has none. It uses `timestamp with time zone`, which you are encouraged to use always. See [Space/Time Continuum](/space-time-continuum) for details.
|
|
208
|
-
* **No automatic `updated_at`.** While you are free to add `updated_at`, in practice this column creates more problems than it solves. If you need to know when data has changed, it is almost always better to do this with an audit table, event log, or special-purpose field.
|
|
209
|
-
* **`create_table` requires `comment:`.** Just document your tables. It takes two seconds and can save a lot of
|
|
210
|
-
time later.
|
|
211
|
-
* **Support for external IDs via `external_id:`.** As discussed above, this will create a unique `external_id`
|
|
212
|
-
column on your table and ensure it has a value on creation.
|
|
213
|
-
* **Columns are `NOT NULL` by default.** Null is not a valid value. In many cases, your columns should not allow
|
|
214
|
-
`NULL` (`nil`), so in Brut apps, you must opt into nullable columns. You can use `null: true` to make a column
|
|
215
|
-
nullable.
|
|
216
|
-
* **Foreign keys are `NOT NULL` and have an index created for them by default.** Foreign keys should rarely be
|
|
217
|
-
`NULL` and you almost always want an index on them, since you are likely to using them in queries, e.g.
|
|
218
|
-
`account.widgets` would join on `accounts.widget_id`. You can opt out of either via `null: true` and `index:
|
|
219
|
-
false`.
|
|
220
|
-
* **The method `key` allows you to specify a non-primary key, AKA a unique index**. Suppose our `accounts` table
|
|
221
|
-
allowed duplicate email addresses, but only one per `organization_id`. You'd model this by creating a unique
|
|
222
|
-
index on `(email,organization_id)`. In Brut:
|
|
223
|
-
```ruby {11}
|
|
224
|
-
Sequel.migration do
|
|
225
|
-
up do
|
|
226
|
-
create_table :accounts,
|
|
227
|
-
comment: "People or systems who can access this system",
|
|
228
|
-
external_id: true do
|
|
229
|
-
|
|
230
|
-
column :email, :text, unique: true
|
|
231
|
-
foreign_key :organization_id, :organizations
|
|
232
|
-
column :deactivated_at, :timestamptz, null: true
|
|
233
|
-
|
|
234
|
-
key [:email, :organization_id]
|
|
235
|
-
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
This allows your migrations to be more expressive *and* make it easier to set up unique constraints that relate
|
|
242
|
-
to your business logic or domain.
|
|
243
|
-
|
|
244
|
-
## Testing
|
|
245
|
-
|
|
246
|
-
Generally, you don't test database migrations, however you may want to test constraints or other logic you have
|
|
247
|
-
set up. Techniques for doing this are in the [database access](/database-access#testing) section.
|
|
248
|
-
|
|
249
|
-
## Recommended Practices
|
|
250
|
-
|
|
251
|
-
### Ephemeral Dev Database
|
|
252
|
-
|
|
253
|
-
Brut intends for your develompent database to be ephemeral. Your entire workflow should be built around it
|
|
254
|
-
being OK and normal to completely blow away your development database and recreate it. This is why down
|
|
255
|
-
migrations (and the use of `change`) are discouraged. You really don't need them.
|
|
256
|
-
|
|
257
|
-
Assuming you have [seed data](/seed-data) set up properly, you can reliably reset everything like so:
|
|
258
|
-
|
|
259
|
-
```
|
|
260
|
-
> bin/db rebuild
|
|
261
|
-
> bin/db seed
|
|
262
|
-
> bin/db rebuild -e test
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
As long as you don't change migrations that have been applied in production, you can safely run the above
|
|
266
|
-
commands to iterate on a schema change.
|
|
267
|
-
|
|
268
|
-
This does imply that you should not run business logic or make *data* changes in your migration files. No migration
|
|
269
|
-
should rely on specific data being in the database.
|
|
270
|
-
|
|
271
|
-
This workflow my be much different from what you are used to, but you will be quite happy when you adopt it. The days
|
|
272
|
-
of downloading a carefully-curated database image and taking great care to never delete it are over.
|
|
273
|
-
|
|
274
|
-
### Use Your Database, It is Awesome
|
|
275
|
-
|
|
276
|
-
Your database is the only part of the system that has any chance of ensuring data integrity. You can use
|
|
277
|
-
constraints, types, foreign keys, etc. to ensure that the data in your database is correct, based on your current
|
|
278
|
-
understandings. Code-based validation systems **cannot achieve this on any level**.
|
|
279
|
-
|
|
280
|
-
Thus, you are encouraged to learn about your database's features and use them!
|
|
281
|
-
|
|
282
|
-
For example, here's a way to add full text search to an existing table in Postgres:
|
|
283
|
-
|
|
284
|
-
```ruby
|
|
285
|
-
add_column :full_text_search,
|
|
286
|
-
:tsvector,
|
|
287
|
-
generated_always_as: Sequel.lit(%{
|
|
288
|
-
(
|
|
289
|
-
setweight(to_tsvector('english', name),'A') ||
|
|
290
|
-
setweight(to_tsvector('english', coalesce(description,'')),'B')
|
|
291
|
-
)
|
|
292
|
-
}),
|
|
293
|
-
generated_type: :stored
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
If you are using Postgtes, why *not* use its features? Unless your app is database-agnostic, you should be using
|
|
297
|
-
the features of your database, even if they aren't explicitly exposed via Sequel's Ruby API (that's why `Sequel.lit` exists).
|
|
298
|
-
|
|
299
|
-
## Technical Notes
|
|
300
|
-
|
|
301
|
-
> [!IMPORTANT]
|
|
302
|
-
> Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's
|
|
303
|
-
> internals, the source code is always more correct.
|
|
304
|
-
|
|
305
|
-
_Last Updated May 8, 2025_
|
|
306
|
-
|
|
307
|
-
As mentioned, Brut uses Sequel under the covers. This is unlikely to change.
|
|
308
|
-
|
|
309
|
-
As also mentioned, Brut's extensions often rely on Postgres. While we can all dream of a world where every
|
|
310
|
-
developer uses the same database server, we don't live in that world. Brut should, some day, support all the
|
|
311
|
-
databases that Sequel supports. For now, however, it only supports Postgres.
|
|
312
|
-
|
|
313
|
-
This hard-coded support is due to:
|
|
314
|
-
|
|
315
|
-
* `pg_array`
|
|
316
|
-
* `pg_json`
|
|
317
|
-
* Reliance on `citext` and `comment`
|
|
318
|
-
* Reliance on `timestamptz`
|
|
319
|
-
|
|
320
|
-
Brut is likely to add more Postgres-specific features before adding support for other databases.
|