libv8 のバージョンを探す

2013年の Ruby on Rails Advent Calendar に参加しようと思ったら、その日のぶんが空いていたので、当日の 23:45 からプログラムを書いてそのまま記事にするというエクストリームアドベントをすることとなりました。プログラマ的には 33 時までは当日だから問題ないよね。

さて今回は Ruby on Rails というか therubyracer というか libv8 の話です。

Ruby on Rails では therubyracer gem をちょくちょく使いますね。therubyracer gem は Ruby から使用できる v8 エンジンで、環境ごとに適切なバイナリパッケージがインストールされるのですが、時折やたらと時間がかかることがあります。*1
この適切なバージョンを見つけ出すためのスクリプトを作成しました。

これを走らせると、

 $ ruby libv8_check.rb
gem 'libv8', '3.16.14.3'

と、Gemfile に書くべき行を表示してくれます。

何が表示されているか?

libv8 gem には、特定の環境用にビルドされたバイナリ gem と汎用の gem とがあります。この汎用gemをインストールすると非常に時間がかかることになります。
したがって、汎用 gem をインストールするのは避けたいのですが、バイナリパッケージが存在するかどうかの確認は案外手間がかかり、「誰でもできる」とは言い難い状況でした。
ということで、その確認を簡単にするためににこのスクリプトを作成した、ということになります。

このスクリプトは、「存在している libv8 gem の中でもっとも新しい、ユーザ環境で使用できるバイナリ gem のバージョンを表示する」ということをしています。つまり Linux x86_64 環境であればその環境用のバイナリ gem を、また MacOS X 用であればそれ用のバイナリ gem を確認して出力します。
ただ、バイナリパッケージのない環境も存在します。そういう環境で実行した場合は、下のように ~> を使用したバージョンを表示します。

gem 'libv8', '~> 3.16.14.3'

やっていること

環境の選択

bundle install の際、インストールするべきバージョンを選択しているのは bundler ですが、環境やバイナリパッケージを選択しているのは rubygems です。
rubygems の中では Gem::Platform クラスがこのあたりをコントロールしていますので、少しだけこれを借りています。

rubygems API v1 の利用

はじめは Gem クラスを利用して完全なバージョンを取得しようと思ったのですが、「特定の gem の全バージョンを取得する」ということをできるメソッドが見つからず。
Rubygems API を直接利用することにしたところ、これが非常に簡単!

$ curl http://rubygems.org/api/v1/versions/libv8.yaml
---
- authors: Charles Lowell
  built_at: '2013-12-05T00:00:00Z'
  description: Distributes the V8 JavaScript engine in binary and source forms in
    order to support fast builds of The Ruby Racer
  downloads_count: 79
  number: 3.16.14.3
  summary: Distribution of the V8 JavaScript engine
  platform: x86_64-solaris-2.11
  prerelease: false
  licenses:
  - MIT
  requirements: []
- authors: Charles Lowell
  built_at: '2013-12-04T00:00:00Z'
  description: Distributes the V8 JavaScript engine in binary and source forms in
    order to support fast builds of The Ruby Racer
  downloads_count: 96
...

API については http://github.com/rubygems/rubygems.org を git clone して rake routes とか適当にして確認していましたが、ちゃんと公式ドキュメントが整備されていたようです…。

GET - /api/v1/versions/[GEM NAME].(json|xml|yaml)

Returns an array of gem version details like the below:

$ curl https://rubygems.org/api/v1/versions/coulda.json

[
{
"number" : "0.6.3",
"built_at" : "2010-12-23T05:00:00Z",
"summary" : "Test::Unit-based acceptance testing DSL",
"downloads_count" : 175,
"platform" : "ruby",
"authors" : "Evan David Light",
"description" : "Behaviour Driven Development derived from Cucumber but
as an internal DSL with methods for reuse",
"prerelease" : false,
}
]

http://guides.rubygems.org/rubygems-org-api/#gem_version_methods

Rubygems API は色々と面白く使えそうですね。

おわりに

ということで、 Ruby on Rails を使用するうえで比較的欠かせない libv8 の gem をより短い時間でインストールできるようにするべく、適切なバージョンを探し出してくれるスクリプトをこねこねと作成しました。

今回ざっくり実装したこの機能ですが、あと若干汎用化すると「バイナリパッケージが準備された gem かどうかを判定する」機能となるので、そこまで作ってから gem として公開しようかなあなどともくろんでいます。さすがに今日中は無理でした。

以上、2013年 の Ruby on Rails Calendar 9 日目の記事でした。
…あれ?この記事 Rails 関係あったと言えるのかな? …まあいいか!

*1:このへんの詳細については以前書いた http://d.hatena.ne.jp/suu-g/20121222/1356189597 参照のこと