Skip to content

Commit

Permalink
Move OnlyHTMLFS to internal and add test
Browse files Browse the repository at this point in the history
  • Loading branch information
sunshineplan committed Sep 10, 2024
1 parent e21fcce commit eb4a5e1
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 78 deletions.
2 changes: 2 additions & 0 deletions docs/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,8 @@ func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
//router.LoadHTMLFS(http.Dir("templates"), "template1.html", "template2.html")
//or
//router.LoadHTMLFS(http.FS(templates), "templates/template1.html", "templates/template2.html")
router.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
Expand Down
17 changes: 0 additions & 17 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package gin

import (
"io/fs"
"net/http"
"os"
)
Expand All @@ -26,22 +25,6 @@ func (o OnlyFilesFS) Open(name string) (http.File, error) {
return neutralizedReaddirFile{f}, nil
}

// OnlyHTMLFS implements an [fs.FS].
type OnlyHTMLFS struct {
FileSystem http.FileSystem
}

// Open passes `Open` to the upstream implementation and return an [fs.File].
func (o OnlyHTMLFS) Open(name string) (fs.File, error) {
f, err := o.FileSystem.Open(name)

if err != nil {
return nil, err
}

return fs.File(f), nil
}

// neutralizedReaddirFile wraps http.File with a specific implementation of `Readdir`.
type neutralizedReaddirFile struct {
http.File
Expand Down
30 changes: 0 additions & 30 deletions fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,36 +48,6 @@ func TestOnlyFilesFS_Open_err(t *testing.T) {
assert.Nil(t, file)
}

func TestOnlyHTMLFS_Open(t *testing.T) {
var testFile *os.File
mockFS := &mockFileSystem{
open: func(name string) (http.File, error) {
return testFile, nil
},
}
fs := &OnlyHTMLFS{FileSystem: mockFS}

file, err := fs.Open("foo")

require.NoError(t, err)
assert.Equal(t, testFile, file)
}

func TestOnlyHTMLFS_Open_err(t *testing.T) {
testError := errors.New("mock")
mockFS := &mockFileSystem{
open: func(_ string) (http.File, error) {
return nil, testError
},
}
fs := &OnlyHTMLFS{FileSystem: mockFS}

file, err := fs.Open("foo")

require.ErrorIs(t, err, testError)
assert.Nil(t, file)
}

func Test_neuteredReaddirFile_Readdir(t *testing.T) {
n := neutralizedReaddirFile{}

Expand Down
6 changes: 4 additions & 2 deletions gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"sync"

"github.com/gin-gonic/gin/internal/bytesconv"
filesystem "github.com/gin-gonic/gin/internal/fs"
"github.com/gin-gonic/gin/render"

"github.com/quic-go/quic-go/http3"
Expand Down Expand Up @@ -289,11 +290,12 @@ func (engine *Engine) LoadHTMLFiles(files ...string) {
// and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLFS(fs http.FileSystem, patterns ...string) {
if IsDebugging() {
engine.HTMLRender = render.HTMLDebug{FS: OnlyHTMLFS{fs}, Patterns: patterns, FuncMap: engine.FuncMap, Delims: engine.delims}
engine.HTMLRender = render.HTMLDebug{FileSystem: fs, Patterns: patterns, FuncMap: engine.FuncMap, Delims: engine.delims}
return
}

templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFS(OnlyHTMLFS{fs}, patterns...))
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFS(
filesystem.FileSystem{FileSystem: fs}, patterns...))
engine.SetHTMLTemplate(templ)
}

Expand Down
14 changes: 6 additions & 8 deletions gin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package gin

import (
"crypto/tls"
"embed"
"fmt"
"html/template"
"io"
Expand Down Expand Up @@ -326,16 +325,15 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
assert.Equal(t, "Date: 2017/07/01", string(resp))
}

//go:embed testdata/template/*.tmpl
var tmplFS embed.FS
var tmplFS = http.Dir("testdata/template")

