~ritho/rweb

95c4637cfc652131be67e1c6c572faf943d025a3 — Ritho 10 months ago cdb5891
Update go to 1.20.

Signed-off-by: Ritho <palvarez@ritho.net>
M Makefile => Makefile +2 -2
@@ 1,4 1,4 @@
# Copyright (C) 2021-2022 Pablo Alvarez de Sotomayor Posadillo
# Copyright (C) 2021-2023 Pablo Alvarez de Sotomayor Posadillo
#
#  This file is part of web.
#


@@ 84,7 84,7 @@ install:
deb-dependencies:
	@sudo apt-get install -y debian-archive-keyring build-essential autoconf autotools-dev git-buildpackage dh-make dh-golang golang-go
	@if [ ! -f /usr/bin/go ]; then \
		ln -s /usr/lib/go-1.19/bin/go /usr/bin/go ; \
		ln -s /usr/lib/go-1.20/bin/go /usr/bin/go ; \
	fi
	@if [ ! -f /var/cache/pbuilder/base.cow ]; then \
		DIST=testing git-pbuilder create --mirror=http://ftp.es.debian.org/debian ; \

M go.mod => go.mod +8 -8
@@ 1,11 1,11 @@
module git.sr.ht/~ritho/rweb

go 1.19
go 1.20

require (
	github.com/golang/glog v1.0.0
	github.com/labstack/echo/v4 v4.9.1
	golang.org/x/crypto v0.3.0
	github.com/golang/glog v1.1.1
	github.com/labstack/echo/v4 v4.10.2
	golang.org/x/crypto v0.9.0
	gopkg.in/yaml.v2 v2.4.0
)



@@ 13,11 13,11 @@ require (
	github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
	github.com/labstack/gommon v0.4.0 // indirect
	github.com/mattn/go-colorable v0.1.13 // indirect
	github.com/mattn/go-isatty v0.0.16 // indirect
	github.com/mattn/go-isatty v0.0.18 // indirect
	github.com/valyala/bytebufferpool v1.0.0 // indirect
	github.com/valyala/fasttemplate v1.2.2 // indirect
	golang.org/x/net v0.2.0 // indirect
	golang.org/x/sys v0.3.0 // indirect
	golang.org/x/text v0.5.0 // indirect
	golang.org/x/net v0.10.0 // indirect
	golang.org/x/sys v0.8.0 // indirect
	golang.org/x/text v0.9.0 // indirect
	golang.org/x/time v0.3.0 // indirect
)

M go.sum => go.sum +17 -30
@@ 3,55 3,42 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/labstack/echo/v4 v4.8.0 h1:wdc6yKVaHxkNOEdz4cRZs1pQkwSXPiRjq69yWP4QQS8=
github.com/labstack/echo/v4 v4.8.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw=
github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=


@@ 59,5 46,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

M internal/config/config.go => internal/config/config.go +9 -9
@@ 1,7 1,7 @@
// Package config implements the web configurations.
package config

