目的別、bundlerの利用法

bundlerは、Gemfileに記述したRuby gemsを維持するパッケージマネージャです。複数のプロジェクトを管理する場合には、bundlerの挙動をよく理解しておくと便利です。

bundlerが不要なケース

bundlerはプロジェクトごとにgemセットを分離する目的のツールです。
たとえばコンテナのように各プロジェクトの環境が混ざることがないのであれば、bundlerを使わない方が維持しやすいケースがあります。

この場合には単にgem installでグローバルにインストールします。gemが提供するCLIには後述のbundle execをつけずに実行します。
gemはgit経由のインストールをサポートしていないのですが、 specific_installプラグインを追加すると対応します。

コンテナ用途によく使われるdebian:slimの場合、次のような流れになります。

# gemコマンドとrubyのインストール
apt install ruby-rubygems

# specific_installのインストール
gem install specific_install

# git経由のgemインストール。-dオプションはmonorepoの場合のサブディレクトリ指定
gem specific_install https://github.com/chumaltd/luca -d lucabook

bundlerを導入すると参照関係が複雑になるため、bundlerを排除するセットアップの方が長期メンテナンス時の挙動は明瞭です。

プロジェクト別のgemをインストールする

bundlerを利用すると、任意のpathにgemをインストールできます。インストール先のディレクトリはbundle installを実行する前にbundle configで設定しておきます。
なお、インストールオプションのbundle install --pathは既にobsoleteです。

$ bundle config --local path vendor/

紛らわしいのですが、--localオプションはインストール先を指定するものではなく、configじたいの保存先(.bundle/)をカレントディレクトリに指定するものです。
--localを付けないデフォルト挙動ではユーザーのホームディレクトリとなり、プロジェクトごとの切り分けができません。
他の bundle configオプションについては、 公式リファレンスを参照してください。

binstubsでbundle execを省略する

rubyのgemの中にはコマンドラインツールを提供するものがあり、bundle installでインストールすると、bundle execつきで実行できます。

さらに、bundlerのbundle binstubsコマンドを実行すると、bundle execをつけずに実行可能なラッパースクリプトを生成でき、期待どおりrails newの形式で利用できます。

コマンドの書式は以下の通りです。

$ bundle binstubs some_gem --path=some_path

引数はコマンド名ではなくgemの名称です。よって、railsコマンドをセットアップしたい場合にはrailtiesを指定します。複数のgemをターゲットにする場合はスペース区切りで羅列します。

--pathオプションにはスクリプトをインストールするpathを指定します。一般的なコマンドのインストールと同様、PATHの通ったディレクトリを指定することで、コマンド名のみで実行可能になります。プロジェクト内にインストールすることもグローバルディレクトリにインストールすることも可能です。

コマンド例のとおりbundle binstubsはインストール済のgemを参照するため、bundle installでひと通りgemをインストールしたあとに実行します。

なお、binstubsが生成するファイルは、gemが供給するrubyプログラムではなく、簡潔なシェルスクリプトです。gemをアップグレードした場合にも、実行ファイルの変更は基本的に必要ありません。

自作ツールをbundle

自分で作ったスクリプトをgemにしておくと、RubyGemsで配布されているgemと同様にbundlerで管理できます。
自作gemの詳細については、 Ruby自作ライブラリの管理で解説しています。bundlerは、gitやローカルのPathにも対応しているため、必ずしもRubyGemsで公開する必要はありません。

Gemfileでpath参照しているgemについては、インストール・アップデートも必要なく、つねに自分で書いた最新版を参照します。
また、bundle installしておけば自作gemにもbinstubsを作成できるため、CLIツールを書くと一般的なコマンドと同様に利用できます。

ネイティブモジュールのコンパイル

依存しているgemがネイティブモジュールを含む場合、bundle installプロセスでCなどのコンパイルを求められます。ビルドエラーに対処するには、Cのビルドに関する基礎知識が必要になります。

まず、Cのツールチェインをインストールしておく必要があります。rubyのネイティブ拡張ビルドがどのツールに依存しているかは自明ではありません。
Debian12の場合、gcc, make, ruby3.1-dev, libc-devが最低限必要でした。

また、各gemが依存しているCライブラリとヘッダー(Debianの場合は-devパッケージ)も必要でしょう。

rubyツールチェインはネイティブモジュールのC依存をまったく解決しないため、ビルドエラーが頻発します。
net-imapの date dependency requires native compilationの議論のように、default gemもバージョンアップがあるとビルドする挙動になります。bundlerはGemfileにversion指定がない場合、アグレッシブに最新バージョンを選択するようです。

また、Cのビルドツールは200MB超のディスク消費を必要とするため、コンテナ用途では期待に反してイメージが肥大化します。
ビルドツールは実行時には不要であるため、必要に応じてビルドステージと実行環境を分割するといった工夫をすることになるでしょう。

中馬崇尋
Chuma Takahiro