railsのルーティングのテスト。
:id => /\d+/ と指定している部分「これじゃ部分一致では?」と思いましたが、ActionController::Routing::Route#generation_requirements で\Aと\Zで挟まれて再コンパイルされてますね。
config/routes.rb
1
2
3
4
5
6
| ActionController::Routing::Routes.draw do |map|
map.with_options(:controller => 'products') do |products|
products.connect 'products/:id', :action => 'show', :id => /\d+/
products.connect 'products', :action => 'create', :conditions => { :method => :post }
end
end |
spec/controllers/products_controller_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| require 'spec_helper'
describe ProductsController do
describe 'ルーティングのテスト' do
it 'Products#show' do
params_from(:get, '/products/123').should == {
:controller => 'products',
:action => 'show',
:id => '123'
}
end
it 'idが数字のみでない場合は404' do
lambda {
params_from(:get, '/products/123abc')
}.should raise_error(ActionController::RoutingError)
end
it 'Product#create' do
params_from(:post, '/products').should == {
:controller => 'products',
:action => 'create'
}
end
it 'createはPOSTのみ' do
lambda {
params_from(:get, '/products')
}.should raise_error(ActionController::MethodNotAllowed)
end
end
end |
参考:
QRコードの生成にGoogle Chart APIを使ってみました。
rspec-railsのhave_tagマッチャを使うときはassert_selectのドキュメントを見ながらの場合が多いですね。早く覚えてしまいたい。

app/helpers/chart_helper.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| module ChartHelper
def qr_code_tag(text, options = {})
param = {
:cht => 'qr',
:chl => text
}
param[:chs] = options.delete(:chart_size) || '120x120'
uri = URI::HTTP.build(:host => 'chart.apis.google.com', :path => '/chart', :query => param.to_query)
image_tag uri.to_s, options
end
end |
spec/helpers/chart_helper_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| require 'spec_helper'
describe ChartHelper do
it '指定したテキストを含むQRコードを得られること' do
tag = helper.qr_code_tag('http://tech.hapicky.com/')
expected_src = 'http://chart.apis.google.com/chart?chl=http%3A%2F%2Ftech.hapicky.com%2F&chs=120x120&cht=qr'
tag.should have_tag('img[src=?]', expected_src)
end
it 'サイズ指定できること' do
tag = helper.qr_code_tag('http://tech.hapicky.com/', :chart_size => '300x300')
expected_src = 'http://chart.apis.google.com/chart?chl=http%3A%2F%2Ftech.hapicky.com%2F&chs=300x300&cht=qr'
tag.should have_tag('img[src=?]', expected_src)
end
it 'altの指定ができること' do
tag = helper.qr_code_tag('http://tech.hapicky.com/', :alt => 'tech-memo')
expected_src = 'http://chart.apis.google.com/chart?chl=http%3A%2F%2Ftech.hapicky.com%2F&chs=120x120&cht=qr'
tag.should have_tag('img[src=?][alt=tech-memo]', expected_src)
end
end |
参考:
rspecのincludeマッチャ。Hashの場合の挙動はバージョン間で色々と違いがありますね。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| require 'rubygems'
gem 'rspec', '>= 1.1.12'
require 'spec'
describe 'includeマッチャ' do
before :each do
@lang_authors = {
:ruby => 'まつもとゆきひろ',
:python => 'Guido van Rossum',
:perl => 'Larry Wall'
}
end
it 'Hashが指定されたキーを持つか' do
# 以下はrspec 1.1.10, 1.1.11 でエラーになる
@lang_authors.should include(:ruby)
@lang_authors.should include(:python)
@lang_authors.should_not include(:vb)
end
it 'Hashが指定されたキーと値の組み合わせを持つか' do
# 以下はrspec 1.1.10より前だと失敗する
@lang_authors.should include(:ruby => 'まつもとゆきひろ')
@lang_authors.should_not include(:ruby => 'まつもとひろゆき')
end
it 'その他はinclude?による評価' do
@lang_authors[:python].should include('os')
@lang_authors.keys.should include(:perl)
end
end |
参考:
HTTP接続に依存した処理をテスト。以下ではwebmockを使っていますが他にもfakewebなどありますね。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| require 'rubygems'
require 'spec'
require 'webmock/rspec'
require 'open-uri'
class HttpClient
def initialize(url)
@url = url
end
def fetch_body
begin
open(@url) { |io| io.read }
rescue OpenURI::HTTPError
nil
end
end
end
describe HttpClient do
TEST_URL = 'http://www.example.com/test'
before :each do
@client = HttpClient.new(TEST_URL)
end
it 'ステータスコード200の場合はbodyの内容を返すこと' do
body = "OK"
WebMock.stub_request(:get, TEST_URL).to_return(:body => body, :status => 200)
@client.fetch_body.should == body
end
it '接続できない場合はnilを返すこと' do
body = "NG!"
WebMock.stub_request(:get, TEST_URL).to_return(:body => body, :status => 404)
@client.fetch_body.should be_nil
end
end |
参考:
テスト中にファイル書き込みを行いたいのでテンポラリディレクトリを利用する。終わったら掃除。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| require 'rubygems'
require 'spec'
require 'tmpdir'
describe 'テンポラリディレクトリを使ったテスト' do
before :all do
@tmp = Dir.mktmpdir
end
it 'なんらかのテスト' do
# @tmp に書き込みを行うテストなど
end
after :all do
FileUtils.remove_entry_secure @tmp if File.exist?(@tmp)
end
end |
参考:
発売してすぐ買ったものの、2週間以上放置していたSnow Leopardをようやくインストールしました。移行手順をちょっとメモしておきます。
環境
- MacOSX Leopard(10.5)からの移行
- マシン: iMac(Early 2008)Core 2 Duo 2.4 GHz
外付けHDDにCarbon Copy Clonerでバックアップ
以下の外付けHDDを購入し、Carbon Copy Cloner でバックアップを作成しました。これで万が一の事があっても大丈夫。
Leopardがインストールされた起動ディスクは123GBほど使用していましたが、バックアップに3時間弱かかりました。おやすみ前の作業をおすすめします。なおCarbon Copy Clonerはドネーション(寄付)ウェアです。

