Skip to content

Commit 24af310

Browse files
authored
Merge pull request #7 from getkalido/feature/rework
Document and clean migration functionality.
2 parents 1da7886 + 43354aa commit 24af310

File tree

5 files changed

+1057
-375
lines changed

5 files changed

+1057
-375
lines changed

casing.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package migrations
2+
3+
import (
4+
"strings"
5+
"time"
6+
"unicode"
7+
"unicode/utf8"
8+
9+
"github.com/pkg/errors"
10+
)
11+
12+
var (
13+
// ErrUnknownNamingConvention indicates that an attempt was made to
14+
// create a migration, without specifying a name.
15+
ErrUnknownNamingConvention = errors.New("unknown naming convention")
16+
)
17+
18+
// MigrationNameConvention represents a naming convention in terms of
19+
// casing and underscores for a Migrator.
20+
type MigrationNameConvention string
21+
22+
const (
23+
// CamelCase represents a camelCase naming convention.
24+
CamelCase MigrationNameConvention = "camelCase"
25+
26+
// SnakeCase represents a snake_case naming convention.
27+
SnakeCase MigrationNameConvention = "snakeCase"
28+
)
29+
30+
// Caser is intended to convert a description to a particular naming
31+
// convention. It should take spaces into account.
32+
type Caser interface {
33+
// ToFileCase converts a description to the casing required for
34+
// a filename. There should not be any spaces in the output.
35+
ToFileCase(time.Time, string) string
36+
37+
// ToFileCase converts a description to the casing required for
38+
// a function name. There should not be any spaces in the output.
39+
ToFuncCase(time.Time, string) string
40+
}
41+
42+
// GetCaser returns the appropriate caser for the given naming convention.
43+
func GetCaser(convention MigrationNameConvention) (Caser, error) {
44+
switch convention {
45+
case SnakeCase:
46+
return SnakeCaser{}, nil
47+
case CamelCase:
48+
return CamelCaser{}, nil
49+
default:
50+
err := errors.Wrapf(
51+
ErrUnknownNamingConvention,
52+
"unknown convention %s",
53+
convention,
54+
)
55+
return nil, err
56+
}
57+
}
58+
59+
var _ Caser = (*SnakeCaser)(nil)
60+
61+
// SnakeCaser will attempt to use snake_case for filenames.
62+
type SnakeCaser struct{}
63+
64+
func (cc SnakeCaser) ToFileCase(date time.Time, input string) string {
65+
// Panicking here is acceptable, because builder.WriteString
66+
// should only ever return an error when out of memory.
67+
builder := strings.Builder{}
68+
_, err := builder.WriteString(date.Format("20060102150405"))
69+
if err != nil {
70+
panic(err)
71+
}
72+
73+
_, err = builder.WriteRune('_')
74+
if err != nil {
75+
panic(err)
76+
}
77+
78+
description := ConvertCamelCaseToSnakeCase(input)
79+
_, err = builder.WriteString(description)
80+
if err != nil {
81+
panic(err)
82+
}
83+
return builder.String()
84+
}
85+
86+
func (cc SnakeCaser) ToFuncCase(date time.Time, input string) string {
87+
// Panicking here is acceptable, because builder.WriteString
88+
// should only ever return an error when out of memory.
89+
builder := strings.Builder{}
90+
_, err := builder.WriteString(date.Format("20060102150405"))
91+
if err != nil {
92+
panic(err)
93+
}
94+
description := ConvertSnakeCaseToCamelCase(input)
95+
_, err = builder.WriteString(description)
96+
if err != nil {
97+
panic(err)
98+
}
99+
return builder.String()
100+
}
101+
102+
var _ Caser = (*CamelCaser)(nil)
103+
104+
// SnakeCaser will attempt to use camelCase for filenames.
105+
type CamelCaser struct{}
106+
107+
func (cc CamelCaser) ToFileCase(date time.Time, input string) string {
108+
// Panicking here is acceptable, because builder.WriteString
109+
// should only ever return an error when out of memory.
110+
builder := strings.Builder{}
111+
_, err := builder.WriteString(date.Format("20060102150405"))
112+
if err != nil {
113+
panic(err)
114+
}
115+
description := ConvertSnakeCaseToCamelCase(input)
116+
_, err = builder.WriteString(description)
117+
if err != nil {
118+
panic(err)
119+
}
120+
return builder.String()
121+
}
122+
123+
func (cc CamelCaser) ToFuncCase(date time.Time, input string) string {
124+
// Panicking here is acceptable, because builder.WriteString
125+
// should only ever return an error when out of memory.
126+
builder := strings.Builder{}
127+
_, err := builder.WriteString(date.Format("20060102150405"))
128+
if err != nil {
129+
panic(err)
130+
}
131+
description := ConvertSnakeCaseToCamelCase(input)
132+
_, err = builder.WriteString(description)
133+
if err != nil {
134+
panic(err)
135+
}
136+
return builder.String()
137+
}
138+
139+
// ConvertCamelCaseToSnakeCase converts a potentially camel-case
140+
// string to snake-case. Should be Unicode-safe.
141+
//
142+
// Spaces are converted to underscores and any uppercase letters
143+
// are replaced with an underscore and the lowercase version of
144+
// the same letter.
145+
func ConvertCamelCaseToSnakeCase(word string) (result string) {
146+
if len(word) == 0 {
147+
return ""
148+
}
149+
150+
var err error
151+
builder := &strings.Builder{}
152+
char, _ := utf8.DecodeRuneInString(word)
153+
_, err = builder.WriteRune(unicode.ToLower(char))
154+
if err != nil {
155+
panic(err)
156+
}
157+
158+
var prevWordBoundary bool
159+
for _, char := range word[1:] {
160+
if char == '_' || unicode.IsSpace(char) {
161+
prevWordBoundary = true
162+
continue
163+
}
164+
if prevWordBoundary || unicode.IsUpper(char) {
165+
prevWordBoundary = false
166+
_, err = builder.WriteRune('_')
167+
if err != nil {
168+
panic(err)
169+
}
170+
}
171+
172+
_, err = builder.WriteRune(unicode.ToLower(char))
173+
if err != nil {
174+
panic(err)
175+
}
176+
}
177+
178+
return builder.String()
179+
}
180+
181+
// ConvertSnakeCaseToCamelCase converts a potentially snake-case
182+
// string to camel-case. Should be Unicode-safe.
183+
//
184+
// Spaces and underscores are removed and any letter following
185+
// immediately after these removed characters will be converted
186+
// to uppercase.
187+
func ConvertSnakeCaseToCamelCase(word string) (result string) {
188+
builder := &strings.Builder{}
189+
var prevWordBoundary bool
190+
for _, char := range word {
191+
if char == '_' || unicode.IsSpace(char) {
192+
prevWordBoundary = true
193+
continue
194+
}
195+
196+
var err error
197+
if prevWordBoundary {
198+
prevWordBoundary = false
199+
_, err = builder.WriteRune(unicode.ToUpper(char))
200+
} else {
201+
_, err = builder.WriteRune(unicode.ToLower(char))
202+
}
203+
if err != nil {
204+
panic(err)
205+
}
206+
}
207+
208+
return builder.String()
209+
}

