Skip to content

Commit f7989cb

Browse files
committed
Begin alias work
- 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.
1 parent 4f1ec23 commit f7989cb

File tree

11 files changed

+731
-88
lines changed

11 files changed

+731
-88
lines changed

boilingcore/aliases.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package boilingcore
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/volatiletech/sqlboiler/drivers"
7+
"github.com/volatiletech/sqlboiler/strmangle"
8+
)
9+
10+
// Aliases defines aliases for the generation run
11+
type Aliases struct {
12+
Tables map[string]TableAlias `toml:"tables,omitempty" json:"tables,omitempty"`
13+
Relationships map[string]RelationshipAlias `toml:"relationships,omitempty" json:"relationships,omitempty"`
14+
}
15+
16+
// TableAlias defines the spellings for a table name in Go
17+
type TableAlias struct {
18+
UpPlural string `toml:"up_plural,omitempty" json:"up_plural,omitempty"`
19+
UpSingular string `toml:"up_singular,omitempty" json:"up_singular,omitempty"`
20+
DownPlural string `toml:"down_plural,omitempty" json:"down_plural,omitempty"`
21+
DownSingular string `toml:"down_singular,omitempty" json:"down_singular,omitempty"`
22+
23+
Columns map[string]string `toml:"columns,omitempty" json:"columns,omitempty"`
24+
}
25+
26+
// RelationshipAlias defines the naming for both sides of
27+
// a foreign key.
28+
type RelationshipAlias struct {
29+
Local string `toml:"local,omitempty" json:"local,omitempty"`
30+
Foreign string `toml:"foreign,omitempty" json:"foreign,omitempty"`
31+
}
32+
33+
// FillAliases takes the table information from the driver
34+
// and fills in aliases where the user has provided none.
35+
//
36+
// This leaves us with a complete list of Go names for all tables,
37+
// columns, and relationships.
38+
func FillAliases(a *Aliases, tables []drivers.Table) {
39+
for _, t := range tables {
40+
if t.IsJoinTable {
41+
continue
42+
}
43+
44+
if a.Tables == nil {
45+
a.Tables = make(map[string]TableAlias)
46+
}
47+
48+
table := a.Tables[t.Name]
49+
50+
if len(table.UpPlural) == 0 {
51+
table.UpPlural = strmangle.TitleCase(strmangle.Plural(t.Name))
52+
}
53+
if len(table.UpSingular) == 0 {
54+
table.UpSingular = strmangle.TitleCase(strmangle.Singular(t.Name))
55+
}
56+
if len(table.DownPlural) == 0 {
57+
table.DownPlural = strmangle.CamelCase(strmangle.Plural(t.Name))
58+
}
59+
if len(table.DownSingular) == 0 {
60+
table.DownSingular = strmangle.CamelCase(strmangle.Singular(t.Name))
61+
}
62+
63+
if table.Columns == nil {
64+
table.Columns = make(map[string]string)
65+
}
66+
67+
for _, c := range t.Columns {
68+
if _, ok := table.Columns[c.Name]; !ok {
69+
table.Columns[c.Name] = strmangle.TitleCase(c.Name)
70+
}
71+
}
72+
73+
a.Tables[t.Name] = table
74+
75+
if a.Relationships == nil {
76+
a.Relationships = make(map[string]RelationshipAlias)
77+
}
78+
79+
for _, k := range t.FKeys {
80+
r := a.Relationships[k.Name]
81+
if len(r.Local) != 0 && len(r.Foreign) != 0 {
82+
continue
83+
}
84+
85+
local, foreign := txtNameToOne(k)
86+
if len(r.Local) == 0 {
87+
r.Local = local
88+
}
89+
if len(r.Foreign) == 0 {
90+
r.Foreign = foreign
91+
}
92+
93+
a.Relationships[k.Name] = r
94+
}
95+
96+
for _, rel := range t.ToManyRelationships {
97+
localFacingAlias, okLocal := a.Relationships[rel.JoinLocalFKeyName]
98+
foreignFacingAlias, okForeign := a.Relationships[rel.JoinForeignFKeyName]
99+
100+
if okLocal && okForeign {
101+
continue
102+
}
103+
104+
local, foreign := txtNameToMany(rel)
105+
106+
switch {
107+
case !okLocal && !okForeign:
108+
localFacingAlias.Local = local
109+
localFacingAlias.Foreign = foreign
110+
foreignFacingAlias.Local = foreign
111+
foreignFacingAlias.Foreign = local
112+
case okLocal:
113+
if len(localFacingAlias.Local) == 0 {
114+
localFacingAlias.Local = local
115+
}
116+
if len(localFacingAlias.Foreign) == 0 {
117+
localFacingAlias.Foreign = foreign
118+
}
119+
120+
foreignFacingAlias.Local = localFacingAlias.Foreign
121+
foreignFacingAlias.Foreign = localFacingAlias.Local
122+
case okForeign:
123+
if len(foreignFacingAlias.Local) == 0 {
124+
foreignFacingAlias.Local = foreign
125+
}
126+
if len(foreignFacingAlias.Foreign) == 0 {
127+
foreignFacingAlias.Foreign = local
128+
}
129+
130+
localFacingAlias.Foreign = foreignFacingAlias.Local
131+
localFacingAlias.Local = foreignFacingAlias.Foreign
132+
}
133+
134+
a.Relationships[rel.JoinLocalFKeyName] = localFacingAlias
135+
a.Relationships[rel.JoinForeignFKeyName] = foreignFacingAlias
136+
}
137+
}
138+
}
139+
140+
// Table gets a table alias, panics if not found.
141+
func (a Aliases) Table(table string) TableAlias {
142+
t, ok := a.Tables[table]
143+
if !ok {
144+
panic("could not find table aliases for: " + table)
145+
}
146+
147+
return t
148+
}
149+
150+
// Column gets a column's aliased name, panics if not found.
151+
func (a Aliases) Column(table, column string) string {
152+
t := a.Table(table)
153+
154+
c, ok := t.Columns[column]
155+
if !ok {
156+
panic(fmt.Sprintf("could not find column alias for: %s.%s", table, column))
157+
}
158+
159+
return c
160+
}

