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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
|
# frozen_string_literal: true
module FakeFS
# FileSystem module
module FileSystem
extend self
def dir_levels
@dir_levels ||= ['/']
end
def fs
@fs ||= FakeDir.new('/')
end
def clear
@dir_levels = nil
@fs = nil
end
def files
fs.entries
end
# Finds files/directories using the exact path, without expanding globs.
def find(path, dir: nil)
parts = path_parts(normalize_path(path, dir: dir))
return fs if parts.empty? # '/'
find_recurser(fs, parts)
end
# Finds files/directories expanding globs.
def find_with_glob(path, find_flags = 0, gave_char_class = false, dir: nil)
parts = path_parts(normalize_path(path, dir: dir))
return fs if parts.empty? # '/'
entries = Globber.expand(path).flat_map do |pattern|
parts = path_parts(normalize_path(pattern, dir: dir))
find_with_glob_recurser(fs, parts, find_flags, gave_char_class).flatten
end
case entries.length
when 0 then nil
when 1 then entries.first
else entries
end
end
def add(path, object = FakeDir.new)
parts = path_parts(normalize_path(path))
d = parts[0...-1].reduce(fs) do |dir, part|
assert_dir dir[part] if dir[part]
dir[part] ||= FakeDir.new(part, dir)
end
assert_dir d
# Short-circuit if added path is file system root, to avoid adding nil-name fs entries:
return fs if parts.empty?
object.name = parts.last
object.parent = d
if object.is_a? FakeDir
d[parts.last] ||= object
else
d[parts.last] = object
end
end
# copies directories and files from the real filesystem
# into our fake one
def clone(path, target = nil)
path = RealFile.expand_path(path)
pattern = File.join(path, '**', '*')
files = if RealFile.file?(path)
[path]
else
[path] + RealDir.glob(pattern, RealFile::FNM_DOTMATCH)
end
files.each do |f|
target_path = target ? f.gsub(path, target) : f
if RealFile.symlink?(f)
FileUtils.ln_s(RealFile.readlink(f), f)
elsif RealFile.file?(f)
FileUtils.mkdir_p(File.dirname(f))
File.open(target_path, File::WRITE_ONLY) do |g|
g.print RealFile.read(f)
end
elsif RealFile.directory?(f)
FileUtils.mkdir_p(target_path)
end
end
end
def delete(path)
return unless (node = FileSystem.find(path))
node.delete
true
end
def chdir(dir, &blk)
new_dir = find(dir)
dir_levels.push dir.to_s if blk
raise Errno::ENOENT, dir.to_s unless new_dir
raise Errno::ENOTDIR, dir.to_s unless File.directory? new_dir
dir_levels.push dir.to_s unless blk
yield(dir) if blk
ensure
dir_levels.pop if blk
end
def path_parts(path)
Globber.path_components(path)
end
def normalize_path(path, dir: nil)
if Pathname.new(path).absolute?
RealFile.expand_path(path)
else
dir ||= dir_levels
dir = Array(dir)
parts = dir + [path]
RealFile.expand_path(parts.reduce do |base, part|
Pathname(base) + part
end.to_s)
end
end
def current_dir
find('.')
end
private
def find_recurser(dir, parts, find_flags = 0, gave_char_class = false)
return nil unless dir.respond_to? :[]
head, *parts = parts
match = dir.entries.find { |e| e.name == head }
if parts.empty? # we're done recursing
match
else
find_recurser(match, parts, find_flags, gave_char_class)
end
end
def find_with_glob_recurser(dir, parts, find_flags = 0, gave_char_class = false)
return [] unless dir.respond_to? :[]
pattern, *parts = parts
matches =
case pattern
when '**'
case parts
when ['*']
parts = [] # end recursion
directories_under(dir).map do |d|
d.entries.select do |f|
(f.is_a?(FakeFile) || f.is_a?(FakeDir)) &&
f.name.match(/\A(?!\.)/)
end
end.flatten.uniq
when []
parts = [] # end recursion
dir.entries.flatten.uniq
else
directories_under(dir)
end
else
Globber.expand(pattern).flat_map do |subpattern|
dir.matches(Globber.regexp(subpattern, find_flags, gave_char_class))
end
end
if parts.empty? # we're done recursing
matches
else
matches.map { |entry| find_with_glob_recurser(entry, parts, find_flags, gave_char_class) }
end
end
def directories_under(dir)
children = dir.entries.select { |f| f.is_a? FakeDir }
([dir] + children + children.map { |c| directories_under(c) })
.flatten.uniq
end
def assert_dir(dir)
raise Errno::EEXIST, dir.name unless dir.is_a?(FakeDir)
end
end
end
|