2012年10月2日火曜日

モテカワるびいすとのための rbenv 入門

このエントリーをはてなブックマークに追加

はじめに

こんにちは。嫌われエンジニアの西村(@Sixeight)です。
さいきん Kyoto.rb というコミュニティを始めました。
どうぞよろしくお願いします。

今日はさいきん巷で話題だった(乗り遅れた感ありますね) rbenv について取り上げたいと思います。
弊社ではまだRVMが主流なのですが、アルバイトの若者をたぶらかして少しずつシェアを増やしています。

rbenv ってなに

https://github.com/sstephenson/rbenv

インストール済の複数のRubyをよしなに切り替えてくれるべんりツールです。
37signalsのsstephensonことSam Stephensonさんによるプロダクトです。
他にもかっこ良いプロダクトを書きまくられています。

最初にREADME.mdから抜粋。

rbenvがしてくれること

rbenvは3つのことを提供してくれます。

  1. ユーザ毎に使用するRubyのバージョンを変更できます -> たとえば 1.9.3 と 2.0.0-dev をインストールしていると、これらを好きな時に切り替えて使用できます
  2. プロジェクト毎にRubyのバージョンをを指定出来ます -> .rbenv-version というファイルを読み取って特定のディレクトリ以下のRubyのバージョンを指定できます。
  3. 環境変数によってRubyのバージョンを指定出来ます -> RBENV_VERSION を指定しておくとその環境変数が有効な範囲でだけRubyのバージョンを指定できます。

rbenv がしてくれないこと

rbenvはRVMと比較して以下のことはしないとしています。 RVMの問題点を改善するために産まれた経緯からもこの“しないこと"は重要です。

  1. シェルに変なスクリプトをロードする必要はありません -> rbenvが採用しているShimを使った方法では$PATHにディレクトリを追加するだけで動作します。
  2. シェルのコマンドを上書きしません -> たとえばcdとか。これは大変危険だし、バグの温床になります
  3. 設定ファイルを必要としません -> 必要なのはRubyのバージョン番号だけ!
  4. Rubyのインストールはしません -> rvm install 1.9.3みたいなことは出来ません。rbenvがやることはあくまでインストールされたRubyのバージョンの切り替えです。代りにruby-buildを使うことでrvm installと同様のことを実現しています。
  5. Gemsetの管理はしません -> BundlerはGemsetより良いGemの管理方法なので、Bundlerを使うべき。まだBundlerを導入していないプロジェクト向きにrbenv-gemsetもあります。
  6. 相性が良くないべつのRubyライブラリを変更する必要はありません -> CapistranoでRVMを使うためには結構たいへんだけど、rbenvではそんなことは起りません。
  7. バージョンを切り替えるときに警告を出しません -> RVMのように任意のコードを実行できるようにはなってなくて、バージョン番号しか指定しないので、そもそも信頼する必要がありません。

インストール方法

ふつうのプログラマのみなさんはMacユーザだと思いますので、当然のようにHomeBrewを使います。

RVMのアンインストール

RVMをインストールされている場合は競合して問題が起きないようアンインストールしておく必要があります。 以下のコマンドをタイプしてRVMをアンインストール後、~/zshrcなどに書いていたRVM関連のスクリプトを削除してください。

$ rvm seppuku

rbenv と ruby-build のインストール

rbenv本体とRubyをインストールするのに使用するruby-buildをインストールします。 Rubyは手動でコンパイルするという方はrbenvのみどうぞ。

$ brew install rbenv ruby-build
$ echo 'eval "$(rbenv init -)"' >> ~/.zshenv

Bashの方は~/.zshenvの代りに~/.bash_profileに追記してください。
以上です。簡単でしょ。

Ruby 1.9.3-p194 を入れてみる

これでRubyを管理する準備が整いました。
さっそく1.9.3をインストールしてみましょう。 HomeBrewでインストールしたReadlineOpenSSLをリンクするように指定しています。

$ brew install readline openssl
$ brew link readline
$ brew link openssl
$ CONFIGURE_OPTS="--with-readline-dir=/usr/local --with-openssl-dir=/usr/local" rbenv install 1.9.3-p194

