💬

RuboCopとERB LintをRailsに導入する

2022/04/13に公開

概要

ERBにRuboCopをできるように、ERB lintをRailsで動かしてみました。

背景

久しぶりにERBを書いていています。理由は、 https://www.hotrails.dev/turbo-rails をやり始めたからです。
ERBは写経しているだけです。しかし、どうもtypoをやりがちで、ストレスがたまるので、lintを探してゆきつきました。erb-lint紹介記事に。

READMEみながらRubocopと連動しつつRailsにいれてみました。

作業ログのRepository

https://github.com/junara/erblint_rubocop_sample

手順

  • Rails 7をminimalに Rails newする
  • RuboCop導入する
  • RuboCopと連動したERB lintを入れる
  • GitHub Actionsを動かす

Rails 7をminimalに Rails newする

rails newのhelpを見ながら、不要な機能をskipしたoptionを付与したcommandを構成します。

helpの一部は以下の通り。

rails new --help
Options:
-d, [--database=DATABASE]                                  # Preconfigure for selected database (options: mysql/postgresql/sqlite3/oracle/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc)
                                                             # Default: sqlite3
  -G, [--skip-git], [--no-skip-git]                          # Skip .gitignore file
      [--skip-keeps], [--no-skip-keeps]                      # Skip source control .keep files
  -M, [--skip-action-mailer], [--no-skip-action-mailer]      # Skip Action Mailer files
      [--skip-action-mailbox], [--no-skip-action-mailbox]    # Skip Action Mailbox gem
      [--skip-action-text], [--no-skip-action-text]          # Skip Action Text gem
  -O, [--skip-active-record], [--no-skip-active-record]      # Skip Active Record files
      [--skip-active-job], [--no-skip-active-job]            # Skip Active Job
      [--skip-active-storage], [--no-skip-active-storage]    # Skip Active Storage files
  -C, [--skip-action-cable], [--no-skip-action-cable]        # Skip Action Cable files
  -A, [--skip-asset-pipeline], [--no-skip-asset-pipeline]    # Indicates when to generate skip asset pipeline
  -a, [--asset-pipeline=ASSET_PIPELINE]                      # Choose your asset pipeline [options: sprockets (default), propshaft]
                                                             # Default: sprockets
  -J, [--skip-javascript], [--no-skip-javascript]            # Skip JavaScript files
      [--skip-hotwire], [--no-skip-hotwire]                  # Skip Hotwire integration
      [--skip-jbuilder], [--no-skip-jbuilder]                # Skip jbuilder gem
  -T, [--skip-test], [--no-skip-test]                        # Skip test files
      [--skip-system-test], [--no-skip-system-test]          # Skip system test files
      [--skip-bootsnap], [--no-skip-bootsnap]                # Skip bootsnap gem
      [--dev], [--no-dev]                                    # Set up the application with Gemfile pointing to your Rails checkout
      [--edge], [--no-edge]                                  # Set up the application with Gemfile pointing to Rails repository
  --master, [--main], [--no-main]                            # Set up the application with Gemfile pointing to Rails repository main branch
      [--rc=RC]                                              # Path to file containing extra configuration options for rails command
      [--no-rc], [--no-no-rc]                                # Skip loading of extra configuration options from .railsrc file
      [--api], [--no-api]                                    # Preconfigure smaller stack for API only apps
      [--minimal], [--no-minimal]                            # Preconfigure a minimal rails app
  -j, [--javascript=JAVASCRIPT]                              # Choose JavaScript approach [options: importmap (default), webpack, esbuild, rollup]
                                                             # Default: 

で、これで、つくったの以下のcommand。--minimalはとりあえず入れた。不要かも。

rails new --database=sqlite3 --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-active-job --skip-active-storage --skip-action-cable --skip-javascript --skip-hotwire --skip-jbuilder --skip-test --skip-system-test --skip-bootsnap --minimal .

RuboCopを導入する

いつも使うrubocopの一連のgemを入れます。Gemfileに以下を加えます。
今回rspecは不要だけど。

https://github.com/junara/erblint_rubocop_sample/blob/ab3511eb75bc1be4962b5f73bc059f9db67ab2db/Gemfile#L27-L32

  gem 'rubocop'
  gem 'rubocop-ast'
  gem 'rubocop-performance'
  gem 'rubocop-rails'
  gem 'rubocop-rake'
  gem 'rubocop-rspec'
bundle install

個人的にいつも、使う設定を .rubocop.ymlとして書く。

https://github.com/junara/erblint_rubocop_sample/blob/main/.rubocop.yml

inherit_from: .rubocop_todo.yml

Style/AsciiComments:
  Enabled: false

Style/Documentation:
  Enabled: false

AllCops:
  TargetRubyVersion: 3.1
  NewCops: enable

require:
  - rubocop-ast
  - rubocop-rails
  - rubocop-rake
  - rubocop-rspec
  - rubocop-performance

.rubocop_todo.ymlを空で作ります。

https://github.com/junara/erblint_rubocop_sample/blob/main/.rubocop_todo.yml

touch .rubocop_todo.yml

rubocopで自動的に修正してもらいます。

rubocop -A

で、今回気づいたけどRails 7になってからRuboCop実行したときの自動修正箇所がまったくなかった。

RuboCopと連動したERB lintを入れる

(だいたいのログのPull requestはこちら
Gemfileに下記を追加します。

https://github.com/junara/erblint_rubocop_sample/blob/ab3511eb75bc1be4962b5f73bc059f9db67ab2db/Gemfile#L26

  gem 'erb_lint',
bundle install

ERB lintは Defaultで LintersがYesとなっているlinterが実行されます。

erblint --lint-all

これだとRuboCopが適用されないので、RuboCopも適用されるように.erb-lint.ymlを作ります。

https://github.com/junara/erblint_rubocop_sample/blob/main/.erb-lint.yml

exclude:
  - '**/vendor/**/*'
  - '**/node_modules/**/*'
EnableDefaultLinters: true
linters:
  Rubocop:
    enabled: true
    rubocop_config:
      inherit_from:
        - .rubocop.yml
      Style/FrozenStringLiteralComment:
        Enabled: false
      Layout/InitialIndentation:
        Enabled: false
      Layout/TrailingEmptyLines:
        Enabled: false

RuboCop解析を有効化するのはこの部分です。

  Rubocop:
    enabled: true
    rubocop_config:
      inherit_from:
        - .rubocop.yml

ただ、上記をそのまま適用するとERR中だと、やりすぎだったので、以下のRuboCopおnLinterは無効化します。

      Style/FrozenStringLiteralComment:
        Enabled: false
      Layout/InitialIndentation:
        Enabled: false
      Layout/TrailingEmptyLines:
        Enabled: false

最後に、自動的にERBファイルを修正してもらいます。

erblint --lint-all -a

GitHub Actionsを動かす

だいたいの作業ログのPull Requestはこちら
GitHub Actionsでrubocoperblintを実行してもらいます

https://github.com/junara/erblint_rubocop_sample/blob/main/.github/workflows/lint.yml

name: Lint

on:
  workflow_dispatch:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        os: [ ubuntu-latest ]
        ruby: [ 3.0, 3.1 ]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v2
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ matrix.ruby }}
          bundler-cache: true
      - name: RuboCop
        run: bundle exec rubocop
      - name: ERB Lint
        run: bundle exec erblint --lint-all

以上

Discussion