Rechargement du code Clojure à l'aide de (require … :reload)
y :reload-all
es très problématique :
-
Si vous modifiez deux espaces de noms qui dépendent l'un de l'autre, vous devez penser à les recharger dans le bon ordre pour éviter des erreurs de compilation de compilation.
-
Si vous supprimez les définitions d'un fichier source et que vous le rechargez ensuite, ces définitions sont toujours disponibles en mémoire. Si un autre code dépend de ces définitions, il continuera à fonctionner mais sera mais sera interrompu au prochain redémarrage de la JVM.
-
Si l'espace de noms rechargé contient defmulti
vous devez également recharger tous les éléments associés defmethod
expressions.
-
Si l'espace de noms rechargé contient defprotocol
vous devez également recharger tout enregistrement ou type 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 tout espaces de noms qui utilisent ces macros.
-
Si le programme en cours d'exécution contient des fonctions qui ferment sur des valeurs dans le fichier l'espace de nom rechargé, ces valeurs fermées ne sont pas mises à jour. (Ceci est courant dans les applications Web qui construisent la "pile de gestionnaires stack" comme une composition de fonctions).
La bibliothèque clojure.tools.namespace améliore la situation de manière significative. Elle fournit une fonction de rafraîchissement facile qui effectue un rechargement intelligent basé sur un graphe de dépendances des espaces de noms.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
Malheureusement, le rechargement une seconde fois échouera si l'espace de nom dans lequel vous avez référencé le fichier refresh
fonction modifiée. 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 pourriez utiliser le nom de var pleinement qualifié comme solution de rechange à ce problème, mais personnellement, je préfère ne pas avoir à le saisir à chaque rafraîchissement. 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'aide standard du REPL (telles que doc
y source
) n'y sont plus référencés.
Pour résoudre ces problèmes, je préfère créer un fichier source réel pour l'espace de noms de l'utilisateur afin qu'il puisse être rechargé de manière fiable. Je place le fichier source dans ~/.lein/src/user.clj
mais vous pouvez le placer n'importe où. Le fichier doit exiger la fonction de rafraîchissement dans la déclaration de la ns supérieure, comme ceci :
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
Vous pouvez configurer un profil d'utilisateur de leiningen sur ~/.lein/profiles.clj
afin que l'emplacement dans lequel vous placez le fichier soit ajouté au chemin de la classe. Le profil doit ressembler à quelque chose comme 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 nom de l'utilisateur comme point d'entrée lors du lancement du REPL. Cela garantit que les fonctions d'aide du REPL seront référencées dans l'espace de noms de l'utilisateur au lieu de l'espace de noms principal de votre application. De cette façon, elles ne seront pas perdues si vous ne modifiez pas le fichier source que nous venons de créer.
J'espère que cela vous aidera !
21 votes
(use 'foo.bar :reload-all)
a toujours bien fonctionné pour moi. Aussi,(load-file)
ne devrait jamais être nécessaire si votre classpath est bien configuré. Quel est "l'effet requis" que vous n'obtenez pas ?0 votes
Oui, qu'est-ce que "l'effet requis" ? Affichez un échantillon
bar.clj
en détaillant "l'effet recherché".1 votes
Par effet requis, je voulais dire que si j'avais une fonction
(defn f [] 1)
et j'ai changé sa définition en(defn f [] 2)
il m'a semblé qu'après avoir émis(use 'foo.bar :reload-all)
et appeler lef
il devrait retourner 2, et non 1. Malheureusement, cela ne fonctionne pas de cette façon pour moi et chaque fois que je change le corps de la fonction, je dois redémarrer le REPL.0 votes
Vous devez avoir un autre problème dans votre installation...
:reload
o:reload-all
devraient tous deux fonctionner.