ServerJS/API/file: Difference between revisions

Jump to navigation Jump to search
Draft 2 of File API discussion
(Posted current notes for the file module API)
 
(Draft 2 of File API discussion)
Line 1: Line 1:
= High Level =
Alright.  The task of the month is to ratify a File API.
 
There's been relatively little discussion on this topic.
# make vs. create
There's not much opportunity to advance the state of the
#* make:
art with yet another file API, which means that this will
#** Mario Valente,
inevitably degenerate into shed painting, but it must be
#** Wes Garland (but only for directories)
done!  Let's just slog through this shall we?
#* create:
#** George Moschovitis,
I've composed all of the functions from Ruby, Python,
#** Wes Garland (but only for files)
and Java into a very long list here. Please note which
# dir vs. directory
names you prefer, whether you feel a particular function
#* directory:
ought to not be part of the standard API, any functions
#** Tom Robinson,
that are missing, and of course
#** Kevin Dangoor,
comment and discuss semantics.  I will attempt
#** George Mosochovitis,
to reserve my opinions for a follow up post.
#** Wes Garland
#* dir:
Let's begin with some high level preferences.  There
#** Mario Valente
are a few naming choices that can be applied throughout
# cur vs. current
the following document with rigor.  Rather than note
#* current:
every occurrence of the offending word, please state your
#** Tom Robinson,
general preference:
#** Kevin Dangoor,
#** Wes Garland,
#** Kris Kowal,
1. make vs create
#** Mario Valente
#* cur:
    make: Wes Garland (for dirs)
# canonize, realize, normalize, qualify, cannon, real, normal, qualified, absolute, cannonicalize
    create: George Mosochovitis, Wes Garland (for file mode)
#* remove . and ..:
#** normalize:
2. dir vs directory
#*** Tom Robinson,
#*** Kevin Dangoor,
    dir:
#*** Kris Kowal,
    directory: Tom Robinson, Kevin Dangoor, George Mosochovitis, Wes Garland
#*** George Mosochovitis
#** qualify:
3. cur vs current
#*** Wes Garland
#* remove . and .., start at /:
    cur:
#** absolute:
    current: Tom Robinson, Kevin Dangoor, Wes Garland, Kris Kowal
#*** Tom Robinson,
#*** Kevin Dangoor,
4. canonize, realize, normalize, qualify,
#*** Kris Kowal,
    canon, real, normal, qualified, absolute,
#*** George Mosochovitis,
    canonicalize
#*** Wes Garland,
#*** Mario Valente
    remove . and ..:
#** qualified:
        normalize: Tom Robinson, Kevin Dangoor, Kris Kowal, George Mosochovitis
#*** Wes Garland
        qualify: Wes Garland
#* remove . and .., start at /, follow symlinks:
    remove . and .., start at /:
#** canonical:
        absolute: Tom Robinson, Kevin Dangoor, Kris Kowal
#*** Tom Robinson,
    remove . and .., start at /, follow symlinks:
#*** Kevin Dangoor,
        canonical: Tom Robinson, Kevin Dangoor, Kris Kowal
#*** Kris Kowal
        real: Tom Robinson, Kevin Dangoor
#** absolute:
#*** George Mosochovitis,
    Could apply for either of the two latter:
#*** Wes Garland,
        absolute: George Moscochovitis, Wes Garland
#*** Mario Valente,
        qualified: Wes Garland
#** real:
#*** Tom Robinson,
5. constantCase vs CONSTANT_CASE vs kConstantCase
#*** Kevin Dangoor
#** qualified:
    There's a lot of legacy Netscape code with
#*** Wes Garland
    the pseudo Hungarian kConstantCase.
# constantCase, CONSTANT_CASE, kConstantCase
#* constantCase:
    CONSTANT_CASE: Tom Robinson, Kevin Dangoor
#** Kris Kowal,
    constantCase: Kris Kowal, Wes Garland
#** Wes Garland,
#** Mario Valente,
#* CONSTANT_CASE:
6. Should "static" File methods be placed in
#** Tom Robinson,
#** Kevin Dangoor
    require('file').File or : Tom Robinson, George Mosochovitis
# "static" methods
    require('file') : Kris Kowal, Wes Garland
#* require(module).staticMethod:
#** Kris Kowal
    e.g.,
#** Wes Garland
#** Mario Valente
    var files = require('file');
#* require(module).Klass.staticMethod:
    files.move(a, b)
#** Tom Robinson
#** George Mosochovitis
    vs.
# properties, accessors, mutators
#* foo.setX(x); foo.getX()
    var File = require('file').File;
#** For:
    File.move(a, b);
#** Against:
#* foo.setX(x); foo.X()
    Tom Robinson: should we use encoding here?
#** For:
    Wes Garland: "b" should be stricken
#** Against:
    Wes Garland: encoding should actually be an encode function
#* no convention
    Kris Kowal: encoding should be the name of an encoding module
