brut 0.17.0 → 0.18.0
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/lib/brut/cli/apps/new/version.rb +3 -0
- data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/versions.rb +2 -2
- 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 +191 -1055
- data/.gitignore +0 -61
- data/.projections.json +0 -10
- data/CHANGELOG.md +0 -172
- data/CODE_OF_CONDUCT.txt +0 -99
- data/Dockerfile.dx +0 -82
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -246
- data/LICENSE.txt +0 -370
- data/README.md +0 -90
- data/Rakefile +0 -25
- data/assets/Logo-Square.pxd +0 -0
- data/assets/LogoPylon.pxd +0 -0
- data/assets/LogoStop.pxd +0 -0
- data/assets/LogoTall.pxd +0 -0
- data/assets/MetroIcon.graffle +0 -0
- data/assets/MetroLogo.graffle +0 -0
- data/assets/SocialImage.png +0 -0
- data/assets/SocialImage.pxd +0 -0
- data/assets/YouTubeThumb.pxd +0 -0
- data/bin/bin_kit.rb +0 -51
- data/bin/build +0 -86
- data/bin/ci +0 -40
- data/bin/dev +0 -20
- data/bin/docs +0 -86
- data/bin/generate-and-run-rubocop +0 -52
- data/bin/new-version +0 -8
- data/bin/publish +0 -61
- data/bin/rake +0 -27
- data/bin/rspec +0 -27
- data/bin/rubocop +0 -27
- data/bin/setup +0 -252
- data/bin/test +0 -18
- data/brut-css/.nvim.lua +0 -1
- data/brut-css/README.md +0 -28
- data/brut-css/bin/build +0 -50
- data/brut-css/bin/ci +0 -19
- data/brut-css/bin/dev +0 -1
- data/brut-css/bin/docs +0 -34
- data/brut-css/bin/publish +0 -21
- data/brut-css/bin/setup +0 -6
- data/brut-css/config/media-queries-all.css +0 -15
- data/brut-css/config/media-queries-minimal.css +0 -5
- data/brut-css/config/postcss.config.cjs +0 -7
- data/brut-css/config/pseudo-classes-all.css +0 -9
- data/brut-css/dx +0 -1
- data/brut-css/package-lock.json +0 -3165
- data/brut-css/package.json +0 -36
- data/brut-css/src/css/appearance.css +0 -145
- data/brut-css/src/css/border.css +0 -522
- data/brut-css/src/css/colors.css +0 -3502
- data/brut-css/src/css/dimensions.css +0 -548
- data/brut-css/src/css/flex.css +0 -179
- data/brut-css/src/css/index.css +0 -13
- data/brut-css/src/css/layout.css +0 -120
- data/brut-css/src/css/list.css +0 -41
- data/brut-css/src/css/positioning.css +0 -354
- data/brut-css/src/css/properties/colors.css +0 -455
- data/brut-css/src/css/properties/index.css +0 -3
- data/brut-css/src/css/properties/spacing.css +0 -140
- data/brut-css/src/css/properties/typography.css +0 -224
- data/brut-css/src/css/reset.css +0 -107
- data/brut-css/src/css/spacing.css +0 -585
- data/brut-css/src/css/typography.css +0 -519
- data/brut-css/src/css/utils.css +0 -104
- data/brut-css/src/docs/1_getting-started/1_overview.md +0 -46
- data/brut-css/src/docs/1_getting-started/2_installation.md +0 -25
- data/brut-css/src/docs/1_getting-started/3_core-concepts.md +0 -75
- data/brut-css/src/docs/1_getting-started/4_simple-example.md +0 -132
- data/brut-css/src/docs/1_getting-started/page.html.ejs +0 -10
- data/brut-css/src/docs/2_properties/page.html.ejs +0 -71
- data/brut-css/src/docs/3_classes/color-demo.html.ejs +0 -31
- data/brut-css/src/docs/3_classes/page.html.ejs +0 -87
- data/brut-css/src/docs/4_customization/1_design-system.md +0 -36
- data/brut-css/src/docs/4_customization/2_breakpoints.md +0 -75
- data/brut-css/src/docs/4_customization/3_pseudo-classes.md +0 -74
- data/brut-css/src/docs/4_customization/4_advanced-configuration.md +0 -40
- data/brut-css/src/docs/4_customization/page.html.ejs +0 -10
- data/brut-css/src/docs/docs.css +0 -98
- data/brut-css/src/docs/includes/body-and-header.html.ejs +0 -30
- data/brut-css/src/docs/includes/footer-and-rest.html.ejs +0 -9
- data/brut-css/src/docs/includes/head.html.ejs +0 -5
- data/brut-css/src/docs/includes/nav.html.ejs +0 -10
- data/brut-css/src/docs/index.html.ejs +0 -32
- data/brut-css/src/docs/prism-twilight.min.css +0 -1
- data/brut-css/src/js/Logger.js +0 -71
- data/brut-css/src/js/build.js +0 -111
- data/brut-css/src/js/cli/CLIArgError.js +0 -7
- data/brut-css/src/js/cli/Debug.js +0 -27
- data/brut-css/src/js/cli/DocsDir.js +0 -16
- data/brut-css/src/js/cli/DocsTemplateSourceDir.js +0 -16
- data/brut-css/src/js/cli/InputFile.js +0 -31
- data/brut-css/src/js/cli/MediaQueryConfigFile.js +0 -10
- data/brut-css/src/js/cli/OutputFile.js +0 -22
- data/brut-css/src/js/cli/ParsedArg.js +0 -17
- data/brut-css/src/js/cli/PathToBrutCSSRoot.js +0 -19
- data/brut-css/src/js/cli/PseudoClassConfigFile.js +0 -11
- data/brut-css/src/js/cli.js +0 -108
- data/brut-css/src/js/docGenerator.js +0 -467
- data/brut-css/src/js/mediaQueryConfigParser.js +0 -98
- data/brut-css/src/js/post-css-plugins/addMediaQueriesPlugin.js +0 -49
- data/brut-css/src/js/post-css-plugins/addPseudoClassesPlugin.js +0 -42
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Category.js +0 -9
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/DocState.js +0 -185
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Documentable.js +0 -8
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Group.js +0 -7
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/ParsedComment.js +0 -73
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Property.js +0 -9
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyCategory.js +0 -4
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyGroup.js +0 -8
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Rule.js +0 -12
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleCategory.js +0 -4
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleGroup.js +0 -8
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeRef.js +0 -5
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeURL.js +0 -9
- data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin.js +0 -49
- data/brut-css/src/js/post-css-plugins/generateRootCustomPropertiesPlugin.js +0 -45
- data/brut-css/src/js/pseudoClassConfigParser.js +0 -145
- data/brut-js/.projections.json +0 -10
- data/brut-js/README.md +0 -118
- data/brut-js/bin/build +0 -19
- data/brut-js/bin/ci +0 -5
- data/brut-js/bin/docs +0 -25
- data/brut-js/bin/publish +0 -21
- data/brut-js/bin/setup +0 -6
- data/brut-js/docs/README.md +0 -8
- data/brut-js/docs/jsdoc-plugins/customElementTag.js +0 -8
- data/brut-js/docs/jsdoc-theme/publish.js +0 -692
- data/brut-js/docs/jsdoc-theme/static/scripts/linenumber.js +0 -25
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/Apache-License-2.0.txt +0 -202
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/lang-css.js +0 -2
- data/brut-js/docs/jsdoc-theme/static/scripts/prettify/prettify.js +0 -28
- data/brut-js/docs/jsdoc-theme/static/styles/jsdoc-default.css +0 -327
- data/brut-js/docs/jsdoc-theme/static/styles/prettify-jsdoc.css +0 -111
- data/brut-js/docs/jsdoc-theme/static/styles/prettify-tomorrow.css +0 -132
- data/brut-js/docs/jsdoc-theme/tmpl/augments.tmpl +0 -10
- data/brut-js/docs/jsdoc-theme/tmpl/container.tmpl +0 -199
- data/brut-js/docs/jsdoc-theme/tmpl/details.tmpl +0 -143
- data/brut-js/docs/jsdoc-theme/tmpl/example.tmpl +0 -2
- data/brut-js/docs/jsdoc-theme/tmpl/examples.tmpl +0 -13
- data/brut-js/docs/jsdoc-theme/tmpl/exceptions.tmpl +0 -32
- data/brut-js/docs/jsdoc-theme/tmpl/layout.tmpl +0 -38
- data/brut-js/docs/jsdoc-theme/tmpl/mainpage.tmpl +0 -14
- data/brut-js/docs/jsdoc-theme/tmpl/members.tmpl +0 -38
- data/brut-js/docs/jsdoc-theme/tmpl/method.tmpl +0 -131
- data/brut-js/docs/jsdoc-theme/tmpl/modifies.tmpl +0 -14
- data/brut-js/docs/jsdoc-theme/tmpl/params.tmpl +0 -131
- data/brut-js/docs/jsdoc-theme/tmpl/properties.tmpl +0 -108
- data/brut-js/docs/jsdoc-theme/tmpl/returns.tmpl +0 -19
- data/brut-js/docs/jsdoc-theme/tmpl/source.tmpl +0 -8
- data/brut-js/docs/jsdoc-theme/tmpl/tutorial.tmpl +0 -19
- data/brut-js/docs/jsdoc-theme/tmpl/type.tmpl +0 -7
- data/brut-js/docs/jsdoc.config.json +0 -23
- data/brut-js/docs/package-lock.json +0 -343
- data/brut-js/docs/package.json +0 -7
- data/brut-js/dx +0 -1
- data/brut-js/package-lock.json +0 -2210
- data/brut-js/package.json +0 -36
- data/brut-js/specs/AjaxSubmit.spec.js +0 -453
- data/brut-js/specs/Autosubmit.spec.js +0 -127
- data/brut-js/specs/ConfirmSubmit.spec.js +0 -224
- data/brut-js/specs/ConstraintViolationMessage.spec.js +0 -33
- data/brut-js/specs/ConstraintViolationMessages.spec.js +0 -32
- data/brut-js/specs/CopyToClipboard.spec.js +0 -35
- data/brut-js/specs/Form.spec.js +0 -137
- data/brut-js/specs/I18nTranslation.spec.js +0 -19
- data/brut-js/specs/LocaleDetection.spec.js +0 -22
- data/brut-js/specs/Message.spec.js +0 -15
- data/brut-js/specs/SpecHelper.js +0 -23
- data/brut-js/specs/Tabs.spec.js +0 -41
- data/brut-js/specs/Toast.spec.js +0 -34
- data/brut-js/specs/config/asset_metadata.json +0 -7
- data/brut-js/src/AjaxSubmit.js +0 -499
- data/brut-js/src/Autosubmit.js +0 -63
- data/brut-js/src/BaseCustomElement.js +0 -261
- data/brut-js/src/ConfirmSubmit.js +0 -137
- data/brut-js/src/ConfirmationDialog.js +0 -143
- data/brut-js/src/ConstraintViolationMessage.js +0 -140
- data/brut-js/src/ConstraintViolationMessages.js +0 -98
- data/brut-js/src/CopyToClipboard.js +0 -96
- data/brut-js/src/Form.js +0 -147
- data/brut-js/src/I18nTranslation.js +0 -64
- data/brut-js/src/LocaleDetection.js +0 -117
- data/brut-js/src/Logger.js +0 -90
- data/brut-js/src/Message.js +0 -62
- data/brut-js/src/RichString.js +0 -116
- data/brut-js/src/Tabs.js +0 -168
- data/brut-js/src/Toast.js +0 -102
- data/brut-js/src/Tracing.js +0 -247
- data/brut-js/src/appForTestingOnly.js +0 -15
- data/brut-js/src/index.js +0 -133
- data/brut-js/src/testing/AssetMetadata.js +0 -35
- data/brut-js/src/testing/AssetMetadataLoader.js +0 -25
- data/brut-js/src/testing/CustomElementTest.js +0 -235
- data/brut-js/src/testing/DOMCreator.js +0 -45
- data/brut-js/src/testing/index.js +0 -48
- data/brut.gemspec +0 -73
- data/brutrb.com/.vitepress/config.mjs +0 -164
- data/brutrb.com/.vitepress/plugins/jsdocLinker.js +0 -34
- data/brutrb.com/.vitepress/plugins/rdocLinker.js +0 -18
- data/brutrb.com/.vitepress/theme/custom.css +0 -14
- data/brutrb.com/.vitepress/theme/index.js +0 -18
- data/brutrb.com/.vitepress/theme/style.css +0 -139
- data/brutrb.com/adrs.md +0 -16
- data/brutrb.com/ai.md +0 -68
- data/brutrb.com/assets.md +0 -131
- data/brutrb.com/bin/build +0 -5
- data/brutrb.com/bin/deploy +0 -7
- data/brutrb.com/bin/dev +0 -5
- data/brutrb.com/bin/setup +0 -6
- data/brutrb.com/brut-js.md +0 -128
- data/brutrb.com/business-logic.md +0 -55
- data/brutrb.com/cli.md +0 -274
- data/brutrb.com/components.md +0 -265
- data/brutrb.com/configuration.md +0 -256
- data/brutrb.com/css.md +0 -103
- data/brutrb.com/custom-element-tests.md +0 -148
- data/brutrb.com/database-access.md +0 -201
- data/brutrb.com/database-schema.md +0 -320
- data/brutrb.com/deployment.md +0 -158
- data/brutrb.com/dev-environment.md +0 -186
- data/brutrb.com/dir-structure.md +0 -120
- data/brutrb.com/doc-conventions.md +0 -41
- data/brutrb.com/dx +0 -1
- data/brutrb.com/end-to-end-tests.md +0 -176
- data/brutrb.com/features.md +0 -373
- data/brutrb.com/flash-and-session.md +0 -208
- data/brutrb.com/form-constraints.md +0 -266
- data/brutrb.com/forms.md +0 -238
- data/brutrb.com/getting-started.md +0 -142
- data/brutrb.com/handlers.md +0 -177
- data/brutrb.com/hooks.md +0 -176
- data/brutrb.com/i18n.md +0 -190
- data/brutrb.com/images/DevEnvironment.graffle +0 -0
- data/brutrb.com/images/DevEnvironment.png +0 -0
- data/brutrb.com/images/LogoSquare.png +0 -0
- data/brutrb.com/images/LogoStop.png +0 -0
- data/brutrb.com/images/LogoTall.png +0 -0
- data/brutrb.com/images/Makefile +0 -10
- data/brutrb.com/images/OverviewMetro.graffle +0 -0
- data/brutrb.com/images/OverviewMetro.png +0 -0
- data/brutrb.com/images/dev-env-overview.dot +0 -54
- data/brutrb.com/images/dev-env-overview.png +0 -0
- data/brutrb.com/images/dev-env-protocol.dot +0 -37
- data/brutrb.com/images/dev-env-protocol.png +0 -0
- data/brutrb.com/images/overview.graffle +0 -0
- data/brutrb.com/images/overview.png +0 -0
- data/brutrb.com/images/spa.dot +0 -19
- data/brutrb.com/images/spa.png +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-dialog-browser-element-styled.png +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-dialog-browser-element.png +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-dialog-browser.png +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-flow.graffle +0 -0
- data/brutrb.com/images/tutorial/02-confirmation-flow.png +0 -0
- data/brutrb.com/images/tutorial/basic-form-with-violations.png +0 -0
- data/brutrb.com/images/tutorial/basic-form.png +0 -0
- data/brutrb.com/images/tutorial/initial-home-page.png +0 -0
- data/brutrb.com/images/tutorial/new-post-editor.png +0 -0
- data/brutrb.com/images/tutorial/new-post-home-page.png +0 -0
- data/brutrb.com/images/tutorial/styled-form-with-server-side-violations.png +0 -0
- data/brutrb.com/images/tutorial/styled-form-with-violations.png +0 -0
- data/brutrb.com/images/tutorial/styled-home-page-with-posts.png +0 -0
- data/brutrb.com/images/tutorial/styled-home-page.png +0 -0
- data/brutrb.com/images/tutorial/welcome-to-brut.png +0 -0
- data/brutrb.com/images/workspace-protocol.dot +0 -44
- data/brutrb.com/images/workspace-protocol.png +0 -0
- data/brutrb.com/index.md +0 -34
- data/brutrb.com/instrumentation.md +0 -331
- data/brutrb.com/javascript.md +0 -122
- data/brutrb.com/jobs.md +0 -114
- data/brutrb.com/keyword-injection.md +0 -195
- data/brutrb.com/layouts.md +0 -156
- data/brutrb.com/lsp.md +0 -23
- data/brutrb.com/markdown-examples.md +0 -85
- data/brutrb.com/middleware.md +0 -80
- data/brutrb.com/overview.md +0 -68
- data/brutrb.com/package-lock.json +0 -2451
- data/brutrb.com/package.json +0 -11
- data/brutrb.com/pages.md +0 -290
- data/brutrb.com/public/SocialImage.png +0 -0
- data/brutrb.com/public/favicon.ico +0 -0
- data/brutrb.com/recipes/alternate-layouts.md +0 -32
- data/brutrb.com/recipes/authentication.md +0 -336
- data/brutrb.com/recipes/custom-flash.md +0 -51
- data/brutrb.com/recipes/dev-env-secrets.md +0 -87
- data/brutrb.com/recipes/form-errors.md +0 -148
- data/brutrb.com/recipes/indexed-forms.md +0 -149
- data/brutrb.com/recipes/migrations.md +0 -210
- data/brutrb.com/recipes/text-field-component.md +0 -182
- data/brutrb.com/roadmap.md +0 -52
- data/brutrb.com/routes.md +0 -189
- data/brutrb.com/security.md +0 -102
- data/brutrb.com/seed-data.md +0 -63
- data/brutrb.com/space-time-continuum.md +0 -81
- data/brutrb.com/tutorial.md +0 -138
- data/brutrb.com/tutorials/01-intro.md +0 -1654
- data/brutrb.com/tutorials/02-dialog.md +0 -569
- data/brutrb.com/unit-tests.md +0 -148
- data/brutrb.com/why.md +0 -19
- data/docker-compose.dx.yml +0 -25
- data/docs/404.html +0 -26
- data/docs/CNAME +0 -1
- data/docs/SocialImage.png +0 -0
- data/docs/adrs.html +0 -29
- data/docs/ai.html +0 -29
- data/docs/api/Brut/BackEnd/SeedData.html +0 -493
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +0 -214
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +0 -125
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +0 -125
- data/docs/api/Brut/BackEnd/Sidekiq.html +0 -125
- data/docs/api/Brut/BackEnd/Validators/FormValidator.html +0 -414
- data/docs/api/Brut/BackEnd/Validators.html +0 -128
- data/docs/api/Brut/BackEnd.html +0 -132
- data/docs/api/Brut/CLI/App.html +0 -1601
- data/docs/api/Brut/CLI/AppRunner.html +0 -491
- data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +0 -264
- data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +0 -306
- data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +0 -262
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +0 -314
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +0 -183
- data/docs/api/Brut/CLI/Apps/DB/Create.html +0 -365
- data/docs/api/Brut/CLI/Apps/DB/Drop.html +0 -357
- data/docs/api/Brut/CLI/Apps/DB/Migrate.html +0 -389
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +0 -339
- data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +0 -329
- data/docs/api/Brut/CLI/Apps/DB/Seed.html +0 -347
- data/docs/api/Brut/CLI/Apps/DB/Status.html +0 -383
- data/docs/api/Brut/CLI/Apps/DB.html +0 -183
- data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +0 -270
- data/docs/api/Brut/CLI/Apps/DeployBase.html +0 -257
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +0 -587
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +0 -196
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +0 -303
- data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +0 -508
- data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +0 -398
- data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +0 -374
- data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +0 -384
- data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +0 -410
- data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +0 -262
- data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +0 -303
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +0 -480
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +0 -450
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +0 -380
- data/docs/api/Brut/CLI/Apps/Scaffold.html +0 -253
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +0 -474
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +0 -407
- data/docs/api/Brut/CLI/Apps/Test/JS.html +0 -262
- data/docs/api/Brut/CLI/Apps/Test/Run.html +0 -578
- data/docs/api/Brut/CLI/Apps/Test.html +0 -253
- data/docs/api/Brut/CLI/Apps.html +0 -125
- data/docs/api/Brut/CLI/Command.html +0 -2425
- data/docs/api/Brut/CLI/Error.html +0 -139
- data/docs/api/Brut/CLI/ExecutionResults/Result.html +0 -664
- data/docs/api/Brut/CLI/ExecutionResults.html +0 -675
- data/docs/api/Brut/CLI/Executor.html +0 -561
- data/docs/api/Brut/CLI/InvalidOption.html +0 -245
- data/docs/api/Brut/CLI/Options.html +0 -880
- data/docs/api/Brut/CLI/Output.html +0 -699
- data/docs/api/Brut/CLI/SystemExecError.html +0 -451
- data/docs/api/Brut/CLI.html +0 -263
- data/docs/api/Brut/FactoryBot.html +0 -225
- data/docs/api/Brut/Framework/App.html +0 -1097
- data/docs/api/Brut/Framework/Config.html +0 -1071
- data/docs/api/Brut/Framework/Container.html +0 -1464
- data/docs/api/Brut/Framework/Error.html +0 -140
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +0 -232
- data/docs/api/Brut/Framework/Errors/Bug.html +0 -234
- data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +0 -257
- data/docs/api/Brut/Framework/Errors/MissingParameter.html +0 -273
- data/docs/api/Brut/Framework/Errors/NoClassForPath.html +0 -471
- data/docs/api/Brut/Framework/Errors/NotFound.html +0 -308
- data/docs/api/Brut/Framework/Errors/NotImplemented.html +0 -234
- data/docs/api/Brut/Framework/Errors.html +0 -351
- data/docs/api/Brut/Framework/FussyTypeEnforcement.html +0 -392
- data/docs/api/Brut/Framework/MCP.html +0 -871
- data/docs/api/Brut/Framework/ProjectEnvironment.html +0 -648
- data/docs/api/Brut/Framework.html +0 -129
- data/docs/api/Brut/FrontEnd/AssetPathResolver.html +0 -317
- data/docs/api/Brut/FrontEnd/Component/Helpers.html +0 -420
- data/docs/api/Brut/FrontEnd/Component.html +0 -434
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +0 -491
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +0 -526
- data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +0 -313
- data/docs/api/Brut/FrontEnd/Components/Input.html +0 -195
- data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +0 -447
- data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +0 -339
- data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +0 -568
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +0 -419
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +0 -610
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +0 -534
- data/docs/api/Brut/FrontEnd/Components/Inputs.html +0 -125
- data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +0 -367
- data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +0 -355
- data/docs/api/Brut/FrontEnd/Components/TimeTag.html +0 -655
- data/docs/api/Brut/FrontEnd/Components/Traceparent.html +0 -352
- data/docs/api/Brut/FrontEnd/Components.html +0 -156
- data/docs/api/Brut/FrontEnd/CsrfProtector.html +0 -250
- data/docs/api/Brut/FrontEnd/Download.html +0 -467
- data/docs/api/Brut/FrontEnd/Flash.html +0 -1150
- data/docs/api/Brut/FrontEnd/Form.html +0 -1227
- data/docs/api/Brut/FrontEnd/Forms/Button.html +0 -331
- data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +0 -537
- data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +0 -590
- data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +0 -201
- data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +0 -535
- data/docs/api/Brut/FrontEnd/Forms/Input.html +0 -1567
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +0 -635
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +0 -1336
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +0 -730
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +0 -587
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +0 -734
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +0 -582
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +0 -659
- data/docs/api/Brut/FrontEnd/Forms.html +0 -127
- data/docs/api/Brut/FrontEnd/GenericResponse.html +0 -377
- data/docs/api/Brut/FrontEnd/Handler.html +0 -442
- data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +0 -318
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +0 -336
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +0 -399
- data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +0 -354
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +0 -151
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +0 -315
- data/docs/api/Brut/FrontEnd/Handlers.html +0 -125
- data/docs/api/Brut/FrontEnd/HandlingResults.html +0 -339
- data/docs/api/Brut/FrontEnd/HttpMethod.html +0 -661
- data/docs/api/Brut/FrontEnd/HttpStatus.html +0 -496
- data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +0 -284
- data/docs/api/Brut/FrontEnd/Layout.html +0 -486
- data/docs/api/Brut/FrontEnd/Middleware.html +0 -135
- data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +0 -288
- data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +0 -292
- data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +0 -324
- data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +0 -376
- data/docs/api/Brut/FrontEnd/Middlewares.html +0 -125
- data/docs/api/Brut/FrontEnd/Page.html +0 -781
- data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +0 -797
- data/docs/api/Brut/FrontEnd/Pages.html +0 -125
- data/docs/api/Brut/FrontEnd/RequestContext.html +0 -1312
- data/docs/api/Brut/FrontEnd/RouteHook.html +0 -424
- data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +0 -242
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +0 -249
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +0 -264
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +0 -261
- data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +0 -284
- data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +0 -252
- data/docs/api/Brut/FrontEnd/RouteHooks.html +0 -115
- data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +0 -227
- data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +0 -305
- data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +0 -324
- data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +0 -319
- data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +0 -315
- data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +0 -315
- data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +0 -327
- data/docs/api/Brut/FrontEnd/Routing/Route.html +0 -761
- data/docs/api/Brut/FrontEnd/Routing.html +0 -927
- data/docs/api/Brut/FrontEnd/Session.html +0 -1195
- data/docs/api/Brut/FrontEnd.html +0 -134
- data/docs/api/Brut/I18n/BaseMethods.html +0 -931
- data/docs/api/Brut/I18n/ForBackEnd.html +0 -302
- data/docs/api/Brut/I18n/ForCLI.html +0 -302
- data/docs/api/Brut/I18n/ForHTML.html +0 -296
- data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +0 -316
- data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +0 -930
- data/docs/api/Brut/I18n.html +0 -127
- data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +0 -435
- data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +0 -596
- data/docs/api/Brut/Instrumentation/Methods.html +0 -173
- data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +0 -286
- data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +0 -302
- data/docs/api/Brut/Instrumentation/OpenTelemetry.html +0 -866
- data/docs/api/Brut/Instrumentation.html +0 -128
- data/docs/api/Brut/RubocopConfig.html +0 -237
- data/docs/api/Brut/SinatraHelpers/ClassMethods.html +0 -534
- data/docs/api/Brut/SinatraHelpers.html +0 -281
- data/docs/api/Brut/SpecSupport/ClockSupport.html +0 -383
- data/docs/api/Brut/SpecSupport/ComponentSupport.html +0 -496
- data/docs/api/Brut/SpecSupport/E2ETestServer.html +0 -503
- data/docs/api/Brut/SpecSupport/E2eSupport.html +0 -142
- data/docs/api/Brut/SpecSupport/EnhancedNode.html +0 -403
- data/docs/api/Brut/SpecSupport/FlashSupport.html +0 -278
- data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +0 -401
- data/docs/api/Brut/SpecSupport/GeneralSupport.html +0 -195
- data/docs/api/Brut/SpecSupport/HandlerSupport.html +0 -160
- data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +0 -142
- data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +0 -142
- data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +0 -155
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +0 -583
- data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +0 -149
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +0 -466
- data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +0 -149
- data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +0 -149
- data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +0 -165
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +0 -158
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +0 -156
- data/docs/api/Brut/SpecSupport/Matchers.html +0 -125
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +0 -335
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +0 -637
- data/docs/api/Brut/SpecSupport/SessionSupport.html +0 -196
- data/docs/api/Brut/SpecSupport.html +0 -129
- data/docs/api/Brut/TUI/AnsiEscapeCode/Mod.html +0 -409
- data/docs/api/Brut/TUI/AnsiEscapeCode.html +0 -426
- data/docs/api/Brut/TUI/EventLoop/Deque.html +0 -531
- data/docs/api/Brut/TUI/EventLoop.html +0 -676
- data/docs/api/Brut/TUI/Events/BaseEvent.html +0 -449
- data/docs/api/Brut/TUI/Events/EventBus.html +0 -485
- data/docs/api/Brut/TUI/Events/EventLoopStarted.html +0 -211
- data/docs/api/Brut/TUI/Events/Exception.html +0 -523
- data/docs/api/Brut/TUI/Events/Tick.html +0 -294
- data/docs/api/Brut/TUI/Events.html +0 -131
- data/docs/api/Brut/TUI/MarkupString.html +0 -537
- data/docs/api/Brut/TUI/Script/BlockStep.html +0 -300
- data/docs/api/Brut/TUI/Script/Events/CommandExecutionFailed.html +0 -252
- data/docs/api/Brut/TUI/Script/Events/CommandExecutionSucceeded.html +0 -163
- data/docs/api/Brut/TUI/Script/Events/CommandStdErr.html +0 -163
- data/docs/api/Brut/TUI/Script/Events/CommandStdOut.html +0 -300
- data/docs/api/Brut/TUI/Script/Events/ExecutingCommand.html +0 -298
- data/docs/api/Brut/TUI/Script/Events/Message.html +0 -345
- data/docs/api/Brut/TUI/Script/Events/PhaseCompleted.html +0 -229
- data/docs/api/Brut/TUI/Script/Events/PhaseStarted.html +0 -350
- data/docs/api/Brut/TUI/Script/Events/ScriptCompleted.html +0 -282
- data/docs/api/Brut/TUI/Script/Events/ScriptStarted.html +0 -343
- data/docs/api/Brut/TUI/Script/Events/StepCompleted.html +0 -163
- data/docs/api/Brut/TUI/Script/Events/StepStarted.html +0 -346
- data/docs/api/Brut/TUI/Script/Events.html +0 -115
- data/docs/api/Brut/TUI/Script/ExecStep/ProcessStatusFailed.html +0 -210
- data/docs/api/Brut/TUI/Script/ExecStep.html +0 -493
- data/docs/api/Brut/TUI/Script/LoggingSubscriber.html +0 -914
- data/docs/api/Brut/TUI/Script/PutsSubscriber.html +0 -783
- data/docs/api/Brut/TUI/Script/Step.html +0 -313
- data/docs/api/Brut/TUI/Script.html +0 -1250
- data/docs/api/Brut/TUI/Terminal.html +0 -593
- data/docs/api/Brut/TUI/TerminalTheme.html +0 -1403
- data/docs/api/Brut/TUI/Themes/Dark.html +0 -706
- data/docs/api/Brut/TUI/Themes/Light.html +0 -804
- data/docs/api/Brut/TUI/Themes/None.html +0 -218
- data/docs/api/Brut/TUI/Themes.html +0 -115
- data/docs/api/Brut/TUI.html +0 -129
- data/docs/api/Brut.html +0 -341
- data/docs/api/Clock.html +0 -603
- data/docs/api/ModuleName.html +0 -595
- data/docs/api/RichString.html +0 -775
- data/docs/api/SemanticLogger/Appender/Async.html +0 -219
- data/docs/api/Sequel/Extensions/BrutInstrumentation.html +0 -119
- data/docs/api/Sequel/Extensions/BrutMigrations.html +0 -541
- data/docs/api/Sequel/Extensions.html +0 -117
- data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +0 -105
- data/docs/api/Sequel/Plugins/CreatedAt.html +0 -125
- data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +0 -207
- data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +0 -186
- data/docs/api/Sequel/Plugins/ExternalId.html +0 -218
- data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +0 -202
- data/docs/api/Sequel/Plugins/FindBang.html +0 -125
- data/docs/api/Sequel/Plugins.html +0 -117
- data/docs/api/Sequel.html +0 -117
- data/docs/api/SpecSupport/Matchers/BeABug.html +0 -143
- data/docs/api/_index.html +0 -1964
- data/docs/api/class_list.html +0 -54
- data/docs/api/css/common.css +0 -1
- data/docs/api/css/full_list.css +0 -59
- data/docs/api/css/style.css +0 -504
- data/docs/api/file.README.html +0 -172
- data/docs/api/file_list.html +0 -59
- data/docs/api/frames.html +0 -22
- data/docs/api/index.html +0 -172
- data/docs/api/js/app.js +0 -344
- data/docs/api/js/full_list.js +0 -242
- data/docs/api/js/jquery.js +0 -4
- data/docs/api/method_list.html +0 -5542
- data/docs/api/top-level-namespace.html +0 -112
- data/docs/assets/02-confirmation-dialog-browser-element-styled.3NEGM20-.png +0 -0
- data/docs/assets/02-confirmation-dialog-browser-element.DPsf0xUW.png +0 -0
- data/docs/assets/02-confirmation-dialog-browser.DH8ALFO4.png +0 -0
- data/docs/assets/02-confirmation-flow.D9gZ0S5U.png +0 -0
- data/docs/assets/DevEnvironment.DaFcVfwP.png +0 -0
- data/docs/assets/LogoStop.Gb3tDhL1.png +0 -0
- data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
- data/docs/assets/adrs.md.YglbWtQe.js +0 -1
- data/docs/assets/adrs.md.YglbWtQe.lean.js +0 -1
- data/docs/assets/ai.md.ChLnvDAX.js +0 -1
- data/docs/assets/ai.md.ChLnvDAX.lean.js +0 -1
- data/docs/assets/app.B8jAEB7R.js +0 -1
- data/docs/assets/assets.md.BEF6Oz6K.js +0 -19
- data/docs/assets/assets.md.BEF6Oz6K.lean.js +0 -1
- data/docs/assets/basic-form-with-violations.Cv6Y9-Q_.png +0 -0
- data/docs/assets/basic-form.DbHnu0oW.png +0 -0
- data/docs/assets/brut-js.md.BMz0X1Rz.js +0 -12
- data/docs/assets/brut-js.md.BMz0X1Rz.lean.js +0 -1
- data/docs/assets/business-logic.md.DbuaOYGU.js +0 -1
- data/docs/assets/business-logic.md.DbuaOYGU.lean.js +0 -1
- data/docs/assets/chunks/@localSearchIndexroot.DJ8mocCj.js +0 -1
- data/docs/assets/chunks/VPLocalSearchBox.gF-Po_fz.js +0 -8
- data/docs/assets/chunks/framework.C4nOkCZI.js +0 -18
- data/docs/assets/chunks/theme.BjPAOJkz.js +0 -2
- data/docs/assets/cli.md.DDMar_51.js +0 -122
- data/docs/assets/cli.md.DDMar_51.lean.js +0 -1
- data/docs/assets/components.md.Ber8UBM0.js +0 -96
- data/docs/assets/components.md.Ber8UBM0.lean.js +0 -1
- data/docs/assets/configuration.md.DrJ6YVoZ.js +0 -78
- data/docs/assets/configuration.md.DrJ6YVoZ.lean.js +0 -1
- data/docs/assets/css.md.K5rOCOQY.js +0 -21
- data/docs/assets/css.md.K5rOCOQY.lean.js +0 -1
- data/docs/assets/custom-element-tests.md.DiLe-eFw.js +0 -69
- data/docs/assets/custom-element-tests.md.DiLe-eFw.lean.js +0 -1
- data/docs/assets/database-access.md.Dc8l2Plf.js +0 -63
- data/docs/assets/database-access.md.Dc8l2Plf.lean.js +0 -1
- data/docs/assets/database-schema.md.BJ_JhXmO.js +0 -70
- data/docs/assets/database-schema.md.BJ_JhXmO.lean.js +0 -1
- data/docs/assets/deployment.md.CHTx2eTR.js +0 -55
- data/docs/assets/deployment.md.CHTx2eTR.lean.js +0 -1
- data/docs/assets/dev-env-protocol.DysDAtnz.png +0 -0
- data/docs/assets/dev-environment.md.B1S9p5ZK.js +0 -16
- data/docs/assets/dev-environment.md.B1S9p5ZK.lean.js +0 -1
- data/docs/assets/dir-structure.md.D1T2kGwj.js +0 -46
- data/docs/assets/dir-structure.md.D1T2kGwj.lean.js +0 -1
- data/docs/assets/doc-conventions.md.CDnWaEFg.js +0 -1
- data/docs/assets/doc-conventions.md.CDnWaEFg.lean.js +0 -1
- data/docs/assets/end-to-end-tests.md.BJJdNDYL.js +0 -28
- data/docs/assets/end-to-end-tests.md.BJJdNDYL.lean.js +0 -1
- data/docs/assets/features.md.BDWxnyNO.js +0 -154
- data/docs/assets/features.md.BDWxnyNO.lean.js +0 -1
- data/docs/assets/flash-and-session.md.CUsMxoNl.js +0 -79
- data/docs/assets/flash-and-session.md.CUsMxoNl.lean.js +0 -1
- data/docs/assets/form-constraints.md.KlfXSKm2.js +0 -90
- data/docs/assets/form-constraints.md.KlfXSKm2.lean.js +0 -1
- data/docs/assets/forms.md.RK0zkhm0.js +0 -64
- data/docs/assets/forms.md.RK0zkhm0.lean.js +0 -1
- data/docs/assets/getting-started.md.CGJ44juQ.js +0 -31
- data/docs/assets/getting-started.md.CGJ44juQ.lean.js +0 -1
- data/docs/assets/handlers.md.C5tUwmmo.js +0 -54
- data/docs/assets/handlers.md.C5tUwmmo.lean.js +0 -1
- data/docs/assets/hooks.md.CoiYCKRc.js +0 -80
- data/docs/assets/hooks.md.CoiYCKRc.lean.js +0 -1
- data/docs/assets/i18n.md.DxkCKhUw.js +0 -23
- data/docs/assets/i18n.md.DxkCKhUw.lean.js +0 -1
- data/docs/assets/index.md.DnphWyQd.js +0 -1
- data/docs/assets/index.md.DnphWyQd.lean.js +0 -1
- data/docs/assets/initial-home-page.DNIaYmgP.png +0 -0
- data/docs/assets/instrumentation.md.BcxjC4jd.js +0 -90
- data/docs/assets/instrumentation.md.BcxjC4jd.lean.js +0 -1
- data/docs/assets/javascript.md.D6fxhaQb.js +0 -31
- data/docs/assets/javascript.md.D6fxhaQb.lean.js +0 -1
- data/docs/assets/jobs.md.Bi3qb3v6.js +0 -25
- data/docs/assets/jobs.md.Bi3qb3v6.lean.js +0 -1
- data/docs/assets/keyword-injection.md.CqLnnzIz.js +0 -21
- data/docs/assets/keyword-injection.md.CqLnnzIz.lean.js +0 -1
- data/docs/assets/layouts.md.HEbeK7Jr.js +0 -68
- data/docs/assets/layouts.md.HEbeK7Jr.lean.js +0 -1
- data/docs/assets/lsp.md.bE9dW8n9.js +0 -1
- data/docs/assets/lsp.md.bE9dW8n9.lean.js +0 -1
- data/docs/assets/markdown-examples.md.BPmtHlc-.js +0 -33
- data/docs/assets/markdown-examples.md.BPmtHlc-.lean.js +0 -1
- data/docs/assets/middleware.md.BhOIsg59.js +0 -20
- data/docs/assets/middleware.md.BhOIsg59.lean.js +0 -1
- data/docs/assets/new-post-editor.DrHr-5oh.png +0 -0
- data/docs/assets/new-post-home-page.Bm34lyMg.png +0 -0
- data/docs/assets/overview.md.BpWAgPFH.js +0 -1
- data/docs/assets/overview.md.BpWAgPFH.lean.js +0 -1
- data/docs/assets/pages.md.B3sQXpEd.js +0 -45
- data/docs/assets/pages.md.B3sQXpEd.lean.js +0 -1
- data/docs/assets/recipes_alternate-layouts.md.C1QzVkA7.js +0 -22
- data/docs/assets/recipes_alternate-layouts.md.C1QzVkA7.lean.js +0 -1
- data/docs/assets/recipes_authentication.md.CyvoIW82.js +0 -157
- data/docs/assets/recipes_authentication.md.CyvoIW82.lean.js +0 -1
- data/docs/assets/recipes_custom-flash.md.6gFqf2uL.js +0 -26
- data/docs/assets/recipes_custom-flash.md.6gFqf2uL.lean.js +0 -1
- data/docs/assets/recipes_dev-env-secrets.md.DC_jVY9U.js +0 -12
- data/docs/assets/recipes_dev-env-secrets.md.DC_jVY9U.lean.js +0 -1
- data/docs/assets/recipes_form-errors.md.B5ptSzMO.js +0 -66
- data/docs/assets/recipes_form-errors.md.B5ptSzMO.lean.js +0 -1
- data/docs/assets/recipes_indexed-forms.md.BYYQGW2C.js +0 -74
- data/docs/assets/recipes_indexed-forms.md.BYYQGW2C.lean.js +0 -1
- data/docs/assets/recipes_migrations.md.Cid7-3cu.js +0 -97
- data/docs/assets/recipes_migrations.md.Cid7-3cu.lean.js +0 -1
- data/docs/assets/recipes_text-field-component.md.VhOsCtKI.js +0 -101
- data/docs/assets/recipes_text-field-component.md.VhOsCtKI.lean.js +0 -1
- data/docs/assets/roadmap.md.DqC1Y7Zt.js +0 -1
- data/docs/assets/roadmap.md.DqC1Y7Zt.lean.js +0 -1
- data/docs/assets/routes.md.C1dgIBtD.js +0 -21
- data/docs/assets/routes.md.C1dgIBtD.lean.js +0 -1
- data/docs/assets/security.md.Jn4SY1uK.js +0 -1
- data/docs/assets/security.md.Jn4SY1uK.lean.js +0 -1
- data/docs/assets/seed-data.md.UZW0WxYN.js +0 -14
- data/docs/assets/seed-data.md.UZW0WxYN.lean.js +0 -1
- data/docs/assets/spa.qejUdp-5.png +0 -0
- data/docs/assets/space-time-continuum.md.D9rYGDFH.js +0 -1
- data/docs/assets/space-time-continuum.md.D9rYGDFH.lean.js +0 -1
- data/docs/assets/style.B1z60PPQ.css +0 -1
- data/docs/assets/styled-form-with-server-side-violations.Bjxd8Dpv.png +0 -0
- data/docs/assets/styled-form-with-violations.Bv_sa9tg.png +0 -0
- data/docs/assets/styled-home-page-with-posts.Dd4kG89D.png +0 -0
- data/docs/assets/styled-home-page.BzdI7dWz.png +0 -0
- data/docs/assets/tutorial.md.BX6f6l00.js +0 -27
- data/docs/assets/tutorial.md.BX6f6l00.lean.js +0 -1
- data/docs/assets/tutorials_01-intro.md.CzZ3kpF_.js +0 -708
- data/docs/assets/tutorials_01-intro.md.CzZ3kpF_.lean.js +0 -1
- data/docs/assets/tutorials_02-dialog.md.DE5WfCXI.js +0 -274
- data/docs/assets/tutorials_02-dialog.md.DE5WfCXI.lean.js +0 -1
- data/docs/assets/unit-tests.md.vDsdBbO_.js +0 -13
- data/docs/assets/unit-tests.md.vDsdBbO_.lean.js +0 -1
- data/docs/assets/welcome-to-brut.VSWzl17-.png +0 -0
- data/docs/assets/why.md.4WpxdrQ2.js +0 -1
- data/docs/assets/why.md.4WpxdrQ2.lean.js +0 -1
- data/docs/assets/workspace-protocol.C0gXsoDb.png +0 -0
- data/docs/assets.html +0 -47
- data/docs/brut-css/brut.css +0 -1
- data/docs/brut-css/brut.max.css +0 -22372
- data/docs/brut-css/classes/appearances.html +0 -783
- data/docs/brut-css/classes/background-colors.html +0 -3529
- data/docs/brut-css/classes/border-colors.html +0 -3529
- data/docs/brut-css/classes/borders.html +0 -2293
- data/docs/brut-css/classes/dimensions.html +0 -2581
- data/docs/brut-css/classes/flex.html +0 -917
- data/docs/brut-css/classes/foreground-colors.html +0 -3261
- data/docs/brut-css/classes/junk-drawer.html +0 -431
- data/docs/brut-css/classes/layout.html +0 -668
- data/docs/brut-css/classes/lists.html +0 -331
- data/docs/brut-css/classes/positioning.html +0 -1751
- data/docs/brut-css/classes/spacings.html +0 -2633
- data/docs/brut-css/classes/typography.html +0 -2206
- data/docs/brut-css/customization/advanced-configuration.html +0 -204
- data/docs/brut-css/customization/breakpoints.html +0 -227
- data/docs/brut-css/customization/design-system.html +0 -197
- data/docs/brut-css/customization/pseudo-classes.html +0 -228
- data/docs/brut-css/docs.css +0 -98
- data/docs/brut-css/getting-started/core-concepts.html +0 -234
- data/docs/brut-css/getting-started/installation.html +0 -190
- data/docs/brut-css/getting-started/overview.html +0 -210
- data/docs/brut-css/getting-started/simple-example.html +0 -285
- data/docs/brut-css/index.html +0 -193
- data/docs/brut-css/prism-twilight.min.css +0 -1
- data/docs/brut-css/properties/colors.html +0 -1548
- data/docs/brut-css/properties/spacings.html +0 -614
- data/docs/brut-css/properties/typography.html +0 -777
- data/docs/brut-js/api/AjaxSubmit.html +0 -452
- data/docs/brut-js/api/AjaxSubmit.js.html +0 -550
- data/docs/brut-js/api/Autosubmit.html +0 -192
- data/docs/brut-js/api/Autosubmit.js.html +0 -114
- data/docs/brut-js/api/BaseCustomElement.html +0 -1091
- data/docs/brut-js/api/BaseCustomElement.js.html +0 -312
- data/docs/brut-js/api/BrutCustomElements.html +0 -172
- data/docs/brut-js/api/BufferedLogger.html +0 -173
- data/docs/brut-js/api/ConfirmSubmit.html +0 -286
- data/docs/brut-js/api/ConfirmSubmit.js.html +0 -188
- data/docs/brut-js/api/ConfirmationDialog.html +0 -425
- data/docs/brut-js/api/ConfirmationDialog.js.html +0 -194
- data/docs/brut-js/api/ConstraintViolationMessage.html +0 -498
- data/docs/brut-js/api/ConstraintViolationMessage.js.html +0 -191
- data/docs/brut-js/api/ConstraintViolationMessages.html +0 -590
- data/docs/brut-js/api/ConstraintViolationMessages.js.html +0 -149
- data/docs/brut-js/api/CopyToClipboard.html +0 -345
- data/docs/brut-js/api/CopyToClipboard.js.html +0 -147
- data/docs/brut-js/api/Form.html +0 -291
- data/docs/brut-js/api/Form.js.html +0 -198
- data/docs/brut-js/api/I18nTranslation.html +0 -409
- data/docs/brut-js/api/I18nTranslation.js.html +0 -115
- data/docs/brut-js/api/LocaleDetection.html +0 -312
- data/docs/brut-js/api/LocaleDetection.js.html +0 -168
- data/docs/brut-js/api/Logger.html +0 -702
- data/docs/brut-js/api/Logger.js.html +0 -141
- data/docs/brut-js/api/Message.html +0 -238
- data/docs/brut-js/api/Message.js.html +0 -113
- data/docs/brut-js/api/PrefixedLogger.html +0 -369
- data/docs/brut-js/api/RichString.html +0 -1049
- data/docs/brut-js/api/RichString.js.html +0 -167
- data/docs/brut-js/api/Tabs.html +0 -295
- data/docs/brut-js/api/Tabs.js.html +0 -219
- data/docs/brut-js/api/Toast.html +0 -270
- data/docs/brut-js/api/Toast.js.html +0 -153
- data/docs/brut-js/api/Tracing.html +0 -277
- data/docs/brut-js/api/Tracing.js.html +0 -298
- data/docs/brut-js/api/external-CustomElementRegistry.html +0 -140
- data/docs/brut-js/api/external-Performance.html +0 -138
- data/docs/brut-js/api/external-Promise.html +0 -138
- data/docs/brut-js/api/external-ValidityState.html +0 -138
- data/docs/brut-js/api/external-Window.html +0 -233
- data/docs/brut-js/api/external-fetch.html +0 -138
- data/docs/brut-js/api/global.html +0 -400
- data/docs/brut-js/api/index.html +0 -168
- data/docs/brut-js/api/index.js.html +0 -184
- data/docs/brut-js/api/module-testing.html +0 -383
- data/docs/brut-js/api/scripts/linenumber.js +0 -25
- data/docs/brut-js/api/scripts/prettify/Apache-License-2.0.txt +0 -202
- data/docs/brut-js/api/scripts/prettify/lang-css.js +0 -2
- data/docs/brut-js/api/scripts/prettify/prettify.js +0 -28
- data/docs/brut-js/api/styles/jsdoc-default.css +0 -327
- data/docs/brut-js/api/styles/prettify-jsdoc.css +0 -111
- data/docs/brut-js/api/styles/prettify-tomorrow.css +0 -132
- data/docs/brut-js/api/testing.AssetMetadata.html +0 -172
- data/docs/brut-js/api/testing.AssetMetadataLoader.html +0 -171
- data/docs/brut-js/api/testing.CustomElementTest.html +0 -679
- data/docs/brut-js/api/testing.DOMCreator.html +0 -171
- data/docs/brut-js/api/testing_AssetMetadata.js.html +0 -86
- data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +0 -76
- data/docs/brut-js/api/testing_CustomElementTest.js.html +0 -286
- data/docs/brut-js/api/testing_DOMCreator.js.html +0 -96
- data/docs/brut-js/api/testing_index.js.html +0 -99
- data/docs/brut-js.html +0 -40
- data/docs/business-logic.html +0 -29
- data/docs/cli.html +0 -150
- data/docs/components.html +0 -124
- data/docs/configuration.html +0 -106
- data/docs/css.html +0 -49
- data/docs/custom-element-tests.html +0 -97
- data/docs/database-access.html +0 -91
- data/docs/database-schema.html +0 -98
- data/docs/deployment.html +0 -83
- data/docs/dev-environment.html +0 -44
- data/docs/dir-structure.html +0 -74
- data/docs/doc-conventions.html +0 -29
- data/docs/end-to-end-tests.html +0 -56
- data/docs/favicon.ico +0 -0
- data/docs/features.html +0 -182
- data/docs/flash-and-session.html +0 -107
- data/docs/form-constraints.html +0 -118
- data/docs/forms.html +0 -92
- data/docs/getting-started.html +0 -59
- data/docs/handlers.html +0 -82
- data/docs/hashmap.json +0 -1
- data/docs/hooks.html +0 -108
- data/docs/i18n.html +0 -51
- data/docs/index.html +0 -29
- data/docs/instrumentation.html +0 -118
- data/docs/javascript.html +0 -59
- data/docs/jobs.html +0 -53
- data/docs/keyword-injection.html +0 -49
- data/docs/layouts.html +0 -96
- data/docs/lsp.html +0 -29
- data/docs/markdown-examples.html +0 -61
- data/docs/middleware.html +0 -48
- data/docs/overview.html +0 -29
- data/docs/pages.html +0 -73
- data/docs/recipes/alternate-layouts.html +0 -50
- data/docs/recipes/authentication.html +0 -185
- data/docs/recipes/custom-flash.html +0 -54
- data/docs/recipes/dev-env-secrets.html +0 -40
- data/docs/recipes/form-errors.html +0 -94
- data/docs/recipes/indexed-forms.html +0 -102
- data/docs/recipes/migrations.html +0 -125
- data/docs/recipes/text-field-component.html +0 -129
- data/docs/roadmap.html +0 -29
- data/docs/routes.html +0 -49
- data/docs/security.html +0 -29
- data/docs/seed-data.html +0 -42
- data/docs/space-time-continuum.html +0 -29
- data/docs/tutorial.html +0 -55
- data/docs/tutorials/01-intro.html +0 -736
- data/docs/tutorials/02-dialog.html +0 -302
- data/docs/unit-tests.html +0 -41
- data/docs/vp-icons.css +0 -1
- data/docs/why.html +0 -29
- data/docs-todo.md +0 -32
- data/dx/bash_customizations +0 -6
- data/dx/build +0 -73
- data/dx/build.pre +0 -15
- data/dx/docker-compose.env +0 -22
- data/dx/dx.sh.lib +0 -24
- data/dx/exec +0 -75
- data/dx/setupkit.sh.lib +0 -144
- data/dx/show-help-in-app-container-then-wait.sh +0 -38
- data/lib/brut/cli/app.rb +0 -238
- data/lib/brut/cli/app_runner.rb +0 -252
- data/lib/brut/cli/command.rb +0 -258
- data/lib/brut/cli/execution_results.rb +0 -119
- data/lib/brut/front_end/layouts/_internal.html.erb +0 -68
- data/lib/brut/front_end/pages/_missing_page.html.erb +0 -17
- data/mkbrut/.gitignore +0 -16
- data/mkbrut/CODE_OF_CONDUCT.txt +0 -100
- data/mkbrut/Gemfile +0 -3
- data/mkbrut/Gemfile.lock +0 -20
- data/mkbrut/LICENSE.txt +0 -370
- data/mkbrut/README.md +0 -145
- data/mkbrut/Rakefile +0 -2
- data/mkbrut/bin/build +0 -36
- data/mkbrut/bin/ci +0 -19
- data/mkbrut/bin/docs +0 -19
- data/mkbrut/bin/publish +0 -129
- data/mkbrut/bin/rake +0 -16
- data/mkbrut/bin/setup +0 -30
- data/mkbrut/brut-welcome.png +0 -0
- data/mkbrut/deploy/.dockerignore +0 -2
- data/mkbrut/deploy/Dockerfile +0 -25
- data/mkbrut/dx +0 -1
- data/mkbrut/exe/mkbrut +0 -5
- data/mkbrut/lib/mkbrut/app_name.rb +0 -29
- data/mkbrut/lib/mkbrut/app_options.rb +0 -36
- data/mkbrut/lib/mkbrut/cli.rb +0 -189
- data/mkbrut/lib/mkbrut/erb_binding_delegate.rb +0 -20
- data/mkbrut/lib/mkbrut/ops.rb +0 -17
- data/mkbrut/lib/mkbrut/organization.rb +0 -5
- data/mkbrut/lib/mkbrut/segments.rb +0 -8
- data/mkbrut/lib/mkbrut/version.rb +0 -3
- data/mkbrut/lib/mkbrut.rb +0 -20
- data/mkbrut/mkbrut.gemspec +0 -34
- data/mkbrut/templates/Base/app/src/front_end/images/LogoPylon.png +0 -0
- data/mkbrut/templates/Base/bin/build-assets +0 -7
- data/mkbrut/templates/Base/bin/ci +0 -39
- data/mkbrut/templates/Base/bin/db +0 -9
- data/mkbrut/templates/Base/bin/scaffold +0 -9
- data/mkbrut/templates/Base/bin/setup +0 -287
- data/mkbrut/templates/Base/bin/test +0 -9
- data/mkbrut/templates/Base/bin/test-server +0 -29
- data/mkbrut/templates/Base/dx/prune +0 -19
- data/mkbrut/templates/Base/dx/start +0 -30
- data/mkbrut/templates/Base/dx/stop +0 -23
- data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +0 -27
- data/specs/brut/front_end/forms/input.spec.rb +0 -978
- data/specs/brut/front_end/forms/radio_button_group_input.spec.rb +0 -54
- data/specs/brut/front_end/forms/select_input.spec.rb +0 -54
- data/specs/brut/instrumentation/methods.spec.rb +0 -399
- data/specs/brut/junk_drawer.spec.rb +0 -79
- data/specs/brut/tui/ansi_escape_code.spec.rb +0 -30
- data/specs/brut/tui/event_loop.spec.rb +0 -70
- data/specs/brut/tui/events/base_event.spec.rb +0 -26
- data/specs/brut/tui/events/event_bus.spec.rb +0 -141
- data/specs/brut/tui/events/exception.spec.rb +0 -19
- data/specs/brut/tui/events/test_event.rb +0 -5
- data/specs/spec_helper.rb +0 -31
- data/specs/support/matchers/have_constraint_violation.rb +0 -23
- data/specs/support/matchers.rb +0 -5
- data/specs/support.rb +0 -3
- /data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/prefixed_io.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/.dockerignore +0 -0
- /data/{mkbrut/templates → templates}/Base/.env.development.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/.env.test.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/.gitignore +0 -0
- /data/{mkbrut/templates → templates}/Base/.projections.json +0 -0
- /data/{mkbrut/templates → templates}/Base/Dockerfile.dx +0 -0
- /data/{mkbrut/templates → templates}/Base/Gemfile.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/Procfile.development +0 -0
- /data/{mkbrut/templates → templates}/Base/Procfile.test +0 -0
- /data/{mkbrut/templates → templates}/Base/README.md +0 -0
- /data/{mkbrut/templates → templates}/Base/README.md.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/bootstrap.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/config/i18n/en/1_defaults.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/config/i18n/en/2_app.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/public/static/manifest.json.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/app.rb.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/app_data_model.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/db.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/migrations/20240101130000_citext.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/seed/seed_data.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/components/app_component.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/components/custom_element_registration.rb.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/css/index.css +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/css/svgs.css +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/forms/app_form.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/handlers/app_handler.rb +0 -0
- /data/{brutrb.com → templates/Base/app/src/front_end}/images/LogoPylon.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/LogoTransit.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-120x120.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-152x152.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-167x167.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-180x180.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/favicon.ico +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/icon.png +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/mkicons.sh +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/js/index.js +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/layouts/blank_layout.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/layouts/default_layout.rb.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/pages/app_page.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/pages/home_page.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/support/app_session.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/svgs/README.md +0 -0
- /data/{mkbrut/templates → templates}/Base/app/src/front_end/svgs/comment-button.svg +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/README.md.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/console +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/dbconsole +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/dev +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/run +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/run.run +0 -0
- /data/{mkbrut/templates → templates}/Base/bin/startup-message +0 -0
- /data/{mkbrut/templates → templates}/Base/config.ru +0 -0
- /data/{mkbrut/templates → templates}/Base/docker-compose.dx.yml +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/README.md +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/bash_customizations +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/bash_customizations.local +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/build +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/dx.sh.lib +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/exec +0 -0
- /data/{dx → templates/Base/dx}/prune +0 -0
- /data/{mkbrut/templates → templates}/Base/dx/show-help-in-app-container-then-wait.sh +0 -0
- /data/{dx → templates/Base/dx}/start +0 -0
- /data/{dx → templates/Base/dx}/stop +0 -0
- /data/{mkbrut/templates → templates}/Base/package.json.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/puma.config.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/e2e/home_page.spec.rb.erb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/front_end/js/SpecHelper.js +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/front_end/pages/home_page.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/lint_factories.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/spec_helper.rb +0 -0
- /data/{mkbrut/templates → templates}/Base/specs/support.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/BareBones/app/src/front_end/handlers/trigger_exception_handler.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/BareBones/app/src/front_end/js/Example.js.erb +0 -0
- /data/{mkbrut/templates → templates}/segments/BareBones/specs/front_end/handlers/trigger_exception_handler.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/BareBones/specs/front_end/js/Example.spec.js.erb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/back_end/data_models/db/guestbook_message.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/back_end/data_models/migrations/20250628194124_guestbook.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/components/flash_component.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/css/constraint-violations.css +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/fonts/monaspace-xenon.ttf +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/forms/guestbook_message_form.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/handlers/guestbook_message_handler.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/guestbook_page/message_component.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/guestbook_page.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/new_guestbook_message_page.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/back_end/data_models/db/guestbook_message.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/e2e/guest_message.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/factories/db/guestbook_message.factory.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/components/flash_component.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/handlers/guestbook_message_handler.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/guestbook_page/message_component.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/guestbook_page.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/new_guestbook_message_page.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Heroku/bin/deploy +0 -0
- /data/{mkbrut/templates → templates}/segments/Heroku/deploy/docker-entrypoint +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/boot_sidekiq.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/config/sidekiq.yml +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/jobs/app_job.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/jobs/example_job.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/segments/sidekiq_segment.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/bin/run.sidekiq +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/specs/back_end/jobs/example_job.spec.rb +0 -0
- /data/{mkbrut/templates → templates}/segments/Sidekiq/specs/integration/sidekiq_works.spec.rb +0 -0
data/docs/database-schema.html
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en-US" dir="ltr">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
-
<title>Database Schema / Migrations | Brut RB</title>
|
|
7
|
-
<meta name="description" content="Documentation for the Brut.RB web framework.">
|
|
8
|
-
<meta name="generator" content="VitePress v1.6.4">
|
|
9
|
-
<link rel="preload stylesheet" href="/assets/style.B1z60PPQ.css" as="style">
|
|
10
|
-
<link rel="preload stylesheet" href="/vp-icons.css" as="style">
|
|
11
|
-
|
|
12
|
-
<script type="module" src="/assets/app.B8jAEB7R.js"></script>
|
|
13
|
-
<link rel="modulepreload" href="/assets/chunks/theme.BjPAOJkz.js">
|
|
14
|
-
<link rel="modulepreload" href="/assets/chunks/framework.C4nOkCZI.js">
|
|
15
|
-
<link rel="modulepreload" href="/assets/database-schema.md.BJ_JhXmO.lean.js">
|
|
16
|
-
<link rel="icon" href="/favicon.ico">
|
|
17
|
-
<meta property="og:title" content="BrutRB Documentation">
|
|
18
|
-
<meta property="og:type" content="website">
|
|
19
|
-
<meta property="og:image" content="https://brutrb.com/SocialImage.png">
|
|
20
|
-
<script defer data-domain="brutrb.com" src="https://plausible.io/js/script.js"></script>
|
|
21
|
-
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
|
|
22
|
-
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
|
|
23
|
-
</head>
|
|
24
|
-
<body>
|
|
25
|
-
<div id="app"><div class="Layout" data-v-d8b57b2d><!--[--><!--]--><!--[--><span tabindex="-1" data-v-fcbfc0e0></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-fcbfc0e0>Skip to content</a><!--]--><!----><header class="VPNav" data-v-d8b57b2d data-v-7ad780c2><div class="VPNavBar" data-v-7ad780c2 data-v-9fd4d1dd><div class="wrapper" data-v-9fd4d1dd><div class="container" data-v-9fd4d1dd><div class="title" data-v-9fd4d1dd><div class="VPNavBarTitle has-sidebar" data-v-9fd4d1dd data-v-9f43907a><a class="title" href="/" data-v-9f43907a><!--[--><!--]--><!----><span data-v-9f43907a>Brut RB</span><!--[--><!--]--></a></div></div><div class="content" data-v-9fd4d1dd><div class="content-body" data-v-9fd4d1dd><!--[--><!--]--><div class="VPNavBarSearch search" data-v-9fd4d1dd><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-9fd4d1dd data-v-afb2845e><span id="main-nav-aria-label" class="visually-hidden" data-v-afb2845e> Main Navigation </span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Home</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/getting-started.html" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Getting Started</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/overview.html" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Overview</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/api/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Brut API</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/brut-js/api/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>BrutJS</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/brut-css/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>BrutCSS</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-9fd4d1dd data-v-3f90c1a5><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-3f90c1a5 data-v-be9742d9 data-v-b4ccac88><span class="check" data-v-b4ccac88><span class="icon" data-v-b4ccac88><!--[--><span class="vpi-sun sun" data-v-be9742d9></span><span class="vpi-moon moon" data-v-be9742d9></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-9fd4d1dd data-v-ef6192dc data-v-e71e869c><!--[--><a class="VPSocialLink no-icon" href="https://github.com/thirdtank/brut" aria-label="github" target="_blank" rel="noopener" data-v-e71e869c data-v-60a9a2d3><span class="vpi-social-github"></span></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-9fd4d1dd data-v-f953d92f data-v-bfe7971f><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-bfe7971f><span class="vpi-more-horizontal icon" data-v-bfe7971f></span></button><div class="menu" data-v-bfe7971f><div class="VPMenu" data-v-bfe7971f data-v-20ed86d6><!----><!--[--><!--[--><!----><div class="group" data-v-f953d92f><div class="item appearance" data-v-f953d92f><p class="label" data-v-f953d92f>Appearance</p><div class="appearance-action" data-v-f953d92f><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-f953d92f data-v-be9742d9 data-v-b4ccac88><span class="check" data-v-b4ccac88><span class="icon" data-v-b4ccac88><!--[--><span class="vpi-sun sun" data-v-be9742d9></span><span class="vpi-moon moon" data-v-be9742d9></span><!--]--></span></span></button></div></div></div><div class="group" data-v-f953d92f><div class="item social-links" data-v-f953d92f><div class="VPSocialLinks social-links-list" data-v-f953d92f data-v-e71e869c><!--[--><a class="VPSocialLink no-icon" href="https://github.com/thirdtank/brut" aria-label="github" target="_blank" rel="noopener" data-v-e71e869c data-v-60a9a2d3><span class="vpi-social-github"></span></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-9fd4d1dd data-v-6bee1efd><span class="container" data-v-6bee1efd><span class="top" data-v-6bee1efd></span><span class="middle" data-v-6bee1efd></span><span class="bottom" data-v-6bee1efd></span></span></button></div></div></div></div><div class="divider" data-v-9fd4d1dd><div class="divider-line" data-v-9fd4d1dd></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-d8b57b2d data-v-2488c25a><div class="container" data-v-2488c25a><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-2488c25a><span class="vpi-align-left menu-icon" data-v-2488c25a></span><span class="menu-text" data-v-2488c25a>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-2488c25a data-v-6b867909><button data-v-6b867909>Return to top</button><!----></div></div></div><aside class="VPSidebar" data-v-d8b57b2d data-v-42c4c606><div class="curtain" data-v-42c4c606></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-42c4c606><span class="visually-hidden" id="sidebar-aria-label" data-v-42c4c606> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Overview</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/getting-started.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Getting Started</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/overview.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Concepts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/features.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Features</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/dir-structure.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Directory Structure</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/dev-environment.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Dev Environment</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/tutorial.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Tutorial</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/doc-conventions.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Documentation Conventions</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Front-End</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/routes.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Routes</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/pages.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Pages</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/layouts.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Layouts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/forms.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Forms</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/form-constraints.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Form Constraints</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/handlers.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Handlers and Actions</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/components.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Components</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/flash-and-session.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Flash and Session</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/space-time-continuum.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Space/Time Continuum</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/javascript.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>JavaScript</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/css.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>CSS</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/assets.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Assets</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/brut-js.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>BrutJS</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible has-active" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Back-End</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/database-schema.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Database Schema</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/database-access.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Database Access</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/seed-data.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Seed Data</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/jobs.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Jobs</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/business-logic.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Business Logic</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Framework</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/configuration.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Configuration</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/keyword-injection.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Keyword Injection</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/i18n.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>I18n</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/cli.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>CLI / Tasks</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/deployment.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Deployment</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Testing</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/unit-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Unit Tests</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/end-to-end-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>End-to-End Tests</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/custom-element-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Testing Custom Elements</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible collapsed" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Advanced Topics</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/hooks.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Route Hooks</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/middleware.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Middleware</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/instrumentation.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Instrumentation</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/security.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Security</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/lsp.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>LSP Support</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible collapsed" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Recipes</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/alternate-layouts.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Alternate Layouts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/authentication.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Authentication</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/custom-flash.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Custom Flash Class</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/indexed-forms.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Indexed Form Elements</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/dev-env-secrets.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Managing Secrets in the Dev Environment</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/migrations.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Migration Basics</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/form-errors.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Styling Form Errors</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/text-field-component.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Text Field Component</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Meta</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/why.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Why?!</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/adrs.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>ADRs</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/roadmap.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Roadmap to 1.0</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/ai.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>AI Declaration</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-d8b57b2d data-v-9a6c75ad><div class="VPDoc has-sidebar has-aside" data-v-9a6c75ad data-v-e6f2a212><!--[--><!--]--><div class="container" data-v-e6f2a212><div class="aside" data-v-e6f2a212><div class="aside-curtain" data-v-e6f2a212></div><div class="aside-container" data-v-e6f2a212><div class="aside-content" data-v-e6f2a212><div class="VPDocAside" data-v-e6f2a212 data-v-cb998dce><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-cb998dce data-v-f610f197><div class="content" data-v-f610f197><div class="outline-marker" data-v-f610f197></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-f610f197>On this page</div><ul class="VPDocOutlineItem root" data-v-f610f197 data-v-53c99d69><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-cb998dce></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-e6f2a212><div class="content-container" data-v-e6f2a212><!--[--><!--]--><main class="main" data-v-e6f2a212><div style="position:relative;" class="vp-doc _database-schema" data-v-e6f2a212><div><h1 id="database-schema-migrations" tabindex="-1">Database Schema / Migrations <a class="header-anchor" href="#database-schema-migrations" aria-label="Permalink to "Database Schema / Migrations""></a></h1><p>Brut provides access to the database via the <a href="https://sequel.jeremyevans.net/" target="_blank" rel="noreferrer">Sequel library</a>. To manage your database schema, Brut uses Sequel's facility for this, with some of its own enhancements.</p><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>Brut currently only supports Postgres. Sequel supports many database systems, however Brut's extensions are currently geared toward Postgres only.</p></div><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to "Overview""></a></h2><p>Your database schema is managed by a series of changes that build upon one another called <em>migrations</em>.</p><p>For example, if you have a table <code>widgets</code> that has a <code>name</code> and <code>description</code>, to add a <code>status</code> field, you cannot <code>drop table widgets</code> and then <code>create table widgets(...)</code> with the fields. You must instead <code>alter table widgets(...)</code> to add the new column.</p><p>Thus, each migration file is a change to the schema produced by all previous migration files.</p><p>Brut's provides this via Sequel. See <a href="https://sequel.jeremyevans.net/rdoc/files/doc/schema_modification_rdoc.html" target="_blank" rel="noreferrer">both</a> <a href="https://sequel.jeremyevans.net/rdoc/files/doc/migration_rdoc.html" target="_blank" rel="noreferrer">docs</a> for details on the API. Any schema modification method Sequel documents is available, however some default behavior has changed.</p><h3 id="creating-migrations" tabindex="-1">Creating Migrations <a class="header-anchor" href="#creating-migrations" aria-label="Permalink to "Creating Migrations""></a></h3><p>To create a migration, use <code>bin/db new-migration</code>. It accepts any number of arguments that will be joined together to form the filename:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>> bin/db new-migration user accounts</span></span>
|
|
26
|
-
<span class="line"><span>[ bin/db ] Migration created:</span></span>
|
|
27
|
-
<span class="line"><span> app/src/back_end/data_models/migrations/20250508132646_user-accounts.rb</span></span></code></pre></div><p>If you will be creating <a href="/database-access.html">database models</a> as well, you may find it easier to use <code>bin/scaffold db_model</code>, which will create an empty database model class, empty test, and empty factory, along with an outline of your migration:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>bin/scaffold db_model widget</span></span>
|
|
28
|
-
<span class="line"><span>[ bin/scaffold ] Executing ["bin/db new_migration create_widget"]</span></span>
|
|
29
|
-
<span class="line"><span>[ bin/db ] Migration created:</span></span>
|
|
30
|
-
<span class="line"><span> app/src/back_end/data_models/migrations/20250712182257_create_widgets.rb</span></span>
|
|
31
|
-
<span class="line"><span>[ bin/scaffold ] ["bin/db new_migration create_widgets"] succeeded</span></span>
|
|
32
|
-
<span class="line"><span>[ bin/scaffold ] Creating DB::Foo in app/src/back_end/data_models/db/widget.rb</span></span>
|
|
33
|
-
<span class="line"><span>[ bin/scaffold ] Creating spec for DB::Foo in specs/back_end/data_models/db/widget.spec.rb</span></span>
|
|
34
|
-
<span class="line"><span>[ bin/scaffold ] Creating factory for DB::Foo in specs/factories/db/widget.factory.rb</span></span></code></pre></div><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Brut doesn't do pluralization logic. Although Sequel does do some, you should not refer to your database model plurally. If you were do run <code>bin/scaffold db_model widgets</code>, you'd create the class <code>DB::Widgets</code>, which would not work. Be aware.</p></div><p>Note that the files are located in <code>app/src/back_end/data_models/migrations</code> and have a name prefixed with a timestamp. This timestamp determins an ordering of how the files are applied to the database.</p><p>The file is created mostly blank:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">migration</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
|
|
35
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> up </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
|
|
36
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
|
|
37
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>Sequels' migration API is similar in concept to Rails', but differs significantly in specifics. Please consult Sequel's documentation and don't assume Railsism will work the same way.</p></div><p>Brut encourages only "up" migrations. Since Brut treats your development database as ephemeral, there is little value to managing "down" migrations.</p><p>This is why Sequel's <code>change</code> method is not included in the scaffolded code. <code>change</code>, like Active Record's method of the same name, automagically creates both "up" and "down" migrations, but <em>only</em> if you use the DSL. If you use raw SQL, <code>change</code> doesn't work. By using only <code>up</code>, you won't have to worry about this.</p><p>Let's create an accounts table that has an email field, a <code>deactivated_at</code> timestamp, and a <code>created_at</code> timestamp:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">migration</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
|
|
38
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> up </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
|
|
39
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> create_table </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:accounts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
|
|
40
|
-
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> comment:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> "People or systems who can access this system"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
|
|
41
|
-
<span class="line"></span>
|
|
42
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">unique:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
|
|
43
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:deactivated_at</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:timestamptz</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">null:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
|
|
44
|
-
<span class="line"></span>
|
|
45
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
|
|
46
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
|
|
47
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>A few notes that aren't obvious without knowing about Brut's extensions:</p><ul><li><code>comment:</code> is required. You must provide documentation about what table is for</li><li>The table has a primary key named <code>id</code> of type <code>int</code> that is a serial.</li><li><code>created_at</code> is created by default, with time <code>timestamptz</code> (AKA <code>timestamp with time zone</code>, see <a href="/space-time-continuum.html">Space/Time Continuum</a>).</li><li><code>email</code> is not null by default. <code>deactivated_at</code> <em>is</em> null because it's specified as such.</li></ul><p>To apply this migration use <code>bin/db migrate</code></p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>> bin/db migrate</span></span></code></pre></div><h3 id="managing-migrations" tabindex="-1">Managing Migrations <a class="header-anchor" href="#managing-migrations" aria-label="Permalink to "Managing Migrations""></a></h3><p>Sequel uses a special database table to understand which migrations have been run. This table will exist in production and prevent you from applying migrations twice or skipping a migration.</p><p>Note that managing a production database in this way requires knowledge of both your database system and the data itself. Brut can only provide so much to make this process manageable. You should consult <a href="https://github.com/ankane/strong_migrations?tab=readme-ov-file" target="_blank" rel="noreferrer">Strong Migrations' README</a> and learn it deeply. Although it's targeted at Rails developers, the information here applies to any database management system.</p><h3 id="brut-extensions-and-changes-in-sequel-s-behavior" tabindex="-1">Brut Extensions and Changes in Sequel's Behavior <a class="header-anchor" href="#brut-extensions-and-changes-in-sequel-s-behavior" aria-label="Permalink to "Brut Extensions and Changes in Sequel's Behavior""></a></h3><p>Brut includes the following standard plugins and extensions:</p><ul><li><a href="https://sequel.jeremyevans.net/rdoc-plugins/files/lib/sequel/extensions/pg_array_rb.html" target="_blank" rel="noreferrer"><code>pg_array</code></a></li><li><a href="https://sequel.jeremyevans.net/rdoc-plugins/files/lib/sequel/extensions/pg_json_rb.html" target="_blank" rel="noreferrer"><code>pg_json</code></a></li><li><a href="https://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/TableSelect.html" target="_blank" rel="noreferrer"><code>table_select</code></a>, which changes queries to prepend <code>*</code> with the table name, e.g. <code>select accounts.*</code> instead of <code>select *</code>.</li><li><a href="https://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/SkipSavingColumns.html" target="_blank" rel="noreferrer"><code>skip_saving_columns</code></a> which will skip saving columns that the database generates.</li></ul><p>Brut also provides the following plugins and behavior changes:</p><ul><li><code>Sequel::Extensions::BrutInstrumentation</code>, which adds OpenTelemetry instrumentation to Sequel (see <a href="/instrumentation.html">Instrumentation</a>).</li><li><code>Sequel::Plugins::FindBang</code>, which adds <code>find!</code> to all models. This wraps Sequel's <code>first!</code> method, but provides a more helpful error message when no records are found</li><li><code>Sequel::Plugins::CreatedAt</code>, which automatically sets <code>created_at</code> when a record is created.</li><li><code>Sequel::Plugins::ExternalId</code>, which adds support for external IDs (see below)</li><li><code>Sequel::Extensions::BrutMigrations</code>, which enhances the migrations API (see below)</li></ul><h4 id="external-ids" tabindex="-1">External IDs <a class="header-anchor" href="#external-ids" aria-label="Permalink to "External IDs""></a></h4><p>It's often useful to provide a unique identifier for a record that is not the database primary key. There are many advantages to doing so, the main being that your primary and foreign keys are considered private and for developer use only. Creating additional externalizable unique keys is trivial, so Brut provides a way to do that.</p><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p><strong>Primary keys</strong> and <strong>keys</strong> are not the same thing. <strong>Primary keys</strong> are what is used to identify a record for the purposes of referential integrity. A <strong>key</strong> simply uniquely identifies a row or is a unique constraint on a table. Tables have only one primary key, but potentially many keys. Brut uses <em>synthetic</em> (sometimes called <em>surrogate</em>) keys as primary keys. This means they have no business meaning and can be safely used for foreign keys and other cases without conflating them with domain concepts.</p></div><p>In Brut, an external ID is automatically generated by the database when a record is created. By convention, it is prefixed with a short string representing your app and a short string representing the table, followed by a unique hash.</p><p>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 <code>myac_3457238947239487</code>. This double-prefixing is extremely useful when sharing these values with the outside world. You can immediately identify an ID from your app <em>and</em> know what sort of thing it refers to.</p><p>To use external IDs in Brut, you must do three things:</p><ol><li><p>You must set your external ID prefix in <code>app/src/app.rb</code>. This should have been done when you created your Brut app, but it looks like so:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> App</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> <</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">App</span></span>
|
|
48
|
-
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
|
|
49
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> initialize</span></span>
|
|
50
|
-
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
|
|
51
|
-
<span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">override</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">"external_id_prefix"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">"my"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
|
|
52
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
|
|
53
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> </span></span>
|
|
54
|
-
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
|
|
55
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div></li><li><p>When creating the table in a migration, use <code>external_id: true</code>:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">migration</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
|
|
56
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> up </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
|
|
57
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> create_table </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:accounts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
|
|
58
|
-
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> comment:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> "People or systems who can access this system"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
|
|
59
|
-
<span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> external_id:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
|
|
60
|
-
<span class="line"></span>
|
|
61
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">unique:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
|
|
62
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:deactivated_at</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:timestamptz</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">null:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
|
|
63
|
-
<span class="line"></span>
|
|
64
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
|
|
65
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
|
|
66
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div></li><li><p>In your <a href="/database-access.html">data model class</a>, use <code>has_external_id</code> to specify the prefix for this table:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>class DB::Account < AppDataModel</span></span>
|
|
67
|
-
<span class="line"><span> has_external_id :ac</span></span>
|
|
68
|
-
<span class="line"><span></span></span>
|
|
69
|
-
<span class="line"><span> # ...</span></span>
|
|
70
|
-
<span class="line"><span>end</span></span></code></pre></div></li></ol><p>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.</p><p>This means that you can set values explicitly if you like, <em>and</em> 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).</p><h3 id="brut-migration-changes-and-enhancement" tabindex="-1">Brut Migration Changes and Enhancement <a class="header-anchor" href="#brut-migration-changes-and-enhancement" aria-label="Permalink to "Brut Migration Changes and Enhancement""></a></h3><p>Brut attempts to set default behavior for migrations to encourage a modicum of best practices. These are:</p><ul><li><p><strong>Automatic synthetic primary key named <code>id</code> of type <code>int</code>.</strong> You almost always want this. You can change the primary key configuration per table if you like, but if you do nothing, you get a primary key that works for 99% of your needs.</p></li><li><p><strong>Automatic <code>created_at</code> of type <code>timestamptz</code>.</strong> 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 <code>timestamp with time zone</code>, which you are encouraged to use always. See <a href="/space-time-continuum.html">Space/Time Continuum</a> for details.</p></li><li><p><strong>No automatic <code>updated_at</code>.</strong> While you are free to add <code>updated_at</code>, 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.</p></li><li><p><strong><code>create_table</code> requires <code>comment:</code>.</strong> Just document your tables. It takes two seconds and can save a lot of time later.</p></li><li><p><strong>Support for external IDs via <code>external_id:</code>.</strong> As discussed above, this will create a unique <code>external_id</code> column on your table and ensure it has a value on creation.</p></li><li><p><strong>Columns are <code>NOT NULL</code> by default.</strong> Null is not a valid value. In many cases, your columns should not allow <code>NULL</code> (<code>nil</code>), so in Brut apps, you must opt into nullable columns. You can use <code>null: true</code> to make a column nullable.</p></li><li><p><strong>Foreign keys are <code>NOT NULL</code> and have an index created for them by default.</strong> Foreign keys should rarely be <code>NULL</code> and you almost always want an index on them, since you are likely to using them in queries, e.g. <code>account.widgets</code> would join on <code>accounts.widget_id</code>. You can opt out of either via <code>null: true</code> and <code>index: false</code>.</p></li><li><p><strong>The method <code>key</code> allows you to specify a non-primary key, AKA a unique index</strong>. Suppose our <code>accounts</code> table allowed duplicate email addresses, but only one per <code>organization_id</code>. You'd model this by creating a unique index on <code>(email,organization_id)</code>. In Brut:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">migration</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
|
|
71
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> up </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
|
|
72
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> create_table </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:accounts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
|
|
73
|
-
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> comment:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> "People or systems who can access this system"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
|
|
74
|
-
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> external_id:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
|
|
75
|
-
<span class="line"></span>
|
|
76
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">unique:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
|
|
77
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> foreign_key </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:organization_id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:organizations</span></span>
|
|
78
|
-
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:deactivated_at</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:timestamptz</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">null:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
|
|
79
|
-
<span class="line"></span>
|
|
80
|
-
<span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> key [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:organization_id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
|
|
81
|
-
<span class="line"></span>
|
|
82
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
|
|
83
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
|
|
84
|
-
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>This allows your migrations to be more expressive <em>and</em> make it easier to set up unique constraints that relate to your business logic or domain.</p></li></ul><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to "Testing""></a></h2><p>Generally, you don't test database migrations, however you may want to test constraints or other logic you have set up. Techniques for doing this are in the <a href="/database-access.html#testing">database access</a> section.</p><h2 id="recommended-practices" tabindex="-1">Recommended Practices <a class="header-anchor" href="#recommended-practices" aria-label="Permalink to "Recommended Practices""></a></h2><h3 id="ephemeral-dev-database" tabindex="-1">Ephemeral Dev Database <a class="header-anchor" href="#ephemeral-dev-database" aria-label="Permalink to "Ephemeral Dev Database""></a></h3><p>Brut intends for your develompent database to be ephemeral. Your entire workflow should be built around it being OK and normal to completely blow away your development database and recreate it. This is why down migrations (and the use of <code>change</code>) are discouraged. You really don't need them.</p><p>Assuming you have <a href="/seed-data.html">seed data</a> set up properly, you can reliably reset everything like so:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>> bin/db rebuild</span></span>
|
|
85
|
-
<span class="line"><span>> bin/db seed</span></span>
|
|
86
|
-
<span class="line"><span>> bin/db rebuild -e test</span></span></code></pre></div><p>As long as you don't change migrations that have been applied in production, you can safely run the above commands to iterate on a schema change.</p><p>This does imply that you should not run business logic or make <em>data</em> changes in your migration files. No migration should rely on specific data being in the database.</p><p>This workflow my be much different from what you are used to, but you will be quite happy when you adopt it. The days of downloading a carefully-curated database image and taking great care to never delete it are over.</p><h3 id="use-your-database-it-is-awesome" tabindex="-1">Use Your Database, It is Awesome <a class="header-anchor" href="#use-your-database-it-is-awesome" aria-label="Permalink to "Use Your Database, It is Awesome""></a></h3><p>Your database is the only part of the system that has any chance of ensuring data integrity. You can use constraints, types, foreign keys, etc. to ensure that the data in your database is correct, based on your current understandings. Code-based validation systems <strong>cannot achieve this on any level</strong>.</p><p>Thus, you are encouraged to learn about your database's features and use them!</p><p>For example, here's a way to add full text search to an existing table in Postgres:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">add_column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:full_text_search</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
|
|
87
|
-
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> :tsvector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
|
|
88
|
-
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> generated_always_as:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">lit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">%{</span></span>
|
|
89
|
-
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> (</span></span>
|
|
90
|
-
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> setweight(to_tsvector('english', name),'A') ||</span></span>
|
|
91
|
-
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> setweight(to_tsvector('english', coalesce(description,'')),'B')</span></span>
|
|
92
|
-
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> )</span></span>
|
|
93
|
-
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> }</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">),</span></span>
|
|
94
|
-
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> generated_type:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> :stored</span></span></code></pre></div><p>If you are using Postgtes, why <em>not</em> use its features? Unless your app is database-agnostic, you should be using the features of your database, even if they aren't explicitly exposed via Sequel's Ruby API (that's why <code>Sequel.lit</code> exists).</p><h2 id="technical-notes" tabindex="-1">Technical Notes <a class="header-anchor" href="#technical-notes" aria-label="Permalink to "Technical Notes""></a></h2><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's internals, the source code is always more correct.</p></div><p><em>Last Updated May 8, 2025</em></p><p>As mentioned, Brut uses Sequel under the covers. This is unlikely to change.</p><p>As also mentioned, Brut's extensions often rely on Postgres. While we can all dream of a world where every developer uses the same database server, we don't live in that world. Brut should, some day, support all the databases that Sequel supports. For now, however, it only supports Postgres.</p><p>This hard-coded support is due to:</p><ul><li><code>pg_array</code></li><li><code>pg_json</code></li><li>Reliance on <code>citext</code> and <code>comment</code></li><li>Reliance on <code>timestamptz</code></li></ul><p>Brut is likely to add more Postgres-specific features before adding support for other databases.</p></div></div></main><footer class="VPDocFooter" data-v-e6f2a212 data-v-1bcd8184><!--[--><!--]--><!----><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-1bcd8184><span class="visually-hidden" id="doc-footer-aria-label" data-v-1bcd8184>Pager</span><div class="pager" data-v-1bcd8184><a class="VPLink link pager-link prev" href="/brut-js.html" data-v-1bcd8184><!--[--><span class="desc" data-v-1bcd8184>Previous page</span><span class="title" data-v-1bcd8184>BrutJS</span><!--]--></a></div><div class="pager" data-v-1bcd8184><a class="VPLink link pager-link next" href="/database-access.html" data-v-1bcd8184><!--[--><span class="desc" data-v-1bcd8184>Next page</span><span class="title" data-v-1bcd8184>Database Access</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
|
|
95
|
-
<script>window.__VP_HASH_MAP__=JSON.parse("{\"adrs.md\":\"YglbWtQe\",\"ai.md\":\"ChLnvDAX\",\"assets.md\":\"BEF6Oz6K\",\"brut-js.md\":\"BMz0X1Rz\",\"business-logic.md\":\"DbuaOYGU\",\"cli.md\":\"DDMar_51\",\"components.md\":\"Ber8UBM0\",\"configuration.md\":\"DrJ6YVoZ\",\"css.md\":\"K5rOCOQY\",\"custom-element-tests.md\":\"DiLe-eFw\",\"database-access.md\":\"Dc8l2Plf\",\"database-schema.md\":\"BJ_JhXmO\",\"deployment.md\":\"CHTx2eTR\",\"dev-environment.md\":\"B1S9p5ZK\",\"dir-structure.md\":\"D1T2kGwj\",\"doc-conventions.md\":\"CDnWaEFg\",\"end-to-end-tests.md\":\"BJJdNDYL\",\"features.md\":\"BDWxnyNO\",\"flash-and-session.md\":\"CUsMxoNl\",\"form-constraints.md\":\"KlfXSKm2\",\"forms.md\":\"RK0zkhm0\",\"getting-started.md\":\"CGJ44juQ\",\"handlers.md\":\"C5tUwmmo\",\"hooks.md\":\"CoiYCKRc\",\"i18n.md\":\"DxkCKhUw\",\"index.md\":\"DnphWyQd\",\"instrumentation.md\":\"BcxjC4jd\",\"javascript.md\":\"D6fxhaQb\",\"jobs.md\":\"Bi3qb3v6\",\"keyword-injection.md\":\"CqLnnzIz\",\"layouts.md\":\"HEbeK7Jr\",\"lsp.md\":\"bE9dW8n9\",\"markdown-examples.md\":\"BPmtHlc-\",\"middleware.md\":\"BhOIsg59\",\"overview.md\":\"BpWAgPFH\",\"pages.md\":\"B3sQXpEd\",\"recipes_alternate-layouts.md\":\"C1QzVkA7\",\"recipes_authentication.md\":\"CyvoIW82\",\"recipes_custom-flash.md\":\"6gFqf2uL\",\"recipes_dev-env-secrets.md\":\"DC_jVY9U\",\"recipes_form-errors.md\":\"B5ptSzMO\",\"recipes_indexed-forms.md\":\"BYYQGW2C\",\"recipes_migrations.md\":\"Cid7-3cu\",\"recipes_text-field-component.md\":\"VhOsCtKI\",\"roadmap.md\":\"DqC1Y7Zt\",\"routes.md\":\"C1dgIBtD\",\"security.md\":\"Jn4SY1uK\",\"seed-data.md\":\"UZW0WxYN\",\"space-time-continuum.md\":\"D9rYGDFH\",\"tutorial.md\":\"BX6f6l00\",\"tutorials_01-intro.md\":\"CzZ3kpF_\",\"tutorials_02-dialog.md\":\"DE5WfCXI\",\"unit-tests.md\":\"vDsdBbO_\",\"why.md\":\"4WpxdrQ2\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Brut RB\",\"description\":\"Documentation for the Brut.RB web framework.\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"search\":{\"provider\":\"local\"},\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Overview\",\"link\":\"/overview\"},{\"text\":\"Brut API\",\"link\":\"/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutCSS\",\"link\":\"/brut-css/index.html\",\"target\":\"_self\"}],\"outline\":[2,3],\"sidebar\":[{\"text\":\"Overview\",\"collapsed\":false,\"items\":[{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Concepts\",\"link\":\"/overview\"},{\"text\":\"Features\",\"link\":\"/features\"},{\"text\":\"Directory Structure\",\"link\":\"/dir-structure\"},{\"text\":\"Dev Environment\",\"link\":\"/dev-environment\"},{\"text\":\"Tutorial\",\"link\":\"/tutorial\"},{\"text\":\"Documentation Conventions\",\"link\":\"/doc-conventions\"}]},{\"text\":\"Front-End\",\"collapsed\":false,\"items\":[{\"text\":\"Routes\",\"link\":\"/routes\"},{\"text\":\"Pages\",\"link\":\"/pages\"},{\"text\":\"Layouts\",\"link\":\"/layouts\"},{\"text\":\"Forms\",\"link\":\"/forms\"},{\"text\":\"Form Constraints\",\"link\":\"/form-constraints\"},{\"text\":\"Handlers and Actions\",\"link\":\"/handlers\"},{\"text\":\"Components\",\"link\":\"/components\"},{\"text\":\"Flash and Session\",\"link\":\"/flash-and-session\"},{\"text\":\"Space/Time Continuum\",\"link\":\"/space-time-continuum\"},{\"text\":\"JavaScript\",\"link\":\"/javascript\"},{\"text\":\"CSS\",\"link\":\"/css\"},{\"text\":\"Assets\",\"link\":\"/assets\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js\"}]},{\"text\":\"Back-End\",\"collapsed\":false,\"items\":[{\"text\":\"Database Schema\",\"link\":\"/database-schema\"},{\"text\":\"Database Access\",\"link\":\"/database-access\"},{\"text\":\"Seed Data\",\"link\":\"/seed-data\"},{\"text\":\"Jobs\",\"link\":\"/jobs\"},{\"text\":\"Business Logic\",\"link\":\"/business-logic\"}]},{\"text\":\"Framework\",\"collapsed\":false,\"items\":[{\"text\":\"Configuration\",\"link\":\"/configuration\"},{\"text\":\"Keyword Injection\",\"link\":\"/keyword-injection\"},{\"text\":\"I18n\",\"link\":\"/i18n\"},{\"text\":\"CLI / Tasks\",\"link\":\"/cli\"},{\"text\":\"Deployment\",\"link\":\"/deployment\"}]},{\"text\":\"Testing\",\"collapsed\":false,\"items\":[{\"text\":\"Unit Tests\",\"link\":\"/unit-tests\"},{\"text\":\"End-to-End Tests\",\"link\":\"/end-to-end-tests\"},{\"text\":\"Testing Custom Elements\",\"link\":\"/custom-element-tests\"}]},{\"text\":\"Advanced Topics\",\"collapsed\":true,\"items\":[{\"text\":\"Route Hooks\",\"link\":\"/hooks\"},{\"text\":\"Middleware\",\"link\":\"/middleware\"},{\"text\":\"Instrumentation\",\"link\":\"/instrumentation\"},{\"text\":\"Security\",\"link\":\"/security\"},{\"text\":\"LSP Support\",\"link\":\"/lsp\"}]},{\"text\":\"Recipes\",\"collapsed\":true,\"items\":[{\"text\":\"Alternate Layouts\",\"link\":\"/recipes/alternate-layouts\"},{\"text\":\"Authentication\",\"link\":\"/recipes/authentication\"},{\"text\":\"Custom Flash Class\",\"link\":\"/recipes/custom-flash\"},{\"text\":\"Indexed Form Elements\",\"link\":\"/recipes/indexed-forms\"},{\"text\":\"Managing Secrets in the Dev Environment\",\"link\":\"/recipes/dev-env-secrets\"},{\"text\":\"Migration Basics\",\"link\":\"/recipes/migrations\"},{\"text\":\"Styling Form Errors\",\"link\":\"/recipes/form-errors\"},{\"text\":\"Text Field Component\",\"link\":\"/recipes/text-field-component\"}]},{\"text\":\"Meta\",\"collapsed\":false,\"items\":[{\"text\":\"Why?!\",\"link\":\"/why\"},{\"text\":\"ADRs\",\"link\":\"/adrs\"},{\"text\":\"Roadmap to 1.0\",\"link\":\"/roadmap\"},{\"text\":\"AI Declaration\",\"link\":\"/ai\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/thirdtank/brut\"}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
|
|
96
|
-
|
|
97
|
-
</body>
|
|
98
|
-
</html>
|
data/docs/deployment.html
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en-US" dir="ltr">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
-
<title>Deployment | Brut RB</title>
|
|
7
|
-
<meta name="description" content="Documentation for the Brut.RB web framework.">
|
|
8
|
-
<meta name="generator" content="VitePress v1.6.4">
|
|
9
|
-
<link rel="preload stylesheet" href="/assets/style.B1z60PPQ.css" as="style">
|
|
10
|
-
<link rel="preload stylesheet" href="/vp-icons.css" as="style">
|
|
11
|
-
|
|
12
|
-
<script type="module" src="/assets/app.B8jAEB7R.js"></script>
|
|
13
|
-
<link rel="modulepreload" href="/assets/chunks/theme.BjPAOJkz.js">
|
|
14
|
-
<link rel="modulepreload" href="/assets/chunks/framework.C4nOkCZI.js">
|
|
15
|
-
<link rel="modulepreload" href="/assets/deployment.md.CHTx2eTR.lean.js">
|
|
16
|
-
<link rel="icon" href="/favicon.ico">
|
|
17
|
-
<meta property="og:title" content="BrutRB Documentation">
|
|
18
|
-
<meta property="og:type" content="website">
|
|
19
|
-
<meta property="og:image" content="https://brutrb.com/SocialImage.png">
|
|
20
|
-
<script defer data-domain="brutrb.com" src="https://plausible.io/js/script.js"></script>
|
|
21
|
-
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
|
|
22
|
-
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
|
|
23
|
-
</head>
|
|
24
|
-
<body>
|
|
25
|
-
<div id="app"><div class="Layout" data-v-d8b57b2d><!--[--><!--]--><!--[--><span tabindex="-1" data-v-fcbfc0e0></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-fcbfc0e0>Skip to content</a><!--]--><!----><header class="VPNav" data-v-d8b57b2d data-v-7ad780c2><div class="VPNavBar" data-v-7ad780c2 data-v-9fd4d1dd><div class="wrapper" data-v-9fd4d1dd><div class="container" data-v-9fd4d1dd><div class="title" data-v-9fd4d1dd><div class="VPNavBarTitle has-sidebar" data-v-9fd4d1dd data-v-9f43907a><a class="title" href="/" data-v-9f43907a><!--[--><!--]--><!----><span data-v-9f43907a>Brut RB</span><!--[--><!--]--></a></div></div><div class="content" data-v-9fd4d1dd><div class="content-body" data-v-9fd4d1dd><!--[--><!--]--><div class="VPNavBarSearch search" data-v-9fd4d1dd><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-9fd4d1dd data-v-afb2845e><span id="main-nav-aria-label" class="visually-hidden" data-v-afb2845e> Main Navigation </span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Home</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/getting-started.html" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Getting Started</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/overview.html" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Overview</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/api/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>Brut API</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/brut-js/api/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>BrutJS</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/brut-css/index.html" target="_self" tabindex="0" data-v-afb2845e data-v-815115f5><!--[--><span data-v-815115f5>BrutCSS</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-9fd4d1dd data-v-3f90c1a5><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-3f90c1a5 data-v-be9742d9 data-v-b4ccac88><span class="check" data-v-b4ccac88><span class="icon" data-v-b4ccac88><!--[--><span class="vpi-sun sun" data-v-be9742d9></span><span class="vpi-moon moon" data-v-be9742d9></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-9fd4d1dd data-v-ef6192dc data-v-e71e869c><!--[--><a class="VPSocialLink no-icon" href="https://github.com/thirdtank/brut" aria-label="github" target="_blank" rel="noopener" data-v-e71e869c data-v-60a9a2d3><span class="vpi-social-github"></span></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-9fd4d1dd data-v-f953d92f data-v-bfe7971f><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-bfe7971f><span class="vpi-more-horizontal icon" data-v-bfe7971f></span></button><div class="menu" data-v-bfe7971f><div class="VPMenu" data-v-bfe7971f data-v-20ed86d6><!----><!--[--><!--[--><!----><div class="group" data-v-f953d92f><div class="item appearance" data-v-f953d92f><p class="label" data-v-f953d92f>Appearance</p><div class="appearance-action" data-v-f953d92f><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title aria-checked="false" data-v-f953d92f data-v-be9742d9 data-v-b4ccac88><span class="check" data-v-b4ccac88><span class="icon" data-v-b4ccac88><!--[--><span class="vpi-sun sun" data-v-be9742d9></span><span class="vpi-moon moon" data-v-be9742d9></span><!--]--></span></span></button></div></div></div><div class="group" data-v-f953d92f><div class="item social-links" data-v-f953d92f><div class="VPSocialLinks social-links-list" data-v-f953d92f data-v-e71e869c><!--[--><a class="VPSocialLink no-icon" href="https://github.com/thirdtank/brut" aria-label="github" target="_blank" rel="noopener" data-v-e71e869c data-v-60a9a2d3><span class="vpi-social-github"></span></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-9fd4d1dd data-v-6bee1efd><span class="container" data-v-6bee1efd><span class="top" data-v-6bee1efd></span><span class="middle" data-v-6bee1efd></span><span class="bottom" data-v-6bee1efd></span></span></button></div></div></div></div><div class="divider" data-v-9fd4d1dd><div class="divider-line" data-v-9fd4d1dd></div></div></div><!----></header><div class="VPLocalNav has-sidebar empty" data-v-d8b57b2d data-v-2488c25a><div class="container" data-v-2488c25a><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-2488c25a><span class="vpi-align-left menu-icon" data-v-2488c25a></span><span class="menu-text" data-v-2488c25a>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-2488c25a data-v-6b867909><button data-v-6b867909>Return to top</button><!----></div></div></div><aside class="VPSidebar" data-v-d8b57b2d data-v-42c4c606><div class="curtain" data-v-42c4c606></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-42c4c606><span class="visually-hidden" id="sidebar-aria-label" data-v-42c4c606> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Overview</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/getting-started.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Getting Started</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/overview.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Concepts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/features.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Features</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/dir-structure.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Directory Structure</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/dev-environment.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Dev Environment</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/tutorial.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Tutorial</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/doc-conventions.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Documentation Conventions</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Front-End</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/routes.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Routes</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/pages.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Pages</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/layouts.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Layouts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/forms.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Forms</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/form-constraints.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Form Constraints</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/handlers.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Handlers and Actions</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/components.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Components</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/flash-and-session.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Flash and Session</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/space-time-continuum.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Space/Time Continuum</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/javascript.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>JavaScript</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/css.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>CSS</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/assets.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Assets</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/brut-js.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>BrutJS</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Back-End</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/database-schema.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Database Schema</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/database-access.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Database Access</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/seed-data.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Seed Data</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/jobs.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Jobs</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/business-logic.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Business Logic</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible has-active" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Framework</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/configuration.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Configuration</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/keyword-injection.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Keyword Injection</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/i18n.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>I18n</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/cli.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>CLI / Tasks</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/deployment.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Deployment</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Testing</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/unit-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Unit Tests</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/end-to-end-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>End-to-End Tests</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/custom-element-tests.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Testing Custom Elements</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible collapsed" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Advanced Topics</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/hooks.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Route Hooks</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/middleware.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Middleware</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/instrumentation.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Instrumentation</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/security.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Security</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/lsp.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>LSP Support</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible collapsed" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Recipes</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/alternate-layouts.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Alternate Layouts</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/authentication.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Authentication</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/custom-flash.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Custom Flash Class</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/indexed-forms.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Indexed Form Elements</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/dev-env-secrets.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Managing Secrets in the Dev Environment</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/migrations.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Migration Basics</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/form-errors.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Styling Form Errors</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/recipes/text-field-component.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Text Field Component</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="no-transition group" data-v-51288d80><section class="VPSidebarItem level-0 collapsible" data-v-51288d80 data-v-0009425e><div class="item" role="button" tabindex="0" data-v-0009425e><div class="indicator" data-v-0009425e></div><h2 class="text" data-v-0009425e>Meta</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-0009425e><span class="vpi-chevron-right caret-icon" data-v-0009425e></span></div></div><div class="items" data-v-0009425e><!--[--><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/why.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Why?!</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/adrs.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>ADRs</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/roadmap.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>Roadmap to 1.0</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-0009425e data-v-0009425e><div class="item" data-v-0009425e><div class="indicator" data-v-0009425e></div><a class="VPLink link link" href="/ai.html" data-v-0009425e><!--[--><p class="text" data-v-0009425e>AI Declaration</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-d8b57b2d data-v-9a6c75ad><div class="VPDoc has-sidebar has-aside" data-v-9a6c75ad data-v-e6f2a212><!--[--><!--]--><div class="container" data-v-e6f2a212><div class="aside" data-v-e6f2a212><div class="aside-curtain" data-v-e6f2a212></div><div class="aside-container" data-v-e6f2a212><div class="aside-content" data-v-e6f2a212><div class="VPDocAside" data-v-e6f2a212 data-v-cb998dce><!--[--><!--]--><!--[--><!--]--><nav aria-labelledby="doc-outline-aria-label" class="VPDocAsideOutline" data-v-cb998dce data-v-f610f197><div class="content" data-v-f610f197><div class="outline-marker" data-v-f610f197></div><div aria-level="2" class="outline-title" id="doc-outline-aria-label" role="heading" data-v-f610f197>On this page</div><ul class="VPDocOutlineItem root" data-v-f610f197 data-v-53c99d69><!--[--><!--]--></ul></div></nav><!--[--><!--]--><div class="spacer" data-v-cb998dce></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-e6f2a212><div class="content-container" data-v-e6f2a212><!--[--><!--]--><main class="main" data-v-e6f2a212><div style="position:relative;" class="vp-doc _deployment" data-v-e6f2a212><div><h1 id="deployment" tabindex="-1">Deployment <a class="header-anchor" href="#deployment" aria-label="Permalink to "Deployment""></a></h1><p>Brut apps are Rack apps, so they can be deployed in conventional ways.</p><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to "Overview""></a></h2><p>There are just too many ways to deploy. Brut attempts to address this by adhering to <a href="https://12factor.net" target="_blank" rel="noreferrer">12-factor principles</a>. Brut also tries not to create artifacts like <code>Procfile</code> or <code>Dockerfile</code> that would conflict with the artifacts you'd need to manage deployment.</p><p>That said, Brut includes first-class support for deploying to Heroku using containers. More options will be included as necessary, either through direct support in code/tooling, or documentation here.</p><h3 id="heroku-container-based-deployment" tabindex="-1">Heroku Container-based Deployment <a class="header-anchor" href="#heroku-container-based-deployment" aria-label="Permalink to "Heroku Container-based Deployment""></a></h3><p>When creating your Brut app with <code>mkbrut</code>, the Heroku segment can be used to create files and scripts for a <a href="https://devcenter.heroku.com/articles/container-registry-and-runtime" target="_blank" rel="noreferrer">Heroku container-based deployment</a>.</p><table tabindex="0"><thead><tr><th>File</th><th>Purpose</th><th>Notes</th></tr></thead><tbody><tr><td><code>bin/deploy</code></td><td>Script to use to perform the deployment</td><td>This wraps <code>HerokuContainerBasedDeploy</code> in <a href="/api/Brut/CLI/Apps.html" target="_self" rel="noopener" data-no-router><code>Brut::CLI::Apps</code></a></td></tr><tr><td><code>deploy/Dockerfile</code></td><td>Template <code>Dockerfile</code> used to create a <code>Dockerfile</code> for each process type</td><td>Heroku requires each process (web, worker, release, etc.) to have its own <code>Dockerfile</code> and own image</td></tr><tr><td><code>deploy/heroku_config.rb</code></td><td>Class that exports optional processes</td><td>By default, your app has a web and release process. <code>HerokuConfig</code> can export others, like Sidekiq</td></tr><tr><td><code>deploy/docker-entrypoint</code></td><td>The <a href="https://docs.docker.com/reference/dockerfile/#entrypoint" target="_blank" rel="noreferrer"><code>ENTRYPOINT</code></a> for production Docker images, which is set up to use jemalloc</td><td>You can modify or remove this as needed</td></tr></tbody></table><p>How to deploy:</p><ol><li><p>Get an auth token from Heroku, which you can do from inside the container, and save it to <code>bash_customizations.local</code>:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>your-computer> dx/exec bash</span></span>
|
|
26
|
-
<span class="line"><span>devcontainer> heroku auth:login</span></span>
|
|
27
|
-
<span class="line"><span># You will need to copy/paste the URL to log in</span></span>
|
|
28
|
-
<span class="line"><span>devcontainer> heroku authorizations:create -d "container pushes" --expires-in 31536000</span></span>
|
|
29
|
-
<span class="line"><span># Copy the token output by this command</span></span>
|
|
30
|
-
<span class="line"><span>devcontainer> echo "HEROKU_API_KEY=«TOKEN YOU COPIED»" >> dx/bash_customizations.local</span></span></code></pre></div></li><li><p>Exit the devcontainer and stop <code>dx/start</code> (e.g. hit <code>Ctrl-C</code> wherever you ran it)</p></li><li><p>Rebuild and restart the devcontainer (this will set <code>HEROKU_API_KEY</code> for you)</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>your-computer> dx/build</span></span>
|
|
31
|
-
<span class="line"><span>your-computer> dx/start</span></span>
|
|
32
|
-
<span class="line"><span># In another terminal window</span></span>
|
|
33
|
-
<span class="line"><span>your-computer> dx/exec bash</span></span>
|
|
34
|
-
<span class="line"><span>devcontainer> echo $HEROKU_API_KEY</span></span>
|
|
35
|
-
<span class="line"><span># You should see the token</span></span></code></pre></div><p>Setting this environment variable avoids having to constantly re-authenticate to Heroku.</p></li><li><p>Create your app using the container stack:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>> heroku create --stack container -a «your heroku app name»</span></span></code></pre></div></li><li><p>Ensure your app's source code is all checked in, there are no uncommitted or unadded files, and you have pushed to the <code>main</code> branch of your remote Git repository.</p></li><li><p><code>bin/deploy</code></p><p>This will generate a <code>Dockerfile</code> for each process (by default, <code>Dockerfile.web</code> and <code>Dockerfile.release</code>), build images, push those images to Heroku, and ask Heroku to release them.</p></li></ol><p>Debugging Tips:</p><ul><li><p>Keep in mind it's hard to make general deployment tools. You are expected to understand your deployment and be capable of deploying an arbitrary Rack app manually. Brut's tooling automates what you need to do based on what you already need to know.</p></li><li><p><code>bin/deploy</code> runs the <code>deploy</code> subcommand, so <code>bin/deploy help deploy</code> can provide some options for debugging issues:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>devcontainer> bin/deploy help deploy</span></span>
|
|
36
|
-
<span class="line"><span>Usage: bin/deploy [global options] deploy [command options] </span></span>
|
|
37
|
-
<span class="line"><span></span></span>
|
|
38
|
-
<span class="line"><span> Build images, push them to Heroku, and deploy them</span></span>
|
|
39
|
-
<span class="line"><span></span></span>
|
|
40
|
-
<span class="line"><span> Manages a deploy process based on using Heroku's Container Registry. See</span></span>
|
|
41
|
-
<span class="line"><span></span></span>
|
|
42
|
-
<span class="line"><span> https://devcenter.heroku.com/articles/container-registry-and-runtime</span></span>
|
|
43
|
-
<span class="line"><span></span></span>
|
|
44
|
-
<span class="line"><span> for details. You are assumed to understand this.</span></span>
|
|
45
|
-
<span class="line"><span> This command will make the process somewhat easier.</span></span>
|
|
46
|
-
<span class="line"><span></span></span>
|
|
47
|
-
<span class="line"><span> This will use deploy/Dockerfile as a template to create</span></span>
|
|
48
|
-
<span class="line"><span> one Dockerfile for each process you want to run in Heroku.</span></span>
|
|
49
|
-
<span class="line"><span> deploy/heroku_config.rb is where the processes and their</span></span>
|
|
50
|
-
<span class="line"><span> commands are configured.</span></span>
|
|
51
|
-
<span class="line"><span></span></span>
|
|
52
|
-
<span class="line"><span> The release phase is included automatically, based on bin/release.</span></span>
|
|
53
|
-
<span class="line"><span></span></span>
|
|
54
|
-
<span class="line"><span>GLOBAL OPTIONS</span></span>
|
|
55
|
-
<span class="line"><span></span></span>
|
|
56
|
-
<span class="line"><span> -h, --help Get help</span></span>
|
|
57
|
-
<span class="line"><span> --log-level=LEVEL Set log level. Allowed values: debug,</span></span>
|
|
58
|
-
<span class="line"><span> info, warn, error, fatal. Default 'fatal'</span></span>
|
|
59
|
-
<span class="line"><span> --verbose Set log level to 'debug', which will produce</span></span>
|
|
60
|
-
<span class="line"><span> maximum output</span></span>
|
|
61
|
-
<span class="line"><span></span></span>
|
|
62
|
-
<span class="line"><span>ENVIRONMENT VARIABLES</span></span>
|
|
63
|
-
<span class="line"><span></span></span>
|
|
64
|
-
<span class="line"><span> BRUT_CLI_RAISE_ON_ERROR - if set, shows backtrace on errors</span></span>
|
|
65
|
-
<span class="line"><span> LOG_LEVEL - log level if --log-level or --verbose is omitted</span></span>
|
|
66
|
-
<span class="line"><span></span></span>
|
|
67
|
-
<span class="line"><span></span></span>
|
|
68
|
-
<span class="line"><span>COMMAND OPTIONS</span></span>
|
|
69
|
-
<span class="line"><span></span></span>
|
|
70
|
-
<span class="line"><span> --platform=PLATFORM Override default platform. Can be any Docker</span></span>
|
|
71
|
-
<span class="line"><span> platform.</span></span>
|
|
72
|
-
<span class="line"><span> --[no-]dry-run Print the commands that would be run and</span></span>
|
|
73
|
-
<span class="line"><span> don't actually do anything. Implies --skip-checks</span></span>
|
|
74
|
-
<span class="line"><span> --[no-]skip-checks Skip checks for code having been</span></span>
|
|
75
|
-
<span class="line"><span> committed and pushed</span></span>
|
|
76
|
-
<span class="line"><span> --[no-]deploy After images are pushed, actually deploy them</span></span>
|
|
77
|
-
<span class="line"><span> --[no-]push After images are created, push them</span></span>
|
|
78
|
-
<span class="line"><span> to Heroku's registry. If false,</span></span>
|
|
79
|
-
<span class="line"><span> implies --no-deploy</span></span></code></pre></div></li><li><p>Try building images first: <code>bin/deploy deploy --no-push --skip-checks</code></p></li><li><p>It's possible to run the images locally. If you are on Apple Silicon, you'll need to set --platform:</p><ul><li><code>bin/deploy deploy --no-push --skip-checks --platform linux/arm64</code></li><li>Create <code>docker-compose.yml</code> for your image and any other services e.g. databases</li><li>Set required environment variables in <code>docker-compose.yml</code></li><li>Start up Docker compose and poke around</li></ul><p>You'll need to have a better understanding of Docker to do this, however if you are deploying with Docker, this is an understanding you hopefully already have.</p></li></ul><h3 id="other-mechanisms-for-deployment" tabindex="-1">Other Mechanisms for Deployment <a class="header-anchor" href="#other-mechanisms-for-deployment" aria-label="Permalink to "Other Mechanisms for Deployment""></a></h3><p>As a Rack app, other deployments should be possible. To make the app work, you'll need to make sure a few things are dealt with:</p><ul><li><code>RACK_ENV</code> <strong>must</strong> be <code>"production"</code></li><li><code>bin/build-assets</code> will build all assets by default. This must either be done on production servers or done ahead of time and the results packaged with the app.</li><li><code>bin/build-assets</code> outputs files in <code>app/public</code> and <code>app/config</code>. Those files are used at runtime. Brut <strong>will not</strong> initiate the build of any assets.</li><li>If you are going to build assets on production servers, you <em>must</em> included developer tooling. This means NodeJS, all modules in <code>package.json</code> and all RubyGems in <code>Gemfile</code>.</li></ul><p>The <code>deploy/Dockerfile</code> created by <code>mkbrut --segment-heroku</code> is not very Heroku-specific and could serve as a reference.</p><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to "Testing""></a></h2><p>Testing deployments is a bit out of scope, but in general:</p><ul><li>A container-based deployment can theoretically be run on your computer as a test.</li><li>Non-production, but production-like environments can be used to validate production configurations.</li><li>You own the means of production…not Brut.</li></ul><h2 id="recommended-practices" tabindex="-1">Recommended Practices <a class="header-anchor" href="#recommended-practices" aria-label="Permalink to "Recommended Practices""></a></h2><ul><li>Avoid a lot of code that checks <code>Brut.container.project_env</code>. Try to consolidate all prod/test/dev differences in environment variables.</li><li>Have a way to get a shell into your production environment for debugging.</li><li>Brut doesn't log much, but if you remove the <code>OTEL_*</code> environment variables, Brut will log OTel telemetry to the console, which may be useful.</li><li>Setting <code>OTEL_LOG_LEVEL=debug</code> is advised if the app isn't starting or you aren't seeing any telemetry or logging</li></ul><h2 id="technical-notes" tabindex="-1">Technical Notes <a class="header-anchor" href="#technical-notes" aria-label="Permalink to "Technical Notes""></a></h2><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut's internals, the source code is always more correct.</p></div><p><em>Last Updated July 3, 2025</em></p><p>None at this time.</p></div></div></main><footer class="VPDocFooter" data-v-e6f2a212 data-v-1bcd8184><!--[--><!--]--><!----><nav class="prev-next" aria-labelledby="doc-footer-aria-label" data-v-1bcd8184><span class="visually-hidden" id="doc-footer-aria-label" data-v-1bcd8184>Pager</span><div class="pager" data-v-1bcd8184><a class="VPLink link pager-link prev" href="/cli.html" data-v-1bcd8184><!--[--><span class="desc" data-v-1bcd8184>Previous page</span><span class="title" data-v-1bcd8184>CLI / Tasks</span><!--]--></a></div><div class="pager" data-v-1bcd8184><a class="VPLink link pager-link next" href="/unit-tests.html" data-v-1bcd8184><!--[--><span class="desc" data-v-1bcd8184>Next page</span><span class="title" data-v-1bcd8184>Unit Tests</span><!--]--></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
|
|
80
|
-
<script>window.__VP_HASH_MAP__=JSON.parse("{\"adrs.md\":\"YglbWtQe\",\"ai.md\":\"ChLnvDAX\",\"assets.md\":\"BEF6Oz6K\",\"brut-js.md\":\"BMz0X1Rz\",\"business-logic.md\":\"DbuaOYGU\",\"cli.md\":\"DDMar_51\",\"components.md\":\"Ber8UBM0\",\"configuration.md\":\"DrJ6YVoZ\",\"css.md\":\"K5rOCOQY\",\"custom-element-tests.md\":\"DiLe-eFw\",\"database-access.md\":\"Dc8l2Plf\",\"database-schema.md\":\"BJ_JhXmO\",\"deployment.md\":\"CHTx2eTR\",\"dev-environment.md\":\"B1S9p5ZK\",\"dir-structure.md\":\"D1T2kGwj\",\"doc-conventions.md\":\"CDnWaEFg\",\"end-to-end-tests.md\":\"BJJdNDYL\",\"features.md\":\"BDWxnyNO\",\"flash-and-session.md\":\"CUsMxoNl\",\"form-constraints.md\":\"KlfXSKm2\",\"forms.md\":\"RK0zkhm0\",\"getting-started.md\":\"CGJ44juQ\",\"handlers.md\":\"C5tUwmmo\",\"hooks.md\":\"CoiYCKRc\",\"i18n.md\":\"DxkCKhUw\",\"index.md\":\"DnphWyQd\",\"instrumentation.md\":\"BcxjC4jd\",\"javascript.md\":\"D6fxhaQb\",\"jobs.md\":\"Bi3qb3v6\",\"keyword-injection.md\":\"CqLnnzIz\",\"layouts.md\":\"HEbeK7Jr\",\"lsp.md\":\"bE9dW8n9\",\"markdown-examples.md\":\"BPmtHlc-\",\"middleware.md\":\"BhOIsg59\",\"overview.md\":\"BpWAgPFH\",\"pages.md\":\"B3sQXpEd\",\"recipes_alternate-layouts.md\":\"C1QzVkA7\",\"recipes_authentication.md\":\"CyvoIW82\",\"recipes_custom-flash.md\":\"6gFqf2uL\",\"recipes_dev-env-secrets.md\":\"DC_jVY9U\",\"recipes_form-errors.md\":\"B5ptSzMO\",\"recipes_indexed-forms.md\":\"BYYQGW2C\",\"recipes_migrations.md\":\"Cid7-3cu\",\"recipes_text-field-component.md\":\"VhOsCtKI\",\"roadmap.md\":\"DqC1Y7Zt\",\"routes.md\":\"C1dgIBtD\",\"security.md\":\"Jn4SY1uK\",\"seed-data.md\":\"UZW0WxYN\",\"space-time-continuum.md\":\"D9rYGDFH\",\"tutorial.md\":\"BX6f6l00\",\"tutorials_01-intro.md\":\"CzZ3kpF_\",\"tutorials_02-dialog.md\":\"DE5WfCXI\",\"unit-tests.md\":\"vDsdBbO_\",\"why.md\":\"4WpxdrQ2\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Brut RB\",\"description\":\"Documentation for the Brut.RB web framework.\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"search\":{\"provider\":\"local\"},\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Overview\",\"link\":\"/overview\"},{\"text\":\"Brut API\",\"link\":\"/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js/api/index.html\",\"target\":\"_self\"},{\"text\":\"BrutCSS\",\"link\":\"/brut-css/index.html\",\"target\":\"_self\"}],\"outline\":[2,3],\"sidebar\":[{\"text\":\"Overview\",\"collapsed\":false,\"items\":[{\"text\":\"Getting Started\",\"link\":\"/getting-started\"},{\"text\":\"Concepts\",\"link\":\"/overview\"},{\"text\":\"Features\",\"link\":\"/features\"},{\"text\":\"Directory Structure\",\"link\":\"/dir-structure\"},{\"text\":\"Dev Environment\",\"link\":\"/dev-environment\"},{\"text\":\"Tutorial\",\"link\":\"/tutorial\"},{\"text\":\"Documentation Conventions\",\"link\":\"/doc-conventions\"}]},{\"text\":\"Front-End\",\"collapsed\":false,\"items\":[{\"text\":\"Routes\",\"link\":\"/routes\"},{\"text\":\"Pages\",\"link\":\"/pages\"},{\"text\":\"Layouts\",\"link\":\"/layouts\"},{\"text\":\"Forms\",\"link\":\"/forms\"},{\"text\":\"Form Constraints\",\"link\":\"/form-constraints\"},{\"text\":\"Handlers and Actions\",\"link\":\"/handlers\"},{\"text\":\"Components\",\"link\":\"/components\"},{\"text\":\"Flash and Session\",\"link\":\"/flash-and-session\"},{\"text\":\"Space/Time Continuum\",\"link\":\"/space-time-continuum\"},{\"text\":\"JavaScript\",\"link\":\"/javascript\"},{\"text\":\"CSS\",\"link\":\"/css\"},{\"text\":\"Assets\",\"link\":\"/assets\"},{\"text\":\"BrutJS\",\"link\":\"/brut-js\"}]},{\"text\":\"Back-End\",\"collapsed\":false,\"items\":[{\"text\":\"Database Schema\",\"link\":\"/database-schema\"},{\"text\":\"Database Access\",\"link\":\"/database-access\"},{\"text\":\"Seed Data\",\"link\":\"/seed-data\"},{\"text\":\"Jobs\",\"link\":\"/jobs\"},{\"text\":\"Business Logic\",\"link\":\"/business-logic\"}]},{\"text\":\"Framework\",\"collapsed\":false,\"items\":[{\"text\":\"Configuration\",\"link\":\"/configuration\"},{\"text\":\"Keyword Injection\",\"link\":\"/keyword-injection\"},{\"text\":\"I18n\",\"link\":\"/i18n\"},{\"text\":\"CLI / Tasks\",\"link\":\"/cli\"},{\"text\":\"Deployment\",\"link\":\"/deployment\"}]},{\"text\":\"Testing\",\"collapsed\":false,\"items\":[{\"text\":\"Unit Tests\",\"link\":\"/unit-tests\"},{\"text\":\"End-to-End Tests\",\"link\":\"/end-to-end-tests\"},{\"text\":\"Testing Custom Elements\",\"link\":\"/custom-element-tests\"}]},{\"text\":\"Advanced Topics\",\"collapsed\":true,\"items\":[{\"text\":\"Route Hooks\",\"link\":\"/hooks\"},{\"text\":\"Middleware\",\"link\":\"/middleware\"},{\"text\":\"Instrumentation\",\"link\":\"/instrumentation\"},{\"text\":\"Security\",\"link\":\"/security\"},{\"text\":\"LSP Support\",\"link\":\"/lsp\"}]},{\"text\":\"Recipes\",\"collapsed\":true,\"items\":[{\"text\":\"Alternate Layouts\",\"link\":\"/recipes/alternate-layouts\"},{\"text\":\"Authentication\",\"link\":\"/recipes/authentication\"},{\"text\":\"Custom Flash Class\",\"link\":\"/recipes/custom-flash\"},{\"text\":\"Indexed Form Elements\",\"link\":\"/recipes/indexed-forms\"},{\"text\":\"Managing Secrets in the Dev Environment\",\"link\":\"/recipes/dev-env-secrets\"},{\"text\":\"Migration Basics\",\"link\":\"/recipes/migrations\"},{\"text\":\"Styling Form Errors\",\"link\":\"/recipes/form-errors\"},{\"text\":\"Text Field Component\",\"link\":\"/recipes/text-field-component\"}]},{\"text\":\"Meta\",\"collapsed\":false,\"items\":[{\"text\":\"Why?!\",\"link\":\"/why\"},{\"text\":\"ADRs\",\"link\":\"/adrs\"},{\"text\":\"Roadmap to 1.0\",\"link\":\"/roadmap\"},{\"text\":\"AI Declaration\",\"link\":\"/ai\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/thirdtank/brut\"}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
|
|
81
|
-
|
|
82
|
-
</body>
|
|
83
|
-
</html>
|