Ricaricare il codice Clojure usando (require … :reload)
ed :reload-all
è molto problematico :
Se si modificano due spazi dei nomi che dipendono l'uno dall'altro, è necessario ricordarsi di ricaricarli nell'ordine corretto per evitare errori di compilazione.
Se si rimuovono le definizioni da un file di origine e lo si ricarica, tali definizioni sono ancora disponibili in memoria. Se altro codice dipende da tali definizioni, continuerà a funzionare ma si interromperà al successivo riavvio di JVM.
Se lo spazio dei nomi ricaricato contiene defmulti
, è inoltre necessario ricaricare tutte le defmethod
espressioni associate .
Se lo spazio dei nomi ricaricato contiene defprotocol
, è inoltre necessario ricaricare tutti i record o tipi che implementano quel protocollo e sostituire eventuali istanze esistenti di tali record / tipi con nuove istanze.
Se lo spazio dei nomi ricaricato contiene macro, è necessario ricaricare anche gli spazi dei nomi che utilizzano tali macro.
Se il programma in esecuzione contiene funzioni che chiudono i valori nello spazio dei nomi ricaricato, tali valori chiusi non vengono aggiornati. (Questo è comune nelle applicazioni web che costruiscono lo "stack gestore" come una composizione di funzioni.)
La libreria clojure.tools.namespace migliora significativamente la situazione. Fornisce una funzione di aggiornamento facile che esegue un ricaricamento intelligente basato su un grafico delle dipendenze degli spazi dei nomi.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
Sfortunatamente il ricaricamento una seconda volta fallirà se lo spazio dei nomi in cui si fa riferimento alla refresh
funzione è cambiato. Ciò è dovuto al fatto che tools.namespace distrugge la versione corrente dello spazio dei nomi prima di caricare il nuovo codice.
myapp.web=> (refresh)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: refresh in this context, compiling:(/private/var/folders/ks/d6qbfg2s6l1bcg6ws_6bq4600000gn/T/form-init819543191440017519.clj:1:1)
È possibile utilizzare il nome var completo come soluzione alternativa per questo problema, ma personalmente preferisco non doverlo digitare ad ogni aggiornamento. Un altro problema con quanto sopra è che dopo aver ricaricato lo spazio dei nomi principale le funzioni di supporto REPL standard (come doc
e source
) non vengono più citate lì.
Per risolvere questi problemi, preferisco creare un file sorgente effettivo per lo spazio dei nomi utente in modo che possa essere ricaricato in modo affidabile. Ho inserito il file sorgente ~/.lein/src/user.clj
ma puoi inserirlo ovunque. Il file dovrebbe richiedere la funzione di aggiornamento nella dichiarazione ns in alto in questo modo:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
È possibile impostare un profilo utente leiningen in ~/.lein/profiles.clj
modo che la posizione in cui è stato inserito il file sia aggiunta al percorso della classe. Il profilo dovrebbe assomigliare a questo:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Si noti che ho impostato lo spazio dei nomi utente come punto di ingresso all'avvio di REPL. Ciò garantisce che le funzioni di supporto REPL vengano referenziate nello spazio dei nomi utente anziché nello spazio dei nomi principale dell'applicazione. In questo modo non andranno persi se non si modifica il file di origine che abbiamo appena creato.
Spero che questo ti aiuti!
(use 'foo.bar :reload-all)
ha sempre funzionato bene per me. Inoltre,(load-file)
non dovrebbe mai essere necessario se il tuo percorso di classe è impostato correttamente. Qual è l '"effetto richiesto" che non ottieni?