validates_length_of では、:minimum, :maximumなどのオプションによって入力最小/最大文字数を定義することができます。いままでは単純に文字数のカウントを行っているだけでしたが、今回追加された :tokenizer オプションによって、文字のカウント方法を定義できるようになりました。
たとえば下記の場合、3行目で入力文字が何ワードあるかを調べるように定義し、10ワード以下であればエラーになるようになっています。
1
2
3
| validates_length_of :article, :minimum => 10,
:too_short => "Your article must be at least %d words in length.",
:tokenizer => lambda {|str| str.scan(/\w+/) } |
# またまた遅れてごめんなさい。。。
今回の機能は、render :partial で :collection を使った場合に好きな変数名を渡せるものです。
恥ずかしながらいままで collection を知らなかったので、自分のためにもそのおさらいから。
render :partial => 'employees', :collection => @workers
Viewで render partial で外部テンプレートをよく呼び出しまが、collectionはその外部テンプレートをループさせるときに利用するものです。
上記の場合、下の _employees.html.erb が @workersの要素(workerひとりひとり)を引き取って、ぐるぐるとまわります。
1
2
| 名前:<%= woker.name %>
年齢:<%= worker.age %> |
collectionは複数形で渡しておくと、テンプレート先で単数形で自動的に引き取るようです。これだと変数名に制約が出てくるので、今回の機能である、:asで変数名を指定することができます。
render :partial => "employees", :collection => @workers, :as => :person
上記のようにすると、workersの要素ひとつひとつがpersonとしてemployeesテンプレートで展開することができます。
今回の機能、というよりも collection を知って嬉しくなりましたが、表示は遅いようですね。。。
iPhoneのような携帯端末で GPS を使ったアプリを作ってみたかったので、テスト端末と自分に言い聞かせて購入に踏み切りました。買ったはいいのですが、アドレスデータの移行がうまいこといかず、ちょっと手を入れたのでメモしておきます。

携帯からアドレスをインポートしたものの、データがうまくない
携帯(MediaSkin)からアドレスのデータをエクスポートしたところ、vcfフォーマットのデータが生成されました。このファイルにはすこし問題があって、
- 姓名がわかれてなかったり
- フリガナがついてなかったり
します。
実際、姓名はMediaSkinに分けて入れるフィールドがなかったためなのですが、フリガナについては入っているものの、インポート先のアドレスブック(Mac)で認識できませんでした。
手順
手順というほどのことではないのですが、移行は下記の手順で行いました。通常ならば3. は必要ないのかもしれませんが、今回「姓名を分ける」「フリガナをつける」に対応するため、スクリプトを書いています。
- 既存の携帯から miniSD や USBケーブルなどでデータを引き抜く
- 抜いた.vcfファイルを UTF-16 に変換
- 自作スクリプトで姓名を分割+フリガナの追加
- アドレスブック(Mac)へインポート
- iTunes でシンク
.vcfはよくわからなかったのですが、下のような感じで BEGIN:VCARD ~ END:VCARD までで一行ずつアドレスデータを記述していくようです(ぼくの携帯からはCHARSET=SHIFT_JISという設定が入っていますが、これは必要ありませんでした)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| BEGIN:VCARD
VERSION:3.0
N;CHARSET=SHIFT_JIS:姓 名;;;;
FN;CHARSET=SHIFT_JIS:姓 名
SORT-STRING;CHARSET=SHIFT_JIS:セイメイ
X-PHONETIC-FIRST-NAME:
X-PHONETIC-LAST-NAME:
TEL;TYPE=PREF,CELL:080********
TEL;TYPE=VOICE:03********
EMAIL;TYPE=PREF,CELL:sei_mei@hoge-mobile.com
EMAIL;TYPE=PCS:sei_mei@hoge-pc.com
CLASS:PUBLIC
BDAY:********
X-CONSTELLATION:
REV:
X-REDUCTION:
X-GNO:
X-GN;CHARSET=SHIFT_JIS:グループ設定なし
END:VCARD |
姓名をわける
上の3行目「N;CHARSET=SHIFT_JIS:姓 名;;;;」の箇所で指定することが出来ます。このままだと「姓 名」が名字になってしまいますが、下記のように「;」で姓名をわけてあげることで、ちゃんと認識してくれます(CHARSET~は取ってしまっても問題ありません)。
ぼくの場合、携帯の電話帳に登録するときに姓名を半角スペースでわけていたので、わけるのは簡単でした。。
フリガナを振る
上の6,7行目「X-PHONETIC ~」に指定することでちゃんと認識します。携帯から出力したときにはここの行がなかったため、スクリプトで追加しました。フリガナは5行目から持ってきたかったのですが、どこからが姓で名なのかsplitのしようがないので、登録されている漢字から、Yahoo! の ルビ振りWebサービス を使って登録しました。
X-PHONETIC-FIRST-NAME:めい
X-PHONETIC-LAST-NAME:せい
(ルビ振りAPI は 光一(こういち)を「ぴかいち」と読むなど、あんまりなところもありましたが、自分で書いていくよりよいので、スクリプトでグルグルまわして取得しました。)
これでアドレスブックにすっきりインポートすることができました。
mod_railsで有名なオランダのPhusion社から Ruby Enterprise Edition がリリースされたので、早速Passenger(mod_rails)と組み合わせて試してみました。
Ruby Enterprise Edition は、Rails に最適化されたRubyのようです(An enhanced garbage collector. An improved memory allocator. )。
インストールはいたって簡単で、指定したディレクトリに(gemも)すべて展開されるため、既存のRubyとの併用も可能です。またアンインストールしたければそのディレクトリを削除すればいいだけです。
Ruby Enterprise Edition のインストール
Ruby Enterprise EdtionはPassengerとの組み合わせでメモリ使用量を33%減にできるようですので、ちょっと期待して使ってみました。
1
2
3
4
5
6
7
8
9
10
| # wget http://rubyforge.org/frs/download.php/38084/ruby-enterprise-1.8.6-20080507.tar.gz
# tar zxvf ruby-enterprise-1.8.6-20080507.tar.gz
# ./installer
......
Where would you like to install Ruby Enterprise Edition to?
(All Ruby Enterprise Edition files will be put inside that directory.)
[/usr/local/ruby-enterprise/] :インストールするディレクトリを指定 |
installerを起動すると、どこに設置するのか(line.10)聞かれてインストールが始まります。途中でRailsもインストールされます(すこし時間がかかりました)。
Passenger(mod_rails) 2.0 RC1 のインストール
ここを参考に。
gemでインストール
2.0RC1はpassenger-1.9.0となっているようです。まずはここからダウンロードしておきます。下記のようにさきほどインストールしたRuby Enterprise Editionを利用してgemでインストール。
1
2
| # cd /usr/local/ruby-enterprise/
# ./bin/ruby ./bin/gem install passenger-1.9.0.gem |
これでRuby Enterprise Editionディレクトリ内にpassengerが配置されます。
Apacheの設定
次にRuby Enterprise Editionディレクトリ内のbin配下にpassenger-install-apache2-moduleというスクリプトを叩きます。
1
2
| # cd /usr/local/ruby-enterprise/
# ./bin/passenger-install-apache2-module |
ずらずらとメッセージが出てきて、httpd.confへの編集を促されるので、下記をhttpd.confなどに追記します。Ruby Enterprise Editionをインストールしたディレクトリによってパスは変わるので注意してください。また、デフォルトでproductionとして起動しますが、もしdevelopmentで起動したいなら『 RailsEnv development』を追記してください(Configuring Passengerを参照)。
LoadModule passenger_module /usr/local/ruby-enterprise/lib/ruby/gems/1.8/gems/passenger-1.9.0/ext/apache2/mod_passenger.so
PassengerRoot /usr/local/ruby-enterprise/lib/ruby/gems/1.8/gems/passenger-1.9.0
PassengerRuby /usr/local/ruby-enterprise/bin/ruby
ヴァーチャルホストの設定は下記の通りです。DocumentRootはRailsアプリのpublicディレクトリを指定します。
<VirtualHost *:80>
ServerName www.yourhost.com
DocumentRoot /somewhere/public
</VirtualHost>
起動
これで設定は完了なはずです。Apacheを再起動して設定ファイルを反映して、アクセスしてみます。productionだとエラーがわかりづらいので、最初はdevelopmentで起動した方がいいのかもしれません。
ぼくの環境の場合、普段使っているgemに入っているものが、Ruby Enterprise Editionのgemに入っていなかったため、エラーが起きてしまいました。その場合は、こっちのgemで再インストールを行います。
1
2
| # cd /usr/local/ruby-enterpriise
# ./bin/ruby ./bin/gem install <パッケージ> |
メモリ使用量の比較
scaffoldしてから、ちょっと手を加えた程度の小さいアプリを動かしてみたところ、140Mくらい利用しているみたいです。増井さんの記事 (素Ruby + Passenger 1.x系)とあまり変わらない。。。うーむ、設定を間違えたのでしょうか。。アプリも環境も異なるので、後ほどmongrelなどと比較してみたいと思います。
Rails2.0でscaffoldを行うと、HTMLと同じURL+.xmlでXMLの取得も行えます。リソースひとつだけ取得するのであればこのままでも大丈夫なのですが、たとえばuserとblogが1:nの関係で、userのXMLを取得する時に同時に複数のblogも取得したい場合どのようにすればよいのかわかりませんでした。
to_xmlを使わずテンプレートでがんばろうかなと思っていたところ、1年前のRyan’s Scrapsでまさにそのものな:includeオプションを見つけました。
1
2
3
4
5
6
7
| @user = User.find(1)
@out = @user.to_xml(:include => :blogs)
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @out }
end |
scaffold後に↑のようなソースになりますが、2行目でto_xmlのオプションに:includeを付与しておくと、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| <?xml version="1.0" encoding="UTF-8"?>
<user>
<id type="integer">2</id>
<login>ホゲ</login>
~ 中略 ~
<blogs type="array">
<blog>
<created-at type="datetime">2008-06-10T15:06:14+09:00</created-at>
<id type="integer">1</id>
<user-id type="integer">2</user-id>
<title>ホゲブログ1</title>
<updated-at type="datetime">2008-06-10T15:06:14+09:00</updated-at>
</blog>
<blog>
<created-at type="datetime">2008-06-10T15:36:23+09:00</created-at>
<id type="integer">5</id>
<user-id type="integer">2</user-id>
<title>ホゲブログ2</title>
<updated-at type="datetime">2008-06-10T15:36:23+09:00</updated-at>
</blog>
<blogs/>
</user> |
のように、userの子要素として blogs > blog が追加されました。
そのほか to_xml には :include 以外にも便利なオプションがあります。
たとえば不必要な項目を除外する:except、指定項目だけを表示する:only。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| user = User.find(1)
user.to_xml(:except => [:id, :created_at])
#=>
# <user>
# <name>Ryan</name>
# <email>ryan@spamme.com</email>
# </user>
user.to_xml(:only => [:email])
#=>
# <user>
# <email>ryan@spamme.com</email>
# </user> |
また、下記のようにXMLにエレメントを追加することもできます。この例ではidとcreated_atを除外して、serialize_versionを1.1として追加しています。
1
2
3
4
5
6
7
8
9
| user.to_xml(:except => [:id, :created_at]) do |xml|
xml.serialize_version 1.1
end
#=>
# <user>
# <name>Ryan</name>
# <email>ryan@spamme.com</email>
# <serialize_version>1.1</serialize_version>
# </user> |
あー便利だなあ。