Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
stanislas-m committed Oct 5, 2019
2 parents c464414 + d04f898 commit 841453f
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 4 deletions.
44 changes: 44 additions & 0 deletions executors.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,50 @@ func (c *Connection) Update(model interface{}, excludeColumns ...string) error {
})
}

// UpdateColumns writes changes from an entry to the database, including only the given columns
// or all columns if no column names are provided.
// It updates the `updated_at` column automatically.
//
// If model is a slice, each item of the slice is updated in the database.
func (c *Connection) UpdateColumns(model interface{}, columnNames ...string) error {
sm := &Model{Value: model}
return sm.iterate(func(m *Model) error {
return c.timeFunc("Update", func() error {
var err error

if err = m.beforeSave(c); err != nil {
return err
}
if err = m.beforeUpdate(c); err != nil {
return err
}

tn := m.TableName()

cols := columns.Columns{}
if len(columnNames) > 0 && tn == sm.TableName() {
cols = columns.NewColumnsWithAlias(tn, m.As)
cols.Add(columnNames...)

} else {
cols = columns.ForStructWithAlias(model, tn, m.As)
}
cols.Remove("id", "created_at")

m.touchUpdatedAt()

if err = c.Dialect.Update(c.Store, m, cols); err != nil {
return err
}
if err = m.afterUpdate(c); err != nil {
return err
}

return m.afterSave(c)
})
})
}

// Destroy deletes a given entry from the database.
//
// If model is a slice, each item of the slice is deleted from the database.
Expand Down
117 changes: 117 additions & 0 deletions executors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,123 @@ func Test_Update(t *testing.T) {
})
}

func Test_UpdateColumns(t *testing.T) {
if PDB == nil {
t.Skip("skipping integration tests")
}
transaction(func(tx *Connection) {
r := require.New(t)

user := User{Name: nulls.NewString("Mark")}
tx.Create(&user)

r.NotZero(user.CreatedAt)
r.NotZero(user.UpdatedAt)

user.Name.String = "Fulano"
user.UserName = "Fulano"
err := tx.UpdateColumns(&user, "user_name") // Update UserName field/column only
r.NoError(err)

r.NoError(tx.Reload(&user))
r.Equal(user.Name.String, "Mark") // Name column should not be updated
r.Equal(user.UserName, "Fulano")
})
}

func Test_UpdateColumns_MultipleColumns(t *testing.T) {
if PDB == nil {
t.Skip("skipping integration tests")
}
transaction(func(tx *Connection) {
r := require.New(t)

user := User{Name: nulls.NewString("Mark"), UserName: "Sagan", Email: "test@example.com"}
tx.Create(&user)

r.NotZero(user.CreatedAt)
r.NotZero(user.UpdatedAt)

user.Name.String = "Ping"
user.UserName = "Pong"
user.Email = "fulano@example"
err := tx.UpdateColumns(&user, "name", "user_name") // Update multiple columns
r.NoError(err)

r.NoError(tx.Reload(&user))
r.Equal(user.Name.String, "Ping")
r.Equal(user.UserName, "Pong")
r.Equal(user.Email, "test@example.com") // Email should not be updated
})
}

func Test_UpdateColumns_All(t *testing.T) {
if PDB == nil {
t.Skip("skipping integration tests")
}
transaction(func(tx *Connection) {
r := require.New(t)

user := User{Name: nulls.NewString("Mark"), UserName: "Sagan"}
tx.Create(&user)

r.NotZero(user.CreatedAt)
r.NotZero(user.UpdatedAt)

user.Name.String = "Ping"
user.UserName = "Pong"
user.Email = "ping@pong.com"
err := tx.UpdateColumns(&user) // Update all columns
r.NoError(err)

r.NoError(tx.Reload(&user))
r.Equal(user.Name.String, "Ping")
r.Equal(user.UserName, "Pong")
r.Equal(user.Email, "ping@pong.com")
})
}

