brut 0.0.27 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (411) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.projections.json +10 -0
  4. data/.rspec +3 -0
  5. data/Dockerfile.dx +32 -14
  6. data/Gemfile.lock +1 -1
  7. data/assets/Logo-Square.pxd +0 -0
  8. data/assets/LogoPylon.pxd +0 -0
  9. data/assets/LogoStop.pxd +0 -0
  10. data/assets/LogoTall.pxd +0 -0
  11. data/bin/docs +24 -2
  12. data/bin/rspec +27 -0
  13. data/bin/setup +3 -3
  14. data/brutrb.com/.vitepress/config.mjs +1 -0
  15. data/brutrb.com/.vitepress/theme/custom.css +7 -0
  16. data/brutrb.com/.vitepress/theme/style.css +26 -15
  17. data/brutrb.com/deployment.md +123 -45
  18. data/brutrb.com/dev-environment.md +6 -5
  19. data/brutrb.com/doc-conventions.md +1 -1
  20. data/brutrb.com/getting-started.md +64 -28
  21. data/brutrb.com/images/LogoPylon.png +0 -0
  22. data/brutrb.com/images/LogoSquare.png +0 -0
  23. data/brutrb.com/images/LogoStop.png +0 -0
  24. data/brutrb.com/images/LogoTall.png +0 -0
  25. data/brutrb.com/images/OverviewMetro.graffle +0 -0
  26. data/brutrb.com/images/OverviewMetro.png +0 -0
  27. data/brutrb.com/index.md +4 -3
  28. data/brutrb.com/instrumentation.md +12 -0
  29. data/brutrb.com/layouts.md +130 -0
  30. data/brutrb.com/overview.md +6 -6
  31. data/docker-compose.dx.yml +5 -2
  32. data/docs/404.html +3 -3
  33. data/docs/ai.html +6 -6
  34. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  35. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  36. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  37. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  38. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  39. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  40. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  41. data/docs/api/Brut/BackEnd.html +1 -1
  42. data/docs/api/Brut/CLI/App.html +38 -13
  43. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +2 -2
  45. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +2 -2
  46. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +2 -2
  47. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +2 -2
  48. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/DB/Create.html +2 -2
  50. data/docs/api/Brut/CLI/Apps/DB/Drop.html +2 -2
  51. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +9 -3
  52. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +11 -11
  53. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +2 -2
  54. data/docs/api/Brut/CLI/Apps/DB/Seed.html +2 -2
  55. data/docs/api/Brut/CLI/Apps/DB/Status.html +12 -12
  56. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +270 -0
  58. data/docs/api/Brut/CLI/Apps/DeployBase.html +257 -0
  59. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +585 -0
  60. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +196 -0
  61. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  62. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +2 -2
  63. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +2 -2
  64. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +2 -2
  65. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +2 -2
  66. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +2 -2
  67. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  68. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +2 -2
  69. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  70. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +2 -2
  71. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  72. data/docs/api/Brut/CLI/Apps/Test/Audit.html +12 -10
  73. data/docs/api/Brut/CLI/Apps/Test/E2e.html +8 -8
  74. data/docs/api/Brut/CLI/Apps/Test/JS.html +9 -9
  75. data/docs/api/Brut/CLI/Apps/Test/Run.html +18 -18
  76. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  77. data/docs/api/Brut/CLI/Apps.html +2 -2
  78. data/docs/api/Brut/CLI/Command.html +113 -28
  79. data/docs/api/Brut/CLI/Error.html +1 -1
  80. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  81. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  82. data/docs/api/Brut/CLI/Executor.html +169 -38
  83. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  84. data/docs/api/Brut/CLI/Options.html +68 -19
  85. data/docs/api/Brut/CLI/Output.html +1 -1
  86. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  87. data/docs/api/Brut/CLI.html +1 -1
  88. data/docs/api/Brut/FactoryBot.html +1 -1
  89. data/docs/api/Brut/Framework/App.html +1 -1
  90. data/docs/api/Brut/Framework/Config.html +1 -1
  91. data/docs/api/Brut/Framework/Container.html +110 -29
  92. data/docs/api/Brut/Framework/Error.html +1 -1
  93. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +89 -1
  94. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  95. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  96. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  97. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  98. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  99. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  100. data/docs/api/Brut/Framework/Errors.html +31 -8
  101. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  102. data/docs/api/Brut/Framework/MCP.html +1 -1
  103. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  104. data/docs/api/Brut/Framework.html +1 -1
  105. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  106. data/docs/api/Brut/FrontEnd/Component/Helpers.html +36 -26
  107. data/docs/api/Brut/FrontEnd/Component.html +7 -7
  108. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  109. data/docs/api/Brut/FrontEnd/Components/FormTag.html +37 -29
  110. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  111. data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
  112. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  113. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +20 -117
  114. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +25 -23
  115. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +73 -380
  116. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +22 -138
  117. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  118. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  119. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  122. data/docs/api/Brut/FrontEnd/Components.html +23 -2
  123. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  124. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  125. data/docs/api/Brut/FrontEnd/Form.html +1 -1
  126. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +24 -68
  127. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +201 -0
  128. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +535 -0
  129. data/docs/api/Brut/FrontEnd/Forms/Input.html +983 -35
  130. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +4 -4
  131. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +29 -19
  132. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +9 -3
  133. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  134. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +9 -3
  135. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  136. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +72 -22
  137. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  138. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  143. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  146. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  147. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  148. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  149. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  150. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  151. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  152. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  153. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  154. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  155. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  156. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +6 -2
  157. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  158. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  159. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  160. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  161. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  162. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  163. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  164. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  165. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  166. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  167. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  168. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  169. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  170. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  171. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  172. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  173. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  174. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  175. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  176. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  177. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  178. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  179. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  180. data/docs/api/Brut/FrontEnd.html +1 -1
  181. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  182. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  183. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  184. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  185. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  186. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  187. data/docs/api/Brut/I18n.html +1 -1
  188. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  189. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  190. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  191. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  192. data/docs/api/Brut/Instrumentation.html +1 -1
  193. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  194. data/docs/api/Brut/SinatraHelpers.html +1 -1
  195. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  196. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  197. data/docs/api/Brut/SpecSupport/E2ETestServer.html +20 -20
  198. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  199. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  200. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  201. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  202. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  203. data/docs/api/Brut/SpecSupport/HandlerSupport.html +2 -2
  204. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +142 -0
  205. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +142 -0
  206. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +155 -0
  207. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +55 -25
  208. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +149 -0
  209. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +46 -19
  210. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +149 -0
  211. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +149 -0
  212. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +165 -0
  213. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +158 -0
  214. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +156 -0
  215. data/docs/api/Brut/SpecSupport/Matchers.html +2 -2
  216. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +24 -24
  217. data/docs/api/Brut/SpecSupport/RSpecSetup.html +55 -20
  218. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  219. data/docs/api/Brut/SpecSupport.html +1 -1
  220. data/docs/api/Brut.html +1 -1
  221. data/docs/api/Clock.html +1 -1
  222. data/docs/api/RichString.html +1 -1
  223. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  224. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +5 -1
  225. data/docs/api/Sequel/Extensions/BrutMigrations.html +36 -28
  226. data/docs/api/Sequel/Extensions.html +1 -1
  227. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  228. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  229. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  230. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  231. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  232. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  233. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  234. data/docs/api/Sequel/Plugins.html +1 -1
  235. data/docs/api/Sequel.html +1 -1
  236. data/docs/api/SpecSupport/Matchers/BeABug.html +143 -0
  237. data/docs/api/_index.html +106 -1
  238. data/docs/api/class_list.html +1 -1
  239. data/docs/api/css/full_list.css +2 -1
  240. data/docs/api/css/style.css +14 -13
  241. data/docs/api/file.README.html +1 -1
  242. data/docs/api/index.html +1 -1
  243. data/docs/api/method_list.html +530 -330
  244. data/docs/api/top-level-namespace.html +1 -1
  245. data/docs/assets/LogoStop.X8x-4riz.png +0 -0
  246. data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
  247. data/docs/assets/{ai.md.tZrjP9im.js → ai.md._6HCDL6d.js} +1 -1
  248. data/docs/assets/ai.md._6HCDL6d.lean.js +1 -0
  249. data/docs/assets/{app.D_yaTITQ.js → app.BhrfSt68.js} +1 -1
  250. data/docs/assets/chunks/@localSearchIndexroot.CeRAdP1K.js +1 -0
  251. data/docs/assets/chunks/{VPLocalSearchBox.B2-ZzyTY.js → VPLocalSearchBox.Dpot_2H4.js} +1 -1
  252. data/docs/assets/chunks/{theme.CfGFVRvE.js → theme.N2SNVLgU.js} +2 -2
  253. data/docs/assets/{components.md.eCttGlN-.js → components.md.CRUMdRoN.js} +1 -1
  254. data/docs/assets/{configuration.md.BRriU0cL.js → configuration.md.LG-zIBww.js} +1 -1
  255. data/docs/assets/deployment.md.BLseERGV.js +48 -0
  256. data/docs/assets/deployment.md.BLseERGV.lean.js +1 -0
  257. data/docs/assets/{dev-environment.md.BNc8AYiK.js → dev-environment.md.GZv6xvi9.js} +1 -1
  258. data/docs/assets/doc-conventions.md.-kN3Xo5C.js +1 -0
  259. data/docs/assets/{doc-conventions.md.DCfRXXi-.lean.js → doc-conventions.md.-kN3Xo5C.lean.js} +1 -1
  260. data/docs/assets/{forms.md.CBTYQ_Cz.js → forms.md.B-koVgyw.js} +23 -23
  261. data/docs/assets/{forms.md.CBTYQ_Cz.lean.js → forms.md.B-koVgyw.lean.js} +1 -1
  262. data/docs/assets/getting-started.md.Dj0qtZI2.js +25 -0
  263. data/docs/assets/getting-started.md.Dj0qtZI2.lean.js +1 -0
  264. data/docs/assets/index.md.CuBB-BdM.js +1 -0
  265. data/docs/assets/index.md.CuBB-BdM.lean.js +1 -0
  266. data/docs/assets/{instrumentation.md.CL6ax7nT.js → instrumentation.md.a9Pjps4P.js} +2 -2
  267. data/docs/assets/{instrumentation.md.CL6ax7nT.lean.js → instrumentation.md.a9Pjps4P.lean.js} +1 -1
  268. data/docs/assets/layouts.md.cPnh3NId.js +51 -0
  269. data/docs/assets/layouts.md.cPnh3NId.lean.js +1 -0
  270. data/docs/assets/lsp.md.Bsu-f6VU.js +1 -0
  271. data/docs/assets/lsp.md.Bsu-f6VU.lean.js +1 -0
  272. data/docs/assets/{overview.md.CDalkuxV.js → overview.md.DVKRM8zl.js} +4 -4
  273. data/docs/assets/overview.md.DVKRM8zl.lean.js +1 -0
  274. data/docs/assets/recipes_authentication.md.CAsXf7hk.js +1 -0
  275. data/docs/assets/recipes_authentication.md.CAsXf7hk.lean.js +1 -0
  276. data/docs/assets/{style.D73IYGCX.css → style.B2o1L9eN.css} +1 -1
  277. data/docs/assets.html +5 -5
  278. data/docs/brut-js/api/AjaxSubmit.html +1 -1
  279. data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
  280. data/docs/brut-js/api/Autosubmit.html +1 -1
  281. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  282. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  283. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  284. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  285. data/docs/brut-js/api/BufferedLogger.html +1 -1
  286. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  287. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  288. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  289. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  290. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  291. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  292. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  293. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  294. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  295. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  296. data/docs/brut-js/api/Form.html +1 -1
  297. data/docs/brut-js/api/Form.js.html +1 -1
  298. data/docs/brut-js/api/I18nTranslation.html +1 -1
  299. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  300. data/docs/brut-js/api/LocaleDetection.html +1 -1
  301. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  302. data/docs/brut-js/api/Logger.html +1 -1
  303. data/docs/brut-js/api/Logger.js.html +1 -1
  304. data/docs/brut-js/api/Message.html +1 -1
  305. data/docs/brut-js/api/Message.js.html +1 -1
  306. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  307. data/docs/brut-js/api/RichString.html +1 -1
  308. data/docs/brut-js/api/RichString.js.html +1 -1
  309. data/docs/brut-js/api/Tabs.html +1 -1
  310. data/docs/brut-js/api/Tabs.js.html +1 -1
  311. data/docs/brut-js/api/Tracing.html +1 -1
  312. data/docs/brut-js/api/Tracing.js.html +1 -1
  313. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  314. data/docs/brut-js/api/external-Performance.html +1 -1
  315. data/docs/brut-js/api/external-Promise.html +1 -1
  316. data/docs/brut-js/api/external-ValidityState.html +1 -1
  317. data/docs/brut-js/api/external-Window.html +1 -1
  318. data/docs/brut-js/api/external-fetch.html +1 -1
  319. data/docs/brut-js/api/global.html +1 -1
  320. data/docs/brut-js/api/index.html +1 -1
  321. data/docs/brut-js/api/index.js.html +1 -1
  322. data/docs/brut-js/api/module-testing.html +1 -1
  323. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  324. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  325. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  326. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  327. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  328. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  329. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  330. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  331. data/docs/brut-js/api/testing_index.js.html +1 -1
  332. data/docs/brut-js.html +5 -5
  333. data/docs/business-logic.html +5 -5
  334. data/docs/cli.html +5 -5
  335. data/docs/components.html +7 -7
  336. data/docs/configuration.html +6 -6
  337. data/docs/css.html +5 -5
  338. data/docs/custom-element-tests.html +5 -5
  339. data/docs/database-access.html +5 -5
  340. data/docs/database-schema.html +5 -5
  341. data/docs/deployment.html +53 -6
  342. data/docs/dev-environment.html +6 -6
  343. data/docs/doc-conventions.html +6 -6
  344. data/docs/end-to-end-tests.html +5 -5
  345. data/docs/flash-and-session.html +5 -5
  346. data/docs/forms.html +28 -28
  347. data/docs/getting-started.html +30 -7
  348. data/docs/handlers.html +5 -5
  349. data/docs/hashmap.json +1 -1
  350. data/docs/hooks.html +5 -5
  351. data/docs/i18n.html +5 -5
  352. data/docs/index.html +6 -6
  353. data/docs/instrumentation.html +6 -6
  354. data/docs/javascript.html +5 -5
  355. data/docs/jobs.html +5 -5
  356. data/docs/keyword-injection.html +5 -5
  357. data/docs/layouts.html +74 -0
  358. data/docs/lsp.html +24 -0
  359. data/docs/markdown-examples.html +5 -5
  360. data/docs/middleware.html +5 -5
  361. data/docs/not-released.html +5 -5
  362. data/docs/overview.html +9 -9
  363. data/docs/pages.html +6 -6
  364. data/docs/recipes/authentication.html +24 -0
  365. data/docs/routes.html +5 -5
  366. data/docs/security.html +5 -5
  367. data/docs/seed-data.html +5 -5
  368. data/docs/space-time-continuum.html +5 -5
  369. data/docs/tutorial.html +5 -5
  370. data/docs/unit-tests.html +5 -5
  371. data/dx/bash_customizations +7 -0
  372. data/dx/build +13 -2
  373. data/dx/docker-compose.env +1 -1
  374. data/dx/exec +25 -8
  375. data/lib/brut/cli/app.rb +7 -2
  376. data/lib/brut/cli/apps/deploy_base.rb +86 -0
  377. data/lib/brut/cli/apps/heroku_container_based_deploy.rb +194 -0
  378. data/lib/brut/cli/command.rb +7 -13
  379. data/lib/brut/cli/executor.rb +31 -5
  380. data/lib/brut/cli/options.rb +4 -0
  381. data/lib/brut/cli.rb +4 -3
  382. data/lib/brut/framework/container.rb +25 -7
  383. data/lib/brut/framework/errors/abstract_method.rb +7 -0
  384. data/lib/brut/framework/errors.rb +4 -2
  385. data/lib/brut/front_end/forms/input.rb +253 -20
  386. data/lib/brut/front_end/forms/input_definition.rb +15 -12
  387. data/lib/brut/front_end/middlewares/reload_app.rb +2 -0
  388. data/lib/brut/front_end.rb +1 -0
  389. data/lib/brut/spec_support/rspec_setup.rb +42 -2
  390. data/lib/brut/version.rb +1 -1
  391. data/lib/sequel/extensions/brut_instrumentation.rb +4 -0
  392. data/specs/brut/front_end/forms/input.spec.rb +978 -0
  393. data/specs/spec_helper.rb +27 -0
  394. data/specs/support/matchers/have_constraint_violation.rb +23 -0
  395. data/specs/support/matchers.rb +5 -0
  396. data/specs/support.rb +3 -0
  397. metadata +77 -29
  398. data/docs/assets/ai.md.tZrjP9im.lean.js +0 -1
  399. data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +0 -1
  400. data/docs/assets/deployment.md.Dbka4OTr.js +0 -1
  401. data/docs/assets/deployment.md.Dbka4OTr.lean.js +0 -1
  402. data/docs/assets/doc-conventions.md.DCfRXXi-.js +0 -1
  403. data/docs/assets/getting-started.md.Bz2s1Vjb.js +0 -2
  404. data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +0 -1
  405. data/docs/assets/index.md.B28EwVpq.js +0 -1
  406. data/docs/assets/index.md.B28EwVpq.lean.js +0 -1
  407. data/docs/assets/overview.Da81cB9R.png +0 -0
  408. data/docs/assets/overview.md.CDalkuxV.lean.js +0 -1
  409. /data/docs/assets/{components.md.eCttGlN-.lean.js → components.md.CRUMdRoN.lean.js} +0 -0
  410. /data/docs/assets/{configuration.md.BRriU0cL.lean.js → configuration.md.LG-zIBww.lean.js} +0 -0
  411. /data/docs/assets/{dev-environment.md.BNc8AYiK.lean.js → dev-environment.md.GZv6xvi9.lean.js} +0 -0
