brut 0.0.27 → 0.0.29

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 (411) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.projections.json +10 -0
  4. data/.rspec +3 -0
  5. data/Dockerfile.dx +32 -14
  6. data/Gemfile.lock +1 -1
  7. data/assets/Logo-Square.pxd +0 -0
  8. data/assets/LogoPylon.pxd +0 -0
  9. data/assets/LogoStop.pxd +0 -0
  10. data/assets/LogoTall.pxd +0 -0
  11. data/bin/docs +24 -2
  12. data/bin/rspec +27 -0
  13. data/bin/setup +3 -3
  14. data/brutrb.com/.vitepress/config.mjs +1 -0
  15. data/brutrb.com/.vitepress/theme/custom.css +7 -0
  16. data/brutrb.com/.vitepress/theme/style.css +26 -15
  17. data/brutrb.com/deployment.md +123 -45
  18. data/brutrb.com/dev-environment.md +6 -5
  19. data/brutrb.com/doc-conventions.md +1 -1
  20. data/brutrb.com/getting-started.md +64 -28
  21. data/brutrb.com/images/LogoPylon.png +0 -0
  22. data/brutrb.com/images/LogoSquare.png +0 -0
  23. data/brutrb.com/images/LogoStop.png +0 -0
  24. data/brutrb.com/images/LogoTall.png +0 -0
  25. data/brutrb.com/images/OverviewMetro.graffle +0 -0
  26. data/brutrb.com/images/OverviewMetro.png +0 -0
  27. data/brutrb.com/index.md +4 -3
  28. data/brutrb.com/instrumentation.md +12 -0
  29. data/brutrb.com/layouts.md +130 -0
  30. data/brutrb.com/overview.md +6 -6
  31. data/docker-compose.dx.yml +5 -2
  32. data/docs/404.html +3 -3
  33. data/docs/ai.html +6 -6
  34. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  35. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  36. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  37. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  38. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  39. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  40. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  41. data/docs/api/Brut/BackEnd.html +1 -1
  42. data/docs/api/Brut/CLI/App.html +38 -13
  43. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +2 -2
  45. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +2 -2
  46. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +2 -2
  47. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +2 -2
  48. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/DB/Create.html +2 -2
  50. data/docs/api/Brut/CLI/Apps/DB/Drop.html +2 -2
  51. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +9 -3
  52. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +11 -11
  53. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +2 -2
  54. data/docs/api/Brut/CLI/Apps/DB/Seed.html +2 -2
  55. data/docs/api/Brut/CLI/Apps/DB/Status.html +12 -12
  56. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +270 -0
  58. data/docs/api/Brut/CLI/Apps/DeployBase.html +257 -0
  59. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +585 -0
  60. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +196 -0
  61. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  62. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +2 -2
  63. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +2 -2
  64. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +2 -2
  65. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +2 -2
  66. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +2 -2
  67. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  68. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +2 -2
  69. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  70. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +2 -2
  71. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  72. data/docs/api/Brut/CLI/Apps/Test/Audit.html +12 -10
  73. data/docs/api/Brut/CLI/Apps/Test/E2e.html +8 -8
  74. data/docs/api/Brut/CLI/Apps/Test/JS.html +9 -9
  75. data/docs/api/Brut/CLI/Apps/Test/Run.html +18 -18
  76. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  77. data/docs/api/Brut/CLI/Apps.html +2 -2
  78. data/docs/api/Brut/CLI/Command.html +113 -28
  79. data/docs/api/Brut/CLI/Error.html +1 -1
  80. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  81. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  82. data/docs/api/Brut/CLI/Executor.html +169 -38
  83. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  84. data/docs/api/Brut/CLI/Options.html +68 -19
  85. data/docs/api/Brut/CLI/Output.html +1 -1
  86. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  87. data/docs/api/Brut/CLI.html +1 -1
  88. data/docs/api/Brut/FactoryBot.html +1 -1
  89. data/docs/api/Brut/Framework/App.html +1 -1
  90. data/docs/api/Brut/Framework/Config.html +1 -1
  91. data/docs/api/Brut/Framework/Container.html +110 -29
  92. data/docs/api/Brut/Framework/Error.html +1 -1
  93. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +89 -1
  94. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  95. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  96. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  97. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  98. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  99. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  100. data/docs/api/Brut/Framework/Errors.html +31 -8
  101. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +1 -1
  102. data/docs/api/Brut/Framework/MCP.html +1 -1
  103. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  104. data/docs/api/Brut/Framework.html +1 -1
  105. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  106. data/docs/api/Brut/FrontEnd/Component/Helpers.html +36 -26
  107. data/docs/api/Brut/FrontEnd/Component.html +7 -7
  108. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  109. data/docs/api/Brut/FrontEnd/Components/FormTag.html +37 -29
  110. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  111. data/docs/api/Brut/FrontEnd/Components/Input.html +1 -1
  112. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  113. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +20 -117
  114. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +25 -23
  115. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +73 -380
  116. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +22 -138
  117. data/docs/api/Brut/FrontEnd/Components/Inputs.html +1 -1
  118. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  119. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  122. data/docs/api/Brut/FrontEnd/Components.html +23 -2
  123. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  124. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  125. data/docs/api/Brut/FrontEnd/Form.html +1 -1
  126. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +24 -68
  127. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +201 -0
  128. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +535 -0
  129. data/docs/api/Brut/FrontEnd/Forms/Input.html +983 -35
  130. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +4 -4
  131. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +29 -19
  132. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +9 -3
  133. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  134. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +9 -3
  135. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  136. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +72 -22
  137. data/docs/api/Brut/FrontEnd/Forms.html +1 -1
  138. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  143. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  146. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  147. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  148. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  149. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  150. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  151. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  152. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  153. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  154. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  155. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  156. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +6 -2
  157. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  158. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  159. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  160. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  161. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  162. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  163. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  164. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  165. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  166. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  167. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  168. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  169. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  170. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  171. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  172. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  173. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  174. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  175. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  176. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  177. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  178. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  179. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  180. data/docs/api/Brut/FrontEnd.html +1 -1
  181. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  182. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  183. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  184. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  185. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  186. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  187. data/docs/api/Brut/I18n.html +1 -1
  188. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  189. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +1 -1
  190. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +1 -1
  191. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +1 -1
  192. data/docs/api/Brut/Instrumentation.html +1 -1
  193. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  194. data/docs/api/Brut/SinatraHelpers.html +1 -1
  195. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  196. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  197. data/docs/api/Brut/SpecSupport/E2ETestServer.html +20 -20
  198. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  199. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  200. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  201. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  202. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  203. data/docs/api/Brut/SpecSupport/HandlerSupport.html +2 -2
  204. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +142 -0
  205. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +142 -0
  206. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +155 -0
  207. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +55 -25
  208. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +149 -0
  209. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +46 -19
  210. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +149 -0
  211. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +149 -0
  212. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +165 -0
  213. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +158 -0
  214. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +156 -0
  215. data/docs/api/Brut/SpecSupport/Matchers.html +2 -2
  216. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +24 -24
  217. data/docs/api/Brut/SpecSupport/RSpecSetup.html +55 -20
  218. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  219. data/docs/api/Brut/SpecSupport.html +1 -1
  220. data/docs/api/Brut.html +1 -1
  221. data/docs/api/Clock.html +1 -1
  222. data/docs/api/RichString.html +1 -1
  223. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  224. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +5 -1
  225. data/docs/api/Sequel/Extensions/BrutMigrations.html +36 -28
  226. data/docs/api/Sequel/Extensions.html +1 -1
  227. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  228. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  229. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  230. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  231. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  232. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  233. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  234. data/docs/api/Sequel/Plugins.html +1 -1
  235. data/docs/api/Sequel.html +1 -1
  236. data/docs/api/SpecSupport/Matchers/BeABug.html +143 -0
  237. data/docs/api/_index.html +106 -1
  238. data/docs/api/class_list.html +1 -1
  239. data/docs/api/css/full_list.css +2 -1
  240. data/docs/api/css/style.css +14 -13
  241. data/docs/api/file.README.html +1 -1
  242. data/docs/api/index.html +1 -1
  243. data/docs/api/method_list.html +530 -330
  244. data/docs/api/top-level-namespace.html +1 -1
  245. data/docs/assets/LogoStop.X8x-4riz.png +0 -0
  246. data/docs/assets/OverviewMetro.DUS-5fUZ.png +0 -0
  247. data/docs/assets/{ai.md.tZrjP9im.js → ai.md._6HCDL6d.js} +1 -1
  248. data/docs/assets/ai.md._6HCDL6d.lean.js +1 -0
  249. data/docs/assets/{app.D_yaTITQ.js → app.BhrfSt68.js} +1 -1
  250. data/docs/assets/chunks/@localSearchIndexroot.CeRAdP1K.js +1 -0
  251. data/docs/assets/chunks/{VPLocalSearchBox.B2-ZzyTY.js → VPLocalSearchBox.Dpot_2H4.js} +1 -1
  252. data/docs/assets/chunks/{theme.CfGFVRvE.js → theme.N2SNVLgU.js} +2 -2
  253. data/docs/assets/{components.md.eCttGlN-.js → components.md.CRUMdRoN.js} +1 -1
  254. data/docs/assets/{configuration.md.BRriU0cL.js → configuration.md.LG-zIBww.js} +1 -1
  255. data/docs/assets/deployment.md.BLseERGV.js +48 -0
  256. data/docs/assets/deployment.md.BLseERGV.lean.js +1 -0
  257. data/docs/assets/{dev-environment.md.BNc8AYiK.js → dev-environment.md.GZv6xvi9.js} +1 -1
  258. data/docs/assets/doc-conventions.md.-kN3Xo5C.js +1 -0
  259. data/docs/assets/{doc-conventions.md.DCfRXXi-.lean.js → doc-conventions.md.-kN3Xo5C.lean.js} +1 -1
  260. data/docs/assets/{forms.md.CBTYQ_Cz.js → forms.md.B-koVgyw.js} +23 -23
  261. data/docs/assets/{forms.md.CBTYQ_Cz.lean.js → forms.md.B-koVgyw.lean.js} +1 -1
  262. data/docs/assets/getting-started.md.Dj0qtZI2.js +25 -0
  263. data/docs/assets/getting-started.md.Dj0qtZI2.lean.js +1 -0
  264. data/docs/assets/index.md.CuBB-BdM.js +1 -0
  265. data/docs/assets/index.md.CuBB-BdM.lean.js +1 -0
  266. data/docs/assets/{instrumentation.md.CL6ax7nT.js → instrumentation.md.a9Pjps4P.js} +2 -2
  267. data/docs/assets/{instrumentation.md.CL6ax7nT.lean.js → instrumentation.md.a9Pjps4P.lean.js} +1 -1
  268. data/docs/assets/layouts.md.cPnh3NId.js +51 -0
  269. data/docs/assets/layouts.md.cPnh3NId.lean.js +1 -0
  270. data/docs/assets/lsp.md.Bsu-f6VU.js +1 -0
  271. data/docs/assets/lsp.md.Bsu-f6VU.lean.js +1 -0
  272. data/docs/assets/{overview.md.CDalkuxV.js → overview.md.DVKRM8zl.js} +4 -4
  273. data/docs/assets/overview.md.DVKRM8zl.lean.js +1 -0
  274. data/docs/assets/recipes_authentication.md.CAsXf7hk.js +1 -0
  275. data/docs/assets/recipes_authentication.md.CAsXf7hk.lean.js +1 -0
  276. data/docs/assets/{style.D73IYGCX.css → style.B2o1L9eN.css} +1 -1
  277. data/docs/assets.html +5 -5
  278. data/docs/brut-js/api/AjaxSubmit.html +1 -1
  279. data/docs/brut-js/api/AjaxSubmit.js.html +1 -1
  280. data/docs/brut-js/api/Autosubmit.html +1 -1
  281. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  282. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  283. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  284. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  285. data/docs/brut-js/api/BufferedLogger.html +1 -1
  286. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  287. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  288. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  289. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  290. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  291. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  292. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  293. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  294. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  295. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  296. data/docs/brut-js/api/Form.html +1 -1
  297. data/docs/brut-js/api/Form.js.html +1 -1
  298. data/docs/brut-js/api/I18nTranslation.html +1 -1
  299. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  300. data/docs/brut-js/api/LocaleDetection.html +1 -1
  301. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  302. data/docs/brut-js/api/Logger.html +1 -1
  303. data/docs/brut-js/api/Logger.js.html +1 -1
  304. data/docs/brut-js/api/Message.html +1 -1
  305. data/docs/brut-js/api/Message.js.html +1 -1
  306. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  307. data/docs/brut-js/api/RichString.html +1 -1
  308. data/docs/brut-js/api/RichString.js.html +1 -1
  309. data/docs/brut-js/api/Tabs.html +1 -1
  310. data/docs/brut-js/api/Tabs.js.html +1 -1
  311. data/docs/brut-js/api/Tracing.html +1 -1
  312. data/docs/brut-js/api/Tracing.js.html +1 -1
  313. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  314. data/docs/brut-js/api/external-Performance.html +1 -1
  315. data/docs/brut-js/api/external-Promise.html +1 -1
  316. data/docs/brut-js/api/external-ValidityState.html +1 -1
  317. data/docs/brut-js/api/external-Window.html +1 -1
  318. data/docs/brut-js/api/external-fetch.html +1 -1
  319. data/docs/brut-js/api/global.html +1 -1
  320. data/docs/brut-js/api/index.html +1 -1
  321. data/docs/brut-js/api/index.js.html +1 -1
  322. data/docs/brut-js/api/module-testing.html +1 -1
  323. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  324. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  325. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  326. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  327. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  328. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  329. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  330. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  331. data/docs/brut-js/api/testing_index.js.html +1 -1
  332. data/docs/brut-js.html +5 -5
  333. data/docs/business-logic.html +5 -5
  334. data/docs/cli.html +5 -5
  335. data/docs/components.html +7 -7
  336. data/docs/configuration.html +6 -6
  337. data/docs/css.html +5 -5
  338. data/docs/custom-element-tests.html +5 -5
  339. data/docs/database-access.html +5 -5
  340. data/docs/database-schema.html +5 -5
  341. data/docs/deployment.html +53 -6
  342. data/docs/dev-environment.html +6 -6
  343. data/docs/doc-conventions.html +6 -6
  344. data/docs/end-to-end-tests.html +5 -5
  345. data/docs/flash-and-session.html +5 -5
  346. data/docs/forms.html +28 -28
  347. data/docs/getting-started.html +30 -7
  348. data/docs/handlers.html +5 -5
  349. data/docs/hashmap.json +1 -1
  350. data/docs/hooks.html +5 -5
  351. data/docs/i18n.html +5 -5
  352. data/docs/index.html +6 -6
  353. data/docs/instrumentation.html +6 -6
  354. data/docs/javascript.html +5 -5
  355. data/docs/jobs.html +5 -5
  356. data/docs/keyword-injection.html +5 -5
  357. data/docs/layouts.html +74 -0
  358. data/docs/lsp.html +24 -0
  359. data/docs/markdown-examples.html +5 -5
  360. data/docs/middleware.html +5 -5
  361. data/docs/not-released.html +5 -5
  362. data/docs/overview.html +9 -9
  363. data/docs/pages.html +6 -6
  364. data/docs/recipes/authentication.html +24 -0
  365. data/docs/routes.html +5 -5
  366. data/docs/security.html +5 -5
  367. data/docs/seed-data.html +5 -5
  368. data/docs/space-time-continuum.html +5 -5
  369. data/docs/tutorial.html +5 -5
  370. data/docs/unit-tests.html +5 -5
  371. data/dx/bash_customizations +7 -0
  372. data/dx/build +13 -2
  373. data/dx/docker-compose.env +1 -1
  374. data/dx/exec +25 -8
  375. data/lib/brut/cli/app.rb +7 -2
  376. data/lib/brut/cli/apps/deploy_base.rb +86 -0
  377. data/lib/brut/cli/apps/heroku_container_based_deploy.rb +194 -0
  378. data/lib/brut/cli/command.rb +7 -13
  379. data/lib/brut/cli/executor.rb +31 -5
  380. data/lib/brut/cli/options.rb +4 -0
  381. data/lib/brut/cli.rb +4 -3
  382. data/lib/brut/framework/container.rb +25 -7
  383. data/lib/brut/framework/errors/abstract_method.rb +7 -0
  384. data/lib/brut/framework/errors.rb +4 -2
  385. data/lib/brut/front_end/forms/input.rb +253 -20
  386. data/lib/brut/front_end/forms/input_definition.rb +15 -12
  387. data/lib/brut/front_end/middlewares/reload_app.rb +2 -0
  388. data/lib/brut/front_end.rb +1 -0
  389. data/lib/brut/spec_support/rspec_setup.rb +42 -2
  390. data/lib/brut/version.rb +1 -1
  391. data/lib/sequel/extensions/brut_instrumentation.rb +4 -0
  392. data/specs/brut/front_end/forms/input.spec.rb +978 -0
  393. data/specs/spec_helper.rb +27 -0
  394. data/specs/support/matchers/have_constraint_violation.rb +23 -0
  395. data/specs/support/matchers.rb +5 -0
  396. data/specs/support.rb +3 -0
  397. metadata +77 -29
  398. data/docs/assets/ai.md.tZrjP9im.lean.js +0 -1
  399. data/docs/assets/chunks/@localSearchIndexroot.BsN5i0Fi.js +0 -1
  400. data/docs/assets/deployment.md.Dbka4OTr.js +0 -1
  401. data/docs/assets/deployment.md.Dbka4OTr.lean.js +0 -1
  402. data/docs/assets/doc-conventions.md.DCfRXXi-.js +0 -1
  403. data/docs/assets/getting-started.md.Bz2s1Vjb.js +0 -2
  404. data/docs/assets/getting-started.md.Bz2s1Vjb.lean.js +0 -1
  405. data/docs/assets/index.md.B28EwVpq.js +0 -1
  406. data/docs/assets/index.md.B28EwVpq.lean.js +0 -1
  407. data/docs/assets/overview.Da81cB9R.png +0 -0
  408. data/docs/assets/overview.md.CDalkuxV.lean.js +0 -1
  409. /data/docs/assets/{components.md.eCttGlN-.lean.js → components.md.CRUMdRoN.lean.js} +0 -0
  410. /data/docs/assets/{configuration.md.BRriU0cL.lean.js → configuration.md.LG-zIBww.lean.js} +0 -0
  411. /data/docs/assets/{dev-environment.md.BNc8AYiK.lean.js → dev-environment.md.GZv6xvi9.lean.js} +0 -0
