Recarregar o código Clojure usando (require … :reload)
e :reload-all
é muito problemático :
Se você modificar dois espaços para nome que dependem um do outro, lembre-se de recarregá-los na ordem correta para evitar erros de compilação.
Se você remover definições de um arquivo de origem e recarregá-lo, essas definições ainda estarão disponíveis na memória. Se outro código depender dessas definições, ele continuará funcionando, mas será interrompido na próxima vez que você reiniciar a JVM.
Se o espaço para nome recarregado contiver defmulti
, você também deverá recarregar todas as defmethod
expressões associadas .
Se o espaço para nome recarregado contiver defprotocol
, você também deverá recarregar todos os registros ou tipos que implementam esse protocolo e substituir quaisquer instâncias existentes desses registros / tipos por novas instâncias.
Se o espaço para nome recarregado contiver macros, você também deverá recarregar todos os espaços para nome que usem essas macros.
Se o programa em execução contiver funções que fechem sobre os valores no espaço para nome recarregado, esses valores fechados não serão atualizados. (Isso é comum em aplicativos da Web que constroem a "pilha do manipulador" como uma composição de funções).
A biblioteca clojure.tools.namespace melhora significativamente a situação. Ele fornece uma função de atualização fácil que faz o recarregamento inteligente com base em um gráfico de dependência dos namespaces.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
Infelizmente, recarregar uma segunda vez falhará se o espaço de nomes no qual você referenciou a refresh
função foi alterado. Isso ocorre porque o tools.namespace destrói a versão atual do namespace antes de carregar o novo 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)
Você pode usar o nome completo do var como solução alternativa para esse problema, mas pessoalmente prefiro não precisar digitar isso a cada atualização. Outro problema com o exposto acima é que, após recarregar o namespace principal, as funções auxiliares do REPL padrão (como doc
e source
) não são mais referenciadas lá.
Para resolver esses problemas, prefiro criar um arquivo de origem real para o espaço de nome do usuário, para que ele possa ser recarregado com segurança. Coloquei o arquivo de origem, ~/.lein/src/user.clj
mas você pode colocá-lo em qualquer lugar. O arquivo deve exigir a função de atualização na declaração ns superior como esta:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
Você pode configurar um perfil de usuário leiningen~/.lein/profiles.clj
para que o local em que você colocou o arquivo seja adicionado ao caminho da classe. O perfil deve ser algo como isto:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Observe que eu defino o espaço para nome do usuário como ponto de entrada ao iniciar o REPL. Isso garante que as funções auxiliares do REPL sejam referenciadas no espaço para nome do usuário em vez do espaço para nome principal do seu aplicativo. Dessa forma, eles não se perderão, a menos que você altere o arquivo de origem que acabamos de criar.
Espero que isto ajude!
(use 'foo.bar :reload-all)
sempre funcionou bem para mim. Além disso,(load-file)
nunca deve ser necessário se você tiver o caminho de classe configurado corretamente. Qual é o "efeito necessário" que você não está obtendo?