Skip to content

Commit

Permalink
Catch errors triggered by the inspector
Browse files Browse the repository at this point in the history
Ember Inspector caused errors are now caught in production
and logged as warnings instead of being thrown.
  • Loading branch information
teddyzeenny committed Apr 12, 2015
1 parent b8f6226 commit 1f7ac6a
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Brocfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ emberDebug = concatFiles(emberDebug, {
});

emberDebug = wrapFiles(emberDebug, {
wrapper: ["(function(adapter) {\n", "\n}('" + (dist || 'basic') + "'));"]
wrapper: ["(function(adapter, env) {\n", "\n}('" + (dist || 'basic') + "', '" + env + "'));"]
});

var tree = app.toTree();
Expand Down
54 changes: 51 additions & 3 deletions ember_debug/adapters/basic.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* globals requireModule */
var Ember = window.Ember;
var computed = Ember.computed;
var $ = Ember.$;
Expand All @@ -12,12 +13,32 @@ export default Ember.Object.extend({
}, null, 'ember-inspector');
},

/**
* Uses the current build's config module to determine
* the environment.
*
* @property environment
* @type {String}
*/
environment: computed(function() {
return requireModule('ember-debug/config')['default'].environment;
}),

debug: function() {
console.debug.apply(console, arguments);
return console.debug.apply(console, arguments);
},

log: function() {
console.log.apply(console, arguments);
return console.log.apply(console, arguments);
},

/**
* A wrapper for `console.warn`.
*
* @method warn
*/
warn: function() {
return console.warn.apply(console, arguments);
},

/**
Expand Down Expand Up @@ -58,6 +79,34 @@ export default Ember.Object.extend({
});
},

/**
* Handle an error caused by EmberDebug.
*
* This function rethrows in development and test envs,
* but warns instead in production.
*
* The idea is to control errors triggered by the inspector
* and make sure that users don't get mislead by inspector-caused
* bugs.
*
* @method handleError
* @param {Error} error
*/
handleError: function(error) {
if (this.get('environment') === 'production') {
if (error && error instanceof Error) {
error = 'Error message: ' + error.message + '\nStack trace: ' + error.stack;
}
this.warn('Ember Inspector has errored.\n' +
'This is likely a bug in the inspector itself.\n' +
'You can report bugs at https://github.com/emberjs/ember-inspector.\n' +
error);
} else {
this.warn('EmberDebug has errored:');
throw error;
}
},

/**
A promise that resolves when the connection
Expand Down Expand Up @@ -110,5 +159,4 @@ export default Ember.Object.extend({
messages.clear();
this._isReady = true;
}

});
38 changes: 37 additions & 1 deletion ember_debug/port.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var Ember = window.Ember;
var oneWay = Ember.computed.oneWay;
var guidFor = Ember.guidFor;
var run = Ember.run;

export default Ember.Object.extend(Ember.Evented, {
adapter: oneWay('namespace.adapter').readOnly(),
Expand All @@ -15,14 +16,49 @@ export default Ember.Object.extend(Ember.Evented, {
var self = this;
this.get('adapter').onMessageReceived(function(message) {
if (self.get('uniqueId') === message.applicationId || !message.applicationId) {
self.trigger(message.type, message);
self.messageReceived(message.type, message);
}
});
},

messageReceived: function(name, message) {
var self = this;
this.wrap(function() {
self.trigger(name, message);
});
},

send: function(messageType, options) {
options.type = messageType;
options.from = 'inspectedWindow';
options.applicationId = this.get('uniqueId');
this.get('adapter').send(options);
},

/**
* Wrap all code triggered from outside of
* EmberDebug with this method.
*
* `wrap` is called by default
* on all callbacks triggered by `port`,
* so no need to call it in this case.
*
* - Wraps a callback in `Ember.run`.
* - Catches all errors during production
* and displays them in a user friendly manner.
*
* @method wrap
* @param {Function} fn
* @return {Mixed} The return value of the passed function
*/
wrap: function(fn) {
return run(this, function() {
try {
return fn();
} catch (error) {
this.get('adapter').handleError(error);
}
});
}
});

13 changes: 12 additions & 1 deletion ember_debug/vendor/startup-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
Also responsible for sending the first tree.
**/

/* globals Ember, adapter, requireModule */
/* globals Ember, adapter, env, requireModule */

var currentAdapter = 'basic';
if (typeof adapter !== 'undefined') {
currentAdapter = adapter;
}
var currentEnv = 'production';
if (typeof env !== 'undefined') {
currentEnv = env;
}

(function(adapter) {
onEmberReady(function() {
Expand All @@ -24,6 +28,13 @@ if (typeof adapter !== 'undefined') {
}
// prevent from injecting twice
if (!Ember.Debug) {
define('ember-debug/config', function() {
return {
default: {
environment: env
}
};
});
window.EmberInspector = Ember.Debug = requireModule('ember-debug/main')['default'];
Ember.Debug.Adapter = requireModule('ember-debug/adapters/' + adapter)['default'];

Expand Down
26 changes: 25 additions & 1 deletion tests/ember_debug/ember_debug_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import Ember from "ember";
import { module, test } from 'qunit';

var EmberDebug;
var port, name, message;
var port, name, message, adapter;
/* jshint ignore:start */
var run = Ember.run;
var App;
var EmberInspector;

function setupApp() {
App = Ember.Application.create();
App.setupForTesting();
App.injectTestHelpers();

}

module("Ember Debug", {
Expand All @@ -33,6 +36,7 @@ module("Ember Debug", {
EmberDebug.start();
EmberInspector = EmberDebug;
port = EmberDebug.port;
adapter = EmberDebug.get('port.adapter');
},
afterEach() {
name = null;
Expand Down Expand Up @@ -63,3 +67,23 @@ test("EmberInspector#inspect sends inspectable objects", function(assert) {
cantSend("a", assert);
cantSend(null, assert);
});

test("Errors are caught and handled by EmberDebug", async function t(assert) {
assert.expect(1);
const error = new Error('test error');
port.on('test:errors', () => {
throw error;
});

const handleError = adapter.handleError;
adapter.reopen({
handleError(e) {
assert.equal(e, error, 'Error handled');
}
});

port.messageReceived('test:errors', {});

await wait();
adapter.reopen({ handleError });
});

0 comments on commit 1f7ac6a

Please sign in to comment.