brut 0.10.0 → 0.12.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 (403) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/Gemfile.lock +21 -21
  4. data/assets/YouTubeThumb.pxd +0 -0
  5. data/bin/new-version +3 -3
  6. data/brut-css/package-lock.json +94 -97
  7. data/brut-css/package.json +2 -2
  8. data/brut-js/package-lock.json +3 -3
  9. data/brut-js/package.json +6 -3
  10. data/brut-js/specs/AjaxSubmit.spec.js +62 -6
  11. data/brut-js/src/AjaxSubmit.js +26 -6
  12. data/brutrb.com/forms.md +1 -0
  13. data/brutrb.com/getting-started.md +3 -0
  14. data/brutrb.com/images/tutorial/02-confirmation-dialog-browser-element-styled.png +0 -0
  15. data/brutrb.com/images/tutorial/02-confirmation-dialog-browser-element.png +0 -0
  16. data/brutrb.com/images/tutorial/02-confirmation-dialog-browser.png +0 -0
  17. data/brutrb.com/images/tutorial/02-confirmation-flow.graffle +0 -0
  18. data/brutrb.com/images/tutorial/02-confirmation-flow.png +0 -0
  19. data/brutrb.com/instrumentation.md +142 -3
  20. data/brutrb.com/recipes/authentication.md +1 -0
  21. data/brutrb.com/tutorial.md +29 -1627
  22. data/brutrb.com/tutorials/01-intro.md +1654 -0
  23. data/brutrb.com/tutorials/02-dialog.md +569 -0
  24. data/docs/404.html +2 -2
  25. data/docs/adrs.html +4 -4
  26. data/docs/ai.html +4 -4
  27. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  28. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  29. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  30. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  31. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  32. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  33. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  34. data/docs/api/Brut/BackEnd.html +1 -1
  35. data/docs/api/Brut/CLI/App.html +1 -1
  36. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  37. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  38. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  39. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
  40. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  41. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  42. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  43. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  45. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  46. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  47. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  48. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  50. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  51. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  52. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
  53. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  54. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  55. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  56. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  58. data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +1 -1
  59. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  60. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  61. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  62. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  63. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  64. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  65. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  66. data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
  67. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  68. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  69. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  70. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  71. data/docs/api/Brut/CLI/Apps.html +1 -1
  72. data/docs/api/Brut/CLI/Command.html +1 -1
  73. data/docs/api/Brut/CLI/Error.html +1 -1
  74. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  75. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  76. data/docs/api/Brut/CLI/Executor.html +1 -1
  77. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  78. data/docs/api/Brut/CLI/Options.html +1 -1
  79. data/docs/api/Brut/CLI/Output.html +1 -1
  80. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  81. data/docs/api/Brut/CLI.html +1 -1
  82. data/docs/api/Brut/FactoryBot.html +1 -1
  83. data/docs/api/Brut/Framework/App.html +1 -1
  84. data/docs/api/Brut/Framework/Config.html +1 -1
  85. data/docs/api/Brut/Framework/Container.html +1 -1
  86. data/docs/api/Brut/Framework/Error.html +1 -1
  87. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  88. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  89. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  90. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  91. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  92. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  93. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  94. data/docs/api/Brut/Framework/Errors.html +1 -1
  95. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +2 -2
  96. data/docs/api/Brut/Framework/MCP.html +1 -1
  97. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  98. data/docs/api/Brut/Framework.html +1 -1
  99. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  100. data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
  101. data/docs/api/Brut/FrontEnd/Component.html +1 -1
  102. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +48 -27
  103. data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
  104. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  105. data/docs/api/Brut/FrontEnd/Components/Input.html +2 -2
  106. data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +443 -0
  107. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  108. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +14 -9
  109. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
  110. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +8 -9
  111. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +10 -11
  112. data/docs/api/Brut/FrontEnd/Components/Inputs.html +2 -2
  113. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  114. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  115. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  116. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  117. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  118. data/docs/api/Brut/FrontEnd/CsrfProtector.html +1 -1
  119. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Form.html +39 -39
  122. data/docs/api/Brut/FrontEnd/Forms/Button.html +331 -0
  123. data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +544 -0
  124. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  125. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +1 -1
  126. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
  127. data/docs/api/Brut/FrontEnd/Forms/Input.html +6 -2
  128. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +111 -23
  129. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +20 -12
  130. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  132. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +1 -1
  133. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  134. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  135. data/docs/api/Brut/FrontEnd/Forms.html +2 -2
  136. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  137. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  138. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +2 -2
  143. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  145. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  146. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  147. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  148. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  149. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  150. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  151. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  152. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  153. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  154. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  155. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  156. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  157. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  158. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  159. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  160. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  161. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  162. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  163. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  164. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  165. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  166. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  167. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  168. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  169. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  170. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  171. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  172. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  173. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  174. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  175. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  176. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  177. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  178. data/docs/api/Brut/FrontEnd.html +1 -1
  179. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  180. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  181. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  182. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  183. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  184. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  185. data/docs/api/Brut/I18n.html +1 -1
  186. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  187. data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +596 -0
  188. data/docs/api/Brut/Instrumentation/Methods.html +173 -0
  189. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +7 -7
  190. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +7 -7
  191. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +19 -17
  192. data/docs/api/Brut/Instrumentation.html +3 -1
  193. data/docs/api/Brut/RubocopConfig.html +1 -1
  194. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  195. data/docs/api/Brut/SinatraHelpers.html +1 -1
  196. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  197. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  198. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  199. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  200. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  201. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  202. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  203. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  204. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  205. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  206. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  207. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  208. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  209. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  210. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  211. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  212. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  213. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  214. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  215. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  216. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  217. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  218. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  219. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  220. data/docs/api/Brut/SpecSupport.html +1 -1
  221. data/docs/api/Brut.html +1 -1
  222. data/docs/api/Clock.html +1 -1
  223. data/docs/api/ModuleName.html +1 -1
  224. data/docs/api/RichString.html +1 -1
  225. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  226. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  227. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  228. data/docs/api/Sequel/Extensions.html +1 -1
  229. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  230. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  231. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  232. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  233. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  234. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  235. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  236. data/docs/api/Sequel/Plugins.html +1 -1
  237. data/docs/api/Sequel.html +1 -1
  238. data/docs/api/_index.html +36 -1
  239. data/docs/api/class_list.html +1 -1
  240. data/docs/api/file.README.html +1 -1
  241. data/docs/api/index.html +1 -1
  242. data/docs/api/method_list.html +364 -252
  243. data/docs/api/top-level-namespace.html +1 -1
  244. data/docs/assets/02-confirmation-dialog-browser-element-styled.3NEGM20-.png +0 -0
  245. data/docs/assets/02-confirmation-dialog-browser-element.DPsf0xUW.png +0 -0
  246. data/docs/assets/02-confirmation-dialog-browser.DH8ALFO4.png +0 -0
  247. data/docs/assets/02-confirmation-flow.D9gZ0S5U.png +0 -0
  248. data/docs/assets/{app.vjGWMSnJ.js → app.V2GOcOrg.js} +1 -1
  249. data/docs/assets/chunks/@localSearchIndexroot.3Lsq4QTb.js +1 -0
  250. data/docs/assets/chunks/{VPLocalSearchBox.C-ymMW2k.js → VPLocalSearchBox.CZTadcAy.js} +1 -1
  251. data/docs/assets/chunks/{theme.pJUosGlI.js → theme.FeuYNxyp.js} +2 -2
  252. data/docs/assets/{components.md.B543a3Lm.js → components.md.f4cdTyvV.js} +3 -3
  253. data/docs/assets/{configuration.md.-foE_iVv.js → configuration.md.Bs4-rxnS.js} +1 -1
  254. data/docs/assets/{form-constraints.md.DK5adCgM.js → form-constraints.md.KTv5cdR4.js} +6 -6
  255. data/docs/assets/{forms.md.D5-2rgHh.js → forms.md.Sys-XxVf.js} +3 -3
  256. data/docs/assets/{forms.md.D5-2rgHh.lean.js → forms.md.Sys-XxVf.lean.js} +1 -1
  257. data/docs/assets/{getting-started.md.Cd4XSZb_.js → getting-started.md.CFIW0bcE.js} +6 -3
  258. data/docs/assets/{getting-started.md.Cd4XSZb_.lean.js → getting-started.md.CFIW0bcE.lean.js} +1 -1
  259. data/docs/assets/instrumentation.md._lNSriEZ.js +90 -0
  260. data/docs/assets/instrumentation.md._lNSriEZ.lean.js +1 -0
  261. data/docs/assets/{recipes_authentication.md.Dzvi_g69.js → recipes_authentication.md.BAISoxmN.js} +1 -0
  262. data/docs/assets/recipes_form-errors.md.Bv5RCKqH.js +66 -0
  263. data/docs/assets/recipes_form-errors.md.Bv5RCKqH.lean.js +1 -0
  264. data/docs/assets/tutorial.md.BM40jnoq.js +27 -0
  265. data/docs/assets/tutorial.md.BM40jnoq.lean.js +1 -0
  266. data/docs/assets/{tutorial.md.C4zR5XPG.js → tutorials_01-intro.md.B4sUBY3X.js} +13 -33
  267. data/docs/assets/tutorials_01-intro.md.B4sUBY3X.lean.js +1 -0
  268. data/docs/assets/tutorials_02-dialog.md.QTFeHdiA.js +274 -0
  269. data/docs/assets/tutorials_02-dialog.md.QTFeHdiA.lean.js +1 -0
  270. data/docs/assets.html +4 -4
  271. data/docs/brut-js/api/AjaxSubmit.html +16 -6
  272. data/docs/brut-js/api/AjaxSubmit.js.html +27 -7
  273. data/docs/brut-js/api/Autosubmit.html +1 -1
  274. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  275. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  276. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  277. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  278. data/docs/brut-js/api/BufferedLogger.html +1 -1
  279. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  280. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  281. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  282. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  283. data/docs/brut-js/api/ConstraintViolationMessage.html +55 -5
  284. data/docs/brut-js/api/ConstraintViolationMessage.js.html +18 -3
  285. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  286. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  287. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  288. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  289. data/docs/brut-js/api/Form.html +7 -10
  290. data/docs/brut-js/api/Form.js.html +20 -24
  291. data/docs/brut-js/api/I18nTranslation.html +1 -1
  292. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  293. data/docs/brut-js/api/LocaleDetection.html +1 -1
  294. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  295. data/docs/brut-js/api/Logger.html +1 -1
  296. data/docs/brut-js/api/Logger.js.html +1 -1
  297. data/docs/brut-js/api/Message.html +1 -1
  298. data/docs/brut-js/api/Message.js.html +1 -1
  299. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  300. data/docs/brut-js/api/RichString.html +1 -1
  301. data/docs/brut-js/api/RichString.js.html +1 -1
  302. data/docs/brut-js/api/Tabs.html +1 -1
  303. data/docs/brut-js/api/Tabs.js.html +1 -1
  304. data/docs/brut-js/api/Tracing.html +1 -1
  305. data/docs/brut-js/api/Tracing.js.html +1 -1
  306. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  307. data/docs/brut-js/api/external-Performance.html +1 -1
  308. data/docs/brut-js/api/external-Promise.html +1 -1
  309. data/docs/brut-js/api/external-ValidityState.html +1 -1
  310. data/docs/brut-js/api/external-Window.html +1 -1
  311. data/docs/brut-js/api/external-fetch.html +1 -1
  312. data/docs/brut-js/api/global.html +1 -1
  313. data/docs/brut-js/api/index.html +1 -1
  314. data/docs/brut-js/api/index.js.html +1 -1
  315. data/docs/brut-js/api/module-testing.html +1 -1
  316. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  317. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  318. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  319. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  320. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  321. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  322. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  323. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  324. data/docs/brut-js/api/testing_index.js.html +1 -1
  325. data/docs/brut-js.html +4 -4
  326. data/docs/business-logic.html +4 -4
  327. data/docs/cli.html +4 -4
  328. data/docs/components.html +8 -8
  329. data/docs/configuration.html +5 -5
  330. data/docs/css.html +4 -4
  331. data/docs/custom-element-tests.html +4 -4
  332. data/docs/database-access.html +4 -4
  333. data/docs/database-schema.html +4 -4
  334. data/docs/deployment.html +4 -4
  335. data/docs/dev-environment.html +4 -4
  336. data/docs/dir-structure.html +4 -4
  337. data/docs/doc-conventions.html +4 -4
  338. data/docs/end-to-end-tests.html +4 -4
  339. data/docs/features.html +4 -4
  340. data/docs/flash-and-session.html +4 -4
  341. data/docs/form-constraints.html +11 -11
  342. data/docs/forms.html +7 -7
  343. data/docs/getting-started.html +10 -7
  344. data/docs/handlers.html +4 -4
  345. data/docs/hashmap.json +1 -1
  346. data/docs/hooks.html +4 -4
  347. data/docs/i18n.html +4 -4
  348. data/docs/index.html +3 -3
  349. data/docs/instrumentation.html +61 -6
  350. data/docs/javascript.html +4 -4
  351. data/docs/jobs.html +4 -4
  352. data/docs/keyword-injection.html +4 -4
  353. data/docs/layouts.html +4 -4
  354. data/docs/lsp.html +4 -4
  355. data/docs/markdown-examples.html +4 -4
  356. data/docs/middleware.html +4 -4
  357. data/docs/overview.html +4 -4
  358. data/docs/pages.html +4 -4
  359. data/docs/recipes/alternate-layouts.html +4 -4
  360. data/docs/recipes/authentication.html +7 -6
  361. data/docs/recipes/blank-layouts.html +4 -4
  362. data/docs/recipes/custom-flash.html +4 -4
  363. data/docs/recipes/form-errors.html +94 -0
  364. data/docs/recipes/indexed-forms.html +4 -4
  365. data/docs/recipes/migrations.html +5 -5
  366. data/docs/recipes/text-field-component.html +4 -4
  367. data/docs/roadmap.html +4 -4
  368. data/docs/routes.html +4 -4
  369. data/docs/security.html +4 -4
  370. data/docs/seed-data.html +4 -4
  371. data/docs/space-time-continuum.html +4 -4
  372. data/docs/tutorial.html +12 -713
  373. data/docs/tutorials/01-intro.html +736 -0
  374. data/docs/tutorials/02-dialog.html +302 -0
  375. data/docs/unit-tests.html +4 -4
  376. data/docs/why.html +4 -4
  377. data/lib/brut/front_end/components/input.rb +1 -0
  378. data/lib/brut/front_end/components/inputs/button_tag.rb +40 -0
  379. data/lib/brut/front_end/components/inputs/input_tag.rb +3 -1
  380. data/lib/brut/front_end/components/inputs/select_tag_with_options.rb +0 -1
  381. data/lib/brut/front_end/components/inputs/textarea_tag.rb +0 -1
  382. data/lib/brut/front_end/form.rb +7 -5
  383. data/lib/brut/front_end/forms/button.rb +21 -0
  384. data/lib/brut/front_end/forms/button_input_definition.rb +37 -0
  385. data/lib/brut/front_end/forms/input_declarations.rb +10 -0
  386. data/lib/brut/front_end/forms/input_definition.rb +7 -2
  387. data/lib/brut/instrumentation/methods.rb +153 -0
  388. data/lib/brut/instrumentation/open_telemetry.rb +1 -0
  389. data/lib/brut/instrumentation.rb +1 -0
  390. data/lib/brut/version.rb +1 -1
  391. data/mkbrut/Gemfile.lock +1 -1
  392. data/mkbrut/bin/publish +1 -1
  393. data/mkbrut/lib/mkbrut/version.rb +1 -1
  394. data/specs/brut/instrumentation/methods.spec.rb +399 -0
  395. metadata +51 -21
  396. data/docs/assets/chunks/@localSearchIndexroot.Dn1xGMv_.js +0 -1
  397. data/docs/assets/instrumentation.md.BgcaGVYH.js +0 -35
  398. data/docs/assets/instrumentation.md.BgcaGVYH.lean.js +0 -1
  399. data/docs/assets/tutorial.md.C4zR5XPG.lean.js +0 -1
  400. /data/docs/assets/{components.md.B543a3Lm.lean.js → components.md.f4cdTyvV.lean.js} +0 -0
  401. /data/docs/assets/{configuration.md.-foE_iVv.lean.js → configuration.md.Bs4-rxnS.lean.js} +0 -0
  402. /data/docs/assets/{form-constraints.md.DK5adCgM.lean.js → form-constraints.md.KTv5cdR4.lean.js} +0 -0
  403. /data/docs/assets/{recipes_authentication.md.Dzvi_g69.lean.js → recipes_authentication.md.BAISoxmN.lean.js} +0 -0