@@ -1,12 +1,54 @@
1
+ require "uri"
2
+
1
3
  # An Input is a stateful object representing a specific input and its value
2
4
  # during the course of a form submission process. In particular, it wraps a value
3
- # and a {Brut::FrontEnd::Forms::ValidityState}. These are mutable, whereas the wrapped {Brut::FrontEnd::Forms::InputDefinition} is not.
5
+ # and a {Brut::FrontEnd::Forms::ValidityState}. These are mutable, whereas
6
+ # the wrapped {Brut::FrontEnd::Forms::InputDefinition} is not.
7
+ #
8
+ # Of note, this class also initiates the server-execution of the
9
+ # client-side validations. When a form is posted, and you ask the form
10
+ # to validate itself, this class examines the value for the inputs
11
+ # and determines if a client-side violation should've happened in the browser.
12
+ #
13
+ # It does its best to mimic the browser's behavior, however there will be
14
+ # some subtle differences.
15
+ #
16
+ # An important considering is that everything is a string. The browser wants
17
+ # strings and its internal API is string-based. So is this. That said,
18
+ # this will convert strings into richer types in order to perform constraint
19
+ # analysis.
20
+ #
21
+ # Where possible, standard library classes are used, but in some cases -
22
+ # colors and times - there is no standard library class.
23
+ #
24
+ # See {#typed_value}.
4
25
  class Brut::FrontEnd::Forms::Input
