RailsプロジェクトにRuboCopを導入したいけど、ルールは何があるの?
はじめに
RuboCopは、Rubyのコードスタイルやベストプラクティスをチェックする静的解析ツールです。
Railsプロジェクトへの導入を検討する際、「どんなルールがあるのか」「どのPluginを入れるべきか」と悩む方も多いのではないでしょうか。
この記事では、RuboCop GemのDepartment(ルールのカテゴリ)と、Railsプロジェクトで役立つPluginを紹介します。
対象読者
- 新規または既存のRailsプロジェクトにRuboCopを導入したいが、どんなルールがあるか分からない人
- 運用中のRuboCopのルールを見直したい人
用語
Cop
RuboCopのルール1つ分のことをCopといいます。各Copは有効化・無効化や設定のカスタマイズが可能です。
Department
Copの種類ごとの集まり。
例: Style, Metrics, Lint, Security
Plugin
RuboCopの機能を拡張するためのgemパッケージ。特定のライブラリや観点に特化したCopを追加できます。
例:rubocop-rails, rubocop-rspec, rubocop-performance
RuboCop GemのDepartment
RuboCop Gemに含まれているCop Departmentを紹介します。
Layout
コードのレイアウトやフォーマットに関するCop。インデント、空白、改行、行の長さなど、コードの見た目や整形ルールを統一します。
例えば、Layout/EmptyLineAfterGuardClauseは、ガード句(early return)の後に空行を入れることを推奨します。
# bad
def process(value)
return if value.nil?
puts value
end
# good
def process(value)
return if value.nil?
puts value
end
Style
Rubyのコーディングスタイルに関するDepartment。コードの書き方や構文の使い方について、Rubyコミュニティで推奨されるスタイルガイドに沿った記述を促します。
例えば、Style/HashSyntaxは、ハッシュのキーと値の記法を統一します。
# bad (古いハッシュロケット記法)
user = { :name => 'Alice', :age => 25 }
# good (Ruby 1.9以降のシンボルキー記法)
user = { name: 'Alice', age: 25 }
Naming
変数、メソッド、クラスなどの命名規則に関するDepartment。Rubyの命名慣習に従った名前付けを促し、コードの可読性を向上させます。
例えば、Naming/VariableNameは、変数名がスネークケース(小文字とアンダースコア)で書かれているかをチェックします。
# bad
myVariable = 1
# good
my_variable = 1
Metrics
コードの複雑さや規模を測定するDepartment。メソッドの長さ、クラスの長さ、循環的複雑度など、コードの保守性に影響する指標をチェックし、適切な範囲に収めることを促します。
例えば、Metrics/MethodLengthは、1メソッドの長さが上限を超えた場合に警告します。デフォルトの上限は10行です。
Lint
コードの潜在的なバグやエラーを検出するDepartment。構文的には正しくても実行時に問題を引き起こす可能性のあるコードパターンを警告します。
例えば、Lint/ShadowingOuterLocalVariableは、外側のスコープの変数名をブロック内で再定義してしまう問題を検出します。
# bad
user = User.find(1)
users.each do |user| # 外側のuserを上書き
puts user.name
end
# good
user = User.find(1)
users.each do |u|
puts u.name
end
Security
セキュリティ上の問題を検出するDepartment。evalメソッドの使用やYAMLの安全でない読み込みなど、潜在的な脆弱性を持つコードパターンを警告します。
例えば、Security/Evalは、悪意あるコードを実行させられる可能性のあるevalメソッドの使用を検出します。
# bad
eval(something)
binding.eval(something)
Kernel.eval(something)
Bundler
Gemfileの記述に関するDepartment。gemの記述方法や順序など、Bundlerを使用する際のベストプラクティスを促します。
例えば、Bundler/OrderedGemsは、Gemfile内のgemをアルファベット順に並べることを促します。
# bad
gem 'rubocop'
gem 'rspec'
gem 'rails'
# good
gem 'rails'
gem 'rspec'
gem 'rubocop'
Gemspec
gemspecファイル(.gemspec)の記述に関するDepartment。gem開発時のメタデータや依存関係の記述方法について、ベストプラクティスを促します。
例えば、Gemspec/RequiredRubyVersionは、gemspecファイルにrequired_ruby_versionを指定することを推奨します。
# bad
Gem::Specification.new do |spec|
spec.name = 'my_gem'
end
# good
Gem::Specification.new do |spec|
spec.name = 'my_gem'
spec.required_ruby_version = '>= 2.7.0'
end
Plugin
Railsプロジェクトに役立つRuboCopのPluginについて紹介します。
rubocop-performance
Rubyのパフォーマンスに関するPluginです。実行速度やメモリ使用量を改善できるコードパターンを検出し、より効率的な書き方を提案します。
例えば、Performance/StringIncludeは、文字列に部分文字列が含まれるかチェックする際に、より高速なinclude?メソッドの使用を推奨します。
# bad
'hello world'.match?(/world/)
# good
'hello world'.include?('world')
rubocop-rails
Ruby on Rails特化のPluginです。Railsのベストプラクティスやアンチパターンを検出し、よりRailsらしいコードの記述を促します。
記事執筆時点で135個ものCopがあるので、既存のプロジェクトに導入する場合は、Copを段階的に導入または選別するのがよさそうです。
例えば、Rails/FindByは、where.firstの代わりにより効率的なfind_byの使用を推奨します。
# bad
User.where(email: 'user@example.com').first
# good
User.find_by(email: 'user@example.com')
rubocop-minitest
Minitest特化のPlugin。Minitestテストフレームワークを使用する際のベストプラクティスや推奨される記述方法を促します。
例えば、Minitest/AssertEmptyは、空であることをチェックする際に、より意図が明確なassert_emptyの使用を推奨します。
# bad
assert(array.empty?)
# good
assert_empty(array)
rubocop-rspec
RSpec特化のPluginです。RSpecのテストコードにおけるベストプラクティスや推奨される記述方法を促します。
例えば、RSpec/LetSetupは、letで定義した変数を使用していない場合に警告します。
# bad - letで定義しているが使われていない
let(:user) { create(:user) }
it 'has users' do
expect(User.count).to eq(1)
end
# good - letで定義した変数を使う
let(:user) { create(:user) }
it 'creates user' do
expect(user).to be_persisted
end
# good - before を使う
before do
create(:user)
end
it 'has users' do
expect(User.count).to eq(1)
end
rubocop-factory_bot
FactoryBot特化のPluginです。テストデータを作成するためのファクトリ定義におけるベストプラクティスを促します。
例えば、FactoryBot/CreateListは、複数のレコードを作成する際に、create_listメソッドの使用を推奨します。
# bad
3.times { create(:user) }
# good
create_list(:user, 3)
rubocop-capybara
Capybara特化のPlugin。Webアプリケーションの統合テストを記述する際のベストプラクティスを促します。
例えば、Capybara/SpecificMatcherは、汎用的なマッチャーの代わりに、より具体的で意図が明確なマッチャーの使用を推奨します。
# bad
expect(page).to have_selector('button', text: 'Submit')
# good
expect(page).to have_button('Submit')
# bad
expect(page).to have_selector('a', text: 'Home')
# good
expect(page).to have_link('Home')
rubocop-thread_safety
スレッドセーフ*を意識したコードを強制するためのPluginです。マルチスレッド環境で安全に動作するコードの記述を促します。
*スレッドセーフ:複数のスレッドが同時に同じリソースにアクセスしてもデータ整合性が保たれ、プログラムが正常に動作する状態
例えば、ThreadSafety/NewThreadは、Thread.newの代わりにスレッドプールやバックグラウンドジョブの使用を推奨します。
# bad - 直接スレッドを作成
def process_items(items)
items.map do |item|
Thread.new { item.process }
end.each(&:join)
end
# good - バックグラウンドジョブを使用
def process_items(items)
items.each do |item|
ProcessItemJob.perform_later(item)
end
end
rubocop-rubycw
ruby -cwを実行した時に出る警告をRuboCopのoffenseとして報告するRuboCop pluginです。
-
ruby -cは、コードを実行せずにRubyの構文チェックを行い、エラーがないかを確認します。 -
ruby -wは、構文チェックに加えて、Ruby処理系がコード内から見つけた警告(未使用の変数があるなど)を報告するよう設定します。
その他のPlugin
今回は詳細には紹介しませんが、他にも様々なPluginがあります。
RuboCop公式
サードパーティ製
- rubocop-require_tools
- cookstyle
- rubocop-packaging
- rubocop-sorbet
- rubocop-graphql
- rubocop-changed
- rubocop-sketchup
おわりに
上記で見てきたように、RuboCopには実に多くのルールがあります。
全てのルールを導入することは現実的ではないので、プロジェクトやチームのニーズに応じて、選択していくことが重要です。
この記事が、RuboCopの導入や改善の参考になれば幸いです。
Discussion