brut 0.14.0 → 0.16.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +10 -0
- data/Dockerfile.dx +3 -6
- data/Gemfile.lock +1 -1
- data/brut-css/package-lock.json +2 -2
- data/brut-css/package.json +1 -1
- data/brut-js/package-lock.json +2 -2
- data/brut-js/package.json +1 -1
- data/brut-js/specs/Toast.spec.js +34 -0
- data/brut-js/src/I18nTranslation.js +3 -0
- data/brut-js/src/Message.js +9 -3
- data/brut-js/src/RichString.js +4 -1
- data/brut-js/src/Toast.js +102 -0
- data/brut-js/src/index.js +3 -0
- data/brutrb.com/.vitepress/config.mjs +4 -3
- data/brutrb.com/brut-js.md +1 -0
- data/brutrb.com/deployment.md +23 -9
- data/brutrb.com/jobs.md +107 -7
- data/brutrb.com/recipes/dev-env-secrets.md +87 -0
- data/brutrb.com/roadmap.md +2 -7
- data/docs/404.html +3 -3
- data/docs/adrs.html +7 -7
- data/docs/ai.html +7 -7
- data/docs/api/Brut/BackEnd/SeedData.html +1 -1
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
- data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
- data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
- data/docs/api/Brut/BackEnd/Validators.html +1 -1
- data/docs/api/Brut/BackEnd.html +1 -1
- data/docs/api/Brut/CLI/App.html +1 -1
- data/docs/api/Brut/CLI/AppRunner.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB.html +1 -1
- data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
- data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test.html +1 -1
- data/docs/api/Brut/CLI/Apps.html +1 -1
- data/docs/api/Brut/CLI/Command.html +1 -1
- data/docs/api/Brut/CLI/Error.html +1 -1
- data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
- data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
- data/docs/api/Brut/CLI/Executor.html +1 -1
- data/docs/api/Brut/CLI/InvalidOption.html +1 -1
- data/docs/api/Brut/CLI/Options.html +1 -1
- data/docs/api/Brut/CLI/Output.html +1 -1
- data/docs/api/Brut/CLI/SystemExecError.html +1 -1
- data/docs/api/Brut/CLI.html +1 -1
- data/docs/api/Brut/FactoryBot.html +1 -1
- data/docs/api/Brut/Framework/App.html +1 -1
- data/docs/api/Brut/Framework/Config.html +1 -1
- data/docs/api/Brut/Framework/Container.html +1 -1
- data/docs/api/Brut/Framework/Error.html +1 -1
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
- data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
- data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
- data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
- data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
- data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
- data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
- data/docs/api/Brut/Framework/Errors.html +1 -1
- data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
- data/docs/api/Brut/Framework/MCP.html +1 -1
- data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
- data/docs/api/Brut/Framework.html +1 -1
- data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
- data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
- data/docs/api/Brut/FrontEnd/Component.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +37 -18
- data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
- data/docs/api/Brut/FrontEnd/Components.html +1 -1
- data/docs/api/Brut/FrontEnd/CsrfProtector.html +1 -1
- data/docs/api/Brut/FrontEnd/Download.html +1 -1
- data/docs/api/Brut/FrontEnd/Flash.html +1 -1
- data/docs/api/Brut/FrontEnd/Form.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/Button.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/Input.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms.html +1 -1
- data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
- data/docs/api/Brut/FrontEnd/Handler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
- data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
- data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
- data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
- data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
- data/docs/api/Brut/FrontEnd/Layout.html +171 -3
- data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
- data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
- data/docs/api/Brut/FrontEnd/Page.html +1 -1
- data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
- data/docs/api/Brut/FrontEnd/Pages.html +1 -1
- data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
- data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
- data/docs/api/Brut/FrontEnd/Routing.html +1 -1
- data/docs/api/Brut/FrontEnd/Session.html +1 -1
- data/docs/api/Brut/FrontEnd.html +1 -1
- data/docs/api/Brut/I18n/BaseMethods.html +1 -1
- data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
- data/docs/api/Brut/I18n/ForCLI.html +1 -1
- data/docs/api/Brut/I18n/ForHTML.html +1 -1
- data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
- data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
- data/docs/api/Brut/I18n.html +1 -1
- data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
- data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +1 -1
- data/docs/api/Brut/Instrumentation/Methods.html +1 -1
- data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
- data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
- data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
- data/docs/api/Brut/Instrumentation.html +1 -1
- data/docs/api/Brut/RubocopConfig.html +1 -1
- data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
- data/docs/api/Brut/SinatraHelpers.html +1 -1
- data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
- data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
- data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
- data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
- data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
- data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
- data/docs/api/Brut/SpecSupport.html +1 -1
- data/docs/api/Brut.html +1 -1
- data/docs/api/Clock.html +1 -1
- data/docs/api/ModuleName.html +1 -1
- data/docs/api/RichString.html +1 -1
- data/docs/api/SemanticLogger/Appender/Async.html +1 -1
- data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
- data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
- data/docs/api/Sequel/Extensions.html +1 -1
- data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
- data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
- data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
- data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
- data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
- data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
- data/docs/api/Sequel/Plugins/FindBang.html +1 -1
- data/docs/api/Sequel/Plugins.html +1 -1
- data/docs/api/Sequel.html +1 -1
- data/docs/api/_index.html +1 -1
- data/docs/api/file.README.html +1 -1
- data/docs/api/index.html +1 -1
- data/docs/api/method_list.html +157 -141
- data/docs/api/top-level-namespace.html +1 -1
- data/docs/assets/adrs.md.YglbWtQe.js +1 -0
- data/docs/assets/adrs.md.YglbWtQe.lean.js +1 -0
- data/docs/assets/ai.md.ChLnvDAX.js +1 -0
- data/docs/assets/ai.md.ChLnvDAX.lean.js +1 -0
- data/docs/assets/{app.BDtsVxyd.js → app.Dm7v_ouO.js} +1 -1
- data/docs/assets/{assets.md.7C3HWkga.js → assets.md.BEF6Oz6K.js} +2 -2
- data/docs/assets/assets.md.BEF6Oz6K.lean.js +1 -0
- data/docs/assets/{brut-js.md.B4GYxQVw.js → brut-js.md.BMz0X1Rz.js} +2 -2
- data/docs/assets/brut-js.md.BMz0X1Rz.lean.js +1 -0
- data/docs/assets/business-logic.md.DbuaOYGU.js +1 -0
- data/docs/assets/business-logic.md.DbuaOYGU.lean.js +1 -0
- data/docs/assets/chunks/@localSearchIndexroot.BiNFswvo.js +1 -0
- data/docs/assets/chunks/VPLocalSearchBox.DQK6jQou.js +8 -0
- data/docs/assets/chunks/framework.C4nOkCZI.js +18 -0
- data/docs/assets/chunks/{theme.DZKmijwi.js → theme.BuExsdM9.js} +2 -2
- data/docs/assets/{cli.md.CjsktgFz.js → cli.md.DDMar_51.js} +2 -2
- data/docs/assets/cli.md.DDMar_51.lean.js +1 -0
- data/docs/assets/{components.md.rMhQ0WdZ.js → components.md.Dfd3w6UW.js} +5 -5
- data/docs/assets/components.md.Dfd3w6UW.lean.js +1 -0
- data/docs/assets/{configuration.md.BK42Yjp_.js → configuration.md.DTYoV2Ea.js} +2 -2
- data/docs/assets/configuration.md.DTYoV2Ea.lean.js +1 -0
- data/docs/assets/{css.md.CltvJqAa.js → css.md.K5rOCOQY.js} +2 -2
- data/docs/assets/css.md.K5rOCOQY.lean.js +1 -0
- data/docs/assets/{custom-element-tests.md.B_rbta32.js → custom-element-tests.md.DiLe-eFw.js} +2 -2
- data/docs/assets/custom-element-tests.md.DiLe-eFw.lean.js +1 -0
- data/docs/assets/{database-access.md.gnluu54N.js → database-access.md.Dc8l2Plf.js} +2 -2
- data/docs/assets/database-access.md.Dc8l2Plf.lean.js +1 -0
- data/docs/assets/{database-schema.md.LpmBPVEU.js → database-schema.md.BJ_JhXmO.js} +2 -2
- data/docs/assets/database-schema.md.BJ_JhXmO.lean.js +1 -0
- data/docs/assets/{deployment.md.BLseERGV.js → deployment.md.C1u5ep0g.js} +2 -2
- data/docs/assets/deployment.md.C1u5ep0g.lean.js +1 -0
- data/docs/assets/{dev-environment.md.DRH2D2-O.js → dev-environment.md.B1S9p5ZK.js} +2 -2
- data/docs/assets/{dev-environment.md.DRH2D2-O.lean.js → dev-environment.md.B1S9p5ZK.lean.js} +1 -1
- data/docs/assets/{dir-structure.md.CWir1pic.js → dir-structure.md.D1T2kGwj.js} +2 -2
- data/docs/assets/dir-structure.md.D1T2kGwj.lean.js +1 -0
- data/docs/assets/doc-conventions.md.CDnWaEFg.js +1 -0
- data/docs/assets/doc-conventions.md.CDnWaEFg.lean.js +1 -0
- data/docs/assets/{end-to-end-tests.md.DzqRpZ43.js → end-to-end-tests.md.BJJdNDYL.js} +2 -2
- data/docs/assets/end-to-end-tests.md.BJJdNDYL.lean.js +1 -0
- data/docs/assets/{features.md.DPFXsy0z.js → features.md.BDWxnyNO.js} +2 -2
- data/docs/assets/features.md.BDWxnyNO.lean.js +1 -0
- data/docs/assets/{flash-and-session.md.nPvUpnUx.js → flash-and-session.md.CUsMxoNl.js} +2 -2
- data/docs/assets/flash-and-session.md.CUsMxoNl.lean.js +1 -0
- data/docs/assets/{form-constraints.md.KTv5cdR4.js → form-constraints.md.KlfXSKm2.js} +2 -2
- data/docs/assets/form-constraints.md.KlfXSKm2.lean.js +1 -0
- data/docs/assets/{forms.md.v9qIbmUM.js → forms.md.DEkmJUvb.js} +3 -3
- data/docs/assets/forms.md.DEkmJUvb.lean.js +1 -0
- data/docs/assets/{getting-started.md.DTOl4c2g.js → getting-started.md.DO-4eoGW.js} +4 -4
- data/docs/assets/getting-started.md.DO-4eoGW.lean.js +1 -0
- data/docs/assets/{handlers.md.h84MMB1R.js → handlers.md.C5tUwmmo.js} +2 -2
- data/docs/assets/handlers.md.C5tUwmmo.lean.js +1 -0
- data/docs/assets/{hooks.md.Jmb5VOLA.js → hooks.md.CoiYCKRc.js} +2 -2
- data/docs/assets/hooks.md.CoiYCKRc.lean.js +1 -0
- data/docs/assets/{i18n.md.BAm9t9JJ.js → i18n.md.DxkCKhUw.js} +2 -2
- data/docs/assets/i18n.md.DxkCKhUw.lean.js +1 -0
- data/docs/assets/{index.md.Bn9e0sRJ.js → index.md.DnphWyQd.js} +1 -1
- data/docs/assets/{index.md.Bn9e0sRJ.lean.js → index.md.DnphWyQd.lean.js} +1 -1
- data/docs/assets/{instrumentation.md._lNSriEZ.js → instrumentation.md.BcxjC4jd.js} +2 -2
- data/docs/assets/instrumentation.md.BcxjC4jd.lean.js +1 -0
- data/docs/assets/{javascript.md.DzrMxUmI.js → javascript.md.D6fxhaQb.js} +2 -2
- data/docs/assets/javascript.md.D6fxhaQb.lean.js +1 -0
- data/docs/assets/jobs.md.Bc7Y1YpK.js +1 -0
- data/docs/assets/jobs.md.Bc7Y1YpK.lean.js +1 -0
- data/docs/assets/{keyword-injection.md.95Zgh2eN.js → keyword-injection.md.CqLnnzIz.js} +2 -2
- data/docs/assets/keyword-injection.md.CqLnnzIz.lean.js +1 -0
- data/docs/assets/layouts.md.HEbeK7Jr.js +68 -0
- data/docs/assets/layouts.md.HEbeK7Jr.lean.js +1 -0
- data/docs/assets/lsp.md.bE9dW8n9.js +1 -0
- data/docs/assets/lsp.md.bE9dW8n9.lean.js +1 -0
- data/docs/assets/{markdown-examples.md.CCFEQO44.js → markdown-examples.md.BPmtHlc-.js} +2 -2
- data/docs/assets/markdown-examples.md.BPmtHlc-.lean.js +1 -0
- data/docs/assets/{middleware.md.Czz_UlJN.js → middleware.md.BhOIsg59.js} +2 -2
- data/docs/assets/middleware.md.BhOIsg59.lean.js +1 -0
- data/docs/assets/overview.md.BpWAgPFH.js +1 -0
- data/docs/assets/overview.md.BpWAgPFH.lean.js +1 -0
- data/docs/assets/{pages.md.B7Hc-i6H.js → pages.md.B3sQXpEd.js} +2 -2
- data/docs/assets/pages.md.B3sQXpEd.lean.js +1 -0
- data/docs/assets/{recipes_alternate-layouts.md.BwEytl59.js → recipes_alternate-layouts.md.C1QzVkA7.js} +2 -2
- data/docs/assets/recipes_alternate-layouts.md.C1QzVkA7.lean.js +1 -0
- data/docs/assets/{recipes_authentication.md.nwO6F7Ou.js → recipes_authentication.md.CyvoIW82.js} +2 -2
- data/docs/assets/recipes_authentication.md.CyvoIW82.lean.js +1 -0
- data/docs/assets/{recipes_custom-flash.md.CrQbI5eH.js → recipes_custom-flash.md.6gFqf2uL.js} +2 -2
- data/docs/assets/recipes_custom-flash.md.6gFqf2uL.lean.js +1 -0
- data/docs/assets/{recipes_form-errors.md.Bv5RCKqH.js → recipes_form-errors.md.B5ptSzMO.js} +2 -2
- data/docs/assets/recipes_form-errors.md.B5ptSzMO.lean.js +1 -0
- data/docs/assets/{recipes_indexed-forms.md.CstYyOSo.js → recipes_indexed-forms.md.BYYQGW2C.js} +2 -2
- data/docs/assets/recipes_indexed-forms.md.BYYQGW2C.lean.js +1 -0
- data/docs/assets/{recipes_migrations.md.CTcnWDJF.js → recipes_migrations.md.Cid7-3cu.js} +2 -2
- data/docs/assets/recipes_migrations.md.Cid7-3cu.lean.js +1 -0
- data/docs/assets/{recipes_text-field-component.md.H4wLAK0Z.js → recipes_text-field-component.md.VhOsCtKI.js} +2 -2
- data/docs/assets/recipes_text-field-component.md.VhOsCtKI.lean.js +1 -0
- data/docs/assets/roadmap.md.CJsbUmK_.js +1 -0
- data/docs/assets/roadmap.md.CJsbUmK_.lean.js +1 -0
- data/docs/assets/{routes.md.BD6y2i-f.js → routes.md.C1dgIBtD.js} +2 -2
- data/docs/assets/routes.md.C1dgIBtD.lean.js +1 -0
- data/docs/assets/security.md.Jn4SY1uK.js +1 -0
- data/docs/assets/security.md.Jn4SY1uK.lean.js +1 -0
- data/docs/assets/{seed-data.md.BvFZlqIk.js → seed-data.md.UZW0WxYN.js} +2 -2
- data/docs/assets/seed-data.md.UZW0WxYN.lean.js +1 -0
- data/docs/assets/space-time-continuum.md.D9rYGDFH.js +1 -0
- data/docs/assets/space-time-continuum.md.D9rYGDFH.lean.js +1 -0
- data/docs/assets/{tutorial.md.BM40jnoq.js → tutorial.md.BX6f6l00.js} +2 -2
- data/docs/assets/tutorial.md.BX6f6l00.lean.js +1 -0
- data/docs/assets/{tutorials_01-intro.md.B4sUBY3X.js → tutorials_01-intro.md.CzZ3kpF_.js} +2 -2
- data/docs/assets/{tutorials_01-intro.md.B4sUBY3X.lean.js → tutorials_01-intro.md.CzZ3kpF_.lean.js} +1 -1
- data/docs/assets/{tutorials_02-dialog.md.CPNK1SC_.js → tutorials_02-dialog.md.D2vSjDVf.js} +2 -2
- data/docs/assets/{tutorials_02-dialog.md.CPNK1SC_.lean.js → tutorials_02-dialog.md.D2vSjDVf.lean.js} +1 -1
- data/docs/assets/{unit-tests.md.DUGrnLj5.js → unit-tests.md.vDsdBbO_.js} +2 -2
- data/docs/assets/unit-tests.md.vDsdBbO_.lean.js +1 -0
- data/docs/assets/why.md.4WpxdrQ2.js +1 -0
- data/docs/assets/why.md.4WpxdrQ2.lean.js +1 -0
- data/docs/assets.html +7 -7
- data/docs/brut-js/api/AjaxSubmit.html +2 -2
- data/docs/brut-js/api/AjaxSubmit.js.html +2 -2
- data/docs/brut-js/api/Autosubmit.html +2 -2
- data/docs/brut-js/api/Autosubmit.js.html +2 -2
- data/docs/brut-js/api/BaseCustomElement.html +2 -2
- data/docs/brut-js/api/BaseCustomElement.js.html +2 -2
- data/docs/brut-js/api/BrutCustomElements.html +3 -3
- data/docs/brut-js/api/BufferedLogger.html +2 -2
- data/docs/brut-js/api/ConfirmSubmit.html +2 -2
- data/docs/brut-js/api/ConfirmSubmit.js.html +2 -2
- data/docs/brut-js/api/ConfirmationDialog.html +2 -2
- data/docs/brut-js/api/ConfirmationDialog.js.html +2 -2
- data/docs/brut-js/api/ConstraintViolationMessage.html +2 -2
- data/docs/brut-js/api/ConstraintViolationMessage.js.html +2 -2
- data/docs/brut-js/api/ConstraintViolationMessages.html +2 -2
- data/docs/brut-js/api/ConstraintViolationMessages.js.html +2 -2
- data/docs/brut-js/api/CopyToClipboard.html +2 -2
- data/docs/brut-js/api/CopyToClipboard.js.html +2 -2
- data/docs/brut-js/api/Form.html +2 -2
- data/docs/brut-js/api/Form.js.html +2 -2
- data/docs/brut-js/api/I18nTranslation.html +2 -2
- data/docs/brut-js/api/I18nTranslation.js.html +5 -2
- data/docs/brut-js/api/LocaleDetection.html +2 -2
- data/docs/brut-js/api/LocaleDetection.js.html +2 -2
- data/docs/brut-js/api/Logger.html +2 -2
- data/docs/brut-js/api/Logger.js.html +2 -2
- data/docs/brut-js/api/Message.html +2 -2
- data/docs/brut-js/api/Message.js.html +11 -5
- data/docs/brut-js/api/PrefixedLogger.html +2 -2
- data/docs/brut-js/api/RichString.html +9 -9
- data/docs/brut-js/api/RichString.js.html +6 -3
- data/docs/brut-js/api/Tabs.html +2 -2
- data/docs/brut-js/api/Tabs.js.html +2 -2
- data/docs/brut-js/api/Toast.html +270 -0
- data/docs/brut-js/api/Toast.js.html +153 -0
- data/docs/brut-js/api/Tracing.html +2 -2
- data/docs/brut-js/api/Tracing.js.html +2 -2
- data/docs/brut-js/api/external-CustomElementRegistry.html +3 -3
- data/docs/brut-js/api/external-Performance.html +3 -3
- data/docs/brut-js/api/external-Promise.html +3 -3
- data/docs/brut-js/api/external-ValidityState.html +3 -3
- data/docs/brut-js/api/external-Window.html +4 -4
- data/docs/brut-js/api/external-fetch.html +3 -3
- data/docs/brut-js/api/global.html +3 -3
- data/docs/brut-js/api/index.html +2 -2
- data/docs/brut-js/api/index.js.html +5 -2
- data/docs/brut-js/api/module-testing.html +2 -2
- data/docs/brut-js/api/testing.AssetMetadata.html +2 -2
- data/docs/brut-js/api/testing.AssetMetadataLoader.html +2 -2
- data/docs/brut-js/api/testing.CustomElementTest.html +2 -2
- data/docs/brut-js/api/testing.DOMCreator.html +2 -2
- data/docs/brut-js/api/testing_AssetMetadata.js.html +2 -2
- data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +2 -2
- data/docs/brut-js/api/testing_CustomElementTest.js.html +2 -2
- data/docs/brut-js/api/testing_DOMCreator.js.html +2 -2
- data/docs/brut-js/api/testing_index.js.html +2 -2
- data/docs/brut-js.html +8 -8
- data/docs/business-logic.html +7 -7
- data/docs/cli.html +7 -7
- data/docs/components.html +10 -10
- data/docs/configuration.html +7 -7
- data/docs/css.html +7 -7
- data/docs/custom-element-tests.html +7 -7
- data/docs/database-access.html +7 -7
- data/docs/database-schema.html +7 -7
- data/docs/deployment.html +7 -7
- data/docs/dev-environment.html +7 -7
- data/docs/dir-structure.html +7 -7
- data/docs/doc-conventions.html +7 -7
- data/docs/end-to-end-tests.html +7 -7
- data/docs/features.html +7 -7
- data/docs/flash-and-session.html +7 -7
- data/docs/form-constraints.html +7 -7
- data/docs/forms.html +8 -8
- data/docs/getting-started.html +9 -9
- data/docs/handlers.html +7 -7
- data/docs/hashmap.json +1 -1
- data/docs/hooks.html +7 -7
- data/docs/i18n.html +7 -7
- data/docs/index.html +6 -6
- data/docs/instrumentation.html +7 -7
- data/docs/javascript.html +7 -7
- data/docs/jobs.html +7 -7
- data/docs/keyword-injection.html +7 -7
- data/docs/layouts.html +42 -12
- data/docs/lsp.html +7 -7
- data/docs/markdown-examples.html +7 -7
- data/docs/middleware.html +7 -7
- data/docs/overview.html +7 -7
- data/docs/pages.html +7 -7
- data/docs/recipes/alternate-layouts.html +8 -8
- data/docs/recipes/authentication.html +7 -7
- data/docs/recipes/custom-flash.html +8 -8
- data/docs/recipes/form-errors.html +7 -7
- data/docs/recipes/indexed-forms.html +7 -7
- data/docs/recipes/migrations.html +7 -7
- data/docs/recipes/text-field-component.html +7 -7
- data/docs/roadmap.html +7 -7
- data/docs/routes.html +7 -7
- data/docs/security.html +7 -7
- data/docs/seed-data.html +7 -7
- data/docs/space-time-continuum.html +7 -7
- data/docs/tutorial.html +7 -7
- data/docs/tutorials/01-intro.html +7 -7
- data/docs/tutorials/02-dialog.html +7 -7
- data/docs/unit-tests.html +7 -7
- data/docs/why.html +7 -7
- data/dx/bash_customizations +3 -4
- data/dx/build.pre +15 -0
- data/lib/brut/cli/apps/heroku_container_based_deploy.rb +41 -15
- data/lib/brut/cli/apps/test.rb +2 -0
- data/lib/brut/framework/container.rb +6 -4
- data/lib/brut/framework/mcp.rb +1 -1
- data/lib/brut/version.rb +1 -1
- data/mkbrut/Gemfile.lock +2 -2
- data/mkbrut/lib/mkbrut/add_segment.rb +38 -0
- data/mkbrut/lib/mkbrut/add_segment_options.rb +22 -0
- data/mkbrut/lib/mkbrut/app.rb +7 -2
- data/mkbrut/lib/mkbrut/base.rb +1 -0
- data/mkbrut/lib/mkbrut/cli.rb +90 -8
- data/mkbrut/lib/mkbrut/ops/insert_code_in_method.rb +19 -7
- data/mkbrut/lib/mkbrut/ops/insert_into_file.rb +36 -0
- data/mkbrut/lib/mkbrut/ops/prism_parsing_op.rb +14 -2
- data/mkbrut/lib/mkbrut/ops.rb +1 -0
- data/mkbrut/lib/mkbrut/segments/bare_bones.rb +8 -0
- data/mkbrut/lib/mkbrut/segments/demo.rb +8 -0
- data/mkbrut/lib/mkbrut/segments/heroku.rb +23 -3
- data/mkbrut/lib/mkbrut/segments/sidekiq.rb +136 -1
- data/mkbrut/lib/mkbrut/segments.rb +1 -1
- data/mkbrut/lib/mkbrut/version.rb +1 -1
- data/mkbrut/lib/mkbrut.rb +2 -0
- data/mkbrut/templates/Base/.gitignore +3 -0
- data/mkbrut/templates/Base/Dockerfile.dx +18 -21
- data/mkbrut/templates/Base/Gemfile.erb +1 -1
- data/mkbrut/templates/Base/bin/run +107 -67
- data/mkbrut/templates/Base/bin/run.run +4 -0
- data/mkbrut/templates/Base/bin/setup +32 -1
- data/mkbrut/templates/Base/dx/bash_customizations +0 -4
- data/mkbrut/templates/Base/package.json.erb +1 -1
- data/mkbrut/templates/segments/Heroku/deploy/Dockerfile +15 -15
- data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +5 -4
- data/mkbrut/templates/segments/Sidekiq/app/boot_sidekiq.rb +2 -0
- data/mkbrut/templates/segments/Sidekiq/app/config/sidekiq.yml +4 -0
- data/mkbrut/templates/segments/Sidekiq/app/src/back_end/jobs/app_job.rb +3 -0
- data/mkbrut/templates/segments/Sidekiq/app/src/back_end/jobs/example_job.rb +12 -0
- data/mkbrut/templates/segments/Sidekiq/app/src/back_end/segments/sidekiq_segment.rb +56 -0
- data/mkbrut/templates/segments/Sidekiq/bin/run.sidekiq +4 -0
- data/mkbrut/templates/segments/Sidekiq/specs/back_end/jobs/example_job.spec.rb +5 -0
- data/mkbrut/templates/segments/Sidekiq/specs/integration/sidekiq_works.spec.rb +38 -0
- metadata +131 -116
- data/docs/assets/adrs.md.BxjHi9-8.js +0 -1
- data/docs/assets/adrs.md.BxjHi9-8.lean.js +0 -1
- data/docs/assets/ai.md.Cy9GWnER.js +0 -1
- data/docs/assets/ai.md.Cy9GWnER.lean.js +0 -1
- data/docs/assets/assets.md.7C3HWkga.lean.js +0 -1
- data/docs/assets/brut-js.md.B4GYxQVw.lean.js +0 -1
- data/docs/assets/business-logic.md.BY4hGy0m.js +0 -1
- data/docs/assets/business-logic.md.BY4hGy0m.lean.js +0 -1
- data/docs/assets/chunks/@localSearchIndexroot.BWVzhs5N.js +0 -1
- data/docs/assets/chunks/VPLocalSearchBox.DCJk5nAW.js +0 -8
- data/docs/assets/chunks/framework.1L-BeKqY.js +0 -18
- data/docs/assets/cli.md.CjsktgFz.lean.js +0 -1
- data/docs/assets/components.md.rMhQ0WdZ.lean.js +0 -1
- data/docs/assets/configuration.md.BK42Yjp_.lean.js +0 -1
- data/docs/assets/css.md.CltvJqAa.lean.js +0 -1
- data/docs/assets/custom-element-tests.md.B_rbta32.lean.js +0 -1
- data/docs/assets/database-access.md.gnluu54N.lean.js +0 -1
- data/docs/assets/database-schema.md.LpmBPVEU.lean.js +0 -1
- data/docs/assets/deployment.md.BLseERGV.lean.js +0 -1
- data/docs/assets/dir-structure.md.CWir1pic.lean.js +0 -1
- data/docs/assets/doc-conventions.md.DOkAuXlt.js +0 -1
- data/docs/assets/doc-conventions.md.DOkAuXlt.lean.js +0 -1
- data/docs/assets/end-to-end-tests.md.DzqRpZ43.lean.js +0 -1
- data/docs/assets/features.md.DPFXsy0z.lean.js +0 -1
- data/docs/assets/flash-and-session.md.nPvUpnUx.lean.js +0 -1
- data/docs/assets/form-constraints.md.KTv5cdR4.lean.js +0 -1
- data/docs/assets/forms.md.v9qIbmUM.lean.js +0 -1
- data/docs/assets/getting-started.md.DTOl4c2g.lean.js +0 -1
- data/docs/assets/handlers.md.h84MMB1R.lean.js +0 -1
- data/docs/assets/hooks.md.Jmb5VOLA.lean.js +0 -1
- data/docs/assets/i18n.md.BAm9t9JJ.lean.js +0 -1
- data/docs/assets/instrumentation.md._lNSriEZ.lean.js +0 -1
- data/docs/assets/javascript.md.DzrMxUmI.lean.js +0 -1
- data/docs/assets/jobs.md.S-2amAYp.js +0 -1
- data/docs/assets/jobs.md.S-2amAYp.lean.js +0 -1
- data/docs/assets/keyword-injection.md.95Zgh2eN.lean.js +0 -1
- data/docs/assets/layouts.md.CVGl9xIO.js +0 -38
- data/docs/assets/layouts.md.CVGl9xIO.lean.js +0 -1
- data/docs/assets/lsp.md.Dn1rIiW0.js +0 -1
- data/docs/assets/lsp.md.Dn1rIiW0.lean.js +0 -1
- data/docs/assets/markdown-examples.md.CCFEQO44.lean.js +0 -1
- data/docs/assets/middleware.md.Czz_UlJN.lean.js +0 -1
- data/docs/assets/overview.md.DlKiRRG_.js +0 -1
- data/docs/assets/overview.md.DlKiRRG_.lean.js +0 -1
- data/docs/assets/pages.md.B7Hc-i6H.lean.js +0 -1
- data/docs/assets/recipes_alternate-layouts.md.BwEytl59.lean.js +0 -1
- data/docs/assets/recipes_authentication.md.nwO6F7Ou.lean.js +0 -1
- data/docs/assets/recipes_blank-layouts.md.fyAUJyJR.js +0 -15
- data/docs/assets/recipes_blank-layouts.md.fyAUJyJR.lean.js +0 -1
- data/docs/assets/recipes_custom-flash.md.CrQbI5eH.lean.js +0 -1
- data/docs/assets/recipes_form-errors.md.Bv5RCKqH.lean.js +0 -1
- data/docs/assets/recipes_indexed-forms.md.CstYyOSo.lean.js +0 -1
- data/docs/assets/recipes_migrations.md.CTcnWDJF.lean.js +0 -1
- data/docs/assets/recipes_text-field-component.md.H4wLAK0Z.lean.js +0 -1
- data/docs/assets/roadmap.md.C6PRi0DX.js +0 -1
- data/docs/assets/roadmap.md.C6PRi0DX.lean.js +0 -1
- data/docs/assets/routes.md.BD6y2i-f.lean.js +0 -1
- data/docs/assets/security.md.C0G_AZR-.js +0 -1
- data/docs/assets/security.md.C0G_AZR-.lean.js +0 -1
- data/docs/assets/seed-data.md.BvFZlqIk.lean.js +0 -1
- data/docs/assets/space-time-continuum.md.xl44xDos.js +0 -1
- data/docs/assets/space-time-continuum.md.xl44xDos.lean.js +0 -1
- data/docs/assets/tutorial.md.BM40jnoq.lean.js +0 -1
- data/docs/assets/unit-tests.md.DUGrnLj5.lean.js +0 -1
- data/docs/assets/why.md.C-hk5xgJ.js +0 -1
- data/docs/assets/why.md.C-hk5xgJ.lean.js +0 -1
- data/docs/recipes/blank-layouts.html +0 -43
data/mkbrut/lib/mkbrut/cli.rb
CHANGED
@@ -11,14 +11,56 @@ module MKBrut
|
|
11
11
|
|
12
12
|
def run
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
command = if @args.first == "add-segment"
|
15
|
+
@args.shift
|
16
|
+
:add_segment
|
17
|
+
elsif @args.first == "new-app" || @args.first == "new"
|
18
|
+
@args.shift
|
19
|
+
:new_app
|
20
|
+
else
|
21
|
+
:new_app
|
22
|
+
end
|
23
|
+
|
24
|
+
if command == :new_app
|
25
|
+
app_options = parse_options(@args, MKBrut::Versions.new)
|
26
|
+
new_app = MKBrut::App.new(
|
27
|
+
current_dir: Pathname.pwd.expand_path,
|
28
|
+
app_options:,
|
29
|
+
out: PrefixedIO.new(@out, "mkbrut"),
|
30
|
+
err: @err
|
31
|
+
)
|
32
|
+
new_app.create!
|
33
|
+
elsif command == :add_segment
|
34
|
+
add_segment_options = parse_add_segment_options(@args, MKBrut::Versions.new)
|
35
|
+
add_segment = MKBrut::AddSegment.new(
|
36
|
+
current_dir: add_segment_options.project_root,
|
37
|
+
add_segment_options:,
|
38
|
+
out: PrefixedIO.new(@out, "mkbrut"),
|
39
|
+
err: @err
|
40
|
+
)
|
41
|
+
add_segment.add!
|
42
|
+
@out.puts ""
|
43
|
+
@out.puts "Sidekiq has now been set up for your app. The configuration used is"
|
44
|
+
@out.puts "a basic one, suitable for getting started, however you know own this"
|
45
|
+
@out.puts "configuration. Most of it is in these files:"
|
46
|
+
@out.puts ""
|
47
|
+
@out.puts " app/config/sidekiq.yml"
|
48
|
+
@out.puts " app/src/back_end/segments/sidekiq_segment.rb"
|
49
|
+
@out.puts ""
|
50
|
+
@out.puts "You are encouraged to verify everything is set up as follows:"
|
51
|
+
@out.puts ""
|
52
|
+
@out.puts "1. Quit dx/start, and start it back up - this will downloaded and set up ValKey/Redis"
|
53
|
+
@out.puts "2. Re-run bin/setup. This will install needed gems and create binstubs"
|
54
|
+
@out.puts "3. Run the example integration test:"
|
55
|
+
@out.puts ""
|
56
|
+
@out.puts " bin/test e2e specs/integration/sidekiq_works.spec.rb"
|
57
|
+
@out.puts ""
|
58
|
+
@out.puts " This will use the actual Sidekiq server, so if it passes, you should"
|
59
|
+
@out.puts " all set and can start creating jobs"
|
60
|
+
@out.puts ""
|
61
|
+
else
|
62
|
+
raise "Unknown command #{command}"
|
63
|
+
end
|
22
64
|
0
|
23
65
|
rescue => e
|
24
66
|
@err.puts "Error: #{e.message}"
|
@@ -103,5 +145,45 @@ module MKBrut
|
|
103
145
|
MKBrut::AppOptions.new(**options, versions:)
|
104
146
|
end
|
105
147
|
|
148
|
+
def parse_add_segment_options(args, versions)
|
149
|
+
options = {}
|
150
|
+
@option_parser = OptionParser.new do |opts|
|
151
|
+
opts.banner = [
|
152
|
+
"Usage: mkbrut add-segment [options] segment-name",
|
153
|
+
"",
|
154
|
+
" Adds a segment to an existing Brut-powered app",
|
155
|
+
"",
|
156
|
+
"VERSION",
|
157
|
+
"",
|
158
|
+
" #{MKBrut::VERSION}",
|
159
|
+
"",
|
160
|
+
"OPTIONS",
|
161
|
+
"",
|
162
|
+
].join("\n")
|
163
|
+
|
164
|
+
opts.on("-r PROJECT_ROOT_DIR", "--project-root=PROJECT_ROOT_DIR", "Path to the root of the existing Brut app (defaults to current directory)")
|
165
|
+
opts.on("--dry-run", "Only show what would happen, don't actually do anything")
|
166
|
+
opts.on("--[no-]demo", "Include, or not, additional files that demonstrate Brut's features (default is true for now")
|
167
|
+
opts.on("-h", "--help", "Show this help message") do
|
168
|
+
@out.puts @option_parser
|
169
|
+
@out.puts
|
170
|
+
@out.puts "ARGUMENTS"
|
171
|
+
@out.puts
|
172
|
+
@out.puts " segment-name - name of the segment to add. Known values:"
|
173
|
+
@out.puts " sidekiq - add support for Sidekiq"
|
174
|
+
@out.puts " heroku - add support for Heroku container-based deployment"
|
175
|
+
@out.puts
|
176
|
+
@out.puts "ENVIRONMENT VARIABLES"
|
177
|
+
@out.puts
|
178
|
+
@out.puts " BRUT_CLI_RAISE_ON_ERROR - if set to 'true', any error will raise an exception instead of printing to stderr"
|
179
|
+
@out.puts
|
180
|
+
exit
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
@option_parser.parse!(args, into: options)
|
185
|
+
options[:project_root] = Pathname.new(options[:'project-root'] || ".").expand_path
|
186
|
+
MKBrut::AddSegmentOptions.new(segment_name: @args[0], **options, versions:)
|
187
|
+
end
|
106
188
|
end
|
107
189
|
end
|
@@ -1,14 +1,25 @@
|
|
1
1
|
class MKBrut::Ops::InsertCodeInMethod < MKBrut::Ops::PrismParsingOp
|
2
|
-
def initialize(file:,
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
def initialize(file:,
|
3
|
+
class_name:,
|
4
|
+
method_name:,
|
5
|
+
class_method: false,
|
6
|
+
code:,
|
7
|
+
ignore_if_file_not_found: false,
|
8
|
+
where: :end)
|
9
|
+
@file = file
|
10
|
+
@class_name = class_name
|
11
|
+
@method_name = method_name.to_sym
|
12
|
+
@class_method = class_method
|
13
|
+
@code = code
|
14
|
+
@ignore_if_file_not_found = ignore_if_file_not_found
|
15
|
+
@where = where
|
8
16
|
end
|
9
17
|
|
10
18
|
def call
|
11
|
-
|
19
|
+
if !@file.exist? && @ignore_if_file_not_found
|
20
|
+
return
|
21
|
+
end
|
22
|
+
method_node = find_method(class_name: @class_name, method_name: @method_name, class_method: @class_method)
|
12
23
|
|
13
24
|
insertion_point = if @where == :start
|
14
25
|
insertion_point_for_code_at_start_of_method(method_node: method_node)
|
@@ -42,6 +53,7 @@ private
|
|
42
53
|
}.join("\n") + post_indent
|
43
54
|
end
|
44
55
|
|
56
|
+
# XXX: This does not work with non-ASCII strings
|
45
57
|
def insertion_point_for_code_at_end_of_method(method_node:)
|
46
58
|
line_number_of_method_end = method_node.location.end_line - 1
|
47
59
|
length_of_method_end = @source.lines[line_number_of_method_end].length
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class MKBrut::Ops::InsertIntoFile < MKBrut::Ops::BaseOp
|
2
|
+
def initialize(file:, content:, before_line:)
|
3
|
+
@file = file
|
4
|
+
@content = content
|
5
|
+
@before_line = before_line
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
|
10
|
+
contents = File.read(@file)
|
11
|
+
locations = []
|
12
|
+
new_contents = []
|
13
|
+
contents.split("\n").each_with_index do |line, index|
|
14
|
+
if line == @before_line
|
15
|
+
new_contents << @content
|
16
|
+
locations << (index + 1)
|
17
|
+
end
|
18
|
+
new_contents << line
|
19
|
+
end
|
20
|
+
if locations.empty?
|
21
|
+
raise "Did not find line '#{@before_line}' exactly in #{@file}"
|
22
|
+
end
|
23
|
+
if locations.size > 1
|
24
|
+
raise "Found exact line '#{@before_line}' #{locations.size} times in #{@file}, should be exactly once so we know where to insert"
|
25
|
+
end
|
26
|
+
|
27
|
+
if dry_run?
|
28
|
+
puts "Would add to #{@file}, resulting in this content:\n#{new_contents.join("\n")}\n"
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
File.open(@file, "w") do |file|
|
33
|
+
file.puts new_contents.join("\n")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -56,10 +56,22 @@ private
|
|
56
56
|
class_node
|
57
57
|
end
|
58
58
|
|
59
|
-
def find_method(class_name:, method_name:)
|
59
|
+
def find_method(class_name:, method_name:, class_method: false)
|
60
60
|
class_node = find_class(class_name:)
|
61
61
|
method_node = class_node.body.body.detect { |node|
|
62
|
-
|
62
|
+
|
63
|
+
is_def = node.is_a?(Prism::DefNode)
|
64
|
+
method_name_match = node.name == @method_name
|
65
|
+
receiver_match = if class_method
|
66
|
+
node.receiver.is_a?(Prism::SelfNode)
|
67
|
+
else
|
68
|
+
node.receiver.nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
if is_def && method_name_match && !receiver_match
|
72
|
+
puts "Found method '#{method_name}' in class '#{class_name}' but it was a #{node.receiver.class} receiver"
|
73
|
+
end
|
74
|
+
is_def && method_name_match && receiver_match
|
63
75
|
}
|
64
76
|
|
65
77
|
if !method_node
|
data/mkbrut/lib/mkbrut/ops.rb
CHANGED
@@ -8,6 +8,7 @@ module MKBrut
|
|
8
8
|
autoload :InsertRoute, "mkbrut/ops/insert_route"
|
9
9
|
autoload :InsertCodeInMethod, "mkbrut/ops/insert_code_in_method"
|
10
10
|
autoload :AppendToFile, "mkbrut/ops/append_to_file"
|
11
|
+
autoload :InsertIntoFile, "mkbrut/ops/insert_into_file"
|
11
12
|
autoload :PrismParsingOp, "mkbrut/ops/prism_parsing_op"
|
12
13
|
autoload :AddI18nMessage, "mkbrut/ops/add_i18n_message"
|
13
14
|
autoload :AddCSSImport, "mkbrut/ops/add_css_import"
|
@@ -9,6 +9,14 @@ class MKBrut::Segments::BareBones < MKBrut::Base
|
|
9
9
|
@erb_binding = ErbBindingDelegate.new(app_options)
|
10
10
|
end
|
11
11
|
|
12
|
+
def <=>(other)
|
13
|
+
if self.class == other.class
|
14
|
+
0
|
15
|
+
else
|
16
|
+
-1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
12
20
|
def add!
|
13
21
|
|
14
22
|
operations = copy_files(@templates_dir, @project_root) +
|
@@ -10,6 +10,14 @@ class MKBrut::Segments::Demo < MKBrut::Base
|
|
10
10
|
@erb_binding = ErbBindingDelegate.new(app_options)
|
11
11
|
end
|
12
12
|
|
13
|
+
def <=>(other)
|
14
|
+
if self.class == other.class
|
15
|
+
0
|
16
|
+
else
|
17
|
+
1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
def add!
|
14
22
|
operations = copy_files(@templates_dir, @project_root) +
|
15
23
|
other_operations(@project_root)
|
@@ -1,10 +1,9 @@
|
|
1
1
|
class MKBrut::Segments::Heroku < MKBrut::Base
|
2
2
|
def self.friendly_name = "Heroku-based Deployment"
|
3
3
|
|
4
|
-
def initialize(
|
5
|
-
@project_root =
|
4
|
+
def initialize(project_root:, templates_dir:)
|
5
|
+
@project_root = project_root
|
6
6
|
@templates_dir = templates_dir / "segments" / "Heroku"
|
7
|
-
@erb_binding = MKBrut::ErbBindingDelegate.new(app_options)
|
8
7
|
end
|
9
8
|
|
10
9
|
def add!
|
@@ -16,8 +15,29 @@ class MKBrut::Segments::Heroku < MKBrut::Base
|
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
18
|
+
def <=>(other)
|
19
|
+
if self.class == other.class
|
20
|
+
0
|
21
|
+
elsif other.class == MKBrut::Segments::Sidekiq
|
22
|
+
# If both herkou and sidekiq segments are activated, we want to do heroku first,
|
23
|
+
# since Sidekiq will need to modify it.
|
24
|
+
-1
|
25
|
+
else
|
26
|
+
1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
19
30
|
def other_operations
|
20
31
|
[
|
32
|
+
MKBrut::Ops::InsertIntoFile.new(
|
33
|
+
file: @project_root / "Dockerfile.dx",
|
34
|
+
contents: %{
|
35
|
+
# Installs the Heroku CLI, per Heroku docs at
|
36
|
+
# https://devcenter.heroku.com/articles/heroku-cli#install-with-ubuntu-debian-apt-get
|
37
|
+
RUN curl https://cli-assets.heroku.com/install-ubuntu.sh | sh
|
38
|
+
},
|
39
|
+
before_line: "# Up to now, RUN directives ran as the root user. When using this container"
|
40
|
+
),
|
21
41
|
MKBrut::Ops::AppendToFile.new(
|
22
42
|
file: @project_root / ".gitignore",
|
23
43
|
content: %{
|
@@ -1,3 +1,138 @@
|
|
1
1
|
# Adds Sidekiq to a Brut app
|
2
|
-
class MKBrut::Segments::Sidekiq
|
2
|
+
class MKBrut::Segments::Sidekiq < MKBrut::Base
|
3
|
+
def self.friendly_name = "Use Sidekiq to Run Background Jobs"
|
4
|
+
def initialize(project_root:, templates_dir:)
|
5
|
+
@project_root = project_root
|
6
|
+
@templates_dir = templates_dir / "segments" / "Sidekiq"
|
7
|
+
end
|
8
|
+
|
9
|
+
def add!
|
10
|
+
operations = copy_files(@templates_dir, @project_root) +
|
11
|
+
other_operations
|
12
|
+
|
13
|
+
operations.each do |operation|
|
14
|
+
operation.call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other)
|
19
|
+
if self.class == other.class
|
20
|
+
0
|
21
|
+
elsif other.class == MKBrut::Segments::Heroku
|
22
|
+
# If both herkou and sidekiq segments are activated, we want to do heroku first,
|
23
|
+
# since Sidekiq will need to modify it.
|
24
|
+
1
|
25
|
+
else
|
26
|
+
-1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def other_operations
|
31
|
+
[
|
32
|
+
MKBrut::Ops::AppendToFile.new(
|
33
|
+
file: @project_root / "docker-compose.dx.yml",
|
34
|
+
content: %{
|
35
|
+
redis:
|
36
|
+
# Change the value to what you are using in production.
|
37
|
+
# If you are using actual Redis, change that here.
|
38
|
+
image: valkey/valkey:8.1
|
39
|
+
},
|
40
|
+
),
|
41
|
+
MKBrut::Ops::AppendToFile.new(
|
42
|
+
file: @project_root / "Procfile.development",
|
43
|
+
content: "sidekiq: bin/run sidekiq\n"
|
44
|
+
),
|
45
|
+
MKBrut::Ops::AppendToFile.new(
|
46
|
+
file: @project_root / "Procfile.test",
|
47
|
+
content: "sidekiq: bin/run sidekiq\n"
|
48
|
+
),
|
49
|
+
MKBrut::Ops::AppendToFile.new(
|
50
|
+
file: @project_root / "Gemfile",
|
51
|
+
content: "# Sidekiq is used for background jobs\ngem \"sidekiq\"\n"
|
52
|
+
),
|
53
|
+
MKBrut::Ops::AppendToFile.new(
|
54
|
+
file: @project_root / ".env.development",
|
55
|
+
content: %{
|
56
|
+
# URL of the Redis/ValKey to use for Sidekiq
|
57
|
+
SIDEKIQ_REDIS_URL=redis://redis:6379/1
|
58
|
+
# Tells Sidekiq which ENV var to use for the Redis/ValKey URL
|
59
|
+
REDIS_PROVIDER=SIDEKIQ_REDIS_URL
|
60
|
+
# Username for basic auth into the Sidekiq Admin UI
|
61
|
+
SIDEKIQ_BASIC_AUTH_USER=sidekiq
|
62
|
+
# Passsword for basic auth into the Sidekiq Admin UI
|
63
|
+
SIDEKIQ_BASIC_AUTH_PASSWORD=password
|
64
|
+
}
|
65
|
+
),
|
66
|
+
MKBrut::Ops::AppendToFile.new(
|
67
|
+
file: @project_root / ".env.test",
|
68
|
+
content: %{
|
69
|
+
SIDEKIQ_REDIS_URL=redis://redis:6379/2
|
70
|
+
REDIS_PROVIDER=SIDEKIQ_REDIS_URL
|
71
|
+
SIDEKIQ_BASIC_AUTH_USER=sidekiq-test
|
72
|
+
SIDEKIQ_BASIC_AUTH_PASSWORD=password
|
73
|
+
}
|
74
|
+
),
|
75
|
+
MKBrut::Ops::InsertIntoFile.new(
|
76
|
+
file: @project_root / "bin" / "test-server",
|
77
|
+
before_line: "wait",
|
78
|
+
content: "bin/run sidekiq &\n",
|
79
|
+
),
|
80
|
+
MKBrut::Ops::InsertIntoFile.new(
|
81
|
+
file: @project_root / "bin" / "setup",
|
82
|
+
before_line: " log \"Installing rspec binstubs\"",
|
83
|
+
content: %{log "Installing sidekiq binstubs"
|
84
|
+
system! "bundle binstub sidekiq"
|
85
|
+
}
|
86
|
+
),
|
87
|
+
MKBrut::Ops::InsertIntoFile.new(
|
88
|
+
file: @project_root / "specs" / "spec_helper.rb",
|
89
|
+
before_line: "require \"brut/spec_support\"",
|
90
|
+
content: "require \"sidekiq/testing\""
|
91
|
+
),
|
92
|
+
MKBrut::Ops::InsertIntoFile.new(
|
93
|
+
file: @project_root / "config.ru",
|
94
|
+
before_line: "bootstrap = Bootstrap.new.bootstrap!",
|
95
|
+
content: "require \"sidekiq/web\"\n"
|
96
|
+
),
|
97
|
+
MKBrut::Ops::InsertIntoFile.new(
|
98
|
+
file: @project_root / "config.ru",
|
99
|
+
before_line: " run bootstrap.rack_app",
|
100
|
+
content: %{
|
101
|
+
map "/sidekiq" do
|
102
|
+
use Rack::Auth::Basic, "Sidekiq" do |username, password|
|
103
|
+
[username, password] == [ENV.fetch("SIDEKIQ_BASIC_AUTH_USER"), ENV.fetch("SIDEKIQ_BASIC_AUTH_PASSWORD")]
|
104
|
+
end
|
105
|
+
run Sidekiq::Web.new
|
106
|
+
end
|
107
|
+
}
|
108
|
+
),
|
109
|
+
MKBrut::Ops::InsertCodeInMethod.new(
|
110
|
+
file: @project_root / "app" / "src" / "app.rb",
|
111
|
+
class_name: "App",
|
112
|
+
method_name: "initialize",
|
113
|
+
code: "@sidekiq_segment = SidekiqSegment.new",
|
114
|
+
),
|
115
|
+
MKBrut::Ops::InsertCodeInMethod.new(
|
116
|
+
file: @project_root / "app" / "src" / "app.rb",
|
117
|
+
class_name: "App",
|
118
|
+
method_name: "boot!",
|
119
|
+
code: "@sidekiq_segment.boot!"
|
120
|
+
),
|
121
|
+
MKBrut::Ops::InsertCodeInMethod.new(
|
122
|
+
file: project_root / "deploy" / "heroku_config.rb",
|
123
|
+
class_name: "HerokuConfig",
|
124
|
+
method_name: "additional_images",
|
125
|
+
class_method: true,
|
126
|
+
ignore_if_file_not_found: true,
|
127
|
+
code: %{
|
128
|
+
{
|
129
|
+
"sidekiq" => {
|
130
|
+
cmd: "bin/run sidekiq",
|
131
|
+
}
|
132
|
+
}
|
133
|
+
},
|
134
|
+
where: :end
|
135
|
+
),
|
136
|
+
]
|
137
|
+
end
|
3
138
|
end
|
@@ -2,7 +2,7 @@ module MKBrut
|
|
2
2
|
module Segments
|
3
3
|
autoload :BareBones, "mkbrut/segments/bare_bones"
|
4
4
|
autoload :Demo, "mkbrut/segments/demo"
|
5
|
-
autoload :Sidekiq, "mkbrut/segments/
|
5
|
+
autoload :Sidekiq, "mkbrut/segments/sidekiq"
|
6
6
|
autoload :Heroku, "mkbrut/segments/heroku"
|
7
7
|
end
|
8
8
|
end
|
data/mkbrut/lib/mkbrut.rb
CHANGED
@@ -3,6 +3,8 @@ module MKBrut
|
|
3
3
|
autoload :AppId, "mkbrut/app_id"
|
4
4
|
autoload :AppName, "mkbrut/app_name"
|
5
5
|
autoload :AppOptions, "mkbrut/app_options"
|
6
|
+
autoload :AddSegment, "mkbrut/add_segment"
|
7
|
+
autoload :AddSegmentOptions, "mkbrut/add_segment_options"
|
6
8
|
autoload :Base, "mkbrut/base"
|
7
9
|
autoload :CLI, "mkbrut/cli"
|
8
10
|
autoload :ErbBindingDelegate, "mkbrut/erb_binding_delegate"
|
@@ -17,6 +17,9 @@
|
|
17
17
|
|
18
18
|
# This could contain actual secrets and should not be checked in
|
19
19
|
/.env.development.local
|
20
|
+
# This could also contain actual secrets and should not be checked in - it's configuration
|
21
|
+
# per developer, not per app
|
22
|
+
/dx/bash_customizations.local
|
20
23
|
|
21
24
|
# These are generated artifacts
|
22
25
|
/deploy/Dockerfile.*
|
@@ -25,13 +25,18 @@ ENV DEBIAN_FRONTEND=noninteractive
|
|
25
25
|
# These packages are needed to set up other repos to install other
|
26
26
|
# packages and/or are useful in installing other software
|
27
27
|
#
|
28
|
+
# --mount=type=cache,target=/var/cache/apt means that we'll save
|
29
|
+
# the downloaded file used to install between builds, so that if they
|
30
|
+
# have not changed, builds will be faster.
|
31
|
+
#
|
28
32
|
# - ca-certificates - needed by Postgres client
|
29
33
|
# - curl - needed by various installation instructions
|
30
34
|
# - gnupg - needed to install Docker
|
31
35
|
# - lsb-release - needed by Postgres install instructions
|
32
36
|
# - rsync - needed by Brut's build_assets command
|
33
37
|
# - vim - needed when we want to edit files inside container
|
34
|
-
RUN apt
|
38
|
+
RUN --mount=type=cache,target=/var/cache/apt \
|
39
|
+
apt-get -y clean && \
|
35
40
|
apt-get -y update && \
|
36
41
|
apt-get install --quiet --yes \
|
37
42
|
ca-certificates \
|
@@ -47,7 +52,8 @@ RUN apt-get -y clean && \
|
|
47
52
|
#
|
48
53
|
# npx playwright install-deps --dry-run chromium
|
49
54
|
#
|
50
|
-
RUN apt
|
55
|
+
RUN --mount=type=cache,target=/var/cache/apt \
|
56
|
+
apt-get install -y --no-install-recommends libasound2 \
|
51
57
|
libatk-bridge2.0-0 \
|
52
58
|
libatk1.0-0 \
|
53
59
|
libatspi2.0-0 \
|
@@ -117,10 +123,6 @@ RUN echo "gem: --no-document" >> ~/.gemrc && \
|
|
117
123
|
gem update --system && \
|
118
124
|
gem install bundler
|
119
125
|
|
120
|
-
# Installs the Heroku CLI, per Heroku docs at
|
121
|
-
# https://devcenter.heroku.com/articles/heroku-cli#install-with-ubuntu-debian-apt-get
|
122
|
-
RUN curl https://cli-assets.heroku.com/install-ubuntu.sh | sh
|
123
|
-
|
124
126
|
# Up to now, RUN directives ran as the root user. When using this container
|
125
127
|
# for development, we do not want to run as the root user. On macOS, this is not
|
126
128
|
# a big deal, but in Linux, the root user inside this container can create files
|
@@ -178,28 +180,23 @@ COPY --chown=appuser:${user_gid} dx/bash_customizations.local /home/appuser/.bas
|
|
178
180
|
# as belonging to.
|
179
181
|
USER appuser
|
180
182
|
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
|
185
|
-
# recommendation.
|
186
|
-
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash && \
|
187
|
-
\. "$HOME/.nvm/nvm.sh" && \
|
188
|
-
nvm install 22 && \
|
189
|
-
node -v && nvm current && npm -v
|
190
|
-
|
191
|
-
|
183
|
+
# Installs NodeJS from the official image. This is simpler
|
184
|
+
# than the canonical instructions which use NVM. Inside the container, there
|
185
|
+
# is not value to use NVM.
|
186
|
+
COPY --from=node:22-slim /usr/local /usr/local
|
192
187
|
|
193
188
|
# This arg should have the version of playwright present in the app's Gemfile.lock.
|
194
189
|
# This is because the playright NodeJS library and the Ruby gem must have the same
|
195
190
|
# version or things won't work right. The value is blank because it really has to be
|
196
191
|
# detected during `dx/build`.
|
197
192
|
ARG PLAYWRIGHT_VERSION
|
193
|
+
|
198
194
|
# Now install the version of Playwright we detected as well as Chromium. NOTE, we
|
199
195
|
# must install Chromium, not chrome, because there is no version of Chrome for an
|
200
196
|
# ARM-based Debian linux, which is what this Dockerfile will build on an Apple
|
201
|
-
# Silicon Mac.
|
202
|
-
|
203
|
-
|
204
|
-
|
197
|
+
# Silicon Mac. Note that we set NPM_CONFIG_PREFIX so that the global
|
198
|
+
# install will work for this user.
|
199
|
+
ENV NPM_CONFIG_PREFIX=/home/appuser/.npm-global
|
200
|
+
ENV PATH=/home/appuser/.npm-global/bin:$PATH
|
201
|
+
RUN npm install -g playwright@$PLAYWRIGHT_VERSION && \
|
205
202
|
npx playwright install chromium
|
@@ -40,7 +40,7 @@ gem "pg"
|
|
40
40
|
# We lock the version because it must be the same as the verison
|
41
41
|
# of playwright in package.json. This ensures that we can reliably
|
42
42
|
# install the right browsers and that everything will work right.
|
43
|
-
gem "playwright-ruby-client", "1.
|
43
|
+
gem "playwright-ruby-client", "1.55.0", groups: [ :test ]
|
44
44
|
|
45
45
|
# Sinatra needs a webserver and puma is the best
|
46
46
|
gem "puma"
|