5
26
 
6
27
  extend Forwardable
7
28
 
8
- # @return [String] the input's value
29
+ # @return [String] the input's value. **DO NOTE** this returns a string. This is
30
+ # because HTML stores these values as Strings. When using a checkbox, in
31
+ # particular, the value will *not* be a boolean. You cannot do `if
32
+ # input.value` and expect that work. See {#typed_value} instead.
9
33
  attr_reader :value
34
+
35
+ # @return [Date|Time|BigDecimal|String|true|false|nil] the value of the input,
36
+ # as coerced to the type used to evaluate constraints.
37
+ # Returns `nil` if the value could not be coerced (which likely means
38
+ # there is or will be a constraint violation), **or**
39
+ # if it is a blank String.
40
+ #
41
+ # Of note, `type=color` will return a String here, but that String
42
+ # should be parseable into a hex code, i.e. #XXXXXX, where each "X"
43
+ # is 0-9 or a-f. Further, `type=time` will also return a string
44
+ # in the format HH:MM or HH:MM:SS, where the time is in 24 hour
45
+ # time. Lastly, `type=datetime-local` will return a `Time`, even
46
+ # though the control allows the visitor to choose an invalid time.
47
+ # If there is no `valueMissing` constraint violation, but this
48
+ # attribute returns `nil`, {#value} *will* return the string sent by
49
+ # the browser, even if it's not a real timestamp.
50
+ attr_reader :typed_value
51
+
10
52
  # @return [Brut::FrontEnd::Forms::ValidityState] Validity state that captures the current constraint violations, if any
