Module: Docscribe::CLI::Run

Defined in:
lib/docscribe/cli/run.rb

Overview

Execute Docscribe from parsed CLI options.

This module handles:

  • config loading and CLI overrides

  • stdin mode

  • file expansion / filtering

  • inspect vs write behavior

  • process exit status

Constant Summary collapse

INITIAL_RUN_STATE =
{
  changed: false,
  had_errors: false,
  checked_ok: 0,
  checked_fail: 0,
  corrected: 0,
  corrected_paths: [], #: Array[String]
  corrected_changes: {}, #: Hash[String, untyped]
  fail_paths: [], #: Array[String]
  fail_changes: {}, #: Hash[String, untyped]
  error_paths: [], #: Array[String]
  error_messages: {}, #: Hash[String, String]
  type_mismatch_paths: [], #: Array[String]
  type_mismatch_changes: {}, #: Hash[String, untyped]
  total: 0,
  processed: 0
}.freeze

Class Method Summary collapse

Class Method Details

.all_fine?(state, checked_error, type_mismatch_count) ⇒ Boolean

Whether no failures, errors, or type mismatches occurred.

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state

  • checked_error (Integer)

    number of files with errors

  • type_mismatch_count (Integer)

    number of files with type mismatches

Returns:

  • (Boolean)


619
620
621
# File 'lib/docscribe/cli/run.rb', line 619

def all_fine?(state, checked_error, type_mismatch_count)
  state[:checked_fail].zero? && checked_error.zero? && type_mismatch_count.zero?
end

.append_expanded_path(files, path) ⇒ void

This method returns an undefined value.

Append a file or recursively expand a directory into the files array.

Parameters:

  • files (Array<String>)

    mutable file path accumulator

  • path (String)

    file or directory path to expand



153
154
155
156
157
158
159
160
161
# File 'lib/docscribe/cli/run.rb', line 153

def append_expanded_path(files, path)
  if File.directory?(path)
    files.concat(Dir.glob(File.join(path, '**', '*.rb')))
  elsif File.file?(path)
    files << path
  else
    warn "Skipping missing path: #{path}"
  end
end

.build_config(options) ⇒ Docscribe::Config

Load and build the effective config from CLI options.

Parameters:

  • options (Docscribe::CLI::Formatters::opts)

    parsed CLI options

Returns:



67
68
69
70
71
72
# File 'lib/docscribe/cli/run.rb', line 67

def build_config(options)
  conf = Docscribe::Config.load(options[:config])
  conf = Docscribe::CLI::ConfigBuilder.build(conf, options)
  conf.load_plugins!
  conf
end

.build_failure_line(state, type_mismatch_count, checked_error) ⇒ String

Build the human-readable failure summary line for check output.

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state

  • type_mismatch_count (Integer)

    number of files with type mismatches

  • checked_error (Integer)

    number of files with errors

Returns:

  • (String)


638
639
640
641
642
643
644
# File 'lib/docscribe/cli/run.rb', line 638

def build_failure_line(state, type_mismatch_count, checked_error)
  parts = ["#{state[:checked_fail]} need updates"]
  parts << "#{type_mismatch_count} type mismatches" if type_mismatch_count.positive?
  parts << "#{checked_error} errors"
  parts << "#{state[:checked_ok]} ok"
  "Docscribe: FAILED (#{parts.join(', ')})"
end

.change_line_suffix(change) ⇒ String

Format the line number suffix for a change reason string.

Parameters:

  • change (Docscribe::CLI::Formatters::change)

    structured change record

Returns:

  • (String)

    “ at line N” or empty



716
717
718
# File 'lib/docscribe/cli/run.rb', line 716

def change_line_suffix(change)
  change[:line] ? " at line #{change[:line]}" : ''
end

.change_method_suffix(change) ⇒ String

Format the method name suffix for a change reason string.

Parameters:

  • change (Docscribe::CLI::Formatters::change)

    structured change record

Returns:

  • (String)

    “ for method_name” or empty



724
725
726
# File 'lib/docscribe/cli/run.rb', line 724

def change_method_suffix(change)
  change[:method] ? " for #{change[:method]}" : ''
end

.core_rbs_provider_for(conf) ⇒ Docscribe::Types::RBS::Provider?

Return the core RBS provider from the config if available.

Parameters:

Returns:



109
110
111
# File 'lib/docscribe/cli/run.rb', line 109

def core_rbs_provider_for(conf)
  conf.respond_to?(:core_rbs_provider) ? conf.core_rbs_provider : nil
end

.direct_message_change?(change) ⇒ Boolean

Whether a change type uses its own :message field directly as the reason.

Parameters:

  • change (Docscribe::CLI::Formatters::change)

    structured change record

Returns:

  • (Boolean)


732
733
734
735
736
737
738
739
740
741
# File 'lib/docscribe/cli/run.rb', line 732

