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,78 +0,0 @@
1
- import{_ as i,c as a,o as e,ag as n}from"./chunks/framework.C4nOkCZI.js";const c=JSON.parse('{"title":"Configuration","description":"","frontmatter":{},"headers":[],"relativePath":"configuration.md","filePath":"configuration.md"}'),t={name:"configuration.md"};function l(h,s,p,r,o,k){return e(),a("div",null,[...s[0]||(s[0]=[n(`<h1 id="configuration" tabindex="-1">Configuration <a class="header-anchor" href="#configuration" aria-label="Permalink to &quot;Configuration&quot;">​</a></h1><p>Brut strives to avoid configuration and flexibility. Much of Brut&#39;s behavior is convention-based, however several aspects of Brut&#39;s behavior relate to literal values like file paths. Some of this set up is designed to be overridden.</p><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to &quot;Overview&quot;">​</a></h2><p>Any configured value, or value otherwise intended to be abstracted from its literal value, is available via <code>Brut.container</code>, which returns an instance of <a href="/api/Brut/Framework/Container.html" target="_self" rel="noopener" data-no-router><code>Brut::Framework::Container</code></a>. This class uses <code>method_missing</code> to provide direct access to configured values. It can also be used to store or override configured values.</p><p>An instance of <a href="/api/Brut/Framework/Config.html" target="_self" rel="noopener" data-no-router><code>Brut::Framework::Config</code></a> is used to set up all of Brut&#39;s initial values. This file is a good reference for determining the literal values of various configuration options.</p><h3 id="basics-of-configuration" tabindex="-1">Basics of Configuration <a class="header-anchor" href="#basics-of-configuration" aria-label="Permalink to &quot;Basics of Configuration&quot;">​</a></h3><p>The configuration system can be used to set literal values, or lazily-derived values. The method <code>store</code> is used for both purposes. Lazy values are managed by calling <code>store</code> with a block.</p><div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-Pbseg" id="tab-g0bPJyx" checked><label data-title="Storing Litral Values" for="tab-g0bPJyx">Storing Litral Values</label><input type="radio" name="group-Pbseg" id="tab-XUwJuip"><label data-title="Storing Lazy Values" for="tab-XUwJuip">Storing Lazy Values</label></div><div class="blocks"><div class="language-ruby vp-adaptive-theme active"><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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
2
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;database_url&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
3
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
4
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;URL to the primary database&quot;</span></span>
5
- <span class="line"><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;DATABASE_URL&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
6
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
7
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;database_url&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
8
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
9
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;URL to the primary database&quot;</span></span>
10
- <span class="line"><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:#6A737D;--shiki-dark:#6A737D;"> # Only evaluated when Brut.container.database_url is called</span></span>
12
- <span class="line"><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;DATABASE_URL&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
13
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div></div></div><p>The configuration system also enables dependencies between values. For example, the Sequel connection to the database requires the URL to the database. Since <code>database_url</code> is configured seperately, <code>sequel_db_handle</code> depends on its value. It declares this by declaring a block parameter with the same name as the configuration option, <code>database_url</code> in this case.</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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
14
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;sequel_db_handle&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
15
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
16
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;Handle to the database&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
17
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> |database_url|</span></span>
18
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">connect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(database_url)</span></span>
19
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>When <code>Brut.container.sequel_db_handle</code> is called, the block is examined. Brut sees that it has a parameter named <code>&quot;database_url&quot;</code>, and will then call <code>Brut.container.database_url</code> to fetch the value and pass it in.</p><p>This reduces repetition amongst the various configuration options.</p><h3 id="special-types-of-configuration" tabindex="-1">Special Types of Configuration <a class="header-anchor" href="#special-types-of-configuration" aria-label="Permalink to &quot;Special Types of Configuration&quot;">​</a></h3><p>Brut&#39;s configuration system enforces some rules, and may enforce more in the future. It&#39;s a strong desire that no Brut app even boot if the configuration is not usable.</p><p>Many of Brut&#39;s configuration options are paths. To avoid having to have a bunch of <code>.keep</code> files all over the plces, Brut allows storing an <em>ensured path</em>, which Brut will created when needed.</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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store_ensured_path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
20
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;images_src_dir&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
21
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;Path to where images are stored&quot;</span></span>
22
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> |front_end_src_dir|</span></span>
23
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> front_end_src_dir </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;images&quot;</span></span>
24
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>If your Brut app has no images at all, when <code>Brut.container.images_src_dir</code> is accessed, the path to where the images should go will be created.</p><p>Some paths <em>must</em> exist in advance. For those, <code>store_required_path</code> can be used. It throws an error if the path does not exist:</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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store_required_path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
25
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;pages_src_dir&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
26
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;Path to where page classes and templates are stored&quot;</span></span>
27
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> |front_end_src_dir|</span></span>
28
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> front_end_src_dir </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;pages&quot;</span></span>
29
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><h3 id="type-and-name-enforcement" tabindex="-1">Type and Name Enforcement <a class="header-anchor" href="#type-and-name-enforcement" aria-label="Permalink to &quot;Type and Name Enforcement&quot;">​</a></h3><p>You&#39;ll note that <code>store</code> accepts a class parameter. This is mostly used for documentation, with two exceptions:</p><ul><li>If the type is <code>Pathname</code> (which is what is used by <code>store_ensured_path</code> and <code>store_required_path</code>), the configuration parameter name <em>must</em> end in <code>_file</code> or <code>_dir</code>.</li><li>If the type is <code>&quot;boolean&quot;</code> or <code>:boolean</code>, the configuration parameter name <em>must</em> end in a question mark. In this case, the value itself is coerced into <code>true</code> or <code>false</code>.</li></ul><p>Brut may add more constraints or conversions over time.</p><h3 id="overridable-and-nilable-values" tabindex="-1">Overridable and <code>nil</code>able Values <a class="header-anchor" href="#overridable-and-nilable-values" aria-label="Permalink to &quot;Overridable and \`nil\`able Values&quot;">​</a></h3><p>By default, Brut configuration values cannot be overridden and they cannot be <code>nil</code>. When calling <code>store</code>, <code>allow_app_override: true</code> and <code>allow_nil: true</code>, can be passed to change this behavior.</p><p>In <a href="/flash-and-session.html">Flash and Session</a>, we discussed that you can set your own class for the flash. This is possible due to how Brut defines the configuration parameter <code>flash_class</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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
30
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;flash_class&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
31
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
32
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;Class to use to represent the Flash&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
33
- <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;">Flash</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
34
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> allow_app_override:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
35
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><p>Since <code>allow_app_override</code> is true, you can call <code>override</code> in your <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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">override</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;flash_class&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">AppFlash</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><p>Calling <code>override</code> for parameters where <code>allow_app_override</code> is not true results in an error. Further, calling <code>store</code> on a previously <code>store</code>-d parameter results in an error.</p><p>The idea is to make it extremely clear what values are being set and overridden, and to avoid setting values that don&#39;t exist or aren&#39;t relevant.</p><p>Some values can be <code>nil</code>. Generally, <code>nil</code> is a pain and will cause you great hardship. On occasion, it&#39;s needed. For example, <a href="/database-schema.html#external-ids">external IDs</a> only work if the app provides an app-wide prefix.</p><p>Here is how Brut sets this up by default:</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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
36
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;external_id_prefix&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
37
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
38
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;String to use as a prefix for external ids in tables using the external_id feature. Nil means the feature is disabled&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
39
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> nil</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
40
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> allow_app_override:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
41
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> allow_nil:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
42
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><p>Brut defaults this to <code>nil</code>, meaning there is no external ID prefix to be used. Apps can opt into this behavior by overriding the value:</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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">override</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;external_id_prefix&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;cc&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><p>Conversly, apps can opt <em>out</em> of <a href="/security.html">Content Security Policy Reporting</a>. By default, Brut sets up its own reporting hook:</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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
43
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;csp_reporting_class&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
44
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
45
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;Route Hook to use for setting the Content-Security-Policy-Report-Only header&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
46
- <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;">RouteHooks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">CSPNoInlineStylesOrScripts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">ReportOnly</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
47
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> allow_app_override:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
48
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> allow_nil:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
49
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><p>If you don&#39;t want this, you can override it and set it to <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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">override</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;csp_reporting_class&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">nil</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><p><code>allow_nil</code> is a good indicator that Brut can handle a <code>nil</code> value for that configuration parameter—it wouldn&#39;t allow it if couldn&#39;t.</p><h3 id="setting-your-own-configuration-values" tabindex="-1">Setting Your Own Configuration Values <a class="header-anchor" href="#setting-your-own-configuration-values" aria-label="Permalink to &quot;Setting Your Own Configuration Values&quot;">​</a></h3><p>In your <code>App</code> class, you can call <code>store</code> as needed to setup configuration values relevant to your app. For example, if you are using SendGrid to send emails, you may want to set the client up during startup:</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;"># app/src/app.rb</span></span>
50
- <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>
51
- <span class="line"></span>
52
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
53
- <span class="line"></span>
54
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> initialize</span></span>
55
- <span class="line"></span>
56
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
57
- <span class="line"></span>
58
- <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:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
59
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;send_grid_client&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
60
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> SendGrid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">API</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
61
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;SendGrid API Client&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
62
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> SendGrid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">API</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;">api_key:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> ENV</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;SENDGRID_API_KEY&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">])</span></span>
63
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> )</span></span>
64
- <span class="line"></span>
65
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # ...</span></span>
66
- <span class="line"></span>
67
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
68
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>Then, elsewhere in your app, you can call <code>Brut.container.send_grid_client</code> to access this object. Because <code>allow_nil</code> is not specified (and therefore false), your app can confidently rely on a value being returned.</p><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;">​</a></h2><p>Do not test configuration. Ideally, the configuration values for production are the same as for dev and test. If you really cannot use the production value for something in dev or test, you can have your value depend on <code>project_env</code>, which is a <a href="/api/Brut/Framework/ProjectEnvironment.html" target="_self" rel="noopener" data-no-router><code>Brut::Framework::ProjectEnvironment</code></a>:</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:#005CC5;--shiki-dark:#79B8FF;">Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">store</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
69
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;send_grid_client&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
70
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> SendGrid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">API</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
71
- <span class="line highlighted"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;SendGrid API Client&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> |project_env|</span></span>
72
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> project_env.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">testing?</span></span>
73
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> FakeSendGrid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">new</span></span>
74
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> else</span></span>
75
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> SendGrid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">API</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;">api_key:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> ENV</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;SENDGRID_API_KEY&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">])</span></span>
76
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
77
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
78
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</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>Try to avoid project environment-specific behavior. The less of that you have, the more confidence you will have that your app will boot in production.</p><p>In genreal, configure custom parameters only when you want them to be a check on app startup. For example, we don&#39;t want our app to even start if <code>SENDGRID_API_KEY</code> is not in the environment. If we defered instantiating <code>send_grid_client</code> until the first time we tried to send email, we wouldn&#39;t notice things were broken.</p><p>Conversely, do not place every object you could ever imagine into the container. <a href="/api/Brut/Framework/Container.html" target="_self" rel="noopener" data-no-router><code>Brut::Framework::Container</code></a> is not intended as a fully armed and operational dependency injection container. Perhaps someday, but not today.</p><p>Lastly, do not <code>store</code> or <code>override</code> outside of your <code>App</code> class&#39; initializer. It will be confusing and <code>override</code> may not work as expected.</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 May 7, 2025</em></p><p><a href="/api/Brut/Framework/Config.html" target="_self" rel="noopener" data-no-router><code>Brut::Framework::Config</code></a> is created and executed in the initializer of <a href="/api/Brut/Framework/MCP.html" target="_self" rel="noopener" data-no-router><code>Brut::Framework::MCP</code></a>, which happens before your <code>App</code>&#39;s initializer is run. This also happens when CLI apps run. Brut tries very hard not to make network connections inside <code>Brut::Framework::Config#configure!</code> for just this reason.</p><p><a href="/api/Brut/Framework/Container.html" target="_self" rel="noopener" data-no-router><code>Brut::Framework::Container</code></a> does not really check for circular dependeices, so please try to avoid making them. If this happens, there will be a stack overflow.</p>`,58)])])}const E=i(t,[["render",l]]);export{c as __pageData,E as default};
@@ -1 +0,0 @@
1
- import{_ as i,c as a,o as e,ag as n}from"./chunks/framework.C4nOkCZI.js";const c=JSON.parse('{"title":"Configuration","description":"","frontmatter":{},"headers":[],"relativePath":"configuration.md","filePath":"configuration.md"}'),t={name:"configuration.md"};function l(h,s,p,r,o,k){return e(),a("div",null,[...s[0]||(s[0]=[n("",58)])])}const E=i(t,[["render",l]]);export{c as __pageData,E as default};
@@ -1,21 +0,0 @@
1
- import{_ as i,c as a,o as e,ag as t}from"./chunks/framework.C4nOkCZI.js";const u=JSON.parse('{"title":"CSS","description":"","frontmatter":{},"headers":[],"relativePath":"css.md","filePath":"css.md"}'),n={name:"css.md"};function o(l,s,p,r,d,h){return e(),a("div",null,[...s[0]||(s[0]=[t(`<h1 id="css" tabindex="-1">CSS <a class="header-anchor" href="#css" aria-label="Permalink to &quot;CSS&quot;">​</a></h1><p>Brut provides basic bundling using <a href="https://esbuild.github.io/" target="_blank" rel="noreferrer">esbuild</a>. You can organize your CSS across multiple files, and you can bring in third party CSS libraries, as long as they are available from NPM (though you can always just download <code>.css</code> files manually)</p><h2 id="managing-your-app-s-css" tabindex="-1">Managing Your App&#39;s CSS <a class="header-anchor" href="#managing-your-app-s-css" aria-label="Permalink to &quot;Managing Your App&#39;s CSS&quot;">​</a></h2><p>All your app&#39;s CSS lives in <code>app/src/front_end/css</code>, or in modules you bring in via <code>package.json</code>. Brut will <em>bundle</em> all of that up into a single <code>.css</code> file that is served up with your app. Brut does this by using esbuild, a stable and standardized tool for bundling CSS.</p><p>The way esbuild works is to be given an <em>entry point</em> that requires, or transitively requires, all of your CSS by using the standard <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@import" target="_blank" rel="noreferrer"><code>@import</code> directive</a>. <code>app/src/front_end/css/index.css</code> is the entry point for your app.</p><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>The reason to bundle CSS instead of allowing browsers to manage the <code>@import</code> directives is to create a single download that is hashed for use with CDNs. This simplifies deployment and ensures all your CSS is available everywhere. The trade-off is that you cannot easily fine-tune each page&#39;s CSS.</p></div><p>Let&#39;s see an example. Let&#39;s say you are using the &quot;foobar-css&quot; CSS framework, and have placed some of your CSS in <code>app/src/front_end/css/pages/HomePage.css</code>.</p><p>First, <code>package.json</code> (in your app&#39;s root) would include <code>&quot;foobar-css&quot;</code>:</p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</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;">{</span></span>
2
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;name&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;your-app&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
3
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;type&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;module&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
4
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;license&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;UNLICENSED&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
5
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;dependencies&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
6
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;foobar-css&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;^0.0.11&quot;</span></span>
7
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> },</span></span>
8
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;devDependencies&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: {</span></span>
9
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;chokidar-cli&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;^3.0.0&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
10
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;esbuild&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;^0.20.2&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
11
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;jsdom&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;^25.0.1&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
12
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;mocha&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;^10.7.3&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
13
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> &quot;playwright&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;^1.50.1&quot;</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><p>Next, <code>app/src/front_end/css/index.css</code> would import both <code>&quot;foobar-css&quot;</code> and <code>&quot;pages/HomePage.css&quot;</code>.</p><div class="language-css vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">css</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;">@import</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;foobar-css/everything.css&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
16
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">@import</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;pages/HomePage.css&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span></code></pre></div><p>In the browser <code>@import</code> accepts any URL and will resolve it when the file is loaded. In Brut, when bundled using esbuild, these strings are relative paths to files in either <code>node_modules/</code> or <code>app/src/front_end/css/</code>. Unlike JavaScript imports, you don&#39;t need <code>&quot;./&quot;</code> to differentiate the two. Also unlike JavaScript imports, these must be the first lines of your <code>index.css</code> file or they will be ignored. All imports must come before any CSS.</p><h2 id="importing-third-party-css" tabindex="-1">Importing Third Party CSS <a class="header-anchor" href="#importing-third-party-css" aria-label="Permalink to &quot;Importing Third Party CSS&quot;">​</a></h2><p>Because there are so many ways to use NPM modules, it&#39;s not common to find reliable documentation on how to use a CSS library you bring in via NPM/<code>package.json</code>.</p><p>The key to figuring out what to use with <code>@import</code> is that the value is relative to <code>node_modules</code> and should an actual filename, including path and extension. You can figure this out by looking inside <code>node_modules</code> after you&#39;ve done <code>bin/setup</code> (or <code>npm install</code>).</p><p>Suppose &quot;foobar-css&quot; has this directory structure:</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>node_modules/</span></span>
17
- <span class="line"><span> foobar-css/</span></span>
18
- <span class="line"><span> css/</span></span>
19
- <span class="line"><span> foobar-all.css</span></span>
20
- <span class="line"><span> foobar-thin.css</span></span></code></pre></div><p>To use <code>foobar-thin.css</code>, you&#39;d write this <code>@import</code> directive:</p><div class="language-css vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">css</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;">@import</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;foobar-css/css/foobar-thin.css&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span></code></pre></div><h2 id="using-brut-css" tabindex="-1">Using Brut-CSS <a class="header-anchor" href="#using-brut-css" aria-label="Permalink to &quot;Using Brut-CSS&quot;">​</a></h2><p>By default, Brut includes a lightweight functional CSS library called <a href="/brut-css/index.html">BrutCSS</a>. It provides a basic design system and single-purpose classes to allow you to quickly prototype or build UIs. It is similar to TailwindCSS but far far smaller and simpler (and less powered).</p><p>It is included so you have something to start with. You can use it by using its various classes like <code>bg-green-300</code> and <code>m-4</code>, or you can use its provided custom properties like <code>var(--green-300)</code> and <code>var(--sp-4)</code>.</p><p>To remove it, remove this line from <code>index.css</code></p><div class="language-css vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">css</span><pre class="shiki shiki-themes github-light github-dark has-diff vp-code" tabindex="0"><code><span class="line diff remove"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">@import</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;brut-css/brut.css&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">; </span></span>
21
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">@import</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;your/css/here.css&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span></code></pre></div><h2 id="using-tailwindcss" tabindex="-1">Using TailwindCSS <a class="header-anchor" href="#using-tailwindcss" aria-label="Permalink to &quot;Using TailwindCSS&quot;">​</a></h2><p>TBD</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 May 8, 2025</em></p><p>Currently, Brut only supports a single entry point and bundle. This could be easily made more flexible if there is a desire to finely tweak the CSS loaded on specific pages.</p><p>Brut also does not expose any esbuild configuration. This could be provided in the future, but for now, it is hard-coded.</p>`,31)])])}const k=i(n,[["render",o]]);export{u as __pageData,k as default};
@@ -1 +0,0 @@
1
- import{_ as i,c as a,o as e,ag as t}from"./chunks/framework.C4nOkCZI.js";const u=JSON.parse('{"title":"CSS","description":"","frontmatter":{},"headers":[],"relativePath":"css.md","filePath":"css.md"}'),n={name:"css.md"};function o(l,s,p,r,d,h){return e(),a("div",null,[...s[0]||(s[0]=[t("",31)])])}const k=i(n,[["render",o]]);export{u as __pageData,k as default};
@@ -1,69 +0,0 @@
1
- import{_ as i,c as a,o as t,ag as n}from"./chunks/framework.C4nOkCZI.js";const o=JSON.parse('{"title":"Testing Custom Elements","description":"","frontmatter":{},"headers":[],"relativePath":"custom-element-tests.md","filePath":"custom-element-tests.md"}'),e={name:"custom-element-tests.md"};function h(l,s,p,k,E,r){return t(),a("div",null,[...s[0]||(s[0]=[n(`<h1 id="testing-custom-elements" tabindex="-1">Testing Custom Elements <a class="header-anchor" href="#testing-custom-elements" aria-label="Permalink to &quot;Testing Custom Elements&quot;">​</a></h1><p>While simple custom elements can be tested as part of an <a href="/end-to-end-tests.html">end-to-end test</a>, more complex custom elements can benefit from a unit test. Spoiler: this is not going to be pleasant.</p><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to &quot;Overview&quot;">​</a></h2><p>BrutJS provides a testing module that uses JSDom to allow you to test your custom elements. There are downsides to JSDom, but it&#39;s the simplest way to achieve a reasonably-useful unit test.</p><p>You can use <code>bin/scaffold</code> to create a test, which will create a <code>.spec.js</code> file in <code>specs/front_end/js/</code>. Suppose we the custom element <code>MyElement</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>&gt; bin/scaffold custom_element_test app/src/front_end/js/MyElement.js</span></span></code></pre></div><p>This creates <code>specs/front_end/js/MyElement.spec.js</code>:</p><div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</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;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { withHTML } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;brut-js/testing/index.js&quot;</span></span>
2
- <span class="line"></span>
3
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">describe</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;&lt;some-element&gt;&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
4
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> withHTML</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">\`</span></span>
5
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;my-element&gt;</span></span>
6
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;/my-element&gt;</span></span>
7
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> \`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;description here&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">document</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">window</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">assert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
8
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> assert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;test goes here&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
9
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> })</span></span>
10
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">})</span></span></code></pre></div><p><code>withHTML</code> creates a JSDom-based document with the given HTML. This HTML is in effect inside the test. Mock versions of <code>document</code> and <code>window</code> are passed to the test, and any other functions you need can be as well, such as <code>assert</code>.</p><p>The idea is that you use the browser APIs to examine the DOM and assert the behavior of the custom element (as opposed to interacting with the custom element&#39;s class).</p><p>Suppose that <code>my-element</code> transform text inside it based on the <code>transform</code> attribute. By default, it&#39;s <code>lower</code> (which will lower-case the text), but can be set to <code>upper</code> to upper case the text inside.</p><p>This means you&#39;ll need three tests, each with a different DOM:</p><div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</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;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { withHTML } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;brut-js/testing/index.js&quot;</span></span>
11
- <span class="line"></span>
12
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">describe</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;&lt;some-element&gt;&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
13
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> withHTML</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">\`</span></span>
14
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;my-element&gt;</span></span>
15
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> Some Text</span></span>
16
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;/my-element&gt;</span></span>
17
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> \`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;lower-cases by default&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">document</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">window</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">assert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
18
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // TBD</span></span>
19
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> })</span></span>
20
- <span class="line"></span>
21
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> withHTML</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">\`</span></span>
22
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;my-element transform=&quot;lower&quot;&gt;</span></span>
23
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> Some Text</span></span>
24
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;/my-element&gt;</span></span>
25
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> \`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;lower-cases explicitly&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">document</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">window</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">assert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
26
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // TBD</span></span>
27
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> })</span></span>
28
- <span class="line"></span>
29
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> withHTML</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">\`</span></span>
30
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;my-element transform=&quot;upper&quot;&gt;</span></span>
31
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> Some Text</span></span>
32
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;/my-element&gt;</span></span>
33
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> \`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;upper-cases explicitly&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">document</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">window</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">assert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
34
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // TBD</span></span>
35
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> })</span></span>
36
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">})</span></span></code></pre></div><p>When the function you give to <code>test</code> is executed, the DOM will have been setup, so you can rely on your custom elements <code>connectedCallback</code> having been called. Assuming the text transformation for <code>my-element</code> occurs in <code>connectedCallback</code>, here is how you&#39;d test all three cases:</p><div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</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;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { withHTML } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;brut-js/testing/index.js&quot;</span></span>
37
- <span class="line"></span>
38
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">describe</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;&lt;some-element&gt;&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
39
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> withHTML</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">\`</span></span>
40
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;my-element&gt;</span></span>
41
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> Some Text</span></span>
42
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;/my-element&gt;</span></span>
43
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> \`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;lower-cases by default&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">document</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">window</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">assert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
44
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> element</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> document.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">querySelector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;my-element&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
45
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> assert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">equal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(element.textContent.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">trim</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(),</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;some text&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
46
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> })</span></span>
47
- <span class="line"></span>
48
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> withHTML</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">\`</span></span>
49
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;my-element transform=&quot;lower&quot;&gt;</span></span>
50
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> Some Text</span></span>
51
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;/my-element&gt;</span></span>
52
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> \`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;lower-cases explicitly&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">document</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">window</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">assert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
53
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> element</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> document.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">querySelector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;my-element&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
54
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> assert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">equal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(element.textContent.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">trim</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(),</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;some text&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
55
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> })</span></span>
56
- <span class="line"></span>
57
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> withHTML</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">\`</span></span>
58
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;my-element transform=&quot;upper&quot;&gt;</span></span>
59
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> Some Text</span></span>
60
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &lt;/my-element&gt;</span></span>
61
- <span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> \`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;upper-cases explicitly&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, ({</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">document</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">window</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">assert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=&gt;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {</span></span>
62
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> element</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> document.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">querySelector</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;my-element&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
63
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> assert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">equal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(element.textContent.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">trim</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(),</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;SOME TEXT&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
64
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> })</span></span>
65
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">})</span></span></code></pre></div><p>You&#39;ll notice almost all of this uses the browser APIs you (should) know and (hopefully) love.</p><p>You can manipulate the DOM inside a test as well, and it should behave as if you are doing it in a browser. Note that many browser APIs are synchronous, so you don&#39;t have to add <code>await</code> before every single line of code.</p><p>Note that all of these test run under NodeJS, which is different from a browser. This means that code like <code>new InputEvent()</code> will succeed in returning an <code>InputEvent</code>, but said object is in no way the <code>InputEvent</code> you&#39;d use in a browser. You must use <code>window.</code>:</p><div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</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;">// does not work, but doesn&#39;t raise an error either</span></span>
66
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">input.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">dispatchEvent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> InputEvent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;input&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {})) </span></span>
67
- <span class="line"></span>
68
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// works</span></span>
69
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">input.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">dispatchEvent</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;"> window.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">InputEvent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;input&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, {}))</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>The custom element test library is <em>very</em> basic. Testing asychronous things like <code>fetch</code> is extremely difficult. Your best bet is to use these tests for edge cases and error conditions.</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 13, 2025</em></p><p>I will be honest with you, this part of Brut needs a lot of work and thinking-through. It&#39;s way to DSL-tasitc for my tastes, but it does work for some needs. JSDom is not ideal and requires a lot of hoops when using events or anything browsers support that it does not.</p><p>This is highly likely to change. My current thinking on addressing the need is to run the tests in a real browser and to make the test setup and code more like what you&#39;d actually write when using these elements.</p>`,26)])])}const g=i(e,[["render",h]]);export{o as __pageData,g as default};
@@ -1 +0,0 @@
1
- import{_ as i,c as a,o as t,ag as n}from"./chunks/framework.C4nOkCZI.js";const o=JSON.parse('{"title":"Testing Custom Elements","description":"","frontmatter":{},"headers":[],"relativePath":"custom-element-tests.md","filePath":"custom-element-tests.md"}'),e={name:"custom-element-tests.md"};function h(l,s,p,k,E,r){return t(),a("div",null,[...s[0]||(s[0]=[n("",26)])])}const g=i(e,[["render",h]]);export{o as __pageData,g as default};
@@ -1,63 +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":"Database Access / Data Models","description":"","frontmatter":{},"headers":[],"relativePath":"database-access.md","filePath":"database-access.md"}'),n={name:"database-access.md"};function l(h,s,p,o,d,k){return e(),i("div",null,[...s[0]||(s[0]=[t(`<h1 id="database-access-data-models" tabindex="-1">Database Access / Data Models <a class="header-anchor" href="#database-access-data-models" aria-label="Permalink to &quot;Database Access / Data Models&quot;">​</a></h1><p>Brut provides access to the database via the <a href="https://sequel.jeremyevans.net/" target="_blank" rel="noreferrer">Sequel library</a>. Sequel is fully featured and provides a lot of ways of interacting with and managing your database. Brut includes several plugins and extensions to provide opinionated default behavior or additional features.</p><p>One thing to keep in mind is that Brut refers to your database layer as <em>database models</em> (notably not the un-qualified &quot;models&quot;). Brut treats this layer as a <em>model</em> of your database, not a model of your <em>domain</em> (though you are free to conflate the two).</p><p>This section details how to access data in your database.</p><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>Brut currently only supports Postgres. Sequel supports many database systems, however Brut&#39;s extensions are currently geared toward Postgres only.</p></div><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to &quot;Overview&quot;">​</a></h2><p>Accessing your database in Brut uses Sequel&#39;s <code>Sequel::Model</code>. A base class called <code>AppDataModel</code> exists in your app from which all other data models extend:</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;"># app/src/back_end/data_models/app_data_model.rb</span></span>
2
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">AppDataModel</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Class</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;">Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
3
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> AppDataModel</span></span>
4
- <span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> # You can insert your own shared methods here</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:#6A737D;--shiki-dark:#6A737D;"># app/src/back_end/data_models/db/account.rb</span></span>
8
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Account</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> AppDataModel</span></span>
9
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>All data models are in the <code>DB</code> namespace. This clearly identifies a model as a model of your database and not your domain.</p><p>Inside a data model, you can use all of Sequel&#39;s API. In particular, you will want to use its API for <a href="https://sequel.jeremyevans.net/rdoc/files/doc/association_basics_rdoc.html" target="_blank" rel="noreferrer">associations</a> so that you can create relationships between models.</p><p>In your business logic or front-end code, you can access your data using these models:</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;">account</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> = </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Account</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">find</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">email:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> form.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span></code></pre></div><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Sequel&#39;s <code>Sequel::Model</code> is different from Active Record, especially when it comes to associations. <code>account.organizations</code> would return an <code>Array</code> of <code>DB::Organization</code> records, all fetched from the database. <code>account.organizations_dataset</code> would return a active-relation style object to allow stacking quieries. <strong>Please</strong> familiarize yourself with Sequel&#39;s API.</p></div><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;">​</a></h2><p>Testing, as it applies to data models, is made up of two parts: managing test <em>data</em> and testing the models themselves.</p><h3 id="test-data-is-managed-with-factorybot" tabindex="-1">Test Data is Managed with FactoryBot <a class="header-anchor" href="#test-data-is-managed-with-factorybot" aria-label="Permalink to &quot;Test Data is Managed with FactoryBot&quot;">​</a></h3><p>Brut apps come with <a href="https://github.com/thoughtbot/factory_bot" target="_blank" rel="noreferrer">FactoryBot</a> installed, and this is how you should create test (and seed) data.</p><p>Factories for data models live in <code>specs/factories/db</code>. Because data models are in the <code>DB</code> namespace, you will need to explicitly state the <code>class:</code> in the factory, but otherwise, you can use FactoryBot in a conventional way. <a href="https://github.com/faker-ruby/faker" target="_blank" rel="noreferrer">Faker</a> is also installed to allow you to create realistic and randomized data.</p><p>Here is a factory for our hypothetical account:</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;"># specs/factories/db/account.factory.rb</span></span>
10
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FactoryBot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">define</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
11
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> factory </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:account</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">class:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;DB::Account&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;"> email { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Faker</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Internet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">unique</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
13
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> organization</span></span>
14
- <span class="line"></span>
15
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> trait </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:inactive</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
16
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> deactivated_at { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Time</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">now</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }</span></span>
17
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
18
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
19
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>The <code>spec_support.rb</code> file generated when you created your Brut app should ensure that <code>FactoryBot::Syntax::Methods</code> is included in all specs, meaning you can do <code>create(:account)</code> to create an instance of <code>DB::Account</code>.</p><p>See <a href="/unit-tests.html">Unit Tests</a> for more details on testing and Factory Bot setup.</p><h3 id="testing-your-data-models" tabindex="-1">Testing Your Data Models <a class="header-anchor" href="#testing-your-data-models" aria-label="Permalink to &quot;Testing Your Data Models&quot;">​</a></h3><p>In general, you don&#39;t want to test the configuration in your data models. For example, testing that <code>account.organization = organization</code> works is largely pointless, since this is provided by Sequel.</p><p>That said, if you have complex or unusual database constraints, having a test for them can be valuable.</p><p>Suppose our <code>DB::Account</code> has the following check constraint that requires an email end with <code>@example.com</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:#005CC5;--shiki-dark:#79B8FF;">Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">migration</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
20
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> up </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span></span>
21
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> create_table </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:accounts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
22
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> comment:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;People or systems who can access this system&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
23
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> external_id:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
24
- <span class="line"></span>
25
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">unique:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
26
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> foreign_key </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:organization_id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:organizations</span></span>
27
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> column </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:deactivated_at</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:timestamptz</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">null:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span></span>
28
- <span class="line"></span>
29
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> key [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:organization_id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
30
- <span class="line"></span>
31
- <span class="line highlighted"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> constraint</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
32
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> :email_must_be_domain</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
33
- <span class="line highlighted"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;email ~* &#39;@example.com$&#39;&quot;</span></span>
34
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> )</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></code></pre></div><p>To test this, you would try to write invalid data into the database an ensure the expected exception is raised:</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;"># specs/back_end/data_models/db/account.spec.rb</span></span>
38
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">require</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;spec_helper&quot;</span></span>
39
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">RSpec</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">describe</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Account</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
40
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> describe </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;email&quot;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
41
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> it </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;must end in @example.com&quot;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
42
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> expect {</span></span>
43
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Account</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
44
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> email:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;pat@example.net&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
45
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> organization:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:organization</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
46
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> )</span></span>
47
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">to</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> raise_error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Sequel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">CheckConstraintViolation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
48
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
49
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
50
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>If you don&#39;t want to be overly coupled to Sequel&#39;s exceptions, you can also assert on the message Postgres will produce, which would include the name of the violated constraint:</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;"># specs/back_end/data_models/db/account.spec.rb</span></span>
51
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">require</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;spec_helper&quot;</span></span>
52
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">RSpec</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">describe</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Account</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
53
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> describe </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;email&quot;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
54
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> it </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;must end in @example.com&quot;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> do</span></span>
55
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> expect {</span></span>
56
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Account</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
57
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> email:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &quot;pat@example.net&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
58
- <span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> organization:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:organization</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
59
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> )</span></span>
60
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">to</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> raise_error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">/email_must_be_domain/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span></span>
61
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
62
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
63
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</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><h3 id="do-not-put-business-logic-on-your-database-models" tabindex="-1">Do Not Put Business Logic On Your Database Models <a class="header-anchor" href="#do-not-put-business-logic-on-your-database-models" aria-label="Permalink to &quot;Do Not Put Business Logic On Your Database Models&quot;">​</a></h3><p>There&#39;s no reason to, or benefit to doing so. What you&#39;ll find is that any app of even moderate complexity will not have a strict mapping from page to business concept to database table. Rather, these things will all differ greatly, and each serves a different purpose.</p><p>The job of your data models—and the tables they provide access to—is to store reliable and unambiguous data. Their job is to ensure there is no bad data such that when you ask the database a question, you get a reliable and correct answer.</p><p>Your views and business logic do not have this exact same job.</p><p>As such, your models should only contain:</p><ul><li>configuration to allow navigating the database.</li><li>methods to manage type conversions between your types and the strings or numbers required in the database</li><li>methods to query the data based on data definitions (not business logic).</li></ul><p>Business logic and data models <em>do</em> overlap at times, so there is some judgement in maintaining a clear separation of concerns. One way to manage this is to always put all logic elsewhere until you see a pattern of re-use that leads you to extract that logic to a data model.</p><h3 id="do-not-use-validations-on-models-unless-there-is-no-other-choice" tabindex="-1">Do Not Use Validations on Models Unless There is No Other Choice <a class="header-anchor" href="#do-not-use-validations-on-models-unless-there-is-no-other-choice" aria-label="Permalink to &quot;Do Not Use Validations on Models Unless There is No Other Choice&quot;">​</a></h3><p>Sequel provides a validation layer for use on models. You should not generally use this, since a) data integrity is baked into your database design, and b) user interactions and constraints are part of the front-end.</p><p>That said, there are times when you have data constraints that cannot be modeled in the database. In that case, a validation on the data model is better than nothing. Since all data access for your app should go through your data models, a validation on a data model has a high chance of being checked.</p><div class="note custom-block github-alert"><p class="custom-block-title">NOTE</p><p>Since any process, app, or tool can manipulate your database, model-based validations won&#39;t be in effect, and therefore won&#39;t be applied. This is why you design your schema to avoid invalid data wherever possible.</p></div><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 8, 2025</em></p><p>None at this time</p>`,47)])])}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":"Database Access / Data Models","description":"","frontmatter":{},"headers":[],"relativePath":"database-access.md","filePath":"database-access.md"}'),n={name:"database-access.md"};function l(h,s,p,o,d,k){return e(),i("div",null,[...s[0]||(s[0]=[t("",47)])])}const g=a(n,[["render",l]]);export{c as __pageData,g as default};