RuboCop
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/