diff --git a/custom_data.go b/custom_data.go index 9a2dfc7..efb445f 100644 --- a/custom_data.go +++ b/custom_data.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" ) func init() { @@ -75,6 +76,10 @@ func LoadInflections(r io.Reader) error { defer singularMoot.Unlock() for s, p := range m { + if strings.Contains(s, " ") || strings.Contains(p, " ") { + // flect works with parts, so multi-words should not be allowed + return fmt.Errorf("inflection elements should be a single word") + } singleToPlural[s] = p pluralToSingle[p] = s } diff --git a/flect_test.go b/flect_test.go index d6b92e8..59782bc 100644 --- a/flect_test.go +++ b/flect_test.go @@ -16,8 +16,8 @@ type tt struct { func Test_LoadInflections(t *testing.T) { r := require.New(t) m := map[string]string{ - "beatle": "the beatles", - "xyz": "zyx", + "baby": "bebe", + "xyz": "zyx", } b, err := json.Marshal(m) @@ -33,6 +33,30 @@ func Test_LoadInflections(t *testing.T) { } } +func Test_LoadInflectionsWrongSingular(t *testing.T) { + r := require.New(t) + m := map[string]string{ + "a file": "files", + } + + b, err := json.Marshal(m) + r.NoError(err) + + r.Error(LoadInflections(bytes.NewReader(b))) +} + +func Test_LoadInflectionsWrongPlural(t *testing.T) { + r := require.New(t) + m := map[string]string{ + "beatle": "the beatles", + } + + b, err := json.Marshal(m) + r.NoError(err) + + r.Error(LoadInflections(bytes.NewReader(b))) +} + func Test_LoadAcronyms(t *testing.T) { r := require.New(t) m := []string{ @@ -53,6 +77,16 @@ func Test_LoadAcronyms(t *testing.T) { var singlePluralAssertions = []tt{ {"", ""}, + {"Car", "Cars"}, + {"Boy", "Boys"}, + {"GoodBoy", "GoodBoys"}, + {"Axis", "Axes"}, + {"Child", "Children"}, + {"GoodChild", "GoodChildren"}, + {"SmartPerson", "SmartPeople"}, + {"SuperbOx", "SuperbOxen"}, + {"WildOx", "WildOxen"}, + {"wild_ox", "wild_oxen"}, {"ability", "abilities"}, {"address", "addresses"}, {"agency", "agencies"}, @@ -76,6 +110,7 @@ var singlePluralAssertions = []tt{ {"child", "children"}, {"circus", "circuses"}, {"city", "cities"}, + {"collapse", "collapses"}, {"comment", "comments"}, {"crisis", "crises"}, {"criterion", "criteria"}, @@ -89,6 +124,7 @@ var singlePluralAssertions = []tt{ {"diagnosis_a", "diagnosis_as"}, {"diagnosis", "diagnoses"}, {"dwarf", "dwarves"}, + {"eclipse", "eclipses"}, {"edge", "edges"}, {"elf", "elves"}, {"ellipsis", "ellipses"}, @@ -105,6 +141,7 @@ var singlePluralAssertions = []tt{ {"fox", "foxes"}, {"funky jeans", "funky jeans"}, {"fuse", "fuses"}, + {"glimpse", "glimpses"}, {"goose", "geese"}, {"great_person", "great_people"}, {"half", "halves"}, @@ -115,11 +152,13 @@ var singlePluralAssertions = []tt{ {"index", "indices"}, {"information", "information"}, {"jeans", "jeans"}, + {"lapse", "lapses"}, {"louse", "lice"}, {"lunch", "lunches"}, {"marsh", "marshes"}, {"matrix", "matrices"}, - {"media", "media"}, + {"medium", "media"}, + {"multimedia", "multimedia"}, {"mouse", "mice"}, {"move", "moves"}, {"movie", "movies"}, @@ -130,6 +169,7 @@ var singlePluralAssertions = []tt{ {"ovum", "ova"}, {"ox", "oxen"}, {"payment_information", "payment_information"}, + {"pepsi", "pepsis"}, {"person", "people"}, {"perspective", "perspectives"}, {"phenomenon", "phenomena"}, @@ -141,6 +181,7 @@ var singlePluralAssertions = []tt{ {"prize", "prizes"}, {"process", "processes"}, {"prometheus", "prometheuses"}, + {"psi", "psis"}, {"puppy", "puppies"}, {"query", "queries"}, {"quiz", "quizzes"}, @@ -170,6 +211,7 @@ var singlePluralAssertions = []tt{ {"tooth", "teeth"}, {"truss", "trusses"}, {"user", "users"}, + {"user_custom_field", "user_custom_fields"}, {"vedalia", "vedalias"}, {"virus", "viri"}, {"wife", "wives"}, diff --git a/go.mod b/go.mod index 83d2244..64ce253 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/gobuffalo/flect go 1.16 -require github.com/stretchr/testify v1.7.1 +require github.com/stretchr/testify v1.8.0 diff --git a/go.sum b/go.sum index 2dca7c9..5164829 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,15 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plural_rules.go b/plural_rules.go index 61d34d3..7205ec0 100644 --- a/plural_rules.go +++ b/plural_rules.go @@ -47,6 +47,7 @@ var singleToPlural = map[string]string{ "concerto": "concertos", "corpus": "corpora", "crisis": "crises", + "criterion": "criteria", "curriculum": "curriculums", "datum": "data", "deer": "deer", @@ -56,7 +57,6 @@ var singleToPlural = map[string]string{ "ellipsis": "ellipses", "equipment": "equipment", "erratum": "errata", - "faux pas": "faux pas", "fez": "fezzes", "fish": "fish", "focus": "foci", @@ -82,6 +82,7 @@ var singleToPlural = map[string]string{ "locus": "loci", "louse": "lice", "matrix": "matrices", + "medium": "media", "minutia": "minutiae", "money": "money", "moose": "moose", @@ -96,6 +97,7 @@ var singleToPlural = map[string]string{ "ovum": "ova", "ox": "oxen", "parenthesis": "parentheses", + "person": "people", "phenomenon": "phenomena", "photo": "photos", "phylum": "phyla", @@ -157,9 +159,7 @@ type singularToPluralSuffix struct { } var singularToPluralSuffixList = []singularToPluralSuffix{ - {"iterion", "iteria"}, {"campus", "campuses"}, - {"genera", "genus"}, {"person", "people"}, {"phylum", "phyla"}, {"randum", "randa"}, @@ -169,6 +169,7 @@ var singularToPluralSuffixList = []singularToPluralSuffix{ {"child", "children"}, {"chive", "chives"}, {"focus", "foci"}, + {"genus", "genera"}, {"hello", "hellos"}, {"jeans", "jeans"}, {"louse", "lice"}, @@ -195,7 +196,6 @@ var singularToPluralSuffixList = []singularToPluralSuffix{ {"oose", "eese"}, {"ouse", "ouses"}, {"ovum", "ova"}, - {"rion", "ria"}, {"shoe", "shoes"}, {"stis", "stes"}, {"tive", "tives"}, @@ -225,7 +225,6 @@ var singularToPluralSuffixList = []singularToPluralSuffix{ {"ode", "odes"}, {"ofe", "oves"}, {"pfe", "pves"}, - {"pse", "psis"}, {"qfe", "qves"}, {"quy", "quies"}, {"rfe", "rves"}, diff --git a/pluralize.go b/pluralize.go index e265f84..88f9ec5 100644 --- a/pluralize.go +++ b/pluralize.go @@ -43,6 +43,9 @@ func (i Ident) Pluralize() Ident { return i } if p, ok := singleToPlural[ls]; ok { + if s == Capitalize(s) { + p = Capitalize(p) + } return i.ReplaceSuffix(s, p) } for _, r := range pluralRules { diff --git a/singularize.go b/singularize.go index 1ed4995..244e382 100644 --- a/singularize.go +++ b/singularize.go @@ -30,28 +30,32 @@ func SingularizeWithSize(s string, i int) string { // data = datum // people = person func (i Ident) Singularize() Ident { - s := i.Original + s := i.LastPart() if len(s) == 0 { return i } singularMoot.RLock() defer singularMoot.RUnlock() + ls := strings.ToLower(s) if p, ok := pluralToSingle[ls]; ok { - return New(p) + if s == Capitalize(s) { + p = Capitalize(p) + } + return i.ReplaceSuffix(s, p) } if _, ok := singleToPlural[ls]; ok { return i } for _, r := range singularRules { if strings.HasSuffix(ls, r.suffix) { - return New(r.fn(s)) + return i.ReplaceSuffix(s, r.fn(s)) } } if strings.HasSuffix(s, "s") { - return New(s[:len(s)-1]) + return i.ReplaceSuffix("s", "") } return i }