rbenvは何をしているのか?

~/.rbenv/以下に複数のRubyバージョンをインストールして、それぞれのRuby環境を切り替える仕組みを提供する。

  • ~/.rbenv/ (rbenvがRubyを管理するルートフォルダ)
  • ~/.rbenv/shims/rubyやgemがインストールするコマンドへのラッパーを保存しておくフォルダ)
  • ~/.rbenv/version (global環境のRubyバージョンを記録するファイル)
  • ~/.rbenv/versions/Rubyの各種バージョンをインストールするフォルダ)

Ruby環境の構造

  • 例えば、rbenvがインストールしたRuby-1.8.7-p375は、以下のような構造となっている。
~/.rbenv/versions/1.8.7-p375/bin/(実行コマンドを含む:erb  gem  irb  rake  rdoc  ri  ruby  testrb)
~/.rbenv/versions/1.8.7-p375/lib/(各種ライブラリを含む:1.8/  gems/  site_ruby/  vendor_ruby/)
                                /1.8/        (標準添付ライブラリ)
                                /gems/       (gemがインストールしたライブラリやコマンド)
                                /site_ruby/  (gem以外がインストールしたもの、ユーザー環境に依存するライブラリやコマンド)
                                /vendor_ruby/(gem以外がインストールしたもの、ユーザー環境に依存せず使えるライブラリやコマンドかな?)
~/.rbenv/versions/1.8.7-p375/share/(マニュアルを含む)
  • OSX標準のRubyも似たような構造。
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/include/
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/share/
  • HomebrewがインストールしたRubyも似たような構造。
