diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..65b45f1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: markbates +patreon: buffalo diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..68804c1 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,22 @@ +name: Tests +on: [push] +jobs: + + tests-on: + name: ${{matrix.go-version}} ${{matrix.os}} + runs-on: ${{ matrix.os }} + strategy: + matrix: + go-version: [1.12.x, 1.13.x] + os: [macos-latest, windows-latest, ubuntu-latest] + steps: + - name: Checkout Code + uses: actions/checkout@v1 + with: + fetch-depth: 1 + - name: Test + run: | + go mod tidy -v + go test -race ./... + + diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 417e2c5..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,71 +0,0 @@ -variables: - GOBIN: "$(GOPATH)/bin" # Go binaries path - GOPATH: "$(system.defaultWorkingDirectory)/gopath" # Go workspace path - modulePath: "$(GOPATH)/src/github.com/$(build.repository.name)" # Path to the module"s code - -jobs: -- job: Windows - pool: - vmImage: "vs2017-win2016" - strategy: - matrix: - go 1.10: - go_version: "1.10" - go 1.11 (on): - go_version: "1.11.5" - GO111MODULE: "on" - go 1.11 (off): - go_version: "1.11.5" - GO111MODULE: "off" - go 1.12 (on): - go_version: "1.12" - GO111MODULE: "on" - go 1.12 (off): - go_version: "1.12" - GO111MODULE: "off" - steps: - - template: azure-tests.yml - -- job: macOS - pool: - vmImage: "macOS-10.13" - strategy: - matrix: - go 1.10: - go_version: "1.10" - go 1.11 (on): - go_version: "1.11.5" - GO111MODULE: "on" - go 1.11 (off): - go_version: "1.11.5" - GO111MODULE: "off" - go 1.12 (on): - go_version: "1.12" - GO111MODULE: "on" - go 1.12 (off): - go_version: "1.12" - GO111MODULE: "off" - steps: - - template: azure-tests.yml - -- job: Linux - pool: - vmImage: "ubuntu-16.04" - strategy: - matrix: - go 1.10: - go_version: "1.10" - go 1.11 (on): - go_version: "1.11.5" - GO111MODULE: "on" - go 1.11 (off): - go_version: "1.11.5" - GO111MODULE: "off" - go 1.12 (on): - go_version: "1.12" - GO111MODULE: "on" - go 1.12 (off): - go_version: "1.12" - GO111MODULE: "off" - steps: - - template: azure-tests.yml diff --git a/azure-tests.yml b/azure-tests.yml deleted file mode 100644 index eea5822..0000000 --- a/azure-tests.yml +++ /dev/null @@ -1,19 +0,0 @@ -steps: - - task: GoTool@0 - inputs: - version: $(go_version) - - task: Bash@3 - inputs: - targetType: inline - script: | - mkdir -p "$(GOBIN)" - mkdir -p "$(GOPATH)/pkg" - mkdir -p "$(modulePath)" - shopt -s extglob - mv !(gopath) "$(modulePath)" - displayName: "Setup Go Workspace" - - script: | - go get -t -v ./... - go test -race ./... - workingDirectory: "$(modulePath)" - displayName: "Tests" diff --git a/flect_test.go b/flect_test.go index 232686e..257e4e1 100644 --- a/flect_test.go +++ b/flect_test.go @@ -172,6 +172,12 @@ var singlePluralAssertions = []tt{ {"prize", "prizes"}, {"edge", "edges"}, {"database", "databases"}, + {"circus", "circuses"}, + {"plus", "pluses"}, + {"fuse", "fuses"}, + {"prometheus", "prometheuses"}, + {"field", "fields"}, + {"custom_field", "custom_fields"}, } var pluralSingularAssertions = []tt{} diff --git a/go.mod b/go.mod index cd02d07..7c8d049 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,5 @@ module github.com/gobuffalo/flect -go 1.12 +go 1.13 -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/stretchr/testify v1.3.0 -) +require github.com/stretchr/testify v1.4.0 diff --git a/go.sum b/go.sum index 4f76e62..8fdee58 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,11 @@ 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.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/ident.go b/ident.go index cab620d..9189e9a 100644 --- a/ident.go +++ b/ident.go @@ -38,9 +38,9 @@ func toParts(s string) []string { return []string{strings.ToUpper(s)} } var prev rune - var x string + var x strings.Builder + x.Grow(len(s)) for _, c := range s { - cs := string(c) // fmt.Println("### cs ->", cs) // fmt.Println("### unicode.IsControl(c) ->", unicode.IsControl(c)) // fmt.Println("### unicode.IsDigit(c) ->", unicode.IsDigit(c)) @@ -58,35 +58,38 @@ func toParts(s string) []string { } if isSpace(c) { - parts = xappend(parts, x) - x = cs + parts = xappend(parts, x.String()) + x.Reset() + x.WriteRune(c) prev = c continue } if unicode.IsUpper(c) && !unicode.IsUpper(prev) { - parts = xappend(parts, x) - x = cs + parts = xappend(parts, x.String()) + x.Reset() + x.WriteRune(c) prev = c continue } - if unicode.IsUpper(c) && baseAcronyms[strings.ToUpper(x)] { - parts = xappend(parts, x) - x = cs + if unicode.IsUpper(c) && baseAcronyms[strings.ToUpper(x.String())] { + parts = xappend(parts, x.String()) + x.Reset() + x.WriteRune(c) prev = c continue } if unicode.IsLetter(c) || unicode.IsDigit(c) || unicode.IsPunct(c) || c == '`' { prev = c - x += cs + x.WriteRune(c) continue } - parts = xappend(parts, x) - x = "" + parts = xappend(parts, x.String()) + x.Reset() prev = c } - parts = xappend(parts, x) + parts = xappend(parts, x.String()) return parts } diff --git a/ident_test.go b/ident_test.go index 034acb0..3994d29 100644 --- a/ident_test.go +++ b/ident_test.go @@ -64,3 +64,49 @@ func Test_MarshalText(t *testing.T) { r.NoError((&n).UnmarshalText([]byte("bates"))) r.Equal("bates", n.String()) } + +func Benchmark_New(b *testing.B) { + + table := []string{ + "", + "widget", + "widget_id", + "WidgetID", + "Widget_ID", + "widget_ID", + "widget/ID", + "widgetID", + "widgetName", + "JWTName", + "JWTname", + "jwtname", + "sql", + "sQl", + "id", + "Id", + "iD", + "html", + "Html", + "HTML", + "with `code` inside", + "Donald E. Knuth", + "Random text with *(bad)* characters", + "Allow_Under_Scores", + "Trailing bad characters!@#", + "!@#Leading bad characters", + "Squeeze separators", + "Test with + sign", + "Malmö", + "Garçons", + "Opsů", + "Ærøskøbing", + "Aßlar", + "Japanese: 日本語", + } + + for n := 0; n < b.N; n++ { + for i := range table { + New(table[i]) + } + } +} diff --git a/name/name_test.go b/name/name_test.go index a0f201a..42be425 100644 --- a/name/name_test.go +++ b/name/name_test.go @@ -20,9 +20,9 @@ func Test_Name(t *testing.T) { {"Widget_ID", "WidgetID"}, {"Widget_Id", "WidgetID"}, {"Widget_id", "WidgetID"}, - {"Nice to see you!", "NiceToSeeYou"}, + {"Nice to see you today!", "NiceToSeeYouToday"}, {"*hello*", "Hello"}, - {"i've read a book! have you?", "IveReadABookHaveYou"}, + {"i've read a book! have you read it?", "IveReadABookHaveYouReadIt"}, {"This is `code` ok", "ThisIsCodeOK"}, {"foo_bar", "FooBar"}, {"admin/widget", "AdminWidget"}, diff --git a/name/tablize_test.go b/name/tablize_test.go index e0c5e5f..91ad1a4 100644 --- a/name/tablize_test.go +++ b/name/tablize_test.go @@ -10,9 +10,9 @@ func Test_Tableize(t *testing.T) { table := []tt{ {"", ""}, {"bob dylan", "bob_dylans"}, - {"Nice to see you!", "nice_to_see_yous"}, + {"Nice to see you!", "nice_to_see_you"}, {"*hello*", "hellos"}, - {"i've read a book! have you?", "ive_read_a_book_have_yous"}, + {"i've read a book! have you?", "ive_read_a_book_have_you"}, {"This is `code` ok", "this_is_code_oks"}, {"foo_bar", "foo_bars"}, {"admin/widget", "admin_widgets"}, diff --git a/plural_rules.go b/plural_rules.go index 1010719..4c88b62 100644 --- a/plural_rules.go +++ b/plural_rules.go @@ -21,36 +21,8 @@ func AddPlural(suffix string, repl string) { } var singleToPlural = map[string]string{ - "human": "humans", - "matrix": "matrices", - "vertix": "vertices", - "index": "indices", - "mouse": "mice", - "louse": "lice", - "ress": "resses", - "ox": "oxen", - "quiz": "quizzes", - "series": "series", - "octopus": "octopi", - "equipment": "equipment", - "information": "information", - "rice": "rice", - "money": "money", - "species": "species", - "fish": "fish", - "sheep": "sheep", - "jeans": "jeans", - "police": "police", - "dear": "dear", - "goose": "geese", - "tooth": "teeth", - "foot": "feet", - "bus": "buses", - "fez": "fezzes", - "piano": "pianos", - "halo": "halos", - "photo": "photos", "aircraft": "aircraft", + "alias": "aliases", "alumna": "alumnae", "alumnus": "alumni", "analysis": "analyses", @@ -65,48 +37,86 @@ var singleToPlural = map[string]string{ "beau": "beaus", "bison": "bison", "bureau": "bureaus", + "bus": "buses", "campus": "campuses", + "caucus": "caucuses", + "child": "children", "château": "châteaux", + "circus": "circuses", "codex": "codices", "concerto": "concertos", "corpus": "corpora", "crisis": "crises", "curriculum": "curriculums", + "datum": "data", + "dear": "dear", "deer": "deer", "diagnosis": "diagnoses", "die": "dice", "dwarf": "dwarves", "ellipsis": "ellipses", + "equipment": "equipment", "erratum": "errata", "faux pas": "faux pas", + "fez": "fezzes", + "fish": "fish", "focus": "foci", + "foo": "foos", + "foot": "feet", "formula": "formulas", "fungus": "fungi", "genus": "genera", + "goose": "geese", "graffito": "graffiti", "grouse": "grouse", "half": "halves", + "halo": "halos", "hoof": "hooves", + "human": "humans", "hypothesis": "hypotheses", + "index": "indices", + "information": "information", + "jeans": "jeans", "larva": "larvae", "libretto": "librettos", "loaf": "loaves", "locus": "loci", + "louse": "lice", + "matrix": "matrices", "minutia": "minutiae", + "money": "money", "moose": "moose", + "mouse": "mice", "nebula": "nebulae", + "news": "news", "nucleus": "nuclei", "oasis": "oases", + "octopus": "octopi", "offspring": "offspring", "opus": "opera", + "ovum": "ova", + "ox": "oxen", "parenthesis": "parentheses", "phenomenon": "phenomena", + "photo": "photos", "phylum": "phyla", + "piano": "pianos", + "plus": "pluses", + "police": "police", "prognosis": "prognoses", + "prometheus": "prometheuses", + "quiz": "quizzes", "radius": "radiuses", "referendum": "referendums", + "ress": "resses", + "rice": "rice", "salmon": "salmon", + "sex": "sexes", + "series": "series", + "sheep": "sheep", + "shoe": "shoes", "shrimp": "shrimp", + "species": "species", "stimulus": "stimuli", "stratum": "strata", "swine": "swine", @@ -114,23 +124,21 @@ var singleToPlural = map[string]string{ "symposium": "symposiums", "synopsis": "synopses", "tableau": "tableaus", + "testis": "testes", "thesis": "theses", "thief": "thieves", + "tooth": "teeth", "trout": "trout", "tuna": "tuna", "vertebra": "vertebrae", + "vertix": "vertices", "vita": "vitae", "vortex": "vortices", "wharf": "wharves", "wife": "wives", + "woman": "women", "wolf": "wolves", - "datum": "data", - "testis": "testes", - "alias": "aliases", - "shoe": "shoes", - "news": "news", - "ovum": "ova", - "foo": "foos", + "you": "you", } var pluralToSingle = map[string]string{} @@ -140,100 +148,139 @@ func init() { pluralToSingle[v] = k } } + +type singularToPluralSuffix struct { + singular string + plural string +} + +var singularToPluralSuffixList = []singularToPluralSuffix{ + {"iterion", "iteria"}, + {"campus", "campuses"}, + {"genera", "genus"}, + {"person", "people"}, + {"phylum", "phyla"}, + {"randum", "randa"}, + {"actus", "acti"}, + {"adium", "adia"}, + {"alias", "aliases"}, + {"basis", "basis"}, + {"child", "children"}, + {"chive", "chives"}, + {"focus", "foci"}, + {"hello", "hellos"}, + {"jeans", "jeans"}, + {"louse", "lice"}, + {"mouse", "mice"}, + {"movie", "movies"}, + {"oasis", "oasis"}, + {"atum", "ata"}, + {"atus", "atuses"}, + {"base", "bases"}, + {"cess", "cesses"}, + {"dium", "diums"}, + {"eses", "esis"}, + {"half", "halves"}, + {"hive", "hives"}, + {"iano", "ianos"}, + {"irus", "iri"}, + {"isis", "ises"}, + {"leus", "li"}, + {"mnus", "mni"}, + {"move", "moves"}, + {"news", "news"}, + {"odex", "odice"}, + {"oose", "eese"}, + {"ouse", "ouses"}, + {"ovum", "ova"}, + {"rion", "ria"}, + {"shoe", "shoes"}, + {"stis", "stes"}, + {"tive", "tives"}, + {"wife", "wives"}, + {"afe", "aves"}, + {"bfe", "bves"}, + {"box", "boxes"}, + {"cfe", "cves"}, + {"dfe", "dves"}, + {"dge", "dges"}, + {"efe", "eves"}, + {"gfe", "gves"}, + {"hfe", "hves"}, + {"ife", "ives"}, + {"itz", "itzes"}, + {"ium", "ia"}, + {"ize", "izes"}, + {"jfe", "jves"}, + {"kfe", "kves"}, + {"man", "men"}, + {"mfe", "mves"}, + {"nfe", "nves"}, + {"nna", "nnas"}, + {"oaf", "oaves"}, + {"oci", "ocus"}, + {"ode", "odes"}, + {"ofe", "oves"}, + {"oot", "eet"}, + {"pfe", "pves"}, + {"pse", "psis"}, + {"qfe", "qves"}, + {"quy", "quies"}, + {"rfe", "rves"}, + {"sfe", "sves"}, + {"tfe", "tves"}, + {"tum", "ta"}, + {"tus", "tuses"}, + {"ufe", "uves"}, + {"ula", "ulae"}, + {"ula", "ulas"}, + {"uli", "ulus"}, + {"use", "uses"}, + {"uss", "usses"}, + {"vfe", "vves"}, + {"wfe", "wves"}, + {"xfe", "xves"}, + {"yfe", "yves"}, + {"you", "you"}, + {"zfe", "zves"}, + {"by", "bies"}, + {"ch", "ches"}, + {"cy", "cies"}, + {"dy", "dies"}, + {"ex", "ices"}, + {"fy", "fies"}, + {"gy", "gies"}, + {"hy", "hies"}, + {"io", "ios"}, + {"jy", "jies"}, + {"ky", "kies"}, + {"lf", "lves"}, + {"ly", "lies"}, + {"my", "mies"}, + {"ny", "nies"}, + {"ox", "oxen"}, + {"py", "pies"}, + {"qy", "qies"}, + {"rf", "rves"}, + {"ry", "ries"}, + {"sh", "shes"}, + {"ss", "sses"}, + {"sy", "sies"}, + {"ty", "ties"}, + {"tz", "tzes"}, + {"va", "vae"}, + {"vy", "vies"}, + {"wy", "wies"}, + {"xy", "xies"}, + {"zy", "zies"}, + {"zz", "zzes"}, + {"o", "oes"}, + {"x", "xes"}, +} + func init() { - AddPlural("campus", "campuses") - AddPlural("man", "men") - AddPlural("tz", "tzes") - AddPlural("alias", "aliases") - AddPlural("oasis", "oasis") - AddPlural("wife", "wives") - AddPlural("basis", "basis") - AddPlural("atum", "ata") - AddPlural("adium", "adia") - AddPlural("actus", "acti") - AddPlural("irus", "iri") - AddPlural("iterion", "iteria") - AddPlural("dium", "diums") - AddPlural("ovum", "ova") - AddPlural("ize", "izes") - AddPlural("dge", "dges") - AddPlural("focus", "foci") - AddPlural("child", "children") - AddPlural("oaf", "oaves") - AddPlural("randum", "randa") - AddPlural("base", "bases") - AddPlural("atus", "atuses") - AddPlural("ode", "odes") - AddPlural("person", "people") - AddPlural("hello", "hellos") - AddPlural("va", "vae") - AddPlural("leus", "li") - AddPlural("oot", "eet") - AddPlural("oose", "eese") - AddPlural("box", "boxes") - AddPlural("ium", "ia") - AddPlural("sis", "ses") - AddPlural("nna", "nnas") - AddPlural("eses", "esis") - AddPlural("stis", "stes") - AddPlural("ex", "ices") - AddPlural("ula", "ulae") - AddPlural("isis", "ises") - AddPlural("use", "uses") - AddPlural("lf", "lves") - AddPlural("rf", "rves") - AddPlural("afe", "aves") - AddPlural("bfe", "bves") - AddPlural("cfe", "cves") - AddPlural("dfe", "dves") - AddPlural("efe", "eves") - AddPlural("gfe", "gves") - AddPlural("hfe", "hves") - AddPlural("ife", "ives") - AddPlural("jfe", "jves") - AddPlural("kfe", "kves") - AddPlural("lfe", "lves") - AddPlural("mfe", "mves") - AddPlural("nfe", "nves") - AddPlural("ofe", "oves") - AddPlural("pfe", "pves") - AddPlural("qfe", "qves") - AddPlural("rfe", "rves") - AddPlural("sfe", "sves") - AddPlural("tfe", "tves") - AddPlural("ufe", "uves") - AddPlural("vfe", "vves") - AddPlural("wfe", "wves") - AddPlural("xfe", "xves") - AddPlural("yfe", "yves") - AddPlural("zfe", "zves") - AddPlural("hive", "hives") - AddPlural("quy", "quies") - AddPlural("by", "bies") - AddPlural("cy", "cies") - AddPlural("dy", "dies") - AddPlural("fy", "fies") - AddPlural("gy", "gies") - AddPlural("hy", "hies") - AddPlural("jy", "jies") - AddPlural("ky", "kies") - AddPlural("ly", "lies") - AddPlural("my", "mies") - AddPlural("ny", "nies") - AddPlural("py", "pies") - AddPlural("qy", "qies") - AddPlural("ry", "ries") - AddPlural("sy", "sies") - AddPlural("ty", "ties") - AddPlural("vy", "vies") - AddPlural("wy", "wies") - AddPlural("xy", "xies") - AddPlural("zy", "zies") - AddPlural("x", "xes") - AddPlural("ch", "ches") - AddPlural("ss", "sses") - AddPlural("sh", "shes") - AddPlural("oe", "oes") - AddPlural("io", "ios") - AddPlural("o", "oes") + for _, suffix := range singularToPluralSuffixList { + AddPlural(suffix.singular, suffix.plural) + AddSingular(suffix.plural, suffix.singular) + } } diff --git a/singular_rules.go b/singular_rules.go index 14b471c..b20371b 100644 --- a/singular_rules.go +++ b/singular_rules.go @@ -21,102 +21,3 @@ func AddSingular(ext string, repl string) { }, }) } - -func init() { - AddSingular("ria", "rion") - AddSingular("news", "news") - AddSingular("halves", "half") - AddSingular("appendix", "appendix") - AddSingular("zzes", "zz") - AddSingular("ulas", "ula") - AddSingular("psis", "pse") - AddSingular("genus", "genera") - AddSingular("phyla", "phylum") - AddSingular("odice", "odex") - AddSingular("oxen", "ox") - AddSingular("ianos", "iano") - AddSingular("ulus", "uli") - AddSingular("mice", "mouse") - AddSingular("ouses", "ouse") - AddSingular("mni", "mnus") - AddSingular("ocus", "oci") - AddSingular("shoes", "shoe") - AddSingular("oasis", "oasis") - AddSingular("lice", "louse") - AddSingular("men", "man") - AddSingular("ta", "tum") - AddSingular("ia", "ium") - AddSingular("tives", "tive") - AddSingular("ldren", "ld") - AddSingular("people", "person") - AddSingular("aves", "afe") - AddSingular("uses", "us") - AddSingular("bves", "bfe") - AddSingular("cves", "cfe") - AddSingular("dves", "dfe") - AddSingular("eves", "efe") - AddSingular("gves", "gfe") - AddSingular("hves", "hfe") - AddSingular("chives", "chive") - AddSingular("ives", "ife") - AddSingular("movies", "movie") - AddSingular("jeans", "jeans") - AddSingular("cesses", "cess") - AddSingular("cess", "cess") - AddSingular("acti", "actus") - AddSingular("itzes", "itz") - AddSingular("usses", "uss") - AddSingular("uss", "uss") - AddSingular("jves", "jfe") - AddSingular("kves", "kfe") - AddSingular("mves", "mfe") - AddSingular("nves", "nfe") - AddSingular("moves", "move") - AddSingular("oves", "ofe") - AddSingular("pves", "pfe") - AddSingular("qves", "qfe") - AddSingular("sves", "sfe") - AddSingular("tves", "tfe") - AddSingular("uves", "ufe") - AddSingular("vves", "vfe") - AddSingular("wves", "wfe") - AddSingular("xves", "xfe") - AddSingular("yves", "yfe") - AddSingular("zves", "zfe") - AddSingular("hives", "hive") - AddSingular("lves", "lf") - AddSingular("rves", "rf") - AddSingular("quies", "quy") - AddSingular("bies", "by") - AddSingular("cies", "cy") - AddSingular("dies", "dy") - AddSingular("fies", "fy") - AddSingular("gies", "gy") - AddSingular("hies", "hy") - AddSingular("jies", "jy") - AddSingular("kies", "ky") - AddSingular("lies", "ly") - AddSingular("mies", "my") - AddSingular("nies", "ny") - AddSingular("pies", "py") - AddSingular("qies", "qy") - AddSingular("ries", "ry") - AddSingular("sies", "sy") - AddSingular("ties", "ty") - AddSingular("vies", "vy") - AddSingular("wies", "wy") - AddSingular("xies", "xy") - AddSingular("zies", "zy") - AddSingular("xes", "x") - AddSingular("ches", "ch") - AddSingular("sses", "ss") - AddSingular("shes", "sh") - AddSingular("oes", "o") - AddSingular("ress", "ress") - AddSingular("iri", "irus") - AddSingular("irus", "irus") - AddSingular("tuses", "tus") - AddSingular("tus", "tus") - AddSingular("s", "") - AddSingular("ss", "ss") -} diff --git a/singularize.go b/singularize.go index a08cbd5..a0f8545 100644 --- a/singularize.go +++ b/singularize.go @@ -40,5 +40,8 @@ func (i Ident) Singularize() Ident { } } + if strings.HasSuffix(s, "s") { + return New(s[:len(s)-1]) + } return i } diff --git a/underscore.go b/underscore.go index b92488a..e1466d9 100644 --- a/underscore.go +++ b/underscore.go @@ -18,16 +18,17 @@ func Underscore(s string) string { // Nice to see you! = nice_to_see_you // widgetID = widget_id func (i Ident) Underscore() Ident { - var out []string + out := make([]string, 0, len(i.Parts)) for _, part := range i.Parts { - var x string + var x strings.Builder + x.Grow(len(part)) for _, c := range part { if unicode.IsLetter(c) || unicode.IsDigit(c) { - x += string(c) + x.WriteRune(c) } } - if x != "" { - out = append(out, x) + if x.Len() > 0 { + out = append(out, x.String()) } } return New(strings.ToLower(strings.Join(out, "_"))) diff --git a/underscore_test.go b/underscore_test.go index 2425738..7122e9d 100644 --- a/underscore_test.go +++ b/underscore_test.go @@ -27,3 +27,22 @@ func Test_Underscore(t *testing.T) { }) } } + +func Benchmark_Underscore(b *testing.B) { + + table := []string{ + "", + "bob dylan", + "Nice to see you!", + "*hello*", + "i've read a book! have you?", + "This is `code` ok", + "TLCForm", + } + + for n := 0; n < b.N; n++ { + for i := range table { + Underscore(table[i]) + } + } +}