brut 0.5.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/CHANGELOG.md +7 -0
  4. data/Dockerfile.dx +19 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +19 -0
  7. data/assets/YouTubeThumb.pxd +0 -0
  8. data/bin/build +86 -0
  9. data/bin/ci +36 -0
  10. data/bin/docs +39 -9
  11. data/bin/publish +61 -0
  12. data/bin/setup +6 -0
  13. data/brut-css/bin/build +19 -0
  14. data/brut-css/bin/ci +19 -0
  15. data/brut-css/bin/docs +19 -0
  16. data/brut-css/bin/publish +21 -0
  17. data/brut-css/bin/setup +1 -0
  18. data/brut-css/package-lock.json +2 -2
  19. data/brut-css/package.json +1 -1
  20. data/brut-js/bin/build +15 -6
  21. data/brut-js/bin/docs +25 -0
  22. data/brut-js/bin/publish +21 -0
  23. data/brut-js/bin/setup +1 -0
  24. data/brut-js/dx +1 -0
  25. data/brut-js/package-lock.json +2 -2
  26. data/brut-js/package.json +1 -1
  27. data/brut.gemspec +2 -2
  28. data/brutrb.com/bin/setup +1 -0
  29. data/brutrb.com/getting-started.md +3 -0
  30. data/brutrb.com/overview.md +6 -0
  31. data/brutrb.com/tutorial.md +7 -3
  32. data/docs/404.html +2 -2
  33. data/docs/adrs.html +3 -3
  34. data/docs/ai.html +3 -3
  35. data/docs/assets/{app.D6BuVHo9.js → app.DyQLb4Ot.js} +1 -1
  36. data/docs/assets/chunks/@localSearchIndexroot.CmtZyrFA.js +1 -0
  37. data/docs/assets/chunks/{VPLocalSearchBox.BpvHMbx6.js → VPLocalSearchBox.T1iA-eJx.js} +1 -1
  38. data/docs/assets/chunks/{theme.wlAOvi2f.js → theme.ChwsbWjK.js} +2 -2
  39. data/docs/assets/{components.md.iLiv2E9X.js → components.md.DHh-NwKs.js} +3 -3
  40. data/docs/assets/{configuration.md.DmuAdsli.js → configuration.md.D8Wz3oJU.js} +1 -1
  41. data/docs/assets/{forms.md.D8aa_qI-.js → forms.md.BRE85eju.js} +1 -1
  42. data/docs/assets/{getting-started.md.DLplsDUd.js → getting-started.md.2ioiTe-B.js} +6 -3
  43. data/docs/assets/{getting-started.md.DLplsDUd.lean.js → getting-started.md.2ioiTe-B.lean.js} +1 -1
  44. data/docs/assets/overview.md.DlKiRRG_.js +1 -0
  45. data/docs/assets/overview.md.DlKiRRG_.lean.js +1 -0
  46. data/docs/assets/tutorial.md.BIb7XT6j.js +1 -0
  47. data/docs/assets/tutorial.md.BIb7XT6j.lean.js +1 -0
  48. data/docs/assets.html +3 -3
  49. data/docs/brut-js.html +3 -3
  50. data/docs/business-logic.html +3 -3
  51. data/docs/cli.html +3 -3
  52. data/docs/components.html +7 -7
  53. data/docs/configuration.html +5 -5
  54. data/docs/css.html +3 -3
  55. data/docs/custom-element-tests.html +3 -3
  56. data/docs/database-access.html +3 -3
  57. data/docs/database-schema.html +3 -3
  58. data/docs/deployment.html +3 -3
  59. data/docs/dev-environment.html +3 -3
  60. data/docs/dir-structure.html +3 -3
  61. data/docs/doc-conventions.html +3 -3
  62. data/docs/end-to-end-tests.html +3 -3
  63. data/docs/features.html +3 -3
  64. data/docs/flash-and-session.html +3 -3
  65. data/docs/form-constraints.html +3 -3
  66. data/docs/forms.html +5 -5
  67. data/docs/getting-started.html +9 -6
  68. data/docs/handlers.html +3 -3
  69. data/docs/hashmap.json +1 -1
  70. data/docs/hooks.html +3 -3
  71. data/docs/i18n.html +3 -3
  72. data/docs/index.html +3 -3
  73. data/docs/instrumentation.html +3 -3
  74. data/docs/javascript.html +3 -3
  75. data/docs/jobs.html +3 -3
  76. data/docs/keyword-injection.html +3 -3
  77. data/docs/layouts.html +3 -3
  78. data/docs/lsp.html +3 -3
  79. data/docs/markdown-examples.html +3 -3
  80. data/docs/middleware.html +3 -3
  81. data/docs/overview.html +5 -5
  82. data/docs/pages.html +3 -3
  83. data/docs/recipes/alternate-layouts.html +3 -3
  84. data/docs/recipes/authentication.html +3 -3
  85. data/docs/recipes/blank-layouts.html +3 -3
  86. data/docs/recipes/custom-flash.html +3 -3
  87. data/docs/recipes/indexed-forms.html +3 -3
  88. data/docs/recipes/migrations.html +3 -3
  89. data/docs/recipes/text-field-component.html +3 -3
  90. data/docs/roadmap.html +3 -3
  91. data/docs/routes.html +3 -3
  92. data/docs/security.html +3 -3
  93. data/docs/seed-data.html +3 -3
  94. data/docs/space-time-continuum.html +3 -3
  95. data/docs/tutorial.html +5 -5
  96. data/docs/unit-tests.html +3 -3
  97. data/docs/why.html +3 -3
  98. data/lib/brut/framework/mcp.rb +1 -1
  99. data/lib/brut/front_end/components/form_tag.rb +2 -2
  100. data/lib/brut/version.rb +1 -1
  101. data/mkbrut/.gitignore +16 -0
  102. data/mkbrut/CODE_OF_CONDUCT.txt +100 -0
  103. data/mkbrut/Gemfile +3 -0
  104. data/mkbrut/Gemfile.lock +19 -0
  105. data/mkbrut/LICENSE.txt +370 -0
  106. data/mkbrut/README.md +145 -0
  107. data/mkbrut/Rakefile +2 -0
  108. data/mkbrut/bin/build +36 -0
  109. data/mkbrut/bin/ci +19 -0
  110. data/mkbrut/bin/docs +19 -0
  111. data/mkbrut/bin/publish +129 -0
  112. data/mkbrut/bin/rake +16 -0
  113. data/mkbrut/bin/setup +30 -0
  114. data/mkbrut/brut-welcome.png +0 -0
  115. data/mkbrut/deploy/.dockerignore +2 -0
  116. data/mkbrut/deploy/Dockerfile +25 -0
  117. data/mkbrut/exe/mkbrut +5 -0
  118. data/mkbrut/lib/mkbrut/app.rb +79 -0
  119. data/mkbrut/lib/mkbrut/app_id.rb +8 -0
  120. data/mkbrut/lib/mkbrut/app_name.rb +29 -0
  121. data/mkbrut/lib/mkbrut/app_options.rb +36 -0
  122. data/mkbrut/lib/mkbrut/base.rb +57 -0
  123. data/mkbrut/lib/mkbrut/cli.rb +107 -0
  124. data/mkbrut/lib/mkbrut/erb_binding_delegate.rb +20 -0
  125. data/mkbrut/lib/mkbrut/internet_identifier.rb +32 -0
  126. data/mkbrut/lib/mkbrut/invalid_identifier.rb +4 -0
  127. data/mkbrut/lib/mkbrut/ops/add_css_import.rb +42 -0
  128. data/mkbrut/lib/mkbrut/ops/add_i18n_message.rb +74 -0
  129. data/mkbrut/lib/mkbrut/ops/add_method.rb +48 -0
  130. data/mkbrut/lib/mkbrut/ops/append_to_file.rb +20 -0
  131. data/mkbrut/lib/mkbrut/ops/base_op.rb +21 -0
  132. data/mkbrut/lib/mkbrut/ops/copy_file.rb +12 -0
  133. data/mkbrut/lib/mkbrut/ops/insert_code_in_method.rb +58 -0
  134. data/mkbrut/lib/mkbrut/ops/insert_route.rb +52 -0
  135. data/mkbrut/lib/mkbrut/ops/mkdir.rb +13 -0
  136. data/mkbrut/lib/mkbrut/ops/prism_parsing_op.rb +70 -0
  137. data/mkbrut/lib/mkbrut/ops/render_template.rb +26 -0
  138. data/mkbrut/lib/mkbrut/ops/skip_file.rb +10 -0
  139. data/mkbrut/lib/mkbrut/ops.rb +16 -0
  140. data/mkbrut/lib/mkbrut/organization.rb +5 -0
  141. data/mkbrut/lib/mkbrut/prefix.rb +26 -0
  142. data/mkbrut/lib/mkbrut/prefixed_io.rb +16 -0
  143. data/mkbrut/lib/mkbrut/segments/bare_bones.rb +185 -0
  144. data/mkbrut/lib/mkbrut/segments/demo.rb +121 -0
  145. data/mkbrut/lib/mkbrut/segments/heroku.rb +30 -0
  146. data/mkbrut/lib/mkbrut/segments/sidekiq.rb +3 -0
  147. data/mkbrut/lib/mkbrut/segments.rb +8 -0
  148. data/mkbrut/lib/mkbrut/version.rb +3 -0
  149. data/mkbrut/lib/mkbrut/versions.rb +13 -0
  150. data/mkbrut/lib/mkbrut.rb +18 -0
  151. data/mkbrut/mkbrut.gemspec +32 -0
  152. data/mkbrut/templates/Base/.dockerignore +25 -0
  153. data/mkbrut/templates/Base/.env.development.erb +60 -0
  154. data/mkbrut/templates/Base/.env.test.erb +8 -0
  155. data/mkbrut/templates/Base/.gitignore +31 -0
  156. data/mkbrut/templates/Base/.projections.json +59 -0
  157. data/mkbrut/templates/Base/Dockerfile.dx +205 -0
  158. data/mkbrut/templates/Base/Gemfile.erb +53 -0
  159. data/mkbrut/templates/Base/Procfile.development +5 -0
  160. data/mkbrut/templates/Base/Procfile.test +1 -0
  161. data/mkbrut/templates/Base/README.md +4 -0
  162. data/mkbrut/templates/Base/README.md.erb +40 -0
  163. data/mkbrut/templates/Base/app/bootstrap.rb +61 -0
  164. data/mkbrut/templates/Base/app/config/i18n/en/1_defaults.rb +128 -0
  165. data/mkbrut/templates/Base/app/config/i18n/en/2_app.rb +24 -0
  166. data/mkbrut/templates/Base/app/public/static/manifest.json.erb +33 -0
  167. data/mkbrut/templates/Base/app/src/app.rb.erb +37 -0
  168. data/mkbrut/templates/Base/app/src/back_end/data_models/app_data_model.rb +5 -0
  169. data/mkbrut/templates/Base/app/src/back_end/data_models/db.rb +19 -0
  170. data/mkbrut/templates/Base/app/src/back_end/data_models/migrations/20240101130000_citext.rb +6 -0
  171. data/mkbrut/templates/Base/app/src/back_end/data_models/seed/seed_data.rb +9 -0
  172. data/mkbrut/templates/Base/app/src/front_end/components/app_component.rb +8 -0
  173. data/mkbrut/templates/Base/app/src/front_end/components/custom_element_registration.rb.erb +7 -0
  174. data/mkbrut/templates/Base/app/src/front_end/css/index.css +2 -0
  175. data/mkbrut/templates/Base/app/src/front_end/css/svgs.css +12 -0
  176. data/mkbrut/templates/Base/app/src/front_end/forms/app_form.rb +4 -0
  177. data/mkbrut/templates/Base/app/src/front_end/handlers/app_handler.rb +4 -0
  178. data/mkbrut/templates/Base/app/src/front_end/images/LogoPylon.png +0 -0
  179. data/mkbrut/templates/Base/app/src/front_end/images/LogoTransit.png +0 -0
  180. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-120x120.png +0 -0
  181. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-152x152.png +0 -0
  182. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-167x167.png +0 -0
  183. data/mkbrut/templates/Base/app/src/front_end/images/apple-touch-icon-180x180.png +0 -0
  184. data/mkbrut/templates/Base/app/src/front_end/images/favicon.ico +0 -0
  185. data/mkbrut/templates/Base/app/src/front_end/images/icon.png +0 -0
  186. data/mkbrut/templates/Base/app/src/front_end/images/mkicons.sh +6 -0
  187. data/mkbrut/templates/Base/app/src/front_end/js/index.js +6 -0
  188. data/mkbrut/templates/Base/app/src/front_end/layouts/default_layout.rb.erb +73 -0
  189. data/mkbrut/templates/Base/app/src/front_end/pages/app_page.rb +11 -0
  190. data/mkbrut/templates/Base/app/src/front_end/pages/home_page.rb +62 -0
  191. data/mkbrut/templates/Base/app/src/front_end/support/app_session.rb +6 -0
  192. data/mkbrut/templates/Base/app/src/front_end/svgs/README.md +5 -0
  193. data/mkbrut/templates/Base/app/src/front_end/svgs/comment-button.svg +59 -0
  194. data/mkbrut/templates/Base/bin/README.md.erb +5 -0
  195. data/mkbrut/templates/Base/bin/build-assets +7 -0
  196. data/mkbrut/templates/Base/bin/ci +39 -0
  197. data/mkbrut/templates/Base/bin/console +31 -0
  198. data/mkbrut/templates/Base/bin/db +9 -0
  199. data/mkbrut/templates/Base/bin/dbconsole +51 -0
  200. data/mkbrut/templates/Base/bin/dev +25 -0
  201. data/mkbrut/templates/Base/bin/release +26 -0
  202. data/mkbrut/templates/Base/bin/run +86 -0
  203. data/mkbrut/templates/Base/bin/scaffold +9 -0
  204. data/mkbrut/templates/Base/bin/setup +256 -0
  205. data/mkbrut/templates/Base/bin/startup-message +65 -0
  206. data/mkbrut/templates/Base/bin/test +9 -0
  207. data/mkbrut/templates/Base/bin/test-server +29 -0
  208. data/mkbrut/templates/Base/bin/watch-and-build-assets +37 -0
  209. data/mkbrut/templates/Base/config.ru +16 -0
  210. data/mkbrut/templates/Base/docker-compose.dx.yml +92 -0
  211. data/mkbrut/templates/Base/dx/README.md +28 -0
  212. data/mkbrut/templates/Base/dx/bash_customizations +12 -0
  213. data/mkbrut/templates/Base/dx/bash_customizations.local +8 -0
  214. data/mkbrut/templates/Base/dx/build +107 -0
  215. data/mkbrut/templates/Base/dx/docker-compose.env.erb +25 -0
  216. data/mkbrut/templates/Base/dx/dx.sh.lib +137 -0
  217. data/mkbrut/templates/Base/dx/exec +68 -0
  218. data/mkbrut/templates/Base/dx/prune +19 -0
  219. data/mkbrut/templates/Base/dx/show-help-in-app-container-then-wait.sh +38 -0
  220. data/mkbrut/templates/Base/dx/start +30 -0
  221. data/mkbrut/templates/Base/dx/stop +23 -0
  222. data/mkbrut/templates/Base/package.json.erb +37 -0
  223. data/mkbrut/templates/Base/puma.config.rb +53 -0
  224. data/mkbrut/templates/Base/specs/e2e/home_page.spec.rb.erb +23 -0
  225. data/mkbrut/templates/Base/specs/front_end/js/SpecHelper.js +24 -0
  226. data/mkbrut/templates/Base/specs/front_end/pages/home_page.spec.rb +22 -0
  227. data/mkbrut/templates/Base/specs/lint_factories.spec.rb +7 -0
  228. data/mkbrut/templates/Base/specs/spec_helper.rb +78 -0
  229. data/mkbrut/templates/Base/specs/support.rb +2 -0
  230. data/mkbrut/templates/segments/BareBones/app/src/front_end/handlers/trigger_exception_handler.rb +24 -0
  231. data/mkbrut/templates/segments/BareBones/app/src/front_end/js/Example.js.erb +49 -0
  232. data/mkbrut/templates/segments/BareBones/specs/front_end/handlers/trigger_exception_handler.spec.rb +41 -0
  233. data/mkbrut/templates/segments/BareBones/specs/front_end/js/Example.spec.js.erb +38 -0
  234. data/mkbrut/templates/segments/Demo/app/src/back_end/data_models/db/guestbook_message.rb +3 -0
  235. data/mkbrut/templates/segments/Demo/app/src/back_end/data_models/migrations/20250628194124_guestbook.rb +14 -0
  236. data/mkbrut/templates/segments/Demo/app/src/front_end/components/flash_component.rb +36 -0
  237. data/mkbrut/templates/segments/Demo/app/src/front_end/css/constraint-violations.css +18 -0
  238. data/mkbrut/templates/segments/Demo/app/src/front_end/css/fonts.css +19 -0
  239. data/mkbrut/templates/segments/Demo/app/src/front_end/fonts/monaspace-xenon.ttf +0 -0
  240. data/mkbrut/templates/segments/Demo/app/src/front_end/forms/guestbook_message_form.rb +4 -0
  241. data/mkbrut/templates/segments/Demo/app/src/front_end/handlers/guestbook_message_handler.rb +64 -0
  242. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/guestbook_page/message_component.rb +41 -0
  243. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/guestbook_page.rb +43 -0
  244. data/mkbrut/templates/segments/Demo/app/src/front_end/pages/new_guestbook_message_page.rb +64 -0
  245. data/mkbrut/templates/segments/Demo/specs/back_end/data_models/db/guestbook_message.spec.rb +5 -0
  246. data/mkbrut/templates/segments/Demo/specs/e2e/guest_message.spec.rb +54 -0
  247. data/mkbrut/templates/segments/Demo/specs/factories/db/guestbook_message.factory.rb +7 -0
  248. data/mkbrut/templates/segments/Demo/specs/front_end/components/flash_component.spec.rb +5 -0
  249. data/mkbrut/templates/segments/Demo/specs/front_end/handlers/guestbook_message_handler.spec.rb +122 -0
  250. data/mkbrut/templates/segments/Demo/specs/front_end/pages/guestbook_page/message_component.spec.rb +5 -0
  251. data/mkbrut/templates/segments/Demo/specs/front_end/pages/guestbook_page.spec.rb +52 -0
  252. data/mkbrut/templates/segments/Demo/specs/front_end/pages/new_guestbook_message_page.spec.rb +5 -0
  253. data/mkbrut/templates/segments/Heroku/bin/deploy +11 -0
  254. data/mkbrut/templates/segments/Heroku/deploy/Dockerfile +125 -0
  255. data/mkbrut/templates/segments/Heroku/deploy/docker-entrypoint +15 -0
  256. data/mkbrut/templates/segments/Heroku/deploy/heroku_config.rb +26 -0
  257. metadata +185 -21
  258. data/docs/assets/chunks/@localSearchIndexroot.COP2Bcmp.js +0 -1
  259. data/docs/assets/overview.md.iMnwLO4x.js +0 -1
  260. data/docs/assets/overview.md.iMnwLO4x.lean.js +0 -1
  261. data/docs/assets/tutorial.md.BYXj4cOu.js +0 -1
  262. data/docs/assets/tutorial.md.BYXj4cOu.lean.js +0 -1
  263. /data/docs/assets/{components.md.iLiv2E9X.lean.js → components.md.DHh-NwKs.lean.js} +0 -0
  264. /data/docs/assets/{configuration.md.DmuAdsli.lean.js → configuration.md.D8Wz3oJU.lean.js} +0 -0
  265. /data/docs/assets/{forms.md.D8aa_qI-.lean.js → forms.md.BRE85eju.lean.js} +0 -0