#** For:
#** Against:
7. Choose:
#* foo.x = x; foo.x
#** For:
    a.) setX/getX  vs
#*** Davey Waterson
        a.) For all functions for which it would make sense to have a setter and a getter.
#*** Wes Garland
            (It would not make sense to have a setSize, so size() would be permissible)
#*** Daniel Friesen
        b.) For all functions that get things
#*** Sam Phillips
            (getSize, getMtime, setMtime, getBaseName)
#** Against:
#*** Tom Robinson
    b.) setX/X
#*** Kris Kowal
   
 
        Kris Kowal
 
= API and Proposed Names =
    c.) pick and choose; no overarching convention
 
== File System Object ==
    d.) X property with getters and setters
 
* the location of the file system type.
        for:
** environment.fs
            Davey Waterson
** The file module would not expose a FileSystem constructor, since that implies "ambient authority" to get wholesale access to the file system, although platform specific modules that depend on ambient authority in a permissive environment might be used to construct this FileSystem object, and it could be then safely chrooted or passed directly to a sandbox in its environment with the same nameThe "fs" object would conform to the following API.
            Wes Garland
* the location of the file system object provided to your environment.
            Daniel Friesen
** is provided as "environment.fs" in all modules and has all of the properties and methods defined for the FileSystem type:
        against:
*** Kris Kowal
            Tom Robinson
 
            Kris Kowal
* whether a file exists at a given path: receives a path and returns whether that path, joined on the current working directory, corresponds to a file that exists.    If the file is a broken symbolic link, returns false.
** exists(path):
exists(path) - Wes Garland
*** Kris Kowal
    Python: exists (uses stat(path) and checks for exception)
*** Wes Garland
    Ruby: exist? exists?
 
linkExists(path)
* whether a symbolic link or file exists at a given path: receives a path and returns whether that path, joined on the current working directory, corresponds to a file, albeit a broken symbolic link, that exists.
    Python: lexists
** exists(path, true):
        Returns whether a file exists, even if it
*** Wes Garland
        ultimately refers to a broken symbolic link.
*** Mario Valente
    Java: not provided
** linkExists(path):
*** Kris Kowal
    Wes Garland: exists(path, true)
** exists(path, "link"):
    Kris Kowal: linkExists(path)
*** Kris Kowal (as a compromise)
 
move(source, target)
* moves a file from a source to a target path. both paths are joined on the current working directory.
    Ruby: move
** move(source, target):
  rename(source, target)
*** uncontested
    Ruby: rename
 
copy(source, target)
* renames a file: receives two paths. The first is a path, joined on the current working directory, that refers to the file to renameThe second argument is a path relative to the former file's fully qualified path.
    Ruby: copy
** rename(path, name):
** should not be included in the spec:
systemCopy(source, target):
*** Kris Kowal
    Ruby: syscopy
 
    Copies a file from to to. If to is a directory, copies from to to/from.
* copies a file from a source to a target path. Both paths are joined on the current working directory.
   
** copy(source, target):
    unnecessary - Wes Garland
*** uncontested
 
makeDirectory(path)
* makes a directory at a given path in the base directory implied by the given path.  Throws an exception if the base directory does not exist, or if the given path already exists.
    Unix: mkdir
** makeDirectory(path):
    createDirectory
*** Kris Kowal
  makePath(path)
** createDirectory(path):
    Ruby: makedirs
*** Mario Valente
    Unix: mkdir -p
 
    mkpath, mkdirs, createPath, createDirectories
* makes any directories that are missing on a given path. Does not throw an exception if any of the base paths are missing, and does not throw an exception if all of the path already exists. Only throws an exception if it can't make the path exist.
** makePath(path):
    Wes Garland: makeDirectory(path, true)
*** Kris Kowal
    Kris Kowal: makePath(path)
** makeDirectory(path, true):
*** Wes Garland
makeTempFile(prefix, suffix)
** makeDirectory(path, "path"):
    Java: createTempFile
*** Kris Kowal (as a compromise)
    Python: mktemp
 
    mktemp(prefix, suffix), makeTemporary, makeTemp
* removes a file.
   
** remove(path):
    Wes Garland: this should be ommitted in favor of an alternate File constructor
*** Kris Kowal
   
*** Tom Robinson
remove(path)
*** Kevin Dangoor
    Ruby: unlink, delete
*** Wes Garland (as a compromise)
    Python: unlink, remove
** unlink(path):
    Java: delete
*** Wes Garland
    Posix: unlink
 
    Unix: rm (remove)
* removes a file or directory and its recursive contents.
** remove(path):
    Tom Robinson, Kevin Dangoor: remove, definitely not delete
** remove(path, true):
    Wes Garland: unlink, but remove and delete are fine
** remove(path, "recursive")
** removeRecursive(path)
 
symlink(source, target)
* creates a hard link of the source path as the target path. Both paths are joined on the current working directory.
    Ruby: symlink(old, new)
** link(source, target):
link(source, target)
*** uncontested
    Creates a hard link
 
    Ruby: File.link(old_name, new_name) => 0
* creates a symbolic link on a given "target" file name. The source path becomes the text of the symbolic link, which the underlying system will join on the containing directory when it's followed, while the target path is joined on the current working directory.
        Creates a new name for an existing file using a hard link.
** symbolicLink(source, target):
        Will not overwrite new_name if it already exists
*** Kris Kowal
        (raising a subclass of SystemCallError).
** link(source, target, true):
        Not available on all platforms.
*** Wes Garland
** link(source, target, "symbolic"):
    Wes Garland: link(source, target, hard:Boolean)
*** Kris Kowal (as a compromise)
    Kris Kowal: symlink, link
 
* creates an empty file at a given path in its implied base directory.  The path is resolved relative to the current working directory. Accepts an optional ``Permissions`` object (or a duck-type thereof, or the, typically octal, numeric representation of permissions in Unix) that notes which permissions the file will be created with, assuming that those permissions are in the "umask".
  truncate(file_name, length)
** create(path, [permissions]):
create(path)
*** Mario Valente
    Java: create
** unnecessary:
    Posix: creat
*** Kris Kowal,
lock(path, "shared|exculsive") - Wes Garland
*** Wes Garland
    writeLock(path), readLock(path)
 
tryLock(path, "shared|exclusive") - Wes Garland
* truncates a the file at a given path. Accepts an optional length that default to zero.  The path is resolved on the current working directory.
unlock(path) - Wes Garland
** truncate(path, [length = 0]):
    opens a lock
*** uncontested
 
  dup(fileno) - unnecessary Wes Garland
* updates the stat/metadata of the file at a given path. Accepts a stat object.  Implicitly updates the time that the file's metadata/stat was updated to the current time, regardless of the metadata modification time provided by the stat object.
dup2(fileno, fileno) - unnecessary Wes Garland
** update(path, stat):
   
*** Wes Garland
 
* updates the modification time of the file at a given path. Accepts an alternate modification time to set the file.  Also implicitly updates the time that the file's metadata/stat object was updated to the current time, regardless of the chosen modification time.
Weights and Measures
** touch(path, [date]):
====================
*** Kris Kowal
 
  [new] Stat(path)
* locks the file at a given path with either an exclusive or shared lock. The path is resolved on the current working directory.  Blocks until the lock is acquired, or if a timeout is defined, when that timeout occurs.
    returns a Stat object (more later)
** lock(path, "shared|exclusive", [timeout=undefined]):
stat(path)
*** Wes Garland
    returns an Array
*** Kris Kowal
    Posix: stat
 
    Java: omitted
* attempts to lock a file at a given path.  The path is resolved relative to the current working directory.  Does not block if the file is not lockable.  Returns whether the lock was obtained.
updateStat(:Stat) - Wes Garland
** tryLock(path, "shared|exclusive"):
setStat(:Stat)
*** Wes Garland
linkStat(path):
 
    Python: lstat
* releases a lock for a given path.
   
** unlock(path):
    Wes Garland: stat(path, true)
*** Kris Kowal
    Kris Kowal: linkStat(path)
** lock(path, "unlock")
 
  atime(path) - Tom Robinson, Kevin Dangoor
* retrieves an object that represents the metadata of a distinct file, for a given path.  The path is resolved relative to the current working directory. The returned object has the properties described in the Stat API.
    Java: omitted
** stat(path):
    Python: getatime
*** Mario Valente
    Ruby: atime
 
    lastAccessed
* retrieves an object that represents the metadata of a distinct file or symbolic link to a file at a given path. The path is resolved relative to the current working directory.  The returned object has the properties described in the Stat API.
  mtime(path) - Tom Robinson, Kevin Dangoor
** stat(path, true):
    Java: lastModified
*** Wes Garland
    Python: getmtime
** linkStat(path):
    Ruby: mtime
*** Kris Kowal
    stat(path).st_mtime
** stat(path, "link"):
    lastModified
*** Kris Kowal (as a compromise)
touch(path) - Kris Kowal
 
    setLastModified(path, date)
* returns the size of the corresponding file.
    setMtime(path, date)
** size(path)
ctime(path) - Tom Robinson, Kevin Dangoor
 
    Unix: touch
* returns the total size of a tree of files.
    Java: omitted
** size(path) // as a special case for a directory
    Python: getctime
** size(path, "recursive")
    Ruby: ctime
** recursiveSize(path)
    lastNodeModified
 
size(path) - Tom Robinson, Kevin Dangoor
* returns an IO object for accessing or manipulating the data in a file at a given path.  The path is resolved relative to the current working directory.
    Ruby: size?
** open(path, "+arwxc", encoding):
    Java: length
*** Mario Valente
    Python: getsize