@@ -0,0 +1,399 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Brut::Instrumentation::Methods do
4
+ describe "#instrument" do
5
+ it "instruments only those methods specified" do
6
+ mock_container = double("Brut::Framework::Container")
7
+ instrumentation = instance_double(Brut::Instrumentation::OpenTelemetry)
8
+ allow(Brut).to receive(:container).and_return(mock_container)
9
+ allow(mock_container).to receive(:instrumentation).and_return(instrumentation)
10
+ allow(instrumentation).to receive(:span).and_yield(double("span that should not be used"))
11
+
12
+ class_to_instrument = Class.new do
13
+ include Brut::Instrumentation::Methods
14
+
15
+ def save
16
+ @save_called = true
17
+ end
18
+
19
+ def search
20
+ @search_called = true
21
+ end
22
+
23
+ def self.name # since this is an anonymous class, need some name to show up
24
+ "TestClass"
25
+ end
26
+
27
+ private
28
+
29
+ def delete
30
+ @delete_called = true
31
+ end
32
+
33
+ instrument :save, :delete
34
+ end
35
+ object = class_to_instrument.new
36
+
37
+ object.save
38
+ object.search
39
+ object.send(:delete)
40
+
41
+ expect(object.instance_variable_get(:@save_called)).to eq(true)
42
+ expect(object.instance_variable_get(:@search_called)).to eq(true)
43
+ expect(object.instance_variable_get(:@delete_called)).to eq(true)
44
+ expect(instrumentation).to have_received(:span).with(
45
+ "TestClass#save",
46
+ attributes: {
47
+ "brut.class" => "TestClass",
48
+ "brut.method" => "save",
49
+ }
50
+ )
51
+ expect(instrumentation).to have_received(:span).with(
52
+ "TestClass#delete",
53
+ attributes: {
54
+ "brut.class" => "TestClass",
55
+ "brut.method" => "delete",
56
+ }
57
+ )
58
+ expect(instrumentation).not_to have_received(:span).with(
59
+ /\#search/,
60
+ attributes: kind_of(Hash)
61
+ )
62
+ end
63
+ it "works when called on a method definition" do
64
+ mock_container = double("Brut::Framework::Container")
65
+ instrumentation = instance_double(Brut::Instrumentation::OpenTelemetry)
66
+ allow(Brut).to receive(:container).and_return(mock_container)
67
+ allow(mock_container).to receive(:instrumentation).and_return(instrumentation)
68
+ allow(instrumentation).to receive(:span).and_yield(double("span that should not be used"))
69
+
70
+ class_to_instrument = Class.new do
71
+ include Brut::Instrumentation::Methods
72
+
73
+ instrument def save
74
+ @save_called = true
75
+ end
76
+
77
+ def search
78
+ @search_called = true
79
+ end
80
+
81
+ def self.name # since this is an anonymous class, need some name to show up
82
+ "TestClass"
83
+ end
84
+
85
+ private
86
+
87
+ instrument def delete
88
+ @delete_called = true
89
+ end
90
+ end
91
+ object = class_to_instrument.new
92
+
93
+ object.save
94
+ object.search
95
+ object.send(:delete)
96
+
97
+ expect(object.instance_variable_get(:@save_called)).to eq(true)
98
+ expect(object.instance_variable_get(:@search_called)).to eq(true)
99
+ expect(object.instance_variable_get(:@delete_called)).to eq(true)
100
+ expect(instrumentation).to have_received(:span).with(
101
+ "TestClass#save",
102
+ attributes: {
103
+ "brut.class" => "TestClass",
104
+ "brut.method" => "save",
105
+ }
106
+ )
107
+ expect(instrumentation).to have_received(:span).with(
108
+ "TestClass#delete",
109
+ attributes: {
110
+ "brut.class" => "TestClass",
111
+ "brut.method" => "delete",
112
+ }
113
+ )
114
+ expect(instrumentation).not_to have_received(:span).with(
115
+ /\#search/,
116
+ attributes: kind_of(Hash)
117
+ )
118
+ end
119
+
120
+ it "calling it twice raises an error" do
121
+ expect {
122
+ Class.new do
123
+ include Brut::Instrumentation::Methods
124
+
125
+ def save
126
+ @save_called = true
127
+ end
128
+
129
+ def self.name # since this is an anonymous class, need some name to show up
130
+ "TestClass"
131
+ end
132
+
133
+ instrument :save
134
+ instrument :save
135
+ end
136
+ }.to raise_error(ArgumentError, /already instrumented/)
137
+ end
138
+
139
+ it "instrumenting a nonexistent method raises an error" do
140
+ expect {
141
+ Class.new do
142
+ include Brut::Instrumentation::Methods
143
+
144
+ def save
145
+ @save_called = true
146
+ end
147
+
148
+ def self.name # since this is an anonymous class, need some name to show up
149
+ "TestClass"
150
+ end
151
+
152
+ instrument :foo
153
+ end
154
+ }.to raise_error(ArgumentError)
155
+ end
156
+ end
157
+ describe "#instrument_all" do
158
+ it "instruments all methods" do
159
+ mock_container = double("Brut::Framework::Container")
160
+ instrumentation = instance_double(Brut::Instrumentation::OpenTelemetry)
161
+ allow(Brut).to receive(:container).and_return(mock_container)
162
+ allow(mock_container).to receive(:instrumentation).and_return(instrumentation)
163
+ allow(instrumentation).to receive(:span).and_yield(double("span that should not be used"))
164
+
165
+ class_to_instrument = Class.new do
166
+ include Brut::Instrumentation::Methods
167
+
168
+ def save
169
+ @save_called = true
170
+ end
171
+
172
+ def search
173
+ @search_called = true
174
+ end
175
+
176
+ def self.name # since this is an anonymous class, need some name to show up
177
+ "TestClass"
178
+ end
179
+
180
+ private
181
+
182
+ def delete
183
+ @delete_called = true
184
+ end
185
+
186
+ instrument_all
187
+ end
188
+ object = class_to_instrument.new
189
+
190
+ object.save
191
+ object.search
192
+ object.send(:delete)
193
+
194
+ expect(object.instance_variable_get(:@save_called)).to eq(true)
195
+ expect(object.instance_variable_get(:@search_called)).to eq(true)
196
+ expect(object.instance_variable_get(:@delete_called)).to eq(true)
197
+ expect(instrumentation).to have_received(:span).with(
198
+ "TestClass#save",
199
+ attributes: {
200
+ "brut.class" => "TestClass",
201
+ "brut.method" => "save",
202
+ }
203
+ )
204
+ expect(instrumentation).to have_received(:span).with(
205
+ "TestClass#delete",
206
+ attributes: {
207
+ "brut.class" => "TestClass",
208
+ "brut.method" => "delete",
209
+ }
210
+ )
211
+ expect(instrumentation).to have_received(:span).with(
212
+ "TestClass#search",
213
+ attributes: {
214
+ "brut.class" => "TestClass",
215
+ "brut.method" => "search",
216
+ }
217
+ )
218
+ end
219
+ it "instruments all methods defined after it is called" do
220
+ mock_container = double("Brut::Framework::Container")
221
+ instrumentation = instance_double(Brut::Instrumentation::OpenTelemetry)
222
+ allow(Brut).to receive(:container).and_return(mock_container)
223
+ allow(mock_container).to receive(:instrumentation).and_return(instrumentation)
224
+ allow(instrumentation).to receive(:span).and_yield(double("span that should not be used"))
225
+
226
+ class_to_instrument = Class.new do
227
+ include Brut::Instrumentation::Methods
228
+ instrument_all
229
+
230
+ def save
231
+ @save_called = true
232
+ end
233
+
234
+ def search
235
+ @search_called = true
236
+ end
237
+
238
+ def self.name # since this is an anonymous class, need some name to show up
239
+ "TestClass"
240
+ end
241
+
242
+ private
243
+
244
+ def delete
245
+ @delete_called = true
246
+ end
247
+ end
248
+ object = class_to_instrument.new
249
+
250
+ object.save
251
+ object.search
252
+ object.send(:delete)
253
+
254
+ expect(object.instance_variable_get(:@save_called)).to eq(true)
255
+ expect(object.instance_variable_get(:@search_called)).to eq(true)
256
+ expect(object.instance_variable_get(:@delete_called)).to eq(true)
257
+ expect(instrumentation).to have_received(:span).with(
258
+ "TestClass#save",
259
+ attributes: {
260
+ "brut.class" => "TestClass",
261
+ "brut.method" => "save",
262
+ }
263
+ )
264
+ expect(instrumentation).to have_received(:span).with(
265
+ "TestClass#delete",
266
+ attributes: {
267
+ "brut.class" => "TestClass",
268
+ "brut.method" => "delete",
269
+ }
270
+ )
271
+ expect(instrumentation).to have_received(:span).with(
272
+ "TestClass#search",
273
+ attributes: {
274
+ "brut.class" => "TestClass",
275
+ "brut.method" => "search",
276
+ }
277
+ )
278
+ end
279
+ end
280
+ describe "#instrument_public" do
281
+ it "instruments only public methods" do
282
+ mock_container = double("Brut::Framework::Container")
283
+ instrumentation = instance_double(Brut::Instrumentation::OpenTelemetry)
284
+ allow(Brut).to receive(:container).and_return(mock_container)
285
+ allow(mock_container).to receive(:instrumentation).and_return(instrumentation)
286
+ allow(instrumentation).to receive(:span).and_yield(double("span that should not be used"))
287
+
288
+ class_to_instrument = Class.new do
289
+ include Brut::Instrumentation::Methods
290
+
291
+ def save
292
+ @save_called = true
293
+ end
294
+
295
+ def search
296
+ @search_called = true
297
+ end
298
+
299
+ def self.name # since this is an anonymous class, need some name to show up
300
+ "TestClass"
301
+ end
302
+
303
+ private
304
+
305
+ def delete
306
+ @delete_called = true
307
+ end
308
+
309
+ instrument_public
310
+
311
+ end
312
+ object = class_to_instrument.new
313
+
314
+ object.save
315
+ object.search
316
+ object.send(:delete)
317
+
318
+ expect(object.instance_variable_get(:@save_called)).to eq(true)
319
+ expect(object.instance_variable_get(:@search_called)).to eq(true)
320
+ expect(object.instance_variable_get(:@delete_called)).to eq(true)
321
+ expect(instrumentation).to have_received(:span).with(
322
+ "TestClass#save",
323
+ attributes: {
324
+ "brut.class" => "TestClass",
325
+ "brut.method" => "save",
326
+ }
327
+ )
328
+ expect(instrumentation).to have_received(:span).with(
329
+ "TestClass#search",
330
+ attributes: {
331
+ "brut.class" => "TestClass",
332
+ "brut.method" => "search",
333
+ }
334
+ )
335
+ expect(instrumentation).not_to have_received(:span).with(
336
+ "TestClass#delete",
337
+ attributes: kind_of(Hash)
338
+ )
339
+ end
340
+ it "instruments only public methods defined after it is called" do
341
+ mock_container = double("Brut::Framework::Container")
342
+ instrumentation = instance_double(Brut::Instrumentation::OpenTelemetry)
343
+ allow(Brut).to receive(:container).and_return(mock_container)
344
+ allow(mock_container).to receive(:instrumentation).and_return(instrumentation)
345
+ allow(instrumentation).to receive(:span).and_yield(double("span that should not be used"))
346
+
347
+ class_to_instrument = Class.new do
348
+ include Brut::Instrumentation::Methods
349
+ instrument_public
350
+
351
+ def save
352
+ @save_called = true
353
+ end
354
+
355
+ def search
356
+ @search_called = true
357
+ end
358
+
359
+ def self.name # since this is an anonymous class, need some name to show up
360
+ "TestClass"
361
+ end
362
+
363
+ private
364
+
365
+ def delete
366
+ @delete_called = true
367
+ end
368
+
369
+ end
370
+ object = class_to_instrument.new
371
+
372
+ object.save
373
+ object.search
374
+ object.send(:delete)
375
+
376
+ expect(object.instance_variable_get(:@save_called)).to eq(true)
377
+ expect(object.instance_variable_get(:@search_called)).to eq(true)
378
+ expect(object.instance_variable_get(:@delete_called)).to eq(true)
379
+ expect(instrumentation).to have_received(:span).with(
380
+ "TestClass#save",
381
+ attributes: {
382
+ "brut.class" => "TestClass",
383
+ "brut.method" => "save",
384
+ }
385
+ )
386
+ expect(instrumentation).to have_received(:span).with(
387
+ "TestClass#search",
388
+ attributes: {
389
+ "brut.class" => "TestClass",
390
+ "brut.method" => "search",
391
+ }
392
+ )
393
+ expect(instrumentation).not_to have_received(:span).with(
394
+ "TestClass#delete",
395
+ attributes: kind_of(Hash)
396
+ )
397
+ end
398
+ end
399
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brut
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Bryant Copeland
@@ -669,6 +669,11 @@ files:
669
669
  - brutrb.com/images/overview.png
670
670
  - brutrb.com/images/spa.dot
671
671
  - brutrb.com/images/spa.png
672
+ - brutrb.com/images/tutorial/02-confirmation-dialog-browser-element-styled.png
673
+ - brutrb.com/images/tutorial/02-confirmation-dialog-browser-element.png
674
+ - brutrb.com/images/tutorial/02-confirmation-dialog-browser.png
675
+ - brutrb.com/images/tutorial/02-confirmation-flow.graffle
676
+ - brutrb.com/images/tutorial/02-confirmation-flow.png
672
677
  - brutrb.com/images/tutorial/basic-form-with-violations.png
673
678
  - brutrb.com/images/tutorial/basic-form.png
674
679
  - brutrb.com/images/tutorial/initial-home-page.png
@@ -710,6 +715,8 @@ files:
710
715
  - brutrb.com/seed-data.md
711
716
  - brutrb.com/space-time-continuum.md
712
717
  - brutrb.com/tutorial.md
718
+ - brutrb.com/tutorials/01-intro.md
719
+ - brutrb.com/tutorials/02-dialog.md
713
720
  - brutrb.com/unit-tests.md
714
721
  - brutrb.com/why.md
715
722
  - docker-compose.dx.yml
@@ -802,6 +809,7 @@ files:
802
809
  - docs/api/Brut/FrontEnd/Components/I18nTranslations.html
