Skip to content
This repository has been archived by the owner on Feb 24, 2024. It is now read-only.

Improved generator using flect to handle plurals properly #1455

Merged
merged 2 commits into from
Nov 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions buffalo/cmd/generate/resource.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package generate

import (
"github.com/markbates/inflect"
"github.com/pkg/errors"

"github.com/gobuffalo/buffalo/generators/resource"
"github.com/gobuffalo/flect/name"
"github.com/gobuffalo/makr"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -36,7 +36,7 @@ var ResourceCmd = &cobra.Command{
o.SkipTemplates = resourceOptions.SkipTemplates
if resourceOptions.ModelName != "" {
o.UseModel = true
o.Model = inflect.Name(resourceOptions.ModelName)
o.Model = name.New(resourceOptions.ModelName)
}

if err := o.Validate(); err != nil {
Expand Down
16 changes: 8 additions & 8 deletions generators/resource/a_resource-packr.go

Large diffs are not rendered by default.

43 changes: 22 additions & 21 deletions generators/resource/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@ import (
"os"
"strings"

"github.com/gobuffalo/flect"
"github.com/gobuffalo/flect/name"
"github.com/gobuffalo/meta"
"github.com/markbates/inflect"
)

// Generator for generating a new resource
type Generator struct {
App meta.App `json:"app"`
Name inflect.Name `json:"name"`
Model inflect.Name `json:"model"`
SkipMigration bool `json:"skip_migration"`
SkipModel bool `json:"skip_model"`
SkipTemplates bool `json:"skip_templates"`
UseModel bool `json:"use_model"`
FilesPath string `json:"files_path"`
ActionsPath string `json:"actions_path"`
Props []Prop `json:"props"`
Args []string `json:"args"`
App meta.App `json:"app"`
Name name.Ident `json:"name"`
Model name.Ident `json:"model"`
SkipMigration bool `json:"skip_migration"`
SkipModel bool `json:"skip_model"`
SkipTemplates bool `json:"skip_templates"`
UseModel bool `json:"use_model"`
FilesPath string `json:"files_path"`
ActionsPath string `json:"actions_path"`
Props []Prop `json:"props"`
Args []string `json:"args"`
}

// New constructs new options for generating a resource
Expand All @@ -33,27 +34,27 @@ func New(modelName string, args ...string) (Generator, error) {
o.App = meta.New(pwd)

if len(o.Args) > 0 {
o.Name = inflect.Name(o.Args[0])
o.Model = inflect.Name(o.Args[0])
o.Name = name.New(flect.Singularize(o.Args[0]))
o.Model = o.Name
}
o.Props = modelPropertiesFromArgs(o.Args)

o.FilesPath = o.Name.PluralUnder()
o.FilesPath = o.Name.File().Pluralize().String()
o.ActionsPath = o.FilesPath
if strings.Contains(string(o.Name), "/") {
parts := strings.Split(string(o.Name), "/")
o.Model = inflect.Name(parts[len(parts)-1])
o.ActionsPath = inflect.Underscore(o.Name.Resource())
if strings.Contains(o.Name.String(), "/") {
parts := strings.Split(o.Name.String(), "/")
o.Model = name.New(parts[len(parts)-1])
o.ActionsPath = o.Name.Resource().Underscore().String()
}
if modelName != "" {
o.Model = inflect.Name(modelName)
o.Model = name.New(modelName)
}
return o, o.Validate()
}

// Validate that the options have what you need to build a new resource
func (o Generator) Validate() error {
if len(o.Args) == 0 && o.Model == "" {
if len(o.Args) == 0 && o.Model.String() == "" {
return errors.New("you must specify a resource name")
}
return nil
Expand Down
20 changes: 19 additions & 1 deletion generators/resource/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,23 @@ func Test_New_WithNestedName(t *testing.T) {
g, err := New("", "admin/user")
r.NoError(err)
name := g.Name
r.Equal("admin_user_id", name.ParamID())
r.Equal("admin_user_id", name.ParamID().String())
}

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

g, err := New("", "admin/users")
r.NoError(err)
name := g.Name
r.Equal("admin_user_id", name.ParamID().String())
}

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

g, err := New("", "Admin/superFast/Plane")
r.NoError(err)
r.Equal("admin/super_fast/planes", g.FilesPath)
r.Equal("admin_super_fast_planes", g.ActionsPath)
}
2 changes: 1 addition & 1 deletion generators/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (res Generator) Run(root string, data makr.Data) error {
func (res Generator) modelCommand() makr.Command {
args := res.Args
args = append(args[:0], args[0+1:]...)
args = append([]string{"db", "g", "model", res.Model.UnderSingular()}, args...)
args = append([]string{"db", "g", "model", res.Model.Singularize().Underscore().String()}, args...)

if res.SkipMigration {
args = append(args, "--skip-migration")
Expand Down
68 changes: 34 additions & 34 deletions generators/resource/templates/actions/resource-json-xml.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ import (
// edit this file.

// Following naming logic is implemented in Buffalo:
// Model: Singular ({{.opts.Model.Model}})
// DB Table: Plural ({{.opts.Model.Table}})
// Model: Singular ({{.opts.Model.Proper}})
// DB Table: Plural ({{.opts.Model.Tableize}})
// Resource: Plural ({{.opts.Name.Resource}})
// Path: Plural (/{{.opts.Name.URL}})
// View Template Folder: Plural (/templates/{{.opts.FilesPath}}/)

// {{.opts.Name.Resource}}Resource is the resource for the {{.opts.Model.Model}} model
// {{.opts.Name.Resource}}Resource is the resource for the {{.opts.Model.Proper}} model
type {{.opts.Name.Resource}}Resource struct{
buffalo.Resource
}

// List gets all {{.opts.Model.ModelPlural}}. This function is mapped to the path
// List gets all {{.opts.Model.Group}}. This function is mapped to the path
// GET /{{.opts.Name.URL}}
func (v {{.opts.Name.Resource}}Resource) List(c buffalo.Context) error {
// Get the DB connection from the context
Expand All @@ -34,13 +34,13 @@ func (v {{.opts.Name.Resource}}Resource) List(c buffalo.Context) error {
return errors.WithStack(errors.New("no transaction found"))
}

{{.opts.Model.VarCasePlural}} := &models.{{.opts.Model.ModelPlural}}{}
{{.opts.Model.VarCasePlural}} := &models.{{.opts.Model.Group}}{}

// Paginate results. Params "page" and "per_page" control pagination.
// Default values are "page=1" and "per_page=20".
q := tx.PaginateFromParams(c.Params())

// Retrieve all {{.opts.Model.ModelPlural}} from the DB
// Retrieve all {{.opts.Model.Group}} from the DB
if err := q.All({{.opts.Model.VarCasePlural}}); err != nil {
return errors.WithStack(err)
}
Expand All @@ -51,7 +51,7 @@ func (v {{.opts.Name.Resource}}Resource) List(c buffalo.Context) error {
return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCasePlural}}))
}

// Show gets the data for one {{.opts.Model.Model}}. This function is mapped to
// Show gets the data for one {{.opts.Model.Proper}}. This function is mapped to
// the path GET /{{.opts.Name.URL}}/{{"{"}}{{.opts.Name.ParamID}}}
func (v {{.opts.Name.Resource}}Resource) Show(c buffalo.Context) error {
// Get the DB connection from the context
Expand All @@ -60,30 +60,30 @@ func (v {{.opts.Name.Resource}}Resource) Show(c buffalo.Context) error {
return errors.WithStack(errors.New("no transaction found"))
}

// Allocate an empty {{.opts.Model.Model}}
{{.opts.Model.VarCaseSingular}} := &models.{{.opts.Model.Model}}{}
// Allocate an empty {{.opts.Model.Proper}}
{{.opts.Model.VarCaseSingle}} := &models.{{.opts.Model.Proper}}{}

// To find the {{.opts.Model.Model}} the parameter {{.opts.Name.ParamID}} is used.
if err := tx.Find({{.opts.Model.VarCaseSingular}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
// To find the {{.opts.Model.Proper}} the parameter {{.opts.Name.ParamID}} is used.
if err := tx.Find({{.opts.Model.VarCaseSingle}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
return c.Error(404, err)
}

return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingular}}))
return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingle}}))
}