*** Kris Kowal
    Posix: size: stat(path).st_size
** Mode:
    getLength, length, getSize, size
*** +, update
   
*** a, append
   
*** r, read
*** w, write
Ownership
*** x, exclusive (lock)
=========
*** c, canon (nonblocking)
 
getOwner(path)
* return a Dir object for a given directory path. The path is resolved relative to the current working directoryThe Dir object is an bidirectional iterator of a snapshot of the contents of the directory, and may be either a lazy Array or genuine array, depending on whether the underlying implementation can retrieve indicies on demand.
getGroupOwner(path)
** list(path)
setOwner(path, user, [group])
 
    chown
* returns an object that, like a Dir, supports the iterator protocol (next, prev) and the Array protocol (length, [index]) for all files that match a given glob pattern.
setGroupOwner(path, group)
** The glob pattern may include any of the following productions:
    chown
*** ? - exactly one character in a path component
  setLinkOwner(path, owner, [group])
*** * - any number of characters in a path component
    Ruby: file.lchown(owner_int, group_int, file_name,..) => integer
*** ** - any number of characters in a path
        Equivalent to File::chown, but does not follow symbolic links
*** {a,b,c} - the letters a, b, or c, or any other union of paths.
        (so it will change the owner associated with the link,
** glob(pattern)
        not the file referenced by the link).
 
        Often not available. Returns number of files in the argument list.
* returns whether a given path matches a glob pattern.
** match(pattern, path)
    Wes Garland: use SetOwner(path, owner, group, true)
 
* returns a new object that implements the file system API that uses the directory at the given path as its root.  Resolves the path relative to the current working directory.
setLinkGroupOwner(path, group)
** chroot(path)
isOwned()
 
    Ruby: owned?
 
        Returns true if the named file exists and the effective
== Paths ==
        used id of the calling process is the owner of the file.
 
isGroupOwned()
More properites of a FileSystem.
    Ruby: grpowned?
 
        Returns true if the named file exists and the effective
* a string directing a path up to the parent of the current directory, usually '..'.
        group id of the calling process is the owner of the file.
** parent
        Returns false on Windows.
