seedify
Let your seed code become a first-class member of the Rails app and put it into seed objects. seedify allows to implement and organize seeds in object-oriented fashion, putting them into app/seeds
alongside the controllers, models etc. It also provides handy syntax for command line parameters and progress logging.
Here's an overview of what you can achieve with seedify:
- organize seed code in object-oriented, Rails convention fitting fashion
- take advantage of inheritance and modularization when writing seeds
- invoke seeds as rake tasks or from within the app/console
- allow to specify custom parameters for your seeds, typed and with defaults
- log the seed progress (e.g. mass creation) without effort
- combine with factory_girl to simplify object creation and share fixtures with specs
Usage
Basic example
You should start by implementing the ApplicationSeed
class:
# app/seeds/application_seed.rb
class ApplicationSeed < Seedify::Base
def call
if Admin.empty?
log 'create first admin'
Admin.create!(email: '[email protected]', password: 'password')
else
log "admin already exists: '#{Admin.first.email}'"
end
end
end
This will create an admin if there's none and output existing admin's e-mail otherwise.
Now, call the seed as rake task:
rake seedify
or from Rails console:
ApplicationSeed.call
Using fixtures
factory_girl is great for sharing model fixtures between tests and seeds. Add factory_girl_rails
gem to development group and include its methods, so you can use #create
in seeds:
class ApplicationSeed < Seedify::Base
include FactoryGirl::Syntax::Methods
def call
create :admin
end
end
Custom parameters
You'll often need to parameterize your seed. You can do this easily via param_reader
:
class ApplicationSeed < Seedify::Base
include FactoryGirl::Syntax::Methods
param_reader :prefix, default: 'user'
param_reader :count, type: :integer, default: 5
param_reader :with_post, type: :boolean, default: false
def call
count.times do |index|
user = create :user, email: "#{prefix}-#{index}@example.com"
create :post, user: user if with_post?
end
end
end
You can specify the type of your param and have it casted automatically. Supported types are:
:string
default type, default value: nil:integer
default value: 0:boolean
default value: false, accessor with?
suffix
Specify the parameters for rake task:
rake seedify prefix=guy count=20 with_post=true
or from Rails console:
ApplicationSeed.call prefix: 'guy', count: 20, with_post: true
Logging
Seed with detailed progress logging feels responsive and is easier to debug in case of a failure. That's why seedify provides three log methods for your convenience:
Simple message (block is optional):
log "create user '#{email}'" do
create :user, email: email
end
Array evaluation with live progress:
log_each ['a', 'b', 'c'], 'create users' do |prefix|
create :user, email: "#{prefix}@example.com"
end
N-times evaluation with live progress:
log_times 5, 'create users' do |n|
create :user, email: "user-#{n}@example.com"
end
Notes:
log_each
/log_times
is great for creating large amounts of data with live progress indication- texts between
'
,"
or*
(like in first example above) quotes are colorized for emphasis - logging to
stdout
is enabled by default; disable it by setting thelog
parameter to false
Organizing seed code
Your seed code will grow along with application development so keeping it clean and well-separated will become important. It's easy thanks to seedify's object-oriented approach. Imagine all-in-one application seed like below:
class ApplicationSeed < Seed::Base
include FactoryGirl::Syntax::Methods
param_reader :admin_email, default: '[email protected]'
param_reader :user_prefix, default: 'user'
param_reader :user_count, type: :integer, default: 5
param_reader :clear_method, default: 'destroy'
def call
clear_model(Admin)
clear_model(User)
log "create admin *#{admin_email}* with root priviliges" do
create :admin, :with_root_priviliges, email: admin_email
end
log user_count, 'create users' do |index|
create :user, email: "#{user_prefix}-#{index}@example.com"
end
end
private
def clear_model(model)
case clear_method
when 'truncate'
ActiveRecord::Base.connection.execute("TRUNCATE #{model.table_name}")
when 'destroy'
model.destroy_all
end
end
end
You can separate the seed code into domains that fit your application best (including modules). In this case, we'll create model-oriented seeds for Admin
and User
models and keep application-wide code and param readers in application seed:
# app/seeds/application_seed.rb
class ApplicationSeed < Seed::Base
include FactoryGirl::Syntax::Methods
param_reader :clear_method, default: 'destroy'
def call
AdminSeed.call
UserSeed.call
end
protected
def clear_model(model)
case clear_method
when 'truncate'
ActiveRecord::Base.connection.execute("TRUNCATE #{model.table_name}")
when 'destroy'
model.destroy_all
end
end
end
# app/seeds/admin_seed.rb
class AdminSeed < ApplicationSeed
param_reader :admin_email, default: '[email protected]'
def call
clear_model(Admin)
log "create admin *#{admin_email}* with root priviliges" do
create :admin, :with_root_priviliges, email: admin_email
end
end
end
# app/seeds/user_seed.rb
class UserSeed < ApplicationSeed
param_reader :user_prefix, default: 'user'
param_reader :user_count, type: :integer, default: 5
def call
clear_model(User)
log user_count, 'create users' do |index|
create :user, email: "#{user_prefix}-#{index}@example.com"
end
end
end
Params defined in application seed will be available in seeds that inherit from it too.
You can now invoke each seed separately:
rake seedify:admin clear_method=none
rake seedify:user user_count=10
or all at once like before with rake seedify
.
You should try to write each seed object in a way that makes it possible to use it either stand-alone or as a sub-seed called from another seed object. This way, for instance, you'll be able to generate more objects in specific domain without touching rest of the system or recreate whole system data - depending on your needs - with the same seed code.
You can customize sub-seed invocation with a syntax you already know - the call parameters:
# excerpt from app/seeds/application_seed.rb
UserSeed.call user_prefix: 'user_generated_by_app_seed'
This way, within UserSeed, the user_prefix
param will equal to user_generated_by_app_seed regardless of the one specified when calling the application seed from console or command-line.
Contributing
- Fork it (https://github.com/visualitypl/seedify/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request