11
53
  attr_reader :validity_state
12
54
 
@@ -20,6 +62,46 @@ class Brut::FrontEnd::Forms::Input
20
62
  self.value=(value)
21
63
  end
22
64
 
65
+ # @!method max
66
+ # @return [String|nil] the value from the `input_definition` given to the
67
+ # initializer
68
+
69
+ # @!method maxlength
70
+ # @return [String|nil] the value from the `input_definition` given to the
71
+ # initializer
72
+
73
+ # @!method min
74
+ # @return [String|nil] the value from the `input_definition` given to the
75
+ # initializer
76
+
77
+ # @!method minlength
78
+ # @return [String|nil] the value from the `input_definition` given to the
79
+ # initializer
80
+
81
+ # @!method name
82
+ # @return [String|nil] the value from the `input_definition` given to the
83
+ # initializer
84
+
85
+ # @!method pattern
86
+ # @return [String|nil] the value from the `input_definition` given to the
87
+ # initializer
88
+
89
+ # @!method required
90
+ # @return [String|nil] the value from the `input_definition` given to the
91
+ # initializer
92
+
93
+ # @!method step
94
+ # @return [String|nil] the value from the `input_definition` given to the
95
+ # initializer
96
+
97
+ # @!method type
98
+ # @return [String|nil] the value from the `input_definition` given to the
99
+ # initializer
100
+
101
+ # @!method array?
102
+ # @return [String|nil] the value from the `input_definition` given to the
103
+ # initializer
104
+
23
105
  def_delegators :"@input_definition", :max,
