Commits

Steve Losh committed 420f547

Start hooking up the rendering pipeline.

Comments (0)

Files changed (7)

src/dram/context.clj

                 result (try-keys possible-keys context)]
             (recur tail result))))
 
-

src/dram/core.clj

             [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))

src/dram/parser.clj

              :blocks (into {} (map (juxt :name :contents) blocks))})))
 
 (defparser template []
-  (let [result (either (template-base)
-                       (template-child))
-        _ (eof)]
+  (let->> [result (either (template-base)
+                          (template-child))
+           _ (eof)]
     (always result)))
 
 

src/dram/rendering.clj

+(ns dram.rendering
+  (:require [dram.parser :refer [parse]]
+            [dram.context :refer [lookup]]))
+
+
+(defn render-block [block context])
+
+(defn render-value [{:keys [base filters]} context]
+  (let [v (if (or (number? base) (string? base))
+            base
+            (lookup base context))]
+    v))
+
+(defn render-chunk [chunk context]
+  (cond
+    (string? chunk) chunk
+    (= :value (:type chunk)) (str (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 (render-base ast context)
+    :child (render-child ast context)))
+
+(defn render-string [raw-template context]
+  (render-ast (parse raw-template) context))

test/dram/test/parser.clj

 (ns dram.test.parser
   (:require [dram.parser :as p]
+            [dram.test.utils.ast :refer [e b v f i it ct bt]]
             [clojure.test :refer :all]
             [the.parsatron :as tron :refer [run defparser let->> >>]]))
 
+
 ; Extra Utility Parsers -------------------------------------------------------
 (defparser complete [p]
   (let->> [r p _ (tron/eof)]
                  data)))))
 
 
-; AST Element Shortcuts -------------------------------------------------------
-(defn e  ; Extends
-  ([path]
-   {:type :extends :path path}))
-
-(defn b  ; Block
-  ([name]
-   (b name []))
-  ([name contents]
-   {:type :block :name name :contents contents}))
-
-(defn v  ; Value
-  ([base]
-   (v base []))
-  ([base filters]
-   {:type :value :base base :filters filters}))
-
-(defn f  ; Filter
-  ([path]
-   (f path []))
-  ([path args]
-   {:path path :args args}))
-
-(defn i  ; Innards
-  ([path]
-   (i path []))
-  ([path args]
-   {:path path :args args}))
-
-(defn it ; Inline Tag
-  ([path]
-   (it path []))
-  ([path args]
-   {:type :inline-tag :path path :args args}))
-
-(defn ct ; Child Template
-  ([extends]
-   (ct extends {}))
-  ([extends blocks]
-   {:type :child :extends extends :blocks blocks}))
-
-(defn bt ; Base Template
-  ([contents]
-   {:type :base :contents contents}))
-
-
 ; Tests -----------------------------------------------------------------------
 (deftest optional-test
   (testing-parser

test/dram/test/rendering.clj

+(ns dram.test.rendering
+  (:require [dram.rendering :as r]
+            [dram.test.utils.ast :as ast]
+            [clojure.test :refer :all]))
+
+
+(deftest render-value-unfiltered-test
+  (testing "Literals render to themselves."
+    (are [base context result] (= result (r/render-value (ast/v base) context))
+         1     {}     1
+         "foo" {}     "foo"
+         ""    {}     ""
+         0     {0 :a} 0
+         0     [:a]   0
+         -1    {}     -1))
+  (testing "Paths look things up in the context."
+    (are [base context] (= :goal (r/render-value (ast/v base) 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 "Paths can bail early if a segment isn't found."
+    (are [base context] (nil? (r/render-value (ast/v base) 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]])))
+
+(deftest render-chunk-simple-test
+  (testing "Strings are passed straight through when rendered as chunks."
+    (are [input context result] (= result (r/render-chunk input context))
+         ""    {}     ""
+         ""    {:a 1} ""
+         "foo" {}     "foo"))
+  (testing "Values get cast to strings on the way out."
+    (are [input context result] (= result (r/render-chunk (ast/v input) context))
+         1     {}       "1"
+         -1    {}       "-1"
+         "foo" {}       "foo"
+         ["a"] {:a nil} ""
+         ["a"] {:a 1}   "1"
+         ["a"] {:a :b}  ":b"
+         ["a"] {:a "x"} "x")))
+
+(deftest render-base-simple-test
+  (testing "Simple, string-only base templates should be trivial."
+    (are [template context result] (= result (r/render-string template context))
+         "foo" {}     "foo"
+         ""    {}     ""
+         ""    {:a 1} ""
+         "foo" {:a 1} "foo"))
+  (testing "Base templates without blocks should be pretty simple too."
+    (are [template context result] (= result (r/render-string template context))
+         "a {{ 1 }} b"       {} "a 1 b"
+         "a {{ \"and\" }} b" {} "a and b"
+         "{{ 1}}{{2}}"       {} "12"
+
+         "Hello, {{ user.name }}!" {:user {:name "Steve"}}   "Hello, Steve!"
+         "Hello, {{ user.name }}!" {:user {"name" "Steve"}}  "Hello, Steve!"
+         "Hello, {{ user.name }}!" {"user" {:name "Steve"}}  "Hello, Steve!"
+         "Hello, {{ user.name }}!" {"user" {"name" "Steve"}} "Hello, Steve!"
+
+         "{{ a.1 }}, {{a.2}}" {:a "xyz"}               "y, z"
+         "{{ a.0 }}, {{a.2}}" {:a ["foo" "bar" "baz"]} "foo, baz"
+         "{{ a.0 }}, {{a.2}}" {:a {0 :q "2" :r}}       ":q, :r"
+         )))

test/dram/test/utils/ast.clj

+(ns dram.test.utils.ast)
+
+; AST Element Shortcuts -------------------------------------------------------
+(defn e  ; Extends
+  ([path]
+   {:type :extends :path path}))
+
+(defn b  ; Block
+  ([name]
+   (b name []))
+  ([name contents]
+   {:type :block :name name :contents contents}))
+
+(defn v  ; Value
+  ([base]
+   (v base []))
+  ([base filters]
+   {:type :value :base base :filters filters}))
+
+(defn f  ; Filter
+  ([path]
+   (f path []))
+  ([path args]
+   {:path path :args args}))
+
+(defn i  ; Innards
+  ([path]
+   (i path []))
+  ([path args]
+   {:path path :args args}))
+
+(defn it ; Inline Tag
+  ([path]
+   (it path []))
+  ([path args]
+   {:type :inline-tag :path path :args args}))
+
+(defn ct ; Child Template
+  ([extends]
+   (ct extends {}))
+  ([extends blocks]
+   {:type :child :extends extends :blocks blocks}))
+
+(defn bt ; Base Template
+  ([contents]
+   {:type :base :contents contents}))
+