起動ディスクを外付けHDDにまるごとバックアップ
バックアップが済んだら、念のためこのHDDから起動できるか確認しておきます。なおこのHDDの説明書には「OS起動はサポートされておりません」と明記されているので、くれぐれも自己責任でお願いします。
さらにTime Machineでバックアップをとっておく
実はCarbon Copy Clonerを使うのは初めて。それとOSXの移行アシスタントのほうがスムーズかも、という思いがあったので、Time Machineでもバックアップをとっておきました。
さらにさらに、Snow Leopardのインストールは別パーティションに
どこまで用心深いんだというかんじですが、起動ディスクの容量に余裕があったので、Leopardは残したままパーティションを分割し、Snow Leopardは別パーティションにインストールしました。
Disk Utilityを使えば既存のLeopardはそのまま、パーティションを分割できます。

Disk UtilityでSnow Leopard用のパーティションを追加します。
Snow Leopardをインストール
ようやくSnow Leopardのインストールを開始。Leopard上からインストールを実行できる(DVD起動でない)ようになったので、だいぶ速いですね。
インストールオプションは
- プリンタ: 「近くにあるプリンタ、および一般的なプリンタ」
- 追加言語環境: 日本語のみ
- その他: X11, Rosetta, QuickTime 7
を選択しました。

