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

[Source]

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

Runs unit tests for a given gem specification

[Source]

     # File lib/rubygems/validator.rb, line 201
201:   def unit_test(gem_spec)
202:     start_dir = Dir.pwd
203:     Dir.chdir(gem_spec.full_gem_path)
204:     $: << gem_spec.full_gem_path
205:     # XXX: why do we need this gem_spec when we've already got 'spec'?
206:     test_files = gem_spec.test_files
207: 
208:     if test_files.empty? then
209:       say "There are no unit tests to run for #{gem_spec.full_name}"
210:       return nil
211:     end
212: 
213:     gem gem_spec.name, "= #{gem_spec.version.version}"
214: 
215:     test_files.each do |f| require f end
216: 
217:     if RUBY_VERSION < '1.9' then
218:       suite = Test::Unit::TestSuite.new("#{gem_spec.name}-#{gem_spec.version}")
219: 
220:       ObjectSpace.each_object(Class) do |klass|
221:         suite << klass.suite if (klass < Test::Unit::TestCase)
222:       end
223: 
224:       result = TestRunner.run suite, ui
225: 
226:       alert_error result.to_s unless result.passed?
227:     else
228:       result = MiniTest::Unit.new
229:       result.run
230:     end
231: 
232:     result
233:   ensure
234:     Dir.chdir(start_dir)
235:   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 31
31:   def verify_gem(gem_data)
32:     raise Gem::VerificationError, 'empty gem file' if gem_data.size == 0
33: 
34:     unless gem_data =~ /MD5SUM/ then
35:       return # Don't worry about it...this sucks.  Need to fix MD5 stuff for
36:       # new format
37:       # FIXME
38:     end
39: 
40:     sum_data = gem_data.gsub(/MD5SUM = "([a-z0-9]+)"/,
41:                              "MD5SUM = \"#{"F" * 32}\"")
42: 
43:     unless Gem::MD5.hexdigest(sum_data) == $1.to_s then
44:       raise Gem::VerificationError, 'invalid checksum for gem file'
45:     end
46:   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 53
53:   def verify_gem_file(gem_path)
54:     open gem_path, Gem.binary_mode do |file|
55:       gem_data = file.read
56:       verify_gem gem_data
57:     end
58:   rescue Errno::ENOENT, Errno::EINVAL
59:     raise Gem::VerificationError, "missing gem file #{gem_path}"
60:   end

[Validate]