We recently implemented a small, mostly static content site (http:// www.goldenfrog.com/) using Lift. Thought I'd offer up a postmortem on what worked well and what didn't, in case it's useful to anyone considering Lift. We're a small team with essentially no prior Scala experience, so take this with a grain of salt & feel free to offer suggestions.
The good: We went with Lift mainly because we wanted: 1. clean separation of code from html; 2. easy localization. With a few minor caveats (see below), we got that. An html designer was able to work on templates & commit directly to our repo without touching any scala code. When he broke things, it was usually immediately obvious because the xml stopped parsing correctly and the page would no longer render (even though it usually took a programmer to interpret the stacktrace & fix the xml). Regarding localization, the use of separate templates (instead of e.g. java properties file references for every single sentence) was a big help. Because everything in templates is well-formed xml, I was able to write less than 100 lines of code to automatically extract translatable text and generate a translated version of all the templates. This is done during build, so essentially duplicate html doesn't get into our repo, until some point when translated templates need to actually be structurally different. The bad: Dreamweaver doesn't deal with head merge, it will automatically move the head tags outside of the lift:surround. Not really a lift issue. PCDataXmlParser converts entity references to actual utf-8 characters. This means templates with entity references won't round- trip correctly through view source, nor through localization properties files / gettext files. Jetty seems to do questionable things with filehandles, opening multiple copies of the same template file and keeping them open too long. Setting ulimit "fixes" the issue of "Too many open files" exceptions & failed requests, but it still seems like at most templates should need 1 access to check mtime. Not really a lift issue, and not a deal breaker for us to switch to a different servlet container, but I am curious to see what the eventual solution for comet performance is. Lift doesn't seem to have any central place to handle URL issues related to servlet context paths and domains / subdomains. We're currently running lift as a root servlet to avoid forcing context path to be visible in urls; this is fine for us but I can see it being useful for lift to know that the frontend proxy is taking care of mapping foo.com/ to the context /foo-lift-app/, instead of always adding the context path to urls. Similarly, we're running multiple subdomains from a single lift instance, because it's all basically common code and templates. The way we ended up making this work was to have 1 folder per subdomain, and subclass Link so that it knows how to createLink correctly based on headers the frontend is sending; e.g. the link for http://foo.bar.com/baz is put into the sitemap as / foo.bar/baz and will be rendered as /baz if the request is already in the foo subdomain, or fully qualified http://foo.bar.com/baz otherwise. In one sense, it was nice that we were even able to do this without modifying Lift. However, we already ran into the issue of default form redirects wanting to go to /foo.bar/baz, not just / baz, and there doesnt seem to be a clean place to handle that. The public face of 1.0 vs snapshot could be handled a little better, in terms of website / liftbook / documentation. If the intention is for all users to track snapshot for now, that should be made explicit and the docs updated. The ugly: There are aspects of the API that are frankly quite difficult for developers to take seriously - "object User extends User with MetaMegaProtoUser[User]" is a common whipping boy around the office, and leads to jokes about giant robots. Similarly, there are a number of places that seem to force syntactic overhead instead of catering to concise default usage, eg the first time I saw the liftbook example of case RewriteRequest( ParsePath(List("account",acctName),_,_,_),_,_) => RewriteResponse("viewAcct" :: Nil, Map("name" -> acctName)) I thought how often are you actually going to fill in those underscores? One of the things we did as part of our link subclass was an implicit conversion to allow writing links in the sitemap as "foo.bar/baz/quux/*" rather than ("foo.bar" :: "baz" :: "quux" :: Nil) -> true. I don't know if that's bad style or not, but it certainly makes the sitemap more scannable as a list of urls. I know conciseness isn't this framework's top priority, but it seems like some judicious use of implicits would help. Thanks to the devs for writing lift, and like I said, please offer any suggestions or counterpoints. -- You received this message because you are subscribed to the Google Groups "Lift" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
