年末なので Rails 依存関係の大掃除

2022年 Ruby on Rails Advent カレンダー 19日目の記事です。

Ruby on Rails はトップページ(2022年12月19日現在)で  "a full-stack framework" とうたっています。実際、Web アプリ開発でよく使われるさまざまな機能が Rails にはてんこ盛りです。そのため、自分のアプリでは使わない機能を提供するジェム(依存関係)も、たくさんインストールされてしまいます。

この1年、仕事で Rails を使った開発に参加するようになって

  • 使ってないジェムがバンドルサイズ  53 MB のうち 11 MB (21%)も消費している。ローカルや CI でインストールが遅くなるのはイヤだな〜
  • 使ってないジェムに脆弱性のアラートや dependabot の自動更新が来てしまう。放っておくと本当に重要なアラートや更新が埋もれてしまってイヤだな〜

と気になってしまったので、使っていない Rails のジェムを削除してみました。

 

想定する Rails 環境

  • Rails アプリ(や Engine)としての初期設定を済ませている
  • 依存関係のバージョンを統一するため、Gemfile.lock をバージョン管理している
  • 十分なテストカバレッジがあり、ジェム削除によるデグレに高確率で気付ける

すべて満たしていることを想定しています。


Rails に必要ないジェムを削除していく手順

rails ジェムの gemspec ファイルを見ると、rails ジェムは 12 個のジェムに依存していることがわかります。この12個すべてではなく、必要なジェムにだけ依存するようにしていきます。

まずは、Gemfile に書かれた

gem 'rails', '7.0.4'

のような rails ジェムを、次のように 12個のジェムに置き換えます。

RAILS_VERSION = '7.0.4'
gem 'actioncable', RAILS_VERSION
gem 'actionmailbox', RAILS_VERSION
gem 'actionmailer', RAILS_VERSION
gem 'actionpack', RAILS_VERSION
gem 'actiontext', RAILS_VERSION
gem 'actionview', RAILS_VERSION
gem 'activejob', RAILS_VERSION
gem 'activemodel', RAILS_VERSION
gem 'activerecord', RAILS_VERSION
gem 'activestorage', RAILS_VERSION
gem 'activesupport', RAILS_VERSION
gem 'railties', RAILS_VERSION

この時点で bundle install コマンドを実行し、Gemfile.lock から rails ジェムだけが消えることを確認します。

 

あとは、これら12個それぞれに対して、次の手順を繰り返していきます。

  1. gem '...' の1行を削除する
  2. bundle install を実行する。更新された Gemfile.lock から該当のジェムが消えるのを確認する
    • もし消えなければ、別のジェムに使われているということ。それら別のジェムを消せないか検討する
  3. ソースコードを検索し、削除するジェムに関するファイルや設定値を見つけて削除する
    • たとえば actioncable ジェムなら Action cable や action_cable といったように、大文字小文字違い、さまざまな区切り文字もある。git grep を使い、git grep --extended-regexp --ignore-case "paction.?cable" のような正規表現で検索するとよい
  4. テストを実行する。問題が見つかれば修正する(後述)
  5. ジェムを削除して問題なさそうであればコミットする。問題あればリバートする

 

具体例1:activejob ジェムの削除

activejob ジェムを削除すると、NameError: unitiliazed constant ActiveJob のようなエラーが起きることがあります。"class ApplicationJob < ActiveJob::Base" のように、削除したジェムに含まれるクラスやモジュールを使っていると、それらが見つからないので起きるエラーです。

activejob を使っていない場合、ApplicationJob クラスは空の実装なので、削除できるでしょう。

 

具体例2: actionmailer ジェムの削除

actionmailer ジェムは、メールを送信する機能を提供します(受信は actionmailbox)。

先に示した正規表現で git grep --extended-regexp --ignore-case "action.?mailbox" と検索すると、以下のようなファイルがヒットします。

  • app
    • mailers
      • application_mailer.rb
    • views
      • layouts
        • mailer.html.erb
        • mailer.txt.erb
    • config
      • environments
        • development.rb
        • development.rb

これらから、actionmailer に関する行やファイル自体を削除します。削除したらテストを実行し、問題がないことを確かめます。 


消せそうなジェム、消せなそうなジェム

  • 消せる可能性がありもの。これらはアプリによっては必要ないケースがある
    1. actioncable
    2. actionmailbox
    3. actionmailer
    4. actiontext
    5. activejob
    6. activestorage
  • 消せる可能性があまりないもの。これらは Rails のコアな機能に相当し、消せないことが多い。Gemfile からは消せても、なんらかの Rails エコシステムのジェムが使っており、Gemfile.lock に残る可能性が高い
    1. actionpack
    2. actionview
    3. activerecord
    4. activemodel
    5. activesupport
    6. railties

まとめ

  • rails ジェムは12個のジェムの集合体(rails 7.0.4 時点)である
  • Gemfile 内で rails ジェムを最小限のジェムに置き換えると、不要なジェムをインストールしないで済む
  • 場合によっては10MBなどバンドルサイズを縮めることができる。また、意味のない脆弱性アラートなども減らせる
Rails の不要なジェムを整理して、2023年もよい Rails 開発者ライフを!