24
106
  :maxlength,
25
107
  :min,
@@ -37,14 +119,52 @@ class Brut::FrontEnd::Forms::Input
37
119
  #
38
120
  # When this method completes, the value of {#validity_state} could change.
39
121
  #
40
- # @param [String] new_value the value for the input. Empty strings are coerced to `nil`
122
+ # @param [String] new_value the value for the input. Any non-String value will
123
+ # be coerced to a String via `to_s`.
41
124
  def value=(new_value)
42
- if new_value.kind_of?(String) && new_value.strip == ""
43
- new_value = nil
125
+ new_value = new_value.to_s
126
+ @typed_value = case self.type
127
+ when "number"
128
+ BigDecimal(new_value, exception: false)
129
+ when "range"
130
+ BigDecimal(new_value, exception: false)
131
+ when "checkbox"
132
+ new_value == "true"
133
+ when "color"
134
+ Color.from_hex_string(new_value)&.to_s
135
+ when "date"
136
+ begin
137
+ Date.parse(new_value)
138
+ rescue
139
+ nil
140
+ end
141
+ when "time"
142
+ TimeOfDay.from_string(new_value)
143
+ when "datetime-local"
144
+ begin
145
+ Time.parse(new_value)
146
+ rescue
147
+ nil
148
+ end
149
+ when "url"
150
+ begin
151
+ URI(new_value)
152
+ rescue
153
+ nil
154
+ end
155
+ else
156
+ new_value.strip == "" ? nil : new_value
157
+ end
158
+
159
+ if type == "hidden" || type == "file"
160
+ @value = new_value
161
+ return
44
162
  end
