brut 0.17.0 → 0.18.1

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 (1103) hide show
  1. checksums.yaml +4 -4
  2. data/exe/brut +34 -0
  3. data/lib/brut/cli/apps/build_assets.rb +78 -48
  4. data/lib/brut/cli/apps/db.rb +168 -202
  5. data/lib/brut/cli/apps/deploy.rb +291 -0
  6. data/lib/brut/cli/apps/heroku_container_based_deploy.rb +6 -0
  7. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/add_segment.rb +5 -5
  8. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/add_segment_options.rb +1 -1
  9. data/lib/brut/cli/apps/new/app.rb +240 -0
  10. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/app_id.rb +1 -1
  11. data/lib/brut/cli/apps/new/app_name.rb +29 -0
  12. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/base.rb +9 -6
  13. data/lib/brut/cli/apps/new/erb_binding_delegate.rb +23 -0
  14. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/internet_identifier.rb +5 -5
  15. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/invalid_identifier.rb +1 -1
  16. data/{mkbrut/lib/mkbrut/app.rb → lib/brut/cli/apps/new/old_app.rb} +8 -11
  17. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/add_css_import.rb +1 -1
  18. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/add_i18n_message.rb +1 -1
  19. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/add_method.rb +1 -1
  20. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/append_to_file.rb +1 -1
  21. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/base_op.rb +3 -3
  22. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/copy_file.rb +1 -1
  23. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/insert_code_in_method.rb +1 -1
  24. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/insert_into_file.rb +1 -1
  25. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/insert_route.rb +1 -1
  26. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/mkdir.rb +1 -1
  27. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/prism_parsing_op.rb +1 -1
  28. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/render_template.rb +1 -1
  29. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/ops/skip_file.rb +1 -1
  30. data/lib/brut/cli/apps/new/ops.rb +17 -0
  31. data/lib/brut/cli/apps/new/organization.rb +5 -0
  32. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/prefix.rb +1 -1
  33. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/segments/bare_bones.rb +12 -11
  34. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/segments/demo.rb +16 -15
  35. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/segments/heroku.rb +9 -5
  36. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/segments/sidekiq.rb +44 -21
  37. data/lib/brut/cli/apps/new/segments.rb +8 -0
  38. data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/versions.rb +3 -3
  39. data/lib/brut/cli/apps/new.rb +26 -0
  40. data/lib/brut/cli/apps/scaffold.rb +150 -141
  41. data/lib/brut/cli/apps/test.rb +92 -68
  42. data/lib/brut/cli/commands/base_command.rb +174 -0
  43. data/lib/brut/cli/commands/compound_command.rb +29 -0
  44. data/lib/brut/cli/commands/execution_context.rb +32 -0
  45. data/lib/brut/cli/commands/help.rb +26 -0
  46. data/lib/brut/cli/commands/output_error.rb +13 -0
  47. data/lib/brut/cli/commands/raise_error.rb +11 -0
  48. data/lib/brut/cli/commands.rb +8 -0
  49. data/lib/brut/cli/execute_result.rb +39 -0
  50. data/lib/brut/cli/executor.rb +9 -4
  51. data/lib/brut/cli/output.rb +13 -0
  52. data/lib/brut/cli/parsed_command_line.rb +143 -0
  53. data/lib/brut/cli/runner.rb +124 -0
  54. data/lib/brut/cli.rb +7 -29
  55. data/lib/brut/framework/container.rb +1 -1
  56. data/lib/brut/framework/mcp.rb +59 -13
  57. data/lib/brut/framework/project_environment.rb +3 -1
  58. data/lib/brut/junk_drawer.rb +3 -1
  59. data/lib/brut/spec_support/cli_command_support.rb +45 -0
  60. data/lib/brut/spec_support/e2e_test_server.rb +3 -0
  61. data/lib/brut/spec_support/general_support.rb +1 -1
  62. data/lib/brut/spec_support/matchers/have_executed.rb +35 -0
  63. data/lib/brut/spec_support/rspec_setup.rb +4 -8
  64. data/lib/brut/spec_support.rb +1 -0
  65. data/lib/brut/tui/markup_string.rb +2 -0
  66. data/lib/brut/tui/script/events/command_std_out.rb +3 -2
  67. data/lib/brut/tui/script/exec_step.rb +11 -4
  68. data/lib/brut/tui/script/puts_subscriber.rb +4 -4
  69. data/lib/brut/tui/script.rb +7 -3
  70. data/lib/brut/tui/terminal_theme.rb +15 -11
  71. data/lib/brut/version.rb +1 -1
  72. data/templates/Base/.env.development.local +2 -0
  73. data/templates/Base/bin/ci +42 -0
  74. data/{mkbrut/templates → templates}/Base/bin/release +2 -2
  75. data/templates/Base/bin/setup +174 -0
  76. data/{mkbrut/templates → templates}/Base/bin/watch-and-build-assets +1 -1
  77. data/{mkbrut/templates → templates}/Base/dx/docker-compose.env.erb +1 -1
  78. data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/css/fonts.css +1 -1
  79. data/{mkbrut/templates → templates}/segments/Heroku/deploy/Dockerfile +2 -2
  80. data/templates/segments/Heroku/deploy/docker_config.rb +30 -0
  81. metadata +190 -1055
  82. data/.gitignore +0 -61
  83. data/.projections.json +0 -10
  84. data/CHANGELOG.md +0 -172
  85. data/CODE_OF_CONDUCT.txt +0 -99
  86. data/Dockerfile.dx +0 -82
  87. data/Gemfile +0 -6
  88. data/Gemfile.lock +0 -246
  89. data/LICENSE.txt +0 -370
  90. data/README.md +0 -90
  91. data/Rakefile +0 -25
  92. data/assets/Logo-Square.pxd +0 -0
  93. data/assets/LogoPylon.pxd +0 -0
  94. data/assets/LogoStop.pxd +0 -0
  95. data/assets/LogoTall.pxd +0 -0
  96. data/assets/MetroIcon.graffle +0 -0
  97. data/assets/MetroLogo.graffle +0 -0
  98. data/assets/SocialImage.png +0 -0
  99. data/assets/SocialImage.pxd +0 -0
  100. data/assets/YouTubeThumb.pxd +0 -0
  101. data/bin/bin_kit.rb +0 -51
  102. data/bin/build +0 -86
  103. data/bin/ci +0 -40
  104. data/bin/dev +0 -20
  105. data/bin/docs +0 -86
  106. data/bin/generate-and-run-rubocop +0 -52
  107. data/bin/new-version +0 -8
  108. data/bin/publish +0 -61
  109. data/bin/rake +0 -27
  110. data/bin/rspec +0 -27
  111. data/bin/rubocop +0 -27
  112. data/bin/setup +0 -252
  113. data/bin/test +0 -18
  114. data/brut-css/.nvim.lua +0 -1
  115. data/brut-css/README.md +0 -28
  116. data/brut-css/bin/build +0 -50
  117. data/brut-css/bin/ci +0 -19
  118. data/brut-css/bin/dev +0 -1
  119. data/brut-css/bin/docs +0 -34
  120. data/brut-css/bin/publish +0 -21
  121. data/brut-css/bin/setup +0 -6
  122. data/brut-css/config/media-queries-all.css +0 -15
  123. data/brut-css/config/media-queries-minimal.css +0 -5
  124. data/brut-css/config/postcss.config.cjs +0 -7
  125. data/brut-css/config/pseudo-classes-all.css +0 -9
  126. data/brut-css/dx +0 -1
  127. data/brut-css/package-lock.json +0 -3165
  128. data/brut-css/package.json +0 -36
  129. data/brut-css/src/css/appearance.css +0 -145
  130. data/brut-css/src/css/border.css +0 -522
  131. data/brut-css/src/css/colors.css +0 -3502
  132. data/brut-css/src/css/dimensions.css +0 -548
  133. data/brut-css/src/css/flex.css +0 -179
  134. data/brut-css/src/css/index.css +0 -13
  135. data/brut-css/src/css/layout.css +0 -120
  136. data/brut-css/src/css/list.css +0 -41
  137. data/brut-css/src/css/positioning.css +0 -354
  138. data/brut-css/src/css/properties/colors.css +0 -455
  139. data/brut-css/src/css/properties/index.css +0 -3
  140. data/brut-css/src/css/properties/spacing.css +0 -140
  141. data/brut-css/src/css/properties/typography.css +0 -224
  142. data/brut-css/src/css/reset.css +0 -107
  143. data/brut-css/src/css/spacing.css +0 -585
  144. data/brut-css/src/css/typography.css +0 -519
  145. data/brut-css/src/css/utils.css +0 -104
  146. data/brut-css/src/docs/1_getting-started/1_overview.md +0 -46
  147. data/brut-css/src/docs/1_getting-started/2_installation.md +0 -25
  148. data/brut-css/src/docs/1_getting-started/3_core-concepts.md +0 -75
  149. data/brut-css/src/docs/1_getting-started/4_simple-example.md +0 -132
  150. data/brut-css/src/docs/1_getting-started/page.html.ejs +0 -10
  151. data/brut-css/src/docs/2_properties/page.html.ejs +0 -71
  152. data/brut-css/src/docs/3_classes/color-demo.html.ejs +0 -31
  153. data/brut-css/src/docs/3_classes/page.html.ejs +0 -87
  154. data/brut-css/src/docs/4_customization/1_design-system.md +0 -36
  155. data/brut-css/src/docs/4_customization/2_breakpoints.md +0 -75
  156. data/brut-css/src/docs/4_customization/3_pseudo-classes.md +0 -74
  157. data/brut-css/src/docs/4_customization/4_advanced-configuration.md +0 -40
  158. data/brut-css/src/docs/4_customization/page.html.ejs +0 -10
  159. data/brut-css/src/docs/docs.css +0 -98
  160. data/brut-css/src/docs/includes/body-and-header.html.ejs +0 -30
  161. data/brut-css/src/docs/includes/footer-and-rest.html.ejs +0 -9
  162. data/brut-css/src/docs/includes/head.html.ejs +0 -5
  163. data/brut-css/src/docs/includes/nav.html.ejs +0 -10
  164. data/brut-css/src/docs/index.html.ejs +0 -32
  165. data/brut-css/src/docs/prism-twilight.min.css +0 -1
  166. data/brut-css/src/js/Logger.js +0 -71
  167. data/brut-css/src/js/build.js +0 -111
  168. data/brut-css/src/js/cli/CLIArgError.js +0 -7
  169. data/brut-css/src/js/cli/Debug.js +0 -27
  170. data/brut-css/src/js/cli/DocsDir.js +0 -16
  171. data/brut-css/src/js/cli/DocsTemplateSourceDir.js +0 -16
  172. data/brut-css/src/js/cli/InputFile.js +0 -31
  173. data/brut-css/src/js/cli/MediaQueryConfigFile.js +0 -10
  174. data/brut-css/src/js/cli/OutputFile.js +0 -22
  175. data/brut-css/src/js/cli/ParsedArg.js +0 -17
  176. data/brut-css/src/js/cli/PathToBrutCSSRoot.js +0 -19
  177. data/brut-css/src/js/cli/PseudoClassConfigFile.js +0 -11
  178. data/brut-css/src/js/cli.js +0 -108
  179. data/brut-css/src/js/docGenerator.js +0 -467
  180. data/brut-css/src/js/mediaQueryConfigParser.js +0 -98
  181. data/brut-css/src/js/post-css-plugins/addMediaQueriesPlugin.js +0 -49
  182. data/brut-css/src/js/post-css-plugins/addPseudoClassesPlugin.js +0 -42
  183. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Category.js +0 -9
  184. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/DocState.js +0 -185
  185. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Documentable.js +0 -8
  186. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Group.js +0 -7
  187. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/ParsedComment.js +0 -73
  188. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Property.js +0 -9
  189. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyCategory.js +0 -4
  190. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/PropertyGroup.js +0 -8
  191. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/Rule.js +0 -12
  192. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleCategory.js +0 -4
  193. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/RuleGroup.js +0 -8
  194. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeRef.js +0 -5
  195. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin/SeeURL.js +0 -9
  196. data/brut-css/src/js/post-css-plugins/generateDocumentationPlugin.js +0 -49
  197. data/brut-css/src/js/post-css-plugins/generateRootCustomPropertiesPlugin.js +0 -45
  198. data/brut-css/src/js/pseudoClassConfigParser.js +0 -145
  199. data/brut-js/.projections.json +0 -10
  200. data/brut-js/README.md +0 -118
  201. data/brut-js/bin/build +0 -19
  202. data/brut-js/bin/ci +0 -5
  203. data/brut-js/bin/docs +0 -25
  204. data/brut-js/bin/publish +0 -21
  205. data/brut-js/bin/setup +0 -6
  206. data/brut-js/docs/README.md +0 -8
  207. data/brut-js/docs/jsdoc-plugins/customElementTag.js +0 -8
  208. data/brut-js/docs/jsdoc-theme/publish.js +0 -692
  209. data/brut-js/docs/jsdoc-theme/static/scripts/linenumber.js +0 -25
  210. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/Apache-License-2.0.txt +0 -202
  211. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/lang-css.js +0 -2
  212. data/brut-js/docs/jsdoc-theme/static/scripts/prettify/prettify.js +0 -28
  213. data/brut-js/docs/jsdoc-theme/static/styles/jsdoc-default.css +0 -327
  214. data/brut-js/docs/jsdoc-theme/static/styles/prettify-jsdoc.css +0 -111
  215. data/brut-js/docs/jsdoc-theme/static/styles/prettify-tomorrow.css +0 -132
  216. data/brut-js/docs/jsdoc-theme/tmpl/augments.tmpl +0 -10
  217. data/brut-js/docs/jsdoc-theme/tmpl/container.tmpl +0 -199
  218. data/brut-js/docs/jsdoc-theme/tmpl/details.tmpl +0 -143
  219. data/brut-js/docs/jsdoc-theme/tmpl/example.tmpl +0 -2
  220. data/brut-js/docs/jsdoc-theme/tmpl/examples.tmpl +0 -13
  221. data/brut-js/docs/jsdoc-theme/tmpl/exceptions.tmpl +0 -32
  222. data/brut-js/docs/jsdoc-theme/tmpl/layout.tmpl +0 -38
  223. data/brut-js/docs/jsdoc-theme/tmpl/mainpage.tmpl +0 -14
  224. data/brut-js/docs/jsdoc-theme/tmpl/members.tmpl +0 -38
  225. data/brut-js/docs/jsdoc-theme/tmpl/method.tmpl +0 -131
  226. data/brut-js/docs/jsdoc-theme/tmpl/modifies.tmpl +0 -14
  227. data/brut-js/docs/jsdoc-theme/tmpl/params.tmpl +0 -131
  228. data/brut-js/docs/jsdoc-theme/tmpl/properties.tmpl +0 -108
  229. data/brut-js/docs/jsdoc-theme/tmpl/returns.tmpl +0 -19
  230. data/brut-js/docs/jsdoc-theme/tmpl/source.tmpl +0 -8
  231. data/brut-js/docs/jsdoc-theme/tmpl/tutorial.tmpl +0 -19
  232. data/brut-js/docs/jsdoc-theme/tmpl/type.tmpl +0 -7
  233. data/brut-js/docs/jsdoc.config.json +0 -23
  234. data/brut-js/docs/package-lock.json +0 -343
  235. data/brut-js/docs/package.json +0 -7
  236. data/brut-js/dx +0 -1
  237. data/brut-js/package-lock.json +0 -2210
  238. data/brut-js/package.json +0 -36
  239. data/brut-js/specs/AjaxSubmit.spec.js +0 -453
  240. data/brut-js/specs/Autosubmit.spec.js +0 -127
  241. data/brut-js/specs/ConfirmSubmit.spec.js +0 -224
  242. data/brut-js/specs/ConstraintViolationMessage.spec.js +0 -33
  243. data/brut-js/specs/ConstraintViolationMessages.spec.js +0 -32
  244. data/brut-js/specs/CopyToClipboard.spec.js +0 -35
  245. data/brut-js/specs/Form.spec.js +0 -137
  246. data/brut-js/specs/I18nTranslation.spec.js +0 -19
  247. data/brut-js/specs/LocaleDetection.spec.js +0 -22
  248. data/brut-js/specs/Message.spec.js +0 -15
  249. data/brut-js/specs/SpecHelper.js +0 -23
  250. data/brut-js/specs/Tabs.spec.js +0 -41
  251. data/brut-js/specs/Toast.spec.js +0 -34
  252. data/brut-js/specs/config/asset_metadata.json +0 -7
  253. data/brut-js/src/AjaxSubmit.js +0 -499
  254. data/brut-js/src/Autosubmit.js +0 -63
  255. data/brut-js/src/BaseCustomElement.js +0 -261
  256. data/brut-js/src/ConfirmSubmit.js +0 -137
  257. data/brut-js/src/ConfirmationDialog.js +0 -143
  258. data/brut-js/src/ConstraintViolationMessage.js +0 -140
  259. data/brut-js/src/ConstraintViolationMessages.js +0 -98
  260. data/brut-js/src/CopyToClipboard.js +0 -96
  261. data/brut-js/src/Form.js +0 -147
  262. data/brut-js/src/I18nTranslation.js +0 -64
  263. data/brut-js/src/LocaleDetection.js +0 -117
  264. data/brut-js/src/Logger.js +0 -90
  265. data/brut-js/src/Message.js +0 -62
  266. data/brut-js/src/RichString.js +0 -116
  267. data/brut-js/src/Tabs.js +0 -168
  268. data/brut-js/src/Toast.js +0 -102
  269. data/brut-js/src/Tracing.js +0 -247
  270. data/brut-js/src/appForTestingOnly.js +0 -15
  271. data/brut-js/src/index.js +0 -133
  272. data/brut-js/src/testing/AssetMetadata.js +0 -35
  273. data/brut-js/src/testing/AssetMetadataLoader.js +0 -25
  274. data/brut-js/src/testing/CustomElementTest.js +0 -235
  275. data/brut-js/src/testing/DOMCreator.js +0 -45
  276. data/brut-js/src/testing/index.js +0 -48
  277. data/brut.gemspec +0 -73
  278. data/brutrb.com/.vitepress/config.mjs +0 -164
  279. data/brutrb.com/.vitepress/plugins/jsdocLinker.js +0 -34
  280. data/brutrb.com/.vitepress/plugins/rdocLinker.js +0 -18
  281. data/brutrb.com/.vitepress/theme/custom.css +0 -14
  282. data/brutrb.com/.vitepress/theme/index.js +0 -18
  283. data/brutrb.com/.vitepress/theme/style.css +0 -139
  284. data/brutrb.com/adrs.md +0 -16
  285. data/brutrb.com/ai.md +0 -68
  286. data/brutrb.com/assets.md +0 -131
  287. data/brutrb.com/bin/build +0 -5
  288. data/brutrb.com/bin/deploy +0 -7
  289. data/brutrb.com/bin/dev +0 -5
  290. data/brutrb.com/bin/setup +0 -6
  291. data/brutrb.com/brut-js.md +0 -128
  292. data/brutrb.com/business-logic.md +0 -55
  293. data/brutrb.com/cli.md +0 -274
  294. data/brutrb.com/components.md +0 -265
  295. data/brutrb.com/configuration.md +0 -256
  296. data/brutrb.com/css.md +0 -103
  297. data/brutrb.com/custom-element-tests.md +0 -148
  298. data/brutrb.com/database-access.md +0 -201
  299. data/brutrb.com/database-schema.md +0 -320
  300. data/brutrb.com/deployment.md +0 -158
  301. data/brutrb.com/dev-environment.md +0 -186
  302. data/brutrb.com/dir-structure.md +0 -120
  303. data/brutrb.com/doc-conventions.md +0 -41
  304. data/brutrb.com/dx +0 -1
  305. data/brutrb.com/end-to-end-tests.md +0 -176
  306. data/brutrb.com/features.md +0 -373
  307. data/brutrb.com/flash-and-session.md +0 -208
  308. data/brutrb.com/form-constraints.md +0 -266
  309. data/brutrb.com/forms.md +0 -238
  310. data/brutrb.com/getting-started.md +0 -142
  311. data/brutrb.com/handlers.md +0 -177
  312. data/brutrb.com/hooks.md +0 -176
  313. data/brutrb.com/i18n.md +0 -190
  314. data/brutrb.com/images/DevEnvironment.graffle +0 -0
  315. data/brutrb.com/images/DevEnvironment.png +0 -0
  316. data/brutrb.com/images/LogoSquare.png +0 -0
  317. data/brutrb.com/images/LogoStop.png +0 -0
  318. data/brutrb.com/images/LogoTall.png +0 -0
  319. data/brutrb.com/images/Makefile +0 -10
  320. data/brutrb.com/images/OverviewMetro.graffle +0 -0
  321. data/brutrb.com/images/OverviewMetro.png +0 -0
  322. data/brutrb.com/images/dev-env-overview.dot +0 -54
  323. data/brutrb.com/images/dev-env-overview.png +0 -0
  324. data/brutrb.com/images/dev-env-protocol.dot +0 -37
  325. data/brutrb.com/images/dev-env-protocol.png +0 -0
  326. data/brutrb.com/images/overview.graffle +0 -0
  327. data/brutrb.com/images/overview.png +0 -0
  328. data/brutrb.com/images/spa.dot +0 -19
  329. data/brutrb.com/images/spa.png +0 -0
  330. data/brutrb.com/images/tutorial/02-confirmation-dialog-browser-element-styled.png +0 -0
  331. data/brutrb.com/images/tutorial/02-confirmation-dialog-browser-element.png +0 -0
  332. data/brutrb.com/images/tutorial/02-confirmation-dialog-browser.png +0 -0
  333. data/brutrb.com/images/tutorial/02-confirmation-flow.graffle +0 -0
  334. data/brutrb.com/images/tutorial/02-confirmation-flow.png +0 -0
  335. data/brutrb.com/images/tutorial/basic-form-with-violations.png +0 -0
  336. data/brutrb.com/images/tutorial/basic-form.png +0 -0
  337. data/brutrb.com/images/tutorial/initial-home-page.png +0 -0
  338. data/brutrb.com/images/tutorial/new-post-editor.png +0 -0
  339. data/brutrb.com/images/tutorial/new-post-home-page.png +0 -0
  340. data/brutrb.com/images/tutorial/styled-form-with-server-side-violations.png +0 -0
  341. data/brutrb.com/images/tutorial/styled-form-with-violations.png +0 -0
  342. data/brutrb.com/images/tutorial/styled-home-page-with-posts.png +0 -0
  343. data/brutrb.com/images/tutorial/styled-home-page.png +0 -0
  344. data/brutrb.com/images/tutorial/welcome-to-brut.png +0 -0
  345. data/brutrb.com/images/workspace-protocol.dot +0 -44
  346. data/brutrb.com/images/workspace-protocol.png +0 -0
  347. data/brutrb.com/index.md +0 -34
  348. data/brutrb.com/instrumentation.md +0 -331
  349. data/brutrb.com/javascript.md +0 -122
  350. data/brutrb.com/jobs.md +0 -114
  351. data/brutrb.com/keyword-injection.md +0 -195
  352. data/brutrb.com/layouts.md +0 -156
  353. data/brutrb.com/lsp.md +0 -23
  354. data/brutrb.com/markdown-examples.md +0 -85
  355. data/brutrb.com/middleware.md +0 -80
  356. data/brutrb.com/overview.md +0 -68
  357. data/brutrb.com/package-lock.json +0 -2451
  358. data/brutrb.com/package.json +0 -11
  359. data/brutrb.com/pages.md +0 -290
  360. data/brutrb.com/public/SocialImage.png +0 -0
  361. data/brutrb.com/public/favicon.ico +0 -0
  362. data/brutrb.com/recipes/alternate-layouts.md +0 -32
  363. data/brutrb.com/recipes/authentication.md +0 -336
  364. data/brutrb.com/recipes/custom-flash.md +0 -51
  365. data/brutrb.com/recipes/dev-env-secrets.md +0 -87
  366. data/brutrb.com/recipes/form-errors.md +0 -148
  367. data/brutrb.com/recipes/indexed-forms.md +0 -149
  368. data/brutrb.com/recipes/migrations.md +0 -210
  369. data/brutrb.com/recipes/text-field-component.md +0 -182
  370. data/brutrb.com/roadmap.md +0 -52
  371. data/brutrb.com/routes.md +0 -189
  372. data/brutrb.com/security.md +0 -102
  373. data/brutrb.com/seed-data.md +0 -63
  374. data/brutrb.com/space-time-continuum.md +0 -81
  375. data/brutrb.com/tutorial.md +0 -138
  376. data/brutrb.com/tutorials/01-intro.md +0 -1654
  377. data/brutrb.com/tutorials/02-dialog.md +0 -569
  378. data/brutrb.com/unit-tests.md +0 -148
  379. data/brutrb.com/why.md +0 -19
  380. data/docker-compose.dx.yml +0 -25
  381. data/docs/404.html +0 -26
  382. data/docs/CNAME +0 -1
  383. data/docs/SocialImage.png +0 -0
  384. data/docs/adrs.html +0 -29
  385. data/docs/ai.html +0 -29
  386. data/docs/api/Brut/BackEnd/SeedData.html +0 -493
  387. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +0 -214
  388. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +0 -125
  389. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +0 -125
  390. data/docs/api/Brut/BackEnd/Sidekiq.html +0 -125
  391. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +0 -414
  392. data/docs/api/Brut/BackEnd/Validators.html +0 -128
  393. data/docs/api/Brut/BackEnd.html +0 -132
  394. data/docs/api/Brut/CLI/App.html +0 -1601
  395. data/docs/api/Brut/CLI/AppRunner.html +0 -491
  396. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +0 -264
  397. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +0 -306
  398. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +0 -262
  399. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +0 -314
  400. data/docs/api/Brut/CLI/Apps/BuildAssets.html +0 -183
  401. data/docs/api/Brut/CLI/Apps/DB/Create.html +0 -365
  402. data/docs/api/Brut/CLI/Apps/DB/Drop.html +0 -357
  403. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +0 -389
  404. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +0 -339
  405. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +0 -329
  406. data/docs/api/Brut/CLI/Apps/DB/Seed.html +0 -347
  407. data/docs/api/Brut/CLI/Apps/DB/Status.html +0 -383
  408. data/docs/api/Brut/CLI/Apps/DB.html +0 -183
  409. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +0 -270
  410. data/docs/api/Brut/CLI/Apps/DeployBase.html +0 -257
  411. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +0 -587
  412. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +0 -196
  413. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +0 -303
  414. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +0 -508
  415. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +0 -398
  416. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +0 -374
  417. data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +0 -384
  418. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +0 -410
  419. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +0 -262
  420. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +0 -303
  421. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +0 -480
  422. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +0 -450
  423. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +0 -380
  424. data/docs/api/Brut/CLI/Apps/Scaffold.html +0 -253
  425. data/docs/api/Brut/CLI/Apps/Test/Audit.html +0 -474
  426. data/docs/api/Brut/CLI/Apps/Test/E2e.html +0 -407
  427. data/docs/api/Brut/CLI/Apps/Test/JS.html +0 -262
  428. data/docs/api/Brut/CLI/Apps/Test/Run.html +0 -578
  429. data/docs/api/Brut/CLI/Apps/Test.html +0 -253
  430. data/docs/api/Brut/CLI/Apps.html +0 -125
  431. data/docs/api/Brut/CLI/Command.html +0 -2425
  432. data/docs/api/Brut/CLI/Error.html +0 -139
  433. data/docs/api/Brut/CLI/ExecutionResults/Result.html +0 -664
  434. data/docs/api/Brut/CLI/ExecutionResults.html +0 -675
  435. data/docs/api/Brut/CLI/Executor.html +0 -561
  436. data/docs/api/Brut/CLI/InvalidOption.html +0 -245
  437. data/docs/api/Brut/CLI/Options.html +0 -880
  438. data/docs/api/Brut/CLI/Output.html +0 -699
  439. data/docs/api/Brut/CLI/SystemExecError.html +0 -451
  440. data/docs/api/Brut/CLI.html +0 -263
  441. data/docs/api/Brut/FactoryBot.html +0 -225
  442. data/docs/api/Brut/Framework/App.html +0 -1097
  443. data/docs/api/Brut/Framework/Config.html +0 -1071
  444. data/docs/api/Brut/Framework/Container.html +0 -1464
  445. data/docs/api/Brut/Framework/Error.html +0 -140
  446. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +0 -232
  447. data/docs/api/Brut/Framework/Errors/Bug.html +0 -234
  448. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +0 -257
  449. data/docs/api/Brut/Framework/Errors/MissingParameter.html +0 -273
  450. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +0 -471
  451. data/docs/api/Brut/Framework/Errors/NotFound.html +0 -308
  452. data/docs/api/Brut/Framework/Errors/NotImplemented.html +0 -234
  453. data/docs/api/Brut/Framework/Errors.html +0 -351
  454. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +0 -392
  455. data/docs/api/Brut/Framework/MCP.html +0 -871
  456. data/docs/api/Brut/Framework/ProjectEnvironment.html +0 -648
  457. data/docs/api/Brut/Framework.html +0 -129
  458. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +0 -317
  459. data/docs/api/Brut/FrontEnd/Component/Helpers.html +0 -420
  460. data/docs/api/Brut/FrontEnd/Component.html +0 -434
  461. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +0 -491
  462. data/docs/api/Brut/FrontEnd/Components/FormTag.html +0 -526
  463. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +0 -313
  464. data/docs/api/Brut/FrontEnd/Components/Input.html +0 -195
  465. data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +0 -447
  466. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +0 -339
  467. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +0 -568
  468. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +0 -419
  469. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +0 -610
  470. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +0 -534
  471. data/docs/api/Brut/FrontEnd/Components/Inputs.html +0 -125
  472. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +0 -367
  473. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +0 -355
  474. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +0 -655
  475. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +0 -352
  476. data/docs/api/Brut/FrontEnd/Components.html +0 -156
  477. data/docs/api/Brut/FrontEnd/CsrfProtector.html +0 -250
  478. data/docs/api/Brut/FrontEnd/Download.html +0 -467
  479. data/docs/api/Brut/FrontEnd/Flash.html +0 -1150
  480. data/docs/api/Brut/FrontEnd/Form.html +0 -1227
  481. data/docs/api/Brut/FrontEnd/Forms/Button.html +0 -331
  482. data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +0 -537
  483. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +0 -590
  484. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +0 -201
  485. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +0 -535
  486. data/docs/api/Brut/FrontEnd/Forms/Input.html +0 -1567
  487. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +0 -635
  488. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +0 -1336
  489. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +0 -730
  490. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +0 -587
  491. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +0 -734
  492. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +0 -582
  493. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +0 -659
  494. data/docs/api/Brut/FrontEnd/Forms.html +0 -127
  495. data/docs/api/Brut/FrontEnd/GenericResponse.html +0 -377
  496. data/docs/api/Brut/FrontEnd/Handler.html +0 -442
  497. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +0 -318
  498. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +0 -336
  499. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +0 -399
  500. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +0 -354
  501. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +0 -151
  502. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +0 -315
  503. data/docs/api/Brut/FrontEnd/Handlers.html +0 -125
  504. data/docs/api/Brut/FrontEnd/HandlingResults.html +0 -339
  505. data/docs/api/Brut/FrontEnd/HttpMethod.html +0 -661
  506. data/docs/api/Brut/FrontEnd/HttpStatus.html +0 -496
  507. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +0 -284
  508. data/docs/api/Brut/FrontEnd/Layout.html +0 -486
  509. data/docs/api/Brut/FrontEnd/Middleware.html +0 -135
  510. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +0 -288
  511. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +0 -292
  512. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +0 -324
  513. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +0 -376
  514. data/docs/api/Brut/FrontEnd/Middlewares.html +0 -125
  515. data/docs/api/Brut/FrontEnd/Page.html +0 -781
  516. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +0 -797
  517. data/docs/api/Brut/FrontEnd/Pages.html +0 -125
  518. data/docs/api/Brut/FrontEnd/RequestContext.html +0 -1312
  519. data/docs/api/Brut/FrontEnd/RouteHook.html +0 -424
  520. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +0 -242
  521. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +0 -249
  522. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +0 -264
  523. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +0 -261
  524. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +0 -284
  525. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +0 -252
  526. data/docs/api/Brut/FrontEnd/RouteHooks.html +0 -115
  527. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +0 -227
  528. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +0 -305
  529. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +0 -324
  530. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +0 -319
  531. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +0 -315
  532. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +0 -315
  533. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +0 -327
  534. data/docs/api/Brut/FrontEnd/Routing/Route.html +0 -761
  535. data/docs/api/Brut/FrontEnd/Routing.html +0 -927
  536. data/docs/api/Brut/FrontEnd/Session.html +0 -1195
  537. data/docs/api/Brut/FrontEnd.html +0 -134
  538. data/docs/api/Brut/I18n/BaseMethods.html +0 -931
  539. data/docs/api/Brut/I18n/ForBackEnd.html +0 -302
  540. data/docs/api/Brut/I18n/ForCLI.html +0 -302
  541. data/docs/api/Brut/I18n/ForHTML.html +0 -296
  542. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +0 -316
  543. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +0 -930
  544. data/docs/api/Brut/I18n.html +0 -127
  545. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +0 -435
  546. data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +0 -596
  547. data/docs/api/Brut/Instrumentation/Methods.html +0 -173
  548. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +0 -286
  549. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +0 -302
  550. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +0 -866
  551. data/docs/api/Brut/Instrumentation.html +0 -128
  552. data/docs/api/Brut/RubocopConfig.html +0 -237
  553. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +0 -534
  554. data/docs/api/Brut/SinatraHelpers.html +0 -281
  555. data/docs/api/Brut/SpecSupport/ClockSupport.html +0 -383
  556. data/docs/api/Brut/SpecSupport/ComponentSupport.html +0 -496
  557. data/docs/api/Brut/SpecSupport/E2ETestServer.html +0 -503
  558. data/docs/api/Brut/SpecSupport/E2eSupport.html +0 -142
  559. data/docs/api/Brut/SpecSupport/EnhancedNode.html +0 -403
  560. data/docs/api/Brut/SpecSupport/FlashSupport.html +0 -278
  561. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +0 -401
  562. data/docs/api/Brut/SpecSupport/GeneralSupport.html +0 -195
  563. data/docs/api/Brut/SpecSupport/HandlerSupport.html +0 -160
  564. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +0 -142
  565. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +0 -142
  566. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +0 -155
  567. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +0 -583
  568. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +0 -149
  569. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +0 -466
  570. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +0 -149
  571. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +0 -149
  572. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +0 -165
  573. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +0 -158
  574. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +0 -156
  575. data/docs/api/Brut/SpecSupport/Matchers.html +0 -125
  576. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +0 -335
  577. data/docs/api/Brut/SpecSupport/RSpecSetup.html +0 -637
  578. data/docs/api/Brut/SpecSupport/SessionSupport.html +0 -196
  579. data/docs/api/Brut/SpecSupport.html +0 -129
  580. data/docs/api/Brut/TUI/AnsiEscapeCode/Mod.html +0 -409
  581. data/docs/api/Brut/TUI/AnsiEscapeCode.html +0 -426
  582. data/docs/api/Brut/TUI/EventLoop/Deque.html +0 -531
  583. data/docs/api/Brut/TUI/EventLoop.html +0 -676
  584. data/docs/api/Brut/TUI/Events/BaseEvent.html +0 -449
  585. data/docs/api/Brut/TUI/Events/EventBus.html +0 -485
  586. data/docs/api/Brut/TUI/Events/EventLoopStarted.html +0 -211
  587. data/docs/api/Brut/TUI/Events/Exception.html +0 -523
  588. data/docs/api/Brut/TUI/Events/Tick.html +0 -294
  589. data/docs/api/Brut/TUI/Events.html +0 -131
  590. data/docs/api/Brut/TUI/MarkupString.html +0 -537
  591. data/docs/api/Brut/TUI/Script/BlockStep.html +0 -300
  592. data/docs/api/Brut/TUI/Script/Events/CommandExecutionFailed.html +0 -252
  593. data/docs/api/Brut/TUI/Script/Events/CommandExecutionSucceeded.html +0 -163
  594. data/docs/api/Brut/TUI/Script/Events/CommandStdErr.html +0 -163
  595. data/docs/api/Brut/TUI/Script/Events/CommandStdOut.html +0 -300
  596. data/docs/api/Brut/TUI/Script/Events/ExecutingCommand.html +0 -298
  597. data/docs/api/Brut/TUI/Script/Events/Message.html +0 -345
  598. data/docs/api/Brut/TUI/Script/Events/PhaseCompleted.html +0 -229
  599. data/docs/api/Brut/TUI/Script/Events/PhaseStarted.html +0 -350
  600. data/docs/api/Brut/TUI/Script/Events/ScriptCompleted.html +0 -282
  601. data/docs/api/Brut/TUI/Script/Events/ScriptStarted.html +0 -343
  602. data/docs/api/Brut/TUI/Script/Events/StepCompleted.html +0 -163
  603. data/docs/api/Brut/TUI/Script/Events/StepStarted.html +0 -346
  604. data/docs/api/Brut/TUI/Script/Events.html +0 -115
  605. data/docs/api/Brut/TUI/Script/ExecStep/ProcessStatusFailed.html +0 -210
  606. data/docs/api/Brut/TUI/Script/ExecStep.html +0 -493
  607. data/docs/api/Brut/TUI/Script/LoggingSubscriber.html +0 -914
  608. data/docs/api/Brut/TUI/Script/PutsSubscriber.html +0 -783
  609. data/docs/api/Brut/TUI/Script/Step.html +0 -313
  610. data/docs/api/Brut/TUI/Script.html +0 -1250
  611. data/docs/api/Brut/TUI/Terminal.html +0 -593
  612. data/docs/api/Brut/TUI/TerminalTheme.html +0 -1403
  613. data/docs/api/Brut/TUI/Themes/Dark.html +0 -706
  614. data/docs/api/Brut/TUI/Themes/Light.html +0 -804
  615. data/docs/api/Brut/TUI/Themes/None.html +0 -218
  616. data/docs/api/Brut/TUI/Themes.html +0 -115
  617. data/docs/api/Brut/TUI.html +0 -129
  618. data/docs/api/Brut.html +0 -341
  619. data/docs/api/Clock.html +0 -603
  620. data/docs/api/ModuleName.html +0 -595
  621. data/docs/api/RichString.html +0 -775
  622. data/docs/api/SemanticLogger/Appender/Async.html +0 -219
  623. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +0 -119
  624. data/docs/api/Sequel/Extensions/BrutMigrations.html +0 -541
  625. data/docs/api/Sequel/Extensions.html +0 -117
  626. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +0 -105
  627. data/docs/api/Sequel/Plugins/CreatedAt.html +0 -125
  628. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +0 -207
  629. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +0 -186
  630. data/docs/api/Sequel/Plugins/ExternalId.html +0 -218
  631. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +0 -202
  632. data/docs/api/Sequel/Plugins/FindBang.html +0 -125
  633. data/docs/api/Sequel/Plugins.html +0 -117
  634. data/docs/api/Sequel.html +0 -117
  635. data/docs/api/SpecSupport/Matchers/BeABug.html +0 -143
  636. data/docs/api/_index.html +0 -1964
  637. data/docs/api/class_list.html +0 -54
  638. data/docs/api/css/common.css +0 -1
  639. data/docs/api/css/full_list.css +0 -59
  640. data/docs/api/css/style.css +0 -504
  641. data/docs/api/file.README.html +0 -172
  642. data/docs/api/file_list.html +0 -59
  643. data/docs/api/frames.html +0 -22
  644. data/docs/api/index.html +0 -172
  645. data/docs/api/js/app.js +0 -344
  646. data/docs/api/js/full_list.js +0 -242
  647. data/docs/api/js/jquery.js +0 -4
  648. data/docs/api/method_list.html +0 -5542
  649. data/docs/api/top-level-namespace.html +0 -112
  650. data/docs/assets/02-confirmation-dialog-browser-element-styled.3NEGM20-.png +0 -0
  651. data/docs/assets/02-confirmation-dialog-browser-element.DPsf0xUW.png +0 -0
  652. data/docs/assets/02-confirmation-dialog-browser.DH8ALFO4.png +0 -0
  653. data/docs/assets/02-confirmation-flow.D9gZ0S5U.png +0 -0
  654. data/docs/assets/DevEnvironment.DaFcVfwP.png +0 -0
  655. data/docs/assets/LogoStop.Gb3tDhL1.png +0 -0
  656. data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
  657. data/docs/assets/adrs.md.YglbWtQe.js +0 -1
  658. data/docs/assets/adrs.md.YglbWtQe.lean.js +0 -1
  659. data/docs/assets/ai.md.ChLnvDAX.js +0 -1
  660. data/docs/assets/ai.md.ChLnvDAX.lean.js +0 -1
  661. data/docs/assets/app.B8jAEB7R.js +0 -1
  662. data/docs/assets/assets.md.BEF6Oz6K.js +0 -19
  663. data/docs/assets/assets.md.BEF6Oz6K.lean.js +0 -1
  664. data/docs/assets/basic-form-with-violations.Cv6Y9-Q_.png +0 -0
  665. data/docs/assets/basic-form.DbHnu0oW.png +0 -0
  666. data/docs/assets/brut-js.md.BMz0X1Rz.js +0 -12
  667. data/docs/assets/brut-js.md.BMz0X1Rz.lean.js +0 -1
  668. data/docs/assets/business-logic.md.DbuaOYGU.js +0 -1
  669. data/docs/assets/business-logic.md.DbuaOYGU.lean.js +0 -1
  670. data/docs/assets/chunks/@localSearchIndexroot.DJ8mocCj.js +0 -1
  671. data/docs/assets/chunks/VPLocalSearchBox.gF-Po_fz.js +0 -8
  672. data/docs/assets/chunks/framework.C4nOkCZI.js +0 -18
  673. data/docs/assets/chunks/theme.BjPAOJkz.js +0 -2
  674. data/docs/assets/cli.md.DDMar_51.js +0 -122
  675. data/docs/assets/cli.md.DDMar_51.lean.js +0 -1
  676. data/docs/assets/components.md.Ber8UBM0.js +0 -96
  677. data/docs/assets/components.md.Ber8UBM0.lean.js +0 -1
  678. data/docs/assets/configuration.md.DrJ6YVoZ.js +0 -78
  679. data/docs/assets/configuration.md.DrJ6YVoZ.lean.js +0 -1
  680. data/docs/assets/css.md.K5rOCOQY.js +0 -21
  681. data/docs/assets/css.md.K5rOCOQY.lean.js +0 -1
  682. data/docs/assets/custom-element-tests.md.DiLe-eFw.js +0 -69
  683. data/docs/assets/custom-element-tests.md.DiLe-eFw.lean.js +0 -1
  684. data/docs/assets/database-access.md.Dc8l2Plf.js +0 -63
  685. data/docs/assets/database-access.md.Dc8l2Plf.lean.js +0 -1
  686. data/docs/assets/database-schema.md.BJ_JhXmO.js +0 -70
  687. data/docs/assets/database-schema.md.BJ_JhXmO.lean.js +0 -1
  688. data/docs/assets/deployment.md.CHTx2eTR.js +0 -55
  689. data/docs/assets/deployment.md.CHTx2eTR.lean.js +0 -1
  690. data/docs/assets/dev-env-protocol.DysDAtnz.png +0 -0
  691. data/docs/assets/dev-environment.md.B1S9p5ZK.js +0 -16
  692. data/docs/assets/dev-environment.md.B1S9p5ZK.lean.js +0 -1
  693. data/docs/assets/dir-structure.md.D1T2kGwj.js +0 -46
  694. data/docs/assets/dir-structure.md.D1T2kGwj.lean.js +0 -1
  695. data/docs/assets/doc-conventions.md.CDnWaEFg.js +0 -1
  696. data/docs/assets/doc-conventions.md.CDnWaEFg.lean.js +0 -1
  697. data/docs/assets/end-to-end-tests.md.BJJdNDYL.js +0 -28
  698. data/docs/assets/end-to-end-tests.md.BJJdNDYL.lean.js +0 -1
  699. data/docs/assets/features.md.BDWxnyNO.js +0 -154
  700. data/docs/assets/features.md.BDWxnyNO.lean.js +0 -1
  701. data/docs/assets/flash-and-session.md.CUsMxoNl.js +0 -79
  702. data/docs/assets/flash-and-session.md.CUsMxoNl.lean.js +0 -1
  703. data/docs/assets/form-constraints.md.KlfXSKm2.js +0 -90
  704. data/docs/assets/form-constraints.md.KlfXSKm2.lean.js +0 -1
  705. data/docs/assets/forms.md.RK0zkhm0.js +0 -64
  706. data/docs/assets/forms.md.RK0zkhm0.lean.js +0 -1
  707. data/docs/assets/getting-started.md.CGJ44juQ.js +0 -31
  708. data/docs/assets/getting-started.md.CGJ44juQ.lean.js +0 -1
  709. data/docs/assets/handlers.md.C5tUwmmo.js +0 -54
  710. data/docs/assets/handlers.md.C5tUwmmo.lean.js +0 -1
  711. data/docs/assets/hooks.md.CoiYCKRc.js +0 -80
  712. data/docs/assets/hooks.md.CoiYCKRc.lean.js +0 -1
  713. data/docs/assets/i18n.md.DxkCKhUw.js +0 -23
  714. data/docs/assets/i18n.md.DxkCKhUw.lean.js +0 -1
  715. data/docs/assets/index.md.DnphWyQd.js +0 -1
  716. data/docs/assets/index.md.DnphWyQd.lean.js +0 -1
  717. data/docs/assets/initial-home-page.DNIaYmgP.png +0 -0
  718. data/docs/assets/instrumentation.md.BcxjC4jd.js +0 -90
  719. data/docs/assets/instrumentation.md.BcxjC4jd.lean.js +0 -1
  720. data/docs/assets/javascript.md.D6fxhaQb.js +0 -31
  721. data/docs/assets/javascript.md.D6fxhaQb.lean.js +0 -1
  722. data/docs/assets/jobs.md.Bi3qb3v6.js +0 -25
  723. data/docs/assets/jobs.md.Bi3qb3v6.lean.js +0 -1
  724. data/docs/assets/keyword-injection.md.CqLnnzIz.js +0 -21
  725. data/docs/assets/keyword-injection.md.CqLnnzIz.lean.js +0 -1
  726. data/docs/assets/layouts.md.HEbeK7Jr.js +0 -68
  727. data/docs/assets/layouts.md.HEbeK7Jr.lean.js +0 -1
  728. data/docs/assets/lsp.md.bE9dW8n9.js +0 -1
  729. data/docs/assets/lsp.md.bE9dW8n9.lean.js +0 -1
  730. data/docs/assets/markdown-examples.md.BPmtHlc-.js +0 -33
  731. data/docs/assets/markdown-examples.md.BPmtHlc-.lean.js +0 -1
  732. data/docs/assets/middleware.md.BhOIsg59.js +0 -20
  733. data/docs/assets/middleware.md.BhOIsg59.lean.js +0 -1
  734. data/docs/assets/new-post-editor.DrHr-5oh.png +0 -0
  735. data/docs/assets/new-post-home-page.Bm34lyMg.png +0 -0
  736. data/docs/assets/overview.md.BpWAgPFH.js +0 -1
  737. data/docs/assets/overview.md.BpWAgPFH.lean.js +0 -1
  738. data/docs/assets/pages.md.B3sQXpEd.js +0 -45
  739. data/docs/assets/pages.md.B3sQXpEd.lean.js +0 -1
  740. data/docs/assets/recipes_alternate-layouts.md.C1QzVkA7.js +0 -22
  741. data/docs/assets/recipes_alternate-layouts.md.C1QzVkA7.lean.js +0 -1
  742. data/docs/assets/recipes_authentication.md.CyvoIW82.js +0 -157
  743. data/docs/assets/recipes_authentication.md.CyvoIW82.lean.js +0 -1
  744. data/docs/assets/recipes_custom-flash.md.6gFqf2uL.js +0 -26
  745. data/docs/assets/recipes_custom-flash.md.6gFqf2uL.lean.js +0 -1
  746. data/docs/assets/recipes_dev-env-secrets.md.DC_jVY9U.js +0 -12
  747. data/docs/assets/recipes_dev-env-secrets.md.DC_jVY9U.lean.js +0 -1
  748. data/docs/assets/recipes_form-errors.md.B5ptSzMO.js +0 -66
  749. data/docs/assets/recipes_form-errors.md.B5ptSzMO.lean.js +0 -1
  750. data/docs/assets/recipes_indexed-forms.md.BYYQGW2C.js +0 -74
  751. data/docs/assets/recipes_indexed-forms.md.BYYQGW2C.lean.js +0 -1
  752. data/docs/assets/recipes_migrations.md.Cid7-3cu.js +0 -97
  753. data/docs/assets/recipes_migrations.md.Cid7-3cu.lean.js +0 -1
  754. data/docs/assets/recipes_text-field-component.md.VhOsCtKI.js +0 -101
  755. data/docs/assets/recipes_text-field-component.md.VhOsCtKI.lean.js +0 -1
  756. data/docs/assets/roadmap.md.DqC1Y7Zt.js +0 -1
  757. data/docs/assets/roadmap.md.DqC1Y7Zt.lean.js +0 -1
  758. data/docs/assets/routes.md.C1dgIBtD.js +0 -21
  759. data/docs/assets/routes.md.C1dgIBtD.lean.js +0 -1
  760. data/docs/assets/security.md.Jn4SY1uK.js +0 -1
  761. data/docs/assets/security.md.Jn4SY1uK.lean.js +0 -1
  762. data/docs/assets/seed-data.md.UZW0WxYN.js +0 -14
  763. data/docs/assets/seed-data.md.UZW0WxYN.lean.js +0 -1
  764. data/docs/assets/spa.qejUdp-5.png +0 -0
  765. data/docs/assets/space-time-continuum.md.D9rYGDFH.js +0 -1
  766. data/docs/assets/space-time-continuum.md.D9rYGDFH.lean.js +0 -1
  767. data/docs/assets/style.B1z60PPQ.css +0 -1
  768. data/docs/assets/styled-form-with-server-side-violations.Bjxd8Dpv.png +0 -0
  769. data/docs/assets/styled-form-with-violations.Bv_sa9tg.png +0 -0
  770. data/docs/assets/styled-home-page-with-posts.Dd4kG89D.png +0 -0
  771. data/docs/assets/styled-home-page.BzdI7dWz.png +0 -0
  772. data/docs/assets/tutorial.md.BX6f6l00.js +0 -27
  773. data/docs/assets/tutorial.md.BX6f6l00.lean.js +0 -1
  774. data/docs/assets/tutorials_01-intro.md.CzZ3kpF_.js +0 -708
  775. data/docs/assets/tutorials_01-intro.md.CzZ3kpF_.lean.js +0 -1
  776. data/docs/assets/tutorials_02-dialog.md.DE5WfCXI.js +0 -274
  777. data/docs/assets/tutorials_02-dialog.md.DE5WfCXI.lean.js +0 -1
  778. data/docs/assets/unit-tests.md.vDsdBbO_.js +0 -13
  779. data/docs/assets/unit-tests.md.vDsdBbO_.lean.js +0 -1
  780. data/docs/assets/welcome-to-brut.VSWzl17-.png +0 -0
  781. data/docs/assets/why.md.4WpxdrQ2.js +0 -1
  782. data/docs/assets/why.md.4WpxdrQ2.lean.js +0 -1
  783. data/docs/assets/workspace-protocol.C0gXsoDb.png +0 -0
  784. data/docs/assets.html +0 -47
  785. data/docs/brut-css/brut.css +0 -1
  786. data/docs/brut-css/brut.max.css +0 -22372
  787. data/docs/brut-css/classes/appearances.html +0 -783
  788. data/docs/brut-css/classes/background-colors.html +0 -3529
  789. data/docs/brut-css/classes/border-colors.html +0 -3529
  790. data/docs/brut-css/classes/borders.html +0 -2293
  791. data/docs/brut-css/classes/dimensions.html +0 -2581
  792. data/docs/brut-css/classes/flex.html +0 -917
  793. data/docs/brut-css/classes/foreground-colors.html +0 -3261
  794. data/docs/brut-css/classes/junk-drawer.html +0 -431
  795. data/docs/brut-css/classes/layout.html +0 -668
  796. data/docs/brut-css/classes/lists.html +0 -331
  797. data/docs/brut-css/classes/positioning.html +0 -1751
  798. data/docs/brut-css/classes/spacings.html +0 -2633
  799. data/docs/brut-css/classes/typography.html +0 -2206
  800. data/docs/brut-css/customization/advanced-configuration.html +0 -204
  801. data/docs/brut-css/customization/breakpoints.html +0 -227
  802. data/docs/brut-css/customization/design-system.html +0 -197
  803. data/docs/brut-css/customization/pseudo-classes.html +0 -228
  804. data/docs/brut-css/docs.css +0 -98
  805. data/docs/brut-css/getting-started/core-concepts.html +0 -234
  806. data/docs/brut-css/getting-started/installation.html +0 -190
  807. data/docs/brut-css/getting-started/overview.html +0 -210
  808. data/docs/brut-css/getting-started/simple-example.html +0 -285
  809. data/docs/brut-css/index.html +0 -193
  810. data/docs/brut-css/prism-twilight.min.css +0 -1
  811. data/docs/brut-css/properties/colors.html +0 -1548
  812. data/docs/brut-css/properties/spacings.html +0 -614
  813. data/docs/brut-css/properties/typography.html +0 -777
  814. data/docs/brut-js/api/AjaxSubmit.html +0 -452
  815. data/docs/brut-js/api/AjaxSubmit.js.html +0 -550
  816. data/docs/brut-js/api/Autosubmit.html +0 -192
  817. data/docs/brut-js/api/Autosubmit.js.html +0 -114
  818. data/docs/brut-js/api/BaseCustomElement.html +0 -1091
  819. data/docs/brut-js/api/BaseCustomElement.js.html +0 -312
  820. data/docs/brut-js/api/BrutCustomElements.html +0 -172
  821. data/docs/brut-js/api/BufferedLogger.html +0 -173
  822. data/docs/brut-js/api/ConfirmSubmit.html +0 -286
  823. data/docs/brut-js/api/ConfirmSubmit.js.html +0 -188
  824. data/docs/brut-js/api/ConfirmationDialog.html +0 -425
  825. data/docs/brut-js/api/ConfirmationDialog.js.html +0 -194
  826. data/docs/brut-js/api/ConstraintViolationMessage.html +0 -498
  827. data/docs/brut-js/api/ConstraintViolationMessage.js.html +0 -191
  828. data/docs/brut-js/api/ConstraintViolationMessages.html +0 -590
  829. data/docs/brut-js/api/ConstraintViolationMessages.js.html +0 -149
  830. data/docs/brut-js/api/CopyToClipboard.html +0 -345
  831. data/docs/brut-js/api/CopyToClipboard.js.html +0 -147
  832. data/docs/brut-js/api/Form.html +0 -291
  833. data/docs/brut-js/api/Form.js.html +0 -198
  834. data/docs/brut-js/api/I18nTranslation.html +0 -409
  835. data/docs/brut-js/api/I18nTranslation.js.html +0 -115
  836. data/docs/brut-js/api/LocaleDetection.html +0 -312
  837. data/docs/brut-js/api/LocaleDetection.js.html +0 -168
  838. data/docs/brut-js/api/Logger.html +0 -702
  839. data/docs/brut-js/api/Logger.js.html +0 -141
  840. data/docs/brut-js/api/Message.html +0 -238
  841. data/docs/brut-js/api/Message.js.html +0 -113
  842. data/docs/brut-js/api/PrefixedLogger.html +0 -369
  843. data/docs/brut-js/api/RichString.html +0 -1049
  844. data/docs/brut-js/api/RichString.js.html +0 -167
  845. data/docs/brut-js/api/Tabs.html +0 -295
  846. data/docs/brut-js/api/Tabs.js.html +0 -219
  847. data/docs/brut-js/api/Toast.html +0 -270
  848. data/docs/brut-js/api/Toast.js.html +0 -153
  849. data/docs/brut-js/api/Tracing.html +0 -277
  850. data/docs/brut-js/api/Tracing.js.html +0 -298
  851. data/docs/brut-js/api/external-CustomElementRegistry.html +0 -140
  852. data/docs/brut-js/api/external-Performance.html +0 -138
  853. data/docs/brut-js/api/external-Promise.html +0 -138
  854. data/docs/brut-js/api/external-ValidityState.html +0 -138
  855. data/docs/brut-js/api/external-Window.html +0 -233
  856. data/docs/brut-js/api/external-fetch.html +0 -138
  857. data/docs/brut-js/api/global.html +0 -400
  858. data/docs/brut-js/api/index.html +0 -168
  859. data/docs/brut-js/api/index.js.html +0 -184
  860. data/docs/brut-js/api/module-testing.html +0 -383
  861. data/docs/brut-js/api/scripts/linenumber.js +0 -25
  862. data/docs/brut-js/api/scripts/prettify/Apache-License-2.0.txt +0 -202
  863. data/docs/brut-js/api/scripts/prettify/lang-css.js +0 -2
  864. data/docs/brut-js/api/scripts/prettify/prettify.js +0 -28
  865. data/docs/brut-js/api/styles/jsdoc-default.css +0 -327
  866. data/docs/brut-js/api/styles/prettify-jsdoc.css +0 -111
  867. data/docs/brut-js/api/styles/prettify-tomorrow.css +0 -132
  868. data/docs/brut-js/api/testing.AssetMetadata.html +0 -172
  869. data/docs/brut-js/api/testing.AssetMetadataLoader.html +0 -171
  870. data/docs/brut-js/api/testing.CustomElementTest.html +0 -679
  871. data/docs/brut-js/api/testing.DOMCreator.html +0 -171
  872. data/docs/brut-js/api/testing_AssetMetadata.js.html +0 -86
  873. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +0 -76
  874. data/docs/brut-js/api/testing_CustomElementTest.js.html +0 -286
  875. data/docs/brut-js/api/testing_DOMCreator.js.html +0 -96
  876. data/docs/brut-js/api/testing_index.js.html +0 -99
  877. data/docs/brut-js.html +0 -40
  878. data/docs/business-logic.html +0 -29
  879. data/docs/cli.html +0 -150
  880. data/docs/components.html +0 -124
  881. data/docs/configuration.html +0 -106
  882. data/docs/css.html +0 -49
  883. data/docs/custom-element-tests.html +0 -97
  884. data/docs/database-access.html +0 -91
  885. data/docs/database-schema.html +0 -98
  886. data/docs/deployment.html +0 -83
  887. data/docs/dev-environment.html +0 -44
  888. data/docs/dir-structure.html +0 -74
  889. data/docs/doc-conventions.html +0 -29
  890. data/docs/end-to-end-tests.html +0 -56
  891. data/docs/favicon.ico +0 -0
  892. data/docs/features.html +0 -182
  893. data/docs/flash-and-session.html +0 -107
  894. data/docs/form-constraints.html +0 -118
  895. data/docs/forms.html +0 -92
  896. data/docs/getting-started.html +0 -59
  897. data/docs/handlers.html +0 -82
  898. data/docs/hashmap.json +0 -1
  899. data/docs/hooks.html +0 -108
  900. data/docs/i18n.html +0 -51
  901. data/docs/index.html +0 -29
  902. data/docs/instrumentation.html +0 -118
  903. data/docs/javascript.html +0 -59
  904. data/docs/jobs.html +0 -53
  905. data/docs/keyword-injection.html +0 -49
  906. data/docs/layouts.html +0 -96
  907. data/docs/lsp.html +0 -29
  908. data/docs/markdown-examples.html +0 -61
  909. data/docs/middleware.html +0 -48
  910. data/docs/overview.html +0 -29
  911. data/docs/pages.html +0 -73
  912. data/docs/recipes/alternate-layouts.html +0 -50
  913. data/docs/recipes/authentication.html +0 -185
  914. data/docs/recipes/custom-flash.html +0 -54
  915. data/docs/recipes/dev-env-secrets.html +0 -40
  916. data/docs/recipes/form-errors.html +0 -94
  917. data/docs/recipes/indexed-forms.html +0 -102
  918. data/docs/recipes/migrations.html +0 -125
  919. data/docs/recipes/text-field-component.html +0 -129
  920. data/docs/roadmap.html +0 -29
  921. data/docs/routes.html +0 -49
  922. data/docs/security.html +0 -29
  923. data/docs/seed-data.html +0 -42
  924. data/docs/space-time-continuum.html +0 -29
  925. data/docs/tutorial.html +0 -55
  926. data/docs/tutorials/01-intro.html +0 -736
  927. data/docs/tutorials/02-dialog.html +0 -302
  928. data/docs/unit-tests.html +0 -41
  929. data/docs/vp-icons.css +0 -1
  930. data/docs/why.html +0 -29
  931. data/docs-todo.md +0 -32
  932. data/dx/bash_customizations +0 -6
  933. data/dx/build +0 -73
  934. data/dx/build.pre +0 -15
  935. data/dx/docker-compose.env +0 -22
  936. data/dx/dx.sh.lib +0 -24
  937. data/dx/exec +0 -75
  938. data/dx/setupkit.sh.lib +0 -144
  939. data/dx/show-help-in-app-container-then-wait.sh +0 -38
  940. data/lib/brut/cli/app.rb +0 -238
  941. data/lib/brut/cli/app_runner.rb +0 -252
  942. data/lib/brut/cli/command.rb +0 -258
  943. data/lib/brut/cli/execution_results.rb +0 -119
  944. data/lib/brut/front_end/layouts/_internal.html.erb +0 -68
  945. data/lib/brut/front_end/pages/_missing_page.html.erb +0 -17
  946. data/mkbrut/.gitignore +0 -16
  947. data/mkbrut/CODE_OF_CONDUCT.txt +0 -100
  948. data/mkbrut/Gemfile +0 -3
  949. data/mkbrut/Gemfile.lock +0 -20
  950. data/mkbrut/LICENSE.txt +0 -370
  951. data/mkbrut/README.md +0 -145
  952. data/mkbrut/Rakefile +0 -2
  953. data/mkbrut/bin/build +0 -36
  954. data/mkbrut/bin/ci +0 -19
  955. data/mkbrut/bin/docs +0 -19
  956. data/mkbrut/bin/publish +0 -129
  957. data/mkbrut/bin/rake +0 -16
  958. data/mkbrut/bin/setup +0 -30
  959. data/mkbrut/brut-welcome.png +0 -0
  960. data/mkbrut/deploy/.dockerignore +0 -2
  961. data/mkbrut/deploy/Dockerfile +0 -25
  962. data/mkbrut/dx +0 -1
  963. data/mkbrut/exe/mkbrut +0 -5
  964. data/mkbrut/lib/mkbrut/app_name.rb +0 -29
  965. data/mkbrut/lib/mkbrut/app_options.rb +0 -36
  966. data/mkbrut/lib/mkbrut/cli.rb +0 -189
  967. data/mkbrut/lib/mkbrut/erb_binding_delegate.rb +0 -20
  968. data/mkbrut/lib/mkbrut/ops.rb +0 -17
  969. data/mkbrut/lib/mkbrut/organization.rb +0 -5
  970. data/mkbrut/lib/mkbrut/segments.rb +0 -8
  971. data/mkbrut/lib/mkbrut/version.rb +0 -3
  972. data/mkbrut/lib/mkbrut.rb +0 -20
  973. data/mkbrut/mkbrut.gemspec +0 -34
  974. data/mkbrut/templates/Base/app/src/front_end/images/LogoPylon.png +0 -0
  975. data/mkbrut/templates/Base/bin/build-assets +0 -7
  976. data/mkbrut/templates/Base/bin/ci +0 -39
  977. data/mkbrut/templates/Base/bin/db +0 -9
  978. data/mkbrut/templates/Base/bin/scaffold +0 -9
  979. data/mkbrut/templates/Base/bin/setup +0 -287
  980. data/mkbrut/templates/Base/bin/test +0 -9
  981. data/mkbrut/templates/Base/bin/test-server +0 -29
  982. data/mkbrut/templates/Base/dx/prune +0 -19
  983. data/mkbrut/templates/Base/dx/start +0 -30
  984. data/mkbrut/templates/Base/dx/stop +0 -23
  985. data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +0 -27
  986. data/specs/brut/front_end/forms/input.spec.rb +0 -978
  987. data/specs/brut/front_end/forms/radio_button_group_input.spec.rb +0 -54
  988. data/specs/brut/front_end/forms/select_input.spec.rb +0 -54
  989. data/specs/brut/instrumentation/methods.spec.rb +0 -399
  990. data/specs/brut/junk_drawer.spec.rb +0 -79
  991. data/specs/brut/tui/ansi_escape_code.spec.rb +0 -30
  992. data/specs/brut/tui/event_loop.spec.rb +0 -70
  993. data/specs/brut/tui/events/base_event.spec.rb +0 -26
  994. data/specs/brut/tui/events/event_bus.spec.rb +0 -141
  995. data/specs/brut/tui/events/exception.spec.rb +0 -19
  996. data/specs/brut/tui/events/test_event.rb +0 -5
  997. data/specs/spec_helper.rb +0 -31
  998. data/specs/support/matchers/have_constraint_violation.rb +0 -23
  999. data/specs/support/matchers.rb +0 -5
  1000. data/specs/support.rb +0 -3
  1001. /data/{mkbrut/lib/mkbrut → lib/brut/cli/apps/new}/prefixed_io.rb +0 -0
  1002. /data/{mkbrut/templates → templates}/Base/.dockerignore +0 -0
  1003. /data/{mkbrut/templates → templates}/Base/.env.development.erb +0 -0
  1004. /data/{mkbrut/templates → templates}/Base/.env.test.erb +0 -0
  1005. /data/{mkbrut/templates → templates}/Base/.gitignore +0 -0
  1006. /data/{mkbrut/templates → templates}/Base/.projections.json +0 -0
  1007. /data/{mkbrut/templates → templates}/Base/Dockerfile.dx +0 -0
  1008. /data/{mkbrut/templates → templates}/Base/Gemfile.erb +0 -0
  1009. /data/{mkbrut/templates → templates}/Base/Procfile.development +0 -0
  1010. /data/{mkbrut/templates → templates}/Base/Procfile.test +0 -0
  1011. /data/{mkbrut/templates → templates}/Base/README.md +0 -0
  1012. /data/{mkbrut/templates → templates}/Base/README.md.erb +0 -0
  1013. /data/{mkbrut/templates → templates}/Base/app/bootstrap.rb +0 -0
  1014. /data/{mkbrut/templates → templates}/Base/app/config/i18n/en/1_defaults.rb +0 -0
  1015. /data/{mkbrut/templates → templates}/Base/app/config/i18n/en/2_app.rb +0 -0
  1016. /data/{mkbrut/templates → templates}/Base/app/public/static/manifest.json.erb +0 -0
  1017. /data/{mkbrut/templates → templates}/Base/app/src/app.rb.erb +0 -0
  1018. /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/app_data_model.rb +0 -0
  1019. /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/db.rb +0 -0
  1020. /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/migrations/20240101130000_citext.rb +0 -0
  1021. /data/{mkbrut/templates → templates}/Base/app/src/back_end/data_models/seed/seed_data.rb +0 -0
  1022. /data/{mkbrut/templates → templates}/Base/app/src/front_end/components/app_component.rb +0 -0
  1023. /data/{mkbrut/templates → templates}/Base/app/src/front_end/components/custom_element_registration.rb.erb +0 -0
  1024. /data/{mkbrut/templates → templates}/Base/app/src/front_end/css/index.css +0 -0
  1025. /data/{mkbrut/templates → templates}/Base/app/src/front_end/css/svgs.css +0 -0
  1026. /data/{mkbrut/templates → templates}/Base/app/src/front_end/forms/app_form.rb +0 -0
  1027. /data/{mkbrut/templates → templates}/Base/app/src/front_end/handlers/app_handler.rb +0 -0
  1028. /data/{brutrb.com → templates/Base/app/src/front_end}/images/LogoPylon.png +0 -0
  1029. /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/LogoTransit.png +0 -0
  1030. /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-120x120.png +0 -0
  1031. /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-152x152.png +0 -0
  1032. /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-167x167.png +0 -0
  1033. /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/apple-touch-icon-180x180.png +0 -0
  1034. /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/favicon.ico +0 -0
  1035. /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/icon.png +0 -0
  1036. /data/{mkbrut/templates → templates}/Base/app/src/front_end/images/mkicons.sh +0 -0
  1037. /data/{mkbrut/templates → templates}/Base/app/src/front_end/js/index.js +0 -0
  1038. /data/{mkbrut/templates → templates}/Base/app/src/front_end/layouts/blank_layout.rb +0 -0
  1039. /data/{mkbrut/templates → templates}/Base/app/src/front_end/layouts/default_layout.rb.erb +0 -0
  1040. /data/{mkbrut/templates → templates}/Base/app/src/front_end/pages/app_page.rb +0 -0
  1041. /data/{mkbrut/templates → templates}/Base/app/src/front_end/pages/home_page.rb +0 -0
  1042. /data/{mkbrut/templates → templates}/Base/app/src/front_end/support/app_session.rb +0 -0
  1043. /data/{mkbrut/templates → templates}/Base/app/src/front_end/svgs/README.md +0 -0
  1044. /data/{mkbrut/templates → templates}/Base/app/src/front_end/svgs/comment-button.svg +0 -0
  1045. /data/{mkbrut/templates → templates}/Base/bin/README.md.erb +0 -0
  1046. /data/{mkbrut/templates → templates}/Base/bin/console +0 -0
  1047. /data/{mkbrut/templates → templates}/Base/bin/dbconsole +0 -0
  1048. /data/{mkbrut/templates → templates}/Base/bin/dev +0 -0
  1049. /data/{mkbrut/templates → templates}/Base/bin/run +0 -0
  1050. /data/{mkbrut/templates → templates}/Base/bin/run.run +0 -0
  1051. /data/{mkbrut/templates → templates}/Base/bin/startup-message +0 -0
  1052. /data/{mkbrut/templates → templates}/Base/config.ru +0 -0
  1053. /data/{mkbrut/templates → templates}/Base/docker-compose.dx.yml +0 -0
  1054. /data/{mkbrut/templates → templates}/Base/dx/README.md +0 -0
  1055. /data/{mkbrut/templates → templates}/Base/dx/bash_customizations +0 -0
  1056. /data/{mkbrut/templates → templates}/Base/dx/bash_customizations.local +0 -0
  1057. /data/{mkbrut/templates → templates}/Base/dx/build +0 -0
  1058. /data/{mkbrut/templates → templates}/Base/dx/dx.sh.lib +0 -0
  1059. /data/{mkbrut/templates → templates}/Base/dx/exec +0 -0
  1060. /data/{dx → templates/Base/dx}/prune +0 -0
  1061. /data/{mkbrut/templates → templates}/Base/dx/show-help-in-app-container-then-wait.sh +0 -0
  1062. /data/{dx → templates/Base/dx}/start +0 -0
  1063. /data/{dx → templates/Base/dx}/stop +0 -0
  1064. /data/{mkbrut/templates → templates}/Base/package.json.erb +0 -0
  1065. /data/{mkbrut/templates → templates}/Base/puma.config.rb +0 -0
  1066. /data/{mkbrut/templates → templates}/Base/specs/e2e/home_page.spec.rb.erb +0 -0
  1067. /data/{mkbrut/templates → templates}/Base/specs/front_end/js/SpecHelper.js +0 -0
  1068. /data/{mkbrut/templates → templates}/Base/specs/front_end/pages/home_page.spec.rb +0 -0
  1069. /data/{mkbrut/templates → templates}/Base/specs/lint_factories.spec.rb +0 -0
  1070. /data/{mkbrut/templates → templates}/Base/specs/spec_helper.rb +0 -0
  1071. /data/{mkbrut/templates → templates}/Base/specs/support.rb +0 -0
  1072. /data/{mkbrut/templates → templates}/segments/BareBones/app/src/front_end/handlers/trigger_exception_handler.rb +0 -0
  1073. /data/{mkbrut/templates → templates}/segments/BareBones/app/src/front_end/js/Example.js.erb +0 -0
  1074. /data/{mkbrut/templates → templates}/segments/BareBones/specs/front_end/handlers/trigger_exception_handler.spec.rb +0 -0
  1075. /data/{mkbrut/templates → templates}/segments/BareBones/specs/front_end/js/Example.spec.js.erb +0 -0
  1076. /data/{mkbrut/templates → templates}/segments/Demo/app/src/back_end/data_models/db/guestbook_message.rb +0 -0
  1077. /data/{mkbrut/templates → templates}/segments/Demo/app/src/back_end/data_models/migrations/20250628194124_guestbook.rb +0 -0
  1078. /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/components/flash_component.rb +0 -0
  1079. /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/css/constraint-violations.css +0 -0
  1080. /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/fonts/monaspace-xenon.ttf +0 -0
  1081. /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/forms/guestbook_message_form.rb +0 -0
  1082. /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/handlers/guestbook_message_handler.rb +0 -0
  1083. /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/guestbook_page/message_component.rb +0 -0
  1084. /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/guestbook_page.rb +0 -0
  1085. /data/{mkbrut/templates → templates}/segments/Demo/app/src/front_end/pages/new_guestbook_message_page.rb +0 -0
  1086. /data/{mkbrut/templates → templates}/segments/Demo/specs/back_end/data_models/db/guestbook_message.spec.rb +0 -0
  1087. /data/{mkbrut/templates → templates}/segments/Demo/specs/e2e/guest_message.spec.rb +0 -0
  1088. /data/{mkbrut/templates → templates}/segments/Demo/specs/factories/db/guestbook_message.factory.rb +0 -0
  1089. /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/components/flash_component.spec.rb +0 -0
  1090. /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/handlers/guestbook_message_handler.spec.rb +0 -0
  1091. /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/guestbook_page/message_component.spec.rb +0 -0
  1092. /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/guestbook_page.spec.rb +0 -0
  1093. /data/{mkbrut/templates → templates}/segments/Demo/specs/front_end/pages/new_guestbook_message_page.spec.rb +0 -0
  1094. /data/{mkbrut/templates → templates}/segments/Heroku/bin/deploy +0 -0
  1095. /data/{mkbrut/templates → templates}/segments/Heroku/deploy/docker-entrypoint +0 -0
  1096. /data/{mkbrut/templates → templates}/segments/Sidekiq/app/boot_sidekiq.rb +0 -0
  1097. /data/{mkbrut/templates → templates}/segments/Sidekiq/app/config/sidekiq.yml +0 -0
  1098. /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/jobs/app_job.rb +0 -0
  1099. /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/jobs/example_job.rb +0 -0
  1100. /data/{mkbrut/templates → templates}/segments/Sidekiq/app/src/back_end/segments/sidekiq_segment.rb +0 -0
  1101. /data/{mkbrut/templates → templates}/segments/Sidekiq/bin/run.sidekiq +0 -0
  1102. /data/{mkbrut/templates → templates}/segments/Sidekiq/specs/back_end/jobs/example_job.spec.rb +0 -0
  1103. /data/{mkbrut/templates → templates}/segments/Sidekiq/specs/integration/sidekiq_works.spec.rb +0 -0
