Lift Web Framework

The following is David Pollak's take on web framework view technology:

The Lift design is born out of my experience (both good and bad) with a variety of technologies including Rails.

I think the best paper on the subject is Terrence Parr's work on StringTemplate.

My first design goal with Lift was to make sure that no programming logic and no programming symbols make it into the static display templates.

ERB and JSP and ASP all have the fatal flaw of allowing code in the view. This is bad for a bunch of reasons. First, it makes editing the templates difficult with HTML layout tools unless those tools are familiar with the syntax being used. Second, there are "foreign" symbols in the layout, which tends to be less than optimal for the HTML designers. (On the Rails side of things, every Rails team I've seen has a Ruby coder that also does the design. While this is the norm in the Rails community, it is the exception when team sizes are more than 2 or 3.) Third, every single Rails, JSP, and ASP project I've ever seen (and I've been seeing them for a very long time) has some non-trivial amount of business logic creep into the display. Fourth, Scala has a very nice type system and when the type system is used correctly, the compiler finds a lot of program errors, but when the code is buried in templates, one has a much more difficult time using the powerful Scala compiler tools.

So, the static templates in Lift are strictly display only. They can be manipulated with standard design tools (e.g., Dreamweaver). They can never contain program logic.

Some asides.

First, I rejected using StringTemplate (or something like it) because it introduced some programming into the templating mechanism and it would have taken a lot of work to make it XMLTemplate (and all of Lift's templating is XHTML and makes use of Scala's excellent support of XML.)

Second, I've been referring to static templates. Lift has a little known and pretty much undocumented feature that supports template generation from Scala code. One of these days, I'll document the feature and put up some examples.

Third, Rails' "controller first" dispatch mechanism makes the assumption that there is only one piece of "logic" on the page and the rest is decoration. My experience doing web work is just the opposite. There are typically 3 or more of pieces of logic on a page (dynamic menu bars, search boxes, shopping cart, real-time chat, etc.) and having to choose which piece of logic make the "controller" is less than optimal.

So, the quintessential use of Lift's templates are as follows:

<html>
...
<lift:show.myForm form="post">

<tr><td>Name</td><td><f:name><input type="text"/></f:name></td></tr>
<tr><td>Birthyear</td><td><f:year><select><option>2007</option><option>2006</option></select></f:year></td></tr>

<tr><td>&nbsp;</td><td><input type="submit" value="Add"/></td></tr>
</lift:show.myForm>
...

</html>

So we've got a Lift snippet invocation with the valid HTML form and some additional tags. So far (with the proper namespace declarations) this page is valid XHTML. This page can be viewed in a browser or opened and edited in Dreamweaver.

In Lift, the snippet is the equivalent of a Rails controller: it is the instantiation of a class and invocation of a method on the class. Because you can have multiple snippets on a page, you can call out multiple logic streams on a given page and there's no need to choose the "primary" logic stream.

The 'form="post"' attribute is a shortcut. It automatically wraps the enclosed XHTML in a <form method='post' target={current page... it's a post-back}>...</form> tag.

The <f:.../> tags are bind points for the business logic. They allow your snippet to easily replace the tag and its children with what is supposed to be displayed. This mechanism is a little heavier than Wicket's mechanism for binding display elements to logic and I've got a to-do to make Lift's mechanism work like Wickets as well... but that's a digression.

So, your Lift code will look like:

class Show {
   def myForm(xhtml: NodeSeq): NodeSeq = {
     var name = ""
     def handleYear(year: String) {
       ... the form's been submitted... do something
     }
     Helpers.bind("f", xhtml, "name" -> SHtml.text(name, name = _),
                   "year" -> SHtml.select((1900 to 2007).toList.reverse.map(v => (v.toString, v.toString)), 
                                       Empty, handleYear _))
   }
}

Note that no display code has crept into the snippet. You've simply bound the HTML created by text() and select() to the <f:name/> and <f:year/> tags in the incoming XHTML.

Also, you've bound two functions (the anonymous function name = _ and handleYear) to the HTML form elements. When the form is POSTed, these functions (which are bound to local variables) will be statefully invoked.

If you are displaying a table rather than a form, then the same binding logic still works. For example:

<table>

<lift:snippet type="show:users">
<tr><td><f:first_name>David</f:first_name></ 
td><td><f:last_name>Pollak</f:last_name></td></tr>
</lift:snippet>

</table>
class Show {
   def users(xhtml: NodeSeq): NodeSeq = Users.findAll.flatMap(user => bind("f", 
             xhtml, "first_name" --> user.firstName, "last_name" --> user.nameName))
}

If you take the time to clearly define the bind points, then you can have no display code at all in your snippets.

Can display logic slip into a snippet? Yes, and as you've seen and pointed out, the examples are less than optimal in this regard.

Has display logic ever crept into a method called from an ERB template? Yes, and very often it's a source of a potential Cross Site Scripting vulnerability.

Has business logic ever crept into an ERB template? Yes.

In Lift, display can creep into a snippet, but business logic cannot creep into a the static display template. Yes, your designers will still have to police putting display logic in the snippet code, but the coders will not have to police business logic in the templates.


Lift is Copyright 2007-2013 WorldWide Conferencing, LLC. Distributed under an Apache 2.0 License.
Lift version 2.5 built on Sat Jun 01 04:22:10 UTC 2013.
Stats: Total Memory: 800,849,920 Free Memory: 113,032,496 Open Sessions: 461
Updated At: Sat Oct 25 12:53:10 UTC 2014 Started At: Wed Jun 11 22:22:17 UTC 2014