class GitDiffService

Constants

ALLOWED_EXTENSION
EXCLUDE_ROOT_FOLDERS

restrict scope: for Rails app exclude autogenerated code and omit any files except .rb

PATTERN_MATCH

Public Class Methods

call(diff_lines, exclude_folders = []) click to toggle source
# File lib/git_diff_service.rb, line 8
def call(diff_lines, exclude_folders = [])
  @diff_lines = diff_lines
  # TO-DO: support @only_folders
  @exclude_folders = exclude_folders + EXCLUDE_ROOT_FOLDERS

  patch_pos_start = lines_with_file_paths.map { |line| patch_start_pos(line) }
  # array with start and end positions for each patch
  @patches_pos = patch_pos_start.zip(patch_end_pos(patch_pos_start))
  slice_diff
  @patches = @patches.select { |patch| allowed_patch?(patch) }
  @diff_data = @patches.map { |patch| fname_diff(patch) }
  @diff_data += @patches.map { |patch| class_diff(patch) }
  @diff_data.compact
end

Private Class Methods

allowed_patch?(patch) click to toggle source
# File lib/git_diff_service.rb, line 30
def allowed_patch?(patch)
  fname_string = patch.first
  extension_allowed = extract_file_paths(fname_string).last.end_with?(ALLOWED_EXTENSION)
  folder_allowed = !extract_file_paths(fname_string).last.start_with?(*@exclude_folders)

  extension_allowed && folder_allowed
end
class_diff(patch) click to toggle source
# File lib/git_diff_service.rb, line 83
def class_diff(patch)
  return { old_name: old_class_name(patch),
                     new_name: new_class_name(patch), status: :renamed_class } if class_renamed?(patch)
end
class_renamed?(patch) click to toggle source
# File lib/git_diff_service.rb, line 50
def class_renamed?(patch)
  patch.select { |line| line.start_with?('-class') }.any? &&
      patch.select { |line| line.start_with?('+class') }.any?
end
deleted_file?(patch) click to toggle source
# File lib/git_diff_service.rb, line 42
def deleted_file?(patch)
  patch.select { |line| line.start_with?('deleted file mode ') }.any?
end
extract_file_paths(line) click to toggle source
# File lib/git_diff_service.rb, line 106
def extract_file_paths(line)
  # diff --git a/project_v1/README.md b/project_v2/README2.md
  line = line.delete_prefix('diff --git ')
  path1, path2 = line.split(' ')
  [path1, path2].map! { |path| file_path(path) }
end
file_path(str) click to toggle source
# File lib/git_diff_service.rb, line 113
def file_path(str)
  # a/project_v1/README.md -> project_v1/README.md
  prefix, *path_parts = str.split('/')
  path_parts.join('/')
end
fname_diff(patch) click to toggle source
# File lib/git_diff_service.rb, line 69
def fname_diff(patch)
  # first line in patch contains info about file path
  file_path_line = patch.first
  return { old_name: extract_file_paths(file_path_line).first,
           new_name: extract_file_paths(file_path_line).last, status: :renamed } if renamed_file?(patch)
  return { old_name: nil, new_name: extract_file_paths(file_path_line).last,
           status: :new } if new_file?(patch)
  return { old_name: extract_file_paths(file_path_line).first,
           new_name: nil,  status: :deleted } if deleted_file?(patch)
  # changed file without renaming
  return { old_name: extract_file_paths(file_path_line).first,
           new_name: extract_file_paths(file_path_line).last,  status: :changed }
end
lines_with_file_paths() click to toggle source
# File lib/git_diff_service.rb, line 98
def lines_with_file_paths
  @diff_lines.select { |line| line.scan(PATTERN_MATCH).any? }
end
new_class_name(patch) click to toggle source
# File lib/git_diff_service.rb, line 62
def new_class_name(patch)
  res = patch.select { |line| line.start_with?('+class') }
  return if res.empty?
  line = res.first
  line.split(' ').last.strip
end
new_file?(patch) click to toggle source
# File lib/git_diff_service.rb, line 38
def new_file?(patch)
  patch.select { |line| line.start_with?('new file mode ') }.any?
end
old_class_name(patch) click to toggle source
# File lib/git_diff_service.rb, line 55
def old_class_name(patch)
  res = patch.select { |line| line.start_with?('-class') }
  return if res.empty?
  line = res.first
  line.split(' ').last.strip
end
patch_end_pos(patch_pos_start) click to toggle source

end line index for current patch

# File lib/git_diff_service.rb, line 89
def patch_end_pos(patch_pos_start)
  end_line_pos = patch_pos_start.size - 1
  pos_end = patch_pos_start.map.with_index do |pos, i|
    i == patch_pos_start.size - 1 ? @diff_lines.size - 1 : patch_pos_start[i + 1] - 1
  end

  pos_end
end
patch_start_pos(line) click to toggle source
# File lib/git_diff_service.rb, line 102
def patch_start_pos(line)
  @diff_lines.index(line)
end
renamed_file?(patch) click to toggle source
# File lib/git_diff_service.rb, line 46
def renamed_file?(patch)
  patch.select { |line| line.start_with?('rename from ') }.any?
end
slice_diff() click to toggle source

split diff lines into separate patches for each file

# File lib/git_diff_service.rb, line 26
def slice_diff
  @patches = @patches_pos.map { |pos| @diff_lines[pos.first..pos.last] }
end