treetop

package module
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2021 License: MIT Imports: 23 Imported by: 22

README

Go Report Card Coverage Status Build Status

Treetop

GoDoc

A lightweight bridge from net/http to html/template

Build HTML endpoints using a hierarchy of nested templates, as supported by the Go standard library [html/template].

  • Lightweight by design
  • No 3rd-party dependencies
  • Protocol for fragment hot-swapping (see Online DEMO)
Template Hierarchy

Each template is paired with an individual data loading function. Pages are constructed by combining templates in different configurations and binding endpoints to your application router.

                         BaseFunc(…)
            |============ base.html =============|
            | <html>                             |
            | …                                  |
            | {{ template "content" .Content }}  |
            | …               /\                 |
            | </html>         ||                 |
            |________________ || ________________|
                             /  \
                            /    \
        ContentAFunc(…)    /      \   ContentBFunc(…)
  |==== content_a.html =====|   |==== content_b.html =====|
  |                         |   |                         |
  | <div id="content">A</…  |   | <div id="content">B</…  |
  |_________________________|   |_________________________|

Basic example of a page hierarchy showing content A and B sharing the same 'base' template

Note. Nested hierarchy is supported, see Golang docs for details [doc]

API Overview

The code example below is an extension of the hierarchy diagram. It binds "/content_a" and "/content_b" routes with two pages that share the same "base", "nav" and "sidebar" templates.

base := treetop.NewView("base.html", BaseHandler)
nav := base.NewSubView("nav", "nav.html", NavHandler)
_ = base.NewDefaultSubView("sidebar", "sidebar.html", SidebarHandler)
contentA := base.NewSubView("content", "content_a.html", ContentAHandler)
contentB := base.NewSubView("content", "content_b.html", ContentBHandler)

exec := treetop.FileExecutor{}
myMux.Handle("/content_a", exec.NewViewHandler(contentA, nav))
myMux.Handle("/content_b", exec.NewViewHandler(contentB, nav))
Template Executor

The 'Executor' is responsible for collecting related views, configuring templates and plumbing it all together to produce a http.Handler instance for your router.

Example of embedded template blocks in "base.html",

...
<div class="layout">
	{{ block "nav" .Nav }}  <div id="nav">default nav</div> {{ end }}

	{{ template "sidebar" .SideBar }}

	{{ template "content" .Content }}
</div>
...

See text/template Nested Template Definitions for more info.

Note the loose coupling between content handlers in the outline below.

func BaseHandler(rsp treetop.Response, req *http.Request) interface{} {
    // data for base template
    return struct {
        ...
    }{
        ...
        Nav: rsp.HandleSubView("nav", req),
        SideBar: rsp.HandleSubView("sidebar", req),
        Content: rsp.HandleSubView("content", req),
    }
}

func ContentAHandler(rsp treetop.Response, req *http.Request) interface{} {
    // data for Content A template
    return ...
}
No Third-Party Dependencies

The Treetop package wraps features of the Go standard library, mostly within "net/http" and "html/template".

HTML Template Protocol

Hot-swap sections of a page without JS boilerplate
Online DEMO

Since views are self-contained, they can be rendered in isolation. Treetop handlers support rendering template fragments that can be applied to a loaded document. The following is an illustration of the protocol.

> GET /content_a HTTP/1.1
  Accept: application/x.treetop-html-template+xml

< HTTP/1.1 200 OK
  Content-Type: application/x.treetop-html-template+xml
  Vary: Accept

  <template>
      <div id="content">...</div>
      <div id="nav">...</div>
  </template>

A Treetop Client Library is available. It sends template requests using XHR and applies fragments to the DOM with a simple find and replace mechanism.

Hot-swapping can be used to enhance user experience in several ways. See demo for more details.

Examples

Template Executor

An 'Executor' is responsible for loading and configuring templates. It constructs a HTTP Handler instance to manage the plumbing between loading data and executing templates for a request. You can implement your own template loader [docs needed], but the following are provided:

  • FileExecutor - load template files using os.Open
  • FileSytemExecutor - loads templates from a supplied http.FileSystem instance
  • StringExecutor - treat the view template property as an inline template string
  • KeyedStringExecutor - treat the view template property is key into a template map
  • DeveloperExecutor - force per request template parsing