803
810
  - docs/api/Brut/FrontEnd/Components/Input.html
804
811
  - docs/api/Brut/FrontEnd/Components/Inputs.html
812
+ - docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html
805
813
  - docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html
806
814
  - docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html
807
815
  - docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html
@@ -816,6 +824,8 @@ files:
816
824
  - docs/api/Brut/FrontEnd/Flash.html
817
825
  - docs/api/Brut/FrontEnd/Form.html
818
826
  - docs/api/Brut/FrontEnd/Forms.html
827
+ - docs/api/Brut/FrontEnd/Forms/Button.html
828
+ - docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html
819
829
  - docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html
820
830
  - docs/api/Brut/FrontEnd/Forms/Input.html
821
831
  - docs/api/Brut/FrontEnd/Forms/Input/Color.html
@@ -878,6 +888,8 @@ files:
878
888
  - docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html
879
889
  - docs/api/Brut/Instrumentation.html
880
890
  - docs/api/Brut/Instrumentation/LoggerSpanExporter.html
891
+ - docs/api/Brut/Instrumentation/Methods.html
892
+ - docs/api/Brut/Instrumentation/Methods/ClassMethods.html
881
893
  - docs/api/Brut/Instrumentation/OpenTelemetry.html
882
894
  - docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html
