K’s diary

プログラミング、ビジネスや時事ニュース、経営/人事、音楽や映画について書いていきます

bundlerの使い方・bundlerのメリット

「bundler」について調べてみました

普段何気に使っているけど、詳しく理解していなかったので調べてみたシリーズ。

今回はRuby on railsユーザーにとってはお馴染みの「bundler」です。

bundlerとは

bundlerとは、gemの「依存関係」と「バージョン」を管理するためのパッケージ管理ツールです。「bundle」とは束ねるという意味です。

bundlerの利点

bundlerの主な利点は以下の4点です。

  • gemの依存関係を自動検知してくれる

  • 自動検知したgemを一括インストールできる

  • gemのバージョンを固定し、一貫性を保てる

  • gemをプロジェクト毎に管理でき、システムファイルを汚さない

<補足>

「ライブラリ」とは

プログラムを書く際に、全ての機能を一から書き出すのは非効率です。そこで、事前に作成され公開されている、ある特定の機能を持たせたプログラムを再利用するのが一般的です。

そのような特定機能を持つプログラムを部品化し、それら複数の部品を一つのファイルに集約したものを「ライブラリ」といいます。

尚、「ライブラリ」はRuby on railsに限らず、プログラミング言語全般で利用されています。

「Gem(ジェム)」とは

Ruby on railsRuby)ではライブラリのことをGem(ジェム)と呼びます。

Gemを利用することで、ユーザー登録などの複雑な処理が必要な機能を簡単にアプリケーションに組み込むことができ、効率的に開発をすることができます。

ちなみに、皆さんご存知の「Ruby on Rails」や「bundler」自体も「gem」の一つです。

gemのインストールについて

Ruby on Rails でアプリケーションを構築する際には、ほぼ必ず専用ライブラリの「gem」を複数インストールして利用することになります。

これらのgemをbundlerを利用せずにインストールする場合、

$ gem install rails
$ gem install bundler
・・・

といった具合で、一つ一つを個別にインストールしていきます。

しかし、1つのrailsアプリで利用するgemは数十種類以上に及びます。これだけでも非常に手間ですね。

しかしbundlerは、gemfileというファイルを事前に編集すれば、後はコマンド一発で一括ダウンロードしてくれ、非常に便利です。

依存関係について

さらに、これらgemには「依存関係」というものがあります。

<補足>

Ruby on Rails のgemに限らず、他の言語、フレームワーク、ライブラリを利用する際には必ず「依存関係」の問題があります)

例えば、以下のような場合です。

  • gemAを使うのにgemBが必要(BはAに依存)

  • gemBを使うのにgemCが必要(CはBに依存)

これは、あるgemを使うために、別のgemが必要になるパターンです。

また同時に、こんなことも起こります。

  • gemAの「バージョン2.0」を使うには、gemBのバージョンは「2.5以上」でなければならない

  • gemBの「バージョン2.5」を使うには、gemCのバージョンは「1.8以上」でなければならない

これは、利用するgemのバージョンごとに依存関係がある場合です。

こういった依存関係を考慮した上でgemをインストールしないと、上手く動作できずにエラーとなってしまいます。

仮に、これらの依存関係を考慮し手動でインストールする場合は、

  1. 依存関係にあるgemを全て調べてリストアップする

  2. 依存関係にあるgemを一つ一つインストールする

という膨大な手間が発生します。これは非常に面倒です。

そこで、それを助けてくれるのもやはり「bundler」です。

「bundler」は、こういったgemの依存関係を自動的に検知して必要なgemの必要ばバージョンを一括インストールしてくれます。

バージョン固定について

「bundler」の優れた点はもう一つあります。インストールするgemを固定する「gemfile.lock」の機能です。

これは特に、複数のメンバーで一つのプロジェクトを開発する際に役立ちます。

「budnler」を利用してgemのインストールする際、まずは

  1. 「gemfile」というファイルに、プロジェクトで使用したいgemのリストとバージョンの要件を手動で記述します。

  2. 記述後に「bundle install」コマンドでgemをインストールすると、gemfileに記述されたgemのリストに基づき、「依存関係にあるgemとバージョン」を自動検知し、一括インストールします。

  3. その結果は「gemfile.locl」に自動的に記録され固定されます。

  4. 次に「bundle install」する時は、「gemfile.lock」に基づいてgemをインストールします。

この「gemfile.lock」に一度記録されたgemとバージョンは固定され、新たなgemが導入される時や、更新用のコマンドが実行された時以外には変更されることはありません。

これによって、複数のメンバーで共通のプロジェクトを構築する際に、メンバーごとに利用するgemやバージョンが異なる、といった問題が起こるのを防ぎ、メンバー間での一貫性を保持することができます。

gemの保存場所の指定について

また、bundlerでgemをインストールする際には、オプションでgemをインストールする場所を指定できます。

通常、bundlerを使ってインストールする時には以下のコマンドを実行します。

$ bundle install

このコマンドを実行すると、gemfile並びにgemfile.lockを参照し、必要なgemが一括インストールされます。

この時、gemファイルは以下の場所に保存されます。

Users/ユーザー名/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/

上のパスの通り、システム環境のrbenvで管理しているRubyのうち、現在利用中のバージョン配下に保存されます。

