Class: UploadSnapshot

Inherits:
ApplicationRecord show all
Defined in:
app/models/upload_snapshot.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#uploadObject



35
36
37
# File 'app/models/upload_snapshot.rb', line 35

def upload
  @upload ||= uploads.find { |s3_file| filenames.include?(s3_file.filename) }
end

Class Method Details

.checksum_compare(checksum1, checksum2) ⇒ Object

Compares two checksums. Accounts for the case in which one of them is a plain MD5 value and the other has been encoded with base64. See also

https://ruby-doc.org/core-2.7.0/Array.html#method-i-pack
https://ruby-doc.org/core-2.7.0/String.html#method-i-unpack


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'app/models/upload_snapshot.rb', line 61

def checksum_compare(checksum1, checksum2)
  if checksum1 == checksum2
    true
  elsif checksum1.nil? || checksum2.nil?
    false
  elsif checksum1.length < checksum2.length
    # Decode the first one and then compare
    checksum1.unpack("m0").first.unpack("H*").first == checksum2
  else
    # Decode the second one and then compare
    checksum1 == checksum2.unpack("m0").first.unpack("H*").first
  end
rescue ArgumentError
  # One of the values was not properly encoded
  false
end

.find_by_filename(work_id:, filename:) ⇒ Object



51
52
53
# File 'app/models/upload_snapshot.rb', line 51

def self.find_by_filename(work_id:, filename:)
  find_by("work_id = ? AND files @> ?", work_id, JSON.dump([{ filename: }]))
end

Instance Method Details

#filenamesObject



43
44
45
# File 'app/models/upload_snapshot.rb', line 43

def filenames
  files.map { |file| file["filename"] }
end

#snapshot_deletions(work_changes, s3_filenames) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'app/models/upload_snapshot.rb', line 8

def snapshot_deletions(work_changes, s3_filenames)
  s3_filenames_sorted = s3_filenames.sort
  existing_files.each do |file|
    filename = file["filename"]
    # Use Ruby's Binary Search functionality instead of a plain Ruby Array `.include?`
    # to detect missing values in the array because the binary search performs
    # much faster when the list of files is large. Notice that the binary search
    # requires that the list of files is sorted.
    # See https://ruby-doc.org/3.3.6/bsearch_rdoc.html
    if s3_filenames_sorted.bsearch { |s3_filename| filename <=> s3_filename }.nil?
      work_changes << { action: "removed", filename:, checksum: file["checksum"] }
    end
  end
end

#snapshot_modifications(work_changes, s3_files) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
# File 'app/models/upload_snapshot.rb', line 23

def snapshot_modifications(work_changes, s3_files)
  # check for modifications
  s3_files.each do |s3_file|
    match = existing_files_sorted.bsearch { |file| s3_file.filename <=> file["filename"] }
    if match.nil?
      work_changes << { action: "added", filename: s3_file.filename, checksum: s3_file.checksum }
    elsif UploadSnapshot.checksum_compare(match["checksum"], s3_file.checksum) == false
      work_changes << { action: "replaced", filename: s3_file.filename, checksum: s3_file.checksum }
    end
  end
end

#store_files(s3_files) ⇒ Object



47
48
49
# File 'app/models/upload_snapshot.rb', line 47

def store_files(s3_files)
  self.files = s3_files.map { |file| { "filename" => file.filename, "checksum" => file.checksum } }
end

#uriObject



39
40
41
# File 'app/models/upload_snapshot.rb', line 39

def uri
  URI.parse(url)
end