RuboCop

コード品質リンターRubyDevOps静的解析自動修正Ruby Style Guide

DevOpsツール

RuboCop

概要

RubocopはRubyのコード品質チェックツールです。Ruby Style Guideの遵守をチェックし、コードスタイルの一貫性を保つ。自動修正機能も提供し、Ruby開発の標準的な品質チェックツールとして広く利用されている。LSPサーバー内蔵、段階的導入システム、柔軟な設定システムにより、レガシープロジェクトから新規プロジェクトまで幅広く対応。Rails拡張機能を含む豊富なエコシステムで、Ruby開発における品質向上とコーディング規約の統一を強力にサポートします。

詳細

RuboCop(ルボコップ)は、Ruby コミュニティが開発・維持するRuby専用の静的コード解析ツール(リンター)およびコードフォーマッターです。2013年にリリースされて以来、Rubyエコシステムにおける事実上の標準的なコード品質チェックツールとして確立されています。

主要な特徴

  • Ruby Style Guide準拠: コミュニティRuby Style Guideの自動チェック
  • 自動修正機能: 安全な修正項目の自動適用(Correctable violations)
  • Cops システム: Style、Lint、Metrics、Rails等の部門別ルール体系
  • LSPサーバー内蔵: エディター統合のためのLanguage Server Protocol対応
  • 段階的導入: Pending Cops機能による既存プロジェクトへの安全な導入
  • 高度な設定機能: 極めて柔軟な設定システムによるカスタマイズ
  • 拡張性: Rails拡張等のサードパーティ拡張機能対応
  • Allowlist生成: 既存違反の許可リスト自動生成機能
  • 豊富な出力形式: JSON、HTML、JUnit等の多様な出力フォーマット

Cops部門の詳細

  • Style Cops: コードスタイルの統一(インデント、命名規則等)
  • Lint Cops: 潜在的なバグや問題の検出
  • Metrics Cops: コード複雑度、メソッド長等の測定
  • Rails Cops: Rails固有のベストプラクティス(別途gem必要)

メリット・デメリット

メリット

  • Ruby開発でのコードスタイル統一と品質向上の標準ツール
  • 自動修正機能により手動修正作業の大幅削減
  • Ruby Style Guide準拠により読みやすく保守性の高いコード生成
  • Cops システムによる体系的で包括的なコード品質チェック
  • LSPサーバー統合によるリアルタイムエディターフィードバック
  • 段階的導入システムによりレガシープロジェクトへの安全な適用
  • Rails拡張を含む豊富なエコシステムによる専門的チェック
  • 高度な設定システムによるプロジェクト要件への柔軟な対応
  • CI/CD統合による自動品質保証とコードレビュー効率化
  • Allowlist機能による大規模プロジェクトでの現実的な導入

デメリット

  • 初期設定とルール選択の複雑さと学習コスト
  • 大規模プロジェクトでの実行時間の長さ
  • 過度な厳密設定による開発速度低下リスク
  • レガシーコードへの適用時の大量警告による運用負荷
  • Ruby専用のため多言語プロジェクトでの統一ツール使用不可
  • Cops の依存関係とバージョン互換性管理の複雑さ
  • 一部自動修正の不完全性(手動確認が必要)
  • チーム内でのコーディング規約に関する議論と合意形成の必要性
  • 外部gem(Rails Cops等)の追加依存関係
  • 設定ファイルの複雑化によるメンテナンス負荷

参考ページ

書き方の例

インストールと基本セットアップ

# 基本インストール
gem install rubocop

# Gemfileに追加(推奨)
echo 'gem "rubocop", "~> 1.68", require: false' >> Gemfile
bundle install

# Rails拡張付きインストール
gem install rubocop rubocop-rails

# 開発グループでのインストール
# Gemfile
group :development do
  gem 'rubocop', '~> 1.68', require: false
  gem 'rubocop-rails', '~> 2.27', require: false
  gem 'rubocop-rspec', '~> 3.2', require: false
  gem 'rubocop-performance', '~> 1.23', require: false
end

# バンドラー経由でのインストール
bundle add rubocop --group=development
bundle add rubocop-rails --group=development

# バージョン確認
rubocop --version

基本的な実行方法

# 基本実行
rubocop

# 特定ファイルの検査
rubocop app/models/user.rb

# 特定ディレクトリの検査
rubocop app/controllers/

# 自動修正実行
rubocop -a app/

# 安全でない修正も含めて実行
rubocop -A app/

# 特定Copのみ実行
rubocop --only Style/StringLiterals app/

# 特定Copを除外
rubocop --except Style/Documentation app/

# 詳細出力
rubocop --display-style-guide app/

# 進捗表示
rubocop --progress app/

# 並列実行
rubocop --parallel app/

# キャッシュ使用
rubocop --cache true app/

設定ファイル(.rubocop.yml)

# .rubocop.yml - 基本設定
# 継承設定
inherit_from:
  - .rubocop_todo.yml

