不格好エンジニア

wordpress.comから引っ越しました。

rbenvを流し読み

注)誤ったことを書いているかもしれません。

動機

rubyで仕事してると職場でも頻繁に使うものなので、中身を理解したくなった。

version

1.1.1

ドキュメントを読んでみる

github.com

冒頭の重要そうなところをピックアップしてみます

No headaches running apps on different versions of Ruby. Just Works™ from the command line and with app servers like Pow. Override the Ruby version anytime: just set an environment variable.

異なるruby versionで動くアプリに頭痛で悩まされることがない。Powのようにコマンドラインで動かすだけ。いつでもRuby versionを上書きできる。環境変数でsetするだけだ。

Rock-solid in production. Your application's executables are its interface with ops. With rbenv and Bundler binstubs you'll never again need to cd in a cron job or Chef recipe to ensure you've selected the right runtime. The Ruby version dependency lives in one place—your app—so upgrades and rollbacks are atomic, even when you switch versions.

productionで頑丈だ。rbenvとBundler binstubsにより、cron jobの中でcdしたり、chep recipeで正しいruntimeを選択していることを保証する必要はない。Ruby versionの依存はただ一箇所、あなたのアプリに存在しているので、upgradesやrollbackはatomicはversionを切り替えるときでもatomicに行える。

One thing well. rbenv is concerned solely with switching Ruby versions. It's simple and predictable. A rich plugin ecosystem lets you tailor it to suit your needs. Compile your own Ruby versions, or use the ruby-build plugin to automate the process. Specify per-application environment variables with rbenv-vars. See more plugins on the wiki.

ecosystemがしっかりしている、ということが書いてある。

ざっくり眺める

.
├── bin
├── completions
├── libexec
├── rbenv.d
│    └── exec
│         └── gem-rehash
├── src
└── test
    └── libexec
  • bin/ メインのロジックはここに入ってるようです。
  • libexec rbenv localなどに対応するファイル群。

bin/rbenv

  command_path="$(command -v "rbenv-$command" || true)"
  if [ -z "$command_path" ]; then
    if [ "$command" == "shell" ]; then
      abort "shell integration not enabled. Run \`rbenv init' for instructions."
    else
      abort "no such command \`$command'"
    fi
  fi

rbenv-local, rbenv-init などの呼び出しはここで行っているようです。メインのロジックがここに入っているようですね。

rbenv initを実行したときに内部で起きていることを理解すれば、大体あとはわかる気がしたので、そのあたりを中心に追ってみると良さそうです。

いきなり動作を把握するのは難しそうなので、rbenv-initのテストを読みにいきます。

@test "adds shims to PATH" {
  export PATH="${BATS_TEST_DIRNAME}/../libexec:/usr/bin:/bin:/usr/local/bin"
  run rbenv-init - bash
  assert_success
  assert_line 0 'export PATH="'${RBENV_ROOT}'/shims:${PATH}"'
}

rbenv initでshimsをPATHに追加します。

@test "outputs sh-compatible syntax" {
  run rbenv-init - bash
  assert_success
  assert_line '  case "$command" in'

  run rbenv-init - zsh
  assert_success
  assert_line '  case "$command" in'
}

rbenv initでshellに互換性のある文字列を出力します。

@test "creates shims and versions directories" {
  assert [ ! -d "${RBENV_ROOT}/shims" ]
  assert [ ! -d "${RBENV_ROOT}/versions" ]
  run rbenv-init -
  assert_success
  assert [ -d "${RBENV_ROOT}/shims" ]
  assert [ -d "${RBENV_ROOT}/versions" ]
}

rbenv initでshimsとversionsディレクトリを作成します。

rbenv-init

{ echo "# Load rbenv automatically by appending"
    echo "# the following to ${profile}:"
    echo
    case "$shell" in
    fish )
      echo 'status --is-interactive; and source (rbenv init -|psub)'
      ;;
    * )
      echo 'eval "$(rbenv init -)"'
      ;;
    esac
    echo
  } >&2

表示される指示をみると、.bashrc, .bash_profile なりに eval "$(rbenv init -)" を書いておけ、とあります。 つまり、rbenv init - の結果が文字列(シェルスクリプト)として返されるので、これをevalすることによって、PATHを通したりしてるみたいですね。

参考

RubyGemコードリーディングのすすめ

rbenv + ruby-build はどうやって動いているのか - takatoshiono's blog

rbenvコードリーディング

rbenvの仕組み | Web Memorandum

次に読みたい

Ruby

wicked_pdf, request_store, bullet, wkhtmltopdf, sidekiq, active_support, webrick, draper, jbuilder, rails/active_job, rails/spring, capistrano, omniauth, committee etc..

iOS

RxSwift, AFNetworkinig, SDWebImage, Alamofire, MBProgressHUD, Masony, SwiftyJSON, MJRefresh, CocoaLumberjack, Realm, SsnapKit, Kingfisher