Le rechargement du code Clojure en utilisant (require … :reload)
et :reload-all
est très problématique :
Si vous modifiez deux espaces de noms qui dépendent l'un de l'autre, vous devez vous rappeler de les recharger dans le bon ordre pour éviter les erreurs de compilation.
Si vous supprimez des définitions d'un fichier source et que vous le rechargez, ces définitions sont toujours disponibles en mémoire. Si un autre code dépend de ces définitions, il continuera à fonctionner mais se cassera la prochaine fois que vous redémarrerez la JVM.
Si l'espace de noms rechargé contient defmulti
, vous devez également recharger toutes les defmethod
expressions associées .
Si l'espace de noms rechargé contient defprotocol
, vous devez également recharger tous les enregistrements ou types implémentant ce protocole et remplacer toutes les instances existantes de ces enregistrements / types par de nouvelles instances.
Si l'espace de noms rechargé contient des macros, vous devez également recharger tous les espaces de noms qui utilisent ces macros.
Si le programme en cours d'exécution contient des fonctions qui ferment les valeurs dans l'espace de noms rechargé, ces valeurs fermées ne sont pas mises à jour. (Ceci est courant dans les applications Web qui construisent la «pile de gestionnaires» comme une composition de fonctions.)
La bibliothèque clojure.tools.namespace améliore considérablement la situation. Il fournit une fonction d'actualisation facile qui effectue un rechargement intelligent basé sur un graphique de dépendance des espaces de noms.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
Malheureusement, un deuxième rechargement échouera si l'espace de noms dans lequel vous avez référencé la refresh
fonction a changé. Cela est dû au fait que tools.namespace détruit la version actuelle de l'espace de noms avant de charger le nouveau code.
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)
Vous pouvez utiliser le nom var complet comme solution de contournement à ce problème, mais personnellement, je préfère ne pas avoir à le taper à chaque actualisation. Un autre problème avec ce qui précède est qu'après le rechargement de l'espace de noms principal, les fonctions d'assistance REPL standard (comme doc
et source
) n'y sont plus référencées.
Pour résoudre ces problèmes, je préfère créer un fichier source réel pour l'espace de noms utilisateur afin qu'il puisse être rechargé de manière fiable. J'ai mis le fichier source ~/.lein/src/user.clj
mais vous pouvez le placer n'importe où. Le fichier doit nécessiter la fonction de rafraîchissement dans la déclaration ns supérieure comme ceci:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
Vous pouvez configurer un profil utilisateur leiningen~/.lein/profiles.clj
pour que l'emplacement dans lequel vous placez le fichier soit ajouté au chemin de classe. Le profil doit ressembler à ceci:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Notez que j'ai défini l'espace de noms utilisateur comme point d'entrée lors du lancement du REPL. Cela garantit que les fonctions d'assistance REPL sont référencées dans l'espace de noms utilisateur au lieu de l'espace de noms principal de votre application. De cette façon, ils ne se perdront que si vous modifiez le fichier source que nous venons de créer.
J'espère que cela t'aides!
(use 'foo.bar :reload-all)
a toujours bien fonctionné pour moi. De plus, cela(load-file)
ne devrait jamais être nécessaire si votre chemin de classe est correctement configuré. Quel est l '«effet requis» que vous n'obtenez pas?