~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.


edfb5a74 Peter John

tag: v0.4.2tag: v0.4.1

v0.4.2 v0.4.1

4 years ago
add wapp context
Files changed (2) hide show
  1. cmd/main.go +0 -1
  2. context.go +111 -0
cmd/main.go CHANGED
@@ -147,7 +147,6 @@ func main() {
147
147
  isLambda := os.Getenv("_LAMBDA_SERVER_PORT") != ""
148
148
  r := mux.NewRouter()
149
149
  r.PathPrefix("/assets/").Handler(http.FileServer(http.FS(assetsFS)))
150
-
151
150
  {{#each routes as |route| }}r.HandleFunc("{{ route.Path }}", context.Wrap({{ route.Pkg }}.{{ route.Method }})).Methods("{{ route.Method }}")
152
151
  {{/each}}
153
152
  if !isLambda {
context.go ADDED
@@ -0,0 +1,111 @@
1
+ package wapp
2
+
3
+ import (
4
+ "bytes"
5
+ c "context"
6
+ "encoding/json"
7
+ "net/http"
8
+ "reflect"
9
+
10
+ "github.com/gobuffalo/velvet"
11
+ "github.com/gorilla/mux"
12
+ )
13
+
14
+ type WappContext struct {
15
+ c.Context
16
+ PathParams map[string]string
17
+ JS *bytes.Buffer
18
+ CSS *bytes.Buffer
19
+ }
20
+
21
+ func NewWappContext(r *http.Request) (WappContext, error) {
22
+ pathParams := mux.Vars(r)
23
+ return WappContext{
24
+ Context: r.Context(),
25
+ PathParams: pathParams,
26
+ JS: bytes.NewBuffer(nil),
27
+ CSS: bytes.NewBuffer(nil),
28
+ }, nil
29
+ }
30
+
31
+ func (r WappContext) UseData(name string, data map[string]interface{}) {
32
+ v := velvet.NewContext()
33
+ v.Set("name", name)
34
+ generatedMap := map[string]interface{}{}
35
+ for k, v := range data {
36
+ if reflect.TypeOf(v).Kind() == reflect.Func {
37
+ f := v.(func() string)
38
+ generatedMap[k] = "() {" + f() + "}"
39
+ } else {
40
+ generatedMap[k+":"] = v
41
+ }
42
+ }
43
+ v.Set("data", generatedMap)
44
+ s, err := velvet.Render(`
45
+ Alpine.data('{{ name }}', () => {
46
+ return {
47
+ {{#each data}}
48
+ {{ @key }}{{ @value }},
49
+ {{/each}}
50
+ };
51
+ });
52
+ `, v)
53
+ if err != nil {
54
+ panic(err)
55
+ }
56
+ r.JS.WriteString(s)
57
+ }
58
+
59
+ func RespondError(w http.ResponseWriter, status int, err error) {
60
+ w.WriteHeader(status)
61
+ w.Header().Set("Content-Type", "application/json")
62
+ data, _ := json.Marshal(map[string]string{
63
+ "error": err.Error(),
64
+ })
65
+ w.Write(data)
66
+ }
67
+
68
+ func PerformRequest(h interface{}, ctx interface{}, w http.ResponseWriter, r *http.Request) {
69
+ args := []reflect.Value{reflect.ValueOf(ctx)}
70
+ funcType := reflect.TypeOf(h)
71
+ icount := funcType.NumIn()
72
+ if icount == 2 {
73
+ structType := funcType.In(1)
74
+ instance := reflect.New(structType)
75
+ if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" {
76
+ err := json.NewDecoder(r.Body).Decode(instance.Interface())
77
+ if err != nil {
78
+ RespondError(w, 500, err)
79
+ return
80
+ }
81
+ } else if r.Method == "GET" {
82
+ rv := instance.Elem()
83
+ for i := 0; i < structType.NumField(); i++ {
84
+ if f := rv.Field(i); f.CanSet() {
85
+ jsonName := structType.Field(i).Tag.Get("json")
86
+ jsonValue := r.URL.Query().Get(jsonName)
87
+ f.SetString(jsonValue)
88
+ }
89
+ }
90
+ }
91
+ args = append(args, instance.Elem())
92
+ }
93
+ values := reflect.ValueOf(h).Call(args)
94
+ response := values[0].Interface()
95
+ responseStatus := values[1].Interface().(int)
96
+ responseError := values[2].Interface()
97
+ if responseError != nil {
98
+ RespondError(w, responseStatus, responseError.(error))
99
+ return
100
+ }
101
+ if v, ok := response.(*Element); ok {
102
+ w.WriteHeader(responseStatus)
103
+ w.Header().Set("Content-Type", "text/html")
104
+ v.WriteHtml(w)
105
+ return
106
+ }
107
+ w.WriteHeader(responseStatus)
108
+ w.Header().Set("Content-Type", "application/json")
109
+ data, _ := json.Marshal(response)
110
+ w.Write(data)
111
+ }