Skip to content

Commit

Permalink
Favor composition over inheritance for FakeDir
Browse files Browse the repository at this point in the history
In particular, this fixes a problem on Rubinius, where
Hash#reject is implemented in terms of Hash#delete, and
overriding #delete with non-standard semantics causes
#reject to fail.
  • Loading branch information
jfirebaugh committed Apr 17, 2012
1 parent d2f7d6f commit 93a8f9e
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 30 deletions.
4 changes: 2 additions & 2 deletions lib/fakefs/dir.rb
Expand Up @@ -12,7 +12,7 @@ def initialize(string)
@path = string
@open = true
@pointer = 0
@contents = [ '.', '..', ] + FileSystem.find(@path).values
@contents = [ '.', '..', ] + FileSystem.find(@path).entries
end

def close
Expand Down Expand Up @@ -75,7 +75,7 @@ def self.chroot(string)

def self.delete(string)
_check_for_valid_file(string)
raise Errno::ENOTEMPTY, "Directory not empty - #{string}" unless FileSystem.find(string).values.empty?
raise Errno::ENOTEMPTY, "Directory not empty - #{string}" unless FileSystem.find(string).empty?

FileSystem.delete(string)
end
Expand Down
29 changes: 25 additions & 4 deletions lib/fakefs/fake/dir.rb
@@ -1,5 +1,5 @@
module FakeFS
class FakeDir < Hash
class FakeDir
attr_accessor :name, :parent, :mode, :uid, :gid
attr_reader :ctime, :mtime, :atime, :content

Expand All @@ -13,19 +13,20 @@ def initialize(name = nil, parent = nil)
@uid = Process.uid
@gid = Process.gid
@content = ""
@entries = {}
end

def entry
self
end

def inspect
"(FakeDir name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{size})"
"(FakeDir name:#{name.inspect} parent:#{parent.to_s.inspect} size:#{@entries.size})"
end

def clone(parent = nil)
clone = Marshal.load(Marshal.dump(self))
clone.each do |key, value|
clone.entries.each do |value|
value.parent = clone
end
clone.parent = parent if parent
Expand All @@ -42,11 +43,31 @@ def to_s
end
end

def empty?
@entries.empty?
end

def entries
@entries.values
end

def matches(pattern)
@entries.reject {|k,v| pattern !~ k }.values
end

def [](name)
@entries[name]
end

def []=(name, value)
@entries[name] = value
end

def delete(node = self)
if node == self
parent.delete(self)
else
super(node.name)
@entries.delete(node.name)
end
end
end
Expand Down
11 changes: 5 additions & 6 deletions lib/fakefs/file_system.rb
Expand Up @@ -16,7 +16,7 @@ def clear
end

def files
fs.values
fs.entries
end

def find(path)
Expand Down Expand Up @@ -114,17 +114,16 @@ def find_recurser(dir, parts)
when ['*']
parts = [] # end recursion
directories_under(dir).map do |d|
d.values.select{|f| f.is_a?(FakeFile) || f.is_a?(FakeDir) }
d.entries.select{|f| f.is_a?(FakeFile) || f.is_a?(FakeDir) }
end.flatten.uniq
when []
parts = [] # end recursion
dir.values.flatten.uniq
dir.entries.flatten.uniq
else
directories_under(dir)
end
else
regexp_pattern = /\A#{pattern.gsub('?','.').gsub('*', '.*').gsub(/\{(.*?)\}/) { "(#{$1.gsub(',', '|')})" }}\Z/
dir.reject {|k,v| regexp_pattern !~ k }.values
dir.matches /\A#{pattern.gsub('?','.').gsub('*', '.*').gsub(/\{(.*?)\}/) { "(#{$1.gsub(',', '|')})" }}\Z/
end

if parts.empty? # we're done recursing
Expand All @@ -135,7 +134,7 @@ def find_recurser(dir, parts)
end

def directories_under(dir)
children = dir.values.select{|f| f.is_a? FakeDir}
children = dir.entries.select{|f| f.is_a? FakeDir}
([dir] + children + children.map{|c| directories_under(c)}).flatten.uniq
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/fakefs/fileutils.rb
Expand Up @@ -23,7 +23,7 @@ def rmdir(list, options = {})
parent.pop
raise Errno::ENOENT, "No such file or directory - #{l}" unless parent.join == "" || FileSystem.find(parent.join('/'))
raise Errno::ENOENT, l unless FileSystem.find(l)
raise Errno::ENOTEMPTY, l unless FileSystem.find(l).values.empty?
raise Errno::ENOTEMPTY, l unless FileSystem.find(l).empty?
rm(l)
end
end
Expand Down Expand Up @@ -101,7 +101,7 @@ def cp_r(src, dest)
# about and cleaned up.
if new_dir
if src[-2..-1] == '/.'
dir.values.each{|f| new_dir[f.name] = f.clone(new_dir) }
dir.entries.each{|f| new_dir[f.name] = f.clone(new_dir) }
else
new_dir[dir.name] = dir.entry.clone(new_dir)
end
Expand Down
32 changes: 16 additions & 16 deletions test/fakefs_test.rb
Expand Up @@ -931,14 +931,14 @@ def test_chdir_changes_directories_like_a_boss
# I know memes!
FileUtils.mkdir_p '/path'
assert_equal '/', FileSystem.fs.name
assert_equal({}, FileSystem.fs['path'])
assert_equal [], Dir.glob('/path/*')
Dir.chdir '/path' do
File.open('foo', 'w') { |f| f.write 'foo'}
File.open('foobar', 'w') { |f| f.write 'foo'}
end

