brut 0.0.20 → 0.0.22

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 (728) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +24 -3
  3. data/.nvim.lua +1 -0
  4. data/Dockerfile.dx +12 -3
  5. data/Gemfile.lock +9 -7
  6. data/README.md +0 -7
  7. data/Rakefile +6 -4
  8. data/bin/dev +20 -0
  9. data/bin/docs +27 -0
  10. data/bin/setup +47 -1
  11. data/brut-css/.nvim.lua +1 -0
  12. data/brut-css/README.md +28 -0
  13. data/brut-css/bin/build +31 -0
  14. data/brut-css/bin/dev +1 -0
  15. data/brut-css/bin/docs +15 -0
  16. data/brut-css/bin/setup +5 -0
  17. data/brut-css/config/media-queries-all.css +15 -0
  18. data/brut-css/config/media-queries-minimal.css +5 -0
  19. data/brut-css/config/postcss.config.cjs +7 -0
  20. data/brut-css/config/pseudo-classes-all.css +9 -0
  21. data/brut-css/dx +1 -0
  22. data/brut-css/package-lock.json +3217 -0
  23. data/brut-css/package.json +36 -0
  24. data/brut-css/src/css/appearance.css +145 -0
  25. data/brut-css/src/css/border.css +522 -0
  26. data/brut-css/src/css/colors.css +3502 -0
  27. data/brut-css/src/css/dimensions.css +548 -0
  28. data/brut-css/src/css/flex.css +179 -0
  29. data/brut-css/src/css/index.css +13 -0
  30. data/brut-css/src/css/layout.css +120 -0
  31. data/brut-css/src/css/list.css +41 -0
  32. data/brut-css/src/css/positioning.css +354 -0
  33. data/brut-css/src/css/properties/colors.css +455 -0
  34. data/brut-css/src/css/properties/index.css +3 -0
  35. data/brut-css/src/css/properties/spacing.css +140 -0
  36. data/brut-css/src/css/properties/typography.css +224 -0
  37. data/brut-css/src/css/reset.css +107 -0
  38. data/brut-css/src/css/spacing.css +585 -0
  39. data/brut-css/src/css/typography.css +519 -0
  40. data/brut-css/src/css/utils.css +104 -0
  41. data/brut-css/src/docs/1_getting-started/1_overview.md +46 -0
  42. data/brut-css/src/docs/1_getting-started/2_installation.md +25 -0
  43. data/brut-css/src/docs/1_getting-started/3_core-concepts.md +75 -0
  44. data/brut-css/src/docs/1_getting-started/4_simple-example.md +132 -0
  45. data/brut-css/src/docs/1_getting-started/page.html.ejs +10 -0
  46. data/brut-css/src/docs/2_properties/page.html.ejs +71 -0
  47. data/brut-css/src/docs/3_classes/color-demo.html.ejs +31 -0
  48. data/brut-css/src/docs/3_classes/page.html.ejs +87 -0
  49. data/brut-css/src/docs/4_customization/1_design-system.md +36 -0
  50. data/brut-css/src/docs/4_customization/2_breakpoints.md +75 -0
  51. data/brut-css/src/docs/4_customization/3_pseudo-classes.md +74 -0
  52. data/brut-css/src/docs/4_customization/4_advanced-configuration.md +40 -0
  53. data/brut-css/src/docs/4_customization/page.html.ejs +10 -0
  54. data/brut-css/src/docs/docs.css +98 -0
  55. data/brut-css/src/docs/includes/body-and-header.html.ejs +30 -0
  56. data/brut-css/src/docs/includes/footer-and-rest.html.ejs +9 -0
  57. data/brut-css/src/docs/includes/head.html.ejs +5 -0
  58. data/brut-css/src/docs/includes/nav.html.ejs +10 -0
  59. data/brut-css/src/docs/index.html.ejs +32 -0
  60. data/brut-css/src/docs/prism-twilight.min.css +1 -0
  61. data/brut-css/src/js/Logger.js +71 -0
  62. data/brut-css/src/js/build.js +111 -0
  63. data/brut-css/src/js/cli/CLIArgError.js +7 -0
  64. data/brut-css/src/js/cli/Debug.js +27 -0
  65. data/brut-css/src/js/cli/DocsDir.js +16 -0
  66. data/brut-css/src/js/cli/DocsTemplateSourceDir.js +16 -0
  67. data/brut-css/src/js/cli/InputFile.js +31 -0
  68. data/brut-css/src/js/cli/MediaQueryConfigFile.js +10 -0
  69. data/brut-css/src/js/cli/OutputFile.js +22 -0
  70. data/brut-css/src/js/cli/ParsedArg.js +17 -0
  71. data/brut-css/src/js/cli/PathToBrutCSSRoot.js +19 -0
  72. data/brut-css/src/js/cli/PseudoClassConfigFile.js +11 -0
  73. data/brut-css/src/js/cli.js +108 -0
  74. data/brut-css/src/js/docGenerator.js +467 -0
  75. data/brut-css/src/js/mediaQueryConfigParser.js +98 -0
  76. data/brut-css/src/js/post-css-plugins/addMediaQueriesPlugin.js +49 -0
  77. data/brut-css/src/js/post-css-plugins/addPseudoClassesPlugin.js +42 -0
  78. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Category.js +9 -0
  79. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/DocState.js +185 -0
  80. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Documentable.js +8 -0
  81. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Group.js +7 -0
  82. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/ParsedComment.js +73 -0
  83. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Property.js +9 -0
  84. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyCategory.js +4 -0
  85. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyGroup.js +8 -0
  86. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Rule.js +12 -0
  87. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleCategory.js +4 -0
  88. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleGroup.js +8 -0
  89. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeRef.js +5 -0
  90. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeURL.js +9 -0
  91. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin.js +49 -0
  92. data/brut-css/src/js/post-css-plugins/generateRootCustomPropertiesPlugin.js +45 -0
  93. data/brut-css/src/js/pseudoClassConfigParser.js +145 -0
  94. data/brut-js/.projections.json +10 -0
  95. data/brut-js/README.md +118 -0
  96. data/brut-js/bin/build +10 -0
  97. data/brut-js/bin/ci +5 -0
  98. data/brut-js/bin/setup +5 -0
  99. data/brut-js/docs/README.md +8 -0
  100. data/brut-js/docs/jsdoc-plugins/customElementTag.js +8 -0
  101. data/brut-js/docs/jsdoc-theme/publish.js +692 -0
  102. data/brut-js/docs/jsdoc-theme/static/scripts/linenumber.js +25 -0
  103. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/Apache-License-2.0.txt +202 -0
  104. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/lang-css.js +2 -0
  105. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/prettify.js +28 -0
  106. data/brut-js/docs/jsdoc-theme/static/styles/jsdoc-default.css +327 -0
  107. data/brut-js/docs/jsdoc-theme/static/styles/prettify-jsdoc.css +111 -0
  108. data/brut-js/docs/jsdoc-theme/static/styles/prettify-tomorrow.css +132 -0
  109. data/brut-js/docs/jsdoc-theme/tmpl/augments.tmpl +10 -0
  110. data/brut-js/docs/jsdoc-theme/tmpl/container.tmpl +199 -0
  111. data/brut-js/docs/jsdoc-theme/tmpl/details.tmpl +143 -0
  112. data/brut-js/docs/jsdoc-theme/tmpl/example.tmpl +2 -0
  113. data/brut-js/docs/jsdoc-theme/tmpl/examples.tmpl +13 -0
  114. data/brut-js/docs/jsdoc-theme/tmpl/exceptions.tmpl +32 -0
  115. data/brut-js/docs/jsdoc-theme/tmpl/layout.tmpl +38 -0
  116. data/brut-js/docs/jsdoc-theme/tmpl/mainpage.tmpl +14 -0
  117. data/brut-js/docs/jsdoc-theme/tmpl/members.tmpl +38 -0
  118. data/brut-js/docs/jsdoc-theme/tmpl/method.tmpl +131 -0
  119. data/brut-js/docs/jsdoc-theme/tmpl/modifies.tmpl +14 -0
  120. data/brut-js/docs/jsdoc-theme/tmpl/params.tmpl +131 -0
  121. data/brut-js/docs/jsdoc-theme/tmpl/properties.tmpl +108 -0
  122. data/brut-js/docs/jsdoc-theme/tmpl/returns.tmpl +19 -0
  123. data/brut-js/docs/jsdoc-theme/tmpl/source.tmpl +8 -0
  124. data/brut-js/docs/jsdoc-theme/tmpl/tutorial.tmpl +19 -0
  125. data/brut-js/docs/jsdoc-theme/tmpl/type.tmpl +7 -0
  126. data/brut-js/docs/jsdoc.config.json +23 -0
  127. data/brut-js/docs/package-lock.json +343 -0
  128. data/brut-js/docs/package.json +7 -0
  129. data/brut-js/package-lock.json +2171 -0
  130. data/brut-js/package.json +32 -0
  131. data/brut-js/specs/AjaxSubmit.spec.js +256 -0
  132. data/brut-js/specs/Autosubmit.spec.js +127 -0
  133. data/brut-js/specs/ConfirmSubmit.spec.js +193 -0
  134. data/brut-js/specs/ConstraintViolationMessage.spec.js +33 -0
  135. data/brut-js/specs/ConstraintViolationMessages.spec.js +29 -0
  136. data/brut-js/specs/CopyToClipboard.spec.js +35 -0
  137. data/brut-js/specs/Form.spec.js +181 -0
  138. data/brut-js/specs/I18nTranslation.spec.js +19 -0
  139. data/brut-js/specs/LocaleDetection.spec.js +22 -0
  140. data/brut-js/specs/Message.spec.js +15 -0
  141. data/brut-js/specs/SpecHelper.js +23 -0
  142. data/brut-js/specs/Tabs.spec.js +41 -0
  143. data/brut-js/specs/config/asset_metadata.json +7 -0
  144. data/brut-js/src/AjaxSubmit.js +384 -0
  145. data/brut-js/src/Autosubmit.js +63 -0
  146. data/brut-js/src/BaseCustomElement.js +261 -0
  147. data/brut-js/src/ConfirmSubmit.js +116 -0
  148. data/brut-js/src/ConfirmationDialog.js +143 -0
  149. data/brut-js/src/ConstraintViolationMessage.js +125 -0
  150. data/brut-js/src/ConstraintViolationMessages.js +98 -0
  151. data/brut-js/src/CopyToClipboard.js +96 -0
  152. data/brut-js/src/Form.js +151 -0
  153. data/brut-js/src/I18nTranslation.js +61 -0
  154. data/brut-js/src/LocaleDetection.js +117 -0
  155. data/brut-js/src/Logger.js +90 -0
  156. data/brut-js/src/Message.js +56 -0
  157. data/brut-js/src/RichString.js +113 -0
  158. data/brut-js/src/Tabs.js +168 -0
  159. data/brut-js/src/Tracing.js +247 -0
  160. data/brut-js/src/appForTestingOnly.js +15 -0
  161. data/brut-js/src/index.js +130 -0
  162. data/brut-js/src/testing/AssetMetadata.js +35 -0
  163. data/brut-js/src/testing/AssetMetadataLoader.js +25 -0
  164. data/brut-js/src/testing/CustomElementTest.js +235 -0
  165. data/brut-js/src/testing/DOMCreator.js +45 -0
  166. data/brut-js/src/testing/index.js +48 -0
  167. data/brutrb.com/.vitepress/config.mjs +106 -0
  168. data/brutrb.com/.vitepress/plugins/jsdocLinker.js +34 -0
  169. data/brutrb.com/.vitepress/plugins/rdocLinker.js +18 -0
  170. data/brutrb.com/.vitepress/theme/custom.css +7 -0
  171. data/brutrb.com/.vitepress/theme/index.js +18 -0
  172. data/brutrb.com/.vitepress/theme/style.css +149 -0
  173. data/brutrb.com/ai.md +68 -0
  174. data/brutrb.com/assets.md +138 -0
  175. data/brutrb.com/bin/build +5 -0
  176. data/brutrb.com/bin/deploy +7 -0
  177. data/brutrb.com/bin/dev +5 -0
  178. data/brutrb.com/bin/setup +5 -0
  179. data/brutrb.com/brut-js.md +117 -0
  180. data/brutrb.com/business-logic.md +55 -0
  181. data/brutrb.com/cli.md +278 -0
  182. data/brutrb.com/components.md +243 -0
  183. data/brutrb.com/configuration.md +257 -0
  184. data/brutrb.com/css.md +103 -0
  185. data/brutrb.com/custom-element-tests.md +149 -0
  186. data/brutrb.com/database-access.md +201 -0
  187. data/brutrb.com/database-schema.md +312 -0
  188. data/brutrb.com/deployment.md +66 -0
  189. data/brutrb.com/dev-environment.md +179 -0
  190. data/brutrb.com/doc-conventions.md +39 -0
  191. data/brutrb.com/end-to-end-tests.md +174 -0
  192. data/brutrb.com/flash-and-session.md +224 -0
  193. data/brutrb.com/forms.md +866 -0
  194. data/brutrb.com/getting-started.md +66 -0
  195. data/brutrb.com/handlers.md +153 -0
  196. data/brutrb.com/hooks.md +178 -0
  197. data/brutrb.com/i18n.md +188 -0
  198. data/brutrb.com/images/Makefile +10 -0
  199. data/brutrb.com/images/dev-env-overview.dot +54 -0
  200. data/brutrb.com/images/dev-env-overview.png +0 -0
  201. data/brutrb.com/images/dev-env-protocol.dot +37 -0
  202. data/brutrb.com/images/dev-env-protocol.png +0 -0
  203. data/brutrb.com/images/logo-300.png +0 -0
  204. data/brutrb.com/images/logo.png +0 -0
  205. data/brutrb.com/images/overview.graffle +0 -0
  206. data/brutrb.com/images/overview.png +0 -0
  207. data/brutrb.com/images/spa.dot +19 -0
  208. data/brutrb.com/images/spa.png +0 -0
  209. data/brutrb.com/images/workspace-protocol.dot +44 -0
  210. data/brutrb.com/images/workspace-protocol.png +0 -0
  211. data/brutrb.com/index.md +36 -0
  212. data/brutrb.com/instrumentation.md +183 -0
  213. data/brutrb.com/javascript.md +122 -0
  214. data/brutrb.com/jobs.md +14 -0
  215. data/{doc-src → brutrb.com}/keyword-injection.md +122 -68
  216. data/brutrb.com/markdown-examples.md +85 -0
  217. data/brutrb.com/middleware.md +80 -0
  218. data/brutrb.com/not-released.md +5 -0
  219. data/brutrb.com/overview.md +404 -0
  220. data/brutrb.com/package-lock.json +2404 -0
  221. data/brutrb.com/package.json +11 -0
  222. data/brutrb.com/pages.md +378 -0
  223. data/brutrb.com/public/images/logo-300.png +0 -0
  224. data/brutrb.com/public/images/logo.png +0 -0
  225. data/brutrb.com/routes.md +215 -0
  226. data/brutrb.com/security.md +105 -0
  227. data/brutrb.com/seed-data.md +63 -0
  228. data/brutrb.com/space-time-continuum.md +85 -0
  229. data/brutrb.com/tutorial.md +3 -0
  230. data/brutrb.com/unit-tests.md +148 -0
  231. data/docker-compose.dx.yml +6 -3
  232. data/docs/404.html +21 -0
  233. data/docs/CNAME +1 -0
  234. data/docs/ai.html +24 -0
  235. data/docs/api/Brut/BackEnd/SeedData.html +493 -0
  236. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +214 -0
  237. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +125 -0
  238. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +125 -0
  239. data/docs/api/Brut/BackEnd/Sidekiq.html +125 -0
  240. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +414 -0
  241. data/docs/api/Brut/BackEnd/Validators.html +128 -0
  242. data/docs/api/Brut/BackEnd.html +132 -0
  243. data/docs/api/Brut/CLI/App.html +1576 -0
  244. data/docs/api/Brut/CLI/AppRunner.html +491 -0
  245. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +264 -0
  246. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +306 -0
  247. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +262 -0
  248. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +314 -0
  249. data/docs/api/Brut/CLI/Apps/BuildAssets.html +183 -0
  250. data/docs/api/Brut/CLI/Apps/DB/Create.html +365 -0
  251. data/docs/api/Brut/CLI/Apps/DB/Drop.html +357 -0
  252. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +383 -0
  253. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +335 -0
  254. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +329 -0
  255. data/docs/api/Brut/CLI/Apps/DB/Seed.html +347 -0
  256. data/docs/api/Brut/CLI/Apps/DB/Status.html +383 -0
  257. data/docs/api/Brut/CLI/Apps/DB.html +183 -0
  258. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +303 -0
  259. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +512 -0
  260. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +398 -0
  261. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +374 -0
  262. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +410 -0
  263. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +262 -0
  264. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +303 -0
  265. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +480 -0
  266. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +450 -0
  267. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +380 -0
  268. data/docs/api/Brut/CLI/Apps/Scaffold.html +253 -0
  269. data/docs/api/Brut/CLI/Apps/Test/Audit.html +464 -0
  270. data/docs/api/Brut/CLI/Apps/Test/E2e.html +407 -0
  271. data/docs/api/Brut/CLI/Apps/Test/JS.html +262 -0
  272. data/docs/api/Brut/CLI/Apps/Test/Run.html +578 -0
  273. data/docs/api/Brut/CLI/Apps/Test.html +253 -0
  274. data/docs/api/Brut/CLI/Apps.html +125 -0
  275. data/docs/api/Brut/CLI/Command.html +2342 -0
  276. data/docs/api/Brut/CLI/Error.html +139 -0
  277. data/docs/api/Brut/CLI/ExecutionResults/Result.html +664 -0
  278. data/docs/api/Brut/CLI/ExecutionResults.html +675 -0
  279. data/docs/api/Brut/CLI/Executor.html +430 -0
  280. data/docs/api/Brut/CLI/InvalidOption.html +245 -0
  281. data/docs/api/Brut/CLI/Options.html +753 -0
  282. data/docs/api/Brut/CLI/Output.html +699 -0
  283. data/docs/api/Brut/CLI/SystemExecError.html +451 -0
  284. data/docs/api/Brut/CLI.html +263 -0
  285. data/docs/api/Brut/FactoryBot.html +225 -0
  286. data/docs/api/Brut/Framework/App.html +1097 -0
  287. data/docs/api/Brut/Framework/Config.html +1045 -0
  288. data/docs/api/Brut/Framework/Container.html +1379 -0
  289. data/docs/api/Brut/Framework/Error.html +140 -0
  290. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +144 -0
  291. data/docs/api/Brut/Framework/Errors/Bug.html +234 -0
  292. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +257 -0
  293. data/docs/api/Brut/Framework/Errors/MissingParameter.html +273 -0
  294. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +471 -0
  295. data/docs/api/Brut/Framework/Errors/NotFound.html +308 -0
  296. data/docs/api/Brut/Framework/Errors/NotImplemented.html +234 -0
  297. data/docs/api/Brut/Framework/Errors.html +328 -0
  298. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +392 -0
  299. data/docs/api/Brut/Framework/MCP.html +861 -0
  300. data/docs/api/Brut/Framework/ProjectEnvironment.html +648 -0
  301. data/docs/api/Brut/Framework.html +129 -0
  302. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +317 -0
  303. data/docs/api/Brut/FrontEnd/Component/Helpers.html +326 -0
  304. data/docs/api/Brut/FrontEnd/Component.html +365 -0
  305. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +470 -0
  306. data/docs/api/Brut/FrontEnd/Components/FormTag.html +518 -0
  307. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +317 -0
  308. data/docs/api/Brut/FrontEnd/Components/Input.html +195 -0
  309. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +339 -0
  310. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +660 -0
  311. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +417 -0
  312. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +918 -0
  313. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +651 -0
  314. data/docs/api/Brut/FrontEnd/Components/Inputs.html +125 -0
  315. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +367 -0
  316. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +336 -0
  317. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +655 -0
  318. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +352 -0
  319. data/docs/api/Brut/FrontEnd/Components.html +135 -0
  320. data/docs/api/Brut/FrontEnd/Download.html +467 -0
  321. data/docs/api/Brut/FrontEnd/Flash.html +1150 -0
  322. data/docs/api/Brut/FrontEnd/Form.html +1157 -0
  323. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +634 -0
  324. data/docs/api/Brut/FrontEnd/Forms/Input.html +615 -0
  325. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +547 -0
  326. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +1318 -0
  327. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +609 -0
  328. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +587 -0
  329. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +613 -0
  330. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +582 -0
  331. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +609 -0
  332. data/docs/api/Brut/FrontEnd/Forms.html +127 -0
  333. data/docs/api/Brut/FrontEnd/GenericResponse.html +377 -0
  334. data/docs/api/Brut/FrontEnd/Handler.html +442 -0
  335. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +318 -0
  336. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +336 -0
  337. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +399 -0
  338. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +354 -0
  339. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +151 -0
  340. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +315 -0
  341. data/docs/api/Brut/FrontEnd/Handlers.html +125 -0
  342. data/docs/api/Brut/FrontEnd/HandlingResults.html +339 -0
  343. data/docs/api/Brut/FrontEnd/HttpMethod.html +661 -0
  344. data/docs/api/Brut/FrontEnd/HttpStatus.html +496 -0
  345. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +284 -0
  346. data/docs/api/Brut/FrontEnd/Layout.html +318 -0
  347. data/docs/api/Brut/FrontEnd/Middleware.html +135 -0
  348. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +288 -0
  349. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +292 -0
  350. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +324 -0
  351. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +372 -0
  352. data/docs/api/Brut/FrontEnd/Middlewares.html +125 -0
  353. data/docs/api/Brut/FrontEnd/Page.html +773 -0
  354. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +797 -0
  355. data/docs/api/Brut/FrontEnd/Pages.html +125 -0
  356. data/docs/api/Brut/FrontEnd/RequestContext.html +1312 -0
  357. data/docs/api/Brut/FrontEnd/RouteHook.html +424 -0
  358. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +242 -0
  359. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +249 -0
  360. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +264 -0
  361. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +261 -0
  362. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +284 -0
  363. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +252 -0
  364. data/docs/api/Brut/FrontEnd/RouteHooks.html +115 -0
  365. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +227 -0
  366. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +305 -0
  367. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +324 -0
  368. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +319 -0
  369. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +315 -0
  370. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +315 -0
  371. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +327 -0
  372. data/docs/api/Brut/FrontEnd/Routing/Route.html +761 -0
  373. data/docs/api/Brut/FrontEnd/Routing.html +927 -0
  374. data/docs/api/Brut/FrontEnd/Session.html +1195 -0
  375. data/docs/api/Brut/FrontEnd.html +134 -0
  376. data/docs/api/Brut/I18n/BaseMethods.html +931 -0
  377. data/docs/api/Brut/I18n/ForBackEnd.html +302 -0
  378. data/docs/api/Brut/I18n/ForCLI.html +302 -0
  379. data/docs/api/Brut/I18n/ForHTML.html +296 -0
  380. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +316 -0
  381. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +930 -0
  382. data/docs/api/Brut/I18n.html +127 -0
  383. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +435 -0
  384. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +286 -0
  385. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +302 -0
  386. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +864 -0
  387. data/docs/api/Brut/Instrumentation.html +126 -0
  388. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +532 -0
  389. data/docs/api/Brut/SinatraHelpers.html +281 -0
  390. data/docs/api/Brut/SpecSupport/ClockSupport.html +383 -0
  391. data/docs/api/Brut/SpecSupport/ComponentSupport.html +502 -0
  392. data/docs/api/Brut/SpecSupport/E2ETestServer.html +503 -0
  393. data/docs/api/Brut/SpecSupport/E2eSupport.html +142 -0
  394. data/docs/api/Brut/SpecSupport/EnhancedNode.html +403 -0
  395. data/docs/api/Brut/SpecSupport/FlashSupport.html +278 -0
  396. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +401 -0
  397. data/docs/api/Brut/SpecSupport/GeneralSupport.html +195 -0
  398. data/docs/api/Brut/SpecSupport/HandlerSupport.html +160 -0
  399. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +553 -0
  400. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +439 -0
  401. data/docs/api/Brut/SpecSupport/Matchers.html +125 -0
  402. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +335 -0
  403. data/docs/api/Brut/SpecSupport/RSpecSetup.html +602 -0
  404. data/docs/api/Brut/SpecSupport/SessionSupport.html +196 -0
  405. data/docs/api/Brut/SpecSupport.html +129 -0
  406. data/docs/api/Brut.html +225 -0
  407. data/docs/api/Clock.html +603 -0
  408. data/docs/api/RichString.html +968 -0
  409. data/docs/api/SemanticLogger/Appender/Async.html +219 -0
  410. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +115 -0
  411. data/docs/api/Sequel/Extensions/BrutMigrations.html +533 -0
  412. data/docs/api/Sequel/Extensions.html +117 -0
  413. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +105 -0
  414. data/docs/api/Sequel/Plugins/CreatedAt.html +125 -0
  415. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +207 -0
  416. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +186 -0
  417. data/docs/api/Sequel/Plugins/ExternalId.html +218 -0
  418. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +202 -0
  419. data/docs/api/Sequel/Plugins/FindBang.html +125 -0
  420. data/docs/api/Sequel/Plugins.html +117 -0
  421. data/docs/api/Sequel.html +117 -0
  422. data/docs/api/_index.html +1553 -0
  423. data/docs/api/class_list.html +54 -0
  424. data/docs/api/css/common.css +1 -0
  425. data/docs/api/css/full_list.css +58 -0
  426. data/docs/api/css/style.css +503 -0
  427. data/docs/api/file.README.html +127 -0
  428. data/docs/api/file_list.html +59 -0
  429. data/docs/api/frames.html +22 -0
  430. data/docs/api/index.html +127 -0
  431. data/docs/api/js/app.js +344 -0
  432. data/docs/api/js/full_list.js +242 -0
  433. data/docs/api/js/jquery.js +4 -0
  434. data/docs/api/method_list.html +3998 -0
  435. data/docs/api/top-level-namespace.html +112 -0
  436. data/docs/assets/ai.md.tZrjP9im.js +1 -0
  437. data/docs/assets/ai.md.tZrjP9im.lean.js +1 -0
  438. data/docs/assets/app.D_yaTITQ.js +1 -0
  439. data/docs/assets/assets.md.D3wunzLx.js +19 -0
  440. data/docs/assets/assets.md.D3wunzLx.lean.js +1 -0
  441. data/docs/assets/brut-js.md.o2DAO2s2.js +12 -0
  442. data/docs/assets/brut-js.md.o2DAO2s2.lean.js +1 -0
  443. data/docs/assets/business-logic.md.BY4hGy0m.js +1 -0
  444. data/docs/assets/business-logic.md.BY4hGy0m.lean.js +1 -0
  445. data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +1 -0
  446. data/docs/assets/chunks/VPLocalSearchBox.B2-ZzyTY.js +8 -0
  447. data/docs/assets/chunks/framework.1L-BeKqY.js +18 -0
  448. data/docs/assets/chunks/theme.CfGFVRvE.js +2 -0
  449. data/docs/assets/cli.md.RmeA2b0i.js +127 -0
  450. data/docs/assets/cli.md.RmeA2b0i.lean.js +1 -0
  451. data/docs/assets/components.md.eCttGlN-.js +104 -0
  452. data/docs/assets/components.md.eCttGlN-.lean.js +1 -0
  453. data/docs/assets/configuration.md.BRriU0cL.js +78 -0
  454. data/docs/assets/configuration.md.BRriU0cL.lean.js +1 -0
  455. data/docs/assets/css.md.DJgj2clw.js +21 -0
  456. data/docs/assets/css.md.DJgj2clw.lean.js +1 -0
  457. data/docs/assets/custom-element-tests.md.BrYJQEl3.js +69 -0
  458. data/docs/assets/custom-element-tests.md.BrYJQEl3.lean.js +1 -0
  459. data/docs/assets/database-access.md.C7l-Vuvb.js +63 -0
  460. data/docs/assets/database-access.md.C7l-Vuvb.lean.js +1 -0
  461. data/docs/assets/database-schema.md.BUjR0VS1.js +63 -0
  462. data/docs/assets/database-schema.md.BUjR0VS1.lean.js +1 -0
  463. data/docs/assets/deployment.md.Dbka4OTr.js +1 -0
  464. data/docs/assets/deployment.md.Dbka4OTr.lean.js +1 -0
  465. data/docs/assets/dev-env-overview.Gj7NWM8-.png +0 -0
  466. data/docs/assets/dev-env-protocol.DysDAtnz.png +0 -0
  467. data/docs/assets/dev-environment.md.BNc8AYiK.js +11 -0
  468. data/docs/assets/dev-environment.md.BNc8AYiK.lean.js +1 -0
  469. data/docs/assets/doc-conventions.md.DCfRXXi-.js +1 -0
  470. data/docs/assets/doc-conventions.md.DCfRXXi-.lean.js +1 -0
  471. data/docs/assets/end-to-end-tests.md.yfQHC0b5.js +26 -0
  472. data/docs/assets/end-to-end-tests.md.yfQHC0b5.lean.js +1 -0
  473. data/docs/assets/flash-and-session.md.BXY8RvT0.js +93 -0
  474. data/docs/assets/flash-and-session.md.BXY8RvT0.lean.js +1 -0
  475. data/docs/assets/forms.md.CBTYQ_Cz.js +379 -0
  476. data/docs/assets/forms.md.CBTYQ_Cz.lean.js +1 -0
  477. data/docs/assets/getting-started.md.Bz2s1Vjb.js +2 -0
  478. data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +1 -0
  479. data/docs/assets/handlers.md.089DVD3v.js +69 -0
  480. data/docs/assets/handlers.md.089DVD3v.lean.js +1 -0
  481. data/docs/assets/hooks.md.C4-moMny.js +80 -0
  482. data/docs/assets/hooks.md.C4-moMny.lean.js +1 -0
  483. data/docs/assets/i18n.md.Do9i1qWl.js +23 -0
  484. data/docs/assets/i18n.md.Do9i1qWl.lean.js +1 -0
  485. data/docs/assets/index.md.B28EwVpq.js +1 -0
  486. data/docs/assets/index.md.B28EwVpq.lean.js +1 -0
  487. data/docs/assets/instrumentation.md.CL6ax7nT.js +35 -0
  488. data/docs/assets/instrumentation.md.CL6ax7nT.lean.js +1 -0
  489. data/docs/assets/javascript.md.GWbhRS51.js +31 -0
  490. data/docs/assets/javascript.md.GWbhRS51.lean.js +1 -0
  491. data/docs/assets/jobs.md.S-2amAYp.js +1 -0
  492. data/docs/assets/jobs.md.S-2amAYp.lean.js +1 -0
  493. data/docs/assets/keyword-injection.md.Dt2tKREs.js +25 -0
  494. data/docs/assets/keyword-injection.md.Dt2tKREs.lean.js +1 -0
  495. data/docs/assets/markdown-examples.md.CCFEQO44.js +33 -0
  496. data/docs/assets/markdown-examples.md.CCFEQO44.lean.js +1 -0
  497. data/docs/assets/middleware.md.Czz_UlJN.js +20 -0
  498. data/docs/assets/middleware.md.Czz_UlJN.lean.js +1 -0
  499. data/docs/assets/not-released.md.BBy28McC.js +1 -0
  500. data/docs/assets/not-released.md.BBy28McC.lean.js +1 -0
  501. data/docs/assets/overview.Da81cB9R.png +0 -0
  502. data/docs/assets/overview.md.CDalkuxV.js +133 -0
  503. data/docs/assets/overview.md.CDalkuxV.lean.js +1 -0
  504. data/docs/assets/pages.md.BE3kfOc5.js +122 -0
  505. data/docs/assets/pages.md.BE3kfOc5.lean.js +1 -0
  506. data/docs/assets/routes.md.BMM7peut.js +29 -0
  507. data/docs/assets/routes.md.BMM7peut.lean.js +1 -0
  508. data/docs/assets/security.md.C668yXCi.js +1 -0
  509. data/docs/assets/security.md.C668yXCi.lean.js +1 -0
  510. data/docs/assets/seed-data.md.BvFZlqIk.js +14 -0
  511. data/docs/assets/seed-data.md.BvFZlqIk.lean.js +1 -0
  512. data/docs/assets/spa.qejUdp-5.png +0 -0
  513. data/docs/assets/space-time-continuum.md.KPUIKysQ.js +1 -0
  514. data/docs/assets/space-time-continuum.md.KPUIKysQ.lean.js +1 -0
  515. data/docs/assets/style.D73IYGCX.css +1 -0
  516. data/docs/assets/tutorial.md.BnoGjrdK.js +1 -0
  517. data/docs/assets/tutorial.md.BnoGjrdK.lean.js +1 -0
  518. data/docs/assets/unit-tests.md.DUGrnLj5.js +13 -0
  519. data/docs/assets/unit-tests.md.DUGrnLj5.lean.js +1 -0
  520. data/docs/assets/workspace-protocol.C0gXsoDb.png +0 -0
  521. data/docs/assets.html +42 -0
  522. data/docs/brut-css/brut.css +1 -0
  523. data/docs/brut-css/brut.max.css +22372 -0
  524. data/docs/brut-css/classes/appearances.html +783 -0
  525. data/docs/brut-css/classes/background-colors.html +3529 -0
  526. data/docs/brut-css/classes/border-colors.html +3529 -0
  527. data/docs/brut-css/classes/borders.html +2293 -0
  528. data/docs/brut-css/classes/dimensions.html +2581 -0
  529. data/docs/brut-css/classes/flex.html +917 -0
  530. data/docs/brut-css/classes/foreground-colors.html +3261 -0
  531. data/docs/brut-css/classes/junk-drawer.html +431 -0
  532. data/docs/brut-css/classes/layout.html +668 -0
  533. data/docs/brut-css/classes/lists.html +331 -0
  534. data/docs/brut-css/classes/positioning.html +1751 -0
  535. data/docs/brut-css/classes/spacings.html +2633 -0
  536. data/docs/brut-css/classes/typography.html +2206 -0
  537. data/docs/brut-css/customization/advanced-configuration.html +204 -0
  538. data/docs/brut-css/customization/breakpoints.html +227 -0
  539. data/docs/brut-css/customization/design-system.html +197 -0
  540. data/docs/brut-css/customization/pseudo-classes.html +228 -0
  541. data/docs/brut-css/docs.css +98 -0
  542. data/docs/brut-css/getting-started/core-concepts.html +234 -0
  543. data/docs/brut-css/getting-started/installation.html +190 -0
  544. data/docs/brut-css/getting-started/overview.html +210 -0
  545. data/docs/brut-css/getting-started/simple-example.html +285 -0
  546. data/docs/brut-css/index.html +193 -0
  547. data/docs/brut-css/prism-twilight.min.css +1 -0
  548. data/docs/brut-css/properties/colors.html +1548 -0
  549. data/docs/brut-css/properties/spacings.html +614 -0
  550. data/docs/brut-css/properties/typography.html +777 -0
  551. data/docs/brut-js/api/AjaxSubmit.html +374 -0
  552. data/docs/brut-js/api/AjaxSubmit.js.html +435 -0
  553. data/docs/brut-js/api/Autosubmit.html +192 -0
  554. data/docs/brut-js/api/Autosubmit.js.html +114 -0
  555. data/docs/brut-js/api/BaseCustomElement.html +1091 -0
  556. data/docs/brut-js/api/BaseCustomElement.js.html +312 -0
  557. data/docs/brut-js/api/BrutCustomElements.html +172 -0
  558. data/docs/brut-js/api/BufferedLogger.html +173 -0
  559. data/docs/brut-js/api/ConfirmSubmit.html +278 -0
  560. data/docs/brut-js/api/ConfirmSubmit.js.html +167 -0
  561. data/docs/brut-js/api/ConfirmationDialog.html +425 -0
  562. data/docs/brut-js/api/ConfirmationDialog.js.html +194 -0
  563. data/docs/brut-js/api/ConstraintViolationMessage.html +448 -0
  564. data/docs/brut-js/api/ConstraintViolationMessage.js.html +176 -0
  565. data/docs/brut-js/api/ConstraintViolationMessages.html +590 -0
  566. data/docs/brut-js/api/ConstraintViolationMessages.js.html +149 -0
  567. data/docs/brut-js/api/CopyToClipboard.html +345 -0
  568. data/docs/brut-js/api/CopyToClipboard.js.html +147 -0
  569. data/docs/brut-js/api/Form.html +294 -0
  570. data/docs/brut-js/api/Form.js.html +202 -0
  571. data/docs/brut-js/api/I18nTranslation.html +409 -0
  572. data/docs/brut-js/api/I18nTranslation.js.html +112 -0
  573. data/docs/brut-js/api/LocaleDetection.html +312 -0
  574. data/docs/brut-js/api/LocaleDetection.js.html +168 -0
  575. data/docs/brut-js/api/Logger.html +702 -0
  576. data/docs/brut-js/api/Logger.js.html +141 -0
  577. data/docs/brut-js/api/Message.html +238 -0
  578. data/docs/brut-js/api/Message.js.html +107 -0
  579. data/docs/brut-js/api/PrefixedLogger.html +369 -0
  580. data/docs/brut-js/api/RichString.html +1049 -0
  581. data/docs/brut-js/api/RichString.js.html +164 -0
  582. data/docs/brut-js/api/Tabs.html +295 -0
  583. data/docs/brut-js/api/Tabs.js.html +219 -0
  584. data/docs/brut-js/api/Tracing.html +277 -0
  585. data/docs/brut-js/api/Tracing.js.html +298 -0
  586. data/docs/brut-js/api/external-CustomElementRegistry.html +140 -0
  587. data/docs/brut-js/api/external-Performance.html +138 -0
  588. data/docs/brut-js/api/external-Promise.html +138 -0
  589. data/docs/brut-js/api/external-ValidityState.html +138 -0
  590. data/docs/brut-js/api/external-Window.html +233 -0
  591. data/docs/brut-js/api/external-fetch.html +138 -0
  592. data/docs/brut-js/api/global.html +400 -0
  593. data/docs/brut-js/api/index.html +168 -0
  594. data/docs/brut-js/api/index.js.html +181 -0
  595. data/docs/brut-js/api/module-testing.html +383 -0
  596. data/docs/brut-js/api/scripts/linenumber.js +25 -0
  597. data/docs/brut-js/api/scripts/prettify/Apache-License-2.0.txt +202 -0
  598. data/docs/brut-js/api/scripts/prettify/lang-css.js +2 -0
  599. data/docs/brut-js/api/scripts/prettify/prettify.js +28 -0
  600. data/docs/brut-js/api/styles/jsdoc-default.css +327 -0
  601. data/docs/brut-js/api/styles/prettify-jsdoc.css +111 -0
  602. data/docs/brut-js/api/styles/prettify-tomorrow.css +132 -0
  603. data/docs/brut-js/api/testing.AssetMetadata.html +172 -0
  604. data/docs/brut-js/api/testing.AssetMetadataLoader.html +171 -0
  605. data/docs/brut-js/api/testing.CustomElementTest.html +679 -0
  606. data/docs/brut-js/api/testing.DOMCreator.html +171 -0
  607. data/docs/brut-js/api/testing_AssetMetadata.js.html +86 -0
  608. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +76 -0
  609. data/docs/brut-js/api/testing_CustomElementTest.js.html +286 -0
  610. data/docs/brut-js/api/testing_DOMCreator.js.html +96 -0
  611. data/docs/brut-js/api/testing_index.js.html +99 -0
  612. data/docs/brut-js.html +35 -0
  613. data/docs/business-logic.html +24 -0
  614. data/docs/cli.html +150 -0
  615. data/docs/components.html +127 -0
  616. data/docs/configuration.html +101 -0
  617. data/docs/css.html +44 -0
  618. data/docs/custom-element-tests.html +92 -0
  619. data/docs/database-access.html +86 -0
  620. data/docs/database-schema.html +86 -0
  621. data/docs/deployment.html +24 -0
  622. data/docs/dev-environment.html +34 -0
  623. data/docs/doc-conventions.html +24 -0
  624. data/docs/end-to-end-tests.html +49 -0
  625. data/docs/flash-and-session.html +116 -0
  626. data/docs/forms.html +402 -0
  627. data/docs/getting-started.html +25 -0
  628. data/docs/handlers.html +92 -0
  629. data/docs/hashmap.json +1 -0
  630. data/docs/hooks.html +103 -0
  631. data/docs/i18n.html +46 -0
  632. data/docs/images/logo-300.png +0 -0
  633. data/docs/images/logo.png +0 -0
  634. data/docs/index.html +24 -0
  635. data/docs/instrumentation.html +58 -0
  636. data/docs/javascript.html +54 -0
  637. data/docs/jobs.html +24 -0
  638. data/docs/keyword-injection.html +48 -0
  639. data/docs/markdown-examples.html +56 -0
  640. data/docs/middleware.html +43 -0
  641. data/docs/not-released.html +24 -0
  642. data/docs/overview.html +156 -0
  643. data/docs/pages.html +145 -0
  644. data/docs/routes.html +52 -0
  645. data/docs/security.html +24 -0
  646. data/docs/seed-data.html +37 -0
  647. data/docs/space-time-continuum.html +24 -0
  648. data/docs/tutorial.html +24 -0
  649. data/docs/unit-tests.html +36 -0
  650. data/docs/vp-icons.css +1 -0
  651. data/lib/brut/back_end/seed_data.rb +19 -2
  652. data/lib/brut/back_end/sidekiq/middlewares/server.rb +2 -1
  653. data/lib/brut/back_end/sidekiq/middlewares.rb +2 -1
  654. data/lib/brut/back_end/sidekiq.rb +2 -1
  655. data/lib/brut/back_end/validator.rb +5 -1
  656. data/lib/brut/back_end.rb +4 -2
  657. data/lib/brut/cli/app_runner.rb +1 -1
  658. data/lib/brut/cli/apps/test.rb +5 -0
  659. data/lib/brut/cli.rb +4 -3
  660. data/lib/brut/factory_bot.rb +0 -5
  661. data/lib/brut/framework/app.rb +70 -5
  662. data/lib/brut/framework/config.rb +5 -3
  663. data/lib/brut/framework/container.rb +3 -2
  664. data/lib/brut/framework/errors.rb +12 -4
  665. data/lib/brut/framework/mcp.rb +58 -1
  666. data/lib/brut/framework/project_environment.rb +6 -2
  667. data/lib/brut/framework.rb +1 -1
  668. data/lib/brut/front_end/component.rb +69 -71
  669. data/lib/brut/front_end/components/constraint_violations.rb +1 -4
  670. data/lib/brut/front_end/components/form_tag.rb +1 -1
  671. data/lib/brut/front_end/components/input.rb +3 -3
  672. data/lib/brut/front_end/components/inputs/csrf_token.rb +1 -1
  673. data/lib/brut/front_end/components/inputs/{text_field.rb → input_tag.rb} +7 -9
  674. data/lib/brut/front_end/components/inputs/radio_button.rb +1 -1
  675. data/lib/brut/front_end/components/inputs/select_tag_with_options.rb +187 -0
  676. data/lib/brut/front_end/components/inputs/{textarea.rb → textarea_tag.rb} +2 -2
  677. data/lib/brut/front_end/components/time_tag.rb +2 -1
  678. data/lib/brut/front_end/form.rb +4 -4
  679. data/lib/brut/front_end/forms/input.rb +2 -1
  680. data/lib/brut/front_end/forms/input_definition.rb +5 -2
  681. data/lib/brut/front_end/forms/radio_button_group_input.rb +2 -1
  682. data/lib/brut/front_end/forms/radio_button_group_input_definition.rb +2 -2
  683. data/lib/brut/front_end/forms/select_input.rb +2 -4
  684. data/lib/brut/front_end/forms/select_input_definition.rb +2 -2
  685. data/lib/brut/front_end/handler.rb +28 -26
  686. data/lib/brut/front_end/handlers/csp_reporting_handler.rb +5 -2
  687. data/lib/brut/front_end/handlers/instrumentation_handler.rb +8 -4
  688. data/lib/brut/front_end/handlers/locale_detection_handler.rb +9 -5
  689. data/lib/brut/front_end/handlers/missing_handler.rb +5 -2
  690. data/lib/brut/front_end/layout.rb +16 -0
  691. data/lib/brut/front_end/page.rb +52 -29
  692. data/lib/brut/front_end/request_context.rb +3 -2
  693. data/lib/brut/front_end/routing.rb +5 -1
  694. data/lib/brut/front_end.rb +4 -13
  695. data/lib/brut/i18n/base_methods.rb +167 -79
  696. data/lib/brut/i18n/for_back_end.rb +4 -0
  697. data/lib/brut/i18n/for_cli.rb +4 -0
  698. data/lib/brut/i18n/for_html.rb +32 -4
  699. data/lib/brut/i18n/http_accept_language.rb +47 -0
  700. data/lib/brut/instrumentation/open_telemetry.rb +36 -1
  701. data/lib/brut/instrumentation.rb +3 -5
  702. data/lib/brut/sinatra_helpers.rb +11 -3
  703. data/lib/brut/spec_support/component_support.rb +30 -16
  704. data/lib/brut/spec_support/e2e_support.rb +1 -1
  705. data/lib/brut/spec_support/e2e_test_server.rb +3 -0
  706. data/lib/brut/spec_support/general_support.rb +3 -0
  707. data/lib/brut/spec_support/handler_support.rb +6 -1
  708. data/lib/brut/spec_support/matcher.rb +1 -0
  709. data/lib/brut/spec_support/matchers/be_page_for.rb +1 -0
  710. data/lib/brut/spec_support/matchers/have_html_attribute.rb +1 -0
  711. data/lib/brut/spec_support/matchers/have_i18n_string.rb +2 -5
  712. data/lib/brut/spec_support/matchers/have_link_to.rb +1 -0
  713. data/lib/brut/spec_support/matchers/have_redirected_to.rb +1 -0
  714. data/lib/brut/spec_support/matchers/have_rendered.rb +1 -0
  715. data/lib/brut/spec_support/matchers/have_returned_rack_response.rb +44 -0
  716. data/lib/brut/spec_support.rb +1 -1
  717. data/lib/brut/version.rb +1 -1
  718. data/lib/brut.rb +5 -4
  719. data/lib/sequel/extensions/brut_migrations.rb +1 -1
  720. metadata +648 -13
  721. data/doc-src/architecture.md +0 -102
  722. data/doc-src/assets.md +0 -98
  723. data/doc-src/forms.md +0 -214
  724. data/doc-src/handlers.md +0 -83
  725. data/doc-src/javascript.md +0 -265
  726. data/doc-src/pages.md +0 -210
  727. data/doc-src/route-hooks.md +0 -59
  728. data/lib/brut/front_end/components/inputs/select.rb +0 -117
