Skip to content

Commit

Permalink
Begin alias work
Browse files Browse the repository at this point in the history
- Add the foreign key name to the relationships structs from the driver
- Modify text helpers to do a lot less work.
- Add an Aliases type in the configuration to allow specifying aliases
  as well as carrying the aliases into the templates. This will now be
  used everywhere for all Go type names inside the templates.
- Add tests for the new config pieces.
  • Loading branch information
aarondl committed Jun 9, 2018
1 parent 4f1ec23 commit f7989cb
Show file tree
Hide file tree
Showing 11 changed files with 731 additions and 88 deletions.
160 changes: 160 additions & 0 deletions boilingcore/aliases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package boilingcore

import (
"fmt"

"github.com/volatiletech/sqlboiler/drivers"
"github.com/volatiletech/sqlboiler/strmangle"
)

// Aliases defines aliases for the generation run
type Aliases struct {
Tables map[string]TableAlias `toml:"tables,omitempty" json:"tables,omitempty"`
Relationships map[string]RelationshipAlias `toml:"relationships,omitempty" json:"relationships,omitempty"`
}

// TableAlias defines the spellings for a table name in Go
type TableAlias struct {
UpPlural string `toml:"up_plural,omitempty" json:"up_plural,omitempty"`
UpSingular string `toml:"up_singular,omitempty" json:"up_singular,omitempty"`
DownPlural string `toml:"down_plural,omitempty" json:"down_plural,omitempty"`
DownSingular string `toml:"down_singular,omitempty" json:"down_singular,omitempty"`

Columns map[string]string `toml:"columns,omitempty" json:"columns,omitempty"`
}

// RelationshipAlias defines the naming for both sides of
// a foreign key.
type RelationshipAlias struct {
Local string `toml:"local,omitempty" json:"local,omitempty"`
Foreign string `toml:"foreign,omitempty" json:"foreign,omitempty"`
}

// FillAliases takes the table information from the driver
// and fills in aliases where the user has provided none.
//
// This leaves us with a complete list of Go names for all tables,
// columns, and relationships.
func FillAliases(a *Aliases, tables []drivers.Table) {
for _, t := range tables {
if t.IsJoinTable {
continue
}

if a.Tables == nil {
a.Tables = make(map[string]TableAlias)
}

table := a.Tables[t.Name]

if len(table.UpPlural) == 0 {
table.UpPlural = strmangle.TitleCase(strmangle.Plural(t.Name))
}
if len(table.UpSingular) == 0 {
table.UpSingular = strmangle.TitleCase(strmangle.Singular(t.Name))
}
if len(table.DownPlural) == 0 {
table.DownPlural = strmangle.CamelCase(strmangle.Plural(t.Name))
}
if len(table.DownSingular) == 0 {
table.DownSingular = strmangle.CamelCase(strmangle.Singular(t.Name))
}

if table.Columns == nil {
table.Columns = make(map[string]string)
}

for _, c := range t.Columns {
if _, ok := table.Columns[c.Name]; !ok {
table.Columns[c.Name] = strmangle.TitleCase(c.Name)
}
}

a.Tables[t.Name] = table

if a.Relationships == nil {
a.Relationships = make(map[string]RelationshipAlias)
}

for _, k := range t.FKeys {
r := a.Relationships[k.Name]
if len(r.Local) != 0 && len(r.Foreign) != 0 {
continue
}

local, foreign := txtNameToOne(k)
if len(r.Local) == 0 {
r.Local = local
}
if len(r.Foreign) == 0 {
r.Foreign = foreign
}

a.Relationships[k.Name] = r
}

for _, rel := range t.ToManyRelationships {
localFacingAlias, okLocal := a.Relationships[rel.JoinLocalFKeyName]
foreignFacingAlias, okForeign := a.Relationships[rel.JoinForeignFKeyName]

if okLocal && okForeign {
continue
}

local, foreign := txtNameToMany(rel)

switch {
case !okLocal && !okForeign:
localFacingAlias.Local = local
localFacingAlias.Foreign = foreign
foreignFacingAlias.Local = foreign
foreignFacingAlias.Foreign = local
case okLocal:
if len(localFacingAlias.Local) == 0 {
localFacingAlias.Local = local
}
if len(localFacingAlias.Foreign) == 0 {
localFacingAlias.Foreign = foreign
}

foreignFacingAlias.Local = localFacingAlias.Foreign
foreignFacingAlias.Foreign = localFacingAlias.Local
case okForeign:
if len(foreignFacingAlias.Local) == 0 {
foreignFacingAlias.Local = foreign
}
if len(foreignFacingAlias.Foreign) == 0 {
foreignFacingAlias.Foreign = local
}

localFacingAlias.Foreign = foreignFacingAlias.Local
localFacingAlias.Local = foreignFacingAlias.Foreign
}

a.Relationships[rel.JoinLocalFKeyName] = localFacingAlias
a.Relationships[rel.JoinForeignFKeyName] = foreignFacingAlias
}
}
}

// Table gets a table alias, panics if not found.
func (a Aliases) Table(table string) TableAlias {
t, ok := a.Tables[table]
if !ok {
panic("could not find table aliases for: " + table)
}

return t
}

// Column gets a column's aliased name, panics if not found.
func (a Aliases) Column(table, column string) string {
t := a.Table(table)

c, ok := t.Columns[column]
if !ok {
panic(fmt.Sprintf("could not find column alias for: %s.%s", table, column))
}

return c
}
235 changes: 235 additions & 0 deletions boilingcore/aliases_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package boilingcore

import (
"reflect"
"testing"

"github.com/volatiletech/sqlboiler/drivers"
)

