initial commit (tested working)

This commit is contained in:
AJ ONeal 2015-07-17 17:07:02 -06:00
parent 4ac9bbcafe
commit 5db9d90c90
3 changed files with 156 additions and 0 deletions

68
README.md Normal file
View File

@ -0,0 +1,68 @@
safe-replace
============
A micro-module for safely replacing a file.
Commandline Reference
---------------------
If I want to safely replace a file with a new version, I would do so like this:
```bash
# create the new version
touch keep.txt.new
# remove the previous backup
rm -f keep.txt.bak
# move the current version to the backup
mv keep.txt keep.txt.bak
# move the new version to the current
mv keep.txt.new keep.txt
```
If `keep.txt` became corrupt and I wanted to use the backup,
I would do this:
```bash
# copy the backup to the new version
rsync keep.txt.bak keep.txt
```
In Node
-------
I ported that proccess to node.
```js
// default behavior is to concat (filename + '.' + 'new')
var safeReplace = require('safe-replace').create({ new: 'new', bak: 'bak' });
var data = new Buffer('A priceless document');
safeReplace.writeFile('keep.txt', data, 'ascii').then(function () {
fs.readdir('.', function (nodes) {
console.log('file system nodes', nodes);
// keep.txt
// keep.txt.bak
});
});
// let's say I wrote keep.txt.new with my own mechanism
safeReplace.commit('keep.txt').then(function () {
fs.readdir('.', function (nodes) {
console.log('file system nodes', nodes);
// keep.txt
// keep.txt.bak
});
});
// let's say I want to revert the file from the '.bak'
safeReplace.revert('keep.txt').then(function () {
fs.readdir('.', function (nodes) {
console.log('file system nodes', nodes);
// keep.txt
// keep.txt.bak
});
});
```

77
index.js Normal file
View File

@ -0,0 +1,77 @@
'use strict';
var PromiseA = require('bluebird').Promise;
var fs = PromiseA.promisifyAll(require('fs'));
function noop() {
}
function create(options) {
if (!options) {
options = {};
}
if (!options.new) {
options.new = 'new';
}
if (!options.bak) {
options.bak = 'bak';
}
if (options.new === options.bak) {
throw new Error("'new' and 'bak' suffixes cannot be the same... duh");
}
var newnamefn = options.newnamefn || function (pathname) {
return pathname + '.' + options.new;
};
var baknamefn = options.baknamefn || function (pathname) {
return pathname + '.' + options.bak;
};
var namefn = options.namefn || function (pathname) {
return pathname;
};
var sfs = {
writeFile: function (filename, data, options) {
//console.log(newnamefn(filename));
return fs.writeFileAsync(newnamefn(filename), data, options).then(function () {
//console.log(namefn(filename));
return sfs.commit(namefn(filename));
});
}
, commit: function (filename) {
// this may not exist
return fs.unlinkAsync(baknamefn(filename)).then(noop, noop).then(function () {
// this may not exist
//console.log(namefn(filename), '->', baknamefn(filename));
return fs.renameAsync(namefn(filename), baknamefn(filename)).then(function () {
//console.log('created bak');
}, noop);
}).then(function () {
// this must be successful
//console.log(newnamefn(filename), '->', namefn(filename));
return fs.renameAsync(newnamefn(filename), namefn(filename)).then(noop, function (err) {
//console.error(err);
return sfs.revert(filename).then(function () {
return PromiseA.reject(err);
});
});
});
}
, revert: function (filename) {
return new PromiseA(function (resolve, reject) {
var reader = fs.createReadStream(baknamefn(filename));
var writer = fs.createWriteStream(namefn(filename));
reader.on('error', reject);
writer.on('error', reject);
reader.pipe(writer);
writer.on('close', resolve);
});
}
};
return sfs;
}
module.exports.create = create;

11
test.js Normal file
View File

@ -0,0 +1,11 @@
'use strict';
var safeReplace = require('./').create();
//var safeReplace = require('safe-replace').create();
var fs = require('fs');
safeReplace.writeFile('keep.txt', 'my precious').then(function () {
fs.readdir('.', function (nodes) {
console.log('file system nodes', nodes);
});
});