brut 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +4 -1
- data/bin/docs +7 -0
- 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.gemspec +2 -0
- data/docs/404.html +2 -2
- data/docs/adrs.html +3 -3
- data/docs/ai.html +3 -3
- 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 +2 -2
- 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 +2 -2
- 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 +10 -6
- 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 +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 +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 +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 +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/TUI/AnsiEscapeCode/Mod.html +409 -0
- data/docs/api/Brut/TUI/AnsiEscapeCode.html +426 -0
- data/docs/api/Brut/TUI/EventLoop/Deque.html +531 -0
- data/docs/api/Brut/TUI/EventLoop.html +676 -0
- data/docs/api/Brut/TUI/Events/BaseEvent.html +449 -0
- data/docs/api/Brut/TUI/Events/EventBus.html +485 -0
- data/docs/api/Brut/TUI/Events/EventLoopStarted.html +211 -0
- data/docs/api/Brut/TUI/Events/Exception.html +523 -0
- data/docs/api/Brut/TUI/Events/Tick.html +294 -0
- data/docs/api/Brut/TUI/Events.html +131 -0
- data/docs/api/Brut/TUI/MarkupString.html +537 -0
- data/docs/api/Brut/TUI/Script/BlockStep.html +300 -0
- data/docs/api/Brut/TUI/Script/Events/CommandExecutionFailed.html +252 -0
- data/docs/api/Brut/TUI/Script/Events/CommandExecutionSucceeded.html +163 -0
- data/docs/api/Brut/TUI/Script/Events/CommandStdErr.html +163 -0
- data/docs/api/Brut/TUI/Script/Events/CommandStdOut.html +300 -0
- data/docs/api/Brut/TUI/Script/Events/ExecutingCommand.html +298 -0
- data/docs/api/Brut/TUI/Script/Events/Message.html +345 -0
- data/docs/api/Brut/TUI/Script/Events/PhaseCompleted.html +229 -0
- data/docs/api/Brut/TUI/Script/Events/PhaseStarted.html +350 -0
- data/docs/api/Brut/TUI/Script/Events/ScriptCompleted.html +282 -0
- data/docs/api/Brut/TUI/Script/Events/ScriptStarted.html +343 -0
- data/docs/api/Brut/TUI/Script/Events/StepCompleted.html +163 -0
- data/docs/api/Brut/TUI/Script/Events/StepStarted.html +346 -0
- data/docs/api/Brut/TUI/Script/Events.html +115 -0
- data/docs/api/Brut/TUI/Script/ExecStep/ProcessStatusFailed.html +210 -0
- data/docs/api/Brut/TUI/Script/ExecStep.html +493 -0
- data/docs/api/Brut/TUI/Script/LoggingSubscriber.html +914 -0
- data/docs/api/Brut/TUI/Script/PutsSubscriber.html +783 -0
- data/docs/api/Brut/TUI/Script/Step.html +313 -0
- data/docs/api/Brut/TUI/Script.html +1250 -0
- data/docs/api/Brut/TUI/Terminal.html +593 -0
- data/docs/api/Brut/TUI/TerminalTheme.html +1403 -0
- data/docs/api/Brut/TUI/Themes/Dark.html +706 -0
- data/docs/api/Brut/TUI/Themes/Light.html +804 -0
- data/docs/api/Brut/TUI/Themes/None.html +218 -0
- data/docs/api/Brut/TUI/Themes.html +115 -0
- data/docs/api/Brut/TUI.html +129 -0
- data/docs/api/Brut.html +3 -3
- 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/SpecSupport/Matchers/BeABug.html +143 -0
- data/docs/api/_index.html +246 -1
- data/docs/api/class_list.html +1 -1
- data/docs/api/file.README.html +1 -1
- data/docs/api/index.html +1 -1
- data/docs/api/method_list.html +1551 -431
- data/docs/api/top-level-namespace.html +1 -1
- data/docs/assets/{app.CovevI7X.js → app.B8jAEB7R.js} +1 -1
- data/docs/assets/chunks/@localSearchIndexroot.DJ8mocCj.js +1 -0
- data/docs/assets/chunks/{VPLocalSearchBox.CrvLAvKW.js → VPLocalSearchBox.gF-Po_fz.js} +1 -1
- data/docs/assets/chunks/{theme.BAi5_yQI.js → theme.BjPAOJkz.js} +2 -2
- data/docs/assets/{components.md.9sqJ27Oc.js → components.md.Ber8UBM0.js} +3 -3
- data/docs/assets/{configuration.md.Cb_oAR8Z.js → configuration.md.DrJ6YVoZ.js} +1 -1
- data/docs/assets/{forms.md.BdpYpNIk.js → forms.md.RK0zkhm0.js} +2 -2
- data/docs/assets/{forms.md.BdpYpNIk.lean.js → forms.md.RK0zkhm0.lean.js} +1 -1
- data/docs/assets/{getting-started.md.CKpNGvno.js → getting-started.md.CGJ44juQ.js} +2 -2
- data/docs/assets/{tutorials_02-dialog.md.Z_DOF2mU.js → tutorials_02-dialog.md.DE5WfCXI.js} +1 -1
- data/docs/assets.html +3 -3
- data/docs/brut-css/brut.css +1 -0
- 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/Toast.html +1 -1
- data/docs/brut-js/api/Toast.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 +3 -3
- data/docs/business-logic.html +3 -3
- data/docs/cli.html +3 -3
- data/docs/components.html +7 -7
- data/docs/configuration.html +5 -5
- data/docs/css.html +3 -3
- data/docs/custom-element-tests.html +3 -3
- data/docs/database-access.html +3 -3
- data/docs/database-schema.html +3 -3
- data/docs/deployment.html +3 -3
- data/docs/dev-environment.html +3 -3
- data/docs/dir-structure.html +3 -3
- data/docs/doc-conventions.html +3 -3
- data/docs/end-to-end-tests.html +3 -3
- data/docs/features.html +3 -3
- data/docs/flash-and-session.html +3 -3
- data/docs/form-constraints.html +3 -3
- data/docs/forms.html +5 -5
- data/docs/getting-started.html +6 -6
- data/docs/handlers.html +3 -3
- data/docs/hashmap.json +1 -1
- data/docs/hooks.html +3 -3
- data/docs/i18n.html +3 -3
- data/docs/index.html +3 -3
- data/docs/instrumentation.html +3 -3
- data/docs/javascript.html +3 -3
- data/docs/jobs.html +3 -3
- data/docs/keyword-injection.html +3 -3
- data/docs/layouts.html +3 -3
- data/docs/lsp.html +3 -3
- data/docs/markdown-examples.html +3 -3
- data/docs/middleware.html +3 -3
- data/docs/overview.html +3 -3
- data/docs/pages.html +3 -3
- data/docs/recipes/alternate-layouts.html +3 -3
- data/docs/recipes/authentication.html +3 -3
- data/docs/recipes/custom-flash.html +3 -3
- data/docs/recipes/dev-env-secrets.html +3 -3
- data/docs/recipes/form-errors.html +3 -3
- data/docs/recipes/indexed-forms.html +3 -3
- data/docs/recipes/migrations.html +3 -3
- data/docs/recipes/text-field-component.html +3 -3
- data/docs/roadmap.html +3 -3
- data/docs/routes.html +3 -3
- data/docs/security.html +3 -3
- data/docs/seed-data.html +3 -3
- data/docs/space-time-continuum.html +3 -3
- data/docs/tutorial.html +3 -3
- data/docs/tutorials/01-intro.html +3 -3
- data/docs/tutorials/02-dialog.html +5 -5
- data/docs/unit-tests.html +3 -3
- data/docs/why.html +3 -3
- data/lib/brut/cli/apps/build_assets.rb +1 -1
- data/lib/brut/cli/apps/heroku_container_based_deploy.rb +1 -1
- data/lib/brut/cli/apps/test.rb +6 -4
- data/lib/brut/tui/ansi_escape_code.rb +104 -0
- data/lib/brut/tui/event_loop.rb +168 -0
- data/lib/brut/tui/events/base_event.rb +29 -0
- data/lib/brut/tui/events/event_bus.rb +73 -0
- data/lib/brut/tui/events/event_loop_started.rb +5 -0
- data/lib/brut/tui/events/exception.rb +24 -0
- data/lib/brut/tui/events/tick.rb +12 -0
- data/lib/brut/tui/events.rb +7 -0
- data/lib/brut/tui/markup_string.rb +68 -0
- data/lib/brut/tui/script/block_step.rb +17 -0
- data/lib/brut/tui/script/events/command_execution_failed.rb +4 -0
- data/lib/brut/tui/script/events/command_execution_succeeded.rb +3 -0
- data/lib/brut/tui/script/events/command_std_err.rb +3 -0
- data/lib/brut/tui/script/events/command_std_out.rb +13 -0
- data/lib/brut/tui/script/events/executing_command.rb +12 -0
- data/lib/brut/tui/script/events/message.rb +15 -0
- data/lib/brut/tui/script/events/phase_completed.rb +4 -0
- data/lib/brut/tui/script/events/phase_started.rb +14 -0
- data/lib/brut/tui/script/events/script_completed.rb +5 -0
- data/lib/brut/tui/script/events/script_started.rb +12 -0
- data/lib/brut/tui/script/events/step_completed.rb +3 -0
- data/lib/brut/tui/script/events/step_started.rb +12 -0
- data/lib/brut/tui/script/events.rb +14 -0
- data/lib/brut/tui/script/exec_step.rb +60 -0
- data/lib/brut/tui/script/logging_subscriber.rb +98 -0
- data/lib/brut/tui/script/puts_subscriber.rb +109 -0
- data/lib/brut/tui/script/step.rb +13 -0
- data/lib/brut/tui/script.rb +211 -0
- data/lib/brut/tui/terminal.rb +74 -0
- data/lib/brut/tui/terminal_theme.rb +140 -0
- data/lib/brut/tui/themes/dark.rb +14 -0
- data/lib/brut/tui/themes/light.rb +17 -0
- data/lib/brut/tui/themes/none.rb +9 -0
- data/lib/brut/tui/themes.rb +5 -0
- data/lib/brut/tui.rb +15 -0
- data/lib/brut/version.rb +1 -1
- data/lib/brut.rb +1 -0
- data/mkbrut/Gemfile.lock +1 -1
- data/mkbrut/lib/mkbrut/version.rb +1 -1
- data/specs/brut/tui/ansi_escape_code.spec.rb +30 -0
- data/specs/brut/tui/event_loop.spec.rb +70 -0
- data/specs/brut/tui/events/base_event.spec.rb +26 -0
- data/specs/brut/tui/events/event_bus.spec.rb +141 -0
- data/specs/brut/tui/events/exception.spec.rb +19 -0
- data/specs/brut/tui/events/test_event.rb +5 -0
- data/specs/spec_helper.rb +4 -0
- metadata +124 -15
- data/docs/assets/chunks/@localSearchIndexroot.BiNc3tFI.js +0 -1
- /data/docs/assets/{components.md.9sqJ27Oc.lean.js → components.md.Ber8UBM0.lean.js} +0 -0
- /data/docs/assets/{configuration.md.Cb_oAR8Z.lean.js → configuration.md.DrJ6YVoZ.lean.js} +0 -0
- /data/docs/assets/{getting-started.md.CKpNGvno.lean.js → getting-started.md.CGJ44juQ.lean.js} +0 -0
- /data/docs/assets/{tutorials_02-dialog.md.Z_DOF2mU.lean.js → tutorials_02-dialog.md.DE5WfCXI.lean.js} +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# A string that responds to limited markup that can be used to apply styles to the string
|
|
2
|
+
class Brut::TUI::MarkupString
|
|
3
|
+
# Create a MarkupString from a normal string.
|
|
4
|
+
#
|
|
5
|
+
# @param string [String|Brut::TUI::MarkupString] string to convert.
|
|
6
|
+
# @return [Brut::TUI::MarkupString] if `string` is a `Brut::TUI::MarkupString` already, returns that, otherwise, wraps
|
|
7
|
+
# `string` in a `Brut::TUI::MarkupString`.
|
|
8
|
+
def self.from_string(string)
|
|
9
|
+
string.kind_of?(Brut::TUI::MarkupString) ? string : self.new(string.to_s)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(string)
|
|
13
|
+
@string = string
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
DELIMITERS = {
|
|
17
|
+
"*" => :bold,
|
|
18
|
+
"_" => :weak,
|
|
19
|
+
"`" => :code,
|
|
20
|
+
"~" => :strike,
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
# Parse the string for known markup, yielding at key parsing events.
|
|
24
|
+
#
|
|
25
|
+
# @yield [directive, value] called for each parsing event, where value depends on directive. The block
|
|
26
|
+
# will be called for all parts of the string.
|
|
27
|
+
# @yieldparam directive [Symbol] one of `:start`, `:stop`, or `:text`. `:text` is for any text and doesn't include
|
|
28
|
+
# the markup characters. `:start` and `:stop` are called when a markup start or stop is found.
|
|
29
|
+
# @yieldparam value [String|Symbol] For the `:text` `directive`, this is the text fragment from the string, so
|
|
30
|
+
# for the string `"*foo*"`, `:text` would be called with `"foo"`. For `:start` or `:stop`, the value
|
|
31
|
+
# is the type of markup encountered, one of `:bold`, `:weak`, `:code`, or `:strike`.
|
|
32
|
+
def parse(&block)
|
|
33
|
+
in_delimiter = DELIMITERS.keys.map { [ it, false ] }.to_h
|
|
34
|
+
|
|
35
|
+
previous_character = nil
|
|
36
|
+
previous_previous_character = nil
|
|
37
|
+
|
|
38
|
+
@string.each_char do |char|
|
|
39
|
+
if DELIMITERS.key?(char) && previous_character != "\\" && previous_previous_character != "\\"
|
|
40
|
+
style = DELIMITERS[char]
|
|
41
|
+
if in_delimiter[char]
|
|
42
|
+
block.(:stop, style)
|
|
43
|
+
in_delimiter[char] = false
|
|
44
|
+
else
|
|
45
|
+
inside_code = in_delimiter["`"]
|
|
46
|
+
if inside_code
|
|
47
|
+
block.(:text, char)
|
|
48
|
+
else
|
|
49
|
+
block.(:start, style)
|
|
50
|
+
in_delimiter[char] = true
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
if char == "\\"
|
|
55
|
+
if previous_character == "\\"
|
|
56
|
+
block.(:text, char)
|
|
57
|
+
else
|
|
58
|
+
# eat it - it is escaping something maybe
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
block.(:text, char)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
previous_previous_character = previous_character
|
|
65
|
+
previous_character = char
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# A step whose behavior is a given block of code.
|
|
2
|
+
# Fires {Brut::TUI::Script::Events::StepStarted} before
|
|
3
|
+
# the code executes and {Brut::TUI::Script::Events::StepCompleted}
|
|
4
|
+
# *only* if the block completes without an exception being thrown.
|
|
5
|
+
class Brut::TUI::Script::BlockStep < Brut::TUI::Script::Step
|
|
6
|
+
def initialize(event_loop, description, &block)
|
|
7
|
+
super(event_loop, description)
|
|
8
|
+
@block = block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run!
|
|
12
|
+
event_loop << Events::StepStarted.new(step: self)
|
|
13
|
+
@block.().tap {
|
|
14
|
+
event_loop << Events::StepCompleted.new(step: self)
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Fired when a command has provided at least some output on its standard output.
|
|
2
|
+
class Brut::TUI::Script::Events::CommandStdOut < Brut::TUI::Events::BaseEvent
|
|
3
|
+
def initialize(step:, output:)
|
|
4
|
+
@step = step
|
|
5
|
+
@output = output
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Adds `description`, `command` and `output` to the event keywords. `output` is a string
|
|
9
|
+
# containing whatever output was available. This is likely not terminated with a newline.
|
|
10
|
+
def deconstruct_keys(keys=nil)
|
|
11
|
+
super.merge({ description: @step.description, command: @step.command, output: @output })
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Fired when a command is executed (this also serves as a base class for the results
|
|
2
|
+
# of such execution).
|
|
3
|
+
class Brut::TUI::Script::Events::ExecutingCommand < Brut::TUI::Events::BaseEvent
|
|
4
|
+
def initialize(step:)
|
|
5
|
+
@step = step
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Includes `description` and `command` in the keyword arguments
|
|
9
|
+
def deconstruct_keys(keys=nil)
|
|
10
|
+
super.merge({ description: @step.description, command: @step.command })
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Fired when a step produces a message
|
|
2
|
+
class Brut::TUI::Script::Events::Message < Brut::TUI::Events::BaseEvent
|
|
3
|
+
def initialize(message:, type:)
|
|
4
|
+
@message = message
|
|
5
|
+
@type = type
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def to_s = @message
|
|
9
|
+
|
|
10
|
+
# Includes `message` and `type`. `type` will be `:notify`, `:warning`, `:success`,
|
|
11
|
+
# `:error`, or `:done`, however it could be anything else the script may choose to use.
|
|
12
|
+
def deconstruct_keys(keys=nil)
|
|
13
|
+
super.merge({ message: @message, type: @type })
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Fired when a phase starts, but before any steps have executed
|
|
2
|
+
class Brut::TUI::Script::Events::PhaseStarted < Brut::TUI::Events::BaseEvent
|
|
3
|
+
def initialize(description, step_number:, total_steps:)
|
|
4
|
+
@description = description
|
|
5
|
+
@step_number = step_number
|
|
6
|
+
@total_steps = total_steps
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Adds `description`, `step_number` (1-based), and `total_steps` to the keyword arguments.
|
|
10
|
+
def deconstruct_keys(keys=nil)
|
|
11
|
+
super.merge({ description: @description, step_number: @step_number, total_steps: @total_steps })
|
|
12
|
+
end
|
|
13
|
+
def to_s = @description
|
|
14
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Fired when the script is just starting
|
|
2
|
+
class Brut::TUI::Script::Events::ScriptStarted < Brut::TUI::Events::BaseEvent
|
|
3
|
+
def initialize(phases:)
|
|
4
|
+
@phases = phases
|
|
5
|
+
end
|
|
6
|
+
# Adds `phases` as an array of `[description,Proc]` representing the phases.
|
|
7
|
+
# You are discouraged from interacting with the `Proc` objects.
|
|
8
|
+
def deconstruct_keys(keys=nil)
|
|
9
|
+
super.merge({ phases: @phases })
|
|
10
|
+
end
|
|
11
|
+
def to_s = "Starting"
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Fired when a step is about to execute
|
|
2
|
+
class Brut::TUI::Script::Events::StepStarted < Brut::TUI::Events::BaseEvent
|
|
3
|
+
def initialize(step:)
|
|
4
|
+
@step = step
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# Adds `description` to the keyword arguments
|
|
8
|
+
def deconstruct_keys(keys=nil)
|
|
9
|
+
super.merge({ description: @step.description })
|
|
10
|
+
end
|
|
11
|
+
def to_s = @description
|
|
12
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Brut::TUI::Script::Events
|
|
2
|
+
autoload :ScriptStarted , "brut/tui/script/events/script_started"
|
|
3
|
+
autoload :ScriptCompleted , "brut/tui/script/events/script_completed"
|
|
4
|
+
autoload :PhaseStarted , "brut/tui/script/events/phase_started"
|
|
5
|
+
autoload :PhaseCompleted , "brut/tui/script/events/phase_completed"
|
|
6
|
+
autoload :StepStarted , "brut/tui/script/events/step_started"
|
|
7
|
+
autoload :StepCompleted , "brut/tui/script/events/step_completed"
|
|
8
|
+
autoload :ExecutingCommand , "brut/tui/script/events/executing_command"
|
|
9
|
+
autoload :CommandStdOut, "brut/tui/script/events/command_std_out"
|
|
10
|
+
autoload :CommandStdErr, "brut/tui/script/events/command_std_err"
|
|
11
|
+
autoload :CommandExecutionSucceeded , "brut/tui/script/events/command_execution_succeeded"
|
|
12
|
+
autoload :CommandExecutionFailed , "brut/tui/script/events/command_execution_failed"
|
|
13
|
+
autoload :Message , "brut/tui/script/events/message"
|
|
14
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "open3"
|
|
2
|
+
|
|
3
|
+
# A step whose behavior is to run a command as a child process.
|
|
4
|
+
# Fires {Brut::TUI::Script::Events::StepStarted} then
|
|
5
|
+
# {Brut::TUI::Script::Events::ExecutingCommand} before the command is run.
|
|
6
|
+
# The command is then run with `Open3.popen3` and the stderr and stdout
|
|
7
|
+
# are streamed as output is available. Each block of bytes read
|
|
8
|
+
# generates a {Brut::TUI::Script::Events::CommandStdOut}
|
|
9
|
+
# or a {Brut::TUI::Script::Events::CommandStdErr} event with
|
|
10
|
+
# the bytes reads.
|
|
11
|
+
#
|
|
12
|
+
# If the command exited nonzero,
|
|
13
|
+
# {Brut::TUI::Script::Events::CommandExecutionSucceeded} is fired,
|
|
14
|
+
# otherwise {Brut::TUI::Script::Events::CommandExecutionFailed} is fired,
|
|
15
|
+
# Either way, {Brut::TUI::Script::Events::StepCompleted} is fired
|
|
16
|
+
# *unless* there is an unhandled exception.
|
|
17
|
+
class Brut::TUI::Script::ExecStep < Brut::TUI::Script::Step
|
|
18
|
+
|
|
19
|
+
attr_reader :command
|
|
20
|
+
def initialize(event_loop, description, command:)
|
|
21
|
+
super(event_loop, description)
|
|
22
|
+
@command = command
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def deconstruct_keys(keys=nil)
|
|
26
|
+
super.deconstruct_keys(keys).merge({ command: @command, strip_ansi: false })
|
|
27
|
+
end
|
|
28
|
+
def run!
|
|
29
|
+
event_loop << Events::StepStarted.new(step: self)
|
|
30
|
+
event_loop << Events::ExecutingCommand.new(step: self)
|
|
31
|
+
|
|
32
|
+
wait_thread = Open3.popen3(*@command) do |_stdin,stdout,stderr,wait_thread|
|
|
33
|
+
o = stdout.read_nonblock(10, exception: false)
|
|
34
|
+
e = stderr.read_nonblock(10, exception: false)
|
|
35
|
+
while o || e
|
|
36
|
+
if o
|
|
37
|
+
if o != :wait_readable
|
|
38
|
+
event_loop << Events::CommandStdOut.new(step: self, output: o)
|
|
39
|
+
end
|
|
40
|
+
o = stdout.read_nonblock(10, exception: false)
|
|
41
|
+
end
|
|
42
|
+
if e
|
|
43
|
+
if e != :wait_readable
|
|
44
|
+
event_loop << Events::CommandStdErr.new(step: self, output: e)
|
|
45
|
+
end
|
|
46
|
+
e = stderr.read_nonblock(10, exception: false)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
wait_thread
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if wait_thread.value.success?
|
|
53
|
+
event_loop << Events::CommandExecutionSucceeded.new(step: self)
|
|
54
|
+
else
|
|
55
|
+
event_loop << Events::CommandExecutionFailed.new(step: self)
|
|
56
|
+
end
|
|
57
|
+
event_loop << Events::StepCompleted.new(step: self)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# A subscriber that uses Ruby's logger to log messages.
|
|
2
|
+
# The purpose of this is to ensure that every bit of available information about
|
|
3
|
+
# a `Script` is placed somewhere for later review. This allows any output to the terminal
|
|
4
|
+
# to be more brief or user-friendly without losing information.
|
|
5
|
+
class Brut::TUI::Script::LoggingSubscriber
|
|
6
|
+
def initialize(progname, logfile:)
|
|
7
|
+
@logger = Logger.new(logfile, progname:)
|
|
8
|
+
@logger.formatter = proc { |severity, time, progname, msg|
|
|
9
|
+
"#{time} - [ #{progname} ] #{severity}: #{strip_ansi(msg)}\n"
|
|
10
|
+
}
|
|
11
|
+
@stdout = {}
|
|
12
|
+
@stderr = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def on_event_loop_started(event)
|
|
16
|
+
@logger.debug("TUI Event loop started")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def on_exception(event)
|
|
20
|
+
@logger.error("#{event.exit? ? 'FATAL' : 'non-fatal'} ExceptionEvent: #{event.exception.class}: #{event.exception.message}\n #{event.exception.backtrace.join("\n ")}")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def on_executing_command(command:)
|
|
24
|
+
@logger.info("Executing command `#{command}`")
|
|
25
|
+
@stdout[command] = ''
|
|
26
|
+
@stderr[command] = ''
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def on_command_std_out(command:, output:)
|
|
30
|
+
@stdout[command] << output
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def on_command_std_err(command:, output:)
|
|
34
|
+
@stderr[command] << output
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def on_command_execution_succeeded(command:)
|
|
38
|
+
if !@stdout[command].empty?
|
|
39
|
+
@logger.info("\n#{strip_ansi(@stdout[command])}")
|
|
40
|
+
end
|
|
41
|
+
if !@stderr[command].empty?
|
|
42
|
+
@logger.warn("\n#{strip_ansi(@stderr[command])}")
|
|
43
|
+
end
|
|
44
|
+
@logger.info("Command `#{command}` executed successfully.")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def on_command_execution_failed(command:)
|
|
48
|
+
if !@stdout[command].empty?
|
|
49
|
+
@logger.info("\n#{strip_ansi(@stdout[command])}")
|
|
50
|
+
end
|
|
51
|
+
if !@stderr[command].empty?
|
|
52
|
+
@logger.warn("\n#{strip_ansi(@stderr[command])}")
|
|
53
|
+
end
|
|
54
|
+
@logger.error("Command `#{command}` failed.")
|
|
55
|
+
raise "DOH"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def on_model_updated(*)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def on_tick(*)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def on_script_completed(*)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def on_script_started(*)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def on_any_event(event)
|
|
71
|
+
case event
|
|
72
|
+
in { description: }
|
|
73
|
+
@logger.info(description)
|
|
74
|
+
in { message:, type: :warning }
|
|
75
|
+
@logger.warn(message)
|
|
76
|
+
in { message:, type: :error }
|
|
77
|
+
@logger.error(message)
|
|
78
|
+
in { message: }
|
|
79
|
+
@logger.info(message)
|
|
80
|
+
in { handler_method_name: }
|
|
81
|
+
@logger.info(handler_method_name)
|
|
82
|
+
else
|
|
83
|
+
@logger.info(event.to_s)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
ANSI_ESCAPE = %r{
|
|
89
|
+
\e\[[@-Z\\-_] | # 1-byte sequences
|
|
90
|
+
\e\[[0-?]*[ -\/]*[@-~] | # CSI sequences
|
|
91
|
+
\e\][^\a]*\a | # OSC sequences
|
|
92
|
+
\eP[^\a]*\a | # DCS
|
|
93
|
+
\e_[^\a]*\a | # APC
|
|
94
|
+
\e\^[^\a]*\a # PM
|
|
95
|
+
}x
|
|
96
|
+
|
|
97
|
+
def strip_ansi(string) = string.gsub(ANSI_ESCAPE, '')
|
|
98
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# A subscriber that attempts to provide a user-friend, brief, summarized, fancy
|
|
2
|
+
# output for the script that is running. It is intended to show you
|
|
3
|
+
# what's happening at a high level, but deferring all details (like child process
|
|
4
|
+
# output) to the {Brut::TUI::Script::LoggingSubscriber}'s log file.
|
|
5
|
+
class Brut::TUI::Script::PutsSubscriber
|
|
6
|
+
def initialize(progname, terminal:, theme:, stdout: false, stderr: false)
|
|
7
|
+
@progname = progname
|
|
8
|
+
@terminal = terminal
|
|
9
|
+
@theme = theme
|
|
10
|
+
@prefix_recent = false
|
|
11
|
+
@stdout = stdout
|
|
12
|
+
@stderr = stderr
|
|
13
|
+
@stdout_buffer = {}
|
|
14
|
+
@stderr_buffer = {}
|
|
15
|
+
@step_indent = "Phase 1/1 ".length
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def on_phase_started(description:, step_number:, total_steps:)
|
|
19
|
+
total_format_string = if total_steps < 10
|
|
20
|
+
"%1d"
|
|
21
|
+
else
|
|
22
|
+
"%2d"
|
|
23
|
+
end
|
|
24
|
+
preamble = sprintf("Phase %d/#{total_format_string}", step_number, total_steps)
|
|
25
|
+
@step_indent = preamble.length + 1
|
|
26
|
+
println @theme.reset + @theme.bold + preamble + @theme.reset + " " + @theme.with_markup(description, text: :heading)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def on_step_started(description:)
|
|
30
|
+
println @theme.with_markup(description), step_indent: true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def on_executing_command(command:)
|
|
34
|
+
println @theme.with_markup("> `#{command}`"), step_indent: true
|
|
35
|
+
$stdout.print @theme.reset
|
|
36
|
+
@stdout_buffer[command] = ""
|
|
37
|
+
@stderr_buffer[command] = ""
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def on_command_execution_failed(command:)
|
|
41
|
+
println @theme.with_markup("FAILED", text: :error), step_indent: true
|
|
42
|
+
if !@stdout
|
|
43
|
+
println ""
|
|
44
|
+
println @stdout_buffer[command] + "\n"
|
|
45
|
+
end
|
|
46
|
+
if !@stderr
|
|
47
|
+
println ""
|
|
48
|
+
println @theme.warning + @stderr_buffer[command]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def on_exception(exception:)
|
|
54
|
+
println @theme.error + "Exception: #{exception.class}: #{exception.message}\n #{exception.backtrace.join("\n ")}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def on_command_std_out(output:, command:)
|
|
58
|
+
if @stdout
|
|
59
|
+
@prefix_recent = false
|
|
60
|
+
$stdout.print @theme.normal + @theme.weak + output + @theme.reset
|
|
61
|
+
$stdout.flush
|
|
62
|
+
else
|
|
63
|
+
@stdout_buffer[command] << output
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def on_command_std_err(output:, command:)
|
|
68
|
+
if @stderr
|
|
69
|
+
@prefix_recent = false
|
|
70
|
+
$stdout.print @theme.warning + output + @theme.reset
|
|
71
|
+
$stdout.flush
|
|
72
|
+
else
|
|
73
|
+
@stderr_buffer[command] << output
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def on_command_execution_succeeded(command:)
|
|
78
|
+
println @theme.with_markup("✅", text: :success), step_indent: true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def on_message(message:, type:)
|
|
82
|
+
if type == :done
|
|
83
|
+
println @theme.with_markup(message, text: :success)
|
|
84
|
+
else
|
|
85
|
+
println @theme.with_markup(message, text: type), step_indent: true
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def prefix_string = "[ #{@progname} ] "
|
|
93
|
+
|
|
94
|
+
def step_indent = @step_indent
|
|
95
|
+
|
|
96
|
+
def println(message, step_indent: false)
|
|
97
|
+
@terminal.io.puts prefix + (step_indent ? " " * self.step_indent : "") + message
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def prefix
|
|
101
|
+
if @prefix_recent
|
|
102
|
+
@theme.italic + @theme.weak + prefix_string + @theme.weak_off + @theme.italic_off
|
|
103
|
+
else
|
|
104
|
+
@prefix_recent = true
|
|
105
|
+
prefix_string
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Base class for specific types of steps
|
|
2
|
+
class Brut::TUI::Script::Step
|
|
3
|
+
|
|
4
|
+
Events = Brut::TUI::Script::Events
|
|
5
|
+
|
|
6
|
+
attr_reader :description
|
|
7
|
+
def initialize(event_loop, description, exec: nil, &block)
|
|
8
|
+
@event_loop = event_loop
|
|
9
|
+
@description = description
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private attr_reader :event_loop
|
|
13
|
+
end
|