Skip to content

Commit

Permalink
0.2.0 Support init-less workflow (closes #4)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky committed Feb 23, 2024
1 parent 1745943 commit 0a082e9
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 7 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.2.0 - Feb 23, 2024

Support optional “init-less” workflow:

- Initialize by default with all dirs on classpath
- Support `:clj-reload/no-reload` and `:clj-reload/no-unload` meta on ns

# 0.1.3 - Feb 21, 2024

- Support namespaces defined in multiple files #3
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This is only about namespace dependencies within a single project. It has nothin
## Dependency

```clojure
io.github.tonsky/clj-reload {:mvn/version "0.1.3"}
io.github.tonsky/clj-reload {:mvn/version "0.2.0"}
```

## The problem
Expand Down Expand Up @@ -227,6 +227,22 @@ Why is this important? With `tools.namespace` you will structure your code in a

Simply put: the fact that you use `clj-reload` during development does not spill into your production code.

## Usage: init-less workflow

Sometimes it might be useful to integrate `clj-reload` into your system-wide profile or into tool like CIDER to be available in all your projects without explicitly adding it as a dependency.

To support that, `clj-reload`:

- Lets you skip `init`, in which case it’ll initialize with every directory it can find on classpath,
- Supports `:clj-reload/no-reload` and `:clj-reload/no-unload` meta on namespace symbol, like this:

```
(ns ^:clj-reload/no-reload no-reload
(:require ...))
```

In that case, you can just call `clj-reload.core/reload` and it should work with default settings.

## Comparison: Evaluating buffer

The simplest way to reload Clojure code is just re-evaluating an entire buffer.
Expand Down
4 changes: 4 additions & 0 deletions fixtures/core_test/no_reload.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(ns ^:clj-reload/no-reload no-reload)

(def rand1
(rand-int Integer/MAX_VALUE))
7 changes: 7 additions & 0 deletions fixtures/core_test/no_unload.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(ns ^:clj-reload/no-unload no-unload)

(def rand1
(rand-int Integer/MAX_VALUE))

(def rand2
(rand-int Integer/MAX_VALUE))
10 changes: 10 additions & 0 deletions src/clj_reload/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
; NS :: {:ns-files #{<file> ...} - files containing (ns ...) declaration
; :in-ns-files #{<file> ...} - files containing (in-ns ...) declaration
; :requires #{<symbol> ...} - other nses this depends on
; :meta {} - metadata from ns symbol
; :keep {<symbol> -> Keep}}} - vars to keep between reloads
;
; Keep :: {:tag <symbol> - type of value ('def, 'defonce etc)
Expand Down Expand Up @@ -170,6 +171,8 @@

unload? #(and
(loaded %)
(not (:clj-reload/no-unload (:meta (namespaces %))))
(not (:clj-reload/no-reload (:meta (namespaces %))))
(not (no-unload %))
(not (no-reload %)))
deps (parse/dependees namespaces)
Expand All @@ -184,6 +187,7 @@

load? #(and
(loaded %)
(not (:clj-reload/no-reload (:meta (namespaces %))))
(not (no-reload %))
(namespaces' %))
deps' (parse/dependees namespaces')
Expand Down Expand Up @@ -329,3 +333,9 @@

(defmethod keep-methods 'defprotocol [_]
keep/keep-methods-defprotocol)

;; Initialize with classpath-dirs to support “init-less” workflow
;; See https://github.com/tonsky/clj-reload/pull/4
;; and https://github.com/clojure-emacs/cider-nrepl/issues/849
(init
{:dirs (util/classpath-dirs)})
7 changes: 4 additions & 3 deletions src/clj_reload/parse.clj
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,10 @@

(= 'ns tag)
(let [[ns requires] (parse-ns-form form)]
(recur ns(update nses ns util/assoc-some
:requires requires
:ns-files (util/some-set file))))
(recur ns (update nses ns util/assoc-some
:meta (meta ns)
:requires requires
:ns-files (util/some-set file))))

