Rails Generators Expert
Use the right generator with the right flags. Generate exactly what you need — nothing more.
Philosophy
Core Principles:
- Generate the minimum — Use
modelwhen you need a model, notscaffold. Usemigrationwhen you need a migration, notmodel. - Skip what you won't use — Pass
--no-helper,--no-assets,--no-jbuilderunless explicitly needed (or configure defaults inconfig.generators). - Know your generators — Rails has 30+ built-in generators. Learn them before writing code by hand.
- Check project defaults first — Read
config/application.rbforconfig.generatorsbefore running anything. - Destroy before regenerating — Use
bin/rails destroyto cleanly undo a generator before re-running with different options.
When To Use This Skill
- Running any
bin/rails generatecommand - Deciding which generator fits the task
- Customizing generator defaults in
config/application.rb - Creating custom generators for project-specific patterns
- Overriding built-in generator templates
- Understanding what files a generator will create
Instructions
Step 1: Check Project Generator Config
Check existing generator configuration first — the project may already skip helpers, use UUIDs, or set other defaults:
# Check for custom generator config
grep -A 20 "config.generators" config/application.rb
# See all available generators
bin/rails generate --help
# See specific generator options
bin/rails generate model --help
Match project conventions. If the project disables helpers or uses UUIDs, your generators should too.
Step 2: Choose the Right Generator
Pick the most specific generator that does what you need:
| Need | Generator | NOT |
|------|-----------|-----|
| Database column change | migration | model |
| New model with table | model | scaffold |
| New controller + views | controller | scaffold |
| Full CRUD resource | scaffold | — |
| API-only resource | scaffold --api | scaffold |
| Background job | job | Writing by hand |
| Mailer | mailer | Writing by hand |
| Database table change | migration | Writing by hand |
The Generator Hierarchy (most → least specific):
migration → model → resource → scaffold
↗
controller ------/
Each level adds more files. Always pick the lowest level that satisfies the requirement.
Step 3: Use the Right Flags
Essential flags to know:
# Skip unwanted files
--no-helper # Skip helper file (almost always want this)
--no-assets # Skip CSS/JS files
--no-jbuilder # Skip JSON builder views
--no-test-framework # Skip test files (if writing tests separately)
--no-fixture # Skip fixture file only
# Control behavior
--skip-migration # Generate model without migration
--skip-routes # Don't add to routes.rb
--force # Overwrite existing files
--pretend # Dry run — show what would happen
# Field types
title:string # Default type, can omit :string
body:text # Long text
price:decimal # Decimal number
published:boolean # Boolean
published_at:datetime # Timestamp
user:references # Foreign key + belongs_to
tags:jsonb # JSON column (PostgreSQL)
Step 4: Run with --pretend First
For any generator that creates multiple files, dry-run first — it's easier to verify than to clean up:
# See what would be created without creating it
bin/rails generate scaffold Post title body:text --pretend
This prevents generating unwanted files you'll have to clean up.
Step 5: Destroy Before Regenerating
If you made a mistake, destroy first:
# Undo a generator completely
bin/rails destroy model Post
bin/rails destroy scaffold Post
bin/rails destroy controller Posts
destroy removes exactly what generate created. Don't manually delete files.
Step 6: Configure Defaults
Set project-wide defaults in config/application.rb:
config.generators do |g|
g.orm :active_record, primary_key_type: :uuid
g.test_framework :test_unit, fixture: true
g.helper false # Never generate helpers
g.assets false # Never generate assets
g.jbuilder false # Never generate jbuilder views
g.system_tests :test_unit # Or nil to skip
g.template_engine :erb # Or :slim, :haml
g.stylesheets false # Skip stylesheets
g.javascripts false # Skip JS files
end
Once configured, you don't need --no-helper etc. on every command.
Built-In Generators Quick Reference
Most Used
| Generator | Creates | Example |
|-----------|---------|---------|
| model | Model + migration + test + fixture | bin/rails g model Post title body:text |
| migration | Migration file only | bin/rails g migration AddSlugToPosts slug:string:uniq |
| controller | Controller + views + test + route + helper | bin/rails g controller Posts index show |
| scaffold | Model + controller + views + migration + tests + routes | bin/rails g scaffold Post title body:text |
| resource | Model + controller (empty) + migration + route | bin/rails g resource Post title body:text |
Other Built-In
| Generator | Creates | Example |
|-----------|---------|---------|
| job | Job class + test | bin/rails g job ProcessPayment |
| mailer | Mailer + views + test | bin/rails g mailer UserMailer welcome reset_password |
| channel | Action Cable channel + JS | bin/rails g channel Chat speak |
| task | Rake task | bin/rails g task feeds fetch |
| mailbox | Action Mailbox mailbox + test | bin/rails g mailbox Forwards |
| generator | Custom generator skeleton | bin/rails g generator my_generator |
| integration_test | Integration test file | bin/rails g integration_test UserFlows |
| system_test | System test file | bin/rails g system_test UserFlows |
| scaffold_controller | Controller + views (no model/migration) | bin/rails g scaffold_controller Post title body:text |
| helper | Helper module + test | bin/rails g helper Posts |
Migration Name Conventions
Rails infers migration behavior from the name:
# Add columns (AddXxxToYyy)
bin/rails g migration AddSlugToPosts slug:string:uniq
# Remove columns (RemoveXxxFromYyy)
bin/rails g migration RemoveBodyFromPosts body:text
# Create join table (CreateJoinTableXxxYyy)
bin/rails g migration CreateJoinTablePostsCategories posts categories
# Standalone (no inference — empty change method)
bin/rails g migration UpdatePostsSchema
Field Type Reference
# Common types
string # VARCHAR (255 default)
text # TEXT (unlimited)
integer # INT
float # FLOAT
decimal # DECIMAL (use for money)
boolean # BOOLEAN
date # DATE
datetime # DATETIME
timestamp # TIMESTAMP
time # TIME
binary # BLOB
# Special types
references # Foreign key (adds _id column + index + belongs_to)
belongs_to # Alias for references
jsonb # JSONB (PostgreSQL)
json # JSON
uuid # UUID
inet # INET (PostgreSQL)
citext # Case-insensitive text (PostgreSQL)
# Modifiers
title:string{100} # String with limit
price:decimal{8,2} # Precision and scale
slug:string:uniq # Add unique index
slug:string:index # Add index
user:references{polymorphic} # Polymorphic association
Creating Custom Generators
When to Create One
Create a custom generator when your project has repeating patterns:
- Service objects with consistent structure
- Form objects
- Query objects
- Policy classes
- Decorators/presenters
How to Create
bin/rails generate generator service
Creates:
lib/generators/service/
├── service_generator.rb
├── USAGE
└── templates/
Generator Anatomy
# lib/generators/service/service_generator.rb
class ServiceGenerator < Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)
# Optional: add arguments and options
class_option :async, type: :boolean, default: false,
desc: "Generate an async service with ActiveJob"
def create_service_file
template "service.rb.tt", "app/services/#{file_name}_service.rb"
end
def create_test_file
template "service_test.rb.tt", "test/services/#{file_name}_service_test.rb"
end
private
def async?
options[:async]
end
end
Template Files
# lib/generators/service/templates/service.rb.tt
class <%= class_name %>Service
def initialize(<%= file_name %>)
@<%= file_name %> = <%= file_name %>
end
def call
# TODO: implement
end
private
attr_reader :<%= file_name %>
end
Available Template Variables
| Variable | Example Input | Result |
|----------|--------------|--------|
| name | admin/user_cleanup | admin/user_cleanup |
| file_name | admin/user_cleanup | user_cleanup |
| class_name | admin/user_cleanup | Admin::UserCleanup |
| human_name | admin/user_cleanup | Admin user cleanup |
| plural_name | user | users |
| singular_name | users | user |
| table_name | user | users |
| file_path | admin/user_cleanup | admin/user_cleanup |
| class_path | admin/user_cleanup | ["admin"] |
Overriding Built-In Templates
Override any built-in generator's templates by placing files in lib/templates/:
lib/templates/
├── erb/
│ └── scaffold/
│ ├── index.html.erb.tt
│ ├── show.html.erb.tt
│ ├── _form.html.erb.tt
│ └── _resource.html.erb.tt
├── rails/
│ └── scaffold_controller/
│ └── controller.rb.tt
└── active_record/
└── migration/
└── create_table_migration.rb.tt
Rails checks lib/templates/ before its own templates. No config needed.
Template escaping: Generator templates that produce ERB need double escaping:
<%%=in template →<%=in output<%=in template → evaluated at generation time
Common Agent Mistakes
-
Using
scaffoldwhenmodelsuffices — Scaffold creates controller, views, routes, helpers, and tests. If you only need a model, usemodel— less cleanup, less noise. -
Forgetting
--no-helper— Helper files are rarely used in modern Rails. Generating them just adds clutter. -
Skipping
--pretend— Dry-running complex generators takes a second and prevents generating files you'll have to manually clean up. -
Writing migrations by hand —
bin/rails g migrationwith naming conventions (AddXxxToYyy, RemoveXxxFromYyy) auto-generates the body. Save the typing. -
Generating then manually deleting files — Use
bin/rails destroyinstead. It cleanly reverts route additions and removes all generated files. -
Not knowing about
scaffold_controller— When you have a model but need controller+views, usescaffold_controller. Runningscaffoldwould try to recreate the model. -
Ignoring project defaults — Check
config.generatorsfirst. The project may already skip helpers, making--no-helperredundant. -
Creating custom generators for one-offs — Custom generators pay off when you repeat a pattern 3+ times. For one-offs, just write the files.
Performance Tips
--pretendis free — Always use it to verify before generating- Batch field definitions —
bin/rails g model Post title body:text published:booleanis one migration, not three - Use
migrationfor schema changes — Don't generate a new model to add a column
Running Generators
# Standard
bin/rails generate model Post title body:text
bin/rails g model Post title body:text # Short alias
# Dry run
bin/rails g model Post title --pretend
# Destroy (undo)
bin/rails destroy model Post
bin/rails d model Post # Short alias
# List all available
bin/rails generate --help
# Generator-specific help
bin/rails g model --help
bin/rails g migration --help
bin/rails g scaffold --help
Debugging Generators
# See what a generator would do
bin/rails g model Post --pretend
# Check generator resolution path
bin/rails g model --help | head -5
# Find where templates live
find lib/templates -name "*.tt" 2>/dev/null
find $(bundle show railties)/lib/rails/generators -name "*.tt" | head -20
# Check current generator config
grep -A 20 "config.generators" config/application.rb
Detailed Reference
See the references/ directory for complete examples and edge cases:
references/built-in-generators.md— All built-in generators with flags, files created, and customizing defaultsreferences/custom-generators.md— Creating custom generators, hooks/composition, resolution orderreferences/templates.md— Overriding built-in templates, application templates forrails newreferences/thor-actions.md— Thor file manipulation methods (create_file, inject_into_file, gsub_file, etc.)references/testing.md— Generator testing, edge cases (STI, UUID, namespaces), recipes, troubleshooting