45
- value_missing = new_value.nil? || (new_value.kind_of?(String) && new_value.strip == "")
163
+
164
+ value_missing = new_value.strip == "" || @typed_value.nil?
165
+
46
166
  missing = if self.required
47
- value_missing
167
+ value_missing || @typed_value == false
48
168
  else
49
169
  false
50
170
  end
@@ -60,38 +180,81 @@ class Brut::FrontEnd::Forms::Input
60
180
  false
61
181
  end
62
182
 
63
- type_mismatch = false # TBD
64
-
65
- range_overflow = if self.max && !value_missing && !type_mismatch
66
- new_value.to_i > self.max
183
+ range_overflow = if self.max && !value_missing
184
+ if type == "date"
185
+ max_date = Date.parse(self.max)
186
+ @typed_value > max_date
187
+ elsif type == "datetime-local"
188
+ max_date = Time.parse(self.max)
189
+ @typed_value > max_date
190
+ elsif type == "time"
191
+ max_time = TimeOfDay.from_string(self.max)
192
+ @typed_value > max_time
193
+ else
194
+ new_value.to_i > self.max
195
+ end
67
196
  else
68
197
  false
69
198
  end
70
199
 
71
- range_underflow = if self.min && !value_missing && !type_mismatch
72
- new_value.to_i < self.min
200
+ range_underflow = if self.min && !value_missing
201
+ if type == "date"
202
+ min_date = Date.parse(self.min)
203
+ @typed_value < min_date
204
+ elsif type == "datetime-local"
205
+ min_date = Time.parse(self.min)
206
+ @typed_value < min_date
207
+ elsif type == "time"
208
+ min_time = TimeOfDay.from_string(self.min)
209
+ @typed_value < min_time
210
+ else
211
+ new_value.to_i < self.min
212
+ end
73
213
  else
74
214
  false
75
215
  end
76
216
 
77
- pattern_mismatch = if self.pattern && !value_missing && !type_mismatch
217
+ pattern_mismatch = if self.pattern && !value_missing
78
218
  !new_value.match?(Regexp.new(self.pattern))
79
- else
80
- false
219
+ elsif type == "url" && !value_missing
220
+ @typed_value.nil? || !@typed_value.absolute
81
221
  end
82
- step_mismatch = false
222
+ step_mismatch = if self.step && !value_missing
223
+ step_big_decimal = BigDecimal(self.step.to_s)
224
+ if type == "date"
225
+ basis_date = self.min ? Date.parse(self.min) : Date.parse("1970-01-01")
226
+ num_days = (@typed_value - basis_date)
227
+ num_days % step_big_decimal != 0
228
+ elsif type == "datetime-local"
229
+ basis_time = self.min ? Time.parse(self.min) : Time.parse("1970-01-01T00:00:00")
230
+ num_seconds = (@typed_value - basis_time)
231
+ num_seconds % step_big_decimal != 0
232
+ elsif type == "time"
233
+ basis_time = self.min ? TimeOfDay.from_string(self.min) : TimeOfDay.new(hour: 0, minute: 0, second: 0)
234
+ num_seconds = (@typed_value - basis_time)
235
+ num_seconds % step_big_decimal != 0
236
+ else
237
+ @typed_value % step_big_decimal != 0
238
+ end
239
+ end
240
+
241
+ if type == "range" || type == "color"
242
+ missing = false
243
+ elsif type == "time"
244
+ @typed_value = @typed_value&.to_s
245
+ end
83
246
 
84
247
  @validity_state = Brut::FrontEnd::Forms::ValidityState.new(
85
248
  valueMissing: missing,
86
249
  tooShort: too_short,
87
- tooLong: too_short,
250
+ tooLong: too_long,
88
251
  rangeOverflow: range_overflow,
89
252
  rangeUnderflow: range_underflow,
90
253
  patternMismatch: pattern_mismatch,
91
254
  stepMismatch: step_mismatch,
92
- typeMismatch: type_mismatch,
255
+ typeMismatch: false,
93
256
  )
94
- @value = new_value
257
+ @value = value_missing ? nil : new_value
95
258
  end
96
259
 
97
260
  # Set a server-side constraint violation on this input. This is essentially arbitrary, but note
