diff --git a/src/midje/checking/checkers/simple.clj b/src/midje/checking/checkers/simple.clj index 9f7338c1e..cb6109c67 100644 --- a/src/midje/checking/checkers/simple.clj +++ b/src/midje/checking/checkers/simple.clj @@ -7,7 +7,7 @@ [midje.checking.checkers.defining :only [as-checker checker defchecker]] [midje.checking.checkers.util :only [named-as-call]] [midje.util.exceptions :only [captured-throwable?]] - [midje.util.ecosystem :only [clojure-1-3? +M -M *M]] + [midje.util.ecosystem :only [clojure-1-3?]] [clojure.algo.monads :only [domonad set-m]]) (:import [midje.util.exceptions ICapturedThrowable])) @@ -39,7 +39,7 @@ (letfn [(abs [n] (if (pos? n) n - (-M n)))] ;; -M not strictly necessary, but... + (-' n)))] ;; -' not strictly necessary, but... (defchecker roughly "With two arguments, accepts a value within delta of the @@ -48,10 +48,10 @@ ([expected delta] (checker [actual] (and (number? actual) - (>= expected (-M actual delta)) - (<= expected (+M actual delta))))) + (>= expected (-' actual delta)) + (<= expected (+' actual delta))))) ([expected] - (roughly expected (abs (*M 0.001 expected)))))) + (roughly expected (abs (*' 0.001 expected)))))) ;; Concerning Throwables diff --git a/src/midje/data/project_state.clj b/src/midje/data/project_state.clj index df0ef7500..f085e6540 100644 --- a/src/midje/data/project_state.clj +++ b/src/midje/data/project_state.clj @@ -8,112 +8,109 @@ [midje.emission.colorize :as color] [midje.config :as config])) -(ecosystem/when-1-3+ - - (require '[clojure.tools.namespace.repl :as nsrepl] - '[clojure.tools.namespace.dir :as nsdir] - '[clojure.tools.namespace.track :as nstrack] - '[clojure.tools.namespace.reload :as nsreload]) +(require '[clojure.tools.namespace.repl :as nsrepl] + '[clojure.tools.namespace.dir :as nsdir] + '[clojure.tools.namespace.track :as nstrack] + '[clojure.tools.namespace.reload :as nsreload]) ;;; Querying the project tree - (defn namespaces [] - (mapcat namespaces-in-dir (ecosystem/leiningen-paths))) - - ;; For some purposes, it matters that the :test-paths files come - ;; before the :source-paths files. That happens to always be true, - ;; but the name below emphasizes it. - (def namespaces-test-first namespaces) - - (defn unglob-partial-namespaces [namespaces] - (mapcat #(if (= \* (last %)) - (namespaces-on-classpath :prefix (apply str (butlast %))) - [(symbol %)]) - (map str namespaces))) - - - ;;; Responding to changed files - - ;; tools.ns keys are annoyingly long. Shorthand. - (def unload-key :clojure.tools.namespace.track/unload) - (def load-key :clojure.tools.namespace.track/load) - (def filemap-key :clojure.tools.namespace.file/filemap) - (def deps-key :clojure.tools.namespace.track/deps) - (def time-key :clojure.tools.namespace.dir/time) - - ;; Global state. - - (defonce state-tracker (atom (nstrack/tracker))) - - (defn file-modification-time [file] - (.lastModified file)) - - (defn latest-modification-time [state-tracker] - (let [ns-to-file (invert (filemap-key state-tracker)) - relevant-files (map ns-to-file (load-key state-tracker))] - (apply max (time-key state-tracker) - (map file-modification-time relevant-files)))) - - - (defn require-namespaces! [namespaces on-require-failure clean-dependents] - (letfn [(broken-source-file? [the-ns] - (try - (require the-ns :reload) - false - (catch Throwable t - (on-require-failure the-ns t) - true))) - - (shorten-ns-list-by-trying-first [[the-ns & remainder]] - (if (broken-source-file? the-ns) - (clean-dependents the-ns remainder) - remainder))] - - (loop [namespaces namespaces] - (when (not (empty? namespaces)) - (recur (shorten-ns-list-by-trying-first namespaces)))))) - - ;; TODO: clojure.tools.namespace also finds a transitive closure when it finds - ;; the namespaces to reload, but I don't see quite how to hook into that mechanism, - ;; so I roll my own. - (defn mkfn:clean-dependents [state-tracker] - (fn [failing-namespace other-namespaces] - (loop [[root-to-handle & roots-to-handle-later] [failing-namespace] - surviving-namespaces other-namespaces] - (if (nil? root-to-handle) - surviving-namespaces - (let [actual-dependent-set (set (get-in state-tracker [deps-key :dependents root-to-handle])) - [new-roots unkilled-descendents] (separate actual-dependent-set surviving-namespaces)] - (recur (concat roots-to-handle-later new-roots) - unkilled-descendents)))))) +(defn namespaces [] + (mapcat namespaces-in-dir (ecosystem/leiningen-paths))) + +;; For some purposes, it matters that the :test-paths files come +;; before the :source-paths files. That happens to always be true, +;; but the name below emphasizes it. +(def namespaces-test-first namespaces) + +(defn unglob-partial-namespaces [namespaces] + (mapcat #(if (= \* (last %)) + (namespaces-on-classpath :prefix (apply str (butlast %))) + [(symbol %)]) + (map str namespaces))) + + +;;; Responding to changed files + +;; tools.ns keys are annoyingly long. Shorthand. +(def unload-key :clojure.tools.namespace.track/unload) +(def load-key :clojure.tools.namespace.track/load) +(def filemap-key :clojure.tools.namespace.file/filemap) +(def deps-key :clojure.tools.namespace.track/deps) +(def time-key :clojure.tools.namespace.dir/time) + +;; Global state. + +(defonce state-tracker (atom (nstrack/tracker))) + +(defn file-modification-time [file] + (.lastModified file)) + +(defn latest-modification-time [state-tracker] + (let [ns-to-file (invert (filemap-key state-tracker)) + relevant-files (map ns-to-file (load-key state-tracker))] + (apply max (time-key state-tracker) + (map file-modification-time relevant-files)))) + + +(defn require-namespaces! [namespaces on-require-failure clean-dependents] + (letfn [(broken-source-file? [the-ns] + (try + (require the-ns :reload) + false + (catch Throwable t + (on-require-failure the-ns t) + true))) + + (shorten-ns-list-by-trying-first [[the-ns & remainder]] + (if (broken-source-file? the-ns) + (clean-dependents the-ns remainder) + remainder))] + + (loop [namespaces namespaces] + (when (not (empty? namespaces)) + (recur (shorten-ns-list-by-trying-first namespaces)))))) + +;; TODO: clojure.tools.namespace also finds a transitive closure when it finds +;; the namespaces to reload, but I don't see quite how to hook into that mechanism, +;; so I roll my own. +(defn mkfn:clean-dependents [state-tracker] + (fn [failing-namespace other-namespaces] + (loop [[root-to-handle & roots-to-handle-later] [failing-namespace] + surviving-namespaces other-namespaces] + (if (nil? root-to-handle) + surviving-namespaces + (let [actual-dependent-set (set (get-in state-tracker [deps-key :dependents root-to-handle])) + [new-roots unkilled-descendents] (separate actual-dependent-set surviving-namespaces)] + (recur (concat roots-to-handle-later new-roots) + unkilled-descendents)))))) - (defn react-to-tracker! [state-tracker options] - (let [namespaces (load-key state-tracker)] - (when (not (empty? namespaces)) - ( (:namespace-stream-checker options) - namespaces - #(require-namespaces! namespaces - (:on-require-failure options) - (mkfn:clean-dependents state-tracker)))))) - - (defn prepare-for-next-scan [state-tracker] - (assoc state-tracker time-key (latest-modification-time state-tracker) - unload-key [] - load-key [])) - - (defn mkfn:scan-and-react [options scanner] - (fn [] - (swap! state-tracker - #(let [new-tracker (apply scanner % (:files options))] - (react-to-tracker! new-tracker options) - (prepare-for-next-scan new-tracker))))) - - - (defn mkfn:react-to-changes [options] - (mkfn:scan-and-react options nsdir/scan)) - - (defn load-everything [options] - ((mkfn:scan-and-react options nsdir/scan-all))) - -) +(defn react-to-tracker! [state-tracker options] + (let [namespaces (load-key state-tracker)] + (when (not (empty? namespaces)) + ( (:namespace-stream-checker options) + namespaces + #(require-namespaces! namespaces + (:on-require-failure options) + (mkfn:clean-dependents state-tracker)))))) + +(defn prepare-for-next-scan [state-tracker] + (assoc state-tracker + time-key (latest-modification-time state-tracker) + unload-key [] + load-key [])) + +(defn mkfn:scan-and-react [options scanner] + (fn [] + (swap! state-tracker + #(let [new-tracker (apply scanner % (:files options))] + (react-to-tracker! new-tracker options) + (prepare-for-next-scan new-tracker))))) + + +(defn mkfn:react-to-changes [options] + (mkfn:scan-and-react options nsdir/scan)) + +(defn load-everything [options] + ((mkfn:scan-and-react options nsdir/scan-all))) diff --git a/src/midje/emission/state.clj b/src/midje/emission/state.clj index 103f182e3..ab1c82e05 100644 --- a/src/midje/emission/state.clj +++ b/src/midje/emission/state.clj @@ -39,7 +39,7 @@ `(do (def ~atom-name (atom :undefined)) (defn ~name [] (deref ~atom-name)) - (ecosystem/when-1-3+ (.setDynamic (var ~atom-name))) + (.setDynamic (var ~atom-name)) (def ~fresh-name ~(zipmap keys (repeat 0))) (defn ~set-name [newval#] (swap! ~atom-name (constantly newval#))) (defn ~reset-name [] (reset! ~atom-name ~fresh-name)) diff --git a/src/midje/util/ecosystem.clj b/src/midje/util/ecosystem.clj index ff000118c..04e45b369 100644 --- a/src/midje/util/ecosystem.clj +++ b/src/midje/util/ecosystem.clj @@ -19,39 +19,10 @@ (and (= 1 (:major *clojure-version*)) (= 3 (:minor *clojure-version*)))) -(defn clojure-1-2-X? [] - (and (= 1 (:major *clojure-version*)) - (= 2 (:minor *clojure-version*)))) - -(defn clojure-1-2-0? [] - (and (= 1 (:major *clojure-version*)) - (= 2 (:minor *clojure-version*)) - (= 0 (:incremental *clojure-version*)))) - -(defmacro when-1-3+ [& body] - (when-not (= 2 (:minor *clojure-version*)) - `(do ~@body))) - -(defmacro when-1-3- [& body] - (when (< (:minor *clojure-version*) 4) - `(do ~@body))) - (defmacro when-1-4+ [& body] (when (>= (:minor *clojure-version*) 4) `(do ~@body))) -(defmacro unless-1-2-0 - "Skip body completely - including 'Unable to resolve classname' errors." - [& body] - (when-not (clojure-1-2-0?) - `(do ~@body))) - -;; The following works because in 1.2 it's parsed as [+ '1]. - -(def +M (first [+' 1])) -(def -M (first [-' 1])) -(def *M (first [*' 1])) - ;; (defn getenv [var] @@ -89,32 +60,28 @@ ;; get that from `lein repl`, so the default value from `project.clj` ;; is returned. -(when-1-3+ - - (def leiningen-paths-var nil) - - (defmacro around-initial-paths [& body] - `(let [original# leiningen-paths-var] - (try - (alter-var-root #'leiningen-paths-var (constantly nil)) - ~@body - (finally (alter-var-root #'leiningen-paths-var (constantly original#)))))) - - (defn set-leiningen-paths! [project] - ;; Note that the order is guaranteed: test paths come before project paths. - (alter-var-root #'leiningen-paths-var - (constantly (concat (:test-paths project) (:source-paths project))))) - - (defn- project-with-paths [] - (try - (project/read) - (catch java.io.FileNotFoundException e - {:test-paths ["test"]}))) +(def leiningen-paths-var nil) + +(defmacro around-initial-paths [& body] + `(let [original# leiningen-paths-var] + (try + (alter-var-root #'leiningen-paths-var (constantly nil)) + ~@body + (finally (alter-var-root #'leiningen-paths-var (constantly original#)))))) + +(defn set-leiningen-paths! [project] + ;; Note that the order is guaranteed: test paths come before project paths. + (alter-var-root #'leiningen-paths-var + (constantly (concat (:test-paths project) (:source-paths project))))) + +(defn- project-with-paths [] + (try + (project/read) + (catch java.io.FileNotFoundException e + {:test-paths ["test"]}))) - (defn leiningen-paths [] - (or leiningen-paths-var - (do - (set-leiningen-paths! (project-with-paths)) - leiningen-paths-var))) - -) +(defn leiningen-paths [] + (or leiningen-paths-var + (do + (set-leiningen-paths! (project-with-paths)) + leiningen-paths-var))) diff --git a/test/midje/data/t_project_state.clj b/test/midje/data/t_project_state.clj index e921d6b03..c6823a888 100644 --- a/test/midje/data/t_project_state.clj +++ b/test/midje/data/t_project_state.clj @@ -7,81 +7,79 @@ ;;; Directory structure -(ecosystem/when-1-3+ +(fact "unglob-partial-namespaces returns namespace symbols" + (fact "from symbols or strings" + (unglob-partial-namespaces ["explicit-namespace1"]) => ['explicit-namespace1] + (unglob-partial-namespaces ['explicit-namespace2]) => ['explicit-namespace2]) + + (fact "can 'unglob' wildcards" + (unglob-partial-namespaces ["ns.foo.*"]) => '[ns.foo.bar ns.foo.baz] + (provided (bultitude.core/namespaces-on-classpath :prefix "ns.foo.") + => '[ns.foo.bar ns.foo.baz]) + + (unglob-partial-namespaces ['ns.foo.*]) => '[ns.foo.bar ns.foo.baz] + (provided (bultitude.core/namespaces-on-classpath :prefix "ns.foo.") + => '[ns.foo.bar ns.foo.baz]))) + + + - (fact "unglob-partial-namespaces returns namespace symbols" - (fact "from symbols or strings" - (unglob-partial-namespaces ["explicit-namespace1"]) => ['explicit-namespace1] - (unglob-partial-namespaces ['explicit-namespace2]) => ['explicit-namespace2]) +;;; Working with modification times and dependencies + +(fact "The files to load can be used to find a modification time" + (against-background (file-modification-time ..file1..) => 222 + (file-modification-time ..file2..) => 3333) + + (let [empty-tracker {time-key 11 + load-key [] + filemap-key {..file1.. ..ns1.. + ..file2.. ..ns2..}} + tracker-with-changes (assoc empty-tracker load-key [..ns1.. ..ns2..])] + + (latest-modification-time empty-tracker) => 11 + (latest-modification-time tracker-with-changes) => 3333 - (fact "can 'unglob' wildcards" - (unglob-partial-namespaces ["ns.foo.*"]) => '[ns.foo.bar ns.foo.baz] - (provided (bultitude.core/namespaces-on-classpath :prefix "ns.foo.") - => '[ns.foo.bar ns.foo.baz]) - - (unglob-partial-namespaces ['ns.foo.*]) => '[ns.foo.bar ns.foo.baz] - (provided (bultitude.core/namespaces-on-classpath :prefix "ns.foo.") - => '[ns.foo.bar ns.foo.baz]))) + (prepare-for-next-scan empty-tracker) => (contains {time-key 11, unload-key [], load-key []}) + (prepare-for-next-scan tracker-with-changes) => (contains {time-key 3333, unload-key [], load-key []}))) +(fact "a dependents cleaner knows how to remove namespaces that depend on a namespace" + (let [tracker {deps-key {:dependents {..ns1.. [..ns2..] + ..ns2.. [..ns3..] + ..ns3.. []}}} + cleaner (mkfn:clean-dependents tracker)] + (cleaner ..ns1.. [..ns2.. ..ns3..]) => empty? + (cleaner ..ns2.. [..ns1.. ..ns3..]) => [..ns1..] + (cleaner ..ns3.. [..ns1..]) => [..ns1..])) + + +(def cleaner) ; standin for the calculated dependency cleaner +(def failure-record (atom {})) +(defn record-failure [ns throwable] + (swap! failure-record (constantly {:ns ns, :throwable throwable}))) + + +(fact "A namespace list can be loaded, obeying dependents" + (require-namespaces! [] record-failure cleaner) => anything + + (require-namespaces! [..ns1.. ..ns2..] record-failure cleaner) => anything + (provided + (require ..ns1.. :reload) => nil + (require ..ns2.. :reload) => nil) + + (require-namespaces! [..ns1.. ..ns2..] record-failure cleaner) => anything + (provided + (require ..ns1.. :reload) => nil + (require ..ns2.. :reload) => nil) -;;; Working with modification times and dependencies - (fact "The files to load can be used to find a modification time" - (against-background (file-modification-time ..file1..) => 222 - (file-modification-time ..file2..) => 3333) - - (let [empty-tracker {time-key 11 - load-key [] - filemap-key {..file1.. ..ns1.. - ..file2.. ..ns2..}} - tracker-with-changes (assoc empty-tracker load-key [..ns1.. ..ns2..])] - - (latest-modification-time empty-tracker) => 11 - (latest-modification-time tracker-with-changes) => 3333 - - (prepare-for-next-scan empty-tracker) => (contains {time-key 11, unload-key [], load-key []}) - (prepare-for-next-scan tracker-with-changes) => (contains {time-key 3333, unload-key [], load-key []}))) - - - (fact "a dependents cleaner knows how to remove namespaces that depend on a namespace" - (let [tracker {deps-key {:dependents {..ns1.. [..ns2..] - ..ns2.. [..ns3..] - ..ns3.. []}}} - cleaner (mkfn:clean-dependents tracker)] - (cleaner ..ns1.. [..ns2.. ..ns3..]) => empty? - (cleaner ..ns2.. [..ns1.. ..ns3..]) => [..ns1..] - (cleaner ..ns3.. [..ns1..]) => [..ns1..])) - - - (def cleaner) ; standin for the calculated dependency cleaner - (def failure-record (atom {})) - (defn record-failure [ns throwable] - (swap! failure-record (constantly {:ns ns, :throwable throwable}))) - - - (fact "A namespace list can be loaded, obeying dependents" - (require-namespaces! [] record-failure cleaner) => anything - - (require-namespaces! [..ns1.. ..ns2..] record-failure cleaner) => anything - (provided - (require ..ns1.. :reload) => nil - (require ..ns2.. :reload) => nil) - - (require-namespaces! [..ns1.. ..ns2..] record-failure cleaner) => anything - (provided - (require ..ns1.. :reload) => nil - (require ..ns2.. :reload) => nil) - - - - (let [throwable (Error.)] - (require-namespaces! [..ns1.. ..ns2.. ..ns3..] record-failure cleaner) => anything - (provided - (require ..ns1.. :reload) =throws=> throwable - (cleaner ..ns1.. [..ns2.. ..ns3..]) => [..ns3..] - (require ..ns3.. :reload) => nil) - @failure-record => {:ns ..ns1.. :throwable throwable})) - - ) + (let [throwable (Error.)] + (require-namespaces! [..ns1.. ..ns2.. ..ns3..] record-failure cleaner) => anything + (provided + (require ..ns1.. :reload) =throws=> throwable + (cleaner ..ns1.. [..ns2.. ..ns3..]) => [..ns3..] + (require ..ns3.. :reload) => nil) + @failure-record => {:ns ..ns1.. :throwable throwable})) + + diff --git a/test/midje/t_repl.clj b/test/midje/t_repl.clj index 25f1d1483..a30b8cb30 100644 --- a/test/midje/t_repl.clj +++ b/test/midje/t_repl.clj @@ -9,8 +9,6 @@ [midje.util.scheduling :as scheduling] midje.util)) -(ecosystem/when-1-3+ - (midje.util/expose-testables midje.repl) ;;;; === Util @@ -750,5 +748,4 @@ ) ; confirming-cumulative-totals-not-stepped-on -) diff --git a/test/midje/t_sweet.clj b/test/midje/t_sweet.clj index 6e24a751c..d426f27f4 100644 --- a/test/midje/t_sweet.clj +++ b/test/midje/t_sweet.clj @@ -404,14 +404,12 @@ -(ecosystem/when-1-3+ - (emit/silently - ;; Don't step on the running count up to this point. - (repl/check-facts *ns* :print-no-summary :integration)) +(emit/silently + ;; Don't step on the running count up to this point. + (repl/check-facts *ns* :print-no-summary :integration)) - (fact - :check-only-at-load-time - @integration-run-count => 2 - @not-integration-run-count => 1) -) +(fact + :check-only-at-load-time + @integration-run-count => 2 + @not-integration-run-count => 1) diff --git a/test/midje/util/t_ecosystem.clj b/test/midje/util/t_ecosystem.clj index c3f57a57d..3053ced3d 100644 --- a/test/midje/util/t_ecosystem.clj +++ b/test/midje/util/t_ecosystem.clj @@ -3,25 +3,23 @@ midje.util.ecosystem midje.test-util)) -(when-1-3+ - - (fact "can find paths to load from project.clj" - (against-background (around :facts (around-initial-paths ?form))) - (fact "if it exists" - (leiningen-paths) => ["/test1" "/src1"] - (provided (leiningen.core.project/read) => {:test-paths ["/test1"] - :source-paths ["/src1"]})) - - (fact "and provides a default if it does not" - (leiningen-paths) => ["test"] - (provided (leiningen.core.project/read) - =throws=> (new java.io.FileNotFoundException))) - - (fact "except that lein-midje can explicitly set the value" - (set-leiningen-paths! {:test-paths ["t"] :source-paths ["s"]}) - (leiningen-paths) => ["t" "s"]) +(fact "can find paths to load from project.clj" + (against-background (around :facts (around-initial-paths ?form))) + (fact "if it exists" + (leiningen-paths) => ["/test1" "/src1"] + (provided (leiningen.core.project/read) => {:test-paths ["/test1"] + :source-paths ["/src1"]})) + + (fact "and provides a default if it does not" + (leiningen-paths) => ["test"] + (provided (leiningen.core.project/read) + =throws=> (new java.io.FileNotFoundException))) + + (fact "except that lein-midje can explicitly set the value" + (set-leiningen-paths! {:test-paths ["t"] :source-paths ["s"]}) + (leiningen-paths) => ["t" "s"]) + + (fact "note that test paths come first" + (set-leiningen-paths! (sorted-map :source-paths ["after"] :test-paths ["before"])) + (leiningen-paths) => ["before" "after"])) - (fact "note that test paths come first" - (set-leiningen-paths! (sorted-map :source-paths ["after"] :test-paths ["before"])) - (leiningen-paths) => ["before" "after"])) -)