This is inspired by http://sixarm.com/about/rails-seed-data.html which i noticed had some errors.

Seed Data in YAML files

We use the Rails convention of putting our seed data files here:

/db/seeds/

We create a YAML file for our seed users:

/db/seeds/users.yml

The users YAML file defines each user’s fields like this:

  alice:
    name: "Alice Adams"
    mail: "[email protected]"
  bob:
    name: "Bob Brown"
    mail: "[email protected]"
  carol:
    name: "Carol Cox"
    mail: "[email protected]"

Rake task to load seed data

We use the Rails convention of putting rake tasks here:

/lib/tasks/

We use a rake file for our seeds:

/lib/tasks/db_seed_users.rake

The task provides the rake namespace, runs any setup tasks, then calls a normal ruby method:

  # rake db:seed:users
  namespace :db do
    namespace :seed do
      desc "Seed Users from /db/seeds/users.yml"
      task :users => :environment do
        db_seed_users
      end
    end
  end

TIP - the desc is required if you want your task to show up with rake -T

The normal ruby method loads the YAML file and iterates on each user:

  # Seed multiple users by loading the YAML file
  def db_seed_users
    path = Rails.root.join('db','seeds','users.yml')
    puts "Seeding file #{path}"
    File.open(path) do |file|
      YAML.load_documents(file) do |doc|
        doc.keys.sort.each do |key|
          puts "Seeding key #{key}"
          attributes = doc[key]
          create_a_seed_user(attributes)
        end
      end
    end
  end

  # Seed one user
  def create_a_seed_user(attributes)
    User.create(attributes)
  end

To run the rake task:

$ rake db:seed:users

To view your rake tasks:

$ rake -T

Remember, your tasks will not show up unless you add a desc

How to make seeds run more than once

We want to be able to run our seed task more than once and still get produce same set of users.

To do this, we add code that checks to see if the user already exists, and if so, skips creating that user:

  # Seed one user
  def db_seed_user(attributes)
    mail = attributes['mail']
    user = User.where(mail: mail).first_or_create
    if user
      puts "This email address exists: #{mail}"
      # update the user with user.update(name: attributes['name'])
    else
      puts "This email address is new: #{mail}"
      User.create(attributes)
    end
  end

Example file looks like this:

namespace :db do
  namespace :seed do
    desc "Seed Users from /db/seeds/users.yml"
    task :users => :environment do
      db_seed_users
    end
  end
end

# Seed multiple users by loading the YAML file
def db_seed_users
  path = Rails.root.join('db','seeds','users.yml')
  puts "Seeding file #{path}"
  File.open(path) do |file|
    YAML.load_documents(file) do |doc|
      doc.keys.sort.each do |key|
        puts "Seeding key #{key}"
        attributes = doc[key]
        db_seed_user(attributes)
      end
    end
  end
end

def db_seed_user(attributes)
  mail = attributes['mail']
  user = User.where(mail: mail).first_or_create
  if user
    puts "This email address exists: #{mail}"
    # update the user with user.update(name: attributes['name'])
  else
    puts "This email address is new: #{mail}"
    User.create(attributes)
  end
end