def direct_message_change?(change)
  %i[
    missing_param
    missing_return
    missing_raise
    missing_visibility
    missing_module_function_note
    insert_full_doc_block
  ].include?(change[:type])
end

.expand_paths(args) ⇒ Array<String>

Expand CLI path arguments into a sorted list of Ruby files.

Directories are expanded recursively to ‘*/.rb`. If no arguments are provided, the current directory is used.

Parameters:

  • args (Array<String>)

    file and/or directory arguments

Returns:

  • (Array<String>)

    unique sorted Ruby file paths



137
138
139
140
141
142
143
144
145
146
# File 'lib/docscribe/cli/run.rb', line 137

def expand_paths(args)
  files = [] #: Array[String]
  args = ['.'] if args.empty?

  args.each do |path|
    append_expanded_path(files, path)
  end

  files.uniq.sort
end

.filtered_paths(argv, conf) ⇒ Array<String>

Expand CLI path arguments and filter through config file patterns.

Parameters:

  • argv (Array<String>)

    CLI path arguments

  • conf (Docscribe::Config)

    effective config

Returns:

  • (Array<String>)

    filtered Ruby file paths



118
119
120
# File 'lib/docscribe/cli/run.rb', line 118

def filtered_paths(argv, conf)
  expand_paths(argv).select { |path| conf.process_file?(path) }
end

.format_change_reason(change) ⇒ String

Format a structured change record into human-readable CLI output.

Parameters:

  • change (Docscribe::CLI::Formatters::change)

    structured change produced by the inline rewriter

Returns:

  • (String)

    human-readable explanation line



702
703
704
705
706
707
708
709
710
# File 'lib/docscribe/cli/run.rb', line 702

def format_change_reason(change)
  line = change_line_suffix(change)
  method = change_method_suffix(change)

  return "unsorted tags#{line}" if change[:type] == :unsorted_tags
  return "#{change[:message]}#{method}#{line}" if direct_message_change?(change)

  "#{change[:message] || change[:type].to_s.tr('_', ' ')}#{method}#{line}"
end

.mismatch_only?(state, checked_error) ⇒ Boolean

Whether type mismatches exist but no failures or errors.

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state

  • checked_error (Integer)

    number of files with errors

Returns:

  • (Boolean)


628
629
630
# File 'lib/docscribe/cli/run.rb', line 628

def mismatch_only?(state, checked_error)
  state[:checked_fail].zero? && checked_error.zero?
end

.no_files_foundInteger

Warn and return exit code when no matching files were found.

Returns:

  • (Integer)

    exit code 2



125
126
127
128
# File 'lib/docscribe/cli/run.rb', line 125

def no_files_found
  warn 'No files found. Pass files or directories (e.g. `docscribe lib`).'
  2
end

This method returns an undefined value.

Print the check-mode status line.

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state



600
601
602
603
604
605
606
607
608
609
610
611
# File 'lib/docscribe/cli/run.rb', line 600

def print_check_status_line(state)
  checked_error = state[:error_paths].size
  type_mismatch_count = state[:type_mismatch_paths].size

  if all_fine?(state, checked_error, type_mismatch_count)
    puts "Docscribe: OK (#{state[:checked_ok]} files checked)"
  elsif mismatch_only?(state, checked_error)
    puts "Docscribe: OK (#{state[:checked_ok]} files checked, #{type_mismatch_count} with type mismatches)"
  else
    puts build_failure_line(state, type_mismatch_count, checked_error)
  end
end

This method returns an undefined value.

Print corrected paths from write-mode summary (stdout).

Skips explanations when –verbose showed them inline per-file.

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state

  • options (Docscribe::CLI::Formatters::opts)

    CLI options



686
687
688
689
690
691
692
693
694
695
696
# File 'lib/docscribe/cli/run.rb', line 686

def print_corrected_paths(state, options)
  state[:corrected_paths].each do |p|
    puts "Updated: #{p}"

    next if options[:verbose] || options[:quiet]

    Array(state[:corrected_changes][p]).each do |change|
      puts "  - #{format_change_reason(change)}"
    end
  end
end

This method returns an undefined value.

Print error paths from check summary.

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state



747
748
749
750
751
752
753
754
755
# File 'lib/docscribe/cli/run.rb', line 747

def print_error_paths(state)
  return if state[:error_paths].empty?

  warn ''
  state[:error_paths].each do |p|
    warn "Error processing: #{p}"
    warn "  #{state[:error_messages][p]}" if state[:error_messages][p]
  end
end

This method returns an undefined value.

Print fail paths from check summary (stdout).

Skips explanations when –verbose showed them inline per-file.

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state

  • options (Docscribe::CLI::Formatters::opts)

    CLI options



584
585
586
587
588
589
590
591
592
593
594
# File 'lib/docscribe/cli/run.rb', line 584

def print_fail_paths(state, options)
  state[:fail_paths].each do |p|
    puts "Would update: #{p}"

    next if options[:verbose] || options[:quiet]

    Array(state[:fail_changes][p]).each do |change|
      puts "  - #{format_change_reason(change)}"
    end
  end
end

This method returns an undefined value.

Print type mismatch paths from check summary.

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state

  • options (Docscribe::CLI::Formatters::opts)

    CLI options



651
652
653
654
655
656
657
658
659
660
661
# File 'lib/docscribe/cli/run.rb', line 651

def print_type_mismatch_paths(state, options)
  return if options[:quiet]
  return unless options[:verbose] || options[:explain]

  state[:type_mismatch_paths].each do |p|
    warn "Type mismatches: #{p}"
    Array(state[:type_mismatch_changes][p]).each do |change|
      warn "  - #{format_change_reason(change)}"
    end
  end
end

This method returns an undefined value.

Print the write-mode summary (files corrected, errors).

Parameters:

  • state (Docscribe::CLI::Formatters::state)

    shared processing state

  • options (Docscribe::CLI::Formatters::opts)

    CLI options



668
669
670
671
672
673
674
675
676
677
# File 'lib/docscribe/cli/run.rb', line 668

def print_write_summary(state:, options:)
  puts
  puts "Docscribe: updated #{state[:corrected]} file(s)" if state[:corrected].positive?
  print_corrected_paths(state, options)

  return unless state[:had_errors]

  warn "Docscribe: #{state[:error_paths].size} file(s) had errors"
  print_error_paths(state)
end

.run(options:, argv:) ⇒ Integer

Run Docscribe for files or STDIN using the selected mode and strategy.

Modes:

  • :check => inspect what the selected strategy would change

  • :write => apply the selected strategy in place

  • :stdin => rewrite STDIN and print to STDOUT

Strategies:

  • :safe => merge/add/normalize non-destructively

  • :aggressive => rebuild existing doc blocks

Parameters:

  • options (Docscribe::CLI::Formatters::opts)

    parsed CLI options

  • argv (Array<String>)

    remaining path arguments

Returns:

  • (Integer)

    process exit code



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

def run(options:, argv:)
  conf = build_config(options)

  return run_stdin(options: options, conf: conf) if options[:mode] == :stdin

  paths = filtered_paths(argv, conf)
  return no_files_found unless paths.any?

  run_files(options: options, conf: conf, paths: paths)
end

.run_files(options:, conf:, paths:) ⇒ Integer

Process file paths in inspect or write mode.

In inspect mode:

  • prints progress/status

  • exits non-zero if any file would change or if any errors occurred

In write mode:

  • rewrites changed files in place

  • exits non-zero only if errors occurred

Parameters:

  • options (Docscribe::CLI::Formatters::opts)

    parsed CLI options

  • conf (Docscribe::Config)

    effective config

  • paths (Array<String>)

    Ruby file paths to process

Returns:

  • (Integer)

    process exit code



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/docscribe/cli/run.rb', line 177

def run_files(options:, conf:, paths:)
  $stdout.sync = true

  state = initial_run_state
  state[:total] = paths.size
  pwd = Pathname.pwd

  paths.each do |path|
    process_one_file(path, options: options, conf: conf, pwd: pwd, state: state)
  end

  finalize_run(options, state)

  run_exit_code(options, state)
end

.run_stdin(options:, conf:) ⇒ Integer

Rewrite code from STDIN using the selected strategy and print the result.

Parameters:

  • options (Docscribe::CLI::Formatters::opts)

    parsed CLI options

  • conf (Docscribe::Config)

    effective config

Returns:

  • (Integer)

    if StandardError

  • (Integer)

    if StandardError

Raises:

  • (StandardError)


82
83
84
85
86
87
88
# File 'lib/docscribe/cli/run.rb', line 82

def run_stdin(options:, conf:)
  puts stdin_rewrite_result(options, conf)[:output]
  0
rescue StandardError => e
  warn "Docscribe: Error processing stdin: #{e.class}: #{e.message}"
  1
end

.stdin_rewrite_result(options, conf) ⇒ Hash<Symbol, Object>

Rewrite STDIN input and return the result report.

Parameters:

  • options (Docscribe::CLI::Formatters::opts)

    parsed CLI options

  • conf (Docscribe::Config)

    effective config

Returns:

  • (Hash<Symbol, Object>)

    rewrite result with :output key



95
96
97
98
99
100
101
102
103
# File 'lib/docscribe/cli/run.rb', line 95

def stdin_rewrite_result(options, conf)
  Docscribe::InlineRewriter.rewrite_with_report(
    $stdin.read,
    strategy: options[:strategy],
    config: conf,
    core_rbs_provider: core_rbs_provider_for(conf),
    file: '(stdin)'
  )
end