(= 'in-ns tag)
(let [[_ ns] (expand-quotes form)]
Expand Down
32 changes: 31 additions & 1 deletion src/clj_reload/util.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
[clojure.string :as str])
(:import
[clojure.lang LineNumberingPushbackReader]
[java.io File StringReader]))
[java.io File StringReader]
[java.net URLClassLoader]))

(def ^:dynamic *log-fn*
println)
Expand Down Expand Up @@ -93,6 +94,9 @@
(defn file? [^File f]
(some-> f .isFile))

(defn directory? [^File f]
(some-> f .isDirectory))

(defn file-name [^File f]
(some-> f .getName))

Expand All @@ -114,3 +118,29 @@
(let [[_ ext] (re-matches #".*\.([^.]+)" (.getName file))
path (-> ns str (str/replace #"\-" "_") (str/replace #"\." "/") (str "." ext))]
(Compiler/load (StringReader. content) path (.getName file))))

(defn loader-classpath []
(->> (clojure.lang.RT/baseLoader)
(iterate #(.getParent ^ClassLoader %))
(take-while identity)
(filter #(instance? URLClassLoader %))
(mapcat #(.getURLs ^URLClassLoader %))
(map io/as-file)
(filter directory?)))

(defn system-classpath []
(-> (System/getProperty "java.class.path")
(str/split (re-pattern (System/getProperty "path.separator")))
(->> (map io/as-file)
(filter directory?))))

(defn classpath-dirs []
(->> (or
(not-empty (loader-classpath))
(not-empty (system-classpath)))
(distinct)
(mapv file-path)))

(comment
(classpath-dirs)
(system-classpath))
26 changes: 24 additions & 2 deletions test/clj_reload/core_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[clojure.test :refer [is are deftest testing use-fixtures]]))

(defn reset []
(tu/reset '[two-nses-second two-nses split o n m l i j k f a g h d c e double b]))
(tu/reset '[two-nses-second two-nses split o n no-unload m l i j k f a g h d c e double b]))

(defn wrap-test [f]
(binding [tu/*dir* "fixtures/core_test"]
Expand Down Expand Up @@ -136,7 +136,7 @@
(deftest reload-all-test
(tu/with-deleted 'err-runtime
(is (= '["Unloading" two-nses-second two-nses split m n o l i j k h f g a d c e double b
"Loading" b double double e c d a g f h k j i l o n m split two-nses two-nses-second]
"Loading" b double double e c d a g f h k j i l no-unload o n m split two-nses two-nses-second]
(modify {:require '[] :only :all})))))

(deftest reload-exception-test
Expand Down Expand Up @@ -272,3 +272,25 @@
(is (= '["Unloading" m n o "Loading" o n " failed to load" n] (tu/trace))))
(tu/reload)
(is (= '["Unloading" n "Loading" n m] (tu/trace))))

(deftest no-unload-meta-test
(tu/init 'no-unload)
(let [rand1 @(resolve 'no-unload/rand1)
rand2 @(resolve 'no-unload/rand2)]
(tu/with-changed 'no-unload #ml "(ns ^:clj-reload/no-unload no-unload)
(def rand1
(rand-int Integer/MAX_VALUE))"
(tu/reload)
(let [rand1' @(resolve 'no-unload/rand1)
rand2' @(resolve 'no-unload/rand2)]
(is (not= rand1' rand1))
(is (= rand2' rand2))))))

(deftest no-reload-meta-test
(tu/init 'no-reload)
(let [rand1 @(resolve 'no-reload/rand1)
_ (tu/touch 'no-reload)
_ (tu/reload)
rand1' @(resolve 'no-reload/rand1)]
(is (= rand1' rand1))))
3 changes: 3 additions & 0 deletions test/clj_reload/parse_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
(is (= '{x nil}
(read-str "(ns x)")))

(is (= '{x {:meta {:clj-reload/no-reload true}}}
(read-str "(ns ^:clj-reload/no-reload x)")))

(is (= '{x nil}
(read-str "(in-ns 'x)"))))

Expand Down

0 comments on commit 0a082e9

Please sign in to comment.