One thing I noticed is that its templates escape all strings passed to
them. In my case I have some strings that are already HTML and don't
need to be escaped; is there any way to skip that?
Also, I keep getting null pointer exceptions when I try to use
deftemplate with a path to an HTML file that isn't on the
classpath. This seems like an odd restriction. Is there a way to use a
file in an arbitrary location as a template?
thanks,
Phil
> Regarding your first question, you can use (net.cgrand.enlive-html/
> escaped your-string) to skip the escaping of your strings.
Thanks, that's helpful.
I've also noticed one more oddity. Consider this snippet:
(deftemplate index "foo/bar/template.html" [articles]
[:div.articles] (for [{:keys [title content]} articles]
~(at
[:h2] title
[:p.content] (escaped content))))
Applied to this template:
<div class="articles">
<h2></h2>
<p class="content"></p>
</div>
When the thing to insert into the element is just a string (as is the
case with the h2 element), it gets inserted inside the element. But in
the second case, the content paragraph is _replaced_ with the escaped
content rather than putting the escaped content inside the paragraph
tag. So if title had the value "Congratulations" and content had the
value "You win", the final result would be something like:
<h2>Congratulations</h2>
You win
The paragraph node disappears...
From what I can tell the difference seems to be that it goes inside the
node if it's a string, and it replaces the node if it's a function call,
even if the function call results in a string.
This behaviour seems really strange. I feel like maybe there's some
logic I'm missing and that it's not the string/function call difference
that determines replacement vs insertion, but I can't figure out why
it's behaving this way. Would appreciate any explanation.
thanks,
Phil
Phil Hagelberg a écrit :
> I'm using the Enlive library, and so far I've been pretty impressed. The
> way it separates templates out into their own files and doesn't allow
> any logic to get mixed up with them is great.
>
Thanks for the kind words.
> One thing I noticed is that its templates escape all strings passed to
> them. In my case I have some strings that are already HTML and don't
> need to be escaped; is there any way to skip that?
>
Tom gave you the answer.
> Also, I keep getting null pointer exceptions when I try to use
> deftemplate with a path to an HTML file that isn't on the
> classpath. This seems like an odd restriction. Is there a way to use a
> file in an arbitrary location as a template?
>
You can pass an xml structure to deftemplate instead of the path, it's
the only way to circumvent the classpath at the moment.
I'll look into it.
Christophe
--
Professional: http://cgrand.net/ (fr)
On Clojure: http://clj-me.blogspot.com/ (en)
I'm not quite happy with this behavior: I wanted to preserve the brevity of setting content from a parameter without resorting to (text my-parameter) but it makes things too irregular.
This "feature" will certainly go away as I'm planning a redesign.
Right now there are several cases:
1/ If the right hand side of a rule is a list and expands to a
template-macro, it is applied without needing to unquote it. The matched
element is replaced by the result of the template-macro.
2/ If the right hand side of a rule is a list and does not expand to a
template-macro, it's random clojure code (which can apply
template-macros on the matched element using unquote). The matched
element is replaced.
3/ Otherwise the right hand side form is implicitly surrounded by the
'text template-macro and go to 1/ ('text replaces the content).
Phil, what's your usecase for inserting raw html, where does it come from?
Thanks,
> I'm not quite happy with this behavior: I wanted to preserve the
> brevity of setting content from a parameter without resorting to (text
> my-parameter) but it makes things too irregular. This "feature" will
> certainly go away as I'm planning a redesign.
Yeah, I'm all for brevity, but it seems that in this case it makes the
rules harder to understand. Please keep the mailing list informed as to
the progress of your redesign.
I notice you use with-test to mix your tests in together with your
implementation. This seems to be less common than storing the tests in
their own file; I'm wondering if you are happy with this approach? One
advantage of keeping things in their own file is that it's easier for
the test suite to serve as a usage example, but keeping it together may
be more convenient for other reasons. I haven't tried it myself.
> Right now there are several cases:
> 1/ If the right hand side of a rule is a list and expands to a
> template-macro, it is applied without needing to unquote it. The matched
> element is replaced by the result of the template-macro.
> 2/ If the right hand side of a rule is a list and does not expand to a
> template-macro, it's random clojure code (which can apply
> template-macros on the matched element using unquote). The matched
> element is replaced.
> 3/ Otherwise the right hand side form is implicitly surrounded by the
> 'text template-macro and go to 1/ ('text replaces the content).
Thanks for explaining.
> > Also, I keep getting null pointer exceptions when I try to use
> > deftemplate with a path to an HTML file that isn't on the
> > classpath. This seems like an odd restriction. Is there a way to use a
> > file in an arbitrary location as a template?
>
> You can pass an xml structure to deftemplate instead of the path, it's
> the only way to circumvent the classpath at the moment.
> I'll look into it.
I can see the benefit of looking on the classpath since it means you can
store your template inside a jar and distribute everything that way. But
I think it would be convenient if it could check for the existence of
the file relative to the current directory first.
> Phil, what's your usecase for inserting raw html, where does it come from?
I'm processing a number of feeds, and I want to output their
contents. But I could think of many others; perhaps people would want to
use an HTML-generating library like Markdown to turn user input into
HTML before inserting it into the template. It's definitely less common
though.
-Phil
I see two advantages to using with-test:
* documentation/usage examples as you pointed out,
* and that makes me feel guiltier for nor updating them.
> I can see the benefit of looking on the classpath since it means you can
> store your template inside a jar and distribute everything that way. But
> I think it would be convenient if it could check for the existence of
> the file relative to the current directory first.
>
I'll add support for java.io.File and java.net.URL as an argument to
template/deftemplate/snippet/defsnippet in complement of String and xml
tree.
> I see two advantages to using with-test:
> * documentation/usage examples as you pointed out,
Actually I meant that it's nicer for documentation to have the tests in
a separate file since when you're trying to learn how to use the library
initially, you don't care about the implementation. So it's easier to
focus just on the behaviour when you only see the tests before you. But
that's a minor point.
> * and that makes me feel guiltier for nor updating them.
Well that's a downside; if they are stored somewhere else it could be
easier to ignore them. =) Depending on your editing style it may be
annoying if your editor doesn't support having the two files open
side-by-side.
But I did notice you have the use test-is line commented out in the
implementation; it seems a bit unfortunate to have to uncomment that to
run the tests and hope you remember to re-comment it before you commit.
> I'll add support for java.io.File and java.net.URL as an argument to
> template/deftemplate/snippet/defsnippet in complement of String and xml
> tree.
Sounds great; thanks.
-Phil
The last commit was during the transition to lazy-seq and test-is was
broken.
I'll fix that.
David Nolen a écrit :
> Considering the above, I'm left wondering if it's possible to further
> eliminate these redundancies and make templates more reusable. I'm not
> sure if this is what you had in mind for Enlive, but allowing
> templates to be created without references to files would make it
> possible to build very, very composable interfaces.
> Of course it's quite possible that you can already do this with Enlive
> and I'm just unclear about how to accomplish it.
I'm sorry for the lack of documentation.
The "source" of a template/snippet can either be:
- a string (a resource path resolved by the classloader)
- a File, URI or URL,
- a map (a "literal" xml/html tree).
A template is a function that returns a seq of strings. (It's the end of
the "pipeline".)
A snippet is a function that now (in the "new" enlive) returns a seq of
nodes. (I suppose I should add "seq of nodes" as a valid source type.)
Snippets are valid values on the right-hand side of rules. The only
difference between templates and snippets is that templates serialize
their return value.
I suppose you really want to break the computation in two and not write:
(deftemplate pageA-template path
[map]
[[:div (attr? :tiptree:replace)]] (fn [xml-node]
(find-the-widget-html-file-that-corresponds-to-a-certain-attr-and-return-as-result))
[[:div (attr? :tiptree:widget)]] (fn [xml]
(use-xml-attr-and-map-to-apply-and-return-a-snippet)))
or:
(deftemplate pageA-template path
[map]
[[:div (attr? :tiptree:replace)]]
(do->
(fn [xml-node]
(find-the-widget-html-file-that-corresponds-to-a-certain-attr-and-return-as-result))
(fn [xml] (use-xml-attr-and-map-to-apply-and-return-a-snippet))))
(well, it would work if do-> has been ported to the new enlive)
Keeping in mind the difference between a snippet and a template, you
will be able to do what you describe with the new enlive once I
implement 'attr? and add support for seq of nodes as a valid source type.
hth,
Christophe
I'll lift the need to quote predicates. I'm worried about
:tiptree:replace, are you using namespaces?
[:div '(attr? :foo :bar)] is equivalent to div *[foo][bar]
[[:div '(attr? :foo :bar)]] is equivalent to div[foo][bar]
With pleasure!
(def snipgets
(let [divs (select (html-resource "widget.html")
[[:div (attr? :tiptree:widget)]])]
(into {}
(for [div divs]
[(-> div :attrs :tiptree:widget)
(snippet div [root]
[widget]
[:div.value] (content (if widget
(:value widget)
"foo")))))))
Christophe Grand a écrit :
>>> <chris...@cgrand.net <mailto:christophe@
rzez...@gmail.com a écrit :
> Either I've missed something, or Enlive *appears* to have problems
> handling comment tags.
>
Indeed. I pushed a fix, please tell me whether it works for you now.
Thanks for the report.
Christophe
Maybe unrelated, but
=>(html-resource (java.io.StringReader. "<!-- o noes a comment --
><html><head><title>t</title></head><body><h1>h</h1></body></html>"))
({:type :comment, :data " o noes a comment "})
Not the result I would expect.