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


aaedf321 Peter John

tag: v0.15.0

v0.15.0

3 years ago
move Template to handlebars
api_explorer.go CHANGED
@@ -5,6 +5,8 @@ import (
5
5
  "encoding/json"
6
6
  "html/template"
7
7
  "strings"
8
+
9
+ . "github.com/pyros2097/gromer/handlebars"
8
10
  )
9
11
 
10
12
  func ApiExplorer(ctx context.Context) (HtmlContent, int, error) {
example/components/header.go CHANGED
@@ -1,10 +1,10 @@
1
1
  package components
2
2
 
3
3
  import (
4
- . "github.com/pyros2097/gromer"
4
+ . "github.com/pyros2097/gromer/handlebars"
5
5
  )
6
6
 
7
- func Header() *HandlersTemplate {
7
+ func Header() *Template {
8
8
  return Html(`
9
9
  <nav class="navbar" role="navigation" aria-label="main navigation">
10
10
  <div class="navbar-brand">
example/components/page.go CHANGED
@@ -3,7 +3,7 @@ package components
3
3
  import (
4
4
  "html/template"
5
5
 
6
- . "github.com/pyros2097/gromer"
6
+ . "github.com/pyros2097/gromer/handlebars"
7
7
  )
8
8
 
9
9
  type PageProps struct {
@@ -11,7 +11,7 @@ type PageProps struct {
11
11
  Children template.HTML `json:"children"`
12
12
  }
13
13
 
14
- func Page(props PageProps) *HandlersTemplate {
14
+ func Page(props PageProps) *Template {
15
15
  return Html(`
16
16
  <!DOCTYPE html>
17
17
  <html lang="en">
example/components/todo.go CHANGED
@@ -1,15 +1,15 @@
1
1
  package components
2
2
 
3
3
  import (
4
- . "github.com/pyros2097/gromer"
5
4
  "github.com/pyros2097/gromer/example/db"
5
+ . "github.com/pyros2097/gromer/handlebars"
6
6
  )
7
7
 
8
8
  type TodoProps struct {
9
9
  Todo *db.Todo `json:"todo"`
10
10
  }
11
11
 
12
- func Todo(props TodoProps) *HandlersTemplate {
12
+ func Todo(props TodoProps) *Template {
13
13
  return Html(`
14
14
  <tr>
15
15
  <td>{{ props.Todo.ID }}</td>
example/main.go CHANGED
@@ -23,6 +23,7 @@ func init() {
23
23
  gromer.RegisterComponent(components.Header)
24
24
  gromer.RegisterComponent(components.Page)
25
25
  gromer.RegisterComponent(components.Todo)
26
+
26
27
  }
27
28
 
28
29
  func main() {
example/pages/about/get.go CHANGED
@@ -3,14 +3,14 @@ package about
3
3
  import (
4
4
  "context"
5
5
 
6
- . "github.com/pyros2097/gromer"
6
+ . "github.com/pyros2097/gromer/handlebars"
7
7
  )
8
8
 
9
9
  func GET(c context.Context) (HtmlContent, int, error) {
10
10
  return Html(`
11
- {{#Page "About me"}}
11
+ {{#Page title="About me"}}
12
12
  <div class="flex flex-col justify-center items-center">
13
- {{#Header "123"}}
13
+ {{#Header}}
14
14
  A new link is here
15
15
  {{/Header}}
16
16
  <h1>About Me</h1>
example/pages/get.go CHANGED
@@ -6,6 +6,7 @@ import (
6
6
  . "github.com/pyros2097/gromer"
7
7
  _ "github.com/pyros2097/gromer/example/components"
8
8
  "github.com/pyros2097/gromer/example/pages/api/todos"
9
+ . "github.com/pyros2097/gromer/handlebars"
9
10
  )
10
11
 
11
12
  type GetParams struct {
handlebars/template.go CHANGED
@@ -1,24 +1,28 @@
1
1
  package handlebars
2
2
 
3
3
  import (
4
- "sync"
4
+ "fmt"
5
5
 
6
6
  "github.com/aymerick/raymond/ast"
7
7
  "github.com/aymerick/raymond/parser"
8
8
  "github.com/pkg/errors"
9
9
  )
10
10
 
11
+ type HtmlContent string
12
+
11
13
  // Template represents an input and helpers to be used
12
14
  // to evaluate and render the input.
13
15
  type Template struct {
14
16
  Input string
17
+ Context *Context
15
18
  program *ast.Program
16
19
  }
17
20
 
18
21
  // NewTemplate from the input string.
19
22
  func NewTemplate(input string) (*Template, error) {
20
23
  t := &Template{
21
- Input: input,
24
+ Input: input,
25
+ Context: NewContext(),
22
26
  }
23
27
  err := t.Parse()
24
28
  if err != nil {
@@ -43,53 +47,45 @@ func (t *Template) Parse() error {
43
47
  }
44
48
 
45
49
  // Exec the template using the content and return the results
46
- func (t *Template) Exec(ctx *Context) (string, error) {
50
+ func (t *Template) Render() (HtmlContent, int, error) {
47
51
  err := t.Parse()
48
52
  if err != nil {
49
- return "", errors.WithStack(err)
53
+ return HtmlContent("Server Erorr"), 500, errors.WithStack(err)
50
54
  }
51
- v := newEvalVisitor(t, ctx)
55
+ v := newEvalVisitor(t, t.Context)
52
56
  r := t.program.Accept(v)
53
57
  switch rp := r.(type) {
54
58
  case string:
55
- return rp, nil
59
+ return HtmlContent(rp), 200, nil
56
60
  case error:
57
- return "", rp
61
+ return HtmlContent("Server Erorr"), 500, rp
58
62
  case nil:
59
- return "", nil
63
+ return HtmlContent(""), 200, nil
60
64
  default:
61
- return "", errors.WithStack(errors.Errorf("unsupport eval return format %T: %+v", r, r))
65
+ return HtmlContent("Server Erorr"), 500, errors.WithStack(errors.Errorf("unsupport eval return format %T: %+v", r, r))
62
66
  }
63
67
  }
64
68
 
65
- var cache = map[string]*Template{}
69
+ func (t *Template) Prop(key string, v any) *Template {
66
- var moot = &sync.Mutex{}
70
+ t.Context.Set(key, v)
71
+ return t
72
+ }
67
73
 
68
- // Parse an input string and return a Template.
69
- func Parse(input string) (*Template, error) {
74
+ func (t *Template) Props(args ...any) *Template {
70
- moot.Lock()
71
- defer moot.Unlock()
72
- if t, ok := cache[input]; ok {
75
+ for i := 0; i < len(args); i += 2 {
76
+ key := fmt.Sprintf("%s", args[i])
73
- return t, nil
77
+ t.Context.Set(key, args[i+1])
74
78
  }
75
- t, err := NewTemplate(input)
79
+ return t
80
+ }
76
81
 
82
+ func Html(tpl string) *Template {
77
- if err == nil {
83
+ return &Template{
78
- cache[input] = t
84
+ Input: tpl,
85
+ Context: NewContext(),
79
86
  }
80
-
81
- if err != nil {
82
- return t, errors.WithStack(err)
83
- }
84
-
85
- return t, nil
86
87
  }
87
88
 
88
- // Render a string using the given the context.
89
- func Render(input string, ctx *Context) (string, error) {
89
+ func HtmlErr(status int, err error) (HtmlContent, int, error) {
90
- t, err := Parse(input)
91
- if err != nil {
92
- return "", errors.WithStack(err)
90
+ return HtmlContent("ErrorPage/AccessDeniedPage/NotFoundPage based on status code"), status, err
93
- }
94
- return t.Exec(ctx)
95
91
  }
http.go CHANGED
@@ -39,41 +39,6 @@ type RouteDefinition struct {
39
39
  Params interface{} `json:"params"`
40
40
  }
41
41
 
42
- type HtmlContent string
43
- type HandlersTemplate struct {
44
- text string
45
- ctx *handlebars.Context
46
- }
47
-
48
- func Html(tpl string) *HandlersTemplate {
49
- return &HandlersTemplate{text: tpl, ctx: handlebars.NewContext()}
50
- }
51
-
52
- func (t *HandlersTemplate) Prop(key string, v any) *HandlersTemplate {
53
- t.ctx.Set(key, v)
54
- return t
55
- }
56
-
57
- func (t *HandlersTemplate) Props(args ...any) *HandlersTemplate {
58
- for i := 0; i < len(args); i += 2 {
59
- key := fmt.Sprintf("%s", args[i])
60
- t.ctx.Set(key, args[i+1])
61
- }
62
- return t
63
- }
64
-
65
- func (t *HandlersTemplate) Render(args ...any) (HtmlContent, int, error) {
66
- s, err := handlebars.Render(t.text, t.ctx)
67
- if err != nil {
68
- return HtmlContent("Server Erorr"), 500, err
69
- }
70
- return HtmlContent(s), 200, nil
71
- }
72
-
73
- func HtmlErr(status int, err error) (HtmlContent, int, error) {
74
- return HtmlContent("ErrorPage/AccessDeniedPage/NotFoundPage based on status code"), status, err
75
- }
76
-
77
42
  func GetFunctionName(temp interface{}) string {
78
43
  strs := strings.Split((runtime.FuncForPC(reflect.ValueOf(temp).Pointer()).Name()), ".")
79
44
  return strs[len(strs)-1]
@@ -96,7 +61,6 @@ func RegisterComponent(fn any, props ...string) {
96
61
  for i := 0; i < structType.NumField(); i++ {
97
62
  if f := rv.Field(i); f.CanSet() {
98
63
  jsonName := structType.Field(i).Tag.Get("json")
99
- fmt.Printf("jsonName %s %+v\n", jsonName, help.Context.Get(jsonName))
100
64
  if jsonName == "children" {
101
65
  s, err := help.Block()
102
66
  if err != nil {
@@ -112,13 +76,13 @@ func RegisterComponent(fn any, props ...string) {
112
76
  props = rv.Interface()
113
77
  }
114
78
  res := fnValue.Call(args)
115
- tpl := res[0].Interface().(*HandlersTemplate)
79
+ tpl := res[0].Interface().(*handlebars.Template)
116
- tpl.ctx.Set("props", props)
80
+ tpl.Context.Set("props", props)
117
- comp, err := handlebars.Render(tpl.text, tpl.ctx)
81
+ s, _, err := tpl.Render()
118
82
  if err != nil {
119
83
  return "", err
120
84
  }
121
- return template.HTML(comp), nil
85
+ return template.HTML(s), nil
122
86
  })
123
87
  }
124
88
 
@@ -254,7 +218,7 @@ func PerformRequest(route string, h interface{}, ctx interface{}, w http.Respons
254
218
  RespondError(w, responseStatus, responseError.(error))
255
219
  return
256
220
  }
257
- if v, ok := response.(HtmlContent); ok {
221
+ if v, ok := response.(handlebars.HtmlContent); ok {
258
222
  w.Header().Set("Content-Type", "text/html")
259
223
  // This has to be at end always
260
224
  w.WriteHeader(responseStatus)