[racket] Using `dynamic-require` for optional user-supplied "template" code?
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!