883
895
  - docs/api/Brut/Instrumentation/OpenTelemetry/Span.html
@@ -941,6 +953,10 @@ files:
941
953
  - docs/api/method_list.html
942
954
  - docs/api/top-level-namespace.html
943
955
  - docs/assets.html
956
+ - docs/assets/02-confirmation-dialog-browser-element-styled.3NEGM20-.png
957
+ - docs/assets/02-confirmation-dialog-browser-element.DPsf0xUW.png
958
+ - docs/assets/02-confirmation-dialog-browser.DH8ALFO4.png
959
+ - docs/assets/02-confirmation-flow.D9gZ0S5U.png
944
960
  - docs/assets/DevEnvironment.DaFcVfwP.png
945
961
  - docs/assets/LogoStop.Gb3tDhL1.png
946
962
  - docs/assets/OverviewMetro.DUS-5fUZ.png
@@ -948,7 +964,7 @@ files:
948
964
  - docs/assets/adrs.md.BxjHi9-8.lean.js
949
965
  - docs/assets/ai.md.Cy9GWnER.js
950
966
  - docs/assets/ai.md.Cy9GWnER.lean.js
951
- - docs/assets/app.vjGWMSnJ.js
967
+ - docs/assets/app.V2GOcOrg.js
952
968
  - docs/assets/assets.md.7C3HWkga.js
