Skip to content

Commit

Permalink
Convert models generator to Genny (#356)
Browse files Browse the repository at this point in the history
* WIP converting models generator

* cleaned some things up

* moar tests

* use the new nulls package

* add support for the new model-path

* wip

* *adjusting for comments from stan, fixmie, and me

* Fix tests on Windows

* Add support for columns in fizz create table

* Fix fizz generator to produce down patches

* Add force ID opt for model generator

Almost done refactor model generator command line.

* Fix typo & WIP SQL migrations

* Finalize SQL migrations & remove legacy generator

* Fix timestamp issue with generated migrations

* Handle auto timestamps with the new generator

* Fix broken test

* Make model generator command more verbose

* Fix panic issue with no-args models generator

* Fix model file name should be singular

* Fix dialect suffix order in SQL patches

* Fix indentation issues with generated model file

* Add more tests
  • Loading branch information
markbates authored and stanislas-m committed Aug 25, 2019
1 parent 55a7e42 commit 219ea04
Show file tree
Hide file tree
Showing 32 changed files with 1,270 additions and 969 deletions.
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -18,7 +18,7 @@ build:
$(GO_BIN) build -v .

test:
packr2
# packr2
$(GO_BIN) test -tags ${TAGS} ./...

ci-test:
Expand Down
100 changes: 19 additions & 81 deletions SHOULDERS.md
Expand Up @@ -5,126 +5,64 @@ Pop does not try to reinvent the wheel! Instead, it uses the already great wheel
Thank you to the following **GIANTS**:


* [github.com/blang/semver](https://godoc.org/github.com/blang/semver)
* [github.com/cockroachdb/apd](https://godoc.org/github.com/cockroachdb/apd)

* [github.com/cockroachdb/cockroach-go/crdb](https://godoc.org/github.com/cockroachdb/cockroach-go/crdb)
* [github.com/cockroachdb/cockroach-go](https://godoc.org/github.com/cockroachdb/cockroach-go)

* [github.com/fatih/color](https://godoc.org/github.com/fatih/color)

* [github.com/fatih/structs](https://godoc.org/github.com/fatih/structs)

* [github.com/go-sql-driver/mysql](https://godoc.org/github.com/go-sql-driver/mysql)

* [github.com/gobuffalo/attrs](https://godoc.org/github.com/gobuffalo/attrs)

* [github.com/gobuffalo/envy](https://godoc.org/github.com/gobuffalo/envy)

* [github.com/gobuffalo/fizz](https://godoc.org/github.com/gobuffalo/fizz)

* [github.com/gobuffalo/fizz/translators](https://godoc.org/github.com/gobuffalo/fizz/translators)

* [github.com/gobuffalo/flect](https://godoc.org/github.com/gobuffalo/flect)

* [github.com/gobuffalo/flect/name](https://godoc.org/github.com/gobuffalo/flect/name)

* [github.com/gobuffalo/github_flavored_markdown](https://godoc.org/github.com/gobuffalo/github_flavored_markdown)

* [github.com/gobuffalo/github_flavored_markdown/internal/russross/blackfriday](https://godoc.org/github.com/gobuffalo/github_flavored_markdown/internal/russross/blackfriday)
* [github.com/gobuffalo/genny](https://godoc.org/github.com/gobuffalo/genny)

* [github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/highlight_diff](https://godoc.org/github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/highlight_diff)
* [github.com/gobuffalo/gogen](https://godoc.org/github.com/gobuffalo/gogen)

* [github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/highlight_go](https://godoc.org/github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/highlight_go)

* [github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/octicon](https://godoc.org/github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/octicon)

* [github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/sanitized_anchor_name](https://godoc.org/github.com/gobuffalo/github_flavored_markdown/internal/shurcooL/sanitized_anchor_name)

* [github.com/gobuffalo/makr](https://godoc.org/github.com/gobuffalo/makr)
* [github.com/gobuffalo/logger](https://godoc.org/github.com/gobuffalo/logger)

* [github.com/gobuffalo/nulls](https://godoc.org/github.com/gobuffalo/nulls)

* [github.com/gobuffalo/packd](https://godoc.org/github.com/gobuffalo/packd)

* [github.com/gobuffalo/plush](https://godoc.org/github.com/gobuffalo/plush)

* [github.com/gobuffalo/plush/ast](https://godoc.org/github.com/gobuffalo/plush/ast)
* [github.com/gobuffalo/packr/v2](https://godoc.org/github.com/gobuffalo/packr/v2)

* [github.com/gobuffalo/plush/lexer](https://godoc.org/github.com/gobuffalo/plush/lexer)

* [github.com/gobuffalo/plush/parser](https://godoc.org/github.com/gobuffalo/plush/parser)

* [github.com/gobuffalo/plush/token](https://godoc.org/github.com/gobuffalo/plush/token)

* [github.com/gobuffalo/syncx](https://godoc.org/github.com/gobuffalo/syncx)

* [github.com/gobuffalo/tags](https://godoc.org/github.com/gobuffalo/tags)

* [github.com/gobuffalo/tags/form](https://godoc.org/github.com/gobuffalo/tags/form)

* [github.com/gobuffalo/tags/form/bootstrap](https://godoc.org/github.com/gobuffalo/tags/form/bootstrap)

* [github.com/gobuffalo/uuid](https://godoc.org/github.com/gobuffalo/uuid)
* [github.com/gobuffalo/plush](https://godoc.org/github.com/gobuffalo/plush)

* [github.com/gobuffalo/validate](https://godoc.org/github.com/gobuffalo/validate)

* [github.com/gobuffalo/validate/validators](https://godoc.org/github.com/gobuffalo/validate/validators)

* [github.com/gobuffalo/x/defaults](https://godoc.org/github.com/gobuffalo/x/defaults)

* [github.com/gofrs/uuid](https://godoc.org/github.com/gofrs/uuid)

* [github.com/jackc/pgx](https://godoc.org/github.com/jackc/pgx)

* [github.com/jackc/pgx/chunkreader](https://godoc.org/github.com/jackc/pgx/chunkreader)

* [github.com/jackc/pgx/internal/sanitize](https://godoc.org/github.com/jackc/pgx/internal/sanitize)

* [github.com/jackc/pgx/pgio](https://godoc.org/github.com/jackc/pgx/pgio)
* [github.com/jackc/fake](https://godoc.org/github.com/jackc/fake)

* [github.com/jackc/pgx/pgproto3](https://godoc.org/github.com/jackc/pgx/pgproto3)

* [github.com/jackc/pgx/pgtype](https://godoc.org/github.com/jackc/pgx/pgtype)
* [github.com/jackc/pgx](https://godoc.org/github.com/jackc/pgx)

* [github.com/jmoiron/sqlx](https://godoc.org/github.com/jmoiron/sqlx)

* [github.com/jmoiron/sqlx/reflectx](https://godoc.org/github.com/jmoiron/sqlx/reflectx)

* [github.com/joho/godotenv](https://godoc.org/github.com/joho/godotenv)

* [github.com/kballard/go-shellquote](https://godoc.org/github.com/kballard/go-shellquote)

* [github.com/lib/pq](https://godoc.org/github.com/lib/pq)

* [github.com/lib/pq/oid](https://godoc.org/github.com/lib/pq/oid)

* [github.com/markbates/going/defaults](https://godoc.org/github.com/markbates/going/defaults)

* [github.com/markbates/going/randx](https://godoc.org/github.com/markbates/going/randx)
* [github.com/mattn/go-colorable](https://godoc.org/github.com/mattn/go-colorable)

* [github.com/markbates/inflect](https://godoc.org/github.com/markbates/inflect)
* [github.com/mattn/go-isatty](https://godoc.org/github.com/mattn/go-isatty)

* [github.com/markbates/oncer](https://godoc.org/github.com/markbates/oncer)

* [github.com/microcosm-cc/bluemonday](https://godoc.org/github.com/microcosm-cc/bluemonday)
* [github.com/mattn/go-sqlite3](https://godoc.org/github.com/mattn/go-sqlite3)

* [github.com/pkg/errors](https://godoc.org/github.com/pkg/errors)

* [github.com/rogpeppe/go-internal/modfile](https://godoc.org/github.com/rogpeppe/go-internal/modfile)

* [github.com/rogpeppe/go-internal/module](https://godoc.org/github.com/rogpeppe/go-internal/module)

* [github.com/rogpeppe/go-internal/semver](https://godoc.org/github.com/rogpeppe/go-internal/semver)

* [github.com/serenize/snaker](https://godoc.org/github.com/serenize/snaker)

* [github.com/sergi/go-diff/diffmatchpatch](https://godoc.org/github.com/sergi/go-diff/diffmatchpatch)

* [github.com/sourcegraph/annotate](https://godoc.org/github.com/sourcegraph/annotate)
* [github.com/satori/go.uuid](https://godoc.org/github.com/satori/go.uuid)

* [github.com/sourcegraph/syntaxhighlight](https://godoc.org/github.com/sourcegraph/syntaxhighlight)
* [github.com/shopspring/decimal](https://godoc.org/github.com/shopspring/decimal)

* [golang.org/x/net/html](https://godoc.org/golang.org/x/net/html)
* [github.com/spf13/cobra](https://godoc.org/github.com/spf13/cobra)

* [golang.org/x/net/html/atom](https://godoc.org/golang.org/x/net/html/atom)
* [github.com/stretchr/testify](https://godoc.org/github.com/stretchr/testify)

* [golang.org/x/sync/errgroup](https://godoc.org/golang.org/x/sync/errgroup)
* [golang.org/x/sync](https://godoc.org/golang.org/x/sync)

* [gopkg.in/yaml.v2](https://godoc.org/gopkg.in/yaml.v2)
11 changes: 11 additions & 0 deletions genny/config/config_test.go
Expand Up @@ -58,3 +58,14 @@ func Test_New_No_Prefix(t *testing.T) {

r.Error(err)
}

func Test_New_BadDialect(t *testing.T) {
r := require.New(t)

_, err := New(&Options{
Prefix: "foo",
Dialect: "unknown",
})

r.EqualError(err, "unable to find database.yml template for dialect unknown")
}
99 changes: 99 additions & 0 deletions genny/fizz/ctable/create_table.go
@@ -0,0 +1,99 @@
package ctable

import (
"fmt"
"path/filepath"
"strings"

"github.com/gobuffalo/fizz"
"github.com/gobuffalo/genny"
"github.com/pkg/errors"
)

// New creates a generator to make files for a table based
// on the given options.
func New(opts *Options) (*genny.Generator, error) {
g := genny.New()

if err := opts.Validate(); err != nil {
return g, err
}

t := fizz.NewTable(opts.TableName, map[string]interface{}{
"timestamps": opts.ForceDefaultTimestamps,
})
for _, attr := range opts.Attrs {
o := fizz.Options{}
name := attr.Name.Underscore().String()
colType := fizzColType(attr.CommonType())
if name == "id" {
o["primary"] = true
}
if strings.HasPrefix(attr.GoType(), "nulls.") {
o["null"] = true
}
if err := t.Column(name, colType, o); err != nil {
return g, err
}
}
var f genny.File
up := t.Fizz()
down := t.UnFizz()
if opts.Type == "sql" {
type nameable interface {
Name() string
}
translatorNameable, ok := opts.Translator.(nameable)
if !ok {
return g, errors.New("fizz translator needs a Name method")
}
m, err := fizz.AString(up, opts.Translator)
if err != nil {
return g, err
}
f = genny.NewFileS(filepath.Join(opts.Path, fmt.Sprintf("%s.%s.up.sql", opts.Name, translatorNameable.Name())), m)
g.File(f)
m, err = fizz.AString(down, opts.Translator)
if err != nil {
return g, err
}
f = genny.NewFileS(filepath.Join(opts.Path, fmt.Sprintf("%s.%s.down.sql", opts.Name, translatorNameable.Name())), m)
g.File(f)
return g, nil
}
f = genny.NewFileS(filepath.Join(opts.Path, opts.Name+".up.fizz"), up)
g.File(f)
f = genny.NewFileS(filepath.Join(opts.Path, opts.Name+".down.fizz"), down)
g.File(f)
return g, nil
}

func fizzColType(s string) string {
switch strings.ToLower(s) {
case "int":
return "integer"
case "time", "datetime":
return "timestamp"
case "uuid.uuid", "uuid":
return "uuid"
case "nulls.float32", "nulls.float64":
return "float"
case "slices.string", "slices.uuid", "[]string":
return "varchar[]"
case "slices.float", "[]float", "[]float32", "[]float64":
return "numeric[]"
case "slices.int":
return "int[]"
case "slices.map":
return "jsonb"
case "float32", "float64", "float":
return "decimal"
case "blob", "[]byte":
return "blob"
default:
if strings.HasPrefix(s, "nulls.") {
return fizzColType(strings.Replace(s, "nulls.", "", -1))
}
return strings.ToLower(s)
}
}
116 changes: 116 additions & 0 deletions genny/fizz/ctable/create_table_test.go
@@ -0,0 +1,116 @@
package ctable

import (
"testing"

"github.com/gobuffalo/attrs"
"github.com/gobuffalo/genny/gentest"
"github.com/stretchr/testify/require"
)

func Test_New(t *testing.T) {
r := require.New(t)

ats, err := attrs.ParseArgs("id:uuid", "created_at:timestamp", "updated_at:timestamp", "name", "description:text", "age:int", "bar:nulls.String")
r.NoError(err)

cases := []struct {
Options *Options
Result string
}{
{
&Options{
TableName: "widgets",
Name: "create_widgets",
ForceDefaultTimestamps: true,
},
`create_table("widgets") {
t.Timestamps()
}`,
},
{
&Options{
TableName: "widgets",
Name: "create_widgets",
Attrs: ats,
ForceDefaultTimestamps: true,
},
`create_table("widgets") {
t.Column("id", "uuid", {primary: true})
t.Column("name", "string", {})
t.Column("description", "text", {})
t.Column("age", "integer", {})
t.Column("bar", "string", {null: true})
t.Timestamps()
}`,
},
}

for _, c := range cases {
g, err := New(c.Options)
r.NoError(err)

run := gentest.NewRunner()
run.With(g)

r.NoError(run.Run())

res := run.Results()

r.Len(res.Commands, 0)
r.Len(res.Files, 2)

f := res.Files[0]
r.Equal("migrations/create_widgets.down.fizz", f.Name())
r.Equal(`drop_table("widgets")`, f.String())

f = res.Files[1]
r.Equal("migrations/create_widgets.up.fizz", f.Name())
r.Equal(c.Result, f.String())
}
}

func Test_New_SQL(t *testing.T) {
r := require.New(t)

ats, err := attrs.ParseArgs("id:uuid", "created_at:timestamp", "updated_at:timestamp", "name", "description:text", "age:int", "bar:nulls.String")
r.NoError(err)

g, err := New(&Options{
TableName: "widgets",
Name: "create_widgets",
Type: "sql",
Translator: mockTranslator{},
Attrs: ats,
})
r.NoError(err)

run := gentest.NewRunner()
run.With(g)

r.NoError(run.Run())

res := run.Results()

r.Len(res.Commands, 0)
r.Len(res.Files, 2)

f := res.Files[0]
r.Equal("migrations/create_widgets.test.down.sql", f.Name())
r.Equal("drop table;", f.String())

f = res.Files[1]
r.Equal("migrations/create_widgets.test.up.sql", f.Name())
r.Equal("create table;", f.String())
}

func Test_New_Fail(t *testing.T) {
r := require.New(t)

g, err := New(&Options{
TableName: "",
Name: "create_widgets.fizz",
})
r.Error(err)
r.NotEmpty(g)
}

0 comments on commit 219ea04

Please sign in to comment.