// New default implementation. Returns a 404
func (v {{.opts.Name.Resource}}Resource) New(c buffalo.Context) error {
return c.Error(404, errors.New("not available"))
}

// Create adds a {{.opts.Model.Model}} to the DB. This function is mapped to the
// Create adds a {{.opts.Model.Proper}} to the DB. This function is mapped to the
// path POST /{{.opts.Name.URL}}
func (v {{.opts.Name.Resource}}Resource) Create(c buffalo.Context) error {
// Allocate an empty {{.opts.Model.Model}}
{{.opts.Model.VarCaseSingular}} := &models.{{.opts.Model.Model}}{}
// Allocate an empty {{.opts.Model.Proper}}
{{.opts.Model.VarCaseSingle}} := &models.{{.opts.Model.Proper}}{}

// Bind {{.opts.Model.VarCaseSingular}} to the html form elements
if err := c.Bind({{.opts.Model.VarCaseSingular}}); err != nil {
// Bind {{.opts.Model.VarCaseSingle}} to the html form elements
if err := c.Bind({{.opts.Model.VarCaseSingle}}); err != nil {
return errors.WithStack(err)
}

Expand All @@ -94,7 +94,7 @@ func (v {{.opts.Name.Resource}}Resource) Create(c buffalo.Context) error {
}

// Validate the data from the html form
verrs, err := tx.ValidateAndCreate({{.opts.Model.VarCaseSingular}})
verrs, err := tx.ValidateAndCreate({{.opts.Model.VarCaseSingle}})
if err != nil {
return errors.WithStack(err)
}
Expand All @@ -104,15 +104,15 @@ func (v {{.opts.Name.Resource}}Resource) Create(c buffalo.Context) error {
return c.Render(400, r.{{.opts.MimeType}}(verrs))
}

return c.Render(201, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingular}}))
return c.Render(201, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingle}}))
}

