diff --git a/.gitignore b/.gitignore
index 5292519..f30e625 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-logs/
\ No newline at end of file
+logs/
+.idea/
\ No newline at end of file
diff --git a/example/main.go b/example/main.go
deleted file mode 100644
index 949ba36..0000000
--- a/example/main.go
+++ /dev/null
@@ -1,117 +0,0 @@
-package main
-
-import (
- "log/slog"
-
- "ron"
-)
-
-type SomethingElements struct {
- Name string
- Description string
-}
-
-var elements = []SomethingElements{
- {"element 1", "description 1"},
- {"element 2", "description 2"},
- {"element 3", "description 3"},
- {"element 4", "description 4"},
- {"element 5", "description 5"},
- {"element 6", "description 6"},
- {"element 7", "description 7"},
- {"element 8", "description 8"},
- {"element 9", "description 9"},
- {"element 10", "description 10"},
- {"element 11", "description 11"},
- {"element 12", "description 12"},
- {"element 13", "description 13"},
- {"element 14", "description 14"},
- {"element 15", "description 15"},
- {"element 16", "description 16"},
- {"element 17", "description 17"},
- {"element 18", "description 18"},
- {"element 19", "description 19"},
- {"element 20", "description 20"},
- {"element 21", "description 21"},
- {"element 22", "description 22"},
- {"element 23", "description 23"},
- {"element 24", "description 24"},
- {"element 25", "description 25"},
- {"element 26", "description 26"},
- {"element 27", "description 27"},
- {"element 28", "description 28"},
- {"element 29", "description 29"},
- {"element 30", "description 30"},
- {"element 31", "description 31"},
- {"element 32", "description 32"},
- {"element 33", "description 33"},
- {"element 34", "description 34"},
- {"element 35", "description 35"},
- {"element 36", "description 36"},
- {"element 37", "description 37"},
- {"element 38", "description 38"},
- {"element 39", "description 39"},
- {"element 40", "description 40"},
- {"element 41", "description 41"},
- {"element 42", "description 42"},
- {"element 43", "description 43"},
- {"element 44", "description 44"},
- {"element 45", "description 45"},
- {"element 46", "description 46"},
- {"element 47", "description 47"},
- {"element 48", "description 48"},
- {"element 49", "description 49"},
-}
-
-func main() {
- r := ron.New(func(e *ron.Engine) {
- e.LogLevel = slog.LevelDebug
- })
-
- htmlRender := ron.NewHTMLRender()
- r.Render = htmlRender
-
- r.Static("static", "static")
-
- r.GET("/json", helloWorldJSON)
- r.POST("/another", anotherHelloWorld)
- r.GET("/html", helloWorldHTML)
- r.GET("/component", componentHTML)
-
- r.Run(":8080")
-}
-
-func helloWorld(c *ron.Context) {
- slog.Info("Dummy info message")
- c.W.Write([]byte("hello world"))
-}
-
-func anotherHelloWorld(c *ron.Context) {
- c.W.Write([]byte("another hello world"))
-}
-
-func helloWorldJSON(c *ron.Context) {
- c.JSON(200, ron.Data{"message": "hello world"})
-}
-
-func helloWorldHTML(c *ron.Context) {
-
- pages := ron.Pages{
- TotalElements: len(elements),
- ElementsPerPage: 5,
- }
-
- pages.PaginationParams(c.R)
- elementsPaginated := pages.PaginateArray(elements)
-
- td := &ron.TemplateData{
- Data: ron.Data{"title": "hello world", "message": "hello world from html", "elements": elementsPaginated},
- Pages: pages,
- }
-
- c.HTML(200, "page.index.gohtml", td)
-}
-
-func componentHTML(c *ron.Context) {
- c.HTML(200, "component.list.gohtml", nil)
-}
diff --git a/example/static/img/dummy.png b/example/static/img/dummy.png
deleted file mode 100644
index 885878c..0000000
Binary files a/example/static/img/dummy.png and /dev/null differ
diff --git a/example/templates/component.list.gohtml b/example/templates/component.list.gohtml
deleted file mode 100644
index 342f35f..0000000
--- a/example/templates/component.list.gohtml
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/example/templates/page.index.gohtml b/example/templates/page.index.gohtml
deleted file mode 100644
index f1eef8a..0000000
--- a/example/templates/page.index.gohtml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
- {{ .Data.title }}
-
-
-
-
-
-
-{{ .Data.message }}
-
-{{ range .Data.elements }}
-
-
- -
- {{ .Name }}
- {{ .Description }}
-
-
-
-{{ end }}
-
-{{ if not .Pages.HasPrevious }}
-primera página
-{{ else }}
-primera
-anterior
-{{ end }}
-
-{{ range .Pages.PageRange 5 }}
-{{ if .Active }}
-{{ .Number }}
-{{ else }}
-{{ .Number }}
-{{ end }}
-{{ end }}
-
-{{ if not .Pages.HasNext }}
-última página
-{{ else }}
-siguiente
-última
-{{ end }}
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ron.go b/ron.go
index aeea964..75fcb78 100644
--- a/ron.go
+++ b/ron.go
@@ -24,10 +24,27 @@ type (
E *Engine
}
+ router struct {
+ path string
+ method string
+ handler func(*Context)
+ group *routerGroup
+ }
+
+ routerGroup struct {
+ prefix string
+ middlewares middlewareChain
+ engine *Engine
+ }
+
+ middlewareChain []func(http.Handler) http.Handler
+
Engine struct {
- mux *http.ServeMux
- LogLevel slog.Level
- Render *Render
+ LogLevel slog.Level
+ Render *Render
+ mux *http.ServeMux
+ middlewares middlewareChain
+ router []router
}
)
@@ -53,8 +70,32 @@ func (e *Engine) apply(opts ...EngineOptions) *Engine {
return e
}
+func (e *Engine) applyMiddlewares(handler http.Handler) http.Handler {
+ for i := len(e.middlewares) - 1; i >= 0; i-- {
+ handler = e.middlewares[i](handler)
+ }
+ return handler
+}
+
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- e.handleRequest(w, r)
+ handler := e.applyMiddlewares(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ for _, route := range e.router {
+ if strings.HasPrefix(r.URL.Path, route.path) && r.Method == route.method {
+ groupHandler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ route.handler(&Context{W: w, R: r, E: e})
+ }))
+ if route.group != nil {
+ for i := len(route.group.middlewares) - 1; i >= 0; i-- {
+ groupHandler = route.group.middlewares[i](groupHandler)
+ }
+ }
+ groupHandler.ServeHTTP(w, r)
+ return
+ }
+ }
+ http.NotFound(w, r)
+ }))
+ handler.ServeHTTP(w, r)
}
func (e *Engine) Run(addr string) error {
@@ -66,24 +107,35 @@ func (e *Engine) handleRequest(w http.ResponseWriter, r *http.Request) {
e.mux.ServeHTTP(w, r)
}
+func (e *Engine) USE(middleware ...func(http.Handler) http.Handler) {
+ e.middlewares = append(e.middlewares, middleware...)
+}
+
func (e *Engine) GET(path string, handler func(*Context)) {
- e.mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
- return
- }
- handler(&Context{W: w, R: r, E: e})
- })
+ e.router = append(e.router, router{path: path, method: http.MethodGet, handler: handler})
}
func (e *Engine) POST(path string, handler func(*Context)) {
- e.mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodPost {
- http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
- return
- }
- handler(&Context{W: w, R: r, E: e})
- })
+ e.router = append(e.router, router{path: path, method: http.MethodPost, handler: handler})
+}
+
+func (e *Engine) GROUP(prefix string) *routerGroup {
+ return &routerGroup{
+ prefix: prefix,
+ engine: e,
+ }
+}
+
+func (rg *routerGroup) USE(middleware ...func(http.Handler) http.Handler) {
+ rg.middlewares = append(rg.middlewares, middleware...)
+}
+
+func (rg *routerGroup) GET(path string, handler func(*Context)) {
+ rg.engine.router = append(rg.engine.router, router{path: rg.prefix + path, method: http.MethodGet, handler: handler, group: rg})
+}
+
+func (rg *routerGroup) POST(path string, handler func(*Context)) {
+ rg.engine.router = append(rg.engine.router, router{path: rg.prefix + path, method: http.MethodPost, handler: handler, group: rg})
}
// Static serves static files from a specified directory, accessible through a defined URL path.
@@ -107,7 +159,9 @@ func (e *Engine) Static(path, dir string) {
}
fs := http.FileServer(http.Dir(dir))
- e.mux.Handle(path, http.StripPrefix(path, fs))
+ e.GET(path, func(c *Context) {
+ http.StripPrefix(path, fs).ServeHTTP(c.W, c.R)
+ })
slog.Info("Static files served", "path", path, "dir", dir)
}
diff --git a/ron_test.go b/ron_test.go
index 05593f1..7b25e30 100644
--- a/ron_test.go
+++ b/ron_test.go
@@ -10,14 +10,14 @@ import (
func Test_defaultEngine(t *testing.T) {
e := defaultEngine()
if e == nil {
- t.Error("Expected Engine, Actual: nil")
+ t.Error("Expected engine, Actual: nil")
}
}
func Test_New(t *testing.T) {
e := New()
if e == nil {
- t.Error("Expected Engine, Actual: nil")
+ t.Error("Expected engine, Actual: nil")
}
if e.Render != nil {
t.Error("No expected Renderer, Actual: Renderer")
@@ -88,6 +88,96 @@ func Test_POST(t *testing.T) {
}
}
+func Test_USE(t *testing.T) {
+ e := New()
+ e.USE(func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("MIDDLEWARE"))
+ next.ServeHTTP(w, r)
+ })
+ })
+ e.GET("/", func(c *Context) {
+ c.W.WriteHeader(http.StatusOK)
+ c.W.Write([]byte("GET"))
+ })
+
+ rr := httptest.NewRecorder()
+ req, _ := http.NewRequest("GET", "/", nil)
+ e.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ t.Errorf("Expected status code: %d, Actual: %d", http.StatusOK, status)
+ }
+
+ if rr.Body.String() != "MIDDLEWAREGET" {
+ t.Errorf("Expected: MIDDLEWAREGET, Actual: %s", rr.Body.String())
+ }
+}
+
+func Test_GROUP(t *testing.T) {
+ tests := []struct {
+ method string
+ path string
+ expectedCode int
+ expectedBody string
+ }{
+ {"GET", "/group/", http.StatusOK, "GET"},
+ {"POST", "/group/", http.StatusOK, "POST"},
+ }
+
+ e := New()
+ g := e.GROUP("/group")
+ g.GET("/", func(c *Context) {
+ c.W.WriteHeader(http.StatusOK)
+ c.W.Write([]byte("GET"))
+ })
+ g.POST("/", func(c *Context) {
+ c.W.WriteHeader(http.StatusOK)
+ c.W.Write([]byte("POST"))
+ })
+
+ for _, tt := range tests {
+ rr := httptest.NewRecorder()
+ req, _ := http.NewRequest(tt.method, tt.path, nil)
+ e.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != tt.expectedCode {
+ t.Errorf("Expected status code: %d, Actual: %d", tt.expectedCode, status)
+ }
+
+ if rr.Body.String() != tt.expectedBody {
+ t.Errorf("Expected: %s, Actual: %s", tt.expectedBody, rr.Body.String())
+ }
+ }
+}
+
+func Test_GROUPUSE(t *testing.T) {
+ e := New()
+ g := e.GROUP("/group")
+ g.USE(func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("MIDDLEWARE"))
+ next.ServeHTTP(w, r)
+ })
+ })
+ g.GET("/", func(c *Context) {
+ c.W.WriteHeader(http.StatusOK)
+ c.W.Write([]byte("GET"))
+ })
+
+ rr := httptest.NewRecorder()
+ req, _ := http.NewRequest("GET", "/group/", nil)
+ e.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ t.Errorf("Expected status code: %d, Actual: %d", http.StatusOK, status)
+ }
+
+ if rr.Body.String() != "MIDDLEWAREGET" {
+ t.Errorf("Expected: MIDDLEWAREGET, Actual: %s", rr.Body.String())
+ }
+}
+
func Test_Static(t *testing.T) {
os.Mkdir("assets", os.ModePerm)
f, _ := os.Create("assets/style.css")