From: Massimiliano Mirra Date: 2002-03-08T22:54:58+09:00 Subject: [CODE] Opinions wanted (long) These are some embryonic ideas that generated after a discussion with Wichael Witrant, author of path.rb. This is about accessing and modifying file system entities (for lack of a better term), such as files, directories, fifos etc., either locally or (for example) via ftp or http, in an unified way. It is also about really treating such entities just like objects in an load-modify-commit (or rollback) cycle, like for file I/O (you might call this ``filesystem I/O''), rather than doing a lot of atomic modify-commit operations (e.g. File.delete('file.txt')) and constantly keeping track of what is on disk. It is *not* about accessing the content of the entity, and the stream discussion we had a while ago, though it might be related. Do you think there is usefulness to pursue this direction? require 'ftools' require 'uri' # This class represents a file system entity, which can be a (local or # remote) file, directory, fifo, link... # Currently only supports local (file://) files and directories. class FSE def initialize(uri) @current_uri = URI.parse uri @final_uri = @current_uri.dup exists? ? @ftype = File.ftype(@current_uri.path) : @ftype = 'file' end # Mark the file for deletion at next _sync_ call. def delete @delete = true end # Clear the delete mark if it was set. def undelete @delete = false end # Returns true if the entity the instance was initialized exists now # (either because it existed before or because we called the _sync_ # method) or not yet. # # passwd_file = "/etc/passwd" # p passwd_file.exists? # true # new_file = "/home/joe/unnamed.txt" # p new_file.exists # false # new_file.sync # p new_file.exists? # true def exists? FileTest.exists? @current_uri.path end # Set the path the entity will have after the next sync, either as a # moved or as a copied entity. # # config_file = FSE.new("/etc/config.rc") # config_file.copying = true # config_file.path = "/home/joe/.confrc" # p config_file.path # "/home/joe/.confrc" # p config_file.current_path # "/etc/config" # config_file.sync # p config_file.path # "/home/joe/.confrc" # p config_file.current_path # "/home/joe/.confrc" def path=(apath) @final_uri.path = apath end # Returns the final path of the entity, i.e. the path it will have # after the next _sync_. This may from the path of the entity on # the disk (_current_path_), if iet has not been _sync_ed yet. def path @final_uri.path end # Current path of the entity. It can only be read. To move an # entity, use the _path=_ method, then apply changes with _sync_. def current_path @current_uri.path end # States whether we are working on a copy of the entity we passed in # at initialization time, or changes such as rename or move should # be applied to the original entity. def copying=(boolean) @copying = boolean end # Define the type of this entity, *if* it was created from scratch # and was not _sync_ed yet. The type of an existing entity (either # because it existed before or because we _sync_ed it to disk # already) cannot be changed. def ftype=(atype) raise "Cannot change the type of an existing filesystem entity." if exists? raise "Unknown file type." unless ['file', 'directory'].include?(atype) @ftype = atype end # Apply pending changes to the entity. def sync if @delete delete_path(@current_uri.path) return end if exists? if @current_uri != @final_uri @copying ? File.copy(@current_uri.path, @final_uri.path) : File.mv(@current_uri.path, @final_uri.path) @current_uri = @final_uri.dup end else case @ftype when 'file' system('touch', @final_uri.path) when 'directory' Dir.mkdir(@final_uri.path) end end end private def delete_path(path) if FileTest.directory?(path) Dir.rmdir(path) else File.delete(path) end end end if $0 == __FILE__ require 'Lapidary/TestCase' class TestFSE < Lapidary::TestCase def initialize(*args) super(*args) Dir.mkdir 'test' unless FileTest.exists? 'test' system('rm -rf test/*') end def testCopy # test whether anything gets copied file = FSE.new('/etc/passwd') file.copying = true file.path = 'test/passwd' file.sync assert FileTest.exists? 'test/passwd' # test if what was copied matches what we started with source_content = File.open('/etc/passwd') {|f| f.read} dest_content = File.open('test/passwd') {|f| f.read} assert source_content, dest_content # cleanup File.delete 'test/passwd' end def testMove # copy something file = FSE.new('/etc/passwd') file.copying = true file.path = 'test/passwd' file.sync # now move it new_file = FSE.new('test/passwd') file.copying = false file.path = 'test/foo' file.sync # test whether anything was moved assert FileTest.exists? 'test/foo' # cleanup File.delete 'test/foo' end def testCreateFile f = FSE.new('test/entity') f.ftype = 'file' f.sync assert FileTest.exists? 'test/entity' assert FileTest.file? 'test/entity' File.delete 'test/entity' end def testCreateDir f = FSE.new('test/sampledir') f.ftype = 'directory' f.sync assert FileTest.exists? 'test/sampledir' assert FileTest.directory? 'test/sampledir' Dir.rmdir('test/sampledir') end def testDelete f = FSE.new('test/entity') f.ftype = 'file' f.sync f.delete assert FileTest.exists? 'test/entity' f.sync assert !FileTest.exists?('test/entity') end def testUndelete f = FSE.new('test/entity') f.ftype = 'file' f.sync f.delete f.undelete f.sync assert FileTest.exists? 'test/entity' f.delete f.sync end end require 'Lapidary/UI/Console/TestRunner' Lapidary::UI::Console::TestRunner.run(TestFSE) end