Ruby seed output wrapper
The Rails-Provided Method
Rails has a handy method that outputs a count and the time it took for a block to run. It’s a nice tool for benchmarking. Here’s how it works:
# db/seeds.rb
# 1. Pass your message as an argument
ActiveRecord::Migration.say_with_time('Seeding kittens') do
# 2. Do something in this block. I'm choosing to create some new records.
names = %w[Murph Mindy Jeff]
names.each do |name|
Kitten.find_or_create_by!(name: name)
end
# 3. As the last line of this block, return a count.
Kitten.count
end
The terminal output you’ll see will have your message, the count, and the time it took to run this block:
Seeding kittens
-> 3
-> 0.1s
Neat! Since running our seeds file at work took several minutes, I was using this method to give more informative terminal output. I personally hate staring at a blank terminal and wondering if a process is running or if it has gotten hung up somewhere. As I implemented this method and ran it with a lot of different seeds, I found that while the overall output was improved, the count indication was still ambiguous. Most of our seeds are find-or-create-by in nature – but not all of them are – so it is helpful to see terminal output that:
- indicates which model is being processed
- how many new records were just created
- how many total records we now have for that model
- how long it took to seed that model
In other words, like this:
Seeding kittens
-> 0 new
-> 3 total
-> 0.15s
The Custom Method
So a coworker and I decided to roll our own seed wrapper output method. In a new file we called db/seeds_helper.rb
, we wrote a method called count_records_for
. It takes a single argument of the model being seeded and it handles all the rest:
# db/seeds_helper.rb
module SeedsHelper
def self.count_records_for(model)
puts "Seeding #{model.table_name}"
starting_record_count = model.count
starting_time = Time.current
yield
ending_count = model.count
puts " -> #{ending_count - starting_record_count} new"
puts " -> #{ending_count} total"
puts " -> #{(Time.current - starting_time).round(3)}s"
end
end
Now we can call it like this:
# db/seeds.rb
SeedsHelper.count_records_for(Kitten) do
names = %w[Murph Mindy Jeff]
names.each do |name|
Kitten.find_or_create_by!(name: name)
end
end
And the output looks like…
# The first time it's run:
Seeding kittens
-> 3 new
-> 3 total
-> 0.15s
# The second time it's run, since the records are find-or-create-by
# we shouldn't see any new records:
Seeding kittens
-> 0 new
-> 3 total
-> 0.15s
Check out this StackOverflow answer for a refresher on how Ruby’s yield
works.