Recargar el código de Clojure usando (require … :reload)
y :reload-all
es muy problemático :
Si modifica dos espacios de nombres que dependen el uno del otro, debe recordar volver a cargarlos en el orden correcto para evitar errores de compilación.
Si elimina definiciones de un archivo de origen y luego las vuelve a cargar, esas definiciones aún estarán disponibles en la memoria. Si otro código depende de esas definiciones, seguirá funcionando pero se interrumpirá la próxima vez que reinicie la JVM.
Si el espacio de nombres recargado contiene defmulti
, también debe recargar todas las defmethod
expresiones asociadas .
Si el espacio de nombres recargado contiene defprotocol
, también debe recargar cualquier registro o tipo que implemente ese protocolo y reemplazar cualquier instancia existente de esos registros / tipos con nuevas instancias.
Si el espacio de nombres recargado contiene macros, también debe volver a cargar cualquier espacio de nombres que utilice esas macros.
Si el programa en ejecución contiene funciones que cierran sobre valores en el espacio de nombres recargado, esos valores cerrados no se actualizan. (Esto es común en aplicaciones web que construyen la "pila de manejadores" como una composición de funciones).
La biblioteca clojure.tools.namespace mejora la situación significativamente. Proporciona una función de actualización sencilla que realiza una recarga inteligente basada en un gráfico de dependencia de los espacios de nombres.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
Desafortunadamente, la recarga por segunda vez fallará si el espacio de nombres en el que hizo referencia a la refresh
función cambió. Esto se debe al hecho de que tools.namespace destruye la versión actual del espacio de nombres antes de cargar el nuevo código.
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)
Puede usar el nombre var completamente calificado como solución para este problema, pero personalmente prefiero no tener que escribirlo en cada actualización. Otro problema con lo anterior es que después de recargar el espacio de nombres principal, ya no se hace referencia a las funciones auxiliares estándar de REPL (como doc
y source
).
Para resolver estos problemas, prefiero crear un archivo fuente real para el espacio de nombres del usuario para que pueda recargarse de manera confiable. Puse el archivo fuente, ~/.lein/src/user.clj
pero puedes colocarlo en cualquier lugar. El archivo debería requerir la función de actualización en la declaración ns superior como esta:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
Puede configurar un perfil de usuario Leiningen de ~/.lein/profiles.clj
modo que la ubicación de poner el archivo en se añade a la ruta de clase. El perfil debería verse así:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Tenga en cuenta que configuro el espacio de nombres de usuario como punto de entrada al iniciar REPL. Esto asegura que las funciones auxiliares de REPL sean referenciadas en el espacio de nombres de usuario en lugar del espacio de nombres principal de su aplicación. De esa manera, no se perderán a menos que modifique el archivo fuente que acabamos de crear.
¡Espero que esto ayude!
(use 'foo.bar :reload-all)
siempre me ha funcionado bien. Además,(load-file)
nunca debería ser necesario si tiene su classpath configurada correctamente. ¿Cuál es el "efecto requerido" que no obtiene?