brut 0.0.27 → 0.0.29
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 +6 -0
- data/.projections.json +10 -0
- data/.rspec +3 -0
- data/Dockerfile.dx +32 -14
- data/Gemfile.lock +1 -1
- 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/bin/docs +24 -2
- data/bin/rspec +27 -0
- data/bin/setup +3 -3
- data/brutrb.com/.vitepress/config.mjs +1 -0
- data/brutrb.com/.vitepress/theme/custom.css +7 -0
- data/brutrb.com/.vitepress/theme/style.css +26 -15
- data/brutrb.com/deployment.md +123 -45
- data/brutrb.com/dev-environment.md +6 -5
- data/brutrb.com/doc-conventions.md +1 -1
- data/brutrb.com/getting-started.md +64 -28
- data/brutrb.com/images/LogoPylon.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/OverviewMetro.graffle +0 -0
- data/brutrb.com/images/OverviewMetro.png +0 -0
- data/brutrb.com/index.md +4 -3
- data/brutrb.com/instrumentation.md +12 -0
- data/brutrb.com/layouts.md +130 -0
- data/brutrb.com/overview.md +6 -6
- data/docker-compose.dx.yml +5 -2
- data/docs/404.html +3 -3
- data/docs/ai.html +6 -6
- 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 +38 -13
- data/docs/api/Brut/CLI/AppRunner.html +1 -1
- data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
- data/docs/api/Brut/CLI/Apps/DB/Create.html +2 -2
- data/docs/api/Brut/CLI/Apps/DB/Drop.html +2 -2
- data/docs/api/Brut/CLI/Apps/DB/Migrate.html +9 -3
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +11 -11
- data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +2 -2
- data/docs/api/Brut/CLI/Apps/DB/Seed.html +2 -2
- data/docs/api/Brut/CLI/Apps/DB/Status.html +12 -12
- data/docs/api/Brut/CLI/Apps/DB.html +1 -1
- data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +270 -0
- data/docs/api/Brut/CLI/Apps/DeployBase.html +257 -0
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +585 -0
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +196 -0
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +12 -10
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +8 -8
- data/docs/api/Brut/CLI/Apps/Test/JS.html +9 -9
- data/docs/api/Brut/CLI/Apps/Test/Run.html +18 -18
- data/docs/api/Brut/CLI/Apps/Test.html +1 -1
- data/docs/api/Brut/CLI/Apps.html +2 -2
- data/docs/api/Brut/CLI/Command.html +113 -28
- 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 +169 -38
- data/docs/api/Brut/CLI/InvalidOption.html +1 -1
- data/docs/api/Brut/CLI/Options.html +68 -19
- 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 +110 -29
- data/docs/api/Brut/Framework/Error.html +1 -1
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +89 -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 +31 -8
- 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 +36 -26
- data/docs/api/Brut/FrontEnd/Component.html +7 -7
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +37 -29
- 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/CsrfToken.html +1 -1
- data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +20 -117
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +25 -23
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +73 -380
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +22 -138
- 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 +1 -1
- 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 +23 -2
- 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/ConstraintViolation.html +24 -68
- data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +201 -0
- data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +535 -0
- data/docs/api/Brut/FrontEnd/Forms/Input.html +983 -35
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +4 -4
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +29 -19
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +9 -3
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +9 -3
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +72 -22
- 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 +1 -1
- 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 +6 -2
- 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/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/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 +20 -20
- 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 +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +142 -0
- data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +142 -0
- data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +155 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +55 -25
- data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +149 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +46 -19
- data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +149 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +149 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +165 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +158 -0
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +156 -0
- data/docs/api/Brut/SpecSupport/Matchers.html +2 -2
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +24 -24
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +55 -20
- 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/RichString.html +1 -1
- data/docs/api/SemanticLogger/Appender/Async.html +1 -1
- data/docs/api/Sequel/Extensions/BrutInstrumentation.html +5 -1
- data/docs/api/Sequel/Extensions/BrutMigrations.html +36 -28
- 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/SpecSupport/Matchers/BeABug.html +143 -0
- data/docs/api/_index.html +106 -1
- data/docs/api/class_list.html +1 -1
- data/docs/api/css/full_list.css +2 -1
- data/docs/api/css/style.css +14 -13
- data/docs/api/file.README.html +1 -1
- data/docs/api/index.html +1 -1
- data/docs/api/method_list.html +530 -330
- data/docs/api/top-level-namespace.html +1 -1
- data/docs/assets/LogoStop.X8x-4riz.png +0 -0
- data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
- data/docs/assets/{ai.md.tZrjP9im.js → ai.md._6HCDL6d.js} +1 -1
- data/docs/assets/ai.md._6HCDL6d.lean.js +1 -0
- data/docs/assets/{app.D_yaTITQ.js → app.BhrfSt68.js} +1 -1
- data/docs/assets/chunks/@localSearchIndexroot.CeRAdP1K.js +1 -0
- data/docs/assets/chunks/{VPLocalSearchBox.B2-ZzyTY.js → VPLocalSearchBox.Dpot_2H4.js} +1 -1
- data/docs/assets/chunks/{theme.CfGFVRvE.js → theme.N2SNVLgU.js} +2 -2
- data/docs/assets/{components.md.eCttGlN-.js → components.md.CRUMdRoN.js} +1 -1
- data/docs/assets/{configuration.md.BRriU0cL.js → configuration.md.LG-zIBww.js} +1 -1
- data/docs/assets/deployment.md.BLseERGV.js +48 -0
- data/docs/assets/deployment.md.BLseERGV.lean.js +1 -0
- data/docs/assets/{dev-environment.md.BNc8AYiK.js → dev-environment.md.GZv6xvi9.js} +1 -1
- data/docs/assets/doc-conventions.md.-kN3Xo5C.js +1 -0
- data/docs/assets/{doc-conventions.md.DCfRXXi-.lean.js → doc-conventions.md.-kN3Xo5C.lean.js} +1 -1
- data/docs/assets/{forms.md.CBTYQ_Cz.js → forms.md.B-koVgyw.js} +23 -23
- data/docs/assets/{forms.md.CBTYQ_Cz.lean.js → forms.md.B-koVgyw.lean.js} +1 -1
- data/docs/assets/getting-started.md.Dj0qtZI2.js +25 -0
- data/docs/assets/getting-started.md.Dj0qtZI2.lean.js +1 -0
- data/docs/assets/index.md.CuBB-BdM.js +1 -0
- data/docs/assets/index.md.CuBB-BdM.lean.js +1 -0
- data/docs/assets/{instrumentation.md.CL6ax7nT.js → instrumentation.md.a9Pjps4P.js} +2 -2
- data/docs/assets/{instrumentation.md.CL6ax7nT.lean.js → instrumentation.md.a9Pjps4P.lean.js} +1 -1
- data/docs/assets/layouts.md.cPnh3NId.js +51 -0
- data/docs/assets/layouts.md.cPnh3NId.lean.js +1 -0
- data/docs/assets/lsp.md.Bsu-f6VU.js +1 -0
- data/docs/assets/lsp.md.Bsu-f6VU.lean.js +1 -0
- data/docs/assets/{overview.md.CDalkuxV.js → overview.md.DVKRM8zl.js} +4 -4
- data/docs/assets/overview.md.DVKRM8zl.lean.js +1 -0
- data/docs/assets/recipes_authentication.md.CAsXf7hk.js +1 -0
- data/docs/assets/recipes_authentication.md.CAsXf7hk.lean.js +1 -0
- data/docs/assets/{style.D73IYGCX.css → style.B2o1L9eN.css} +1 -1
- data/docs/assets.html +5 -5
- data/docs/brut-js/api/AjaxSubmit.html +1 -1
- data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
- data/docs/brut-js/api/Autosubmit.html +1 -1
- data/docs/brut-js/api/Autosubmit.js.html +1 -1
- data/docs/brut-js/api/BaseCustomElement.html +1 -1
- data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
- data/docs/brut-js/api/BrutCustomElements.html +1 -1
- data/docs/brut-js/api/BufferedLogger.html +1 -1
- data/docs/brut-js/api/ConfirmSubmit.html +1 -1
- data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
- data/docs/brut-js/api/ConfirmationDialog.html +1 -1
- data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
- data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
- data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
- data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
- data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
- data/docs/brut-js/api/CopyToClipboard.html +1 -1
- data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
- data/docs/brut-js/api/Form.html +1 -1
- data/docs/brut-js/api/Form.js.html +1 -1
- data/docs/brut-js/api/I18nTranslation.html +1 -1
- data/docs/brut-js/api/I18nTranslation.js.html +1 -1
- data/docs/brut-js/api/LocaleDetection.html +1 -1
- data/docs/brut-js/api/LocaleDetection.js.html +1 -1
- data/docs/brut-js/api/Logger.html +1 -1
- data/docs/brut-js/api/Logger.js.html +1 -1
- data/docs/brut-js/api/Message.html +1 -1
- data/docs/brut-js/api/Message.js.html +1 -1
- data/docs/brut-js/api/PrefixedLogger.html +1 -1
- data/docs/brut-js/api/RichString.html +1 -1
- data/docs/brut-js/api/RichString.js.html +1 -1
- data/docs/brut-js/api/Tabs.html +1 -1
- data/docs/brut-js/api/Tabs.js.html +1 -1
- data/docs/brut-js/api/Tracing.html +1 -1
- data/docs/brut-js/api/Tracing.js.html +1 -1
- data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
- data/docs/brut-js/api/external-Performance.html +1 -1
- data/docs/brut-js/api/external-Promise.html +1 -1
- data/docs/brut-js/api/external-ValidityState.html +1 -1
- data/docs/brut-js/api/external-Window.html +1 -1
- data/docs/brut-js/api/external-fetch.html +1 -1
- data/docs/brut-js/api/global.html +1 -1
- data/docs/brut-js/api/index.html +1 -1
- data/docs/brut-js/api/index.js.html +1 -1
- data/docs/brut-js/api/module-testing.html +1 -1
- data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
- data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
- data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
- data/docs/brut-js/api/testing.DOMCreator.html +1 -1
- data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
- data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
- data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
- data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
- data/docs/brut-js/api/testing_index.js.html +1 -1
- data/docs/brut-js.html +5 -5
- data/docs/business-logic.html +5 -5
- data/docs/cli.html +5 -5
- data/docs/components.html +7 -7
- data/docs/configuration.html +6 -6
- data/docs/css.html +5 -5
- data/docs/custom-element-tests.html +5 -5
- data/docs/database-access.html +5 -5
- data/docs/database-schema.html +5 -5
- data/docs/deployment.html +53 -6
- data/docs/dev-environment.html +6 -6
- data/docs/doc-conventions.html +6 -6
- data/docs/end-to-end-tests.html +5 -5
- data/docs/flash-and-session.html +5 -5
- data/docs/forms.html +28 -28
- data/docs/getting-started.html +30 -7
- data/docs/handlers.html +5 -5
- data/docs/hashmap.json +1 -1
- data/docs/hooks.html +5 -5
- data/docs/i18n.html +5 -5
- data/docs/index.html +6 -6
- data/docs/instrumentation.html +6 -6
- data/docs/javascript.html +5 -5
- data/docs/jobs.html +5 -5
- data/docs/keyword-injection.html +5 -5
- data/docs/layouts.html +74 -0
- data/docs/lsp.html +24 -0
- data/docs/markdown-examples.html +5 -5
- data/docs/middleware.html +5 -5
- data/docs/not-released.html +5 -5
- data/docs/overview.html +9 -9
- data/docs/pages.html +6 -6
- data/docs/recipes/authentication.html +24 -0
- data/docs/routes.html +5 -5
- data/docs/security.html +5 -5
- data/docs/seed-data.html +5 -5
- data/docs/space-time-continuum.html +5 -5
- data/docs/tutorial.html +5 -5
- data/docs/unit-tests.html +5 -5
- data/dx/bash_customizations +7 -0
- data/dx/build +13 -2
- data/dx/docker-compose.env +1 -1
- data/dx/exec +25 -8
- data/lib/brut/cli/app.rb +7 -2
- data/lib/brut/cli/apps/deploy_base.rb +86 -0
- data/lib/brut/cli/apps/heroku_container_based_deploy.rb +194 -0
- data/lib/brut/cli/command.rb +7 -13
- data/lib/brut/cli/executor.rb +31 -5
- data/lib/brut/cli/options.rb +4 -0
- data/lib/brut/cli.rb +4 -3
- data/lib/brut/framework/container.rb +25 -7
- data/lib/brut/framework/errors/abstract_method.rb +7 -0
- data/lib/brut/framework/errors.rb +4 -2
- data/lib/brut/front_end/forms/input.rb +253 -20
- data/lib/brut/front_end/forms/input_definition.rb +15 -12
- data/lib/brut/front_end/middlewares/reload_app.rb +2 -0
- data/lib/brut/front_end.rb +1 -0
- data/lib/brut/spec_support/rspec_setup.rb +42 -2
- data/lib/brut/version.rb +1 -1
- data/lib/sequel/extensions/brut_instrumentation.rb +4 -0
- data/specs/brut/front_end/forms/input.spec.rb +978 -0
- data/specs/spec_helper.rb +27 -0
- data/specs/support/matchers/have_constraint_violation.rb +23 -0
- data/specs/support/matchers.rb +5 -0
- data/specs/support.rb +3 -0
- metadata +77 -29
- data/docs/assets/ai.md.tZrjP9im.lean.js +0 -1
- data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +0 -1
- data/docs/assets/deployment.md.Dbka4OTr.js +0 -1
- data/docs/assets/deployment.md.Dbka4OTr.lean.js +0 -1
- data/docs/assets/doc-conventions.md.DCfRXXi-.js +0 -1
- data/docs/assets/getting-started.md.Bz2s1Vjb.js +0 -2
- data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +0 -1
- data/docs/assets/index.md.B28EwVpq.js +0 -1
- data/docs/assets/index.md.B28EwVpq.lean.js +0 -1
- data/docs/assets/overview.Da81cB9R.png +0 -0
- data/docs/assets/overview.md.CDalkuxV.lean.js +0 -1
- /data/docs/assets/{components.md.eCttGlN-.lean.js → components.md.CRUMdRoN.lean.js} +0 -0
- /data/docs/assets/{configuration.md.BRriU0cL.lean.js → configuration.md.LG-zIBww.lean.js} +0 -0
- /data/docs/assets/{dev-environment.md.BNc8AYiK.lean.js → dev-environment.md.GZv6xvi9.lean.js} +0 -0
data/lib/brut/cli/app.rb
CHANGED
@@ -179,7 +179,9 @@ class Brut::CLI::App
|
|
179
179
|
|
180
180
|
# Called after all setup has been executed. Brut will have been started/loaded. This will *not* be called if anything
|
181
181
|
# caused execution to be aborted.
|
182
|
-
|
182
|
+
#
|
183
|
+
# @param [Brut::Framework::App] app Your Brut app.
|
184
|
+
def after_bootstrap(app:)
|
183
185
|
end
|
184
186
|
|
185
187
|
# Executes the command. Called by {Brut::CLI::AppRunner}.
|
@@ -192,6 +194,7 @@ class Brut::CLI::App
|
|
192
194
|
command.set_env_if_needed
|
193
195
|
load_env(project_root:)
|
194
196
|
command.before_execute
|
197
|
+
app = nil
|
195
198
|
bootstrap_result = begin
|
196
199
|
require "#{project_root}/app/bootstrap"
|
197
200
|
bootstrap = Bootstrap.new
|
@@ -200,6 +203,7 @@ class Brut::CLI::App
|
|
200
203
|
else
|
201
204
|
bootstrap.bootstrap!
|
202
205
|
end
|
206
|
+
app = bootstrap.app
|
203
207
|
continue_execution
|
204
208
|
rescue => ex
|
205
209
|
as_execution_result(command.handle_bootstrap_exception(ex))
|
@@ -207,7 +211,8 @@ class Brut::CLI::App
|
|
207
211
|
if bootstrap_result.stop?
|
208
212
|
return bootstrap_result
|
209
213
|
end
|
210
|
-
after_bootstrap
|
214
|
+
after_bootstrap(app:)
|
215
|
+
command.after_bootstrap(app:)
|
211
216
|
if self.class.configure_only?
|
212
217
|
as_execution_result(command.execute)
|
213
218
|
else
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "brut/cli"
|
2
|
+
|
3
|
+
class Brut::CLI::Apps::DeployBase < Brut::CLI::App
|
4
|
+
def before_execute
|
5
|
+
ENV["RACK_ENV"] = "production"
|
6
|
+
end
|
7
|
+
|
8
|
+
class GitChecks
|
9
|
+
def initialize(out:, err:, executor:, warn_only: false)
|
10
|
+
@out = out
|
11
|
+
@err = err
|
12
|
+
@executor = executor
|
13
|
+
@warn_only = warn_only
|
14
|
+
end
|
15
|
+
|
16
|
+
def check!
|
17
|
+
require_main_branch!
|
18
|
+
require_no_local_changes!
|
19
|
+
require_pushed_to_main!
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def require_main_branch!
|
25
|
+
# XXX: This should be put into executor
|
26
|
+
current_branch = `git branch --show-current`.strip.chomp
|
27
|
+
|
28
|
+
if current_branch != "main"
|
29
|
+
@err.puts "You are not on the 'main' branch, but on the '#{current_branch}' branch"
|
30
|
+
@err.puts "You may only deploy from the 'main' branch"
|
31
|
+
if @warn_only
|
32
|
+
@err.puts "Ignoring"
|
33
|
+
return
|
34
|
+
else
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def require_no_local_changes!
|
41
|
+
@out.puts "Running 'git status' to reset local caches to account for Docker<->host oddities"
|
42
|
+
@executor.system!("git status")
|
43
|
+
local_changes = `git diff-index --name-only HEAD --`
|
44
|
+
if local_changes.strip != ""
|
45
|
+
@err.puts "You have local changes:"
|
46
|
+
local_changes.split(/\n/).each do |change|
|
47
|
+
@err.puts " #{change}"
|
48
|
+
end
|
49
|
+
@err.puts "Commit these, run bin/ci, then push to origin/main"
|
50
|
+
if @warn_only
|
51
|
+
@err.puts "Ignoring"
|
52
|
+
return
|
53
|
+
else
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def require_pushed_to_main!
|
60
|
+
# XXX: This should be put into executor
|
61
|
+
git_status = `git rev-list --left-right --count origin/main...main`.strip.chomp
|
62
|
+
remote_ahead, local_ahead = git_status.split(/\t/,2).map(&:to_i)
|
63
|
+
|
64
|
+
if remote_ahead != 0
|
65
|
+
@err.puts "There are commits in origin you don't have. Pull those in, re-run bin/ci, THEN deploy"
|
66
|
+
if @warn_only
|
67
|
+
@err.puts "Ignoring"
|
68
|
+
return
|
69
|
+
else
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if local_ahead != 0
|
75
|
+
@out.puts "You have not pushed to origin. Do that before deploying"
|
76
|
+
if @warn_only
|
77
|
+
@err.puts "Ignoring"
|
78
|
+
return
|
79
|
+
else
|
80
|
+
exit 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require "brut/cli"
|
2
|
+
class Brut::CLI::Apps::HerokuContainerBasedDeploy < Brut::CLI::Apps::DeployBase
|
3
|
+
description "Deploy to Heroku using containers"
|
4
|
+
default_command :deploy
|
5
|
+
configure_only!
|
6
|
+
|
7
|
+
class Deploy < Brut::CLI::Command
|
8
|
+
description "Build images, push them to Heroku, and deploy them"
|
9
|
+
|
10
|
+
detailed_description %{
|
11
|
+
Manages a deploy process based on using Heroku's Container Registry. See
|
12
|
+
|
13
|
+
https://devcenter.heroku.com/articles/container-registry-and-runtime
|
14
|
+
|
15
|
+
for details. You are assumed to understand this. This command will make the process somewhat easier.
|
16
|
+
|
17
|
+
This will use deploy/Dockerfile as a template to create one Dockerfile for each process you want to run in Heroku. deploy/heroku_config.rb is where the processes and their commands are configured.
|
18
|
+
|
19
|
+
The release phase is included automatically, based on bin/release.
|
20
|
+
}
|
21
|
+
|
22
|
+
opts.on("--platform=PLATFORM","Override default platform. Can be any Docker platform.")
|
23
|
+
opts.on("--[no-]dry-run", "Print the commands that would be run and don't actually do anything. Implies --skip-checks")
|
24
|
+
opts.on("--[no-]skip-checks", "Skip checks for code having been committed and pushed")
|
25
|
+
opts.on("--[no-]deploy", "After images are pushed, actually deploy them")
|
26
|
+
opts.on("--[no-]push", "After images are created, push them to Heroku's registry. If false, implies --no-deploy")
|
27
|
+
|
28
|
+
def after_bootstrap(app:)
|
29
|
+
@app_id = app.id
|
30
|
+
@organization = app.organization
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute
|
34
|
+
options.set_default(:deploy, true)
|
35
|
+
options.set_default(:push, true)
|
36
|
+
if !options.push?
|
37
|
+
options[:deploy] = false
|
38
|
+
end
|
39
|
+
local_repo_checks = Brut::CLI::Apps::DeployBase::GitChecks.new(
|
40
|
+
out: @out,
|
41
|
+
err: @err,
|
42
|
+
executor: @executor,
|
43
|
+
warn_only: options.skip_checks? || options.dry_run?
|
44
|
+
)
|
45
|
+
if options.dry_run?
|
46
|
+
def system!(*args)
|
47
|
+
out.puts "DRY RUN, NOT EXECUTING '#{args}'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
version = begin
|
52
|
+
git_guess = %{git rev-parse HEAD}
|
53
|
+
stdout, stderr, status = Open3.capture3(git_guess)
|
54
|
+
if status.success?
|
55
|
+
stdout.strip
|
56
|
+
else
|
57
|
+
raise "Attempt to use git via command '#{git_guess}' to figure out the version failed: #{stdout}#{stderr}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
short_version = version[0..7]
|
61
|
+
|
62
|
+
platform = options.platform || "linux/amd64"
|
63
|
+
heroku_app_name = @app_id
|
64
|
+
|
65
|
+
out.puts "Reading HerokuConfig:"
|
66
|
+
require_relative Brut.container.project_root / "deploy" / "heroku_config"
|
67
|
+
|
68
|
+
additional_images = HerokuConfig.additional_images.map { |name,config|
|
69
|
+
cmd = config.fetch(:cmd)
|
70
|
+
out.puts " - #{name} will run #{cmd} in production"
|
71
|
+
image_name = %{#{@organization}/#{@app_id}:#{short_version}-#{name}}
|
72
|
+
[
|
73
|
+
name,
|
74
|
+
{
|
75
|
+
cmd:,
|
76
|
+
image_name:,
|
77
|
+
dockerfile: "deploy/Dockerfile.#{name}",
|
78
|
+
heroku_image_name: "registry.heroku.com/#{heroku_app_name}/#{name}",
|
79
|
+
}
|
80
|
+
]
|
81
|
+
}.to_h
|
82
|
+
|
83
|
+
images = {
|
84
|
+
"web" => {
|
85
|
+
cmd: "bin/run",
|
86
|
+
image_name: %{#{@organization}/#{@app_id}:#{short_version}-web},
|
87
|
+
dockerfile: "deploy/Dockerfile.web",
|
88
|
+
heroku_image_name: "registry.heroku.com/#{heroku_app_name}/web",
|
89
|
+
},
|
90
|
+
"release" => {
|
91
|
+
cmd: "bin/release",
|
92
|
+
image_name: %{#{@organization}/#{@app_id}:#{short_version}-release},
|
93
|
+
dockerfile: "deploy/Dockerfile.release",
|
94
|
+
heroku_image_name: "registry.heroku.com/#{heroku_app_name}/release",
|
95
|
+
},
|
96
|
+
}.merge(additional_images)
|
97
|
+
|
98
|
+
out.puts " - release will run bin/release in production"
|
99
|
+
|
100
|
+
local_repo_checks.check!
|
101
|
+
require_heroku_login!(options)
|
102
|
+
|
103
|
+
FileUtils.chdir Brut.container.project_root do
|
104
|
+
|
105
|
+
out.puts "Generating Dockerfiles"
|
106
|
+
images.each do |name,metadata|
|
107
|
+
cmd = metadata.fetch(:cmd)
|
108
|
+
dockerfile = metadata.fetch(:dockerfile)
|
109
|
+
|
110
|
+
out.puts "Creating '#{dockerfile}' for '#{name}' that will use command '#{cmd}'"
|
111
|
+
|
112
|
+
File.open(dockerfile,"w") do |file|
|
113
|
+
file.puts "# DO NOT EDIT - THIS IS GENERATED"
|
114
|
+
file.puts "# To make changes, modifiy deploy/Dockerfile and run #{$0}"
|
115
|
+
file.puts File.read("deploy/Dockerfile")
|
116
|
+
file.puts
|
117
|
+
file.puts "# Added by #{$0}"
|
118
|
+
file.puts %{CMD [ "bundle", "exec", "#{cmd}" ]}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
out.puts "Building images"
|
123
|
+
docker_quiet_option = if global_options.log_level != "debug"
|
124
|
+
"--quiet"
|
125
|
+
else
|
126
|
+
""
|
127
|
+
end
|
128
|
+
images.each do |name,metadata|
|
129
|
+
image_name = metadata.fetch(:image_name)
|
130
|
+
dockerfile = metadata.fetch(:dockerfile)
|
131
|
+
|
132
|
+
|
133
|
+
out.puts "Creating docker image with name '#{image_name}' and platform '#{platform}'"
|
134
|
+
command = %{docker build #{docker_quiet_option} --build-arg app_git_sha1=#{version} --file #{Brut.container.project_root}/#{dockerfile} --platform #{platform} --tag #{image_name} .}
|
135
|
+
system!(command)
|
136
|
+
end
|
137
|
+
|
138
|
+
out.puts "Taggging images for Heroku"
|
139
|
+
images.each do |name,metadata|
|
140
|
+
image_name = metadata.fetch(:image_name)
|
141
|
+
heroku_image_name = metadata.fetch(:heroku_image_name)
|
142
|
+
|
143
|
+
out.puts "Tagging '#{image_name}' with '#{heroku_image_name}' for Heroku"
|
144
|
+
command = %{docker tag #{image_name} #{heroku_image_name}}
|
145
|
+
system!(command)
|
146
|
+
end
|
147
|
+
|
148
|
+
if options.push?
|
149
|
+
out.puts "Pushing to Heroku Registry"
|
150
|
+
images.each do |name,metadata|
|
151
|
+
heroku_image_name = metadata.fetch(:heroku_image_name)
|
152
|
+
|
153
|
+
out.puts "Pushing '#{heroku_image_name}'"
|
154
|
+
|
155
|
+
command = %{docker push #{docker_quiet_option} #{heroku_image_name}}
|
156
|
+
system!(command)
|
157
|
+
end
|
158
|
+
else
|
159
|
+
out.puts "Not pushing images"
|
160
|
+
end
|
161
|
+
|
162
|
+
names = images.map(&:first).join(" ")
|
163
|
+
deploy_command = "heroku container:release #{names}"
|
164
|
+
if options.deploy?
|
165
|
+
out.puts "Deploying images to Heroku"
|
166
|
+
system!(deploy_command)
|
167
|
+
else
|
168
|
+
out.puts "Not deploying. To deploy the images just pushed:"
|
169
|
+
out.puts ""
|
170
|
+
out.puts " #{deploy_command}"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
private
|
175
|
+
def require_heroku_login!(options)
|
176
|
+
if system("heroku whoami")
|
177
|
+
out.puts "You are logged in to Heroku"
|
178
|
+
else
|
179
|
+
out.puts "You are not logged into Heroku."
|
180
|
+
out.puts "Please run the following:"
|
181
|
+
out.puts ""
|
182
|
+
out.puts "heroku auth:login"
|
183
|
+
out.puts "heroku container:login"
|
184
|
+
out.puts ""
|
185
|
+
out.puts "Then, re-run this"
|
186
|
+
if options.dry_run?
|
187
|
+
out.puts "Dry run - ignoring"
|
188
|
+
return
|
189
|
+
end
|
190
|
+
exit 1
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
data/lib/brut/cli/command.rb
CHANGED
@@ -198,6 +198,13 @@ class Brut::CLI::Command
|
|
198
198
|
def before_execute
|
199
199
|
end
|
200
200
|
|
201
|
+
# Called after all setup has been executed. Brut will have been started/loaded.
|
202
|
+
# This will *not* be called if anything caused execution to be aborted.
|
203
|
+
#
|
204
|
+
# @param [Brut::Framework::App] app Your Brut app.
|
205
|
+
def after_bootstrap(app:)
|
206
|
+
end
|
207
|
+
|
201
208
|
# @!visibility private
|
202
209
|
def set_env_if_needed
|
203
210
|
if self.class.requires_project_env?
|
@@ -219,19 +226,6 @@ class Brut::CLI::Command
|
|
219
226
|
raise ex
|
220
227
|
end
|
221
228
|
|
222
|
-
# @!visibility private
|
223
|
-
def bootstrap!(project_root:, configure_only:)
|
224
|
-
require "bundler"
|
225
|
-
Bundler.require(:default, ENV["RACK_ENV"].to_sym)
|
226
|
-
if configure_only
|
227
|
-
require "#{project_root}/app/pre_boot"
|
228
|
-
Brut::Framework.new(app: ::App.new)
|
229
|
-
else
|
230
|
-
require "#{project_root}/app/boot"
|
231
|
-
end
|
232
|
-
continue_execution
|
233
|
-
end
|
234
|
-
|
235
229
|
private
|
236
230
|
|
237
231
|
# @!visibility public
|
data/lib/brut/cli/executor.rb
CHANGED
@@ -16,14 +16,40 @@ class Brut::CLI::Executor
|
|
16
16
|
@err = err
|
17
17
|
end
|
18
18
|
|
19
|
-
# Execute a command, logging it to the standard output and outputing the
|
20
|
-
#
|
19
|
+
# Execute a command, logging it to the standard output and outputing the
|
20
|
+
# commands output and error to the standard output and error, respectively. If
|
21
|
+
# the command exits nonzero, the exit status is returned.
|
22
|
+
#
|
23
|
+
# Generally, you want to use {#system!} for commands that must succeed
|
24
|
+
# for the caller to continue. Only use this method if you need
|
25
|
+
# to do special error-handling when the underlying command fails.
|
26
|
+
#
|
27
|
+
# @see https://docs.ruby-lang.org/en/3.3/Open3.html#method-c-popen3
|
28
|
+
# @see #system!
|
29
|
+
#
|
30
|
+
# @param [String|Array] args Whatever you would give to `Kernel#system` or `Open3.popen3`.
|
31
|
+
# @return [int] 0 if the command completed normally, otherwise the nonzero exit status. **DO NOT TREAT THIS AS A BOOLEAN VALUE**
|
32
|
+
def system(*args)
|
33
|
+
self.system!(*args)
|
34
|
+
0
|
35
|
+
rescue Brut::CLI::SystemExecError => e
|
36
|
+
e.exit_status
|
37
|
+
end
|
38
|
+
|
39
|
+
# Execute a command, logging it to the standard output and outputing the
|
40
|
+
# commands output and error to the standard output and error, respectively. If
|
41
|
+
# the command exits nonzero, an exception is raised and your CLI app will
|
42
|
+
# also exit nonzero.
|
43
|
+
#
|
44
|
+
# If you need to handle the command exiting nonzero, use {#system}
|
45
|
+
# instead, as it will not raise an exception.
|
21
46
|
#
|
22
47
|
# @see https://docs.ruby-lang.org/en/3.3/Open3.html#method-c-popen3
|
48
|
+
# @see #system
|
23
49
|
#
|
24
50
|
# @param [String|Array] args Whatever you would give to `Kernel#system` or `Open3.popen3`.
|
25
|
-
# @raise Brut::CLI::Error::SystemExecError if the spawed command exits
|
26
|
-
# @return [
|
51
|
+
# @raise Brut::CLI::Error::SystemExecError if the spawed command exits nonzero
|
52
|
+
# @return [int] Always returns 0
|
27
53
|
def system!(*args)
|
28
54
|
@out.puts "Executing #{args}"
|
29
55
|
wait_thread = Open3.popen3(*args) do |_stdin,stdout,stderr,wait_thread|
|
@@ -52,6 +78,6 @@ class Brut::CLI::Executor
|
|
52
78
|
else
|
53
79
|
raise Brut::CLI::SystemExecError.new(args,wait_thread.value.exitstatus)
|
54
80
|
end
|
55
|
-
|
81
|
+
0
|
56
82
|
end
|
57
83
|
end
|
data/lib/brut/cli/options.rb
CHANGED
@@ -12,6 +12,10 @@ class Brut::CLI::Options
|
|
12
12
|
# Access an options value directly.
|
13
13
|
# @param [String] key the key to use. This must be the exact name you used when calling `opts.on` or when creating the `OptionParser` for your app or command. Generally, use the {#method_missing}-provided version instead of this.
|
14
14
|
def [](key) = @parsed_options[key]
|
15
|
+
|
16
|
+
def []=(key,value)
|
17
|
+
@parsed_options[key] = value
|
18
|
+
end
|
15
19
|
# Check if `key` was provided on the command line.
|
16
20
|
# @param [String] key the key to use. This must be the exact name you used when calling `opts.on` or when creating the `OptionParser` for your app or command.
|
17
21
|
def key?(key) = @parsed_options.key?(key)
|
data/lib/brut/cli.rb
CHANGED
@@ -42,9 +42,10 @@ module Brut
|
|
42
42
|
# Holds Brut-provided CLI apps that are set up in your project.
|
43
43
|
module Apps
|
44
44
|
autoload(:DB,"brut/cli/apps/db")
|
45
|
-
autoload(:
|
46
|
-
autoload(:
|
47
|
-
autoload(:
|
45
|
+
autoload(:Test,"brut/cli/apps/test")
|
46
|
+
autoload(:BuildAssets,"brut/cli/apps/build_assets")
|
47
|
+
autoload(:Scaffold,"brut/cli/apps/scaffold")
|
48
|
+
autoload(:DeployBase,"brut/cli/apps/deploy_base")
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
@@ -78,7 +78,7 @@ class Brut::Framework::Container
|
|
78
78
|
self.validate_name!(name:,type:,allow_app_override:)
|
79
79
|
if value == :use_block
|
80
80
|
derive_with = block
|
81
|
-
@container[name] = {
|
81
|
+
@container[name] = { derive_with: derive_with }
|
82
82
|
else
|
83
83
|
if type == :boolean
|
84
84
|
value = !!value
|
@@ -111,9 +111,11 @@ class Brut::Framework::Container
|
|
111
111
|
raise ArgumentError,"#{name} does not allow the app to override it"
|
112
112
|
end
|
113
113
|
if value == :use_block
|
114
|
-
@container[name] =
|
114
|
+
@container[name][:derive_with] = block
|
115
|
+
@container[name].delete(:value)
|
115
116
|
else
|
116
|
-
@container[name] =
|
117
|
+
@container[name][:value] = value
|
118
|
+
@container[name].delete(:derive_with)
|
117
119
|
end
|
118
120
|
end
|
119
121
|
|
@@ -191,16 +193,16 @@ class Brut::Framework::Container
|
|
191
193
|
# TODO: Provide a cleanr impl and better error checking if things go wrong
|
192
194
|
x = @container.fetch(name)
|
193
195
|
|
194
|
-
|
196
|
+
has_value = x.key?(:value)
|
195
197
|
|
196
|
-
if
|
198
|
+
if has_value
|
197
199
|
handle_path_values(name,x)
|
198
|
-
return value
|
200
|
+
return x[:value]
|
199
201
|
end
|
200
202
|
|
201
203
|
deriver = x[:derive_with]
|
202
204
|
if deriver.nil?
|
203
|
-
raise "Something is seriously wrong. '#{name}' was stored in container without a derive_with
|
205
|
+
raise "Something is seriously wrong. '#{name}' was stored in container without a static value, but also without a derive_with key"
|
204
206
|
end
|
205
207
|
|
206
208
|
parameters = deriver.parameters(lambda: true)
|
@@ -218,6 +220,22 @@ class Brut::Framework::Container
|
|
218
220
|
handle_path_values(name,x)
|
219
221
|
x[:value]
|
220
222
|
end
|
223
|
+
|
224
|
+
def reload
|
225
|
+
@container.each do |name, contained_value|
|
226
|
+
if contained_value.key?(:value) && contained_value[:type].to_s == "Class"
|
227
|
+
if contained_value.key?(:derive_with)
|
228
|
+
contained_value.delete(:value)
|
229
|
+
else
|
230
|
+
klass = contained_value[:value]
|
231
|
+
new_klass = klass.name.split(/::/).reduce(Module) { |mod,part|
|
232
|
+
mod.const_get(part)
|
233
|
+
}
|
234
|
+
contained_value[:value] = new_klass
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
221
239
|
private
|
222
240
|
|
223
241
|
def handle_path_values(name,contained_value)
|
@@ -2,4 +2,11 @@
|
|
2
2
|
# which methods a subclass is expected to override and for which no default behavior
|
3
3
|
# makes sense.
|
4
4
|
class Brut::Framework::Errors::AbstractMethod < Brut::Framework::Error
|
5
|
+
def initialize(method_name=nil)
|
6
|
+
if method_name
|
7
|
+
super("The method `#{method_name}` must be implemented")
|
8
|
+
else
|
9
|
+
super("An abstract method must be implemented")
|
10
|
+
end
|
11
|
+
end
|
5
12
|
end
|
@@ -27,9 +27,11 @@ module Brut
|
|
27
27
|
# a method that a subclass must implement, but for which there is no useful default
|
28
28
|
# implementation.
|
29
29
|
#
|
30
|
+
# @param [String|Symbol] method_name name of the method that must be implemented. If omitted, the value is guessed from `caller_locations`
|
30
31
|
# @raise [Brut::Framework::Errors::AbstractMethod]
|
31
|
-
def abstract_method!
|
32
|
-
|
32
|
+
def abstract_method!(method_name=nil)
|
33
|
+
method_name ||= caller_locations(1,1)[0].label
|
34
|
+
raise Brut::Framework::Errors::AbstractMethod.new(method_name)
|
33
35
|
end
|
34
36
|
end
|
35
37
|
# Base class for errors thrown by Brut classes. Generally, Brut will not create its own version
|