// Edit default implementation. Returns a 404
func (v {{.opts.Name.Resource}}Resource) Edit(c buffalo.Context) error {
return c.Error(404, errors.New("not available"))
}

// Update changes a {{.opts.Model.Model}} in the DB. This function is mapped to
// Update changes a {{.opts.Model.Proper}} in the DB. This function is mapped to
// the path PUT /{{.opts.Name.URL}}/{{"{"}}{{.opts.Name.ParamID}}}
func (v {{.opts.Name.Resource}}Resource) Update(c buffalo.Context) error {
// Get the DB connection from the context
Expand All @@ -121,19 +121,19 @@ func (v {{.opts.Name.Resource}}Resource) Update(c buffalo.Context) error {
return errors.WithStack(errors.New("no transaction found"))
}

// Allocate an empty {{.opts.Model.Model}}
{{.opts.Model.VarCaseSingular}} := &models.{{.opts.Model.Model}}{}
// Allocate an empty {{.opts.Model.Proper}}
{{.opts.Model.VarCaseSingle}} := &models.{{.opts.Model.Proper}}{}

if err := tx.Find({{.opts.Model.VarCaseSingular}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
if err := tx.Find({{.opts.Model.VarCaseSingle}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
return c.Error(404, err)
}

// Bind {{.opts.Model.Model}} to the html form elements
if err := c.Bind({{.opts.Model.VarCaseSingular}}); err != nil {
// Bind {{.opts.Model.Proper}} to the html form elements
if err := c.Bind({{.opts.Model.VarCaseSingle}}); err != nil {
return errors.WithStack(err)
}

verrs, err := tx.ValidateAndUpdate({{.opts.Model.VarCaseSingular}})
verrs, err := tx.ValidateAndUpdate({{.opts.Model.VarCaseSingle}})
if err != nil {
return errors.WithStack(err)
}
Expand All @@ -143,10 +143,10 @@ func (v {{.opts.Name.Resource}}Resource) Update(c buffalo.Context) error {
return c.Render(400, r.{{.opts.MimeType}}(verrs))
}

return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingular}}))
return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingle}}))
}

// Destroy deletes a {{.opts.Model.Model}} from the DB. This function is mapped
// Destroy deletes a {{.opts.Model.Proper}} from the DB. This function is mapped
// to the path DELETE /{{.opts.Name.URL}}/{{"{"}}{{.opts.Name.ParamID}}}
func (v {{.opts.Name.Resource}}Resource) Destroy(c buffalo.Context) error {
// Get the DB connection from the context
Expand All @@ -155,17 +155,17 @@ func (v {{.opts.Name.Resource}}Resource) Destroy(c buffalo.Context) error {
return errors.WithStack(errors.New("no transaction found"))
}

// Allocate an empty {{.opts.Model.Model}}
{{.opts.Model.VarCaseSingular}} := &models.{{.opts.Model.Model}}{}
// Allocate an empty {{.opts.Model.Proper}}
{{.opts.Model.VarCaseSingle}} := &models.{{.opts.Model.Proper}}{}

// To find the {{.opts.Model.Model}} the parameter {{.opts.Name.ParamID}} is used.
if err := tx.Find({{.opts.Model.VarCaseSingular}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
// To find the {{.opts.Model.Proper}} the parameter {{.opts.Name.ParamID}} is used.
if err := tx.Find({{.opts.Model.VarCaseSingle}}, c.Param("{{.opts.Name.ParamID}}")); err != nil {
return c.Error(404, err)
}

if err := tx.Destroy({{.opts.Model.VarCaseSingular}}); err != nil {
if err := tx.Destroy({{.opts.Model.VarCaseSingle}}); err != nil {
return errors.WithStack(err)
}

return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingular}}))
return c.Render(200, r.{{.opts.MimeType}}({{.opts.Model.VarCaseSingle}}))
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type {{.opts.Name.Resource}}Resource struct{
{{ range $a := .actions }}
// {{$a}} default implementation.
func (v {{$.opts.Name.Resource}}Resource) {{$a}}(c buffalo.Context) error {
return c.Render(200, r.String("{{$.opts.Model.Model}}#{{$a}}"))
return c.Render(200, r.String("{{$.opts.Model.Proper}}#{{$a}}"))
}

{{end}}
Loading