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,62 @@
1
+ class HomePage < AppPage
2
+ def page_template
3
+ # The duplication and excessive class sizes here are to
4
+ # make it easier for you to remove this when you start working
5
+ # on your app. There are pros and cons to how this code
6
+ # is written, so don't take this is as a directive on how to
7
+ # build your app. You do you!
8
+ img(src: "/static/images/LogoPylon.png",
9
+ class: "dn db-ns pos-fixed top-0 left-0 h-100vh w-auto z-2")
10
+
11
+ header(class: "flex flex-column items-center justify-center h-100vh") do
12
+ div(class: "flex flex-column flex-row-ns items-center justify-center gap-3") do
13
+ img(src: "/static/images/LogoTransit.png", class: "h-5")
14
+ div do
15
+ h1(class: "ff-sans ma-0 lh-title f-5 tc tl-ns flex flex-column flex-row-ns items-center gap-2") do
16
+ plain("Welcome to Brut")
17
+ end
18
+ h2(class: "ff-sans ma-0 lh-title f-3 fw-normal tc tl-ns") do
19
+ plain("v")
20
+ plain(Gem.loaded_specs["brut"].version.to_s)
21
+ end
22
+
23
+ end
24
+ end
25
+ nav(class: [ "ff-sans",
26
+ "flex",
27
+ "flex-column",
28
+ "flex-row-ns",
29
+ "items-center",
30
+ "justify-start",
31
+ "gap-3",
32
+ "mt-3",
33
+ ]) do
34
+ a(href: "https://brutrb.com",
35
+ target: "_blank",
36
+ class: "f-3 red-300 tdu tdn-ns hover-tdu-ns"
37
+ ) do
38
+ code { "brutrb.com" }
39
+ end
40
+ span(role: "separator", class: "dn di-ns f-3 red-300") do
41
+ raw(safe "&#x2299;")
42
+ end
43
+ a(href: "https://brutrb.com/api/index.html",
44
+ target: "_blank",
45
+ class: "f-3 red-300 tdu tdn-ns hover-tdu-ns"
46
+ ) do
47
+ "API Docs"
48
+ end
49
+ span(role: "separator", class: "dn di-ns f-3 red-300") do
50
+ raw(safe "&#x2299;")
51
+ end
52
+ a(href: "http://localhost:6504",
53
+ target: "_blank",
54
+ class: "f-3 red-300 tdu tdn-ns hover-tdu-ns"
55
+ ) do
56
+ "Local OpenTelemetry"
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,6 @@
1
+ # Reprensetation of the session your app uses.
2
+ # This is intended to provide a rich API
3
+ # to whatever is in the sesion, so your app
4
+ # is not dealing with a Hash of Whatever.
5
+ class AppSession < Brut::FrontEnd::Session
6
+ end
@@ -0,0 +1,5 @@
1
+ The SVG here is public domain, as documented here:
2
+
3
+ https://commons.wikimedia.org/wiki/File:Comment_button.svg
4
+
5
+
@@ -0,0 +1,59 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4
+ xmlns:cc="http://creativecommons.org/ns#"
5
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
6
+ xmlns:svg="http://www.w3.org/2000/svg"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
9
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10
+ aria-label="Comment"
11
+ class="_8-yf5 "
12
+ fill="#262626"
13
+ height="24"
14
+ viewBox="0 0 48 48"
15
+ width="24"
16
+ version="1.1"
17
+ id="svg22"
18
+ sodipodi:docname="comment button.svg"
19
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
20
+ <metadata
21
+ id="metadata28">
22
+ <rdf:RDF>
23
+ <cc:Work
24
+ rdf:about="">
25
+ <dc:format>image/svg+xml</dc:format>
26
+ <dc:type
27
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
28
+ <dc:title></dc:title>
29
+ </cc:Work>
30
+ </rdf:RDF>
31
+ </metadata>
32
+ <defs
33
+ id="defs26" />
34
+ <sodipodi:namedview
35
+ pagecolor="#ffffff"
36
+ bordercolor="#666666"
37
+ borderopacity="1"
38
+ objecttolerance="10"
39
+ gridtolerance="10"
40
+ guidetolerance="10"
41
+ inkscape:pageopacity="0"
42
+ inkscape:pageshadow="2"
43
+ inkscape:window-width="1920"
44
+ inkscape:window-height="1001"
45
+ id="namedview24"
46
+ showgrid="false"
47
+ inkscape:zoom="23.805928"
48
+ inkscape:cx="12.634482"
49
+ inkscape:cy="10.463496"
50
+ inkscape:window-x="-9"
51
+ inkscape:window-y="-9"
52
+ inkscape:window-maximized="1"
53
+ inkscape:current-layer="svg22" />
54
+ <path
55
+ d="M47.5 46.1l-2.8-11c1.8-3.3 2.8-7.1 2.8-11.1C47.5 11 37 .5 24 .5S.5 11 .5 24 11 47.5 24 47.5c4 0 7.8-1 11.1-2.8l11 2.8c.8.2 1.6-.6 1.4-1.4zm-3-22.1c0 4-1 7-2.6 10-.2.4-.3.9-.2 1.4l2.1 8.4-8.3-2.1c-.5-.1-1-.1-1.4.2-1.8 1-5.2 2.6-10 2.6-11.4 0-20.6-9.2-20.6-20.5S12.7 3.5 24 3.5 44.5 12.7 44.5 24z"
56
+ id="path20"
57
+ clip-rule="evenodd"
58
+ fill-rule="evenodd" />
59
+ </svg>
@@ -0,0 +1,5 @@
1
+ # Foundation Scripts
2
+
3
+ `bin/` is where scripts for maintaining the app go. This includes scripts managed by Brut as well as any scripts you may create for yourself.
4
+
5
+
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler"
4
+ Bundler.require
5
+ require "pathname"
6
+ require "brut/cli/apps/build_assets"
7
+ exit Brut::CLI.app(Brut::CLI::Apps::BuildAssets,project_root: Pathname($0).dirname / "..")
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ usage() {
6
+ echo "Usage: $0"
7
+ echo
8
+ echo " Run all tests and quality checks, as if on a CI server"
9
+ echo
10
+ }
11
+
12
+ for arg in "$@"; do
13
+ if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ] || [ "${arg}" = "help" ]; then
14
+ usage
15
+ exit 0
16
+ fi
17
+ done
18
+
19
+
20
+ echo "[ bin/ci ] Building Assets"
21
+ bin/build-assets
22
+
23
+ echo "[ bin/ci ] Running non E2E tests"
24
+ bin/test run --rebuild
25
+
26
+ echo "[ bin/ci ] Running JS tests"
27
+ bin/test js
28
+
29
+ echo "[ bin/ci ] Running E2E tests"
30
+ bin/test e2e --rebuild --rebuild-after
31
+
32
+ echo "[ bin/ci ] Analyzing Ruby gems for"
33
+ echo "[ bin/ci ] security vulnerabilities"
34
+ bundle exec bundle audit check --update
35
+
36
+ echo "[ bin/ci ] Checking to see that all classes have tests"
37
+ bin/test audit --ignore app/src/front_end/components/custom_element_registration.rb
38
+
39
+ echo "[ bin/ci ] Done"
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+
5
+ option_parser = OptionParser.new do |opts|
6
+ opts.banner = "Usage: bin/console [options]\n\n Get an IRB session with your app loaded in the development RACK_ENV\n\nOPTIONS"
7
+
8
+ opts.on("-h", "--help", "Display this help message") do
9
+ puts opts
10
+ exit
11
+ end
12
+ end
13
+ option_parser.parse!
14
+ if ARGV[0] == "help"
15
+ puts option_parser
16
+ exit
17
+ end
18
+
19
+ ENV["RACK_ENV"] = "development"
20
+ if ENV["LOG_LEVEL"].to_s == ""
21
+ ENV["LOG_LEVEL"] = "warn"
22
+ end
23
+ require "pathname"
24
+ require "dotenv"
25
+
26
+ project_root = Pathname(__dir__) / ".."
27
+ Dotenv.load(project_root / ".env.development.local",
28
+ project_root / ".env.development")
29
+ require_relative project_root / "app" / "bootstrap"
30
+ Bootstrap.new.bootstrap!
31
+ binding.irb
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler"
4
+ Bundler.require
5
+ require "pathname"
6
+ require "brut/cli/apps/db"
7
+
8
+ exit Brut::CLI.app(Brut::CLI::Apps::DB,project_root: Pathname($0).dirname / "..")
9
+
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+ SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
5
+
6
+ usage() {
7
+ echo "Usage: $0 -e env"
8
+ echo
9
+ echo " Get a PostgreSQL console for the specified environment"
10
+ echo
11
+ echo "OPTIONS"
12
+ echo
13
+ echo " env - 'test' or 'development' (default is 'development')"
14
+ }
15
+ for arg in "${@}"; do
16
+ if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ] || [ "${arg}" = "help" ]; then
17
+ usage
18
+ exit 0
19
+ fi
20
+ done
21
+
22
+ RACK_ENV=development
23
+ while getopts ":e:" opt "${@}"; do
24
+ case ${opt} in
25
+ e )
26
+ RACK_ENV="${OPTARG}"
27
+ ;;
28
+ \? )
29
+ echo "[ $0 ] Unknown option: ${opt}"
30
+ usage
31
+ exit 1
32
+ ;;
33
+ : )
34
+ echo "[ $0 ] Invalid option: ${opt}"
35
+ exit 0
36
+ usage
37
+ ;;
38
+ esac
39
+ done
40
+ shift $((OPTIND -1))
41
+ ENV_FILE="${SCRIPT_DIR}"/../".env.${RACK_ENV}"
42
+
43
+ if [ -e "${ENV_FILE}" ]; then
44
+ database_url=$(grep DATABASE_URL "${ENV_FILE}" | sed 's/^[^=]*=//g')
45
+ psql "${database_url}"
46
+ else
47
+ echo "[ $0 ] error: '${RACK_ENV}' is not a valid environment"
48
+ echo
49
+ usage
50
+ exit 1
51
+ fi
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+ SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
5
+ ROOT_DIR="${SCRIPT_DIR}/.."
6
+
7
+ usage() {
8
+ echo "Usage: $0"
9
+ echo
10
+ echo " Run the app for local development, with asset and code reloading"
11
+ echo
12
+ }
13
+
14
+ for arg in "$@"; do
15
+ if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ] || [ "${arg}" = "help" ]; then
16
+ usage
17
+ exit 0
18
+ fi
19
+ done
20
+
21
+ RACK_ENV="development"
22
+ export RACK_ENV
23
+ "${SCRIPT_DIR}"/build-assets
24
+ foreman start --procfile "${ROOT_DIR}/Procfile.${RACK_ENV}" --root "${ROOT_DIR}"
25
+
@@ -0,0 +1,26 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ usage() {
5
+ echo "Usage: $0"
6
+ echo
7
+ echo " Run tasks on production after code is avaiable, but before deployment"
8
+ echo
9
+ }
10
+
11
+ for arg in "$@"; do
12
+ if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ] || [ "${arg}" = "help" ]; then
13
+ usage
14
+ exit 0
15
+ fi
16
+ done
17
+
18
+ echo "[ bin/release ] started"
19
+ echo "[ bin/release ] Creating DB if needed"
20
+ BRUT_CLI_RAISE_ON_ERROR=true bundle exec ./bin/db create --env=production
21
+ echo "[ bin/release ] Migrating DB if needed"
22
+ BRUT_CLI_RAISE_ON_ERROR=true bundle exec ./bin/db migrate --env=production
23
+
24
+ # Add additional commands here as needed
25
+
26
+ echo "[ bin/release ] DONE"
@@ -0,0 +1,86 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+ SCRIPT_DIR=$( cd -- "$( dirname -- "${0}" )" > /dev/null 2>&1 && pwd )
5
+
6
+ usage() {
7
+ echo "Usage: $0"
8
+ echo
9
+ echo " Run the app in the given RACK_ENV."
10
+ echo " You likely want to use bin/dev instead of this command."
11
+ echo
12
+ echo "ENVIRONMENT VARIABLES"
13
+ echo
14
+ echo " PORT - The port to run the app on. Default is 6502"
15
+ echo " RACK_ENV - The Rack environment to use. Default is development"
16
+ echo
17
+ }
18
+
19
+ for arg in "$@"; do
20
+ if [ "${arg}" = "-h" ] || [ "${arg}" = "--help" ] || [ "${arg}" = "help" ]; then
21
+ usage
22
+ exit 0
23
+ fi
24
+ done
25
+
26
+ PORT="${PORT:-6502}"
27
+ RACK_ENV="${RACK_ENV:-development}"
28
+
29
+ export PORT
30
+ export RACK_ENV
31
+
32
+ set -e
33
+
34
+ # Everything here is trying its best to prevent more than
35
+ # one server from running, as this is extremely confusing.
36
+ # Note that the puma invocation at the bottom
37
+ # of this script uses the pidfile concept.
38
+ if [ -f tmp/pidfile ]; then
39
+ echo "[ $0 ] pidfile found"
40
+ pid=$(cat tmp/pidfile)
41
+
42
+ # First, try to gracefully stop the server with kill
43
+ if ps -p "${pid}" > /dev/null; then
44
+ echo "[ $0 ] Attempting to kill PID '${pid}'"
45
+ kill "${pid}"
46
+ else
47
+ echo "[ $0 ] PID '${pid}' no longer running"
48
+ fi
49
+
50
+ # Now, wait 5 seconds to see if it stopped
51
+ if timeout 5 tail --pid="${pid}" -f /dev/null; then
52
+ echo "[ $0 ] PID '${pid}' stopped. Restarting server"
53
+ else
54
+ # if it has not stopeed, use kill -9 which should work.
55
+ # But, like all things computer, it's not guaranteed.
56
+ echo "[ $0 ] PID '${pid}' has not stopped. Trying kill -9"
57
+ kill -9 "${pid}"
58
+ if timeout 1 tail --pid="${pid}" -f /dev/null; then
59
+ echo "[ $0 ] PID '${pid}' killed. Restarting server"
60
+ else
61
+ echo "[ $0 ] PID '${pid}' still running. Something seriously wrong"
62
+ echo "[ $0 ] You may need to stop all Docker containers and restart them"
63
+ exit 1
64
+ fi
65
+ fi
66
+
67
+ else
68
+ echo "[ $0 ] No pidfile-Starting up"
69
+ fi
70
+
71
+ # Run puma in a UNIX environment provided by dotenv.
72
+ #
73
+ # A few things to note:
74
+ #
75
+ # * dotenv is OK if -f is given non-existent files. That's why this works
76
+ # in production where there are no .env files.
77
+ # * the `--` marks the end of dotenv's options and the start of the command
78
+ # to run. That's why the flags given to puma are not interpreted by dotenv
79
+ # as flags for itself.
80
+ dotenv \
81
+ -f "${SCRIPT_DIR}/../.env.${RACK_ENV}.local,${SCRIPT_DIR}/../.env.${RACK_ENV}" \
82
+ --ignore \
83
+ -- \
84
+ bin/puma \
85
+ -C puma.config.rb \
86
+ --pid tmp/pidfile
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler"
4
+ Bundler.require
5
+ require "pathname"
6
+
7
+ require "brut/cli/apps/scaffold"
8
+ exit Brut::CLI.app(Brut::CLI::Apps::Scaffold,project_root: Pathname($0).dirname / "..")
9
+
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require "fileutils"
5
+ require "open3"
6
+ require "optparse"
7
+ require "pathname"
8
+
9
+ # This is intended to run inside the Workspace (i.e. Docker container) to
10
+ # set up the Foundation, thus enabling development. This script should:
11
+ #
12
+ # a) Rely only on Ruby and its standard library, since it runs
13
+ # before any gems are installed
14
+ # b) Be idempotent, with the ability to run in a CI server
15
+ # c) Be organized with the setup method first, as this is the main chunk
16
+ # of logic, with all other supporting methods following
17
+ #
18
+ # NOTE: It is not clear yet how Brut will manage this file for you, especially
19
+ # given that you will need to add to it based on your own setup needs.
20
+ #
21
+ # Take care to only add code and not change what is here.
22
+
23
+ def setup(update_gems:,update_node:)
24
+ if update_gems
25
+ log "Updating gems"
26
+ system! "bundle update"
27
+ else
28
+ log "Installing gems"
29
+ # Only do bundle install if the much-faster
30
+ # bundle check indicates we need to
31
+ system! "bundle check --no-color || bundle install --no-color --quiet"
32
+ end
33
+
34
+ log "Installing puma binstubs"
35
+ system! "bundle binstub puma"
36
+
37
+ log "Installing rspec binstubs"
38
+ system! "bundle binstub rspec-core"
39
+
40
+ # https://github.com/ddollar/foreman/wiki/Don't-Bundle-Foreman
41
+ log "Installing foreman"
42
+ system! "gem install foreman"
43
+
44
+ # Installs Shopify's LSP. You can remove this if you don't want that,
45
+ # but this is here to show where to do it vs requiring you to read
46
+ # documentation.
47
+ log "Installing Ruby LSP"
48
+ system! "gem install ruby-lsp"
49
+
50
+ if update_node
51
+ log "Updating Node Modules"
52
+ system! "npm --no-color --no-progress update"
53
+ else
54
+ log "Installing Node Modules"
55
+ system! "npm --no-color --no-progress install"
56
+ end
57
+
58
+ # In theory, this was installed when the Workspace was setup, but
59
+ # it doesn't alwaysd work propertly, so this ensures
60
+ # Chromium is set up. See comments in Dockerfile.dx as to why
61
+ # Chromium and not Chrome.
62
+ log "Re-installing playwright/chromium if needed"
63
+ system! "node_modules/.bin/playwright install chromium"
64
+
65
+ log "Ensuring tmp dir exists"
66
+ system! "mkdir -p tmp"
67
+
68
+ setup_dot_env_local
69
+
70
+ log "Setting up databases"
71
+
72
+ log "Re-creating development database"
73
+ system! "bin/db rebuild --env=development"
74
+
75
+ log "Re-creating test database"
76
+ system! "bin/db rebuild --env=test"
77
+
78
+ log "Loading seed data into the development database"
79
+ system! "bin/db seed --env=development"
80
+
81
+ log "All set up."
82
+ help
83
+ end
84
+
85
+ def setup_dot_env_local()
86
+
87
+ dot_env = ROOT_DIR / ".env.development"
88
+ if !File.exist?(dot_env)
89
+ raise "Problem: '#{dot_env}' does not exist"
90
+ end
91
+
92
+ log "Checking #{dot_env} for required local environment variables"
93
+ previous_comment = ""
94
+ dot_env_need_local = {}
95
+ File.read(dot_env).split(/\n/).each do |line|
96
+ if line =~ /^#(.*)$/
97
+ previous_comment << line
98
+ elsif line =~ /^LOCAL:([^=]+)$/
99
+ dot_env_need_local[$1] = previous_comment
100
+ previous_comment = ""
101
+ elsif line =~ /^([^=])+/
102
+ previous_comment = ""
103
+ end
104
+ end
105
+
106
+ dot_env_local = ROOT_DIR / ".env.development.local"
107
+ log "Ensuring '#{dot_env_local}' exists"
108
+ if !File.exist?(dot_env_local)
109
+ log "Creating '#{dot_env_local}'"
110
+ FileUtils.touch dot_env_local
111
+ end
112
+
113
+ log "Checking #{dot_env_local} for values for required local environment variables"
114
+ local_contents = File.read(dot_env_local).split(/\n/).map { |line|
115
+ if line =~ /^([^=]+)=(.*)$/
116
+ var = $1
117
+ val = $2
118
+ if dot_env_need_local[var] && val.to_s != ""
119
+ log "Value for '#{var}' is good"
120
+ dot_env_need_local.delete(var)
121
+ end
122
+ end
123
+ line
124
+ }.compact
125
+
126
+ dot_env_need_local.each do |var,comment|
127
+ log "A value is needed locally for '#{var}'"
128
+ log ""
129
+ log comment
130
+ log ""
131
+ puts "Enter the value below and hit Return"
132
+ value = gets
133
+ local_contents << "#{var}=#{value}"
134
+ log "Got it, thanks"
135
+ end
136
+
137
+ log "Updating '#{dot_env_local}'"
138
+ File.open(dot_env_local,"w") do |file|
139
+ local_contents.each do |line|
140
+ file.puts line
141
+ end
142
+ end
143
+
144
+ log "Checking that #{dot_env_local} is being ignored"
145
+ gitignore = ROOT_DIR / ".gitignore"
146
+ if File.exist?(gitignore)
147
+ lines = File.read(gitignore).split(/\n/)
148
+ if lines.include?("/#{dot_env_local.basename}")
149
+ log "It's there!"
150
+ else
151
+ log "It's missing. Adding..."
152
+ lines << ""
153
+ lines << "# This contains actual secrets and should not be checked in"
154
+ lines << "/#{dot_env_local.basename}"
155
+ File.open(gitignore,"w") do |file|
156
+ lines.each do |line|
157
+ file.puts line
158
+ end
159
+ end
160
+ end
161
+ else
162
+ log "No #{gitignore}, so make sure you DO NOT CHECK #{dot_env_local} IN!!!"
163
+ end
164
+ end
165
+
166
+ # We don't want the setup method to have to do all this error
167
+ # checking, and we also want to explicitly log what we are
168
+ # executing. Thus, we use this method instead of Kernel#system
169
+ def system!(*args)
170
+ if ENV["BRUT_BIN_KIT_DEBUG"] == "true"
171
+ log "Executing #{args}"
172
+ out,err,status = Open3.capture3(*args)
173
+ if status.success?
174
+ log "#{args} succeeded"
175
+ else
176
+ log "#{args} failed"
177
+ log "STDOUT:"
178
+ $stdout.puts out
179
+ log "STDERR:"
180
+ $stderr.puts err
181
+ abort
182
+ end
183
+ else
184
+ log "Executing #{args}"
185
+ if system(*args)
186
+ log "#{args} succeeded"
187
+ else
188
+ log "#{args} failed"
189
+ abort
190
+ end
191
+ end
192
+ end
193
+
194
+ # It's helpful to know what messages came from this
195
+ # script, so we'll use log instead of `puts`
196
+ def log(message)
197
+ puts "[ #{$0} ] #{message}"
198
+ end
199
+
200
+ ROOT_DIR = ((Pathname(__dir__) / ".." ).expand_path)
201
+
202
+ def help(option_parser=nil)
203
+ if option_parser
204
+ puts option_parser
205
+ puts
206
+ puts "OTHER USEFUL COMMANDS"
207
+ else
208
+ puts
209
+ puts "USEFUL COMMANDS"
210
+ end
211
+ puts ""
212
+ puts " bin/dev"
213
+ puts " # run app locally, rebuilding and reloading as needed"
214
+ puts ""
215
+ puts " bin/ci"
216
+ puts " # runs all tests and checks as CI would"
217
+ puts ""
218
+ puts " bin/console"
219
+ puts " # get an IRB console with the app loaded"
220
+ puts ""
221
+ puts " bin/db"
222
+ puts " # interact with the DB for migrations, information, etc"
223
+ puts ""
224
+ puts " bin/dbconsole"
225
+ puts " # get a PSQL session to the database"
226
+ puts ""
227
+ puts " bin/scaffold"
228
+ puts " # Create various structures in your app, like pages or forms"
229
+ puts ""
230
+ puts " bin/setup help"
231
+ puts " # show this help"
232
+ puts ""
233
+ end
234
+
235
+ options = {
236
+ update_gems: false,
237
+ update_node: false,
238
+ }
239
+ option_parser = OptionParser.new do |opts|
240
+ opts.banner = "Usage: bin/setup [options]\n\n Set up the Foundation, allowing for development and testing of the app\n\nOPTIONS\n"
241
+ opts.on("--update[=TYPE]","Update gems or modules to get the latest versions consistent with Gemfile or package.json. TYPE can be 'all', 'gems', or 'node'. If TYPE is omitted, 'all' is assumed.") do |value|
242
+ options[:update_gems] = value.nil? || value == "gems"
243
+ options[:update_node] = value.nil? || value == "node"
244
+ end
245
+ opts.on("-h", "--help", "Display this help message") do
246
+ help(opts)
247
+ exit
248
+ end
249
+ end
250
+ option_parser.parse!
251
+
252
+ if ARGV[0] == "help"
253
+ help(option_parser)
254
+ else
255
+ setup(**options)
256
+ end