# 除外パターン
AllCops:
  # 対象Rubyバージョン
  TargetRubyVersion: 3.2
  
  # 新しいCopsを自動で有効化
  NewCops: enable
  
  # 除外パターン
  Exclude:
    - 'db/schema.rb'
    - 'db/migrate/*'
    - 'vendor/**/*'
    - 'bin/*'
    - 'node_modules/**/*'
    - 'tmp/**/*'
    - '.git/**/*'
  
  # 含めるパターン
  Include:
    - '**/*.rb'
    - '**/*.rake'
    - '**/Rakefile'
    - '**/Gemfile'
    - '**/config.ru'

# Style Cops設定
Style/Documentation:
  Enabled: false

Style/StringLiterals:
  EnforcedStyle: single_quotes

Style/StringLiteralsInInterpolation:
  EnforcedStyle: single_quotes

Style/TrailingCommaInArrayLiteral:
  EnforcedStyleForMultiline: consistent_comma

Style/TrailingCommaInHashLiteral:
  EnforcedStyleForMultiline: consistent_comma

Style/FrozenStringLiteralComment:
  Enabled: true
  EnforcedStyle: always

# Lint Cops設定
Lint/UnusedMethodArgument:
  AllowUnusedKeywordArguments: true

# Metrics Cops設定
Metrics/ClassLength:
  Max: 150

Metrics/MethodLength:
  Max: 20
  CountAsOne:
    - 'array'
    - 'hash'
    - 'heredoc'

Metrics/AbcSize:
  Max: 20

Metrics/CyclomaticComplexity:
  Max: 10

Metrics/PerceivedComplexity:
  Max: 10

Metrics/BlockLength:
  Exclude:
    - 'spec/**/*'
    - 'config/routes.rb'
    - 'db/seeds.rb'

# Layout Cops設定
Layout/LineLength:
  Max: 120
  AllowHeredoc: true
  AllowURI: true
  URISchemes:
    - http
    - https

Layout/MultilineMethodCallIndentation:
  EnforcedStyle: aligned

Layout/HashAlignment:
  EnforcedHashRocketStyle: key
  EnforcedColonStyle: key

Rails専用設定

# .rubocop.yml - Rails設定
require:
  - rubocop-rails
  - rubocop-rspec
  - rubocop-performance

inherit_from: .rubocop_todo.yml

AllCops:
  TargetRubyVersion: 3.2
  NewCops: enable
  Exclude:
    - 'db/schema.rb'
    - 'db/migrate/*'
    - 'vendor/**/*'
    - 'bin/*'
    - 'node_modules/**/*'

# Rails Cops設定
Rails/Enabled: true

Rails/HasManyOrHasOneDependent:
  Enabled: true

Rails/InversOf:
  Enabled: true

Rails/Presence:
  Enabled: true

Rails/UniqBeforePluck:
  Enabled: true

Rails/BulkChangeTable:
  Enabled: true

# RSpec Cops設定
RSpec/ExampleLength:
  Max: 10

RSpec/MultipleExpectations:
  Max: 3

RSpec/NestedGroups:
  Max: 3

RSpec/DescribeClass:
  Enabled: true

# Performance Cops設定
Performance/Caller:
  Enabled: true

Performance/CaseWhenSplat:
  Enabled: true

Performance/StringReplacement:
  Enabled: true

CI/CD統合例

GitHub Actions

# .github/workflows/rubocop.yml
name: RuboCop

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  rubocop:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup Ruby
      uses: ruby/setup-ruby@v1
      with:
        ruby-version: 3.2
        bundler-cache: true
    
    - name: Install dependencies
      run: bundle install
    
    - name: Run RuboCop
      run: bundle exec rubocop --format github
    
    - name: Run RuboCop with auto-correct (if main branch)
      if: github.ref == 'refs/heads/main'
      run: |
        bundle exec rubocop -a
        if [ -n "$(git status --porcelain)" ]; then
          git config --local user.email "[email protected]"
          git config --local user.name "GitHub Action"
          git add -A
          git commit -m "Auto-fix RuboCop violations" -a
          git push
        fi

GitLab CI

# .gitlab-ci.yml
stages:
  - quality

rubocop:
  stage: quality
  image: ruby:3.2-alpine
  before_script:
    - apk add --no-cache git
    - bundle install --jobs $(nproc) --retry 3
  script:
    - bundle exec rubocop --format json --out rubocop-results.json
    - bundle exec rubocop
  artifacts:
    reports:
      codequality: rubocop-results.json
    paths:
      - rubocop-results.json
    expire_in: 1 week
  only:
    - main
    - develop
    - merge_requests

エディター統合設定

VS Code設定

