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.
Files changed (444) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile.lock +4 -1
  5. data/bin/docs +7 -0
  6. data/brut-css/package-lock.json +2 -2
  7. data/brut-css/package.json +1 -1
  8. data/brut-js/package-lock.json +2 -2
  9. data/brut-js/package.json +1 -1
  10. data/brut.gemspec +2 -0
  11. data/docs/404.html +2 -2
  12. data/docs/adrs.html +3 -3
  13. data/docs/ai.html +3 -3
  14. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  15. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  16. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  17. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  18. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  19. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  20. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  21. data/docs/api/Brut/BackEnd.html +1 -1
  22. data/docs/api/Brut/CLI/App.html +1 -1
  23. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  24. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  25. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  26. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +2 -2
  27. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  28. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  29. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  30. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  31. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  32. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  33. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  34. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  35. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  36. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  37. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  38. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  39. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +2 -2
  40. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  41. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  42. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  43. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  45. data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +1 -1
  46. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  47. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  48. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  50. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  51. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  52. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  53. data/docs/api/Brut/CLI/Apps/Test/Audit.html +10 -6
  54. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  55. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  56. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  58. data/docs/api/Brut/CLI/Apps.html +1 -1
  59. data/docs/api/Brut/CLI/Command.html +1 -1
  60. data/docs/api/Brut/CLI/Error.html +1 -1
  61. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  62. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  63. data/docs/api/Brut/CLI/Executor.html +1 -1
  64. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  65. data/docs/api/Brut/CLI/Options.html +1 -1
  66. data/docs/api/Brut/CLI/Output.html +1 -1
  67. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  68. data/docs/api/Brut/CLI.html +1 -1
  69. data/docs/api/Brut/FactoryBot.html +1 -1
  70. data/docs/api/Brut/Framework/App.html +1 -1
  71. data/docs/api/Brut/Framework/Config.html +1 -1
  72. data/docs/api/Brut/Framework/Container.html +1 -1
  73. data/docs/api/Brut/Framework/Error.html +1 -1
  74. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  75. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  76. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  77. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  78. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  79. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  80. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  81. data/docs/api/Brut/Framework/Errors.html +1 -1
  82. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  83. data/docs/api/Brut/Framework/MCP.html +1 -1
  84. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  85. data/docs/api/Brut/Framework.html +1 -1
  86. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  87. data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
  88. data/docs/api/Brut/FrontEnd/Component.html +1 -1
  89. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  90. data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
  91. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  92. data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
  93. data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +1 -1
  94. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  95. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +1 -1
  96. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
  97. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +1 -1
  98. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +1 -1
  99. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  100. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  101. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  102. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  103. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  104. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  105. data/docs/api/Brut/FrontEnd/CsrfProtector.html +1 -1
  106. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  107. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  108. data/docs/api/Brut/FrontEnd/Form.html +1 -1
  109. data/docs/api/Brut/FrontEnd/Forms/Button.html +1 -1
  110. data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +1 -1
  111. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  112. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +1 -1
  113. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
  114. data/docs/api/Brut/FrontEnd/Forms/Input.html +1 -1
  115. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +1 -1
  116. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1 -1
  117. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +1 -1
  118. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  119. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  122. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  123. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  124. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  125. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  126. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  127. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  128. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  129. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  130. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  132. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  133. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  134. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  135. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  136. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  137. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  138. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  143. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  146. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  147. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  148. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  149. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  150. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  151. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  152. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  153. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  154. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  155. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  156. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  157. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  158. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  159. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  160. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  161. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  162. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  163. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  164. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  165. data/docs/api/Brut/FrontEnd.html +1 -1
  166. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  167. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  168. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  169. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  170. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  171. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  172. data/docs/api/Brut/I18n.html +1 -1
  173. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  174. data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +1 -1
  175. data/docs/api/Brut/Instrumentation/Methods.html +1 -1
  176. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  177. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  178. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  179. data/docs/api/Brut/Instrumentation.html +1 -1
  180. data/docs/api/Brut/RubocopConfig.html +1 -1
  181. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  182. data/docs/api/Brut/SinatraHelpers.html +1 -1
  183. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  184. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  185. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  186. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  187. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  188. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  189. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  190. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  191. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  192. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  193. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  194. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  195. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  196. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  197. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  198. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  199. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  200. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  201. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  202. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  203. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  204. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  205. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  206. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  207. data/docs/api/Brut/SpecSupport.html +1 -1
  208. data/docs/api/Brut/TUI/AnsiEscapeCode/Mod.html +409 -0
  209. data/docs/api/Brut/TUI/AnsiEscapeCode.html +426 -0
  210. data/docs/api/Brut/TUI/EventLoop/Deque.html +531 -0
  211. data/docs/api/Brut/TUI/EventLoop.html +676 -0
  212. data/docs/api/Brut/TUI/Events/BaseEvent.html +449 -0
  213. data/docs/api/Brut/TUI/Events/EventBus.html +485 -0
  214. data/docs/api/Brut/TUI/Events/EventLoopStarted.html +211 -0
  215. data/docs/api/Brut/TUI/Events/Exception.html +523 -0
  216. data/docs/api/Brut/TUI/Events/Tick.html +294 -0
  217. data/docs/api/Brut/TUI/Events.html +131 -0
  218. data/docs/api/Brut/TUI/MarkupString.html +537 -0
  219. data/docs/api/Brut/TUI/Script/BlockStep.html +300 -0
  220. data/docs/api/Brut/TUI/Script/Events/CommandExecutionFailed.html +252 -0
  221. data/docs/api/Brut/TUI/Script/Events/CommandExecutionSucceeded.html +163 -0
  222. data/docs/api/Brut/TUI/Script/Events/CommandStdErr.html +163 -0
  223. data/docs/api/Brut/TUI/Script/Events/CommandStdOut.html +300 -0
  224. data/docs/api/Brut/TUI/Script/Events/ExecutingCommand.html +298 -0
  225. data/docs/api/Brut/TUI/Script/Events/Message.html +345 -0
  226. data/docs/api/Brut/TUI/Script/Events/PhaseCompleted.html +229 -0
  227. data/docs/api/Brut/TUI/Script/Events/PhaseStarted.html +350 -0
  228. data/docs/api/Brut/TUI/Script/Events/ScriptCompleted.html +282 -0
  229. data/docs/api/Brut/TUI/Script/Events/ScriptStarted.html +343 -0
  230. data/docs/api/Brut/TUI/Script/Events/StepCompleted.html +163 -0
  231. data/docs/api/Brut/TUI/Script/Events/StepStarted.html +346 -0
  232. data/docs/api/Brut/TUI/Script/Events.html +115 -0
  233. data/docs/api/Brut/TUI/Script/ExecStep/ProcessStatusFailed.html +210 -0
  234. data/docs/api/Brut/TUI/Script/ExecStep.html +493 -0
  235. data/docs/api/Brut/TUI/Script/LoggingSubscriber.html +914 -0
  236. data/docs/api/Brut/TUI/Script/PutsSubscriber.html +783 -0
  237. data/docs/api/Brut/TUI/Script/Step.html +313 -0
  238. data/docs/api/Brut/TUI/Script.html +1250 -0
  239. data/docs/api/Brut/TUI/Terminal.html +593 -0
  240. data/docs/api/Brut/TUI/TerminalTheme.html +1403 -0
  241. data/docs/api/Brut/TUI/Themes/Dark.html +706 -0
  242. data/docs/api/Brut/TUI/Themes/Light.html +804 -0
  243. data/docs/api/Brut/TUI/Themes/None.html +218 -0
  244. data/docs/api/Brut/TUI/Themes.html +115 -0
  245. data/docs/api/Brut/TUI.html +129 -0
  246. data/docs/api/Brut.html +3 -3
  247. data/docs/api/Clock.html +1 -1
  248. data/docs/api/ModuleName.html +1 -1
  249. data/docs/api/RichString.html +1 -1
  250. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  251. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  252. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  253. data/docs/api/Sequel/Extensions.html +1 -1
  254. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  255. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  256. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  257. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  258. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  259. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  260. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  261. data/docs/api/Sequel/Plugins.html +1 -1
  262. data/docs/api/Sequel.html +1 -1
  263. data/docs/api/SpecSupport/Matchers/BeABug.html +143 -0
  264. data/docs/api/_index.html +246 -1
  265. data/docs/api/class_list.html +1 -1
  266. data/docs/api/file.README.html +1 -1
  267. data/docs/api/index.html +1 -1
  268. data/docs/api/method_list.html +1551 -431
  269. data/docs/api/top-level-namespace.html +1 -1
  270. data/docs/assets/{app.CovevI7X.js → app.B8jAEB7R.js} +1 -1
  271. data/docs/assets/chunks/@localSearchIndexroot.DJ8mocCj.js +1 -0
  272. data/docs/assets/chunks/{VPLocalSearchBox.CrvLAvKW.js → VPLocalSearchBox.gF-Po_fz.js} +1 -1
  273. data/docs/assets/chunks/{theme.BAi5_yQI.js → theme.BjPAOJkz.js} +2 -2
  274. data/docs/assets/{components.md.9sqJ27Oc.js → components.md.Ber8UBM0.js} +3 -3
  275. data/docs/assets/{configuration.md.Cb_oAR8Z.js → configuration.md.DrJ6YVoZ.js} +1 -1
  276. data/docs/assets/{forms.md.BdpYpNIk.js → forms.md.RK0zkhm0.js} +2 -2
  277. data/docs/assets/{forms.md.BdpYpNIk.lean.js → forms.md.RK0zkhm0.lean.js} +1 -1
  278. data/docs/assets/{getting-started.md.CKpNGvno.js → getting-started.md.CGJ44juQ.js} +2 -2
  279. data/docs/assets/{tutorials_02-dialog.md.Z_DOF2mU.js → tutorials_02-dialog.md.DE5WfCXI.js} +1 -1
  280. data/docs/assets.html +3 -3
  281. data/docs/brut-css/brut.css +1 -0
  282. data/docs/brut-js/api/AjaxSubmit.html +1 -1
  283. data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
  284. data/docs/brut-js/api/Autosubmit.html +1 -1
  285. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  286. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  287. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  288. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  289. data/docs/brut-js/api/BufferedLogger.html +1 -1
  290. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  291. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  292. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  293. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  294. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  295. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  296. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  297. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  298. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  299. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  300. data/docs/brut-js/api/Form.html +1 -1
  301. data/docs/brut-js/api/Form.js.html +1 -1
  302. data/docs/brut-js/api/I18nTranslation.html +1 -1
  303. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  304. data/docs/brut-js/api/LocaleDetection.html +1 -1
  305. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  306. data/docs/brut-js/api/Logger.html +1 -1
  307. data/docs/brut-js/api/Logger.js.html +1 -1
  308. data/docs/brut-js/api/Message.html +1 -1
  309. data/docs/brut-js/api/Message.js.html +1 -1
  310. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  311. data/docs/brut-js/api/RichString.html +1 -1
  312. data/docs/brut-js/api/RichString.js.html +1 -1
  313. data/docs/brut-js/api/Tabs.html +1 -1
  314. data/docs/brut-js/api/Tabs.js.html +1 -1
  315. data/docs/brut-js/api/Toast.html +1 -1
  316. data/docs/brut-js/api/Toast.js.html +1 -1
  317. data/docs/brut-js/api/Tracing.html +1 -1
  318. data/docs/brut-js/api/Tracing.js.html +1 -1
  319. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  320. data/docs/brut-js/api/external-Performance.html +1 -1
  321. data/docs/brut-js/api/external-Promise.html +1 -1
  322. data/docs/brut-js/api/external-ValidityState.html +1 -1
  323. data/docs/brut-js/api/external-Window.html +1 -1
  324. data/docs/brut-js/api/external-fetch.html +1 -1
  325. data/docs/brut-js/api/global.html +1 -1
  326. data/docs/brut-js/api/index.html +1 -1
  327. data/docs/brut-js/api/index.js.html +1 -1
  328. data/docs/brut-js/api/module-testing.html +1 -1
  329. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  330. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  331. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  332. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  333. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  334. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  335. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  336. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  337. data/docs/brut-js/api/testing_index.js.html +1 -1
  338. data/docs/brut-js.html +3 -3
  339. data/docs/business-logic.html +3 -3
  340. data/docs/cli.html +3 -3
  341. data/docs/components.html +7 -7
  342. data/docs/configuration.html +5 -5
  343. data/docs/css.html +3 -3
  344. data/docs/custom-element-tests.html +3 -3
  345. data/docs/database-access.html +3 -3
  346. data/docs/database-schema.html +3 -3
  347. data/docs/deployment.html +3 -3
  348. data/docs/dev-environment.html +3 -3
  349. data/docs/dir-structure.html +3 -3
  350. data/docs/doc-conventions.html +3 -3
  351. data/docs/end-to-end-tests.html +3 -3
  352. data/docs/features.html +3 -3
  353. data/docs/flash-and-session.html +3 -3
  354. data/docs/form-constraints.html +3 -3
  355. data/docs/forms.html +5 -5
  356. data/docs/getting-started.html +6 -6
  357. data/docs/handlers.html +3 -3
  358. data/docs/hashmap.json +1 -1
  359. data/docs/hooks.html +3 -3
  360. data/docs/i18n.html +3 -3
  361. data/docs/index.html +3 -3
  362. data/docs/instrumentation.html +3 -3
  363. data/docs/javascript.html +3 -3
  364. data/docs/jobs.html +3 -3
  365. data/docs/keyword-injection.html +3 -3
  366. data/docs/layouts.html +3 -3
  367. data/docs/lsp.html +3 -3
  368. data/docs/markdown-examples.html +3 -3
  369. data/docs/middleware.html +3 -3
  370. data/docs/overview.html +3 -3
  371. data/docs/pages.html +3 -3
  372. data/docs/recipes/alternate-layouts.html +3 -3
  373. data/docs/recipes/authentication.html +3 -3
  374. data/docs/recipes/custom-flash.html +3 -3
  375. data/docs/recipes/dev-env-secrets.html +3 -3
  376. data/docs/recipes/form-errors.html +3 -3
  377. data/docs/recipes/indexed-forms.html +3 -3
  378. data/docs/recipes/migrations.html +3 -3
  379. data/docs/recipes/text-field-component.html +3 -3
  380. data/docs/roadmap.html +3 -3
  381. data/docs/routes.html +3 -3
  382. data/docs/security.html +3 -3
  383. data/docs/seed-data.html +3 -3
  384. data/docs/space-time-continuum.html +3 -3
  385. data/docs/tutorial.html +3 -3
  386. data/docs/tutorials/01-intro.html +3 -3
  387. data/docs/tutorials/02-dialog.html +5 -5
  388. data/docs/unit-tests.html +3 -3
  389. data/docs/why.html +3 -3
  390. data/lib/brut/cli/apps/build_assets.rb +1 -1
  391. data/lib/brut/cli/apps/heroku_container_based_deploy.rb +1 -1
  392. data/lib/brut/cli/apps/test.rb +6 -4
  393. data/lib/brut/tui/ansi_escape_code.rb +104 -0
  394. data/lib/brut/tui/event_loop.rb +168 -0
  395. data/lib/brut/tui/events/base_event.rb +29 -0
  396. data/lib/brut/tui/events/event_bus.rb +73 -0
  397. data/lib/brut/tui/events/event_loop_started.rb +5 -0
  398. data/lib/brut/tui/events/exception.rb +24 -0
  399. data/lib/brut/tui/events/tick.rb +12 -0
  400. data/lib/brut/tui/events.rb +7 -0
  401. data/lib/brut/tui/markup_string.rb +68 -0
  402. data/lib/brut/tui/script/block_step.rb +17 -0
  403. data/lib/brut/tui/script/events/command_execution_failed.rb +4 -0
  404. data/lib/brut/tui/script/events/command_execution_succeeded.rb +3 -0
  405. data/lib/brut/tui/script/events/command_std_err.rb +3 -0
  406. data/lib/brut/tui/script/events/command_std_out.rb +13 -0
  407. data/lib/brut/tui/script/events/executing_command.rb +12 -0
  408. data/lib/brut/tui/script/events/message.rb +15 -0
  409. data/lib/brut/tui/script/events/phase_completed.rb +4 -0
  410. data/lib/brut/tui/script/events/phase_started.rb +14 -0
  411. data/lib/brut/tui/script/events/script_completed.rb +5 -0
  412. data/lib/brut/tui/script/events/script_started.rb +12 -0
  413. data/lib/brut/tui/script/events/step_completed.rb +3 -0
  414. data/lib/brut/tui/script/events/step_started.rb +12 -0
  415. data/lib/brut/tui/script/events.rb +14 -0
  416. data/lib/brut/tui/script/exec_step.rb +60 -0
  417. data/lib/brut/tui/script/logging_subscriber.rb +98 -0
  418. data/lib/brut/tui/script/puts_subscriber.rb +109 -0
  419. data/lib/brut/tui/script/step.rb +13 -0
  420. data/lib/brut/tui/script.rb +211 -0
  421. data/lib/brut/tui/terminal.rb +74 -0
  422. data/lib/brut/tui/terminal_theme.rb +140 -0
  423. data/lib/brut/tui/themes/dark.rb +14 -0
  424. data/lib/brut/tui/themes/light.rb +17 -0
  425. data/lib/brut/tui/themes/none.rb +9 -0
  426. data/lib/brut/tui/themes.rb +5 -0
  427. data/lib/brut/tui.rb +15 -0
  428. data/lib/brut/version.rb +1 -1
  429. data/lib/brut.rb +1 -0
  430. data/mkbrut/Gemfile.lock +1 -1
  431. data/mkbrut/lib/mkbrut/version.rb +1 -1
  432. data/specs/brut/tui/ansi_escape_code.spec.rb +30 -0
  433. data/specs/brut/tui/event_loop.spec.rb +70 -0
  434. data/specs/brut/tui/events/base_event.spec.rb +26 -0
  435. data/specs/brut/tui/events/event_bus.spec.rb +141 -0
  436. data/specs/brut/tui/events/exception.spec.rb +19 -0
  437. data/specs/brut/tui/events/test_event.rb +5 -0
  438. data/specs/spec_helper.rb +4 -0
  439. metadata +124 -15
  440. data/docs/assets/chunks/@localSearchIndexroot.BiNc3tFI.js +0 -1
  441. /data/docs/assets/{components.md.9sqJ27Oc.lean.js → components.md.Ber8UBM0.lean.js} +0 -0
  442. /data/docs/assets/{configuration.md.Cb_oAR8Z.lean.js → configuration.md.DrJ6YVoZ.lean.js} +0 -0
  443. /data/docs/assets/{getting-started.md.CKpNGvno.lean.js → getting-started.md.CGJ44juQ.lean.js} +0 -0
  444. /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,4 @@