go.mod

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
module github.com/getkalido/hb_migrations
22

3-
go 1.14
3+
go 1.18
44

55
require (
66
github.com/go-pg/pg/v10 v10.10.6
77
github.com/pkg/errors v0.9.1
88
)
9+
10+
require (
11+
github.com/go-pg/zerochecker v0.2.0 // indirect
12+
github.com/jinzhu/inflection v1.0.0 // indirect
13+
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
14+
github.com/vmihailenco/bufpool v0.1.11 // indirect
15+
github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
16+
github.com/vmihailenco/tagparser v0.1.2 // indirect
17+
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
18+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
19+
golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7 // indirect
20+
mellium.im/sasl v0.3.1 // indirect
21+
)

go.sum

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,13 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
2424
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
2525
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
2626
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
27-
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
2827
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
29-
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
3028
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
3129
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
3230
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
3331
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
34-
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
3532
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
3633
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
37-
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
3834
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
3935
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
4036
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@@ -52,7 +48,6 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
5248
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
5349
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
5450
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
55-
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
5651
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
5752
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
5853
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
@@ -76,9 +71,7 @@ github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vb
7671
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
7772
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
7873
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
79-
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
8074
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
81-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
8275
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
8376
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
8477
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
@@ -93,7 +86,6 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r
9386
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
9487
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
9588
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
96-
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
9789
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
9890
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
9991
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
@@ -110,7 +102,6 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w
110102
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
111103
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
112104
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
113-
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
114105
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
115106
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
116107
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -119,7 +110,6 @@ golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7 h1:c20P3CcPbopVp2f7099WLOqSN
119110
golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
120111
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
121112
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
122-
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
123113
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
124114
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
125115
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -144,10 +134,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
144134
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
145135
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
146136
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
147-
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
148137
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
149138
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
150-
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
151139
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
152140
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
153141
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -164,5 +152,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie
164152
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
165153
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
166154
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
167-
mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w=
168155
mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ=
156+
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
157+
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=

0 commit comments

Comments
 (0)