Permalink
Browse files

Fix async writer interface, fixes #38

  • Loading branch information...
1 parent 889a5e2 commit dbe358220ea72b46275889f9010256f51c2db357 @gotwarlost committed Mar 31, 2013
Showing with 210 additions and 86 deletions.
  1. +5 −4 lib/report/cobertura.js
  2. +4 −4 lib/report/html.js
  3. +2 −2 lib/report/lcovonly.js
  4. +110 −44 lib/util/file-writer.js
  5. +56 −0 lib/util/writer.js
  6. +33 −32 test/other/test-file-writer.js
View
@@ -5,7 +5,7 @@
var path = require('path'),
Report = require('./index'),
- Writer = require('../util/file-writer'),
+ FileWriter = require('../util/file-writer'),
TreeSummarizer = require('../util/tree-summarizer'),
utils = require('../object-utils');
@@ -29,6 +29,7 @@ function CoberturaReport(opts) {
opts = opts || {};
this.dir = opts.dir || process.cwd();
this.file = opts.file || 'cobertura-coverage.xml';
+ this.opts = opts;
}
CoberturaReport.TYPE = 'cobertura';
@@ -168,7 +169,7 @@ Report.mix(CoberturaReport, {
writeReport: function (collector, sync) {
var summarizer = new TreeSummarizer(),
outputFile = path.join(this.dir, this.file),
- writer = new Writer(sync),
+ writer = this.opts.writer || new FileWriter(sync),
tree,
root;
@@ -177,8 +178,8 @@ Report.mix(CoberturaReport, {
});
tree = summarizer.getTreeSummary();
root = tree.root;
- writer.writeFile(outputFile, function () {
- walk(root, collector, writer, 0);
+ writer.writeFile(outputFile, function (contentWriter) {
+ walk(root, collector, contentWriter, 0);
});
}
});
View
@@ -422,17 +422,17 @@ Report.mix(HtmlReport, {
indexFile = path.resolve(dir, 'index.html'),
childFile;
if (this.opts.verbose) { console.error('Writing ' + indexFile); }
- writer.writeFile(indexFile, function () {
- that.writeIndexPage(writer, node);
+ writer.writeFile(indexFile, function (contentWriter) {
+ that.writeIndexPage(contentWriter, node);
});
node.children.forEach(function (child) {
if (child.kind === 'dir') {
that.writeFiles(writer, child, path.resolve(dir, child.relativeName), collector);
} else {
childFile = path.resolve(dir, child.relativeName + '.html');
if (that.opts.verbose) { console.error('Writing ' + childFile); }
- writer.writeFile(childFile, function () {
- that.writeDetailPage(writer, child, collector.fileCoverageFor(child.fullPath()));
+ writer.writeFile(childFile, function (contentWriter) {
+ that.writeDetailPage(contentWriter, child, collector.fileCoverageFor(child.fullPath()));
});
}
});
View
@@ -82,9 +82,9 @@ Report.mix(LcovOnlyReport, {
var outputFile = path.resolve(this.opts.dir, 'lcov.info'),
writer = this.opts.writer || new Writer(sync),
that = this;
- writer.writeFile(outputFile, function () {
+ writer.writeFile(outputFile, function (contentWriter) {
collector.files().forEach(function (key) {
- that.writeFileCoverage(writer, collector.fileCoverageFor(key));
+ that.writeFileCoverage(contentWriter, collector.fileCoverageFor(key));
});
});
}
View
@@ -4,62 +4,128 @@
*/
var path = require('path'),
+ util = require('util'),
fs = require('fs'),
- mkdirp = require('mkdirp');
+ async = require('async'),
+ mkdirp = require('mkdirp'),
+ writer = require('./writer'),
+ Writer = writer.Writer,
+ ContentWriter = writer.ContentWriter;
-function FileWriter(sync) {
- this.writing = false;
- this.start = sync ? this.startSync : this.startAsync;
- this.write = sync ? this.writeSync : this.writeAsync;
- this.end = sync ? this.endSync : this.endAsync;
+function extend(cons, proto) {
+ Object.keys(proto).forEach(function (k) {
+ cons.prototype[k] = proto[k];
+ });
}
-FileWriter.prototype = {
- copyFile: function (source, dest) {
- mkdirp.sync(path.dirname(dest));
- fs.writeFileSync(dest, fs.readFileSync(source));
+function BufferedContentWriter() {
+ ContentWriter.call(this);
+ this.content = '';
+}
+util.inherits(BufferedContentWriter, ContentWriter);
+
+extend(BufferedContentWriter, {
+ write: function (str) {
+ this.content += str;
},
+ getContent: function () {
+ return this.content;
+ }
+});
+
+function StreamContentWriter(stream) {
+ ContentWriter.call(this);
+ this.stream = stream;
+}
+util.inherits(StreamContentWriter, ContentWriter);
+
+extend(StreamContentWriter, {
+ write: function (str) {
+ this.stream.write(str);
+ }
+});
+
+function SyncFileWriter() {
+ Writer.call(this);
+}
+util.inherits(SyncFileWriter, Writer);
+
+extend(SyncFileWriter, {
writeFile: function (file, callback) {
- this.start(file);
- callback(this);
- this.end();
- },
- println: function (str) {
- this.write(str);
- this.write('\n');
- },
- startSync: function (fileName) {
- this.doStart(fileName);
- this.contents = '';
- this.filename = fileName;
- },
- writeSync: function (str) {
- this.contents += str;
+ mkdirp.sync(path.dirname(file));
+ var cw = new BufferedContentWriter();
+ callback(cw);
+ fs.writeFileSync(file, cw.getContent(), 'utf8');
},
- endSync: function () {
- this.doEnd();
- fs.writeFileSync(this.filename, this.contents, 'utf8');
+ done: function () {
+ this.emit('done'); //everything already done
+ }
+});
+
+function AsyncFileWriter() {
+ this.queue = async.queue(this.processFile.bind(this), 20);
+ this.openFileMap = {};
+}
+
+util.inherits(AsyncFileWriter, Writer);
+
+extend(AsyncFileWriter, {
+ writeFile: function (file, callback) {
+ this.openFileMap[file] = true;
+ this.queue.push({ file: file, callback: callback });
},
- startAsync: function (fileName) {
- this.doStart(fileName);
- this.stream = fs.createWriteStream(fileName);
+ processFile: function (task, cb) {
+ var file = task.file,
+ userCallback = task.callback,
+ that = this,
+ stream,
+ contentWriter;
+
+ mkdirp.sync(path.dirname(file));
+ stream = fs.createWriteStream(file);
+ stream.on('close', function () {
+ delete that.openFileMap[file];
+ cb();
+ that.checkDone();
+ });
+ stream.on('error', function (err) { that.emit('error', err); });
+ contentWriter = new StreamContentWriter(stream);
+ userCallback(contentWriter);
+ stream.end();
},
- writeAsync: function (str) {
- this.stream.write(str);
+ done: function () {
+ this.doneCalled = true;
+ this.checkDone();
},
- endAsync: function () {
- this.doEnd();
- this.stream.end();
+ checkDone: function () {
+ if (!this.doneCalled) { return; }
+ if (Object.keys(this.openFileMap).length === 0) {
+ this.emit('done');
+ }
+ }
+});
+
+function FileWriter(sync) {
+ Writer.call(this);
+ var that = this;
+ this.delegate = sync ? new SyncFileWriter() : new AsyncFileWriter();
+ this.delegate.on('error', function (err) { that.emit('error', err); });
+ this.delegate.on('done', function () { that.emit('done'); });
+}
+
+util.inherits(FileWriter, Writer);
+
+extend(FileWriter, {
+ copyFile: function (source, dest) {
+ mkdirp.sync(path.dirname(dest));
+ fs.writeFileSync(dest, fs.readFileSync(source));
},
- doStart: function (fileName) {
- if (this.writing) { throw new Error('Attempt to start a new file before ending the previous one'); }
- this.writing = true;
- mkdirp.sync(path.dirname(fileName));
+ writeFile: function (file, callback) {
+ this.delegate.writeFile(file, callback);
},
- doEnd : function () {
- if (!this.writing) { throw new Error('Attempt to end a file without starting it'); }
- this.writing = false;
+ done: function () {
+ this.delegate.done();
}
-};
+});
module.exports = FileWriter;
View
@@ -0,0 +1,56 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var util = require('util'),
+ EventEmitter = require('events').EventEmitter;
+
+function extend(cons, proto) {
+ Object.keys(proto).forEach(function (k) {
+ cons.prototype[k] = proto[k];
+ });
+}
+
+//abstract interface for writing content
+function ContentWriter() {
+}
+
+ContentWriter.prototype = {
+ write: function (/* str */) { throw new Error('write: must be overridden'); },
+ println: function (str) { this.write(str); this.write('\n'); }
+};
+
+//abstract interface for writing files and assets
+function Writer() {
+ EventEmitter.call(this);
+}
+
+util.inherits(Writer, EventEmitter);
+
+extend(Writer, {
+ /**
+ * allows writing content to a file using a callback that is passed a content writer
+ * @param file the name of the file to write
+ * @param callback the callback that is called as `callback(contentWriter)`
+ */
+ writeFile: function (/* file, callback */) { throw new Error('writeFile: must be overridden'); },
+ /**
+ * copies a file from source to destination
+ * @param source the file to copy, found on the file system
+ * @param dest the destination path
+ */
+ copyFile: function (/* source, dest */) { throw new Error('copyFile: must be overridden'); },
+ /**
+ * marker method to indicate that the caller is done with this writer object
+ * The writer is expected to emit a `done` event only after this method is called
+ * and it is truly done.
+ */
+ done: function () { throw new Error('done: must be overridden'); }
+});
+
+module.exports = {
+ Writer: Writer,
+ ContentWriter: ContentWriter
+};
+
Oops, something went wrong.

0 comments on commit dbe3582

Please sign in to comment.