diff --git a/DEV.md b/DEV.md index 23a25ff..f69eed4 100644 --- a/DEV.md +++ b/DEV.md @@ -25,6 +25,8 @@ git push --atomic origin main $VERSION # TODOs +- HTMX headers consts + - giter8 template for REST - add more validators https://javaee.github.io/javaee-spec/javadocs/javax/validation/constraints/package-summary.html - webjars diff --git a/build.sc b/build.sc index d5b2023..84378a9 100644 --- a/build.sc +++ b/build.sc @@ -15,7 +15,7 @@ object sharaf extends SharafPublishModule { ivy"com.lihaoyi::requests:0.8.0", ivy"ba.sake::tupson:0.11.0", ivy"ba.sake::tupson-config:0.11.0", - ivy"ba.sake::hepek-components:0.25.0" + ivy"ba.sake::hepek-components:0.26.0" ) def moduleDeps = Seq(querson, formson) diff --git a/docs/src/files/philosophy/DependencyInjection.scala b/docs/src/files/philosophy/DependencyInjection.scala index 8cf13fb..f5a816b 100644 --- a/docs/src/files/philosophy/DependencyInjection.scala +++ b/docs/src/files/philosophy/DependencyInjection.scala @@ -17,15 +17,19 @@ object DependencyInjection extends PhilosophyPage { Not in a purely-functional-monadic style. - Yes in a direct, context functions (implicit functions) scala 3 style. - If you ever used PlayFramework, Slick 2 and similar you might be used to this pattern: + Yes in a direct style. + For singletons: + - just instantiate a class and pass the object around + - for request/session-scoped instances use scala 3 context functions (implicit functions) + + If you ever used PlayFramework, Slick 2 and similar, you might have used this pattern: ```scala - someFunction { implicit ctx: Ctx => + someFunction { implicit ctx => // some code that needs an implicit Ctx } ``` - In Scala 3 there is a new concept called "context function" which represents the pattern above through a type: + In Scala 3 there is a new concept called "context function" which represents the pattern from above with a type: ```scala type ContextualAction = Ctx ?=> Unit ``` @@ -35,24 +39,28 @@ object DependencyInjection extends PhilosophyPage { // some code that needs an implicit Ctx } ``` - and compiler will take care of it. + and compiler will fill it in for us. --- - As an example in Sharaf itself, the `Routes` type is defined as `Request ?=> PartialFunction[RequestParams, Response[?]]`. - This means, for example, that you can call `Request.current` only in a `Routes` definition body (because it requires a `given Request`). + Sharaf has the `Routes` type that is defined as `Request ?=> PartialFunction[RequestParams, Response[?]]`. + This means that you can call `Request.current` only in a `Routes` definition body (because it requires a `given Request`). - As a concrete example, instead of making a `@RequestScoped @Bean` like in Spring, you would define a function that requires a `given Request`: + If you need a request-scoped instance (`@RequestScoped @Bean` in Spring), + you need to define a function that is `using Request`: ```scala def currentUser(using req: Request): User = // extract stuff from request ``` + Same as `Request.current`, you can only use the `currentUser` function in a context of a request! + + --- - Plus you avoid [banging your head against the wall](https://stackoverflow.com/questions/26305295/how-is-the-requestscoped-bean-instance-provided-to-sessionscoped-bean-in-runti) + By using context functions, you avoid [banging your head against the wall](https://stackoverflow.com/questions/26305295/how-is-the-requestscoped-bean-instance-provided-to-sessionscoped-bean-in-runti) while trying to figure out how-the-hell can you inject a request-scoped-thing into a singleton/session-scoped thing... Proxy to proxy to proxy, something, something.. ok. - And you avoid reading yet-another-lousy-monad-tutorial, losing your brain-battle agains `State`, `RWS`, `Kleisli`, higher-kinded-types, weird macros, compile times and type inference... + You also avoid reading yet-another-lousy-monad-tutorial, losing your brain-battle agains `State`, `RWS`, `Kleisli`, higher-kinded-types, weird macros, compile times and type inference... """.md ) diff --git a/examples/scala-cli/htmx/htmx_tabs_hateoas.sc b/examples/scala-cli/htmx/htmx_tabs_hateoas.sc index 54009a5..56d4a1b 100644 --- a/examples/scala-cli/htmx/htmx_tabs_hateoas.sc +++ b/examples/scala-cli/htmx/htmx_tabs_hateoas.sc @@ -11,16 +11,15 @@ object IndexView extends HtmlPage with HtmxDependencies: override def bodyContent = div(id := "tabs", hx.get := "/tab1", hx.trigger := "load delay:100ms", hx.target := "#tabs", hx.swap := "innerHTML") -def tabSnippet(tabNum: Int) = div( - div( - cls := "tab-list", - role := "tablist", - button(hx.get := "/tab1", Option.when(tabNum == 1)(cls := "selected"), role := "tab", "Tab 1"), - button(hx.get := "/tab2", Option.when(tabNum == 2)(cls := "selected"), role := "tab", "Tab 2"), - button(hx.get := "/tab3", Option.when(tabNum == 3)(cls := "selected"), role := "tab", "Tab 3") - ), - div(id := "tab-content", role := "tabpanel", cls := "tab-content")(s"TAB ${tabNum} content ....") - ) +def tabSnippet(tabNum: Int) = div( + div( + cls := "tab-list", + button(hx.get := "/tab1", Option.when(tabNum == 1)(cls := "selected"), "Tab 1"), + button(hx.get := "/tab2", Option.when(tabNum == 2)(cls := "selected"), "Tab 2"), + button(hx.get := "/tab3", Option.when(tabNum == 3)(cls := "selected"), "Tab 3") + ), + div(id := "tab-content", cls := "tab-content")(s"TAB ${tabNum} content ....") +) val routes = Routes: case GET() -> Path() =>