From c6b0cd81d36776f3ee6f6e5f9a8c8edff29358e9 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Tue, 20 Jan 2015 16:43:30 +0200 Subject: [PATCH 1/2] Implement GonicMapper --- mapper.go | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ mapper_test.go | 45 +++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 mapper_test.go diff --git a/mapper.go b/mapper.go index 3cc4735e..1c080a66 100644 --- a/mapper.go +++ b/mapper.go @@ -152,6 +152,111 @@ func (mapper SnakeMapper) TableName(t string) string { return t } +// GonicMapper implements IMapper. It will consider initialisms when mapping names. +// E.g. id -> ID, user -> User and to table names: UserID -> user_id, MyUID -> my_uid +type GonicMapper struct{} + +func isASCIIUpper(r rune) bool { + return 'A' <= r && r <= 'Z' +} + +func toASCIIUpper(r rune) rune { + if 'a' <= r && r <= 'z' { + r -= ('a' - 'A') + } + return r +} + +func gonicCasedName(name string) string { + newstr := make([]rune, 0, len(name)+3) + for idx, chr := range name { + if isASCIIUpper(chr) && idx > 0 { + if !isASCIIUpper(newstr[len(newstr)-1]) { + newstr = append(newstr, '_') + } + } + + if !isASCIIUpper(chr) && idx > 1 { + l := len(newstr) + if isASCIIUpper(newstr[l-1]) && isASCIIUpper(newstr[l-2]) { + newstr = append(newstr, newstr[l-1]) + newstr[l-1] = '_' + } + } + + newstr = append(newstr, chr) + } + return strings.ToLower(string(newstr)) +} + +func (mapper GonicMapper) Obj2Table(name string) string { + return gonicCasedName(name) +} + +// A list of common initialisms taken from golang/lint +var commonInitialisms = map[string]bool{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SSH": true, + "TLS": true, + "TTL": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, +} + +func gonicTitleCasedName(name string) string { + newstr := make([]rune, 0) + + name = strings.ToLower(name) + parts := strings.Split(name, "_") + + for _, p := range parts { + _, isInitialism := commonInitialisms[strings.ToUpper(p)] + for i, r := range p { + if i == 0 || isInitialism { + r = toASCIIUpper(r) + } + newstr = append(newstr, r) + } + } + + return string(newstr) +} + +func (mapper GonicMapper) Table2Obj(name string) string { + return gonicTitleCasedName(name) +} + +func (mapper GonicMapper) TableName(t string) string { + return t +} + // provide prefix table name support type PrefixMapper struct { Mapper IMapper diff --git a/mapper_test.go b/mapper_test.go new file mode 100644 index 00000000..7dff090a --- /dev/null +++ b/mapper_test.go @@ -0,0 +1,45 @@ +package core + +import ( + "testing" +) + +func TestGonicMapperFromObj(t *testing.T) { + testCases := map[string]string{ + "HTTPLib": "http_lib", + "id": "id", + "ID": "id", + "IDa": "i_da", + "iDa": "i_da", + "IDAa": "id_aa", + "aID": "a_id", + "aaID": "aa_id", + "aaaID": "aaa_id", + "MyREalFunkYLONgNAME": "my_r_eal_funk_ylo_ng_name", + } + + for in, expected := range testCases { + out := gonicCasedName(in) + if out != expected { + t.Errorf("Given %s, expected %s but got %s", in, expected, out) + } + } +} + +func TestGonicMapperToObj(t *testing.T) { + testCases := map[string]string{ + "http_lib": "HTTPLib", + "id": "ID", + "ida": "Ida", + "id_aa": "IDAa", + "aa_id": "AaID", + "my_r_eal_funk_ylo_ng_name": "MyREalFunkYloNgName", + } + + for in, expected := range testCases { + out := gonicTitleCasedName(in) + if out != expected { + t.Errorf("Given %s, expected %s but got %s", in, expected, out) + } + } +} From 8ee59e351b1d200af9f3f174c8008ffe4c5dd3c7 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Tue, 20 Jan 2015 17:11:10 +0200 Subject: [PATCH 2/2] Make initialisms configurable --- mapper.go | 56 +++++++++++++++++++++++--------------------------- mapper_test.go | 2 +- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/mapper.go b/mapper.go index 1c080a66..e0ce24aa 100644 --- a/mapper.go +++ b/mapper.go @@ -154,7 +154,7 @@ func (mapper SnakeMapper) TableName(t string) string { // GonicMapper implements IMapper. It will consider initialisms when mapping names. // E.g. id -> ID, user -> User and to table names: UserID -> user_id, MyUID -> my_uid -type GonicMapper struct{} +type GonicMapper map[string]bool func isASCIIUpper(r rune) bool { return 'A' <= r && r <= 'Z' @@ -193,8 +193,31 @@ func (mapper GonicMapper) Obj2Table(name string) string { return gonicCasedName(name) } -// A list of common initialisms taken from golang/lint -var commonInitialisms = map[string]bool{ +func (mapper GonicMapper) Table2Obj(name string) string { + newstr := make([]rune, 0) + + name = strings.ToLower(name) + parts := strings.Split(name, "_") + + for _, p := range parts { + _, isInitialism := mapper[strings.ToUpper(p)] + for i, r := range p { + if i == 0 || isInitialism { + r = toASCIIUpper(r) + } + newstr = append(newstr, r) + } + } + + return string(newstr) +} + +func (mapper GonicMapper) TableName(t string) string { + return t +} + +// A GonicMapper that contains a list of common initialisms taken from golang/lint +var LintGonicMapper = GonicMapper{ "API": true, "ASCII": true, "CPU": true, @@ -230,33 +253,6 @@ var commonInitialisms = map[string]bool{ "XSS": true, } -func gonicTitleCasedName(name string) string { - newstr := make([]rune, 0) - - name = strings.ToLower(name) - parts := strings.Split(name, "_") - - for _, p := range parts { - _, isInitialism := commonInitialisms[strings.ToUpper(p)] - for i, r := range p { - if i == 0 || isInitialism { - r = toASCIIUpper(r) - } - newstr = append(newstr, r) - } - } - - return string(newstr) -} - -func (mapper GonicMapper) Table2Obj(name string) string { - return gonicTitleCasedName(name) -} - -func (mapper GonicMapper) TableName(t string) string { - return t -} - // provide prefix table name support type PrefixMapper struct { Mapper IMapper diff --git a/mapper_test.go b/mapper_test.go index 7dff090a..043087a2 100644 --- a/mapper_test.go +++ b/mapper_test.go @@ -37,7 +37,7 @@ func TestGonicMapperToObj(t *testing.T) { } for in, expected := range testCases { - out := gonicTitleCasedName(in) + out := LintGonicMapper.Table2Obj(in) if out != expected { t.Errorf("Given %s, expected %s but got %s", in, expected, out) }