class VirtualFile

Attributes

business_value[R]
commits[R]
file_id[R]
issues[R]
lines[R]
path[R]

Public Class Methods

from_json(file_id, json) click to toggle source
# File lib/virtual_file.rb, line 32
def self.from_json(file_id, json)
  file = VirtualFile.new(json["path"])
  touched_at_times = (json["touched_at_times"] || []).map { |time| DateTime.rfc3339(time) }
  lines = (json["lines"] || []).map { |line| line.deep_symbolize_keys }
  functions = (json["functions"] || []).map { |function| function.deep_symbolize_keys }

  file.instance_variable_set(:@file_id, file_id)
  file.instance_variable_set(:@previous_file_id, json["previous_file_id"])
  file.instance_variable_set(:@touched_at_times, touched_at_times)
  file.instance_variable_set(:@lines, lines)
  file.instance_variable_set(:@functions, functions)
  file.instance_variable_set(:@commits, json["commits"] || [])
  file.instance_variable_set(:@issues, json["issues"] || [])
  file.instance_variable_set(:@bugs, json["bugs"] || [])
  file.instance_variable_set(:@business_value, json["business_value"] || 0)
  file.instance_variable_set(:@is_binary, false)
  file
end
new(file_path, diff=nil) click to toggle source
# File lib/virtual_file.rb, line 18
def initialize(file_path, diff=nil)
  @path = file_path
  @file_id = nil
  @previous_file_id = nil
  @touched_at_times = []
  @commits = []
  @issues = []
  @bugs = []
  @lines = []
  @functions = []
  @business_value = 0
  @is_binary = diff.blank? ? false : diff.operation == :binary
end

Public Instance Methods

apply_file_diff!(commit, file_diff) click to toggle source
# File lib/virtual_file.rb, line 71
def apply_file_diff!(commit, file_diff)
  touch(commit, file_diff)
  return if binary?

  context = file_diff.diffs
    .reduce(new_context(commit), &method(:apply_diff))

  new_lines = context[:new_lines]

  unchanged_lines = @lines[context[:position]..-1]
  new_lines.concat(unchanged_lines) if unchanged_lines

  @lines = new_lines
end
as_json() click to toggle source
# File lib/virtual_file.rb, line 127
def as_json()
  {
    path: @path,
    previous_file_id: @previous_file_id,
    lines: @lines,
    functions: @functions.map { |function| function.as_json() },
    commits: @commits,
    issues: @issues,
    business_value: @business_value,
    bugs: @bugs
  }
end
binary?() click to toggle source
# File lib/virtual_file.rb, line 86
def binary?
  @is_binary
end
buggy_lines_total() click to toggle source
# File lib/virtual_file.rb, line 155
def buggy_lines_total()
  @lines
    .map do |line|
      line[:revisions].flat_map { |revision| revision[:bugs] }.count +
        line[:bugs].count
    end
    .reduce(0, &:+)
end
change_line!(line_change, line_number) click to toggle source
# File lib/virtual_file.rb, line 102
def change_line!(line_change, line_number)
  @lines[line_number] = Line::change!(line_change, @lines[line_number])
end
covered_lines() click to toggle source
# File lib/virtual_file.rb, line 164
def covered_lines
  @lines
    .select { |line| line[:coverage] != nil && line[:coverage] > 0 }
    .count
end
created_at() click to toggle source
# File lib/virtual_file.rb, line 94
def created_at
  @touched_at_times.first
end
errors_total() click to toggle source
# File lib/virtual_file.rb, line 146
def errors_total()
  @lines
    .map do |line|
      line[:revisions].flat_map { |revision| revision[:errors] }.count +
        line[:errors].count
    end
    .reduce(0, &:+)
end
last_updated_at() click to toggle source
# File lib/virtual_file.rb, line 98
def last_updated_at
  @touched_at_times.last
end
merge_coverage!(file_coverage) click to toggle source
# File lib/virtual_file.rb, line 116
def merge_coverage!(file_coverage)
  @lines.each_with_index do |line, index|
    line[:coverage] = file_coverage.dig("lines", (index + 1).to_s, "hits")
  end
end
merge_error!(error, trace, depth) click to toggle source
# File lib/virtual_file.rb, line 122
def merge_error!(error, trace, depth)
  line = @lines[trace[:line] - 1]
  Line::merge_error!(line, error, depth)
end
move_line!(line_movement, line_number) click to toggle source
# File lib/virtual_file.rb, line 106
def move_line!(line_movement, line_number)
  line = @lines[line_number]
  if line_movement[:text].strip() != line[:text].strip()
    # TODO: raise reconstruction error.
    Loggr.instance.warn("MISMATCH: #{line[:text]} VS #{line_movement[:text]}")
  else
    @lines[line_number] = Line::move!(line_movement, line)
  end
end
plain_text() click to toggle source
# File lib/virtual_file.rb, line 90
def plain_text
  @lines.map { |line| line[:text] }
end
revisions_total() click to toggle source
# File lib/virtual_file.rb, line 140
def revisions_total()
  @lines
    .map { |line| line[:revisions].count }
    .reduce(0, &:+)
end
save() click to toggle source
# File lib/virtual_file.rb, line 62
def save()
  @functions = Tracer
    .new
    .trace(@path, as_text())
    .map { |function| VirtualFunction.new(function, @path, @lines) }

  Cache::write_object(@file_id, as_json())
end
touch(commit, file_diff) click to toggle source
# File lib/virtual_file.rb, line 51
def touch(commit, file_diff)
  @path = file_diff.b_file_name
  @file_id = file_diff.b_file_id if not file_diff.b_file_id.nil?
  @previous_file_id = file_diff.a_file_id
  @touched_at_times << commit.date
  @commits << commit.id
  @issues << commit.issue_id if commit.issue_id.present?
  @bugs << commit.bug_id if commit.bug_id.present?
  @business_value += diff_business_value(commit, file_diff)
end
uncovered_lines() click to toggle source
# File lib/virtual_file.rb, line 170
def uncovered_lines
  @lines
    .select { |line| line[:coverage] == 0 }
    .count
end

Private Instance Methods

apply_diff(acc, diff) click to toggle source
# File lib/virtual_file.rb, line 184
def apply_diff(acc, diff)
  delete_start = diff.delete_start
  delete_count = diff.delete_count
  copy_end = delete_count == 0 ?
    delete_start :
    delete_start - 1

  new_lines = diff.insertions
    .each_with_index
    .map do |line, index|
      line_num = diff.insert_start + index
      Line::new_line(line, @path, line_num, acc[:commit])
    end
  unchanged_lines = acc[:existing_lines][acc[:position]...copy_end]

  acc[:new_lines].concat(unchanged_lines) if unchanged_lines
  acc[:position] = copy_end + delete_count

  acc[:new_lines].concat(new_lines)
  acc
end
as_text() click to toggle source
# File lib/virtual_file.rb, line 178
def as_text()
  @lines
    .map { |line| line[:text] }
    .join("\n")
end
diff_business_value(commit, file_diff) click to toggle source
# File lib/virtual_file.rb, line 206
def diff_business_value(commit, file_diff)
  (file_diff.changes_total / commit.changes_total.to_f) * commit.business_value
end
new_context(commit) click to toggle source
# File lib/virtual_file.rb, line 210
def new_context(commit)
  {
    position: 0,
    new_lines: [],
    existing_lines: @lines,
    commit: commit
  }
end