Recent Projects

Creating Valid Records with Populator and Faker

I’m an avid Railscasts watcher, and I was quite happy with one of the recent episodes called Populating a Database. The episode shows you how to load a database with lots of records for demos, design, and/or acceptance testing in an efficient way, but it doesn’t cover making sure you’re creating valid stuff. I love speedy rake tasks just as much as the next guy, but I found that creating invalid records lead to problems pretty quickly. Luckily, this is something that’s very easily fixed.

Here’s an example populate task. It’s pretty self explanatory, so I won’t go into much detail. The highlight is the random(model) bit, which allows you to grab a valid object of any kind. In this case, I use this to pull a random User that creates a valid Post. Note how you can still use the cool stuff in Faker and Populator without following the same technique as described in the Railscasts episode.

# lib/tasks/populate.rake
namespace :db do
  task :populate => :environment do
    require 'populator' # http://populator.rubyforge.org/
    require 'faker' # http://faker.rubyforge.org/rdoc/

    [User, Post].each(&:delete_all)
    Rake::Task['db:fixtures:load'].invoke

    def random(model)
      ids = ActiveRecord::Base.connection.select_all("SELECT id FROM #{model.to_s.tableize}")
      model.find(ids[rand(ids.length)]["id"].to_i) unless ids.blank?
    end

    puts 'creating users...'
    10.times { |i|
      User.create(:login => Faker::Lorem.words(1).to_s, :email => Faker::Internet.email, :password => 'monkey')
    }

    puts 'creating posts...'
    40.times { |i|
      random(User).posts.create(:body => Faker::Lorem.words, :created_at => Populator.value_in_range(10.years.ago..Time.now).to_s)
    }    

  end
end

Of course there are other ways to achieve this random(model) business without quite as much overhead. For example, you could load all of the users with one query and then choose a random one like this:

@users = User.all

40.times { |i|
  @users.rand.posts.create(:body => Faker::Lorem.words, :body => Populator.value_in_range(10.years.ago..Time.now).to_s)
}

But I’m not a big believer in premature or unnecessary optimization. In any case, long running Rake tasks make a good excuse to take a walk while it’s still nice outside here in Chicago :)

Validating your Fixtures

Here’s a easy way to ensure that you’re using valid data in your fixtures with Rails and Test::Unit:

# test/integration/fixture_validation_test.rb
require 'test_helper'

class FixtureValidationTest < ActionController::IntegrationTest

  test "fixtures should be valid" do
    models = Fixtures.all_loaded_fixtures.keys
    models.each do |model|
      model = model.camelize.singularize.constantize
      fixtures = model.find(:all)
      fixtures.each do |fixture|
        if !fixture.valid?
          puts; puts "WARNING: Invalid fixture: #{fixture.inspect}"
        end
        assert_valid fixture
      end
    end
  end
end

Please feel free to suggest any improvements. Thanks!