From 93a8f9e76324c12fc0c14a6476e01f45df0f1520 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 17 Apr 2012 15:01:07 -0700 Subject: [PATCH] Favor composition over inheritance for FakeDir 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. --- lib/fakefs/dir.rb | 4 ++-- lib/fakefs/fake/dir.rb | 29 +++++++++++++++++++++++++---- lib/fakefs/file_system.rb | 11 +++++------ lib/fakefs/fileutils.rb | 4 ++-- test/fakefs_test.rb | 32 ++++++++++++++++---------------- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/lib/fakefs/dir.rb b/lib/fakefs/dir.rb index 97b6a73d..5af72d08 100644 --- a/lib/fakefs/dir.rb +++ b/lib/fakefs/dir.rb @@ -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 @@ -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 diff --git a/lib/fakefs/fake/dir.rb b/lib/fakefs/fake/dir.rb index 1225f678..ad97ac6a 100644 --- a/lib/fakefs/fake/dir.rb +++ b/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 @@ -13,6 +13,7 @@ def initialize(name = nil, parent = nil) @uid = Process.uid @gid = Process.gid @content = "" + @entries = {} end def entry @@ -20,12 +21,12 @@ def entry 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 @@ -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 diff --git a/lib/fakefs/file_system.rb b/lib/fakefs/file_system.rb index da58ad99..b92a7d5e 100644 --- a/lib/fakefs/file_system.rb +++ b/lib/fakefs/file_system.rb @@ -16,7 +16,7 @@ def clear end def files - fs.values + fs.entries end def find(path) @@ -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 @@ -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 diff --git a/lib/fakefs/fileutils.rb b/lib/fakefs/fileutils.rb index fe912887..f34fa382 100644 --- a/lib/fakefs/fileutils.rb +++ b/lib/fakefs/fileutils.rb @@ -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 @@ -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 diff --git a/test/fakefs_test.rb b/test/fakefs_test.rb index 6dbf472a..72aaead2 100644 --- a/test/fakefs_test.rb +++ b/test/fakefs_test.rb @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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