Compare commits
No commits in common. "5cbb6576662f552f7c0dcc29aa8236e1bfeb4d8e" and "4cba2af670677f6b6158c60f47460fbd47b0199f" have entirely different histories.
5cbb657666
...
4cba2af670
12
doc/TODO.txt
12
doc/TODO.txt
@ -1,9 +1,17 @@
|
|||||||
TODO: soft-deletion
|
TODO: auto-timestamps
|
||||||
- enable soft-deletion if table has `is_deleted` and `deleted_at` fields
|
- SaveXyz should set created_at and updated_at; shouldn't touch is_deleted or deleted_at
|
||||||
- if soft delete is enabled, DeleteXyz should do update (not delete) and set is_deleted and deleted_at
|
- if soft delete is enabled, DeleteXyz should do update (not delete) and set is_deleted and deleted_at
|
||||||
- ...and DeleteXyz should have pointer receiver for soft-delete
|
- ...and DeleteXyz should have pointer receiver for soft-delete
|
||||||
|
- SaveXyz shouldn't set created_at in the do-update branch
|
||||||
- GetXyzByID should include `ErrItemIsDeleted` if item is soft-deleted
|
- GetXyzByID should include `ErrItemIsDeleted` if item is soft-deleted
|
||||||
|
|
||||||
|
TODO: modified-timestamps
|
||||||
|
- set updated_at and created_at in SaveXYZ
|
||||||
|
- soft delete option
|
||||||
|
|
||||||
|
TODO: generator-foreign-keys
|
||||||
|
- add auto-foreign-key checking blocks to SaveXyz
|
||||||
|
|
||||||
TODO: migration-structs
|
TODO: migration-structs
|
||||||
- Right now, migrations are strings. Could be a struct with "name", "up" and "down" fields
|
- Right now, migrations are strings. Could be a struct with "name", "up" and "down" fields
|
||||||
- Adding a "down" operation enables handling newer DB versions with "down instead of error-out" for development (perhaps a flag)
|
- Adding a "down" operation enables handling newer DB versions with "down instead of error-out" for development (perhaps a flag)
|
||||||
|
|||||||
@ -27,6 +27,14 @@ func SQLFieldsConstIdent(tbl schema.Table) *ast.Ident {
|
|||||||
return ast.NewIdent(strings.ToLower(tbl.GoTypeName) + "SQLFields")
|
return ast.NewIdent(strings.ToLower(tbl.GoTypeName) + "SQLFields")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fkFieldName(col schema.Column) string {
|
||||||
|
if col.IsNonCodeTableForeignKey() {
|
||||||
|
return textutils.SnakeToCamel(strings.TrimSuffix(col.Name, "_id")) + "ID"
|
||||||
|
} else {
|
||||||
|
return textutils.SnakeToCamel(col.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
// Generators
|
// Generators
|
||||||
// ---------------
|
// ---------------
|
||||||
@ -58,12 +66,31 @@ func GenerateModelAST(table schema.Table) *ast.GenDecl {
|
|||||||
default:
|
default:
|
||||||
if col.IsNonCodeTableForeignKey() {
|
if col.IsNonCodeTableForeignKey() {
|
||||||
fields = append(fields, &ast.Field{
|
fields = append(fields, &ast.Field{
|
||||||
Names: []*ast.Ident{ast.NewIdent(col.GoFieldName())},
|
Names: []*ast.Ident{ast.NewIdent(fkFieldName(col))},
|
||||||
Type: ast.NewIdent(schema.TypenameFromTablename(col.ForeignKeyTargetTable) + "ID"),
|
Type: ast.NewIdent(schema.TypenameFromTablename(col.ForeignKeyTargetTable) + "ID"),
|
||||||
Tag: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`db:\"%s\" json:\"%s\"`", col.Name, col.Name)},
|
Tag: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("`db:\"%s\" json:\"%s\"`", col.Name, col.Name)},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
typeName := col.GoType()
|
typeName := "string"
|
||||||
|
switch col.Type {
|
||||||
|
case "integer", "int":
|
||||||
|
if strings.HasPrefix(col.Name, "is_") || strings.HasPrefix(col.Name, "has_") {
|
||||||
|
typeName = "bool"
|
||||||
|
} else if strings.HasSuffix(col.Name, "_at") {
|
||||||
|
typeName = "Timestamp"
|
||||||
|
} else {
|
||||||
|
typeName = "int"
|
||||||
|
}
|
||||||
|
case "text":
|
||||||
|
typeName = "string"
|
||||||
|
case "real":
|
||||||
|
typeName = "float32"
|
||||||
|
case "blob":
|
||||||
|
typeName = "[]byte"
|
||||||
|
default:
|
||||||
|
panic("Unrecognized sqlite column type: " + col.Type)
|
||||||
|
}
|
||||||
|
|
||||||
fields = append(fields, &ast.Field{
|
fields = append(fields, &ast.Field{
|
||||||
Names: []*ast.Ident{ast.NewIdent(textutils.SnakeToCamel(col.Name))},
|
Names: []*ast.Ident{ast.NewIdent(textutils.SnakeToCamel(col.Name))},
|
||||||
Type: ast.NewIdent(typeName),
|
Type: ast.NewIdent(typeName),
|
||||||
@ -143,7 +170,7 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
|
|||||||
}
|
}
|
||||||
hasFks = true
|
hasFks = true
|
||||||
|
|
||||||
structFieldName := col.GoFieldName()
|
structFieldName := fkFieldName(col)
|
||||||
structField := &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent(structFieldName)}
|
structField := &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent(structFieldName)}
|
||||||
|
|
||||||
if col.IsNonCodeTableForeignKey() {
|
if col.IsNonCodeTableForeignKey() {
|
||||||
|
|||||||
@ -305,7 +305,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
Lhs: []ast.Expr{
|
Lhs: []ast.Expr{
|
||||||
&ast.SelectorExpr{
|
&ast.SelectorExpr{
|
||||||
X: ast.NewIdent(tbl.VarName),
|
X: ast.NewIdent(tbl.VarName),
|
||||||
Sel: ast.NewIdent(col.GoFieldName()),
|
Sel: ast.NewIdent(fkFieldName(col)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Tok: token.ASSIGN,
|
Tok: token.ASSIGN,
|
||||||
@ -346,11 +346,11 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
|
|||||||
ast.NewIdent("err"),
|
ast.NewIdent("err"),
|
||||||
&ast.BasicLit{
|
&ast.BasicLit{
|
||||||
Kind: token.STRING,
|
Kind: token.STRING,
|
||||||
Value: fmt.Sprintf("%q", col.GoFieldName()),
|
Value: fmt.Sprintf("%q", fkFieldName(col)),
|
||||||
},
|
},
|
||||||
&ast.SelectorExpr{
|
&ast.SelectorExpr{
|
||||||
X: ast.NewIdent(tbl.VarName),
|
X: ast.NewIdent(tbl.VarName),
|
||||||
Sel: ast.NewIdent(col.GoFieldName()),
|
Sel: ast.NewIdent(fkFieldName(col)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
package schema
|
package schema
|
||||||
|
|
||||||
import (
|
import "strings"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.offline-twitter.com/offline-labs/gas-stack/pkg/textutils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Column represents a single column in a table.
|
// Column represents a single column in a table.
|
||||||
type Column struct {
|
type Column struct {
|
||||||
@ -30,39 +26,6 @@ func (c Column) IsNonCodeTableForeignKey() bool {
|
|||||||
return c.IsForeignKey && strings.HasSuffix(c.Name, "_id")
|
return c.IsForeignKey && strings.HasSuffix(c.Name, "_id")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Column) GoFieldName() string {
|
|
||||||
if c.Name == "rowid" {
|
|
||||||
return "ID"
|
|
||||||
}
|
|
||||||
if c.IsNonCodeTableForeignKey() {
|
|
||||||
return textutils.SnakeToCamel(strings.TrimSuffix(c.Name, "_id")) + "ID"
|
|
||||||
}
|
|
||||||
return textutils.SnakeToCamel(c.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Column) GoType() string {
|
|
||||||
if c.IsNonCodeTableForeignKey() {
|
|
||||||
return TypenameFromTablename(c.ForeignKeyTargetTable) + "ID"
|
|
||||||
}
|
|
||||||
switch c.Type {
|
|
||||||
case "integer", "int":
|
|
||||||
if strings.HasPrefix(c.Name, "is_") || strings.HasPrefix(c.Name, "has_") {
|
|
||||||
return "bool"
|
|
||||||
} else if strings.HasSuffix(c.Name, "_at") {
|
|
||||||
return "Timestamp"
|
|
||||||
}
|
|
||||||
return "int"
|
|
||||||
case "text":
|
|
||||||
return "string"
|
|
||||||
case "real":
|
|
||||||
return "float32"
|
|
||||||
case "blob":
|
|
||||||
return "[]byte"
|
|
||||||
default:
|
|
||||||
panic("Unrecognized sqlite column type: " + c.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Table is a single SQLite table.
|
// Table is a single SQLite table.
|
||||||
type Table struct {
|
type Table struct {
|
||||||
TableName string `db:"name"`
|
TableName string `db:"name"`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user