module FakeFS::FileSystem
def add(path, object = FakeDir.new)
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
def assert_dir(dir)
def assert_dir(dir) raise Errno::EEXIST, dir.name unless dir.is_a?(FakeDir) end
def chdir(dir, &blk)
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 clear
def clear @dir_levels = nil @fs = nil end
def clone(path, target = nil)
copies directories and files from the real filesystem
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 current_dir
def current_dir find('.') end
def delete(path)
def delete(path) return unless (node = FileSystem.find(path)) node.delete true end
def dir_levels
def dir_levels @dir_levels ||= ['/'] end
def directories_under(dir)
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 files
def files fs.entries end
def find(path, dir: nil)
def find(path, dir: nil) parts = path_parts(normalize_path(path, dir: dir)) return fs if parts.empty? # '/' find_recurser(fs, parts) end
def find_recurser(dir, parts, find_flags = 0, gave_char_class = false)
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(path, find_flags = 0, gave_char_class = false, dir: nil)
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 find_with_glob_recurser(dir, parts, find_flags = 0, gave_char_class = false)
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 fs
def fs @fs ||= FakeDir.new('/') end
def normalize_path(path, dir: nil)
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 path_parts(path)
def path_parts(path) Globber.path_components(path) end