Skip to content

Instantly share code, notes, and snippets.

@matthutchinson
Created February 10, 2010 16:49
Show Gist options
  • Star 32 You must be signed in to star a gist
  • Fork 14 You must be signed in to fork a gist
  • Save matthutchinson/300536 to your computer and use it in GitHub Desktop.
Save matthutchinson/300536 to your computer and use it in GitHub Desktop.
fakeout.rake - a simple/configurable rake task that generates some random fake data for the app (using faker) at various sizes
# a simple/configurable rake task that generates some random fake data for the app (using faker) at various sizes
# NOTE: requires the faker or ffaker gem
# sudo gem install faker - http://faker.rubyforge.org
# OR
# sudo gem install ffaker - http://github.com/EmmanuelOga/ffaker
require 'faker'
class Fakeout
# START Customizing
# 1. first these are the model names we're going to fake out, note in this example, we don't create tags/taggings specifically
# but they are defined here so they get wiped on the clean operation
# e.g. this example fakes out, Users, Questions and Answers, and in doing so fakes some Tags/Taggings
MODELS = %w(User Question Answer Tag Tagging)
# 2. now define a build method for each model, returning a list of attributes for Model.create! calls
# check out the very excellent faker gem rdoc for faking out anything from emails, to full addresses; http://faker.rubyforge.org/rdoc
# NOTE: a build_??? method MUST exist for each model you specify above
def build_user(username = "#{Faker::Internet.user_name}_#{random_letters}", email = Faker::Internet.email, password = 'password')
{ :username => username,
:email => email,
:password => password,
:password_confirmation => password }
end
# in this example i'm faking out time - like Marty McFly!
def build_question
question_time = fake_time_from(1.year.ago)
{ :title => "#{Faker::Lorem.sentence(8+rand(8)).chop}?",
:information => Faker::Lorem.paragraph(rand(3)),
:tag_list => random_tag_list(all_tags),
:notify_user => false,
:created_at => question_time,
:updated_at => question_time,
:spam_answer => '2',
:spam_question => '1+1 is?',
:possible_answers => [Digest::MD5.hexdigest('2')],
:user => pick_random(User, true) }
end
# in this example i'm faking out time again! - this time to be after the question's created at time
def build_answer
question = pick_random(Question)
answer_time = question.created_at+rand(168).hours
{ :title => Faker::Lorem.paragraph(1+rand(4)),
:question => question,
:created_at => answer_time,
:updated_at => answer_time,
:user => pick_random(User, true),
:spam_answer => '2',
:spam_question => '1+1 is?',
:possible_answers => [Digest::MD5.hexdigest('2')],
}
end
# return nil, or an empty hash for models you don't want to be faked out on create, but DO want to be clearer away
def build_tag; end
def build_tagging; end
# called after faking out, use this method for additional updates or additions
def post_fake
User.create!(build_user('matt', 'matt@hiddenloop.com', 'password'))
User.update_all('email_confirmed = true')
end
# 3. optionally you can change these numbers, basically they are used to determine the number of models to create
# and also the size of the tags array to choose from. To check things work quickly use the tiny size (1 for everything)
def tiny
1
end
def small
25+rand(50)
end
def medium
250+rand(250)
end
def large
1000+rand(500)
end
# END Customizing
attr_accessor :all_tags, :size
def initialize(size, prompt=true)
self.size = size
self.all_tags = Faker::Lorem.words(send(size))
end
def fakeout
puts "Faking it ... (#{size})"
Fakeout.disable_mailers
MODELS.each do |model|
if !respond_to?("build_#{model.downcase}")
puts " * #{model.pluralize}: **warning** I couldn't find a build_#{model.downcase} method"
next
end
1.upto(send(size)) do
attributes = send("build_#{model.downcase}")
model.constantize.create!(attributes) if attributes && !attributes.empty?
end
puts " * #{model.pluralize}: #{model.constantize.count(:all)}"
end
post_fake
puts "Done, I Faked it!"
end
def self.prompt
puts "Really? This will clean all #{MODELS.map(&:pluralize).join(', ')} from your #{RAILS_ENV} database y/n? "
STDOUT.flush
(STDIN.gets =~ /^y|^Y/) ? true : exit(0)
end
def self.clean(prompt = true)
self.prompt if prompt
puts "Cleaning all ..."
Fakeout.disable_mailers
MODELS.each do |model|
model.constantize.delete_all
end
end
# by default, all mailings are disabled on faking out
def self.disable_mailers
ActionMailer::Base.perform_deliveries = false
end
private
# pick a random model from the db, done this way to avoid differences in mySQL rand() and postgres random()
def pick_random(model, optional = false)
return nil if optional && (rand(2) > 0)
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
# useful for prepending to a string for getting a more unique string
def random_letters(length = 2)
Array.new(length) { (rand(122-97) + 97).chr }.join
end
# pick a random number of tags up to max_tags, from an array of words, join the result with seperator
def random_tag_list(tags, max_tags = 5, seperator = ',')
start = rand(tags.length)
return '' if start < 1
tags[start..(start+rand(max_tags))].join(seperator)
end
# fake a time from: time ago + 1-8770 (a year) hours after
def fake_time_from(time_ago = 1.year.ago)
time_ago+(rand(8770)).hours
end
end
# the tasks, hook to class above - use like so;
# rake fakeout:clean
# rake fakeout:small[noprompt] - no confirm prompt asked, useful for heroku or non-interactive use
# rake fakeout:medium RAILS_ENV=bananas
#.. etc.
namespace :fakeout do
desc "clean away all data"
task :clean, [:no_prompt] => :environment do |t, args|
Fakeout.clean(args.no_prompt.nil?)
end
desc "fake out a tiny dataset"
task :tiny, [:no_prompt] => :clean do |t, args|
Fakeout.new(:tiny).fakeout
end
desc "fake out a small dataset"
task :small, [:no_prompt] => :clean do |t, args|
Fakeout.new(:small).fakeout
end
desc "fake out a medium dataset"
task :medium, [:no_prompt] => :clean do |t, args|
Fakeout.new(:medium).fakeout
end
desc "fake out a large dataset"
task :large, [:no_prompt] => :clean do |t, args|
Fakeout.new(:large).fakeout
end
end
@sadfuzzy
Copy link

sadfuzzy commented May 8, 2012

How can i overcome mass_assignment protection gracefully? Now i have to add and remove :user_id each time i have to repopulate my db. :( Is there any way to generate random images for gallery?

@matthutchinson
Copy link
Author

Since the faker always calls, model.constantize.create!(attributes) this means in its current incarnation, you can't pass attributes that are protected from mass assignment. To generate random images for a gallery, you could put a bunch of images in a test or data folder and name them; image_1.jpg image_2.jpg etc. - then in this script assign the image to the model using the fixture_file_upload method.

@matthutchinson
Copy link
Author

FYI, this is quite an old gist, and my new approach to faking goes a little like this; https://gist.github.com/2654461

@sadfuzzy
Copy link

Thanks!

@rmcsharry
Copy link

Anyone coming here, goto Matt's new approach it is VASTLY improved!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment