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


a5c4f8b2 Peter John

4 years ago
add hooks
Files changed (7) hide show
  1. .snapshots/{TestHtmlPage.html → TestHtml.html} +6 -6
  2. go.mod +1 -0
  3. go.sum +2 -0
  4. hooks.go +31 -0
  5. hooks_test.go +31 -0
  6. html.go +12 -8
  7. html_test.go +69 -70
.snapshots/{TestHtmlPage.html → TestHtml.html} RENAMED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
6
- <meta http-equiv="encoding" content="utf-8">
6
+ <meta content="utf-8" http-equiv="encoding">
7
7
  <title> 123 </title>
8
8
 
9
9
  <meta name="description" content="123">
@@ -11,11 +11,11 @@
11
11
  <meta name="keywords" content="123">
12
12
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover">
13
13
  <link rel="icon" href="/assets/icon.png">
14
- <link rel="apple-touch-icon" href="/assets/icon.png">
14
+ <link href="/assets/icon.png" rel="apple-touch-icon">
15
15
  <link rel="stylesheet" href="/assets/styles.css">
16
16
  <script src="/assets/alpine.js" defer></script>
17
17
 
18
- <meta name="title" content="title">
18
+ <meta content="title" name="title">
19
19
  <style>
20
20
  *, ::before, ::after { box-sizing: border-box; }
21
21
  html { -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; line-height: 1.15; -webkit-text-size-adjust: 100%; }
@@ -89,11 +89,11 @@
89
89
  </div>
90
90
 
91
91
  <div class="flex flex-row justify-center items-center">
92
- <button @click="Decrement"> - </button>
92
+ <button @click="decrement"> - </button>
93
93
 
94
- <div x-text="state.count" class="flex flex-row justify-center items-center m-20 text-5xl"> 4 </div>
94
+ <div class="flex flex-row justify-center items-center m-20 text-5xl" x-text="count"> 4 </div>
95
95
 
96
- <button @click="Increment"> + </button>
96
+ <button @click="increment"> + </button>
97
97
  </div>
98
98
  </div>
99
99
 
