diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..7dea424 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,117 @@ +## Contribution Guidelines + +### Pull requests are always welcome + +We are always thrilled to receive pull requests, and do our best to +process them as fast as possible. Not sure if that typo is worth a pull +request? Do it! We will appreciate it. + +If your pull request is not accepted on the first try, don't be +discouraged! If there's a problem with the implementation, hopefully you +received feedback on what to improve. + +We're trying very hard to keep go-swagger lean and focused. We don't want it +to do everything for everybody. This means that we might decide against +incorporating a new feature. However, there might be a way to implement +that feature *on top of* go-swagger. + + +### Conventions + +Fork the repo and make changes on your fork in a feature branch: + +- If it's a bugfix branch, name it XXX-something where XXX is the number of the + issue +- If it's a feature branch, create an enhancement issue to announce your + intentions, and name it XXX-something where XXX is the number of the issue. + +Submit unit tests for your changes. Go has a great test framework built in; use +it! Take a look at existing tests for inspiration. Run the full test suite on +your branch before submitting a pull request. + +Update the documentation when creating or modifying features. Test +your documentation changes for clarity, concision, and correctness, as +well as a clean documentation build. See ``docs/README.md`` for more +information on building the docs and how docs get released. + +Write clean code. Universally formatted code promotes ease of writing, reading, +and maintenance. Always run `gofmt -s -w file.go` on each changed file before +committing your changes. Most editors have plugins that do this automatically. + +Pull requests descriptions should be as clear as possible and include a +reference to all the issues that they address. + +Pull requests must not contain commits from other users or branches. + +Commit messages must start with a capitalized and short summary (max. 50 +chars) written in the imperative, followed by an optional, more detailed +explanatory text which is separated from the summary by an empty line. + +Code review comments may be added to your pull request. Discuss, then make the +suggested modifications and push additional commits to your feature branch. Be +sure to post a comment after pushing. The new commits will show up in the pull +request automatically, but the reviewers will not be notified unless you +comment. + +Before the pull request is merged, make sure that you squash your commits into +logical units of work using `git rebase -i` and `git push -f`. After every +commit the test suite should be passing. Include documentation changes in the +same commit so that a revert would remove all traces of the feature or fix. + +Commits that fix or close an issue should include a reference like `Closes #XXX` +or `Fixes #XXX`, which will automatically close the issue when merged. + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as an open-source patch. The rules are pretty simple: if you +can certify the below (from +[developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +You can add the sign off when creating the git commit via `git commit -s`. diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..976af1b --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,56 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "friday" + open-pull-requests-limit: 2 # <- default is 5 + groups: # <- group all github actions updates in a single PR + # 1. development-dependencies are auto-merged + development-dependencies: + patterns: + - '*' + + - package-ecosystem: "gomod" + # We define 4 groups of dependencies to regroup update pull requests: + # - development (e.g. test dependencies) + # - go-openapi updates + # - golang.org (e.g. golang.org/x/... packages) + # - other dependencies (direct or indirect) + # + # * All groups are checked once a week and each produce at most 1 PR. + # * All dependabot PRs are auto-approved + # + # Auto-merging policy, when requirements are met: + # 1. development-dependencies are auto-merged + # 2. golang.org-dependencies are auto-merged + # 3. go-openapi patch updates are auto-merged. Minor/major version updates require a manual merge. + # 4. other dependencies require a manual merge + directory: "/" + schedule: + interval: "weekly" + day: "friday" + open-pull-requests-limit: 4 + groups: + development-dependencies: + dependency-type: development + patterns: + - "github.com/stretchr/testify" + + golang.org-dependencies: + dependency-type: production + patterns: + - "golang.org/*" + + go-openapi-dependencies: + dependency-type: production + patterns: + - "github.com/go-openapi/*" + + other-dependencies: + dependency-type: production + exclude-patterns: + - "github.com/go-openapi/*" + - "github.com/stretchr/testify" + - "golang.org/*" diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml new file mode 100644 index 0000000..b4009db --- /dev/null +++ b/.github/workflows/auto-merge.yml @@ -0,0 +1,43 @@ +name: Dependabot auto-merge +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v1 + + - name: Auto-approve all dependabot PRs + run: gh pr review --approve "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge dependabot PRs for development dependencies + if: contains(steps.metadata.outputs.dependency-group, 'development-dependencies') + run: gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge dependabot PRs for go-openapi patches + if: contains(steps.metadata.outputs.dependency-group, 'go-openapi-dependencies') && (steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch') + run: gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge dependabot PRs for golang.org updates + if: contains(steps.metadata.outputs.dependency-group, 'golang.org-dependencies') + run: gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} + diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml new file mode 100644 index 0000000..e3c7dda --- /dev/null +++ b/.github/workflows/go-test.yml @@ -0,0 +1,62 @@ +name: go test + +on: + push: + tags: + - v* + branches: + - master + paths-ignore: + - '**.md' + + pull_request: + paths-ignore: + - '**.md' + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: stable + check-latest: true + cache: true + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest + only-new-issues: true + skip-cache: true + + test: + name: Unit tests + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] + go_version: ['oldstable', 'stable' ] + + steps: + - name: Run unit tests + uses: actions/setup-go@v5 + with: + go-version: '${{ matrix.go_version }}' + check-latest: true + cache: true + + - uses: actions/checkout@v4 + + - run: go test -v -race -coverprofile="coverage-${{ matrix.os }}.${{ matrix.go_version }}.out" -covermode=atomic -coverpkg=$(go list)/... ./... + + - name: Upload coverage to codecov + uses: codecov/codecov-action@v3 + with: + files: './coverage-${{ matrix.os }}.${{ matrix.go_version }}.out' + flags: '${{ matrix.go_version }}' + os: '${{ matrix.os }}' + fail_ci_if_error: false + verbose: true diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..22f8d21 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,61 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 45 + maligned: + suggest-new: true + dupl: + threshold: 200 + goconst: + min-len: 2 + min-occurrences: 3 + +linters: + enable-all: true + disable: + - maligned + - unparam + - lll + - gochecknoinits + - gochecknoglobals + - funlen + - godox + - gocognit + - whitespace + - wsl + - wrapcheck + - testpackage + - nlreturn + - gomnd + - exhaustivestruct + - goerr113 + - errorlint + - nestif + - godot + - gofumpt + - paralleltest + - tparallel + - thelper + - ifshort + - exhaustruct + - varnamelen + - gci + - depguard + - errchkjson + - inamedparam + - nonamedreturns + - musttag + - ireturn + - forcetypeassert + - cyclop + # deprecated linters + - deadcode + - interfacer + - scopelint + - varcheck + - structcheck + - golint + - nosnakecase diff --git a/LICENCE b/LICENSE similarity index 100% rename from LICENCE rename to LICENSE diff --git a/README b/README deleted file mode 100644 index 014699a..0000000 --- a/README +++ /dev/null @@ -1,168 +0,0 @@ -INSTALLATION - -go get bitbucket.org/pkg/inflect - -PACKAGE - -package inflect - - -FUNCTIONS - -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 - -type Rule struct { - // contains filtered or unexported fields -} -used by rulesets - -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 inconsistant pluralizing/sinularizing 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 sentance captitilized -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 seperator - -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 -Captitilize every word in sentance "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" - - diff --git a/README.md b/README.md new file mode 100644 index 0000000..4eddc76 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# inflect [![Build Status](https://github.com/go-openapi/inflect/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/inflect/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/inflect/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/inflect) + +[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/inflect/master/LICENSE) +[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/inflect.svg)](https://pkg.go.dev/github.com/go-openapi/inflect) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/inflect)](https://goreportcard.com/report/github.com/go-openapi/inflect) + +A package to pluralize words. + +Originally forked from fork of https://bitbucket.org/pkg/inflect under a MIT License. diff --git a/go.mod b/go.mod index 6768467..3d329b1 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,3 @@ module github.com/go-openapi/inflect + +go 1.19 diff --git a/inflect.go b/inflect.go index 3008844..233f510 100644 --- a/inflect.go +++ b/inflect.go @@ -19,12 +19,11 @@ type Rule struct { // 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 - acronymMatcher *regexp.Regexp + uncountables map[string]bool + plurals []*Rule + singulars []*Rule + humans []*Rule + acronyms []*Rule } // create a blank ruleset. Unless you are going to @@ -282,7 +281,7 @@ func (rs *Ruleset) AddHuman(suffix, replacement string) { rs.humans = append([]*Rule{r}, rs.humans...) } -// Add any inconsistant pluralizing/sinularizing rules +// Add any inconsistent pluralizing/sinularizing rules // to the set here. func (rs *Ruleset) AddIrregular(singular, plural string) { delete(rs.uncountables, singular) @@ -387,7 +386,7 @@ func (rs *Ruleset) Titleize(word string) string { func (rs *Ruleset) safeCaseAcronyms(word string) string { // convert an acroymn like HTML into Html for _, rule := range rs.acronyms { - word = strings.Replace(word, rule.suffix, rule.replacement, -1) + word = strings.ReplaceAll(word, rule.suffix, rule.replacement) } return word } @@ -409,7 +408,7 @@ 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) + word = strings.ReplaceAll(word, rule.suffix, rule.replacement) } sentance := rs.seperatedWords(word, " ") return strings.ToUpper(sentance[:1]) + sentance[1:] @@ -430,19 +429,19 @@ func (rs *Ruleset) Tableize(word string) string { return rs.Pluralize(rs.Underscore(rs.Typeify(word))) } -var notUrlSafe *regexp.Regexp = regexp.MustCompile(`[^\w\d\-_ ]`) +var notURLSafe = regexp.MustCompile(`[^\w\d\-_ ]`) // param safe dasherized names like "my-param" func (rs *Ruleset) Parameterize(word string) string { return ParameterizeJoin(word, "-") } -// param safe dasherized names with custom seperator +// 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) + word = notURLSafe.ReplaceAllString(word, "") + word = strings.ReplaceAll(word, " ", sep) if len(sep) > 0 { squash, err := regexp.Compile(sep + "+") if err == nil { @@ -453,7 +452,7 @@ func (rs *Ruleset) ParameterizeJoin(word, sep string) string { return word } -var lookalikes map[string]*regexp.Regexp = map[string]*regexp.Regexp{ +var lookalikes = map[string]*regexp.Regexp{ "A": regexp.MustCompile(`À|Á|Â|Ã|Ä|Å`), "AE": regexp.MustCompile(`Æ`), "C": regexp.MustCompile(`Ç`), @@ -487,7 +486,7 @@ func (rs *Ruleset) Asciify(word string) string { return word } -var tablePrefix *regexp.Regexp = regexp.MustCompile(`^[^.]*\.`) +var tablePrefix = regexp.MustCompile(`^[^.]*\.`) // "something_like_this" -> "SomethingLikeThis" func (rs *Ruleset) Typeify(word string) string { diff --git a/inflect_test.go b/inflect_test.go index 2955467..02e19c6 100644 --- a/inflect_test.go +++ b/inflect_test.go @@ -292,25 +292,25 @@ type AcronymCase struct { 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"}, + {camel: "API", under: "api", human: "API", title: "API"}, + {"APIController", "api_controller", "API controller", "API Controller"}, + {"Nokogiri::HTML", "nokogiri/html", "Nokogiri/HTML", "Nokogiri/HTML"}, + {"HTTPAPI", "http_api", "HTTP API", "HTTP API"}, + {"HTTP::Get", "http/get", "HTTP/get", "HTTP/Get"}, + {"SSLError", "ssl_error", "SSL error", "SSL Error"}, + {"RESTful", "restful", "RESTful", "RESTful"}, + {"RESTfulController", "restful_controller", "RESTful controller", "RESTful Controller"}, + {"IHeartW3C", "i_heart_w3c", "I heart W3C", "I Heart W3C"}, + {"PhDRequired", "phd_required", "PhD required", "PhD Required"}, + {"IRoRU", "i_ror_u", "I RoR u", "I RoR U"}, + {"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"}, + {"Capistrano", "capistrano", "Capistrano", "Capistrano"}, + {"CapiController", "capi_controller", "Capi controller", "Capi Controller"}, + {"HttpsApis", "https_apis", "Https apis", "Https Apis"}, + {"Html5", "html5", "Html5", "Html5"}, + {"Restfully", "restfully", "Restfully", "Restfully"}, + {"RoRails", "ro_rails", "Ro rails", "Ro Rails"}, } // tests @@ -536,9 +536,11 @@ func TestUnderscoreToLowerCamel(t *testing.T) { } } +/* func Test_clear_all(t *testing.T) { // test a way of resetting inflexions } +*/ func TestIrregularityBetweenSingularAndPlural(t *testing.T) { for singular, plural := range Irregularities {