data/lib/brut/cli/app.rb CHANGED
@@ -179,7 +179,9 @@ class Brut::CLI::App
179
179
 
180
180
  # Called after all setup has been executed. Brut will have been started/loaded. This will *not* be called if anything
181
181
  # caused execution to be aborted.
182
- def after_bootstrap
182
+ #
183
+ # @param [Brut::Framework::App] app Your Brut app.
184
+ def after_bootstrap(app:)
183
185
  end
184
186
 
185
187
  # Executes the command. Called by {Brut::CLI::AppRunner}.
@@ -192,6 +194,7 @@ class Brut::CLI::App
192
194
  command.set_env_if_needed
193
195
  load_env(project_root:)
194
196
  command.before_execute
197
+ app = nil
195
198
  bootstrap_result = begin
196
199
  require "#{project_root}/app/bootstrap"
197
200
  bootstrap = Bootstrap.new
@@ -200,6 +203,7 @@ class Brut::CLI::App
200
203
  else
201
204
  bootstrap.bootstrap!
202
205
  end
206
+ app = bootstrap.app
203
207
  continue_execution
204
208
  rescue => ex
205
209
  as_execution_result(command.handle_bootstrap_exception(ex))
@@ -207,7 +211,8 @@ class Brut::CLI::App
207
211
  if bootstrap_result.stop?
