pax_global_header00006660000000000000000000000064133641535640014524gustar00rootroot0000000000000052 comment=24b83195037b3bc61fcda2d28b7b0518bce293b6 inflect-1.0.4/000077500000000000000000000000001336415356400131525ustar00rootroot00000000000000inflect-1.0.4/.gitignore000066400000000000000000000003761336415356400151500ustar00rootroot00000000000000*.log .DS_Store doc tmp pkg *.gem *.pid coverage coverage.data build/* *.pbxuser *.mode1v3 .svn profile .console_history .sass-cache/* .rake_tasks~ *.log.lck solr/ .jhw-cache/ jhw.* *.sublime* node_modules/ dist/ generated/ .vendor/ bin/* gin-bin .idea/ inflect-1.0.4/.gometalinter.json000066400000000000000000000002221336415356400166110ustar00rootroot00000000000000{ "Enable": ["vet", "golint", "goimports", "deadcode", "gotype", "ineffassign", "misspell", "nakedret", "unconvert", "megacheck", "varcheck"] } inflect-1.0.4/.hgignore000066400000000000000000000000051336415356400147500ustar00rootroot00000000000000swp$ inflect-1.0.4/.travis.yml000066400000000000000000000005331336415356400152640ustar00rootroot00000000000000language: go sudo: false matrix: include: - go: "1.9.x" - go: "1.10.x" - go: "1.11.x" env: - GO111MODULE=off - go: "1.11.x" env: - GO111MODULE=on - go: "tip" env: - GO111MODULE=off - go: "tip" env: - GO111MODULE=on allow_failures: - go: "tip" install: make deps script: make ci-test inflect-1.0.4/LICENCE000066400000000000000000000020421336415356400141350ustar00rootroot00000000000000Copyright (c) 2011 Chris Farmiloe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. inflect-1.0.4/Makefile000066400000000000000000000014441336415356400146150ustar00rootroot00000000000000TAGS ?= "sqlite" GO_BIN ?= go install: packr $(GO_BIN) install -tags ${TAGS} -v . make tidy tidy: ifeq ($(GO111MODULE),on) $(GO_BIN) mod tidy else echo skipping go mod tidy endif deps: $(GO_BIN) get github.com/gobuffalo/release $(GO_BIN) get github.com/gobuffalo/packr/packr $(GO_BIN) get -tags ${TAGS} -t ./... make tidy build: packr $(GO_BIN) build -v . make tidy test: packr $(GO_BIN) test -tags ${TAGS} ./... make tidy ci-test: $(GO_BIN) test -tags ${TAGS} -race ./... make tidy lint: gometalinter --vendor ./... --deadline=1m --skip=internal make tidy update: $(GO_BIN) get -u -tags ${TAGS} make tidy packr make test make install make tidy release-test: $(GO_BIN) test -tags ${TAGS} -race ./... make tidy release: make tidy release -y -f version.go make tidy inflect-1.0.4/README.md000066400000000000000000000111621336415356400144320ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/markbates/inflect.svg?branch=master)](https://travis-ci.org/markbates/inflect) #### INSTALLATION go get github.com/markbates/inflect #### PACKAGE package inflect #### FUNCTIONS ```go func AddAcronym(word string) func AddHuman(suffix, replacement string) func AddIrregular(singular, plural string) func AddPlural(suffix, replacement string) func AddSingular(suffix, replacement string) func AddUncountable(word string) func Asciify(word string) string func Camelize(word string) string func CamelizeDownFirst(word string) string func Capitalize(word string) string func Dasherize(word string) string func ForeignKey(word string) string func ForeignKeyCondensed(word string) string func Humanize(word string) string func Ordinalize(word string) string func Parameterize(word string) string func ParameterizeJoin(word, sep string) string func Pluralize(word string) string func Singularize(word string) string func Tableize(word string) string func Titleize(word string) string func Typeify(word string) string func Uncountables() map[string]bool func Underscore(word string) string ``` #### TYPES ```go type Rule struct { // contains filtered or unexported fields } ``` used by rulesets ```go type Ruleset struct { // contains filtered or unexported fields } ``` a Ruleset is the config of pluralization rules you can extend the rules with the Add* methods ``` func NewDefaultRuleset() *Ruleset ``` create a new ruleset and load it with the default set of common English pluralization rules ``` func NewRuleset() *Ruleset ``` create a blank ruleset. Unless you are going to build your own rules from scratch you probably won't need this and can just use the defaultRuleset via the global inflect.* methods ``` func (rs *Ruleset) AddAcronym(word string) ``` if you use acronym you may need to add them to the ruleset to prevent Underscored words of things like "HTML" coming out as "h_t_m_l" ``` func (rs *Ruleset) AddHuman(suffix, replacement string) ``` Human rules are applied by humanize to show more friendly versions of words ``` func (rs *Ruleset) AddIrregular(singular, plural string) ``` Add any inconsistent pluralizing/singularizing rules to the set here. ``` func (rs *Ruleset) AddPlural(suffix, replacement string) ``` add a pluralization rule ``` func (rs *Ruleset) AddPluralExact(suffix, replacement string, exact bool) ``` add a pluralization rule with full string match ``` func (rs *Ruleset) AddSingular(suffix, replacement string) ``` add a singular rule ``` func (rs *Ruleset) AddSingularExact(suffix, replacement string, exact bool) ``` same as AddSingular but you can set `exact` to force a full string match ``` func (rs *Ruleset) AddUncountable(word string) ``` add a word to this ruleset that has the same singular and plural form for example: "rice" ``` func (rs *Ruleset) Asciify(word string) string ``` transforms Latin characters like é -> e ``` func (rs *Ruleset) Camelize(word string) string ``` "dino_party" -> "DinoParty" ``` func (rs *Ruleset) CamelizeDownFirst(word string) string ``` same as Camelcase but with first letter downcased ``` func (rs *Ruleset) Capitalize(word string) string ``` uppercase first character ``` func (rs *Ruleset) Dasherize(word string) string ``` "SomeText" -> "some-text" ``` func (rs *Ruleset) ForeignKey(word string) string ``` an underscored foreign key name "Person" -> "person_id" ``` func (rs *Ruleset) ForeignKeyCondensed(word string) string ``` a foreign key (with an underscore) "Person" -> "personid" ``` func (rs *Ruleset) Humanize(word string) string ``` First letter of sentence capitalized Uses custom friendly replacements via AddHuman() ``` func (rs *Ruleset) Ordinalize(str string) string ``` "1031" -> "1031st" ``` func (rs *Ruleset) Parameterize(word string) string ``` param safe dasherized names like "my-param" ``` func (rs *Ruleset) ParameterizeJoin(word, sep string) string ``` param safe dasherized names with custom separator ``` func (rs *Ruleset) Pluralize(word string) string ``` returns the plural form of a singular word ``` func (rs *Ruleset) Singularize(word string) string ``` returns the singular form of a plural word ``` func (rs *Ruleset) Tableize(word string) string ``` Rails style pluralized table names: "SuperPerson" -> "super_people" ``` func (rs *Ruleset) Titleize(word string) string ``` Capitalize every word in sentence "hello there" -> "Hello There" ``` func (rs *Ruleset) Typeify(word string) string ``` "something_like_this" -> "SomethingLikeThis" ``` func (rs *Ruleset) Uncountables() map[string]bool ``` ``` func (rs *Ruleset) Underscore(word string) string ``` lowercase underscore version "BigBen" -> "big_ben" inflect-1.0.4/go.mod000066400000000000000000000001671336415356400142640ustar00rootroot00000000000000module github.com/markbates/inflect require ( github.com/gobuffalo/envy v1.6.5 github.com/stretchr/testify v1.2.2 ) inflect-1.0.4/go.sum000066400000000000000000000015311336415356400143050ustar00rootroot00000000000000github.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/gobuffalo/envy v1.6.5 h1:X3is06x7v0nW2xiy2yFbbIjwHz57CD6z6MkvqULTCm8= github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= inflect-1.0.4/helpers.go000066400000000000000000000012031336415356400151370ustar00rootroot00000000000000package inflect //Helpers is a map of the helper names with its corresponding inflect function var Helpers = map[string]interface{}{ "asciffy": Asciify, "camelize": Camelize, "camelize_down_first": CamelizeDownFirst, "capitalize": Capitalize, "dasherize": Dasherize, "humanize": Humanize, "ordinalize": Ordinalize, "parameterize": Parameterize, "pluralize": Pluralize, "pluralize_with_size": PluralizeWithSize, "singularize": Singularize, "tableize": Tableize, "typeify": Typeify, "underscore": Underscore, } inflect-1.0.4/inflect.go000066400000000000000000000572541336415356400151420ustar00rootroot00000000000000package inflect import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "os" "path/filepath" "regexp" "strconv" "strings" "unicode" "unicode/utf8" ) // baseAcronyms comes from https://en.wikipedia.org/wiki/List_of_information_technology_acronymss const baseAcronyms = `JSON,JWT,ID,UUID,SQL,ACK,ACL,ADSL,AES,ANSI,API,ARP,ATM,BGP,BSS,CAT,CCITT,CHAP,CIDR,CIR,CLI,CPE,CPU,CRC,CRT,CSMA,CMOS,DCE,DEC,DES,DHCP,DNS,DRAM,DSL,DSLAM,DTE,DMI,EHA,EIA,EIGRP,EOF,ESS,FCC,FCS,FDDI,FTP,GBIC,gbps,GEPOF,HDLC,HTTP,HTTPS,IANA,ICMP,IDF,IDS,IEEE,IETF,IMAP,IP,IPS,ISDN,ISP,kbps,LACP,LAN,LAPB,LAPF,LLC,MAC,MAN,Mbps,MC,MDF,MIB,MoCA,MPLS,MTU,NAC,NAT,NBMA,NIC,NRZ,NRZI,NVRAM,OSI,OSPF,OUI,PAP,PAT,PC,PIM,PIM,PCM,PDU,POP3,POP,POTS,PPP,PPTP,PTT,PVST,RADIUS,RAM,RARP,RFC,RIP,RLL,ROM,RSTP,RTP,RCP,SDLC,SFD,SFP,SLARP,SLIP,SMTP,SNA,SNAP,SNMP,SOF,SRAM,SSH,SSID,STP,SYN,TDM,TFTP,TIA,TOFU,UDP,URL,URI,USB,UTP,VC,VLAN,VLSM,VPN,W3C,WAN,WEP,WiFi,WPA,WWW` // Rule used by rulesets type Rule struct { suffix string replacement string exact bool } // Ruleset a Ruleset is the config of pluralization rules // you can extend the rules with the Add* methods type Ruleset struct { uncountables map[string]bool plurals []*Rule singulars []*Rule humans []*Rule acronyms []*Rule } // NewRuleset creates a blank ruleset. Unless you are going to // build your own rules from scratch you probably // won't need this and can just use the defaultRuleset // via the global inflect.* methods func NewRuleset() *Ruleset { rs := new(Ruleset) rs.uncountables = make(map[string]bool) rs.plurals = make([]*Rule, 0) rs.singulars = make([]*Rule, 0) rs.humans = make([]*Rule, 0) rs.acronyms = make([]*Rule, 0) return rs } // NewDefaultRuleset creates a new ruleset and load it with the default // set of common English pluralization rules func NewDefaultRuleset() *Ruleset { rs := NewRuleset() rs.AddPlural("movie", "movies") rs.AddPlural("s", "s") rs.AddPlural("testis", "testes") rs.AddPlural("axis", "axes") rs.AddPlural("octopus", "octopi") rs.AddPlural("virus", "viri") rs.AddPlural("octopi", "octopi") rs.AddPlural("viri", "viri") rs.AddPlural("alias", "aliases") rs.AddPlural("status", "statuses") rs.AddPlural("Status", "Statuses") rs.AddPlural("campus", "campuses") rs.AddPlural("bus", "buses") rs.AddPlural("buffalo", "buffaloes") rs.AddPlural("tomato", "tomatoes") rs.AddPlural("tum", "ta") rs.AddPlural("ium", "ia") rs.AddPlural("ta", "ta") rs.AddPlural("ia", "ia") rs.AddPlural("sis", "ses") rs.AddPlural("lf", "lves") rs.AddPlural("rf", "rves") rs.AddPlural("afe", "aves") rs.AddPlural("bfe", "bves") rs.AddPlural("cfe", "cves") rs.AddPlural("dfe", "dves") rs.AddPlural("efe", "eves") rs.AddPlural("gfe", "gves") rs.AddPlural("hfe", "hves") rs.AddPlural("ife", "ives") rs.AddPlural("jfe", "jves") rs.AddPlural("kfe", "kves") rs.AddPlural("lfe", "lves") rs.AddPlural("mfe", "mves") rs.AddPlural("nfe", "nves") rs.AddPlural("ofe", "oves") rs.AddPlural("pfe", "pves") rs.AddPlural("qfe", "qves") rs.AddPlural("rfe", "rves") rs.AddPlural("sfe", "sves") rs.AddPlural("tfe", "tves") rs.AddPlural("ufe", "uves") rs.AddPlural("vfe", "vves") rs.AddPlural("wfe", "wves") rs.AddPlural("xfe", "xves") rs.AddPlural("yfe", "yves") rs.AddPlural("zfe", "zves") rs.AddPlural("hive", "hives") rs.AddPlural("quy", "quies") rs.AddPlural("by", "bies") rs.AddPlural("cy", "cies") rs.AddPlural("dy", "dies") rs.AddPlural("fy", "fies") rs.AddPlural("gy", "gies") rs.AddPlural("hy", "hies") rs.AddPlural("jy", "jies") rs.AddPlural("ky", "kies") rs.AddPlural("ly", "lies") rs.AddPlural("my", "mies") rs.AddPlural("ny", "nies") rs.AddPlural("py", "pies") rs.AddPlural("qy", "qies") rs.AddPlural("ry", "ries") rs.AddPlural("sy", "sies") rs.AddPlural("ty", "ties") rs.AddPlural("vy", "vies") rs.AddPlural("wy", "wies") rs.AddPlural("xy", "xies") rs.AddPlural("zy", "zies") rs.AddPlural("x", "xes") rs.AddPlural("ch", "ches") rs.AddPlural("ss", "sses") rs.AddPlural("sh", "shes") rs.AddPlural("matrix", "matrices") rs.AddPlural("vertix", "vertices") rs.AddPlural("indix", "indices") rs.AddPlural("matrex", "matrices") rs.AddPlural("vertex", "vertices") rs.AddPlural("index", "indices") rs.AddPlural("mouse", "mice") rs.AddPlural("louse", "lice") rs.AddPlural("mice", "mice") rs.AddPlural("lice", "lice") rs.AddPlural("ress", "resses") rs.AddPluralExact("ox", "oxen", true) rs.AddPluralExact("oxen", "oxen", true) rs.AddPluralExact("quiz", "quizzes", true) rs.AddSingular("s", "") rs.AddSingular("ss", "ss") rs.AddSingular("news", "news") rs.AddSingular("ta", "tum") rs.AddSingular("ia", "ium") rs.AddSingular("analyses", "analysis") rs.AddSingular("bases", "basis") rs.AddSingularExact("basis", "basis", true) rs.AddSingular("diagnoses", "diagnosis") rs.AddSingularExact("diagnosis", "diagnosis", true) rs.AddSingular("parentheses", "parenthesis") rs.AddSingular("prognoses", "prognosis") rs.AddSingular("synopses", "synopsis") rs.AddSingular("theses", "thesis") rs.AddSingular("analyses", "analysis") rs.AddSingularExact("analysis", "analysis", true) rs.AddSingular("ovies", "ovie") rs.AddSingular("aves", "afe") rs.AddSingular("bves", "bfe") rs.AddSingular("cves", "cfe") rs.AddSingular("dves", "dfe") rs.AddSingular("eves", "efe") rs.AddSingular("gves", "gfe") rs.AddSingular("hves", "hfe") rs.AddSingular("ives", "ife") rs.AddSingular("jves", "jfe") rs.AddSingular("kves", "kfe") rs.AddSingular("lves", "lfe") rs.AddSingular("mves", "mfe") rs.AddSingular("nves", "nfe") rs.AddSingular("oves", "ofe") rs.AddSingular("pves", "pfe") rs.AddSingular("qves", "qfe") rs.AddSingular("rves", "rfe") rs.AddSingular("sves", "sfe") rs.AddSingular("tves", "tfe") rs.AddSingular("uves", "ufe") rs.AddSingular("vves", "vfe") rs.AddSingular("wves", "wfe") rs.AddSingular("xves", "xfe") rs.AddSingular("yves", "yfe") rs.AddSingular("zves", "zfe") rs.AddSingular("hives", "hive") rs.AddSingular("tives", "tive") rs.AddSingular("lves", "lf") rs.AddSingular("rves", "rf") rs.AddSingular("quies", "quy") rs.AddSingular("bies", "by") rs.AddSingular("cies", "cy") rs.AddSingular("dies", "dy") rs.AddSingular("fies", "fy") rs.AddSingular("gies", "gy") rs.AddSingular("hies", "hy") rs.AddSingular("jies", "jy") rs.AddSingular("kies", "ky") rs.AddSingular("lies", "ly") rs.AddSingular("mies", "my") rs.AddSingular("nies", "ny") rs.AddSingular("pies", "py") rs.AddSingular("qies", "qy") rs.AddSingular("ries", "ry") rs.AddSingular("sies", "sy") rs.AddSingular("ties", "ty") // rs.AddSingular("vies", "vy") rs.AddSingular("wies", "wy") rs.AddSingular("xies", "xy") rs.AddSingular("zies", "zy") rs.AddSingular("series", "series") rs.AddSingular("xes", "x") rs.AddSingular("ches", "ch") rs.AddSingular("sses", "ss") rs.AddSingular("shes", "sh") rs.AddSingular("mice", "mouse") rs.AddSingular("lice", "louse") rs.AddSingular("buses", "bus") rs.AddSingularExact("bus", "bus", true) rs.AddSingular("oes", "o") rs.AddSingular("shoes", "shoe") rs.AddSingular("crises", "crisis") rs.AddSingularExact("crisis", "crisis", true) rs.AddSingular("axes", "axis") rs.AddSingularExact("axis", "axis", true) rs.AddSingular("testes", "testis") rs.AddSingularExact("testis", "testis", true) rs.AddSingular("octopi", "octopus") rs.AddSingularExact("octopus", "octopus", true) rs.AddSingular("viri", "virus") rs.AddSingularExact("virus", "virus", true) rs.AddSingular("statuses", "status") rs.AddSingular("Statuses", "Status") rs.AddSingular("campuses", "campus") rs.AddSingularExact("status", "status", true) rs.AddSingularExact("Status", "Status", true) rs.AddSingularExact("campus", "campus", true) rs.AddSingular("aliases", "alias") rs.AddSingularExact("alias", "alias", true) rs.AddSingularExact("oxen", "ox", true) rs.AddSingular("vertices", "vertex") rs.AddSingular("indices", "index") rs.AddSingular("matrices", "matrix") rs.AddSingularExact("quizzes", "quiz", true) rs.AddSingular("databases", "database") rs.AddSingular("resses", "ress") rs.AddSingular("ress", "ress") rs.AddIrregular("person", "people") rs.AddIrregular("man", "men") rs.AddIrregular("child", "children") rs.AddIrregular("sex", "sexes") rs.AddIrregular("move", "moves") rs.AddIrregular("zombie", "zombies") rs.AddIrregular("Status", "Statuses") rs.AddIrregular("status", "statuses") rs.AddIrregular("campus", "campuses") rs.AddIrregular("human", "humans") rs.AddUncountable("equipment") rs.AddUncountable("information") rs.AddUncountable("rice") rs.AddUncountable("money") rs.AddUncountable("species") rs.AddUncountable("series") rs.AddUncountable("fish") rs.AddUncountable("sheep") rs.AddUncountable("jeans") rs.AddUncountable("police") acronyms := strings.Split(baseAcronyms, ",") for _, acr := range acronyms { rs.AddAcronym(acr) } return rs } // Uncountables returns a map of uncountables in the ruleset func (rs *Ruleset) Uncountables() map[string]bool { return rs.uncountables } // AddPlural add a pluralization rule func (rs *Ruleset) AddPlural(suffix, replacement string) { rs.AddPluralExact(suffix, replacement, false) } // AddPluralExact add a pluralization rule with full string match func (rs *Ruleset) AddPluralExact(suffix, replacement string, exact bool) { // remove uncountable delete(rs.uncountables, suffix) // create rule r := new(Rule) r.suffix = suffix r.replacement = replacement r.exact = exact // prepend rs.plurals = append([]*Rule{r}, rs.plurals...) } // AddSingular add a singular rule func (rs *Ruleset) AddSingular(suffix, replacement string) { rs.AddSingularExact(suffix, replacement, false) } // AddSingularExact same as AddSingular but you can set `exact` to force // a full string match func (rs *Ruleset) AddSingularExact(suffix, replacement string, exact bool) { // remove from uncountable delete(rs.uncountables, suffix) // create rule r := new(Rule) r.suffix = suffix r.replacement = replacement r.exact = exact rs.singulars = append([]*Rule{r}, rs.singulars...) } // AddHuman Human rules are applied by humanize to show more friendly // versions of words func (rs *Ruleset) AddHuman(suffix, replacement string) { r := new(Rule) r.suffix = suffix r.replacement = replacement rs.humans = append([]*Rule{r}, rs.humans...) } // AddIrregular Add any inconsistent pluralizing/singularizing rules // to the set here. func (rs *Ruleset) AddIrregular(singular, plural string) { delete(rs.uncountables, singular) delete(rs.uncountables, plural) rs.AddPlural(singular, plural) rs.AddPlural(plural, plural) rs.AddSingular(plural, singular) } // AddAcronym if you use acronym you may need to add them to the ruleset // to prevent Underscored words of things like "HTML" coming out // as "h_t_m_l" func (rs *Ruleset) AddAcronym(word string) { r := new(Rule) r.suffix = word r.replacement = rs.Titleize(strings.ToLower(word)) rs.acronyms = append(rs.acronyms, r) } // AddUncountable add a word to this ruleset that has the same singular and plural form // for example: "rice" func (rs *Ruleset) AddUncountable(word string) { rs.uncountables[strings.ToLower(word)] = true } func (rs *Ruleset) isUncountable(word string) bool { // handle multiple words by using the last one words := strings.Split(word, " ") if _, exists := rs.uncountables[strings.ToLower(words[len(words)-1])]; exists { return true } return false } //isAcronym returns if a word is acronym or not. func (rs *Ruleset) isAcronym(word string) bool { for _, rule := range rs.acronyms { if strings.ToUpper(rule.suffix) == strings.ToUpper(word) { return true } } return false } //PluralizeWithSize pluralize with taking number into account func (rs *Ruleset) PluralizeWithSize(word string, size int) string { if size == 1 { return rs.Singularize(word) } return rs.Pluralize(word) } // Pluralize returns the plural form of a singular word func (rs *Ruleset) Pluralize(word string) string { if len(word) == 0 { return word } lWord := strings.ToLower(word) if rs.isUncountable(lWord) { return word } var candidate string for _, rule := range rs.plurals { if rule.exact { if lWord == rule.suffix { // Capitalized word if lWord[0] != word[0] && lWord[1:] == word[1:] { return rs.Capitalize(rule.replacement) } return rule.replacement } continue } if strings.EqualFold(word, rule.suffix) { candidate = rule.replacement } if strings.HasSuffix(word, rule.suffix) { return replaceLast(word, rule.suffix, rule.replacement) } } if candidate != "" { return candidate } return word + "s" } //Singularize returns the singular form of a plural word func (rs *Ruleset) Singularize(word string) string { if len(word) <= 1 { return word } lWord := strings.ToLower(word) if rs.isUncountable(lWord) { return word } var candidate string for _, rule := range rs.singulars { if rule.exact { if lWord == rule.suffix { // Capitalized word if lWord[0] != word[0] && lWord[1:] == word[1:] { return rs.Capitalize(rule.replacement) } return rule.replacement } continue } if strings.EqualFold(word, rule.suffix) { candidate = rule.replacement } if strings.HasSuffix(word, rule.suffix) { return replaceLast(word, rule.suffix, rule.replacement) } } if candidate != "" { return candidate } return word } //Capitalize uppercase first character func (rs *Ruleset) Capitalize(word string) string { if rs.isAcronym(word) { return strings.ToUpper(word) } return strings.ToUpper(word[:1]) + word[1:] } //Camelize "dino_party" -> "DinoParty" func (rs *Ruleset) Camelize(word string) string { if rs.isAcronym(word) { return strings.ToUpper(word) } words := splitAtCaseChangeWithTitlecase(word) return strings.Join(words, "") } //CamelizeDownFirst same as Camelcase but with first letter downcased func (rs *Ruleset) CamelizeDownFirst(word string) string { word = Camelize(word) return strings.ToLower(word[:1]) + word[1:] } //Titleize Capitalize every word in sentence "hello there" -> "Hello There" func (rs *Ruleset) Titleize(word string) string { words := splitAtCaseChangeWithTitlecase(word) result := strings.Join(words, " ") var acronymWords []string for index, word := range words { if len(word) == 1 { acronymWords = append(acronymWords, word) } if len(word) > 1 || index == len(words)-1 || len(acronymWords) > 1 { acronym := strings.Join(acronymWords, "") if !rs.isAcronym(acronym) { acronymWords = acronymWords[:len(acronymWords)] continue } result = strings.Replace(result, strings.Join(acronymWords, " "), acronym, 1) acronymWords = []string{} } } return result } func (rs *Ruleset) safeCaseAcronyms(word string) string { // convert an acronym like HTML into Html for _, rule := range rs.acronyms { word = strings.Replace(word, rule.suffix, rule.replacement, -1) } return word } func (rs *Ruleset) separatedWords(word, sep string) string { word = rs.safeCaseAcronyms(word) words := splitAtCaseChange(word) return strings.Join(words, sep) } //Underscore lowercase underscore version "BigBen" -> "big_ben" func (rs *Ruleset) Underscore(word string) string { return rs.separatedWords(word, "_") } //Humanize First letter of sentence capitalized // Uses custom friendly replacements via AddHuman() func (rs *Ruleset) Humanize(word string) string { word = replaceLast(word, "_id", "") // strip foreign key kinds // replace and strings in humans list for _, rule := range rs.humans { word = strings.Replace(word, rule.suffix, rule.replacement, -1) } sentence := rs.separatedWords(word, " ") r, n := utf8.DecodeRuneInString(sentence) return string(unicode.ToUpper(r)) + sentence[n:] } //ForeignKey an underscored foreign key name "Person" -> "person_id" func (rs *Ruleset) ForeignKey(word string) string { return rs.Underscore(rs.Singularize(word)) + "_id" } //ForeignKeyCondensed a foreign key (with an underscore) "Person" -> "personid" func (rs *Ruleset) ForeignKeyCondensed(word string) string { return rs.Underscore(word) + "id" } //Tableize Rails style pluralized table names: "SuperPerson" -> "super_people" func (rs *Ruleset) Tableize(word string) string { return rs.Pluralize(rs.Underscore(rs.Typeify(word))) } var notUrlSafe *regexp.Regexp = regexp.MustCompile(`[^\w\d\-_ ]`) //Parameterize param safe dasherized names like "my-param" func (rs *Ruleset) Parameterize(word string) string { return ParameterizeJoin(word, "-") } //ParameterizeJoin param safe dasherized names with custom separator func (rs *Ruleset) ParameterizeJoin(word, sep string) string { word = strings.ToLower(word) word = rs.Asciify(word) word = notUrlSafe.ReplaceAllString(word, "") word = strings.Replace(word, " ", sep, -1) if len(sep) > 0 { squash, err := regexp.Compile(sep + "+") if err == nil { word = squash.ReplaceAllString(word, sep) } } word = strings.Trim(word, sep+" ") return word } var lookalikes = map[string]*regexp.Regexp{ "A": regexp.MustCompile(`À|Á|Â|Ã|Ä|Å`), "AE": regexp.MustCompile(`Æ`), "C": regexp.MustCompile(`Ç`), "E": regexp.MustCompile(`È|É|Ê|Ë`), "G": regexp.MustCompile(`Ğ`), "I": regexp.MustCompile(`Ì|Í|Î|Ï|İ`), "N": regexp.MustCompile(`Ñ`), "O": regexp.MustCompile(`Ò|Ó|Ô|Õ|Ö|Ø`), "S": regexp.MustCompile(`Ş`), "U": regexp.MustCompile(`Ù|Ú|Û|Ü`), "Y": regexp.MustCompile(`Ý`), "ss": regexp.MustCompile(`ß`), "a": regexp.MustCompile(`à|á|â|ã|ä|å`), "ae": regexp.MustCompile(`æ`), "c": regexp.MustCompile(`ç`), "e": regexp.MustCompile(`è|é|ê|ë`), "g": regexp.MustCompile(`ğ`), "i": regexp.MustCompile(`ì|í|î|ï|ı`), "n": regexp.MustCompile(`ñ`), "o": regexp.MustCompile(`ò|ó|ô|õ|ö|ø`), "s": regexp.MustCompile(`ş`), "u": regexp.MustCompile(`ù|ú|û|ü|ũ|ū|ŭ|ů|ű|ų`), "y": regexp.MustCompile(`ý|ÿ`), } //Asciify transforms Latin characters like é -> e func (rs *Ruleset) Asciify(word string) string { for repl, regex := range lookalikes { word = regex.ReplaceAllString(word, repl) } return word } var tablePrefix = regexp.MustCompile(`^[^.]*\.`) //Typeify "something_like_this" -> "SomethingLikeThis" func (rs *Ruleset) Typeify(word string) string { word = tablePrefix.ReplaceAllString(word, "") return rs.Camelize(rs.Singularize(word)) } //Dasherize "SomeText" -> "some-text" func (rs *Ruleset) Dasherize(word string) string { return rs.separatedWords(word, "-") } //Ordinalize "1031" -> "1031st" func (rs *Ruleset) Ordinalize(str string) string { number, err := strconv.Atoi(str) if err != nil { return str } switch abs(number) % 100 { case 11, 12, 13: return fmt.Sprintf("%dth", number) default: switch abs(number) % 10 { case 1: return fmt.Sprintf("%dst", number) case 2: return fmt.Sprintf("%dnd", number) case 3: return fmt.Sprintf("%drd", number) } } return fmt.Sprintf("%dth", number) } //ForeignKeyToAttribute returns the attribute name from the foreign key func (rs *Ruleset) ForeignKeyToAttribute(str string) string { w := rs.Camelize(str) if strings.HasSuffix(w, "Id") { return strings.TrimSuffix(w, "Id") + "ID" } return w } //LoadReader loads rules from io.Reader param func (rs *Ruleset) LoadReader(r io.Reader) error { m := map[string]string{} err := json.NewDecoder(r).Decode(&m) if err != nil { return fmt.Errorf("could not decode inflection JSON from reader: %s", err) } for s, p := range m { defaultRuleset.AddIrregular(s, p) } return nil } ///////////////////////////////////////// // the default global ruleset ////////////////////////////////////////// var defaultRuleset *Ruleset //LoadReader loads rules from io.Reader param func LoadReader(r io.Reader) error { return defaultRuleset.LoadReader(r) } func init() { defaultRuleset = NewDefaultRuleset() pwd, _ := os.Getwd() cfg := filepath.Join(pwd, "inflections.json") if p := os.Getenv("INFLECT_PATH"); p != "" { cfg = p } if _, err := os.Stat(cfg); err == nil { b, err := ioutil.ReadFile(cfg) if err != nil { fmt.Printf("could not read inflection file %s (%s)\n", cfg, err) return } if err = defaultRuleset.LoadReader(bytes.NewReader(b)); err != nil { fmt.Println(err) } } } //Uncountables returns a list of uncountables rules func Uncountables() map[string]bool { return defaultRuleset.Uncountables() } //AddPlural adds plural to the ruleset func AddPlural(suffix, replacement string) { defaultRuleset.AddPlural(suffix, replacement) } //AddSingular adds singular to the ruleset func AddSingular(suffix, replacement string) { defaultRuleset.AddSingular(suffix, replacement) } //AddHuman adds human func AddHuman(suffix, replacement string) { defaultRuleset.AddHuman(suffix, replacement) } func AddIrregular(singular, plural string) { defaultRuleset.AddIrregular(singular, plural) } func AddAcronym(word string) { defaultRuleset.AddAcronym(word) } func AddUncountable(word string) { defaultRuleset.AddUncountable(word) } func Pluralize(word string) string { return defaultRuleset.Pluralize(word) } func PluralizeWithSize(word string, size int) string { return defaultRuleset.PluralizeWithSize(word, size) } func Singularize(word string) string { return defaultRuleset.Singularize(word) } func Capitalize(word string) string { return defaultRuleset.Capitalize(word) } func Camelize(word string) string { return defaultRuleset.Camelize(word) } func CamelizeDownFirst(word string) string { return defaultRuleset.CamelizeDownFirst(word) } func Titleize(word string) string { return defaultRuleset.Titleize(word) } func Underscore(word string) string { return defaultRuleset.Underscore(word) } func Humanize(word string) string { return defaultRuleset.Humanize(word) } func ForeignKey(word string) string { return defaultRuleset.ForeignKey(word) } func ForeignKeyCondensed(word string) string { return defaultRuleset.ForeignKeyCondensed(word) } func Tableize(word string) string { return defaultRuleset.Tableize(word) } func Parameterize(word string) string { return defaultRuleset.Parameterize(word) } func ParameterizeJoin(word, sep string) string { return defaultRuleset.ParameterizeJoin(word, sep) } func Typeify(word string) string { return defaultRuleset.Typeify(word) } func Dasherize(word string) string { return defaultRuleset.Dasherize(word) } func Ordinalize(word string) string { return defaultRuleset.Ordinalize(word) } func Asciify(word string) string { return defaultRuleset.Asciify(word) } func ForeignKeyToAttribute(word string) string { return defaultRuleset.ForeignKeyToAttribute(word) } // helper funcs func reverse(s string) string { o := make([]rune, utf8.RuneCountInString(s)) i := len(o) for _, c := range s { i-- o[i] = c } return string(o) } func isSpacerChar(c rune) bool { switch { case c == rune("_"[0]): return true case c == rune(" "[0]): return true case c == rune(":"[0]): return true case c == rune("-"[0]): return true } return false } func splitAtCaseChange(s string) []string { words := make([]string, 0) word := make([]rune, 0) for _, c := range s { spacer := isSpacerChar(c) if len(word) > 0 { if unicode.IsUpper(c) || spacer { words = append(words, string(word)) word = make([]rune, 0) } } if !spacer { word = append(word, unicode.ToLower(c)) } } words = append(words, string(word)) return words } func splitAtCaseChangeWithTitlecase(s string) []string { words := make([]string, 0) word := make([]rune, 0) for _, c := range s { spacer := isSpacerChar(c) if len(word) > 0 { if unicode.IsUpper(c) || spacer { words = append(words, string(word)) word = make([]rune, 0) } } if !spacer { if len(word) > 0 { word = append(word, unicode.ToLower(c)) } else { word = append(word, unicode.ToUpper(c)) } } } words = append(words, string(word)) return words } func replaceLast(s, match, repl string) string { // reverse strings srev := reverse(s) mrev := reverse(match) rrev := reverse(repl) // match first and reverse back return reverse(strings.Replace(srev, mrev, rrev, 1)) } func abs(x int) int { if x < 0 { return -x } return x } inflect-1.0.4/inflect_test.go000066400000000000000000000426171336415356400161760ustar00rootroot00000000000000package inflect import ( "testing" "github.com/stretchr/testify/require" ) // test data var SingularToPlural = map[string]string{ "search": "searches", "switch": "switches", "fix": "fixes", "box": "boxes", "process": "processes", "address": "addresses", "case": "cases", "stack": "stacks", "wish": "wishes", "fish": "fish", "jeans": "jeans", "funky jeans": "funky jeans", "category": "categories", "query": "queries", "ability": "abilities", "agency": "agencies", "movie": "movies", "archive": "archives", "index": "indices", "wife": "wives", "safe": "saves", "half": "halves", "move": "moves", "salesperson": "salespeople", "person": "people", "spokesman": "spokesmen", "man": "men", "woman": "women", "basis": "bases", "diagnosis": "diagnoses", "diagnosis_a": "diagnosis_as", "datum": "data", "medium": "media", "stadium": "stadia", "analysis": "analyses", "node_child": "node_children", "child": "children", "experience": "experiences", "day": "days", "comment": "comments", "foobar": "foobars", "newsletter": "newsletters", "old_news": "old_news", "news": "news", "series": "series", "species": "species", "quiz": "quizzes", "perspective": "perspectives", "ox": "oxen", "photo": "photos", "buffalo": "buffaloes", "tomato": "tomatoes", "dwarf": "dwarves", "elf": "elves", "information": "information", "equipment": "equipment", "bus": "buses", "status": "statuses", "Status": "Statuses", "status_code": "status_codes", "mouse": "mice", "louse": "lice", "house": "houses", "octopus": "octopi", "virus": "viri", "alias": "aliases", "portfolio": "portfolios", "vertex": "vertices", "matrix": "matrices", "matrix_fu": "matrix_fus", "axis": "axes", "testis": "testes", "crisis": "crises", "rice": "rice", "shoe": "shoes", "horse": "horses", "prize": "prizes", "edge": "edges", "database": "databases", } var CapitalizeMixture = map[string]string{ "product": "Product", "special_guest": "Special_guest", "applicationController": "ApplicationController", "Area51Controller": "Area51Controller", "id": "ID", "SQL": "SQL", "sql": "SQL", "sQL": "SQL", } var CamelToUnderscore = map[string]string{ "Product": "product", "SpecialGuest": "special_guest", "ApplicationController": "application_controller", "Area51Controller": "area51_controller", } var UnderscoreToLowerCamel = map[string]string{ "product": "product", "special_guest": "specialGuest", "application_controller": "applicationController", "area51_controller": "area51Controller", } var CamelToUnderscoreWithoutReverse = map[string]string{ "HTMLTidy": "html_tidy", "HTMLTidyGenerator": "html_tidy_generator", "FreeBsd": "free_bsd", "HTML": "html", } var ClassNameToForeignKeyWithUnderscore = map[string]string{ "Person": "person_id", "Account": "account_id", } var PluralToForeignKeyWithUnderscore = map[string]string{ "people": "person_id", "accounts": "account_id", } var ClassNameToForeignKeyWithoutUnderscore = map[string]string{ "Person": "personid", "Account": "accountid", } var ClassNameToTableName = map[string]string{ "PrimarySpokesman": "primary_spokesmen", "NodeChild": "node_children", "Alias": "aliases", } var StringToParameterized = map[string]string{ "Donald E. Knuth": "donald-e-knuth", "Random text with *(bad)* characters": "random-text-with-bad-characters", "Allow_Under_Scores": "allow_under_scores", "Trailing bad characters!@#": "trailing-bad-characters", "!@#Leading bad characters": "leading-bad-characters", "Squeeze separators": "squeeze-separators", "Test with + sign": "test-with-sign", "Test with malformed utf8 \251": "test-with-malformed-utf8", } var StringToParameterizeWithNoSeparator = map[string]string{ "Donald E. Knuth": "donaldeknuth", "With-some-dashes": "with-some-dashes", "Random text with *(bad)* characters": "randomtextwithbadcharacters", "Trailing bad characters!@#": "trailingbadcharacters", "!@#Leading bad characters": "leadingbadcharacters", "Squeeze separators": "squeezeseparators", "Test with + sign": "testwithsign", "Test with malformed utf8 \251": "testwithmalformedutf8", } var StringToParameterizeWithUnderscore = map[string]string{ "Donald E. Knuth": "donald_e_knuth", "Random text with *(bad)* characters": "random_text_with_bad_characters", "With-some-dashes": "with-some-dashes", "Retain_underscore": "retain_underscore", "Trailing bad characters!@#": "trailing_bad_characters", "!@#Leading bad characters": "leading_bad_characters", "Squeeze separators": "squeeze_separators", "Test with + sign": "test_with_sign", "Test with malformed utf8 \251": "test_with_malformed_utf8", } var StringToParameterizedAndNormalized = map[string]string{ "Malmö": "malmo", "Garçons": "garcons", "Opsů": "opsu", "Ærøskøbing": "aeroskobing", "Aßlar": "asslar", "Japanese: 日本語": "japanese", } var UnderscoreToHuman = map[string]string{ "employee_salary": "Employee salary", "employee_id": "Employee", "underground": "Underground", "óbito": "Óbito", } var MixtureToTitleCase = map[string]string{ "active_record": "Active Record", "ActiveRecord": "Active Record", "action web service": "Action Web Service", "Action Web Service": "Action Web Service", "Action web service": "Action Web Service", "actionwebservice": "Actionwebservice", "Actionwebservice": "Actionwebservice", "david's code": "David's Code", "David's code": "David's Code", "david's Code": "David's Code", "my_cool_URL_enabled": "My Cool URL Enabled", "service_API_URL": "Service API URL", } var OrdinalNumbers = map[string]string{ "-1": "-1st", "-2": "-2nd", "-3": "-3rd", "-4": "-4th", "-5": "-5th", "-6": "-6th", "-7": "-7th", "-8": "-8th", "-9": "-9th", "-10": "-10th", "-11": "-11th", "-12": "-12th", "-13": "-13th", "-14": "-14th", "-20": "-20th", "-21": "-21st", "-22": "-22nd", "-23": "-23rd", "-24": "-24th", "-100": "-100th", "-101": "-101st", "-102": "-102nd", "-103": "-103rd", "-104": "-104th", "-110": "-110th", "-111": "-111th", "-112": "-112th", "-113": "-113th", "-1000": "-1000th", "-1001": "-1001st", "0": "0th", "1": "1st", "2": "2nd", "3": "3rd", "4": "4th", "5": "5th", "6": "6th", "7": "7th", "8": "8th", "9": "9th", "10": "10th", "11": "11th", "12": "12th", "13": "13th", "14": "14th", "20": "20th", "21": "21st", "22": "22nd", "23": "23rd", "24": "24th", "100": "100th", "101": "101st", "102": "102nd", "103": "103rd", "104": "104th", "110": "110th", "111": "111th", "112": "112th", "113": "113th", "1000": "1000th", "1001": "1001st", } var UnderscoresToDashes = map[string]string{ "street": "street", "street_address": "street-address", "person_street_address": "person-street-address", } var Irregularities = map[string]string{ "person": "people", "man": "men", "child": "children", "sex": "sexes", "move": "moves", "human": "humans", } type AcronymCase struct { camel string under string human string title string } var AcronymCases = []*AcronymCase{ // camelize underscore humanize titleize &AcronymCase{"API", "api", "API", "API"}, &AcronymCase{"APIController", "api_controller", "API controller", "API Controller"}, &AcronymCase{"Nokogiri::HTML", "nokogiri/html", "Nokogiri/HTML", "Nokogiri/HTML"}, &AcronymCase{"HTTPAPI", "http_api", "HTTP API", "HTTP API"}, &AcronymCase{"HTTP::Get", "http/get", "HTTP/get", "HTTP/Get"}, &AcronymCase{"SSLError", "ssl_error", "SSL error", "SSL Error"}, &AcronymCase{"RESTful", "restful", "RESTful", "RESTful"}, &AcronymCase{"RESTfulController", "restful_controller", "RESTful controller", "RESTful Controller"}, &AcronymCase{"IHeartW3C", "i_heart_w3c", "I heart W3C", "I Heart W3C"}, &AcronymCase{"PhDRequired", "phd_required", "PhD required", "PhD Required"}, &AcronymCase{"IRoRU", "i_ror_u", "I RoR u", "I RoR U"}, &AcronymCase{"RESTfulHTTPAPI", "restful_http_api", "RESTful HTTP API", "RESTful HTTP API"}, // misdirection &AcronymCase{"Capistrano", "capistrano", "Capistrano", "Capistrano"}, &AcronymCase{"CapiController", "capi_controller", "Capi controller", "Capi Controller"}, &AcronymCase{"HttpsApis", "https_apis", "Https apis", "Https Apis"}, &AcronymCase{"Html5", "html5", "Html5", "Html5"}, &AcronymCase{"Restfully", "restfully", "Restfully", "Restfully"}, &AcronymCase{"RoRails", "ro_rails", "Ro rails", "Ro Rails"}, } // tests func Test_LoadViaFile(t *testing.T) { require.Equal(t, "feedback", Pluralize("feedback")) require.Equal(t, "buffalo!", Singularize("buffalos!")) } func TestForeignKeyToAttribute(t *testing.T) { require.Equal(t, "PersonID", ForeignKeyToAttribute("person_id")) require.Equal(t, "ID", ForeignKeyToAttribute("id")) } func TestPluralizeWithSize(t *testing.T) { require.Equal(t, "plurals", PluralizeWithSize("plurals", 2)) require.Equal(t, "plurals", PluralizeWithSize("plurals", 0)) require.Equal(t, "plural", PluralizeWithSize("plurals", 1)) } func TestPluralizePlurals(t *testing.T) { require.Equal(t, "plurals", Pluralize("plurals")) require.Equal(t, "Plurals", Pluralize("Plurals")) } func TestPluralizeEmptyString(t *testing.T) { require.Equal(t, "", Pluralize("")) } func TestUncountables(t *testing.T) { for word := range Uncountables() { require.Equal(t, word, Singularize(word)) require.Equal(t, word, Pluralize(word)) require.Equal(t, Pluralize(word), Singularize(word)) } } func TestUncountableWordIsNotGreedy(t *testing.T) { uncountableWord := "ors" countableWord := "sponsor" AddUncountable(uncountableWord) require.Equal(t, uncountableWord, Singularize(uncountableWord)) require.Equal(t, uncountableWord, Pluralize(uncountableWord)) require.Equal(t, Pluralize(uncountableWord), Singularize(uncountableWord)) require.Equal(t, "sponsor", Singularize(countableWord)) require.Equal(t, "sponsors", Pluralize(countableWord)) require.Equal(t, "sponsor", Singularize(Pluralize(countableWord))) } func TestPluralizeSingular(t *testing.T) { for singular, plural := range SingularToPlural { require.Equal(t, plural, Pluralize(singular)) require.Equal(t, Capitalize(plural), Capitalize(Pluralize(singular))) } } func TestSingularizePlural(t *testing.T) { for singular, plural := range SingularToPlural { require.Equal(t, singular, Singularize(plural)) require.Equal(t, Capitalize(singular), Capitalize(Singularize(plural))) } } func TestSingularizeSingular(t *testing.T) { for singular := range SingularToPlural { require.Equal(t, singular, Singularize(singular)) require.Equal(t, Capitalize(singular), Capitalize(Singularize(singular))) } } func TestPluralizePlural(t *testing.T) { for _, plural := range SingularToPlural { require.Equal(t, plural, Pluralize(plural)) require.Equal(t, Capitalize(plural), Capitalize(Pluralize(plural))) } } func TestOverwritePreviousInflectors(t *testing.T) { require.Equal(t, "series", Singularize("series")) AddSingular("series", "serie") require.Equal(t, "serie", Singularize("series")) AddUncountable("series") // reset } func TestTitleize(t *testing.T) { for before, titleized := range MixtureToTitleCase { require.Equal(t, titleized, Titleize(before)) } } func TestCapitalize(t *testing.T) { for lower, capitalized := range CapitalizeMixture { require.Equal(t, capitalized, Capitalize(lower)) } } func TestCamelize(t *testing.T) { for camel, underscore := range CamelToUnderscore { require.Equal(t, camel, Camelize(underscore)) } } func TestCamelizeWithLowerDowncasesTheFirstLetter(t *testing.T) { require.Equal(t, "capital", CamelizeDownFirst("Capital")) } func TestCamelizeWithUnderscores(t *testing.T) { require.Equal(t, "CamelCase", Camelize("Camel_Case")) } // func TestAcronyms(t *testing.T) { // AddAcronym("API") // AddAcronym("HTML") // AddAcronym("HTTP") // AddAcronym("RESTful") // AddAcronym("W3C") // AddAcronym("PhD") // AddAcronym("RoR") // AddAcronym("SSL") // // each in table // for _,x := range AcronymCases { // require.Equal(t, x.camel, Camelize(x.under)) // require.Equal(t, x.camel, Camelize(x.camel)) // require.Equal(t, x.under, Underscore(x.under)) // require.Equal(t, x.under, Underscore(x.camel)) // require.Equal(t, x.title, Titleize(x.under)) // require.Equal(t, x.title, Titleize(x.camel)) // require.Equal(t, x.human, Humanize(x.under)) // } // } // func TestAcronymOverride(t *testing.T) { // AddAcronym("API") // AddAcronym("LegacyApi") // require.Equal(t, "LegacyApi", Camelize("legacyapi")) // require.Equal(t, "LegacyAPI", Camelize("legacy_api")) // require.Equal(t, "SomeLegacyApi", Camelize("some_legacyapi")) // require.Equal(t, "Nonlegacyapi", Camelize("nonlegacyapi")) // } // func TestAcronymsCamelizeLower(t *testing.T) { // AddAcronym("API") // AddAcronym("HTML") // require.Equal(t, "htmlAPI", CamelizeDownFirst("html_api")) // require.Equal(t, "htmlAPI", CamelizeDownFirst("htmlAPI")) // require.Equal(t, "htmlAPI", CamelizeDownFirst("HTMLAPI")) // } func TestUnderscoreAcronymSequence(t *testing.T) { AddAcronym("API") AddAcronym("HTML5") AddAcronym("HTML") require.Equal(t, "html5_html_api", Underscore("HTML5HTMLAPI")) } func TestUnderscore(t *testing.T) { for camel, underscore := range CamelToUnderscore { require.Equal(t, underscore, Underscore(camel)) } for camel, underscore := range CamelToUnderscoreWithoutReverse { require.Equal(t, underscore, Underscore(camel)) } } func TestForeignKey(t *testing.T) { for klass, foreignKey := range ClassNameToForeignKeyWithUnderscore { require.Equal(t, foreignKey, ForeignKey(klass)) } for word, foreignKey := range PluralToForeignKeyWithUnderscore { require.Equal(t, foreignKey, ForeignKey(word)) } for klass, foreignKey := range ClassNameToForeignKeyWithoutUnderscore { require.Equal(t, foreignKey, ForeignKeyCondensed(klass)) } } func TestTableize(t *testing.T) { for klass, table := range ClassNameToTableName { require.Equal(t, table, Tableize(klass)) } } func TestParameterize(t *testing.T) { for str, parameterized := range StringToParameterized { require.Equal(t, parameterized, Parameterize(str)) } } func TestParameterizeAndNormalize(t *testing.T) { for str, parameterized := range StringToParameterizedAndNormalized { require.Equal(t, parameterized, Parameterize(str)) } } func TestParameterizeWithCustomSeparator(t *testing.T) { for str, parameterized := range StringToParameterizeWithUnderscore { require.Equal(t, parameterized, ParameterizeJoin(str, "_")) } } func TestTypeify(t *testing.T) { for klass, table := range ClassNameToTableName { require.Equal(t, klass, Typeify(table)) require.Equal(t, klass, Typeify("table_prefix."+table)) } } func TestTypeifyWithLeadingSchemaName(t *testing.T) { require.Equal(t, "FooBar", Typeify("schema.foo_bar")) } func TestHumanize(t *testing.T) { for underscore, human := range UnderscoreToHuman { require.Equal(t, human, Humanize(underscore)) } } func TestHumanizeByString(t *testing.T) { AddHuman("col_rpted_bugs", "reported bugs") require.Equal(t, "90 reported bugs recently", Humanize("90 col_rpted_bugs recently")) } func TestOrdinal(t *testing.T) { for number, ordinalized := range OrdinalNumbers { require.Equal(t, ordinalized, Ordinalize(number)) } } func TestDasherize(t *testing.T) { for underscored, dasherized := range UnderscoresToDashes { require.Equal(t, dasherized, Dasherize(underscored)) } } func TestUnderscoreAsReverseOfDasherize(t *testing.T) { for underscored := range UnderscoresToDashes { require.Equal(t, underscored, Underscore(Dasherize(underscored))) } } func TestUnderscoreToLowerCamel(t *testing.T) { for underscored, lower := range UnderscoreToLowerCamel { require.Equal(t, lower, CamelizeDownFirst(underscored)) } } func Test_clear_all(t *testing.T) { // test a way of resetting inflexions } func TestIrregularityBetweenSingularAndPlural(t *testing.T) { for singular, plural := range Irregularities { AddIrregular(singular, plural) require.Equal(t, singular, Singularize(plural)) require.Equal(t, plural, Pluralize(singular)) } } func TestPluralizeOfIrregularity(t *testing.T) { for singular, plural := range Irregularities { AddIrregular(singular, plural) require.Equal(t, plural, Pluralize(plural)) } } func Test_Address(t *testing.T) { require.Equal(t, "address", Singularize("address")) require.Equal(t, "addresses", Pluralize("address")) require.Equal(t, "address", Singularize("addresses")) require.Equal(t, "addresses", Pluralize("addresses")) } inflect-1.0.4/inflections.json000066400000000000000000000000701336415356400163570ustar00rootroot00000000000000{ "feedback": "feedback", "buffalo!": "buffalos!" } inflect-1.0.4/name.go000066400000000000000000000066631336415356400144340ustar00rootroot00000000000000package inflect import ( "fmt" "path/filepath" "strings" "github.com/gobuffalo/envy" ) // Name is a string that represents the "name" of a thing, like an app, model, etc... type Name string // Title version of a name. ie. "foo_bar" => "Foo Bar" func (n Name) Title() string { x := strings.Split(string(n), "/") for i, s := range x { x[i] = Titleize(s) } return strings.Join(x, " ") } // Underscore version of a name. ie. "FooBar" => "foo_bar" func (n Name) Underscore() string { w := string(n) if strings.ToUpper(w) == w { return strings.ToLower(w) } return Underscore(w) } // Plural version of a name func (n Name) Plural() string { return Pluralize(string(n)) } // Singular version of a name func (n Name) Singular() string { return Singularize(string(n)) } // Camel version of a name func (n Name) Camel() string { c := Camelize(string(n)) if strings.HasSuffix(c, "Id") { c = strings.TrimSuffix(c, "Id") c += "ID" } return c } // Model version of a name. ie. "user" => "User" func (n Name) Model() string { x := strings.Split(string(n), "/") for i, s := range x { x[i] = Camelize(Singularize(s)) } return strings.Join(x, "") } // Resource version of a name func (n Name) Resource() string { name := n.Underscore() x := strings.FieldsFunc(name, func(r rune) bool { return r == '_' || r == '/' }) for i, w := range x { if i == len(x)-1 { x[i] = Camelize(Pluralize(strings.ToLower(w))) continue } x[i] = Camelize(w) } return strings.Join(x, "") } // ModelPlural version of a name. ie. "user" => "Users" func (n Name) ModelPlural() string { return Camelize(Pluralize(n.Model())) } // File version of a name func (n Name) File() string { return Underscore(Camelize(string(n))) } // Table version of a name func (n Name) Table() string { return Underscore(Pluralize(string(n))) } // UnderSingular version of a name func (n Name) UnderSingular() string { return Underscore(Singularize(string(n))) } // PluralCamel version of a name func (n Name) PluralCamel() string { return Pluralize(Camelize(string(n))) } // PluralUnder version of a name func (n Name) PluralUnder() string { return Pluralize(Underscore(string(n))) } // URL version of a name func (n Name) URL() string { return n.PluralUnder() } // CamelSingular version of a name func (n Name) CamelSingular() string { return Camelize(Singularize(string(n))) } // VarCaseSingular version of a name. ie. "FooBar" => "fooBar" func (n Name) VarCaseSingular() string { return CamelizeDownFirst(Singularize(Underscore(n.Resource()))) } // VarCasePlural version of a name. ie. "FooBar" => "fooBar" func (n Name) VarCasePlural() string { return CamelizeDownFirst(n.Resource()) } // Lower case version of a string func (n Name) Lower() string { return strings.ToLower(string(n)) } // ParamID returns foo_bar_id func (n Name) ParamID() string { return fmt.Sprintf("%s_id", strings.Replace(n.UnderSingular(), "/", "_", -1)) } // Package returns go package func (n Name) Package() string { key := string(n) for _, gp := range envy.GoPaths() { key = strings.TrimPrefix(key, filepath.Join(gp, "src")) key = strings.TrimPrefix(key, gp) } key = strings.TrimPrefix(key, string(filepath.Separator)) key = strings.Replace(key, "\\", "/", -1) return key } // Char returns first character in lower case, this is useful for methods inside a struct. func (n Name) Char() string { return strings.ToLower(string(n[0])) } func (n Name) String() string { return string(n) } inflect-1.0.4/name_test.go000066400000000000000000000127141336415356400154650ustar00rootroot00000000000000package inflect import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/require" ) func Test_Name_Camel(t *testing.T) { r := require.New(t) table := []struct { V string E string }{ {V: "foo_bar", E: "FooBar"}, {V: "widget", E: "Widget"}, {V: "User", E: "User"}, {V: "user_id", E: "UserID"}, {V: "post", E: "Post"}, } for _, tt := range table { r.Equal(tt.E, Name(tt.V).Camel()) } } func Test_Name_ParamID(t *testing.T) { r := require.New(t) table := []struct { V string E string }{ {V: "foo_bar", E: "foo_bar_id"}, {V: "admin/widget", E: "admin_widget_id"}, {V: "widget", E: "widget_id"}, {V: "User", E: "user_id"}, {V: "Movies", E: "movie_id"}, {V: "movies", E: "movie_id"}, {V: "Movie", E: "movie_id"}, {V: "Post", E: "post_id"}, } for _, tt := range table { r.Equal(tt.E, Name(tt.V).ParamID()) } } func Test_Name_Title(t *testing.T) { r := require.New(t) table := []struct { V string E string }{ {V: "foo_bar", E: "Foo Bar"}, {V: "admin/widget", E: "Admin Widget"}, {V: "admin/post", E: "Admin Post"}, {V: "widget", E: "Widget"}, {V: "post", E: "Post"}, } for _, tt := range table { r.Equal(tt.E, Name(tt.V).Title()) } } func Test_Name_Model(t *testing.T) { r := require.New(t) table := []struct { V string E string }{ {V: "foo_bar", E: "FooBar"}, {V: "admin/widget", E: "AdminWidget"}, {V: "widget", E: "Widget"}, {V: "widgets", E: "Widget"}, {V: "status", E: "Status"}, {V: "Statuses", E: "Status"}, {V: "statuses", E: "Status"}, {V: "People", E: "Person"}, {V: "people", E: "Person"}, {V: "post", E: "Post"}, {V: "posts", E: "Post"}, {V: "admin/posts", E: "AdminPost"}, } for _, tt := range table { r.Equal(tt.E, Name(tt.V).Model()) } } func Test_Name_Resource(t *testing.T) { table := []struct { V string E string }{ {V: "Person", E: "People"}, {V: "foo_bar", E: "FooBars"}, {V: "admin/widget", E: "AdminWidgets"}, {V: "widget", E: "Widgets"}, {V: "widgets", E: "Widgets"}, {V: "greatPerson", E: "GreatPeople"}, {V: "great/person", E: "GreatPeople"}, {V: "status", E: "Statuses"}, {V: "Status", E: "Statuses"}, {V: "Statuses", E: "Statuses"}, {V: "statuses", E: "Statuses"}, {V: "post", E: "Posts"}, {V: "posts", E: "Posts"}, {V: "POSTS", E: "Posts"}, {V: "POST", E: "Posts"}, {V: "admin/post", E: "AdminPosts"}, {V: "admin/posts", E: "AdminPosts"}, } for _, tt := range table { t.Run(tt.V, func(st *testing.T) { r := require.New(st) r.Equal(tt.E, Name(tt.V).Resource()) }) } } func Test_Name_ModelPlural(t *testing.T) { r := require.New(t) table := []struct { V string E string }{ {V: "foo_bar", E: "FooBars"}, {V: "admin/widget", E: "AdminWidgets"}, {V: "widget", E: "Widgets"}, {V: "widgets", E: "Widgets"}, {V: "status", E: "Statuses"}, {V: "statuses", E: "Statuses"}, {V: "people", E: "People"}, {V: "person", E: "People"}, {V: "People", E: "People"}, {V: "Status", E: "Statuses"}, {V: "Post", E: "Posts"}, {V: "post", E: "Posts"}, {V: "posts", E: "Posts"}, {V: "admin/posts", E: "AdminPosts"}, } for _, tt := range table { r.Equal(tt.E, Name(tt.V).ModelPlural()) } } func Test_Name_File(t *testing.T) { table := []struct { V string E string }{ {V: "foo_bar", E: "foo_bar"}, {V: "admin/widget", E: "admin/widget"}, {V: "widget", E: "widget"}, {V: "widgets", E: "widgets"}, {V: "User", E: "user"}, {V: "admin/posts", E: "admin/posts"}, {V: "AdminPosts", E: "admin_posts"}, {V: "post", E: "post"}, {V: "posts", E: "posts"}, } for _, tt := range table { t.Run(tt.V, func(st *testing.T) { r := require.New(st) r.Equal(tt.E, Name(tt.V).File()) }) } } func Test_Name_VarCaseSingular(t *testing.T) { r := require.New(t) table := []struct { V string E string }{ {V: "foo_bar", E: "fooBar"}, {V: "admin/widget", E: "adminWidget"}, {V: "widget", E: "widget"}, {V: "widgets", E: "widget"}, {V: "User", E: "user"}, {V: "FooBar", E: "fooBar"}, {V: "status", E: "status"}, {V: "statuses", E: "status"}, {V: "Status", E: "status"}, {V: "Statuses", E: "status"}, {V: "admin/post", E: "adminPost"}, {V: "post", E: "post"}, {V: "posts", E: "post"}, } for _, tt := range table { r.Equal(tt.E, Name(tt.V).VarCaseSingular()) } } func Test_Name_VarCasePlural(t *testing.T) { r := require.New(t) table := []struct { V string E string }{ {V: "foo_bar", E: "fooBars"}, {V: "admin/widget", E: "adminWidgets"}, {V: "widget", E: "widgets"}, {V: "widgets", E: "widgets"}, {V: "User", E: "users"}, {V: "FooBar", E: "fooBars"}, {V: "status", E: "statuses"}, {V: "statuses", E: "statuses"}, {V: "Status", E: "statuses"}, {V: "Statuses", E: "statuses"}, {V: "admin/post", E: "adminPosts"}, {V: "post", E: "posts"}, {V: "posts", E: "posts"}, } for _, tt := range table { r.Equal(tt.E, Name(tt.V).VarCasePlural()) } } func Test_Name_Package(t *testing.T) { gp := os.Getenv("GOPATH") r := require.New(t) table := []struct { V string E string }{ {V: filepath.Join(gp, "src", "admin/widget"), E: "admin/widget"}, {V: filepath.Join(gp, "admin/widget"), E: "admin/widget"}, {V: "admin/widget", E: "admin/widget"}, {V: filepath.Join(gp, "src", "admin/post"), E: "admin/post"}, {V: filepath.Join(gp, "admin/post"), E: "admin/post"}, {V: "admin/post", E: "admin/post"}, } for _, tt := range table { r.Equal(tt.E, Name(tt.V).Package()) } } func Test_Name_Char(t *testing.T) { r := require.New(t) n := Name("Foo") r.Equal("f", n.Char()) } inflect-1.0.4/shoulders.md000066400000000000000000000012301336415356400155000ustar00rootroot00000000000000# github.com/markbates/inflect Stands on the Shoulders of Giants github.com/markbates/inflect does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants this project would not be possible. Please make sure to check them out and thank them for all of their hard work. Thank you to the following **GIANTS**: * [github.com/gobuffalo/envy](https://godoc.org/github.com/gobuffalo/envy) * [github.com/joho/godotenv](https://godoc.org/github.com/joho/godotenv) * [github.com/markbates/inflect](https://godoc.org/github.com/markbates/inflect) inflect-1.0.4/version.go000066400000000000000000000000521336415356400151630ustar00rootroot00000000000000package inflect const Version = "v1.0.4"