953
969
  - docs/assets/assets.md.7C3HWkga.lean.js
954
970
  - docs/assets/basic-form-with-violations.Cv6Y9-Q_.png
@@ -957,16 +973,16 @@ files:
957
973
  - docs/assets/brut-js.md.B4GYxQVw.lean.js
958
974
  - docs/assets/business-logic.md.BY4hGy0m.js
959
975
  - docs/assets/business-logic.md.BY4hGy0m.lean.js
960
- - docs/assets/chunks/@localSearchIndexroot.Dn1xGMv_.js
961
- - docs/assets/chunks/VPLocalSearchBox.C-ymMW2k.js
976
+ - docs/assets/chunks/@localSearchIndexroot.3Lsq4QTb.js
977
+ - docs/assets/chunks/VPLocalSearchBox.CZTadcAy.js
962
978
  - docs/assets/chunks/framework.1L-BeKqY.js
963
- - docs/assets/chunks/theme.pJUosGlI.js
979
+ - docs/assets/chunks/theme.FeuYNxyp.js
964
980
  - docs/assets/cli.md.CjsktgFz.js
965
981
  - docs/assets/cli.md.CjsktgFz.lean.js
966
- - docs/assets/components.md.B543a3Lm.js
967
- - docs/assets/components.md.B543a3Lm.lean.js
968
- - docs/assets/configuration.md.-foE_iVv.js
969
- - docs/assets/configuration.md.-foE_iVv.lean.js
982
+ - docs/assets/components.md.f4cdTyvV.js
983
+ - docs/assets/components.md.f4cdTyvV.lean.js
984
+ - docs/assets/configuration.md.Bs4-rxnS.js
985
+ - docs/assets/configuration.md.Bs4-rxnS.lean.js
970
986
  - docs/assets/css.md.CltvJqAa.js
