Bundlerを使ったRuby環境構築

現在,Rubyでの開発はもっぱらMacで行っております.Mac OSX 10.6でのRubyのバージョンは,1.8.7ですので,これまでRVMを使って,1.9系環境を構築して利用しておりました.

しかし,私の場合,Rubyはもうバージョン1.9系しか使っていませんので,RVMを利用する意味が無くなりました.どちらかというとgemとのバージョンの整合性が問題であって,それはBundlerを利用することで何とかなりそうです.

インタプリタ言語の場合,PHPもPerlもそうですが,拡張モジュールをシステムにインストールして,それを複数のアプリケーションで利用するというのは,HDDの資源も無駄にならず,理にかなった方法なのですが,例えば,Aというシステム環境でアプリケーションを開発していて,そのアプリケーションを別のBというシステム環境で動かしたくなった場合,BのシステムをAのシステムと同様の環境にしなければなりません.Rubyの場合もバージョンがどんどん上がっていくgemもあり,バージョンの整合性で上手く動かないシステムも出てきました.

そこで,Bundlerを利用することで,アプリケーション単位でgemを管理する手法に切り換えることにしました.即ち,システム側にはgemはインストールせず,アプリケーションディレクトリにインストールするという方法です.この方法だとファイルの冗長性が増すので,サイズも無駄にとることになりますが,システムに依存しませんので,アプリケーション内でのgemのバージョンの整合性の問題は無くなります.

ということで,RVMでの利用をやめました.以下にその検証結果を示します.

まずは,RVMのアンインストールから...RVMのアンインストールは「rvm seppuku」...(切腹!)だそうです...(・∀・)ナゼ?に切腹?という話は置いといて進めて行きます.

$ rvm seppuku
WARN: Are you SURE you wish for rvm to implode?
This will recursively remove /Users/hoge/.rvm and other rvm traces?
(type 'yes' or 'no')> yes ←【"yes"と答える】
Removing rvm-shipped binaries (rvm-prompt, rvm, rvm-sudom rvm-shell and
rvm-auto-ruby)
Removing rvm wrappers in /Users/hoge/.rvm/bin
Hai! Removing /Users/hoge/.rvm
/Users/hoge/.rvm has been removed.
rvm has been fully removed. Note you may need to manually remove /etc/rv
mrc and ~/.rvmrc if they exist still.

完全に削除するには,「~/.rvmrc」も削除とありますので,これを削除し,それと,「.bashrc」や「.bash_profile」に書いてあるであろう rvm スクリプトのロードの部分を削除しておきます.RVMはシェル起動時にコマンドをロードします

ターミナルを起動し直して現在のRuby環境を確認します.

$ which ruby
/usr/bin/ruby
$ which gem
/usr/bin/gem
$ gem env
RubyGems Environment:
  - RUBYGEMS VERSION: 1.3.5
  - RUBY VERSION: 1.8.7 (2010-01-10 patchlevel 249) [universal-darwin10.0]
  - INSTALLATION DIRECTORY: /Library/Ruby/Gems/1.8
  - RUBY EXECUTABLE: /System/Library/Frameworks/Ruby.framework/Versions/1.
8/usr/bin/ruby
  - EXECUTABLE DIRECTORY: /usr/bin
  - RUBYGEMS PLATFORMS:
    - ruby
    - universal-darwin-10
  - GEM PATHS:
     - /Library/Ruby/Gems/1.8
     - /Users/hoge/.gem/ruby/1.8
     - /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/rub
y/gems/1.8
  - GEM CONFIGURATION:
     - :update_sources => true
     - :verbose => true
     - :benchmark => false
     - :backtrace => false
     - :bulk_threshold => 1000
  - REMOTE SOURCES:
     - http://gems.rubyforge.org/

MacデフォルトのRuby環境に戻ったようです.

それでは,portでRuby Ver.1.9系を入れます.サーチしてみると,バージョン 1.9.2 パッチレベル290です.

$ port search ruby19
ruby19 @1.9.2-p290 (lang, ruby)
    Powerful and clean object-oriented scripting language

デフォルトでインストールしてしまうと「ruby1.9」なんていう,コマンドに余計な「1.9」が付いてしまうので,オプション「+nosuffix」を付加してインストール.

$ sudo port install ruby19 +nosuffix

インストール後,環境を確認します.(追記: ターミナルを再起動させます)