View Handler Function

A view handler function loads data for a corresponding Go template. Just as nested templates are embedded in a parent, nested handler data is embedded in the data of it's parent.

Example of a handler loading data for a child template,

func ParentHandler(rsp treetop.Response, req *http.Request) interface{} {
    return struct {
        ...
        Child interface{}
    }{
        ...
        Child: rsp.HandleSubView("child", req),
    }
}

Data is subsequently passed down within the template like so,

<div id="parent">
    ...
    {{ template "child" .Child }}
</div>
Hijacking the Response

The treetop.Response type implements http.ResponseWriter. Any handler can halt the executor and take full responsibility for the response by using one of the 'Write' methods of that interface2. This is a common and useful practice making things like error messages and redirects possible.

Response Writer

The Treetop Go library provides utilities for writing ad-hoc template responses when needed. PartialWriter and FragmentWriter wrap a supplied http.ResponseWriter.

For example,

func myHandler(w http.ResponseWriter, req *http.Request) {
    // check for treetop request and construct a writer
    if tw, ok := treetop.NewFragmentWriter(w, req); ok {
        fmt.Fprintf(tw, `<h3 id="greeting">Hello, %s</h3>`, "Treetop")
    }
}

This is useful when you want to use the template protocol with a conventional handler function.

The difference between a "Partial" and "Fragment" writer has to with navigation history in the web browser [docs needed]

Client Library

The client library is used to send template requests from the browser using XHR. Response fragments are handled mechanically on the client. This avoids the need for callbacks or other IO boilerplate.

treetop.request("GET", "/some/path")

See Client Library for more information.

Footnotes

1. Go supports template inheritance through nested template definitions. 2. A http.ResponseWriter will flush headers when either WriteHeaders(..) or Write(..) methods are invoked.

Documentation

Overview

Package treetop implements tools for constructing HTTP handlers for nested templates

To read about nested template support in Go see https://tip.golang.org/pkg/text/template/#hdr-Nested_template_definitions

Multi-page web apps require a lot of endpoints. Template inheritance is commonly used to reduce HTML boilerplate and improve reuse. Treetop views incorporate request handlers into the hierarchy to gain the same advantage.

A 'View' is a template string (usually file path) paired with a handler function. Go templates can contain named nested blocks. Defining a 'SubView' associates a handler and a template with a block embedded within a parent template. HTTP handlers can then be constructed for various page configurations.

Example of a basic template hierarchy

              baseHandler(...)
            | base.html ========================|
            | …                                 |
            | {{ template "content" .Content }} |
            | …               ^                 |
            |_________________|_________________|
                              |
                       ______/ \______
  contentAHandler(...)               contentBHandler(...)
| contentA.html ========== |        | contentB.html ========== |
|                          |        |                          |
| {{ block "content" . }}… |        | {{ block "content" . }}… |
|__________________________|        |__________________________|

Example of using the library to constructs handlers for HTTP routes.

base := treetop.NewView(
	"base.html",
	baseHandler,
)

contentA := base.NewSubView(
	"content",
	"contentA.html",
	contentAHandler,
)

contentB := base.NewSubView(
	"content",
	"contentB.html",
	contentBHandler,
)

exec := treetop.FileExecutor{}
mymux.Handle("/path/to/a", exec.ViewHandler(contentA))
mymux.Handle("/path/to/b", exec.ViewHandler(contentB))

The generated handlers bind togeather related views. Thus views can be mixed and matched to create many endpoints.

GET /path/to/a
> HTTP/1.1 200 OK
> <!-- base.html --><html>
> ...
> <!-- contentA.html --><div id="content"> Content A </div>
> ...
> </html>

GET /path/to/b
> HTTP/1.1 200 OK
> <!-- base.html --><html>
> ...
> <!-- contentB.html --><div id="content"> Content B </div>
> ...
> </html>

Note, many levels of nesting are possible once block names remain unique.

HTML Template Protocol