208
212
  return bootstrap_result
209
213
  end
210
- after_bootstrap
214
+ after_bootstrap(app:)
215
+ command.after_bootstrap(app:)
211
216
  if self.class.configure_only?
212
217
  as_execution_result(command.execute)
213
218
  else
@@ -0,0 +1,86 @@
1
+ require "brut/cli"
2
+
3
+ class Brut::CLI::Apps::DeployBase < Brut::CLI::App
4
+ def before_execute
5
+ ENV["RACK_ENV"] = "production"
6
+ end
7
+
8
+ class GitChecks
9
+ def initialize(out:, err:, executor:, warn_only: false)
10
+ @out = out
11
+ @err = err
12
+ @executor = executor
13
+ @warn_only = warn_only
14
+ end
15
+
16
+ def check!
17
+ require_main_branch!
18
+ require_no_local_changes!
19
+ require_pushed_to_main!
20
+ end
21
+
22
+ private
23
+
24
+ def require_main_branch!
25
+ # XXX: This should be put into executor
26
+ current_branch = `git branch --show-current`.strip.chomp
27
+
28
+ if current_branch != "main"
29
+ @err.puts "You are not on the 'main' branch, but on the '#{current_branch}' branch"
30
+ @err.puts "You may only deploy from the 'main' branch"
31
+ if @warn_only
32
+ @err.puts "Ignoring"
33
+ return
34
+ else
35
+ exit 1
36
+ end
37
+ end
38
+ end
39
+
40
+ def require_no_local_changes!
41
+ @out.puts "Running 'git status' to reset local caches to account for Docker<->host oddities"
42
+ @executor.system!("git status")
43
+ local_changes = `git diff-index --name-only HEAD --`
44
+ if local_changes.strip != ""
45
+ @err.puts "You have local changes:"
46
+ local_changes.split(/\n/).each do |change|
47
+ @err.puts " #{change}"
48
+ end
49
+ @err.puts "Commit these, run bin/ci, then push to origin/main"
50
+ if @warn_only
51
+ @err.puts "Ignoring"
52
+ return
53
+ else
54
+ exit 1
55
+ end
56
+ end
57
+ end
58
+
59
+ def require_pushed_to_main!
60
+ # XXX: This should be put into executor
61
+ git_status = `git rev-list --left-right --count origin/main...main`.strip.chomp
62
+ remote_ahead, local_ahead = git_status.split(/\t/,2).map(&:to_i)
63
+
64
+ if remote_ahead != 0
65
+ @err.puts "There are commits in origin you don't have. Pull those in, re-run bin/ci, THEN deploy"
66
+ if @warn_only
67
+ @err.puts "Ignoring"
68
+ return
69
+ else
70
+ exit 1
71
+ end
72
+ end
73
+
74
+ if local_ahead != 0
75
+ @out.puts "You have not pushed to origin. Do that before deploying"
76
+ if @warn_only
77
+ @err.puts "Ignoring"
78
+ return
79
+ else
80
+ exit 1
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
@@ -0,0 +1,194 @@
1
+ require "brut/cli"
2
+ class Brut::CLI::Apps::HerokuContainerBasedDeploy < Brut::CLI::Apps::DeployBase
3
+ description "Deploy to Heroku using containers"
4
+ default_command :deploy
5
+ configure_only!
6
+
7
+ class Deploy < Brut::CLI::Command
8
+ description "Build images, push them to Heroku, and deploy them"
9
+
10
+ detailed_description %{
11
+ Manages a deploy process based on using Heroku's Container Registry. See
12
+
13
+ https://devcenter.heroku.com/articles/container-registry-and-runtime
14
+
15
+ for details. You are assumed to understand this. This command will make the process somewhat easier.
16
+
17
+ This will use deploy/Dockerfile as a template to create one Dockerfile for each process you want to run in Heroku. deploy/heroku_config.rb is where the processes and their commands are configured.
18
+
19
+ The release phase is included automatically, based on bin/release.
20
+ }
21
+
22
+ opts.on("--platform=PLATFORM","Override default platform. Can be any Docker platform.")
23
+ opts.on("--[no-]dry-run", "Print the commands that would be run and don't actually do anything. Implies --skip-checks")
24
+ opts.on("--[no-]skip-checks", "Skip checks for code having been committed and pushed")
25
+ opts.on("--[no-]deploy", "After images are pushed, actually deploy them")
26
+ opts.on("--[no-]push", "After images are created, push them to Heroku's registry. If false, implies --no-deploy")
27
+
28
+ def after_bootstrap(app:)
29
+ @app_id = app.id
30
+ @organization = app.organization
31
+ end
32
+
33
+ def execute
34
+ options.set_default(:deploy, true)
35
+ options.set_default(:push, true)
36
+ if !options.push?
37
+ options[:deploy] = false
38
+ end
39
+ local_repo_checks = Brut::CLI::Apps::DeployBase::GitChecks.new(
40
+ out: @out,
41
+ err: @err,
42
+ executor: @executor,
43
+ warn_only: options.skip_checks? || options.dry_run?
44
+ )
45
+ if options.dry_run?
46
+ def system!(*args)
47
+ out.puts "DRY RUN, NOT EXECUTING '#{args}'"
48
+ end
49
+ end
50
+
51
+ version = begin
52
+ git_guess = %{git rev-parse HEAD}
53
+ stdout, stderr, status = Open3.capture3(git_guess)
54
+ if status.success?
55
+ stdout.strip
56
+ else
57
+ raise "Attempt to use git via command '#{git_guess}' to figure out the version failed: #{stdout}#{stderr}"
58
+ end
59
+ end
60
+ short_version = version[0..7]
61
+
62
+ platform = options.platform || "linux/amd64"
63
+ heroku_app_name = @app_id
64
+
65
+ out.puts "Reading HerokuConfig:"
66
+ require_relative Brut.container.project_root / "deploy" / "heroku_config"
67
+
68
+ additional_images = HerokuConfig.additional_images.map { |name,config|
69
+ cmd = config.fetch(:cmd)
70
+ out.puts " - #{name} will run #{cmd} in production"
71
+ image_name = %{#{@organization}/#{@app_id}:#{short_version}-#{name}}
72
+ [
73
+ name,
74
+ {
75
+ cmd:,
76
+ image_name:,
77
+ dockerfile: "deploy/Dockerfile.#{name}",
78
+ heroku_image_name: "registry.heroku.com/#{heroku_app_name}/#{name}",
79
+ }
80
+ ]
81
+ }.to_h
82
+
83
+ images = {
84
+ "web" => {
85
+ cmd: "bin/run",
86
+ image_name: %{#{@organization}/#{@app_id}:#{short_version}-web},
87
+ dockerfile: "deploy/Dockerfile.web",
88
+ heroku_image_name: "registry.heroku.com/#{heroku_app_name}/web",
89
+ },
90
+ "release" => {
91
+ cmd: "bin/release",
92
+ image_name: %{#{@organization}/#{@app_id}:#{short_version}-release},
93
+ dockerfile: "deploy/Dockerfile.release",
94
+ heroku_image_name: "registry.heroku.com/#{heroku_app_name}/release",
95
+ },
96
+ }.merge(additional_images)
97
+
98
+ out.puts " - release will run bin/release in production"
99
+
100
+ local_repo_checks.check!
101
+ require_heroku_login!(options)
102
+
103
+ FileUtils.chdir Brut.container.project_root do
104
+
105
+ out.puts "Generating Dockerfiles"
106
+ images.each do |name,metadata|
107
+ cmd = metadata.fetch(:cmd)
108
+ dockerfile = metadata.fetch(:dockerfile)
109
+
110
+ out.puts "Creating '#{dockerfile}' for '#{name}' that will use command '#{cmd}'"
111
+
112
+ File.open(dockerfile,"w") do |file|
113
+ file.puts "# DO NOT EDIT - THIS IS GENERATED"
114
+ file.puts "# To make changes, modifiy deploy/Dockerfile and run #{$0}"
115
+ file.puts File.read("deploy/Dockerfile")
116
+ file.puts
117
+ file.puts "# Added by #{$0}"
118
+ file.puts %{CMD [ "bundle", "exec", "#{cmd}" ]}
119
+ end
120
+ end
121
+
122
+ out.puts "Building images"
123
+ docker_quiet_option = if global_options.log_level != "debug"
124
+ "--quiet"
125
+ else
126
+ ""
127
+ end
128
+ images.each do |name,metadata|
129
+ image_name = metadata.fetch(:image_name)
130
+ dockerfile = metadata.fetch(:dockerfile)
131
+
132
+
133
+ out.puts "Creating docker image with name '#{image_name}' and platform '#{platform}'"
134
+ command = %{docker build #{docker_quiet_option} --build-arg app_git_sha1=#{version} --file #{Brut.container.project_root}/#{dockerfile} --platform #{platform} --tag #{image_name} .}
135
+ system!(command)
136
+ end
137
+
138
+ out.puts "Taggging images for Heroku"
139
+ images.each do |name,metadata|
140
+ image_name = metadata.fetch(:image_name)
141
+ heroku_image_name = metadata.fetch(:heroku_image_name)
142
+
143
+ out.puts "Tagging '#{image_name}' with '#{heroku_image_name}' for Heroku"
144
+ command = %{docker tag #{image_name} #{heroku_image_name}}
145
+ system!(command)
146
+ end
147
+
148
+ if options.push?
149
+ out.puts "Pushing to Heroku Registry"
150
+ images.each do |name,metadata|
151
+ heroku_image_name = metadata.fetch(:heroku_image_name)
152
+
153
+ out.puts "Pushing '#{heroku_image_name}'"
154
+
155
+ command = %{docker push #{docker_quiet_option} #{heroku_image_name}}
156
+ system!(command)
157
+ end
158
+ else
159
+ out.puts "Not pushing images"
160
+ end
161
+
162
+ names = images.map(&:first).join(" ")
163
+ deploy_command = "heroku container:release #{names}"
164
+ if options.deploy?
165
+ out.puts "Deploying images to Heroku"
166
+ system!(deploy_command)
167
+ else
168
+ out.puts "Not deploying. To deploy the images just pushed:"
169
+ out.puts ""
170
+ out.puts " #{deploy_command}"
171
+ end
172
+ end
173
+ end
174
+ private
175
+ def require_heroku_login!(options)
176
+ if system("heroku whoami")
177
+ out.puts "You are logged in to Heroku"
178
+ else
179
+ out.puts "You are not logged into Heroku."
180
+ out.puts "Please run the following:"
181
+ out.puts ""
182
+ out.puts "heroku auth:login"
183
+ out.puts "heroku container:login"
184
+ out.puts ""
185
+ out.puts "Then, re-run this"
186
+ if options.dry_run?
187
+ out.puts "Dry run - ignoring"
188
+ return
189
+ end
190
+ exit 1
191
+ end
192
+ end
193
+ end
194
+ end
@@ -198,6 +198,13 @@ class Brut::CLI::Command
198
198
  def before_execute
199
199
  end
200
200
 
201
+ # Called after all setup has been executed. Brut will have been started/loaded.
202
+ # This will *not* be called if anything caused execution to be aborted.
203
+ #
204
+ # @param [Brut::Framework::App] app Your Brut app.
205
+ def after_bootstrap(app:)
206
+ end
207
+
201
208
  # @!visibility private
202
209
  def set_env_if_needed
203
210
  if self.class.requires_project_env?
@@ -219,19 +226,6 @@ class Brut::CLI::Command
219
226
  raise ex
220
227
  end
221
228
 
222
- # @!visibility private
223
- def bootstrap!(project_root:, configure_only:)
224
- require "bundler"
225
- Bundler.require(:default, ENV["RACK_ENV"].to_sym)
226
- if configure_only
227
- require "#{project_root}/app/pre_boot"
228
- Brut::Framework.new(app: ::App.new)
229
- else
230
- require "#{project_root}/app/boot"
231
- end
232
- continue_execution
233
- end
234
-
235
229
  private
236
230
 
237
231
  # @!visibility public
@@ -16,14 +16,40 @@ class Brut::CLI::Executor
16
16
  @err = err
17
17
  end
18
18
 
19
- # Execute a command, logging it to the standard output and outputing the commands output and error to the standard output and error,
20
- # respecitively
19
+ # Execute a command, logging it to the standard output and outputing the
20
+ # commands output and error to the standard output and error, respectively. If
21
+ # the command exits nonzero, the exit status is returned.
22
+ #
23
+ # Generally, you want to use {#system!} for commands that must succeed
24
+ # for the caller to continue. Only use this method if you need
25
+ # to do special error-handling when the underlying command fails.
26
+ #
27
+ # @see https://docs.ruby-lang.org/en/3.3/Open3.html#method-c-popen3
28
+ # @see #system!
29
+ #
30
+ # @param [String|Array] args Whatever you would give to `Kernel#system` or `Open3.popen3`.
31
+ # @return [int] 0 if the command completed normally, otherwise the nonzero exit status. **DO NOT TREAT THIS AS A BOOLEAN VALUE**
32
+ def system(*args)
33
+ self.system!(*args)
34
+ 0
35
+ rescue Brut::CLI::SystemExecError => e
36
+ e.exit_status
37
+ end
38
+
39
+ # Execute a command, logging it to the standard output and outputing the
40
+ # commands output and error to the standard output and error, respectively. If
41
+ # the command exits nonzero, an exception is raised and your CLI app will
42
+ # also exit nonzero.
43
+ #
44
+ # If you need to handle the command exiting nonzero, use {#system}
45
+ # instead, as it will not raise an exception.
21
46
  #
22
47
  # @see https://docs.ruby-lang.org/en/3.3/Open3.html#method-c-popen3
48
+ # @see #system
23
49
  #
24
50
  # @param [String|Array] args Whatever you would give to `Kernel#system` or `Open3.popen3`.
25
- # @raise Brut::CLI::Error::SystemExecError if the spawed command exits nonzer
26
- # @return [true]
51
+ # @raise Brut::CLI::Error::SystemExecError if the spawed command exits nonzero
52
+ # @return [int] Always returns 0
27
53
  def system!(*args)
28
54
  @out.puts "Executing #{args}"
29
55
  wait_thread = Open3.popen3(*args) do |_stdin,stdout,stderr,wait_thread|
@@ -52,6 +78,6 @@ class Brut::CLI::Executor
52
78
  else
53
79
  raise Brut::CLI::SystemExecError.new(args,wait_thread.value.exitstatus)
54
80
  end
55
- true
81
+ 0
56
82
  end
57
83
  end
@@ -12,6 +12,10 @@ class Brut::CLI::Options
12
12
  # Access an options value directly.
13
13
  # @param [String] key the key to use. This must be the exact name you used when calling `opts.on` or when creating the `OptionParser` for your app or command. Generally, use the {#method_missing}-provided version instead of this.
14
14
  def [](key) = @parsed_options[key]
15
+
16
+ def []=(key,value)
17
+ @parsed_options[key] = value
18
+ end
15
19
  # Check if `key` was provided on the command line.
16
20
  # @param [String] key the key to use. This must be the exact name you used when calling `opts.on` or when creating the `OptionParser` for your app or command.
17
21
  def key?(key) = @parsed_options.key?(key)
data/lib/brut/cli.rb CHANGED
@@ -42,9 +42,10 @@ module Brut
42
42
  # Holds Brut-provided CLI apps that are set up in your project.
43
43
  module Apps
44
44
  autoload(:DB,"brut/cli/apps/db")
45
- autoload(:DB,"brut/cli/apps/test")
46
- autoload(:DB,"brut/cli/apps/build_assets")
47
- autoload(:DB,"brut/cli/apps/scaffold")
45
+ autoload(:Test,"brut/cli/apps/test")
46
+ autoload(:BuildAssets,"brut/cli/apps/build_assets")
47
+ autoload(:Scaffold,"brut/cli/apps/scaffold")
48
+ autoload(:DeployBase,"brut/cli/apps/deploy_base")
48
49
  end
49
50
  end
50
51
  end
@@ -78,7 +78,7 @@ class Brut::Framework::Container
78
78
  self.validate_name!(name:,type:,allow_app_override:)
79
79
  if value == :use_block
80
80
  derive_with = block
81
- @container[name] = { value: nil, derive_with: derive_with }
81
+ @container[name] = { derive_with: derive_with }
82
82
  else
83
83
  if type == :boolean
84
84
  value = !!value
@@ -111,9 +111,11 @@ class Brut::Framework::Container
111
111
  raise ArgumentError,"#{name} does not allow the app to override it"
112
112
  end
113
113
  if value == :use_block
114
- @container[name] = { value: nil, derive_with: block }
114
+ @container[name][:derive_with] = block
115
+ @container[name].delete(:value)
115
116
  else
116
- @container[name] = { value: value }
117
+ @container[name][:value] = value
118
+ @container[name].delete(:derive_with)
117
119
  end
118
120
  end
119
121
 
@@ -191,16 +193,16 @@ class Brut::Framework::Container
191
193
  # TODO: Provide a cleanr impl and better error checking if things go wrong
192
194
  x = @container.fetch(name)
193
195
 
194
- value = x[:value]
196
+ has_value = x.key?(:value)
195
197
 
196
- if !value.nil? || (x[:allow_nil] && value.nil?)
198
+ if has_value
197
199
  handle_path_values(name,x)
198
- return value
200
+ return x[:value]
199
201
  end
200
202
 
201
203
  deriver = x[:derive_with]
202
204
  if deriver.nil?
203
- raise "Something is seriously wrong. '#{name}' was stored in container without a derive_with value"
205
+ raise "Something is seriously wrong. '#{name}' was stored in container without a static value, but also without a derive_with key"
204
206
  end
205
207
 
206
208
  parameters = deriver.parameters(lambda: true)
@@ -218,6 +220,22 @@ class Brut::Framework::Container
218
220
  handle_path_values(name,x)
219
221
  x[:value]
220
222
  end
223
+
224
+ def reload
225
+ @container.each do |name, contained_value|
226
+ if contained_value.key?(:value) && contained_value[:type].to_s == "Class"
227
+ if contained_value.key?(:derive_with)
228
+ contained_value.delete(:value)
229
+ else
230
+ klass = contained_value[:value]
231
+ new_klass = klass.name.split(/::/).reduce(Module) { |mod,part|
232
+ mod.const_get(part)
233
+ }
234
+ contained_value[:value] = new_klass
235
+ end
236
+ end
237
+ end
238
+ end
221
239
  private
222
240
 
223
241
  def handle_path_values(name,contained_value)
@@ -2,4 +2,11 @@
2
2
  # which methods a subclass is expected to override and for which no default behavior
3
3
  # makes sense.
4
4
  class Brut::Framework::Errors::AbstractMethod < Brut::Framework::Error
5
+ def initialize(method_name=nil)
6
+ if method_name
7
+ super("The method `#{method_name}` must be implemented")
8
+ else
9
+ super("An abstract method must be implemented")
10
+ end
11
+ end
5
12
  end
@@ -27,9 +27,11 @@ module Brut
27
27
  # a method that a subclass must implement, but for which there is no useful default
28
28
  # implementation.
29
29
  #
30
+ # @param [String|Symbol] method_name name of the method that must be implemented. If omitted, the value is guessed from `caller_locations`
30
31
  # @raise [Brut::Framework::Errors::AbstractMethod]
31
- def abstract_method!
32
- raise Brut::Framework::Errors::AbstractMethod
32
+ def abstract_method!(method_name=nil)
33
+ method_name ||= caller_locations(1,1)[0].label
34
+ raise Brut::Framework::Errors::AbstractMethod.new(method_name)
33
35
  end
34
36
  end
35
37
  # Base class for errors thrown by Brut classes. Generally, Brut will not create its own version