diff --git a/deps.edn b/deps.edn index 275b302e..4d895b93 100644 --- a/deps.edn +++ b/deps.edn @@ -37,7 +37,7 @@ org.clojure/core.match {:mvn/version "1.0.0"} hiccup/hiccup {:mvn/version "2.0.0-alpha2"} rewrite-clj/rewrite-clj {:mvn/version "1.0.605-alpha"} - selmer/selmer {:mvn/version "1.12.40"} + selmer/selmer {:mvn/version "1.12.44"} com.taoensso/timbre {:mvn/version "5.1.2"} org.clojure/tools.logging {:mvn/version "1.1.0"}} :aliases {:babashka/dev @@ -87,9 +87,9 @@ com.grammarly/omniconf {:mvn/version "0.4.3"} crispin/crispin {:mvn/version "0.3.8"} org.clojure/data.json {:mvn/version "2.4.0"}} - :classpath-overrides {org.clojure/clojure nil - org.clojure/spec.alpha nil - org.clojure/core.specs.alpha nil}} + :classpath-overrides {org.clojure/clojure nil + org.clojure/spec.alpha nil + org.clojure/core.specs.alpha nil}} :clj-nvd {:extra-deps {clj-nvd/clj-nvd {:git/url "https://github.com/miikka/clj-nvd.git" :sha "f2ec98699e057a379baf170cb49cf7ad76874a70"}} diff --git a/feature-selmer/babashka/impl/selmer.clj b/feature-selmer/babashka/impl/selmer.clj index 49ad7dda..14ce03c8 100644 --- a/feature-selmer/babashka/impl/selmer.clj +++ b/feature-selmer/babashka/impl/selmer.clj @@ -1,6 +1,7 @@ (ns babashka.impl.selmer {:no-doc true} (:require [babashka.impl.classpath :refer [resource]] + [babashka.impl.common :refer [ctx]] [sci.core :as sci] [selmer.filters :as filters] [selmer.parser] @@ -10,20 +11,23 @@ (def spns (sci/create-ns 'selmer.parser nil)) +(def include #{'env-map}) + (defn make-ns [ns sci-ns] (reduce (fn [ns-map [var-name var]] (let [m (meta var) no-doc (:no-doc m) doc (:doc m) arglists (:arglists m)] - (if no-doc ns-map - (assoc ns-map var-name - (sci/new-var (symbol var-name) @var - (cond-> {:ns sci-ns - :name (:name m)} - (:macro m) (assoc :macro true) - doc (assoc :doc doc) - arglists (assoc :arglists arglists))))))) + (if (and no-doc (not (contains? include var-name))) + ns-map + (assoc ns-map var-name + (sci/new-var (symbol var-name) @var + (cond-> {:ns sci-ns + :name (:name m)} + (:macro m) (assoc :macro true) + doc (assoc :doc doc) + arglists (assoc :arglists arglists))))))) {} (ns-publics ns))) @@ -55,11 +59,37 @@ (binding [util/*escape-variables* @escape-variables] (selmer.parser/render-template template context-map))) +(defn sci-ns-resolve [ns fqs] + (sci/eval-form @ctx (list 'clojure.core/ns-resolve ns (list 'quote fqs)))) + +(defn force! [x] + (if (instance? clojure.lang.IDeref x) @x x)) + +(defn ^:no-doc resolve-var-from-kw [ns env kw] + (if (namespace kw) + (when-let [v (sci-ns-resolve ns (symbol (str (namespace kw) "/" (name kw))))] {kw (force! v)}) + (or + ;; check local env first + (when-let [[_ v] (find env kw)] {kw v}) + (when-let [v (sci-ns-resolve ns (symbol (name kw)))] {kw (force! v)})))) + +(defmacro << + "Resolves the variables from your template string from the local-env, or the + namespace and puts them into your template for you. + e.g. (let [a 1] (<< \"{{a}} + {{a}} = 2\")) ;;=> \"1 + 1 = 2\" " + [s] + `(->> (selmer.parser/known-variables ~s) + (mapv #(selmer.parser/resolve-var-from-kw ~(deref sci/ns) (selmer.parser/env-map) %)) + (apply merge) + (selmer.parser/render ~s))) + (def selmer-parser-namespace (-> selmer-parser-ns (assoc 'render-file (sci/copy-var render-file spns) 'render (sci/copy-var render spns) - 'render-template (sci/copy-var render-template spns)))) + 'render-template (sci/copy-var render-template spns) + 'resolve-var-from-kw (sci/copy-var resolve-var-from-kw spns) + '<< (sci/copy-var << spns)))) (def stns (sci/create-ns 'selmer.tags nil)) diff --git a/project.clj b/project.clj index 2b2c2787..d7055f0e 100644 --- a/project.clj +++ b/project.clj @@ -59,7 +59,7 @@ :feature/rewrite-clj {:source-paths ["feature-rewrite-clj"] :dependencies [[rewrite-clj/rewrite-clj "1.0.605-alpha"]]} :feature/selmer {:source-paths ["feature-selmer"] - :dependencies [[selmer/selmer "1.12.40"]]} + :dependencies [[selmer/selmer "1.12.44"]]} :test [:feature/xml :feature/lanterna :feature/yaml diff --git a/test-resources/lib_tests/babashka/run_all_libtests.clj b/test-resources/lib_tests/babashka/run_all_libtests.clj index a9b11e6d..31b98f02 100644 --- a/test-resources/lib_tests/babashka/run_all_libtests.clj +++ b/test-resources/lib_tests/babashka/run_all_libtests.clj @@ -11,14 +11,17 @@ (or (empty? ns-args) (contains? ns-args ns))) -(defn test-namespaces [& namespaces] - (let [namespaces (seq (filter test-namespace? namespaces))] - (when namespaces - (doseq [ns namespaces] - (require ns)) - (let [m (apply t/run-tests namespaces)] - (swap! status (fn [status] - (merge-with + status (dissoc m :type)))))))) +(defmacro test-namespaces [& namespaces] + (let [namespaces (map second namespaces) + namespaces (seq (filter test-namespace? namespaces)) + quoted-namespaces (map #(list 'quote %) namespaces) + requires (map #(list 'require %) quoted-namespaces)] + (when (seq requires) + `(do + ~@requires + (let [m# (t/run-tests ~@quoted-namespaces)] + (swap! status (fn [status#] + (merge-with + status# (dissoc m# :type))))))))) (def windows? (-> (System/getProperty "os.name") (str/lower-case) diff --git a/test-resources/lib_tests/selmer/benchmark.clj b/test-resources/lib_tests/selmer/benchmark.clj new file mode 100644 index 00000000..ca5e2983 --- /dev/null +++ b/test-resources/lib_tests/selmer/benchmark.clj @@ -0,0 +1,4 @@ +(ns selmer.benchmark) + +(def user (repeat 10 [{:name "test"}])) + diff --git a/test-resources/lib_tests/selmer/core_test.clj b/test-resources/lib_tests/selmer/core_test.clj index a2f35782..0e200953 100644 --- a/test-resources/lib_tests/selmer/core_test.clj +++ b/test-resources/lib_tests/selmer/core_test.clj @@ -7,8 +7,10 @@ [clojure.test :refer [deftest are is testing]] [selmer.filters :as f] [selmer.parser :as p :refer [render render-file render-template - parse parse-input known-variables]] - [selmer.tags :as tags]) + parse parse-input known-variables + << resolve-var-from-kw env-map]] + [selmer.tags :as tags] + [clojure.set :as set]) (:import (java.io StringReader ByteArrayInputStream) java.io.File java.util.Locale)) @@ -1257,3 +1259,38 @@ "debug-value")) (testing "basic rendering escapes HTML" (is (str/includes? (basic-edn->html {:a "
"}) """))))
+
+(deftest allow-whitespace-in-filter-test
+ (is (= "bar" (render "{{ foo | default:bar }}" {:dude 1}))))
+
+;; String interopolation
+
+;; setup namespaces, vars + alias for << tests
+(def one "one")
+(def y 1)
+(require '[selmer.benchmark :as sb])
+
+(deftest string-interpolation-test
+ (is (= "one plus one is two."
+ (<< "{{one}} plus {{one}} is two.")))
+
+ (let [one 1]
+ (is (= "1 + 1 = 2"
+ (<< "{{one}} + {{one}} = 2"))))
+
+ (let [one 1
+ one 11]
+ (is (= "11 + 11 = 2"
+ (<< "{{one}} + {{one}} = 2"))))
+
+ (is (= "selmer.benchmark/user has 10 items."
+ (<< "selmer.benchmark/user has {{selmer..benchmark/user|count}} items.")))
+
+ (is (= "sb/user has 10 items."
+ (<< "sb/user has {{sb/user|count}} items.")))
+
+ (is (= "" (let [y nil] (<< "{{y}}")))
+ "<< picks up local values even if they are nil")
+
+ (is (= "false" (let [y false] (<< "{{y}}")))
+ "<< picks up local values even if they are false"))