From 5db9d90c9074c6a93cdf103287dfe24e9bc93ecc Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 17 Jul 2015 17:07:02 -0600 Subject: [PATCH] initial commit (tested working) --- README.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ index.js | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ test.js | 11 ++++++++ 3 files changed, 156 insertions(+) create mode 100644 README.md create mode 100644 index.js create mode 100644 test.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..fd03ff4 --- /dev/null +++ b/README.md @@ -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 + }); +}); +``` diff --git a/index.js b/index.js new file mode 100644 index 0000000..41b6e31 --- /dev/null +++ b/index.js @@ -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; diff --git a/test.js b/test.js new file mode 100644 index 0000000..ee89f25 --- /dev/null +++ b/test.js @@ -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); + }); +});