@@ -1,9 +1,10 @@
1
1
  # An "App" in Brut paralance is the collection of source code and configuration that is needed to operate
2
2
  # a website. This includes everything needed to serve HTTP requests, but also includes ancillary
3
- # tasks and any related files required for the app to exist and function. Your app will have an `App` class that subclasses this
4
- # class.
3
+ # tasks and any related files required for the app to exist and function.
4
+ # Your app will have an `App` class that subclasses this class.
5
5
  #
6
- # When your app is initialized, Brut will have been configured, but access to internal resources may not be available. It is here
6
+ # When your app is initialized, Brut will have been configured,
7
+ # but access to internal resources may not be available. It is here
7
8
  # that you can override configuration values or do any other setup before everything boots.
8
9
  class Brut::Framework::App
9
10
  include Brut::Framework::Errors
@@ -18,7 +19,8 @@ class Brut::Framework::App
18
19
  # actions where an app needs to exist inside some organizational context.
19
20
  def organization = id
20
21
 
21
- # Call this in your app's definition to define your app's routes. The contents of the block will be evaluated in the context of
22
+ # Call this in your app's definition to define your app's routes.
23
+ # The contents of the block will be evaluated in the context of
22
24
  # {Brut::SinatraHelpers::ClassMethods}, and the methods there are generally the ones you should be calling.
