This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-08-01
Channels
- # announcements (4)
- # babashka (12)
- # calva (8)
- # clj-kondo (40)
- # clojure (39)
- # clojure-europe (23)
- # clojure-losangeles (1)
- # clojure-nl (1)
- # clojure-norway (52)
- # clojure-uk (3)
- # community-development (6)
- # cursive (1)
- # datomic (14)
- # devops (1)
- # gratitude (21)
- # humbleui (7)
- # hyperfiddle (7)
- # introduce-yourself (2)
- # java (5)
- # portland-or (4)
- # shadow-cljs (69)
- # tools-deps (4)
- # xtdb (6)
Morn!
Jeg har prøvd #C06TTMERLEP på tampen av ferien - en plattform for å kjøre clojure-apper. Tenk Heroku, men kun for Clojure. Her er en 200 linjers app: https://go.teod.eu/ / https://github.com/teodorlu/go.teod.eu Liker deploy-plattformen godt så langt. Deploy av ny versjon tar 1-2 sekunder for appen min.
@magnars Velkommen tilbake fra ferie! Vi (eller jeg, da) som har vært på jobb noen dager nå prøver å få Optimus til å bygge og optimalisere assets i byggsteget, og pakke disse assetene i uberjaren. Formålet er å unngå alt dette arbeidet under app-oppstart, for det tar ganske lang tid. Skulle gjerne stilt deg noen spørsmål - ta det her, eller foretrekker du GH-issue i Optimus-repoet?
Jeg pleier be Optimus skrive optimaliserte assets til disk for mine statiske nettsider.
Jeg kjenner til optimus.export/save-assets
, den er fin. Da kan jeg pakke disse filene i uberjaren. Men så, på andre siden, når appen skal starte, så trenger middlewaren å få vite om disse assetene.
Du tenker kanskje på å få på lange expires headers og sånt. Det gjør jo jeg da med nginx-konfig.
Høres ut som man må eksportere en slags manifest-fil som inneholder metadataene om assetene.
Ja, det er så langt jeg har kommet, men så stanger jeg på binære assets: de med :resource
.
Det gjør de, men verdien til en :resource
er en java.io-dings. Så ikke "rene" data. Kan altså ikke bare spit/slurp-e en slik manifest.edn-fil.
:resource #object[java.net.URL 0x66601b05 "file:/Users/martin/c/foo/bar/image.gif"
- sånn ser det ut.
Ved bygg:
(defn export-assets []
(let [assets (optimize (get-assets ,,,))]
(optimus.export/save-assets assets export-directory)
(spit "assets-manifest.edn" (for [asset assets]
(dissoc asset :content :resource)))))
Ved oppstart:
(defn get-prod-assets []
(for [asset (slurp "assets-manifest.edn")]
(assoc asset :resource (io/resource (:uri asset)))))
Da lagrer du alle metadata om alle assets, men fjerne innholdet (som du lagrer på disk)
Skal prøve litt nå - kanskje du vil ha noe til en liten seksjon in README-en om dette hvis det virker?
Foreløpige resultater er veldig gode. Har barbert gode 20 sekunder av oppstartstiden på en app her. Skriver mer i morra neste uke!
Thank you #rubber-duck-norway! Etter å ha skrevet en liten avhandling for å spørre om makro versus funksjon, fant jeg egentlig ut at det beste er nok å gjøre minst mulig fancy greier. For å kanskje ta det som var noe interessant i det jeg skrev, så lurte jeg for eksempel på om det var noen ulemper med kjøretidsgenererte klasser (bruk av funksjon for å interne en funksjon) versus AOT-kompilerte klasser (defn i en makro), tar det lenger tid å varme opp en kjøretidsgenerert klasse og få JIT-kompilatoren i gang? Tar det noe merkbar tid å generere disse indre klassene? Er det noen ulemper minnemessig? Endte uansett opp med at jeg dropper begge deler og beholder en helt vanlig defn, og heller lager en liten funksjon for å registrere disse funksjonene i et handler-register (et atom med en map). Jeg reloader nesten alltid hele namespacet på nytt i REPL istedenfor å bare evaluere én defn, så det påvirker ikke arbeidsflyten noe særlig om det er ett funksjonskall til slutt i namespacet for å registrere handlerne.
Husk også at du kan bruke var-quota funksjoner i mappet ditt, slik at den nyeste versjonen blir brukt, selv uten å re-evaluere mappet.
Jeg har gjort det samme selv noen ganger - først skrevet en makro, så funnet ut at jeg like gjerne kunne brukt en funksjon. Funksjon+annen datastruktur kan noen ganger gi det samme som en makro, med færre overraskelser enn det en makro gir.
Har aldri hatt noe behov for var-quoting før. Visste ikke at en funksjonsreferanse kunne kalles direkte uten å derefereres. Burde kanskje lese litt mer av Java-kildekoden til Clojure en gang. Fra Var:
public final IFn fn() {
return (IFn)this.deref();
}
public Object call() {
return this.invoke();
}
En fordel med å ha en funksjon og et atom er at om jeg skal legge til nye handlere (handlere er gruppert i forskjellige navnerom for de forskjellige APIene de kaller videre på), så blir de nye handlerne registrert med en gang. Hmm, må tenke litt på hva som er mest praktisk. Liker å slippe å bruke tilstand om jeg kan det.
Bruker en merge på toppnivå i dag for å slå sammen maps fra hvert navnerom, men kunne jo lagt alle på toppnivå. Synes bare at den store mappen blir litt rotete.
Da ville jeg nok gått for def
og alter-var-root
, https://clojuredocs.org/clojure.core/alter-var-root
Jeg vil bare ha litt bedre ergonomi når jeg jobber med koden, så jeg slipper å reloade et ekstra navnerom. Også kjent som latskap.
Jeg har pleid å gjøre noe sånt:
;; Et sentralt register for handlere:
(ns my-app.handlers)
(defonce handlers (atom {}))
(defn defhandler [id params]
;; bra plass for validering av params
(swap! handlers assoc id params))
(defn by-id [id]
(get @handlers id))
;; Hver handler kan da registrere seg selv:
(ns my-app.handlers.foo-handler
(:require [my-app.handlers :refer [defpage]]))
(defn handle [,,,] ,,,)
(defhandler :foo-handler
{:param 1
:infos 2
:fn #'handle})
;; Og så må alt dras inn en plass
(ns my-app.the-handlers
(:require '[my-app.handlers.foo-handler]))
Det blir en ja-takk-begge-deler-versjon. Da får man registreringen gjort på samme plass som definisjonen, som er det jeg liker med atom-varianten, og den registrerer seg i registeret med en referanse, så man kan endre funksjonen i REPL uten å swappe den inn på nytt.
Endte opp med å bare bruke var-quote og et namespace som bare inneholder registeret, med grupperinger av handlere som merges sammen i samme fil:
(ns my-app.handler-registry
(:require [my-app.services.ns1 :as ns1]
...)
(def ns1-handlers
{:ns1/handler1 #'ns1/handler1
...})
(def handlers
(merge
ns1-handlers
...))
Føles som det er enkelt nok å finne fram i. Men så er det et par år siden det var noen ferske inne og jobbet med appen, så litt vanskelig å teste ut kanskje.