func TestLoadHTMLFSTestMode(t *testing.T) {
ts := setupHTMLFiles(
t,
TestMode,
false,
func(router *Engine) {
router.LoadHTMLFS(http.FS(tmplFS), "testdata/template/hello.tmpl", "testdata/template/raw.tmpl")
router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl")
},
)
defer ts.Close()
Expand All @@ -355,7 +353,7 @@ func TestLoadHTMLFSDebugMode(t *testing.T) {
DebugMode,
false,
func(router *Engine) {
router.LoadHTMLFS(http.FS(tmplFS), "testdata/template/hello.tmpl", "testdata/template/raw.tmpl")
router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl")
},
)
defer ts.Close()
Expand All @@ -375,7 +373,7 @@ func TestLoadHTMLFSReleaseMode(t *testing.T) {
ReleaseMode,
false,
func(router *Engine) {
router.LoadHTMLFS(http.FS(tmplFS), "testdata/template/hello.tmpl", "testdata/template/raw.tmpl")
router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl")
},
)
defer ts.Close()
Expand All @@ -395,7 +393,7 @@ func TestLoadHTMLFSUsingTLS(t *testing.T) {
TestMode,
true,
func(router *Engine) {
router.LoadHTMLFS(http.FS(tmplFS), "testdata/template/hello.tmpl", "testdata/template/raw.tmpl")
router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl")
},
)
defer ts.Close()
Expand All @@ -422,7 +420,7 @@ func TestLoadHTMLFSFuncMap(t *testing.T) {
TestMode,
false,
func(router *Engine) {
router.LoadHTMLFS(http.FS(tmplFS), "testdata/template/hello.tmpl", "testdata/template/raw.tmpl")
router.LoadHTMLFS(tmplFS, "hello.tmpl", "raw.tmpl")
},
)
defer ts.Close()
Expand Down
22 changes: 22 additions & 0 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package fs

import (
"io/fs"
"net/http"
)

// FileSystem implements an [fs.FS].
type FileSystem struct {
http.FileSystem
}

// Open passes `Open` to the upstream implementation and return an [fs.File].
func (o FileSystem) Open(name string) (fs.File, error) {
f, err := o.FileSystem.Open(name)

if err != nil {
return nil, err
}

return fs.File(f), nil
}
49 changes: 49 additions & 0 deletions internal/fs/fs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package fs

import (
"errors"
"net/http"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type mockFileSystem struct {
open func(name string) (http.File, error)
}

func (m *mockFileSystem) Open(name string) (http.File, error) {
return m.open(name)
}

func TesFileSystem_Open(t *testing.T) {
var testFile *os.File
mockFS := &mockFileSystem{
open: func(name string) (http.File, error) {
return testFile, nil
},
}
fs := &FileSystem{mockFS}

file, err := fs.Open("foo")

require.NoError(t, err)
assert.Equal(t, testFile, file)
}

func TestFileSystem_Open_err(t *testing.T) {
testError := errors.New("mock")
mockFS := &mockFileSystem{
open: func(_ string) (http.File, error) {
return nil, testError
},
}
fs := &FileSystem{mockFS}

file, err := fs.Open("foo")

require.ErrorIs(t, err, testError)
assert.Nil(t, file)
}
20 changes: 11 additions & 9 deletions render/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ package render

import (
"html/template"
"io/fs"
"net/http"

"github.com/gin-gonic/gin/internal/fs"
)

// Delims represents a set of Left and Right delimiters for HTML template rendering.
Expand All @@ -32,12 +33,12 @@ type HTMLProduction struct {

// HTMLDebug contains template delims and pattern and function with file list.
type HTMLDebug struct {
Files []string
Glob string
FS fs.FS
Patterns []string
Delims Delims
FuncMap template.FuncMap
Files []string
Glob string
FileSystem http.FileSystem
Patterns []string
Delims Delims
FuncMap template.FuncMap
}

// HTML contains template reference and its name with given interface object.
Expand Down Expand Up @@ -76,8 +77,9 @@ func (r HTMLDebug) loadTemplate() *template.Template {
if r.Glob != "" {
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob))
}
if r.FS != nil && len(r.Patterns) > 0 {
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFS(r.FS, r.Patterns...))
if r.FileSystem != nil && len(r.Patterns) > 0 {
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFS(
fs.FileSystem{FileSystem: r.FileSystem}, r.Patterns...))
}
panic("the HTML debug render was created without files or glob pattern or file system with patterns")
}
Expand Down
51 changes: 39 additions & 12 deletions render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,10 +489,12 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) {
func TestRenderHTMLDebugFiles(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{
Files: []string{"../testdata/template/hello.tmpl"},
Glob: "",
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
Files: []string{"../testdata/template/hello.tmpl"},
Glob: "",
FileSystem: nil,
Patterns: nil,
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
instance := htmlRender.Instance("hello.tmpl", map[string]any{
"name": "thinkerou",
Expand All @@ -508,10 +510,33 @@ func TestRenderHTMLDebugFiles(t *testing.T) {
func TestRenderHTMLDebugGlob(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{
Files: nil,
Glob: "../testdata/template/hello*",
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
Files: nil,
Glob: "../testdata/template/hello*",
FileSystem: nil,
Patterns: nil,
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
instance := htmlRender.Instance("hello.tmpl", map[string]any{
"name": "thinkerou",
})

err := instance.Render(w)

require.NoError(t, err)
assert.Equal(t, "<h1>Hello thinkerou</h1>", w.Body.String())
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
}

func TestRenderHTMLDebugFS(t *testing.T) {
w := httptest.NewRecorder()
htmlRender := HTMLDebug{
Files: nil,
Glob: "",
FileSystem: http.Dir("../testdata/template"),
Patterns: []string{"hello.tmpl"},
Delims: Delims{Left: "{[{", Right: "}]}"},
FuncMap: nil,
}
instance := htmlRender.Instance("hello.tmpl", map[string]any{
"name": "thinkerou",
Expand All @@ -526,10 +551,12 @@ func TestRenderHTMLDebugGlob(t *testing.T) {

func TestRenderHTMLDebugPanics(t *testing.T) {
htmlRender := HTMLDebug{
Files: nil,
Glob: "",
Delims: Delims{"{{", "}}"},
FuncMap: nil,
Files: nil,
Glob: "",
FileSystem: nil,
Patterns: nil,
Delims: Delims{"{{", "}}"},
FuncMap: nil,
}
assert.Panics(t, func() { htmlRender.Instance("", nil) })
}
Expand Down

0 comments on commit eb4a5e1

Please sign in to comment.