23
25
  #
24
26
  # You can call this multiple times and the routes will be concatenated together.
@@ -31,6 +33,69 @@ class Brut::Framework::App
31
33
  end
32
34
  end
33
35
 
36
+ # Call this to specify what happens when an unhandled exception occurs. You may call this mulitple times,
37
+ # however note that if an error is caught that matches more than one block's condition, the one that is called
38
+ # will be the first one declared.
39
+ #
40
+ # The only deviation from this rule is when you call this
41
+ # without any condition. Doing that establishes the behavior for a "catch all" handler, which is
42
+ # only called when no other configured block can handle the exception. You can declare
43
+ # this at any time. **Do note** the "catch all" handler is more of a best effort. Brut is currently
44
+ # based on Sinatra which provides no way to arbitrarily catch all exceptions. What Brut does here is to
45
+ # explicitly catch the range of http status codes from 400 to 999.
46
+ #
47
+ # Note that Brut will record the exception via OpenTelemetry so you should not do this in your handlers. It
48
+ # would be preferable to instead record an event if you want to have observability from your error handlers.
49
+ #
50
+ # @param [Class|Integer|Range<Integer>] condition if given this specifies the conditions under which the given
51
+ # block will handle the error. If omitted, this block will handle any error that doesn't have a more
52
+ # specific handler configured. Meaning of values:
53
+ # * A class - this is an exception class that, if caught, triggers the handler
54
+ # * An integer - this is an HTTP status code that, if returned, triggers the handler
55
+ # * A range of integers - this is a range of HTTP status codes that, if returned, triggers the handler
56
+ # @yield [Exception] the block is given two named parameters: `exception:` and `http_status_code:`. Your block
57
+ # can declare both, either, or none. Any that are declared will be given values. At least one
58
+ # will be non-`nil`, however are encouraged to code defensively inside this block.
59
+ # @yieldparam [Exception] exception: the exception that was raised. This will be `nil`
60
+ # if the error was caused by an HTTP status code.
61
+ # @yieldparam [Integer] http_status_code: the HTTP status code that was returned. If `exception:` is
62
+ # not `nil`, this value is highly likely to be 500.
63
+ # @yieldreturn The block should return a valid Rack response. For now.
64
+ def self.error(condition=:catch_all, &block)
65
+ @error_blocks ||= {}
66
+ if block.nil?
67
+ raise ArgumentError, "You must provide a block to error"
68
+ end
69
+ parameters = block.parameters.reject { |type,name|
70
+ type == :keyreq && [ :http_status_code, :exception ].include?(name)
71
+ }
72
+ if parameters.any?
73
+ messages = parameters.map { |type,name|
74
+ case type
75
+ when :keyreq
76
+ "required keyword parameter '#{name}:'"
77
+ when :key
78
+ "optional keyword parameter '#{name}:'"
79
+ when :rest
80
+ "rest parameter '#{name}'"
81
+ when :opt
82
+ "optional parameter '#{name}'"
83
+ when :req
84
+ "required parameter '#{name}'"
85
+ else
86
+ "unknown parameter '#{name}'"
87
+ end
88
+ }
89
+ raise ArgumentError, "Your error handler block may only accept exception: and http_status_code: as required keyword parameters. The following parameters were found:\n #{messages.join("\n ")}"
90
+ end
91
+ if @error_blocks[condition]
92
+ raise ArgumentError, "You have already configured error handling for condition '#{condition.to_s}'"
93
+ end
94
+ @error_blocks[condition] = block
95
+ end
96
+
97
+ def self.error_blocks = @error_blocks || {}
98
+
34
99
  # Add a Rack middleware to your app. Middlewares are configured in the order in which you call this method.
