ServerJS/API/file

From MozillaWiki
< ServerJS‎ | API
Revision as of 07:02, 13 March 2009 by KrisKowal (talk | contribs) (Posted current notes for the file module API)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
Alright.  The task of the month is to ratify a File API.
There's been relatively little discussion on this topic.
There's not much opportunity to advance the state of the
art with yet another file API, which means that this will
inevitably degenerate into shed painting, but it must be
done!  Let's just slog through this shall we?

I've composed all of the functions from Ruby, Python, 
and Java into a very long list here.  Please note which
names you prefer, whether you feel a particular function
ought to not be part of the standard API, any functions
that are missing, and of course
comment and discuss semantics.  I will attempt
to reserve my opinions for a follow up post.

Let's begin with some high level preferences.  There
are a few naming choices that can be applied throughout
the following document with rigor.  Rather than note
every occurrence of the offending word, please state your
general preference:


1. make vs create

    make: Wes Garland (for dirs)
    create: George Mosochovitis, Wes Garland (for file mode)

2. dir vs directory

    dir:
    directory: Tom Robinson, Kevin Dangoor, George Mosochovitis, Wes Garland

3. cur vs current

    cur:
    current: Tom Robinson, Kevin Dangoor, Wes Garland, Kris Kowal

4. canonize, realize, normalize, qualify,
   canon, real, normal, qualified, absolute,
   canonicalize

    remove . and ..:
        normalize: Tom Robinson, Kevin Dangoor, Kris Kowal, George Mosochovitis
        qualify: Wes Garland
    remove . and .., start at /:
        absolute: Tom Robinson, Kevin Dangoor, Kris Kowal
    remove . and .., start at /, follow symlinks:
        canonical: Tom Robinson, Kevin Dangoor, Kris Kowal
        real: Tom Robinson, Kevin Dangoor

    Could apply for either of the two latter:
        absolute: George Moscochovitis, Wes Garland
        qualified: Wes Garland

5. constantCase vs CONSTANT_CASE vs kConstantCase

    There's a lot of legacy Netscape code with
    the pseudo Hungarian kConstantCase.

    CONSTANT_CASE: Tom Robinson, Kevin Dangoor
    constantCase: Kris Kowal, Wes Garland


6. Should "static" File methods be placed in

   require('file').File or : Tom Robinson, George Mosochovitis
   require('file') : Kris Kowal, Wes Garland

   e.g.,

   var files = require('file');
   files.move(a, b)

   vs.

   var File = require('file').File;
   File.move(a, b);

    Tom Robinson: should we use encoding here?
    Wes Garland: "b" should be stricken
    Wes Garland: encoding should actually be an encode function
    Kris Kowal: encoding should be the name of an encoding module

7. Choose:

   a.) setX/getX  vs
       a.) For all functions for which it would make sense to have a setter and a getter.
            (It would not make sense to have a setSize, so size() would be permissible)
       b.) For all functions that get things
            (getSize, getMtime, setMtime, getBaseName)

   b.) setX/X

        Kris Kowal

   c.) pick and choose; no overarching convention

   d.) X property with getters and setters

        for:
            Davey Waterson
            Wes Garland
            Daniel Friesen
        against:
            Tom Robinson
            Kris Kowal

exists(path) - Wes Garland
    Python: exists (uses stat(path) and checks for exception)
    Ruby: exist? exists?
linkExists(path)
    Python: lexists
        Returns whether a file exists, even if it
        ultimately refers to a broken symbolic link.
    Java: not provided

    Wes Garland: exists(path, true)
    Kris Kowal: linkExists(path)

move(source, target)
    Ruby: move
rename(source, target)
    Ruby: rename
copy(source, target)
    Ruby: copy

systemCopy(source, target):
    Ruby: syscopy
    Copies a file from to to. If to is a directory, copies from to to/from.

    unnecessary - Wes Garland