func TestAliasesTables(t *testing.T) {
t.Parallel()

tables := []drivers.Table{
{
Name: "videos",
Columns: []drivers.Column{
{Name: "id"},
{Name: "name"},
},
},
}

t.Run("Automatic", func(t *testing.T) {
expect := TableAlias{
UpPlural: "Videos",
UpSingular: "Video",
DownPlural: "videos",
DownSingular: "video",
Columns: map[string]string{
"id": "ID",
"name": "Name",
},
}

a := Aliases{}
FillAliases(&a, tables)

if got := a.Tables["videos"]; !reflect.DeepEqual(expect, got) {
t.Errorf("it should fill in the blanks: %#v", got)
}
})

t.Run("UserOverride", func(t *testing.T) {
expect := TableAlias{
UpPlural: "NotVideos",
UpSingular: "NotVideo",
DownPlural: "notVideos",
DownSingular: "notVideo",
Columns: map[string]string{
"id": "NotID",
"name": "NotName",
},
}

a := Aliases{}
a.Tables = map[string]TableAlias{"videos": expect}
FillAliases(&a, tables)

if !reflect.DeepEqual(expect, a.Tables["videos"]) {
t.Error("it should not alter things that were specified by user")
}
})
}

func TestAliasesRelationships(t *testing.T) {
t.Parallel()

tables := []drivers.Table{
{
Name: "videos",
Columns: []drivers.Column{
{Name: "id"},
{Name: "name"},
},
FKeys: []drivers.ForeignKey{
{
Name: "fkey_1",
Table: "videos",
Column: "user_id",
ForeignTable: "users",
ForeignColumn: "id",
},
{
Name: "fkey_2",
Table: "videos",
Column: "publisher_id",
ForeignTable: "users",
ForeignColumn: "id",
},
{
Name: "fkey_3",
Table: "videos",
Column: "one_id",
Unique: true,
ForeignTable: "ones",
ForeignColumn: "one",
},
},
},
}

t.Run("Automatic", func(t *testing.T) {
expect1 := RelationshipAlias{
Local: "User",
Foreign: "Videos",
}
expect2 := RelationshipAlias{
Local: "Publisher",
Foreign: "PublisherVideos",
}
expect3 := RelationshipAlias{
Local: "One",
Foreign: "Video",
}

a := Aliases{}
FillAliases(&a, tables)

if got := a.Relationships["fkey_1"]; !reflect.DeepEqual(expect1, got) {
t.Errorf("bad values: %#v", got)
}
if got := a.Relationships["fkey_2"]; !reflect.DeepEqual(expect2, got) {
t.Errorf("bad values: %#v", got)
}
if got := a.Relationships["fkey_3"]; !reflect.DeepEqual(expect3, got) {
t.Errorf("bad values: %#v", got)
}
})

t.Run("UserOverride", func(t *testing.T) {
expect1 := RelationshipAlias{
Local: "TheUser",
Foreign: "Videos",
}
expect2 := RelationshipAlias{
Local: "Publisher",
Foreign: "PublishedVideos",
}
expect3 := RelationshipAlias{
Local: "TheOne",
Foreign: "AwesomeOneVideo",
}

a := Aliases{
Relationships: map[string]RelationshipAlias{
"fkey_1": {Local: "TheUser"},
"fkey_2": {Foreign: "PublishedVideos"},
"fkey_3": {Local: "TheOne", Foreign: "AwesomeOneVideo"},
},
}
FillAliases(&a, tables)

if got := a.Relationships["fkey_1"]; !reflect.DeepEqual(expect1, got) {
t.Errorf("bad values: %#v", got)
}
if got := a.Relationships["fkey_2"]; !reflect.DeepEqual(expect2, got) {
t.Errorf("bad values: %#v", got)
}
if got := a.Relationships["fkey_3"]; !reflect.DeepEqual(expect3, got) {
t.Errorf("bad values: %#v", got)
}
})
}

func TestAliasesRelationshipsJoinTable(t *testing.T) {
t.Parallel()

tables := []drivers.Table{
{
Name: "videos",
ToManyRelationships: []drivers.ToManyRelationship{
{
Table: "videos",
ForeignTable: "tags",
Column: "id",
ForeignColumn: "id",

ToJoinTable: true,
JoinTable: "video_tags",

JoinLocalFKeyName: "fk_video_id",
JoinLocalColumn: "video_id",
JoinForeignFKeyName: "fk_tag_id",
JoinForeignColumn: "tag_id",
},
},
},
}

t.Run("Automatic", func(t *testing.T) {
expect1 := RelationshipAlias{
Local: "Tags",
Foreign: "Videos",
}
expect2 := RelationshipAlias{
Local: "Videos",
Foreign: "Tags",
}

a := Aliases{}
FillAliases(&a, tables)

if got := a.Relationships["fk_video_id"]; !reflect.DeepEqual(expect1, got) {
t.Errorf("bad values: %#v", got)
}
if got := a.Relationships["fk_tag_id"]; !reflect.DeepEqual(expect2, got) {
t.Errorf("bad values: %#v", got)
}
})

t.Run("UserOverride", func(t *testing.T) {
expect1 := RelationshipAlias{
Local: "NotTags",
Foreign: "NotVideos",
}
expect2 := RelationshipAlias{
Local: "NotVideos",
Foreign: "NotTags",
}

a := Aliases{
Relationships: map[string]RelationshipAlias{
"fk_video_id": {Local: "NotTags", Foreign: "NotVideos"},
},
}
FillAliases(&a, tables)

if got := a.Relationships["fk_video_id"]; !reflect.DeepEqual(expect1, got) {
t.Errorf("bad values: %#v", got)
}
if got := a.Relationships["fk_tag_id"]; !reflect.DeepEqual(expect2, got) {
t.Errorf("bad values: %#v", got)
}
})
}
Loading

0 comments on commit f7989cb

Please sign in to comment.