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