makeDirectory(path)
    Unix: mkdir
    createDirectory
makePath(path)
    Ruby: makedirs
    Unix: mkdir -p
    mkpath, mkdirs, createPath, createDirectories

    Wes Garland: makeDirectory(path, true)
    Kris Kowal: makePath(path)

makeTempFile(prefix, suffix)
    Java: createTempFile
    Python: mktemp
    mktemp(prefix, suffix), makeTemporary, makeTemp

    Wes Garland: this should be ommitted in favor of an alternate File constructor

remove(path)
    Ruby: unlink, delete
    Python: unlink, remove
    Java: delete
    Posix: unlink
    Unix: rm (remove)

    Tom Robinson, Kevin Dangoor: remove, definitely not delete
    Wes Garland: unlink, but remove and delete are fine


symlink(source, target)
    Ruby: symlink(old, new)
link(source, target)
    Creates a hard link
    Ruby: File.link(old_name, new_name) => 0
        Creates a new name for an existing file using a hard link.
        Will not overwrite new_name if it already exists
        (raising a subclass of SystemCallError).
        Not available on all platforms.

    Wes Garland: link(source, target, hard:Boolean)
    Kris Kowal: symlink, link

truncate(file_name, length)
create(path)
    Java: create
    Posix: creat
lock(path, "shared|exculsive") - Wes Garland
    writeLock(path), readLock(path)
tryLock(path, "shared|exclusive") - Wes Garland
unlock(path) - Wes Garland
    opens a lock

dup(fileno) - unnecessary Wes Garland
dup2(fileno, fileno) - unnecessary Wes Garland
    


Weights and Measures
====================

[new] Stat(path)
    returns a Stat object (more later)
stat(path)
    returns an Array
    Posix: stat
    Java: omitted
updateStat(:Stat) - Wes Garland
setStat(:Stat)
linkStat(path):
    Python: lstat

    Wes Garland: stat(path, true)
    Kris Kowal: linkStat(path)

atime(path) - Tom Robinson, Kevin Dangoor
    Java: omitted
    Python: getatime
    Ruby: atime
    lastAccessed
mtime(path) - Tom Robinson, Kevin Dangoor
    Java: lastModified
    Python: getmtime
    Ruby: mtime
    stat(path).st_mtime
    lastModified
touch(path) - Kris Kowal
    setLastModified(path, date)
    setMtime(path, date)
ctime(path) - Tom Robinson, Kevin Dangoor
    Unix: touch
    Java: omitted
    Python: getctime
    Ruby: ctime
    lastNodeModified
size(path) - Tom Robinson, Kevin Dangoor
    Ruby: size?
    Java: length
    Python: getsize
    Posix: size: stat(path).st_size
    getLength, length, getSize, size



Ownership
=========

getOwner(path)
getGroupOwner(path)
setOwner(path, user, [group])
    chown
setGroupOwner(path, group)
    chown
setLinkOwner(path, owner, [group])
    Ruby: file.lchown(owner_int, group_int, file_name,..) => integer
        Equivalent to File::chown, but does not follow symbolic links
        (so it will change the owner associated with the link,
        not the file referenced by the link).
        Often not available. Returns number of files in the argument list.

    Wes Garland: use SetOwner(path, owner, group, true)

setLinkGroupOwner(path, group)
isOwned()
    Ruby: owned?
        Returns true if the named file exists and the effective
        used id of the calling process is the owner of the file.
isGroupOwned()
    Ruby: grpowned?
        Returns true if the named file exists and the effective
        group id of the calling process is the owner of the file.
        Returns false on Windows.


Please Sir, Can I Have Some More
================================

isReadable(path)
    Java: canRead
    Ruby: readable
isReallyReadable(path)
    Ruby: readable_real?
    returns whether the file is readable by the current
    process owner.
isWritable(path)
    Java: canWrite
    Ruby: writable
