Crazy directory re-organization
This commit is contained in:
parent
14dac336a5
commit
42d9951661
@ -1,13 +1,12 @@
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
require 'fakefs/safe'
|
||||
require 'fakefs/base'
|
||||
require 'fakefs/mock/file'
|
||||
require 'fakefs/mock/dir'
|
||||
require 'fakefs/mock/symlink'
|
||||
require 'fakefs/file_system'
|
||||
require 'fakefs/fileutils'
|
||||
require 'fakefs/file'
|
||||
require 'fakefs/dir'
|
||||
|
||||
Object.class_eval do
|
||||
remove_const(:Dir)
|
||||
remove_const(:File)
|
||||
remove_const(:FileUtils)
|
||||
end
|
||||
|
||||
File = FakeFS::File
|
||||
FileUtils = FakeFS::FileUtils
|
||||
Dir = FakeFS::Dir
|
||||
FakeFS.activate!
|
||||
|
31
lib/fakefs/base.rb
Normal file
31
lib/fakefs/base.rb
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
RealFile = File
|
||||
RealFileUtils = FileUtils
|
||||
RealDir = Dir
|
||||
RealFileUtils::Dir = RealDir
|
||||
RealFileUtils::File = RealFile
|
||||
|
||||
module FakeFS
|
||||
def self.activate!
|
||||
Object.class_eval do
|
||||
remove_const(:Dir)
|
||||
remove_const(:File)
|
||||
remove_const(:FileUtils)
|
||||
const_set(:Dir, FakeFS::Dir)
|
||||
const_set(:File, FakeFS::File)
|
||||
const_set(:FileUtils, FakeFS::FileUtils)
|
||||
end
|
||||
end
|
||||
|
||||
def self.deactivate!
|
||||
Object.class_eval do
|
||||
remove_const(:Dir)
|
||||
remove_const(:File)
|
||||
remove_const(:FileUtils)
|
||||
const_set(:Dir, RealDir)
|
||||
const_set(:File, RealFile)
|
||||
const_set(:FileUtils, RealFileUtils)
|
||||
end
|
||||
end
|
||||
end
|
20
lib/fakefs/dir.rb
Normal file
20
lib/fakefs/dir.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module FakeFS
|
||||
class Dir
|
||||
def self.glob(pattern)
|
||||
if pattern[-1,1] == '*'
|
||||
blk = proc { |entry| entry.to_s }
|
||||
else
|
||||
blk = proc { |entry| entry[1].parent.to_s }
|
||||
end
|
||||
(FileSystem.find(pattern) || []).map(&blk).uniq.sort
|
||||
end
|
||||
|
||||
def self.[](pattern)
|
||||
glob(pattern)
|
||||
end
|
||||
|
||||
def self.chdir(dir, &blk)
|
||||
FileSystem.chdir(dir, &blk)
|
||||
end
|
||||
end
|
||||
end
|
120
lib/fakefs/file.rb
Normal file
120
lib/fakefs/file.rb
Normal file
@ -0,0 +1,120 @@
|
||||
module FakeFS
|
||||
class File
|
||||
PATH_SEPARATOR = '/'
|
||||
|
||||
def self.join(*parts)
|
||||
parts * PATH_SEPARATOR
|
||||
end
|
||||
|
||||
def self.exist?(path)
|
||||
!!FileSystem.find(path)
|
||||
end
|
||||
|
||||
class << self
|
||||
alias_method :exists?, :exist?
|
||||
end
|
||||
|
||||
def self.directory?(path)
|
||||
if path.respond_to? :entry
|
||||
path.entry.is_a? MockDir
|
||||
else
|
||||
result = FileSystem.find(path)
|
||||
result ? result.entry.is_a?(MockDir) : false
|
||||
end
|
||||
end
|
||||
|
||||
def self.symlink?(path)
|
||||
if path.respond_to? :entry
|
||||
path.is_a? MockSymlink
|
||||
else
|
||||
FileSystem.find(path).is_a? MockSymlink
|
||||
end
|
||||
end
|
||||
|
||||
def self.file?(path)
|
||||
if path.respond_to? :entry
|
||||
path.entry.is_a? MockFile
|
||||
else
|
||||
result = FileSystem.find(path)
|
||||
result ? result.entry.is_a?(MockFile) : false
|
||||
end
|
||||
end
|
||||
|
||||
def self.expand_path(*args)
|
||||
RealFile.expand_path(*args)
|
||||
end
|
||||
|
||||
def self.basename(*args)
|
||||
RealFile.basename(*args)
|
||||
end
|
||||
|
||||
def self.dirname(path)
|
||||
RealFile.dirname(path)
|
||||
end
|
||||
|
||||
def self.readlink(path)
|
||||
symlink = FileSystem.find(path)
|
||||
FileSystem.find(symlink.target).to_s
|
||||
end
|
||||
|
||||
def self.open(path, mode='r')
|
||||
if block_given?
|
||||
yield new(path, mode)
|
||||
else
|
||||
new(path, mode)
|
||||
end
|
||||
end
|
||||
|
||||
def self.read(path)
|
||||
file = new(path)
|
||||
if file.exists?
|
||||
file.read
|
||||
else
|
||||
raise Errno::ENOENT
|
||||
end
|
||||
end
|
||||
|
||||
def self.readlines(path)
|
||||
read(path).split("\n")
|
||||
end
|
||||
|
||||
attr_reader :path
|
||||
def initialize(path, mode = nil)
|
||||
@path = path
|
||||
@mode = mode
|
||||
@file = FileSystem.find(path)
|
||||
@open = true
|
||||
end
|
||||
|
||||
def close
|
||||
@open = false
|
||||
end
|
||||
|
||||
def read
|
||||
raise IOError.new('closed stream') unless @open
|
||||
@file.content
|
||||
end
|
||||
|
||||
def exists?
|
||||
@file
|
||||
end
|
||||
|
||||
def puts(content)
|
||||
write(content + "\n")
|
||||
end
|
||||
|
||||
def write(content)
|
||||
raise IOError.new('closed stream') unless @open
|
||||
|
||||
if !File.exists?(@path)
|
||||
@file = FileSystem.add(path, MockFile.new)
|
||||
end
|
||||
|
||||
@file.content += content
|
||||
end
|
||||
alias_method :print, :write
|
||||
alias_method :<<, :write
|
||||
|
||||
def flush; self; end
|
||||
end
|
||||
end
|
105
lib/fakefs/file_system.rb
Normal file
105
lib/fakefs/file_system.rb
Normal file
@ -0,0 +1,105 @@
|
||||
module FakeFS
|
||||
module FileSystem
|
||||
extend self
|
||||
|
||||
def dir_levels
|
||||
@dir_levels ||= []
|
||||
end
|
||||
|
||||
def fs
|
||||
@fs ||= MockDir.new('.')
|
||||
end
|
||||
|
||||
def clear
|
||||
@dir_levels = nil
|
||||
@fs = nil
|
||||
end
|
||||
|
||||
def files
|
||||
fs.values
|
||||
end
|
||||
|
||||
def find(path)
|
||||
parts = path_parts(normalize_path(path))
|
||||
|
||||
target = parts[0...-1].inject(fs) do |dir, part|
|
||||
dir[part] || {}
|
||||
end
|
||||
|
||||
case parts.last
|
||||
when '*'
|
||||
target.values
|
||||
else
|
||||
target[parts.last]
|
||||
end
|
||||
end
|
||||
|
||||
def add(path, object=MockDir.new)
|
||||
parts = path_parts(normalize_path(path))
|
||||
|
||||
d = parts[0...-1].inject(fs) do |dir, part|
|
||||
dir[part] ||= MockDir.new(part, dir)
|
||||
end
|
||||
|
||||
object.name = parts.last
|
||||
object.parent = d
|
||||
d[parts.last] ||= object
|
||||
end
|
||||
|
||||
# copies directories and files from the real filesystem
|
||||
# into our fake one
|
||||
def clone(path)
|
||||
path = File.expand_path(path)
|
||||
pattern = File.join(path, '**', '*')
|
||||
files = RealFile.file?(path) ? [path] : [path] + RealDir.glob(pattern, RealFile::FNM_DOTMATCH)
|
||||
|
||||
files.each do |f|
|
||||
if RealFile.file?(f)
|
||||
FileUtils.mkdir_p(File.dirname(f))
|
||||
File.open(f, 'w') do |g|
|
||||
g.print RealFile.open(f){|h| h.read }
|
||||
end
|
||||
elsif RealFile.directory?(f)
|
||||
FileUtils.mkdir_p(f)
|
||||
elsif RealFile.symlink?(f)
|
||||
FileUtils.ln_s()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def delete(path)
|
||||
if dir = FileSystem.find(path)
|
||||
dir.parent.delete(dir.name)
|
||||
end
|
||||
end
|
||||
|
||||
def chdir(dir, &blk)
|
||||
new_dir = find(dir)
|
||||
dir_levels.push dir if blk
|
||||
|
||||
raise Errno::ENOENT, dir unless new_dir
|
||||
|
||||
dir_levels.push dir if !blk
|
||||
blk.call if blk
|
||||
ensure
|
||||
dir_levels.pop if blk
|
||||
end
|
||||
|
||||
def path_parts(path)
|
||||
path.split(File::PATH_SEPARATOR).reject { |part| part.empty? }
|
||||
end
|
||||
|
||||
def normalize_path(path)
|
||||
if Pathname.new(path).absolute?
|
||||
File.expand_path(path)
|
||||
else
|
||||
parts = dir_levels + [path]
|
||||
File.expand_path(File.join(*parts))
|
||||
end
|
||||
end
|
||||
|
||||
def current_dir
|
||||
find(normalize_path('.'))
|
||||
end
|
||||
end
|
||||
end
|
108
lib/fakefs/fileutils.rb
Normal file
108
lib/fakefs/fileutils.rb
Normal file
@ -0,0 +1,108 @@
|
||||
module FakeFS
|
||||
module FileUtils
|
||||
extend self
|
||||
|
||||
def mkdir_p(path)
|
||||
FileSystem.add(path, MockDir.new)
|
||||
end
|
||||
|
||||
def rm(path)
|
||||
FileSystem.delete(path)
|
||||
end
|
||||
alias_method :rm_rf, :rm
|
||||
alias_method :rm_r, :rm
|
||||
|
||||
def ln_s(target, path)
|
||||
raise Errno::EEXIST, path if FileSystem.find(path)
|
||||
FileSystem.add(path, MockSymlink.new(target))
|
||||
end
|
||||
|
||||
def cp(src, dest)
|
||||
dst_file = FileSystem.find(dest)
|
||||
src_file = FileSystem.find(src)
|
||||
|
||||
if !src_file
|
||||
raise Errno::ENOENT, src
|
||||
end
|
||||
|
||||
if File.directory? src_file
|
||||
raise Errno::EISDIR, src
|
||||
end
|
||||
|
||||
if dst_file and File.directory?(dst_file)
|
||||
FileSystem.add(File.join(dest, src), src_file.entry.clone(dst_file))
|
||||
else
|
||||
FileSystem.delete(dest)
|
||||
FileSystem.add(dest, src_file.entry.clone)
|
||||
end
|
||||
end
|
||||
|
||||
def cp_r(src, dest)
|
||||
# This error sucks, but it conforms to the original Ruby
|
||||
# method.
|
||||
raise "unknown file type: #{src}" unless dir = FileSystem.find(src)
|
||||
|
||||
new_dir = FileSystem.find(dest)
|
||||
|
||||
if new_dir && !File.directory?(dest)
|
||||
raise Errno::EEXIST, dest
|
||||
end
|
||||
|
||||
if !new_dir && !FileSystem.find(dest+'/../')
|
||||
raise Errno::ENOENT, dest
|
||||
end
|
||||
|
||||
# This last bit is a total abuse and should be thought hard
|
||||
# about and cleaned up.
|
||||
if new_dir
|
||||
if src[-2..-1] == '/.'
|
||||
dir.values.each{|f| new_dir[f.name] = f.clone(new_dir) }
|
||||
else
|
||||
new_dir[dir.name] = dir.entry.clone(new_dir)
|
||||
end
|
||||
else
|
||||
FileSystem.add(dest, dir.entry.clone)
|
||||
end
|
||||
end
|
||||
|
||||
def mv(src, dest)
|
||||
if target = FileSystem.find(src)
|
||||
FileSystem.add(dest, target.entry.clone)
|
||||
FileSystem.delete(src)
|
||||
else
|
||||
raise Errno::ENOENT, src
|
||||
end
|
||||
end
|
||||
|
||||
def chown(user, group, list, options={})
|
||||
list = Array(list)
|
||||
list.each do |f|
|
||||
unless File.exists?(f)
|
||||
raise Errno::ENOENT, f
|
||||
end
|
||||
end
|
||||
list
|
||||
end
|
||||
|
||||
def chown_R(user, group, list, options={})
|
||||
chown(user, group, list, options={})
|
||||
end
|
||||
|
||||
def touch(list, options={})
|
||||
Array(list).each do |f|
|
||||
directory = File.dirname(f)
|
||||
# FIXME this explicit check for '.' shouldn't need to happen
|
||||
if File.exists?(directory) || directory == '.'
|
||||
FileSystem.add(f, MockFile.new)
|
||||
else
|
||||
raise Errno::ENOENT, f
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cd(dir)
|
||||
FileSystem.chdir(dir)
|
||||
end
|
||||
alias_method :chdir, :cd
|
||||
end
|
||||
end
|
37
lib/fakefs/mock/dir.rb
Normal file
37
lib/fakefs/mock/dir.rb
Normal file
@ -0,0 +1,37 @@
|
||||
module FakeFS
|
||||
class MockDir < Hash
|
||||
attr_accessor :name, :parent
|
||||
|
||||
def initialize(name = nil, parent = nil)
|
||||
@name = name
|
||||
@parent = parent
|
||||
end
|
||||
|
||||
def entry
|
||||
self
|
||||
end
|
||||
|
||||
def inspect
|
||||
"(MockDir name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{size})"
|
||||
end
|
||||
|
||||
def clone(parent = nil)
|
||||
clone = Marshal.load(Marshal.dump(self))
|
||||
clone.each do |key, value|
|
||||
value.parent = clone
|
||||
end
|
||||
clone.parent = parent if parent
|
||||
clone
|
||||
end
|
||||
|
||||
def to_s
|
||||
if parent && parent.to_s != '.'
|
||||
File.join(parent.to_s, name)
|
||||
elsif parent && parent.to_s == '.'
|
||||
"#{File::PATH_SEPARATOR}#{name}"
|
||||
else
|
||||
name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
29
lib/fakefs/mock/file.rb
Normal file
29
lib/fakefs/mock/file.rb
Normal file
@ -0,0 +1,29 @@
|
||||
module FakeFS
|
||||
class MockFile
|
||||
attr_accessor :name, :parent, :content
|
||||
|
||||
def initialize(name = nil, parent = nil)
|
||||
@name = name
|
||||
@parent = parent
|
||||
@content = ''
|
||||
end
|
||||
|
||||
def clone(parent = nil)
|
||||
clone = super()
|
||||
clone.parent = parent if parent
|
||||
clone
|
||||
end
|
||||
|
||||
def entry
|
||||
self
|
||||
end
|
||||
|
||||
def inspect
|
||||
"(MockFile name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{content.size})"
|
||||
end
|
||||
|
||||
def to_s
|
||||
File.join(parent.to_s, name)
|
||||
end
|
||||
end
|
||||
end
|
22
lib/fakefs/mock/symlink.rb
Normal file
22
lib/fakefs/mock/symlink.rb
Normal file
@ -0,0 +1,22 @@
|
||||
module FakeFS
|
||||
class MockSymlink
|
||||
attr_accessor :name, :target
|
||||
alias_method :to_s, :name
|
||||
|
||||
def initialize(target)
|
||||
@target = target
|
||||
end
|
||||
|
||||
def inspect
|
||||
"symlink(#{target.split('/').last})"
|
||||
end
|
||||
|
||||
def entry
|
||||
FileSystem.find(target)
|
||||
end
|
||||
|
||||
def method_missing(*args, &block)
|
||||
entry.send(*args, &block)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,466 +1,4 @@
|
||||
RealFile = File
|
||||
RealFileUtils = FileUtils
|
||||
RealDir = Dir
|
||||
RealFileUtils::Dir = RealDir
|
||||
RealFileUtils::File = RealFile
|
||||
|
||||
module FakeFS
|
||||
def self.activate!
|
||||
Object.class_eval do
|
||||
remove_const(:Dir)
|
||||
remove_const(:File)
|
||||
remove_const(:FileUtils)
|
||||
const_set(:Dir, FakeFS::Dir)
|
||||
const_set(:File, FakeFS::File)
|
||||
const_set(:FileUtils, FakeFS::FileUtils)
|
||||
end
|
||||
end
|
||||
|
||||
def self.deactivate!
|
||||
Object.class_eval do
|
||||
remove_const(:Dir)
|
||||
remove_const(:File)
|
||||
remove_const(:FileUtils)
|
||||
const_set(:Dir, RealDir)
|
||||
const_set(:File, RealFile)
|
||||
const_set(:FileUtils, RealFileUtils)
|
||||
end
|
||||
end
|
||||
|
||||
module FileUtils
|
||||
extend self
|
||||
|
||||
def mkdir_p(path)
|
||||
FileSystem.add(path, MockDir.new)
|
||||
end
|
||||
|
||||
def rm(path)
|
||||
FileSystem.delete(path)
|
||||
end
|
||||
alias_method :rm_rf, :rm
|
||||
alias_method :rm_r, :rm
|
||||
|
||||
def ln_s(target, path)
|
||||
raise Errno::EEXIST, path if FileSystem.find(path)
|
||||
FileSystem.add(path, MockSymlink.new(target))
|
||||
end
|
||||
|
||||
def cp(src, dest)
|
||||
dst_file = FileSystem.find(dest)
|
||||
src_file = FileSystem.find(src)
|
||||
|
||||
if !src_file
|
||||
raise Errno::ENOENT, src
|
||||
end
|
||||
|
||||
if File.directory? src_file
|
||||
raise Errno::EISDIR, src
|
||||
end
|
||||
|
||||
if dst_file and File.directory?(dst_file)
|
||||
FileSystem.add(File.join(dest, src), src_file.entry.clone(dst_file))
|
||||
else
|
||||
FileSystem.delete(dest)
|
||||
FileSystem.add(dest, src_file.entry.clone)
|
||||
end
|
||||
end
|
||||
|
||||
def cp_r(src, dest)
|
||||
# This error sucks, but it conforms to the original Ruby
|
||||
# method.
|
||||
raise "unknown file type: #{src}" unless dir = FileSystem.find(src)
|
||||
|
||||
new_dir = FileSystem.find(dest)
|
||||
|
||||
if new_dir && !File.directory?(dest)
|
||||
raise Errno::EEXIST, dest
|
||||
end
|
||||
|
||||
if !new_dir && !FileSystem.find(dest+'/../')
|
||||
raise Errno::ENOENT, dest
|
||||
end
|
||||
|
||||
# This last bit is a total abuse and should be thought hard
|
||||
# about and cleaned up.
|
||||
if new_dir
|
||||
if src[-2..-1] == '/.'
|
||||
dir.values.each{|f| new_dir[f.name] = f.clone(new_dir) }
|
||||
else
|
||||
new_dir[dir.name] = dir.entry.clone(new_dir)
|
||||
end
|
||||
else
|
||||
FileSystem.add(dest, dir.entry.clone)
|
||||
end
|
||||
end
|
||||
|
||||
def mv(src, dest)
|
||||
if target = FileSystem.find(src)
|
||||
FileSystem.add(dest, target.entry.clone)
|
||||
FileSystem.delete(src)
|
||||
else
|
||||
raise Errno::ENOENT, src
|
||||
end
|
||||
end
|
||||
|
||||
def chown(user, group, list, options={})
|
||||
list = Array(list)
|
||||
list.each do |f|
|
||||
unless File.exists?(f)
|
||||
raise Errno::ENOENT, f
|
||||
end
|
||||
end
|
||||
list
|
||||
end
|
||||
|
||||
def chown_R(user, group, list, options={})
|
||||
chown(user, group, list, options={})
|
||||
end
|
||||
|
||||
def touch(list, options={})
|
||||
Array(list).each do |f|
|
||||
directory = File.dirname(f)
|
||||
# FIXME this explicit check for '.' shouldn't need to happen
|
||||
if File.exists?(directory) || directory == '.'
|
||||
FileSystem.add(f, MockFile.new)
|
||||
else
|
||||
raise Errno::ENOENT, f
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cd(dir)
|
||||
FileSystem.chdir(dir)
|
||||
end
|
||||
alias_method :chdir, :cd
|
||||
end
|
||||
|
||||
class File
|
||||
PATH_SEPARATOR = '/'
|
||||
|
||||
def self.join(*parts)
|
||||
parts * PATH_SEPARATOR
|
||||
end
|
||||
|
||||
def self.exist?(path)
|
||||
!!FileSystem.find(path)
|
||||
end
|
||||
|
||||
class << self
|
||||
alias_method :exists?, :exist?
|
||||
end
|
||||
|
||||
def self.directory?(path)
|
||||
if path.respond_to? :entry
|
||||
path.entry.is_a? MockDir
|
||||
else
|
||||
result = FileSystem.find(path)
|
||||
result ? result.entry.is_a?(MockDir) : false
|
||||
end
|
||||
end
|
||||
|
||||
def self.symlink?(path)
|
||||
if path.respond_to? :entry
|
||||
path.is_a? MockSymlink
|
||||
else
|
||||
FileSystem.find(path).is_a? MockSymlink
|
||||
end
|
||||
end
|
||||
|
||||
def self.file?(path)
|
||||
if path.respond_to? :entry
|
||||
path.entry.is_a? MockFile
|
||||
else
|
||||
result = FileSystem.find(path)
|
||||
result ? result.entry.is_a?(MockFile) : false
|
||||
end
|
||||
end
|
||||
|
||||
def self.expand_path(*args)
|
||||
RealFile.expand_path(*args)
|
||||
end
|
||||
|
||||
def self.basename(*args)
|
||||
RealFile.basename(*args)
|
||||
end
|
||||
|
||||
def self.dirname(path)
|
||||
RealFile.dirname(path)
|
||||
end
|
||||
|
||||
def self.readlink(path)
|
||||
symlink = FileSystem.find(path)
|
||||
FileSystem.find(symlink.target).to_s
|
||||
end
|
||||
|
||||
def self.open(path, mode='r')
|
||||
if block_given?
|
||||
yield new(path, mode)
|
||||
else
|
||||
new(path, mode)
|
||||
end
|
||||
end
|
||||
|
||||
def self.read(path)
|
||||
file = new(path)
|
||||
if file.exists?
|
||||
file.read
|
||||
else
|
||||
raise Errno::ENOENT
|
||||
end
|
||||
end
|
||||
|
||||
def self.readlines(path)
|
||||
read(path).split("\n")
|
||||
end
|
||||
|
||||
attr_reader :path
|
||||
def initialize(path, mode = nil)
|
||||
@path = path
|
||||
@mode = mode
|
||||
@file = FileSystem.find(path)
|
||||
@open = true
|
||||
end
|
||||
|
||||
def close
|
||||
@open = false
|
||||
end
|
||||
|
||||
def read
|
||||
raise IOError.new('closed stream') unless @open
|
||||
@file.content
|
||||
end
|
||||
|
||||
def exists?
|
||||
@file
|
||||
end
|
||||
|
||||
def puts(content)
|
||||
write(content + "\n")
|
||||
end
|
||||
|
||||
def write(content)
|
||||
raise IOError.new('closed stream') unless @open
|
||||
|
||||
if !File.exists?(@path)
|
||||
@file = FileSystem.add(path, MockFile.new)
|
||||
end
|
||||
|
||||
@file.content += content
|
||||
end
|
||||
alias_method :print, :write
|
||||
alias_method :<<, :write
|
||||
|
||||
def flush; self; end
|
||||
end
|
||||
|
||||
class Dir
|
||||
def self.glob(pattern)
|
||||
if pattern[-1,1] == '*'
|
||||
blk = proc { |entry| entry.to_s }
|
||||
else
|
||||
blk = proc { |entry| entry[1].parent.to_s }
|
||||
end
|
||||
(FileSystem.find(pattern) || []).map(&blk).uniq.sort
|
||||
end
|
||||
|
||||
def self.[](pattern)
|
||||
glob(pattern)
|
||||
end
|
||||
|
||||
def self.chdir(dir, &blk)
|
||||
FileSystem.chdir(dir, &blk)
|
||||
end
|
||||
end
|
||||
|
||||
module FileSystem
|
||||
extend self
|
||||
|
||||
def dir_levels
|
||||
@dir_levels ||= []
|
||||
end
|
||||
|
||||
def fs
|
||||
@fs ||= MockDir.new('.')
|
||||
end
|
||||
|
||||
def clear
|
||||
@dir_levels = nil
|
||||
@fs = nil
|
||||
end
|
||||
|
||||
def files
|
||||
fs.values
|
||||
end
|
||||
|
||||
def find(path)
|
||||
parts = path_parts(normalize_path(path))
|
||||
|
||||
target = parts[0...-1].inject(fs) do |dir, part|
|
||||
dir[part] || {}
|
||||
end
|
||||
|
||||
case parts.last
|
||||
when '*'
|
||||
target.values
|
||||
else
|
||||
target[parts.last]
|
||||
end
|
||||
end
|
||||
|
||||
def add(path, object=MockDir.new)
|
||||
parts = path_parts(normalize_path(path))
|
||||
|
||||
d = parts[0...-1].inject(fs) do |dir, part|
|
||||
dir[part] ||= MockDir.new(part, dir)
|
||||
end
|
||||
|
||||
object.name = parts.last
|
||||
object.parent = d
|
||||
d[parts.last] ||= object
|
||||
end
|
||||
|
||||
# copies directories and files from the real filesystem
|
||||
# into our fake one
|
||||
def clone(path)
|
||||
path = File.expand_path(path)
|
||||
pattern = File.join(path, '**', '*')
|
||||
files = RealFile.file?(path) ? [path] : [path] + RealDir.glob(pattern, RealFile::FNM_DOTMATCH)
|
||||
|
||||
files.each do |f|
|
||||
if RealFile.file?(f)
|
||||
FileUtils.mkdir_p(File.dirname(f))
|
||||
File.open(f, 'w') do |g|
|
||||
g.print RealFile.open(f){|h| h.read }
|
||||
end
|
||||
elsif RealFile.directory?(f)
|
||||
FileUtils.mkdir_p(f)
|
||||
elsif RealFile.symlink?(f)
|
||||
FileUtils.ln_s()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def delete(path)
|
||||
if dir = FileSystem.find(path)
|
||||
dir.parent.delete(dir.name)
|
||||
end
|
||||
end
|
||||
|
||||
def chdir(dir, &blk)
|
||||
new_dir = find(dir)
|
||||
dir_levels.push dir if blk
|
||||
|
||||
raise Errno::ENOENT, dir unless new_dir
|
||||
|
||||
dir_levels.push dir if !blk
|
||||
blk.call if blk
|
||||
ensure
|
||||
dir_levels.pop if blk
|
||||
end
|
||||
|
||||
def path_parts(path)
|
||||
path.split(File::PATH_SEPARATOR).reject { |part| part.empty? }
|
||||
end
|
||||
|
||||
def normalize_path(path)
|
||||
if Pathname.new(path).absolute?
|
||||
File.expand_path(path)
|
||||
else
|
||||
parts = dir_levels + [path]
|
||||
File.expand_path(File.join(*parts))
|
||||
end
|
||||
end
|
||||
|
||||
def current_dir
|
||||
find(normalize_path('.'))
|
||||
end
|
||||
end
|
||||
|
||||
class MockFile
|
||||
attr_accessor :name, :parent, :content
|
||||
|
||||
def initialize(name = nil, parent = nil)
|
||||
@name = name
|
||||
@parent = parent
|
||||
@content = ''
|
||||
end
|
||||
|
||||
def clone(parent = nil)
|
||||
clone = super()
|
||||
clone.parent = parent if parent
|
||||
clone
|
||||
end
|
||||
|
||||
def entry
|
||||
self
|
||||
end
|
||||
|
||||
def inspect
|
||||
"(MockFile name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{content.size})"
|
||||
end
|
||||
|
||||
def to_s
|
||||
File.join(parent.to_s, name)
|
||||
end
|
||||
end
|
||||
|
||||
class MockDir < Hash
|
||||
attr_accessor :name, :parent
|
||||
|
||||
def initialize(name = nil, parent = nil)
|
||||
@name = name
|
||||
@parent = parent
|
||||
end
|
||||
|
||||
def entry
|
||||
self
|
||||
end
|
||||
|
||||
def inspect
|
||||
"(MockDir name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{size})"
|
||||
end
|
||||
|
||||
def clone(parent = nil)
|
||||
clone = Marshal.load(Marshal.dump(self))
|
||||
clone.each do |key, value|
|
||||
value.parent = clone
|
||||
end
|
||||
clone.parent = parent if parent
|
||||
clone
|
||||
end
|
||||
|
||||
def to_s
|
||||
if parent && parent.to_s != '.'
|
||||
File.join(parent.to_s, name)
|
||||
elsif parent && parent.to_s == '.'
|
||||
"#{File::PATH_SEPARATOR}#{name}"
|
||||
else
|
||||
name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MockSymlink
|
||||
attr_accessor :name, :target
|
||||
alias_method :to_s, :name
|
||||
|
||||
def initialize(target)
|
||||
@target = target
|
||||
end
|
||||
|
||||
def inspect
|
||||
"symlink(#{target.split('/').last})"
|
||||
end
|
||||
|
||||
def entry
|
||||
FileSystem.find(target)
|
||||
end
|
||||
|
||||
def method_missing(*args, &block)
|
||||
entry.send(*args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
require 'fakefs/base'
|
||||
|
||||
def FakeFS
|
||||
return ::FakeFS unless block_given?
|
||||
|
Loading…
Reference in New Issue
Block a user