class Gem::Comparator::FileListComparator

Gem::Comparator::FileListComparator can compare file lists from gem's specs based on the given Gem::Package objects

To compare the files it needs to extract gem packages to +options+

Public Class Methods

new() click to toggle source
# File lib/rubygems/comparator/file_list_comparator.rb, line 18
def initialize
  expect(:packages)

  # We need diff
  begin
    IO.popen('diff --version')
  rescue Exception
    error('Calling `diff` command failed. Do you have it installed?')
  end
end

Public Instance Methods

compare(packages, report, options = {}) click to toggle source

Compare file lists for gem's Gem::Package objects in packages and writes the changes to the report

If +options+ is set, it compares only that file list

# File lib/rubygems/comparator/file_list_comparator.rb, line 36
def compare(packages, report, options = {})
  info 'Checking file lists...'

  @packages = packages

  # Check file lists from older versions to newer
  filter_params(SPEC_FILES_PARAMS, options[:param]).each do |param|
    all_same = true

    packages.each_with_index do |pkg, index|
      unpacked_gem_dirs[packages[index].spec.version] = extract_gem(pkg, options[:output])
      next if index == 0

      # File lists as arrays
      previous = value_from_spec(param, packages[index-1].spec)
      current = value_from_spec(param, pkg.spec)
      next unless (previous && current)

      vers = "#{packages[index-1].spec.version}->#{packages[index].spec.version}"

      deleted = previous - current
      added = current - previous
      same = current - added

      if options[:brief]
        deleted, dirs_added = dir_changed(previous, current)
      end

      report[param].set_header "#{different} #{param}:"

      report[param][vers].section do
        set_header "#{Rainbow(packages[index-1].spec.version).cyan}->" +
                   "#{Rainbow(packages[index].spec.version).cyan}:"
        nest('deleted').section do
          set_header '* Deleted:'
          puts deleted unless deleted.empty?
        end

        nest('added').section do
          set_header '* Added:'
          puts dirs_added if options[:brief]
        end
      end
      # Add information about permissions, shebangs etc.
      report = check_added_files(param, vers, index, added, report, options[:brief])

      report[param][vers]['changed'].set_header '* Changed:'
      report = check_same_files(param, vers, index, same, report, options[:brief])
      same_files = report[param][vers]['changed'].messages.empty?
      all_same = false unless same_files

      if previous == current && same_files && !all_same
        report[param][vers] << "#{Rainbow(packages[index-1].spec.version).cyan}->" + \
                               "#{Rainbow(packages[index].spec.version).cyan}: No change"
      end

    end

    if all_same && options[:log_all]
      report[param].set_header "#{same} #{param}:"
      value = value_from_spec(param, @packages[0].spec)
      value = '[]' if value.empty?
      report[param] << value
    end
  end
  report
end

Private Instance Methods

check_added_files(param, vers, index, files, report, brief_mode) click to toggle source
# File lib/rubygems/comparator/file_list_comparator.rb, line 130
def check_added_files(param, vers, index, files, report, brief_mode)
  files.each do |file|
    added_file = File.join(unpacked_gem_dirs[@packages[index].spec.version], file)

    #line_changes = lines_changed(prev_file, curr_file)

    changes = Monitor.new_file_permissions(added_file),
              Monitor.new_file_executability(added_file),
              Monitor.new_file_shebang(added_file)

    if(!changes.join.empty? || !brief_mode)
      report[param][vers]['added'] << "#{file}"
    end

    changes.each do |change|
      report[param][vers]['added'] << change unless change.empty?
    end
  end
  report
end
check_files(files) click to toggle source

Check that files exist

# File lib/rubygems/comparator/file_list_comparator.rb, line 179
def check_files(files)
  files.each do |file|
    unless File.exist? file
      warn "#{file} mentioned in spec does not exist " +
           "in the gem package, skipping check"
      return false
    end
  end
  true
end
check_same_files(param, vers, index, files, report, brief_mode) click to toggle source
# File lib/rubygems/comparator/file_list_comparator.rb, line 151
def check_same_files(param, vers, index, files, report, brief_mode)
  files.each do |file|
    prev_file = File.join(unpacked_gem_dirs[@packages[index-1].spec.version], file)
    curr_file = File.join(unpacked_gem_dirs[@packages[index].spec.version], file)

    next unless check_files([prev_file, curr_file])

    line_changes = Monitor.lines_changed(prev_file, curr_file)

    changes = Monitor.files_permissions_changes(prev_file, curr_file),
              Monitor.files_executability_changes(prev_file, curr_file),
              Monitor.files_shebang_changes(prev_file, curr_file)

    if(!changes.join.empty? || (!brief_mode && !line_changes.empty?))
      report[param][vers]['changed'] << \
        "#{file} #{line_changes}"
    end

    changes.each do |change|
      report[param][vers]['changed'] << change unless change.empty?
    end
  end
  report
end
dir_changed(previous, current) click to toggle source

This returns [deleted, added] directories between previous and current file lists

For top level (.) it compares files themselves

# File lib/rubygems/comparator/file_list_comparator.rb, line 122
def dir_changed(previous, current)
  prev_dirs = DirUtils.dirs_of_files(previous)
  curr_dirs = DirUtils.dirs_of_files(current)
  deleted = DirUtils.remove_subdirs(prev_dirs - curr_dirs)
  added = DirUtils.remove_subdirs(curr_dirs - prev_dirs)
  [deleted, added]
end
unpacked_gem_dirs() click to toggle source

Access @unpacked_gem_dirs hash that stores paths to the unpacked gem dirs

Keys of the hash are gem's versions

# File lib/rubygems/comparator/file_list_comparator.rb, line 112
def unpacked_gem_dirs
  @unpacked_gem_dirs ||= {}
end