ゆうなんとかさんの雑記帳的な。

Twitterで踊ったり音ゲーしたりしてるあの名前がよくわからない人が書いてるらしいよ。

Bundlerをゆるふわに使っていていたい目にあったお話

今日のハイライト

きっ、聞いてないよ!Bundlerを使って必要なGemをインストールするまではよかったのに、バージョンが違うみたいでそんな動きかたしてないだなんて!

改めましてBundlerとは

RubyのライブラリはRubyGemsというツールでインストールすることができます。名前もルビーだけにGem(宝石)となかなかおしゃれですね。しかし、プロジェクトを他の環境で動かしたいとき、

あ、このアプリはSinatraとLessとTypeScriptと…と、あとテスト用にRSpec使ってるから

というのを何かしらの手段で伝えないといけません。何も考えずにやるとすごく面倒で、足りないものがないかヒヤヒヤしますね。間違って他のライブラリをインストールしてしまう単純なうっかりから、バージョン違いで挙動の整合性がとれないくなって「原因不明のバグ」が起きたりもします。機能を追加して必要なライブラリが増えると、その度に報告しなければいけません。とてもめんどくさいですね。
そこでBundlerの出番です。アプリケーションのルートディレクトリにGemfileというファイルを作っておいて、そこに必要なものを書いていけばいいわけです。あとはBundlerさんがGemfileに書かれた必要なライブラリを持ってきてくれる、という寸法です。

さて冒頭に戻りまして

ちなみにBundlerさん、このバージョンのGemを持ってきてねと指定することもできるのですが、私はあまりここをまじめに書いていなかったので今回痛い目を見ました。Gemfile.lockを持ち回らなかった環境でGemのバージョンが違って、「原因不明のエラー」に頭を抱えていたのでした。それほど難しい話ではないのですが、後学のためメモしておきます。

Gemspecについて

今回は特に触れません。なにやら依存性を書いておくファイルらしいよとだけいっておきます。たぶんGemを作る機会があればそのときに覚え直すことになりそう

Gemfileについて

Gemfileは必要なGemをどこから持ってくるかを指定します。

バージョン指定の書き方

決め打ちするならGemの名前の次にバージョンの数字を書いておけばOK。

  gem 'rails', '4.0.0'

わかりやすいですね。
比較的よく見る気がする~>はマイナーバージョンは何でもいいよと伝えます。

  gem 'devise', '~>3.0.0'

とすると、3.0.0や3.0.1といったバージョンのDeviseがインストールされますが、3.1.0といったバージョンはインストールされません。
このバージョン以上のものがほしいときはこちらを使います。

  gem 'rspec', '>=2.8.0'

この場合は2.8.0以上のなるべく新しいバージョンのRSpecがインストールされます。バージョンの上限を制限することもできて、

  gem 'sinatra', '< 1.0.0'

としておくと、1.0.0以上のSinatraはインストールされません。ちなみに、以上と未満は組み合わせて指定できます。ドキュメントを見る限りでは、以下とそれより大きいバージョンで指定することはできないみたいです。
Bundlerが今の環境に持ってきたGemが知りたいときは

bundle list

で一覧表示してくれるので、それをもとにバージョンを固定するか、Gemfile.lockも一緒に持っていくかしましょう。

Gemfile.lockって何者?

必要なGemが仔細にまとめて書いてあるファイルです。Gemfileでバージョンを指定しなかったGemのバージョンはここで決め打ちされるようです。BundlerでGemをインストール、アップデートしたときに自動で作られたり書き換えられたりするファイルで、中には人間にとってはあんまりフレンドリーじゃないフォーマットでBundlerが持ってきたGemが書かれています。手で書いてもいいのかもしれませんがあんまり考えたくないですね。
私のところではbundle installすると何度でもよみがえるので、バージョン管理していてマージに失敗すると消される憂き目に遭っています*1
GemfileとGemfile.lockがあれば、どんな環境でも同じGem使うことができます(なかなか反例が思いつかない程度にはそのはず)。
3兄弟の中で唯一ファイルをバージョン管理するべきか否かという議論が持ち上がるファイルでもあります。

今後はどうしよう

Ruby界隈のバージョンアップは他の言語に比べるとかなり過激に過去のものを切り捨ててくる感があるので*2、取り残されないようについていくのももちろんですが、もうすこしバージョン指定に気を使いたいと思います。

*1:まあGemfileの競合だけ解消しておけばコマンドでよみがえらせられるしいいよねと思ってたけどそういうしくみだったとは

*2:Railsとかがいい例