Class: Gem::Commands::RepairCommand

Inherits:
Gem::Command
  • Object
show all
Defined in:
lib/rubygems/commands/repair_command.rb

Instance Method Summary collapse

Constructor Details

#initializeRepairCommand

Returns a new instance of RepairCommand.



6
7
8
9
10
11
12
13
# File 'lib/rubygems/commands/repair_command.rb', line 6

def initialize
  super 'repair', 'Repairs gems with missing extensions by reinstalling them'

  add_option('-j', '--jobs JOBS', Integer,
             'Number of parallel threads to use (default: 4)') do |value, options|
    options[:jobs] = value
  end
end

Instance Method Details

#argumentsObject

:nodoc:



15
16
17
# File 'lib/rubygems/commands/repair_command.rb', line 15

def arguments # :nodoc:
  ""
end

#descriptionObject

:nodoc:



19
20
21
22
23
24
25
26
27
# File 'lib/rubygems/commands/repair_command.rb', line 19

def description # :nodoc:
  <<-EOF
The repair command finds all installed gems that are missing their compiled
extensions and attempts to reinstall them. This can be useful after upgrading
Ruby or changing system libraries.

Use -j to specify the number of parallel threads (default: 4).
  EOF
end

#executeObject



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/rubygems/commands/repair_command.rb', line 33

def execute
  say "Searching for gems with missing extensions..."

  specs = Gem::Specification.select do |spec|
    spec.platform == RUBY_ENGINE && spec.respond_to?(:missing_extensions?) && spec.missing_extensions?
  end

  specs = specs.group_by { |s| [s.name, s.version] }.map do |_, gems|
    non_default_specs = gems.select { |s| !s.default_gem? }
    if non_default_specs.empty?
      gems
    else
      non_default_specs
    end
  end.flatten(1).shuffle

  if specs.empty?
    say "No gems found with missing extensions."
    return
  end

  say "Found #{specs.count} gem(s) to repair: #{specs.map(&:full_name).join(', ')}"

  # Get number of threads from -j option, default to 4
  num_threads = options[:jobs] || 4
  # Running Gem::Installer in multiple threads deadlocks on old Rubies
  # (fatal "No live threads left" on Ruby 2.3), so repair sequentially there.
  num_threads = 1 if RUBY_VERSION < "2.6"

  if num_threads > 1
    say "Repairing gems using #{num_threads} parallel threads..."

    queue = Queue.new
    specs.each { |spec| queue << spec }

    threads = num_threads.times.map do
      Thread.new do
        while (spec = (queue.pop(true) rescue nil))
          repair_gem(spec)
        end
      end
    end

    threads.each(&:join)
  else
    say "Repairing gems sequentially..."

    specs.each { |spec| repair_gem(spec) }
  end

  say "Gem repair process complete."
end

#usageObject

:nodoc:



29
30
31
# File 'lib/rubygems/commands/repair_command.rb', line 29

def usage # :nodoc:
  "#{program_name} [options]"
end