// .vscode/settings.json
{
  "ruby.rubocop.executePath": "bundle exec rubocop",
  "ruby.rubocop.configFilePath": ".rubocop.yml",
  "ruby.rubocop.onSave": true,
  "ruby.format": "rubocop",
  "ruby.lint": {
    "rubocop": {
      "useBundler": true,
      "lint": true,
      "rails": true
    }
  },
  "[ruby]": {
    "editor.defaultFormatter": "misogi.ruby-rubocop",
    "editor.formatOnSave": true,
    "editor.rulers": [120]
  }
}

// RuboCop LSP使用設定
{
  "ruby.rubocop.useLsp": true,
  "ruby.rubocop.lspConfigurationOptions": {
    "safeAutocorrect": true
  }
}

RubyMine/IntelliJ設定

# RubyMine設定
# File → Settings → Editor → Inspections → Ruby
# RuboCop有効化

# External Tools設定
# File → Settings → Tools → External Tools
Name: RuboCop
Description: Ruby static code analyzer
Program: bundle
Arguments: exec rubocop $FilePath$
Working Directory: $ProjectFileDir$

# File Watchers設定
# File Type: Ruby
# Scope: Project Files
# Program: bundle
# Arguments: exec rubocop -a $FilePath$

段階的導入戦略

TODO ファイル生成

# 既存プロジェクトでのTODOファイル生成
rubocop --auto-gen-config

# 生成される .rubocop_todo.yml の例
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2025-01-01 12:00:00 UTC using RuboCop version 1.68.0.

# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.

Style/Documentation:
  Exclude:
    - 'app/controllers/application_controller.rb'
    - 'app/models/user.rb'

Metrics/MethodLength:
  Max: 25  # Goal: 15

段階的改善スクリプト

# scripts/rubocop_gradual.rb
#!/usr/bin/env ruby

require 'yaml'
require 'json'

class RubocopGradualImprovement
  def initialize
    @todo_file = '.rubocop_todo.yml'
    @config = load_todo_config
  end

  def improve_gradually
    @config.each do |cop_name, settings|
      next unless improvable?(cop_name, settings)
      
      puts "Improving #{cop_name}..."
      improve_cop(cop_name, settings)
    end
    
    save_config
  end

  private

  def improvable?(cop_name, settings)
    # Metrics系のCopで数値設定があるもの
    cop_name.start_with?('Metrics/') && 
      settings.is_a?(Hash) && 
      settings['Max']
  end

  def improve_cop(cop_name, settings)
    current_max = settings['Max']
    target_max = target_value(cop_name)
    
    if current_max > target_max
      new_max = [current_max - 1, target_max].max
      settings['Max'] = new_max
      puts "  #{cop_name}: #{current_max}#{new_max}"
    end
  end

  def target_value(cop_name)
    targets = {
      'Metrics/MethodLength' => 15,
      'Metrics/ClassLength' => 100,
      'Metrics/AbcSize' => 15,
      'Metrics/CyclomaticComplexity' => 6
    }
    targets[cop_name] || 10
  end

  def load_todo_config
    YAML.load_file(@todo_file) if File.exist?(@todo_file)
  rescue
    {}
  end

  def save_config
    File.write(@todo_file, @config.to_yaml)
  end
end

# 実行
RubocopGradualImprovement.new.improve_gradually if __FILE__ == $0

高度な設定とカスタマイズ

カスタムCop作成

# lib/custom_cops/no_binding_pry.rb
module CustomCops
  class NoBindingPry < RuboCop::Cop::Base
    MSG = 'Remove binding.pry from production code.'

    def on_send(node)
      return unless binding_pry?(node)

      add_offense(node, message: MSG)
    end

    private

    def binding_pry?(node)
      node.receiver&.const_name == 'Kernel' &&
        node.method_name == :binding &&
        node.parent&.method_name == :pry
    end
  end
end
# .rubocop.yml でカスタムCop読み込み
require:
  - './lib/custom_cops/no_binding_pry'

CustomCops/NoBindingPry:
  Enabled: true
  Exclude:
    - 'spec/**/*'

複数環境での設定管理

# .rubocop.yml - 本番用設定
production: &production
  AllCops:
    TargetRubyVersion: 3.2
  Metrics/LineLength:
    Max: 100

# .rubocop_development.yml - 開発用設定
inherit_from: .rubocop.yml

AllCops:
  NewCops: enable

Style/Documentation:
  Enabled: false

Metrics/MethodLength:
  Max: 25  # 開発時は緩め
# 環境別実行
rubocop --config .rubocop.yml           # 本番設定
rubocop --config .rubocop_development.yml  # 開発設定

パフォーマンス最適化とトラブルシューティング

# パフォーマンス診断
rubocop --profile

# 並列実行(高速化)
rubocop --parallel

# キャッシュ利用
rubocop --cache true

# デバッグ出力
rubocop --debug app/models/user.rb

# 特定Copの詳細情報
rubocop --show-cops Style/StringLiterals

# 設定ファイル検証
rubocop --config .rubocop.yml --lint

# メモリ使用量確認
time rubocop --parallel app/