Browse files

Fix and improve filesystem abstraction

  • Loading branch information...
1 parent e5bbe2c commit 342510bc01821997d723b8e6e3187ee91156ea7d @Benvie Benvie committed Feb 22, 2012
Showing with 174 additions and 66 deletions.
  1. +174 −66 fsExplorer.js
View
240 fsExplorer.js
@@ -1,38 +1,59 @@
var path = require('path');
var fs = require('fs');
-
-
var exists = fs.exists || path.exists;
var existsSync = fs.existsSync || path.existsSync;
-var resolve = path.resolve;
-
var isWin = process.platform === 'win32';
var slice = Function.call.bind([].slice);
-
module.exports = {
- explore: function explore(path){
- return new Path(path);
- },
-
+ explore: explore,
+ resolve: resolve,
Path: Path,
File: File,
Directory: Directory,
-
existsSync: existsSync,
- exists: exists,
- resolve: resolve
+ exists: exists
};
+
var cache = {};
var existCache = {};
+var types = {
+ path: Path,
+ file: File,
+ directory: Directory
+};
+
+
+
+function explore(path){
+ return new Path(path);
+}
+
+
+function resolve(){
+ var parts = arguments.length ? 1 in arguments ? slice(arguments) : [arguments[0]] : null;
+ if (!parts) return '';
+
+ if (parts[0][0] === '~') {
+ var home = process.env[isWin ? 'homepath' : 'home'];
+ if (home) {
+ parts[0] = parts[0].slice(1);
+ parts.unshift(home);
+ }
+ }
+ return path.resolve.apply(null, parts);
+}
+
+
+
function Path(request, type){
if (type) {
@@ -52,66 +73,81 @@ Path.isPath = function isPath(o){
return Path.prototype.isPrototypeOf(o);
};
+Path.getType = function getType(o){
+ if (Path.isPath(o)) return o.getType(), o.constructor;
+ if (typeof o === 'string') {
+ if (o.toLowerCase() in types) return types[o.toLowerCase()];
+ if (o in cache) return cache[o].constructor;
+ }
+}
+
+
+
+
Path.prototype = {
constructor: Path,
+ deref: function deref(){
+ this.getName();
+ this.getDirname();
+ this.getParts();
+ this.getType();
+ this.getParent();
+ return this;
+ },
+
getExtname: function getExtname(){
- return defineIfUnset(this, 'extname', path.extname(this.path));
+ return object.getResolve(this, 'extname', path.extname(this.path));
},
get extname(){ return this.getExtname() },
getDirname: function getDirname(){
- return defineIfUnset(this, 'dirname', path.dirname(this.path));
+ return object.getResolve(this, 'dirname', path.dirname(this.path));
},
get dirname(){ return this.getDirname() },
getBasename: function getBasename(){
- return defineIfUnset(this, 'basename', path.basename(this.path));
+ return object.getResolve(this, 'basename', path.basename(this.path));
},
get basename(){ return this.getBasename() },
getName: function getName(){
- return defineIfUnset(this, 'name', this.basename.slice(0, -this.extname.length));
+//return object.getResolve(this, 'name', this.extname.length ? this.basename.slice(0, -this.extname.length) : this.basename.slice);
},
get name(){ return this.getName() },
getStats: function getStats(){
- return defineIfUnset(this, 'stats', fs.statSync(this.path));
+ return object.getResolve(this, 'stats', new Stat(this.path));
},
get stats(){ return this.getStats() },
getParts: function getParts(){
- return defineIfUnset(this, 'parts', this.path.split(Path.separator[0]), function(parts){
- isWin && define(this, 'drive', parts.shift());
+ return object.getResolve(this, 'parts', function(parts){
+ parts = this.path.split(Path.separator[0]);
+ isWin && object.define(this, 'drive', parts.shift());
return parts;
});
},
get parts(){ return this.getParts() },
getType: function getType(){
if (!this.exists()) return null;
- if ('type' in this) return this[type];
+ if (!this.constructor === Path) return this.type;
var stats = this.stats;
- if (stats.isFile()) {
- this.__proto__ = File.prototype;
- return 'File';
- }
- if (stats.isDirectory()) {
- this.__proto__ = Directory.prototype;
- return 'Directory';
- }
- return null;
+ this.__proto__ = Path.getType(stats.type).prototype;
+ return stats.type;
},
get type(){ return this.getType() },
getParent: function getParent(){
if (this.root) return
- return defineIfUnset(this, 'parent', new Directory([this.path, '..']), function(parent){
+ return object.getResolve(this, 'parent', function(parent){
+ parent = new Directory([this.path, '..']);
if (parent === this) {
- define(this, 'root', true);
+ object.define(this, 'root', true);
return;
}
return parent;
@@ -120,7 +156,6 @@ Path.prototype = {
get parent(){ return this.getParent() },
resolve: function resolve(request, type){
- type = type || File;
return new type([this.path].concat(request));
},
@@ -137,9 +172,7 @@ Path.prototype = {
},
toIdentifier: function toIdentifier(){
- return this.basename.slice(0,-this.extname.length).replace(/[^\w]+(.)?/g, function(m,c){
- return c ? c.toUpperCase() : '';
- });
+ return string.camelCase(this.basename.slice(0, -this.extname.length));
},
toString: function toString(){
@@ -157,6 +190,8 @@ Path.prototype = {
};
+
+
function File(name){
if (!Path.isPath(this)) return new File(name);
if (Path.call(this, name) === name) return name;
@@ -165,43 +200,56 @@ function File(name){
File.prototype = {
__proto__: Path.prototype,
constructor: File,
- encoding: 'utf8',
- chunkSize: 2 << 15,
- isFile: true,
type: 'File',
+ encoding: 'utf8',
+ chunkSize: 65536,
+
+
+ getSize: function getSize(){
+ return object.getResolve(this, 'size', this.stats.size);
+ },
+ get size(){ return this.getSize() },
+
read: function read(){
return this.contents = fs.readFileSync(this.path, this.encoding);
},
+
write: function write(content){
this.contents = content || this.contents || '';
fs.writeFileSync(this.path, this.contents);
return this;
},
+
delete: function delete_(){
fs.unlinkSync(this.path);
delete this.contents;
return this;
},
+
move: function move(to){
to = new File(to);
fs.renameSync(this.path, to.path);
return to;
},
+
copy: function copy(to, processor){
to = new File(to);
to.write(processor ? processor(this.read()) : this.read());
return to;
},
+
open: function open(flag){
this.fd && this.close();
this.fd = fs.openSync(this.path, flag);
return this;
},
+
close: function close(){
this.fd && fs.closeSync(this.fd);
delete this.fd;
return this;
},
+
streamCopy: function streamCopy(to){
var buffer = new Buffer(this.chunkSize);
var dest = new File(to);
@@ -222,6 +270,8 @@ File.prototype = {
+
+
function Directory(name){
if (!Path.isPath(this)) return new Directory(name);
if (Path.call(this, name) === name) return name;
@@ -245,15 +295,17 @@ Directory.filters = {
Directory.prototype = {
__proto__: Path.prototype,
constructor: Directory,
- isDir: true,
type: 'Directory',
+
map: function map(names){
return (Array.isArray(names) ? names : [names]).map(resolve.bind(null, this.path));
},
+
read: function read(unmapped){
var names = fs.readdirSync(this.path);
return unmapped ? names : this.map(names);
},
+
filter: function filter(filterBy){
var callback;
if (typeof filtberBy === 'function') {
@@ -264,43 +316,99 @@ Directory.prototype = {
}
}
return this.read().filter(callback);
- }
+ },
+
+ getChildren: function getChildren(){
+ return object.getResolve(this, 'children', function(children){
+ children = this.read().map(explore);
+ return children;
+ });
+ },
+ get children(){ return this.getChildren() },
};
-function get(o, name){
- if (o.hasOwnProperty(name)) return o[name];
-}
-function defineIfUnset(o, name, value, hidden, readonly){
- var val = get(o, name);
- if (typeof val !== 'undefined') return val;
- var args = slice(arguments);
- if (args.length > 3 && typeof args[args.length-1] === 'function') {
- var cb = args.pop();
+var statTypes = Object.keys(fs.Stats.prototype);
+
+
+function Stat(stats){
+ if (typeof stats === 'string' && existsSync(stats)) {
+ stats = fs.statSync(stats);
}
- val = define.apply(this, args);
- return cb ? cb.call(o, val) : val;
-}
-function define(o, name, value, hidden, readonly){
- if (arguments.length === 2) {
- hidden = o.constructor.prototype === o;
+ Object.getOwnPropertyNames(stats).forEach(function(s){
+ if (stats[s]) this[s] = stats[s];
+ }, this);
+
+ for (var i=0; statTypes[i]; i++) {
+ if (stats[statTypes[i]]()) {
+ this.type = statTypes[i].slice(2);
+ break;
+ }
+ }
+
+ if (this.type === 'File') {
+ this.sizeLabel = string.filesize(this.size);
}
- Object.defineProperty(o, name, {
- value: value,
- enumerable: !hidden,
- writable: !readonly,
- configurable: true
- });
- return value;
}
-// function filesize(a){
-// for (var b=0; a>=1024; b++) a /= 1024;
-// return (b ? a.toFixed(2)+' '+' kmgt'[b] : a+' ')+'b';
-// }
+
+var object = {
+ getOwn: function getOwn(o, name){
+ if (o.hasOwnProperty(name)) return o[name];
+ },
+
+ getResolve: function getResolve(o, name, value, hidden, readonly){
+ var val = object.getOwn(o, name);
+ if (typeof val !== 'undefined') return val;
+
+ var args = slice(arguments);
+ if (args.length > 2 && typeof args[args.length-1] === 'function') {
+ var cb = args.pop();
+ }
+ args[2] = cb ? cb.call(o, val || args[2]) : val || args[2];
+ return object.define.apply(this, args);
+ },
+
+ define: function define(o, name, value, hidden, readonly){
+ if (arguments.length === 3) {
+ hidden = o.constructor.prototype === o;
+ }
+ Object.defineProperty(o, name, {
+ value: value,
+ enumerable: !hidden,
+ writable: !readonly,
+ configurable: true
+ });
+ return value;
+ },
+
+ hide: function hide(o,p){
+ p = p || Object.keys(o);
+ return Array.isArray(p) ? p.map(hide.bind(null, o)) : Object.defineProperty(o, p, { enumerable: false });
+ }
+};
+
+
+
+
+var string = {
+ camelCase: function(s){
+ return s.replace(/[^\w]+(.)?/g, function(m, c){
+ return c ? c.toUpperCase() : '';
+ });
+ },
+ filesize: function(s){
+ for (var b=0; s >= 1024; b++) s /= 1024;
+ return (b ? s.toFixed(2)+' '+' kmgt'[b] : s+' ')+'b';
+ }
+};
+
+object.hide(Path.prototype);
+object.hide(File.prototype);
+object.hide(Directory.prototype);

0 comments on commit 342510b

Please sign in to comment.