The constructed handlers are capable of rendering just sections of the page depending upon the request headers. See the Treetop JS library for more details. (https://github.com/rur/treetop-client)

Index

Constants

View Source
const (
	// TemplateContentType is used for content negotiation within template requests
	TemplateContentType = "application/x.treetop-html-template+xml"
)

Variables

View Source
var (
	// ErrNotAcceptable is produced by ServeHTTP when a request
	// does not contain an accept header that can be handled by this endpoint
	ErrNotAcceptable = errors.New(
		"treetop template handler: server cannot produce a response matching the list of acceptable values")
)

Errors used by the TemplateHandler.

View Source
var (
	ErrResponseHijacked = errors.New(
		"treetop response: cannot write, HTTP response has been hijacked by another handler")
)
View Source
var (
	// ServeClientLibrary TODO: add docs
	ServeClientLibrary http.Handler
)

Functions

func CompileViews added in v0.3.0

func CompileViews(view *View, includes ...*View) (page, part *View, postscript []*View)

CompileViews is used to create an endpoint configuration combining supplied view definitions based upon the template names they define.

This returns:

  • a full-page view instance,
  • a partial page view instance, and
  • any disconnect fragment views that should be appended to partial requests.

func IsTemplateRequest added in v0.3.0

func IsTemplateRequest(req *http.Request) bool

IsTemplateRequest is a predicate function which will check the headers of a given request and return true if a template response is supported by the client.

func NewTemplateHandler added in v0.4.0

func NewTemplateHandler(view *View, includes []*View, load *TemplateLoader) (*TemplateHandler, ExecutorErrors)

NewTemplateHandler compiles an endpoint view hierarchy and loads corresponding HTML templates

func Noop

func Noop(_ Response, _ *http.Request) interface{}

Noop treetop handler helper is useful when a treetop.HandlerFunc instance is needed but you don't want it to do anything. Function returns `nil`.

func Redirect

func Redirect(w http.ResponseWriter, req *http.Request, location string, status int)

Redirect is a helper that will instruct the Treetop client library to direct the web browser to a new URL. If the request is not from a Treetop client, the 3xx redirect method is used.

This is necessary because 3xx HTTP redirects are opaque to XHR, when a full browser redirect is needed a 'X-Treetop-Redirect' header is used.

Example:

treetop.Redirect(w, req, "/some/other/path", http.StatusSeeOther)

func SprintViewInfo added in v0.3.0

func SprintViewInfo(v *View) string

SprintViewInfo will create a string preview of view

func SprintViewTree added in v0.3.0

func SprintViewTree(v *View) string

SprintViewTree create a string with a tree representation of a a view hierarchy.

For example, the view definition 'v'

v := NewView("base.html", Constant("base!"))
a := v.NewDefaultSubView("A", "A.html", Constant("A!"))
a.NewDefaultSubView("A1", "A1.html", Constant("A1!"))
a.NewDefaultSubView("A2", "A2.html", Constant("A2!"))
b := v.NewDefaultSubView("B", "B.html", Constant("B!"))
b.NewDefaultSubView("B1", "B1.html", Constant("B1!"))
b.NewDefaultSubView("B2", "B2.html", Constant("B2!"))

fmt.Println(treetop.SprintViewTree(v))

will be outputted as the string

  • View("base.html", github.com/rur/treetop.Constant.func1) |- A: SubView("A", "A.html", github.com/rur/treetop.Constant.func1) | |- A1: SubView("A1", "A1.html", github.com/rur/treetop.Constant.func1) | '- A2: SubView("A2", "A2.html", github.com/rur/treetop.Constant.func1) | '- B: SubView("B", "B.html", github.com/rur/treetop.Constant.func1) |- B1: SubView("B1", "B1.html", github.com/rur/treetop.Constant.func1) '- B2: SubView("B2", "B2.html", github.com/rur/treetop.Constant.func1)

Types

type CaptureErrors added in v0.4.0

type CaptureErrors struct {
	Errors ExecutorErrors
}

CaptureErrors is a base type for implementing concreate view executors

func (*CaptureErrors) AddErrors added in v0.4.0

func (ce *CaptureErrors) AddErrors(errs ExecutorErrors)

AddErrors will store a list of errors to flushe later

func (*CaptureErrors) FlushErrors added in v0.4.0

func (ce *CaptureErrors) FlushErrors() ExecutorErrors

FlushErrors will return the list of template creation errors that occurred while ViewHandlers were begin created, since the last time it was called.

type DeveloperExecutor added in v0.3.0

type DeveloperExecutor struct {
	ViewExecutor
}

DeveloperExecutor wraps another executor, it will re-generate the view handler for every request. This can be used to live-reload templates during development.

Example:

exec := DeveloperExecutor{FileExecutor{}}
mux.Handle("/hello", exec.NewViewHandler(v))

Note: this is for development use only, it is not suitable for production systems

func (*DeveloperExecutor) NewViewHandler added in v0.3.0

func (de *DeveloperExecutor) NewViewHandler(view *View, includes ...*View) ViewHandler

NewViewHandler will create a special handler that will reload the templates for ever request. Any template errors that occur will be rendered to the client.

Note: this is for development use only, it is not suitable for production systems

type ExecutorError added in v0.3.0

type ExecutorError struct {
	View *View
	Err  error
}

ExecutorError is created within the executor when a template cannot be created for a view. Call exec.FlushErrors() to obtain a list of the template errors that occurred.

func (*ExecutorError) Error added in v0.3.0

func (te *ExecutorError) Error() string

Error implement the error interface

type ExecutorErrors added in v0.3.0

type ExecutorErrors []*ExecutorError

ExecutorErrors is a list zero or more template errors created when parsing templates

func (ExecutorErrors) Error added in v0.3.0

func (ee ExecutorErrors) Error() string

Errors implements error interface

type FileExecutor added in v0.3.0

type FileExecutor struct {
	CaptureErrors
	Funcs       template.FuncMap
	KeyedString map[string]string
}

FileExecutor loads view templates as a path from a template file.

func (*FileExecutor) NewViewHandler added in v0.3.0

func (fe *FileExecutor) NewViewHandler(view *View, includes ...*View) ViewHandler

NewViewHandler creates a ViewHandler from a View endpoint definition treating view template strings as a file path using os.Open.

type FileSystemExecutor added in v0.3.0

type FileSystemExecutor struct {
	CaptureErrors
	FS          http.FileSystem
	Funcs       template.FuncMap
	KeyedString map[string]string
}

FileSystemExecutor loads view templates as a path from a Go HTML template file. The underlying file system is abstracted through the http.FileSystem interface to allow for in-memory use.

The optional KeyedString map will be checked before the loader attempts to use the FS instance when obtain a template string

func (*FileSystemExecutor) NewViewHandler added in v0.3.0

func (fse *FileSystemExecutor) NewViewHandler(view *View, includes ...*View) ViewHandler

NewViewHandler creates a ViewHandler from a View endpoint definition treating view template strings as keys into the string template dictionary.

type KeyedStringExecutor added in v0.3.0

type KeyedStringExecutor struct {
	CaptureErrors
	Templates map[string]string
	Funcs     template.FuncMap
}

KeyedStringExecutor builds handlers templates from a map of available templates. The view templates are treated as keys into the map for the purpose of build handlers.

func NewKeyedStringExecutor added in v0.3.0

func NewKeyedStringExecutor(templates map[string]string) *KeyedStringExecutor

NewKeyedStringExecutor is a deprecated method for constructing an instance from a template map

func (*KeyedStringExecutor) NewViewHandler added in v0.3.0

func (ks *KeyedStringExecutor) NewViewHandler(view *View, includes ...*View) ViewHandler

NewViewHandler creates a ViewHandler from a View endpoint definition treating view template strings as keys into the string template dictionary.

type Response

type Response interface {
	http.ResponseWriter

	// Status allows a handler to indicate (not determine) what the HTTP status
	// should be for the response.
	//
	// When different handlers indicate a different status,
	// the code with the greater numeric value is chosen.
	//
	// For example, given: Bad Request, Unauthorized and Internal Server Error.
	// Status values are differentiated as follows, 400 < 401 < 500,
	// 'Internal Server Error' is chosen for the response header.
	//
	// The resulting response status is returned. Getting the current status
	// without affecting the response can be done as follows
	//
	// 		status := rsp.Status(0)
	//
	Status(int) int

	// DesignatePageURL forces the response to be handled as a navigation event with a specified URL.
	// The browser will have a new history entry created for the supplied URL.
	DesignatePageURL(string)

	// ReplacePageURL forces the location bar in the web browser to be updated with the supplied
	// URL. This should be done by *replacing* the existing history entry. (not adding a new one)
	ReplacePageURL(string)

	// Finished will return true if a handler has taken direct responsibility for writing the
	// response.
	Finished() bool

	// HandleSubView loads data from a named child subview handler. If no handler is available for the name,
	// nil will be returned.
	//
	// NOTE: Since a sub handler may have returned nil, there is no way for the parent handler to determine
	//       whether the name resolved to a concrete view.
	HandleSubView(string, *http.Request) interface{}

	// ResponseID returns the ID treetop has associated with this request.
	// Since multiple handlers may be involved, the ID is useful for logging and caching.
	//
	// Response IDs avoid potential pitfalls around Request instance comparison that can affect middleware.
	//
	// NOTE: This is *not* a UUID, response IDs are incremented from zero when the server is started
	ResponseID() uint32

	// Context returns the context associated with the treetop process.
	// This is a child of the http Request context.
	Context() context.Context
}

Response extends the http.ResponseWriter interface to give ViewHandelersFunc's limited ability to control the hierarchical request handling.

Note that writing directly to the underlying ResponseWriter in the handler will cancel the treetop handling process. Taking control of response writing in this way is a very common and useful practice especially for error messages or redirects.

type ResponseWrapper added in v0.3.0

type ResponseWrapper struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

ResponseWrapper is the concrete implementation of the response writer wrapper supplied to view handler functions

func BeginResponse added in v0.3.0

func BeginResponse(cxt context.Context, w http.ResponseWriter) *ResponseWrapper

BeginResponse initializes the context for a treetop request response

func (*ResponseWrapper) Cancel added in v0.3.0

func (rsp *ResponseWrapper) Cancel()

Cancel will teardown the treetop handing process

func (*ResponseWrapper) Context added in v0.3.0

func (rsp *ResponseWrapper) Context() context.Context

Context is getter for the treetop response context which will indicate when the request has been completed as was cancelled. This is derived from the request context so it can safely be used for cleanup.

func (*ResponseWrapper) DesignatePageURL added in v0.3.0

func (rsp *ResponseWrapper) DesignatePageURL(url string)

DesignatePageURL will result in a header being added to the response that will create a new history entry for the supplied URL

func (*ResponseWrapper) Finished added in v0.3.0

func (rsp *ResponseWrapper) Finished() bool

Finished will return true if the response headers have been written to the client, effectively cancelling the treetop view handler lifecycle

func (*ResponseWrapper) HandleSubView added in v0.3.0

func (rsp *ResponseWrapper) HandleSubView(name string, req *http.Request) interface{}

HandleSubView will execute the handler for a specified sub view of the current view if there is no match for the name, nil will be returned.

func (*ResponseWrapper) NewTemplateWriter added in v0.3.0

func (rsp *ResponseWrapper) NewTemplateWriter(req *http.Request) (Writer, bool)

NewTemplateWriter will return a template Writer configured to add Treetop headers based up on the state of the response. If the request is not a template request the writer will be nil and the ok flag will be false

func (*ResponseWrapper) ReplacePageURL added in v0.3.0

func (rsp *ResponseWrapper) ReplacePageURL(url string)

ReplacePageURL will instruct the client to replace the current history entry with the supplied URL

func (*ResponseWrapper) ResponseID added in v0.3.0

func (rsp *ResponseWrapper) ResponseID() uint32

ResponseID is a getter which returns a locally unique ID for a Treetop HTTP response. This is intended to be used to keep track of the request as is passes between handlers. The ID will increment by one starting at zero, every time the server is restarted.

func (*ResponseWrapper) Status added in v0.3.0

func (rsp *ResponseWrapper) Status(status int) int

Status will set a status for the treetop response headers if a response status has been set previously, the larger code value will be adopted

func (*ResponseWrapper) WithSubViews added in v0.3.0

func (rsp *ResponseWrapper) WithSubViews(subViews map[string]*View) *ResponseWrapper

WithSubViews creates a derived response wrapper for a different view, inheriting request

func (*ResponseWrapper) Write added in v0.3.0

func (rsp *ResponseWrapper) Write(b []byte) (int, error)

Write delegates to the underlying ResponseWriter while aborting the treetop executor handler.

func (*ResponseWrapper) WriteHeader added in v0.3.0

func (rsp *ResponseWrapper) WriteHeader(statusCode int)

WriteHeader delegates to the underlying ResponseWriter while setting finished flag to true

type StringExecutor added in v0.3.0

type StringExecutor struct {
	CaptureErrors
	Funcs template.FuncMap
}

StringExecutor loads view templates as an inline template string.

Example:

exec := StringExecutor{}
v := treetop.NewView("<p>Hello {{ . }}!</p>", Constant("world"))
mux.Handle("/hello", exec.NewViewHandler(v))

func (*StringExecutor) NewViewHandler added in v0.3.0

func (se *StringExecutor) NewViewHandler(view *View, includes ...*View) ViewHandler

NewViewHandler creates a ViewHandler from a View endpoint definition treating view template strings as keys into the string template dictionary.

type Template added in v0.3.1

type Template interface {
	ExecuteTemplate(io.Writer, string, interface{}) error
}

Template is an interface so that the concrete template implementation can be changed

type TemplateHandler added in v0.3.0

type TemplateHandler struct {
	Page             *View
	PageTemplate     Template
	Partial          *View
	PartialTemplate  Template
	Includes         []*View
	IncludeTemplates []Template
	// optional developer defined error handler
	ServeTemplateError func(error, Response, *http.Request)
}

TemplateHandler implements the treetop.ViewHandler interface for endpoints that support the treetop protocol

func (*TemplateHandler) FragmentOnly added in v0.3.0

func (h *TemplateHandler) FragmentOnly() ViewHandler

FragmentOnly creates a new Handler that only responds to fragment requests

func (*TemplateHandler) PageOnly added in v0.3.0

func (h *TemplateHandler) PageOnly() ViewHandler

PageOnly create a new handler that will only respond to non-fragment (full page) requests

func (*TemplateHandler) ServeHTTP added in v0.3.0

func (h *TemplateHandler) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP is responsible for directing the handing of an incoming request. Implements the procedure through which views functions and templates are to be executed.

type TemplateLoader added in v0.4.0

type TemplateLoader struct {
	Load  func(string) (string, error)
	Funcs template.FuncMap
}

func NewTemplateLoader added in v0.4.0

func NewTemplateLoader(funcs template.FuncMap, load func(string) (string, error)) *TemplateLoader

func (TemplateLoader) ViewTemplate added in v0.4.0

func (tl TemplateLoader) ViewTemplate(view *View) (*template.Template, error)

type View

type View struct {
	Template    string
	HandlerFunc ViewHandlerFunc
	SubViews    map[string]*View
	Defines     string
	Parent      *View
}

View is used to define hierarchies of nested template-handler pairs so that HTTP endpoints can be constructed for different page configurations.

A 'View' is a template string (usually file path) paired with a handler function. Go templates can contain named nested blocks. Defining a 'SubView' associates a handler and a template with a block embedded within a parent template. HTTP handlers can then be constructed for various page configurations.

Example of a basic template hierarchy

              baseHandler(...)
            | base.html ========================|
            | …                                 |
            | {{ template "content" .Content }} |
            | …               ^                 |
            |_________________|_________________|
                              |
                       ______/ \______
  contentAHandler(...)               contentBHandler(...)
| contentA.html ========== |        | contentB.html ========== |
|                          |        |                          |
| {{ block "content" . }}… |        | {{ block "content" . }}… |
|__________________________|        |__________________________|

Pseudo request and response:

GET /path/to/a
> HTTP/1.1 200 OK
> ... base.html { Content: contentA.html }

GET /path/to/b
> HTTP/1.1 200 OK
> ... base.html { Content: contentB.html }

Example of using the library to bind constructed handlers to a HTTP router.

base := treetop.NewView(
	"base.html",
	baseHandler,
)

contentA := base.NewSubView(
	"content",
	"contentA.html",
	contentAHandler,
)

contentB := base.NewSubView(
	"content",
	"contentB.html",
	contentBHandler,
)

exec := treetop.FileExecutor{}
mymux.Handle("/path/to/a", exec.ViewHandler(contentA))
mymux.Handle("/path/to/b", exec.ViewHandler(contentB))

This is useful for creating Treetop enabled endpoints because the constructed handler is capable of loading either a full page or just the "content" part of the page depending upon the request.

func NewSubView added in v0.3.0

func NewSubView(defines, tmpl string, handler ViewHandlerFunc) *View

NewSubView creates an instance of a view given a template + handler pair this view is a detached subview, in that is does not reference a parent

func NewView

func NewView(tmpl string, handler ViewHandlerFunc) *View

NewView creates an instance of a view given a template + handler pair

func (*View) Copy added in v0.3.0

func (v *View) Copy() *View

Copy creates a duplicate so that the original is not affected by changes. This will propegate as a 'deep copy' to all default subviews

func (*View) HasSubView added in v0.4.0

func (v *View) HasSubView(name string)

HasSubView asserts that a subview name exists without the need to define a template

func (*View) NewDefaultSubView added in v0.3.0

func (v *View) NewDefaultSubView(defines string, tmpl string, handler ViewHandlerFunc) *View

NewDefaultSubView create a new view extending a named block within the current view and updates the parent to use this view by default

func (*View) NewSubView added in v0.3.0

func (v *View) NewSubView(defines string, tmpl string, handler ViewHandlerFunc) *View

NewSubView create a new view extending a named block within the current view

type ViewExecutor added in v0.3.0

type ViewExecutor interface {
	NewViewHandler(view *View, includes ...*View) ViewHandler
	FlushErrors() ExecutorErrors
}

ViewExecutor is an interface for objects that implement transforming a View definition into a ViewHandler that supports full page and template requests.

type ViewHandler

type ViewHandler interface {
	http.Handler
	FragmentOnly() ViewHandler
	PageOnly() ViewHandler
}

ViewHandler is an extension of the http.Handler interface with methods added for extra treetop endpoint configuration

type ViewHandlerFunc added in v0.2.0

type ViewHandlerFunc func(Response, *http.Request) interface{}

ViewHandlerFunc is the interface for treetop handler functions that support hierarchical partial data loading.

func Constant

func Constant(data interface{}) ViewHandlerFunc

Constant treetop handler helper is used to generate a treetop.HandlerFunc that always returns the same value.

func Delegate

func Delegate(blockname string) ViewHandlerFunc

Delegate handler helper will delegate partial handling to a named block of that partial. The designated block data will be adopted as the partial template data and no other block handler will be executed.

func RequestHandler

func RequestHandler(f func(*http.Request) interface{}) ViewHandlerFunc

RequestHandler handler helper is used where only the http.Request instance is needed to resolve the template data so the treetop.Response isn't part of the actual handler function.

type Writer

type Writer interface {
	http.ResponseWriter
	Status(int)
	DesignatePageURL(string)
	ReplacePageURL(string)
}

Writer is an interface for writing HTTP responses that conform to the Treetop protocol

func NewFragmentWriter added in v0.2.0

func NewFragmentWriter(w http.ResponseWriter, req *http.Request) (Writer, bool)

NewFragmentWriter will check if the client accepts one of the Treetop content types, if so it will return a wrapped response writer for a Treetop html fragment.

Example:

func MyHandler(w http.ResponseWriter, req *http.Request) {
	if ttW, ok := treetop.NewFragmentWriter(w, req); ok {
		// this is a treetop request, write a HTML fragment
		fmt.Fprintf(tw, `<h3 id="greeting">Hello, %s</h3>`, "Treetop")
		return
	}
	/* otherwise handle request in a different way (unspecified) */
}

func NewPartialWriter added in v0.2.0

func NewPartialWriter(w http.ResponseWriter, req *http.Request) (Writer, bool)

NewPartialWriter will check if the client accepts the template content type. If so it will return a wrapped response writer that will add the appropriate headers.

The partial writer will include the 'X-Page-URL' response header with the URI of the request. By the protocol, this means that it must be possible for a subsequent request to load a full HTML document from that URL by varying the accept header.

Example:

func MyHandler(w http.ResponseWriter, req *http.Request) {
	if ttW, ok := treetop.NewPartialWriter(w, req); ok {
		// this is a treetop request, write a HTML fragment
		fmt.Fprintf(tw, `<h3 id="greeting">Hello, %s</h3>`, "Treetop")
		return
	}
	/* otherwise render a full HTML page as normal */
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL