Skip to content

Loading…

Debugability implemented -with tests. #1807

Closed
wants to merge 1 commit into from

3 participants

@jpka

I make this PR as part of an effort at work to improve Backbone for our own purposes. We thought of contributing it because we think it will add to the whole of the community even though it is minimal and not everyone will make use of it. And also because it will be great to have feedback from the core Backbone contributors and users. We got this idea of adding debugability-friendly features after our experience with Ember (we are in the process of researching and prototyping with every promising frontend framework we can find).

Basically, it makes use of named constructors and toString() to print out information about Backbone components, its subclasses and its instances while debugging. Also it adds a uid to each of the components to show in the toString().
See the test file for details.

I get that this may not be something that everyone will see the point of, but I don't think it hurts anyone either.

@wookiehangover wookiehangover commented on the diff
backbone.js
@@ -1443,6 +1448,8 @@
// by us to simply call the parent's constructor.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
+ } else if (protoProps && _.has(protoProps, 'name')) {
+ eval("child = function " + protoProps.name + "(){ parent.apply(this, arguments); };");
@wookiehangover Collaborator

:suspect:

@caseywebdev Collaborator

Is that your eval face?

@wookiehangover Collaborator

@caseywebdev probably more like :rage4:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@wookiehangover
Collaborator

@jpka thanks for opening a pull request.

Interesting concept, but unfortunately I don't think this is something that Backbone should include. It's easy enough to patch this type of behavior in if you really need it, and anyway you should really be using a breakpoint debugger if you're looking for detailed call stack information (like the ones in Chrome Dev Tools, Firebug, IE8+).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 9, 2012
  1. @jpka
This page is out of date. Refresh to see the latest.
Showing with 104 additions and 2 deletions.
  1. +32 −2 backbone.js
  2. +71 −0 test/debugging.js
  3. +1 −0 test/index.html
View
34 backbone.js
@@ -188,6 +188,7 @@
this.attributes = {};
this._escapedAttributes = {};
this.cid = _.uniqueId('c');
+ this.uid = _.uniqueId();
this.changed = {};
this._changes = {};
this._pending = {};
@@ -572,6 +573,7 @@
if (options.parse) models = this.parse(models);
this.reset(models, {silent: true, parse: options.parse});
}
+ this.uid = _.uniqueId();
};
// Define the Collection's inheritable methods.
@@ -903,6 +905,7 @@
if (options.routes) this.routes = options.routes;
this._bindRoutes();
this.initialize.apply(this, arguments);
+ this.uid = _.uniqueId();
};
// Cached regular expressions for matching named param parts and splatted
@@ -986,6 +989,7 @@
this.location = window.location;
this.history = window.history;
}
+ this.uid = _.uniqueId();
};
// Cached regex for cleaning leading hashes and slashes.
@@ -1194,6 +1198,7 @@
// if an existing element is not provided...
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
+ this.uid = _.uniqueId();
this._configure(options || {});
this._ensureElement();
this.initialize.apply(this, arguments);
@@ -1443,6 +1448,8 @@
// by us to simply call the parent's constructor.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
+ } else if (protoProps && _.has(protoProps, 'name')) {
+ eval("child = function " + protoProps.name + "(){ parent.apply(this, arguments); };");
@wookiehangover Collaborator

:suspect:

@caseywebdev Collaborator

Is that your eval face?

@wookiehangover Collaborator

@caseywebdev probably more like :rage4:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
} else {
child = function(){ parent.apply(this, arguments); };
}
@@ -1464,11 +1471,34 @@
// later.
child.__super__ = parent.prototype;
+ // Set an informative toString for proper debugging
+ child.toString = function() {
+ return "subclass of " + (parent.name !== "" ? parent.name : parent.toString());
+ };
+
return child;
};
- // Set up inheritance for the model, collection, router, view and history.
- Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
+ // For each extendable component
+ _.each(["Model", "Collection", "Router", "View", "History"], function(component) {
+ var Component = Backbone[component];
+
+ // Set an informative toString
+ Component.toString = function() {
+ return "Backbone." + component;
+ };
+
+ // Set an informative toString to instances
+ Component.prototype.toString = function() {
+ var constructor = this.constructor,
+ parent = constructor.name !== "" ? constructor.name : constructor.toString();
+
+ return "<" + parent + ":" + this.uid + ">";
+ };
+
+ // Set up inheritance
+ Component.extend = extend;
+ });
// Throw an error when a URL is needed, and none is supplied.
var urlError = function() {
View
71 test/debugging.js
@@ -0,0 +1,71 @@
+$(document).ready(function() {
+
+ var components = ["View", "Model", "Collection", "Router", "History"];
+
+ module("Debugging");
+
+ test("Backbone components have an informative toString()", 5, function() {
+ _.each(components, function(component) {
+ equal(Backbone[component].toString(), "Backbone." + component);
+ });
+ });
+
+ test("Subclasses permit name setting", 1, function() {
+ var sc = Backbone.Model.extend({
+ name: "Person"
+ });
+
+ equal(sc.name, "Person");
+ });
+
+ test("Subclasses of Backbone components have an informative toString()", 5, function() {
+ _.each(components, function(component) {
+ equal(Backbone[component].extend({}).toString(), "subclass of Backbone." + component);
+ });
+ });
+
+ test("Subclasses of anonymous subclasses declare their whole line of inheritance up to the Backbone component", 5, function() {
+ _.each(components, function(component) {
+ equal(Backbone[component].extend({}).extend({}).toString(), "subclass of subclass of Backbone." + component);
+ });
+ });
+
+ test("Subclasses of named subclasses declare their parent by name", 5, function() {
+ _.each(components, function(component) {
+ equal(Backbone[component].extend({name: "Person"}).extend({}).toString(), "subclass of Person");
+ });
+ });
+
+ test("Instances have a unique id", 5, function() {
+ _.each(components, function(component) {
+ ok(new Backbone[component]().uid);
+ });
+ });
+
+ test("Instances of Backbone components have an informative toString()", 5, function() {
+ _.each(components, function(component) {
+ var instance = new Backbone[component]();
+
+ equal(instance.toString(), "<Backbone." + component + ":" + instance.uid + ">");
+ });
+ });
+
+ test("Instances of unnamed subclasses have an informative toString()", 5, function() {
+ _.each(components, function(component) {
+ var Sc = Backbone[component].extend({}),
+ instance = new Sc();
+
+ equal(instance.toString(), "<" + Sc.toString() + ":" + instance.uid + ">");
+ });
+ });
+
+ test("Instances of named subclasses have an informative toString()", 5, function() {
+ _.each(components, function(component) {
+ var Sc = Backbone[component].extend({name: "Person"}),
+ instance = new Sc();
+
+ equal(instance.toString(), "<Person:" + instance.uid + ">");
+ });
+ });
+
+});
View
1 test/index.html
@@ -20,6 +20,7 @@
<script src="view.js"></script>
<script src="sync.js"></script>
<script src="speed.js"></script>
+ <script src="debugging.js"></script>
</head>
<body>
<div id="qunit"></div>
Something went wrong with that request. Please try again.