1
+ # Fired when a subcommand exited nonzero
2
+ class Brut::TUI::Script::Events::CommandExecutionFailed < Brut::TUI::Script::Events::ExecutingCommand
3
+ def exit? = true
4
+ end
@@ -0,0 +1,3 @@
1
+ # Fired when a subcommand exited zero (i.e. was successful)
2
+ class Brut::TUI::Script::Events::CommandExecutionSucceeded < Brut::TUI::Script::Events::ExecutingCommand
3
+ end
@@ -0,0 +1,3 @@
1
+ # Fired when a command has provided at least some output on its standard error.
2
+ class Brut::TUI::Script::Events::CommandStdErr < Brut::TUI::Script::Events::CommandStdOut
3
+ 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,4 @@
1
+ # Fired when a phase completes
2
+ class Brut::TUI::Script::Events::PhaseCompleted < Brut::TUI::Script::Events::PhaseStarted
3
+ def to_s = "#{@name} completed"
4
+ 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,5 @@
1
+ # Fired when the script has completed
2
+ class Brut::TUI::Script::Events::ScriptCompleted < Brut::TUI::Events::BaseEvent
3
+ def drain_then_exit? = true
4
+ def to_s = "Done"
5
+ 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,3 @@
1
+ # Fired when a step completes.
2
+ class Brut::TUI::Script::Events::StepCompleted < Brut::TUI::Script::Events::StepStarted
3
+ 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