templating: Remove unused lazy code

This commit is contained in:
Tom 2023-07-13 22:51:20 +02:00
parent 1c68893a02
commit 46b16e5700
4 changed files with 30 additions and 110 deletions

View file

@ -3,7 +3,6 @@ package templating
import ( import (
"context" "context"
_ "embed" _ "embed"
"encoding/json"
"fmt" "fmt"
"html/template" "html/template"
"net/http" "net/http"
@ -14,7 +13,6 @@ import (
"github.com/gorilla/csrf" "github.com/gorilla/csrf"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/tkw1536/pkglib/httpx" "github.com/tkw1536/pkglib/httpx"
"github.com/tkw1536/pkglib/timex"
) )
//go:embed "src/base.html" //go:embed "src/base.html"
@ -32,17 +30,10 @@ func (tpl *Template[C]) Template() *template.Template {
return baseTemplate return baseTemplate
} }
// LazyContext is like the lazy context
func (tpl *Template[C]) LazyContext(r *http.Request, f func() (C, error), funcs ...FlagFunc) (ctx *tContext[C]) {
ctx = tpl.context(r, funcs...)
ctx.startLazy(f)
return ctx
}
// Context generates the context to pass to an instance of the template returned by Template. // Context generates the context to pass to an instance of the template returned by Template.
func (tpl *Template[C]) Context(r *http.Request, c C, funcs ...FlagFunc) (ctx *tContext[C]) { func (tpl *Template[C]) Context(r *http.Request, c C, funcs ...FlagFunc) (ctx *tContext[C]) {
ctx = tpl.context(r, funcs...) ctx = tpl.context(r, funcs...)
ctx.start(c, nil) // setup the request ctx.cMain = c
return ctx return ctx
} }
@ -93,27 +84,22 @@ func FormTemplateContext(tw *Template[FormContext]) func(ctx httpx.FormContext,
} }
} }
// Hander returns a function that returns a context for the given template // HandlerWithFlags returns a function that, given a request, generates context and error to pass to the generated template.
// The worker implements the actual buisness logic, it takes a request, and returns the content for the main template, and any error.
// See also HandlerWithFlags.
func (tw *Template[C]) Handler(f func(r *http.Request) (C, error)) func(r *http.Request) (any, error) { func (tw *Template[C]) Handler(f func(r *http.Request) (C, error)) func(r *http.Request) (any, error) {
// TODO: Should this one be removed?
return tw.HandlerWithFlags(func(r *http.Request) (C, []FlagFunc, error) { return tw.HandlerWithFlags(func(r *http.Request) (C, []FlagFunc, error) {
c, err := f(r) c, err := f(r)
return c, nil, err return c, nil, err
}) })
} }
// HTMLHandler returns a new HTMLHandler for this request // HandlerWithFlags returns a function that, given a request, generates context and error to pass to the generated template.
func (tw *Template[C]) HTMLHandler(f func(r *http.Request) (C, error)) httpx.HTMLHandler[any] { // The worker implements the actual buisness logic, it takes a request, and returns the content for the main template, flag functions and error.
return httpx.HTMLHandler[any]{ // See also Handler.
Handler: tw.Handler(f), func (tw *Template[C]) HandlerWithFlags(worker func(r *http.Request) (C, []FlagFunc, error)) func(r *http.Request) (any, error) {
Template: tw.Template(),
}
}
// HandlerWithFlags works like handler, but additionally receive funcs to generate flags
func (tw *Template[C]) HandlerWithFlags(f func(r *http.Request) (C, []FlagFunc, error)) func(r *http.Request) (any, error) {
return func(r *http.Request) (any, error) { return func(r *http.Request) (any, error) {
c, funcs, err := f(r) c, funcs, err := worker(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -122,9 +108,20 @@ func (tw *Template[C]) HandlerWithFlags(f func(r *http.Request) (C, []FlagFunc,
} }
} }
func (tw *Template[C]) HTMLHandlerWithFlags(f func(r *http.Request) (C, []FlagFunc, error)) httpx.HTMLHandler[any] { // HTMLHandler creates a new httpx.HTMLHandler that calls tw.Handler(worker) and tw.Template.
// See also Handler.
func (tw *Template[C]) HTMLHandler(worker func(r *http.Request) (C, error)) httpx.HTMLHandler[any] {
return httpx.HTMLHandler[any]{ return httpx.HTMLHandler[any]{
Handler: tw.HandlerWithFlags(f), Handler: tw.Handler(worker),
Template: tw.Template(),
}
}
// HTMLHandlerWithFlags creates a new httpx.HTMLHandler that calls tw.HandlerWithFlags(worker) and tw.Template.
// See also HandlerWithFlags.
func (tw *Template[C]) HTMLHandlerWithFlags(worker func(r *http.Request) (C, []FlagFunc, error)) httpx.HTMLHandler[any] {
return httpx.HTMLHandler[any]{
Handler: tw.HandlerWithFlags(worker),
Template: tw.Template(), Template: tw.Template(),
} }
} }
@ -140,74 +137,16 @@ type tContext[C any] struct {
ctx context.Context // underlying context for render ctx context.Context // underlying context for render
// the main template and context // the main template and context
eMain chan error // are we done? tMain *template.Template
cWaiting bool cMain C
tMain *template.Template
cMain C
// the footer template and context // the footer template and context
tFooter *template.Template tFooter *template.Template
cFooter RuntimeFlags cFooter RuntimeFlags
} }
func (ctx *tContext[C]) start(c C, err error) {
ctx.cMain = c
ctx.eMain = make(chan error, 1)
ctx.eMain <- err
}
func (ctx *tContext[C]) startLazy(f func() (C, error)) {
ctx.eMain = make(chan error, 1)
go func() {
defer close(ctx.eMain)
// compute the result, storing the error
var err error
ctx.cMain, err = f()
ctx.eMain <- err
}()
}
const mainDelay = time.Second
// Main renders the main template. // Main renders the main template.
func (ctx *tContext[C]) Main() (template.HTML, error) { func (ctx *tContext[C]) Main() (template.HTML, error) {
timer := timex.NewTimer()
defer timex.ReleaseTimer(timer)
timer.Reset(mainDelay)
select {
case err := <-ctx.eMain:
// we received the result within the given time
// so we can render it immediatly
ctx.cWaiting = false
return ctx.doMain(err)
case <-timer.C:
// the template is taking longer than expected.
// we should display a spinner, and do something later
ctx.cWaiting = true
return timeWait, nil
}
}
// Footer renders the footer template
func (ctx *tContext[C]) Footer() (template.HTML, error) {
return ctx.renderSafe("footer", ctx.tFooter, ctx.cFooter)
}
const (
timeWait = "Loading"
errUnknown = "An unknown error occured, see the server log for details. "
)
func (ctx *tContext[C]) doMain(err error) (template.HTML, error) {
if err != nil {
zerolog.Ctx(ctx.ctx).Err(err).Msg("error lazy loading template")
return errUnknown, nil
}
// if the context has a runtime flags embed, then set the field properly // if the context has a runtime flags embed, then set the field properly
if ctx.updateEmbedded { if ctx.updateEmbedded {
reflect.ValueOf(&ctx.cMain).Elem(). reflect.ValueOf(&ctx.cMain).Elem().
@ -218,34 +157,16 @@ func (ctx *tContext[C]) doMain(err error) (template.HTML, error) {
return ctx.renderSafe("main", ctx.tMain, ctx.cMain) return ctx.renderSafe("main", ctx.tMain, ctx.cMain)
} }
func (ctx *tContext[C]) AfterBody() (template.HTML, error) { // Footer renders the footer template
// everything was done already func (ctx *tContext[C]) Footer() (template.HTML, error) {
if !ctx.cWaiting { return ctx.renderSafe("footer", ctx.tFooter, ctx.cFooter)
return "", nil
}
// wait for the result to appear
res, err := ctx.doMain(<-ctx.eMain)
if err != nil {
return "", err
}
str, err := json.Marshal(string(res))
if err != nil {
return "", err
}
fix := "<script>document.getElementById('main').innerHTML=" + string(str) + "</script>"
// hook that is called after the body is complete
return template.HTML(fix), nil
} }
const renderSafeError = "Error displaying page. See server log for details. " const renderSafeError = "Error displaying page. See server log for details. "
func (ctx *tContext[C]) renderSafe(name string, t *template.Template, c any) (template.HTML, error) { func (ctx *tContext[C]) renderSafe(name string, t *template.Template, c any) (template.HTML, error) {
// already done // already done with context => return
if err := ctx.ctx.Err(); err != nil { if err := ctx.ctx.Err(); err != nil {
return "", err return "", err
} }

View file

@ -40,7 +40,7 @@ type RuntimeFlags struct {
CSRF template.HTML // csrf data (if any) CSRF template.HTML // csrf data (if any)
} }
var runtimeFlagsName = reflectx.TypeOf[RuntimeFlags]().Name() var runtimeFlagsName = reflectx.MakeType[RuntimeFlags]().Name()
// Clone clones this flags // Clone clones this flags
func (flags Flags) Clone() Flags { func (flags Flags) Clone() Flags {

View file

@ -21,7 +21,7 @@ type Parsed[C any] struct {
// If base is not nil, every template associated with the base template is copied into the given template. // If base is not nil, every template associated with the base template is copied into the given template.
// Functions will be applied on creation time to represent the context for the given template. // Functions will be applied on creation time to represent the context for the given template.
func Parse[C any](name string, source []byte, base *template.Template, funcs ...FlagFunc) Parsed[C] { func Parse[C any](name string, source []byte, base *template.Template, funcs ...FlagFunc) Parsed[C] {
tp := reflectx.TypeOf[C]() tp := reflectx.MakeType[C]()
// determine if we have an embedded field in the struct // determine if we have an embedded field in the struct
var hasEmbed bool var hasEmbed bool

View file

@ -49,7 +49,6 @@
{{ .Footer }} {{ .Footer }}
</footer> </footer>
{{ .AfterBody }}
{{ .Runtime.Flags.Assets.Scripts }} {{ .Runtime.Flags.Assets.Scripts }}
</body> </body>