Class: CreateRailsApp::Wizard
- Inherits:
-
Object
- Object
- CreateRailsApp::Wizard
- Defined in:
- lib/create_rails_app/wizard.rb
Overview
Step-by-step interactive prompt loop for choosing rails new options.
Walks through each supported option in Options::Catalog::ORDER, presenting only the options supported by the detected Rails version. Supports back-navigation via CtrlB+ and smart filtering via SKIP_RULES.
Constant Summary collapse
- BACK =
Sentinel returned by the prompter when the user presses Ctrl+B.
Object.new.tap { |o| o.define_singleton_method(:inspect) { '#<BACK>' } }.freeze
- LABELS =
Human-readable labels for each option key.
{ api: 'API-only mode', active_record: 'Active Record (ORM)', database: 'Database', javascript: 'JavaScript approach', css: 'CSS framework', asset_pipeline: 'Asset pipeline', hotwire: 'Hotwire (Turbo + Stimulus)', jbuilder: 'Jbuilder (JSON templates)', action_mailer: 'Action Mailer', action_mailbox: 'Action Mailbox', action_text: 'Action Text (rich text)', active_job: 'Active Job', active_storage: 'Active Storage (file uploads)', action_cable: 'Action Cable (WebSockets)', test: 'Tests', system_test: 'System tests', brakeman: 'Brakeman (security scanner)', bundler_audit: 'Bundler Audit (dependency checker)', rubocop: 'RuboCop (linter)', ci: 'CI files', docker: 'Dockerfile', kamal: 'Kamal (deployment)', thruster: 'Thruster (HTTP/2 proxy)', solid: 'Solid (Cache/Queue/Cable)', devcontainer: 'Dev Container', bootsnap: 'Bootsnap (boot speedup)', dev_gems: 'Dev gems', keeps: 'Source control .keep files', decrypted_diffs: 'Decrypted diffs', git: 'Initialize git', bundle: 'Run bundle install' }.freeze
- HELP_TEXT =
Short explanations shown below each wizard step.
{ api: 'Generates a slimmed-down app optimized for API backends.', active_record: 'Database ORM layer. Skipping also skips the database choice.', database: 'Which database adapter to configure.', javascript: 'How JavaScript is managed in the asset pipeline.', css: 'Which CSS framework to pre-install.', asset_pipeline: 'Which asset pipeline to use for JS/CSS bundling.', hotwire: 'Turbo + Stimulus for SPA-like behavior over HTML.', jbuilder: 'DSL for building JSON views.', action_mailer: 'Framework for sending emails.', action_mailbox: 'Routes inbound emails to controller-like mailboxes.', action_text: 'Rich text content and editing with Trix.', active_job: 'Framework for declaring and running background jobs.', active_storage: 'Upload files to cloud services like S3 or GCS.', action_cable: 'WebSocket framework for real-time features.', test: 'Generates test directory and helpers.', system_test: 'Browser-based integration tests via Capybara.', brakeman: 'Static analysis for security vulnerabilities.', bundler_audit: 'Checks dependencies for known vulnerabilities.', rubocop: 'Ruby style and lint checking.', ci: 'Generates CI workflow configuration.', docker: 'Generates Dockerfile for containerized deployment.', kamal: 'Generates Kamal deploy configuration.', thruster: 'HTTP/2 proxy with asset caching and X-Sendfile.', solid: 'Solid Cache, Solid Queue, and Solid Cable adapters.', devcontainer: 'Generates VS Code dev container configuration.', bootsnap: 'Speeds up boot times with caching.', dev_gems: 'Development gems like web-console.', keeps: 'Empty directories preserved via .keep files.', decrypted_diffs: 'Show decrypted diffs of encrypted credentials in git.', git: 'Initializes a git repository for the new app.', bundle: 'Runs bundle install after generating the app.' }.freeze
- CHOICE_HELP =
Per-choice hints displayed next to enum choices.
{ database: { 'sqlite3' => 'simple file-based, great for development', 'postgresql' => 'full-featured, most popular for production', 'mysql' => 'widely used relational database', 'trilogy' => 'modern MySQL-compatible client', 'mariadb-mysql' => 'MariaDB with mysql2 adapter', 'mariadb-trilogy' => 'MariaDB with Trilogy adapter' }, javascript: { 'importmap' => 'no bundler, uses browser-native import maps', 'bun' => 'fast all-in-one JS runtime and bundler', 'webpack' => 'established full-featured bundler', 'esbuild' => 'extremely fast JS bundler', 'rollup' => 'ES module-focused bundler', 'none' => 'no JavaScript setup' }, asset_pipeline: { 'propshaft' => 'modern, lightweight asset pipeline', 'sprockets' => 'classic asset pipeline with preprocessing', 'none' => 'no asset pipeline' }, css: { 'tailwind' => 'utility-first CSS framework', 'bootstrap' => 'popular component-based framework', 'bulma' => 'modern CSS-only framework', 'postcss' => 'CSS transformations via plugins', 'sass' => 'CSS with variables, nesting, and mixins', 'none' => 'no CSS framework' } }.freeze
- SKIP_RULES =
Rules that determine when a wizard step should be silently skipped. Each lambda receives the current values hash and returns true to skip.
{ database: ->(values) { values[:active_record] == false }, javascript: ->(values) { values[:api] == true }, css: ->(values) { values[:api] == true }, asset_pipeline: ->(values) { values[:api] == true }, hotwire: ->(values) { values[:api] == true }, jbuilder: ->(values) { values[:api] == true }, action_mailbox: ->(values) { values[:active_record] == false }, action_text: ->(values) { values[:api] == true || values[:active_record] == false }, active_storage: ->(values) { values[:active_record] == false }, system_test: ->(values) { values[:test] == false || values[:api] == true } }.freeze
Instance Attribute Summary collapse
-
#last_presented_index ⇒ Object
readonly
Returns the value of attribute last_presented_index.
Instance Method Summary collapse
-
#initialize(compatibility_entry:, defaults:, prompter:) ⇒ Wizard
constructor
A new instance of Wizard.
-
#run(start_index: 0) ⇒ Hash{Symbol => Object}
Runs the wizard and returns the selected options.
Constructor Details
#initialize(compatibility_entry:, defaults:, prompter:) ⇒ Wizard
146 147 148 149 150 151 152 |
# File 'lib/create_rails_app/wizard.rb', line 146 def initialize(compatibility_entry:, defaults:, prompter:) @compatibility_entry = compatibility_entry @prompter = prompter @values = sanitize_defaults(defaults) @stashed = {} @last_presented_index = 0 end |
Instance Attribute Details
#last_presented_index ⇒ Object (readonly)
Returns the value of attribute last_presented_index.
141 142 143 |
# File 'lib/create_rails_app/wizard.rb', line 141 def last_presented_index @last_presented_index end |
Instance Method Details
#run(start_index: 0) ⇒ Hash{Symbol => Object}
Runs the wizard and returns the selected options.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/create_rails_app/wizard.rb', line 158 def run(start_index: 0) keys = Options::Catalog::ORDER.select { |key| @compatibility_entry.supports_option?(key) } index = [start_index, keys.length - 1].min while index < keys.length key = keys[index] if skip_step?(key) @stashed[key] = @values.delete(key) if @values.key?(key) index += 1 next end @values[key] = @stashed.delete(key) if @stashed.key?(key) && !@values.key?(key) @last_presented_index = index answer = ask_for(key, index:, total: keys.length) case answer when BACK index = find_previous_unskipped(keys, index) else assign_value(key, answer) index += 1 end end @values.dup end |