func Test_UpdateColumns_With_Slice(t *testing.T) {
if PDB == nil {
t.Skip("skipping integration tests")
}
transaction(func(tx *Connection) {
r := require.New(t)

user := Users{
{
Name: nulls.NewString("Mark"),
UserName: "Ping",
},
{
Name: nulls.NewString("Larry"),
UserName: "Pong",
},
}
tx.Create(&user)

r.NotZero(user[0].CreatedAt)
r.NotZero(user[0].UpdatedAt)

r.NotZero(user[1].CreatedAt)
r.NotZero(user[1].UpdatedAt)

user[0].Name.String = "Fulano"
user[0].UserName = "Thor"
user[1].Name.String = "Fulana"
user[1].UserName = "Freya"

err := tx.UpdateColumns(&user, "name") // Update Name field/column only
r.NoError(err)

r.NoError(tx.Reload(&user))
r.Equal(user[0].Name.String, "Fulano")
r.Equal(user[0].UserName, "Ping") // UserName should not be updated
r.Equal(user[1].Name.String, "Fulana")
r.Equal(user[1].UserName, "Pong") // UserName should not be updated
})
}

func Test_Update_With_Slice(t *testing.T) {
if PDB == nil {
t.Skip("skipping integration tests")
Expand Down
19 changes: 19 additions & 0 deletions genny/fizz/ctable/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type Options struct {
Type string
// ForceDefaultTimestamps enables auto timestamping for the generated table.
ForceDefaultTimestamps bool `json:"force_default_timestamps"`
// ForceDefaultID enables auto UUID for the generated table.
ForceDefaultID bool `json:"force_default_id"`
}

// Validate that options are usuable
Expand All @@ -53,5 +55,22 @@ func (opts *Options) Validate() error {
if opts.Type == "sql" && opts.Translator == nil {
return errors.New("sql migrations require a fizz translator")
}
if opts.ForceDefaultID {
var idFound bool
for _, a := range opts.Attrs {
switch a.Name.Underscore().String() {
case "id":
idFound = true
}
}
if !idFound {
// Add a default UUID
id, err := attrs.Parse("id:uuid")
if err != nil {
return err
}
opts.Attrs = append([]attrs.Attr{id}, opts.Attrs...)
}
}
return nil
}
3 changes: 2 additions & 1 deletion genny/model/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
func buildImports(opts *Options) []string {
imps := map[string]bool{
"github.com/gobuffalo/validate": true,
"github.com/gobuffalo/pop": true,
}
if opts.Encoding == "jsonapi" {
imps["github.com/google/jsonapi"] = true
Expand All @@ -19,7 +20,7 @@ func buildImports(opts *Options) []string {
ats := opts.Attrs
for _, a := range ats {
switch a.GoType() {
case "uuid":
case "uuid", "uuid.UUID":
imps["github.com/gofrs/uuid"] = true
case "time.Time":
imps["time"] = true
Expand Down
9 changes: 9 additions & 0 deletions genny/model/model.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package model

import (
"strings"

"github.com/gobuffalo/flect"
"github.com/gobuffalo/flect/name"
"github.com/gobuffalo/genny"
Expand Down Expand Up @@ -33,6 +35,13 @@ func New(opts *Options) (*genny.Generator, error) {
}
help := map[string]interface{}{
"capitalize": flect.Capitalize,
"trim_package": func(t string) string {
i := strings.LastIndex(t, ".")
if i == -1 {
return t
}
return t[i+1:]
},
}

t := gogen.TemplateTransformer(ctx, help)
Expand Down
2 changes: 1 addition & 1 deletion genny/model/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (opts *Options) forceDefaults() error {
if err != nil {
return err
}
opts.Attrs = append(opts.Attrs, id)
opts.Attrs = append([]attrs.Attr{id}, opts.Attrs...)
}

// Add default timestamp columns if they were not provided
Expand Down
2 changes: 1 addition & 1 deletion genny/model/templates/-path-/-name-.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func ({{.model.Name.Char}} *{{.model.Name.Proper}}) Validate(tx *pop.Connection)
{{- if .model.Validations }}
return validate.Validate(
{{- range $a := .model.Validations }}
&validators.{{capitalize $a.GoType}}IsPresent{Field: {{$.model.Name.Char}}.{{$a.Name.Pascalize}}, Name: "{{$a.Name.Pascalize}}"},
&validators.{{capitalize (trim_package $a.GoType)}}IsPresent{Field: {{$.model.Name.Char}}.{{$a.Name.Pascalize}}, Name: "{{$a.Name.Pascalize}}"},
{{- end}}
), nil
{{- else }}
Expand Down
2 changes: 1 addition & 1 deletion packrd/packed-packr.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions soda/cmd/generate/model_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ var ModelCmd = &cobra.Command{
Path: path,
Type: modelCmdConfig.MigrationType,
Translator: translator,
ForceDefaultID: true,
ForceDefaultTimestamps: true,
})
if err != nil {
Expand Down

0 comments on commit 841453f

Please sign in to comment.