以上でインストール完了です。rbenv versionsと入力してちゃんとインストール出来ているか確認してみてください。 私の環境では以下のように表示されました。(他のバージョンもインストールしています)

$ rbenv versions
  1.8.7-p358
  1.9.2-p290
* 1.9.3-p194 (set by /Users/tomohiro/.rbenv/version)
  2.0.0-dev

via: https://gist.github.com/50aa2ed60c2cec3d49d3

Rubyを切り替える

rbenvを使ってRubyを切り替える方法は3つあります。(rbenvがしてくれること参照)

  1. 通常使用するRubyのバージョンを指定する(global)
  2. プロジェクト毎(特定のディレクトリ以下)で使用するRubyのバージョンを指定する(local)
  3. そのシェル内でのみ使用するRubyのバージョンを指定する(shell)

通常使用するRubyのバージョンを指定する(global)

広く使用するRubyのバージョンを指定します。基本的にはここで指定したバージョンのRubyが使用されることになります。

$ rbenv global 1.9.3-p194

ここで指定したバージョンは~/.rbenv/versionファイルに書き込まれて保持されます。 このバージョンは後述する、.rbenv-version$RBENV_VERSIONによって上書きされます。

プロジェクト毎(特定のディレクトリ以下)で使用するRubyのバージョンを指定する(local)

あるディレクトリ以下にいるときのRubyのバージョンを指定するにはrbenv localを使用します。

$ rbenv local 1.9.3-p194

このコマンドを実行したディレクトリに.rbenv-versionというファイルが作成され、そこにバージョン番号が記述されます。 今後、このファイルがあるディレクトリ以下に移動すると、ファイルに記述されたバージョンのRubyが使用されることになります。 また、このバージョンは$RBENV_VERSIONによって上書きされます。

そのシェル内でのみ使用するRubyのバージョンを指定する(shell)

実行中のシェル内でのみ有効なRubyのバージョンを指定するにrbenv shellを使用します。

$ rbenv shell 1.9.3-p194

これによって、RBENV_VERSIONという環境変数がセットされ、global/localどちらの指定も上書きすること出来ます。

設定した環境変数をリセットするには--unsetオプションを使用します。

$ rbenv shell --unset

仕組み

さて、使い方は分かりましたでしょうか。次は仕組みをみていこうと思います。 詳しい解説はsugyanさんのrbenvの切り替えの仕組み…と、他言語での実験を見て頂くとして、ざっくり概要をまとめたいと思います。

HomeBrewでインストールしたrbenvのユーザ側のディレクトリ構成は以下のようになっています。

$ tree -L 1 .rbenv
.rbenv
├── shims
├── version
└── versions

rbenvではRuby関連の実行ファイルを全て~/.rbenv/shims以下のラッパスクリプトを経由して実行します。 ~/.rbenv/shims以下にはインストールしている全てのバージョンのRubyに関連する実行ファイルのラッパスクリプトが配置されています。 (中身は全て同じです)

当然Rubyインタプリタ自体もここに収められており、which rubyの結果は私の環境の場合/Users/tomohiro/.rbenv/shims/rubyとなります。

このラッパスクリプトが実行されると、rbenvはrbenv-whichを使って実際の実行ファイルの場所を探しに行きます。rbenv-which~/.rbenv/versions/#{rbenv-version}/bin以下を探索します。

$ tree -L 2 .rbenv/versions
.rbenv/versions
├── 1.8.7-p358
│   ├── bin
│   ├── lib
│   └── share
├── 1.9.2-p290
│   ├── bin
│   ├── include
│   ├── lib
│   └── share
├── 1.9.3-p194
│   ├── bin
│   ├── include
│   ├── lib
│   └── share
└── 2.0.0-dev
    ├── bin
    ├── include
    ├── lib
    └── share

ここで言うrbenv-versionは、rbenv versionの実態であり現在使用しているRubyのバージョンを返してくれます。 つまり、1.9.3-p194を使用しているときは~/.rbenv/versions/1.9.3-p194/bin/rubyが実行されて、2.0.0-devを使用しているときは~/.rbenv/versions/2.0.0-dev/bin/rubyが使用される仕組みです。 単純ですよね。

