Commits

Steve Losh committed d5f07ed

More parser work and README update.

Comments (0)

Files changed (3)

 # Dram
 
+**Not ready yet.  Do not use this.**
+
 Clojure templating that won't make you drink.
 
-Not ready yet.
+Dram is a Clojure templating library for Django/Jinja-like templates.
+
+**Not ready yet.  Do not use this.**
 
 Usage
 -----
 
 Don't yet.
 
+Why?
+----
+
+Because I wasn't happy with any of the existing Clojure templating libraries.
+
+Clostache and friends implement Mustache, which is a crippled templating
+library.  It doesn't have template inheritance (without which I'd go crazy in
+the real world).
+
+It also takes a very strict approach to having logic in templates (basically:
+"don't").  But sometimes you *want* logic in the templates, if it belongs there!
+
+Hiccup is useless once you hire a frontend developer.
+
+> Frontend Dev: "Okay I'm ready to start, where's the HTML?"
+> 
+> Backend Dev: "It's produced by these files full of s-expressions, vectors, and
+> hashes.  There are even some API docs!"
+> 
+> Frontend Dev: "Did you just tell me to go fuck myself?"
+> 
+> Backend Dev: "I believe I did, Bob."
+
+Enlive pushes logic into the Clojure layer where frontend developers can't
+effectively get at it.  From my limited experience it also seems like you need
+to write a lot of custom Clojure code when you're using it, even for things as
+simple as for loops.
+
+It might be great for teams where everyone knows Clojure.  I dunno.  It's pretty
+crazy.  It's certainly not something your frontend developer is going to pick up
+in a week.
+
+Django/Jinja-style templates are easy to learn for frontend developers and
+provide a good mix of power and simplicity.
+
 License
 -------
 

src/dram/parser.clj

 
 
 ; Block -----------------------------------------------------------------------
+(declare template-chunk)
+
 (defparser tag-block-name-char []
   (choice (letter)
           (digit)
            (let->> [_ (string "endblock")]
              (always nil))))
 
+(defparser tag-block-contents []
+  (many (template-chunk)))
+
 (defparser tag-block []
   (let->> [{:keys [name]} (tag-block-open)
-           guts (string "")
+           contents (tag-block-contents)
            close (tag-block-close)]
     (always {:type :block
              :name name
-             :contents []
-             })))
+             :contents contents})))
 
 
 ; Raw Text --------------------------------------------------------------------
 
 ; High-Level ------------------------------------------------------------------
 (defparser template-chunk []
-  (choice (variable)))
+  (choice (attempt (variable))
+          (attempt (raw-text))))
 
 (defparser template-base []
-  (many (choice (template-chunk)
-                (tag-block))))
+  (many (choice (attempt (tag-block))
+                (attempt (template-chunk)))))
 
 (defparser template-child []
   (optional-whitespace)

test/dram/test/parser.clj

     (is-error "{% endblock foo %}" (p/tag-block-open)))
   (testing "Empty blocks are totally fine."
     (parses-as "{% block foo %}{% endblock %}" (p/tag-block)
-               {:type :block :name "foo" :contents []})))
+               {:type :block :name "foo" :contents []}))
+  (testing "Blocks can contain anything except other blocks."
+    (parses-as "{% block foo %}hi{% endblock %}" (p/tag-block)
+               {:type :block :name "foo" :contents ["hi"]})
+    (parses-as "{% block foo %}hi {{ 1 }} five{% endblock %}" (p/tag-block)
+               {:type :block :name "foo" :contents ["hi " 1 " five"]})
+
+    ))
+
 (deftest raw-text-test
   (testing "Raw text parses to a Clojure string."
     (parses-as "Hello" (p/raw-text) "Hello")
     (parses-as "hello there world" (p/raw-text) "hello there world")
-    (parses-as "  { foo } is okay" (p/raw-text) "  { foo } is okay"))
-    (parses-as "so is { % foo % }" (p/raw-text) "so is { % foo % }") 
+    (parses-as "  { foo } is okay" (p/raw-text) "  { foo } is okay")
+    (parses-as "so is { % foo % }" (p/raw-text) "so is { % foo % }"))
   (testing "Reserved characters do not parse as raw text."
     (parses-as "Hello{{ world }}" (p/raw-text) "Hello")
     (parses-as "Hello{% block world %}" (p/raw-text) "Hello"))
   (testing "Raw text is not zero-length."
     (is-error "{{ world }}" (p/raw-text))
-    (is-error "" (p/raw-text)))
-  )
+    (is-error "" (p/raw-text))))
 
+(deftest template-chunk-test
+ (testing "A template chunk can be raw text."
+    (parses-as "Hello" (p/template-chunk) "Hello")
+    (parses-as "  { foo }" (p/template-chunk) "  { foo }"))
+  (testing "A template chunk can be a variable."
+    (parses-as "{{ 1 }}" (p/template-chunk) 1)))
+
+(deftest template-base-test
+  (testing "A base template can be made up of raw text, variables, ...."
+    (parses-as "" (p/template-base) [])
+    (parses-as "Hello" (p/template-base)
+               ["Hello"])
+    (parses-as "Hello {{ \"Steve\" }}" (p/template-base)
+               ["Hello " "Steve"])
+    (parses-as "Age: {{ 27 }} years old" (p/template-base)
+               ["Age: " 27 " years old"]))
+  (testing "A base template can contain blocks."
+    (parses-as "{% block foo %}{% endblock %}" (p/template-base)
+               [{:type :block :name "foo" :contents []}])
+    (parses-as "hello {% block username %}{% endblock %}" (p/template-base)
+               ["hello " {:type :block :name "username" :contents []}])
+    (parses-as "foo {% block a %}{% endblock %} bar {{ 42 }}" (p/template-base)
+               ["foo "
+                {:type :block :name "a" :contents []}
+                " bar "
+                42])))