bundlerでgemをプロジェクトごとに管理する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

前回ではrubyの環境を構築しました。
今回はプロジェクトごとにgemを管理する方法をご紹介したいと思います。

gemとは

rubyではライブラリやフレームワークをgemというパッケージにして再利用ができ、
rubygemsという管理ツールでインストールやアップデート、アンインストールが可能です。
これは他の言語にも同じようなシステムが存在します。

  • Python の PyPi
  • Perl の CPAN
  • Node の npm

ruby 1.9からはrubygemsは標準で組み込まれるようになったので特に準備は必要ありません。 またgemはrubygems.orgにて検索が可能です。

rubygemsを使ってみる

rubygemsのコマンドはgemです。以下によく使うコマンドとオプションを載せておきます。

gemをインストールする

$ gem install (gem name)

デフォルトではドキュメントも一緒にインストールされます。しかしインストール時間が長くかかってしまうので省くオプションをつけることもできます。

$ gem install (gem name) --no-ri --no-rdoc

gemをアップデート

$ gem update (gem name)

gemをアンインストール

$ gem uninstall (gem name)

インストール済みのgemの一覧を出力

$ gem list

インストール済みのgem一覧をブラウザでも確認できます

$ gem server

gemコマンドのヘルプ

$ gem help (command)

bundlerを使う

Bundler: The best way to manage Ruby applications

さて本題のbundlerを使用したgemの管理方法に触れたいと思います
bundler自体もgemですのでまずはインストールします。

$ gem install bundler --no-ri --no-rdoc

インストールができたら作業ディレクトリに移動し初期化コマンドを実行します。
bundlerのコマンドはbundleです。

$ bundle init
Writing new Gemfile to /path/to/project/Gemfile

Gemfileという名前のファイルが作られました。
これは使用するgemをrubyで記述して、バージョンを指定したり環境ごとにグループ分けができるようになります。

例えばスクレイピングのライブラリであるMechanizeを使う場合は以下のように記述します。

source "https://rubygems.org"

gem 'mechanize', '~> 2.5.1'

一行目にはgemがホスティングされているサーバを記述します。基本的にはrubygems.orgで問題ありません
次にgemメソッドの第一引数に名称、第二引数は省略も可能ですがバージョンを指定しています。
~>は2.5.1以上で2.6未満の最新を指定しています。
他にも>=<などが使えますが詳しくはこちらのページを御覧ください。

Gemfileに記述しただけではまだ使用できませんのでインストールを実行します。
また今回はプロジェクトごとにというのが目的ですのでvendor/bundleフォルダをインストールパスに指定しました。
フォルダは事前に用意しなくてもbundlerが作成してくれるので大丈夫です。

$ bundle install --path vendor/bundle
Fetching full source index from https://rubygems.org/
Installing unf_ext (0.0.5) with native extensions 
Installing unf (0.0.5) 
Installing domain_name (0.5.4) 
Installing mime-types (1.19) 
Installing net-http-digest_auth (1.2.1) 
Installing net-http-persistent (2.8) 
Installing nokogiri (1.5.5) with native extensions 
Installing ntlm-http (0.1.1) 
Installing webrobots (0.0.13) 
Installing mechanize (2.5.1) 
Using bundler (1.2.1) 
Your bundle is complete! It was installed into ./vendor/bundle

依存ライブラリも含めてvendor/bundleフォルダにインストールされました。
そしてmechanizeを使ったサンプルを作成してみます。このブログのトップページにアクセスしてtitleタグの中身を出力してみるようにしました。

require 'mechanize'

agent = Mechanize.new
agent.get('https://dev.classmethod.jp/')
puts agent.page.title

そしてこのrubyスクリプトを実行してみましょう。通常であれば ruby sample.rb などとします。
しかしmechanizeはシステムにインストールされていませんので、エラーしてしまいます。
プロジェクトローカルな環境で実行するには以下のようにします。

$ bundle exec ruby sample.rb
AWS, iPhone, Android, HTML5に関する情報発信 | クラスメソッド開発ブログ

bundle exec をつけることで問題なく動作しました。

githubにあるgemを使う

githubに限りませんがgitリポジトリからインストールもできます。

gem "foo", :git => "https://github.com/bar/foo.git"

環境別にgemを管理する

テストフレームワーク等プロダクション環境には必要ないgemがある場合はgroupを使うと環境別にgemを管理できます。
例えば以下のように記述しbundle install時にオプションをつけることで実現できます。

source "https://rubygems.org"

gem "mechanize", "~> 2.5.1"

group :development, :test do
  gem "rspec"
end
$ bundle install --path vendor/bundle --without development test

このようにインストール時に指定したオプションは.bundle/configというファイルに保存されていますので、gemを追加した場合など2回目以降では省略できます。
またこれらのオプションは以下のコマンドで確認できます。

$ bundle config
Settings are listed in order of priority. The top value will be used.

path
  Set for your local app (/path/to/project/.bundle/config): "vendor/bundle"

disable_shared_gems
  Set for your local app (/path/to/project/.bundle/config): "1"

without
  Set for your local app (/path/to/project/.bundle/config): "development:test"

bundle install後にはプロジェクトフォルダに Gemfile.lock というファイルが作られています。これはGemfileに記述したバージョンが曖昧だった場合でも実際にどのバージョンをインストールしたのかが示されています。
これによって別に環境を構築する際にも同じ状態が作れるようになります。

gemをアップデートする

新しいバージョンがリリースされたらGemfileのバージョンを書き換えて、bundle updateを実行します。
単体のgemの指定もできます。

gem "mechanize", "~> 2.6" # 2.6がリリースされたと仮定
$ bundle update mechanize 

オフライン環境でもbundle installできるようにする

プロダクション環境は外部ネットワークに出られないといったこともあるかもしれません。そういった場合にも対応できるようなオプションも存在します。
以下のように bundle package を使うと vendor/cache フォルダにgemをキャッシュします。

$ bundle package
Using unf_ext (0.0.5) 
Using unf (0.0.5) 
Using domain_name (0.5.4) 
Using mime-types (1.19) 
Using net-http-digest_auth (1.2.1) 
Using net-http-persistent (2.8) 
Using nokogiri (1.5.5) 
Using ntlm-http (0.1.1) 
Using webrobots (0.0.13) 
Using mechanize (2.5.1) 
Using bundler (1.2.1) 
Your bundle is complete! It was installed into ./vendor/bundle
Updating files in vendor/cache
  * unf_ext-0.0.5.gem
  * unf-0.0.5.gem
  * domain_name-0.5.4.gem
  * mime-types-1.19.gem
  * net-http-digest_auth-1.2.1.gem
  * net-http-persistent-2.8.gem
  * nokogiri-1.5.5.gem
  * ntlm-http-0.1.1.gem
  * webrobots-0.0.13.gem
  * mechanize-2.5.1.gem

オフライン環境では --local オプションを付けることでrubygems.org等を見に行かずに、vendor/cacheフォルダを見るようになります。

$ bundle install --local

まとめ

bundlerには他にもbundler自体がインストールされていない環境でも動かせるようにするオプション(--standalone)等、便利な機能がたくさん用意されています。
大まかにはGemfileを元にインストールとロードパスを通すのが役目です。
ちょっとだけ試してみたいgemがあるときにも気軽に使えるようになると思うので是非活用してみてください。