Skip to content

Commit

Permalink
Adding context.Context to all interface methods
Browse files Browse the repository at this point in the history
  • Loading branch information
stanistan committed Nov 30, 2023
1 parent 3b46e0c commit 81d7c2d
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 55 deletions.
13 changes: 8 additions & 5 deletions error_renderable.go
@@ -1,6 +1,9 @@
package veun

import "html/template"
import (
"context"
"html/template"
)

type ErrorRenderable interface {
// ErrorRenderable can return bubble the error
Expand All @@ -11,10 +14,10 @@ type ErrorRenderable interface {
// which will ignore the error entirely.
//
// Otherwise we will attempt to render next one.
ErrorRenderable(err error) (AsRenderable, error)
ErrorRenderable(ctx context.Context, err error) (AsRenderable, error)
}

func handleRenderError(err error, with any) (template.HTML, error) {
func handleRenderError(ctx context.Context, err error, with any) (template.HTML, error) {
var empty template.HTML

if with == nil {
Expand All @@ -26,7 +29,7 @@ func handleRenderError(err error, with any) (template.HTML, error) {
return empty, err
}

r, err := errRenderable.ErrorRenderable(err)
r, err := errRenderable.ErrorRenderable(ctx, err)
if err != nil {
return empty, err
}
Expand All @@ -35,5 +38,5 @@ func handleRenderError(err error, with any) (template.HTML, error) {
return empty, nil
}

return Render(r)
return Render(ctx, r)
}
5 changes: 3 additions & 2 deletions render_container_as_view_test.go
@@ -1,6 +1,7 @@
package veun_test

import (
"context"
"html/template"
"testing"

Expand All @@ -14,15 +15,15 @@ type ContainerView2 struct {
Body AsRenderable
}

func (v ContainerView2) Renderable() (Renderable, error) {
func (v ContainerView2) Renderable(ctx context.Context) (Renderable, error) {
return View{
Tpl: containerViewTpl,
Slots: Slots{"heading": v.Heading, "body": v.Body},
}, nil
}

func TestRenderContainerAsView(t *testing.T) {
html, err := Render(ContainerView2{
html, err := Render(context.Background(), ContainerView2{
Heading: ChildView1{},
Body: ChildView2{},
})
Expand Down
17 changes: 9 additions & 8 deletions render_container_error_test.go
@@ -1,6 +1,7 @@
package veun_test

import (
"context"
"errors"
"fmt"
"html/template"
Expand All @@ -15,7 +16,7 @@ type FailingView struct {
Err error
}

func (v FailingView) Renderable() (Renderable, error) {
func (v FailingView) Renderable(_ context.Context) (Renderable, error) {
return nil, fmt.Errorf("FailingView.Renderable(): %w", v.Err)
}

Expand All @@ -24,11 +25,11 @@ type FallibleView struct {
Child AsRenderable
}

func (v FallibleView) Renderable() (Renderable, error) {
return v.Child.Renderable()
func (v FallibleView) Renderable(ctx context.Context) (Renderable, error) {
return v.Child.Renderable(ctx)
}

func (v FallibleView) ErrorRenderable(err error) (AsRenderable, error) {
func (v FallibleView) ErrorRenderable(ctx context.Context, err error) (AsRenderable, error) {
if v.CapturesErr == nil {
return nil, err
}
Expand All @@ -41,7 +42,7 @@ func (v FallibleView) ErrorRenderable(err error) (AsRenderable, error) {
}

func TestRenderContainerWithFailingView(t *testing.T) {
_, err := Render(ContainerView2{
_, err := Render(context.Background(), ContainerView2{
Heading: ChildView1{},
Body: FailingView{
Err: fmt.Errorf("construction: %w", errSomethingFailed),
Expand All @@ -52,7 +53,7 @@ func TestRenderContainerWithFailingView(t *testing.T) {

func TestRenderContainerWithCapturedError(t *testing.T) {
t.Run("errors_bubble_out", func(t *testing.T) {
_, err := Render(ContainerView2{
_, err := Render(context.Background(), ContainerView2{
Heading: ChildView1{},
Body: FallibleView{
Child: FailingView{Err: errSomethingFailed},
Expand All @@ -62,7 +63,7 @@ func TestRenderContainerWithCapturedError(t *testing.T) {
})

t.Run("errors_can_push_replacement_views", func(t *testing.T) {
html, err := Render(ContainerView2{
html, err := Render(context.Background(), ContainerView2{
Heading: ChildView1{},
Body: FallibleView{
Child: FailingView{Err: errSomethingFailed},
Expand All @@ -77,7 +78,7 @@ func TestRenderContainerWithCapturedError(t *testing.T) {
})

t.Run("errors_can_return_nil_views", func(t *testing.T) {
html, err := Render(ContainerView2{
html, err := Render(context.Background(), ContainerView2{
Heading: ChildView1{},
Body: FallibleView{
Child: FailingView{Err: errors.New("hi")},
Expand Down
19 changes: 10 additions & 9 deletions render_container_test.go
@@ -1,6 +1,7 @@
package veun_test

import (
"context"
"html/template"
"testing"

Expand All @@ -19,30 +20,30 @@ var containerViewTpl = MustParseTemplate("containerView", `<div>
<div class="body">{{ slot "body" }}</div>
</div>`)

func tplWithRealSlotFunc(tpl *template.Template, slots map[string]AsRenderable) *template.Template {
func tplWithRealSlotFunc(ctx context.Context, tpl *template.Template, slots map[string]AsRenderable) *template.Template {
return tpl.Funcs(template.FuncMap{
"slot": func(name string) (template.HTML, error) {
slot, ok := slots[name]
if ok {
return Render(slot)
return Render(ctx, slot)
}
return template.HTML(""), nil
},
})
}

func (v ContainerView) Template() (*template.Template, error) {
return tplWithRealSlotFunc(containerViewTpl, map[string]AsRenderable{
func (v ContainerView) Template(ctx context.Context) (*template.Template, error) {
return tplWithRealSlotFunc(ctx, containerViewTpl, map[string]AsRenderable{
"heading": v.Heading,
"body": v.Body,
}), nil
}

func (v ContainerView) TemplateData() (any, error) {
func (v ContainerView) TemplateData(_ context.Context) (any, error) {
return nil, nil
}

func (v ContainerView) Renderable() (Renderable, error) {
func (v ContainerView) Renderable(_ context.Context) (Renderable, error) {
return v, nil
}

Expand All @@ -52,18 +53,18 @@ var childViewTemplate = template.Must(

type ChildView1 struct{}

func (v ChildView1) Renderable() (Renderable, error) {
func (v ChildView1) Renderable(_ context.Context) (Renderable, error) {
return View{Tpl: childViewTemplate, Data: "HEADING"}, nil
}

type ChildView2 struct{}

func (v ChildView2) Renderable() (Renderable, error) {
func (v ChildView2) Renderable(_ context.Context) (Renderable, error) {
return View{Tpl: childViewTemplate, Data: "BODY"}, nil
}

func TestRenderContainer(t *testing.T) {
html, err := Render(&ContainerView{
html, err := Render(context.Background(), &ContainerView{
Heading: ChildView1{},
Body: ChildView2{},
})
Expand Down
5 changes: 3 additions & 2 deletions render_person_test.go
@@ -1,6 +1,7 @@
package veun_test

import (
"context"
"html/template"
"testing"

Expand All @@ -24,12 +25,12 @@ var personViewTpl = template.Must(
template.New("PersonView").Parse(`<div>Hi, {{ .Name }}.</div>`),
)

func (v *personView) Renderable() (Renderable, error) {
func (v *personView) Renderable(_ context.Context) (Renderable, error) {
return View{Tpl: personViewTpl, Data: v.Person}, nil
}

func TestRenderPerson(t *testing.T) {
html, err := Render(PersonView(Person{Name: "Stan"}))
html, err := Render(context.Background(), PersonView(Person{Name: "Stan"}))
assert.NoError(t, err)
assert.Equal(t, html, template.HTML(`<div>Hi, Stan.</div>`))
}
7 changes: 4 additions & 3 deletions render_with_data_fetch_test.go
@@ -1,6 +1,7 @@
package veun_test

import (
"context"
"fmt"
"html/template"
"testing"
Expand Down Expand Up @@ -45,7 +46,7 @@ func NewExpensiveView(shouldErr bool) *ExpensiveView {
return &ExpensiveView{Data: dataCh, Err: errCh}
}

func (v *ExpensiveView) Renderable() (Renderable, error) {
func (v *ExpensiveView) Renderable(_ context.Context) (Renderable, error) {
select {
case err := <-v.Err:
return nil, err
Expand All @@ -56,13 +57,13 @@ func (v *ExpensiveView) Renderable() (Renderable, error) {

func TestViewWithChannels(t *testing.T) {
t.Run("successful", func(t *testing.T) {
html, err := Render(NewExpensiveView(false))
html, err := Render(context.Background(), NewExpensiveView(false))
assert.NoError(t, err)
assert.Equal(t, template.HTML(`hi success`), html)
})

t.Run("failed", func(t *testing.T) {
_, err := Render(NewExpensiveView(true))
_, err := Render(context.Background(), NewExpensiveView(true))
assert.Error(t, err)
})
}
23 changes: 12 additions & 11 deletions renderer.go
Expand Up @@ -2,37 +2,38 @@ package veun

import (
"bytes"
"context"
"fmt"
"html/template"
)

type Renderable interface {
Template() (*template.Template, error)
TemplateData() (any, error)
Template(ctx context.Context) (*template.Template, error)
TemplateData(ctx context.Context) (any, error)
}

type AsRenderable interface {
Renderable() (Renderable, error)
Renderable(ctx context.Context) (Renderable, error)
}

func Render(r AsRenderable) (template.HTML, error) {
renderable, err := r.Renderable()
func Render(ctx context.Context, r AsRenderable) (template.HTML, error) {
renderable, err := r.Renderable(ctx)
if err != nil {
return handleRenderError(err, r)
return handleRenderError(ctx, err, r)
}

out, err := render(renderable)
out, err := render(ctx, renderable)
if err != nil {
return handleRenderError(err, r)
return handleRenderError(ctx, err, r)
}

return out, nil
}

func render(r Renderable) (template.HTML, error) {
func render(ctx context.Context, r Renderable) (template.HTML, error) {
var empty template.HTML

tpl, err := r.Template()
tpl, err := r.Template(ctx)
if err != nil {
return empty, err
}
Expand All @@ -41,7 +42,7 @@ func render(r Renderable) (template.HTML, error) {
return empty, fmt.Errorf("missing template")
}

data, err := r.TemplateData()
data, err := r.TemplateData(ctx)
if err != nil {
return empty, err
}
Expand Down
25 changes: 15 additions & 10 deletions slots.go
@@ -1,19 +1,24 @@
package veun

import "html/template"
import (
"context"
"html/template"
)

type Slots map[string]AsRenderable

func (s Slots) renderSlot(name string) (template.HTML, error) {
slot, ok := s[name]
if ok {
return Render(slot)
}
func (s Slots) renderSlot(ctx context.Context) func(string) (template.HTML, error) {
return func(name string) (template.HTML, error) {
slot, ok := s[name]
if ok {
return Render(ctx, slot)
}

var empty template.HTML
return empty, nil
var empty template.HTML
return empty, nil
}
}

func (s Slots) addToTemplate(t *template.Template) *template.Template {
return t.Funcs(template.FuncMap{"slot": s.renderSlot})
func (s Slots) addToTemplate(ctx context.Context, t *template.Template) *template.Template {
return t.Funcs(template.FuncMap{"slot": s.renderSlot(ctx)})
}
13 changes: 8 additions & 5 deletions view.go
@@ -1,22 +1,25 @@
package veun

import "html/template"
import (
"context"
"html/template"
)

type View struct {
Tpl *template.Template
Slots Slots
Data any
}

func (v View) Template() (*template.Template, error) {
return v.Slots.addToTemplate(v.Tpl), nil
func (v View) Template(ctx context.Context) (*template.Template, error) {
return v.Slots.addToTemplate(ctx, v.Tpl), nil
}

func (v View) TemplateData() (any, error) {
func (v View) TemplateData(_ context.Context) (any, error) {
return v.Data, nil
}

func (v View) Renderable() (Renderable, error) {
func (v View) Renderable(_ context.Context) (Renderable, error) {
return v, nil
}

Expand Down

0 comments on commit 81d7c2d

Please sign in to comment.