/usr/local/Cellar/ruby/1.9.3-p194/bin/
/usr/local/Cellar/ruby/1.9.3-p194/include/
/usr/local/Cellar/ruby/1.9.3-p194/lib/
/usr/local/Cellar/ruby/1.9.3-p194/share/
  • Rubyは上記のような定義されたフォルダ構造を持っている。
  • そのフォルダ構造の中に、標準ライブラリやgemで追加インストールしたライブラリ、実行コマンドなどがすべて保持されている。
  • RubyはOS内部に散らばらず、バージョンごとに一つのルートフォルダにまとめられる。
    • ~/.rbenv/versions/1.8.7-p375/(rbenvが管理するRuby
    • /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/(OSX標準のRuby
    • /usr/local/Cellar/ruby/1.9.3-p194/(Homebrewが管理するRuby
  • それぞれのRuby環境が、上記フォルダの中に保存されているのだ。

rbenvのインストール

$ brew install rbenv-default-gems rbenv-gem-rehash
  • 依存関係によって、rbenv、ruby-buld、rbenv-default-gems、rbenv-gem-rehashがインストールされる。

一連の流れ

# 初期化する
$ eval "$(rbenv init -)"


# 必要なRubyバージョンをインストールする
$ rbenv install -l
$ rbenv install 1.8.7-p375


# Rubyバージョンリストを確認する
$ rbenv versions
* system (set by /Users/zari/.rbenv/version)
  1.8.7-p375


# Rubyバージョンを切り替える
$ rbenv global 1.8.7-p375


# 現在のバージョンを表示する
$ rbenv version
1.8.7-p375 (set by /Users/zari/.rbenv/version)


# リハッシュする
$ rbenv rehash


# 使う
$ ruby -v
ruby 1.8.7 (2013-12-22 patchlevel 375) [i686-darwin13.4.0]
初期化する
$ eval "$(rbenv init -)"
  • rbenvを使い始める準備をする。
  • ターミナルでタブやウィンドウを開くごとに、最初に必ず実行する必要がある。
    • つまり、シェルごとに必要。
  • よって、シェルの設定ファイルに追記しておくのだ。
  • 自分の場合、bashを使っているので~/.bashrcに追記した。
$ echo "$(rbenv init -)"
export PATH="/Users/zari/.rbenv/shims:${PATH}"
source "/usr/local/Cellar/rbenv/0.4.0/libexec/../completions/rbenv.bash"
rbenv rehash 2>/dev/null
rbenv() {
  typeset command
  command="$1"
  if [ "$#" -gt 0 ]; then
    shift
  fi

  case "$command" in
  rehash|shell)
    eval `rbenv "sh-$command" "$@"`;;
  *)
    command rbenv "$command" "$@";;
  esac
}
  • 実際にはechoの部分はevalなので、上記コードが実行されるということ。
    • コマンドサーチパスの先頭に、~/.rbenv/shims/を追加して、
    • /usr/local/Cellar/rbenv/0.4.0/completions/rbenv.bashを評価して、
    • rbenv rehashして、
    • rbenv()関数を定義している。
  • ややや、completions/rbenv.bashって、rbenvには補完機能があったのか!
      • ちなみに、/usr/local/Cellar/rbenv/0.4.0/completions/rbenv.bashbash用の補完機能ファイルなのだけど、
      • 以下のように指定すれば、zsh用の補完機能ファイルも指定できる。
$ echo "$(rbenv init - zsh)"
インストールする
  • そうそう、rbenvには補完機能があるのだから、さっそく試してみる。
$ rbenv install [tab][tab]
Display all 123 possibilities? (y or n)
1.8.6-p383            1.9.3-dev             2.0.0-p353            jruby-1.6.5           jruby-1.7.4           rbx-2.2.10
1.8.6-p420            1.9.3-p0              2.0.0-p451            jruby-1.6.5.1         jruby-1.7.5           rbx-2.2.2
1.8.7-p249            1.9.3-p125            2.0.0-p481            jruby-1.6.6           jruby-1.7.6           rbx-2.2.3
1.8.7-p302            1.9.3-p194            2.0.0-p576            jruby-1.6.7           jruby-1.7.7           rbx-2.2.4
1.8.7-p334            1.9.3-p286            2.0.0-preview1        jruby-1.6.7.2         jruby-1.7.8           rbx-2.2.5
1.8.7-p352            1.9.3-p327            2.0.0-preview2        jruby-1.6.8           jruby-1.7.9           rbx-2.2.6
1.8.7-p357            1.9.3-p362            2.0.0-rc1             jruby-1.7.0           jruby-9000+graal-dev  rbx-2.2.7
1.8.7-p358            1.9.3-p374            2.0.0-rc2             jruby-1.7.0-preview1  jruby-9000-dev        rbx-2.2.9
1.8.7-p370            1.9.3-p385            2.1.0                 jruby-1.7.0-preview2  maglev-1.0.0          ree-1.8.6-2009.06
1.8.7-p371            1.9.3-p392            2.1.0-dev             jruby-1.7.0-rc1       maglev-1.1.0-dev      ree-1.8.7-2009.09
1.8.7-p374            1.9.3-p429            2.1.0-preview1        jruby-1.7.0-rc2       maglev-2.0.0-dev      ree-1.8.7-2009.10
1.8.7-p375            1.9.3-p448            2.1.0-preview2        jruby-1.7.1           mruby-1.0.0           ree-1.8.7-2010.01
1.9.1-p378            1.9.3-p484            2.1.0-rc1             jruby-1.7.10          mruby-dev             ree-1.8.7-2010.02
1.9.1-p430            1.9.3-p545            2.1.1                 jruby-1.7.11          rbx-1.2.4             ree-1.8.7-2011.03
1.9.2-p0              1.9.3-p547            2.1.2                 jruby-1.7.12          rbx-2.0.0             ree-1.8.7-2011.12
1.9.2-p180            1.9.3-preview1        2.1.3                 jruby-1.7.13          rbx-2.0.0-dev         ree-1.8.7-2012.01
1.9.2-p290            1.9.3-rc1             2.2.0-dev             jruby-1.7.14          rbx-2.0.0-rc1         ree-1.8.7-2012.02
1.9.2-p318            2.0.0-dev             2.2.0-preview1        jruby-1.7.15          rbx-2.1.0             topaz-dev
1.9.2-p320            2.0.0-p0              jruby-1.5.6           jruby-1.7.16          rbx-2.1.1             
1.9.2-p326            2.0.0-p195            jruby-1.6.3           jruby-1.7.2           rbx-2.2.0             
1.9.2-p330            2.0.0-p247            jruby-1.6.4           jruby-1.7.3           rbx-2.2.1             

$ rbenv install 1.8.7-p375
  • 素晴らしい!これでもう、事前にrbenv install -lなんてやるのは不要になる。
  • ~/.rbenv/versions/1.8.7-p375/Ruby-1.8.7-p375がインストールされた。
切り替える
  • 同様に、もはや事前のrbenv versionsは不要。補完機能で選択できる。
$ rbenv global [tab][tab]
1.8.7-p375  system

$ rbenv global 1.8.7-p375

$ rbenv version
1.8.7-p375 (set by /Users/zari/.rbenv/version)
  • global環境のRubyを1.8.7-p375に切り替えるとは、~/.rbenv/versionに"1.8.7-p375"を書き込むことである。
$ cat ~/.rbenv/version
1.8.7-p375
  • 初期化で実行したeval "$(rbenv init -)"によって、コマンドサーチパスは~/.rbenv/shims/から優先して検索する設定になっている。
$ echo $PATH
/Users/zari/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/git/bin:/Applications/Xcode.app/Contents/Developer/usr/bin
  • ~/.rbenv/shimsには、RubyやGemなどがインストールしたコマンドが保存されている。
$ ls ~/.rbenv/shims
bundle*  bundler* erb*     gem*     irb*     rails*   rake*    rdoc*    ri*      ruby*    testrb*
$ cat ~/.rbenv/shims/ruby
#!/usr/bin/env bash
set -e
[ -n "$RBENV_DEBUG" ] && set -x

program="${0##*/}"
if [ "$program" = "ruby" ]; then
  for arg; do
    case "$arg" in
    -e* | -- ) break ;;
    */* )
      if [ -f "$arg" ]; then
        export RBENV_DIR="${arg%/*}"
        break
      fi
      ;;
    esac
  done
fi

export RBENV_ROOT="/Users/zari/.rbenv"
exec "/usr/local/Cellar/rbenv/0.4.0/libexec/rbenv" exec "$program" "$@"
  • そして、~/.rbenv/shims/内のコマンド群は、名前は違ってもその中身はすべて同じ。そこには上記と同じコードが書かれているのだ。
    • 例えば、rubyrailsの中身に差はない。
$ diff ~/.rbenv/shims/ruby ~/.rbenv/shims/rails
  • いろいろやっているように見えるけど、思いっきり要約すると...
    • 必要に応じてRBENV_DIRと、RBENV_ROOTを設定してから、
    • exec rbenv exec "$program" "$@"を呼び出している。
  • rbenvでは、最終的にrbenv-exec "$program" "$@"というコマンドが実行される。
  • rbenv-execでは、~/.rbenv/versionに書かれたバージョンのディレクトリをPATH変数に追加する。
    • 例えば~/.rbenv/versionに"1.8.7-p375"と書かれていたなら、~/.rbenv/versions/1.8.7-p375/binが追加される。
    • よって、この環境でrubyを実行した時は、~/.rbenv/versions/1.8.7-p375/bin/rubyが実行されることになるのだ。

~/.rbenv/shimsにあるラッパー スクリプトが、rbenvのバージョン情報を元に、Ruby環境を選択していたのだ!

リハッシュする
$ rbenv rehash
  • そうすると重要なのが、それぞれのRuby環境にある実行可能なコマンドを、漏れなく~/.rbenv/shimsにラッパースクリプトとして登録しておくこと。
  • 実行可能なコマンドは、gemなどで追加される可能性がある。油断はできない。
  • その役割を担っているのがrbenv rehashコマンドだった。
  • Ruby環境を切り替えたら、必ずrbenv rehashを実行するのはこのため。
  • でも、rbenv-gem-rehashをインストール済みなら、良きに計らいrbenv rehashは自動で実行される。

素晴らしい!自分はインストール済みなので、もう忘れても大丈夫。

Rubyバージョンを指定する4つの方法
  • 実はrbenvには、Rubyバージョンを指定する方法が4つある。
rbenv global
ログインユーザー環境全体のRubyバージョンを設定する
$ rbenv global 1.8.7-p375
  • これまでの方法。
  • ~/.rbenv/versionファイルにRubyバージョンを保存する。
rbenv local
フォルダごとにRubyバージョンを設定する
$ cd ~/Desktop/rails/todo
$ rbenv local 1.8.7-p375
  • rbenv localを実行した時のフォルダに.ruby-versionファイルを作成して、Rubyバージョンを保存する。
  • 例えば、上記のようにコマンドを実行すると、
  • ~/Desktop/rails/todo/.ruby-versionに"1.8.7-p375"が保存され、
  • ~/Desktop/rails/todo/以下のフォルダでは、バージョン1.8.7-p375のRuby環境が選択される。
  • globalの設定よりも、localの設定が優先される。
rbenv shell
現在のシェル環境にRubyバージョンを設定する
$ rbenv shell 1.8.7-p375
  • 現在のシェル環境では、常にRuby-1.8.7-p375環境で実行する。
  • global・localの設定よりも、shellの設定が優先される。
  • ちなみに、shell設定を解除するには--unsetオプションを指定する。
$ rbenv shell --unset
RBENV_VERSION
コマンド実行ごとにRubyバージョンを指定する
$ RBENV_VERSION=1.8.7-p375 ruby -v
  • 環境変数RBENV_VERSIONにRubyのバージョンを設定して、コマンドを実行する。
  • global・local・sytemの設定よりも、RBENV_VERSIONの設定が優先される。
      • ちなみに、rbenv shell 1.8.7-p375とは、export RBENV_VERSION=1.8.7-p375とほぼ同等なのだと思う。
使う
$ ruby -v
ruby 1.8.7 (2013-12-22 patchlevel 375) [i686-darwin13.4.0]

以上のようなbashスクリプトの壮大な連携によって、Rubyのバージョンが選択されていたのだ。

  • rbenvのほとんどすべては、bashスクリプトで出来ている。
  • 複雑なことはせず、シンプルに、シェルの機能を上手に活用している。
  • 枯れた技術で、Ruby環境を素早く切り替える技に感動した!