assert_equal '/', FileSystem.fs.name
assert_equal(['foo', 'foobar'], FileSystem.fs['path'].keys.sort)
assert_equal(['/path/foo', '/path/foobar'], Dir.glob('/path/*').sort)

c = nil
Dir.chdir '/path' do
Expand All @@ -955,16 +955,16 @@ def test_chdir_shouldnt_keep_us_from_absolute_paths
File.open('foo', 'w') { |f| f.write 'foo'}
File.open('/foobar', 'w') { |f| f.write 'foo'}
end
assert_equal ['foo'], FileSystem.fs['path'].keys.sort
assert_equal ['foobar', 'path'], FileSystem.fs.keys.sort
assert_equal ['/path/foo'], Dir.glob('/path/*').sort
assert_equal ['/foobar', '/path'], Dir.glob('/*').sort

Dir.chdir '/path' do
FileUtils.rm('foo')
FileUtils.rm('/foobar')
end

assert_equal [], FileSystem.fs['path'].keys.sort
assert_equal ['path'], FileSystem.fs.keys.sort
assert_equal [], Dir.glob('/path/*').sort
assert_equal ['/path'], Dir.glob('/*').sort
end

def test_chdir_should_be_nestable
Expand All @@ -976,8 +976,8 @@ def test_chdir_should_be_nestable
end
end

assert_equal ['foo','me'], FileSystem.fs['path'].keys.sort
assert_equal ['foobar'], FileSystem.fs['path']['me'].keys.sort
assert_equal ['/path/foo','/path/me'], Dir.glob('/path/*').sort
assert_equal ['/path/me/foobar'], Dir.glob('/path/me/*').sort
end

def test_chdir_should_flop_over_and_die_if_the_dir_doesnt_exist
Expand Down Expand Up @@ -1012,23 +1012,23 @@ def test_chdir_shouldnt_lose_state_because_of_errors
assert_equal ['/path'], FileSystem.dir_levels
end

assert_equal(['foo', 'foobar'], FileSystem.fs['path'].keys.sort)
assert_equal(['/path/foo', '/path/foobar'], Dir.glob('/path/*').sort)
end

def test_chdir_with_no_block_is_awesome
FileUtils.mkdir_p '/path'
Dir.chdir('/path')
FileUtils.mkdir_p 'subdir'
assert_equal ['subdir'], FileSystem.current_dir.keys
assert_equal ['subdir'], Dir.glob('*')
Dir.chdir('subdir')
File.open('foo', 'w') { |f| f.write 'foo'}
assert_equal ['foo'], FileSystem.current_dir.keys
assert_equal ['foo'], Dir.glob('*')

assert_raises(Errno::ENOENT) do
Dir.chdir('subsubdir')
end

assert_equal ['foo'], FileSystem.current_dir.keys
assert_equal ['foo'], Dir.glob('*')
end

def test_current_dir_reflected_by_pwd
Expand Down Expand Up @@ -1259,8 +1259,8 @@ def test_clone_clones_dot_files_even_hard_to_find_ones
assert !File.exists?(here('subdir'))

FileSystem.clone(here('subdir'))
assert_equal ['.bar'], FileSystem.find(here('subdir')).keys
assert_equal ['foo'], FileSystem.find(here('subdir/.bar/baz/.quux')).keys
assert_equal ['.', '..', '.bar'], Dir.entries(here('subdir'))
assert_equal ['.', '..', 'foo'], Dir.entries(here('subdir/.bar/baz/.quux'))
ensure
act_on_real_fs { RealFileUtils.rm_rf(here('subdir')) }
end
Expand All @@ -1272,8 +1272,8 @@ def test_clone_with_target_specified

FileSystem.clone(here('subdir'), here('subdir2'))
assert !File.exists?(here('subdir'))
assert_equal ['.bar'], FileSystem.find(here('subdir2')).keys
assert_equal ['foo'], FileSystem.find(here('subdir2/.bar/baz/.quux')).keys
assert_equal ['.', '..', '.bar'], Dir.entries(here('subdir2'))
assert_equal ['.', '..', 'foo'], Dir.entries(here('subdir2/.bar/baz/.quux'))
ensure
act_on_real_fs { RealFileUtils.rm_rf(here('subdir')) }
end
Expand Down

0 comments on commit 93a8f9e

Please sign in to comment.