Beginner Compiled Splices Question

26 views
Skip to first unread message

Julian Birch

unread,
Feb 7, 2015, 7:47:43 AM2/7/15
to snap_fr...@googlegroups.com
Hi, I'm hoping someone can help me.  It's taken me a bit of time to figure this out. It works, but it works "wrong"

I've got a handler that returns a blog post from the database

getPost3 :: Handler App App (Maybe FullPost)
getPost3 = do
  liftIO (traceIO "GET POST 3")
  getPost 3 commentWindow

A "get title" function.

getTitle :: Maybe FullPost -> T.Text
getTitle = maybe "" (_title . _post)

A way to turn the two into a compiled splice

titleSplice :: (MonadReader App (Handler App App)) => C.Splice (Handler App App)
titleSplice = (C.pureSplice (C.textSplice getTitle)) (lift getPost3)

Finally, I load it up into the compiled splices

theBlogSplices :: SpliceConfig (Handler App App)
theBlogSplices = mempty & scCompiledSplices .~ do
  "XYZ" ## x

Then I render the following:

<h1><XYZ></XYZ></h1>
<h2><XYZ></XYZ></h2>

using cRender.

This produces the correct result on the screen, but logs the trace message twice, which means I'm running the database query twice. So, I don't really understand how I'm meant to be using compiled splices. If I want to store state between invocations, I'd need to actually put it into App, am I correct? Is there a better way of approaching this?

Thanks,

Julian.


MightyByte

unread,
Feb 7, 2015, 6:44:36 PM2/7/15
to snap_fr...@googlegroups.com
Splices are run every time they are encountered on the page. If you
want to avoid this, the usual pattern is to wrap every instance in an
enclosing splice that actually does the expensive stuff. So something
like this.

<withPost>
<h1><XYZ/></h1>
<h2><XYZ/></h2>
</withPost>

The withPost splice would do the database lookup and then call
"withSplices runChildren ..." with the resulting splices bound.
> --
>
> ---
> You received this message because you are subscribed to the Google Groups
> "Snap Framework" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to snap_framewor...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Julian Birch

unread,
Feb 8, 2015, 5:13:04 AM2/8/15
to snap_fr...@googlegroups.com
Ah, thankyou. A supplemental question: I've finally discovered "callTemplate", which appears to provide a mapping of templates to compiled splices. So, theoretically could I use withSplices instead of cRender?

A second question, on withSplices, when do the inner splices get "compiled"? On each invocation? If so, what's the advantage of C.textSplice over I.textSplice?

Thanks,

Julian.

MightyByte

unread,
Feb 8, 2015, 10:51:46 AM2/8/15
to snap_fr...@googlegroups.com
On Sun, Feb 8, 2015 at 5:13 AM, Julian Birch <julian...@gmail.com> wrote:
> Ah, thankyou. A supplemental question: I've finally discovered
> "callTemplate", which appears to provide a mapping of templates to compiled
> splices. So, theoretically could I use withSplices instead of cRender?

I'm not sure what you mean here. withSplices and cRender are
completely different.

> A second question, on withSplices, when do the inner splices get "compiled"?
> On each invocation? If so, what's the advantage of C.textSplice over
> I.textSplice?

Compiled splices are all compiled when the app is initialized. See
this blog post for an overview on the difference between compiled
templates and interpreted templates.

http://snapframework.com/blog/2012/12/9/heist-0.10-released

Julian Birch

unread,
Feb 20, 2015, 4:32:31 PM2/20/15
to snap_fr...@googlegroups.com
Okay, I've worked a on this a bit more. This is definitely "off-road" now. I've now got this:

showPost :: Handler App App ()
showPost = do
    fp <- glike etPost 3 commentWindow
    liftIO $ print fp
    maybe failure servePost fp
  where
        rootSplice = C.callTemplate "post"
        splice fp = C.withSplices rootSplice theBlogSplices2 (return fp)
        servePost p = maybe pass serve $ _
        serve (b, mime) = do
                modifyResponse $ setContentType mime
                writeBuilder =<< b

The idea I had was to actually resolve the data the same way you would with interpreted splices, outside of the template. With callTemplate and withSplices, I get a splice that would result in the correct HTML. I've also got a function that would serve it, so I'm trying to figure out if I can fill in _ (which has type Maybe (m Blaze.ByteString.Builder.Internal.Types.Builder, ByteString)).

It looks like interpret would be useful here, but that requires direct reference to Data.HeterogenousEnvironment, which is hidden.

I appreciate that this isn't really how it's intended to be used, but I quite like the model of "determine data, then render page".

Is there a way to make this work? Will it retain the performance advantages if it did?

Thanks,

Julian.

MightyByte

unread,
Feb 24, 2015, 12:05:54 AM2/24/15
to snap_fr...@googlegroups.com
If I'm understanding you correctly, no, there is no way to do that.
All of your top-level compiled splices MUST all be defined at load
time--i.e. when initHeist is called. There is a technique that allows
you to somewhat work around this. Create a splice that runs its
children with other splices bound. That is essentially what you are
doing there with the withSplices call. But you have to do that in a
splice that is bound up front in the scCompiledSplices field of
SpliceConfig. This means that your post splice will need the
knowledge of how to figure out which post is being rendered.

More philosophically speaking, compiled splices are fundamentally
incompatible with this "determine data, then render page" model.
Compiled splices should be thought of more like a fixed API that any
template can call on at any time. This makes debugging easier in some
ways because you don't run into the question "has this splice been
bound yet"? All splices are bound everywhere (except for splices
bound inside of another splice). They're available no matter which
handler you take.
Reply all
Reply to author
Forward
0 new messages