[racket] Using `dynamic-require` for optional user-supplied "template" code?
p.s.
> Also: Am I correct that the user-supplied function gets JIT-ed, and
> there's no big performance hit from this? (I think the answer is no,
That question had mixed polarities. :) I should have written, I think
the answer is No there's no big performance hit, and Yes it gets
JIT-ed.
On Thu, May 23, 2013 at 2:22 PM, Greg Hendershott
<greghendershott at gmail.com> wrote:
> I've never had a reason to use `dynamic-require`, but now I think I do.
>
> For my Frog static blog generator, I'd like to provide a light
> "template" feature, where the user can override the default layout of
> the main container <div> in the page.
>
> I have an existing function that returns an xexpr for this. So my idea
> is to let the user optionally supply a `template.rkt` file that
> `provide`s a function named (say) `container` that returns (listof
> xexpr?).
>
> So I think what I want to do is something like this:
>
> (define (container-proc)
> (define p (build-path (src-path) "template.rkt"))
> (cond [(file-exists? p)
> (dynamic-require p
> 'container
> (thunk default-container))]
> [else default-container]))
>
> The built-in `default-container` looks something like this:
>
> (define (default-container bootstrap-row-class ;"row" or "row-fluid"
> bodies ;listof xexpr?: main content
> tocs ;listof xexpr?: TOC
> tags/feeds ;listof xexpr?: tag/feed links
> follow) ;listof xexpr?: Twitter, etc.
> `((div ([class ,bootstrap-row-class])
> ;; Left column
> (div ([id "left-sidebar"]
> [class "span2 bs-docs-sidebar"])
> , at tocs
> (p nbsp))
> ;; Main column
> (div ([id "content"]
> [class "span8"])
> , at bodies)
> ;; Right column
> (div ([id "right-sidebar"]
> [class "span2"])
> ,@(tags/feeds)
> ,@(follow)))))
>
> The user-supplied template.rkt would provide some variation on it. For
> example they don't want any left column with a TOC, so they supply
> this:
>
> ;; template.rkt
> #lang racket
> (provide container)
> (define (container bootstrap-row-class ;"row" or "row-fluid"
> bodies ;listof xexpr?: main content
> tocs ;listof xexpr?: TOC
> tags/feeds ;listof xexpr?: tag/feed links
> follow) ;listof xexpr?: Twitter, etc.
> `((div ([class ,bootstrap-row-class])
> ;; Don't want any left column; ignore `tocs`
> ;; Main column
> (div ([id "content"]
> [class "span9"]) ;wider
> , at bodies)
> ;; Right column: Tags/feeds/follow
> (div ([id "right-sidebar"]
> [class "span3"]) ;wider
> ,@(tags/feeds)
> ,@(follow)))))
>
> In a quick experiment this seems to work fine. I could also go further
> and make this a tiny s-exp #lang, to make the end user experience even
> simpler. But meanwhile, and before that, I just want to make sure that
> `dynamic-require` is a reasonable way to do this? Does anyone
> disagree?
>
> Also: Am I correct that the user-supplied function gets JIT-ed, and
> there's no big performance hit from this? (I think the answer is no,
> because from reviewing Racket's own source, I got the impression that
> all modules are built on dynamic-require, but I might have
> understood.)
>
> Thank you in advance for any advice or suggestions!