@@ -105,4 +268,74 @@ class Brut::FrontEnd::Forms::Input
105
268
 
106
269
  # @return [true|false] true if the underlying {#validity_state} has no constraint violations
107
270
  def valid? = @validity_state.valid?
271
+
272
+ class Color
273
+ def self.from_hex_string(string)
274
+ string = string.to_s
275
+ if string.match?(/\A#[0-9a-fA-F]{6}\z/)
276
+ string.downcase
277
+ else
278
+ nil
279
+ end
280
+ end
281
+ end
282
+
283
+ class TimeOfDay
284
+ include Comparable
285
+ def self.from_string(string)
286
+ match_data = string.match(/\A([01]?[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?\z/)
287
+ if match_data
288
+ hour = match_data[1].to_i
289
+ minute = match_data[2].to_i
290
+ second = match_data[4] ? match_data[4].to_i : nil
291
+ self.new(hour:, minute:, second:)
292
+ else
293
+ nil
294
+ end
295
+ end
296
+
297
+ def initialize(hour:, minute:, second:)
298
+ @hour = in_range!(hour, 0, 23, "hour")
299
+ @minute = in_range!(minute, 0, 59, "minute")
300
+ @second = second.nil? ? nil : in_range!(second, 0, 59, "second")
301
+ end
302
+
303
+ def to_s
304
+ if @second.nil?
305
+ sprintf("%02d:%02d", @hour, @minute)
306
+ else
307
+ sprintf("%02d:%02d:%02d", @hour, @minute, @second)
308
+ end
309
+ end
310
+
311
+ def to_seconds
312
+ (@second || 0) +
313
+ (@minute * 60) +
314
+ (@hour * 3600)
315
+ end
316
+
317
+ def -(other)
318
+ if other.kind_of?(self.class)
319
+ self.to_seconds - other.to_seconds
320
+ else
321
+ raise ArgumentError, "Cannot subtract #{other.class} from TimeOfDay"
322
+ end
323
+ end
324
+
325
+ def <=>(other)
326
+ return -1 if !other.kind_of?(self.class)
327
+ self.to_seconds <=> other.to_seconds
328
+ end
329
+
330
+ private
331
+
332
+ def in_range!(value, min, max, name)
333
+ if value < min || value > max
334
+ raise ArgumentError, "Invalid #{name} '#{value}' (must be between #{min} and #{max})"
335
+ end
336
+ value
337
+ rescue ArgumentError => e
338
+ raise ArgumentError, "Invalid TimeOfDay: #{e.message}"
339
+ end
340
+ end
108
341
  end
@@ -22,12 +22,11 @@ class Brut::FrontEnd::Forms::InputDefinition
22
22
  INPUT_TYPES_TO_CLASS = {
23
23
  "checkbox" => String,
24
24
  "color" => String,
25
- "date" => String,
26
- "datetime-local" => String,
25
+ "date" => Date,
26
+ "datetime-local" => Time,
27
27
  "email" => String,
28
28
  "file" => String,
29
29
  "hidden" => String,
30
- "month" => String,
31
30
  "number" => Numeric,
32
31
  "password" => String,
33
32
  "radio" => String,
@@ -35,9 +34,8 @@ class Brut::FrontEnd::Forms::InputDefinition
35
34
  "search" => String,
36
35
  "tel" => String,
37
36
  "text" => String,
38
- "time" => String,
37
+ "time" => Time, # XXX
39
38
  "url" => String,
40
- "week" => String,
41
39
  }
42
40
 
43
41
  # Create an InputDefinition. This should very closely mirror
@@ -65,7 +63,7 @@ class Brut::FrontEnd::Forms::InputDefinition
65
63
  min: nil,
66
64
  minlength: nil,
67
65
  name: nil,
68
- pattern: nil,
66
+ pattern: :based_on_type,
69
67
  required: :based_on_type,
70
68
  step: nil,
71
69
  type: nil,
@@ -89,20 +87,25 @@ class Brut::FrontEnd::Forms::InputDefinition
89
87
  required = type != "checkbox"
90
88
  end
91
89
 
92
- @max = type!( max , Numeric , "max")
90
+ @max = max
93
91
  @maxlength = type!( maxlength , Numeric , "maxlength")
94
- @min = type!( min , Numeric , "min")
92
+ @min = min
95
93
  @minlength = type!( minlength , Numeric , "minlength")
96
94
  @name = type!( name , String , "name", required: true)
97
- @pattern = type!( pattern , String , "pattern")
98
95
  @required = type!( required , [true, false] , "required", required: true)
99
96
  @step = type!( step , Numeric , "step")
100
97
  @type = type!( type , INPUT_TYPES_TO_CLASS.keys , "type", required: true)
101
98
  @array = type!( array , [true, false] , "array", required: true)
102
99
 
103
- if @pattern.nil? && type == "email"
104
- @pattern = /^[^@]+@[^@]+\.[^@]+$/.source
105
- end
100
+ @pattern = if pattern == :based_on_type
101
+ if type == "email"
102
+ /^[^@]+@[^@]+\.[^@]+$/.source
103
+ else
104
+ nil
105
+ end
106
+ else
107
+ type!( pattern, String, "pattern" )
108
+ end
106
109
  end
107
110
 
108
111
  def array? = @array
@@ -28,6 +28,8 @@ class Brut::FrontEnd::Middlewares::ReloadApp < Brut::FrontEnd::Middleware
28
28
  span.add_event("Routing reloaded")
29
29
  Brut.container.asset_path_resolver.reload
30
30
  span.add_event("Asset Path Resolver reloaded")
31
+ Brut.container.reload
32
+ span.add_event("Brut.container reloaded")
31
33
  ::I18n.reload!
32
34
  span.add_event("I18n reloaded")
33
35
  rescue => ex
@@ -12,6 +12,7 @@ module Brut::FrontEnd
12
12
  autoload(:Download, "brut/front_end/download")
13
13
  autoload(:Flash, "brut/front_end/flash")
14
14
  autoload(:Form, "brut/front_end/form")
15
+ autoload(:Forms, "brut/front_end/form")
15
16
  autoload(:GenericResponse, "brut/front_end/generic_response")
16
17
  autoload(:Handler, "brut/front_end/handler")
17
18
  autoload(:Handlers, "brut/front_end/handler")
@@ -43,8 +43,16 @@ class Brut::SpecSupport::RSpecSetup
43
43
  # Create the setup with the given RSpec configuration.
44
44
  #
45
45
  # @param [RSpec::Core::Configuration] rspec_config yielded from `RSpec.configure`
46
- def initialize(rspec_config:)
47
- @config = rspec_config
46
+ # @param [boolean] monkey_patch_summary_notification if true (the default),
47
+ # RSpec's Notifications::SumaryNotification will be monkey-patched to show
48
+ # `bin/test` in the summary instead of `./rspec`, as that is how you would
49
+ # re-run a test. But, it's still monkey-patching.
50
+ def initialize(rspec_config:,
51
+ monkey_patch_summary_notification: true)
52
+
53
+ @config = rspec_config
54
+ @monkey_patch_summary_notification = monkey_patch_summary_notification
55
+
48
56
  SemanticLogger.default_level = ENV.fetch("LOGGER_LEVEL_FOR_TESTS","warn")
49
57
  end
50
58
 
@@ -155,6 +163,9 @@ class Brut::SpecSupport::RSpecSetup
155
163
  @config.after(:suite) do
156
164
  Brut::SpecSupport::E2ETestServer.instance.stop
157
165
  end
166
+ if @monkey_patch_summary_notification
167
+ monkey_patch_summary_notification!
168
+ end
158
169
  end
159
170
 
160
171
  class OptionalSidekiqSupport
@@ -181,4 +192,33 @@ class Brut::SpecSupport::RSpecSetup
181
192
  end
182
193
  end
183
194
 
195
+ private
196
+
197
+ def monkey_patch_summary_notification!
198
+ require "rspec/core/notifications"
199
+ klass = RSpec::Core::Notifications::SummaryNotification
200
+ if !klass.instance_variable_get("@_brut_has_monkeypatched")
201
+ klass.class_eval do
202
+ # Slightly reformatted, but the only line that's different
203
+ # is the one that said to use './rspec'. Now it says 'bin/test run'
204
+ def colorized_rerun_commands(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
205
+ "\nFailed examples:\n\n" +
206
+ failed_examples.map do |example|
207
+ rerun_argument = rerun_argument_for(example)
208
+ colorizer.wrap(
209
+ "bin/test run #{rerun_argument}", # <--- this is the only difference
210
+ RSpec.configuration.failure_color
211
+ ) +
212
+ " " +
213
+ colorizer.wrap(
214
+ "# #{example.full_description}",
215
+ RSpec.configuration.detail_color
216
+ )
217
+ end.join("\n")
218
+ end
219
+ end
220
+ klass.instance_variable_set("@_brut_has_monkeypatched",true)
221
+ end
222
+ end
184
223
  end
224
+
data/lib/brut/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Brut
2
2
  # @!visibility private
3
- VERSION = "0.0.27"
3
+ VERSION = "0.0.29"
4
4
  end
@@ -1,6 +1,10 @@
1
1
  module Sequel
2
2
  module Extensions
3
3
  # Instruments all SQL executions.
4
+ #
5
+ # *NOTE* this will include the actual SQL query text in the
6
+ # span, so any literal values in the query will be in the observability
7
+ # platforms.
4
8
  module BrutInstrumentation
5
9
  # @!visibility private
6
10
  def log_connection_yield(sql,conn,args=nil)