| name | railstart-preset-builder |
| description | Guide for creating railstart preset configuration files. Use when building new presets, customizing Rails 8 app generation, creating team-specific configurations, or defining opinionated Rails stacks. Covers YAML structure, ID-based merging, post-action configuration, and testing presets for the railstart gem. |
Railstart Preset Builder
Purpose
This skill guides you through creating preset configuration files for the railstart gem. Presets enable quick Rails 8 application generation with predefined choices for database, CSS, JavaScript, test framework, and post-generation actions.
When to Use This Skill
Use this skill when:
- Creating new railstart presets for specific tech stacks
- Defining team or organization default configurations
- Building quick-start templates for common Rails patterns
- Customizing Rails app generation workflows
- Setting up opinionated Rails configurations
Preset System Overview
Three-Layer Configuration
Railstart uses a three-layer configuration merge system:
- Base Config (
config/rails8_defaults.yaml) - Defines all available questions and post-actions - User Config (
~/.config/railstart/config.yaml) - User's personal overrides (optional) - Preset Config (
config/presets/*.yaml) - Named preset that overrides defaults
Merge Order: Base → User → Preset (later layers win)
ID-Based Merging
Questions and post-actions merge by unique id field:
- Questions with matching IDs have their
choicesarray replaced entirely - Individual choice
defaultflags override base configuration - Post-actions with matching IDs have their
enabledstate overridden
multi_select Defaults
For multi_select questions (like skip_features), always use choice values (stable IDs), not display names:
# ✅ CORRECT - Uses stable value identifiers
- id: skip_features
default:
- action_mailer # Stable internal ID
- action_text # Won't break if display text changes
- hotwire
# ❌ WRONG - Uses display names (fragile)
- id: skip_features
default:
- Action Mailer # Breaks if wording changes
- Hotwire (Turbo + Stimulus) # Fragile, avoid
Why values are better:
- Stability: Display text can change for UX improvements without breaking presets
- Consistency: Values are used throughout the system (CommandBuilder, answers storage)
- Correctness: Generator automatically transforms values → names for TTY::Prompt display
Available Questions
From config/rails8_defaults.yaml:
Database (select)
- ID:
database - Choices:
sqlite3,postgresql,mysql - Default:
sqlite3 - Flag:
--database=%{value}
CSS Framework (select)
- ID:
css - Choices:
tailwind,bootstrap,bulma,postcss,sass,none - Default:
tailwind - Flag:
--css=%{value}
JavaScript Approach (select)
- ID:
javascript - Choices:
importmap,bun,esbuild,rollup,webpack,vite,none - Default:
importmap - Flags: Choice-level flags (e.g.,
--javascript=bunor--skip-javascript)
Test Framework (select)
- ID:
test_framework - Choices:
minitest,rspec - Default:
minitest - Flag: RSpec uses
--skip-test
Skip Features (multi_select)
- ID:
skip_features - Values:
action_mailer,action_mailbox,action_text,active_record,active_job,active_storage,action_cable,hotwire - Default:
[](none skipped) - Flags: Individual choice flags (e.g.,
--skip-action-mailer) - Note: Use values in
defaultarray, not display names (see multi_select Defaults section above)
Boolean Options (yes_no)
- IDs:
api_only,skip_git,skip_docker,skip_bundle - Flags: Various (e.g.,
--api,--skip-git)
Available Post-Actions
init_git
- Default: Enabled if
skip_git= false - Command:
git init && git add . && git commit -m 'Initial commit' - Purpose: Initialize git repository
bundle_install
- Default: Disabled
- Condition: Only if
skip_bundle= true - Command:
bundle install - Purpose: Manual gem installation
setup_rspec
- Default: Enabled
- Condition: Only if
test_framework=rspec - Command:
bundle add rspec-rails --group development,test && bundle exec rails generate rspec:install - Purpose: Automatic RSpec setup
setup_vite
- Default: Enabled
- Condition: Only if
javascript=vite - Command:
bundle add vite_rails && bundle install && bundle exec vite install - Purpose: Automatic Vite Rails setup
setup_bundlebun
- Default: Disabled
- Condition: Only if
javascript=bun - Command:
bundle add bundlebun && bundle install && bundle exec rake bun:install - Purpose: Optional Bundlebun (Bun as a gem) setup
Preset Creation Workflow
Step 1: Define Requirements
Answer these questions:
- What stack does this preset target? (e.g., "API-only", "Modern SPA", "Traditional Rails")
- Which database? (PostgreSQL for production, SQLite for quick prototypes)
- Which CSS framework? (Tailwind for utility-first, none for APIs)
- Which JavaScript approach? (Vite for SPAs, importmap for traditional, none for APIs)
- Which test framework? (Minitest default, RSpec for BDD)
- Which features to skip? (Action Mailer, Hotwire, etc. for APIs)
- Which post-actions to enable? (Vite setup, Bundlebun, RSpec)
Step 2: Create Preset File
Location: config/presets/{preset-name}.yaml
Naming Convention:
- Lowercase with hyphens
- Descriptive of stack (e.g.,
vite-bun,api-only,default) - Short and memorable
Basic Structure:
---
# Preset Name - Brief Description
# Explain what this preset configures and when to use it
questions:
- id: database
choices:
- name: PostgreSQL
value: postgresql
default: true
- id: css
choices:
- name: Tailwind
value: tailwind
default: true
- id: javascript
choices:
- name: Importmap (default)
value: importmap
default: true
- id: test_framework
choices:
- name: Minitest (default)
value: minitest
default: true
- id: skip_features
default: []
- id: api_only
default: false
- id: skip_git
default: false
- id: skip_docker
default: false
- id: skip_bundle
default: false
post_actions:
- id: init_git
name: "Initialize git repository"
enabled: true
command: "git init && git add . && git commit -m 'Initial commit'"
- id: setup_rspec
name: "Setup RSpec"
enabled: false
command: "bundle add rspec-rails --group development,test && bundle exec rails generate rspec:install"
- id: setup_vite
name: "Setup Vite Rails"
enabled: false
command: "bundle add vite_rails && bundle install && bundle exec vite install"
- id: setup_bundlebun
name: "Setup Bundlebun (Bun packaged as a gem)"
enabled: false
command: "bundle add bundlebun && bundle install && bundle exec rake bun:install"
Step 3: Override Defaults
Only include questions you want to override. Minimal example:
---
# API-Only Preset - Minimal Rails for JSON APIs
questions:
- id: database
choices:
- name: PostgreSQL
value: postgresql
default: true
- id: css
choices:
- name: None
value: none
default: true
- id: skip_features
default:
- action_mailer
- action_text
- hotwire
- id: api_only
default: true
post_actions:
- id: init_git
name: "Initialize git repository"
enabled: true
command: "git init && git add . && git commit -m 'Initial commit'"
Step 4: Enable Post-Actions
Enable relevant post-actions based on your choices. IMPORTANT: Always include complete post-action definitions with name and command:
post_actions:
- id: setup_vite
name: "Setup Vite Rails"
enabled: true
command: "bundle add vite_rails && bundle install && bundle exec vite install"
- id: setup_bundlebun
name: "Setup Bundlebun (Bun packaged as a gem)"
enabled: true
command: "bundle add bundlebun && bundle install && bundle exec rake bun:install"
- id: setup_rspec
name: "Setup RSpec"
enabled: true
command: "bundle add rspec-rails --group development,test && bundle exec rails generate rspec:install"
Step 5: Test Preset
Manual Testing:
# Test preset loading
ruby -ryaml -e "puts YAML.load_file('config/presets/your-preset.yaml').inspect"
# Test with railstart (if installed locally)
bundle exec exe/railstart new testapp --preset your-preset --default
# Verify generated app
cd testapp
cat Gemfile # Check for expected gems
ls -la # Verify structure
Automated Testing:
# Run test suite
bundle exec rake test
# Run linter
bundle exec rubocop
Real-World Examples
Example 1: Modern SPA Stack (vite-bun.yaml)
---
# Vite + Bundlebun Preset - Modern frontend with Vite and Bun packaged as a gem
questions:
- id: database
choices:
- name: PostgreSQL
value: postgresql
default: true
- id: css
choices:
- name: Tailwind
value: tailwind
default: true
- id: javascript
choices:
- name: Vite (via vite_rails gem)
value: vite
default: true
- id: test_framework
choices:
- name: Minitest (default)
value: minitest
default: true
- id: skip_features
default: []
- id: api_only
default: false
- id: skip_git
default: false
- id: skip_docker
default: false
- id: skip_bundle
default: false
post_actions:
- id: init_git
name: "Initialize git repository"
enabled: true
command: "git init && git add . && git commit -m 'Initial commit'"
- id: setup_vite
name: "Setup Vite Rails"
enabled: true
command: "bundle add vite_rails && bundle install && bundle exec vite install"
- id: setup_bundlebun
name: "Setup Bundlebun (Bun packaged as a gem)"
enabled: true
command: "bundle add bundlebun && bundle install && bundle exec rake bun:install"
Use Case: Building modern SPAs with fast HMR (Vite) and unified JavaScript runtime (Bundlebun)
Usage:
railstart new myapp --preset vite-bun
Example 2: API-Only Stack (api-only.yaml)
---
# API-Only Preset - Minimal Rails app for JSON APIs
questions:
- id: database
choices:
- name: PostgreSQL
value: postgresql
default: true
- id: css
choices:
- name: None
value: none
default: true
- id: javascript
choices:
- name: Importmap (default)
value: importmap
default: true
- id: skip_features
default:
- action_mailer
- action_text
- hotwire
- id: api_only
default: true
- id: skip_git
default: false
- id: skip_docker
default: false
- id: skip_bundle
default: false
post_actions:
- id: init_git
name: "Initialize git repository"
enabled: true
command: "git init && git add . && git commit -m 'Initial commit'"
Use Case: Building JSON APIs without views or frontend assets
Usage:
railstart new api --preset api-only
Example 3: RSpec + PostgreSQL Stack
---
# RSpec Preset - BDD workflow with RSpec and PostgreSQL
questions:
- id: database
choices:
- name: PostgreSQL
value: postgresql
default: true
- id: test_framework
choices:
- name: RSpec
value: rspec
default: true
- id: skip_git
default: false
post_actions:
- id: init_git
name: "Initialize git repository"
enabled: true
command: "git init && git add . && git commit -m 'Initial commit'"
- id: setup_rspec
name: "Setup RSpec"
enabled: true
command: "bundle add rspec-rails --group development,test && bundle exec rails generate rspec:install"
Use Case: Teams preferring RSpec over Minitest for BDD-style testing
Usage:
railstart new myapp --preset rspec
Best Practices
Naming
✅ DO:
- Use descriptive hyphenated names (
vite-bun,api-only) - Keep names short and memorable
- Use stack technology names when relevant
❌ DON'T:
- Use underscores or spaces
- Use overly generic names (
preset1,test) - Use version numbers unless necessary
Documentation
✅ DO:
- Add clear comments explaining the preset's purpose
- Document when to use this preset
- Explain non-obvious choices
❌ DON'T:
- Leave presets undocumented
- Assume users know why choices were made
Scope
✅ DO:
- Override only what's necessary for the stack
- Leave unrelated options at defaults
- Enable relevant post-actions
❌ DON'T:
- Override every single option
- Enable unrelated post-actions
- Create overly complex presets
Testing
✅ DO:
- Test YAML syntax before committing
- Generate test apps with the preset
- Verify all post-actions execute correctly
- Check generated Gemfile and config files
❌ DON'T:
- Commit untested presets
- Assume merging works without verification
- Skip validation of post-action commands
Common Patterns
Pattern 1: PostgreSQL + Tailwind (Production Default)
Most production apps benefit from:
questions:
- id: database
choices:
- name: PostgreSQL
value: postgresql
default: true
- id: css
choices:
- name: Tailwind
value: tailwind
default: true
Pattern 2: Skip All Frontend (Pure API)
API-only apps should skip frontend tooling:
questions:
- id: css
choices:
- name: None (skip CSS setup)
value: none
default: true
- id: javascript
choices:
- name: None (skip JavaScript)
value: none
default: true
- id: skip_features
default:
- action_mailer
- action_text
- hotwire
- id: api_only
default: true
Pattern 3: Modern Frontend Stack
SPAs and modern frontends typically use:
questions:
- id: javascript
choices:
- name: Vite (via vite_rails gem)
value: vite
default: true
post_actions:
- id: setup_vite
enabled: true
Troubleshooting
Preset Not Loading
Symptom: Preset file ignored, defaults used
Solutions:
- Verify file location:
config/presets/{name}.yaml - Check YAML syntax:
ruby -ryaml -e "YAML.load_file('config/presets/name.yaml')" - Ensure preset name matches filename (without .yaml)
- Check for typos in
--presetflag
Wrong Defaults Selected
Symptom: Different choice selected than expected
Solutions:
- Verify
default: trueis on the correct choice - Check for multiple
default: truein same question (last wins) - Ensure question
idmatches exactly - Test ID-based merging manually
Post-Action Not Running
Symptom: Expected post-action skipped
Solutions:
- Check
enabled: truein preset - Verify conditional logic (
iffield) matches selected answers - Ensure post-action
idmatches exactly - Check command syntax for errors
YAML Syntax Error
Symptom: Parse error or invalid YAML
Solutions:
- Validate YAML:
yq . config/presets/name.yaml - Check indentation (2 spaces, no tabs)
- Ensure proper array syntax (
- item) - Verify all strings are properly quoted if containing special chars
Quick Reference
Minimal Preset Template
---
# Preset Name - Description
questions:
- id: database
choices:
- name: PostgreSQL
value: postgresql
default: true
post_actions:
- id: init_git
enabled: true
Usage Commands
# List available presets
ls config/presets/
# Use preset with interactive mode
railstart new myapp --preset name
# Use preset with default mode (no prompts)
railstart new myapp --preset name --default
# Validate YAML syntax
ruby -ryaml -e "puts YAML.load_file('config/presets/name.yaml').inspect"
Testing Checklist
- YAML syntax valid (no parse errors)
- All question IDs match base config
- Only one
default: trueper select question - Post-action IDs match base config
- Post-action conditionals match question choices
- Test app generates successfully
- All post-actions execute correctly
- Gemfile contains expected gems
- Generated config files are correct
Status: Complete - Ready for use ✅ Version: 1.0.0 Last Updated: 2025-11-22