読者です 読者をやめる 読者になる 読者になる

seeds.rbの使い方とマスタデータの取り扱いついて考える

Rails Ruby RSpec

1. マスタデータの扱いについて

rake db:seedするたびにデータが挿入される。

idをインクリメントで自動挿入するときにめんどくさいことになる。

最初に挿入するマスターデータにassociationを張っている場合は、何回も生成してidが更新されると致命的なのでなんとかしたい。

以前はmigrationファイルに書く方法が主流だったみたいだけど、このseeds.rbが2.3.4で追加された理由は「スキーマの生成とDBへの実データの投入は書くところ分けようぜ!」って話だったわけだしmigrationファイルに書いちゃったらマスタ変更したいときに結構無理ゲーになってきそうな感じはある。

方法としては、seed-fuを使うか、seeds.rbで毎回シーケンスをdeleteしてからデータを挿入し直すようにするか

seed-fuはdb/fixturesという新しいディレクトリを最低限作らないといけないので、それに抵抗がある。チーム全体でseeds.rbを使わないようにしてそちらに合わせてもらうようにするか、それかseeds.rbとseed-fuの使い分け方をきちんと明確にしておかないといけないことになると思う。それは結構めんどい。

といろいろ考えてたら以下でいい感じの方法が。

Railsでdb:seedでマスターテーブルを管理するオススメのやり方

http://d.hatena.ne.jp/JironBach/20131011/1381485761

なるほどidの値も含めてfirst_or_createを呼べばいいのか。

ってこれならfirst_or_createじゃなくてfind_or_createでも一緒では、って思ったら、どうやらfirst_or_createはdeprecateになるらしい。

https://github.com/rails/rails/blob/300d080ada31ac297264e6abba6ca16cd2db5925/activerecord/CHANGELOG.md

ということでそちらを使って書くことに。例えばこんな感じかな。

Origin.find_or_create_by { name: "Original Blend" }

今までの分をリセットするには

$ bin/rake db:reset

すればok。別にdropするのは同じなので、associationとかを追加してない限りdb:migrate:resetする必要はないかな。

2. seeds.rbをキレイに書きたい。

seeds.rbが長くなりがちなのでキレイに書きたい。

seeds.rbはただのRubyスクリプトなのでどんな書き方でもいいんだけど、だからこそみんなで統率がとれなくなりがちだ。

特にマスタデータに関しては初期で大量のデータが入る可能性もあると思うので。ここにやり方が書いてあるけど、まだ冗長な感じがする。

seeds.rbをスッキリさせる3つのポイント

http://blog.interfirm.co.jp/entry/2014/05/30/195044

以下も参考に。

Rails 2.3.4で追加されたseeds.rbについて

http://higelog.brassworks.jp/?p=350

いちいちハッシュを作ってるとDRYにならないのでほんとは以下のように書きたい。

%w{テント 寝袋}.each do |name|
  Category.create(:name => name)
end

要素は一行に一つと決めたら、編集もしやすいかなということでそんな感じで書くことにしてみた。%wは一文字スペースじゃなくて改行で配列を作ってくれるので。

こんな感じのコードが、

Origin.create([{ name: "Kenya" }, { name: "Rwanda" }, { name: "Ethiopia" },
                    { name: "Brazil" }, { name: "Costalica" }, { name: "Indonesia" },
                    { name: "Tanzania" }])

最終的にこんな感じになりました。originsに一回入れるかは迷ったけど、上から読んでいったときにこちらの方がわかりやすいと思ったのと、最後に.eachをくっつけるとなんか気持ち悪かったので。

origins = %w(
  Kenya
  Rwanda
  Ethiopia
  Brazil
  Costalica
  Indonesia
  Tanzania
)
origins.each do |origin|
  Origin.find_or_create_by(name: origin)
end

2つ以上の要素を突っ込みたいときは以下の方法でできそう。

Rubyにて複数の配列を同時にeachする方法

http://blog.sachin21.info/entry/2014/03/13/133513