Class: Railstart::CLI

Inherits:
Thor
  • Object
show all
Defined in:
lib/railstart/cli.rb

Overview

CLI commands for Railstart, exposing Thor tasks for interactive generation.

Examples:

Run the wizard with defaults

Railstart::CLI.start(%w[new my_app --default])

Print version

Railstart::CLI.start(%w[version])

Constant Summary collapse

PRESET_DIR =
File.expand_path("~/.config/railstart/presets")
GEM_PRESET_DIR =
File.expand_path("../../config/presets", __dir__)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

rubocop:disable Style/OptionalBooleanParameter



46
47
48
# File 'lib/railstart/cli.rb', line 46

def banner(command, _namespace = nil, _subcommand = false)
  "#{basename} #{command.usage}"
end

.exit_on_failure?Boolean



15
16
17
# File 'lib/railstart/cli.rb', line 15

def self.exit_on_failure?
  true
end

.help(shell, subcommand = false) ⇒ Object

Override to show only positive form of boolean options



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/railstart/cli.rb', line 51

def help(shell, subcommand = false)
  # rubocop:enable Style/OptionalBooleanParameter
  list = printable_commands(true, subcommand)
  Thor::Util.thor_classes_in(self).each do |klass|
    list += klass.printable_commands(false)
  end

  shell.say "Commands:"
  shell.print_table(list, indent: 2, truncate: true)
  shell.say
  class_options_help(shell)
end

.start(given_args = ARGV, config = {}) ⇒ Object

Show help by default when no command is given



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/railstart/cli.rb', line 20

def self.start(given_args = ARGV, config = {})
  if given_args.empty?
    # Show command list instead of requiring a command argument
    puts "Railstart - Interactive Rails 8 application generator"
    puts ""
    puts "Usage:"
    puts "  railstart init                      # Generate config files"
    puts "  railstart new [APP_NAME] [OPTIONS]  # Generate a new Rails app"
    puts "  railstart version                   # Show version"
    puts "  railstart help [COMMAND]            # Show help for a command"
    puts ""
    puts "Quick Start:"
    puts "  railstart init                      # Create config files (optional)"
    puts "  railstart new my_app                # Interactive mode"
    puts "  railstart new my_app --default      # Use defaults"
    puts "  railstart new my_app --preset api-only  # Use preset"
    puts ""
    puts "Run 'railstart help init' or 'railstart help new' for details"
    return
  end
  super
end

Instance Method Details

#determine_preset_nameObject (private)



229
230
231
232
233
234
235
236
237
# File 'lib/railstart/cli.rb', line 229

def determine_preset_name
  # Explicit --preset flag takes priority
  return options[:preset] if options[:preset]

  # --default maps to "default" preset
  return "default" if options[:default]

  nil
end

#example_preset_configObject (private)



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/railstart/cli.rb', line 265

def example_preset_config
  <<~YAML
    ---
    # Example Preset - Customize this for your use case
    # Use with: railstart new my_app --preset example

    questions:
      - id: database
        choices:
          - name: PostgreSQL
            value: postgresql
            default: true

      - id: css
        choices:
          - name: Tailwind
            value: tailwind
            default: true

      - id: api_only
        default: false

    post_actions:
      - id: init_git
        enabled: true

      - id: bundle_install
        enabled: true
  YAML
end

#example_user_configObject (private)



259
260
261
262
263
# File 'lib/railstart/cli.rb', line 259

def example_user_config
  # Copy the full rails8_defaults.yaml as the user config template
  defaults_path = File.expand_path("../../config/rails8_defaults.yaml", __dir__)
  File.read(defaults_path)
end

#explicit_preset_path(name) ⇒ Object (private)



296
297
298
299
300
301
302
303
# File 'lib/railstart/cli.rb', line 296

def explicit_preset_path(name)
  return unless name&.match?(/\.ya?ml\z/)

  expanded = File.expand_path(name)
  return expanded if File.exist?(expanded)

  raise Railstart::ConfigLoadError, "Preset file '#{name}' not found"
end

#help(command = nil, subcommand = false) ⇒ Object

