だいたい
sequence(:name){|n| "name_#{n}"}
っていう形のサンプルコードが多いんだけど、実はこれは
sequence(:name, "name_1")
と書ける。ブロックを渡さずに第二引数を渡すと、 .next
が呼ばれるようになっているのだ。
そして、(これは ruby 自体の機能だが) String#next には以下のように末尾の数値を増やす機能があるのだ。
> "name_1".next
=> "name_2"
> "name_9".next
=> "name_10"
これは便利!と日本語で使おうとすると、
> 'テスト_1'.next
=> "テスト_2"
> 'テスト_9'.next
=> "テスド_0"
残念。うまくいかないのだった。「ト」の次で「ド」になってるのが健気でかわいい。
どうやら、 英字 + 記号 + 数字 が、数字だけインクリメントの発動ルールのようだ。
記号はスペースもアリで、複数文字でも良い。最初の英字は無しでもいけるけど、英字以外があると駄目みたい。
('#9'.next == '#10' だったのでもしや?と思ったが、 'テスト#9' だとやはり駄目でがっかり。)
もちろん、末尾の数字にこだわらず、ユニークならなんでもいいぜ!という豪快なアナタは
sequence(:name, "太郎")
とでもすれば、太郎に続いて "太郏" "太郐" "太郑" "太郒" と、一見太郎っぽいけどよく見ると違う名前に次々出会うことが出来る。
漢字はさすがにアレなので、
sequence(:name, "ゆりあ")
あたりにしておくと、10個に1つくらいの高頻度で名前らしい名前に出会えていいかもしれない。
.next
に対応してれば何でもいいので、Date.today
もイケる。
sequence(:reserve_on, Date.today)
こうすれば、今日、明日、明後日、という感じで順に生成される。
あとちょっと面白いのは cycle
を使って
sequence(:color, %w[red green blue].cycle)
とすると、 red green blue がこの順番で延々繰り返される。
sequence(:color){ %w[red green blue].sample }
てのがわりとよくやる手だけど、cycle だと出方が均等になるので便利なことも。
ところで、 cycle の sequence 便利じゃん、といってテストじゃない本体のほうで使うと production 環境で死ぬ ので気をつけましょう。
しかも FiberError: fiber called across threads
という見慣れないエラーだし development では再現しないので知らないと本当にハマる。
(より具体的には unicorn や passenger のようなマルチスレッド系のサーバで問題が起きます)
このエラーなんなのかというと、cycle が返す Iterator インスタンスを FactoryGirl が内部に保持していて、 Iterator は Fiber で実装されていて、 ruby はスレッドを跨いで Fiber を使用することを禁じているから。いやあ、いい勉強になりました。