@@ -1,25 +0,0 @@
1
- import{_ as e,c as i,o as a,ag as t}from"./chunks/framework.C4nOkCZI.js";const k=JSON.parse('{"title":"Background Jobs","description":"","frontmatter":{},"headers":[],"relativePath":"jobs.md","filePath":"jobs.md"}'),n={name:"jobs.md"};function o(p,s,l,d,h,r){return a(),i("div",null,[...s[0]||(s[0]=[t(`<h1 id="background-jobs" tabindex="-1">Background Jobs <a class="header-anchor" href="#background-jobs" aria-label="Permalink to &quot;Background Jobs&quot;">​</a></h1><p>Brut ships without any background job system, however it should work with any system you&#39;d like to use. Brut can install/configure Sidekiq for you, however you are expected to understand Sidekiq in order to use it.</p><h2 id="setting-up-sidekiq" tabindex="-1">Setting up Sidekiq <a class="header-anchor" href="#setting-up-sidekiq" aria-label="Permalink to &quot;Setting up Sidekiq&quot;">​</a></h2><p>Brut&#39;s code-generation system used for installing capabilities are called <em>segments</em>, and Brut provides a Sidekiq segment you can use to get an initial working setup of Sidekiq in your Brut app.</p><h3 id="adding-the-segment" tabindex="-1">Adding the Segment <a class="header-anchor" href="#adding-the-segment" aria-label="Permalink to &quot;Adding the Segment&quot;">​</a></h3><ol><li><p>Ensure your project files are all committed. This is so you can easily see (and, if needed, undo) the changes <code>mkbrut</code> will make.</p></li><li><p>Use <code>mkbrut</code> to add the segment:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>docker run \\</span></span>
2
- <span class="line"><span> --pull always \\</span></span>
3
- <span class="line"><span> -v &quot;$PWD&quot;:&quot;$PWD&quot; \\</span></span>
4
- <span class="line"><span> -w &quot;$PWD&quot; \\</span></span>
5
- <span class="line"><span> -u $(id -u):$(id -g) \\</span></span>
6
- <span class="line"><span> -it \\</span></span>
7
- <span class="line"><span> thirdtank/mkbrut \\</span></span>
8
- <span class="line"><span> mkbrut add-segment -r /path/to/your/project sidekiq</span></span></code></pre></div></li><li><p>This will modify and create various files in your project. Check them out if you like:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>&gt; git status</span></span></code></pre></div></li><li><p>Exit your dev environment (i.e. hit <code>Ctrl-C</code> wherever you ran <code>dx/start</code>).</p></li><li><p>Rebuild and restart your dev environment. This may take a moment, since Valkey will be downloaded.</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>your-computer&gt; dx/build</span></span>
9
- <span class="line"><span>your-computer&gt; dx/start</span></span></code></pre></div></li><li><p>In another Terminal, connect to your dev container and run <code>bin/setup</code></p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>your-computer&gt; dx/exec bash</span></span>
10
- <span class="line"><span>devcontainer&gt; bin/setup</span></span></code></pre></div></li><li><p>The segment provides an integration test that will use the actual Sidekiq server and client, running against the actual Valkey database that was installed:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span>devcontainer&gt; bin/test e2e specs/integration/sidekiq_works.spec.rb</span></span></code></pre></div></li></ol><p>If this test passes, you are ready to go.</p><h3 id="using-sidekiq-in-brut" tabindex="-1">Using Sidekiq in Brut <a class="header-anchor" href="#using-sidekiq-in-brut" aria-label="Permalink to &quot;Using Sidekiq in Brut&quot;">​</a></h3><p>Jobs live in <code>app/src/back_end/jobs</code>, however this is just a convention and is not enforced - you can place a job anywhere that Zeitwerk will find the class. Brut also provides basic configuration and a base job.</p><table tabindex="0"><thead><tr><th>File</th><th>Purpose</th></tr></thead><tbody><tr><td><code>app/config/sidekiq.yml</code></td><td>Standard configuration for Sidekiq</td></tr><tr><td><code>app/src/back-end/jobs/app_job.r</code></td><td>Base class for your jobs that includes <code>Sidekiq::Job</code></td></tr><tr><td><code>app/src/back-end/segments/sidekiq_segment.rb</code></td><td>Initial client and server configuration for Sidekiq (that you can&#39;t do with <code>sidekiq.yml</code>. This sets up basic observability for your jobs</td></tr></tbody></table><h3 id="accessing-the-web-ui" tabindex="-1">Accessing the Web UI <a class="header-anchor" href="#accessing-the-web-ui" aria-label="Permalink to &quot;Accessing the Web UI&quot;">​</a></h3><p>The Sidekiq segment mounts the Sidekiq Web UI to your app inside <code>config.ru</code>:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># ...</span></span>
11
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">map </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;/sidekiq&quot;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
12
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> use </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Rack</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Basic</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;Sidekiq&quot;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> |username, password|</span></span>
13
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> [username, password] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">ENV</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fetch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;SIDEKIQ_BASIC_AUTH_USER&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">ENV</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fetch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;SIDEKIQ_BASIC_AUTH_PASSWORD&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)]</span></span>
14
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
15
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> run </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Sidekiq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Web</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">new</span></span>
16
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span>
17
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># ...</span></span></code></pre></div><p>Values for <code>SIDEKIQ_BASIC_AUTH_USER</code> and <code>SIDEKIQ_BASIC_AUTH_PASSWORD</code> for dev and test are placed into <code>.env.development</code> and <code>.env.test</code>, respectively. You must provide these values for production, based on however you are managing environment variables.</p><p>Once you start the app, navigat to <code>http://localhost:6502/sidekiq</code> and enter the username/password from <code>.env.development</code>. You should see the web UI.</p><h3 id="deploying-with-the-heroku-segment" tabindex="-1">Deploying with The Heroku Segment <a class="header-anchor" href="#deploying-with-the-heroku-segment" aria-label="Permalink to &quot;Deploying with The Heroku Segment&quot;">​</a></h3><p>If you have set up <a href="/deployment.html#heroku-container-based-deployment">Heroku Container-based Deployment</a>, you may need to modify <code>deploy/heroku_config.rb</code>. The Sidekiq segement should have edited this, however if you installed the Heroku segment after setting up Sidekiq, you&#39;ll need to add to the file:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> HerokuConfig</span></span>
18
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> self.additional_images</span></span>
19
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
20
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;sidekiq&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> =&gt; {</span></span>
21
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> cmd:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;bin/run sidekiq&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
22
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
23
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
24
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
25
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><h2 id="setting-up-other-job-systems" tabindex="-1">Setting Up Other Job Systems <a class="header-anchor" href="#setting-up-other-job-systems" aria-label="Permalink to &quot;Setting Up Other Job Systems&quot;">​</a></h2><p>To use another job system, you&#39;ll likely want to start with <code>app/src/app.rb</code>. You can place all your initialize code in <code>#boot!</code> to get things working, then factor it out from there. <code>App</code>, the class in that file, is a normal class, so you can extract your setup to other normal classes and bring them in as you would in any other Ruby app.</p><p>Just note that <code>App</code>&#39;s <code>initialize</code> method should avoid making network connections, so while you are safe to create objects and configuration here, do not connect to databases or anything like that. You <em>can</em> do that inside <code>boot!</code>.</p>`,21)])])}const u=e(n,[["render",o]]);export{k as __pageData,u as default};
@@ -1 +0,0 @@
1
- import{_ as e,c as i,o as a,ag as t}from"./chunks/framework.C4nOkCZI.js";const k=JSON.parse('{"title":"Background Jobs","description":"","frontmatter":{},"headers":[],"relativePath":"jobs.md","filePath":"jobs.md"}'),n={name:"jobs.md"};function o(p,s,l,d,h,r){return a(),i("div",null,[...s[0]||(s[0]=[t("",21)])])}const u=e(n,[["render",o]]);export{k as __pageData,u as default};
@@ -1,21 +0,0 @@
1
- import{_ as t,c as s,o as a,ag as i}from"./chunks/framework.C4nOkCZI.js";const u=JSON.parse('{"title":"Keyword Injection","description":"","frontmatter":{},"headers":[],"relativePath":"keyword-injection.md","filePath":"keyword-injection.md"}'),n={name:"keyword-injection.md"};function o(r,e,l,d,h,p){return a(),s("div",null,[...e[0]||(e[0]=[i(`<h1 id="keyword-injection" tabindex="-1">Keyword Injection <a class="header-anchor" href="#keyword-injection" aria-label="Permalink to &quot;Keyword Injection&quot;">​</a></h1><p>Brut is desiged around classes and objects, as compared to modules and DSLs. Almost everything you do when building your app is to create a class that has an initializer and implements one or more methods. But, these classes often need information from the request that Brut is managing.</p><p>In a basic Rack or Sinatra app, you would access this information via Rack&#39;s API, which is essentially a Hash of Whatever. It&#39;s error-prone and requires consulting documentation, source code, or runtime information to figure out what&#39;s stored where.</p><p>Brut can instead inject these values explicitly into the classes of yours it creates. It does this based on the names of keyword arguments declared by your class&#39; intializer.</p><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to &quot;Overview&quot;">​</a></h2><p>A <a href="/pages.html">Page</a> may need the session, flash, HTTP headers, query string parameters, or placeholder values from the URI. These can all be provided by declaring them as keyword arguments to the page&#39;s initializer:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">clas </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">WidgetsByIdPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> AppPage</span></span>
2
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> initialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">id:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># &quot;:id&quot; from /widgets/:id</span></span>
3
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> session:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># AppSession instance for this request</span></span>
4
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> flash:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># Flash for this request</span></span>
5
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> http_user_agent:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># Value of User-Agent header</span></span>
6
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> compact:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># query string param &quot;compact&quot;</span></span>
7
- <span class="line"></span>
8
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
9
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
10
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>Brut uses this technique in multiple places. It allows you to design classes whose dependencies are clear and explicit, but without having to dig around into hashes or manually construct higher-level objects.</p><h3 id="standard-injectible-information" tabindex="-1">Standard Injectible Information <a class="header-anchor" href="#standard-injectible-information" aria-label="Permalink to &quot;Standard Injectible Information&quot;">​</a></h3><p>In any request, the following information is available to be injected:</p><table tabindex="0"><thead><tr><th>Value</th><th>Always Present?</th><th>Description</th></tr></thead><tbody><tr><td><code>session:</code></td><td>✅ Yes</td><td>An instance of your app&#39;s <a href="/api/Brut/FrontEnd/Session.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Session</code></a> subclass for the current visitor&#39;s session.</td></tr><tr><td><code>flash:</code></td><td>✅ Yes</td><td>An instance of your app&#39;s <a href="/api/Brut/FrontEnd/Flash.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Flash</code></a> subclass.</td></tr><tr><td><code>xhr:</code></td><td>✅ Yes</td><td>true if this was an Ajax request.</td></tr><tr><td><code>body:</code></td><td>✅ Yes</td><td>the body submitted, if any.</td></tr><tr><td><code>csrf_token:</code></td><td>✅ Yes</td><td>The current CSRF token.</td></tr><tr><td><code>clock:</code></td><td>✅ Yes</td><td>A <code>Clock</code> to be used to access the current time in the visitor&#39;s time zone.</td></tr><tr><td><code>http_*</code></td><td>❌ No</td><td>any parameter that starts with <code>http_</code> is assumed to be for an HTTP header. For example, <code>http_accept_language</code> would be given the value for the &quot;Accept-Language&quot; header. See the section on HTTP headers below.</td></tr><tr><td><code>rack_request_*:</code></td><td>❌ No</td><td>Any value from the <a href="https://rubydoc.info/gems/rack/3.1.16/Rack/Request" target="_blank" rel="noreferrer"><code>Rack::Request</code></a> or, more likely, from the <a href="https://rubydoc.info/gems/rack/3.1.16/Rack/Request/Helpers" target="_blank" rel="noreferrer"><code>Helpers</code></a> module.</td></tr><tr><td><code>env:</code></td><td>✅ Yes</td><td>The Rack env. This is discouraged, but available if you can&#39;t get what you want directly</td></tr><tr><td><code>form:</code></td><td>❌ No</td><td>The <a href="/forms.html">form</a> that was submitted, for <a href="/handlers.html">handlers</a> only</td></tr><tr><td>Any query string parameter</td><td>❌ No</td><td>For <a href="/pages.html">pages</a> only</td></tr><tr><td>Any route placeholder</td><td>✅ Yes</td><td>For <a href="/pages.html">pages</a> and <a href="/handlers.html">handlers</a></td></tr></tbody></table><h4 id="route-hooks" tabindex="-1">Route Hooks <a class="header-anchor" href="#route-hooks" aria-label="Permalink to &quot;Route Hooks&quot;">​</a></h4><p><a href="/hooks.html">Route hooks</a> are slightly different. They have access to only these values:</p><table tabindex="0"><thead><tr><th>Name</th><th>Always Present?</th><th>Description</th></tr></thead><tbody><tr><td><code>request_context:</code></td><td>❌ No</td><td>The current <a href="/api/Brut/FrontEnd/RequestContext.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::RequestContext</code></a>, thought it may be <code>nil</code> if the hook runs before <a href="/api/Brut/FrontEnd/InlineSvgLocator.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::InlineSvgLocator</code></a></td></tr><tr><td><code>session:</code></td><td>✅ Yes</td><td>An instance of your app&#39;s <a href="/api/Brut/FrontEnd/Session.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Session</code></a> subclass for the current visitor&#39;s session.</td></tr><tr><td><code>request:</code></td><td>✅ Yes</td><td>The Rack request</td></tr><tr><td><code>response:</code></td><td>✅ Yes</td><td>The Rack response</td></tr><tr><td><code>env:</code></td><td>✅ Yes</td><td>The Rack env.</td></tr></tbody></table><h4 id="http-headers" tabindex="-1">HTTP Headers <a class="header-anchor" href="#http-headers" aria-label="Permalink to &quot;HTTP Headers&quot;">​</a></h4><p>Since any header can be sent with a request, Brut allows you to access them, including non-standard ones. Rack (which is based on CGI), provides access to all HTTP headers in the <code>env</code> by taking the header name, replacing dashes (&quot;-&quot;) with underscores (&quot;_&quot;), and prepending <code>http_</code> to the name, then uppercasing it. Thus, &quot;User-Agent&quot; becomes <code>HTTP_USER_AGENT</code>.</p><p>Because Ruby parameters and variables must start with a lower-case letter, Brut uses the lowercased version of the Rack/CGI variable. Thus, to receive the &quot;User-Agent&quot;, you would declare the keyword parameter <code>http_user_agent</code>.</p><p>Further, because headers come from the client and may not be under your control, the value that is actually injected depends on a few things:</p><ul><li>If your keyword arg is required, i.e. there is no default value: <ul><li>If the header was not provided, <code>nil</code> is injected.</li><li>If the header <em>was</em> provided, it&#39;s value is injected, even if it&#39;s the empty string.</li></ul></li><li>If your keyword arg is optional, i.e. it has a default value <ul><li>If the header was not provided, no value is injected, and your code will receive the default value.</li><li>If the header <em>was</em> provided, it&#39;s value is injected, even if it&#39;s the empty string.</li></ul></li></ul><h3 id="injecting-custom-data" tabindex="-1">Injecting Custom Data <a class="header-anchor" href="#injecting-custom-data" aria-label="Permalink to &quot;Injecting Custom Data&quot;">​</a></h3><p>The true power of keyword injection is that you can store your own data into the request context and have it injected into classes when Brut instantiates them.</p><p>The place to do this is in a <a href="/hooks.html">before hook</a>, since that happens before any page or handler is created, but <em>after</em> the <a href="/api/Brut/FrontEnd/RequestContext.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::RequestContext</code></a> is created (which is where all of this information is stored).</p><p>For example, here is how you might inject the currently logged-in account based on the session:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> AuthBeforeHook</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">RouteHook</span></span>
11
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> before</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">request_context:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">session:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
12
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> session.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">authenticated_account</span></span>
13
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> request_context[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:authenticated_account</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> session.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">authenticated_account</span></span>
14
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
15
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> continue</span></span>
16
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
17
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>Note that the value is only injected if it exists. It&#39;s important not to inject <code>nil</code> for values that don&#39;t exist.</p><p>With this in place, any page that requires an authenticated account can declare it:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> PreferencesPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> AppPage</span></span>
18
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> initialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">authenticated_account:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
19
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
20
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
21
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>If the request context has no value for <code>authenticated_account</code>, the page cannot be instantiated. Thus, the page&#39;s code can always rely on a non-<code>nil</code> value for <code>authenticated_account</code> (provided you don&#39;t inject <code>nil</code>).</p><div class="warning custom-block github-alert"><p class="custom-block-title">WARNING</p><p>Do not inject <code>nil</code> into the request context. Brut currently allows it, but may prevent it in a future update. <code>nil</code> is no good for nobody.</p></div><h3 id="when-values-aren-t-available" tabindex="-1">When Values Aren&#39;t Available <a class="header-anchor" href="#when-values-aren-t-available" aria-label="Permalink to &quot;When Values Aren&#39;t Available&quot;">​</a></h3><p>When a value is not available for injection, and the keyword doesn&#39;t provide a default, Brut will raise an error. This is because such a situation represents a design error.</p><p>The tables above document which values should always be available. You should never provide a default value for these, e.g. <code>session:</code> or <code>env:</code>. For values that are not always available, you should provide a default value unless you are sure there will be no routing to the page or handler without the value set.</p><p>This is most important for query string parameters. Since a user can easily manipulate these, if your page accepts, say, the parameter <code>use_detailed_view</code>, but that parameter isn&#39;t present, Brut will not be able to instantiate your page unless <code>use_detailed_view:</code> has a default value in the initializer&#39;s keyword arguments.</p><p>See <a href="/hooks.html">route hooks</a>.</p><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;">​</a></h2><p>Brut will not create your classes in a test. Instead, you must pass in the values you want. There are various helpers in <a href="/api/Brut/SpecSupport.html" target="_self" rel="noopener" data-no-router><code>Brut::SpecSupport</code></a> to create blank or empty versions of the special classes.</p><p>In particular, A basic <code>request_context</code> is setup per test and injected into the Thread local storage. This means that if your test should trigger a codepath that <em>does</em> cause Brut to use keyword injection, useful values will be injected.</p><p>For your tests, however, you should pass in directly what you need:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> = </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">WidgetsByIdPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">id:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> widget.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">session:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> empty_session)</span></span></code></pre></div><h2 id="recommended-practices" tabindex="-1">Recommended Practices <a class="header-anchor" href="#recommended-practices" aria-label="Permalink to &quot;Recommended Practices&quot;">​</a></h2><p>Consider a method like so:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> create_widget</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">name:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">organization:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> nil</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">quantity:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><p>Outside of Brut, the way to interpret this arguments is as follows:</p><ul><li><code>name</code> is required</li><li><code>organization</code> is optional</li><li><code>quantity</code> has a default value of 10 if not provided</li></ul><p>Any method or intializer that will be keyword-injected should be designed with this in mind. Thus, the following guidelines will be helpful in managing your app:</p><ul><li><strong>Do not provide default values when Brut documents the value is always available</strong><ul><li>If your page needs the session, it will always be there. Don&#39;t default <code>session:</code> to some other value (especially <code>nil</code>!)</li></ul></li><li><strong>Choose arguments based on the needs of the class:</strong><ul><li>If a value is optional, default it to either <code>nil</code> or a symbol that indicates what happens when the value is omitted</li><li>If an optional value has a default, use that (this should be rare for pages, handlers, components, and hooks)</li><li>Otherwise, do not provide a default for the keyword</li></ul></li><li><strong>Design for non-<code>nil</code> values instead of allowing <code>nil</code> and checking for it</strong><ul><li>If a page needs, say, the currently logged-in user, set that up as injectible with no default.</li><li>If a codepath creates that page without the logged-in user, you will get a very obvious error and can figure out how it happened. Your page&#39;s code doesn&#39;t need to figure out what to do with <code>nil</code></li></ul></li><li><strong>Do not inject <code>nil</code> into the request context.</strong> When your code requires a value for a keyword, you want to rely on that value being non-nil. Thus, avoid injecting <code>nil</code> into the request context. Brut will allow it as a sort-of escape hatch, but you should design your app to avoid it</li><li><strong>Be careful injecting global data.</strong> The request context instance is per request, but you could certainly put global data into it. For example, you may put an initialized API client into the request context as a convieniece. <strong>Be careful</strong> because your app is multi-threaded. Any object that is not scoped to the request must be thread-safe.</li></ul><h2 id="technical-notes" tabindex="-1">Technical Notes <a class="header-anchor" href="#technical-notes" aria-label="Permalink to &quot;Technical Notes&quot;">​</a></h2><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut&#39;s internals, the source code is always more correct.</p></div><p><em>Last Updated May 7, 2025</em></p><p>Keyword injection is currently implemented in a few places and not available via public API. It could be useful as an API and it will be exposed at some point. For now, it&#39;s only available for Brut-managed classes as documented here.</p>`,50)])])}const k=t(n,[["render",o]]);export{u as __pageData,k as default};
@@ -1 +0,0 @@
1
- import{_ as t,c as s,o as a,ag as i}from"./chunks/framework.C4nOkCZI.js";const u=JSON.parse('{"title":"Keyword Injection","description":"","frontmatter":{},"headers":[],"relativePath":"keyword-injection.md","filePath":"keyword-injection.md"}'),n={name:"keyword-injection.md"};function o(r,e,l,d,h,p){return a(),s("div",null,[...e[0]||(e[0]=[i("",50)])])}const k=t(n,[["render",o]]);export{u as __pageData,k as default};
@@ -1,68 +0,0 @@
1
- import{_ as i,c as a,o as t,ag as n}from"./chunks/framework.C4nOkCZI.js";const y=JSON.parse('{"title":"Layouts","description":"","frontmatter":{},"headers":[],"relativePath":"layouts.md","filePath":"layouts.md"}'),e={name:"layouts.md"};function l(h,s,p,k,r,d){return t(),a("div",null,[...s[0]||(s[0]=[n(`<h1 id="layouts" tabindex="-1">Layouts <a class="header-anchor" href="#layouts" aria-label="Permalink to &quot;Layouts&quot;">​</a></h1><p>Brut supports <em>layouts</em>, which are a way to centralizing common HTML amongst many different pages. Conceptually, they are the same as a Rails layout. Technically, they are a Phlex component designed to render a page from a <code>yield</code> block.</p><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to &quot;Overview&quot;">​</a></h2><p>Your app should include <code>app/src/front_end/layouts/default_layout.rb</code>. The name &quot;default&quot; isn&#39;t special, it&#39;s just what the <code>layout</code> method from <a href="/api/Brut/FrontEnd/Page.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Page</code></a> returns.</p><p>A layout is a Phlex component that&#39;s expected to have a single call to <code>yield</code> in its <code>view_template</code> method.</p><h3 id="default-layout-and-common-layout-needs" tabindex="-1">Default Layout and Common Layout Needs <a class="header-anchor" href="#default-layout-and-common-layout-needs" aria-label="Permalink to &quot;Default Layout and Common Layout Needs&quot;">​</a></h3><p>Here is the <code>DefaultLayout</code> provided to new Brut apps:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> DefaultLayout</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Layout</span></span>
2
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> include</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Components</span></span>
3
- <span class="line"></span>
4
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> initialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">page:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
5
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> @page </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> page</span></span>
6
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
7
- <span class="line"></span>
8
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> view_template</span></span>
9
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> doctype</span></span>
10
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> html</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">lang:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;en&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
11
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> head </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
12
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> meta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">charset:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;utf-8&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
13
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> meta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">content:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;width=device-width,initial-scale=1&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;viewport&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
14
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> meta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">content:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;website&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">property:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;og:type&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
15
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> link</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">rel:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;manifest&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">href:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;/static/manifest.json&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
16
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> link</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">rel:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;preload&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">as:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;style&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">href:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> asset_path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;/css/styles.css&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">))</span></span>
17
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> link</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">rel:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;stylesheet&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">href:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> asset_path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;/css/styles.css&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">))</span></span>
18
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">defer:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">src:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> asset_path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;/js/app.js&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">))</span></span>
19
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> title { app_name }</span></span>
20
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> PageIdentifier</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(page)</span></span>
21
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> I18nTranslations</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;cv.cs&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
22
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> I18nTranslations</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;cv.this_field&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
23
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Traceparent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">()</span></span>
24
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> render</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
25
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">RequestContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">inject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
26
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Components</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">LocaleDetection</span></span>
27
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> )</span></span>
28
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> )</span></span>
29
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
30
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> body </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
31
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> brut_tracing </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">url:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;/__brut/instrumentation&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">show_warnings:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
32
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> main </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">class:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> page.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">page_name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
33
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> yield</span></span>
34
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
35
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
36
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
37
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
38
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>You will likely want to customize what&#39;s in your layout, but a few components included by default are important for other features of Brut:</p><table tabindex="0"><thead><tr><th>Component</th><th>Purpose</th></tr></thead><tbody><tr><td><a href="/api/Brut/FrontEnd/Components/PageIdentifier.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Components::PageIdentifier</code></a></td><td>Creates a <code>&lt;meta&gt;</code> tag with the page&#39;s name in it, which is handy for managing your end-to-end tests.</td></tr><tr><td><a href="/api/Brut/FrontEnd/Components/I18nTranslations.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Components::I18nTranslations</code></a></td><td>Includes translatsion for common client-side constraint violations. These are used by <a href="/brut-js/api/ConstraintViolationMessages.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-cv-messages&gt;</code></a> and <code>,brut-cv&gt;</code>. See <a href="/forms.html">Forms</a>, <a href="/i18n.html">I18n</a>, and <a href="/javascript.html">JavaScript</a> for more details</td></tr><tr><td><a href="/api/Brut/FrontEnd/Components/Traceparent.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Components::Traceparent</code></a></td><td>Includes the OpenTelemetry <em>traceparent</em> on the page so that client-side telemetry is reported back to the server. See <a href="/brut-js/api/Tracing.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-tracing&gt;</code></a> and <a href="/instrumentation.html">observability</a></td></tr><tr><td><a href="/brut-js/api/Tracing.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-tracing&gt;</code></a> / <code>brut_tracing</code></td><td>Custom element that collects the client-side telemetry and sends it back to the server. See <a href="/instrumentation.html">observability</a></td></tr></tbody></table><h3 id="adding-logic-dynamic-behavior-to-layouts" tabindex="-1">Adding Logic/Dynamic Behavior to Layouts <a class="header-anchor" href="#adding-logic-dynamic-behavior-to-layouts" aria-label="Permalink to &quot;Adding Logic/Dynamic Behavior to Layouts&quot;">​</a></h3><p>Often, your pages will need to make slight tweaks to the layout that don&#39;t apply to all pages. For example, you may wish for a certain page to refresh on a schedule and you want to do that with a <a href="https://en.wikipedia.org/wiki/Meta_refresh" target="_blank" rel="noreferrer">meta refresh</a>, which must appear in the <code>&lt;head&gt;</code> of the page.</p><p>Unlike Rails, which uses named blocks to render optional or dynamic content, Brut allows you to use methods and normal Ruby-based flow logic. Since your layouts have access to the page they are laying out, you can use your pages&#39; APIs to do whatever it is you need.</p><p>Taking the meta refresh example, suppose your <code>AppPage</code> defines a method, <code>auto_refresh_seconds</code> that, if non-<code>nil</code> means your page should automatically reload itself after that many seconds. By default, you don&#39;t refresh, so it returns <code>nil</code>:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> AppPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">page</span></span>
39
- <span class="line"></span>
40
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
41
- <span class="line"></span>
42
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> auto_refresh_seconds</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> nil</span></span>
43
- <span class="line"></span>
44
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
45
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>Your layout can refrence this API, since it&#39;s just a method on a class:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> DefaultLayout</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Layout</span></span>
46
- <span class="line"></span>
47
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
48
- <span class="line"></span>
49
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> view_template</span></span>
50
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> doctype</span></span>
51
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> html</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">lang:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;en&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
52
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> head </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
53
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> page.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">auto_refresh_seconds</span></span>
54
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> meta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">http_equiv:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> safe</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;refresh&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">content:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> page.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">auto_refresh_seconds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
55
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
56
- <span class="line"></span>
57
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
58
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
59
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
60
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>Since your pages are a class hierarchy, you can override <code>auto_refresh_seconds</code> in any page, and that page will automatically refresh itself:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> DashboardPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> AppPage</span></span>
61
- <span class="line"></span>
62
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> auto_refresh_seconds</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 60</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 60</span></span>
63
- <span class="line"></span>
64
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
65
- <span class="line"></span>
66
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><h3 id="alternate-layouts" tabindex="-1">Alternate Layouts <a class="header-anchor" href="#alternate-layouts" aria-label="Permalink to &quot;Alternate Layouts&quot;">​</a></h3><p>If you used <code>mkbrut</code>, you should have access to a <code>BlankLayout</code> that is useful for allowing a page to respond to Ajax requests:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> SomePage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> AppPage</span></span>
67
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> layout</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;blank&quot;</span></span>
68
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>See <a href="/recipes/alternate-layouts.html">creating alternate layouts</a> for more information on creating alternate layouts based on your needs.</p><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;">​</a></h2><p>You generally don&#39;t test a layout, aside from end-to-end tests. If your layout needs complex logic, you are encouraged to extract that to a <a href="/components.html">component</a> and test that instead.</p><h2 id="recommended-practices" tabindex="-1">Recommended Practices <a class="header-anchor" href="#recommended-practices" aria-label="Permalink to &quot;Recommended Practices&quot;">​</a></h2><p>Layouts can use components, just keep in mind that any data a component needs must be passed to its initializer. Since the layout doesn&#39;t have access to the page, this implies that components used in your layout must either not require dynamic data or be <a href="/components.html#global-components">global components</a></p><h2 id="technical-notes" tabindex="-1">Technical Notes <a class="header-anchor" href="#technical-notes" aria-label="Permalink to &quot;Technical Notes&quot;">​</a></h2><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut&#39;s internals, the source code is always more correct.</p></div><p><em>Last Updated Sep 9, 2025</em></p><p>Layouts work due to the implementation of the method <code>view_template</code> in <a href="/api/Brut/FrontEnd/Page.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Page</code></a>. This is why a page class must provide <code>page_template</code> instead.</p>`,31)])])}const c=i(e,[["render",l]]);export{y as __pageData,c as default};
@@ -1 +0,0 @@
1
- import{_ as i,c as a,o as t,ag as n}from"./chunks/framework.C4nOkCZI.js";const y=JSON.parse('{"title":"Layouts","description":"","frontmatter":{},"headers":[],"relativePath":"layouts.md","filePath":"layouts.md"}'),e={name:"layouts.md"};function l(h,s,p,k,r,d){return t(),a("div",null,[...s[0]||(s[0]=[n("",31)])])}const c=i(e,[["render",l]]);export{y as __pageData,c as default};
@@ -1 +0,0 @@
1
- import{_ as t,c as o,o as r,ag as a}from"./chunks/framework.C4nOkCZI.js";const h=JSON.parse('{"title":"Language Server Protocol (LSP) Support","description":"","frontmatter":{},"headers":[],"relativePath":"lsp.md","filePath":"lsp.md"}'),s={name:"lsp.md"};function d(i,e,n,l,c,u){return r(),o("div",null,[...e[0]||(e[0]=[a('<h1 id="language-server-protocol-lsp-support" tabindex="-1">Language Server Protocol (LSP) Support <a class="header-anchor" href="#language-server-protocol-lsp-support" aria-label="Permalink to &quot;Language Server Protocol (LSP) Support&quot;">​</a></h1><p>Because Brut development happens inside Docker, but your editor likely runs on your computer, getting LSP servers running takes a few more steps.</p><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to &quot;Overview&quot;">​</a></h2><p>When you created your app with <code>mkbrut</code>, the following LSP-related modules are set up and/or installed:</p><ul><li>Shopify&#39;s Ruby LSP server (installed from <code>bin/setup</code>)</li><li>Microsoft&#39;s TypeScript/JavaScript and CSS LSP serfvers (specified in <code>package.json</code>, installed when <code>npm install</code> runs from <code>bin/setup</code>)</li></ul><p>In order to use them from your computer a few configurations are needed, some of which Brut has done, and some you will need to do.</p><table tabindex="0"><thead><tr><th>Configuration</th><th>Description</th><th>Brut Handled?</th></tr></thead><tbody><tr><td>Paths inside Docker Must Match Your Computer</td><td>When an LSP server communicates about a file, it does so with a path. That means that paths inside the Docker container must be the same as those on your computer. Brut achievecs this by using <code>${CWD}</code> inside <code>docker-compose.dx.yml</code></td><td>✅</td></tr><tr><td>Third party libraries must <em>also</em> be installed in a path that is the same in both places</td><td>When jumping to a definition, the LSP server will again use paths, which must match. Because Node modules are installed local to your app, they already work. Ruby Gems, however, are configured to be installed in <code>local-gems</code> in your app. Brut should&#39;ve added this to <code>.gitignore</code> and setup everything inside Docker to use it.</td><td>✅</td></tr><tr><td>Your editor must use <code>dx/exec</code> to execute LSP commands</td><td>Your editor will need to know that the LSP servers are running inside Docker. If your editor allows configuring the commands used to do this, you must prefix them with <code>dx/exec</code>. See <a href="https://naildrivin5.com/blog/2025/06/12/neovim-and-lsp-servers-working-with-docker-based-development.html" target="_blank" rel="noreferrer">my blog post</a> for details.</td><td>❌</td></tr><tr><td>Other languages or plugins to existing LSP servers</td><td>I haven&#39;t used these, so no idea how well they work.</td><td>❌</td></tr></tbody></table>',7)])])}const m=t(s,[["render",d]]);export{h as __pageData,m as default};
@@ -1 +0,0 @@
1
- import{_ as t,c as o,o as r,ag as a}from"./chunks/framework.C4nOkCZI.js";const h=JSON.parse('{"title":"Language Server Protocol (LSP) Support","description":"","frontmatter":{},"headers":[],"relativePath":"lsp.md","filePath":"lsp.md"}'),s={name:"lsp.md"};function d(i,e,n,l,c,u){return r(),o("div",null,[...e[0]||(e[0]=[a("",7)])])}const m=t(s,[["render",d]]);export{h as __pageData,m as default};
@@ -1,33 +0,0 @@
1
- import{_ as a,c as i,o as n,ag as t}from"./chunks/framework.C4nOkCZI.js";const E=JSON.parse('{"title":"Markdown Extension Examples","description":"","frontmatter":{},"headers":[],"relativePath":"markdown-examples.md","filePath":"markdown-examples.md"}'),e={name:"markdown-examples.md"};function l(p,s,h,k,r,d){return n(),i("div",null,[...s[0]||(s[0]=[t(`<h1 id="markdown-extension-examples" tabindex="-1">Markdown Extension Examples <a class="header-anchor" href="#markdown-extension-examples" aria-label="Permalink to &quot;Markdown Extension Examples&quot;">​</a></h1><p>This page demonstrates some of the built-in markdown extensions provided by VitePress.</p><h2 id="syntax-highlighting" tabindex="-1">Syntax Highlighting <a class="header-anchor" href="#syntax-highlighting" aria-label="Permalink to &quot;Syntax Highlighting&quot;">​</a></h2><p>VitePress provides Syntax Highlighting powered by <a href="https://github.com/shikijs/shiki" target="_blank" rel="noreferrer">Shiki</a>, with additional features like line-highlighting:</p><p><strong>Input</strong></p><div class="language-md vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">md</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">\`\`\`js{4}</span></span>
2
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
3
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> () {</span></span>
4
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
5
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> msg: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;Highlighted!&#39;</span></span>
6
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
7
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
8
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span>
9
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">\`\`\`</span></span></code></pre></div><p><strong>Output</strong></p><div class="language-js vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
10
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> () {</span></span>
11
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
12
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> msg: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;Highlighted!&#39;</span></span>
13
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
14
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
15
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre></div><h2 id="custom-containers" tabindex="-1">Custom Containers <a class="header-anchor" href="#custom-containers" aria-label="Permalink to &quot;Custom Containers&quot;">​</a></h2><p><strong>Input</strong></p><div class="language-md vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">md</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::: info</span></span>
16
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">This is an info box.</span></span>
17
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">:::</span></span>
18
- <span class="line"></span>
19
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::: tip</span></span>
20
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">This is a tip.</span></span>
21
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">:::</span></span>
22
- <span class="line"></span>
23
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::: warning</span></span>
24
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">This is a warning.</span></span>
25
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">:::</span></span>
26
- <span class="line"></span>
27
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::: danger</span></span>
28
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">This is a dangerous warning.</span></span>
29
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">:::</span></span>
30
- <span class="line"></span>
31
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::: details</span></span>
32
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">This is a details block.</span></span>
33
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">:::</span></span></code></pre></div><p><strong>Output</strong></p><div class="info custom-block"><p class="custom-block-title">INFO</p><p>This is an info box.</p></div><div class="tip custom-block"><p class="custom-block-title">TIP</p><p>This is a tip.</p></div><div class="warning custom-block"><p class="custom-block-title">WARNING</p><p>This is a warning.</p></div><div class="danger custom-block"><p class="custom-block-title">DANGER</p><p>This is a dangerous warning.</p></div><details class="details custom-block"><summary>Details</summary><p>This is a details block.</p></details><h2 id="more" tabindex="-1">More <a class="header-anchor" href="#more" aria-label="Permalink to &quot;More&quot;">​</a></h2><p>Check out the documentation for the <a href="https://vitepress.dev/guide/markdown" target="_blank" rel="noreferrer">full list of markdown extensions</a>.</p>`,19)])])}const c=a(e,[["render",l]]);export{E as __pageData,c as default};
@@ -1 +0,0 @@
1
- import{_ as a,c as i,o as n,ag as t}from"./chunks/framework.C4nOkCZI.js";const E=JSON.parse('{"title":"Markdown Extension Examples","description":"","frontmatter":{},"headers":[],"relativePath":"markdown-examples.md","filePath":"markdown-examples.md"}'),e={name:"markdown-examples.md"};function l(p,s,h,k,r,d){return n(),i("div",null,[...s[0]||(s[0]=[t("",19)])])}const c=a(e,[["render",l]]);export{E as __pageData,c as default};
@@ -1,20 +0,0 @@
1
- import{_ as a,c as i,o as e,ag as t}from"./chunks/framework.C4nOkCZI.js";const c=JSON.parse('{"title":"Middleware","description":"","frontmatter":{},"headers":[],"relativePath":"middleware.md","filePath":"middleware.md"}'),n={name:"middleware.md"};function l(h,s,p,d,r,o){return e(),i("div",null,[...s[0]||(s[0]=[t(`<h1 id="middleware" tabindex="-1">Middleware <a class="header-anchor" href="#middleware" aria-label="Permalink to &quot;Middleware&quot;">​</a></h1><p>Brut supports Rack Middleware.</p><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to &quot;Overview&quot;">​</a></h2><p>Similar to <a href="/hooks.html">route hooks</a>, Brut supports Rack Middleware, which is a lower-level way of modifying a request or changing behavior.</p><p>Middleware is recommended if what you want to do is not dependent on your application&#39;s code or classes and is relatively simple.</p><p>To use a middleware, create the class in <code>app/src/front_end/middleware/</code>. You are encouraged to extend <a href="/api/Brut/FrontEnd/Middleware.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Middleware</code></a>, however this is an empty class currently. It could grow to have helper methods you&#39;ll find useful.</p><p>The class itself should conform to Rack&#39;s specification, which is typically that it will be given the Rack &quot;app&quot; in the initializer, and then have a method <code>call</code> which will be given the Rack environment.</p><p>Here&#39;s a middleware that adds a tag to the environment for paths that are &quot;special&quot; to our app, which in this case means they start with <code>/special</code>:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> TagSpecialPathsMiddleware</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Middleware</span></span>
2
- <span class="line"></span>
3
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> initializer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(app)</span></span>
4
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> @app </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> app</span></span>
5
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
6
- <span class="line"></span>
7
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> call</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(env)</span></span>
8
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> env[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;PATH_INFO&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=~</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;"> /^</span><span style="--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold;">\\/</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">special</span><span style="--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold;">\\/</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">/</span></span>
9
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> env[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;app.special_path&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
10
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
11
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> @app.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">call</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(env)</span></span>
12
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
13
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>To use this middleware, call <code>use</code> with the class name as a string inside <code>App</code>:</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> App</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Framework</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">App</span></span>
14
- <span class="line"></span>
15
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
16
- <span class="line"></span>
17
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> use </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:TagSpecialPathsMiddleware</span></span>
18
- <span class="line"></span>
19
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
20
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>Don&#39;t use the actual class as this can create load order issues.</p><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;">​</a></h2><p>Like hooks, Rack middleware can be tested as a normal class. That said, you are encouraged to test the middleware as part of an end-to-end test if possible, since this will ensure it&#39;s configured properly in the context of your app.</p><h2 id="recommended-practices" tabindex="-1">Recommended Practices <a class="header-anchor" href="#recommended-practices" aria-label="Permalink to &quot;Recommended Practices&quot;">​</a></h2><p>Middleware should be used when its logic can be entirely based on the Rack environment passed-in. While your database models and other classes should be available, excessive use of your domain logic in a middleware can create a confusing situation.</p><h2 id="technical-notes" tabindex="-1">Technical Notes <a class="header-anchor" href="#technical-notes" aria-label="Permalink to &quot;Technical Notes&quot;">​</a></h2><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut&#39;s internals, the source code is always more correct.</p></div><p><em>Last Updated June 12, 2025</em></p><p>Route hooks and Middlewares do not share implementations, however they are similar in concept. These concepts may be unified in the future.</p><p><code>use</code> and the way Middleware behaves follows Sinatra&#39;s implementation as Brut is currently based on Sinatra. This may not always be the case, however as things change, we will do our best to ensure the semantics remain the same. Nevertheless, it&#39;s advisable to have end to end tests assert the behavior of your configured middleware and not just a unit test of the class itself.</p>`,21)])])}const g=a(n,[["render",l]]);export{c as __pageData,g as default};
@@ -1 +0,0 @@
1
- import{_ as a,c as i,o as e,ag as t}from"./chunks/framework.C4nOkCZI.js";const c=JSON.parse('{"title":"Middleware","description":"","frontmatter":{},"headers":[],"relativePath":"middleware.md","filePath":"middleware.md"}'),n={name:"middleware.md"};function l(h,s,p,d,r,o){return e(),i("div",null,[...s[0]||(s[0]=[t("",21)])])}const g=a(n,[["render",l]]);export{c as __pageData,g as default};
@@ -1 +0,0 @@
1
- import{_ as a,c as r,o as s,ag as t,j as o}from"./chunks/framework.C4nOkCZI.js";const i="/assets/OverviewMetro.DUS-5fUZ.png",g=JSON.parse('{"title":"Conceptual Overview","description":"","frontmatter":{},"headers":[],"relativePath":"overview.md","filePath":"overview.md"}'),n={name:"overview.md"};function l(d,e,c,h,u,p){return s(),r("div",null,[...e[0]||(e[0]=[t('<h1 id="conceptual-overview" tabindex="-1">Conceptual Overview <a class="header-anchor" href="#conceptual-overview" aria-label="Permalink to &quot;Conceptual Overview&quot;">​</a></h1><p>Brut is a way to build web apps that generate HTML, have JavaScript and CSS, and interact with a Database. It&#39;s built on Ruby standard libraries and community libraries like <a href="https://sequel.jeremyevans.net/" target="_blank" rel="noreferrer">Sequel</a> and <a href="https://phlex.fun" target="_blank" rel="noreferrer">Phlex</a>.</p><p>Brut&#39;s approach and design are built on three core values:</p><ul><li><strong>Leverage Standards</strong> - The web platform is great, and Brut wants you to use it.</li><li><strong>There&#39;s One Best Way To Do It</strong> - Flexibility leads to chaos.</li><li><strong>Simple over Easy</strong> - Verbose code that can be quickly understood beats impenetrable compact DSLs every day.</li></ul><p>Brut&#39;s abstractions tend to mirror concepts found in the domain of web sites. For example, a browser serves up a web page at a URL. In Brut, that&#39;s called a <em>page</em> and you&#39;d create a subclass of <a href="/api/Brut/FrontEnd/Page.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Page</code></a> to implement it.</p><p>Brut tries to avoid abstractions that simply translate existing standards into a more aestheticly pleasing form. You already need to know CSS, HTML, the Web Platform, and SQL, so there&#39;s little to gain by requiring you to learn a different way to use them.</p><h2 id="yes-you-can-build-a-blog-in-15-minutes" tabindex="-1">Yes, You Can Build a Blog in 15 Minutes <a class="header-anchor" href="#yes-you-can-build-a-blog-in-15-minutes" aria-label="Permalink to &quot;Yes, You Can Build a Blog in 15 Minutes&quot;">​</a></h2>',7),o("iframe",{title:"Make a Blog App in 15(ish) minutes With BrutRB - A New Ruby Web Framework",width:"560",height:"315",src:"https://video.hardlimit.com/videos/embed/ae7EMhwjDq9kSH5dqQ9swV",frameborder:"0",allowfullscreen:"",sandbox:"allow-same-origin allow-scripts allow-popups allow-forms"},null,-1),t('<p><a href="https://github.com/thirdtank/blog-demo" target="_blank" rel="noreferrer">Check out the source code</a> if you want to dive right in.</p><h2 id="basic-elements" tabindex="-1">Basic Elements <a class="header-anchor" href="#basic-elements" aria-label="Permalink to &quot;Basic Elements&quot;">​</a></h2><p>Brut organizes its code and behavior around four basic concepts:</p><ul><li><strong>Client</strong> or <em>Client Side</em> is the web browser (or HTTP client). This is where CSS is applied to HTML and where JavaScript is executed. HTTP requests are initiated here.</li><li><strong>Server</strong> or <em>Server Side</em> is where any code not in the browser runs. In Brut, this includes HTML generation, SQL queries, and everything in between.</li><li><strong>Front End</strong> is the code that deals with producing your user interface or HTTP API. A lot of this code runs on the <em>server side</em>, however it exists to provide a user interface of some sort.</li><li><strong>Back End</strong> is the code that deals with everything else, such as accessing a database, executing business logic, or managing background jobs.</li></ul><p><img src="'+i+'" alt="Architectural Overview"></p><ul><li><strong>Visitor</strong> is someone visiting your web site or app.</li><li><strong>Browser</strong> is, well, a web browser</li><li><a href="/pages.html"><strong>Pages</strong></a> generate web pages, which is what happens when a browser&#39;s UI navigates to a URL.</li><li><a href="/forms.html"><strong>Forms</strong></a> are submitted by the browser to the server. In Brut, a form describes the contents of a <code>&lt;form&gt;</code> as well as provides access to the submitted data.</li><li><a href="/handlers.html"><strong>Handlers</strong></a> receive non-GET HTTP requests from the browser, notably form submissions.</li><li><a href="/components.html"><strong>Components</strong></a> generate HTML fragments and are used to generate the HTML of a page or for re-use across pages.</li><li><a href="/javascript.html"><strong>JavaScript</strong></a> and <a href="/assets.html"><strong>Assets</strong></a> (including <a href="/css.html">CSS</a>) are bundled on the server and sent to the client.</li><li><a href="/business-logic.html"><strong>Domain Logic</strong></a> as where your business and domain logic lives and can be implemented however you like.</li><li><a href="/database-access.html"><strong>DB Models</strong></a> are objects that provide access to your database.</li><li><strong>Relational Database</strong> is your database, where data is stored.</li></ul><p>Brut doesn&#39;t prevent the addition of more pieces of infrastructure or code. You can add a Redis cache, a Sidekiq job backend, or integrate with third party APIs.</p><h2 id="brut-is-not-mvc" tabindex="-1">Brut is Not MVC <a class="header-anchor" href="#brut-is-not-mvc" aria-label="Permalink to &quot;Brut is Not MVC&quot;">​</a></h2><p>Brut is <em>not</em> an MVC framework, nor does it use the concept of <em>resources</em> as an abstraction. Although HTTP does include this concept, we find it&#39;s not as useful for managing web apps as it may seem.</p><p>We&#39;ve found that teams often struggle with mapping what everyone else calls pages to resources and HTTP verbs. We also find that the consonance of features, resources, actions, and database tables never materializes. We&#39;re basically not going to debate what the meaning of the <code>DELETE</code> verb on the <code>widgets</code> resource is actually supposed to mean.</p><h2 id="brut-is-hippocratic-licensed" tabindex="-1">Brut is Hippocratic Licensed <a class="header-anchor" href="#brut-is-hippocratic-licensed" aria-label="Permalink to &quot;Brut is Hippocratic Licensed&quot;">​</a></h2><p>It&#39;s important to me that Brut is used to make the world a better place. Please take a note of <a href="https://firstdonoharm.dev/version/3/0/cl-eco-media-my-tal-xuar.txt" target="_blank" rel="noreferrer">its license</a></p>',12)])])}const b=a(n,[["render",l]]);export{g as __pageData,b as default};
@@ -1 +0,0 @@
1
- import{_ as a,c as r,o as s,ag as t,j as o}from"./chunks/framework.C4nOkCZI.js";const i="/assets/OverviewMetro.DUS-5fUZ.png",g=JSON.parse('{"title":"Conceptual Overview","description":"","frontmatter":{},"headers":[],"relativePath":"overview.md","filePath":"overview.md"}'),n={name:"overview.md"};function l(d,e,c,h,u,p){return s(),r("div",null,[...e[0]||(e[0]=[t("",7),o("iframe",{title:"Make a Blog App in 15(ish) minutes With BrutRB - A New Ruby Web Framework",width:"560",height:"315",src:"https://video.hardlimit.com/videos/embed/ae7EMhwjDq9kSH5dqQ9swV",frameborder:"0",allowfullscreen:"",sandbox:"allow-same-origin allow-scripts allow-popups allow-forms"},null,-1),t("",12)])])}const b=a(n,[["render",l]]);export{g as __pageData,b as default};