~repos /gromer

#golang#htmx#ssr

git clone https://pyrossh.dev/repos/gromer.git

gromer is a framework and cli to build multipage web apps in golang using htmx and alpinejs.


4bd0b2cb Peter John

3 years ago
sort routes
cmd/gromer/main.go CHANGED
@@ -8,6 +8,7 @@ import (
8
8
  "log"
9
9
  "os"
10
10
  "path/filepath"
11
+ "sort"
11
12
  "strings"
12
13
  "unicode"
13
14
 
@@ -50,10 +51,6 @@ func getRoute(method, src string) string {
50
51
  return strings.ReplaceAll(src, "/"+strings.ToLower(method)+".go", "")
51
52
  }
52
53
 
53
- func getPackage(src string) string {
54
- return src
55
- }
56
-
57
54
  func rewritePath(route string) string {
58
55
  muxRoute := bytes.NewBuffer(nil)
59
56
  foundStart := false
@@ -87,6 +84,10 @@ func lowerFirst(s string) string {
87
84
  return ""
88
85
  }
89
86
 
87
+ func getPackage(src string) string {
88
+ return src
89
+ }
90
+
90
91
  func main() {
91
92
  moduleName := ""
92
93
  pkgFlag := flag.String("pkg", "", "specify a package name")
@@ -104,7 +105,6 @@ func main() {
104
105
  } else {
105
106
  moduleName = *pkgFlag
106
107
  }
107
- allPkgs := map[string]string{}
108
108
  err := filepath.Walk("pages",
109
109
  func(filesrc string, info os.FileInfo, err error) error {
110
110
  if err != nil {
@@ -115,15 +115,15 @@ func main() {
115
115
  method := getMethod(route)
116
116
  path := getRoute(method, route)
117
117
  pkg := getPackage(path)
118
- allPkgs[pkg] = ""
119
118
  if path == "" { // for index page
120
119
  path = "/"
121
120
  pkg = "pages"
122
121
  }
123
122
  gromer.RouteDefs = append(gromer.RouteDefs, gromer.RouteDefinition{
123
+ Pkg: rewritePkg(pkg),
124
+ PkgPath: getRoute(method, route),
124
- Method: method,
125
+ Method: method,
125
- Path: rewritePath(path),
126
+ Path: rewritePath(path),
126
- Pkg: rewritePkg(pkg),
127
127
  })
128
128
  }
129
129
  return nil
@@ -131,8 +131,11 @@ func main() {
131
131
  if err != nil {
132
132
  log.Fatal(err)
133
133
  }
134
+ sort.Slice(gromer.RouteDefs, func(i, j int) bool {
135
+ return gromer.RouteDefs[i].Path < gromer.RouteDefs[j].Path
136
+ })
134
137
  for _, r := range gromer.RouteDefs {
135
- fmt.Printf("%-6s %s\n", r.Method, r.Path)
138
+ fmt.Printf("%-6s %s %-6s\n", r.Method, r.Path, r.PkgPath)
136
139
  }
137
140
  err = velvet.Helpers.Add("title", func(v string) string {
138
141
  return strings.Title(strings.ToLower(v))
@@ -140,10 +143,18 @@ func main() {
140
143
  if err != nil {
141
144
  log.Fatal(err)
142
145
  }
146
+ hasRouteMap := map[string]bool{}
147
+ routeImports := []gromer.RouteDefinition{}
148
+ for _, v := range gromer.RouteDefs {
149
+ if _, ok := hasRouteMap[v.PkgPath]; !ok {
150
+ routeImports = append(routeImports, v)
151
+ hasRouteMap[v.PkgPath] = true
152
+ }
153
+ }
143
154
  ctx := velvet.NewContext()
144
155
  ctx.Set("moduleName", moduleName)
145
- ctx.Set("allPkgs", allPkgs)
146
156
  ctx.Set("routes", gromer.RouteDefs)
157
+ ctx.Set("routeImports", routeImports)
147
158
  ctx.Set("tick", "`")
148
159
  s, err := velvet.Render(`// Code generated by gromer. DO NOT EDIT.
149
160
  package main
@@ -157,7 +168,7 @@ import (
157
168
  "gocloud.dev/server"
158
169
 
159
170
  "{{ moduleName }}/assets"
160
- {{#each allPkgs }}"{{ moduleName }}/pages{{ @key }}"
171
+ {{#each routeImports as |route| }}"{{ moduleName }}/pages{{ route.PkgPath }}"
161
172
  {{/each}}
162
173
  )
163
174
 
@@ -168,10 +179,10 @@ func main() {
168
179
  r.Use(gromer.LogMiddleware)
169
180
  r.NotFoundHandler = gromer.NotFoundHandler
170
181
  gromer.Static(r, "/assets/", assets.FS)
171
- gromer.Handle(r, "GET", "/api", gromer.ApiExplorer, nil)
182
+ gromer.Handle(r, "GET", "/api", gromer.ApiExplorer)
172
- {{#each routes as |route| }}gromer.Handle(r, "{{ route.Method }}", "{{ route.Path }}", {{ route.Pkg }}.{{ route.Method }}, {{ route.Pkg }}.{{ title route.Method }}Params{})
183
+ {{#each routes as |route| }}gromer.Handle(r, "{{ route.Method }}", "{{ route.Path }}", {{ route.Pkg }}.{{ route.Method }})
173
184
  {{/each}}
174
- println("http server listening on http://localhost:"+port)
185
+ log.Info().Msg("http server listening on http://localhost:"+port)
175
186
  srv := server.New(r, nil)
176
187
  if err := srv.ListenAndServe(":"+port); err != nil {
177
188
  log.Fatal().Stack().Err(err).Msg("failed to listen")
example/main.go CHANGED
@@ -10,10 +10,10 @@ import (
10
10
  "gocloud.dev/server"
11
11
 
12
12
  "github.com/pyros2097/gromer/example/assets"
13
- "github.com/pyros2097/gromer/example/pages/api/todos"
14
13
  "github.com/pyros2097/gromer/example/pages"
15
14
  "github.com/pyros2097/gromer/example/pages/about"
16
15
  "github.com/pyros2097/gromer/example/pages/api/recover"
16
+ "github.com/pyros2097/gromer/example/pages/api/todos"
17
17
  "github.com/pyros2097/gromer/example/pages/api/todos/_todoId_"
18
18
 
19
19
  )
@@ -25,17 +25,17 @@ func main() {
25
25
  r.Use(gromer.LogMiddleware)
26
26
  r.NotFoundHandler = gromer.NotFoundHandler
27
27
  gromer.Static(r, "/assets/", assets.FS)
28
- gromer.Handle(r, "GET", "/api", gromer.ApiExplorer, nil)
28
+ gromer.Handle(r, "GET", "/api", gromer.ApiExplorer)
29
+ gromer.Handle(r, "GET", "/", pages.GET)
29
- gromer.Handle(r, "GET", "/about", about.GET, about.GetParams{})
30
+ gromer.Handle(r, "GET", "/about", about.GET)
30
- gromer.Handle(r, "GET", "/api/recover", recover.GET, recover.GetParams{})
31
+ gromer.Handle(r, "GET", "/api/recover", recover.GET)
31
- gromer.Handle(r, "DELETE", "/api/todos/{todoId}", todos_todoId_.DELETE, todos_todoId_.DeleteParams{})
32
- gromer.Handle(r, "GET", "/api/todos/{todoId}", todos_todoId_.GET, todos_todoId_.GetParams{})
33
- gromer.Handle(r, "PUT", "/api/todos/{todoId}", todos_todoId_.PUT, todos_todoId_.PutParams{})
34
- gromer.Handle(r, "GET", "/api/todos", todos.GET, todos.GetParams{})
32
+ gromer.Handle(r, "GET", "/api/todos", todos.GET)
35
- gromer.Handle(r, "POST", "/api/todos", todos.POST, todos.PostParams{})
33
+ gromer.Handle(r, "POST", "/api/todos", todos.POST)
34
+ gromer.Handle(r, "DELETE", "/api/todos/{todoId}", todos_todoId_.DELETE)
36
- gromer.Handle(r, "GET", "/", pages.GET, pages.GetParams{})
35
+ gromer.Handle(r, "GET", "/api/todos/{todoId}", todos_todoId_.GET)
36
+ gromer.Handle(r, "PUT", "/api/todos/{todoId}", todos_todoId_.PUT)
37
37
 
38
- println("http server listening on http://localhost:"+port)
38
+ log.Info().Msg("http server listening on http://localhost:"+port)
39
39
  srv := server.New(r, nil)
40
40
  if err := srv.ListenAndServe(":"+port); err != nil {
41
41
  log.Fatal().Stack().Err(err).Msg("failed to listen")
example/pages/about/get.go CHANGED
@@ -6,8 +6,6 @@ import (
6
6
  . "github.com/pyros2097/gromer"
7
7
  )
8
8
 
9
- type GetParams struct{}
10
-
11
9
  func GET(c context.Context) (HtmlContent, int, error) {
12
10
  return Html(`
13
11
  {{#Page "gromer example"}}
example/pages/api/todos/_todoId_/delete.go CHANGED
@@ -6,8 +6,6 @@ import (
6
6
  "github.com/pyros2097/gromer/example/db"
7
7
  )
8
8
 
9
- type DeleteParams struct{}
10
-
11
9
  func DELETE(ctx context.Context, id string) (string, int, error) {
12
10
  _, status, err := GET(ctx, id, GetParams{})
13
11
  if err != nil {
http.go CHANGED
@@ -31,10 +31,11 @@ var IsCloundRun bool
31
31
  var RouteDefs []RouteDefinition
32
32
 
33
33
  type RouteDefinition struct {
34
+ Pkg string `json:"pkg"`
35
+ PkgPath string `json:"pkgPath"`
34
- Method string `json:"method"`
36
+ Method string `json:"method"`
35
- Pkg string `json:"pkg"`
36
- Path string `json:"path"`
37
+ Path string `json:"path"`
37
- Params interface{} `json:"params"`
38
+ Params interface{} `json:"params"`
38
39
  }
39
40
 
40
41
  type HtmlContent string
@@ -121,6 +122,25 @@ func GetRouteParams(route string) []string {
121
122
  return params
122
123
  }
123
124
 
125
+ func addRouteDef(method, route string, h interface{}) {
126
+ pathParams := GetRouteParams(route)
127
+ var body any = nil
128
+ funcType := reflect.TypeOf(h)
129
+ if funcType.NumIn() > len(pathParams)+1 {
130
+ structType := funcType.In(funcType.NumIn() - 1)
131
+ instance := reflect.New(structType)
132
+ if structType.Kind() != reflect.Struct {
133
+ log.Fatal().Msgf("router '%s' '%s' func final param should be a struct", method, route)
134
+ }
135
+ body = instance.Interface()
136
+ }
137
+ RouteDefs = append(RouteDefs, RouteDefinition{
138
+ Method: method,
139
+ Path: route,
140
+ Params: body,
141
+ })
142
+ }
143
+
124
144
  func PerformRequest(route string, h interface{}, ctx interface{}, w http.ResponseWriter, r *http.Request) {
125
145
  params := GetRouteParams(route)
126
146
  args := []reflect.Value{reflect.ValueOf(ctx)}
@@ -302,8 +322,8 @@ func Static(router *mux.Router, path string, fs embed.FS) {
302
322
  router.PathPrefix(path).Handler(http.StripPrefix(path, WrapCache(http.FileServer(http.FS(fs)))))
303
323
  }
304
324
 
305
- func Handle(router *mux.Router, method, route string, h interface{}, params interface{}) {
325
+ func Handle(router *mux.Router, method, route string, h interface{}) {
306
- RouteDefs = append(RouteDefs, RouteDefinition{method, route, "", params})
326
+ addRouteDef(method, route, h)
307
327
  router.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
308
328
  ctx := context.WithValue(context.WithValue(r.Context(), "url", r.URL), "header", r.Header)
309
329
  PerformRequest(route, h, ctx, w, r)