** (a function that gets an opaque reference to the actual parent node on the file system, as referenced by the directory's parent hard link, has been deliberately omitted to avoid leaking the capability to access that object from within a chroot file system)
 
* a string directing a path back to itself, usually '.'.
Please Sir, Can I Have Some More
** self
  ================================
 
* a string that contains the path separator, usually '/', '\', or ':'.
isReadable(path)
** separator
    Java: canRead
 
    Ruby: readable
* a string that contains the alternate path separator, if it exists.  Otherwise, undefined.
isReallyReadable(path)
** alternateSeparator
    Ruby: readable_real?
 
    returns whether the file is readable by the current
* a string that contains the extension separator, usually '.'.
    process owner.
** extensionSeparator
  isWritable(path)
 
    Java: canWrite
* whether the file system supports unicode file names
    Ruby: writable
** supportsUnicodeFileNames
isReallyWritable(path)
 
    Ruby: writable_real?
* accepts a variadic list of paths as arguments and follows each successive path from the current working directory, left to right, and returns the normalized path of the ultimate file or directory. Paths that end with a directory separator refer to the contents of a directory as a place to continue resolving, whereas paths that do not end with a directory separator indicate that the next path should be followed from the directory containing that file.
isExecutable(path)
** join([path, [path, [path, [...]]]])
    Ruby: executable?
 
    Python: omitted
* returns the path components for a given path
isReallyExecutable(path)
** split(path)
    Ruby: executable_real?
 
isSticky(path)
* takes a string and returns an escaped path component of that string, such that directory separators and other special characters do not partition it into multiple path components
    returns whether the sticky bit is set, which is to say
** escape(string)
    that anyone can create a file in the directory
 
    but cannot delete those created by other users.
* returns the path of the directory containing a given path.
executesAsOwner(path)
** dirName(path):
    returns whether the file has the SID bit set.
*** Kris Kowal
executesAsGroupOwner(path)
** dirname(path)
    returns whether the file has the GID bit set.
*** Wes Garland
 
setMode(path, mode)
* returns the last path component without its extension. The extension begins after the first extension separator.
    Posix: chmod
** baseName(path):
    Unix: chmod
*** Kris Kowal
    Ruby: chmod
** basename(path):
getMode(path)
*** Wes Garland
setLinkMode(path, mode)
 
    Ruby: File.lchmod(mode_int, file_name, ...) => integer
* returns the extension of the given path.  The extension begins after the first extension separator.
        Equivalent to File::chmod, but does not follow
** extension(path):
        symbolic links (so it will change the permissions
*** George Mosochovitis
        associated with the link, not the file
*** Kris Kowal
        referenced by the link). Often not available.
*** Wes Garland
setReadOnly(true|false)
 
* removes '.' path components and simplifies '..' paths, if possible for a given path. If the file system is case sensitive, transforms all letters to lower-case to make them unambiguous.
getModeMask()
** normal(path)
    Ruby: umask()
 
setModeMask(umask)
* returns whether a given path has no self and parent path components.
    Ruby: umask(umask)
** isNormal(path)
 
* returns the absolute path, starting with the root of this file system object, for the given path, resolved relative to the current working directory. If the file system supports home directory aliases, absolute resolves those from the root of the file system.  The resulting path is in normal form.
What is My Name
** absolute(path)
===============
 
* returns whether the given path is normal and starts with the root of the file system.
dirName(path)
** isAbsolute(path)
    Python: dirname
 
        Returns the directory that contains the given path
* returns the intrinsic path to a given file. Resolves the path relative to the current working directory. This involves tracing the hard parent links of it and its ancestors until reaching the root of the file system. If the file system was returned by chroot, returns undefined if the parent links traverse to a path that's inaccessible from the chroot, if the file system is chrooted.
    Ruby: File.dirname(file_name)
** canonical(path)
 
    Wes Garland: dirname
* returns whether the given path is the same as the corresponding cannonical path.
    Kris Kowal: dirName
** isCanonical(path)
 
baseName(path)
* returns whether the given paths both refer to the same intrinsic file. Both paths are resolved relative to the working directory.
    Java: File(fileName).getName()
** same(path, path)
    Python: basename(file_name)
 
    Ruby: basename(file_name, [suffix])
== Stat Object ==
    Unix: basename name [suffix]
 
* the Date that the file was last accessed.
    Wes Garland: basename
** lastAccessed:
    Kris Kowal: baseName
*** Kris Kowal
** atime:
extension(path)
*** Tom Robinson
    Ruby: extname
*** Kevin Dangoor
    extensionName
 
   
* the Date that the file was last modified.
    extension: George Mosochovitis, Wes Garland, Kris Kowal
** lastModified:
*** Kris Kowal
split
*** Mario Valente
    Ruby: split
** mtime:
    Python: split
*** Tom Robinson
        Split a path in head (everything up to the last '/') and tail (the
*** Kevin Dangoor
        rest).  If the path ends in '/', tail will be empty. If there is no
 
        '/' in the path, head  will be empty.
* the Date that the file's stat/metadata was last modified.
        Trailing '/'es are stripped from head unless it is the root.
** lastChanged:
pathComponents(path)
*** Wes Garland
    Wes Garland
** lastStatModified:
splitExtension
*** Kris Kowal
    Python: splitext
** ctime:
        Split a path in root and extension.
*** Tom Robinson
        The extension is everything starting at the last dot in the last
*** Kevin Dangoor
        pathname component; the root is everything before that.
 
        It is always true that root + ext == p.
* the Date that the file was created, if the underlying file system makes that datum available (most Unix systems do not store creation time, but Windows and some Unix varieties do).  Returns undefined if the underlying file system does not provide a time.
splitDrive
** created:
    Python: splitDrive
** creationTime:
        Split a pathname into a drive specification and the rest of the
** firstModified:
        path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
 
* a String name for the type of the fileOne of: "file", "directory", "characterSpecial", "blockSpecial", "fifo", "link", "socket", or "unknown"
** type:
Where Did I Come From
** kind:
  =====================
** ommit entirely:
*** Kris Kowal
getType(fileName)
 
    Ruby: ftype(file_name)
* whether the corresponding object is a file (not a directory).
        Identifies the type of the named file; the return
** isFile:
        string is one of ``file‚Äô‚Äô, ``directory‚Äô‚Äô, ``characterSpecial‚Äô‚Äô,
*** Kris Kowal
        ``blockSpecial‚Äô‚Äô, ``fifo‚Äô‚Äô, ``link‚Äô‚Äô, ``socket‚Äô‚Äô, or ``unknown‚Äô‚Äô.
 
* whether the corresponding object is a directory
    Wes Garland: type or osType
** isDirectory
 
  isFile
* whether the corresponding object is a symbolic link
    Python: isfile
** isLink
    Java: isFile
 
    Ruby: file?
* whether the corresponding object is a mount point
isDirectory
** isMountPoint:
    Java: isDirectory
** ommit entirely:
    Python: isdir
*** Wes Garland
    Ruby: directory?
 
  isLink
* whether the corresponding object is a socket
    Java: omitted
** isSocket
    Python: islink
 
    Ruby: symlink?
* whether the corresponding object is a pipe
    isSymlink would be more accurate since it doesn't
** isPipe
    test hard links, but everything's a hard link so it's
** isFifo
    not like the name "isLink" will ever be needed for
 
    that purpose.
* whether the correpsonding object is a block device
isMount
** isBlockDevice
    Python: ismount with some question of inter-unix compatibility
 
    undesirable - Wes Garland
* the size of the corresponding object in bytes
  isSocket
** size
    Ruby: socket?
 
  isPipe
* the name or id of the owner of the file (following symlinks)
    Ruby: pipe?
** owner
  isBlockDevice
 
    Ruby: blockdev?(file_name)
* the name or id of the group owner of the file (following symlinks)
** groupOwner
 
Where Am I
* the name or id of the owner of the file or symbolic link
==========
** linkOwner
 
  cwd
* the name or id of the group owner of the file or symbolic link
    Python: os.getcwd
** linkGroupOwner
    Unix: pwd
 
    getCwd, workingDir, presentWorkingDir, currentWorkingDir
* whether the owner of the process at the time of this stat object's construction is the same as the owner of this file.
** owned
Projection
 
----------
* whether the corresponding object can be opened for reading by the current process owner
** isReadable
join(base, path)
 
    Python: join(base, rel)
* whether the corresponding object can be opened for writing by the current process owner
    Java: File(parent, child)
** isWritable
resolve(path, [base=cwd])
 
    the path to a given path from another
* whether the corresponding object is an executable for the current process owner
    Python: resolve(rel, abs = '.') -> join(abs, rel)
** isExecutable
    Python: abspath(path) -> normpath(join(cwd(), path))
 
    unnecessary - Wes Garland
* whether the corresponding executable file will be forced to run as the owner of the file if any user executes it.
relative(source, target)
** executesAsOwner
    the relative path from source to target
 
parent(path)
* whether the corresponding executable file wil be forced to run with its group owner if any user executes it.
    Java: getParent
** executesAsGroupOwner
    Wes Garland: needs to be clear whether it's the lexical parent on the path
 
    or the parent link in the file system
* an object that represents the permissions granted to the owner of the file.
concatName(base, rel)
** This object would contain:
    Ruby: catname(from, to)
*** isReadable
        If to is a valid directory, from will be appended to to,
*** isWritable
        adding and escaping backslashes as necessary. Otherwise, to will
*** isExecutable
        be returned. Useful for appending from to to only if the filename
** ownerPermissions
        was not specified in to.
 
    unnecessary - Wes Garland
* an object that represents the permissions granted to the group that owns the file.
   
** The object would contain:
Identification
*** isReadable
--------------
*** isWritable
*** isExecutable
absolute(path)
** groupPermissions
    follows symlinks from the root and produces
 
    a canonical, normal form of a path
* an object that represents the permissions granted to anyone who has no claim to the file.
    Python: realpath
** The object would contain:
    Unix: realpath
*** isReadable
    realize, realPath, canon, canonize, canonical, getCanonical, normalize
*** isWritable
isAbsolute(path)
*** isExecutable
    Java: isAbsolute
** worldPermissions
    Python: isabs
 
normalize(path)
* returns an object that represents the permissions granted to the user corresponding to a particular name or identifier.
    Python: normpath
** The returned object would contain:
        Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
*** isReadable
        It should be understood that this may change the meaning of the path
*** isWritable
        if it contains symbolic links!
*** isExecutable
    Jack: canonicalize
** getPermissions(user)
    canon, canonize, canonical, normal, getCanonical, getNormal
 
normalizeCase(path)
* the inode/vnode number of the underlying object (optional)
    Python: normcase
** inode
        Normalize the case of a pathname.  Trivial in Posix, string.lower on Mac.
 
        On MS-DOS this may also turn slashes into backslashes; however, other
* the device number of the underlying object (optional)
        normalizations (such as optimizing '../' away) are not allowed
** device
        (another function should be defined to do that).
 
    unnecessary - Wes Garland
 
samePath(a, b)
== File Object ==
    Python: samefile
 
    Ruby: identical?
File objects can only be constructed by methods of FileSystem objects. Different IO types might be returned for calls to open with different modes, generally following the protocol outlined in Python's PEP 3116. 
sameFile(a, b)
 
    Python: sameopenfile
== IO Objects ==
sameStat(a, b)
 
    Python: sameStat
* reads as many bytes as are available from an IO stream and returns the number of actually read bytes.
    Returns whether the inode and dev are the same
** read()
 
* reads as many bytes as are available but less than a given number from an IO stream and returns the number of actually read bytes.
Platform Constants
** read(n)
------------------
 
* reads a line of characters from a line buffered IO object.  Returns a blank line on EOF.
selfDirectory ('.')
** readLine()
    Python: curdir
 
parentDirectory ('..')
* returns an Array-like view of the lines in an IO stream, or simply an Array of Strings.
    Python: pardir
** readLines()
    parDir
 
extensionSeparator
* returns the next line in the stream. Throws a StopIteration exception on EOF.
    Java: separator
** next()
    Python: extsep
 
    extsep, extSep, extensionSeparator, EXTENSION_SEPARATOR
* returns an iteration on the lines of the IO stream, which is simply itself since it implements "next".
separator
** iter()
    Java: pathSeparator
 
    Python: sep
* calls a relation with each line of input from the IO stream.
    sep, separator, SEPARATOR
** forEach(relation)
alternateSeparator
 
    Python: altsep
* reads bytes into a buffer, up to as many as the length of the buffer, pending availablility.  Returns the number of bytes actually read.
    altsep alternateSeparator ALTERNATE_SEPARATOR
** readInto(buffer)
deviceNull
 
    Python: devnull
* writes bytes to a stream. Returns the number of bytes that were actually written.
    devNull
** write(buffer)
    DEV_NULL:
 
    unnecessary - Wes Garland
* moves the position of the cursor in a random access IO stream.
path
** Accepts a code that determines whether to move the position relative to the beginning, end, or current position:
    Python: defpath = ':/bin:/usr/bin'
*** 0 or "begin": beginning of file (default)
    PATH
*** 1 or "relative": current position
    unnecessary - Wes Garland
*** 2 or "end": end of file
supportsUnicodeFileName
** seek(pos, whence)
    Python: supports_unicode_filenames
 
* returns the current position of the random access cursor for an IO stream.  The position may be a "cookie" object or a Number, depending on the IO type.
** tell()
What is a "Phone Book"
 
======================
* truncates the size of a file to the current cursor position or a given position, which must be either a "cookie" object or a Number, depending on the IO type.
** truncate(pos)
match(pattern, fileName)
 
    Ruby: fnmatch(pattern, file_name, [flags])
* returns whether the cursor is at the end of the IO stream.
glob(pattern) - Wes Garland, Kris Kowal
** isEof():
    Ruby: Dir[pattern]
*** Kris Kowal
list(path)
** atEOF():
    Python: os.listdir
*** Wes Garland (non-commital)
    Ruby: Dir[path_glob]
** eof()
    listdir, Dir
 
    listFiles, listDirs
* flushes buffers for an IO stream
listIter
** flush()
    next
 
    prev
* blocks until an advisory lock is acquiredThrows an error if a lock cannot be acquired on the system.
listRoots
** lock("exclusive|shared")
    List the available filesystem roots.
 
* tries to grab an advisory lock without blocking, and returns whether it succeeded.
expand
** tryLock("exclusive|shared")
    Python: expandUser -> expandVars -> normalize -> real
 
    Ruby: expand_path
* releases an advisory lock.
expandUser
** unlock()
    Python: expanduser
 
    expands '~user'
* closes a stream
  expandVars
** close()
    Python: expandvars
 
    expands shell variables like $var and ${var}
* whether the IO stream is readable
    unnecessary - Wes Garland
** isReadable
 
* whether the IO stream is writable
Is That Really Necessary
** isWritable
========================
 
* whether the IO stream supports random access
isHidden
** isSeekable
    Java: isHidden
 
    no - Wes Garland
* whether the IO stream is attached to a virtual or real teletype.
isZero(fileName)
** isTTY:
    Ruby: zero?
*** Wes Garland
    Perl: -z
** isTty:
    returns whether the file exists and is of zero length
*** Kris Kowal
    no - Wes Garland
 
  compare(source, target)
* the descriptor of the underlying file, an opaque reference to the file descriptor, or undefined if file descriptors are not supported.
    Ruby: compare
** fileNo
    no - Wes Garland
 
install(from, to, [mode], [verbose = false])
* the underlying unbuffered IO stream, if it exists.  Otherwise undefined.
    Ruby: install
** raw
        If src is not the same as dest, copies it and changes the permission
 
        mode to mode. If dest is a directory, destination is dest/src. If
* creates a duplicate of the IO stream, with its own buffereing and cursor.
        mode is not set, default is used. If verbose is set to true, the
** copy()
        name of each file copied will be printed.
*** Kris Kowal
    no - Wes Garland
** dup()
url(path)
 
    Java: toURL
 
    no - Wes Garland
= File Module =
uri(path)
 
    Java: toURI
The file module would export a copy of every property on "environment.fs", for convenience.  Thus::
    no - Wes Garland
 
walk
  require('file').open(foo)
    Python: Walk a directory tree.
 
    no - Kris Kowal
Would be equivalent to:
commonPrefix
 
    return the longest common prefix of a list of paths.
  environment.fs.open(foo)
    no - Kris Kowal
 
 
= Notes for Further Discussion =
File Type
 
===========
* input, output, stdin, stdout, STDIN, STDOUT
* ...placed on environment or in a module?
[new] File(path, "+rwab", encoding)
* separation of knowledge of paths between file system and files
* currentDirectory, getCurrentDirectory
    File(fileNo:Number)
* temporary files and directories
    File(path:String, mode, encoding)
* opening a temp file with an implied immediate exclusive lock
    File({path, mode, buffering, encoding, newLine})
* dup and dup2 and other file-descriptor specific constructs for permissive environments.
    File({directory, prefix}) - temp file
* canRead, canWrite, poll, select
    File({command})
* taxonomy of IO classes
* default encoding for String, line-buffered IO
    Mode:
 
        +, update
 
        a, append
= References =
        r, read
 
        w, write
; http://www.python.org/dev/peps/pep-3116/: PEP on new File IO
        x, exclusive (lock)
; http://java.sun.com/j2se/1.4.2/docs/api/java/io/File.html: Java File API
        c, canon (nonblocking)
; http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileOutputStream.html: Java Output API
       
; http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileInputStream.html: Java Input API
    construction implicitly opens
; http://www.erights.org/javadoc/java/io/File.html: E Secure File API
    does the javascript GC call a destructor function?  needs to.
; http://www.cs.berkeley.edu/~daw/joe-e/api/org/joe_e/file/Filesystem.html: Joe-E Secure File System API
          - Kris Kowal, Wes Garland
; http://docs.python.org/library/os.path.html: Python Path API
; http://www.ruby-doc.org/core/classes/File.html: Ruby File API
    Java: FileStream
; http://www.ruby-doc.org/core/classes/IO.html: Ruby IO API
    Python: open is also filefile is generally preferred
: http://spreadsheets.google.com/pub?key=p9uiX8MUHeTiP0kPT591RUw: A matrix of existing ServerJS File API nomenclature and support
read() actual:Number
    reads to eof and returns binary
read(length) actual:Number
    Tom Robinson, Kevin Dangoor, Wes Garland, Kris Kowal:
        should return Binary and Binary should have .toString(encoding=default)
readLine() :String
next()
readInto(buffer:Binary) actual:Number
write(:Binary) actual:Number
seek(pos:Number, whence:Number code)
tell() pos:Cookie
truncate([pos:Cookie])
close() undefined
readable()
    whether it was opened for reading
writable()
    whether it was opened for writing
seekable()
    whether there is random access
fileNo
    undefined if the underlying implementation does not use file descriptors
raw()
    raw is undefined if there is no underlying unbuffered file object
readLine() :String
    returns "" if EOF hit immediately
    alternately input() to distinguish from read
next()
    next line
    throws if EOF hit immediately
iter()
    returns self (since it implements next())
forEach(relation)
  each(relation)
readLines()
writeLine()
    writeln, print
  writeLines(lines)
isEOF
    isEof - Kris Kowal
    atEOF - Wes Garland, maybe
fcntl - not recommended , use ioctl - Wes Garland
getFileNo - not recommended, - Wes Garland
toNumber an alias for `getFileNo` - not recommeded - Wes Garland
getLineNo - encoding dependent, not recommended - Wes Garland
isTty
    isTTY - Wes Garland
rewind
flush
seek
stat
select
fsync
ioctl
truncate
writeLock
readLock
unlock
dup()
stat
lastAccessed
lastModified
setLastModified
lastNodeModified
getSize
setPos
    Posix/Python: seek
getPos
    Posix/Python: tell
isReadable
isReallyReadable
isWritable
isReallyWritable
isExecutable
isReallyExecutable
executesAsOwner
executesAsGroupOwner
setMode
getMode
setReadOnly
pathComponents - Wes Garland
drive
extensions
baseName
dirName
absolute
    fullPath - Wes Garland
Directory Type
==============
list
forEach
each
iter
isSticky
remove
match
glob
Stat Type
=========
permissions  Permissions
inode        Number  // optional
linkCount    Number  // optional
owner        String
group        String  // optional
size          Number
atime        Date
mtime        Date
ctime        Date
Permissions Type
================
owner ["read", "write"]
group ["read"]
world []
sticky
setuid
setgid
Binary Type
===========
Binary(:Array, [width, endian])
Binary(:String, [ecoding])
Binary.fromArray(:Array, [width, endian])
Binary.fromString(:String, [encoding])
Binary.createBuffer(length, [fill = 0])
Binary.prototype.toString(encoding)
Array.prototype.toBinary([width, endian])
Todo
====
Split File into File and IO base (Tom Robinson, Kevin Dangoor)
Clarify difference between canRead, isReadable, and select
Resolve the default encoding for Binary.toString()
Note distinction between read():Binary and input():String, as well as write(:Binary) and print(String)
getEncoding
setEncoding
George Mosochovitis: divide among FileSystem, Path, and File
References
==========
http://www.python.org/dev/peps/pep-3116/
http://www.erights.org/javadoc/java/io/File.html
http://java.sun.com/j2se/1.4.2/docs/api/java/io/File.html
http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileOutputStream.html
http://java.sun.com/j2se/1.4.2/docs/api/java/io/FileInputStream.html
http://www.cs.berkeley.edu/~daw/joe-e/api/org/joe_e/file/Filesystem.html
http://docs.python.org/library/os.path.html
http://www.ruby-doc.org/core/classes/File.html
http://www.ruby-doc.org/core/classes/IO.html
171

edits

Navigation menu