Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

error handling much better for missing templates

  • Loading branch information...
commit 71a73d43aea0c695b3c4ea85edaf110f5cf418ef 1 parent fa71faa
@malgorithms authored
View
4 lib/consts.js
@@ -8,4 +8,8 @@
exports.TAB_SPACES = 2;
+ exports.tweakables = {
+ MISSING_FILE_RECHECK: 1000
+ };
+
}).call(this);
View
108 lib/engine.js
@@ -1,11 +1,11 @@
// Generated by CoffeeScript 1.3.3
(function() {
- var engine, fs, path, states, util, utils, view,
+ var engine, fs, path, states, tweakables, util, utils, view, _ref,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
view = require('./view').view;
- states = require('./consts').states;
+ _ref = require('./consts'), states = _ref.states, tweakables = _ref.tweakables;
utils = require('./utils');
@@ -32,12 +32,13 @@
this.minimize = options.minimize || false;
this.prettyPrintErrors = options.prettyPrintErrors != null ? options.prettyPrintErrors : true;
this.viewCache = {};
+ this.fsErrorCache = {};
}
engine.prototype._log = function(o) {
- var _ref;
+ var _ref1;
if (this.verbose) {
- if ((_ref = typeof o) === "string" || _ref === "number" || _ref === "boolean") {
+ if ((_ref1 = typeof o) === "string" || _ref1 === "number" || _ref1 === "boolean") {
return console.log("toffee: " + o);
} else {
return console.log("toffee: " + (util.inspect(o)));
@@ -59,7 +60,7 @@
__toffee.autoEscape: if set as false, don't escape output of #{} vars by default
*/
- var err, k, layout_options, res, v, _ref, _ref1, _ref2, _ref3;
+ var err, k, layout_options, res, v, _ref1, _ref2, _ref3, _ref4;
if (options != null ? options.layout : void 0) {
layout_options = {};
for (k in options) {
@@ -69,15 +70,15 @@
}
}
}
- _ref = this.runSync(filename, options), err = _ref[0], res = _ref[1];
+ _ref1 = this.runSync(filename, options), err = _ref1[0], res = _ref1[1];
if (err && this.prettyPrintErrors) {
- _ref1 = [null, err], err = _ref1[0], res = _ref1[1];
+ _ref2 = [null, err], err = _ref2[0], res = _ref2[1];
}
if ((!err) && (layout_options != null)) {
layout_options.body = res;
- _ref2 = this.runSync(options.layout, layout_options), err = _ref2[0], res = _ref2[1];
+ _ref3 = this.runSync(options.layout, layout_options), err = _ref3[0], res = _ref3[1];
if (err && this.prettyPrintErrors) {
- _ref3 = [null, err], err = _ref3[0], res = _ref3[1];
+ _ref4 = [null, err], err = _ref4[0], res = _ref4[1];
}
}
return cb(err, res);
@@ -88,7 +89,7 @@
"options" the same as run() above
*/
- var err, pwd, realpath, res, start_time, v, _ref, _ref1,
+ var err, pwd, realpath, res, start_time, v, _ref1, _ref2,
_this = this;
start_time = Date.now();
options = options || {};
@@ -114,16 +115,16 @@
log: console.log
};
}
- _ref = v.run(options), err = _ref[0], res = _ref[1];
+ _ref1 = v.run(options), err = _ref1[0], res = _ref1[1];
} else {
- _ref1 = ["Couldn't load " + realpath, null], err = _ref1[0], res = _ref1[1];
+ _ref2 = ["Couldn't load " + realpath, null], err = _ref2[0], res = _ref2[1];
}
this._log("" + realpath + " run in " + (Date.now() - start_time) + "ms");
return [err, res];
};
engine.prototype._inlineInclude = function(filename, local_vars, parent_realpath, parent_options) {
- var err, k, options, res, v, _ref;
+ var err, k, options, res, v, _ref1;
options = local_vars || {};
options.__toffee = options.__toffee || {};
options.__toffee.dir = path.dirname(parent_realpath);
@@ -138,7 +139,7 @@
}
}
}
- _ref = this.runSync(filename, options), err = _ref[0], res = _ref[1];
+ _ref1 = this.runSync(filename, options), err = _ref1[0], res = _ref1[1];
return err || res;
};
@@ -163,14 +164,15 @@
};
engine.prototype._loadCacheAndMonitor = function(filename, options) {
- var txt, v, view_options, _ref;
+ var txt, v, view_options, _ref1;
try {
txt = fs.readFileSync(filename, 'utf8');
} catch (e) {
txt = "Error: Could not read " + filename;
- if (((_ref = options.__toffee) != null ? _ref.parent : void 0) != null) {
+ if (((_ref1 = options.__toffee) != null ? _ref1.parent : void 0) != null) {
txt += " requested in " + options.__toffee.parent;
}
+ this.fsErrorCache[filename] = Date.now();
}
view_options = {
fileName: filename,
@@ -184,31 +186,19 @@
return v;
};
- engine.prototype._monitorForChanges = function(filename, options) {
- /*
- we must continuously unwatch/rewatch because some editors/systems invoke a "rename"
- event and we'll end up following the wrong, old 'file' as a new one
- is dropped in its place.
- */
-
- var fsw,
- _this = this;
- fsw = null;
- return fsw = fs.watch(filename, {
- persistent: true
- }, function(change) {
- fsw.close();
- _this._log("Got an fs.watch hit on " + filename);
- return fs.readFile(filename, 'utf8', function(err, txt) {
- var v, view_options, _ref;
- _this._monitorForChanges(filename, options);
- if (txt !== _this.viewCache[filename].txt) {
- if (err) {
- txt = "Error: Could not read " + filename + " after fs.watch() hit.";
- if (((_ref = options.__toffee) != null ? _ref.parent : void 0) != null) {
- txt += " requested in " + options.__toffee.parent;
- }
+ engine.prototype._reloadFileInBkg = function(filename, options) {
+ var _this = this;
+ return fs.readFile(filename, 'utf8', function(err, txt) {
+ var v, view_options, _ref1;
+ if (err || (txt !== _this.viewCache[filename].txt)) {
+ if (err) {
+ _this.fsErrorCache[filename] = Date.now();
+ txt = "Error: Could not read " + filename;
+ if (((_ref1 = options.__toffee) != null ? _ref1.parent : void 0) != null) {
+ txt += " requested in " + options.__toffee.parent;
}
+ }
+ if (!(err && _this.viewCache[filename].fsError)) {
view_options = {
fileName: filename,
verbose: _this.verbose,
@@ -219,12 +209,48 @@
return _this.viewCache[filename] = v;
}
};
+ if (err) {
+ view_options.fsError = true;
+ }
return v = new view(txt, view_options);
}
- });
+ }
});
};
+ engine.prototype._monitorForChanges = function(filename, options) {
+ /*
+ we must continuously unwatch/rewatch because some editors/systems invoke a "rename"
+ event and we'll end up following the wrong, old 'file' as a new one
+ is dropped in its place.
+ */
+
+ var fsw,
+ _this = this;
+ if ((this.fsErrorCache[filename] != null) && ((Date.now() - this.fsErrorCache[filename]) > tweakables.MISSING_FILE_RECHECK)) {
+ delete this.fsErrorCache[filename];
+ this._reloadFileInBkg(filename, options);
+ return this._monitorForChanges(filename, options);
+ } else {
+ fsw = null;
+ try {
+ this._log("" + filename + " starting fs.watch()");
+ return fsw = fs.watch(filename, {
+ persistent: true
+ }, function(change) {
+ _this._log("" + filename + " closing fs.watch()");
+ fsw.close();
+ _this._monitorForChanges(filename, options);
+ return _this._reloadFileInBkg(filename, options);
+ });
+ } catch (e) {
+ return setTimeout((function() {
+ return _this._monitorForChanges(filename, options);
+ }), tweakables.MISSING_FILE_RECHECK);
+ }
+ }
+ };
+
return engine;
})();
View
1  lib/view.js
@@ -80,6 +80,7 @@
this.browserMode = options.browserMode || false;
this.minimize = options.minimize || false;
this.verbose = options.verbose || false;
+ this.fsError = options.fsError || false;
this.prettyPrintErrors = options.prettyPrintErrors != null ? options.prettyPrintErrors : true;
this.txt = txt;
this.tokenObj = null;
View
2  package.json
@@ -1,7 +1,7 @@
{
"name": "toffee",
"description": "An Express 3.x and 2.x templating language based on CoffeeScript with slicker tokens and syntax. Built with love at OkCupid.",
- "version": "0.0.45",
+ "version": "0.0.46",
"directories": {
"lib": "./lib"
},
View
5 src/consts.coffee
@@ -2,4 +2,7 @@ exports.states =
TOFFEE: 1
COFFEE: 2
-exports.TAB_SPACES = 2
+exports.TAB_SPACES = 2
+
+exports.tweakables =
+ MISSING_FILE_RECHECK: 1000 #ms
View
68 src/engine.coffee
@@ -1,9 +1,9 @@
-{view} = require './view'
-{states} = require './consts'
-utils = require './utils'
-fs = require 'fs'
-path = require 'path'
-util = require 'util'
+{view} = require './view'
+{states, tweakables} = require './consts'
+utils = require './utils'
+fs = require 'fs'
+path = require 'path'
+util = require 'util'
class engine
@@ -12,7 +12,8 @@ class engine
@verbose = options.verbose or false
@minimize = options.minimize or false
@prettyPrintErrors = if options.prettyPrintErrors? then options.prettyPrintErrors else true
- @viewCache = {} # filename
+ @viewCache = {} # filename -> view
+ @fsErrorCache = {} # filename -> timestamp last failed
_log: (o) ->
if @verbose
@@ -117,32 +118,25 @@ class engine
catch e
txt = "Error: Could not read #{filename}"
if options.__toffee?.parent? then txt += " requested in #{options.__toffee.parent}"
- view_options =
+ @fsErrorCache[filename] = Date.now()
+ view_options =
fileName: filename
verbose: @verbose
prettyPrintErrors: @prettyPrintErrors
minimize: @minimize
- v = new view txt, view_options
+ v = new view txt, view_options
@viewCache[filename] = v
@_monitorForChanges filename, options
v
- _monitorForChanges: (filename, options) ->
- ###
- we must continuously unwatch/rewatch because some editors/systems invoke a "rename"
- event and we'll end up following the wrong, old 'file' as a new one
- is dropped in its place.
- ###
- fsw = null
- fsw = fs.watch filename, {persistent: true}, (change) =>
- fsw.close()
- @_log "Got an fs.watch hit on #{filename}"
- fs.readFile filename, 'utf8', (err, txt) =>
- @_monitorForChanges filename, options
- if txt isnt @viewCache[filename].txt
- if err
- txt = "Error: Could not read #{filename} after fs.watch() hit."
- if options.__toffee?.parent? then txt += " requested in #{options.__toffee.parent}"
+ _reloadFileInBkg: (filename, options) ->
+ fs.readFile filename, 'utf8', (err, txt) =>
+ if err or (txt isnt @viewCache[filename].txt)
+ if err
+ @fsErrorCache[filename] = Date.now()
+ txt = "Error: Could not read #{filename}"
+ if options.__toffee?.parent? then txt += " requested in #{options.__toffee.parent}"
+ if not (err and @viewCache[filename].fsError) # i.e., don't just create a new error view
view_options =
fileName: filename
verbose: @verbose
@@ -151,6 +145,30 @@ class engine
cb: (v) =>
@_log "#{filename} updated and ready"
@viewCache[filename] = v
+ if err
+ view_options.fsError = true
v = new view txt, view_options
+ _monitorForChanges: (filename, options) ->
+ ###
+ we must continuously unwatch/rewatch because some editors/systems invoke a "rename"
+ event and we'll end up following the wrong, old 'file' as a new one
+ is dropped in its place.
+ ###
+ if @fsErrorCache[filename]? and ((Date.now() - @fsErrorCache[filename]) > tweakables.MISSING_FILE_RECHECK)
+ delete @fsErrorCache[filename]
+ @_reloadFileInBkg filename, options
+ @_monitorForChanges filename, options
+ else
+ fsw = null
+ try
+ @_log "#{filename} starting fs.watch()"
+ fsw = fs.watch filename, {persistent: true}, (change) =>
+ @_log "#{filename} closing fs.watch()"
+ fsw.close()
+ @_monitorForChanges filename, options
+ @_reloadFileInBkg filename, options
+ catch e
+ setTimeout (=> @_monitorForChanges filename, options), tweakables.MISSING_FILE_RECHECK
+
exports.engine = engine
View
1  src/view.coffee
@@ -157,6 +157,7 @@ class view
@browserMode = options.browserMode or false
@minimize = options.minimize or false # excludes line numbers from coffee ; uses uglify.JS
@verbose = options.verbose or false
+ @fsError = options.fsError or false # pass true if you could not load the view template and passed in error text
@prettyPrintErrors = if options.prettyPrintErrors? then options.prettyPrintErrors else true
@txt = txt
@tokenObj = null # constructed as needed
Please sign in to comment.
Something went wrong with that request. Please try again.