そうだ、Rubyをやろう(8日目)

Bundlerというものがあるらしい

今回の記事は、Mac OS X上で、rbenvとBundlerを使って、システム全体への依存性を極力排除した上で、独立した環境で動作するRuby + SQLiteの簡単なアプリを作ってみる、という主旨です。rbenvのインストールは、前回の記事で実施済みです。

OS X 10.9.2, Ruby 2.1.1, SQLite3 1.3.9

プログラムを書いていると、「ちょっとしたこと」を枯れた共通のライブラリを流用して解決したい、なんてことはよくある。例えば、ZIPファイルを解凍して中身を取り出したいだとか、XMLをパースしたいだとか、Base64エンコーディングを使いたいだとか。中には、Rubyの標準APIとして用意されている機能もあるだろう。たとえ標準APIに用意されていなくても、ある程度一般的な機能であれば大抵、どこかの誰かがライブラリとして用意、配布してくれていたりする。

それは、Rubygemsというパッケージ管理システムでインストールすることができ、Rubygemsでインストールするライブラリのプログラムはgemと呼ばれるそうな。

要は、gemを使えば、簡単な手続き(インストールとrequire)で、OSSの恩恵にあずかることができるということだ。
※言うまでもないが、使用する各gemのライセンスは確認しよう。

そうして私は、自分の作成するプログラムで必要な機能のいくつかを、様々なgemをインストールすることでカバーしてきた。もはや、どのプログラムがどのgemのどのバージョンを使用しているのかすらわからなくなってしまった。


前置きが長くなったが、アプリケーションが必要とするgemを、アプリケーション単位で管理できるのがBundlerというものである。


【参考サイト】
・記事中段あたりにBundlerとはなにかの解説あり、わかりやすい
http://d.hatena.ne.jp/kanonji/20120704/1341378341

・Bundler全般の解説
http://shokai.org/blog/archives/7262
http://qiita.com/sumyapp/items/6843ceebbbfc09a2b6ac


・Bundlerをインストールする

$ rbenv exec gem install bundler
$ rbenv rehash

・Bundlerの使い方
Bundlerを使うようになると、Rubygemsを直接使うことはほとんどなくなる。

たとえば、これからSampleAppというRubyアプリケーションを開発するとしよう。
プロジェクトの基準ディレクトリを、

~/Documents/SampleApp

としてみる。

たとえば、このプロジェクトでSQLiteを使いたいとする。

Bundlerを使えば、他のプロジェクトに影響することなく、このアプリで使いたい特定のバージョンのSQLite gemを指定することができる。

そのためには、Gemfileというテキストファイルを作成する。

$ touch ~/Documents/SampleApp/Gemfile
$ vim ~/Documents/SampleApp/Gemfile

# 以下2行を追加
source 'https://rubygems.org'
gem 'sqlite3'

以下のコマンドで指定したgemをローカルにインストールする。
このコマンドは、Gemfileが存在するディレクトリ内で実行すること。

$ bundle install --path vendor/bundle

これでSQLiteの最新版がインストールできた。Gemfileでgemのバージョンを指定しなければ最新版がインストールされる。上記のコマンドを実行すると、gemは、~/Documents/SampleApp/vendor/bundle以下にインストールされるので、他のアプリに影響することはない。

gem 'sqlite3', "~>1.3"

Gemfile内のgemの定義を上記のように記述した場合は、1.3以降、2.0以前のバージョンがインストールされる。

gem 'sqlite3', "~>1.3.5"

Gemfile内のgemの定義を上記のように記述した場合は、1.3.5以降、1.4.0以前のバージョンがインストールされる。



では、試しにSQLiteを使ってみる。本当に動くだろうか。

$ touch ~/Documents/SampleApp/test.rb
$ vim ~/Documents/SampleApp/test.rb

【動作確認用の簡単なRuby+SQLiteコード】

require 'rubygems'
require 'sqlite3'

db = SQLite3::Database.new("test.db")

sql = <<SQL
create table users (
    id integer,
    name varchar(10),
    email varchar(128)
);
SQL

db.execute(sql);

db.execute("insert into users values(1, 'pizza1', 'pizza1@hotml.co.jp')");
db.execute("insert into users values(2, 'pizza2', 'pizza2@gml.com')");
db.execute("insert into users values(3, 'pizza3', 'pizza3@yah.co.jp')");

db.execute("select * from users") do |row|
    puts row.join("\t");
end

db.close

【実行】

$ bundle exec ruby test.rb
1	pizza1	pizza1@hotml.co.jp
2	pizza2	pizza2@gml.com
3	pizza3	pizza3@yah.co.jp

できた・・・(感動)

実行したカレントディレクトリに、ちゃんとtest.dbファイルができているし、SQLも実行できている。

korehasugoi!!

何より重要なのは、このSampleAppの完成後、他のマシン上でこのアプリを実行する際に、Gemfileを含めてリリースするということだ。そうすることによって、異なった環境でも、もう一度bundle installしてやることで、必要なgemを一括インストールできる。無論、rbenvやBundlerの環境はインストール機に必要になるが、アプリに必要なgemとそのバージョンを1つ1つ確認して手作業で環境構築することに比べれば、Bundlerによるgem管理の威力は大きい。そのアプリが必要とするもの「だけ」、かつ「必要なバージョンを指定できる」、そしてそのアプリの動作環境は「他のアプリに影響しない」という点が主な利点であろう。多数のRubyアプリを1台のマシンにインストールするときには、尚更である。



まあ、実際、Bundlerが本格的に役立つのは、どうも、Railsを使うようになってからのような気はする。


ということで、次回はいよいよRuby on Railsの環境構築に挑戦してみようと思う。


それでは。
楽しい時間でした!
またの機会を!

つづく