仮に、複数の異なるプロジェクトで、同じバージョンのRubyを利用している場合、

bundle installすることで、そのRubyファイルの中にバージョンの異なる同じgemが混在することになります。

実は、これ自体はさほど問題ではありません。

bundlerにはプロジェクトごとにバージョンを固定する仕組みがあるため、これが原因でgemの依存関係によるエラーが起こることはさほどありません。

しかし、例えば、

  • 特定のプロジェクトでのみ利用するgemを自身のシステム環境に入れたくない

  • システム環境に大量のgemが入ると管理が煩雑になる。後の整理が面倒なので避けたい

  • 複数プロジェクト毎のgemのバージョンの違いを意識せずに開発したい

といった要望がある場合には、gemを(PC上の環境全体に影響を及ぼすシステムファイルにインストールせずに)「ローカル(プロジェクト)ごとに個別管理する」方法が有効です。

そういった場合は、最初のインストール時に以下のコマンドを実行します。

bundle install --path vender/bundle

「--path」部分はオプションで、pathの後の場所をインストールする先として指定しています。

また「vender/bundle」フォルダはインストール時点で存在していなくても、コマンド実行時に自動で作成されます。

これを実行すると、プロジェクトの配下に「vender/bundle」というgemを配置するためのディレクトリが自動作成され、そこにプロジェクト内で利用するgemがインストールされる、ということです。

これにより、rbenvのRuby環境を汚すことなく、プロジェクト毎にgemを配置することができ、管理もしやすくなります。

<補足>

一度bundle install —path vender/bundleを実行すると、次回からpathを指定せずbundle installするだけで、自動で「vendor/bundle」にgemがインストールされます。

これは、bundle install —path vender/bundleを実行した際に「.bundle/config」という設定ファイルが自動生成され、その中で保存先が指定されているからです。ファイルを開けると以下の記述があります。

「BUNDLE_PATH: "vendor/bundle"」

もしgemの保存先を変更したい場合は、上記の記述を変更するか、不要であればディレクトリごと削除しましょう。

bundlerとrbenvの使い分けについて

「bundler」と似た立ち位置のツールに「rbenv」というものがあります。

「rbenv」についてはこちらの記事をご参考ください。

「rbenv」と「bundler」のそれぞれの役割は以下の通りです。

  • Rubyのインストールとバージョン管理は「rbenv」

  • gemのインストールとバージョン管理は 「bundler」

Rubyをインストールしたりバージョンを切り替えたり、Rubyのバージョンごとにgemをインストールしたりするのがこの「rbenv」でした。

上で書いたように、bundler install--pathを指定しないとrbenv配下のRubyの各バージョンの中にgemがインストールされます。

では、gemを「rbenv配下のRuby環境」か「プロジェクト配下(vendor/bundle)」どちらに置くのが望ましいか?

これには意見が分かれますが、メリットデメリットを考慮して選択するといいと思います。

rbenv/Ruby配下に置く
メリット
  • プロジェクトごとに置く場合と比較して容量の圧迫が少ない など
デメリット
  • Rubyバージョンごとにgemが保持され、gemの管理がやや煩雑になる など
vendor/bundlerに置く場合
メリット
  • システム環境を汚ず、gemの管理がしやすい

  • Rubyを初期状態に戻したりgemを削除したりも快適 など

デメリット
  • プロジェクトが増える度にgemファイルが新たにインストールされるため容量を圧迫する

  • railsやrakeコマンド実行時に頭にbundle execを付ける必要がある など

      

私個人的には、gemは「vendor/bundler」に置くようにしています。

  • 「rbenv」のRuby配下のgemファイルに置くのは「bundler」だけにする

  • プロジェクト毎の「vender/bundle」配下にgemを配置し個別に管理する

としています。

Ruby環境が汚れずクリーンに保てるのでベターかなと思っています(精神衛生上)。

ただ、railsコマンドの頭に毎回bundle execを付けるのがちょっと面倒臭いですね。

<補足>

bundle execコマンドについて

bundlerのコマンドを実行する際に頭にbundle execを付けることで、「今のRailsプロジェクト環境のGemfileに基づきbundlerが実行」されます。

(例:$ bundle exec rake routesなど)→プロジェクト固有のgemで「rake」コマンドを実行する

逆にbundle execを付けずに実行すると、「rbenv配下のRubyの中にある、システム環境のgem」が読み込まれます。

(例:$ rake routesなど)→システム環境にあるgemで「rake」コマンドを実行する

当然、上記の様に、rbenvのRuby内にgemが存在しない状態ならコマンドは実行できません。

なお、gemをプロジェクト環境下の「vendor/bundle」で管理する点について、

  • rbenvにgemを保持する方法ではプロジェクトごとの独立性が保たれない

  • gemのバージョン違いが混在することでの依存関係のエラーが起こる

と考えられている様な記事がたまにありますが、bundlerにはプロジェクト毎に 利用するgemのバージョンを固定する機能が備わっていることから、基本的には上記の様なことは起こらないとのこと。

あくまで、上に書いたメリットとデメリットを考慮して決めれば良いと思います。

参考ページ

bundler公式

rbenv:github.com

最後に

bundlerの無い人生はありえない

関連ページ

rbenvについて調べた