[racket] Dynamic Templates?

From: Matthew Butterick (mb.list.acct at gmail.com)
Date: Mon Oct 14 14:14:16 EDT 2013

At RacketCon I talked about my Pollen system for making web pages. Though I
don't use the Racket webserver to provide live generation of web pages, I
do use it to provide a kind of browser-assisted REPL for development. As I
edit the project files, I want to reload the URL in the browser and see the
changes (without restarting the webserver).

Moreover, since it's a development tool, it wants to be maximally dynamic.
For instance, I might be editing source files containing page content, or
the design templates (which might also contain Racket code), or supporting
Racket libraries (e.g., CSS generators).

Jay has suggested that this can be done with eval. And indeed it can.
Here's the quasicode I use:

  (parameterize ([current-namespace (make-base-empty-namespace)])
    (namespace-require 'racket)
    (eval `(begin
             (require web-server/templates)
             (require ,my-source-path-string)
             (include-template ,my-template-path-string))
          (current-namespace))))

This works great, with one wrinkle: it's the slowest operation in the whole
system, taking about 7-8 secs to render each page. This is not a grave
hardship, and certainly faster than restarting the web server. But I'd
still be curious if I'm overlooking a better approach.

In general, the render time is fairly consistent regardless of what's in
the page, which suggests to me that most of the expense comes from setting
up the fresh namespace. I did try making a namespace separately and reusing
it, but that didn't work (code wouldn't get reloaded). Is there a way to
"clean" a namespace that's cheaper than setting up a new one?

Another option I considered is starting up another instance of Racket in a
subprocess, but it would need to be made into a synchronous operation. Long
drive for a short day at the beach, etc.

It's also possible that I'm running up against the irreducible cost of
high-quality eval, in which case, I will accept it and move on.


Matthew Butterick




On Thu, Jun 6, 2013 at 7:36 PM, Greg Hendershott
<greghendershott at gmail.com>wrote:

> Great timing! It's been on my short list to make my static blog
> generator more flexible. From
> http://docs.racket-lang.org/web-server/templates.html I got the idea
> that it would be suitable only for static templates, but now I
> understand that's not the case. Today I made a topic branch using this
> approach that's working well, which I plan to merge after some testing
> and polishing.
>
> p.s. Maybe it's obvious, but because `include-template` uses
> `include-at/relative-to/reader`, it won't include a template if you
> give it a full path. Maybe there's a more-elegant way, but I found it
> worked to parameterize `current-directory` around the call to `eval`.
>
>
> On Wed, Jun 5, 2013 at 4:45 PM, Jay McCarthy <jay.mccarthy at gmail.com>
> wrote:
> > While it is supposed to be pithy, it is also serious.
> >
> > When I wrote it, I imagine that people would do something like this:
> >
> > #lang racket/base
> > (require web-server/templates)
> >
> > (define (template-content t x)
> >   (eval #`(let ([x #,x]) (include-template #,t))))
> >
> > (template-content "t.txt" 5)
> > (template-content "t.txt" 6)
> >
> > and trust that the template wouldn't include @(system "rm -fr /"). I
> > think that a simple eval works just fine for when you, the programmer,
> > want to change the content dynamic (although I'd say it is better to
> > use the web-server language so you can restart with the same
> > continuations, etc.)
> >
> > Your solution is great for a template that a user provides, although
> > it has the hole that the template could call @include-template and
> > maybe find itself and go into a infinite loop, so it's not totally
> > "secure" unless you use a sandbox.
> >
> > Jay
> >
> > On Wed, Jun 5, 2013 at 1:53 PM, Joe Gibbs Politz <joe at cs.brown.edu>
> wrote:
> >> I'm writing a web server in a #lang that doesn't support macros or
> >> syntactic abstraction.  I would like to be able to use some sort of
> >> templating dynamically with bindings given by values, e.g.
> >>
> >> template.render("page-with-hole-for-username.html", { username: "Joe" })
> >>
> >> The FAQ (
> http://docs.racket-lang.org/web-server/faq.html#(part._.How_do_.I_use_templates__dynamically__)
> )
> >> in the documentation for templates ends with this pithy
> >> recommendation:
> >>
> >> "If you insist on dynamicism, there is always eval."
> >>
> >> I assume this isn't intended seriously, and instead to discourage what
> >> I'm asking about, but I'm unfortunately not seeing another way to
> >> write my program.  The code at the end of this message seems like the
> >> best I can do while being relatively safe, but also like a dubious
> >> inclusion on a web server given the eval.
> >>
> >> Is this the best I can hope for?  Is there another collection I should
> >> be looking into?  Any other recommendations?
> >>
> >> Thanks!
> >> Joe P.
> >>
> >>
> >> #lang racket/base
> >>
> >> (require
> >>   web-server/templates)
> >>
> >> (define (render-template filename dict)
> >>   (define namespace-for-template (make-empty-namespace))
> >>   (namespace-attach-module (current-namespace) 'web-server/templates
> >> namespace-for-template)
> >>   (hash-map dict
> >>     (lambda (key value)
> >>       (define name-of-identifier (string->symbol key))
> >>       (namespace-set-variable-value!
> >>         name-of-identifier
> >>         value
> >>         #f
> >>         namespace-for-template)))
> >>   (parameterize [(current-namespace namespace-for-template)]
> >>     (namespace-require 'web-server/templates))
> >>   (define to-eval #`(include-template #,(datum->syntax
> >> #'render-template filename)))
> >>   (eval to-eval namespace-for-template))
> >> ____________________
> >>   Racket Users list:
> >>   http://lists.racket-lang.org/users
> >
> >
> >
> > --
> > Jay McCarthy <jay at cs.byu.edu>
> > Assistant Professor / Brigham Young University
> > http://faculty.cs.byu.edu/~jay
> >
> > "The glory of God is Intelligence" - D&C 93
> > ____________________
> >   Racket Users list:
> >   http://lists.racket-lang.org/users
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20131014/0aeddc47/attachment.html>

Posted on the users mailing list.