go.mod CHANGED
@@ -7,6 +7,7 @@ require (
7
7
  github.com/apex/gateway/v2 v2.0.0
8
8
  github.com/aymerick/raymond v2.0.2+incompatible // indirect
9
9
  github.com/bradleyjkemp/cupaloy v2.3.0+incompatible // indirect
10
+ github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf // indirect
10
11
  github.com/gobuffalo/velvet v0.0.0-20170320144106-d97471bf5d8f // indirect
11
12
  github.com/gorilla/mux v1.8.0
12
13
  github.com/kr/pretty v0.1.0 // indirect
go.sum CHANGED
@@ -18,6 +18,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
18
18
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
19
19
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
20
20
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
21
+ github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf h1:NrF81UtW8gG2LBGkXFQFqlfNnvMt9WdB46sfdJY4oqc=
22
+ github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=
21
23
  github.com/gobuffalo/envy v1.6.5 h1:X3is06x7v0nW2xiy2yFbbIjwHz57CD6z6MkvqULTCm8=
22
24
  github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
23
25
  github.com/gobuffalo/velvet v0.0.0-20170320144106-d97471bf5d8f h1:ddIdPdlkAgKMB0mbkft2LT3oxN1h3MN1fopCFrOgkhY=
hooks.go ADDED
@@ -0,0 +1,31 @@
1
+ package wapp
2
+
3
+ type Context struct {
4
+ index int
5
+ datas []interface{}
6
+ }
7
+
8
+ // Go 1.18 stuff
9
+ // func UseState[T any](ctx *Context , s T) (func() T, func(v T)) {
10
+ // if len(ctx.datas) <= ctx.index + 1 {
11
+ // ctx .datas = append(ctx.datas, s)
12
+ // }
13
+ // return func() T {
14
+ // return ctx.datas[ctx.index].(T)
15
+ // }, func(v T) {
16
+ // ctx.datas[ctx.index] = v
17
+ // }
18
+ // }
19
+
20
+ func UseState(c *Context, s interface{}) (func() interface{}, func(v interface{})) {
21
+ localIndex := c.index
22
+ if len(c.datas) <= c.index+1 {
23
+ c.datas = append(c.datas, s)
24
+ c.index += 1
25
+ }
26
+ return func() interface{} {
27
+ return c.datas[localIndex].(interface{})
28
+ }, func(v interface{}) {
29
+ c.datas[localIndex] = v
30
+ }
31
+ }
hooks_test.go ADDED
@@ -0,0 +1,31 @@
1
+ package wapp
2
+
3
+ import (
4
+ "testing"
5
+
6
+ . "github.com/franela/goblin"
7
+ )
8
+
9
+ func TestHooks(t *testing.T) {
10
+ g := Goblin(t)
11
+ g.Describe("useState", func() {
12
+ ctx := &Context{index: 0, datas: []interface{}{}}
13
+ getValue, setValue := UseState(ctx, 12)
14
+
15
+ g.It("should be initialized ", func() {
16
+ g.Assert(1).Equal(ctx.index)
17
+ g.Assert(1).Equal(len(ctx.datas))
18
+ g.Assert(ctx.datas[0]).Equal(12)
19
+ })
20
+
21
+ g.It("should get value ", func() {
22
+ g.Assert(getValue()).Equal(12)
23
+ })
24
+
25
+ g.It("should set value", func() {
26
+ setValue(15)
27
+ g.Assert(ctx.datas[0]).Equal(15)
28
+ g.Assert(getValue()).Equal(15)
29
+ })
30
+ })
31
+ }
html.go CHANGED
@@ -24,6 +24,16 @@ func mergeAttributes(parent *Element, uis ...interface{}) *Element {
24
24
  switch c := v.(type) {
25
25
  case Attribute:
26
26
  parent.setAttr(c.Key, c.Value)
27
+ case M:
28
+ for k, v := range c {
29
+ if a, ok := v.(string); ok {
30
+ parent.setAttr(k, a)
31
+ } else {
32
+ // store some server state on the component ctx
33
+ // parent.setAttr(k, a)
34
+ }
35
+
36
+ }
27
37
  case *Element:
28
38
  elems = append(elems, c)
29
39
  case nil:
@@ -339,14 +349,8 @@ func Attr(k, v string) Attribute {
339
349
  return Attribute{k, v}
340
350
  }
341
351
 
342
- func GetFunctionName(i interface{}) string {
343
- fnName := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
344
- parts := strings.Split(fnName, ".")
345
- return strings.Replace(parts[len(parts)-1], "-fm", "", 1)
346
- }
347
-
348
- func OnClick(v interface{}) Attribute {
352
+ func OnClick(v string) Attribute {
349
- return Attribute{"@click", GetFunctionName(v)}
353
+ return Attribute{"@click", v}
350
354
  }
351
355
 
352
356
  func ID(v string) Attribute {
html_test.go CHANGED
@@ -6,6 +6,7 @@ import (
6
6
  "testing"
7
7
 
8
8
  "github.com/bradleyjkemp/cupaloy"
9
+ . "github.com/franela/goblin"
9
10
  )
10
11
 
11
12
  func Row(uis ...interface{}) *Element {
@@ -16,24 +17,14 @@ func Col(uis ...interface{}) *Element {
16
17
  return NewElement("div", false, append([]interface{}{Css("flex flex-col justify-center items-center")}, uis...)...)
17
18
  }
18
19
 
20
+ func Counter(c *Context, start int) *Element {
19
- type CounterState struct {
21
+ count, setCount := UseState(c, start)
22
+ increment := func() {
20
- Count int
23
+ setCount(count().(int) + 1)
21
- }
24
+ }
22
-
23
- func Counter(start int) *Element {
25
+ decrement := func() {
24
- c := CounterState{Count: start}
26
+ setCount(count().(int) + 1)
25
- return c.Render()
26
- }
27
+ }
27
-
28
- func (c *CounterState) Increment() {
29
- c.Count += 1
30
- }
31
-
32
- func (c *CounterState) Decrement() {
33
- c.Count -= 1
34
- }
35
-
36
- func (c *CounterState) Render() *Element {
37
28
  return Col(Css("text-3xl text-gray-700"),
38
29
  Row(
39
30
  Row(Css("underline"),
@@ -41,61 +32,69 @@ func (c *CounterState) Render() *Element {
41
32
  ),
42
33
  ),
43
34
  Row(
44
- Button(OnClick(c.Decrement), Text("-")),
35
+ Button(OnClick("decrement"), Text("-")),
45
- Row(Css("m-20 text-5xl"), XText("state.count"),
36
+ Row(Css("m-20 text-5xl"), XText("count"),
46
- Text(strconv.Itoa(c.Count)),
37
+ Text(strconv.Itoa(count().(int))),
47
38
  ),
48
- Button(OnClick(c.Increment), Text("+")),
39
+ Button(OnClick("increment"), Text("+")),
49
40
  ),
41
+ M{
42
+ "count": count,
43
+ "increment": increment,
44
+ "decrement": decrement,
45
+ },
50
46
  )
47
+ // return `
48
+ // <div class="flex flex-col justify-center items-center text-3xl text-gray-700">
49
+ // <div class="flex flex-row justify-center items-center">
50
+ // <div class="flex flex-row justify-center items-center underline">
51
+ // Counter
52
+ // </div>
53
+ // </div>
54
+ // <div class="flex flex-row justify-center items-center">
55
+ // <button class="btn m-20" @click="Increment">
56
+ // -
57
+ // </button>
58
+ // <div class="flex flex-row justify-center items-center m-20 text-8xl">
59
+ // {{ count }}
60
+ // </div>
61
+ // <button class="btn m-20" @click="Decrement">
62
+ // +
63
+ // </button>
64
+ // </div>
65
+ // </div>
66
+ // `
51
67
  }
52
68
 
53
- // func CounterHtml() string {
54
- // return `
55
- // <div class="flex flex-col justify-center items-center text-3xl text-gray-700">
56
- // <div class="flex flex-row justify-center items-center">
57
- // <div class="flex flex-row justify-center items-center underline">
58
- // Counter
59
- // </div>
60
- // </div>
61
- // <div class="flex flex-row justify-center items-center">
62
- // <button class="btn m-20" @click={{actions.Increment}}>
63
- // -
64
- // </button>
65
- // <div class="flex flex-row justify-center items-center m-20 text-8xl">
66
- // {{ state.count }}
67
- // </div>
68
- // <button class="btn m-20" @click={{actions.Decrement}}>
69
- // +
70
- // </button>
71
- // </div>
72
- // </div>
73
- // `
74
- // }
75
-
76
- func TestHtmlPage(t *testing.T) {
69
+ func TestHtml(t *testing.T) {
70
+ g := Goblin(t)
71
+ g.Describe("Html", func() {
72
+ g.It("should match snapshot", func() {
73
+ ctx := &Context{index: 0, datas: []interface{}{}}
77
- b := bytes.NewBuffer(nil)
74
+ b := bytes.NewBuffer(nil)
78
- p := Html(
75
+ p := Html(
79
- Head(
76
+ Head(
80
- Title("123"),
77
+ Title("123"),
81
- Meta("description", "123"),
78
+ Meta("description", "123"),
82
- Meta("author", "123"),
79
+ Meta("author", "123"),
83
- Meta("keywords", "123"),
80
+ Meta("keywords", "123"),
84
- Meta("viewport", "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"),
81
+ Meta("viewport", "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"),
85
- Link("icon", "/assets/icon.png"),
82
+ Link("icon", "/assets/icon.png"),
86
- Link("apple-touch-icon", "/assets/icon.png"),
83
+ Link("apple-touch-icon", "/assets/icon.png"),
87
- Link("stylesheet", "/assets/styles.css"),
84
+ Link("stylesheet", "/assets/styles.css"),
88
- Script(Src("/assets/alpine.js"), Defer()),
85
+ Script(Src("/assets/alpine.js"), Defer()),
89
- Meta("title", "title"),
86
+ Meta("title", "title"),
90
- ),
87
+ ),
91
- Body(
88
+ Body(
92
- H1(Text("Hello this is a h1")),
89
+ H1(Text("Hello this is a h1")),
93
- H2(Text("Hello this is a h2")),
90
+ H2(Text("Hello this is a h2")),
94
- H3(XData("{ message: 'I ❤️ Alpine' }"), XText("message"), Text("")),
91
+ H3(XData("{ message: 'I ❤️ Alpine' }"), XText("message"), Text("")),
95
- Counter(4),
92
+ Counter(ctx, 4),
96
- ),
93
+ ),
97
- )
94
+ )
98
- p.WriteHtml(b)
95
+ p.WriteHtml(b)
99
- c := cupaloy.New(cupaloy.SnapshotFileExtension(".html"))
96
+ c := cupaloy.New(cupaloy.SnapshotFileExtension(".html"))
100
- c.SnapshotT(t, b.String())
97
+ c.SnapshotT(t, b.String())
98
+ })
99
+ })
101
100
  }