Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Detect changes in namespaces split over several files #10

Merged
merged 1 commit into from

2 participants

Bob Hutchison James Reeves
Bob Hutchison

Addresses open issues #1, #7, #8, and #9

  • will now re-load namespaces if a source file that is explicitly loaded changes. This will only work if the first non-comment form of the 'helper' or 'extension' source file is an (in-ns) form. It will not look for any subsequent in-ns forms in the file. This is the same idiomatic usage of load/in-ns that is found in clojure/core.
    • newer-namespace-decls returns a list of ns-decls (as before) but also a set of namespace names (symbols) that have been affected.
  • avoid calling .fileModifed twice for each source file
  • replace contains? (as in ahjones/ns-tracker@1648229)
  • use tools.namespace 0.2.2 (similarly to jakemcc@6884b4c)
  • use clojure 1.3.0 (required by tools.namespace)
Bob Hutchison hutch -- will now re-load namespaces if a source file that is loaded changes.
   This will only work if the first non-comment form of the 'helper' or
   'extension' source file is an (in-ns) form. It will not look for any
   subsequent in-ns forms in the file. This is the same idiomatic usage of
   load/in-ns that is found in clojure/core.
-- newer-namespace-decls returns a list of ns-decls (as before) but also
   a set of namespace names (symbols) that have been affected.
-- avoid calling .fileModifed twice
-- replace contains? (as in
   ahjones/ns-tracker@1648229)
-- use tools.namespace 0.2.2 (similarly to
   jakemcc@6884b4c)
-- use clojure 1.3.0 (required by tools.namespace)
5b3d9ff
Bob Hutchison

I just noticed that I didn't remove a println -- line 130 of core.clj.

James Reeves
Owner

I'd be avoiding the upgrade to Clojure 1.3.0 because I wanted to retain support for older versions of Clojure in Ring for as long as possible. However, I suspect that I'll need to bite the bullet and upgrade.

Bob Hutchison

I wasn't aware you were trying to avoid the upgrade. There's very little of clojure.tools.namespace actually used, three methods, two of which are trivial. I would think it's possible to avoid the upgrade with some small changes to what I did.

Bob Hutchison

Though with 1.5 imminent you have to wonder how far back you're prepared to support.

James Reeves
Owner

Given that 1.5 is probably going to be out at the same time as Ring 1.2, maybe it's reasonable to have a policy of supporting Clojure 2 versions back.

Bob Hutchison

That sounds perfectly reasonable to me.

James Reeves weavejester merged commit 882ed8d into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 28, 2012
  1. Bob Hutchison

    -- will now re-load namespaces if a source file that is loaded changes.

    hutch authored
       This will only work if the first non-comment form of the 'helper' or
       'extension' source file is an (in-ns) form. It will not look for any
       subsequent in-ns forms in the file. This is the same idiomatic usage of
       load/in-ns that is found in clojure/core.
    -- newer-namespace-decls returns a list of ns-decls (as before) but also
       a set of namespace names (symbols) that have been affected.
    -- avoid calling .fileModifed twice
    -- replace contains? (as in
       ahjones/ns-tracker@1648229)
    -- use tools.namespace 0.2.2 (similarly to
       jakemcc@6884b4c)
    -- use clojure 1.3.0 (required by tools.namespace)
