Initial commit
This commit is contained in:
40
template/html/data.go
Normal file
40
template/html/data.go
Normal file
@ -0,0 +1,40 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"forge.cadoles.com/wpetit/goweb/service"
|
||||
"forge.cadoles.com/wpetit/goweb/service/session"
|
||||
"forge.cadoles.com/wpetit/goweb/service/template"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// WithFlashes extends the template's data with session's flash messages
|
||||
func WithFlashes(w http.ResponseWriter, r *http.Request, container *service.Container) template.DataExtFunc {
|
||||
return func(data template.Data) (template.Data, error) {
|
||||
|
||||
sessionService, err := session.From(container)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error while retrieving session service")
|
||||
}
|
||||
|
||||
sess, err := sessionService.Get(w, r)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error while retrieving session")
|
||||
}
|
||||
|
||||
flashes := sess.Flashes(
|
||||
session.FlashError, session.FlashWarn,
|
||||
session.FlashSuccess, session.FlashInfo,
|
||||
)
|
||||
|
||||
data["Flashes"] = flashes
|
||||
|
||||
if err := sess.Save(w, r); err != nil {
|
||||
return nil, errors.Wrap(err, "error while saving session")
|
||||
}
|
||||
|
||||
return data, nil
|
||||
|
||||
}
|
||||
}
|
71
template/html/helper.go
Normal file
71
template/html/helper.go
Normal file
@ -0,0 +1,71 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func dump(args ...interface{}) (string, error) {
|
||||
out := ""
|
||||
for _, a := range args {
|
||||
str, err := json.MarshalIndent(a, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out += string(str) + "\n"
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func increment(value int) int {
|
||||
return value + 1
|
||||
}
|
||||
|
||||
func ellipsis(str string, max int) string {
|
||||
if utf8.RuneCountInString(str) > max {
|
||||
return string([]rune(str)[0:(max-3)]) + "..."
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func customMap(values ...interface{}) (map[string]interface{}, error) {
|
||||
if len(values)%2 != 0 {
|
||||
return nil, errors.New("invalid custoMmap call")
|
||||
}
|
||||
customMap := make(map[string]interface{}, len(values)/2)
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
key, ok := values[i].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("map keys must be strings")
|
||||
}
|
||||
customMap[key] = values[i+1]
|
||||
}
|
||||
return customMap, nil
|
||||
}
|
||||
|
||||
func defaultVal(src interface{}, defaultValue interface{}) interface{} {
|
||||
switch typ := src.(type) {
|
||||
case string:
|
||||
if typ == "" {
|
||||
return defaultValue
|
||||
}
|
||||
default:
|
||||
if src == nil {
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
func has(v interface{}, name string) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
if rv.Kind() != reflect.Struct {
|
||||
return false
|
||||
}
|
||||
return rv.FieldByName(name).IsValid()
|
||||
}
|
58
template/html/option.go
Normal file
58
template/html/option.go
Normal file
@ -0,0 +1,58 @@
|
||||
package html
|
||||
|
||||
import "html/template"
|
||||
|
||||
// Options are configuration options for the template service
|
||||
type Options struct {
|
||||
Helpers template.FuncMap
|
||||
PoolSize int
|
||||
}
|
||||
|
||||
// OptionFunc configures options for the template service
|
||||
type OptionFunc func(*Options)
|
||||
|
||||
// WithDefaultHelpers configures the template service
|
||||
// to expose the default helpers
|
||||
func WithDefaultHelpers() OptionFunc {
|
||||
return func(opts *Options) {
|
||||
helpers := template.FuncMap{
|
||||
"dump": dump,
|
||||
"ellipsis": ellipsis,
|
||||
"map": customMap,
|
||||
"default": defaultVal,
|
||||
"inc": increment,
|
||||
"has": has,
|
||||
}
|
||||
for name, fn := range helpers {
|
||||
WithHelper(name, fn)(opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithHelper configures the template service
|
||||
// to expose the default helpers
|
||||
func WithHelper(name string, fn interface{}) OptionFunc {
|
||||
return func(opts *Options) {
|
||||
opts.Helpers[name] = fn
|
||||
}
|
||||
}
|
||||
|
||||
// WithPoolSize configures the template service
|
||||
// to use the given pool size
|
||||
func WithPoolSize(size int) OptionFunc {
|
||||
return func(opts *Options) {
|
||||
opts.PoolSize = size
|
||||
}
|
||||
}
|
||||
|
||||
func defaultOptions() *Options {
|
||||
options := &Options{}
|
||||
funcs := []OptionFunc{
|
||||
WithPoolSize(64),
|
||||
WithDefaultHelpers(),
|
||||
}
|
||||
for _, f := range funcs {
|
||||
f(options)
|
||||
}
|
||||
return options
|
||||
}
|
89
template/html/service.go
Normal file
89
template/html/service.go
Normal file
@ -0,0 +1,89 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/oxtoacart/bpool"
|
||||
)
|
||||
|
||||
// TemplateService is a template/html based templating service
|
||||
type TemplateService struct {
|
||||
templates map[string]*template.Template
|
||||
pool *bpool.BufferPool
|
||||
helpers template.FuncMap
|
||||
}
|
||||
|
||||
// LoadTemplates loads the templates used by the service
|
||||
func (t *TemplateService) LoadTemplates(templatesDir string) error {
|
||||
|
||||
layouts, err := filepath.Glob(filepath.Join(templatesDir, "layouts", "*.tmpl"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blocks, err := filepath.Glob(filepath.Join(templatesDir, "blocks", "*.tmpl"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate our templates map from our layouts/ and blocks/ directories
|
||||
for _, layout := range layouts {
|
||||
|
||||
var err error
|
||||
files := append(blocks, layout)
|
||||
|
||||
tmpl := template.New("")
|
||||
tmpl.Funcs(t.helpers)
|
||||
|
||||
tmpl, err = tmpl.ParseFiles(files...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.templates[filepath.Base(layout)] = tmpl
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// RenderPage renders a template to the given http.ResponseWriter
|
||||
func (t *TemplateService) RenderPage(w http.ResponseWriter, templateName string, data interface{}) error {
|
||||
|
||||
// Ensure the template exists in the map.
|
||||
tmpl, ok := t.templates[templateName]
|
||||
if !ok {
|
||||
return fmt.Errorf("the template '%s' does not exist", templateName)
|
||||
}
|
||||
|
||||
// Create a buffer to temporarily write to and check if any errors were encountered.
|
||||
buf := t.pool.Get()
|
||||
defer t.pool.Put(buf)
|
||||
|
||||
if err := tmpl.ExecuteTemplate(buf, templateName, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the header and write the buffer to the http.ResponseWriter
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
_, err := buf.WriteTo(w)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
// NewTemplateService returns a new Service
|
||||
func NewTemplateService(funcs ...OptionFunc) *TemplateService {
|
||||
options := defaultOptions()
|
||||
for _, f := range funcs {
|
||||
f(options)
|
||||
}
|
||||
return &TemplateService{
|
||||
templates: make(map[string]*template.Template),
|
||||
pool: bpool.NewBufferPool(options.PoolSize),
|
||||
helpers: options.Helpers,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user