Yannick,
Si tu veux profiter de la productivité du repl il faut effectivement que tu travailles dans le repl ou du moins dans sa jvm.
Plus généralement si tu veux du rechargement de code, il faut que les modifications de ton code impactent la jvm où tourne ton application.
Petit récap jvm
- lein repl => jvm 1
- Tu lances un repl local à ton éditeur/IDE => jvm 2
- Tu connectes ton éditeur/IDE à "remote repl" lancé avec lein => jvm1 (le code évalué dans l'éditeur est "visible" dans la console lein repl)
- lein run => jvm 3
- lein ring server => jvm 4
Maintenant tu veux du rechargement de code, tu as différentes techniques plus ou moins efficaces ou plus ou moins à la mode.
1 - Les outils de base de Clojure (évaluation & require)
Clojure fourni un mécanisme de rechargement de code dans le repl, il suffit en générale de ré-évaluer une expression. Quand on a fait des changements un peu partout, difficile de savoir quoi ré-évaluer alors on peut utiliser `require` avec les options :reload ou :reload-all.
En utilisant `require` les vars définies avec `defonce` ne seront pas affectées, ce qui veux dire que si tu as un état de ton appli défini avec `defonce`, après rechargement tu conserves ton état.
1.1 - Require manuel
- Tu démarres un repl, tu connectes ton IDE au repl, vous êtes dans la même jvm youpi
- Tu démarres ton serveur web dans le repl avec ton handler ring (oui oui tout seul comme un grand du démarre jetty)
- Ya plus qu'à faire des modifs de code, a ré-évaluer des expressions ou à faire des `require` :reload
points + : IDE et repl sont liés (introspection des vars, etc), utilisation possible de `defonce`
points - : on démarre tout soi-même dans la jvm du repl
1.2 - Require automatique avec Lein Ring
Si tu utilises le plugin Ring de Lein, celui-ci démarre un serveur web avec ton handler dans une jvm spécifique qui n'est pas liée à ton IDE.
Le plugin surveille les fichiers sources, dès qu'il y a des changements il fait des `require` en tache de fond.
points + : Ya plus qu'à coder, utilisation possible de `defonce`
points - : Le code évaluer dans le repl de l'IDE n'a pas de lien avec la jvm du plugin, donc pas possible de faire de l'introspection des vars du programme
2 - Un rechargement plus robuste : clojure.tools.namespace.repl
Je te laisse lire plus en détail le README du projet clojure.tools.namespace, mais le `require` de Clojure peut louper quelques redéfinitions notamment lorsque tu utilises les protocols, les multimethods, les macros entre différents namespaces.
Donc M. Sierra à fait cette lib pour faire tous les `require` nécessaires au bon rechargement de ton code, et comme il veut un état bien propre après rechargement il vire même les `defonce`.
2.1 - Emergence d'un workflow
Travailler avec c.t.n.repl impose un certain workflow, qui est un peu expliqué dans le README du projet. En effet on ne peut conserver l'état de l'application après rechargement, il faut donc avoir un moyen de re-créer rapidement cet état. M. Sierra propose de se faire des fonctions d'aides à la création/destruction de l'état du programme dans le namespace 'user' (namespace par défaut du repl qui peut être adossé à un fichier user.clj dans le classpath).
2.2 - Component
M. Sierra a poussé la réflexion plus loin et a fini par faire la lib Component pour formaliser un workflow adapté à l'utilisation de c.t.n.repl.
2.3 - Mount
D'aucuns diront qu'utiliser Component demande de se soumettre à une façon d'exprimer ses dépendances qui ressemble beaucoup aux concepts OO avec une utilisation massive des Records.
D'où la création de la lib Mount pour n'utiliser que des fonctions et faire confiance à Clojure pour gérer la dépendance entre namespace.
Voilà voilà j'avais un peu de temps libre ;)