1require 'rubygems/command'
2require 'rubygems/package'
3require 'rubygems/installer'
4require 'rubygems/version_option'
5
6class Gem::Commands::PristineCommand < Gem::Command
7
8  include Gem::VersionOption
9
10  def initialize
11    super 'pristine',
12          'Restores installed gems to pristine condition from files located in the gem cache',
13          :version => Gem::Requirement.default,
14          :extensions => true,
15          :all => false
16
17    add_option('--all',
18               'Restore all installed gems to pristine',
19               'condition') do |value, options|
20      options[:all] = value
21    end
22
23    add_option('--[no-]extensions',
24               'Restore gems with extensions',
25               'in addition to regular gems') do |value, options|
26      options[:extensions] = value
27    end
28
29    add_option('--only-executables',
30               'Only restore executables') do |value, options|
31      options[:only_executables] = value
32    end
33
34    add_version_option('restore to', 'pristine condition')
35  end
36
37  def arguments # :nodoc:
38    "GEMNAME       gem to restore to pristine condition (unless --all)"
39  end
40
41  def defaults_str # :nodoc:
42    '--extensions'
43  end
44
45  def description # :nodoc:
46    <<-EOF
47The pristine command compares an installed gem with the contents of its
48cached .gem file and restores any files that don't match the cached .gem's
49copy.
50
51If you have made modifications to an installed gem, the pristine command
52will revert them.  All extensions are rebuilt and all bin stubs for the gem
53are regenerated after checking for modifications.
54
55If the cached gem cannot be found it will be downloaded.
56
57If --no-extensions is provided pristine will not attempt to restore a gem
58with an extension.
59    EOF
60  end
61
62  def usage # :nodoc:
63    "#{program_name} [GEMNAME ...]"
64  end
65
66  def execute
67    specs = if options[:all] then
68              Gem::Specification.map
69            else
70              get_all_gem_names.map do |gem_name|
71                Gem::Specification.find_all_by_name gem_name, options[:version]
72              end.flatten
73            end
74
75    if specs.to_a.empty? then
76      raise Gem::Exception,
77            "Failed to find gems #{options[:args]} #{options[:version]}"
78    end
79
80    install_dir = Gem.dir # TODO use installer option
81
82    raise Gem::FilePermissionError.new(install_dir) unless
83      File.writable?(install_dir)
84
85    say "Restoring gems to pristine condition..."
86
87    specs.each do |spec|
88      if spec.default_gem?
89        say "Skipped #{spec.full_name}, it is a default gem"
90        next
91      end
92
93      unless spec.extensions.empty? or options[:extensions] then
94        say "Skipped #{spec.full_name}, it needs to compile an extension"
95        next
96      end
97
98      gem = spec.cache_file
99
100      unless File.exist? gem then
101        require 'rubygems/remote_fetcher'
102
103        say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
104        dep = Gem::Dependency.new spec.name, spec.version
105        Gem::RemoteFetcher.fetcher.download_to_cache dep
106      end
107
108      # TODO use installer options
109      install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
110      installer_env_shebang = install_defaults.to_s['--env-shebang']
111
112      installer = Gem::Installer.new(gem,
113                                     :wrappers => true,
114                                     :force => true,
115                                     :install_dir => spec.base_dir,
116                                     :env_shebang => installer_env_shebang,
117                                     :build_args => spec.build_args)
118      if options[:only_executables] then
119        installer.generate_bin
120      else
121        installer.install
122      end
123
124      say "Restored #{spec.full_name}"
125    end
126  end
127end
128