Commits

Steve Losh committed 38e3325

Context lookups and a bit more.

Comments (0)

Files changed (4)

src/dram/context.clj

+(ns dram.context)
+
+
+(defn to-keys
+  "Return a vector of possible keys for this string.
+
+  Possible keys for a string include the string itself or a keyword version of
+  it.  If the string contains something parseable as an integer, an integer is
+  also a possibility.
+
+  Note that by integer I actually mean long because Clojure is stupid at
+  comparing the two.  But whatever, you get the idea.
+
+  "
+  [s]
+  (let [i (try
+            (Long. s)
+            (catch java.lang.NumberFormatException e nil))
+        k (keyword s)]
+    (if i
+      [i k s]
+      [k s])))
+
+(defn try-keys [keys context]
+  (when (seq keys)
+    (let [[k & ks] keys]
+      (if (contains? context k)
+        (get context k)
+        (recur ks context)))))
+
+(defn lookup [path context]
+  (cond
+    (empty? path) context
+    (nil? context) nil
+    :else (let [[head & tail] path
+                possible-keys (to-keys head)
+                result (try-keys possible-keys context)]
+            (recur tail result))))
+
+

src/dram/core.clj

-(ns dram.core)
+(ns dram.core
+  (:require [dram.parser :refer [parse]]
+            [dram.context :refer [lookup]]))
 
+
+(defn render-block [block context])
+
+(defn render-value [{:keys [base filters]} context]
+  (let [v (cond
+            (number? base) (str base)
+            (string? base) base
+            (seq base) (lookup base context))]
+    v))
+
+(defn render-chunk [chunk context]
+  (cond
+    (string? chunk) chunk
+    (= :value (:type chunk)) (render-value chunk context)
+    (= :block (:type chunk)) (render-block chunk context)))
+
+(defn render-base [{:keys [contents]} context]
+  (apply str (map #(render-chunk % context) contents)))
+
+(defn render-child [ast context])
+
+(defn render-ast [ast context]
+  (case (:type ast)
+    :base-template (render-base ast context)
+    :child-template (render-child ast context)))
+
+(defn render-string [raw-template context]
+  (render-ast (parse raw-template) context))

test/dram/test/context.clj

+(ns dram.test.context
+  (:require [dram.context :as c]
+            [clojure.test :refer :all]))
+
+
+(deftest to-keys-test
+  (testing "Strings parse to string, keyword, and possibly numeric keys."
+    (are [s ks] (= ks (set (c/to-keys s)))
+         "foo"        #{:foo "foo"}
+         "a"          #{:a "a"}
+         "boots-cats" #{:boots-cats "boots-cats"}
+         "1"          #{:1 "1" 1}
+         "a-1"        #{:a-1 "a-1"}
+         "-1"         #{:-1 "-1" -1})))
+
+(deftest try-keys-test
+  (testing "try-keys finds the value if it exists in the context"
+    (are [ks context value] (= value (c/try-keys ks context))
+         [:foo :bar]  {:foo 1}   1
+         [:foo :bar]  {:bar 1}   1
+         ["a" :foo]   {"a" 1}    1
+         [:foo "a" 0] {"a" 1}    1
+         [1 :foo "a"] {"a" 1}    1
+         [0]          [:a :b :c] :a
+         [0 1]        [:a :b :c] :a
+         [1 0]        [:a :b :c] :b
+         [:foo 0]     [:a :b :c] :a))
+  (testing "try-keys returns nil if it can't find the key in the context"
+    (are [ks context] (= nil (c/try-keys ks context))
+         [:foo :bar] {:dogs 1}
+         [:bar]      {:dogs 1}
+         []          {:dogs 1}
+         [0]         {:dogs 1}
+         [:foo "a"]  [1 2 3]
+         [-1 4]      [1 2 3])))
+
+(deftest lookup-test
+  (testing "lookup finds things!"
+    (are [path context] (= :goal (c/lookup path context))
+         ; Flat contexts are fine.
+         ["a"]  {:a :goal}
+         ["a"]  {"a" :goal}
+         ["0"]  {0 :goal}
+         ["1"]  {1 :goal}
+         ["-1"] {-1 :goal}
+         ["0"]  {:0 :goal}
+         ["1"]  {:1 :goal}
+         ["-1"] {:-1 :goal}
+         ["0"]  {"0" :goal}
+         ["1"]  {"1" :goal}
+         ["-1"] {"-1" :goal}
+         ["a"]  {:b 0 :a :goal}
+         ; Vectors can be contexts too.
+         ["0"] [:goal :b :c]
+         ["1"] [:a :goal :c]
+         ; Multi-level contexts should work too.
+         ["a" "b"]     {:a {:b :goal}}
+         ["a" "0"]     {:a {0 :goal}}
+         ["a" "0"]     {:a [:goal]}
+         ["a" "b" "c"] {:a {"b" {"c" :goal}}}
+         ["a" "b" "c"] {:a {"b" {:c :goal}}}
+         ["0" "0"]     [[:goal]]))
+  (testing "lookup returns nil if it can't find the path at any stage"
+    (are [path context] (nil? (c/lookup path context))
+         ["a"]     {}
+         ["a"]     []
+         ["a"]     {:b 0}
+         ["a" "b"] {:a 0}
+         ["a" "b"] {:b 0}
+         ["a" "b"] {:a {:c 0}}
+         ["a" "b"] {:a [1 2 3]}
+         ["a" "b"] {:q {:b 0}}
+         ["-1"]    [:a]
+         ["0" "1"] [[:a]]
+         ["1" "0"] [[:a]])))

test/dram/test/core.clj

Empty file added.