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,
  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]
}.freeze

Class Method Summary collapse

Class Method Details

.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



147
148
149
150
151
152
153
154
155
# File 'lib/docscribe/cli/run.rb', line 147

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 (Hash)

    parsed CLI options

Returns:



62
63
64
65
66
67
# File 'lib/docscribe/cli/run.rb', line 62

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

.change_line_suffix(change) ⇒ String

Format the line number suffix for a change reason string.

Parameters:

  • change (Hash)

    structured change record

Returns:

  • (String)

    “ at line N” or empty



645
646
647
# File 'lib/docscribe/cli/run.rb', line 645

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 (Hash)

    structured change record

Returns:

  • (String)

    “ for method_name” or empty



653
654
655
# File 'lib/docscribe/cli/run.rb', line 653

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

.core_rbs_provider_for(conf) ⇒ Object?

Return the core RBS provider from the config if available.

Parameters:

Returns:

  • (Object, nil)

    core RBS provider or nil



103
104
105
# File 'lib/docscribe/cli/run.rb', line 103

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 (Hash)

    structured change record

Returns:

  • (Boolean)


661
662
663
664
665
666
667
668
669
670
# File 'lib/docscribe/cli/run.rb', line 661

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



131
132
133
134
135
136
137
138
139
140
# File 'lib/docscribe/cli/run.rb', line 131

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



112
113
114
# File 'lib/docscribe/cli/run.rb', line 112

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 (Hash)

    structured change produced by the inline rewriter

Returns:

  • (String)

    human-readable explanation line



631
632
633
634
635
636
637
638
639
# File 'lib/docscribe/cli/run.rb', line 631

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

.no_files_foundInteger

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

Returns:

  • (Integer)

    exit code 1



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

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

This method returns an undefined value.

Print error paths from check summary.

Parameters:

  • state (Hash)


692
693
694
695
696
697
# File 'lib/docscribe/cli/run.rb', line 692

def print_error_paths(state)
  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.

Parameters:

  • state (Hash)
  • options (Hash)


598
599
600
601
602
603
604
605
606
607
# File 'lib/docscribe/cli/run.rb', line 598

def print_fail_paths(state, options)
  state[:fail_paths].each do |p|
    warn "Would update docs: #{p}"
    next unless options[:explain] && !options[:verbose]

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

This method returns an undefined value.

Print type mismatch paths from check summary.

Parameters:

  • state (Hash)
  • options (Hash)


615
616
617
618
619
620
621
622
623
624
# File 'lib/docscribe/cli/run.rb', line 615

def print_type_mismatch_paths(state, options)
  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 (Hash)

    shared processing state



677
678
679
680
681
682
683
684
685
# File 'lib/docscribe/cli/run.rb', line 677

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

  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 (Hash)

    parsed CLI options

  • argv (Array<String>)

    remaining path arguments

Returns:

  • (Integer)

    process exit code



47
48
49
50
51
52
53
54
55
56
# File 'lib/docscribe/cli/run.rb', line 47

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 (Hash)

    parsed CLI options

  • conf (Docscribe::Config)

    effective config

  • paths (Array<String>)

    Ruby file paths to process

Returns:

  • (Integer)

    process exit code



171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/docscribe/cli/run.rb', line 171

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

  state = initial_run_state
  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 (Hash)

    parsed CLI options

  • conf (Docscribe::Config)

    effective config

Returns:

  • (Integer)

    process exit code

Raises:

  • (StandardError)


76
77
78
79
80
81
82
# File 'lib/docscribe/cli/run.rb', line 76

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

Rewrite STDIN input and return the result report.

Parameters:

  • options (Hash)

    parsed CLI options

  • conf (Docscribe::Config)

    effective config

Returns:

  • (Hash)

    rewrite result with :output key



89
90
91
92
93
94
95
96
97
# File 'lib/docscribe/cli/run.rb', line 89

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