diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..165ff874 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @github/rubocop-reviewers diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bdb77e0f..8afc1973 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,25 @@ +--- version: 2 updates: - - package-ecosystem: bundler - directory: '/' - versioning-strategy: increase - schedule: - interval: weekly +- package-ecosystem: bundler + directory: "/" + versioning-strategy: increase + schedule: + interval: weekly + open-pull-requests-limit: 10 + allow: + - dependency-type: "all" + groups: + bundler-rubocop: + patterns: + - "rubocop*" + bundler-all: + update-types: + - minor + - patch + exclude-patterns: + - "rubocop*" +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..8ba68a68 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,42 @@ +name: build + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_call: + +permissions: + contents: read + +jobs: + build: + name: build + + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + + steps: + - name: checkout + uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@472790540115ce5bd69d399a020189a8c87d641f # pin@v1.247.0 + with: + bundler-cache: true + + - name: build + run: | + GEM_NAME=$(ls | grep gemspec | cut -d. -f1) + echo "Attempting to build gem $GEM_NAME..." + gem build $GEM_NAME + if [ $? -eq 0 ]; then + echo "Gem built successfully!" + else + echo "Gem build failed!" + exit 1 + fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index c694d568..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: CI - -on: [push, pull_request] - -jobs: - test: - strategy: - fail-fast: false - matrix: - ruby_version: ["2.7", "3.0", "3.1"] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby_version }} - - name: Install dependencies - run: bundle install --jobs 4 --retry 3 - - name: Run tests - run: bundle exec rake diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..b1584d39 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,26 @@ +name: lint + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@472790540115ce5bd69d399a020189a8c87d641f # pin@v1.247.0 + with: + bundler-cache: true + + - name: lint + run: script/lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..d5ba6bf0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,64 @@ +name: release + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - lib/version.rb + +permissions: + contents: write + packages: write + id-token: write + attestations: write + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v4 + + - uses: ruby/setup-ruby@472790540115ce5bd69d399a020189a8c87d641f # pin@v1.247.0 + with: + bundler-cache: true + + - name: lint + run: script/lint + + - name: test + run: script/test + + - name: set GEM_NAME from gemspec + run: echo "GEM_NAME=$(ls | grep gemspec | cut -d. -f1)" >> $GITHUB_ENV + + # builds the gem and saves the version to GITHUB_ENV + - name: build + run: echo "GEM_VERSION=$(gem build ${{ env.GEM_NAME }}.gemspec 2>&1 | grep Version | cut -d':' -f 2 | tr -d " \t\n\r")" >> $GITHUB_ENV + + - uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # pin@v2 + with: + subject-path: "${{ env.GEM_NAME }}-${{ env.GEM_VERSION }}.gem" + + - name: publish to GitHub packages + run: | + export OWNER=$( echo ${{ github.repository }} | cut -d "/" -f 1 ) + GEM_HOST_API_KEY=${{ secrets.GITHUB_TOKEN }} gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} ${{ env.GEM_NAME }}-${{ env.GEM_VERSION }}.gem + + - name: release + uses: ncipollo/release-action@bcfe5470707e8832e12347755757cec0eb3c22af # pin@v1.18.0 + with: + artifacts: "${{ env.GEM_NAME }}-${{ env.GEM_VERSION }}.gem" + tag: "v${{ env.GEM_VERSION }}" + generateReleaseNotes: true + + - name: publish to RubyGems + run: | + mkdir -p ~/.gem + echo -e "---\n:rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }}" > ~/.gem/credentials + chmod 0600 ~/.gem/credentials + gem push ${{ env.GEM_NAME }}-${{ env.GEM_VERSION }}.gem + rm ~/.gem/credentials diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..981c487f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: test + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby_version: ["3.1", "3.2", "3.3", "3.4"] + + steps: + - uses: actions/checkout@v4 + + - name: Update .ruby-version with matrix value + run: echo "${{ matrix.ruby_version }}" >| .ruby-version + + - uses: ruby/setup-ruby@472790540115ce5bd69d399a020189a8c87d641f # pin@v1.247.0 + with: + bundler-cache: true + + - name: test + run: script/test diff --git a/.gitignore b/.gitignore index fb280015..25f6e982 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ spec/reports test/tmp test/version_tmp tmp +bin/ +vendor/ # YARD artifacts .yardoc diff --git a/.rubocop.yml b/.rubocop.yml index afe2d021..50ed10da 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,3 +8,7 @@ Naming/FileName: - "rubocop-github.gemspec" - "lib/rubocop-github.rb" - "lib/rubocop-github-rails.rb" + +Gemspec/DevelopmentDependencies: + Enabled: true + EnforcedStyle: gemspec diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..4d9d11cf --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.4.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 08647f45..f9ed0a51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,30 @@ # rubocop-github -## Unreleased +## v0.26.0 + +- Read the automatic release notes on [the /releases page for this gem](https://github.com/github/rubocop-github/releases). + +## v0.25.0 + +- Read the automatic release notes on [the /releases page for this gem](https://github.com/github/rubocop-github/releases). +- Updated related gems +- Specify plugin class names for included rubocop plugins + +## v0.24.0 + +- Read the automatic release notes on [the /releases page for this gem](https://github.com/github/rubocop-github/releases). + +## v0.23.0 + +- Read the automatic release notes on [the /releases page for this gem](https://github.com/github/rubocop-github/releases). + +## v0.22.0 + +- Read the automatic release notes on [the /releases page for this gem](https://github.com/github/rubocop-github/releases). + +## v0.21.0 + +- Added new GitHub/AvoidObjectSendWithDynamicMethod cop to discourage use of methods like Object#send ## v0.20.0 diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index fb48a77e..00000000 --- a/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @github/ruby-architecture-reviewers diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fcab75d4..e5ef656b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,6 @@ We welcome your contributions to the project. Thank you! Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. - ## What to contribute This repository, `rubocop-github`, is part of a broader RuboCop ecosystem. @@ -50,12 +49,8 @@ Rubocop regularly releases new versions with new cops. We want to keep up to dat ### Releasing a new version -1. Update `rubocop-github.gemspec` with the next version number -1. Update the `CHANGELOG` with changes and contributor -1. Run `bundle` to update gem version contained in the lockfile -1. Make a commit: `Release v{VERSION}` -1. Tag the commit : `git tag v{VERSION}` -1. Create the package: `gem build rubocop-github.gemspec` -1. Push to GitHub: `git push origin && git push origin --tags` -1. Push to Rubygems: `gem push rubocop-github-{VERSION}.gem` -1. Publish a new release on GitHub: https://github.com/github/rubocop-github/releases/new +1. Update [`lib/version.rb`](lib/version.rb) with the next version number +2. Update the `CHANGELOG` with changes and contributor +3. Run `bundle install` to update gem version contained in the lockfile +4. Commit your changes and open a pull request +5. When the pull request is approved and merged into `main`, the [`.github/workflows/release.yml`](.github/workflows/release.yml) workflow will automatically run to release the new version to RubyGems and GitHub Packages 🎉. diff --git a/Gemfile.lock b/Gemfile.lock index 37967364..2a3020e1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,89 +1,123 @@ PATH remote: . specs: - rubocop-github (0.20.0) - rubocop (>= 1.37) - rubocop-performance (>= 1.15) - rubocop-rails (>= 2.17) + rubocop-github (0.26.0) + rubocop (>= 1.76) + rubocop-performance (>= 1.24) + rubocop-rails (>= 2.23) GEM remote: https://rubygems.org/ specs: - actionview (7.0.4) - activesupport (= 7.0.4) + actionview (7.2.2.1) + activesupport (= 7.2.2.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activesupport (7.0.4) - concurrent-ruby (~> 1.0, >= 1.0.2) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activesupport (7.2.2.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - ast (2.4.2) - builder (3.2.4) - concurrent-ruby (1.1.10) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + ast (2.4.3) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.9) + builder (3.3.0) + concurrent-ruby (1.3.5) + connection_pool (2.5.3) crass (1.0.6) - erubi (1.11.0) - i18n (1.12.0) + drb (2.2.3) + erubi (1.13.1) + i18n (1.14.7) concurrent-ruby (~> 1.0) - json (2.6.2) - loofah (2.19.0) + json (2.12.2) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + loofah (2.24.0) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - minitest (5.16.3) - nokogiri (1.13.9-arm64-darwin) + nokogiri (>= 1.12.0) + mini_portile2 (2.8.8) + minitest (5.25.5) + nokogiri (1.18.8) + mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.13.9-x86_64-darwin) + nokogiri (1.18.8-arm64-darwin) racc (~> 1.4) - parallel (1.22.1) - parser (3.1.2.1) + nokogiri (1.18.8-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.18.8-x86_64-linux-gnu) + racc (~> 1.4) + parallel (1.27.0) + parser (3.3.8.0) ast (~> 2.4.1) - racc (1.6.0) - rack (3.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + racc + prism (1.4.0) + racc (1.8.1) + rack (3.1.16) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.3) - loofah (~> 2.3) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) rainbow (3.1.1) - rake (13.0.6) - regexp_parser (2.6.0) - rexml (3.2.5) - rubocop (1.37.1) + rake (13.3.0) + regexp_parser (2.10.0) + rubocop (1.78.0) json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) - parser (>= 3.1.2.1) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.23.0, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.45.1, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.23.0) - parser (>= 3.1.1.0) - rubocop-performance (1.15.0) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) - rubocop-rails (2.17.2) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.45.1) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-performance (1.25.0) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails (2.32.0) activesupport (>= 4.2.0) + lint_roller (~> 1.1) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) - ruby-progressbar (1.11.0) - tzinfo (2.0.5) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.44.0, < 2.0) + ruby-progressbar (1.13.0) + securerandom (0.4.1) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.3.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) PLATFORMS arm64-darwin-21 + ruby x86_64-darwin-19 x86_64-darwin-20 + x86_64-linux DEPENDENCIES - actionview + actionview (~> 7.2.2.1) minitest rake rubocop-github! BUNDLED WITH - 2.3.24 + 2.6.5 diff --git a/README.md b/README.md index 7ba558fa..ed631b24 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -# RuboCop GitHub ![CI](https://github.com/github/rubocop-github/workflows/CI/badge.svg?event=push) +# RuboCop GitHub + +[![test](https://github.com/github/rubocop-github/actions/workflows/test.yml/badge.svg)](https://github.com/github/rubocop-github/actions/workflows/test.yml) +[![build](https://github.com/github/rubocop-github/actions/workflows/build.yml/badge.svg)](https://github.com/github/rubocop-github/actions/workflows/build.yml) +[![lint](https://github.com/github/rubocop-github/actions/workflows/lint.yml/badge.svg)](https://github.com/github/rubocop-github/actions/workflows/lint.yml) +[![release](https://github.com/github/rubocop-github/actions/workflows/release.yml/badge.svg)](https://github.com/github/rubocop-github/actions/workflows/release.yml) This repository provides recommended RuboCop configuration and additional Cops for use on GitHub open source and internal Ruby projects, and is the home of [GitHub's Ruby Style Guide](./STYLEGUIDE.md). @@ -16,10 +21,10 @@ Inherit all of the stylistic rules and cops through an inheritance declaration i ```yaml # .rubocop.yml - inherit_from: + inherit_gem: rubocop-github: - - config/default.yml # generic Ruby rules and cops - - config/rails.yml # Rails-specific rules and cops + - config/default.yml # generic Ruby rules and cops + - config/rails.yml # Rails-specific rules and cops ``` Alternatively, only require the additional custom cops in your `.rubocop.yml` without inheriting/enabling the other stylistic rules: diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 758651b6..31f33664 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -3,6 +3,7 @@ This is GitHub's Ruby Style Guide, inspired by [RuboCop's guide][rubocop-guide]. ## Table of Contents + 1. [Layout](#layout) 1. [Indentation](#indentation) 2. [Inline](#inline) @@ -678,7 +679,7 @@ end * Never put a space between a method name and the opening parenthesis. [[link](#no-spaces-method-parens)] - * RuboCop rule: Style/ParenthesesAsGroupedExpression + * RuboCop rule: Lint/ParenthesesAsGroupedExpression ``` ruby # bad @@ -763,6 +764,22 @@ if x > 10 end ``` +* Don't use `unless` with a negated condition. + [[link](#no-unless-negation)] + * RuboCop rule: Style/NegatedUnless + +```ruby +# bad +unless !condition? + do_something +end + +# good +if condition? + do_something +end +``` + ### Ternary operator * Avoid the ternary operator (`?:`) except in cases where all expressions are extremely diff --git a/config/default.yml b/config/default.yml index 0a3672c7..55fb0aff 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3,7 +3,10 @@ inherit_from: require: - rubocop-github - - rubocop-performance + +plugins: + - rubocop-performance: + plugin_class_name: RuboCop::Performance::Plugin Bundler/DuplicatedGem: Enabled: true @@ -44,6 +47,9 @@ Gemspec/RequiredRubyVersion: Gemspec/RubyVersionGlobalsUsage: Enabled: false +GitHub/AvoidObjectSendWithDynamicMethod: + Enabled: true + GitHub/InsecureHashAlgorithm: Enabled: true @@ -68,10 +74,10 @@ Layout/BlockAlignment: Layout/BlockEndNewline: Enabled: true -# TODO: Enable this since it's in the styleguide. Layout/CaseIndentation: - Enabled: false + Enabled: true StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#indent-when-as-start-of-case + EnforcedStyle: end Layout/ClassStructure: Enabled: false @@ -216,6 +222,7 @@ Layout/LineEndStringConcatenationIndentation: Layout/LineLength: Enabled: false + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#line-length Layout/MultilineArrayBraceLayout: Enabled: false @@ -290,7 +297,10 @@ Layout/SpaceAroundMethodCallOperator: Enabled: false Layout/SpaceAroundOperators: - Enabled: false + Enabled: true + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#spaces-operators + Exclude: + - "**/*.erb" Layout/SpaceBeforeBlockBraces: Enabled: true @@ -324,7 +334,8 @@ Layout/SpaceInsideBlockBraces: Enabled: true Layout/SpaceInsideHashLiteralBraces: - Enabled: false + Enabled: true + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#spaces-operators Layout/SpaceInsideParens: Enabled: true @@ -561,7 +572,9 @@ Lint/OutOfRangeRegexpRef: Enabled: false Lint/ParenthesesAsGroupedExpression: - Enabled: false + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#no-spaces-method-parens + Exclude: + - "**/*.erb" Lint/PercentStringArray: Enabled: false @@ -805,7 +818,7 @@ Naming/MethodName: Naming/MethodParameterName: Enabled: false -Naming/PredicateName: +Naming/PredicatePrefix: Enabled: false Naming/RescuedExceptionsVariableName: @@ -995,9 +1008,8 @@ Style/AccessorGrouping: Style/Alias: Enabled: false -# TODO: Enable this since it's in the styleguide. Style/AndOr: - Enabled: false + Enabled: true StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#no-and-or-or Style/ArgumentsForwarding: @@ -1035,6 +1047,7 @@ Style/BlockDelimiters: Style/CaseEquality: Enabled: true + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#type-checking-is-a-kind-of Style/CaseLikeIf: Enabled: false @@ -1055,10 +1068,12 @@ Style/ClassMethods: Enabled: true Style/ClassMethodsDefinitions: - Enabled: false + Enabled: true + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#classes Style/ClassVars: Enabled: false + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#class-variables Style/CollectionCompact: Enabled: false @@ -1356,7 +1371,7 @@ Style/NegatedIfElseCondition: Enabled: false Style/NegatedUnless: - Enabled: false + Enabled: true Style/NegatedWhile: Enabled: false @@ -1371,7 +1386,10 @@ Style/NestedParenthesizedCalls: Enabled: false Style/NestedTernaryOperator: - Enabled: false + Enabled: true + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#one-expression-per-branch + Exclude: + - "**/*.erb" Style/Next: Enabled: false @@ -1422,7 +1440,10 @@ Style/OptionalBooleanParameter: Enabled: false Style/OrAssignment: - Enabled: false + Enabled: true + StyleGuide: https://github.com/github/rubocop-github/blob/main/STYLEGUIDE.md#memoize-away + Exclude: + - "**/*.erb" Style/ParallelAssignment: Enabled: false diff --git a/config/default_pending.yml b/config/default_pending.yml index ee85c603..5ae412d3 100644 --- a/config/default_pending.yml +++ b/config/default_pending.yml @@ -4,3 +4,113 @@ Style/OperatorMethodCall: # new in 1.37 Enabled: false Style/RedundantStringEscape: # new in 1.37 Enabled: false +Gemspec/DevelopmentDependencies: # new in 1.44 + Enabled: false +Lint/DuplicateMatchPattern: # new in 1.50 + Enabled: false +Lint/ItWithoutArgumentsInBlock: # new in 1.59 + Enabled: false +Lint/LiteralAssignmentInCondition: # new in 1.58 + Enabled: false +Lint/MixedCaseRange: # new in 1.53 + Enabled: false +Lint/RedundantRegexpQuantifiers: # new in 1.53 + Enabled: false +Lint/UselessRescue: # new in 1.43 + Enabled: false +Metrics/CollectionLiteralLength: # new in 1.47 + Enabled: false +Style/ArrayIntersect: # new in 1.40 + Enabled: false +Style/ComparableClamp: # new in 1.44 + Enabled: false +Style/ConcatArrayLiterals: # new in 1.41 + Enabled: false +Style/DataInheritance: # new in 1.49 + Enabled: false +Style/DirEmpty: # new in 1.48 + Enabled: false +Style/ExactRegexpMatch: # new in 1.51 + Enabled: false +Style/FileEmpty: # new in 1.48 + Enabled: false +Style/MapIntoArray: # new in 1.63 + Enabled: false +Style/MapToSet: # new in 1.42 + Enabled: false +Style/MinMaxComparison: # new in 1.42 + Enabled: false +Style/RedundantArrayConstructor: # new in 1.52 + Enabled: false +Style/RedundantConstantBase: # new in 1.40 + Enabled: false +Style/RedundantCurrentDirectoryInPath: # new in 1.53 + Enabled: false +Style/RedundantDoubleSplatHashBraces: # new in 1.41 + Enabled: false +Style/RedundantEach: # new in 1.38 + Enabled: false +Style/RedundantFilterChain: # new in 1.52 + Enabled: false +Style/RedundantHeredocDelimiterQuotes: # new in 1.45 + Enabled: false +Style/RedundantLineContinuation: # new in 1.49 + Enabled: false +Style/RedundantRegexpArgument: # new in 1.53 + Enabled: false +Style/RedundantRegexpConstructor: # new in 1.52 + Enabled: false +Style/ReturnNilInPredicateMethodDefinition: # new in 1.53 + Enabled: false +Style/SingleLineDoEndBlock: # new in 1.57 + Enabled: false +Style/SuperWithArgsParentheses: # new in 1.58 + Enabled: false +Style/YAMLFileRead: # new in 1.53 + Enabled: false +Performance/MapMethodChain: # new in 1.19 + Enabled: false +Gemspec/AddRuntimeDependency: # new in 1.65 + Enabled: false +Lint/ConstantReassignment: # new in 1.70 + Enabled: false +Lint/DuplicateSetElement: # new in 1.67 + Enabled: false +Lint/HashNewWithKeywordArgumentsAsDefault: # new in 1.69 + Enabled: false +Lint/NumericOperationWithConstantResult: # new in 1.69 + Enabled: false +Lint/SharedMutableDefault: # new in 1.70 + Enabled: false +Lint/UnescapedBracketInRegexp: # new in 1.68 + Enabled: false +Lint/UselessDefined: # new in 1.69 + Enabled: false +Lint/UselessNumericOperation: # new in 1.66 + Enabled: false +Style/AmbiguousEndlessMethodDefinition: # new in 1.68 + Enabled: false +Style/BitwisePredicate: # new in 1.68 + Enabled: false +Style/CombinableDefined: # new in 1.68 + Enabled: false +Style/DigChain: # new in 1.69 + Enabled: false +Style/FileNull: # new in 1.69 + Enabled: false +Style/FileTouch: # new in 1.69 + Enabled: false +Style/ItAssignment: # new in 1.70 + Enabled: false +Style/KeywordArgumentsMerging: # new in 1.68 + Enabled: false +Style/RedundantInterpolationUnfreeze: # new in 1.66 + Enabled: false +Style/SafeNavigationChainLength: # new in 1.68 + Enabled: false +Style/SendWithLiteralMethodName: # new in 1.64 + Enabled: false +Style/SuperArguments: # new in 1.64 + Enabled: false +Performance/StringBytesize: # new in 1.23 + Enabled: false diff --git a/config/rails.yml b/config/rails.yml index b7b8d6ec..cba38c11 100644 --- a/config/rails.yml +++ b/config/rails.yml @@ -3,7 +3,10 @@ inherit_from: require: - rubocop-github-rails - - rubocop-rails + +plugins: + - rubocop-rails: + plugin_class_name: RuboCop::Rails::Plugin GitHub/RailsControllerRenderActionSymbol: Enabled: true diff --git a/config/rails_pending.yml b/config/rails_pending.yml index b3abb0b5..941a743c 100644 --- a/config/rails_pending.yml +++ b/config/rails_pending.yml @@ -18,3 +18,85 @@ Rails/WhereMissing: # new in 2.16 Enabled: false Rails/WhereNotWithMultipleConditions: # new in 2.17 Enabled: false +Gemspec/DevelopmentDependencies: # new in 1.44 + Enabled: false +Lint/DuplicateMatchPattern: # new in 1.50 + Enabled: false +Lint/ItWithoutArgumentsInBlock: # new in 1.59 + Enabled: false +Lint/LiteralAssignmentInCondition: # new in 1.58 + Enabled: false +Lint/MixedCaseRange: # new in 1.53 + Enabled: false +Lint/RedundantRegexpQuantifiers: # new in 1.53 + Enabled: false +Lint/UselessRescue: # new in 1.43 + Enabled: false +Metrics/CollectionLiteralLength: # new in 1.47 + Enabled: false +Style/ArrayIntersect: # new in 1.40 + Enabled: false +Style/ComparableClamp: # new in 1.44 + Enabled: false +Style/ConcatArrayLiterals: # new in 1.41 + Enabled: false +Style/DataInheritance: # new in 1.49 + Enabled: false +Style/DirEmpty: # new in 1.48 + Enabled: false +Style/ExactRegexpMatch: # new in 1.51 + Enabled: false +Style/FileEmpty: # new in 1.48 + Enabled: false +Style/MapIntoArray: # new in 1.63 + Enabled: false +Style/MapToSet: # new in 1.42 + Enabled: false +Style/MinMaxComparison: # new in 1.42 + Enabled: false +Style/RedundantArrayConstructor: # new in 1.52 + Enabled: false +Style/RedundantConstantBase: # new in 1.40 + Enabled: false +Style/RedundantCurrentDirectoryInPath: # new in 1.53 + Enabled: false +Style/RedundantDoubleSplatHashBraces: # new in 1.41 + Enabled: false +Style/RedundantEach: # new in 1.38 + Enabled: false +Style/RedundantFilterChain: # new in 1.52 + Enabled: false +Style/RedundantHeredocDelimiterQuotes: # new in 1.45 + Enabled: false +Style/RedundantLineContinuation: # new in 1.49 + Enabled: false +Style/RedundantRegexpArgument: # new in 1.53 + Enabled: false +Style/RedundantRegexpConstructor: # new in 1.52 + Enabled: false +Style/ReturnNilInPredicateMethodDefinition: # new in 1.53 + Enabled: false +Style/SingleLineDoEndBlock: # new in 1.57 + Enabled: false +Style/SuperWithArgsParentheses: # new in 1.58 + Enabled: false +Style/YAMLFileRead: # new in 1.53 + Enabled: false +Performance/MapMethodChain: # new in 1.19 + Enabled: false +Rails/DangerousColumnNames: # new in 2.21 + Enabled: false +Rails/EnvLocal: # new in 2.22 + Enabled: false +Rails/RedundantActiveRecordAllMethod: # new in 2.21 + Enabled: false +Rails/ResponseParsedBody: # new in 2.18 + Enabled: false +Rails/ThreeStateBooleanColumn: # new in 2.19 + Enabled: false +Rails/UnusedRenderContent: # new in 2.21 + Enabled: false +Rails/EnumSyntax: # new in 2.26 + Enabled: false +Rails/WhereRange: # new in 2.25 + Enabled: false diff --git a/lib/rubocop-github.rb b/lib/rubocop-github.rb index 17c07e77..65bc8dc8 100644 --- a/lib/rubocop-github.rb +++ b/lib/rubocop-github.rb @@ -6,4 +6,5 @@ RuboCop::GitHub::Inject.default_defaults! +require "rubocop/cop/github/avoid_object_send_with_dynamic_method" require "rubocop/cop/github/insecure_hash_algorithm" diff --git a/lib/rubocop/cop/github/avoid_object_send_with_dynamic_method.rb b/lib/rubocop/cop/github/avoid_object_send_with_dynamic_method.rb new file mode 100644 index 00000000..e5e7677d --- /dev/null +++ b/lib/rubocop/cop/github/avoid_object_send_with_dynamic_method.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require "rubocop" + +module RuboCop + module Cop + module GitHub + # Public: A Rubocop to discourage using methods like Object#send that allow you to dynamically call other + # methods on a Ruby object, when the method being called is itself completely dynamic. Instead, explicitly call + # methods by name. + # + # Examples: + # + # # bad + # foo.send(some_variable) + # + # # good + # case some_variable + # when "bar" + # foo.bar + # else + # foo.baz + # end + # + # # fine + # foo.send(:bar) + # foo.public_send("some_method") + # foo.__send__("some_#{variable}_method") + class AvoidObjectSendWithDynamicMethod < Base + MESSAGE_TEMPLATE = "Avoid using Object#%s with a dynamic method name." + SEND_METHODS = %i(send public_send __send__).freeze + CONSTANT_TYPES = %i(sym str const).freeze + + def on_send(node) + return unless send_method?(node) + return if method_being_sent_is_constrained?(node) + add_offense(source_range_for_method_call(node), message: MESSAGE_TEMPLATE % node.method_name) + end + + private + + def send_method?(node) + SEND_METHODS.include?(node.method_name) + end + + def method_being_sent_is_constrained?(node) + method_name_being_sent_is_constant?(node) || method_name_being_sent_is_dynamic_string_with_constants?(node) + end + + def method_name_being_sent_is_constant?(node) + method_being_sent = node.arguments.first + # e.g., `worker.send(:perform)` or `base.send("extend", Foo)` + CONSTANT_TYPES.include?(method_being_sent.type) + end + + def method_name_being_sent_is_dynamic_string_with_constants?(node) + method_being_sent = node.arguments.first + return false unless method_being_sent.type == :dstr + + # e.g., `foo.send("can_#{action}?")` + method_being_sent.child_nodes.any? { |child_node| CONSTANT_TYPES.include?(child_node.type) } + end + + def source_range_for_method_call(node) + begin_pos = + if node.receiver # e.g., for `foo.send(:bar)`, `foo` is the receiver + node.receiver.source_range.end_pos + else # e.g., `send(:bar)` + node.source_range.begin_pos + end + end_pos = node.loc.selector.end_pos + Parser::Source::Range.new(processed_source.buffer, begin_pos, end_pos) + end + end + end + end +end diff --git a/lib/rubocop/cop/github/rails_controller_render_literal.rb b/lib/rubocop/cop/github/rails_controller_render_literal.rb index 0f2efd39..4ed294e1 100644 --- a/lib/rubocop/cop/github/rails_controller_render_literal.rb +++ b/lib/rubocop/cop/github/rails_controller_render_literal.rb @@ -9,7 +9,7 @@ module GitHub class RailsControllerRenderLiteral < Base include RenderLiteralHelpers - MSG = "render must be used with a string literal or an instance of a Class" + MSG = "render must be used with a string literal or an instance of a Class, and use literals for locals keys" def_node_matcher :ignore_key?, <<-PATTERN (pair (sym { diff --git a/lib/rubocop/cop/github/rails_view_render_literal.rb b/lib/rubocop/cop/github/rails_view_render_literal.rb index c8563868..357b2ee1 100644 --- a/lib/rubocop/cop/github/rails_view_render_literal.rb +++ b/lib/rubocop/cop/github/rails_view_render_literal.rb @@ -54,7 +54,7 @@ def on_send(node) if render_literal?(node) && node.arguments.count > 1 locals = node.arguments[1] - elsif options_pairs = render_with_options?(node) + elsif option_pairs = render_with_options?(node) locals = option_pairs.map { |pair| locals_key?(pair) }.compact.first end diff --git a/lib/rubocop/cop/github/render_literal_helpers.rb b/lib/rubocop/cop/github/render_literal_helpers.rb index 67417a54..29820b0e 100644 --- a/lib/rubocop/cop/github/render_literal_helpers.rb +++ b/lib/rubocop/cop/github/render_literal_helpers.rb @@ -41,7 +41,8 @@ module RenderLiteralHelpers PATTERN def hash_with_literal_keys?(hash) - hash.pairs.all? { |pair| literal?(pair.key) } + hash.children.all? { |child| child.pair_type? } && + hash.pairs.all? { |pair| literal?(pair.key) } end def render_view_component?(node) diff --git a/lib/version.rb b/lib/version.rb new file mode 100644 index 00000000..8463eaf1 --- /dev/null +++ b/lib/version.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +VERSION = "0.26.0" diff --git a/rubocop-github.gemspec b/rubocop-github.gemspec index 00242746..b1cf9cc4 100644 --- a/rubocop-github.gemspec +++ b/rubocop-github.gemspec @@ -1,25 +1,33 @@ # frozen_string_literal: true +require_relative "lib/version" + Gem::Specification.new do |s| s.name = "rubocop-github" - s.version = "0.20.0" + s.version = VERSION s.summary = "RuboCop GitHub" - s.description = "Code style checking for GitHub Ruby repositories " + s.description = "Code style checking for GitHub Ruby repositories" s.homepage = "https://github.com/github/rubocop-github" s.license = "MIT" + s.metadata = { + "source_code_uri" => "https://github.com/github/rubocop-github", + "documentation_uri" => "https://github.com/github/rubocop-github", + "bug_tracker_uri" => "https://github.com/github/rubocop-github/issues" + } + s.files = Dir["README.md", "STYLEGUIDE.md", "LICENSE", "config/*.yml", "lib/**/*.rb", "guides/*.md"] - s.add_dependency "rubocop", ">= 1.37" - s.add_dependency "rubocop-performance", ">= 1.15" - s.add_dependency "rubocop-rails", ">= 2.17" + s.required_ruby_version = ">= 3.1.0" - s.add_development_dependency "actionview" + s.add_dependency "rubocop", ">= 1.76" + s.add_dependency "rubocop-performance", ">= 1.24" + s.add_dependency "rubocop-rails", ">= 2.23" + + s.add_development_dependency "actionview", "~> 7.2.2.1" s.add_development_dependency "minitest" s.add_development_dependency "rake" - s.required_ruby_version = ">= 2.5.0" - s.email = "engineering@github.com" s.authors = "GitHub" end diff --git a/script/lint b/script/lint new file mode 100755 index 00000000..4c06652f --- /dev/null +++ b/script/lint @@ -0,0 +1,5 @@ +#! /usr/bin/env bash + +set -e + +bundle exec rake rubocop diff --git a/script/test b/script/test new file mode 100755 index 00000000..05166157 --- /dev/null +++ b/script/test @@ -0,0 +1,5 @@ +#! /usr/bin/env bash + +set -e + +bundle exec rake test diff --git a/test/cop_test.rb b/test/cop_test.rb index 4af051c1..cb4047ab 100644 --- a/test/cop_test.rb +++ b/test/cop_test.rb @@ -3,7 +3,7 @@ require "action_view" require "minitest" -class CopTest < MiniTest::Test +class CopTest < Minitest::Test def cop_class raise NotImplementedError end @@ -17,7 +17,7 @@ def setup def investigate(cop, src, filename = nil) processed_source = RuboCop::ProcessedSource.new(src, RUBY_VERSION.to_f, filename) - team = RuboCop::Cop::Team.new([cop], nil, raise_error: true) + team = RuboCop::Cop::Team.new([cop], cop.config, raise_error: true) report = team.investigate(processed_source) report.offenses end diff --git a/test/test_avoid_object_send_with_dynamic_method.rb b/test/test_avoid_object_send_with_dynamic_method.rb new file mode 100644 index 00000000..b176cbcc --- /dev/null +++ b/test/test_avoid_object_send_with_dynamic_method.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require_relative "cop_test" +require "minitest/autorun" +require "rubocop/cop/github/avoid_object_send_with_dynamic_method" + +class TestAvoidObjectSendWithDynamicMethod < CopTest + def cop_class + RuboCop::Cop::GitHub::AvoidObjectSendWithDynamicMethod + end + + def test_offended_by_send_call + offenses = investigate cop, <<-RUBY + def my_method(foo) + foo.send(@some_ivar) + end + RUBY + assert_equal 1, offenses.size + assert_equal "#{cop.name}: Avoid using Object#send with a dynamic method name.", offenses.first.message + end + + def test_offended_by_public_send_call + offenses = investigate cop, <<-RUBY + foo.public_send(bar) + RUBY + assert_equal 1, offenses.size + assert_equal "#{cop.name}: Avoid using Object#public_send with a dynamic method name.", offenses.first.message + end + + def test_offended_by_call_to___send__ + offenses = investigate cop, <<-RUBY + foo.__send__(bar) + RUBY + assert_equal 1, offenses.size + assert_equal "#{cop.name}: Avoid using Object#__send__ with a dynamic method name.", offenses.first.message + end + + def test_offended_by_send_calls_without_receiver + offenses = investigate cop, <<-RUBY + send(some_method) + public_send(@some_ivar) + __send__(a_variable, "foo", "bar") + RUBY + assert_equal 3, offenses.size + assert_equal "#{cop.name}: Avoid using Object#send with a dynamic method name.", offenses[0].message + assert_equal "#{cop.name}: Avoid using Object#public_send with a dynamic method name.", offenses[1].message + assert_equal "#{cop.name}: Avoid using Object#__send__ with a dynamic method name.", offenses[2].message + end + + def test_unoffended_by_other_method_calls + offenses = investigate cop, <<-RUBY + foo.bar(arg1, arg2) + case @some_ivar + when :foo + baz.foo + when :bar + baz.bar + end + puts "public_send" if send? + RUBY + assert_equal 0, offenses.size + end + + def test_unoffended_by_send_calls_to_dynamic_methods_that_include_hardcoded_strings + offenses = investigate cop, <<-'RUBY' + foo.send("can_#{action}?") + foo.public_send("make_#{SOME_CONSTANT}") + RUBY + assert_equal 0, offenses.size + end + + def test_unoffended_by_send_calls_without_dynamic_methods + offenses = investigate cop, <<-RUBY + base.send :extend, ClassMethods + foo.public_send(:bar) + foo.__send__("bar", arg1, arg2) + RUBY + assert_equal 0, offenses.size + end +end diff --git a/test/test_insecure_hash_algorithm.rb b/test/test_insecure_hash_algorithm.rb index 597450b8..317562e1 100644 --- a/test/test_insecure_hash_algorithm.rb +++ b/test/test_insecure_hash_algorithm.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./cop_test" +require_relative "cop_test" require "minitest/autorun" require "rubocop/cop/github/insecure_hash_algorithm" @@ -10,7 +10,7 @@ def cop_class end def make_cop(allowed:) - config = RuboCop::Config.new({"GitHub/InsecureHashAlgorithm" => {"Allowed" => allowed}}) + config = RuboCop::Config.new({ "GitHub/InsecureHashAlgorithm" => { "Allowed" => allowed } }) cop_class.new(config) end @@ -57,7 +57,7 @@ def secret_hmac RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_digest_method_md5_str @@ -70,7 +70,7 @@ def h RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_digest_method_md5_symbol @@ -83,7 +83,7 @@ def h RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_digest_method_sha256_str @@ -122,7 +122,7 @@ class Something RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_alias_for_openssl_digest_md5 @@ -133,7 +133,7 @@ class Something RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_alias_for_digest_sha1 @@ -144,7 +144,7 @@ class Something RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_alias_for_openssl_digest_sha1 @@ -155,7 +155,7 @@ class Something RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_alias_for_digest_sha256 @@ -200,7 +200,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_md5_hexdigest @@ -213,7 +213,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_md5_digest_by_name @@ -226,7 +226,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_sha1_digest_by_name @@ -239,7 +239,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_sha1_hexdigest_by_name_mixed_case @@ -252,7 +252,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_sha256_digest_by_name @@ -277,7 +277,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_sha1_hmac_by_name @@ -290,7 +290,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_sha256_hmac_by_name @@ -315,7 +315,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_sha1_digest_instance_by_name @@ -328,7 +328,7 @@ def something(str) RUBY assert_equal 1, offenses.count - assert_equal cop_class::MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::MSG}", offenses.first.message end def test_openssl_sha256_digest_instance_by_name @@ -367,7 +367,7 @@ def uuid RUBY assert_equal 1, offenses.count - assert_equal cop_class::UUID_V3_MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::UUID_V3_MSG}", offenses.first.message end def test_uuid_v3_with_md5_allowed @@ -405,7 +405,7 @@ def uuid RUBY assert_equal 1, offenses.count - assert_equal cop_class::UUID_V5_MSG, offenses.first.message + assert_equal "#{cop.name}: #{cop_class::UUID_V5_MSG}", offenses.first.message end def test_uuid_v5_with_sha1_allowed diff --git a/test/test_rails_controller_render_action_symbol.rb b/test/test_rails_controller_render_action_symbol.rb index 5731d82e..9b67a2c5 100644 --- a/test/test_rails_controller_render_action_symbol.rb +++ b/test/test_rails_controller_render_action_symbol.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./cop_test" +require_relative "cop_test" require "minitest/autorun" require "rubocop/cop/github/rails_controller_render_action_symbol" @@ -47,7 +47,7 @@ def update RUBY assert_equal 3, offenses.count - expected_message = "Prefer `render` with string instead of symbol" + expected_message = "#{cop.name}: Prefer `render` with string instead of symbol" assert_equal expected_message, offenses[0].message assert_equal expected_message, offenses[1].message assert_equal expected_message, offenses[2].message diff --git a/test/test_rails_controller_render_literal.rb b/test/test_rails_controller_render_literal.rb index 9da2f16c..eb13928f 100644 --- a/test/test_rails_controller_render_literal.rb +++ b/test/test_rails_controller_render_literal.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./cop_test" +require_relative "cop_test" require "minitest/autorun" require "rubocop/cop/github/rails_controller_render_literal" @@ -274,7 +274,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_implicit_with_layout_offense @@ -287,7 +289,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_implicit_with_status_offense @@ -300,7 +304,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_variable_offense @@ -313,7 +319,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_to_string_variable_offense @@ -326,7 +334,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_action_variable_offense @@ -339,7 +349,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_template_variable_offense @@ -352,7 +364,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_partial_variable_offense @@ -365,7 +379,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_template_with_layout_variable_offense @@ -378,7 +394,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_template_variable_with_layout_offense @@ -391,7 +409,9 @@ def index RUBY assert_equal 1, offenses.count - assert_equal "render must be used with a string literal or an instance of a Class", offenses[0].message + assert_equal \ + "#{cop.name}: render must be used with a string literal or an instance of a Class, and use literals for locals keys", + offenses[0].message end def test_render_shorthand_static_locals_no_offsense @@ -442,6 +462,17 @@ def index assert_equal 1, offenses.count end + def test_render_literal_splat_locals_offense + offenses = investigate cop, <<-RUBY, "app/controllers/products_controller.rb" + class ProductsController < ActionController::Base + def index + render "products/product", locals: { **locals } + end + end + RUBY + + assert_equal 1, offenses.count + end def test_render_literal_dynamic_local_key_offense offenses = investigate cop, <<-RUBY, "app/controllers/products_controller.rb" diff --git a/test/test_rails_controller_render_shorthand.rb b/test/test_rails_controller_render_shorthand.rb index 0c16bd56..f4bb111a 100644 --- a/test/test_rails_controller_render_shorthand.rb +++ b/test/test_rails_controller_render_shorthand.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./cop_test" +require_relative "cop_test" require "minitest/autorun" require "rubocop/cop/github/rails_controller_render_shorthand" @@ -55,9 +55,9 @@ def confirm RUBY assert_equal 3, offenses.count - assert_equal "Use `render \"edit\"` instead", offenses[0].message - assert_equal "Use `render \"new\"` instead", offenses[1].message - assert_equal "Use `render \"confirm.html.erb\"` instead", offenses[2].message + assert_equal "#{cop.name}: Use `render \"edit\"` instead", offenses[0].message + assert_equal "#{cop.name}: Use `render \"new\"` instead", offenses[1].message + assert_equal "#{cop.name}: Use `render \"confirm.html.erb\"` instead", offenses[2].message end def test_render_template_offense @@ -78,8 +78,8 @@ def edit RUBY assert_equal 3, offenses.count - assert_equal "Use `render \"books/new\"` instead", offenses[0].message - assert_equal "Use `render \"books/show\", locals: { book: @book }` instead", offenses[1].message - assert_equal "Use `render \"books/edit.html.erb\", status: :ok, layout: \"application\"` instead", offenses[2].message + assert_equal "#{cop.name}: Use `render \"books/new\"` instead", offenses[0].message + assert_equal "#{cop.name}: Use `render \"books/show\", locals: { book: @book }` instead", offenses[1].message + assert_equal "#{cop.name}: Use `render \"books/edit.html.erb\", status: :ok, layout: \"application\"` instead", offenses[2].message end end diff --git a/test/test_rails_render_object_collection.rb b/test/test_rails_render_object_collection.rb index 7786dedd..c13ec9af 100644 --- a/test/test_rails_render_object_collection.rb +++ b/test/test_rails_render_object_collection.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./cop_test" +require_relative "cop_test" require "minitest/autorun" require "rubocop/cop/github/rails_render_object_collection" @@ -27,7 +27,7 @@ def test_render_partial_with_object_offense ERB assert_equal 1, offenses.count - assert_equal "Avoid `render object:`, instead `render partial: \"account\", locals: { account: @buyer }`", offenses[0].message + assert_equal "#{cop.name}: Avoid `render object:`, instead `render partial: \"account\", locals: { account: @buyer }`", offenses[0].message end def test_render_collection_with_object_offense @@ -36,7 +36,7 @@ def test_render_collection_with_object_offense ERB assert_equal 1, offenses.count - assert_equal "Avoid `render collection:`", offenses[0].message + assert_equal "#{cop.name}: Avoid `render collection:`", offenses[0].message end def test_render_spacer_template_with_object_offense @@ -45,6 +45,6 @@ def test_render_spacer_template_with_object_offense ERB assert_equal 1, offenses.count - assert_equal "Avoid `render collection:`", offenses[0].message + assert_equal "#{cop.name}: Avoid `render collection:`", offenses[0].message end end diff --git a/test/test_rails_view_render_literal.rb b/test/test_rails_view_render_literal.rb index b983d5f8..7c081dd3 100644 --- a/test/test_rails_view_render_literal.rb +++ b/test/test_rails_view_render_literal.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./cop_test" +require_relative "cop_test" require "minitest/autorun" require "rubocop/cop/github/rails_view_render_literal" @@ -43,7 +43,7 @@ def test_render_variable_offense ERB assert_equal 2, offenses.count - assert_equal "render must be used with a literal template and use literals for locals keys", offenses[0].message + assert_equal "#{cop.name}: render must be used with a literal template and use literals for locals keys", offenses[0].message end def test_render_component_no_offense @@ -102,7 +102,7 @@ def test_render_layout_variable_literal_no_offense ERB assert_equal 1, offenses.count - assert_equal "render must be used with a literal template and use literals for locals keys", offenses[0].message + assert_equal "#{cop.name}: render must be used with a literal template and use literals for locals keys", offenses[0].message end def test_render_inline_no_offense @@ -145,6 +145,14 @@ def test_render_literal_dynamic_local_key_offense assert_equal 1, offenses.count end + def test_render_literal_splat_locals_offense + offenses = erb_investigate cop, <<-ERB, "app/views/products/index.html.erb" + <%= render "products/product", { **locals } %> + ERB + + assert_equal 1, offenses.count + end + def test_render_options_static_locals_no_offense offenses = erb_investigate cop, <<-ERB, "app/views/products/index.html.erb" <%= render partial: "products/product", locals: { product: product } %> @@ -168,4 +176,12 @@ def test_render_options_dynamic_local_key_offense assert_equal 1, offenses.count end + + def test_render_options_local_splat_offense + offenses = erb_investigate cop, <<-ERB, "app/views/products/index.html.erb" + <%= render partial: "products/product", locals: { **locals } %> + ERB + + assert_equal 1, offenses.count + end end diff --git a/test/test_rails_view_render_shorthand.rb b/test/test_rails_view_render_shorthand.rb index 22ff453a..0077ceea 100644 --- a/test/test_rails_view_render_shorthand.rb +++ b/test/test_rails_view_render_shorthand.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./cop_test" +require_relative "cop_test" require "minitest/autorun" require "rubocop/cop/github/rails_view_render_shorthand" @@ -31,7 +31,7 @@ def test_render_partial_offense ERB assert_equal 1, offenses.count - assert_equal "Use `render \"foo/bar\"` instead", offenses[0].message + assert_equal "#{cop.name}: Use `render \"foo/bar\"` instead", offenses[0].message end def test_render_partial_with_locals_offense @@ -40,7 +40,7 @@ def test_render_partial_with_locals_offense ERB assert_equal 1, offenses.count - assert_equal "Use `render \"foo/bar\", { bar: 42 }` instead", offenses[0].message + assert_equal "#{cop.name}: Use `render \"foo/bar\", { bar: 42 }` instead", offenses[0].message end def test_render_layout_no_offense pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy