大ちゃんの駆け出し技術ブログ

RUNTEQ受講生のかわいいといわれるアウトプットブログ

【無職に転生 ~ 就職するまで毎日ブログ出す⑥】【Rails】seed_fu

はじめに

こんにちは、大ちゃんの駆け出し技術ブログです。


タイトルにあるとおり【無職に転生 ~ 就職するまで毎日ブログ出す】というチャレンジをしています!!!大人気アニメのタイトルをまるパクリした毎日投稿チャレンジです。

RailsやらRubyやらSQLやらその他Webの知識やらが色々と抜け落ちているのを感じており、知識の定着のためにもアウトプットする機会を増やすためです。加えて、退職して文字通り無職に転生しましてプロニートになり、毎日時間に余裕ができたので引き締めるためにも毎日投稿を思い至りました!

【投稿内容】

  • SQLの難しい処理 (副問合せ、JOINとか複雑な処理が書けない)
  • Rails全般 (純粋に必要な知識が多すぎる、網羅的な理解が足りない)
  • Rubyのあまり使わないメソッドや記述方法 (あまり重要ではないけど特に)
  • Web知識全般 (クッキーやら、セッションやらなんとなくで理解しているものの自分の言葉で説明できない)
  • 書籍 (スタートアップ企業に勤めるので、自分が会社に与える影響やパフォーマンスを高めるためビジネス書を読んでいきます。)

本記事でやること

gemであるseed_fuについて書いてみたいと思います。たまたま今やっている開発で使う機会がありました。

github.com

gemのステータスとしては以下のとおり。

  • star数・・・1.2k
  • user数・・・3.7k
  • 最終更新日・・・2018年4月7日

かなり多くのユーザーから使われているgemですが、最終更新日が約4年前になっていますね。ただ、seedファイルの設定であり、railsの仕様が大きく変更されない限りは不具合が起きないと思うので、メンテナンス性はそこまで大きな問題ではなさそうです。


セットアップ

早速導入方法から!

gemを追加

gem 'seed-fu'

bundle installする

$ bundle install

次にseed_fuのテストデータを作成するファイルを追加します。seed_fuのファイルを作成する前にfixturesディレクトリを作成します。

$ mkdir db/fixtures
$ mkdir db/fixtures/development
$ mkdir db/fixtures/production

developmentproductionディレクトリは作成しなくてもいいのですが、本番環境と開発環境でファイルを分けたい場合は上記のようにfixtures配下に分けてディレクトリを作成します。

これでセットアップは完了です。

初期データを作成

作るデータは下記のモデルにします。

https://i.gyazo.com/fd9514ffdf0c79fb0c47a50179bbb20a.png

# user.rb
class User < ApplicationRecord
    validates :name, presence: true
    validates :email, presence: true
    has_many :posts, dependent: :destroy
end
# post.rb
class Post < ApplicationRecord
    validates :content, presence: true
    belongs_to :user
end

まず、Userのデータから作成します。ファイル名「01_user.rb」をdb/fixtures/development配下に作成します。ここで「01」とプレフィックスをつける理由は後ほど説明します。

$ touch db/fixtures/development/01_user.rb

そして、ファイルを以下のように記述します。

# db/fixtures/development/01_user.rb
User.seed do |s|
  s.id    = 1
  s.email = "daiki@example.com"
  s.name  = "Daiki"
end

これで下記コマンドを実行すると初期データを作成することができます。

$ rake db:seed_fu
== Seed from /path/to/app/db/fixtures/development/01_user.rb
 - User {:id=>1,:email=>"daiki@example.com", :name=>"Daiki"}

今だとユーザーが1人しか作成されていないので、もう1人追加します。

# db/fixtures/development/01_user.rb
User.seed do |s|
  s.id    = 1
  s.email = "daiki@example.com"
  s.name  = "Daiki"
end

User.seed do |s|
  s.id    = 2
  s.email = "yusuke@example.com"
  s.name  = "Yusuke"
end

再度seedデータを入れます。

$ rake db:seed_fu
== Seed from /path/to/app/db/fixtures/development/01_user.rb
 - User {:id=>1,:email=>"daiki@example.com", :name=>"Daiki"}
 - User {:id=>2,:email=>"yusuke@example.com", :name=>"Yusuke"}

このように作成するデータを並べるように記述していきます。しかし、これではたくさんのデータを作成するときに冗長になってしまうため、下記のような書き方もできます。

# db/fixtures/development/01_user.rb
User.seed(
  :id,
  { id: 1, name: "daiki@example.com", email: "daiki@example.com" },
    { id: 2, name: "yusuke@example.com", email: "Yusuke@example.com" },
)

個人的にはたとえ作成するデータが一つでも上記のように書いておく方が後々初期データを追加する際に手間が省けて便利だと思います。


次にpostの初期データを作成します。ファイル名「02_post.rb」をdb/fixtures/development配下に作成します。

$ touch db/fixtures/development/02_post.rb

次にテストデータを作成します。

# db/fixtures/development/01_user.rb
Post.seed(
  :id,
  { id: 1, content: "content1", user: User.find(1) },
    { id: 2, content: "content2", user: User.find(2) },
)
$ rake db:seed_fu
== Seed from /path/to/app/db/fixtures/development/01_user.rb
 - User {:id=>1,:email=>"daiki@example.com", :name=>"Daiki"}
 - User {:id=>2,:email=>"yusuke@example.com", :name=>"Yusuke"}

== Seed from /path/to/app/db/fixtures/development/02_post.rb
 - Post {:id=>1,:content=>"content1", :user_id=>1}
 - Post {:id=>2,:content=>"content2", :user_id=>2}

関連データも作成することができました。


プレフィックスをつけた理由ですが、関連データを作成する順番のためプレフィックスをつけました。親モデルは必ず子モデルより先に作成されています。そのため、テストデータも親モデルから先に作成する必要があります。そして、seed_fuが初期データを作成する時、ファイルを実行する順番はファイルの並び順で上から下にかけて実行します。

もし、ファイル名が「post.rb」と「user.rb」の場合、seed_fuの実行順序は下記になります。

post.rb

↓

user.rb

しかし、ユーザーが作成されていないので上記の場合は失敗します。

プレフィックスをつければファイルの順番をプレフィックスで調整できるので、実行順序を操作することができます。

01_user.rb

↓

02_post.rb

seed_fuを使う際はプレフィックスをつける方法が良いでしょう。

seed_fuのメリット

ここまでseed_fuの使い方を書いてきましたが、seed.rbでよくないかと思う方もいると思います。しかし、seed_fuは下記の2点で便利です。

  • いちいちデータを初期化する必要がない

seed.rbでは再度初期データを入れる場合は下記コマンドを実行します。

$ rails db:reset

つまり、データを全部削除しその後初期データを入れ直します。このとき作成する初期データが膨大であれば待ち時間が多くなりますし、そもそもちょっとした変更でもデータを全部削除しなければならないのは効率が悪いです。

しかし、seed_fuであればそれは解決できます。まだ紹介していなかったのですが、seed_fuではテストデータをファイルごとに分けているため、パスを指定して入れるデータを指定できます。例えば、上述したPostモデルの初期データのcontentを変更します。

# db/fixtures/development/01_user.rb
Post.seed(
  :id,
  { id: 1, content: "hogehoge", user: User.find(1) },
    { id: 2, content: "hogehoge", user: User.find(2) },
)

そしてパスを指定して初期データを入れてあげます。

$ bundle exec rake db:seed_fu FILTER=02_post
 - Post {:id=>1,:content=>"hogehoge", :user_id=>1}
 - Post {:id=>2,:content=>"hogehoge", :user_id=>2}

先ほどはUserも初期化されましたが今回はパスを指定したことによりPostモデルのデータだけ再度作成されました。これにより、無駄なデータの全削除を回避することができます。

  • モデルごとにファイルを分けることができる

seed.rbを使う場合は全てのモデルを一つのファイルにまとめなければいけません。

User.create!(
  email: 'daiki@example.com',
  name: 'daiki',
)

Post.create!(
    content: 'content1',
    user: User.find(1)
)

まだモデル数が少ないので分ける必要はないかもしれませんが、大体のアプリはモデル数がもっとあるはずです。そのときに上記のように一つのファイルにまとめた場合、記述量が膨大になってしまいます。

しかし、上述したとおりseed_fuであればファイルを分けることができます。

参考記事

github.com

qiita.com

bagelee.com