@@ -0,0 +1,53 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Gemfile organization:
4
+ #
5
+ # 1. Brut is always at the top
6
+ # 2. The rest of the gems are in alphabetical order
7
+ # 3. Do not specify a gem version unless there is a specific reason.
8
+ # 4. Each gem should have a comment preceding it explaining
9
+ # what the gem is for and why the project needs it.
10
+ # 5. If a version was specified, make sure the comment explains
11
+ # why it had to be specified and the conditions under which it
12
+ # can be removed.
13
+ # 6. Do not create group blocks. Instead, use the group: option for each
14
+ # gem. This creates a single clean list of gems in order.
15
+
16
+ gem "brut", "<%= versions.brut_version_specifier %>"
17
+
18
+ # Audit our dependencies
19
+ gem "bundler-audit", groups: [ :test ]
20
+
21
+ # This allows us to make assertions about test setup that are not themselves tests
22
+ gem "confidence-check", groups: [ :test ]
23
+
24
+ # Dotenv manages the environment variables in the development
25
+ # and test environments. See .env.development, .env.development.local,
26
+ # and .env.test
27
+ gem "dotenv", groups: [ :development, :test ]
28
+
29
+ # FactoryBot manages test and seed data
30
+ gem "factory_bot", groups: [ :development, :test ]
31
+
32
+ # Faker is used to create realistic data for test and seed data.
33
+ gem "faker", groups: [ :development, :test ]
34
+
35
+
36
+ # We use Postgres
37
+ gem "pg"
38
+
39
+ # Allows using Playwright from Ruby.
40
+ # We lock the version because it must be the same as the verison
41
+ # of playwright in package.json. This ensures that we can reliably
42
+ # install the right browsers and that everything will work right.
43
+ gem "playwright-ruby-client", "1.52.0", groups: [ :test ]
44
+
45
+ # Sinatra needs a webserver and puma is the best
46
+ gem "puma"
47
+
48
+ # Uses RSpec for testing
49
+ gem "rspec", groups: [ :test ]
50
+
51
+ # Allows for diagnosing failing tests
52
+ gem "with_clues", groups: [ :test ]
53
+
@@ -0,0 +1,5 @@
1
+ web: PORT=6502 bin/run
2
+ css: bin/watch-and-build-assets css
3
+ js: bin/watch-and-build-assets js
4
+ images: bin/watch-and-build-assets images
5
+ startup_message: PORT=6502 bin/startup-message
@@ -0,0 +1 @@
1
+ web: PORT=6503 bin/run
@@ -0,0 +1,4 @@
1
+ # Workspace
2
+
3
+ The *workspace* is the part of the dev environment that runs directly on the
4
+ developer's machine. It is made up of Bash and Docker.
@@ -0,0 +1,40 @@
1
+ # <%= app_name %>
2
+
3
+ A great new app!
4
+
5
+ ## Setup
6
+
7
+ 1. Clone this repo
8
+ 2. Ensure you have Docker installed
9
+ 3. Build the Foundation of your dev environment:
10
+
11
+ ```
12
+ dx/build
13
+ ```
14
+ 4. Start it up:
15
+
16
+ ```
17
+ dx/start
18
+ ```
19
+ 5. In another terminal, shell into the container you just started:
20
+
21
+ ```
22
+ dx/exec bash
23
+ ```
24
+ 6. Run setup:
25
+
26
+ ```
27
+ dx/setup
28
+ ```
29
+ 7. Start the app:
30
+
31
+ ```
32
+ bin/dev
33
+ ```
34
+
35
+ 8. Visit `http://localhost:6502`
36
+ 9. Now get to work!
37
+
38
+
39
+
40
+
@@ -0,0 +1,61 @@
1
+ # Handles the configuration and startup process for your Brut-powered
2
+ # app and anything that needs its configuration, such as a CLI.
3
+ #
4
+ # This class models three phases:
5
+ #
6
+ # 1. *Initialized* - Ruby is running, but no Brut configuration has happened.
7
+ #
8
+ # ```ruby
9
+ # bootstrap = Bootstrap.new
10
+ # ```
11
+ # 2. *Configured* - Brut's static configuration is set up.
12
+ # See `Brut::Framework::MCP.initialize`.
13
+ #
14
+ # ```ruby
15
+ # configured = bootstrap.configure_only!
16
+ # ```
17
+ #
18
+ # 3. *Bootstrapped* - Everything is set up and configured, services
19
+ # have been connected-to and the app is generally ready to receive
20
+ # requests or have its logic executed.
21
+ #
22
+ # ```ruby
23
+ # booted = configured.bootstrap!
24
+ # ```
25
+ class Bootstrap
26
+
27
+ def configure_only!
28
+ require "bundler"
29
+
30
+ Bundler.require(:default, ENV.fetch("RACK_ENV").to_sym)
31
+
32
+ require "brut"
33
+ require "pathname"
34
+
35
+ Brut.container.store_required_path(
36
+ "project_root",
37
+ "Root of the entire project's source code checkout",
38
+ (Pathname(__dir__) / "..").expand_path)
39
+
40
+
41
+ $: << File.join(Brut.container.project_root,"app","src")
42
+
43
+ require "app"
44
+
45
+ @mcp = Brut::Framework::MCP.new(app_klass: ::App)
46
+ @app = @mcp.app
47
+ self
48
+ end
49
+
50
+ attr_reader :rack_app, :app
51
+
52
+ # @return [Bootstrap::ConfiguredBootstrap] that contains
53
+ # the {Brut::Framework::App} and {Brut::Framework::MCP}.
54
+ def bootstrap!
55
+ self.configure_only!
56
+ @mcp.boot!
57
+ @rack_app = @mcp.sinatra_app.new
58
+ self
59
+ end
60
+
61
+ end
@@ -0,0 +1,128 @@
1
+ # Brut's defaults for various strings required by Brut's internals.
2
+ #
3
+ # You are discouraged from changing this file.
4
+ {
5
+ # en: must be the first entry, thus indicating this is the EN translations
6
+ en: {
7
+ time:{
8
+ formats: { # strftime formats
9
+ iso_8601: "%Y-%m-%d %H:%M:%S.%6N %Z",
10
+ full_with_tz: "%Y-%m-%d %H:%M:%S %Z",
11
+ full: "%Y-%m-%d %H:%M:%S",
12
+ default: "%Y-%m-%d %H:%M:%S",
13
+ date: "%a, %b %e, %Y",
14
+ date_no_dow: "%b %e, %Y",
15
+ date_no_year: "%a, %b %e",
16
+ date_no_year_no_dow: "%b %e",
17
+ month_day_year: "%b %e, %Y",
18
+ month_day_year_no_year: "%b %e",
19
+ day_of_week: "%A",
20
+ time_only: "%l:%M %p",
21
+ },
22
+ am: "AM",
23
+ pm: "PM",
24
+ },
25
+ date: {
26
+ formats: {
27
+ iso_8601: "%Y-%m-%d",
28
+ full: "%Y-%m-%d",
29
+ default: "%a, %b %e, %Y",
30
+ date: "%a, %b %e, %Y",
31
+ date_no_dow: "%b %e, %Y",
32
+ date_no_year: "%a, %b %e",
33
+ date_no_year_no_dow: "%b %e",
34
+ month_day_year: "%b %e, %Y",
35
+ month_day_year_no_year: "%b %e",
36
+ day_of_week: "%A",
37
+ day_of_week_short: "%a",
38
+ time_only: "%l:%M %p",
39
+ },
40
+ abbr_day_names: [
41
+ "Sun",
42
+ "Mon",
43
+ "Tue",
44
+ "Wed",
45
+ "Thu",
46
+ "Fri",
47
+ "Sat",
48
+ ],
49
+ day_names: [
50
+ "Sunday",
51
+ "Monday",
52
+ "Tuesday",
53
+ "Wednesday",
54
+ "Thursday",
55
+ "Friday",
56
+ "Saturday",
57
+ ],
58
+ abbr_month_names: [
59
+ "",
60
+ "Jan",
61
+ "Feb",
62
+ "Mar",
63
+ "Apr",
64
+ "May",
65
+ "Jun",
66
+ "Jul",
67
+ "Aug",
68
+ "Sep",
69
+ "Oct",
70
+ "Nov",
71
+ "Dec",
72
+ ],
73
+ month_names: [
74
+ "",
75
+ "January",
76
+ "February",
77
+ "March",
78
+ "April",
79
+ "May",
80
+ "June",
81
+ "July",
82
+ "August",
83
+ "September",
84
+ "October",
85
+ "November",
86
+ "December",
87
+ ],
88
+ },
89
+ cv: { # short for "constraint violations" to avoid having to type that out
90
+ this_field: "This field",
91
+ cs: { # short for "client-side", again to not have to type it out
92
+ # These keys use camel-case because that is how the browser defines
93
+ # these values, based on ValidityState
94
+ badInput: "%{field} is the wrong type of data",
95
+ patternMismatch: "%{field} isn't in the right format",
96
+ rangeOverflow: "%{field} is too big",
97
+ rangeUnderflow: "%{field} is too small",
98
+ stepMismatch: "%{field} is not a valid value in the range",
99
+ tooLong: "%{field} is too long",
100
+ tooShort: "%{field} is too short",
101
+ typeMismatch: "%{field} is the wrong type",
102
+ valueMissing: "%{field} is required",
103
+ general: "Form is invalid",
104
+ },
105
+ ss: { # short for "server-side", again not to have to type it out
106
+ # These are snake case, which is idiomatic for Ruby. The values
107
+ # here are all based on DataObjectValidator's behavior
108
+ required: "%{field} is required",
109
+ too_short: "%{field} is too short; must be at least %{minlength} characters",
110
+ },
111
+ },
112
+ diagnostics: { # This is to help diagnose issues with the translation system and is not
113
+ # intended to store real strings to be used by your app
114
+ has_interpolations: "Test %{interp} Test",
115
+ has_pluralizations: {
116
+ one: "Once!",
117
+ other: "More than once!"
118
+ },
119
+ has_pluralizations_and_zero: {
120
+ zero: "ZERO",
121
+ one: "ONE",
122
+ other: "MORE",
123
+ },
124
+ simple1: "SIMPLE1",
125
+ simple2: "SIMPLE2",
126
+ }
127
+ },
128
+ }
@@ -0,0 +1,24 @@
1
+ # All app-specific translations, or overrides of Brut's defaults, go here.
2
+ {
3
+ # en: must be the first entry, thus indicating this is the EN translations
4
+ en: {
5
+ cv: {
6
+ cs: {
7
+ },
8
+ ss: {
9
+ email_taken: "This email has been taken",
10
+ },
11
+ },
12
+ pages: { # Page-specific messages - this key is relied-upon by Brut to exist
13
+ HomePage: {
14
+ title: "Welcome!",
15
+ }
16
+ },
17
+ components: { # Component-specific messages - this key is relied-upon by Brut to exist
18
+ },
19
+ clis: { # Messages used in CLIs - this key is relied-upon by Brut to exist
20
+ },
21
+ hello: "Hello, and welcome to Brut!",
22
+ # Other keys can go here and you can organize them however you like.
23
+ },
24
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "<%= app_name %>",
3
+ "short_name": "<%= app_name %>",
4
+ "description": "The <%= app_name %> App",
5
+ "start_url": "/",
6
+ "scope": "/",
7
+ "display": "standalone",
8
+ "theme_color": "#A70C00",
9
+ "icons": [
10
+ {
11
+ "src": "/static/images/icon.png"
12
+ },
13
+ {
14
+ "src": "/static/images/apple-touch-icon-120x120.png",
15
+ "sizes": "120x120"
16
+ },
17
+ {
18
+ "src": "/static/images/apple-touch-icon-152x152.png",
19
+ "sizes": "152x152"
20
+ },
21
+ {
22
+ "src": "/static/images/apple-touch-icon-167x167.png",
23
+ "sizes": "167x167"
24
+ },
25
+ {
26
+ "src": "/static/images/apple-touch-icon-180x180.png",
27
+ "sizes": "180x180"
28
+ }
29
+ ]
30
+ }
31
+
32
+
33
+
@@ -0,0 +1,37 @@
1
+ class App < Brut::Framework::App
2
+ def id = "<%= app_id %>"
3
+ def organization = "<%= organization %>"
4
+
5
+ def initialize
6
+ # Add additional initialization to this initializers.
7
+ # Be sure to use the lazy/block form of store because when this code
8
+ # runs, the app may not have access to networked resources.
9
+ Brut.container.override("session_class",AppSession)
10
+ Brut.container.override("external_id_prefix","<%= prefix %>")
11
+ end
12
+
13
+ def boot!
14
+ # Perform any additional bootstrapping here, such as configuration of
15
+ # job back-ends or other data stores. When this runs, you can be sure
16
+ # the app has access to networked resources
17
+ end
18
+
19
+ # Various class methods may be called here to configure your app. See the API
20
+ # documentation for Brut::Framework::App.
21
+
22
+ # Generic error handler. Is expected to return a Rack response.
23
+ error do |exception:, http_status_code:|
24
+ SemanticLogger["App"].error(exception || "Unknown error", http_status_code:)
25
+ [
26
+ http_status_code || 500,
27
+ {},
28
+ exception&.message || "Internal Server Error '#{http_status_code}'"
29
+ ]
30
+ end
31
+
32
+ # Declare your app's routes.
33
+ routes do
34
+ page "/" # Special route for HomePage
35
+ end
36
+ end
37
+
@@ -0,0 +1,5 @@
1
+ AppDataModel = Class.new(Sequel::Model)
2
+ # Base class for all your data models
3
+ class AppDataModel
4
+ end
5
+
@@ -0,0 +1,19 @@
1
+ # Namespace for all data models. This makes it easy
2
+ # to avoid the conflation of database table models from
3
+ # domain models.
4
+ module DB
5
+ # Execute the code in the block in a database transaction.
6
+ # This calls `Sequel::Model.db.transaction`, which is ultimately
7
+ # `Sequel::Database#transaction`. Please review that documentation as well
8
+ # as the documentation of your databnase so you understand
9
+ # how transactions work.
10
+ #
11
+ # @see https://rubydoc.info/gems/sequel/5.93.0/Sequel/Database#transaction-instance_method
12
+ def self.transaction(opts=:use_default,&block)
13
+ if opts == :use_default
14
+ opts = Sequel::Database::OPTS
15
+ end
16
+ Sequel::Model.db.transaction(opts,&block)
17
+ end
18
+ end
19
+
@@ -0,0 +1,6 @@
1
+ Sequel.migration do
2
+ up do
3
+ run %{CREATE EXTENSION IF NOT EXISTS citext}
4
+ end
5
+ end
6
+
@@ -0,0 +1,9 @@
1
+ require "brut/back_end/seed_data"
2
+ class SeedData < Brut::BackEnd::SeedData
3
+ include FactoryBot::Syntax::Methods
4
+ def seed!
5
+ # Create records here. This method is not expected
6
+ # to be idempotent. You can (and should) use your
7
+ # FactoryBot factories here.
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ # Base class for your app's components.
2
+ class AppComponent < Brut::FrontEnd::Component
3
+ include Brut::Framework::Errors # provides access to methods in this module
4
+ include Brut::FrontEnd::Components # Allows Brut-provided components to be
5
+ # used as a Phlex "kit"
6
+ include CustomElementRegistration
7
+ end
8
+
@@ -0,0 +1,7 @@
1
+ # Registers your app's autnomous custom elements
2
+ # with Phlex, so you can use them like normal HTML elements.
3
+ module CustomElementRegistration
4
+ def self.included(other)
5
+ other.register_element :<%= prefix %>_example
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ @import "brut-css/dist/brut.css";
2
+ @import "svgs.css";
@@ -0,0 +1,12 @@
1
+ /* Gives all SVGS a somewhat small height so as CSS loads,
2
+ * you don't have a big huge SVG */
3
+ svg {
4
+ width: 1em;
5
+ height: 1em;
6
+ }
7
+
8
+ /* Assuming SVGs are monochrome line art,
9
+ * this ensures the lines are drawn in the current color */
10
+ svg path {
11
+ fill: currentColor;
12
+ }
@@ -0,0 +1,4 @@
1
+ # Base class for your app's forms.
2
+ class AppForm < Brut::FrontEnd::Form
3
+ end
4
+
@@ -0,0 +1,4 @@
1
+ # Base class for your app's handlers.
2
+ class AppHandler < Brut::FrontEnd::Handler
3
+ end
4
+
@@ -0,0 +1,6 @@
1
+ set -e
2
+ magick icon.png -define icon:auto-resize=16,32,48 favicon.ico
3
+ sizes=(120 152 167 180)
4
+ for size in "${sizes[@]}"; do
5
+ magick icon.png -resize ${size}x${size} apple-touch-icon-${size}x${size}.png
6
+ done
@@ -0,0 +1,6 @@
1
+ import { BrutCustomElements } from "brut-js"
2
+
3
+ document.addEventListener("DOMContentLoaded", () => {
4
+ BrutCustomElements.define() // Define all of BrutJS's autnomous custom elements
5
+ })
6
+
@@ -0,0 +1,73 @@
1
+ # The default layout used by all pages.
2
+ # This code is heavily commented to explain what each
3
+ # line that isn't hopefully obvious does. Feel free to remove.
4
+ class DefaultLayout < Brut::FrontEnd::Layout
5
+ include Brut::FrontEnd::Components
6
+
7
+ def initialize(page_name:)
8
+ @page_name = page_name
9
+ end
10
+
11
+ # Brut::FrontEnd::Page's view_template ultimately calls this method
12
+ # to wrap itself in the layout defined below.
13
+ def view_template
14
+ doctype
15
+ html(lang: "en") do
16
+ head do
17
+ meta(charset: "utf-8")
18
+ meta(content: "width=device-width,initial-scale=1", name:"viewport")
19
+ meta(content: "website", property:"og:type")
20
+ # Sets this up as a PWA - see app/public/static/manifest.json
21
+ link(rel: "manifest", href: "/static/manifest.json")
22
+
23
+ # Load the bundled stylesheet. asset_path translates the logical
24
+ # path to the hased value produced by esbuild
25
+ link(rel: "preload", as: "style", href: asset_path("/css/styles.css"))
26
+ link(rel: "stylesheet", href: asset_path("/css/styles.css"))
27
+
28
+ # Load the bundled JavaScript. asset_path translates the logical
29
+ # path to the hased value produced by esbuild
30
+ script(defer: true, src: asset_path("/js/app.js"))
31
+
32
+ title do
33
+ "<%= app_name %>"
34
+ end
35
+
36
+ # Brut::FrontEnd::Components::PageIdentifier, which produces
37
+ # a <meta> tag with the page's name. Useful for end to end tests.
38
+ PageIdentifier(@page_name)
39
+
40
+ # Brut::FrontEnd::Components::I18nTranslations, which includes
41
+ # translations starting with cv.cs for use with client-side
42
+ # constraint violation messaging.
43
+ I18nTranslations("cv.cs")
44
+ # Brut::FrontEnd::Components::I18nTranslations, which includes
45
+ # translations for cv.this_field, which is used with client-side
46
+ # constraint violation messaging.
47
+ I18nTranslations("cv.this_field")
48
+ # Brut::FrontEnd::Components::Traceparent, which stores
49
+ # the OpenTelemetry traceparent in a <meta> attribute for use
50
+ # by the <brut-tracing> element.
51
+ Traceparent()
52
+ # Brut::FrontEnd::Components::LocaleDetection, which
53
+ # will send the browser's locale and timezone back to the server to
54
+ # help with locale determination.
55
+ render(
56
+ Brut::FrontEnd::RequestContext.inject(
57
+ Brut::FrontEnd::Components::LocaleDetection
58
+ )
59
+ )
60
+ end
61
+ # Adds the page's name as a class name. You can use this for
62
+ # CSS if needed.
63
+ body(class: @page_name) do
64
+ # Render the <brut-tracing> element, which will send
65
+ # OpenTelemetry traces back to the server to be joined up with
66
+ # the server's traces.
67
+ brut_tracing url: "/__brut/instrumentation", show_warnings: true
68
+ yield # Contents of the page are inserted here
69
+ end
70
+ end
71
+ end
72
+ end
73
+
@@ -0,0 +1,11 @@
1
+ # Base class for all pages in the app
2
+ # Note that while Brut::FrontEnd::Page is a subclass
3
+ # of Brut::FrontEnd::Component, that is not the case
4
+ # for your app's pages and components. Thus, there is a small amount
5
+ # of duplication here.
6
+ class AppPage < Brut::FrontEnd::Page
7
+ include Brut::Framework::Errors # See AppComponent
8
+ include CustomElementRegistration # See AppComponent
9
+ include Brut::FrontEnd::Components # See AppComponent
10
+ end
11
+