Ponowne ładowanie kodu Clojure przy użyciu (require … :reload)i :reload-alljest bardzo problematyczne :
Jeśli zmodyfikujesz dwie przestrzenie nazw, które są od siebie zależne, musisz pamiętać o ich ponownym załadowaniu we właściwej kolejności, aby uniknąć błędów kompilacji.
Jeśli usuniesz definicje z pliku źródłowego, a następnie załadujesz go ponownie, te definicje będą nadal dostępne w pamięci. Jeśli inny kod zależy od tych definicji, będzie nadal działał, ale zepsuje się przy następnym ponownym uruchomieniu maszyny JVM.
Jeśli ponownie załadowana przestrzeń nazw zawiera defmulti, należy również ponownie załadować wszystkie skojarzone z nią defmethodwyrażenia.
Jeśli przeładowana przestrzeń nazw zawiera defprotocol, należy również ponownie załadować wszelkie rekordy lub typy implementujące ten protokół i zastąpić wszystkie istniejące instancje tych rekordów / typów nowymi instancjami.
Jeśli przeładowana przestrzeń nazw zawiera makra, należy również ponownie załadować wszystkie przestrzenie nazw, które używają tych makr.
Jeśli uruchomiony program zawiera funkcje, które zamykają wartości w przeładowywanej przestrzeni nazw, te zamknięte wartości nie są aktualizowane. (Jest to powszechne w aplikacjach internetowych, które konstruują „stos obsługi” jako kompozycję funkcji).
Biblioteka clojure.tools.namespace znacznie poprawia sytuację. Zapewnia łatwą funkcję odświeżania, która wykonuje inteligentne ponowne ładowanie w oparciu o wykres zależności przestrzeni nazw.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
Niestety ponowne załadowanie nie powiedzie się, jeśli przestrzeń nazw, w której odwołujesz się do refreshfunkcji, ulegnie zmianie. Wynika to z faktu, że tools.namespace niszczy aktualną wersję przestrzeni nazw przed załadowaniem nowego kodu.
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)
Możesz użyć w pełni kwalifikowanej nazwy var jako obejścia tego problemu, ale osobiście wolę nie wpisywać tego przy każdym odświeżaniu. Innym problemem związanym z powyższym jest to, że po przeładowaniu głównej przestrzeni nazw nie ma tam odwołań do standardowych funkcji pomocniczych REPL (takich jak doci source).
Aby rozwiązać te problemy, wolę utworzyć rzeczywisty plik źródłowy dla przestrzeni nazw użytkownika, aby można go było niezawodnie ponownie załadować. Umieściłem plik źródłowy, ~/.lein/src/user.cljale możesz go umieścić w dowolnym miejscu. Plik powinien wymagać funkcji odświeżania w górnej deklaracji ns w następujący sposób:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
Możesz skonfigurować profil użytkownika leiningen w ~/.lein/profiles.cljtak, aby lokalizacja, w której umieściłeś plik, była dodawana do ścieżki klasy. Profil powinien wyglądać mniej więcej tak:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Zauważ, że ustawiłem przestrzeń nazw użytkownika jako punkt wejścia podczas uruchamiania REPL. Gwarantuje to, że do funkcji pomocniczych REPL będą odwoływać się przestrzeń nazw użytkownika, a nie główna przestrzeń nazw aplikacji. W ten sposób nie zgubią się, chyba że zmienisz plik źródłowy, który właśnie utworzyliśmy.
Mam nadzieję że to pomoże!
(use 'foo.bar :reload-all)zawsze działało dobrze dla mnie. Ponadto(load-file)nie powinno być nigdy konieczne, jeśli masz prawidłowo skonfigurowaną ścieżkę klas. Jaki jest „wymagany efekt”, którego nie osiągasz?