Commit a10255a6 authored by Dustin L. Howett's avatar Dustin L. Howett
Browse files

Add global functions, functional options, logging.

parent 8bbce4e5
......@@ -2,11 +2,13 @@
Package views provides Ghostbin's view model. Views are templates loaded
from a set of globbed files.
The view model provides four predefined template functions, and disallows
the creation of additional bound functions.
The view model provides four predefined template functions, and allows the
binding of additional template functions by way of the
GlobalFunctionProviderOption.
global . <var>
Fetches a variable from the global view data provider.
NOTE: Only available if the Model was provided the GlobalDataProviderOption.
local . <var>
Fetches a variable from the local view data provider.
now
......
......@@ -6,8 +6,20 @@ import (
"html/template"
"sync"
"time"
"github.com/Sirupsen/logrus"
)
// FuncMap is the type of the map providing template functions to all of
// a model's derived views.
type FuncMap template.FuncMap
// FunctionProvider is the interface that allows Model consumers to
// provide their own template functions.
type FunctionProvider interface {
GetViewFunctions() FuncMap
}
// Model represents a view model loaded from a set of files. Its
// behavior is documented in the package-level documentation above.
type Model struct {
......@@ -16,6 +28,7 @@ type Model struct {
baseTemplate *template.Template
tmpl *template.Template
bound []*View
logger logrus.FieldLogger
}
// Bind combines a view model, a view ID, and a data provider into a
......@@ -37,6 +50,7 @@ func (m *Model) Bind(id interface{}, dp DataProvider) (*View, error) {
}
view := &View{
m: m,
id: vid,
dp: dp,
}
......@@ -81,7 +95,7 @@ func (m *Model) Reload() error {
// New returns a new Model bound to the supplied data provider. The data
// provider will be used for all `global` variable lookups.
func New(glob string, globalDataProvider DataProvider) (*Model, error) {
func New(glob string, options ...ModelOption) (*Model, error) {
m := &Model{
glob: glob,
}
......@@ -89,8 +103,7 @@ func New(glob string, globalDataProvider DataProvider) (*Model, error) {
tmpl := template.New(".base").Funcs(template.FuncMap{
// all provided functions must be defined here,
// otherwise the global parse will fail.
"global": varFromDataProvider(globalDataProvider),
"now": time.Now,
"now": time.Now,
// rebind in subviews.
"subtemplate": func(args ...interface{}) interface{} {
......@@ -101,7 +114,14 @@ func New(glob string, globalDataProvider DataProvider) (*Model, error) {
panic(errors.New("unbound use of local"))
},
})
m.baseTemplate = tmpl
for _, opt := range options {
if err := opt(m); err != nil {
return nil, err
}
}
return m, m.Reload()
}
......@@ -6,7 +6,7 @@ import (
"net/http"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus"
)
// viewContext represents the template context passed to each render.
......@@ -54,6 +54,7 @@ func (s stringViewID) baseContext() *viewContext {
// View represents an ID bound to a data provider and Model. Its behavior
// is documented in the package-level documentation above.
type View struct {
m *Model
mu sync.RWMutex
// immutable
......@@ -74,11 +75,13 @@ func (v *View) subtemplate(vctx *viewContext, name string) template.HTML {
err := st.Execute(buf, vctx)
if err != nil {
log.WithFields(log.Fields{
"page": vctx.page,
"subtemplate": name,
"error": err,
}).Error("failed to service subtemplate request")
if v.m.logger != nil {
v.m.logger.WithFields(logrus.Fields{
"page": vctx.page,
"subtemplate": name,
"error": err,
}).Error("failed to service subtemplate request")
}
}
return template.HTML(buf.String())
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment