Перегрузочный Clojure код , используя (require … :reload)
и :reload-all
является весьма проблематичным :
Если вы изменяете два пространства имен, которые зависят друг от друга, не забудьте перезагрузить их в правильном порядке, чтобы избежать ошибок компиляции.
Если вы удалите определения из исходного файла, а затем перезагрузите его, эти определения все еще будут доступны в памяти. Если другой код зависит от этих определений, он продолжит работать, но перестанет работать при следующем перезапуске JVM.
Если перезагруженное пространство имен содержит defmulti
, вы также должны перезагрузить все связанные defmethod
выражения.
Если перезагруженное пространство имен содержит defprotocol
, вы также должны перезагрузить все записи или типы, реализующие этот протокол, и заменить любые существующие экземпляры этих записей / типов новыми экземплярами.
Если перезагруженное пространство имен содержит макросы, необходимо также перезагрузить все пространства имен, которые используют эти макросы.
Если запущенная программа содержит функции, которые закрывают значения в перезагруженном пространстве имен, эти закрытые значения не обновляются. (Это часто встречается в веб-приложениях, которые создают «стек обработчиков» как набор функций.)
Библиотека clojure.tools.namespace значительно улучшает ситуацию. Он предоставляет функцию простого обновления, которая выполняет интеллектуальную перезагрузку на основе графа зависимостей пространств имен.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
К сожалению, повторная загрузка не удастся, если пространство имен, в котором вы ссылались на refresh
функцию, изменилось. Это связано с тем, что tools.namespace уничтожает текущую версию пространства имен перед загрузкой нового кода.
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)
Вы можете использовать полное имя var в качестве обходного пути для этой проблемы, но лично я предпочитаю не вводить это при каждом обновлении. Другая проблема, связанная с вышеизложенным, заключается в том, что после перезагрузки основного пространства имен на стандартные вспомогательные функции REPL (например, doc
и source
) больше нет ссылок.
Для решения этих проблем я предпочитаю создать фактический исходный файл для пространства имен пользователя, чтобы его можно было надежно перезагрузить. Я положил исходный файл, ~/.lein/src/user.clj
но вы можете разместить в любом месте. Файл должен требовать функцию обновления в объявлении top ns следующим образом:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
Вы можете настроить профиль пользователя leiningen~/.lein/profiles.clj
таким образом, чтобы местоположение, в которое вы помещаете файл, было добавлено в путь к классам. Профиль должен выглядеть примерно так:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Обратите внимание, что я установил пространство имен пользователя в качестве точки входа при запуске REPL. Это гарантирует, что на вспомогательные функции REPL будут ссылаться в пространстве имен пользователя вместо основного пространства имен вашего приложения. Таким образом, они не потеряются, если вы не измените исходный файл, который мы только что создали.
Надеюсь это поможет!
(use 'foo.bar :reload-all)
всегда работал хорошо для меня. Кроме того,(load-file)
никогда не должно быть необходимости, если у вас правильно настроен путь к классам. Какой «требуемый эффект» вы не получаете?