Skip to content

Instantly share code, notes, and snippets.

@matthewdowney
Created February 27, 2019 18:46
Show Gist options
  • Save matthewdowney/67e870a3e1e3638b897976fad57fc580 to your computer and use it in GitHub Desktop.
Save matthewdowney/67e870a3e1e3638b897976fad57fc580 to your computer and use it in GitHub Desktop.
Lazy Clojure file-watch, re-parsing when file changes.
(defn parsed-file-delay
"A delay that returns `(parse-fn (slurp file-path))` on each dereference,
recalculating the return value only when `(.lastModified (io/file file-path))`
changes.
Useful if you don't need any sort of push notification when the file changes."
[file-path parse-fn]
(let [last-contents (atom {:modified -1 :contents nil})
^File f (io/file file-path)]
(reify IDeref
(deref [this]
(let [{:keys [modified contents] :as prev} @last-contents
last-mod (.lastModified f)]
(if (= modified last-mod)
contents
(let [updated {:modified last-mod :contents (parse-fn (slurp f))}]
(compare-and-set! last-contents prev updated)
(recur))))))))
(comment
(spit "watch-me.txt" "(+ 10 10)")
(def evaluated-file
(parsed-file-delay
"watch-me.txt"
(fn [x]
(println "Evaling...")
(-> x read-string eval))))
@eval
; => Evaling...
; 20
@eval
; => 20
(spit "watch-me.txt" "(* 10 10)")
@eval
; => Evaling...
; 100
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment