Module: Cul::PreservationUtils::FilePath

Defined in:
lib/cul/preservation_utils/file_path.rb

Constant Summary collapse

DISALLOWED_ASCII_REGEX =

About Cloud Storage objects: https://cloud.google.com/storage/docs/objects According to the above (and quite probably most Google Cloud Storage documentation), objects have names AWS - Creating object key names: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html As seen in the title for the above page, an object in AWS S3 has a key name (or key)

'[^-a-zA-Z0-9_.()]'

Class Method Summary collapse

Class Method Details

.argument_check(filepath_key) ⇒ Object

Raises:

  • (ArgumentError)


72
73
74
75
# File 'lib/cul/preservation_utils/file_path.rb', line 72

def self.argument_check(filepath_key)
  raise ArgumentError, "Bad argument: '#{filepath_key}'" if ['', '.', '..', '/'].include? filepath_key
  raise ArgumentError, 'Bad argument: absolute path' if filepath_key.start_with?('/')
end

.handle_collision(remediated_file_path, unavailable_file_path) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/cul/preservation_utils/file_path.rb', line 86

def self.handle_collision(remediated_file_path, unavailable_file_path)
  pathname = Pathname.new(remediated_file_path)
  base = pathname.to_s.delete_suffix(pathname.extname)
  new_remediated_file_path = "#{base}_1#{pathname.extname}"
  suffix_num = 1
  while unavailable_file_path.include? new_remediated_file_path
    suffix_num += 1
    new_remediated_file_path = "#{base}_#{suffix_num}#{pathname.extname}"
  end
  new_remediated_file_path
end

.remediate_file_path(filepath, unavailable_file_paths = []) ⇒ Object

rubocop:disable Metrics/AbcSize



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/cul/preservation_utils/file_path.rb', line 50

def self.remediate_file_path(filepath, unavailable_file_paths = []) # rubocop:disable Metrics/AbcSize
  return filepath if !unavailable_file_paths.include?(filepath) && self.valid_file_path?(filepath)

  self.argument_check(filepath)

  pathname = Pathname.new(filepath)

  remediated_pathname = Pathname.new('')
  path_to_file, filename = pathname.split

  filename_valid_ascii =
    Stringex::Unidecoder.decode(filename.to_s).gsub(/#{DISALLOWED_ASCII_REGEX}/, '_').gsub(/\.$/, '_')

  remediated_key_name = self.remediate_path(path_to_file, remediated_pathname).join(filename_valid_ascii).to_s

  # no collisions
  return remediated_key_name unless unavailable_file_paths.include? remediated_key_name

  # handle collisions
  self.handle_collision(remediated_key_name, unavailable_file_paths)
end

.remediate_path(path_to_file, remediated_pathname) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/cul/preservation_utils/file_path.rb', line 77

def self.remediate_path(path_to_file, remediated_pathname)
  # remediate each component in the path to the file
  path_to_file.each_filename do |path_segment|
    remediated_path_segment = Stringex::Unidecoder.decode(path_segment).gsub(/#{DISALLOWED_ASCII_REGEX}/, '_')
    remediated_pathname += remediated_path_segment
  end
  remediated_pathname
end

.valid_file_path?(path_filename) ⇒ Boolean

Returns:

  • (Boolean)


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/cul/preservation_utils/file_path.rb', line 27

def self.valid_file_path?(path_filename)
  return false if ['', '.', '..', '/'].include? path_filename

  pathname = Pathname.new(path_filename)

  # a relative path is invalid
  # todo : doesn't this code do the opposite of that?
  return false if pathname.absolute?

  path_to_file, filename = pathname.split

  # validate filename
  return false if filename.to_s.end_with?('.') || /#{DISALLOWED_ASCII_REGEX}/.match?(filename.to_s)
  # if the valid filename is at the top level, return true
  return true if pathname == pathname.basename

  # check each component in the path to the file
  path_to_file.each_filename do |path_segment|
    return false if /#{DISALLOWED_ASCII_REGEX}/.match? path_segment
  end
  true
end