[racket] Dynamic Templates?

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

Yes, I like dynamic-rerequire. Because it already has the file-watching
facility built in, I rely on it as a sort of sonar device to find out if a
source file has changed, like so:

(define (source-changed? source-path)

  (define port-for-catching-file-info (open-output-string))

  (parameterize ([current-directory source-dir]
                 [current-error-port port-for-catching-file-info])
    (dynamic-rerequire source-path))

  ((length (get-output-string port-for-catching-file-info)) . > . 0)))


On Mon, Oct 14, 2013 at 12:31 PM, Matthew Flatt <mflatt at cs.utah.edu> wrote:

> I think the advice that Robby gives is probably better, but...
>
> If you only need to pick up changes to a module (as opposed to running
> a module afresh for its side effects), and if you're using 5.90.x, you
> could try using `dynamic-rerequire` in a single namespace to reload
> modules.
>
> At Mon, 14 Oct 2013 13:22:21 -0500, Robby Findler wrote:
> > Did you try using namespace-attach-module to attach 'racket and
> > 'web-server/templates? Something like:
> >
> >   (define orig-ns (current-namespace))
> >   (parameterize ([current-namespace (make-base-empty-namespace)])
> >     (namespace-attach-module orig-ns 'racket)
> >     (namespace-attach-module orig-ns 'web-server/templates)
> >     (namespace-require 'racket)
> >     (eval `(begin
> >              (require web-server/templates)
> >              (require ,my-source-path-string)
> >              (include-template ,my-template-path-string))
> >           (current-namespace)))
> >
> > But 7 seconds seems remarkably long (I see a speed up of about 50% for
> that
> > function (with the my-source-path-string and my-template-path-string
> lines
> > commented out): from 250 to 170 msec).
> >
> > It may be that your .zo files or something are not up to date. So you may
> > want to stick a 'raco make' somewhere in there to avoid recompiling your
> > files every time and just recompile the ones that changed.
> >
> > Robby
> >
> >
> >
> >
> > On Mon, Oct 14, 2013 at 1:14 PM, Matthew Butterick
> > <mb.list.acct at gmail.com>wrote:
> >
> > > 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
> > >>
> > >
> > >
> > > ____________________
> > >   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/e02b3c4d/attachment-0001.html>

Posted on the users mailing list.