これはインストールしているGemの実行ファイルについても同様で、たとえばBundlerなら~/.rbenv/versions/#{rvenv-version}/bin/bundlerが実行されます。

これはインストールのときにした、rbenv init -によって、$PATH~/.rbenv/shimsが追加されているため実現できています。

つまり

思い切りはしょってまとめると以下のようになります。

  1. rubyを実行すると~/.rbenv/shims/rubyが実行される
  2. ~/.rbenv/shims/rubyは実際の実行ファイルを探して実行しようとする
  3. 実際のファイルは~/.rbenv/versions/#{rbenv-version}/binに配置されていることが分っているためrbenv-versionを決定する
  4. 実行ファイルが見つかったので実行する

rvenv-versionは3ステップでバージョンを確定します。

  1. $RBENV_VERSIONが設定されているか確認する
  2. 設定されていなければ、.rbenv-versionが存在しているか確認する
  3. 存在していなければ~/.rbenv/versionを見る

Shimは誰が作るか

大変重要な役割をしているShimたちは一体誰が作るのでしょうか。答はrbenv-rehashです。 そう、あの毎回実行しなくてはならなくて面倒くさいrbenv rehashの実体です。
これを実行すると、~/.rbenv/versions/*bin/以下を探索して~/.rbenv/shims/以下のShimを再生成してくれます。 とてもめんどうくさいのですがこれを怠るとインストールしたべんりツールが実行できなくなってしまうのです。
(理由を知ってもそれでも面倒だと思うかたはgem i rbenv-rehashしておくとしあわせになれるようです)

ちなみに今まで上げてきたrbenv-*というスクリプトはHomeBrewでインストールした場合は/usr/local/Cellar/rbenv/#{version}/libexec以下にインストールされています。

$ ls /usr/local/Cellar/rbenv/0.3.0/libexec
rbenv                     rbenv-exec                rbenv-hooks               rbenv-prefix              rbenv-sh-shell            rbenv-version-file        rbenv-version-name        rbenv-whence
rbenv-commands            rbenv-global              rbenv-init                rbenv-rehash              rbenv-shims               rbenv-version-file-read   rbenv-version-origin      rbenv-which
rbenv-completions         rbenv-help                rbenv-local               rbenv-root                rbenv-version             rbenv-version-file-write  rbenv-versions

ちょっと便利にする

現在のRubyのバージョンはrbenv versionで確認することが出来ますが、私みたいに面倒くさがりな方にはシェルのプロンプトに現在のバージョンを表示しておくことをお勧めします。
Zshの場合は以下のようにして実現します。

まずはバージョンを文字列として返す関数を定義します。

function rbenv_version() {
  echo -n "${$(rbenv version)%% *}"
}

あとはこれを良い感じにプロンプトに表示します。例えば以下のようにすると1.9.3-p194 >のようなプロンプトを表示できます。

PROMPT='$(rbenv_version) >'

さいごに

rbenvの欠点はrvmに比べてタイプ数が多く左側に集中しているため非常に入力が困難な点です。 一時期はあまりの苦行に耐え切れずにalias rvm=rbenvなどとしていましたが、さすがに気がひけたので最近は入力できるように訓練しています。
貴重なネームスペースを消費しないためにキラキラネームが推奨されているとはいえ、入力しやすい名前をつけるのは重要なことだと思いました。

最後まで読んで頂きありがとうございました。

追記

rb<tab>で補完すれば良いのでは???」というご意見を頂きました。
たしかにと思って確認したら、rb<tab>だとrb[space]に補完されました。そういえばこれのせいでrbenvとフルで入力することになっていたのでした。
このrbって一体なんだと思って調べると、何にことはない、自分で定義した使ってないaliasでした。
ということ、rbをaliasから削除して、快適に補完できるようにしました。


一緒に働きたい方、絶賛 募集中
京都で開発してみたいというエンジニアの皆さん、ご応募お待ちしています!
技術力を伸ばしたい学生さん、アルバイトも可能なのでご応募お待ちしています!
大阪、滋賀、神戸から通勤実績あり

0 件のコメント:

コメントを投稿