Help us understand the problem. What is going on with this article?

FactoryBot (旧FactoryGirl) の sequence と .next

More than 1 year has passed since last update.

だいたい

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 を使用することを禁じているから。いやあ、いい勉強になりました。

heartrails
ハートレイルズは、新規事業の立ち上げに伴うウェブサービス、スマホアプリの企画、開発、運用に特化した開発会社です。
http://www.heartrails.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away