brut 0.16.0.pre.pre → 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 +9 -3
- data/Gemfile.lock +44 -34
- 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 +311 -2
- data/brut-js/package.json +2 -1
- data/brut.gemspec +2 -0
- data/brutrb.com/jobs.md +1 -1
- data/docs/404.html +2 -2
- data/docs/adrs.html +4 -4
- data/docs/ai.html +4 -4
- data/docs/api/Brut/BackEnd/SeedData.html +2 -2
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +2 -2
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +2 -2
- data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +2 -2
- data/docs/api/Brut/BackEnd/Sidekiq.html +2 -2
- data/docs/api/Brut/BackEnd/Validators/FormValidator.html +2 -2
- data/docs/api/Brut/BackEnd/Validators.html +2 -2
- data/docs/api/Brut/BackEnd.html +2 -2
- data/docs/api/Brut/CLI/App.html +2 -2
- data/docs/api/Brut/CLI/AppRunner.html +2 -2
- 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 +3 -3
- data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +2 -2
- data/docs/api/Brut/CLI/Apps/BuildAssets.html +2 -2
- 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 +2 -2
- data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +2 -2
- 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 +2 -2
- data/docs/api/Brut/CLI/Apps/DB.html +2 -2
- data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +2 -2
- data/docs/api/Brut/CLI/Apps/DeployBase.html +2 -2
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +8 -6
- data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +2 -2
- 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/DbModel.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 +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +2 -2
- data/docs/api/Brut/CLI/Apps/Scaffold.html +2 -2
- data/docs/api/Brut/CLI/Apps/Test/Audit.html +15 -7
- data/docs/api/Brut/CLI/Apps/Test/E2e.html +2 -2
- data/docs/api/Brut/CLI/Apps/Test/JS.html +2 -2
- data/docs/api/Brut/CLI/Apps/Test/Run.html +2 -2
- data/docs/api/Brut/CLI/Apps/Test.html +2 -2
- data/docs/api/Brut/CLI/Apps.html +2 -2
- data/docs/api/Brut/CLI/Command.html +2 -2
- data/docs/api/Brut/CLI/Error.html +2 -2
- data/docs/api/Brut/CLI/ExecutionResults/Result.html +2 -2
- data/docs/api/Brut/CLI/ExecutionResults.html +2 -2
- data/docs/api/Brut/CLI/Executor.html +2 -2
- data/docs/api/Brut/CLI/InvalidOption.html +2 -2
- data/docs/api/Brut/CLI/Options.html +2 -2
- data/docs/api/Brut/CLI/Output.html +2 -2
- data/docs/api/Brut/CLI/SystemExecError.html +2 -2
- data/docs/api/Brut/CLI.html +2 -2
- data/docs/api/Brut/FactoryBot.html +2 -2
- data/docs/api/Brut/Framework/App.html +2 -2
- data/docs/api/Brut/Framework/Config.html +2 -2
- data/docs/api/Brut/Framework/Container.html +11 -7
- data/docs/api/Brut/Framework/Error.html +2 -2
- data/docs/api/Brut/Framework/Errors/AbstractMethod.html +2 -2
- data/docs/api/Brut/Framework/Errors/Bug.html +2 -2
- data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +2 -2
- data/docs/api/Brut/Framework/Errors/MissingParameter.html +2 -2
- data/docs/api/Brut/Framework/Errors/NoClassForPath.html +2 -2
- data/docs/api/Brut/Framework/Errors/NotFound.html +2 -2
- data/docs/api/Brut/Framework/Errors/NotImplemented.html +2 -2
- data/docs/api/Brut/Framework/Errors.html +2 -2
- data/docs/api/Brut/Framework/FussyTypeEnforcement.html +2 -2
- data/docs/api/Brut/Framework/MCP.html +3 -3
- data/docs/api/Brut/Framework/ProjectEnvironment.html +2 -2
- data/docs/api/Brut/Framework.html +2 -2
- data/docs/api/Brut/FrontEnd/AssetPathResolver.html +2 -2
- data/docs/api/Brut/FrontEnd/Component/Helpers.html +2 -2
- data/docs/api/Brut/FrontEnd/Component.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/FormTag.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Input.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Inputs.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/TimeTag.html +2 -2
- data/docs/api/Brut/FrontEnd/Components/Traceparent.html +2 -2
- data/docs/api/Brut/FrontEnd/Components.html +2 -2
- data/docs/api/Brut/FrontEnd/CsrfProtector.html +2 -2
- data/docs/api/Brut/FrontEnd/Download.html +2 -2
- data/docs/api/Brut/FrontEnd/Flash.html +2 -2
- data/docs/api/Brut/FrontEnd/Form.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/Button.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/Input.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +2 -2
- data/docs/api/Brut/FrontEnd/Forms.html +2 -2
- data/docs/api/Brut/FrontEnd/GenericResponse.html +2 -2
- data/docs/api/Brut/FrontEnd/Handler.html +2 -2
- data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +2 -2
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +2 -2
- data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +2 -2
- data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +2 -2
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +2 -2
- data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +2 -2
- data/docs/api/Brut/FrontEnd/Handlers.html +2 -2
- data/docs/api/Brut/FrontEnd/HandlingResults.html +2 -2
- data/docs/api/Brut/FrontEnd/HttpMethod.html +2 -2
- data/docs/api/Brut/FrontEnd/HttpStatus.html +2 -2
- data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +2 -2
- data/docs/api/Brut/FrontEnd/Layout.html +2 -2
- data/docs/api/Brut/FrontEnd/Middleware.html +2 -2
- data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +2 -2
- data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +2 -2
- data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +2 -2
- data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +2 -2
- data/docs/api/Brut/FrontEnd/Middlewares.html +2 -2
- data/docs/api/Brut/FrontEnd/Page.html +2 -2
- data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +2 -2
- data/docs/api/Brut/FrontEnd/Pages.html +2 -2
- data/docs/api/Brut/FrontEnd/RequestContext.html +2 -2
- data/docs/api/Brut/FrontEnd/RouteHook.html +2 -2
- data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +2 -2
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +2 -2
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +2 -2
- data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +2 -2
- data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +2 -2
- data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +2 -2
- data/docs/api/Brut/FrontEnd/RouteHooks.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing/Route.html +2 -2
- data/docs/api/Brut/FrontEnd/Routing.html +2 -2
- data/docs/api/Brut/FrontEnd/Session.html +2 -2
- data/docs/api/Brut/FrontEnd.html +2 -2
- data/docs/api/Brut/I18n/BaseMethods.html +2 -2
- data/docs/api/Brut/I18n/ForBackEnd.html +2 -2
- data/docs/api/Brut/I18n/ForCLI.html +2 -2
- data/docs/api/Brut/I18n/ForHTML.html +2 -2
- data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +2 -2
- data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +2 -2
- data/docs/api/Brut/I18n.html +2 -2
- data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +2 -2
- data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +2 -2
- data/docs/api/Brut/Instrumentation/Methods.html +2 -2
- data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +2 -2
- data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +2 -2
- data/docs/api/Brut/Instrumentation/OpenTelemetry.html +2 -2
- data/docs/api/Brut/Instrumentation.html +2 -2
- data/docs/api/Brut/RubocopConfig.html +2 -2
- data/docs/api/Brut/SinatraHelpers/ClassMethods.html +2 -2
- data/docs/api/Brut/SinatraHelpers.html +2 -2
- data/docs/api/Brut/SpecSupport/ClockSupport.html +2 -2
- data/docs/api/Brut/SpecSupport/ComponentSupport.html +2 -2
- data/docs/api/Brut/SpecSupport/E2ETestServer.html +2 -2
- data/docs/api/Brut/SpecSupport/E2eSupport.html +2 -2
- data/docs/api/Brut/SpecSupport/EnhancedNode.html +2 -2
- data/docs/api/Brut/SpecSupport/FlashSupport.html +2 -2
- data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +2 -2
- data/docs/api/Brut/SpecSupport/GeneralSupport.html +2 -2
- data/docs/api/Brut/SpecSupport/HandlerSupport.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +2 -2
- data/docs/api/Brut/SpecSupport/Matchers.html +2 -2
- data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +2 -2
- data/docs/api/Brut/SpecSupport/RSpecSetup.html +2 -2
- data/docs/api/Brut/SpecSupport/SessionSupport.html +2 -2
- data/docs/api/Brut/SpecSupport.html +2 -2
- 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 +4 -4
- data/docs/api/Clock.html +2 -2
- data/docs/api/ModuleName.html +2 -2
- data/docs/api/RichString.html +2 -2
- data/docs/api/SemanticLogger/Appender/Async.html +2 -2
- data/docs/api/Sequel/Extensions/BrutInstrumentation.html +2 -2
- data/docs/api/Sequel/Extensions/BrutMigrations.html +2 -2
- data/docs/api/Sequel/Extensions.html +2 -2
- data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +2 -2
- data/docs/api/Sequel/Plugins/CreatedAt.html +2 -2
- data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +2 -2
- data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +2 -2
- data/docs/api/Sequel/Plugins/ExternalId.html +2 -2
- data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +2 -2
- data/docs/api/Sequel/Plugins/FindBang.html +2 -2
- data/docs/api/Sequel/Plugins.html +2 -2
- data/docs/api/Sequel.html +2 -2
- data/docs/api/_index.html +247 -2
- data/docs/api/class_list.html +1 -1
- data/docs/api/file.README.html +2 -2
- data/docs/api/index.html +2 -2
- data/docs/api/method_list.html +1551 -431
- data/docs/api/top-level-namespace.html +2 -2
- data/docs/assets/{app.Dm7v_ouO.js → app.B8jAEB7R.js} +1 -1
- data/docs/assets/chunks/@localSearchIndexroot.DJ8mocCj.js +1 -0
- data/docs/assets/chunks/{VPLocalSearchBox.DQK6jQou.js → VPLocalSearchBox.gF-Po_fz.js} +1 -1
- data/docs/assets/chunks/{theme.BuExsdM9.js → theme.BjPAOJkz.js} +2 -2
- data/docs/assets/{components.md.Dfd3w6UW.js → components.md.Ber8UBM0.js} +3 -3
- data/docs/assets/{configuration.md.DTYoV2Ea.js → configuration.md.DrJ6YVoZ.js} +1 -1
- data/docs/assets/{deployment.md.C1u5ep0g.js → deployment.md.CHTx2eTR.js} +10 -3
- data/docs/assets/{deployment.md.C1u5ep0g.lean.js → deployment.md.CHTx2eTR.lean.js} +1 -1
- data/docs/assets/{forms.md.DEkmJUvb.js → forms.md.RK0zkhm0.js} +2 -2
- data/docs/assets/{forms.md.DEkmJUvb.lean.js → forms.md.RK0zkhm0.lean.js} +1 -1
- data/docs/assets/{getting-started.md.DO-4eoGW.js → getting-started.md.CGJ44juQ.js} +2 -2
- data/docs/assets/jobs.md.Bi3qb3v6.js +25 -0
- data/docs/assets/jobs.md.Bi3qb3v6.lean.js +1 -0
- data/docs/assets/recipes_dev-env-secrets.md.DC_jVY9U.js +12 -0
- data/docs/assets/recipes_dev-env-secrets.md.DC_jVY9U.lean.js +1 -0
- data/docs/assets/roadmap.md.DqC1Y7Zt.js +1 -0
- data/docs/assets/{roadmap.md.CJsbUmK_.lean.js → roadmap.md.DqC1Y7Zt.lean.js} +1 -1
- data/docs/assets/{tutorials_02-dialog.md.D2vSjDVf.js → tutorials_02-dialog.md.DE5WfCXI.js} +1 -1
- data/docs/assets.html +4 -4
- 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 +4 -4
- data/docs/business-logic.html +4 -4
- data/docs/cli.html +4 -4
- data/docs/components.html +8 -8
- data/docs/configuration.html +5 -5
- data/docs/css.html +4 -4
- data/docs/custom-element-tests.html +4 -4
- data/docs/database-access.html +4 -4
- data/docs/database-schema.html +4 -4
- data/docs/deployment.html +13 -6
- data/docs/dev-environment.html +4 -4
- data/docs/dir-structure.html +4 -4
- data/docs/doc-conventions.html +4 -4
- data/docs/end-to-end-tests.html +4 -4
- data/docs/features.html +4 -4
- data/docs/flash-and-session.html +4 -4
- data/docs/form-constraints.html +4 -4
- data/docs/forms.html +6 -6
- data/docs/getting-started.html +7 -7
- data/docs/handlers.html +4 -4
- data/docs/hashmap.json +1 -1
- data/docs/hooks.html +4 -4
- data/docs/i18n.html +4 -4
- data/docs/index.html +3 -3
- data/docs/instrumentation.html +4 -4
- data/docs/javascript.html +4 -4
- data/docs/jobs.html +29 -5
- data/docs/keyword-injection.html +4 -4
- data/docs/layouts.html +4 -4
- data/docs/lsp.html +4 -4
- data/docs/markdown-examples.html +4 -4
- data/docs/middleware.html +4 -4
- data/docs/overview.html +4 -4
- data/docs/pages.html +4 -4
- data/docs/recipes/alternate-layouts.html +5 -5
- data/docs/recipes/authentication.html +5 -5
- data/docs/recipes/custom-flash.html +5 -5
- data/docs/recipes/dev-env-secrets.html +40 -0
- data/docs/recipes/form-errors.html +5 -5
- data/docs/recipes/indexed-forms.html +5 -5
- data/docs/recipes/migrations.html +5 -5
- data/docs/recipes/text-field-component.html +5 -5
- data/docs/roadmap.html +5 -5
- data/docs/routes.html +4 -4
- data/docs/security.html +4 -4
- data/docs/seed-data.html +4 -4
- data/docs/space-time-continuum.html +4 -4
- data/docs/tutorial.html +4 -4
- data/docs/tutorials/01-intro.html +4 -4
- data/docs/tutorials/02-dialog.html +5 -5
- data/docs/unit-tests.html +4 -4
- data/docs/why.html +4 -4
- 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 +2 -1
- data/mkbrut/lib/mkbrut/version.rb +1 -1
- data/mkbrut/templates/segments/Heroku/deploy/Dockerfile +9 -7
- data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +2 -2
- 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 +131 -21
- data/docs/assets/chunks/@localSearchIndexroot.BiNFswvo.js +0 -1
- data/docs/assets/jobs.md.Bc7Y1YpK.js +0 -1
- data/docs/assets/jobs.md.Bc7Y1YpK.lean.js +0 -1
- data/docs/assets/roadmap.md.CJsbUmK_.js +0 -1
- /data/docs/assets/{components.md.Dfd3w6UW.lean.js → components.md.Ber8UBM0.lean.js} +0 -0
- /data/docs/assets/{configuration.md.DTYoV2Ea.lean.js → configuration.md.DrJ6YVoZ.lean.js} +0 -0
- /data/docs/assets/{getting-started.md.DO-4eoGW.lean.js → getting-started.md.CGJ44juQ.lean.js} +0 -0
- /data/docs/assets/{tutorials_02-dialog.md.D2vSjDVf.lean.js → tutorials_02-dialog.md.DE5WfCXI.lean.js} +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# A terminal theme is a set of semantic styles for the terminal that
|
|
2
|
+
# map to actual colors or styles based on the terminal's metadata or
|
|
3
|
+
# the user's preferences or both.
|
|
4
|
+
#
|
|
5
|
+
# This class is coupled to the behavior of ANSI escape
|
|
6
|
+
# codes in the terminal, in that they aren't markup, but more
|
|
7
|
+
# like drawing with a pen.
|
|
8
|
+
|
|
9
|
+
# That said, this class' API does abstract from the codes
|
|
10
|
+
# themselves and creates a semantic layer, for example
|
|
11
|
+
# the definition of "succes" or "weak".
|
|
12
|
+
|
|
13
|
+
# For subclass implementors, there are various private
|
|
14
|
+
# methods that do encapsulate the ANSI escape codes, and these
|
|
15
|
+
# can be used or overridden.
|
|
16
|
+
#
|
|
17
|
+
# This particular implementation avoids the use of black or white, and uses
|
|
18
|
+
# the normal ANSI colors. This should, in theory, work with any terminal
|
|
19
|
+
# theme where all colors are legible on the chosen background.
|
|
20
|
+
class Brut::TUI::TerminalTheme
|
|
21
|
+
|
|
22
|
+
def self.based_on_background(terminal)
|
|
23
|
+
if dark_background?(terminal)
|
|
24
|
+
Brut::TUI::Themes::Dark.new(terminal)
|
|
25
|
+
else
|
|
26
|
+
Brut::TUI::Themes::Light.new(terminal)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.dark_background?(terminal)
|
|
31
|
+
r, g, b = terminal.background_color.map { it / 255.0 }
|
|
32
|
+
luminance = (0.21 * r) + (0.72 * g) + (0.07 * b)
|
|
33
|
+
|
|
34
|
+
return luminance < 0.5
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize(terminal)
|
|
38
|
+
@terminal = terminal
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns a string with its markup turned into escape codes.
|
|
42
|
+
# Note that due to the way ANSI escape codes work, the state
|
|
43
|
+
# of the terminal may not be in the same state you found it.
|
|
44
|
+
#
|
|
45
|
+
# This method tries to turn features on and off (e.g. after a bold
|
|
46
|
+
# string, the codes to turn off bold are applied), but if a subclass
|
|
47
|
+
# mixes colors and styles, any text output after this one may
|
|
48
|
+
# not look like the text before it.
|
|
49
|
+
def with_markup(string, text: :normal, reset: true)
|
|
50
|
+
result = +""
|
|
51
|
+
result << send(text)
|
|
52
|
+
Brut::TUI::MarkupString.from_string(string).parse do |directive, value|
|
|
53
|
+
case directive
|
|
54
|
+
in :start
|
|
55
|
+
case value
|
|
56
|
+
in :bold
|
|
57
|
+
result << bold
|
|
58
|
+
in :strike
|
|
59
|
+
result << strike
|
|
60
|
+
in :code
|
|
61
|
+
result << code
|
|
62
|
+
in :weak
|
|
63
|
+
result << weak
|
|
64
|
+
end
|
|
65
|
+
in :stop
|
|
66
|
+
case value
|
|
67
|
+
in :bold
|
|
68
|
+
result << bold_off << send(text)
|
|
69
|
+
in :strike
|
|
70
|
+
result << strike_off << send(text)
|
|
71
|
+
in :code
|
|
72
|
+
result << code_off << send(text)
|
|
73
|
+
in :weak
|
|
74
|
+
result << weak_off << send(text)
|
|
75
|
+
end
|
|
76
|
+
in :text
|
|
77
|
+
result << value
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
if reset
|
|
81
|
+
result << self.reset
|
|
82
|
+
end
|
|
83
|
+
result
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def success = bold + bright_green
|
|
87
|
+
def error = bold + bright_red
|
|
88
|
+
def warning = bold + yellow
|
|
89
|
+
|
|
90
|
+
def bold = esc("1")
|
|
91
|
+
def bold_off = normal
|
|
92
|
+
|
|
93
|
+
def bright = esc("1")
|
|
94
|
+
def bright_off = normal
|
|
95
|
+
|
|
96
|
+
def italic = esc("3")
|
|
97
|
+
def italic_off = esc("23")
|
|
98
|
+
|
|
99
|
+
def strike = esc("9")
|
|
100
|
+
def strike_off = esc("29")
|
|
101
|
+
|
|
102
|
+
def weak = esc("2")
|
|
103
|
+
def weak_off = normal
|
|
104
|
+
|
|
105
|
+
def code = underline
|
|
106
|
+
def code_off = underline_off
|
|
107
|
+
|
|
108
|
+
def normal = esc("22")
|
|
109
|
+
|
|
110
|
+
def reset = esc("0")
|
|
111
|
+
|
|
112
|
+
def heading = bold + underline + bright_blue
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def underline = esc("4")
|
|
117
|
+
def underline_off = esc("24")
|
|
118
|
+
|
|
119
|
+
def black = esc("30")
|
|
120
|
+
def red = esc("31")
|
|
121
|
+
def green = esc("32")
|
|
122
|
+
def yellow = esc("33")
|
|
123
|
+
def blue = esc("34")
|
|
124
|
+
def magenta = esc("35")
|
|
125
|
+
def cyan = esc("36")
|
|
126
|
+
def white = esc("37")
|
|
127
|
+
|
|
128
|
+
def bright_black = esc("90")
|
|
129
|
+
def bright_red = esc("91")
|
|
130
|
+
def bright_green = esc("92")
|
|
131
|
+
def bright_yellow = esc("93")
|
|
132
|
+
def bright_blue = esc("94")
|
|
133
|
+
def bright_magenta = esc("95")
|
|
134
|
+
def bright_cyan = esc("96")
|
|
135
|
+
def bright_white = esc("97")
|
|
136
|
+
|
|
137
|
+
def esc(code)
|
|
138
|
+
"\e[#{code}m"
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# A theme designed to work on a dark background terminal.
|
|
2
|
+
class Brut::TUI::Themes::Dark < Brut::TUI::TerminalTheme
|
|
3
|
+
def black = esc("38;2;96;96;96")
|
|
4
|
+
def bright_black = esc("38;2;128;128;128")
|
|
5
|
+
def white = esc("38;2;233;233;233")
|
|
6
|
+
def bright_white = esc("38;2;255;255;255")
|
|
7
|
+
def yellow = esc("38;2;191;191;0")
|
|
8
|
+
def bright_yellow = esc("38;2;255;255;0")
|
|
9
|
+
def normal = super + white
|
|
10
|
+
def code = esc("38;2;0;255;0")
|
|
11
|
+
def code_off = normal
|
|
12
|
+
def bright = bright_white
|
|
13
|
+
def bright_off = normal
|
|
14
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# A theme designed to work on a white background terminal.
|
|
2
|
+
class Brut::TUI::Themes::Light < Brut::TUI::TerminalTheme
|
|
3
|
+
def black = esc("38;2;0;0;0")
|
|
4
|
+
def bright_black = esc("38;2;64;64;64")
|
|
5
|
+
def bright_white = esc("38;2;128;128;128")
|
|
6
|
+
def white = esc("38;2;191;191;191")
|
|
7
|
+
def yellow = esc("38;2;191;191;0")
|
|
8
|
+
def bright_yellow = esc("38;2;255;255;0")
|
|
9
|
+
def normal = super + black
|
|
10
|
+
def code = esc("38;2;0;128;0")
|
|
11
|
+
def code_off = normal
|
|
12
|
+
def green = esc("38;2;25;105;0")
|
|
13
|
+
def bright_green = esc("38;2;34;155;0")
|
|
14
|
+
def bright = bright_blue
|
|
15
|
+
def bright_off = normal
|
|
16
|
+
end
|
|
17
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# A theme without any ANSI styling at all. This is suitable
|
|
2
|
+
# for showing to a user who does not want any colors or styles, but
|
|
3
|
+
# still wants to see output in a somewhat conventional way.
|
|
4
|
+
class Brut::TUI::Themes::None < Brut::TUI::TerminalTheme
|
|
5
|
+
def esc(*) = ""
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
data/lib/brut/tui.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Brut
|
|
2
|
+
# Brut provides a basic API for creating rich text user interfaces. Currently, Brut
|
|
3
|
+
# provides {Brut::TUI::Script} to create more user-friendly scripts like `bin/setup` or
|
|
4
|
+
# `bin/ci`.
|
|
5
|
+
module TUI
|
|
6
|
+
autoload(:EventLoop, "brut/tui/event_loop")
|
|
7
|
+
autoload(:Events, "brut/tui/events")
|
|
8
|
+
autoload(:AnsiEscapeCode, "brut/tui/ansi_escape_code")
|
|
9
|
+
autoload(:Script, "brut/tui/script")
|
|
10
|
+
autoload(:Terminal, "brut/tui/terminal")
|
|
11
|
+
autoload(:TerminalTheme, "brut/tui/terminal_theme")
|
|
12
|
+
autoload(:Themes, "brut/tui/themes")
|
|
13
|
+
autoload(:MarkupString, "brut/tui/markup_string")
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/brut/version.rb
CHANGED
data/lib/brut.rb
CHANGED
|
@@ -16,5 +16,6 @@ module Brut
|
|
|
16
16
|
autoload(:Instrumentation,"brut/instrumentation")
|
|
17
17
|
autoload(:SinatraHelpers, "brut/sinatra_helpers")
|
|
18
18
|
# DO NOT autoload(:CLI) - that is intended to be require-able on its own
|
|
19
|
+
# DO NOT autoload(:TUI) - that is intended to be require-able on its own
|
|
19
20
|
end
|
|
20
21
|
require "sequel/plugins"
|
data/mkbrut/Gemfile.lock
CHANGED
|
@@ -55,8 +55,8 @@ RUN --mount=type=cache,target=/var/cache/apt \
|
|
|
55
55
|
#
|
|
56
56
|
# Incancation is based on: https://www.postgresql.org/download/linux/debian/
|
|
57
57
|
RUN /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
|
|
58
|
-
apt-get update && \
|
|
59
|
-
apt-get --yes --quiet install postgresql-client-16 && \
|
|
58
|
+
apt-get update --quiet --yes && \
|
|
59
|
+
apt-get --yes --quiet --no-install-recommends install postgresql-client-16 && \
|
|
60
60
|
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
61
61
|
|
|
62
62
|
# Set basic env vars for production
|
|
@@ -65,6 +65,8 @@ ENV RACK_ENV="production" \
|
|
|
65
65
|
BUNDLE_PATH="/usr/local/bundle" \
|
|
66
66
|
BUNDLE_WITHOUT="development"
|
|
67
67
|
|
|
68
|
+
RUN rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
69
|
+
|
|
68
70
|
# This makes a new image that we'll throw away after building
|
|
69
71
|
# needed artifacts.
|
|
70
72
|
FROM base AS build
|
|
@@ -82,9 +84,11 @@ RUN --mount=type=cache,target=/var/cache/apt \
|
|
|
82
84
|
libpq-dev \
|
|
83
85
|
pkg-config
|
|
84
86
|
|
|
85
|
-
#
|
|
87
|
+
# Copy NodeJS from the official image. This should be faster than
|
|
88
|
+
# installing it via e.g. nvm
|
|
86
89
|
COPY --from=node:22-slim /usr/local /usr/local
|
|
87
90
|
|
|
91
|
+
# Copy Gemfile only as it may not change between deploys
|
|
88
92
|
COPY Gemfile Gemfile.lock .
|
|
89
93
|
|
|
90
94
|
# Install RubyGems from app's Gemfile
|
|
@@ -99,9 +103,7 @@ RUN npm clean-install --no-audit --no-fund --verbose
|
|
|
99
103
|
COPY . .
|
|
100
104
|
|
|
101
105
|
# Build all assets
|
|
102
|
-
RUN
|
|
103
|
-
nvm use default && \
|
|
104
|
-
bin/build-assets && \
|
|
106
|
+
RUN bin/build-assets && \
|
|
105
107
|
rm -rf node_modules
|
|
106
108
|
|
|
107
109
|
# We are now switching back to building the image that will be deployed.
|
|
@@ -118,7 +120,7 @@ RUN groupadd --system --gid 1000 brut && \
|
|
|
118
120
|
USER 1000:1000
|
|
119
121
|
|
|
120
122
|
ENV APP_GIT_SHA1="${app_git_sha1}"
|
|
123
|
+
|
|
121
124
|
# This is used to execute other commands. When the app is run in production,
|
|
122
125
|
# this script is used to run it.
|
|
123
126
|
ENTRYPOINT ["/brut-app/deploy/docker-entrypoint"]
|
|
124
|
-
|
|
@@ -13,12 +13,12 @@ class HerokuConfig
|
|
|
13
13
|
# }
|
|
14
14
|
# }
|
|
15
15
|
#
|
|
16
|
-
# For example, if you have the Sidekiq segment installed, `bin/run
|
|
16
|
+
# For example, if you have the Sidekiq segment installed, `bin/run sidekiq`
|
|
17
17
|
# runs Sidekiq, so you would return this hash:
|
|
18
18
|
#
|
|
19
19
|
# {
|
|
20
20
|
# "sidekiq" => {
|
|
21
|
-
# cmd: "bin/run
|
|
21
|
+
# cmd: "bin/run sidekiq",
|
|
22
22
|
# }
|
|
23
23
|
# }
|
|
24
24
|
#
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "brut/tui"
|
|
3
|
+
|
|
4
|
+
RSpec.describe Brut::TUI::AnsiEscapeCode do
|
|
5
|
+
describe "#to_s" do
|
|
6
|
+
it "returns the ANSI escape code as a string" do
|
|
7
|
+
code = Brut::TUI::AnsiEscapeCode.new(:red, "31")
|
|
8
|
+
expect(code.to_s).to eq("\e[31m")
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe ".ansi" do
|
|
13
|
+
it "returns the correct ANSI escape code for a given name" do
|
|
14
|
+
red_code = Brut::TUI::AnsiEscapeCode.ansi(:red)
|
|
15
|
+
expect(red_code.to_s).to eq("\e[31m")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "returns an RGB ANSI escape code when given RGB values" do
|
|
19
|
+
rgb_code = Brut::TUI::AnsiEscapeCode.ansi(255, 0, 0)
|
|
20
|
+
expect(rgb_code.to_s).to eq("\e[38;2;255;0;0m")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "returns a fluent objects when called without arguments" do
|
|
24
|
+
fluent = Brut::TUI::AnsiEscapeCode.ansi
|
|
25
|
+
expect(fluent.bold.to_s).to eq("\e[1m")
|
|
26
|
+
expect(fluent.red.to_s).to eq("\e[31m")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "brut/tui"
|
|
3
|
+
|
|
4
|
+
RSpec.describe Brut::TUI::EventLoop do
|
|
5
|
+
subject(:event_loop) { described_class.new(tick: false) }
|
|
6
|
+
|
|
7
|
+
class ExitEvent < Brut::TUI::Events::BaseEvent
|
|
8
|
+
def exit? = true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class RaiseExceptionEvent < Brut::TUI::Events::BaseEvent
|
|
12
|
+
def initialize(message)
|
|
13
|
+
@message = message
|
|
14
|
+
end
|
|
15
|
+
def raise_exception!
|
|
16
|
+
raise StandardError.new(@message)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class GeneralSubscriber
|
|
21
|
+
attr_reader :events
|
|
22
|
+
def initialize
|
|
23
|
+
@events = []
|
|
24
|
+
end
|
|
25
|
+
def on_any_event(event)
|
|
26
|
+
if event.respond_to?(:raise_exception!)
|
|
27
|
+
event.raise_exception!
|
|
28
|
+
else
|
|
29
|
+
@events << event
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "stops the loop when an event returns true for exit?" do
|
|
35
|
+
main_thread = Thread.new {
|
|
36
|
+
event_loop.run
|
|
37
|
+
}
|
|
38
|
+
event_loop << ExitEvent.new
|
|
39
|
+
main_thread.join(0.1)
|
|
40
|
+
expect(main_thread.alive?).to eq(false)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def start_loop(event_loop, wait_time: 0.1)
|
|
44
|
+
exception = nil
|
|
45
|
+
main_thread = Thread.new {
|
|
46
|
+
begin
|
|
47
|
+
event_loop.run
|
|
48
|
+
rescue => ex
|
|
49
|
+
exception = ex
|
|
50
|
+
end
|
|
51
|
+
}
|
|
52
|
+
main_thread.join(wait_time)
|
|
53
|
+
expect(exception).to eq(nil)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "fires the event LoopStarted event when run" do
|
|
57
|
+
general_subscriber = GeneralSubscriber.new
|
|
58
|
+
event_loop.subscribe_to_all(general_subscriber)
|
|
59
|
+
start_loop(event_loop)
|
|
60
|
+
expect(general_subscriber.events[0].class).to eq(Brut::TUI::Events::EventLoopStarted)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "fires exception events when errors are raised" do
|
|
64
|
+
general_subscriber = GeneralSubscriber.new
|
|
65
|
+
event_loop.subscribe_to_all(general_subscriber)
|
|
66
|
+
event_loop << RaiseExceptionEvent.new("Test exception")
|
|
67
|
+
start_loop(event_loop)
|
|
68
|
+
expect(general_subscriber.events[1].class).to eq(Brut::TUI::Events::Exception)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "brut/tui"
|
|
3
|
+
|
|
4
|
+
RSpec.describe Brut::TUI::Events::BaseEvent do
|
|
5
|
+
class TestEvent < Brut::TUI::Events::BaseEvent; end
|
|
6
|
+
module Deeply
|
|
7
|
+
module Nested
|
|
8
|
+
class TestEvent < Brut::TUI::Events::BaseEvent; end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe ".handler_method_name" do
|
|
13
|
+
it "returns the underscorized simple class name prefixed with 'on_'" do
|
|
14
|
+
|
|
15
|
+
expect(TestEvent.handler_method_name).to eq("on_test_event")
|
|
16
|
+
expect(Deeply::Nested::TestEvent.handler_method_name).to eq("on_test_event")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe "#exit?" do
|
|
21
|
+
it "returns false by default" do
|
|
22
|
+
event = Brut::TUI::Events::BaseEvent.new
|
|
23
|
+
expect(event.exit?).to be false
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "brut/tui"
|
|
3
|
+
require_relative "test_event"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Brut::TUI::Events::EventBus do
|
|
6
|
+
class OtherTestEvent < Brut::TUI::Events::BaseEvent
|
|
7
|
+
def self.handler_method_name
|
|
8
|
+
:on_other_test_event
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class SubscriberToTestEvent
|
|
13
|
+
def notified = @notified
|
|
14
|
+
def notified_method = @notified_method
|
|
15
|
+
def on_test_event(event)
|
|
16
|
+
@notified = event
|
|
17
|
+
@notified_method = :on_test_event
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
class SubscriberToAnyEventOnly
|
|
21
|
+
def notified = @notified
|
|
22
|
+
def notified_method = @notified_method
|
|
23
|
+
def on_any_event(event)
|
|
24
|
+
@notified = event
|
|
25
|
+
@notified_method = :on_any_event
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
class SubscriberToTestEventAndAnyEvent
|
|
29
|
+
def notified = @notified
|
|
30
|
+
def notified_method = @notified_method
|
|
31
|
+
def on_test_event(event)
|
|
32
|
+
@notified = event
|
|
33
|
+
@notified_method = :on_test_event
|
|
34
|
+
end
|
|
35
|
+
def on_any_event(event)
|
|
36
|
+
@notified = event
|
|
37
|
+
@notified_method = :on_any_event
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class RaisesErrorInTestEvent
|
|
42
|
+
def on_test_event(event)
|
|
43
|
+
raise "Error in on_test_event"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class RaisesErrorInAnyEvent
|
|
48
|
+
def on_any_event(event)
|
|
49
|
+
raise "Error in on_any_event"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
subject(:event_bus) { described_class.new }
|
|
54
|
+
|
|
55
|
+
describe "#subscribe" do
|
|
56
|
+
context "subscriber implements the handler method" do
|
|
57
|
+
it "is notified" do
|
|
58
|
+
subscriber = SubscriberToTestEvent.new
|
|
59
|
+
event_bus.subscribe(SpecSupport::TestEvent, subscriber)
|
|
60
|
+
event = SpecSupport::TestEvent.new
|
|
61
|
+
|
|
62
|
+
errors = event_bus.notify(event)
|
|
63
|
+
|
|
64
|
+
expect(errors).to eq([])
|
|
65
|
+
expect(subscriber.notified).to eq(event)
|
|
66
|
+
expect(subscriber.notified_method).to eq(:on_test_event)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
context "subscriber does not implement the handler method" do
|
|
70
|
+
it "is raises when subscribing" do
|
|
71
|
+
subscriber = SubscriberToAnyEventOnly.new
|
|
72
|
+
expect {
|
|
73
|
+
event_bus.subscribe(SpecSupport::TestEvent, subscriber)
|
|
74
|
+
}.to raise_error(ArgumentError, /does not implement handler method/)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
describe "#subscribe_to_all" do
|
|
79
|
+
context "subscriber implements the handler method" do
|
|
80
|
+
it "is notified via that method" do
|
|
81
|
+
subscriber = SubscriberToTestEventAndAnyEvent.new
|
|
82
|
+
event_bus.subscribe_to_all(subscriber)
|
|
83
|
+
event = SpecSupport::TestEvent.new
|
|
84
|
+
|
|
85
|
+
errors = event_bus.notify(event)
|
|
86
|
+
|
|
87
|
+
expect(errors).to eq([])
|
|
88
|
+
expect(subscriber.notified).to eq(event)
|
|
89
|
+
expect(subscriber.notified_method).to eq(:on_test_event)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
context "subscriber does not implement the handler method" do
|
|
93
|
+
context "subscriber implements on_any_event" do
|
|
94
|
+
it "calls on_any_event when notified" do
|
|
95
|
+
subscriber = SubscriberToAnyEventOnly.new
|
|
96
|
+
event_bus.subscribe_to_all(subscriber)
|
|
97
|
+
event = SpecSupport::TestEvent.new
|
|
98
|
+
|
|
99
|
+
errors = event_bus.notify(event)
|
|
100
|
+
|
|
101
|
+
expect(errors).to eq([])
|
|
102
|
+
expect(subscriber.notified).to eq(event)
|
|
103
|
+
expect(subscriber.notified_method).to eq(:on_any_event)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
context "subscriber does not implement on_any_event" do
|
|
107
|
+
it "nothing happens" do
|
|
108
|
+
subscriber = SubscriberToTestEvent.new
|
|
109
|
+
event_bus.subscribe_to_all(subscriber)
|
|
110
|
+
event = OtherTestEvent.new
|
|
111
|
+
|
|
112
|
+
errors = event_bus.notify(event)
|
|
113
|
+
|
|
114
|
+
expect(errors).to eq([])
|
|
115
|
+
expect(subscriber.notified).to eq(nil)
|
|
116
|
+
expect(subscriber.notified_method).to eq(nil)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
describe "#notify" do
|
|
122
|
+
|
|
123
|
+
# some of this method's behavior is covered by the tests above
|
|
124
|
+
|
|
125
|
+
context "when subscribers raise errors" do
|
|
126
|
+
it "returns them as an array" do
|
|
127
|
+
event_bus.subscribe(SpecSupport::TestEvent, RaisesErrorInTestEvent.new)
|
|
128
|
+
event_bus.subscribe_to_all(RaisesErrorInAnyEvent.new)
|
|
129
|
+
event_bus.subscribe_to_all(SubscriberToAnyEventOnly.new)
|
|
130
|
+
|
|
131
|
+
event = SpecSupport::TestEvent.new
|
|
132
|
+
|
|
133
|
+
errors = event_bus.notify(event)
|
|
134
|
+
|
|
135
|
+
expect(errors.length).to eq(2)
|
|
136
|
+
expect(errors.map(&:message)).to include("Error in on_test_event")
|
|
137
|
+
expect(errors.map(&:message)).to include("Error in on_any_event")
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "brut/tui"
|
|
3
|
+
|
|
4
|
+
RSpec.describe Brut::TUI::Events::Exception do
|
|
5
|
+
|
|
6
|
+
describe ":#exit?" do
|
|
7
|
+
it "is true" do
|
|
8
|
+
event = described_class.new(StandardError.new("Test exception"))
|
|
9
|
+
expect(event.exit?).to eq(true)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
describe ":#drain_then_exit?" do
|
|
13
|
+
it "is false" do
|
|
14
|
+
event = described_class.new(StandardError.new("Test exception"))
|
|
15
|
+
expect(event.drain_then_exit?).to eq(false)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
data/specs/spec_helper.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
require "brut"
|
|
2
|
+
require "confidence_check/for_rspec"
|
|
3
|
+
|
|
2
4
|
require_relative "support"
|
|
3
5
|
|
|
4
6
|
RSpec.configure do |config|
|
|
7
|
+
config.include ConfidenceCheck::ForRSpec
|
|
8
|
+
|
|
5
9
|
config.expect_with :rspec do |expectations|
|
|
6
10
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
7
11
|
end
|