35
100
  #
36
101
  # @param [Class] middleware a class that implements [Rack Middleware](https://github.com/rack/rack/blob/main/SPEC.rdoc).
@@ -81,7 +146,7 @@ class Brut::Framework::App
81
146
  # code required *after* Brut has been set up and started. You can rely on the
82
147
  # database being available. Any attempts to override configuration values
83
148
  # may not succeed. This is called after the framework has booted, but before
84
- # your apps' routes are set up.
149
+ # your app's routes are set up.
85
150
  def boot!
86
151
  end
87
152
 
@@ -1,11 +1,13 @@
1
1
  require_relative "project_environment"
2
2
  require "pathname"
3
3
 
4
- # Holds configuration for the framework and your app. In general, you should not interact with this class, however it's source code
5
- # is a good reference for what is configured by default by Brut.
4
+ # Holds configuration for the framework and your app. In general, you
5
+ # should not interact with this class, however it's source code is a good
6
+ # reference for what is configured by default by Brut.
6
7
  class Brut::Framework::Config
7
8
 
8
- # Configures all defaults. In general, this attempts to be lazy in setting things up, so calling this should not attempt to make a
9
+ # Configures all defaults. In general, this attempts to be lazy in
10
+ # setting things up, so calling this should not attempt to make a
9
11
  # connection to your database.
10
12
  def configure!
11
13
  Brut.container do |c|
@@ -21,7 +21,8 @@ end
21
21
  #
22
22
  # There is no namespacing/hierarchy.
23
23
  #
24
- # In general, you should not create instances of this class, but you may need to access it via {Brut.container} in order to obtain
24
+ # In general, you should not create instances of this class, but you may
25
+ # need to access it via {Brut.container} in order to obtain
25
26
  # configuration values or set your own.
26
27
  class Brut::Framework::Container
27
28
  def initialize
@@ -92,7 +93,7 @@ class Brut::Framework::Container
92
93
  end
93
94
 
94
95
  # Called by your app to override an existing value. The value must be overridable (see {#store}). Generally, you should call this
95
- # in the initializer of your {Brut::Framework::App} subclass. Calling this after the fact may not have the affect you want.
96
+ # in the initializer of your {Brut::Framework::App} subclass. Calling this after the fact may not have the effect you want.
96
97
  #
97
98
  # @param [String|Symbol] name name of the value to override. Will be coerced to a String. This name must have been previously
98
99
  # configured.
@@ -1,7 +1,7 @@
1
1
  module Brut
2
2
  module Framework
3
- # Include this in your class to access some helpful methods that throw commonly-needed
4
- # errors
3
+ # Namespace for Brut-specific error classes, and a holder of several error-related convienience
4
+ # methods. Include this module to gain access to those methods.
5
5
  module Errors