isReallyWritable(path)
    Ruby: writable_real?
isExecutable(path)
    Ruby: executable?
    Python: omitted
isReallyExecutable(path)
    Ruby: executable_real? 
isSticky(path)
    returns whether the sticky bit is set, which is to say
    that anyone can create a file in the directory
    but cannot delete those created by other users.
executesAsOwner(path)
    returns whether the file has the SID bit set.
executesAsGroupOwner(path)
    returns whether the file has the GID bit set.

setMode(path, mode)
    Posix: chmod
    Unix: chmod
    Ruby: chmod
getMode(path)
setLinkMode(path, mode)
    Ruby: File.lchmod(mode_int, file_name, ...) => integer
        Equivalent to File::chmod, but does not follow
        symbolic links (so it will change the permissions
        associated with the link, not the file
        referenced by the link).  Often not available.
setReadOnly(true|false)

getModeMask()
    Ruby: umask()
setModeMask(umask)
    Ruby: umask(umask)


What is My Name
===============

dirName(path)
    Python: dirname
        Returns the directory that contains the given path
    Ruby: File.dirname(file_name)

    Wes Garland: dirname
    Kris Kowal: dirName

baseName(path)
    Java: File(fileName).getName()
    Python: basename(file_name)
    Ruby: basename(file_name, [suffix])
    Unix: basename name [suffix]

    Wes Garland: basename
    Kris Kowal: baseName

extension(path)
    Ruby: extname
    extensionName

    extension: George Mosochovitis, Wes Garland, Kris Kowal

split
    Ruby: split
    Python: split
        Split a path in head (everything up to the last '/') and tail (the
        rest).  If the path ends in '/', tail will be empty.  If there is no
        '/' in the path, head  will be empty.
        Trailing '/'es are stripped from head unless it is the root.
pathComponents(path)
    Wes Garland
splitExtension
    Python: splitext
        Split a path in root and extension.
        The extension is everything starting at the last dot in the last
        pathname component; the root is everything before that.
        It is always true that root + ext == p.
splitDrive
    Python: splitDrive
        Split a pathname into a drive specification and the rest of the
        path.  Useful on DOS/Windows/NT; on Unix, the drive is always empty.


Where Did I Come From
=====================

getType(fileName)
    Ruby: ftype(file_name)
        Identifies the type of the named file; the return
        string is one of ``file’’, ``directory’’, ``characterSpecial’’,
        ``blockSpecial’’, ``fifo’’, ``link’’, ``socket’’, or ``unknown’’.

    Wes Garland: type or osType

isFile
    Python: isfile
    Java: isFile
    Ruby: file?
isDirectory
    Java: isDirectory
    Python: isdir
    Ruby: directory?
isLink
    Java: omitted
    Python: islink
    Ruby: symlink?
    isSymlink would be more accurate since it doesn't
    test hard links, but everything's a hard link so it's
    not like the name "isLink" will ever be needed for
    that purpose.
isMount
    Python: ismount with some question of inter-unix compatibility
    undesirable - Wes Garland
isSocket
    Ruby: socket?
isPipe
    Ruby: pipe?
isBlockDevice
    Ruby: blockdev?(file_name)


Where Am I
==========

cwd
    Python: os.getcwd
    Unix: pwd
    getCwd, workingDir, presentWorkingDir, currentWorkingDir

Projection
----------

join(base, path)
    Python: join(base, rel)
    Java: File(parent, child)
resolve(path, [base=cwd])
    the path to a given path from another
    Python: resolve(rel, abs = '.') -> join(abs, rel)
    Python: abspath(path) -> normpath(join(cwd(), path))
    unnecessary - Wes Garland
relative(source, target)
    the relative path from source to target
parent(path)
    Java: getParent
    Wes Garland: needs to be clear whether it's the lexical parent on the path
    or the parent link in the file system
