Rails

Railsアプリケーションでseedデータをべき等に適用するツールを作った

More than 1 year has passed since last update.

Railsを使ってWEBアプリケーションを作るとき、本番環境のマスタデータや開発用のダミーデータをDBに流すとき、 db/seeds.rb を使い rails db:seed コマンドでseedデータを流すというやり方をやってきている方も少なくないかと思います。

そんな db/seeds.rb では何度流してもべき等な作りになるように、UPSERTキーを気にしつつ下記の様なコードが乱立することになるかなぁとおもいます。

ApplicationRecord.transaction do

user = User.find_or_initialize_by(id: 1, name: 'hoge')
user.birthday = Time.zone.parse('1996/11/24 20:00')
user.save!

category = Category.find_or_initialize_by(id: 1, name: 'fuga')
category.save!

article = Article.find_or_initialize_by(user: user, category: category)
article.text = 'hello'
article.save!
end

こういったデータをproduction・開発環境に反映する際、自分にとっては以下のような課題がありました。


  1. 開発環境に流すデータを作るにも、コード量がが多すぎる

  2. 流すとどのような差分が生まれるか事前に確認したい

  3. 実行結果のロギング

  4. テスト環境でもマスタデータを利用したい

そこで、DSLでseedデータをべき等に適用しつつ、実行結果のロギングやdry-runモードを備えたツールを簡単に作りました。

https://github.com/iguchi1124/seedog


インストール方法

Gemfileに以下を追記し、bundle install

gem 'seedog'

db/Seedfile というファイルを作成

$ touch db/Seedfile

これで完了です。 db/Seedfile にこれから紹介するDSLを記述することでseedデータの適用作業を rails db:seedrails db:seed:dry-run で実行できるようになります。


使い方


DSLでseedを記述

seedogでは以下のようなDSLで、UPSERTキーを指定しつつ、値を書き換える事ができます。


db/Seedfile

transaction do

model User do
record id: 1, name: 'hoge' do
birthday Date.parse('1996/11/24')
end
end

model Category do
record id: 1, name: 'fuga' # UPSERTキーのみで生成する場合
end

model Article do
record user_id: 1, category_id: 1 do
text 'hello'
end
end
end

if Rails.env.development?
# 開発環境のみに適用したいデータがあれば
model User do
record id: 2, name: 'suzu' do
birthday Date.parse('1998/06/19')
end
end
end


transaction ブロックを使うことでデータのUPSERT処理にトランザクションを発行できます。

Ruby DSLでできていますから、csvデータを読み込みつつ、idやslugでUPSERTするなどの記述も利用できます。


流すとどのような差分が生まれるか事前に確認する

seedogをインストールすると、db:seed:dry-runというタスクが利用可能になります。

$ rails db:seed:dry-run

Apply `db/Seedfile` (dry-run)
Create user {:id=>1, :name=>"hoge", "birthday"=>Sun, 24 Nov 1996}
Create category {:id=>1, :name=>"fuga"}
Create article {:user_id=>1, :category_id=>1, "text"=>"hello"}
Create user {:id=>2, :name=>"suzu", "birthday"=>Fri, 19 Jun 1998}

今回は初回なので、上記のように新しく生成されるデータを出力します。


実行結果の出力

$ rails db:seed

Apply `db/Seedfile`
Create user {:id=>1, :name=>"hoge", "birthday"=>Sun, 24 Nov 1996}
Create category {:id=>1, :name=>"fuga"}
Create article {:user_id=>1, :category_id=>1, "text"=>"hello"}
Create user {:id=>2, :name=>"suzu", "birthday"=>Fri, 19 Jun 1998}

このように作成されたデータが出力されます。次にデータを一部書き換えてみます。


db/Seedfile

  model User do

record id: 1, name: 'hoge' do
+ birthday Date.parse('1996/11/23')
- birthday Date.parse('1996/11/24')
end
end

$ rails db:seed

Apply `db/Seedfile`
Update user {:id=>1, :name=>"hoge", "birthday"=>Sun, 24 Nov 1996} to {:id=>1, :name=>"hoge", "birthday"=>Sat, 23 Nov 1996}

このようにアップデートした結果を残します。変化がない場合は以下のようになります。

$ rails db:seed

Apply `db/Seedfile`
No change


テスト環境でもマスタデータを利用したい

テスト環境でマスタデータを利用する場合、例えばrspecで使うならば以下のように Seedog.run と記述することで db/Seedfile をテストの実行前に適用できます。


spec/rails_helper.rb

RSpec.configure do |config|

config.before(:suite) do
DatabaseRewinder.clean_all
Seedog.run
end
end


今後投入したい機能

マスタデータの適用にも力を入れたいので、大量にデータがある場合のbulk upsertや、すでにレコードがあればアップデートしないような初回のみ適用する機能を開発をすすめつつ、必要に応じて拡張していこうと思ってます。もし、開発を手伝っていただける場合は気軽にPull requestをいただけると!