FactorySeeder
A powerful Ruby gem that simplifies database seeding using your existing FactoryBot factories, traits, and associations.
Features
- Automatic Factory Detection: Scans your FactoryBot factories automatically
- Trait Support: Use existing traits to create varied data
- Association Handling: Manage complex relationships between models
- Web Interface: Beautiful web UI for visual seeding with Rails Engine integration
- CLI Interface: Command-line tool for quick seeding
- Custom Seeds System: Define reusable seeds with parameter validation
- Preview Mode: See what data will be generated before creating it
- Configuration: Flexible configuration for different environments
- Auto-reload: Changes to custom seeds are automatically reloaded without server restart
Installation
Add this line to your application's Gemfile:
group :development, :test do
gem 'factory_seeder'
end
Then execute:
bundle install
Quick Start
1. Initialize FactorySeeder
bundle exec factory_seeder init
This creates:
config/initializers/factory_seeder.rb- Configuration filedb/seeds_factory_seeder.rb- Sample seeds file
2. List Available Factories
bundle exec factory_seeder list
The CLI now displays detailed information for each factory including class name, traits, associations, and key attributes - matching the web interface experience.
3. Generate Seeds
Interactive Mode
bundle exec factory_seeder generate
Direct Mode
bundle exec factory_seeder generate user --count=10 --traits=admin,vip
The generate and preview commands now use config.default_count and config.default_strategy when options are omitted.
With Custom Attributes
bundle exec factory_seeder generate user --count=5 --attributes='{"email":"admin@example.com","role":"admin"}'
4. Web Interface (Rails Engine)
Mount the engine in your Rails application routes:
# config/routes.rb
Rails.application.routes.draw do
mount FactorySeeder::Engine => "/factory-seeder"
end
Then access the web interface at http://localhost:3000/factory-seeder
Features:
- Visual factory listing with detailed metadata
- Trait selection with checkboxes
- Custom attribute inputs for each factory field
- Data preview before generation
- Real-time generation
- Auto-reload: Custom seeds under
db/factory_seeds/*.rbare automatically reloaded when the Rails engine prepares a request - no server restart needed
5. Standalone Web Interface
For non-Rails projects or standalone usage:
bundle exec factory_seeder web --port=4567
The standalone web interface calls FactorySeeder.reload! before each request, so file edits take effect immediately.
Usage
Ruby API
Basic Generation
# In your seeds file
FactorySeeder.generate do |seeder|
# Create 10 users with admin trait
seeder.create(:user, count: 10, traits: [:admin])
# Create posts with associations
seeder.create_with_associations(:post, count: 5, associations: {
author: { factory: :user, count: 1 },
comments: { factory: :comment, count: 3 }
})
# Create with custom attributes
seeder.create(:user, count: 5, attributes: {
email: "custom@example.com",
role: "moderator"
})
end
Different Strategies
# Build instead of create (useful for testing)
seeder.create(:user, count: 3, strategy: :build)
Custom Seeds System
Define reusable seeds with parameter validation:
# db/factory_seeds/users.rb
FactorySeeder.define_seed(:create_users) do |builder|
builder
.description("Create users with customizable options")
.integer_param(:count, required: true, min: 1, max: 100, description: "Number of users")
.boolean_param(:admin, required: false, default: false, description: "Create admin users")
.symbol_param(:country, required: false, default: :fr,
allowed_values: [:fr, :en, :es, :de], description: "User country")
end do |count:, admin: false, country: :fr|
traits = admin ? [:admin] : []
count.times do
FactoryBot.create(:user, *traits, country: country)
end
puts "✅ Created #{count} user(s) with country: #{country}#{admin ? ' (admin)' : ''}"
end
Parameter Types
- Integer:
.integer_param(:count, min: 1, max: 100) - Boolean:
.boolean_param(:admin, default: false) - Symbol:
.symbol_param(:status, allowed_values: [:active, :inactive]) - String:
.string_param(:name, allowed_values: ['admin', 'user']) - Array:
.array_param(:items, default: [])
Running Custom Seeds
# Programmatically
result = FactorySeeder.run_custom_seed(:create_users, count: 10, admin: true)
# Via web interface - navigate to custom seeds section
# Via CLI - use the custom seeds commands
CLI Commands
# List all factories with detailed metadata
factory_seeder list
# Generate seeds interactively
factory_seeder generate
# Generate specific factory
factory_seeder generate user --count=5 --traits=admin,vip
# Generate with custom attributes (JSON format)
factory_seeder generate user --count=3 --attributes='{"email":"test@example.com","admin":true}'
# Preview factory data
factory_seeder preview user --traits=admin
# Start standalone web interface
factory_seeder web --port=4567
# Initialize configuration
factory_seeder init
# List available custom seeds
factory_seeder seeds --list
# Run a specific custom seed
factory_seeder seeds development
# Run all custom seeds
factory_seeder seeds --all
# Preview what would be generated (dry run)
factory_seeder seeds development --dry_run
Configuration
Edit config/factory_seeder.rb:
FactorySeeder.configure do |config|
# Add custom factory paths
config.factory_paths << "spec/factories"
config.factory_paths << "test/factories"
# Default options
config.default_count = 10
config.default_strategy = :create
# Verbose mode
config.verbose = Rails.env.development?
# Environment-specific settings
config.environments = {
development: { default_count: 50 },
test: { default_count: 5 },
production: { default_count: 1 }
}
end
Rails Integration
FactorySeeder integrates seamlessly with Rails through a Rails Engine:
Features
- Automatic Model Loading: Models are loaded before factory analysis using
config.after_initialize - Development Reloading: In development, models are reloaded when files change via
config.to_prepare - Conditional Loading: Only forces eager loading when necessary
- Error Handling: Gracefully handles uninitialized constants and missing dependencies
Troubleshooting Rails Integration
"uninitialized constant" errors
If you encounter errors like NameError: uninitialized constant ModelName, the Rails engine should handle this automatically. If issues persist:
- Ensure all migrations are up to date:
rails db:migrate - Check that models are properly defined in
app/models - Enable verbose mode to see detailed loading information:
FactorySeeder.configure do |config|
config.verbose = true
end
Factories not detected
If factories aren't appearing:
FactorySeeder.configure do |config|
config.factory_paths << 'spec/factories'
config.factory_paths << 'test/factories'
end
Factory Examples
Basic Factory
# spec/factories/users.rb
FactoryBot.define do
factory :user do
sequence(:email) { |n| "user#{n}@example.com" }
password { "password123" }
trait :admin do
role { "admin" }
end
trait :vip do
vip_status { true }
end
end
end
Factory with Associations
# spec/factories/posts.rb
FactoryBot.define do
factory :post do
title { Faker::Lorem.sentence }
content { Faker::Lorem.paragraph }
association :author, factory: :user
trait :published do
published_at { Time.current }
end
trait :with_comments do
after(:create) do |post|
create_list(:comment, 3, post: post)
end
end
end
end
Advanced Usage
Environment-Specific Seeds
FactorySeeder.generate do |seeder|
if Rails.env.development?
seeder.create(:user, count: 100, traits: [:admin])
seeder.create(:post, count: 500)
elsif Rails.env.test?
seeder.create(:user, count: 5)
seeder.create(:post, count: 10)
elsif Rails.env.production?
seeder.create(:user, count: 1, traits: [:admin])
end
end
Complex Associations
seeder.create_with_associations(:order, count: 10, associations: {
customer: { factory: :user, traits: [:vip] },
items: { factory: :product, count: 3 },
shipping_address: { factory: :address, count: 1 }
})
Using Rails Model Constants
With the Rails engine, you can safely use model constants in your custom seeds:
FactorySeeder.define_seed(:create_orders_with_status) do |builder|
# Rails models are automatically loaded
order_statuses = if defined?(Order) && Order.const_defined?(:STATUSES)
Order::STATUSES.map(&:to_sym)
else
[:pending, :confirmed, :completed, :cancelled]
end
builder
.description("Create orders with specific status")
.symbol_param(:status, required: true, allowed_values: order_statuses)
end do |status:, count: 1|
count.times do
Order.create!(status: status)
end
end
Development
Setup
git clone https://github.com/wecasa/factory_seeder.git
cd factory_seeder
bundle install
Testing
bundle exec rspec
Building the Gem
gem build factory_seeder.gemspec
gem install factory_seeder-0.1.0.gem
Release Process
The gem is published to RubyGems automatically from CI using Trusted Publishing (OIDC). No API key is stored in the repo.
How it works
mainis protected: every change lands via a pull request approved by a Wecasa engineer.- The release workflow (
.github/workflows/release.yml) runs on every merge tomain. - If
lib/factory_seeder/version.rbhas been bumped (i.e. no git tag matches the current version yet), the workflow:- Runs the specs.
- Tags the merge commit
vX.Y.Z. - Builds the gem and pushes it to RubyGems via OIDC.
- Creates a GitHub Release with auto-generated notes and attaches the
.gemfile.
- If the version on
mainalready has a tag, the workflow is a no-op.
How to ship a new version
- Open a PR from a feature branch.
- In the same PR, bump
lib/factory_seeder/version.rbfollowing semver:- patch (
0.1.0->0.1.1) - bug fix, no API change - minor (
0.1.0->0.2.0) - new feature, backward compatible - major (
0.1.0->1.0.0) - breaking change
- patch (
- Add a corresponding entry at the top of
CHANGELOG.md. - Get the PR approved by a Wecasa engineer (branch protection enforces this).
- Merge. The release workflow tags the commit and publishes the gem within a minute or two.
Ship without releasing
If your PR does not change the public API (docs, internal refactor, CI tweak), leave version.rb untouched. The workflow will detect that the version already has a tag and skip the release step. No manual step needed.
Gem ownership
The gem is owned by the devops@wecasa.fr RubyGems service account (MFA enabled). Human maintainers are added as co-owners via:
gem owner factory_seeder -a <email>
Trusted Publishing is configured at https://rubygems.org/profile/oidc/pending_trusted_publishers (or the gem page once a version is published) with:
- Repository:
wecasa/factory_seeder - Workflow filename:
release.yml - Environment: (empty)
Architecture
Core Components
- FactoryScanner: Automatically detects and analyzes FactoryBot factories
- SeedGenerator: Creates database records using factories and traits
- SeedManager: Manages custom seed definitions with parameter validation
- Seed & SeedBuilder: Define reusable seeds with type-safe parameters
- CLI: Command-line interface with interactive prompts
- WebInterface: Sinatra-based web UI for visual seeding
- Engine: Rails Engine for seamless Rails integration
- Configuration: Flexible configuration system
Dependencies
- Ruby: >= 2.7.0
- FactoryBot: ~> 6.0
- ActiveSupport: >= 6.0
- Thor: ~> 1.0 (CLI)
- Sinatra: ~> 2.0 (Web interface)
- Faker: ~> 3.0 (Test data generation)
- Zeitwerk: ~> 2.6 (Autoloading)
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This gem is available as open source under the terms of the MIT License.
Support
- Issues: GitHub Issues
- Email: team@factoryseeder.com
Acknowledgments
- Built on top of FactoryBot
- CLI powered by Thor
- Web interface built with Sinatra
- Autoloading with Zeitwerk
Made with ❤️ by Wecasa