-
-
Notifications
You must be signed in to change notification settings - Fork 9.9k
/
path_manager.rb
74 lines (66 loc) · 2.95 KB
/
path_manager.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# frozen_string_literal: true
module Jekyll
# A singleton class that caches frozen instances of path strings returned from its methods.
#
# NOTE:
# This class exists because `File.join` allocates an Array and returns a new String on every
# call using **the same arguments**. Caching the result means reduced memory usage.
# However, the caches are never flushed so that they can be used even when a site is
# regenerating. The results are frozen to deter mutation of the cached string.
#
# Therefore, employ this class only for situations where caching the result is necessary
# for performance reasons.
#
class PathManager
# This class cannot be initialized from outside
private_class_method :new
class << self
# Wraps `File.join` to cache the frozen result.
# Reassigns `nil`, empty strings and empty arrays to a frozen empty string beforehand.
#
# Returns a frozen string.
def join(base, item)
base = "" if base.nil? || base.empty?
item = "" if item.nil? || item.empty?
@join ||= {}
@join[base] ||= {}
@join[base][item] ||= File.join(base, item).freeze
end
# Ensures the questionable path is prefixed with the base directory
# and prepends the questionable path with the base directory if false.
#
# Returns a frozen string.
def sanitized_path(base_directory, questionable_path)
@sanitized_path ||= {}
@sanitized_path[base_directory] ||= {}
@sanitized_path[base_directory][questionable_path] ||= if questionable_path.nil?
base_directory.freeze
else
sanitize_and_join(
base_directory, questionable_path
).freeze
end
end
private
def sanitize_and_join(base_directory, questionable_path)
clean_path = if questionable_path.start_with?("~")
questionable_path.dup.insert(0, "/")
else
questionable_path
end
clean_path = File.expand_path(clean_path, "/")
return clean_path if clean_path.eql?(base_directory)
# remove any remaining extra leading slashes not stripped away by calling
# `File.expand_path` above.
clean_path.squeeze!("/")
return clean_path if clean_path.start_with?(slashed_dir_cache(base_directory))
clean_path.sub!(%r!\A\w:/!, "/")
join(base_directory, clean_path)
end
def slashed_dir_cache(base_directory)
@slashed_dir_cache ||= {}
@slashed_dir_cache[base_directory] ||= base_directory.sub(%r!\z!, "/")
end
end
end
end