boilingcore/aliases_test.go

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package boilingcore
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"github.com/volatiletech/sqlboiler/drivers"
8+
)
9+
10+
func TestAliasesTables(t *testing.T) {
11+
t.Parallel()
12+
13+
tables := []drivers.Table{
14+
{
15+
Name: "videos",
16+
Columns: []drivers.Column{
17+
{Name: "id"},
18+
{Name: "name"},
19+
},
20+
},
21+
}
22+
23+
t.Run("Automatic", func(t *testing.T) {
24+
expect := TableAlias{
25+
UpPlural: "Videos",
26+
UpSingular: "Video",
27+
DownPlural: "videos",
28+
DownSingular: "video",
29+
Columns: map[string]string{
30+
"id": "ID",
31+
"name": "Name",
32+
},
33+
}
34+
35+
a := Aliases{}
36+
FillAliases(&a, tables)
37+
38+
if got := a.Tables["videos"]; !reflect.DeepEqual(expect, got) {
39+
t.Errorf("it should fill in the blanks: %#v", got)
40+
}
41+
})
42+
43+
t.Run("UserOverride", func(t *testing.T) {
44+
expect := TableAlias{
45+
UpPlural: "NotVideos",
46+
UpSingular: "NotVideo",
47+
DownPlural: "notVideos",
48+
DownSingular: "notVideo",
49+
Columns: map[string]string{
50+
"id": "NotID",
51+
"name": "NotName",
52+
},
53+
}
54+
55+
a := Aliases{}
56+
a.Tables = map[string]TableAlias{"videos": expect}
57+
FillAliases(&a, tables)
58+
59+
if !reflect.DeepEqual(expect, a.Tables["videos"]) {
60+
t.Error("it should not alter things that were specified by user")
61+
}
62+
})
63+
}
64+
65+
func TestAliasesRelationships(t *testing.T) {
66+
t.Parallel()
67+
68+
tables := []drivers.Table{
69+
{
70+
Name: "videos",
71+
Columns: []drivers.Column{
72+
{Name: "id"},
73+
{Name: "name"},
74+
},
75+
FKeys: []drivers.ForeignKey{
76+
{
77+
Name: "fkey_1",
78+
Table: "videos",
79+
Column: "user_id",
80+
ForeignTable: "users",
81+
ForeignColumn: "id",
82+
},
83+
{
84+
Name: "fkey_2",
85+
Table: "videos",
86+
Column: "publisher_id",
87+
ForeignTable: "users",
88+
ForeignColumn: "id",
89+
},
90+
{
91+
Name: "fkey_3",
92+
Table: "videos",
93+
Column: "one_id",
94+
Unique: true,
95+
ForeignTable: "ones",
96+
ForeignColumn: "one",
97+
},
98+
},
99+
},
100+
}
101+
102+
t.Run("Automatic", func(t *testing.T) {
103+
expect1 := RelationshipAlias{
104+
Local: "User",
105+
Foreign: "Videos",
106+
}
107+
expect2 := RelationshipAlias{
108+
Local: "Publisher",
109+
Foreign: "PublisherVideos",
110+
}
111+
expect3 := RelationshipAlias{
112+
Local: "One",
113+
Foreign: "Video",
114+
}
115+
116+
a := Aliases{}
117+
FillAliases(&a, tables)
118+
119+
if got := a.Relationships["fkey_1"]; !reflect.DeepEqual(expect1, got) {
120+
t.Errorf("bad values: %#v", got)
121+
}
122+
if got := a.Relationships["fkey_2"]; !reflect.DeepEqual(expect2, got) {
123+
t.Errorf("bad values: %#v", got)
124+
}
125+
if got := a.Relationships["fkey_3"]; !reflect.DeepEqual(expect3, got) {
126+
t.Errorf("bad values: %#v", got)
127+
}
128+
})
129+
130+
t.Run("UserOverride", func(t *testing.T) {
131+
expect1 := RelationshipAlias{
132+
Local: "TheUser",
133+
Foreign: "Videos",
134+
}
135+
expect2 := RelationshipAlias{
136+
Local: "Publisher",
137+
Foreign: "PublishedVideos",
138+
}
139+
expect3 := RelationshipAlias{
140+
Local: "TheOne",
141+
Foreign: "AwesomeOneVideo",
142+
}
143+
144+
a := Aliases{
145+
Relationships: map[string]RelationshipAlias{
146+
"fkey_1": {Local: "TheUser"},
147+
"fkey_2": {Foreign: "PublishedVideos"},
148+
"fkey_3": {Local: "TheOne", Foreign: "AwesomeOneVideo"},
149+
},
150+
}
151+
FillAliases(&a, tables)
152+
153+
if got := a.Relationships["fkey_1"]; !reflect.DeepEqual(expect1, got) {
154+
t.Errorf("bad values: %#v", got)
155+
}
156+
if got := a.Relationships["fkey_2"]; !reflect.DeepEqual(expect2, got) {
157+
t.Errorf("bad values: %#v", got)
158+
}
159+
if got := a.Relationships["fkey_3"]; !reflect.DeepEqual(expect3, got) {
160+
t.Errorf("bad values: %#v", got)
161+
}
162+
})
163+
}
164+
165+
func TestAliasesRelationshipsJoinTable(t *testing.T) {
166+
t.Parallel()
167+
168+
tables := []drivers.Table{
169+
{
170+
Name: "videos",
171+
ToManyRelationships: []drivers.ToManyRelationship{
172+
{
173+
Table: "videos",
174+
ForeignTable: "tags",
175+
Column: "id",
176+
ForeignColumn: "id",
177+
178+
ToJoinTable: true,
179+
JoinTable: "video_tags",
180+
181+
JoinLocalFKeyName: "fk_video_id",
182+
JoinLocalColumn: "video_id",
183+
JoinForeignFKeyName: "fk_tag_id",
184+
JoinForeignColumn: "tag_id",
185+
},
186+
},
187+
},
188+
}
189+
190+
t.Run("Automatic", func(t *testing.T) {
191+
expect1 := RelationshipAlias{
192+
Local: "Tags",
193+
Foreign: "Videos",
194+
}
195+
expect2 := RelationshipAlias{
196+
Local: "Videos",
197+
Foreign: "Tags",
198+
}
199+
200+
a := Aliases{}
201+
FillAliases(&a, tables)
202+
203+
if got := a.Relationships["fk_video_id"]; !reflect.DeepEqual(expect1, got) {
204+
t.Errorf("bad values: %#v", got)
205+
}
206+
if got := a.Relationships["fk_tag_id"]; !reflect.DeepEqual(expect2, got) {
207+
t.Errorf("bad values: %#v", got)
208+
}
209+
})
210+
211+
t.Run("UserOverride", func(t *testing.T) {
212+
expect1 := RelationshipAlias{
213+
Local: "NotTags",
214+
Foreign: "NotVideos",
215+
}
216+
expect2 := RelationshipAlias{
217+
Local: "NotVideos",
218+
Foreign: "NotTags",
219+
}
220+
221+
a := Aliases{
222+
Relationships: map[string]RelationshipAlias{
223+
"fk_video_id": {Local: "NotTags", Foreign: "NotVideos"},
224+
},
225+
}
226+
FillAliases(&a, tables)
227+
228+
if got := a.Relationships["fk_video_id"]; !reflect.DeepEqual(expect1, got) {
229+
t.Errorf("bad values: %#v", got)
230+
}
231+
if got := a.Relationships["fk_tag_id"]; !reflect.DeepEqual(expect2, got) {
232+
t.Errorf("bad values: %#v", got)
233+
}
234+
})
235+
}

0 commit comments

Comments
 (0)