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


fb9440fa Peter John

3 years ago
simplify example
_example/Dockerfile.dbmate DELETED
@@ -1,4 +0,0 @@
1
- FROM amacneil/dbmate
2
-
3
- WORKDIR /
4
- COPY ./db ./db
_example/components/todo.go CHANGED
@@ -1,12 +1,12 @@
1
1
  package components
2
2
 
3
3
  import (
4
- "github.com/pyros2097/gromer/_example/db"
4
+ "github.com/pyros2097/gromer/_example/services"
5
5
  . "github.com/pyros2097/gromer/handlebars"
6
6
  )
7
7
 
8
8
  type TodoProps struct {
9
- Todo *db.Todo `json:"todo"`
9
+ Todo *services.Todo `json:"todo"`
10
10
  }
11
11
 
12
12
  func Todo(props TodoProps) *Template {
_example/config/config.go DELETED
@@ -1,11 +0,0 @@
1
- package config
2
-
3
- import (
4
- "os"
5
- )
6
-
7
- var DATABASE_URL string
8
-
9
- func init() {
10
- DATABASE_URL = os.Getenv("DATABASE_URL")
11
- }
_example/db/db.go DELETED
@@ -1,29 +0,0 @@
1
- // Code generated by sqlc. DO NOT EDIT.
2
-
3
- package db
4
-
5
- import (
6
- "context"
7
- "database/sql"
8
- )
9
-
10
- type DBTX interface {
11
- ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
12
- PrepareContext(context.Context, string) (*sql.Stmt, error)
13
- QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
14
- QueryRowContext(context.Context, string, ...interface{}) *sql.Row
15
- }
16
-
17
- func New(db DBTX) *Queries {
18
- return &Queries{db: db}
19
- }
20
-
21
- type Queries struct {
22
- db DBTX
23
- }
24
-
25
- func (q *Queries) WithTx(tx *sql.Tx) *Queries {
26
- return &Queries{
27
- db: tx,
28
- }
29
- }
_example/db/init.go DELETED
@@ -1,18 +0,0 @@
1
- package db
2
-
3
- import (
4
- "database/sql"
5
-
6
- _ "github.com/lib/pq"
7
- "github.com/pyros2097/gromer/_example/config"
8
- )
9
-
10
- var Query *Queries
11
-
12
- func init() {
13
- sqlDB, err := sql.Open("postgres", config.DATABASE_URL)
14
- if err != nil {
15
- panic(err)
16
- }
17
- Query = New(sqlDB)
18
- }
_example/db/migrations/20211128110219_create_todos_table.sql DELETED
@@ -1,11 +0,0 @@
1
- -- migrate:up
2
- CREATE TABLE todos (
3
- id TEXT NOT NULL PRIMARY KEY,
4
- text TEXT NOT NULL,
5
- completed BOOLEAN NOT NULL,
6
- created_at TIMESTAMP NOT NULL,
7
- updated_at TIMESTAMP NOT NULL
8
- );
9
-
10
- -- migrate:down
11
- DROP TABLE todos;
_example/db/models.go DELETED
@@ -1,15 +0,0 @@
1
- // Code generated by sqlc. DO NOT EDIT.
2
-
3
- package db
4
-
5
- import (
6
- "time"
7
- )
8
-
9
- type Todo struct {
10
- ID string `json:"id"`
11
- Text string `json:"text"`
12
- Completed bool `json:"completed"`
13
- CreatedAt time.Time `json:"createdAt"`
14
- UpdatedAt time.Time `json:"updatedAt"`
15
- }
_example/db/queries/todo.sql DELETED
@@ -1,26 +0,0 @@
1
- -- name: GetTodo :one
2
- SELECT * FROM todos
3
- WHERE id = $1 LIMIT 1;
4
-
5
- -- name: ListTodos :many
6
- SELECT * FROM todos
7
- ORDER BY id LIMIT $1 OFFSET $2;
8
-
9
- -- name: CreateTodo :one
10
- INSERT INTO todos (
11
- id, text, completed, created_at, updated_at
12
- ) VALUES (
13
- $1, $2, $3, $4, $5
14
- ) RETURNING *;
15
-
16
- -- name: UpdateTodo :one
17
- UPDATE todos
18
- SET
19
- completed = $2,
20
- updated_at = $3
21
- WHERE id = $1
22
- RETURNING *;
23
-
24
- -- name: DeleteTodo :exec
25
- DELETE FROM todos
26
- WHERE id = $1;
_example/db/schema.sql DELETED
@@ -1,64 +0,0 @@
1
- SET statement_timeout = 0;
2
- SET lock_timeout = 0;
3
- SET idle_in_transaction_session_timeout = 0;
4
- SET client_encoding = 'UTF8';
5
- SET standard_conforming_strings = on;
6
- SELECT pg_catalog.set_config('search_path', '', false);
7
- SET check_function_bodies = false;
8
- SET xmloption = content;
9
- SET client_min_messages = warning;
10
- SET row_security = off;
11
-
12
- SET default_tablespace = '';
13
-
14
- SET default_table_access_method = heap;
15
-
16
- --
17
- -- Name: schema_migrations; Type: TABLE; Schema: public; Owner: -
18
- --
19
-
20
- CREATE TABLE public.schema_migrations (
21
- version character varying(255) NOT NULL
22
- );
23
-
24
-
25
- --
26
- -- Name: todos; Type: TABLE; Schema: public; Owner: -
27
- --
28
-
29
- CREATE TABLE public.todos (
30
- id text NOT NULL,
31
- text text NOT NULL,
32
- completed boolean NOT NULL,
33
- created_at timestamp without time zone NOT NULL,
34
- updated_at timestamp without time zone NOT NULL
35
- );
36
-
37
-
38
- --
39
- -- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
40
- --
41
-
42
- ALTER TABLE ONLY public.schema_migrations
43
- ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
44
-
45
-
46
- --
47
- -- Name: todos todos_pkey; Type: CONSTRAINT; Schema: public; Owner: -
48
- --
49
-
50
- ALTER TABLE ONLY public.todos
51
- ADD CONSTRAINT todos_pkey PRIMARY KEY (id);
52
-
53
-
54
- --
55
- -- PostgreSQL database dump complete
56
- --
57
-
58
-
59
- --
60
- -- Dbmate schema migrations
61
- --
62
-
63
- INSERT INTO public.schema_migrations (version) VALUES
64
- ('20211128110219');
_example/db/sqlc.yaml DELETED
@@ -1,11 +0,0 @@
1
- version: '1'
2
- packages:
3
- - name: 'db'
4
- path: './'
5
- engine: 'postgresql'
6
- schema: './migrations/'
7
- queries: './queries/'
8
- emit_json_tags: true
9
- emit_empty_slices: true
10
- emit_result_struct_pointers: true
11
- json_tags_case_style: camel
_example/db/todo.sql.go DELETED
@@ -1,139 +0,0 @@
1
- // Code generated by sqlc. DO NOT EDIT.
2
- // source: todo.sql
3
-
4
- package db
5
-
6
- import (
7
- "context"
8
- "time"
9
- )
10
-
11
- const createTodo = `-- name: CreateTodo :one
12
- INSERT INTO todos (
13
- id, text, completed, created_at, updated_at
14
- ) VALUES (
15
- $1, $2, $3, $4, $5
16
- ) RETURNING id, text, completed, created_at, updated_at
17
- `
18
-
19
- type CreateTodoParams struct {
20
- ID string `json:"id"`
21
- Text string `json:"text"`
22
- Completed bool `json:"completed"`
23
- CreatedAt time.Time `json:"createdAt"`
24
- UpdatedAt time.Time `json:"updatedAt"`
25
- }
26
-
27
- func (q *Queries) CreateTodo(ctx context.Context, arg CreateTodoParams) (*Todo, error) {
28
- row := q.db.QueryRowContext(ctx, createTodo,
29
- arg.ID,
30
- arg.Text,
31
- arg.Completed,
32
- arg.CreatedAt,
33
- arg.UpdatedAt,
34
- )
35
- var i Todo
36
- err := row.Scan(
37
- &i.ID,
38
- &i.Text,
39
- &i.Completed,
40
- &i.CreatedAt,
41
- &i.UpdatedAt,
42
- )
43
- return &i, err
44
- }
45
-
46
- const deleteTodo = `-- name: DeleteTodo :exec
47
- DELETE FROM todos
48
- WHERE id = $1
49
- `
50
-
51
- func (q *Queries) DeleteTodo(ctx context.Context, id string) error {
52
- _, err := q.db.ExecContext(ctx, deleteTodo, id)
53
- return err
54
- }
55
-
56
- const getTodo = `-- name: GetTodo :one
57
- SELECT id, text, completed, created_at, updated_at FROM todos
58
- WHERE id = $1 LIMIT 1
59
- `
60
-
61
- func (q *Queries) GetTodo(ctx context.Context, id string) (*Todo, error) {
62
- row := q.db.QueryRowContext(ctx, getTodo, id)
63
- var i Todo
64
- err := row.Scan(
65
- &i.ID,
66
- &i.Text,
67
- &i.Completed,
68
- &i.CreatedAt,
69
- &i.UpdatedAt,
70
- )
71
- return &i, err
72
- }
73
-
74
- const listTodos = `-- name: ListTodos :many
75
- SELECT id, text, completed, created_at, updated_at FROM todos
76
- ORDER BY id LIMIT $1 OFFSET $2
77
- `
78
-
79
- type ListTodosParams struct {
80
- Limit int32 `json:"limit"`
81
- Offset int32 `json:"offset"`
82
- }
83
-
84
- func (q *Queries) ListTodos(ctx context.Context, arg ListTodosParams) ([]*Todo, error) {
85
- rows, err := q.db.QueryContext(ctx, listTodos, arg.Limit, arg.Offset)
86
- if err != nil {
87
- return nil, err
88
- }
89
- defer rows.Close()
90
- items := []*Todo{}
91
- for rows.Next() {
92
- var i Todo
93
- if err := rows.Scan(
94
- &i.ID,
95
- &i.Text,
96
- &i.Completed,
97
- &i.CreatedAt,
98
- &i.UpdatedAt,
99
- ); err != nil {
100
- return nil, err
101
- }
102
- items = append(items, &i)
103
- }
104
- if err := rows.Close(); err != nil {
105
- return nil, err
106
- }
107
- if err := rows.Err(); err != nil {
108
- return nil, err
109
- }
110
- return items, nil
111
- }
112
-
113
- const updateTodo = `-- name: UpdateTodo :one
114
- UPDATE todos
115
- SET
116
- completed = $2,
117
- updated_at = $3
118
- WHERE id = $1
119
- RETURNING id, text, completed, created_at, updated_at
120
- `
121
-
122
- type UpdateTodoParams struct {
123
- ID string `json:"id"`
124
- Completed bool `json:"completed"`
125
- UpdatedAt time.Time `json:"updatedAt"`
126
- }
127
-
128
- func (q *Queries) UpdateTodo(ctx context.Context, arg UpdateTodoParams) (*Todo, error) {
129
- row := q.db.QueryRowContext(ctx, updateTodo, arg.ID, arg.Completed, arg.UpdatedAt)
130
- var i Todo
131
- err := row.Scan(
132
- &i.ID,
133
- &i.Text,
134
- &i.Completed,
135
- &i.CreatedAt,
136
- &i.UpdatedAt,
137
- )
138
- return &i, err
139
- }
_example/makefile CHANGED
@@ -1,19 +1,11 @@
1
1
  setup:
2
- go install github.com/kyleconroy/sqlc/cmd/sqlc@v1.13.0
3
- go install github.com/amacneil/dbmate@v1.12.1
4
2
  go install github.com/mitranim/gow@latest
5
3
  go install github.com/pyros2097/gromer/cmd/gromer@latest
6
- docker pull postgres:14.1
7
- docker run --name postgres141 -p 5432:5432 -e POSTGRES_PASSWORD=demo -d postgres:14.1
8
4
 
9
- generate: export DATABASE_URL=postgres://postgres:demo@127.0.0.1:5432/postgres?sslmode=disable
10
- generate:
5
+ update:
11
- sqlc generate -f db/sqlc.yaml
12
6
  gromer -pkg github.com/pyros2097/gromer/_example
13
- dbmate migrate
14
7
 
15
8
  run: export PORT=3000
16
- run: export DATABASE_URL=postgres://postgres:demo@127.0.0.1:5432/postgres?sslmode=disable
17
9
  run:
18
10
  gow run main.go
19
11
 
@@ -25,6 +17,5 @@ build:
25
17
  docker-build:
26
18
  docker build -f ../_example/Dockerfile -t example-app:develop ../
27
19
 
28
- docker-run: export DATABASE_URL=postgres://postgres:demo@docker.for.mac.host.internal:5432/postgres?sslmode=disable
29
20
  docker-run:
30
- docker run -p 3000:3000 -e DATABASE_URL=$$DATABASE_URL example:latest
21
+ docker run -p 3000:3000 example-app:develop
_example/pages/api/todos/_todoId_/delete.go CHANGED
@@ -3,7 +3,7 @@ package todos_todoId_
3
3
  import (
4
4
  "context"
5
5
 
6
- "github.com/pyros2097/gromer/_example/db"
6
+ "github.com/pyros2097/gromer/_example/services"
7
7
  )
8
8
 
9
9
  func DELETE(ctx context.Context, id string) (string, int, error) {
@@ -11,7 +11,7 @@ func DELETE(ctx context.Context, id string) (string, int, error) {
11
11
  if err != nil {
12
12
  return "", status, err
13
13
  }
14
- err = db.Query.DeleteTodo(ctx, id)
14
+ _, err = services.DeleteTodo(ctx, id)
15
15
  if err != nil {
16
16
  return id, 500, err
17
17
  }
_example/pages/api/todos/_todoId_/get.go CHANGED
@@ -2,22 +2,17 @@ package todos_todoId_
2
2
 
3
3
  import (
4
4
  "context"
5
- "fmt"
6
- "strings"
7
5
 
8
- "github.com/pyros2097/gromer/_example/db"
6
+ "github.com/pyros2097/gromer/_example/services"
9
7
  )
10
8
 
11
9
  type GetParams struct {
12
10
  Show string `json:"show"`
13
11
  }
14
12
 
15
- func GET(ctx context.Context, id string, params GetParams) (*db.Todo, int, error) {
13
+ func GET(ctx context.Context, id string, params GetParams) (*services.Todo, int, error) {
16
- todo, err := db.Query.GetTodo(ctx, id)
14
+ todo, err := services.GetTodo(ctx, id)
17
15
  if err != nil {
18
- if strings.Contains(err.Error(), "no rows") {
19
- return nil, 404, fmt.Errorf("Todo with id '%s' not found", id)
20
- }
21
16
  return nil, 500, err
22
17
  }
23
18
  if params.Show == "true" {
_example/pages/api/todos/_todoId_/put.go CHANGED
@@ -2,24 +2,23 @@ package todos_todoId_
2
2
 
3
3
  import (
4
4
  "context"
5
- "time"
6
5
 
7
- "github.com/pyros2097/gromer/_example/db"
6
+ "github.com/pyros2097/gromer/_example/services"
8
7
  )
9
8
 
10
9
  type PutParams struct {
10
+ Text string `json:"text"`
11
- Completed bool `json:"completed"`
11
+ Completed bool `json:"completed"`
12
12
  }
13
13
 
14
- func PUT(ctx context.Context, id string, params PutParams) (*db.Todo, int, error) {
14
+ func PUT(ctx context.Context, id string, params PutParams) (*services.Todo, int, error) {
15
15
  _, status, err := GET(ctx, id, GetParams{})
16
16
  if err != nil {
17
17
  return nil, status, err
18
18
  }
19
- todo, err := db.Query.UpdateTodo(ctx, db.UpdateTodoParams{
19
+ todo, err := services.UpdateTodo(ctx, id, services.UpdateTodoParams{
20
- ID: id,
20
+ Text: params.Text,
21
21
  Completed: params.Completed,
22
- UpdatedAt: time.Now(),
23
22
  })
24
23
  if err != nil {
25
24
  return nil, 500, err
_example/pages/api/todos/get.go CHANGED
@@ -4,22 +4,17 @@ import (
4
4
  "context"
5
5
 
6
6
  . "github.com/pyros2097/gromer"
7
- "github.com/pyros2097/gromer/_example/db"
7
+ "github.com/pyros2097/gromer/_example/services"
8
8
  )
9
9
 
10
10
  type GetParams struct {
11
- Limit int `json:"limit"`
11
+ Limit int `json:"limit"`
12
- Offset int `json:"offset"`
13
12
  }
14
13
 
15
- func GET(ctx context.Context, params GetParams) ([]*db.Todo, int, error) {
14
+ func GET(ctx context.Context, params GetParams) ([]*services.Todo, int, error) {
16
15
  limit := Default(params.Limit, 10)
17
- todos, err := db.Query.ListTodos(ctx, db.ListTodosParams{
16
+ todos := services.GetAllTodo(ctx, services.GetAllTodoParams{
18
- Limit: int32(limit),
17
+ Limit: limit,
19
- Offset: int32(params.Offset),
20
18
  })
21
- if err != nil {
22
- return nil, 500, err
23
- }
24
19
  return todos, 200, nil
25
20
  }
_example/pages/api/todos/post.go CHANGED
@@ -5,15 +5,15 @@ import (
5
5
  "time"
6
6
 
7
7
  "github.com/google/uuid"
8
- "github.com/pyros2097/gromer/_example/db"
8
+ "github.com/pyros2097/gromer/_example/services"
9
9
  )
10
10
 
11
11
  type PostParams struct {
12
12
  Text string `json:"text"`
13
13
  }
14
14
 
15
- func POST(ctx context.Context, b PostParams) (*db.Todo, int, error) {
15
+ func POST(ctx context.Context, b PostParams) (*services.Todo, int, error) {
16
- todo, err := db.Query.CreateTodo(ctx, db.CreateTodoParams{
16
+ todo, err := services.CreateTodo(ctx, services.Todo{
17
17
  ID: uuid.New().String(),
18
18
  Text: b.Text,
19
19
  Completed: false,
_example/pages/get.go CHANGED
@@ -16,8 +16,7 @@ type GetParams struct {
16
16
  func GET(ctx context.Context, params GetParams) (HtmlContent, int, error) {
17
17
  page := Default(params.Page, 1)
18
18
  todos, status, err := todos.GET(ctx, todos.GetParams{
19
- Limit: 10,
19
+ Limit: page * 10,
20
- Offset: 10 * (page - 1),
21
20
  })
22
21
  if err != nil {
23
22
  return HtmlErr(status, err)
_example/services/todo.go ADDED
@@ -0,0 +1,77 @@
1
+ // Code generated by sqlc. DO NOT EDIT.
2
+
3
+ package services
4
+
5
+ import (
6
+ "context"
7
+ "errors"
8
+ "time"
9
+ )
10
+
11
+ type Todo struct {
12
+ ID string `json:"id"`
13
+ Text string `json:"text"`
14
+ Completed bool `json:"completed"`
15
+ CreatedAt time.Time `json:"createdAt"`
16
+ UpdatedAt time.Time `json:"updatedAt"`
17
+ }
18
+
19
+ var todos = []*Todo{}
20
+
21
+ func CreateTodo(ctx context.Context, todo Todo) (*Todo, error) {
22
+ todos = append(todos, &todo)
23
+ return &todo, nil
24
+ }
25
+
26
+ type UpdateTodoParams struct {
27
+ Text string `json:"text"`
28
+ Completed bool `json:"completed"`
29
+ }
30
+
31
+ func UpdateTodo(ctx context.Context, id string, params UpdateTodoParams) (*Todo, error) {
32
+ updateIndex := -1
33
+ for i, todo := range todos {
34
+ if todo.ID == id {
35
+ updateIndex = i
36
+ }
37
+ }
38
+ if updateIndex != -1 {
39
+ todos[updateIndex].Text = params.Text
40
+ todos[updateIndex].Completed = params.Completed
41
+ todos[updateIndex].UpdatedAt = time.Now()
42
+ return todos[updateIndex], nil
43
+ }
44
+ return nil, errors.New("Todo not found")
45
+ }
46
+
47
+ func DeleteTodo(ctx context.Context, id string) (string, error) {
48
+ deleteIndex := -1
49
+ for i, todo := range todos {
50
+ if todo.ID == id {
51
+ deleteIndex = i
52
+ }
53
+ }
54
+ if deleteIndex != -1 {
55
+ todos = append(todos[:deleteIndex], todos[deleteIndex+1:]...)
56
+ return id, nil
57
+ }
58
+ return "", errors.New("Todo not found")
59
+ }
60
+
61
+ func GetTodo(ctx context.Context, id string) (*Todo, error) {
62
+ for _, todo := range todos {
63
+ if todo.ID == id {
64
+ return todo, nil
65
+ }
66
+ }
67
+ return nil, errors.New("Todo not found")
68
+ }
69
+
70
+
71
+ type GetAllTodoParams struct {
72
+ Limit int `json:"limit"`
73
+ }
74
+
75
+ func GetAllTodo(ctx context.Context, params GetAllTodoParams) []*Todo {
76
+ return todos
77
+ }