Class Gem::Validator
In: lib/rubygems/validator.rb
Parent: Object

Validator performs various gem file and gem database validation

Methods

Included Modules

Gem::UserInteraction

Classes and Modules

Class Gem::Validator::TestRunner

Constants

ErrorData = Struct.new :path, :problem

Public Instance methods

Checks the gem directory for the following potential inconsistencies/problems:

  • Checksum gem itself
  • For each file in each gem, check consistency of installed versions
  • Check for files that aren‘t part of the gem but are in the gems directory
  • 1 cache - 1 spec - 1 directory.

returns a hash of ErrorData objects, keyed on the problem gem‘s name.

[Source]

     # File lib/rubygems/validator.rb, line 88
 88:   def alien(gems=[])
 89:     errors = Hash.new { |h,k| h[k] = {} }
 90: 
 91:     Gem::SourceIndex.from_installed_gems.each do |gem_name, gem_spec|
 92:       next unless gems.include? gem_spec.name unless gems.empty?
 93: 
 94:       install_dir = gem_spec.installation_path
 95:       gem_path = File.join install_dir, "cache", gem_spec.file_name
 96:       spec_path = File.join install_dir, "specifications", gem_spec.spec_name
 97:       gem_directory = gem_spec.full_gem_path
 98: 
 99:       unless File.directory? gem_directory then
100:         errors[gem_name][gem_spec.full_name] =
101:           "Gem registered but doesn't exist at #{gem_directory}"
102:         next
103:       end
104: 
105:       unless File.exist? spec_path then
106:         errors[gem_name][spec_path] = "Spec file missing for installed gem"
107:       end
108: 
109:       begin
110:         verify_gem_file(gem_path)
111: 
112:         good, gone, unreadable = nil, nil, nil, nil
113: 
114:         open gem_path, Gem.binary_mode do |file|
115:           format = Gem::Format.from_file_by_path(gem_path)
116: 
117:           good, gone = format.file_entries.partition { |entry, _|
118:             File.exist? File.join(gem_directory, entry['path'])
119:           }
120: 
121:           gone.map! { |entry, _| entry['path'] }
122:           gone.sort.each do |path|
123:             errors[gem_name][path] = "Missing file"
124:           end
125: 
126:           good, unreadable = good.partition { |entry, _|
127:             File.readable? File.join(gem_directory, entry['path'])
128:           }
129: 
130:           unreadable.map! { |entry, _| entry['path'] }
131:           unreadable.sort.each do |path|
132:             errors[gem_name][path] = "Unreadable file"
133:           end
134: 
135:           good.each do |entry, data|
136:             begin
137:               next unless data # HACK `gem check -a mkrf`
138: 
139:               open File.join(gem_directory, entry['path']), Gem.binary_mode do |f|
140:                 unless Digest::MD5.hexdigest(f.read).to_s ==
141:                     Digest::MD5.hexdigest(data).to_s then
142:                   errors[gem_name][entry['path']] = "Modified from original"
143:                 end
144:               end
145:             end
146:           end
147:         end
148: 
149:         installed_files = find_files_for_gem(gem_directory)
150:         good.map! { |entry, _| entry['path'] }
151:         extras = installed_files - good - unreadable
152: 
153:         extras.each do |extra|
154:           errors[gem_name][extra] = "Extra file"
155:         end
156:       rescue Gem::VerificationError => e
157:         errors[gem_name][gem_path] = e.message
158:       end
159:     end
160: 
161:     errors.each do |name, subhash|
162:       errors[name] = subhash.map { |path, msg| ErrorData.new(path, msg) }
163:     end
164: 
165:     errors
166:   end

[Source]

     # File lib/rubygems/validator.rb, line 235
235:   def remove_leading_dot_dir(path)
236:     path.sub(/^\.\//, "")
237:   end

Runs unit tests for a given gem specification

[Source]

     # File lib/rubygems/validator.rb, line 199
199:   def unit_test(gem_spec)
200:     start_dir = Dir.pwd
201:     Dir.chdir(gem_spec.full_gem_path)
202:     $: << gem_spec.full_gem_path
203:     # XXX: why do we need this gem_spec when we've already got 'spec'?
204:     test_files = gem_spec.test_files
205: 
206:     if test_files.empty? then
207:       say "There are no unit tests to run for #{gem_spec.full_name}"
208:       return nil
209:     end
210: 
211:     gem gem_spec.name, "= #{gem_spec.version.version}"
212: 
213:     test_files.each do |f| require f end
214: 
215:     if RUBY_VERSION < '1.9' then
216:       suite = Test::Unit::TestSuite.new("#{gem_spec.name}-#{gem_spec.version}")
217: 
218:       ObjectSpace.each_object(Class) do |klass|
219:         suite << klass.suite if (klass < Test::Unit::TestCase)
220:       end
221: 
222:       result = TestRunner.run suite, ui
223: 
224:       alert_error result.to_s unless result.passed?
225:     else
226:       result = MiniTest::Unit.new
227:       result.run
228:     end
229: 
230:     result
231:   ensure
232:     Dir.chdir(start_dir)
233:   end

Given a gem file‘s contents, validates against its own MD5 checksum

gem_data:[String] Contents of the gem file

[Source]

    # File lib/rubygems/validator.rb, line 30
30:   def verify_gem(gem_data)
31:     raise Gem::VerificationError, 'empty gem file' if gem_data.size == 0
32: 
33:     unless gem_data =~ /MD5SUM/ then
34:       return # Don't worry about it...this sucks.  Need to fix MD5 stuff for
35:       # new format
36:       # FIXME
37:     end
38: 
39:     sum_data = gem_data.gsub(/MD5SUM = "([a-z0-9]+)"/,
40:                              "MD5SUM = \"#{"F" * 32}\"")
41: 
42:     unless Digest::MD5.hexdigest(sum_data) == $1.to_s then
43:       raise Gem::VerificationError, 'invalid checksum for gem file'
44:     end
45:   end

Given the path to a gem file, validates against its own MD5 checksum

gem_path:[String] Path to gem file

[Source]

    # File lib/rubygems/validator.rb, line 52
52:   def verify_gem_file(gem_path)
53:     open gem_path, Gem.binary_mode do |file|
54:       gem_data = file.read
55:       verify_gem gem_data
56:     end
57:   rescue Errno::ENOENT, Errno::EINVAL
58:     raise Gem::VerificationError, "missing gem file #{gem_path}"
59:   end

[Validate]