6
6
  autoload(:Bug,"brut/framework/errors/bug")
7
7
  autoload(:NotImplemented,"brut/framework/errors/not_implemented")
@@ -10,7 +10,12 @@ module Brut
10
10
  autoload(:MissingConfiguration,"brut/framework/errors/missing_configuration")
11
11
  autoload(:AbstractMethod,"brut/framework/errors/abstract_method")
12
12
  autoload(:NoClassForPath,"brut/framework/errors/no_class_for_path")
13
- # Raises {Brut::Framework::Errors::Bug}
13
+ # Raises {Brut::Framework::Errors::Bug}, used to indicate a codepath is a bug. "But, why write a
14
+ # bug in the first place?" you may be asking. Sometimes, a code path exists, but external factors
15
+ # mean that it should never be executed. Or, sometimes an API can be mis-used, but the current
16
+ # state of the system is such that it would never be misused.
17
+ #
18
+ # This method allows you to indicate such situations and provide a meaningful explanation.
14
19
  #
15
20
  # @param message Message to include in the error
16
21
  # @raise [Brut::Framework::Errors::Bug]
@@ -18,7 +23,10 @@ module Brut
18
23
  raise Brut::Framework::Errors::Bug,message
19
24
  end
20
25
 
21
- # Raises {Brut::Framework::Errors::AbstractMethod}
26
+ # Raises {Brut::Framework::Errors::AbstractMethod}, which is useful if you need to document
27
+ # a method that a subclass must implement, but for which there is no useful default
28
+ # implementation.
29
+ #
22
30
  # @raise [Brut::Framework::Errors::AbstractMethod]
