bundlerの使い方・bundlerのメリット
「bundler」について調べてみました
普段何気に使っているけど、詳しく理解していなかったので調べてみたシリーズ。
今回はRuby on railsユーザーにとってはお馴染みの「bundler」です。
bundlerとは
bundlerとは、gemの「依存関係」と「バージョン」を管理するためのパッケージ管理ツールです。「bundle」とは束ねるという意味です。
bundlerの利点
bundlerの主な利点は以下の4点です。
gemの依存関係を自動検知してくれる
自動検知したgemを一括インストールできる
gemのバージョンを固定し、一貫性を保てる
gemをプロジェクト毎に管理でき、システムファイルを汚さない
<補足>
「ライブラリ」とは
プログラムを書く際に、全ての機能を一から書き出すのは非効率です。そこで、事前に作成され公開されている、ある特定の機能を持たせたプログラムを再利用するのが一般的です。
そのような特定機能を持つプログラムを部品化し、それら複数の部品を一つのファイルに集約したものを「ライブラリ」といいます。
尚、「ライブラリ」はRuby on railsに限らず、プログラミング言語全般で利用されています。
「Gem(ジェム)」とは
Ruby on rails(Ruby)ではライブラリのことを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をインストールしないと、上手く動作できずにエラーとなってしまいます。
仮に、これらの依存関係を考慮し手動でインストールする場合は、
依存関係にあるgemを全て調べてリストアップする
依存関係にあるgemを一つ一つインストールする
という膨大な手間が発生します。これは非常に面倒です。
そこで、それを助けてくれるのもやはり「bundler」です。
「bundler」は、こういったgemの依存関係を自動的に検知して必要なgemの必要ばバージョンを一括インストールしてくれます。
バージョン固定について
「bundler」の優れた点はもう一つあります。インストールするgemを固定する「gemfile.lock」の機能です。
これは特に、複数のメンバーで一つのプロジェクトを開発する際に役立ちます。
「budnler」を利用してgemのインストールする際、まずは
「gemfile」というファイルに、プロジェクトで使用したいgemのリストとバージョンの要件を手動で記述します。
記述後に「bundle install」コマンドでgemをインストールすると、gemfileに記述されたgemのリストに基づき、「依存関係にあるgemとバージョン」を自動検知し、一括インストールします。
その結果は「gemfile.locl」に自動的に記録され固定されます。
次に「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の無い人生はありえない