Override to customize option display rubocop:disable Style/OptionalBooleanParameter



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/railstart/cli.rb', line 67

def help(command = nil, subcommand = false)
  # rubocop:enable Style/OptionalBooleanParameter
  if command
    if self.class.subcommands.include?(command)
      self.class.subcommand_classes[command].help(shell, subcommand)
    else
      cmd = self.class.all_commands[command]
      raise Thor::UndefinedCommandError.new(command, nil, self.class.all_commands.keys) unless cmd

      shell.say "Usage:"
      shell.say "  #{self.class.banner(cmd)}"
      shell.say
      if cmd.long_description
        shell.say "Description:"
        shell.print_wrapped(cmd.long_description, indent: 2)
      else
        shell.say cmd.description
      end

      print_custom_options(cmd)
    end
  else
    super
  end
end

#initvoid

This method returns an undefined value.

Examples:

Generate config files

Railstart::CLI.start(%w[init])


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/railstart/cli.rb', line 181

def init
  config_dir = File.expand_path("~/.config/railstart")
  presets_dir = File.join(config_dir, "presets")

  # Create directories
  FileUtils.mkdir_p(presets_dir)
  puts "✓ Created #{config_dir}"
  puts "✓ Created #{presets_dir}"

  # Generate example user config
  user_config_path = File.join(config_dir, "config.yaml")
  if File.exist?(user_config_path) && !options[:force]
    puts "⊗ Skipped #{user_config_path} (already exists, use --force to overwrite)"
  else
    File.write(user_config_path, example_user_config)
    puts "✓ Created #{user_config_path}"
  end

  # Generate example preset
  example_preset_path = File.join(presets_dir, "example.yaml")
  if File.exist?(example_preset_path) && !options[:force]
    puts "⊗ Skipped #{example_preset_path} (already exists, use --force to overwrite)"
  else
    File.write(example_preset_path, example_preset_config)
    puts "✓ Created #{example_preset_path}"
  end

  puts "\n✨ Configuration files initialized!"
  puts "\nNext steps:"
  puts "  1. Edit ~/.config/railstart/config.yaml to customize defaults"
  puts "  2. Create custom presets in ~/.config/railstart/presets/"
  puts "  3. Use with: railstart new my_app --preset example"
end

#new(app_name = nil) ⇒ void

This method returns an undefined value.

Examples:

Start wizard with prompts

Railstart::CLI.start(%w[new my_app])

Use preset

Railstart::CLI.start(%w[new my_app --preset api-only])

Use default preset non-interactively

Railstart::CLI.start(%w[new my_app --default])

Raises:

  • (Railstart::Error)

    when generation fails due to configuration or runtime errors



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/railstart/cli.rb', line 147

def new(app_name = nil)
  preset_name = determine_preset_name
  preset_path = preset_name ? preset_file_for(preset_name) : nil

  config = Config.load(preset_path: preset_path)

  generator = Generator.new(
    app_name,
    config: config,
    use_defaults: options[:default]
  )

  generator.run
rescue Railstart::Error => e
  puts "Error: #{e.message}"
  exit 1
end

#preset_file_for(name) ⇒ Object (private)



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/railstart/cli.rb', line 239

def preset_file_for(name)
  if (direct_path = explicit_preset_path(name))
    return direct_path
  end

  # Check user presets first
  user_path = File.join(PRESET_DIR, "#{name}.yaml")
  return user_path if File.exist?(user_path)

  # Fall back to built-in gem presets
  gem_path = File.join(GEM_PRESET_DIR, "#{name}.yaml")
  return gem_path if File.exist?(gem_path)

  # If explicit --preset was used, raise error
  raise Railstart::ConfigLoadError, "Preset '#{name}' not found in #{PRESET_DIR} or gem presets" if options[:preset]

  # For --default with missing preset, return nil (fall back to builtin config)
  nil
end

#versionvoid

This method returns an undefined value.

Examples:

Display version string

Railstart::CLI.start(%w[version])


220
221
222
# File 'lib/railstart/cli.rb', line 220

def version
  puts "Railstart v#{Railstart::VERSION}"
end