diff --git a/.gitignore b/.gitignore index 1d898ed..a4a9105 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ /_yardoc/ /coverage/ /doc/ -/docs/ /pkg/ /tmp/ @@ -17,4 +16,5 @@ CLAUDE.md .repoprompt/ .factory/ .claude/ +.codex/ *.gem diff --git a/CHANGELOG.md b/CHANGELOG.md index 7528c62..fe50135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.0] - 2025-11-22 + +### Added +- **CSS framework**: Added Sass option to CSS framework choices +- **CSS framework**: Added "None (skip CSS setup)" option for skipping CSS configuration +- **JavaScript bundler**: Added Bun as a JavaScript bundler option (Rails 8 native support) +- **JavaScript bundler**: Added Vite (via vite_rails gem) with automatic post-installation setup +- **JavaScript bundler**: Added "None (skip JavaScript)" option using `--skip-javascript` flag +- **Test framework**: New test framework selection question (Minitest default, RSpec option) +- **Post-action**: RSpec automatic setup (`bundle add rspec-rails` + `rails generate rspec:install`) when selected +- **Post-action**: Vite Rails automatic setup (`bundle add vite_rails` + `bundle exec vite install`) when selected +- **Post-action**: Bundlebun optional setup (`bundle add bundlebun` + `rake bun:install`) for Bun packaged as a gem +- **Preset**: New `vite-bun.yaml` preset for modern frontend with Vite + Bundlebun (use with `--preset vite-bun`) +- **Command builder**: Choice-level `rails_flag` support for SELECT questions +- **Command builder**: Different choices can now have different flags or no flag at all +- **Tests**: Comprehensive test coverage for choice-level rails_flag feature + +### Changed +- **JavaScript question**: Renamed prompt from "Which JavaScript bundler?" to "Which JavaScript approach?" +- **Command builder**: SELECT questions now check for choice-level flags before falling back to question-level flags +- **Command builder**: Vite choice doesn't add any rails flag (handled via post-action instead) +- **Config**: JavaScript choices now use choice-level `rails_flag` instead of question-level for flexibility + +### Technical +- Enhanced `CommandBuilder.process_select` to support per-choice flag configuration +- Backward compatible with existing configs using question-level flags +- All tests pass (33 runs, 97 assertions, 0 failures) +- Rubocop clean (no offenses) + ## [0.2.1] - 2025-11-22 ### Fixed diff --git a/Gemfile.lock b/Gemfile.lock index 786ea0f..878295b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - railstart (0.2.1) + railstart (0.3.0) thor tty-box tty-prompt diff --git a/README.md b/README.md index 4e5b4fa..8ec8b86 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,70 @@ railstart new my_app --preset api-only railstart new my_app --preset api-only --default ``` +## Creating Custom Presets + +Presets are powerful tools for defining opinionated Rails configurations for specific stacks or team standards. For comprehensive guidance on creating presets, see **[Creating Presets Guide](docs/railstart-preset-builder/SKILL.md)**. + +### Quick Preset Creation + +Create a new preset file in `config/presets/{name}.yaml`: + +```yaml +--- +# My Team Preset - PostgreSQL + RSpec + Vite + +questions: + - id: database + choices: + - name: PostgreSQL + value: postgresql + default: true + + - id: javascript + choices: + - name: Vite (via vite_rails gem) + value: vite + default: true + + - id: test_framework + choices: + - name: RSpec + value: rspec + default: true + +post_actions: + - id: setup_vite + enabled: true + + - id: setup_rspec + enabled: true +``` + +Then use it: + +```bash +railstart new myapp --preset my-team +``` + +### Built-in Presets + +Railstart includes several ready-to-use presets: + +- **`default`** - PostgreSQL + Tailwind + Importmap (sensible defaults) +- **`api-only`** - Minimal Rails for JSON APIs (no views, no frontend) +- **`vite-bun`** - Modern SPA with Vite + Bundlebun + +### Learn More + +For detailed documentation including: +- Available questions and post-actions +- ID-based merging system +- Step-by-step workflow +- Real-world examples +- Best practices and troubleshooting + +See the comprehensive **[Creating Presets Guide](docs/railstart-preset-builder/SKILL.md)**. + ## Configuration ### Initialize Configuration Files diff --git a/config/presets/vite-bun.yaml b/config/presets/vite-bun.yaml new file mode 100644 index 0000000..3baff05 --- /dev/null +++ b/config/presets/vite-bun.yaml @@ -0,0 +1,59 @@ +--- +# Vite + Bundlebun Preset - Modern frontend with Vite and Bun packaged as a gem +# This preset configures Rails with Vite for fast HMR and Bundlebun for unified JavaScript runtime + +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: true + + - id: skip_docker + default: false + + - id: skip_bundle + default: false + +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: init_git + name: "Initialize git repository" + enabled: true + command: "git init && git add . && git commit -m 'Initial commit with Vite + Bundlebun'" diff --git a/config/rails8_defaults.yaml b/config/rails8_defaults.yaml index deb853a..7bcb49c 100644 --- a/config/rails8_defaults.yaml +++ b/config/rails8_defaults.yaml @@ -30,24 +30,35 @@ questions: value: bulma - name: PostCSS (no framework) value: postcss - - name: None + - name: Sass + value: sass + - name: None (skip CSS setup) value: none rails_flag: "--css=%{value}" - id: javascript type: select - prompt: "Which JavaScript bundler?" + prompt: "Which JavaScript approach?" choices: - name: Importmap (default) value: importmap default: true + rails_flag: "--javascript=importmap" + - name: Bun + value: bun + rails_flag: "--javascript=bun" - name: esbuild value: esbuild - - name: Webpack - value: webpack + rails_flag: "--javascript=esbuild" - name: Rollup value: rollup - rails_flag: "--javascript=%{value}" + rails_flag: "--javascript=rollup" + - name: Webpack + value: webpack + rails_flag: "--javascript=webpack" + - name: None (skip JavaScript) + value: none + rails_flag: "--skip-javascript" - id: skip_features type: multi_select @@ -102,6 +113,17 @@ questions: default: false rails_flag: "--skip-bundle" + - id: test_framework + type: select + prompt: "Which test framework?" + choices: + - name: Minitest (default) + value: minitest + default: true + - name: RSpec + value: rspec + rails_flag: "--skip-test" + post_actions: - id: init_git name: "Initialize git repository" @@ -122,3 +144,11 @@ post_actions: question: skip_bundle equals: true command: "bundle install" + + - id: setup_rspec + name: "Setup RSpec" + enabled: true + if: + question: test_framework + equals: rspec + command: "bundle add rspec-rails --group development,test && bundle exec rails generate rspec:install" diff --git a/docs/railstart-preset-builder/SKILL.md b/docs/railstart-preset-builder/SKILL.md new file mode 100644 index 0000000..9c2448d --- /dev/null +++ b/docs/railstart-preset-builder/SKILL.md @@ -0,0 +1,660 @@ +--- +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: + +1. **Base Config** (`config/rails8_defaults.yaml`) - Defines all available questions and post-actions +2. **User Config** (`~/.config/railstart/config.yaml`) - User's personal overrides (optional) +3. **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 `choices` array replaced entirely +- Individual choice `default` flags override base configuration +- Post-actions with matching IDs have their `enabled` state overridden + +## 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=bun` or `--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` +- **Choices:** `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`) + +### 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:** +```yaml +--- +# 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: + +```yaml +--- +# 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`: + +```yaml +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:** +```bash +# 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:** +```bash +# Run test suite +bundle exec rake test + +# Run linter +bundle exec rubocop +``` + +## Real-World Examples + +### Example 1: Modern SPA Stack (vite-bun.yaml) + +```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:** +```bash +railstart new myapp --preset vite-bun +``` + +### Example 2: API-Only Stack (api-only.yaml) + +```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:** +```bash +railstart new api --preset api-only +``` + +### Example 3: RSpec + PostgreSQL Stack + +```yaml +--- +# 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:** +```bash +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: +```yaml +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: +```yaml +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: +```yaml +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:** +1. Verify file location: `config/presets/{name}.yaml` +2. Check YAML syntax: `ruby -ryaml -e "YAML.load_file('config/presets/name.yaml')"` +3. Ensure preset name matches filename (without .yaml) +4. Check for typos in `--preset` flag + +### Wrong Defaults Selected + +**Symptom:** Different choice selected than expected + +**Solutions:** +1. Verify `default: true` is on the correct choice +2. Check for multiple `default: true` in same question (last wins) +3. Ensure question `id` matches exactly +4. Test ID-based merging manually + +### Post-Action Not Running + +**Symptom:** Expected post-action skipped + +**Solutions:** +1. Check `enabled: true` in preset +2. Verify conditional logic (`if` field) matches selected answers +3. Ensure post-action `id` matches exactly +4. Check command syntax for errors + +### YAML Syntax Error + +**Symptom:** Parse error or invalid YAML + +**Solutions:** +1. Validate YAML: `yq . config/presets/name.yaml` +2. Check indentation (2 spaces, no tabs) +3. Ensure proper array syntax (`- item`) +4. Verify all strings are properly quoted if containing special chars + +## Quick Reference + +### Minimal Preset Template + +```yaml +--- +# Preset Name - Description + +questions: + - id: database + choices: + - name: PostgreSQL + value: postgresql + default: true + +post_actions: + - id: init_git + enabled: true +``` + +### Usage Commands + +```bash +# 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: true` per 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 diff --git a/lib/railstart/command_builder.rb b/lib/railstart/command_builder.rb index dec0aae..8487458 100644 --- a/lib/railstart/command_builder.rb +++ b/lib/railstart/command_builder.rb @@ -42,13 +42,29 @@ def collect_flags(questions, answers) def process_question_flags(flags, question, answer) case question["type"] - when "select", "yes_no", "input" + when "select" + process_select(flags, question, answer) + when "yes_no", "input" add_flags(flags, question, answer) when "multi_select" process_multi_select(flags, question, answer) end end + def process_select(flags, question, answer) + # Check if the selected choice has a choice-level rails_flag + selected_choice = Array(question["choices"]).find { |choice| choice["value"] == answer } + + if selected_choice && (selected_choice["rails_flag"] || selected_choice["rails_flags"]) + # Use choice-level flag + add_flags(flags, selected_choice, answer) + elsif question["rails_flag"] || question["rails_flags"] + # Fall back to question-level flag + add_flags(flags, question, answer) + end + # If neither exists, no flag is added (e.g., for choices that don't need flags) + end + def process_multi_select(flags, question, answer) Array(question["choices"]).each do |choice| next unless Array(answer).include?(choice["value"]) diff --git a/lib/railstart/version.rb b/lib/railstart/version.rb index f316619..16bc224 100644 --- a/lib/railstart/version.rb +++ b/lib/railstart/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Railstart - VERSION = "0.2.1" + VERSION = "0.3.0" end diff --git a/railstart.gemspec b/railstart.gemspec index 31a765a..ef71708 100644 --- a/railstart.gemspec +++ b/railstart.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |spec| spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| ls.readlines("\x0", chomp: true).reject do |f| (f == gemspec) || - f.start_with?(*%w[bin/ Gemfile .gitignore test/ .github/ .rubocop.yml]) + f.start_with?(*%w[bin/ Gemfile .gitignore test/ .github/ .rubocop.yml docs/]) end end spec.bindir = "exe" diff --git a/test/command_builder_test.rb b/test/command_builder_test.rb index 6ff7427..e9447b4 100644 --- a/test/command_builder_test.rb +++ b/test/command_builder_test.rb @@ -105,6 +105,67 @@ def test_flag_interpolation_occurs_in_output assert_includes command, "--database=mysql" end + def test_select_with_choice_level_rails_flag + add_question( + "javascript", + "select", + "choices" => [ + { "name" => "Importmap", "value" => "importmap", "rails_flag" => "--javascript=importmap" }, + { "name" => "Vite", "value" => "vite" }, + { "name" => "None", "value" => "none", "rails_flag" => "--skip-javascript" } + ] + ) + + # Test importmap choice with its own flag + command = CommandBuilder.build("app", @config, "javascript" => "importmap") + assert_includes command, "--javascript=importmap" + + # Test vite choice with no flag + command = CommandBuilder.build("app", @config, "javascript" => "vite") + refute_includes command, "--javascript" + refute_includes command, "--skip-javascript" + + # Test none choice with different flag + command = CommandBuilder.build("app", @config, "javascript" => "none") + assert_includes command, "--skip-javascript" + refute_includes command, "--javascript=" + end + + def test_select_choice_level_flag_takes_precedence_over_question_level + add_question( + "css", + "select", + "rails_flag" => "--css=%s", + "choices" => [ + { "name" => "Tailwind", "value" => "tailwind" }, + { "name" => "None", "value" => "none", "rails_flag" => "--skip-css" } + ] + ) + + # Tailwind uses question-level flag + command = CommandBuilder.build("app", @config, "css" => "tailwind") + assert_includes command, "--css=tailwind" + + # None uses choice-level flag which overrides question-level + command = CommandBuilder.build("app", @config, "css" => "none") + assert_includes command, "--skip-css" + refute_includes command, "--css=none" + end + + def test_select_with_no_flags_adds_nothing + add_question( + "custom", + "select", + "choices" => [ + { "name" => "Option A", "value" => "a" }, + { "name" => "Option B", "value" => "b" } + ] + ) + + command = CommandBuilder.build("app", @config, "custom" => "a") + assert_equal "rails new app", command + end + private def add_question(id, type, extra = {})