971
987
  - docs/assets/css.md.CltvJqAa.lean.js
972
988
  - docs/assets/custom-element-tests.md.B_rbta32.js
@@ -990,12 +1006,12 @@ files:
990
1006
  - docs/assets/features.md.DPFXsy0z.lean.js
991
1007
  - docs/assets/flash-and-session.md.nPvUpnUx.js
992
1008
  - docs/assets/flash-and-session.md.nPvUpnUx.lean.js
993
- - docs/assets/form-constraints.md.DK5adCgM.js
994
- - docs/assets/form-constraints.md.DK5adCgM.lean.js
995
- - docs/assets/forms.md.D5-2rgHh.js
996
- - docs/assets/forms.md.D5-2rgHh.lean.js
997
- - docs/assets/getting-started.md.Cd4XSZb_.js
998
- - docs/assets/getting-started.md.Cd4XSZb_.lean.js
1009
+ - docs/assets/form-constraints.md.KTv5cdR4.js
1010
+ - docs/assets/form-constraints.md.KTv5cdR4.lean.js
1011
+ - docs/assets/forms.md.Sys-XxVf.js
1012
+ - docs/assets/forms.md.Sys-XxVf.lean.js
1013
+ - docs/assets/getting-started.md.CFIW0bcE.js
1014
+ - docs/assets/getting-started.md.CFIW0bcE.lean.js
999
1015
  - docs/assets/handlers.md.h84MMB1R.js
1000
1016
  - docs/assets/handlers.md.h84MMB1R.lean.js
1001
1017
  - docs/assets/hooks.md.Jmb5VOLA.js
@@ -1005,8 +1021,8 @@ files:
1005
1021
  - docs/assets/index.md.Bn9e0sRJ.js
1006
1022
  - docs/assets/index.md.Bn9e0sRJ.lean.js
1007
1023
  - docs/assets/initial-home-page.DNIaYmgP.png
1008
- - docs/assets/instrumentation.md.BgcaGVYH.js
1009
- - docs/assets/instrumentation.md.BgcaGVYH.lean.js
1024
+ - docs/assets/instrumentation.md._lNSriEZ.js
1025
+ - docs/assets/instrumentation.md._lNSriEZ.lean.js
1010
1026
  - docs/assets/javascript.md.DzrMxUmI.js
1011
1027
  - docs/assets/javascript.md.DzrMxUmI.lean.js
1012
1028
  - docs/assets/jobs.md.S-2amAYp.js
@@ -1029,12 +1045,14 @@ files:
1029
1045
  - docs/assets/pages.md.B7Hc-i6H.lean.js
1030
1046
  - docs/assets/recipes_alternate-layouts.md.BwEytl59.js
1031
1047
  - docs/assets/recipes_alternate-layouts.md.BwEytl59.lean.js
1032
- - docs/assets/recipes_authentication.md.Dzvi_g69.js
1033
- - docs/assets/recipes_authentication.md.Dzvi_g69.lean.js
1048
+ - docs/assets/recipes_authentication.md.BAISoxmN.js
1049
+ - docs/assets/recipes_authentication.md.BAISoxmN.lean.js
1034
1050
  - docs/assets/recipes_blank-layouts.md.fyAUJyJR.js
1035
1051
  - docs/assets/recipes_blank-layouts.md.fyAUJyJR.lean.js
1036
1052
  - docs/assets/recipes_custom-flash.md.CrQbI5eH.js
1037
1053
  - docs/assets/recipes_custom-flash.md.CrQbI5eH.lean.js
1054
+ - docs/assets/recipes_form-errors.md.Bv5RCKqH.js
1055
+ - docs/assets/recipes_form-errors.md.Bv5RCKqH.lean.js
1038
1056
  - docs/assets/recipes_indexed-forms.md.CstYyOSo.js
1039
1057
  - docs/assets/recipes_indexed-forms.md.CstYyOSo.lean.js
1040
1058
  - docs/assets/recipes_migrations.md.CTcnWDJF.js
@@ -1057,8 +1075,12 @@ files:
1057
1075
  - docs/assets/styled-form-with-violations.Bv_sa9tg.png
1058
1076
  - docs/assets/styled-home-page-with-posts.Dd4kG89D.png
1059
1077
  - docs/assets/styled-home-page.BzdI7dWz.png
1060
- - docs/assets/tutorial.md.C4zR5XPG.js
1061
- - docs/assets/tutorial.md.C4zR5XPG.lean.js
1078
+ - docs/assets/tutorial.md.BM40jnoq.js
1079
+ - docs/assets/tutorial.md.BM40jnoq.lean.js
1080
+ - docs/assets/tutorials_01-intro.md.B4sUBY3X.js
1081
+ - docs/assets/tutorials_01-intro.md.B4sUBY3X.lean.js
1082
+ - docs/assets/tutorials_02-dialog.md.QTFeHdiA.js
1083
+ - docs/assets/tutorials_02-dialog.md.QTFeHdiA.lean.js
1062
1084
  - docs/assets/unit-tests.md.DUGrnLj5.js
1063
1085
  - docs/assets/unit-tests.md.DUGrnLj5.lean.js
1064
1086
  - docs/assets/welcome-to-brut.VSWzl17-.png
@@ -1194,6 +1216,7 @@ files:
1194
1216
  - docs/recipes/authentication.html
1195
1217
  - docs/recipes/blank-layouts.html
1196
1218
  - docs/recipes/custom-flash.html
1219
+ - docs/recipes/form-errors.html
1197
1220
  - docs/recipes/indexed-forms.html
1198
1221
  - docs/recipes/migrations.html
1199
1222
  - docs/recipes/text-field-component.html
@@ -1203,6 +1226,8 @@ files:
1203
1226
  - docs/seed-data.html
1204
1227
  - docs/space-time-continuum.html
1205
1228
  - docs/tutorial.html
1229
+ - docs/tutorials/01-intro.html
1230
+ - docs/tutorials/02-dialog.html
1206
1231
  - docs/unit-tests.html
1207
1232
  - docs/vp-icons.css
1208
1233
  - docs/why.html
@@ -1265,6 +1290,7 @@ files:
1265
1290
  - lib/brut/front_end/components/form_tag.rb
1266
1291
  - lib/brut/front_end/components/i18n_translations.rb
1267
1292
  - lib/brut/front_end/components/input.rb
1293
+ - lib/brut/front_end/components/inputs/button_tag.rb
1268
1294
  - lib/brut/front_end/components/inputs/csrf_token.rb
1269
1295
  - lib/brut/front_end/components/inputs/input_tag.rb
1270
1296
  - lib/brut/front_end/components/inputs/radio_button.rb
@@ -1278,6 +1304,8 @@ files:
1278
1304
  - lib/brut/front_end/download.rb
1279
1305
  - lib/brut/front_end/flash.rb
1280
1306
  - lib/brut/front_end/form.rb
1307
+ - lib/brut/front_end/forms/button.rb
1308
+ - lib/brut/front_end/forms/button_input_definition.rb
1281
1309
  - lib/brut/front_end/forms/constraint_violation.rb
1282
1310
  - lib/brut/front_end/forms/input.rb
1283
1311
  - lib/brut/front_end/forms/input_declarations.rb
@@ -1324,6 +1352,7 @@ files:
1324
1352
  - lib/brut/i18n/http_accept_language.rb
1325
1353
  - lib/brut/instrumentation.rb
1326
1354
  - lib/brut/instrumentation/logger_span_exporter.rb
1355
+ - lib/brut/instrumentation/methods.rb
1327
1356
  - lib/brut/instrumentation/open_telemetry.rb
1328
1357
  - lib/brut/junk_drawer.rb
1329
1358
  - lib/brut/rubocop_config.rb
@@ -1518,6 +1547,7 @@ files:
1518
1547
  - specs/brut/front_end/forms/input.spec.rb
1519
1548
  - specs/brut/front_end/forms/radio_button_group_input.spec.rb
1520
1549
  - specs/brut/front_end/forms/select_input.spec.rb
1550
+ - specs/brut/instrumentation/methods.spec.rb
1521
1551
  - specs/brut/junk_drawer.spec.rb
1522
1552
  - specs/spec_helper.rb
1523
1553
  - specs/support.rb