This page is out of date. Refresh to see the latest.
4 project.clj
View
@@ -1,7 +1,7 @@
(defproject ns-tracker "0.2.0"
:description "Keep track of which namespaces have been modified"
- :dependencies [[org.clojure/clojure "1.2.1"]
- [org.clojure/tools.namespace "0.1.3"]
+ :dependencies [[org.clojure/clojure "1.3.0"]
+ [org.clojure/tools.namespace "0.2.2"]
[org.clojure/java.classpath "0.2.0"]]
:profiles
{:dev {:dependencies [[commons-io "1.4"]]}
78 src/ns_tracker/core.clj
View
@@ -1,10 +1,13 @@
(ns ns-tracker.core
"Keeps track of which namespaces have changed and need to be reloaded."
(:use [ns-tracker.dependency :only (graph seq-union depend dependents remove-key)]
- [ns-tracker.nsdeps :only (deps-from-ns-decl)]
+ [ns-tracker.nsdeps :only (deps-from-ns-decl)]
[clojure.java.io :only (file)]
- [clojure.tools.namespace :only (find-clojure-sources-in-dir
- read-file-ns-decl)]))
+ [clojure.tools.namespace
+ [find :only (find-clojure-sources-in-dir)]
+ [parse :only (comment? ns-decl?)]])
+ (:require [clojure.java.io :as io])
+ (:import (java.io PushbackReader)))
(defn- file? [f]
(instance? java.io.File f))
@@ -21,14 +24,59 @@
(defn- modified?
"Compare a file to a timestamp map to see if it's been modified since."
- [timestamp-map file]
- (> (.lastModified file) (get timestamp-map file 0)))
+ [then now file]
+ (> (get now file 0) (get then file 0)))
-(defn- newer-sources [timestamp-map files]
- (filter (partial modified? timestamp-map) files))
+(defn- newer-sources [then now]
+ (filter (partial modified? then now) (keys now)))
-(defn- newer-namespace-decls [timestamp-map files]
- (remove nil? (map read-file-ns-decl (newer-sources timestamp-map files))))
+(defn ns-in?
+ "Returns true if form is a (in-ns ...) declaration."
+ [form]
+ (and (list? form) (= 'in-ns (first form))))
+
+(defn read-ns-decl
+ "Attempts to read a (ns ...) or (in-ns ...) declaration from a
+ java.io.PushbackReader. Returns [form nil] for ns declarations and
+ [nil form] for in-ns declarations, and [nil nil] if read fails or
+ if a ns or in-ns declaration cannot be found. The ns/in-ns
+ declaration must be the first Clojure form in the file, except
+ for (comment ...) forms. Based on the function with the same
+ name in core.tools.namespace.parse"
+
+ [rdr]
+ (try
+ (loop [] (let [form (doto (read rdr) str)]
+ (cond
+ (ns-decl? form) [form nil]
+ (ns-in? form) [nil form]
+ (comment? form) (recur)
+ :else [nil nil])))
+ (catch Exception e [nil nil])))
+
+(defn read-file-ns-decl
+ "Attempts to read a (ns ...) or (in-ns ...) declaration from file.
+ Returns [form nil] for ns declarations and [nil form] for in-ns
+ declarations, and [nil nil] if read fails or if a ns or in-ns
+ declaration cannot be found. Based on the function with the same
+ name in core.tools.namespace.file"
+ [file]
+ (with-open [rdr (PushbackReader. (io/reader file))]
+ (read-ns-decl rdr)))
+
+(defn- newer-namespace-decls [then now]
+ (loop [new-decls []
+ new-names #{}
+ files (newer-sources then now)]
+ (let [file (first files)]
+ (if file
+ (let [[ns-decl ns-in] (read-file-ns-decl file)]
+ (if ns-decl
+ (recur (conj new-decls ns-decl) (conj new-names (second ns-decl)) (rest files))
+ (if ns-in
+ (recur new-decls (conj new-names (second (second ns-in))) (rest files))
+ (recur new-decls new-names (rest files)))))
+ [new-decls new-names]))))
(defn- add-to-dep-graph [dep-graph namespace-decls]
(reduce (fn [g decl]
@@ -70,16 +118,16 @@
{:pre [(map? initial-timestamp-map)]}
(let [dirs (normalize-dirs dirs)
timestamp-map (atom initial-timestamp-map)
- init-decls (newer-namespace-decls {} (keys @timestamp-map))
+ [init-decls init-names] (newer-namespace-decls {} @timestamp-map)
dependency-graph (atom (update-dependency-graph (graph) init-decls))]
(fn []
(let [then @timestamp-map
now (current-timestamp-map (normalize-dirs dirs))
- new-decls (newer-namespace-decls then (keys now))]
- (when (seq new-decls)
- (let [new-names (map second new-decls)
- affected-names
+ [new-decls new-names] (newer-namespace-decls then now)]
+ (when (seq new-names)
+ (let [ affected-names
(affected-namespaces new-names @dependency-graph)]
+ (println "Reload Namespaces: " (pr-str affected-names))
(reset! timestamp-map now)
(swap! dependency-graph update-dependency-graph new-decls)
- affected-names)))))))
+ affected-names)))))))
4 src/ns_tracker/dependency.clj
View
@@ -35,12 +35,12 @@
(defn depends?
"True if x is directly or transitively dependent on y."
[graph x y]
- (contains? (dependencies graph x) y))
+ (some #(= y %) (dependencies graph x)))
(defn dependent
"True if y is a dependent of x."
[graph x y]
- (contains? (dependents graph x) y))
+ (some #(= y %) (dependents graph x)))
(defn- add-relationship [graph key x y]
(update-in graph [key x] union #{y}))
Something went wrong with that request. Please try again.