23
31
  def abstract_method!
24
32
  raise Brut::Framework::Errors::AbstractMethod
@@ -116,8 +116,65 @@ class Brut::Framework::MCP
116
116
  @sinatra_app = Class.new(Sinatra::Base)
117
117
  @sinatra_app.include(Brut::SinatraHelpers)
118
118
 
119
+ safely_record_exception = ->(exception,http_status_code) {
120
+ begin
121
+ if exception.nil?
122
+ Brut.container.instrumentation.add_event("error triggered without exception", http_status_code:)
123
+ else
124
+ Brut.container.instrumentation.record_exception(exception, http_status_code:)
125
+ end
126
+ rescue => ex
127
+ begin
128
+ SemanticLogger[self.class].error(
129
+ "Error recording exception",
130
+ original_excerption: exception,
131
+ exception: ex)
132
+ rescue => ex2
133
+ $stderr.puts "While handling an error recording an exception, we get another error from SemanticLogger." + [
134
+ [ :original_exception, exception, ].join(": "),
135
+ [ :exception_from_recording_exception, ex, ].join(": "),
136
+ [ :exception_from_semantic_logger, ex2 ].join(": "),
137
+
138
+ ].join(", ")
139
+ end
140
+ end
141
+ }
142
+
143
+ @app.class.error_blocks.each do |condition,block|
144
+ if condition != :catch_all
145
+ @sinatra_app.error(condition) do
146
+ exception = request.env["sinatra.error"]
147
+ safely_record_exception.(exception, response.status)
148
+ block_args = if exception
149
+ { exception: }
150
+ else
151
+ { http_status_code: response.status }
152
+ end
153
+ block.(**block_args)
154
+ end
155
+ end
156
+ end
157
+ if @app.class.error_blocks[:catch_all]
158
+ block = @app.class.error_blocks[:catch_all]
159
+ block_args = block.parameters.map { |(type,name)|
160
+ [ name, nil ]
161
+ }.to_h
162
+ @sinatra_app.error(400..999) do
163
+ exception = request.env["sinatra.error"]
164
+ safely_record_exception.(exception, response.status)
165
+ if block_args.key?(:exception)
166
+ block_args[:exception] = exception
167
+ end
168
+ if block_args.key?(:http_status_code)
169
+ block_args[:http_status_code] = response.status
170
+ end
171
+ block.(**block_args)
172
+ end
173
+ else
174
+ end
175
+
119
176
  message = if Brut.container.project_env.development?
120
- "Form submission did not include an authenticity token. All forms must include one. To add one, use the `form_tag` helper, or include <%= component(Brut::FrontEnd::Components::Inputs::CsrfToken) %> somewhere inside your <form> tag"
177
+ "Form submission did not include an authenticity token. All forms must include one. To add one, use the `form_tag` helper, or include Brut::FrontEnd::Components::Inputs::CsrfToken somewhere inside your <form> tag"
121
178
  else
122
179
  "Forbidden"
123
180
  end
@@ -1,5 +1,7 @@
1
- # Manages the interpretation of dev/test/prod. The canonical instance is available via `Brut.container.project_env`. Generally, you
2
- # should avoid basing logic on this, or at least contain the conditional behavior to the configuration values. But, you do you.
1
+ # Manages the interpretation of dev/test/prod. The canonical instance is available
2
+ # via `Brut.container.project_env`. Generally, you
3
+ # should avoid basing logic on this, or at least contain the conditional behavior
4
+ # to the configuration values. But, you do you.
3
5
  class Brut::Framework::ProjectEnvironment
4
6
  # Create the project environment based on the string
5
7
  # @param [String] string_value value from e.g. `ENV["RACK_ENV"]` to use to set the environment
@@ -21,6 +23,8 @@ class Brut::Framework::ProjectEnvironment
21
23
  # @return [true|false] true is this is production
22
24
  def production? = @value == "production"
23
25
 
26
+ def staging? = raise "Staging is a lie, please consider feature flags or literally any other way to manage in-development features of your app. I promise you, you will regret ever having to do anything with a staging server"
27
+
24
28
  # @return [String] the string value (which should be suitable for the constructor)
25
29
  def to_s = @value
26
30
  end
@@ -1,5 +1,5 @@
1
1
  module Brut
2
- # The Framework module holds a lot of Brut's internals, or classes that cut across the back end and front end.
2
+ # Namespace for Brut's internals as well as classes that aren't strictly front or back end.
3
3
  module Framework
4
4
  autoload(:App,"brut/framework/app")
5
5
  autoload(:Config,"brut/framework/config")
@@ -16,21 +16,18 @@ module Brut::FrontEnd::Components
16
16
  end
17
17
 
18
18
  # A Component is the top level class for managing the rendering of
19
- # content. A component is essentially an ERB template and a class whose
20
- # instance servces as it's binding. It is very similar to a View Component, though
21
- # not quite as fancy.
19
+ # content. It is a Phlex component with additional features.
20
+ # Components are the primary mechanism for managing view complexity and managing
21
+ # markup re-use in Brut.
22
22
  #
23
- # When subclassing this to create a component, your initializer's signature will determine what data
24
- # is required for your component to work. It can be anything, just keep in mind that any page or component
25
- # that uses your component must be able to provide those values.
23
+ # To create a component, subclass this class (or, more likely, your app's `AppComponent`) and
24
+ # provide an initializer that accepts keyword arguments. The names of these arguments will be used to locate the
25
+ # values that Brut will pass in when creating your component object.
26
26
  #
27
- # If your component does not override {#render} (which, generally, it won't), an ERB file is expected to exist alongside it in the
28
- # app. For example, if you have a component named `Auth::LoginButtonComponent`, it would expected to be in
29
- # `app/src/front_end/components/auth/login_button_component.rb`. Thus, Brut will also expect
30
- # `app/src/front_end/components/auth/login_button_component.html.erb` to exist as well. That ERB file is used with an instance of your
31
- # component's class to render the component's HTML.
27
+ # Consult Brut's documentation on keyword injection to know what values you may use and how values are located.
32
28
  #
33
- # @see Brut::FrontEnd::Component::Helpers
29
+ # Becuase this is a Phlex component, you must implement `view_template` and make calls to Phlex's API to create
30
+ # the markup for your component.
34
31
  class Brut::FrontEnd::Component < Phlex::HTML
35
32
 
36
33
  include Brut::Framework::Errors
@@ -53,71 +50,72 @@ class Brut::FrontEnd::Component < Phlex::HTML
53
50
  register_element :brut_tabs
54
51
  register_element :brut_tracing
55
52
 
56
- def inline_svg(svg)
57
- Brut.container.svg_locator.locate(svg).then { |svg_file|
58
- File.read(svg_file)
59
- }.then { |svg_content|
60
- raw(safe(svg_content))
61
- }
62
- end
53
+ # Module for the various "free methods" available to all components.
54
+ # Generally, we don't want to build up mega-classes with lots of modules, but
55
+ # this provides a nice, singular namespace to document the helpers as apart
56
+ # from the various methods of the component.
57
+ module Helpers
58
+ # Render an inline an SVG that is part of your app. **Note** this does not
59
+ # return the SVG's contents, but it renders it into the current Phlex
60
+ # context.
61
+ #
62
+ # @param [String] svg path to the SVG file, relative to where SVGs are
63
+ # stored, which is `app/src/front_end/svgs` or where `Brut.container.svg_locator` is
64
+ # looking
65
+ #
66
+ # @see Brut::FrontEnd::InlineSvgLocator
67
+ def inline_svg(svg)
68
+ Brut.container.svg_locator.locate(svg).then { |svg_file|
69
+ File.read(svg_file)
70
+ }.then { |svg_content|
71
+ raw(safe(svg_content))
72
+ }
73
+ end
63
74
 
64
- def time_tag(timestamp:nil,**component_options, &contents)
65
- args = component_options.merge(timestamp:)
66
- render Brut::FrontEnd::Components::TimeTag.new(**args,&contents)
75
+ # Return a component that you would like Brut to instantiate.
76
+ # This will use keyword injection to create the component, which means that if the component
77
+ # doesn't require any data from this component, you do not need to pass through those values.
78
+ # For example, you may have a component that renders the flash message. To avoid requiring your component to
79
+ # be passed the flash, a global component can be injected with it from Brut.
80
+ #
81
+ # @return [Object] instance of `component_klass`, as created by Brut. This will
82
+ # not render the component.
83
+ def global_component(component_klass)
84
+ Brut::FrontEnd::RequestContext.inject(component_klass)
85
+ end
67
86
  end
87
+ include Helpers
68
88
 
69
- def form_tag(**args, &block)
70
- render Brut::FrontEnd::Components::FormTag.new(**args,&block)
71
- end
89
+ # The name of this component, used for debugging and other purposes. Do not
90
+ # override this.
91
+ def self.component_name = self.name
92
+ # Convenience method to get the component name. This just calls the class
93
+ # method {.component_name}.
94
+ def component_name = self.class.component_name
72
95
 
73
- def global_component(component_klass)
74
- render Brut::FrontEnd::RequestContext.inject(component_klass)
75
- end
96
+ # True if this component is page private.
97
+ # @!visibility private
98
+ def page_private? = !!self.containing_page_class
76
99
 
77
- def constraint_violations(form:, input_name:, index: nil, message_html_attributes: {}, **html_attributes)
78
- render(
79
- Brut::FrontEnd::Components::ConstraintViolations.new(
80
- form:,
81
- input_name:,
82
- index:,
83
- message_html_attributes:,
84
- **html_attributes
85
- )
86
- )
87
- end
100
+ # Returns the {Brut::FrontEnd::Page.page_name} of the page containing this component,
101
+ # if it is {#page_private?}. Do not call if it's not.
102
+ # @!visibility private
103
+ def containing_page_name = self.containing_page_class.page_name
88
104
 
89
- # Create an HTML input tag for the given input of a form. This is a convieniece method
90
- # that calls {Brut::FrontEnd::Components::Inputs::TextField.for_form_input}.
91
- def input_tag(form:, input_name:, index: nil, **html_attributes)
92
- render(
93
- Brut::FrontEnd::Components::Inputs::TextField.for_form_input(
94
- form:,
95
- input_name:,
96
- index:,
97
- html_attributes:)
98
- )
99
- end
100
-
101
- def self.component_name = self.name
102
- def component_name = self.class.component_name
105
+ private
103
106
 
104
- def page_name
105
- @page_name ||= begin
106
- page = self.class.name.split(/::/).reduce(Module) { |accumulator,class_path_part|
107
- if accumulator.ancestors.include?(Brut::FrontEnd::Page)
108
- accumulator
109
- else
110
- accumulator.const_get(class_path_part)
111
- end
112
- }
113
- if page.ancestors.include?(Brut::FrontEnd::Page)
114
- page.name
115
- elsif page.respond_to?(:page_name)
116
- page.page_name
117
- else
118
- raise "#{self.class} is not nested inside a page, so #page_name should not have been called"
119
- end
120
- end
107
+ def containing_page_class
108
+ page_class = self.class.name.split(/::/).reduce(Module) { |accumulator,class_path_part|
109
+ if accumulator.ancestors.include?(Brut::FrontEnd::Page)
110
+ accumulator
111
+ else
112
+ accumulator.const_get(class_path_part)
113
+ end
114
+ }
115
+ if page_class.ancestors.include?(Brut::FrontEnd::Page)
116
+ page_class
117
+ else
118
+ nil
119
+ end
121
120
  end
122
-
123
121
  end
@@ -14,10 +14,7 @@
14
14
  # </brut-cv-messages>
15
15
  # ```
16
16
  #
17
- # Note that if you are using `<brut-form>` then `<brut-cv>` elements will be inserted into the `<brut-cv-messages>` element, however
18
- # they will not have the `server-side` attribute.
19
- #
20
- # You will most commonly use this component via {Brut::FrontEnd::Component::Helpers#constraint_violations}.
17
+ # Note that if you are using `<brut-form>` then `<brut-cv>` elements will be inserted into the `<brut-cv-messages>` element, however they will not have the `server-side` attribute.
21
18
  class Brut::FrontEnd::Components::ConstraintViolations < Brut::FrontEnd::Component
22
19
  # Create a new ConstraintViolations component
23
20
  #
@@ -1,4 +1,4 @@
1
- # Represents a `<form>` HTML element that includes a CSRF token as needed. You likely want to use this class via the {Brut::FrontEnd::Component::Helpers#form_tag} method.
1
+ # Represents a `<form>` HTML element that includes a CSRF token as needed.
2
2
  class Brut::FrontEnd::Components::FormTag < Brut::FrontEnd::Component
3
3
  # Creates the form surrounding the contents of the block yielded to it. If the form's action is a POST, it will include a CSRF token.
4
4
  # If the form's action is GET, it will not.
@@ -2,10 +2,10 @@ module Brut::FrontEnd::Components
2
2
 
3
3
  # Holds components designed to render HTML `<input>` and other form components.
4
4
  module Inputs
5
- autoload(:TextField,"brut/front_end/components/inputs/text_field")
5
+ autoload(:InputTag,"brut/front_end/components/inputs/input_tag")
6
6
  autoload(:RadioButton,"brut/front_end/components/inputs/radio_button")
7
- autoload(:Select,"brut/front_end/components/inputs/select")
8
- autoload(:Textarea,"brut/front_end/components/inputs/textarea")
7
+ autoload(:SelectTagWithOptions,"brut/front_end/components/inputs/select_tag_with_options")
8
+ autoload(:TextareaTag,"brut/front_end/components/inputs/textarea_tag")
9
9
  autoload(:CsrfToken,"brut/front_end/components/inputs/csrf_token")
10
10
  end
11
11
 
@@ -1,5 +1,5 @@
1
1
  # Renders a hidden field for a form that contains the current CSRF token. You only need
2
- # to use this directly if you are building a form without {Brut::FrontEnd::Component::Helpers#form_tag}.
2
+ # to use this directly if you are building a form without {Brut::FrontEnd::Components::FormTag}
3
3
  class Brut::FrontEnd::Components::Inputs::CsrfToken < Brut::FrontEnd::Components::Input
4
4
  def initialize(csrf_token:)
5
5
  @csrf_token = csrf_token
@@ -1,5 +1,5 @@
1
1
  # Generates an HTML `<input>` field.
2
- class Brut::FrontEnd::Components::Inputs::TextField < Brut::FrontEnd::Components::Input
2
+ class Brut::FrontEnd::Components::Inputs::InputTag < Brut::FrontEnd::Components::Input
3
3
  # Creates the appropriate input for the given {Brut::FrontEnd::Form} and input name.
4
4
  # Generally, you want to use this method over the initializer.
5
5
  #
@@ -7,10 +7,10 @@ class Brut::FrontEnd::Components::Inputs::TextField < Brut::FrontEnd::Components
7
7
  # @param [String] input_name the name of the input, which should be a member of `form`
8
8
  # @param [Integer] index if this input is part of an array, this is the index into that array. This is used to get the input's value.
9
9
  # @param [Hash] html_attributes any additional HTML attributes to include on the `<input>` element.
10
- def self.for_form_input(form:, input_name:, index: nil, html_attributes: {})
10
+ def self.for_form_input(form:, input_name:, index: nil, **html_attributes)
11
+ input = form.input(input_name, index:)
11
12
  default_html_attributes = {}
12
13
  html_attributes = html_attributes.map { |key,value| [ key.to_sym, value ] }.to_h
13
- input = form.input(input_name, index:)
14
14
 
15
15
  default_html_attributes[:required] = input.required
16
16
  default_html_attributes[:pattern] = input.pattern
@@ -41,17 +41,15 @@ class Brut::FrontEnd::Components::Inputs::TextField < Brut::FrontEnd::Components
41
41
  default_html_attributes[:value] = (index || true).to_s
42
42
  default_html_attributes[:checked] = value == "true"
43
43
  else
44
- default_html_attributes[:value] = value.to_s
44
+ default_html_attributes[:value] = value.nil? ? nil : value.to_s
45
45
  end
46
46
  if !form.new? && !input.valid?
47
47
  default_html_attributes["data-invalid"] = true
48
- input.validity_state.each do |constraint,violated|
49
- if violated
50
- default_html_attributes["data-#{constraint}"] = true
51
- end
48
+ input.validity_state.each do |constraint|
49
+ default_html_attributes["data-#{constraint}"] = true
52
50
  end
53
51
  end
54
- Brut::FrontEnd::Components::Inputs::TextField.new(default_html_attributes.merge(html_attributes))
52
+ Brut::FrontEnd::Components::Inputs::InputTag.new(default_html_attributes.merge(html_attributes))
55
53
  end
56
54
 
57
55
  def invalid? = @attributes["data-invalid"] == true
@@ -3,7 +3,7 @@
3
3
  # internal to the {Brut::FrontEnd::Form} treat the radio button group as a single input with
4
4
  # a single name and value. When it comes time to generate HTML, this class is used
5
5
  # to generate a single radio button from a group.
6
- class Brut::FrontEnd::Components::Inputs::RadioButton < Brut::FrontEnd::Components::Inputs::TextField
6
+ class Brut::FrontEnd::Components::Inputs::RadioButton < Brut::FrontEnd::Components::Inputs::InputTag
7
7
  # Creates a radio button that is part of a radio button group. You should call this
8
8
  # method once for each radio button in the group.
9
9
  #