¿Cuál es la forma preferida de recargar funciones definidas en un archivo Clojure sin tener que reiniciar el REPL? En este momento, para utilizar el archivo actualizado, tengo que:
- editar
src/foo/bar.clj
- cerrar la REPL
- abre el REPL
(load-file "src/foo/bar.clj")
(use 'foo.bar)
Además, (use 'foo.bar :reload-all)
no genera el efecto requerido, que es evaluar los cuerpos modificados de funciones y devolver nuevos valores, en lugar de comportarse como si la fuente no hubiera cambiado en absoluto.
Documentación:
clojure
reload
read-eval-print-loop
leiningen
pkaleta
fuente
fuente
(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?bar.clj
detallando el "efecto requerido".(defn f [] 1)
y cambiaba su definición a(defn f [] 2)
, me parecía que después de emitir(use 'foo.bar :reload-all)
y llamar a laf
función debería devolver 2, no 1. Desafortunadamente, no funciona de esa manera para mí y para todos vez que cambio el cuerpo de la función tengo que reiniciar el REPL.:reload
o:reload-all
ambos deberían funcionar.Respuestas:
O
(use 'your.namespace :reload)
fuente
:reload-all
también debería funcionar. El OP dice específicamente que no lo hace, pero creo que había algo más mal en el entorno de desarrollo del OP porque para un solo archivo los dos (:reload
y:reload-all
) deberían tener el mismo efecto. Aquí está el comando completo para:reload-all
:(use 'your.namespace :reload-all)
Esto también recarga todas las dependencias.También hay una alternativa como usar tools.namespace , es bastante eficiente:
user=> (use '[clojure.tools.namespace.repl :only (refresh)]) user=> (refresh) :reloading (namespace.app) :ok
fuente
(refresh)
también parece hacer que el REPL olvide lo que ha requeridoclojure.tools.namespace.repl
. Las llamadas posteriores a(refresh)
le darán una RuntimeException, "No se puede resolver el símbolo: actualizar en este contexto". Probablemente lo mejor que puede hacer es o bien(require 'your.namespace :reload-all)
, o, si se sabe que va a querer refrescar la REPL mucho para un proyecto dado, hacer un:dev
perfil y añadir[clojure.tools.namespace.repl :refer (refresh refresh-all)]
adev/user.clj
.Recargar el código de Clojure usando
(require … :reload)
y:reload-all
es muy problemático :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
ysource
).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!
fuente
:source-paths
I get#<FileNotFoundException java.io.FileNotFoundException: Could not locate user__init.class or user.clj on classpath: >
, mientras que con:resource-paths
todo está bien.:resource-paths
, estoy en mi espacio de nombres de usuario dentro de repl.reload
problema. Luego resultó que todo lo que pensaba que estaba funcionando ya no funcionaba. ¿Quizás alguien debería arreglar esta situación?La mejor respuesta es:
(require 'my.namespace :reload-all)
Esto no solo recargará su espacio de nombres especificado, sino que también recargará todos los espacios de nombres de dependencia.
Documentación:
exigir
fuente
lein repl
Coljure 1.7.0 y nREPL 0.3.5. Si es nuevo en clojure: el espacio de nombres ('my.namespace
) se define con(ns ...)
insrc/
.../core.clj
, por ejemplo.proj.stuff.core
refleja la estructura del archivo en el discosrc/proj/stuff/core.clj
, el REPL puede localizar el archivo correcto y usted no lo necesitaload-file
.Una línea basada en la respuesta de papachan:
(clojure.tools.namespace.repl/refresh)
fuente
Lo uso en Lighttable (y el increíble instarepl) pero debería ser útil en otras herramientas de desarrollo. Estaba teniendo el mismo problema con las definiciones antiguas de funciones y métodos múltiples que se encuentran después de las recargas, así que ahora durante el desarrollo en lugar de declarar espacios de nombres con:
(ns my.namespace)
Declaro mis espacios de nombres así:
(clojure.core/let [s 'my.namespace] (clojure.core/remove-ns s) (clojure.core/in-ns s) (clojure.core/require '[clojure.core]) (clojure.core/refer 'clojure.core))
Bastante feo, pero cada vez que vuelvo a evaluar todo el espacio de nombres (Cmd-Shift-Enter en Lighttable para obtener los nuevos resultados de instarepl de cada expresión), destruye todas las definiciones antiguas y me brinda un entorno limpio. Antes de empezar a hacer esto, me tropezaba cada pocos días con definiciones antiguas y me ha salvado la cordura. :)
fuente
¿Intentar cargar archivo de nuevo?
Si está utilizando un IDE, generalmente hay un atajo de teclado para enviar un bloque de código al REPL, redefiniendo así de manera efectiva las funciones asociadas.
fuente
Tan pronto como
(use 'foo.bar)
funcione para usted, significa que tiene foo / bar.clj o foo / bar_init.class en su CLASSPATH. Bar_init.class sería una versión compilada por AOT de bar.clj. Si lo haces(use 'foo.bar)
, no estoy exactamente seguro de si Clojure prefiere la clase sobre clj o al revés. Si prefiere archivos de clase y tiene ambos archivos, entonces está claro que editar el archivo clj y luego volver a cargar el espacio de nombres no tiene ningún efecto.Por cierto: no es necesario
load-file
antes deluse
si su CLASSPATH está configurado correctamente.BTW2: Si necesita usarlo
load-file
por alguna razón, simplemente puede hacerlo nuevamente si editó el archivo.fuente