Utterly sensible HTML templating in Common Lisp
trapm.com
trapm.com
https://github.com/cgrand/enlive
This has some really nice properties:
1. The only thing your devs and designers need to agree on is some basic semantic markup and tag IDs. Then they can make a full comp (complete with sample data) for their purposes. You simply rewrite that data.
2. The approach is very declarative, which makes it a lot easier to reason about what is actually being shown and what is actually changing.
3. It's amenable to all the same functional properties of the sexp->xml stuff that you see here.
Enlive's approach is a nearly 100% win for doing site templating over these sorts of sexp->xml libraries.
For example, the function 'tag-name' really needs to HTML-escape the input parameter content. The simple example (tag-name "div" "Hello world") breaks down when "Hello World" is replaced by a variable reference.
Fortunately this is an relatively easy problem to solve. A better definition for tag-name is something like:
(defun tag-name (name content) (html (format nil "<~A>~A</~A>" name (to-html content) name))
The function to-html will return the HTML-escaped version of content. The function html returns an object/string that marked as already being HTML-escaped. They work together so that (to-html (html x)) just returns x.
A similar type system can be set up to handle url escaping rules.
If the templating system doesn't keep track of what is escaped and what isn't then that burden is left to the programmer with all of the associated risks of producing malformed output and creating opportunities for injection attacks.
Edit: To elaborate, as some have asked: I'm speaking in terms of HTML templating. Having the software render your HTML form for you, or rending out a table full of data is fine. But by and large, the HTML should remain HTML. Granted, in this case, your not writing HTML, your writing code to generate a form. Not sitting there adding HTML attributes, which is handled for you.
As to why I disagree with this approach? At it's core, portability. You can now no longer take an HTML file and use it. You have to rewrite everything into your strange, personal, customized, non-standard language and hope everything comes out alright. Their is also the learning curve in bringing anyone else on board. You also suddenly lack the ability to use any of the tools associated with HTML. Abstraction for the sack of abstraction is silly.
If you have multiple functions for every single tag, your essentially taking HTML and just making the syntax your own. This "(ul '(id "navbar" class "horizontal_list")" is no better than HTML, but it isn't special or unique. It's cumbersome. Your also limited by the framework (which you hope will add new tags as they appear, and support the customization HTML offers).
I'm not against HTML generating code. I'm against rewriting HTML as code. Maybe someone can explain how
(ul '(id "navbar" class "horizontal_list")...
is better than
<ul id="navbar" class="horizontal_list">...
Edit 2: Also, it should be fairly obviously, but all of this is of course just my opinion. =)
This thing generates plain, standards-compliant html. Just like the rails app I'm porting over to it. If you can't easily take html and port it in/out of whatever tool you're working in, you're doing it wrong. Even with all the abstractions, partials, specialized rails functions, I was able to port the majority of the view layer over in a lazy Sunday afternoon.
Maybe someone can explain how (ul '(id "navbar" class "horizontal_list")... is better than <ul id="navbar" class="horizontal_list">...
It's not any better. Well, there is the part where if it's invalid, there'll be an error instead of some mangled html that browser tries to fix on its own, and you don't have to remember to close the tag, but those are really somewhat minor.
The principle of lowest friction is that this templating system must, at the very minimum, match, if not exceed the ease of HTML. So the above is not very different, but when it grows larger, you can abstract away the repeated calls, make your own tags (as I show with the image and link-to "tags") that make the html more readable and vastly more DRY.
And again, it all goes out to nice html. Take that and import it into whatever framework you're headed off to.
> The principle of lowest friction is that this templating system must, at the very minimum, match, if not exceed the ease of HTML.
Realizing also you mentioned as it grows larger, abstraction occurs, this is where I don't see the value. For anyone to use this templating system, they need to know HTML and then they need to know this templating system (which amounts to learning a programming language). Mistakes in the templating system will result in HTML errors. Maybe I've just had too many bad experience with systems like this where fixing the HTML would have been easy, except I had to go through a templating system that abstracted away critical parts.
I'll assume I'm just missing something, or I've had this problem your solving before, and solved it in some other way. Like I said, I don't see how this method benefits anything (and I've done my fair share of work on the web), but I'll chalk that up to a failing on my part to grasp some concept. =)
It is more consistent. All of my code is now (in my case) Scheme. This makes it easier to change things as you don't have to go between two different languages. And it is simpler.
My favorite reason is the fact that I can use S-Exps. I no longer have to type the ending tag. I remove the possibility for spelling it wrong, or forgetting the slash. And it takes less space and is easier to get the placement correct, as you just utilize paren matching.
This could be solved by some editor placing arrows showing which tags are parents to your current position, but I don't think that's available (at least not in emacs, sadly), and paren highlighting is.
Also, never missing a closed tag, mispellings, abstractions, etc. All very nice. When loading a broken page in chrome, it's crazy how much the html will be transformed, so that it's hard to tell why the page renders a certain way. A bit easier with this approach.
HTML is great, but there are a few areas where it's tough to do things, like abstracting repetitive elements. There are plenty of ways around this using other languages, and perhaps the best solution for someone like you would be writing html snippets and using javascript to replicate them, thus writing purely in html?
The ideas there are definitely very powerful... and just plain sensible. I'd like to find a way to work this in, but it will take time. Do you see any fundamental reason a lisp equivalent of this technique couldn't be reproduced, using something like parenscript?
I've myself been using a declarative style HTML output engine that I wrote in Python. It's simple, easier, outputs correct HTML and it merges well with my workflow. I don't have to write HTML, nor JavaScript to programmatically generate what I need. It may sound naive, but I find myself comfortable indenting code in Python than write matching HTML tags. If there are errors, the Python code wouldn't run, but HTML code would still render, though it wouldn't be what I want.
Relevant: http://code.google.com/p/zen-coding/
Enlive has a lot in common with Vekz' suggestion elsewhere in the comments, and I feel like there's some unifying theme underlying all of this, that should make it easy for newcomers, but allow experienced developers to transform the DOM programmatically.
I'm searching for it now, and I'm sure there'll be a lot of false-starts and dead-ends, but the only sure way not to get there is to not try.
(string-downcase (string thing)) should be written (string-downcase thing)
The fdefinition trick is not a good one. Define the functions at compile-time, not at runtime.
I learnt this trick only after working on CL for a couple of years!
And thanks for the (string-downcase thing) trick, I'll update that right now :)
I don't get all this html templating. With ajax and websockets on the way, why are we still generating html in the application? This all seems like a solution to a problem we should be leaving behind.
Then you have all the problems of a purely client-side application (dealing with deep-linking, bookmarks, back buttons, users with exotic browsers, search engines, etc). That stuff can all be dealt with, but all dramatically increases the complexity. Your "Hello World" quickly becomes complex and unwieldy.
I personally think the right way is probably a templating engine that can work either client- or server-side so you can generate whatever you need wherever you are.
You said it - websockets aren't here yet.
Plus I think there are still plenty of good applications for static HTML. For starters, even full AJAX apps should work without JS enabled if you care about accessibility.
This should be just about possible using Node.js these days: You can run your entire client-side templating system—and even the <canvas>—on the server, and then server static HTML and PNGs to the browser. Unfortunately, quite a bit of elbow grease is still required.
The GP seemed to be implying that we should only generate our HTML programatically on the client. Thinking about it more, I don't agree with this. I don't write webapps these days, but in our platform we do a lot of code generation. We use a programmatic method to do this because we have to manipulate the intermediate representation a lot - add or remove fields added by earlier phases in the generation etc. This is much more flexible but it's much harder to maintain, and looking at the code generation code it's very hard to see what the end result will be. If we were simply generating code in a single pass I'd definitely use templating, it's just much simpler, easier to develop, and easier to see what the end result will be.
Add in the need with HTML to work with designers and I think templating will be with us for some time to come.
That said, there are a ton of different approaches - At the oak.js meetup, I saw a sammy.js app embedded into couchdb. Definitely not my style, but a cool idea anyway.
I'm very eager to get this into several html5 apps that are currently node.js or rails backends and for particular reasons would be well suited for a lisp backend. The goal is to tie in very closely with the html5 landscape, and drop a lot of the legacy stuff that other frameworks care about.
What are the chances that such global names will collide with something else?
http://gigamonkeys.com/book/programming-in-the-large-package...
HAML's great, and I disagree with the other "I hate haml" post on the frontpage today, but this approach actually alleviates the issues pointed out with haml's whitespace-is-meaningful approach.
Anyway, it's all in good fun.