Skip to content

Commit

Permalink
feat: 增强模板函数
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangmj committed Nov 21, 2024
1 parent a31bc7c commit cdfaa60
Show file tree
Hide file tree
Showing 24 changed files with 454 additions and 312 deletions.
1 change: 1 addition & 0 deletions components/jethtml/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package jethtml
27 changes: 10 additions & 17 deletions components/jethtml/jet.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package jethtml
import (
"github.com/CloudyKit/jet/v6"
"github.com/make-money-fast/gin"

Check failure on line 5 in components/jethtml/jet.go

View workflow job for this annotation

GitHub Actions / lint

could not import github.com/make-money-fast/gin (-: # github.com/make-money-fast/gin
"github.com/make-money-fast/gin/render"
"net/http"
)

Expand All @@ -16,7 +15,7 @@ type JetInstantRenderOption struct {
extensions []string
leftDelim string
rightDelim string
functions []jet.Func
functions map[string]jet.Func
debug bool
}

Expand All @@ -34,7 +33,7 @@ func Debug() Options {
}
}

func Functions(functions ...jet.Func) Options {
func Functions(functions map[string]jet.Func) Options {
return func(o *JetInstantRenderOption) {
o.functions = functions
}
Expand Down Expand Up @@ -70,28 +69,22 @@ func NewJetRender(directory string, options ...Options) *JetInstantRender {
opts...,
)

return &JetInstantRender{
views: views,
if len(jetOption.functions) != 0 {
for name, fn := range jetOption.functions {
views.AddGlobalFunc(name, fn)
}
}
}

var (
contextKey = struct{}{}
)

func NewContext(name string, ctx *gin.Context) *render.Context {
c := &render.Context{
Name: name,
return &JetInstantRender{
views: views,
}
c.Set(contextKey, ctx)
return c
}

func (j *JetInstantRender) Instance(ctx *render.Context) render.Render {
func (j *JetInstantRender) Instance(ctx *gin.RenderContext) gin.Render {
return &JetHtmlRender{
views: j.views,
name: ctx.Name,
ctx: ctx.ContextValue(contextKey).(*gin.Context),
ctx: ctx.GinContext,
}
}

Expand Down
68 changes: 45 additions & 23 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"time"

"github.com/make-money-fast/gin/binding"
"github.com/make-money-fast/gin/render"
)

// Content-Type MIME of the most common data formats.
Expand Down Expand Up @@ -93,6 +92,7 @@ type Context struct {
muxVarCache map[string]string

templateVariables map[string]any
templateName string
}

func (c *Context) initMuxVars() {
Expand Down Expand Up @@ -124,6 +124,7 @@ func (c *Context) reset() {
c.sameSite = 0
c.muxVarCache = nil
c.templateVariables = nil
c.templateName = ""
*c.params = (*c.params)[:0]
*c.skippedNodes = (*c.skippedNodes)[:0]
}
Expand Down Expand Up @@ -1039,8 +1040,8 @@ func (c *Context) Cookie(name string) (string, error) {
return val, nil
}

// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {
// Render writes the response headers and calls Render to render data.
func (c *Context) Render(code int, r Render) {
c.Status(code)

if !bodyAllowedForStatus(code) {
Expand All @@ -1052,21 +1053,38 @@ func (c *Context) Render(code int, r render.Render) {
if err := r.Render(c.Writer); err != nil {
// Pushing error to c.Errors
_ = c.Error(err)
c.Abort()
log.Println("render failed:" + err.Error())
c.AbortWithError(400, err)
}
}

// HTML renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html".
// See http://golang.org/doc/articles/wiki/
func (c *Context) HTML(ctx *render.Context) {
func (c *Context) HTML(tpl ...string) {
rc := RenderContext{
GinContext: c,
Name: c.templateName,
DataMap: c.templateVariables,
}
if len(tpl) > 0 {
rc.Name = tpl[0]
}
code := 200
instance := c.engine.HTMLRender.Instance(ctx)
instance := c.engine.HTMLRender.Instance(&rc)
c.Render(code, instance)
}

func (c *Context) HTMLWithCode(code int, ctx *render.Context) {
instance := c.engine.HTMLRender.Instance(ctx)
func (c *Context) HTMLWithCode(code int, tpl ...string) {
rc := RenderContext{
GinContext: c,
Name: c.templateName,
DataMap: c.templateVariables,
}
if len(tpl) > 0 {
rc.Name = tpl[0]
}
instance := c.engine.HTMLRender.Instance(&rc)
c.Render(code, instance)
}

Expand All @@ -1075,14 +1093,14 @@ func (c *Context) HTMLWithCode(code int, ctx *render.Context) {
// WARNING: we recommend using this only for development purposes since printing pretty JSON is
// more CPU and bandwidth consuming. Use Context.JSON() instead.
func (c *Context) IndentedJSON(code int, obj any) {
c.Render(code, render.IndentedJSON{Data: obj})
c.Render(code, IndentedJSON{Data: obj})
}

// SecureJSON serializes the given struct as Secure JSON into the response body.
// Default prepends "while(1)," to response body if the given struct is array values.
// It also sets the Content-Type as "application/json".
func (c *Context) SecureJSON(code int, obj any) {
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
c.Render(code, SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
}

// JSONP serializes the given struct as JSON into the response body.
Expand All @@ -1091,59 +1109,59 @@ func (c *Context) SecureJSON(code int, obj any) {
func (c *Context) JSONP(code int, obj any) {
callback := c.DefaultQuery("callback", "")
if callback == "" {
c.Render(code, render.JSON{Data: obj})
c.Render(code, JSON{Data: obj})
return
}
c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
c.Render(code, JsonpJSON{Callback: callback, Data: obj})
}

// JSON serializes the given struct as JSON into the response body.
// It also sets the Content-Type as "application/json".
func (c *Context) JSON(code int, obj any) {
c.Render(code, render.JSON{Data: obj})
c.Render(code, JSON{Data: obj})
}

// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
// It also sets the Content-Type as "application/json".
func (c *Context) AsciiJSON(code int, obj any) {
c.Render(code, render.AsciiJSON{Data: obj})
c.Render(code, AsciiJSON{Data: obj})
}

// PureJSON serializes the given struct as JSON into the response body.
// PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
func (c *Context) PureJSON(code int, obj any) {
c.Render(code, render.PureJSON{Data: obj})
c.Render(code, PureJSON{Data: obj})
}

// XML serializes the given struct as XML into the response body.
// It also sets the Content-Type as "application/xml".
func (c *Context) XML(code int, obj any) {
c.Render(code, render.XML{Data: obj})
c.Render(code, XML{Data: obj})
}

// YAML serializes the given struct as YAML into the response body.
func (c *Context) YAML(code int, obj any) {
c.Render(code, render.YAML{Data: obj})
c.Render(code, YAML{Data: obj})
}

// TOML serializes the given struct as TOML into the response body.
func (c *Context) TOML(code int, obj any) {
c.Render(code, render.TOML{Data: obj})
c.Render(code, TOML{Data: obj})
}

// ProtoBuf serializes the given struct as ProtoBuf into the response body.
func (c *Context) ProtoBuf(code int, obj any) {
c.Render(code, render.ProtoBuf{Data: obj})
c.Render(code, ProtoBuf{Data: obj})
}

// String writes the given string into the response body.
func (c *Context) String(code int, format string, values ...any) {
c.Render(code, render.String{Format: format, Data: values})
c.Render(code, String{Format: format, Data: values})
}

// Redirect returns an HTTP redirect to the specific location.
func (c *Context) Redirect(code int, location string) {
c.Render(-1, render.Redirect{
c.Render(-1, Redirect{
Code: code,
Location: location,
Request: c.Request,
Expand All @@ -1152,15 +1170,15 @@ func (c *Context) Redirect(code int, location string) {

// Data writes some data into the body stream and updates the HTTP code.
func (c *Context) Data(code int, contentType string, data []byte) {
c.Render(code, render.Data{
c.Render(code, Data{
ContentType: contentType,
Data: data,
})
}

// DataFromReader writes the specified reader into the body stream and updates the HTTP code.
func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) {
c.Render(code, render.Reader{
c.Render(code, Reader{
Headers: extraHeaders,
ContentType: contentType,
ContentLength: contentLength,
Expand Down Expand Up @@ -1285,6 +1303,10 @@ func (c *Context) AssignH(h H) {
}
}

func (c *Context) SetView(s string) {
c.templateName = s
}

func (c *Context) Assign(key string, v any) {
if c.templateVariables == nil {
c.templateVariables = make(map[string]any)
Expand Down
2 changes: 1 addition & 1 deletion render/data.go → data.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package render
package gin

import "net/http"

Expand Down
53 changes: 1 addition & 52 deletions gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"sync"

"github.com/make-money-fast/gin/internal/bytesconv"
"github.com/make-money-fast/gin/render"

"github.com/quic-go/quic-go/http3"
"golang.org/x/net/http2"
Expand Down Expand Up @@ -167,9 +166,8 @@ type Engine struct {
// ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.
ContextWithFallback bool

delims render.Delims
secureJSONPrefix string
HTMLRender render.HTMLRender
HTMLRender HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
Expand Down Expand Up @@ -213,7 +211,6 @@ func New(opts ...OptionFunc) *Engine {
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJSONPrefix: "while(1);",
trustedProxies: []string{"0.0.0.0/0", "::/0"},
trustedCIDRs: defaultTrustedCIDRs,
Expand Down Expand Up @@ -249,60 +246,12 @@ func (engine *Engine) allocateContext(maxParams uint16) *Context {
return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes}
}

// Delims sets template left and right delims and returns an Engine instance.
func (engine *Engine) Delims(left, right string) *Engine {
engine.delims = render.Delims{Left: left, Right: right}
return engine
}

// SecureJsonPrefix sets the secureJSONPrefix used in Context.SecureJSON.
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
engine.secureJSONPrefix = prefix
return engine
}

// LoadHTMLGlob loads HTML files identified by glob pattern
// and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLGlob(pattern string) {
left := engine.delims.Left
right := engine.delims.Right
templ := template.Must(template.New("").Delims(left, right).Funcs(engine.FuncMap).ParseGlob(pattern))

if IsDebugging() {
debugPrintLoadTemplate(templ)
engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
return
}

engine.SetHTMLTemplate(templ)
}

// LoadHTMLFiles loads a slice of HTML files
// and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLFiles(files ...string) {
if IsDebugging() {
engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
return
}

templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
engine.SetHTMLTemplate(templ)
}

// SetHTMLTemplate associate a template with HTML renderer.
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
if len(engine.trees) > 0 {
debugPrintWARNINGSetHTMLTemplate()
}

engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
}

// SetFuncMap sets the FuncMap used for template.FuncMap.
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
engine.FuncMap = funcMap
}

// NoRoute adds handlers for NoRoute. It returns a 404 code by default.
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
engine.noRoute = handlers
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang-module/carbon/v2 v2.4.1 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/kr/pretty v0.3.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-module/carbon/v2 v2.4.1 h1:cYUD8T+rHeX+qIybGYpnJ8I90F10dvyEF67VNOO+zZM=
github.com/golang-module/carbon/v2 v2.4.1/go.mod h1:1jP9AZ4k2+lmfgY/wZgmtsN52VcHC5YuPM6varKDTkM=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
Expand Down
17 changes: 17 additions & 0 deletions html.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package gin

type RenderContext struct {
GinContext *Context
Name string
DataMap map[string]any
}

// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
type HTMLRender interface {
// Instance returns an HTML instance.
Instance(*RenderContext) Render
}
Loading

0 comments on commit cdfaa60

Please sign in to comment.