ServerJS/API/file
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