diff --git a/src/aero/core.cljc b/src/aero/core.cljc index a1909b4..7476a72 100644 --- a/src/aero/core.cljc +++ b/src/aero/core.cljc @@ -23,6 +23,23 @@ (defmulti reader (fn [opts tag value] tag)) +(defn- println-stderr [s] + (binding [*out* *err*] + (println s))) + +(defn- make-warning-msg [old-tag new-tag] + (format "WARNING: #%s is decrecated; use #%s instead." old-tag new-tag)) + +(defn- namespace-tag [tag] + (symbol "aero" (str tag))) + +(defmacro deprecate-reader + [dispatch-val] + `(defmethod reader ~dispatch-val [opts# tag# value#] + (let [new-tag# (namespace-tag tag#)] + (-> tag# (make-warning-msg new-tag#) println-stderr) + (reader opts# new-tag# value#)))) + (defmethod reader :default [_ tag value] (cond @@ -35,58 +52,75 @@ :else (throw (ex-info (#?(:clj format :cljs gstring/format) "No reader for tag %s" tag) {:tag tag :value value})))) - (defn- env [s] #?(:clj (System/getenv (str s))) #?(:cljs (aget js/process.env s))) -(defmethod reader 'env +(defmethod reader 'aero/env [opts tag value] (env value)) -(defmethod reader 'envf +(deprecate-reader 'env) + +(defmethod reader 'aero/envf [opts tag value] (let [[fmt & args] value] (apply #?(:clj format :cljs gstring/format) fmt (map #(env (str %)) args)))) -(defmethod reader 'prop - [opts tag value] - #?(:clj (System/getProperty (str value)) - :cljs nil)) +(deprecate-reader 'envf) + +(defmethod reader 'aero/prop + [opts tag value] + #?(:clj (System/getProperty (str value)) + :cljs nil)) + +(deprecate-reader 'prop) -(defmethod reader 'or +(defmethod reader 'aero/or [opts tag value] (first (filter some? value))) -(defmethod reader 'long +(deprecate-reader 'or) + +(defmethod reader 'aero/long [opts tag value] #?(:clj (Long/parseLong (str value))) #?(:cljs (js/parseInt (str value)))) -(defmethod reader 'double +(deprecate-reader 'long) + +(defmethod reader 'aero/double [opts tag value] #?(:clj (Double/parseDouble (str value))) #?(:cljs (js/parseFloat (str value)))) -(defmethod reader 'keyword +(deprecate-reader 'double) + +(defmethod reader 'aero/keyword [opts tag value] (if (keyword? value) value (keyword (str value)))) -(defmethod reader 'boolean +(deprecate-reader 'keyword) + +(defmethod reader 'aero/boolean [opts tag value] #?(:clj (Boolean/parseBoolean (str value))) #?(:cljs (= "true" (.toLowerCase (str value))))) -(defmethod reader 'profile +(deprecate-reader 'boolean) + +(defmethod reader 'aero/profile [{:keys [profile]} tag value] (cond (contains? value profile) (get value profile) (contains? value :default) (get value :default) :otherwise nil)) -(defmethod reader 'hostname +(deprecate-reader 'profile) + +(defmethod reader 'aero/hostname [{:keys [hostname]} tag value] (let [hostn (or hostname #?(:clj (env "HOSTNAME") :cljs (os.hostname)))] @@ -98,7 +132,9 @@ value) (get value :default)))) -(defmethod reader 'user +(deprecate-reader 'hostname) + +(defmethod reader 'aero/user [{:keys [user]} tag value] (let [user (or user (env "USER"))] (or @@ -109,26 +145,36 @@ value) (get value :default)))) -(defmethod reader 'include +(deprecate-reader 'user) + +(defmethod reader 'aero/include [{:keys [resolver source] :as opts} tag value] (read-config - (if (map? resolver) - (get resolver value) - (resolver source value)) - opts)) + (if (map? resolver) + (get resolver value) + (resolver source value)) + opts)) + +(deprecate-reader 'include) -(defmethod reader 'join +(defmethod reader 'aero/join [opts tag value] (apply str value)) -(defmethod reader 'read-edn +(deprecate-reader 'join) + +(defmethod reader 'aero/read-edn [opts tag value] (some-> value str edn/read-string)) -(defmethod aero.core/reader 'merge +(deprecate-reader 'read-edn) + +(defmethod reader 'aero/merge [opts tag values] (apply merge values)) +(deprecate-reader 'merge) + (defn- get-in-ref [config] (letfn [(get-in-conf [m] @@ -215,25 +261,30 @@ (defn- tag-wrapper-of-ref? [x] - (tag-wrapper-of? x 'ref)) + (if (tag-wrapper-of? x 'ref) + (do + (println-stderr (make-warning-msg 'ref 'aero/ref)) + true) + (tag-wrapper-of? x 'aero/ref))) (defn- resolve-tag-wrappers [tagged-config tag-fn] (postwalk - (fn [x] - (if (tag-wrapper? x) - (tag-fn (:tag x) (:value x)) - x)) - tagged-config)) + (fn [x] + (if (tag-wrapper? x) + (tag-fn (:tag x) (:value x)) + x)) + tagged-config)) + (defn- ref-meta-to-tag-wrapper [config] (letfn [(get-in-conf [m] (postwalk - (fn [v] - (if-not (contains? (meta v) :ref) - v - (tag-wrapper 'ref v))) - m))] + (fn [v] + (if-not (contains? (meta v) :ref) + v + (tag-wrapper 'aero/ref v))) + m))] (get-in-conf config))) (defn pathify @@ -275,56 +326,58 @@ "Recursively checks a ref for nested dependencies" [ref*] (let [nested-deps (sequence - (comp (filter tag-wrapper-of-ref?) - (map :value)) - (:value ref*))] + (comp (filter tag-wrapper-of-ref?) + (map :value)) + (:value ref*))] (concat - (when-some [r (ref-dependency ref*)] [r]) - (when (seq nested-deps) - (concat nested-deps - (->> nested-deps - (mapcat ref-dependencies))))))) + (when-some [r (ref-dependency ref*)] [r]) + (when (seq nested-deps) + (concat nested-deps + (->> nested-deps + (mapcat ref-dependencies))))))) (defn config->ref-graph [config] (reduce - (fn [graph [k v]] - (as-> graph % - (reduce (fn [acc sk] (dep/depend acc sk k)) % (shorter-variations k)) - (reduce (fn [acc sk] (dep/depend acc k sk)) % (shorter-variations (:value v))) - (reduce (fn [acc d] (dep/depend acc k d)) % (ref-dependencies v)))) - (dep/graph) - (filter - (comp tag-wrapper-of-ref? second) - (pathify conj [] config)))) + (fn [graph [k v]] + (as-> graph % + (reduce (fn [acc sk] (dep/depend acc sk k)) % (shorter-variations k)) + (reduce (fn [acc sk] (dep/depend acc k sk)) % (shorter-variations (:value v))) + (reduce (fn [acc d] (dep/depend acc k d)) % (ref-dependencies v)))) + (dep/graph) + (filter + (comp tag-wrapper-of-ref? second) + (pathify conj [] config)))) + + (defn resolve-refs "Resolves refs & any necessary tags in order to do it's job" [config resolve-fn] (reduce - (fn [acc ks] - (let [resolved-ks (map - (fn [x] - ;; if there's a sub-ref here, it should already be - ;; a value due to recursive deps being resolved for - ;; us - (if (tag-wrapper-of-ref? x) - (get-in acc (ref-dependency x)) - x)) - ks) - b (get-in acc resolved-ks)] - (cond - (tag-wrapper-of-ref? b) - (assoc-in acc resolved-ks (get-in acc (ref-dependency b))) - (tag-wrapper? b) - (assoc-in acc - resolved-ks - (resolve-fn (:tag b) - (resolve-tag-wrappers (:value b) resolve-fn))) - :else - acc))) - config - (dep/topo-sort (config->ref-graph config)))) + (fn [acc ks] + (let [resolved-ks (map + (fn [x] + ;; if there's a sub-ref here, it should already be + ;; a value due to recursive deps being resolved for + ;; us + (if (tag-wrapper-of-ref? x) + (get-in acc (ref-dependency x)) + x)) + ks) + b (get-in acc resolved-ks)] + (cond + (tag-wrapper-of-ref? b) + (assoc-in acc resolved-ks (get-in acc (ref-dependency b))) + (tag-wrapper? b) + (assoc-in acc + resolved-ks + (resolve-fn (:tag b) + (resolve-tag-wrappers (:value b) resolve-fn))) + :else + acc))) + config + (dep/topo-sort (config->ref-graph config)))) (defn read-config-into-tag-wrapper [source] diff --git a/test/aero/config.edn b/test/aero/config.edn index 6382bfd..0ac588b 100644 --- a/test/aero/config.edn +++ b/test/aero/config.edn @@ -1,33 +1,33 @@ {:greeting "Hello World!" - :flavor #myflavor :favorite - :dumb-term #join ["Terminal is " #env TERM] - :smart-term #join ["Terminal is " #or [#env NONE "smart"]] - :dumb-term-envf #envf ["Terminal is %s" TERM] - :flavor-string #join ["My favorite flavor is " #or [#env TERM "flaa"] " " #myflavor :favorite] + :flavor #my/flavor :favorite + :dumb-term #aero/join ["Terminal is " #aero/env TERM] + :smart-term #aero/join ["Terminal is " #aero/or [#aero/env NONE "smart"]] + :dumb-term-envf #aero/envf ["Terminal is %s" TERM] + :flavor-string #aero/join ["My favorite flavor is " #aero/or [#aero/env TERM "flaa"] " " #my/flavor :favorite] :test ^:ref [:greeting] - :remote #include "included.edn" + :remote #aero/include "included.edn" :test-nested ^:ref [:test] - :port #profile {:dev 8000 - :prod 80} - :True-boolean #boolean "True" - :true-boolean #boolean "true" - :false-boolean #boolean "false" - :trivial-false-boolean #boolean "OTHER" - :nil-false-boolean #boolean "" - :long #long "1234" - :double #double "4567.8" - :keyword #keyword "foo/bar" - :already-a-keyword #keyword :foo/bar - :env-keyword #keyword #or [#env KEYWORD "abc"] - :dummy #or [#env "DUMMY" "dummy"] - :triple-or #or [#env "DUMMY" #prop "DUMMY" "dummy"] - :prop #prop "DUMMY" - :network-call #expensive-network-call 7 - :test-read-str #read-edn "[:foo :bar :baz]" - :test-read-env #read-edn #prop DUMMY_READ + :port #aero/profile {:dev 8000 + :prod 80} + :True-boolean #aero/boolean "True" + :true-boolean #aero/boolean "true" + :false-boolean #aero/boolean "false" + :trivial-false-boolean #aero/boolean "OTHER" + :nil-false-boolean #aero/boolean "" + :long #aero/long "1234" + :double #aero/double "4567.8" + :keyword #aero/keyword "foo/bar" + :already-a-keyword #aero/keyword :foo/bar + :env-keyword #aero/keyword #aero/or [#aero/env KEYWORD "abc"] + :dummy #aero/or [#aero/env "DUMMY" "dummy"] + :triple-or #aero/or [#aero/env "DUMMY" #aero/prop "DUMMY" "dummy"] + :prop #aero/prop "DUMMY" + :network-call #my/expensive-network-call 7 + :test-read-str #aero/read-edn "[:foo :bar :baz]" + :test-read-env #aero/read-edn #aero/prop DUMMY_READ :referencing-network-call ^:ref [:network-call] :key-part :b :refer-me {:a {:b {1234 :sentinel}}} - :complex-ref #ref [:refer-me :a #ref [:key-part] #ref [:long]]} + :complex-ref #aero/ref [:refer-me :a #aero/ref [:key-part] #aero/ref [:long]]} diff --git a/test/aero/core_test.cljc b/test/aero/core_test.cljc index 8b5abd5..928ebcc 100644 --- a/test/aero/core_test.cljc +++ b/test/aero/core_test.cljc @@ -18,13 +18,13 @@ #?(:clj (System/getenv (str s))) #?(:cljs (aget js/process.env s))) -(defmethod reader 'expensive-network-call - [_ tag value] - (deferred - (swap! network-call-count inc) - (inc value))) +(defmethod reader 'my/expensive-network-call + [_ tag value] + (deferred + (swap! network-call-count inc) + (inc value))) -(defmethod reader 'myflavor +(defmethod reader 'my/flavor [opts tag value] (if (= value :favorite) :chocolate :vanilla)) @@ -192,3 +192,43 @@ (let [before-call-count @network-call-count config (read-config "test/aero/config.edn")] (is (= (inc before-call-count) @network-call-count)))) + +#?(:clj + (defmacro return-stderr-stream [& body] + `(let [sw# (java.io.StringWriter.)] + (binding [*err* sw#] + ~@body) + (str sw#)))) + +#?(:clj + (deftest deprecation-test + (is (= (return-stderr-stream + (read-config "test/aero/deprecated-config.edn")) + (clojure.string/join "\n" + ["WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #long is decrecated; use #aero/long instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #ref is decrecated; use #aero/ref instead." + "WARNING: #include is decrecated; use #aero/include instead." + "WARNING: #env is decrecated; use #aero/env instead." + "WARNING: #or is decrecated; use #aero/or instead." + "WARNING: #join is decrecated; use #aero/join instead." + "WARNING: #double is decrecated; use #aero/double instead." + "WARNING: #hostname is decrecated; use #aero/hostname instead." + "WARNING: #profile is decrecated; use #aero/profile instead." + "WARNING: #merge is decrecated; use #aero/merge instead." + "WARNING: #keyword is decrecated; use #aero/keyword instead." + "WARNING: #read-edn is decrecated; use #aero/read-edn instead." + "WARNING: #prop is decrecated; use #aero/prop instead." + "WARNING: #envf is decrecated; use #aero/envf instead." + "WARNING: #user is decrecated; use #aero/user instead." + "WARNING: #boolean is decrecated; use #aero/boolean instead." + ""]))))) diff --git a/test/aero/deprecated-config.edn b/test/aero/deprecated-config.edn new file mode 100644 index 0000000..972fa4e --- /dev/null +++ b/test/aero/deprecated-config.edn @@ -0,0 +1,18 @@ +{:greeting "Hello World!" + :smart-term #join ["Terminal is " #or [#env NONE "smart"]] + :dumb-term-envf #envf ["Terminal is %s" TERM] + :remote #include "included.edn" + :port #profile {:dev 8000 + :prod 80} + :true-boolean #boolean "true" + :long #long "1234" + :double #double "4567.8" + :keyword #keyword "foo/bar" + :prop #prop "DUMMY" + :test-read-str #read-edn "[:foo :bar :baz]" + :hostname #hostname {"emerald" 10} + :user #user {"dbowie" 69} + :merge #merge [{:foo :bar} {:foo :zip}] + :key-part :b + :refer-me {:a {:b {1234 :sentinel}}} + :complex-ref #ref [:refer-me :a #ref [:key-part] #ref [:long]]} diff --git a/test/aero/hosts.edn b/test/aero/hosts.edn index db5ef1d..1f2b10b 100644 --- a/test/aero/hosts.edn +++ b/test/aero/hosts.edn @@ -1,4 +1,4 @@ -{:color #hostname {"emerald" "green" - #{"diamond"} "white" - :default "black"} - :weight #hostname {"emerald" 10}} +{:color #aero/hostname {"emerald" "green" + #{"diamond"} "white" + :default "black"} + :weight #aero/hostname {"emerald" 10}} diff --git a/test/aero/includes.edn b/test/aero/includes.edn index eae4f67..ee9d7fd 100644 --- a/test/aero/includes.edn +++ b/test/aero/includes.edn @@ -1,6 +1,6 @@ -#include #profile {:relative "sub/includes.edn" - :relative-abs #join [#env "PWD" "/test/aero/sub/includes.edn"] - :resource "aero/sub/includes.edn" - :file "test/aero/sub/includes.edn" - :file-does-not-exist "goodluck.edn" - :map :sub-includes} +#aero/include #aero/profile {:relative "sub/includes.edn" + :relative-abs #aero/join [#aero/env "PWD" "/test/aero/sub/includes.edn"] + :resource "aero/sub/includes.edn" + :file "test/aero/sub/includes.edn" + :file-does-not-exist "goodluck.edn" + :map :sub-includes} diff --git a/test/aero/long_prop.edn b/test/aero/long_prop.edn index 6f2ef3e..53a8bf3 100644 --- a/test/aero/long_prop.edn +++ b/test/aero/long_prop.edn @@ -1 +1 @@ -{:long-prop #long #prop "FOO"} +{:long-prop #aero/long #aero/prop "FOO"} diff --git a/test/aero/sub/includes.edn b/test/aero/sub/includes.edn index e51e772..1222cdf 100644 --- a/test/aero/sub/includes.edn +++ b/test/aero/sub/includes.edn @@ -1,5 +1,5 @@ -#include #profile {:relative "../valid.edn" - :relative-abs "../valid.edn" - :resource "aero/valid.edn" - :file "test/aero/valid.edn" - :map :valid-file} +#aero/include #aero/profile {:relative "../valid.edn" + :relative-abs "../valid.edn" + :resource "aero/valid.edn" + :file "test/aero/valid.edn" + :map :valid-file}