Перевантажувальний Clojure код , використовуючи (require … :reload)
і :reload-all
є досить проблематичним :
Якщо ви змінюєте два простори імен, які залежать один від одного, ви повинні пам'ятати, щоб перезавантажити їх у правильному порядку, щоб уникнути помилок компіляції.
Якщо ви вилучите визначення з вихідного файлу, а потім перезавантажте їх, ці визначення все ще доступні в пам'яті. Якщо інший код залежить від цих визначень, він буде продовжувати працювати, але порушиться при наступному перезапуску JVM.
Якщо перезавантажена область імен містить defmulti
, ви також повинні перезавантажити всі пов'язані з ними defmethod
вирази.
Якщо перезавантажена область імен містить defprotocol
, ви також повинні перезавантажити будь-які записи чи типи, що реалізують цей протокол, та замінити всі наявні екземпляри цих записів / типів на нові екземпляри.
Якщо перезавантажена область імен містить макроси, ви також повинні перезавантажити будь-які простори імен, які використовують ці макроси.
Якщо запущена програма містить функції, які закривають більше значень у перезавантаженому просторі імен, ці закриті значення не оновлюються. (Це часто зустрічається у веб-додатках, які будують "стек обробника" як композицію функцій.)
Бібліотека простору clojure.tools.names значно покращує ситуацію. Він забезпечує функцію легкого оновлення, яка робить розумне перезавантаження на основі графіка залежності просторів імен.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
На жаль, повторне завантаження не вдасться, якщо refresh
змінився простір імен, на який ви посилалися на функцію. Це пов’язано з тим, що space.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
але його можна розмістити в будь-якому місці. Файл повинен вимагати функції оновлення у верхній 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)
це не повинно бути необхідним, якщо ваш класний шлях налаштований правильно. Який «необхідний ефект» ви не отримуєте?