/* Copyright (C) 2021-2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2021-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 40,14 40,14 @@ type Server struct {

// Section has a specific section configurations.
type Section struct {
	Name        string                 `yaml:"name"`
	Path        string                 `yaml:"path"`
	Type        string                 `yaml:"type"`
	Title       string                 `yaml:"title"`
	Description string                 `yaml:"description"`
	Javascripts []string               `yaml:"javascripts"`
	CSS         []string               `yaml:"css"`
	Extra       map[string]interface{} `yaml:"extra"`
	Name        string         `yaml:"name"`
	Path        string         `yaml:"path"`
	Type        string         `yaml:"type"`
	Title       string         `yaml:"title"`
	Description string         `yaml:"description"`
	Javascripts []string       `yaml:"javascripts"`
	CSS         []string       `yaml:"css"`
	Extra       map[string]any `yaml:"extra"`
}

// Project contains the project information for the list of projects.

M internal/engine/pages/blog/blog.go => internal/engine/pages/blog/blog.go +5 -5
@@ 1,7 1,7 @@
// Package blog contains generates a blog page.
package blog

/* Copyright (C) 2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2022-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 46,7 46,7 @@ type Blog struct {
	content    string
	notFound   string
	name       string
	params     map[string]interface{}
	params     map[string]any
	tmpl       *template.Template
}



@@ 78,9 78,9 @@ func (b *Blog) WithContent(content string) pages.Page {
}

// WithParam add a new parameter to the web page.
func (b *Blog) WithParam(name string, value interface{}) pages.Page {
func (b *Blog) WithParam(name string, value any) pages.Page {
	if b.params == nil {
		b.params = make(map[string]interface{}, 1)
		b.params = make(map[string]any, 1)
	}

	b.params[name] = value


@@ 193,7 193,7 @@ func (b *Blog) Render() (string, error) {
	}

	if b.params == nil {
		b.params = make(map[string]interface{}, 1)
		b.params = make(map[string]any, 1)
	}

	b.params["page"] = content.Content{

M internal/engine/pages/blog/post/post.go => internal/engine/pages/blog/post/post.go +2 -2
@@ 1,7 1,7 @@
// Package post implements the post model.
package post

/* Copyright (C) 2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2022-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 44,7 44,7 @@ func (a *Post) Content() string {
		glog.Errorf("Error rendenring the article: %s", err)
	}

	err = r.Render(&content, "body", map[string]interface{}{
	err = r.Render(&content, "body", map[string]any{
		"body": articleBuffer.String(),
	}, nil)
	if err != nil {

M internal/engine/pages/blog/section/section.go => internal/engine/pages/blog/section/section.go +3 -3
@@ 1,7 1,7 @@
// Package section implements the blog sections for the html template.
package section

/* Copyright (C) 2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2022-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 192,7 192,7 @@ func (s *Section) Content() string {
	}

	r := renderer.New(s.info.assetsPath)
	err := r.Render(&articlesBuffer, "articles", map[string]interface{}{
	err := r.Render(&articlesBuffer, "articles", map[string]any{
		"Articles":   articles,
		"Categories": s.info.categories,
		"Pages":      s.info.pages,


@@ 204,7 204,7 @@ func (s *Section) Content() string {
		log.Printf("Error rendenring the list of articles: %s", err)
	}

	err = r.Render(&content, "body", map[string]interface{}{
	err = r.Render(&content, "body", map[string]any{
		"body": articlesBuffer.String(),
	}, nil)
	if err != nil {

M internal/engine/pages/pages.go => internal/engine/pages/pages.go +2 -2
@@ 1,7 1,7 @@
// Package pages contains all the web page types for the web.
package pages

/* Copyright (C) 2021-2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2021-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 21,6 21,6 @@ package pages
// Page represents a web page to render.
type Page interface {
	WithContent(string) Page
	WithParam(string, interface{}) Page
	WithParam(string, any) Page
	Render() (string, error)
}

M internal/engine/pages/projects/projects.go => internal/engine/pages/projects/projects.go +7 -7
@@ 2,7 2,7 @@
// for the web.
package projects

/* Copyright (C) 2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2022-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 40,7 40,7 @@ type Static struct {
	content    string
	notFound   string
	name       string
	params     map[string]interface{}
	params     map[string]any
	tmpl       *template.Template
}



@@ 71,9 71,9 @@ func (s *Static) WithContent(content string) pages.Page {
}

// WithParam add a new parameter to the web page.
func (s *Static) WithParam(name string, value interface{}) pages.Page {
func (s *Static) WithParam(name string, value any) pages.Page {
	if s.params == nil {
		s.params = make(map[string]interface{}, 1)
		s.params = make(map[string]any, 1)
	}

	s.params[name] = value


@@ 92,7 92,7 @@ func (s *Static) body() (string, error) {
		sections[project.Section] = append(sections[project.Section], project)
	}

	params := map[string]interface{}{
	params := map[string]any{
		"sections": sections,
		"url":      "/" + s.name,
	}


@@ 103,7 103,7 @@ func (s *Static) body() (string, error) {
		p := strings.Split(s.content, "/")
		for i := range s.cfg.Projects {
			if s.cfg.Projects[i].Name == p[1] {
				params = map[string]interface{}{
				params = map[string]any{
					"parent":  "/" + s.name,
					"project": s.cfg.Projects[i],
				}


@@ 137,7 137,7 @@ func (s *Static) Render() (string, error) {
	}

	if s.params == nil {
		s.params = make(map[string]interface{}, 1)
		s.params = make(map[string]any, 1)
	}

	s.params["page"] = content.Content{

M internal/engine/pages/static/static.go => internal/engine/pages/static/static.go +5 -5
@@ 1,7 1,7 @@
// Package static contains a static page for the web.
package static

/* Copyright (C) 2021-2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2021-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 44,7 44,7 @@ type Static struct {
	content    string
	notFound   string
	name       string
	params     map[string]interface{}
	params     map[string]any
	tmpl       *template.Template
}



@@ 75,9 75,9 @@ func (s *Static) WithContent(content string) pages.Page {
}

// WithParam add a new parameter to the web page.
func (s *Static) WithParam(name string, value interface{}) pages.Page {
func (s *Static) WithParam(name string, value any) pages.Page {
	if s.params == nil {
		s.params = make(map[string]interface{}, 1)
		s.params = make(map[string]any, 1)
	}

	s.params[name] = value


@@ 115,7 115,7 @@ func (s *Static) Render() (string, error) {
	}

	if s.params == nil {
		s.params = make(map[string]interface{}, 1)
		s.params = make(map[string]any, 1)
	}

	s.params["page"] = content.Content{

M internal/server/errors/errors.go => internal/server/errors/errors.go +2 -2
@@ 1,7 1,7 @@
// Package errors manage the web error handler.
package errors

/* Copyright (C) 2021-2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2021-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 32,7 32,7 @@ func Handler(err error, c echo.Context) {
		code = he.Code
	}

	rerr := c.Render(code, fmt.Sprintf("%d", code), map[string]interface{}{
	rerr := c.Render(code, fmt.Sprintf("%d", code), map[string]any{
		"title": "Error",
	})
	if rerr != nil {

M internal/server/renderer/renderer.go => internal/server/renderer/renderer.go +3 -3
@@ 1,7 1,7 @@
// Package renderer implements the html renderer.
package renderer

/* Copyright (C) 2021-2022 Pablo Alvarez de Sotomayor Posadillo
/* Copyright (C) 2021-2023 Pablo Alvarez de Sotomayor Posadillo

   This file is part of web.



@@ 45,11 45,11 @@ func New(assetsPath string) echo.Renderer {
}

// Render the templates.
func (r *render) Render(w io.Writer, name string, data interface{},
func (r *render) Render(w io.Writer, name string, data any,
	ec echo.Context,
) error {
	// Add global methods if data is a map
	if viewContext, isMap := data.(map[string]interface{}); isMap && ec != nil {
	if viewContext, isMap := data.(map[string]any); isMap && ec != nil {
		viewContext["reverse"] = ec.Echo().Reverse
	}