Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: アプリケーションを静的解析で"防弾"する3つの便利ワザ(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Rails: アプリケーションを静的解析で"防弾"する3つの便利ワザ(翻訳)

静的解析とは、コードを実行せずに潜在的な問題を特定して品質を改善することです。有用な静的解析手法を導入することで、アプリケーションの信頼性を高められます。本記事では、コードベースの問題を静的解析で解決するうえで便利な手法を3つ解説します。

🔗 1: ファイル名が間違っているテストファイルを検出する

先頃、顧客のアプリケーションに含まれている未使用のコードを追いかけていたときに、明らかにパスしないRSpecテストが1つ見つかりました。rspecでパスを指定してこのテストだけを実行してみると、やはり失敗しました。
しかしbundle exec rspecでテストスイートを実行するとテストがすべてパスしてしまいます。さらに調べてみると、このテストファイルがRSpecの命名規則に沿っていないことが判明しました。

RSpecのドキュメントには以下のように書かれています。

# デフォルト: すべてのspecファイルを実行する(spec/**/*_spec.rbにマッチするファイルが対象)
$ bundle exec rspec

RSpecでバイパスされるテストファイルが他にもあるかどうかを検証したかったのですが、プロジェクトが巨大なため手動で調べるのは無理でした。

そこで以下のコマンドを使うことで、問題のあるテストファイルを残らず特定できました。

find ./spec -type f -not -name \*_spec.rb -not -path "./spec/factories/*" -not -path "./spec/support/*" | xargs rg RSpec\.describe

調べた結果、*.spec.rb*_sepc.rbといったファイルの命名ミスが続々見つかりました。これらのテストファイルを正しくリネームすると、それらの半分近くが失敗していたことが判明しました。

訳注

上のコマンドで使われているrgはripgrepというgrepツールです。ripgrepをインストールしていない場合は、インストールするかgrepなどに置き換えてください。

BurntSushi/ripgrep - GitHub

🔗 2: 解決しない定数を検出する

参考: Tracking down not resolving constants in Ruby with parser | Arkency Blog

上の過去記事で、解決しない定数をparser gemで追跡する方法を詳しく解説しました。解決されない定数は実行時エラーになる可能性がありますが、静的解析で手軽に防止できます。このスクリプトは以下のリポジトリで公開しているので、このcollector.rbを自分のプロジェクトにコピーすれば、以下のように簡単に実行できます。

arkency/constants-resolver - GitHub

bundle exec ruby collector.rb app/

🔗 3: 不要なルーティングを検出する

コードベースをきれいにする便利なスクリプトをもうひとつご紹介します。
このスクリプトは、routes.rbファイル内で定義されているルーティングの中に、対応するコントローラアクションが存在しないものや、暗黙のレンダリング用のビューが存在しないものがあるかどうかをチェックします。

# unused_routes.rb
require_relative "config/environment"

Rails.application.routes.routes.map(&:requirements).each do |route|
  next if route.blank?
  next if route[:internal]

  controller_name = "#{route[:controller].camelcase}Controller"
  next if controller_name.constantize.new.respond_to?(route[:action])

  implicit_render_view = Rails.root.join("app", "views", *route[:controller].split('::'), "#{route[:action]}.*")
  next if Dir.glob(implicit_render_view).any?

  puts "#{controller_name}##{route[:action]}"
rescue NameError, LoadError
  puts "#{controller_name}##{route[:action]} - controller not found"
end

このスクリプトをコピーしてruby unused_routes.rbを実行するだけでチェックできます。もしかすると凄い結果が表示されるかもしれません。

訳注

参考: 現在のRailsのmainリポジトリには、bin/rails routes --unusedで未使用のルーティングを検出する機能がマージされています(#45701)。おそらくRails 7.1から利用できると思われます。

参考: 週刊Railsウォッチ20220822前編: routes --unusedで冗長なルーティングを検出


元記事末尾のフォームに登録いただくと、Arkencyのベテランプログラマーによる磨き抜かれた洞察や知恵を結晶したメールマガジンを配信いたします。

関連記事

Rails: Active Recordモデルのスレッド安全性問題をインスタンス変数で解決する(翻訳)

Rails: ルーティングを’only’や’except’オプションで整理する(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。