Fork me on GitHub
#clojure-norway
<
2023-05-08
>
leifericf06:05:55

God morgen, folkens!

magnars07:05:53

Morn morn!

teodorlu07:05:02

Morn! 😊

augustl07:05:53

god morgen

oddsor14:05:11

Jeg ble sittende og syssle litt for lenge med et scoreboard i gĂ„r, bygd pĂ„ en clojure-backend med integrant-komponenter og reitit-ruting. En ting jeg begynte Ă„ lure litt pĂ„ er hva som er best practise for Ă„ fĂ„ dynamisk reloada ruting mens man utvikler, spesielt hvis man har latt det gĂ„ sĂ„ langt som jeg har med at integrant-komponenten tar inn ruter som avhengigheter 😬 For Ăžyeblikket jukser jeg ved Ă„ redeffe funksjoner fĂžr de gĂ„r inn i ruten:

(defmethod ig/init-key ::routes [_ {:keys [db-conn]}]
  (let [admin (fn [db-conn req] (#'admin db-conn req))
        get-scores-admin (fn [db req] (#'get-scores-admin db req))
        add-score (fn [db req] (#'add-score db req))]
    [["/" {:get (partial home db-conn)}]
...
Tydeligvis liker ikke reitit at man gjĂžr om handlere i ruten til vars :thinking_face: Kan hende spĂžrsmĂ„let er vagt, men kanskje det er noe noen har vĂŠrt borti fĂžr og har noen tips! 🙏 đŸ€ž

oddsor14:05:47

GjĂžr jeg ikke det sneaky trikset mĂ„ jeg restarte systemet for Ă„ fĂ„ oppdatert handleren, som er litt kjedelig. Jeg kan gĂ„ andre veien og slĂ„ sammen all rutingen i en funksjon (som er “varifisert”) slik at den bare kan sendes til repl’et, men da blir systemet litt mer sammenkoblet, som er noe jeg hĂ„per Ă„ unngĂ„

isak15:05:58

Jeg tror jeg har et eksempel:

(ns clojure-web-template.core
  (:gen-class) ; for -main method in uberjar
  (:require
    [config.core :refer [env reload-env]]
    [io.pedestal.http]
    [integrant.core :as ig]
    [integrant.repl :refer [clear go halt prep init reset reset-all]]
    [integrant.repl.state :refer [system]]
    [io.pedestal.log :as log]))

(defn service-config []
  (reload-env)
  {:clojure-web-template.services.web/service {}})

(integrant.repl/set-prep! service-config)

(ig/load-namespaces (service-config))

(defonce app-started? (atom false))

(when (and (not @app-started?) (not= "prod" :mode env))
  (reset! app-started? true)
  (go))

(defn -main
  [& args]
  (log/info :msg "Starting app...")
  (let [p (promise)]
    (go)
    @p))
Og web:
(ns clojure-web-template.services.web
  (:require
    [config.core :refer [env]]
    [integrant.core :as ig]
    [io.pedestal.http :as http]
    [io.pedestal.log :as log]
    [io.pedestal.interceptor :as interceptor]
    [io.pedestal.http.route :as route]
    [io.pedestal.http.body-params :as body-params]
    [ring.util.response :as ring-resp]
    [ring.util.response :as ring-response]
    [hiccup2.core :refer [html]])
  (:import (clojure.lang IDeref)))

(defn about-page
  [request]
  (ring-resp/response (format "Clojure %s - served from %s"
                              (clojure-version)
                              (route/url-for ::about-page))))

(defn home-page
  [request]
  (ring-response/response
    (str
      (html
        {:mode :xhtml}
        [:html
         [:head
          [:meta {:charset "utf-8"}]
          [:title "App"]
          [:link {:rel "stylesheet" :href "/dist/styles_css.css"}]]
         [:body
          [:div#app]
          [:script {:src "/assets/js/main.js"}]]]))))

;; Defines "/" and "/about" routes with their associated :get handlers.
;; The interceptors defined after the verb map (e.g., {:get home-page}
;; apply to / and its children (/about).
(def common-interceptors [(body-params/body-params) http/html-body])

;; Tabular routes
(def routes #{["/" :get (conj common-interceptors `home-page)]
              ["/about" :get (conj common-interceptors `about-page)]})

(def fix-charset
  {:name  ::fix-utf8
   :leave (fn [context]
            (update context :response
                    (fn [resp]
                      (ring-response/charset resp "utf-8"))))})

(defn base-service-config [system dev?]
  (-> {:env                     :prod
       ;; You can bring your own non-default interceptors. Make
       ;; sure you include routing and set it up right for
       ;; dev-mode. If you do, many other keys for configuring
       ;; default interceptors will be ignored.
       ;; ::http/interceptors []

       ;; do not block thread that starts web server
       ::http/join?             false

       ::http/routes            (if dev?
                                  #(route/expand-routes (deref #'routes))
                                  (route/expand-routes routes))

       ::http/resource-path "/public"

       ;; Uncomment next line to enable CORS support, add
       ;; string(s) specifying scheme, host and port for
       ;; allowed source(s):
       ;;
       ;; ""
       ;;
       ;;::http/allowed-origins [":port"]

       ;; Tune the Secure Headers
       ;; and specifically the Content Security Policy appropriate to your service/application
       ;; For more information, see: 
       ;;   See also: 
       ::http/secure-headers    {:content-security-policy-settings {:object-src "'none'"
                                                                    ;:script-src "'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:"
                                                                    ;:frame-ancestors "'none'"
                                                                    }}

       ;; Root for resource interceptor that is available by default.
       ;::http/resource-path     "/public"

       ;; Either :jetty, :immutant or :tomcat (see comments in project.clj)
       ;;  This can also be your own chain provider/server-fn -- 
       ::http/type              :jetty
       ;;::http/container-options {:context-configurator #(ws/add-ws-endpoints % ws-paths)}
       ;;::http/host "localhost"
       ::http/port              (:port env)

       ::http/container-options {:h2c? true
                                 :h2?  false
                                 ;:keystore "test/hp/keystore.jks"
                                 ;:key-password "password"
                                 ;:ssl-port 8443
                                 :ssl? false}
       #_#_::http/ext-mime-types {"htm"  "text/html;charset=utf-8"
                                  "html" "text/html;charset=utf-8"}}
      http/default-interceptors

      (update ::http/interceptors
              (fn [xs s] (into [s] xs))
              (interceptor/interceptor fix-charset))
      (update ::http/interceptors
              (fn [xs s] (into [s] xs))
              (interceptor/interceptor
                {:name  :add-system
                 :enter (fn [ctx]
                          (update
                            ctx
                            :request
                            assoc
                            ;; Add system, which has the db-pool
                            ;; Wrap in IDeref to prevent explosion when printed in an exception.
                            :system (reify IDeref
                                      (deref [_] system))))}))))

(defn add-dev-config [service-map]
  (-> service-map
      (merge {:env                   :dev
              ;; Routes can be a function that resolve routes,
              ;;  we can use this to set the routes to be reloadable
              ;; all origins are allowed in dev mode
              ::http/allowed-origins {:creds true :allowed-origins (constantly true)}
              ;; Content Security Policy (CSP) is mostly turned off in dev mode
              ::http/secure-headers  {:content-security-policy-settings {:object-src "'none'"}}})
      http/dev-interceptors))

(defmethod ig/init-key ::service
  [_ system]
  (let [dev?        (not= "prod" :mode env)
        service-map (cond-> (base-service-config system dev?)
                            dev? add-dev-config)]
    (log/info :msg "Starting" :dev? dev? :service ::service)

    {:system system
     :server (-> service-map
                 http/create-server
                 http/start)}))

(defmethod ig/halt-key! ::service [_ {:keys [system server]}]
  (when server
    (log/info :msg "Halting" :service ::service)
    (http/stop server)))

isak15:05:06

SĂ„ TLDR: For :routes key:

(if dev?
                                  #(route/expand-routes (deref #'routes))
                                  (route/expand-routes routes))
og rebuild service map i component lifecycle.

isak15:05:00

Ah jeg ser at du spĂžrr om reitit, da blir det sikkert litt annerledes

oddsor16:05:17

godt mulig! men jeg ser jo at jeg helt har glemt Ă„ se i integrant.repl-namespacet picard-facepalm Er sikkert noe der som kan hjelpe!

augustl07:05:22

dette er en litt interessant “skyggeside” av Clojure du er inne pĂ„ synes jeg. NĂ„r man er dreven og har ting i fingra sĂ„ gjĂžr man det alltid riktig og har ikke problemer med at ting ikke reloades riktig osv. Men det tar litt tid Ă„ komme dit, og man fĂ„r det ikke “gratis” ut av boksen fra dag 1

oddsor13:05:33

Tja, nĂ„r jeg starta Ă„ skrive kodebasen sĂ„ hadde jeg jo et mer “koblet” oppsett hvor hver ny last av namespacet reloadet rutingen, sĂ„ da funka det fint Ă„ iterere raskt og legge til/endre nye ruter. Men sĂ„ begynte jeg med fancy overforbruk av integrant og endte opp i en situasjon hvor rutingen ikke enkelt kunne reloades sammen med namespacet.

oddsor13:05:11

I starten kunne jeg jo bare “varifisere” handleren som gikk inn i serveren, da funka det greit 😁