# Rush::Entry is the base class for Rush::File and Rush::Dir. One or more of # these is instantiated whenever you use square brackets to access the # filesystem on a box, as well as any other operation that returns an entry or # list of entries. class Rush::Entry attr_reader :box, :name, :path # Initialize with full path to the file or dir, and the box it resides on. def initialize(full_path, box=nil) full_path = ::File.expand_path(full_path, '/') @path = ::File.dirname(full_path) @name = ::File.basename(full_path) @box = box || Rush::Box.new('localhost') end # The factory checks to see if the full path has a trailing slash for # creating a Rush::Dir rather than the default Rush::File. def self.factory(full_path, box=nil) if full_path.tail(1) == '/' Rush::Dir.new(full_path, box) elsif File.directory?(full_path) Rush::Dir.new(full_path, box) else Rush::File.new(full_path, box) end end def to_s # :nodoc: if box.host == 'localhost' "#{full_path}" else inspect end end def inspect # :nodoc: "#{box}:#{full_path}" end def connection box ? box.connection : Rush::Connection::Local.new end # The parent dir. For example, box['/etc/hosts'].parent == box['etc/'] def parent @parent ||= Rush::Dir.new(@path) end def full_path "#{@path}/#{@name}" end def quoted_path Rush.quote(full_path) end # Return true if the entry currently exists on the filesystem of the box. def exists? stat true rescue Rush::DoesNotExist false end # Timestamp of most recent change to the entry (permissions, contents, etc). def changed_at stat[:ctime] end # Timestamp of last modification of the contents. def last_modified stat[:mtime] end # Timestamp that entry was last accessed (read from or written to). def last_accessed stat[:atime] end # Attempts to rename, copy, or otherwise place an entry into a dir that already contains an entry by that name will fail with this exception. class NameAlreadyExists < Exception; end # Do not use rename or duplicate with a slash; use copy_to or move_to instead. class NameCannotContainSlash < Exception; end # Rename an entry to another name within the same dir. The object's name # will be updated to match the change on the filesystem. def rename(new_name) connection.rename(@path, @name, new_name) @name = new_name self end # Rename an entry to another name within the same dir. The existing object # will not be affected, but a new object representing the newly-created # entry will be returned. def duplicate(new_name) raise Rush::NameCannotContainSlash if new_name.match(/\//) new_full_path = "#{@path}/#{new_name}" connection.copy(full_path, new_full_path) self.class.new(new_full_path, box) end # Copy the entry to another dir. Returns an object representing the new # copy. def copy_to(dir) raise Rush::NotADir unless dir.class == Rush::Dir if box == dir.box connection.copy(full_path, dir.full_path) else archive = connection.read_archive(full_path) dir.box.connection.write_archive(archive, dir.full_path) end new_full_path = "#{dir.full_path}#{name}" self.class.new(new_full_path, dir.box) end # Move the entry to another dir. The object will be updated to show its new # location. def move_to(dir) moved = copy_to(dir) destroy mimic(moved) end def mimic(from) # :nodoc: @box = from.box @path = from.path @name = from.name end # Unix convention considers entries starting with a . to be hidden. def hidden? name.slice(0, 1) == '.' end # Set the access permissions for the entry. # # Permissions are set by role and permissions combinations which can be specified individually # or grouped together. :user_can => :read, :user_can => :write is the same # as :user_can => :read_write. # # You can also insert 'and' if you find it reads better, like :user_and_group_can => :read_and_write. # # Any permission excluded is set to deny access. The access call does not set partial # permissions which combine with the existing state of the entry, like "chmod o+r" would. # # Examples: # # file.access = { :user_can => :read_write, :group_other_can => :read } # dir.access = { :user => 'adam', :group => 'users', :read_write_execute => :user_group } # def access=(options) connection.set_access(full_path, Rush::Access.parse(options)) end # Returns a hash with up to nine values, combining user/group/other with read/write/execute. # The key is omitted if the value is false. # # Examples: # # entry.access # -> { :user_can_read => true, :user_can_write => true, :group_can_read => true } # entry.access[:other_can_read] # -> true or nil # def access Rush::Access.new.from_octal(stat[:mode]).display_hash end # Destroy the entry. If it is a dir, everything inside it will also be destroyed. def destroy connection.destroy(full_path) end def ==(other) # :nodoc: full_path == other.full_path and box == other.box end private def stat connection.stat(full_path) end end