lib/jekyll/path_manager.rb
# 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