diff options
author | isaacs <i@izs.me> | 2014-09-20 03:23:02 +0400 |
---|---|---|
committer | isaacs <i@izs.me> | 2014-09-20 03:23:02 +0400 |
commit | 42c872b32cadc0e555638fc78eab3a38a04401d8 (patch) | |
tree | 00c64ac3f1867aab1effd4bc5cb6af7ebe718412 /node_modules/fs-write-stream-atomic | |
parent | 4ab5a79c68dbbfb8a468d89d27aea877e0fae3b6 (diff) |
Add fs-write-stream-atomic dep
Diffstat (limited to 'node_modules/fs-write-stream-atomic')
-rw-r--r-- | node_modules/fs-write-stream-atomic/LICENSE | 15 | ||||
-rw-r--r-- | node_modules/fs-write-stream-atomic/README.md | 35 | ||||
-rw-r--r-- | node_modules/fs-write-stream-atomic/index.js | 68 | ||||
-rw-r--r-- | node_modules/fs-write-stream-atomic/package.json | 38 | ||||
-rw-r--r-- | node_modules/fs-write-stream-atomic/test/basic.js | 78 |
5 files changed, 234 insertions, 0 deletions
diff --git a/node_modules/fs-write-stream-atomic/LICENSE b/node_modules/fs-write-stream-atomic/LICENSE new file mode 100644 index 000000000..19129e315 --- /dev/null +++ b/node_modules/fs-write-stream-atomic/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/fs-write-stream-atomic/README.md b/node_modules/fs-write-stream-atomic/README.md new file mode 100644 index 000000000..0554d6861 --- /dev/null +++ b/node_modules/fs-write-stream-atomic/README.md @@ -0,0 +1,35 @@ +# fs-write-stream-atomic + +Like `fs.createWriteStream(...)`, but atomic. + +Writes to a tmp file and does an atomic `fs.rename` to move it into +place when it's done. + +First rule of debugging: **It's always a write condition.** + +## USAGE + +```javascript +var fsWriteStreamAtomic = require('fs-write-stream-atomic') +// options are optional. +var write = fsWriteStreamAtomic('output.txt', options) +var read = fs.createReadStream('input.txt') +read.pipe(write) + +// When the write stream emits a 'finish' or 'close' event, +// you can be sure that it is moved into place, and contains +// all the bytes that were written to it, even if something else +// was writing to `output.txt` at the same time. +``` + +### `fsWriteStreamAtomic(filename, [options])` + +* `filename` {String} The file we want to write to +* `options` {Object} + * `chown` {Object} User and group to set ownership after write + * `uid` {Number} + * `gid` {Number} + * `encoding` {String} default = 'utf8' + * `mode` {Number} default = `0666` + * `flags` {String} default = `'w'` + diff --git a/node_modules/fs-write-stream-atomic/index.js b/node_modules/fs-write-stream-atomic/index.js new file mode 100644 index 000000000..cb9b99aa7 --- /dev/null +++ b/node_modules/fs-write-stream-atomic/index.js @@ -0,0 +1,68 @@ +var fs = require('graceful-fs') +var util = require('util') +var crypto = require('crypto') + +function md5hex () { + var hash = crypto.createHash('md5') + for (var ii=0; ii<arguments.length; ++ii) { + hash.update(''+arguments[ii]) + } + return hash.digest('hex') +} + +var invocations = 0; +function getTmpname (filename) { + return filename + "." + md5hex(__filename, process.pid, ++invocations) +} + +module.exports = WriteStream + +util.inherits(WriteStream, fs.WriteStream) +function WriteStream (path, options) { + if (!options) + options = {} + + if (!(this instanceof WriteStream)) + return new WriteStream(path, options) + + this.__atomicTarget = path + this.__atomicChown = options.chown + this.__atomicDidStuff = false + this.__atomicTmp = getTmpname(path) + + fs.WriteStream.call(this, this.__atomicTmp, options) +} + +// When we *would* emit 'close' or 'finish', instead do our stuff +WriteStream.prototype.emit = function (ev) { + if (this.__atomicDidStuff || (ev !== 'close' && ev !== 'finish')) + return fs.WriteStream.prototype.emit.apply(this, arguments) + + atomicDoStuff.call(this, function (er) { + if (er) + this.emit('error', er) + else + this.emit(ev) + }.bind(this)) +} + +function atomicDoStuff(cb) { + if (this.__atomicDidStuff) + throw new Error('Already did atomic move-into-place') + + this.__atomicDidStuff = true + if (this.__atomicChown) { + var uid = this.__atomicChown.uid + var gid = this.__atomicChown.gid + return fs.chown(this.__atomicTmp, uid, gid, function (er) { + if (er) return cb(er) + moveIntoPlace.call(this, cb) + }.bind(this)) + } else { + moveIntoPlace.call(this, cb) + } +} + +function moveIntoPlace (cb) { + fs.rename(this.__atomicTmp, this.__atomicTarget, cb) +} diff --git a/node_modules/fs-write-stream-atomic/package.json b/node_modules/fs-write-stream-atomic/package.json new file mode 100644 index 000000000..9c3babd5c --- /dev/null +++ b/node_modules/fs-write-stream-atomic/package.json @@ -0,0 +1,38 @@ +{ + "name": "fs-write-stream-atomic", + "version": "1.0.0", + "description": "Like `fs.createWriteStream(...)`, but atomic.", + "main": "index.js", + "directories": { + "test": "test" + }, + "dependencies": { + "graceful-fs": "^3.0.2" + }, + "devDependencies": { + "tap": "^0.4.12" + }, + "scripts": { + "test": "tap test/*.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/npm/fs-write-stream-atomic" + }, + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "ISC", + "bugs": { + "url": "https://github.com/npm/fs-write-stream-atomic/issues" + }, + "homepage": "https://github.com/npm/fs-write-stream-atomic", + "readme": "# fs-write-stream-atomic\n\nLike `fs.createWriteStream(...)`, but atomic.\n\nWrites to a tmp file and does an atomic `fs.rename` to move it into\nplace when it's done.\n\nFirst rule of debugging: **It's always a write condition.**\n\n## USAGE\n\n```javascript\nvar fsWriteStreamAtomic = require('fs-write-stream-atomic')\n// options are optional.\nvar write = fsWriteStreamAtomic('output.txt', options)\nvar read = fs.createReadStream('input.txt')\nread.pipe(write)\n\n// When the write stream emits a 'finish' or 'close' event,\n// you can be sure that it is moved into place, and contains\n// all the bytes that were written to it, even if something else\n// was writing to `output.txt` at the same time.\n```\n\n### `fsWriteStreamAtomic(filename, [options])`\n\n* `filename` {String} The file we want to write to\n* `options` {Object}\n * `chown` {Object} User and group to set ownership after write\n * `uid` {Number}\n * `gid` {Number}\n * `encoding` {String} default = 'utf8'\n * `mode` {Number} default = `0666`\n * `flags` {String} default = `'w'`\n\n", + "readmeFilename": "README.md", + "gitHead": "81a7c1e0f9dbcfc6e7a2e004389f362c7997566c", + "_id": "fs-write-stream-atomic@1.0.0", + "_shasum": "df22968876ac5163dce116790792cb3592d16930", + "_from": "fs-write-stream-atomic@*" +} diff --git a/node_modules/fs-write-stream-atomic/test/basic.js b/node_modules/fs-write-stream-atomic/test/basic.js new file mode 100644 index 000000000..c68c7315a --- /dev/null +++ b/node_modules/fs-write-stream-atomic/test/basic.js @@ -0,0 +1,78 @@ +var test = require('tap').test +var writeStream = require('../index.js') +var fs = require('fs') +var path = require('path') + +test('basic', function (t) { + // open 10 write streams to the same file. + // then write to each of them, and to the target + // and verify at the end that each of them does their thing + var target = path.resolve(__dirname, 'test.txt') + var n = 10 + + var streams = [] + for (var i = 0; i < n; i++) { + var s = writeStream(target) + s.on('finish', verifier) + streams.push(s) + } + + var verifierCalled = 0 + function verifier () { + if (++verifierCalled < n) return + // make sure that one of the atomic streams won. + var res = fs.readFileSync(target, 'utf8') + var lines = res.trim().split(/\n/) + lines.forEach(function (line) { + var first = lines[0].match(/\d+$/)[0] + var cur = line.match(/\d+$/)[0] + t.equal(cur, first) + }) + + var resExpr = /^first write \d+\nsecond write \d+\nthird write \d+\nfinal write \d+\n$/ + t.similar(res, resExpr) + t.end() + } + + // now write something to each stream. + streams.forEach(function (stream, i) { + stream.write('first write ' + i + '\n') + }) + + // wait a sec for those writes to go out. + setTimeout(function () { + // write something else to the target. + fs.writeFileSync(target, 'brutality!\n') + + // write some more stuff. + streams.forEach(function (stream, i) { + stream.write('second write ' + i + '\n') + }) + + setTimeout(function () { + // Oops! Deleted the file! + fs.unlinkSync(target) + + // write some more stuff. + streams.forEach(function (stream, i) { + stream.write('third write ' + i + '\n') + }) + + setTimeout(function () { + fs.writeFileSync(target, 'brutality TWO!\n') + streams.forEach(function (stream, i) { + stream.end('final write ' + i + '\n') + }) + }, 50) + }, 50) + }, 50) +}) + +test('cleanup', function (t) { + fs.readdirSync(__dirname).filter(function (f) { + return f.match(/^test.txt/) + }).forEach(function (file) { + fs.unlinkSync(path.resolve(__dirname, file)) + }) + t.end() +}) |