インストールオプション
インストール直後にTime Machineから移行
インストールが終わりWelcomeムービーが流れると、「すでに Mac をお持ちですか?」画面が表示されます。今回はここでTime Machineから移行作業を実行しました。
いったんログインしてから移行アシスタントを実行することもできるのですが、その場合ログイン中のユーザーは復元することができません。別のユーザーを作るのも面倒なので、ログイン前に済ませてしまいました。
.png)
移行アシスタント
移行アシスタントで移行したもの、しなかったものは以下のとおりです。
移行したもの
- 移行したもの
- ユーザ
- ミュージック、ピクチャなど
- 「その他のファイルとフォルダ」(~/.emacsなどが対象になるらしい)
- 設定
- 移行しなかったもの
- アプリケーション
- 「<ドライブ名>上のその他のファイルとフォルダ」
- (MacPortsで/opt以下にインストールしたものなどは再インストールしたい)
注意事項
今回は既存のLeopardをそのまま残し、さらに移行アシスタントでDockやメニューバーの設定を復元しました。こうした場合、Snow LeopardでDockに表示されるアイコンがLeopard上のアプリケーションを参照したままとなります。
インストール直後に設定しなおすことをおすすめします。
移行を終えて
今のところの感想としては、
- よい
- OSの起動が速い
- Finderが多少キビキビしている
- 3,000円程度というお得感
- 気になる
- VirtualBoxでWindows XPの画面が乱れる
- MacPortsでruby, Railsをインストールしたが、マイグレーションなどのRakeタスクが動かなかった(詳細未調査)
といったところでしょうか。全体的にはおおむね快調です。
参考
MySQL(5.0系)で、すでにレコードが存在するテーブルにNOT NULLかつDEFAULTなしの列を追加してもエラーにならなかった。きちんと理解していないので調べておく。
以下のテーブルを作成し、レコードを追加しておく。
mysql> CREATE TABLE users (id INTEGER PRIMARY KEY);
mysql> DESC users;
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | |
+-------+---------+------+-----+---------+----------------+
mysql> INSERT INTO users VALUES(1); |
NOT NULLでDEFAULTなしの列を追加してみる。
mysql> ALTER TABLE users ADD (name VARCHAR(255) NOT NULL,
age INTEGER NOT NULL, created_at DATETIME NOT NULL); |
エラーにならず、以下の値が設定された。
mysql> SELECT * FROM USERS;
+----+------+-----+---------------------+
| id | name | age | created_at |
+----+------+-----+---------------------+
| 1 | | 0 | 0000-00-00 00:00:00 |
+----+------+-----+---------------------+ |
どうやら「データタイプデフォルト値」が設定されるもよう。
これは「ストリクトSQLモード」が有効な場合でも同じ結果となる。試してみる。
-- いったん列を削除
mysql> ALTER TABLE users DROP name;
mysql> ALTER TABLE users DROP age;
mysql> ALTER TABLE users DROP created_at;
-- ストリクトSQLモードを有効に
mysql> SET sql_mode = 'STRICT_ALL_TABLES';
mysql> SELECT @@sql_mode;
+-------------------+
| @@sql_mode |
+-------------------+
| STRICT_ALL_TABLES |
+-------------------+
-- ストリクトSQLモードでもエラーにならない
mysql> ALTER TABLE users ADD (name VARCHAR(255) NOT NULL,
age INTEGER NOT NULL, created_at DATETIME NOT NULL);
mysql> SELECT * FROM USERS;
+----+------+-----+---------------------+
| id | name | age | created_at |
+----+------+-----+---------------------+
| 1 | | 0 | 0000-00-00 00:00:00 |
+----+------+-----+---------------------+ |
せめてストリクトSQLモードが有効な場合にはエラーにしてほしいですね。要注意です。
参照ドキュメント:
危なかった。勘違いしてた。
int(11)の「11」って表示桁数を揃えるためのものであって、格納できるのはあくまで4バイトなんですね。
参照: 本家ドキュメント
試してみる。
mysql> create table int_test (id int(11) primary key);
Query OK, 0 rows affected (0.04 sec)
mysql> desc int_test;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)
mysql> insert into int_test values(2147483647);
Query OK, 1 row affected (0.00 sec)
mysql> insert into int_test values(2147483648);
ERROR 1062 (23000): Duplicate entry '2147483647' for key 1 |
引き続き2, 3日目。
卜部 昌平氏「Ruby 1.8 のゆくえ」
1.8のメンテナの卜部さんが、現状と今後について説明してくださいました。
- 1.8.5
- 1.8.6
- 1.8.7を使いたくないという人もいるそうで、メンテが必要な状況。Engine Yardの方がやっている。
- 1.8.7
- 最近のコミットのうち1/3がバグ修正。
- いつまでメンテするかはなんともいえない。
- 1.8.8
- 1.9への移行ギャップを少なくしたいという意図がある。
- 以下のようなコードがパースエラーにならないようにしたい。
if RUBY_VERSION >= "1.9.0"
# code for 1.9
else
# code for 1.8
end |
- 1.8.9
Yugui氏 「Ruby 1.9.2ロードマップ」
続いてYuguiさんが1.9について説明。
- 1.9.1
- 1.9.2
- プレビューリリースする。
- 仕様に対する要望があれば今のうちに。
- 機能としては地味だが、1.9のgrand designは固まった。as better 1.9.1.
ruby1.9自体は安定してきているので、どんどん移行してほしいとのこと。
大井 宏友氏「Railsサイト安定運用の心構え ~8つのサービスから学ぶ」
Xen上でRailsを運用してますよ、というお話。
- mongrelからpassengerへ移行してきている。
- Xen上でRailsを動作させている。
- webとappだけ仮想マシンに載せている。
- メモリ:16GB, CPU:Quad x 2のハードウェアに最大13VM(割当メモリ各1GB)
- mongrelの最大インスタンス数はメモリ1GBで10, 2GBで24
- よくある注意事項
- 無意識にActiveRecordを使ってパフォーマンスに問題が発生しがち。
- ログのローテーションを忘れがち。
- Xen上ではひとつのVMがスワップを使いだすと、他のVMにも影響する。mongrelの定期リスタートや監視が必要。
遠藤 侑介氏「concov: 時系列に注目したテストカバレッジビューア」
テストカバレッジを時系列で把握することで、テストがないコードの追加やカバレッジの低下を可視化するツールconcovの紹介。
参照: http://d.hatena.ne.jp/ku-ma-me/20090708/
nari氏「RubyのGC改善による私のエコライフ」
GCのお話。
Ruby Enterprise Editionで行われている改善を本家に採用できないか検証したが、「forkが頻繁に発生しないようなアプリだと逆に遅くなる」とのこと。ちょっと期待しちゃいました。
3日目は遅刻&早退+体調不良でセッションの内容があんまり頭に入らなかった…。
私は去年参加した時と今回で勤めている会社が違うのですが、そのせいもあって前回とは違った聞き方をできたような気がします。少しだけ、事前に目的意識を持ったり、今後の取り組みへの判断材料を得たりできたかな。
今年も貴重な体験となりました。ruby会議実行委員会のみなさん、rubyコミュニティのみなさん、ありがとうございました。
去年に続き、今年もRuby会議に参加しています。参加したセッションで気になったことなどをメモ。
Scott Chacon氏「Using Git and GitHub to Develop One Million Times Faster」
gitいいよ、どんどん使って。という話
- オフラインな状況でも使える
- ローカルに対する操作だから速い
- ブランチ、マージが楽
- githubとか探せばプロジェクトもいっぱいある
というような話だったように思う。英語自信なし。
いくつかプロジェクトをgithubでwatchしてはいるものの、自分ではまだほとんど使っていません。ちょっと使ってみようと思います。
高井 直人氏「『エンタープライズRails』に学ぶ企業ユーザのためのRails活用の極意」
ご自身が監訳された書籍を紹介しつつ、その内容について講演。「きっと業務に対してrubyをどう適用したらいい結果がでるか、常日頃考えているんだろうな」という印象を受けました。こういう方、尊敬しちゃいますね。
- 企業にとって最も重要な資産は「データ」
- データ中心アプローチのメリットを整理
- RailsにおけるDOAとの類似、相違を整理
- 類似: モデル中心、CRUD分析重視
- 相違: データモデルを重視しない、業務ルールはアプリケーション層のみで実現
くらいを導入として話された後、書籍の内容をいくつか紹介
- データベースの制約をうまく使ってデータを保護しよう
- NOT NULLだけでなく、チェック制約、外部キー制約など
- モデルのバリデーションはあくまでユーザー向け
- マイグレーションではexecuteでテーブル定義
- 複合主キーを使う
- 複合主キーだと、無駄なSQLを回避できるなどメリットもあるよね
- composite_primary_keysプラグイン使って
- データベースビューを利用する
-
多数のJOINが必要な場合などは、ビューを定義してそれを参照するARを用意するとシンプルになるよ
セッション後は会場内の書籍売り場が、購入希望者でいっぱいでした。
Yehuda Katz氏「From Rails to Rack: Making Rails 3 a Better Ruby Citizen」
このセッションの英語はあんまり理解できなかった…。だいぶ雰囲気。
Rails3では各種コンポーネントへの密結合を解消し、rubyの色んな資産と協調していくよ、という話。たぶん。
そのためにAPIを定義しなおし、インターフェイスさえ揃えれば他のコンポーネントを利用できるようするみたい。具体的にコードを交えて説明してくださいました。
ActiveSupport::Concernのdepends_onメソッドが気になりますね。AOPっぽいことをスマートに表現してる印象。ちょっと違う?
明日も朝から参加なのでいいかげん寝ます。