その理由:
- プロジェクトごとに、使用する gem とそのバージョンを管理できる
- Gemfile, Gemfile.lock をコミットしておくことで git clone && bundler install ですぐに必要な gem が整う
- 多くの gem は全プロジェクトで共通でよい
- 例:pry, awesome_print, digest, logger など
- バージョンアップを即適用するべきものも少なくない(opensslなど)
- 今後 Ruby の添付ライブラリの gem 化が促進されるので、そのようなケースはますます増える
- .rb を実行するのに毎回 cd PROJECT_DIR && bundle exec hoge.rb が面倒
- とくに cron で実行するもの
- gem を厳選して global にインストールする。バージョン依存しないと思われるもの。
- bundler, pry, awesome_print, json, openssl, rack, passenger
- 各.rb では以下のように記述する
require 'global_gem1'
require 'global_gem2'
:
require 'bundler/setup' # これ以降 Project 管理の gem を require する
require 'gem_in_this_project1'
require 'gem_in_this_project2'
:
これにより、不便な点のほとんどが解消される(唯一 cd PROJECT_DIR しないといけない点は残る)。
「gem が global かどうかをスクリプト側で記載するのはメンテナンス性に欠ける」
→ 逆に、同じ gem でもプロジェクトで管理するかシステムに任せる(=global gem を使用する)かをスクリプトで制御できる。
「これでは、git clone & bundle install ですぐ動作しない。global gem がシステムにインストールされているかは分からない」
→ それはその通り。しかし元々「すべての gem をプロジェクトごとに持ちたくない」がこの運用のモチベーションなので、それはトレードオフ。
「disable-shared-gems を無効にすればいいんじゃね?」
→ 確かに disable-shared-gems を無効にすると Gemfile に指定した gem がシステムにあればインストールはスキップされてシステムのgemを使う。
しかしそれだと「このプロジェクトではこのgemをプロジェクトローカルに持っておきたい」というコントロールができない。
「rbenv使ってるなら、いっそのこと bundler 使わなくても・・・」
→ ううむ、実際 Ruby バージョンごとに gemset 持っていれば非互換性のためにスクリプトが動作しないということはまず起きないだろう・・・。
しかし他のサーバーで動かそうとしたとき git clone && bundle install すればよいだけなのはやっぱり楽だ。
- sinatraアプリをpassengerで走らせているのだが、passengerは自動的にbundle execするのでbundler/setupする前のrequire(global gemを呼び出すもの)がエラーになってしまう
- sinatraアプリだけはすべてgemをプロジェクト管理にするしかない。が、そんな使い分けは面倒だしシンプルじゃない・・・。
参考
- Markdownチートシート https://gist.github.com/wate/7072365
- Bundlerちょっと似た悩み http://takuya-1st.hatenablog.jp/entry/2016/01/03/172013