concatName(base, rel)
    Ruby: catname(from, to)
        If to is a valid directory, from will be appended to to,
        adding and escaping backslashes as necessary. Otherwise, to will
        be returned. Useful for appending from to to only if the filename
        was not specified in to.
    unnecessary - Wes Garland

Identification
--------------

absolute(path)
    follows symlinks from the root and produces
    a canonical, normal form of a path
    Python: realpath
    Unix: realpath
    realize, realPath, canon, canonize, canonical, getCanonical, normalize
isAbsolute(path)
    Java: isAbsolute
    Python: isabs
normalize(path)
    Python: normpath
        Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
        It should be understood that this may change the meaning of the path
        if it contains symbolic links!
    Jack: canonicalize
    canon, canonize, canonical, normal, getCanonical, getNormal
normalizeCase(path)
    Python: normcase
        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
        normalizations (such as optimizing '../' away) are not allowed
        (another function should be defined to do that).
    unnecessary - Wes Garland
samePath(a, b)
    Python: samefile
    Ruby: identical?
sameFile(a, b)
    Python: sameopenfile
sameStat(a, b)
    Python: sameStat
    Returns whether the inode and dev are the same


Platform Constants
------------------

selfDirectory ('.')
    Python: curdir
parentDirectory ('..')
    Python: pardir
    parDir
extensionSeparator
    Java: separator
    Python: extsep
    extsep, extSep, extensionSeparator, EXTENSION_SEPARATOR
separator
    Java: pathSeparator
    Python: sep
    sep, separator, SEPARATOR
alternateSeparator
    Python: altsep
    altsep alternateSeparator ALTERNATE_SEPARATOR
deviceNull
    Python: devnull
    devNull
    DEV_NULL:
    unnecessary - Wes Garland
path
    Python: defpath = ':/bin:/usr/bin'
    PATH
    unnecessary - Wes Garland
supportsUnicodeFileName
    Python: supports_unicode_filenames


What is a "Phone Book"
======================

match(pattern, fileName)
    Ruby: fnmatch(pattern, file_name, [flags])
glob(pattern) - Wes Garland, Kris Kowal
    Ruby: Dir[pattern]
list(path)
    Python: os.listdir
    Ruby: Dir[path_glob]
    listdir, Dir
    listFiles, listDirs
listIter
    next
    prev
listRoots
    List the available filesystem roots.

expand
    Python: expandUser -> expandVars -> normalize -> real
    Ruby: expand_path
expandUser
    Python: expanduser
    expands '~user'
expandVars
    Python: expandvars
    expands shell variables like $var and ${var}
    unnecessary - Wes Garland


Is That Really Necessary
========================

isHidden
    Java: isHidden
    no - Wes Garland
isZero(fileName)
    Ruby: zero?
    Perl: -z
    returns whether the file exists and is of zero length
    no - Wes Garland
compare(source, target)
    Ruby: compare
    no - Wes Garland
install(from, to, [mode], [verbose = false])
    Ruby: install
        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
        mode is not set, default is used. If verbose is set to true, the
        name of each file copied will be printed.
    no - Wes Garland
url(path)
    Java: toURL
    no - Wes Garland
uri(path)
    Java: toURI
    no - Wes Garland
walk
    Python: Walk a directory tree.
    no - Kris Kowal
commonPrefix
    return the longest common prefix of a list of paths.
    no - Kris Kowal


File Type
===========

[new] File(path, "+rwab", encoding)

    File(fileNo:Number)
    File(path:String, mode, encoding)
    File({path, mode, buffering, encoding, newLine})
    File({directory, prefix}) - temp file
    File({command})

    Mode:
        +, update
        a, append
        r, read
        w, write
        x, exclusive (lock)
        c, canon (nonblocking)
        
    construction implicitly opens
    does the javascript GC call a destructor function?  needs to.
         - Kris Kowal, Wes Garland

    Java: FileStream
    Python: open is also file.  file is generally preferred

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