`rails stats`が任意のディレクトリから実行可能に

rspec/rspec-rails

この変更により、bin/rails statsをアプリケーションルート以外のディレクトリから実行しても、specファイルの統計情報が正しく表示されるようになりました。これにより、開発時のディレクトリ移動を気にせずコード統計を確認できます。

背景

これまで bin/rails stats をアプリケーションルート以外のディレクトリから実行すると、RSpecのテストコードが統計に含まれず Test LOC: 0 と表示される問題がありました。#2862 で報告されたこの問題は、コード統計の登録処理が Dir.pwd に依存していたことが原因です。

Rails 8.0で導入された Rails::CodeStatistics.register_directory を使用したコード統計登録は、Railtieクラス本体で Dir['./spec/**/*_spec.rb'] のように相対パスを使用していました。このパターンは現在の作業ディレクトリ(Dir.pwd)を基準に展開されるため、/tmp などの別ディレクトリから実行すると、specファイルが見つからなくなります。

技術的な変更

lib/rspec-rails.rb のコード統計登録処理が、Railtieクラス本体からイニシャライザ内に移動され、Rails.root を基準とした絶対パス解決に変更されました。

変更前:

if ::Rails::VERSION::STRING >= "8.0.0"
  require 'rails/code_statistics'

  dirs = Dir['./spec/**/*_spec.rb']
    .map { |f| f.sub(/^\.\/(spec\/\w+)\/.*/, '\\1') }
    .uniq
    .select { |f| File.directory?(f) }

  Hash[dirs.map { |d| [d.split('/').last, d] }].each do |type, dir|
    name = type.singularize.capitalize
    ::Rails::CodeStatistics.register_directory "#{name} specs", dir, test_directory: true
  end
end

変更後:

if ::Rails::VERSION::STRING >= "8.0.0"
  initializer "rspec_rails.code_statistics" do
    require 'rails/code_statistics'

    root = ::Rails.root
    dirs = Dir[root.join('spec', '**', '*_spec.rb').to_s]
             .map { |f| f.sub(%r{^#{Regexp.escape(root.to_s)}/(spec/\w+)/.*}, '\\1') }
             .uniq
             .select { |f| File.directory?(root.join(f)) }

    Hash[dirs.map { |d| [d.split('/').last, d] }].each do |type, dir|
      name = type.singularize.capitalize
      ::Rails::CodeStatistics.register_directory "#{name} specs", root.join(dir).to_s, test_directory: true
    end
  end
end

主な変更点は3つです。まず、処理全体を initializer ブロック内に配置することで、Railsアプリケーションの初期化時に Rails.root が確定した後に実行されるようになりました。次に、Dir['./spec/**/*_spec.rb']Dir[root.join('spec', '**', '*_spec.rb').to_s] に変更し、相対パスから絶対パスベースの検索に変更しています。最後に、ディレクトリの存在チェックとパス登録も root.join(f)root.join(dir).to_s を使用して絶対パス化されました。

テストケースは features/rails_stats.feature に追加され、/tmp ディレクトリに移動してから bin/rails stats を実行するシナリオで検証されています。

設計判断

コード統計の登録を Railtieクラス本体からイニシャライザへ移動 する方式が採用されました。

Railtieクラス本体で実行されるコードは、Railsアプリケーションの初期化が完了する前に評価されます。この時点では Rails.root がまだ確定していない可能性があるため、相対パスに依存した実装が選択されていました。しかし、この設計は作業ディレクトリへの依存という脆弱性を生んでいました。

イニシャライザに移動することで、Rails.root が確実に利用可能な状態で処理が実行されるようになり、絶対パス解決が可能になります。この変更により、Rails本体が想定する「アプリケーションルートを基準とした動作」に統一され、作業ディレクトリに依存しない堅牢な実装になりました。

まとめ

本PRは、コード統計登録のタイミングを変更することで、rails stats の実行場所への依存を解消した改善です。Railtieの初期化フローを適切に活用し、Rails.root による絶対パス解決を導入することで、開発時の利便性を損なわずに堅牢性を向上させています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)」という構成が明確に守られており、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```ruby:lib/rspec-rails.rb)やGitHubへのリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Railtieやinitializerといった用語を前提としており、専門知識を持つエンジニアという対象読者に適合した内容です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論で構成され、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が非常によく守られており、可読性が高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiffの内容を正確に反映しています。テストファイルへの言及もDiffの内容と一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Railtie」「initializer」「Dir.pwd」「Rails.root」などの技術用語が、PRの文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「Dir.pwdへの依存」という問題の原因と、「initializerへの移動による絶対パス解決」という解決策の関係性が論理的かつ技術的に正確に説明されています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのDescriptionやDiff内のコードで裏付けられています。特に「設計判断」セクションは、PRの変更意図を技術的背景に基づいて的確に解説しており、ハルシネーションは見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#2879)、Issue番号(#2862)、バージョン番号(Rails 8.0)などの数値や固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「`rails stats`が任意のディレクトリから実行可能に」は、PRのタイトル「Fix rails stats not finding spec directories when run outside app root」の内容を的確に要約しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事に含まれるバージョン情報(Rails 8.0)はDiffから取得可能であり、PRに記載のないLTSやEOLなどの外部知識の追加はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「これまで」や「導入された」といった時間表現は、問題の発生時期と修正の文脈を正確に反映しています。