$ gem env
RubyGems Environment:
  - RUBYGEMS VERSION: 1.3.7
  - RUBY VERSION: 1.9.2 (2011-07-09 patchlevel 290) [x86_64-darwin10]
  - INSTALLATION DIRECTORY: /opt/local/lib/ruby1.9/gems/1.9.1
  - RUBY EXECUTABLE: /opt/local/bin/ruby
  - EXECUTABLE DIRECTORY: /opt/local/bin
  - RUBYGEMS PLATFORMS:
    - ruby
    - x86_64-darwin-10
  - GEM PATHS:
     - /opt/local/lib/ruby1.9/gems/1.9.1
     - /Users/hoge/.gem/ruby/1.9.1
  - GEM CONFIGURATION:
     - :update_sources => true
     - :verbose => true
     - :benchmark => false
     - :backtrace => false
     - :bulk_threshold => 1000
  - REMOTE SOURCES:
     - http://rubygems.org/

Ruby環境が/opt/local配下にインストールされ,GEM_PATHも変わっていることを確認します.

RubyGemsのバージョンが1.3.7ということで,ちょっと古いようです.RubyGems自体をアップデートします.

$ sudo gem update --system
Updating RubyGems
Updating rubygems-update
Successfully installed rubygems-update-1.8.11
Updating RubyGems to 1.8.11
Installing RubyGems 1.8.11
RubyGems 1.8.11 installed
 :
RubyGems installed the following executables:
     /opt/local/bin/gem

現在のローカルgemを確認します.

$ gem list
*** LOCAL GEMS ***

minitest (1.6.0)
rake (0.8.7)
rdoc (2.5.8)
rubygems-update (1.8.11)

これらもアップデートする必要がありますが,後で比較のためとりあえずこのままにしておきます.

それでは,大事な「bundler」をインストールします.systemに入れるのはこれだけです.

$ sudo gem install bundler

試しにRailsアプリを作ってみましょう.

当然,rails gemをインストールしていませんので,「rails new なんちゃら」なんてできません.まずは,ディレクトリをつくって,その中で bundle installしてから,railsコマンドを実行するという手順を踏みます.

$ mkdir testpro
$ cd testpro

このディレクトリ内で「Gemfile」だけ手作業で作成します.

$ vi Gemfile

(追記)「bundle init」を実行するとシンプルな「Gemfile」をカレントディレクトリに作成してくれます.

[Gemfile]

source "http://rubygems.org"
gem "rails", "~> 3.0.0"

そこで,ここが重要なのですがインストールパスを指定してbundle installします

$ bundle install --path vendor/bundle
Fetching source index for http://rubygems.org/
Installing rake (0.9.2.2)
 :
Installing rails (3.0.10)
Your bundle is complete! It was installed into ./vendor/bundle

実際にインストールされたかどうか確認してみます.

$ ls vendor/bundle/ruby/1.9.1/gems/
abstract-1.0.0/        erubis-2.6.6/          rails-3.0.10/
actionmailer-3.0.10/   i18n-0.5.0/            railties-3.0.10/
actionpack-3.0.10/     json-1.6.1/            rake-0.9.2.2/
activemodel-3.0.10/    mail-2.2.19/           rdoc-3.11/
activerecord-3.0.10/   mime-types-1.17.2/     thor-0.14.6/
activeresource-3.0.10/ polyglot-0.3.3/        treetop-1.4.10/
activesupport-3.0.10/  rack-1.2.4/            tzinfo-0.3.31/
arel-2.0.10/           rack-mount-0.6.14/
builder-2.1.2/         rack-test-0.5.7/
$ du -sh vendor/bundle
 30M     vendor/bundle

ちゃんとvendor/bundle配下にインストールされているのが分かります.サイズもトータルで30MBくらいです.

では,カレントディレクトリにRailsのスケルトンを作成します.ここで重要なのが,必ず頭に「bundle exec」を付けなければならないということです.

$ bundle exec rails new .
       exist
      create  README
      create  Rakefile
      create  config.ru
      create  .gitignore
    conflict  Gemfile
Overwrite /Users/hoge/testpro/Gemfile? (enter "h" for help) [Ynaqdh] Y
       force  Gemfile
      create  app
      :

途中,Gemfileを上書きするか聞いてきますので,「y」を押して必ず上書きしてください.

では,また改めて,bundle installを実行します.

$ bundle install
Fetching source index for http://rubygems.org/
Using rake (0.9.2.2)
Using abstract (1.0.0)
 :
Using rails (3.0.10)
Installing sqlite3 (1.3.4) with native extensions
Your bundle is complete! It was installed into ./vendor/bundle

「sqlite3」がまだインストールされていなかったので,インストールされているのがわかります.
今回はパスを指定しませんでしたが恐るる事なかれ,一回パスを指定して実行すれば,ちゃんとコンフィグに残っているのです.

