FactoryGirl を使って冪等な Database seeding をやってみた
development, test 以外で FactoryGirl を使うのどうなんだ、という話は全力で脇に置く。こういう誤魔化し方もあるのかな、という試み。
んー、seeds.rb で find_or_create_by
を書かなくて良い、程度のおもちゃかな…。
構造
db/ 以下に色々置く感じの構造。
$ tree db/seeds db/seeds ├── development.rb ├── factories │ ├── cities.rb │ ├── countries.rb │ └── prefectures.rb ├── factories.rb ├── production.rb └── test.rb
rake db:seed
するとdb/seeds/#{env}.rb
が require されてコードが実行される- 共通処理書きたければ、各ファイルから共通ファイル require する、とか?
db/seeds/factories.rb
には共通処理を書いておくdb/seeds/factories/**/*.rb
にファクトリを置く- ファクトリは
initialize_with { find_or_initialize_by }
して冪等保証してるだけ
- ファクトリは
db/seeds.rb
これで spec/factories/ の方との干渉を防げるか自信なし。
表出力は本題とは関係なくて適当に。 Thread.current
も特に意味はなく適当に。
require 'factory_girl' include FactoryGirl::Syntax::Methods begin original_definition_file_path = FactoryGirl.definition_file_paths FactoryGirl.definition_file_paths = %W(db/seeds/factories) FactoryGirl.reload begin Thread.current[:seeding_results] = Hash.new {|h, k| h[k] = [] } ActiveRecord::Base.transaction do require_relative "seeds/#{Rails.env}" end Thread.current[:seeding_results].each do |klass, ids| puts Hirb::Helpers::AutoTable.render(klass.where(id: ids)) end end ensure FactoryGirl.definition_file_paths = original_definition_file_path end
使い方
ファクトリを書く
冪等保証のために initialize_with { find_or_initialize_by }
を使っているので、モデルごとにファクトリを作る必要がある。
ファクトリは db/seeds/factories/
以下に配置する想定。
FactoryGirl.define do factory :prefecture do initialize_with { Prefecture.find_or_initialize_by(country: country, name: name) } end end
seed を書く
実際の seed 処理は db/seeds/#{Rails.env}.rb
に書く想定なので、それぞれ書く必要がある。
create(:country, name: '日本') do |japan| create(:prefecture, country: japan, name: '東京都') do |tokyo| create(:city, prefecture: tokyo, name: '千代田区') create(:city, prefecture: tokyo, name: '港区') end create(:prefecture, country: japan, name: '神奈川県') do |kanagawa| create(:city, prefecture: kanagawa, name: '横浜市') create(:city, prefecture: kanagawa, name: '川崎市') end end