$ cat .bundle/config
---
BUNDLE_PATH: vendor/bundle
BUNDLE_DISABLE_SHARED_GEMS: '1'

ここの「BUNDLE_DISABLE_SHARED_GEMS」の値が「1」の時は,システムにインストールしたgemsは使わないという設定になります.つまりシステムのgemには依存しないというか関わりをもたないということになり,アプリケーションの独立性が確保されます.

では,scaffoldを使って適当なCRUDアプリを作って,Webサーバ(WEBrick)を起動してみます.

$ bundle exec rails generate scaffold User name:string
$ bundle exec rake db:migrate
$ bundle exec rails server

全てのコマンドにおいて頭に「bundle exec」を付けます.これが面倒なのですがしょうがありません,面倒な人はエイリアスを設定しておいたら良いと思います.「bundle exec」を付けると付けないとでどうなるか...

付けないと,システム側のgemを指定したことになります.このようにバージョンが異なるので違いが分かると思います.

$ rake --version
rake, version 0.8.7
$ bundle exec rake --version
rake, version 0.9.2.2

システム側のgemもアップデートしておいたほうがいいかもですね.

$ gem update

最後に,Railsとは関係なく一般的な例で見てみます.

UNIXには昔から uname というシステム情報について表示するコマンドがありますが,Rubyにもそのシステムの情報を取得する「sys-uname」というgemが用意されています.これを利用したアプリケーションを作成してみましょう.

まずは,ディレクトリを作ってから,Gemfileを作成し,bundle installを実行するという手順を行います.

$ mkdir uname2
$ cd uname2
$ vi Gemfile

[Gemfile]

source "http://rubygems.org"
gem "sys-uname", "~> 0.8.6"
$ bundle install --path vendor/bundle
Fetching source index for http://rubygems.org/
Installing sys-uname (0.8.6) with native extensions
Using bundler (1.0.21)
Your bundle is complete! It was installed into ./vendor/bundle

現在のgemのリストを見てみます.システムのほうは「gem list」コマンドですが,bundlerで入れたものは「bundle list」で確認します.

$ bundle list
Gems included by the bundle:
  * bundler (1.0.21)
  * sys-uname (0.8.6)

この「sys-uname」の使い方は,「vendor/bundle/ruby/1.9.1/gems/sys-uname-0.8.6/examples/uname_test.rb」にあります.「require ‘sys/uname’」とすれば良いようです.irbで確認してみます.

$ irb
irb(main):001:0> require 'sys/uname'
LoadError: no such file to load -- sys/uname
     from /opt/local/lib/ruby1.9/site_ruby/1.9.1/rubygems/custom_requi
re.rb:36:in `require'
     from /opt/local/lib/ruby1.9/site_ruby/1.9.1/rubygems/custom_requi
re.rb:36:in `require'
     from (irb):1
     from /opt/local/bin/irb:12:in `<main>'

あれれ,上手くいきません.そんなファイルはないと怒られました.これは,bundleでラップせずにirbを実行したからです.頭に「bundle exec」を付けてirbを実行します.

$ bundle exec irb
irb(main):001:0> require 'sys/uname'
=> true

ちゃんと認識してくれました.

では,スクリプトファイルを書いてみます.

[uname2.rb]

require "sys/uname"
include Sys

p Uname.uname

実行するときも...

$ ruby uname2.rb
<internal:lib/rubygems/custom_require>:29:in `require': no such file
to load -- sys/uname (LoadError)
     from <internal:lib/rubygems/custom_require>:29:in `require'
     from uname2.rb:3:in `<main>'

このように個別にgemをbundle installした場合は,単に ruby コマンドだけではダメなようです.ここでもやっぱり「bundle exec」を頭に付けて...

$ bundle exec ruby uname2.rb
#<struct Struct::UnameStruct sysname="Darwin", nodename="bullseye.local"
, machine="i386", version="Darwin Kernel Version 10.8.0: Tue Jun  7 16:3
3:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386", release="10.8.0", mod
el="MacBookAir3,1">

上手くいきました!

検証として,システムレベルではなくアプリケーションレベルでgemを管理でき,システムのgem環境に依存しないgem環境を構築できる点でメリットがあると思いますが,コマンド発行の度に,いちいちbundlerをラップしなければならないのがちょっと不満です.その点では,何も考えずにrailsコマンドが打てるRVMの方が良いのかもしれません.

【追記】native extension なGemの場合(例えば,mysql2,sqlite3など)はシステムに依存する場合があるので,注意が必要かも.

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中