Skip to content

Instantly share code, notes, and snippets.

@jasonkarns
Last active December 21, 2015 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonkarns/6284853 to your computer and use it in GitHub Desktop.
Save jasonkarns/6284853 to your computer and use it in GitHub Desktop.
Since I can't put markdown here in the description... read the problem.md

Situation

We want to monkey patch (wrap, really) core Backbone objects. Let's use Backbone.Router for this example.

Setup

The original Backbone.Router is wrapped as shown in duck_punch.js.

  1. The constructor function (Backbone.Router) is replaced with ours (which runs the console profiler around the invocation of the original function). L4-9
  2. Since we've overwritten Backbone.Router, we restore its prototype to be the original's prototype. L12
  3. We also need to restore any 'static' methods that existed directly on the original. L15-18

JavaScript loves us

This works great in JS (or even CS) when the app routers are created using Backbone.Router.extend.

CoffeeScript hates us

However, when using CS extends (AppRouter extends Backbone.Router), our wrapped constructor is never invoked. This is due to fact that CS generates:

function AppRouter(){
  _ref = AppRouter.__super__.constructor.apply(this, arguments);
  return _ref;
}

where __super__ is Backbone.Router.prototype. Since it is invoking Backbone.Router.prototype.constructor, and not Backbone.Router, our patched implementation is never invoked! #sadface

// given Backbone has loaded, let's profile all the views
(function(yeOldeBackboneRouter){
window.Backbone.Router = function(){
console.profile();
var instance = yeOldeBackboneRouter.apply(this, arguments);
console.profileEnd();
return instance;
};
// restore prototype
window.Backbone.Router.prototype = yeOldeBackboneRouter.prototype;
// restore own methods
var methods = Object.keys(yeOldeBackboneRouter);
for ( m in methods ) {
window.Backbone.Router[methods[m]] = yeOldeBackboneRouter[methods[m]];
}
})(Backbone.Router);
class Workspace extends Backbone.Router
# Workspace = Backbone.Router.extend
routes:
'*filter': 'setFilter'
setFilter: (param) ->
app.TodoFilter = param || ''
app.todos.trigger('filter')
app.TodoRouter = new Workspace()
# this flavor of the Router is NOT profiled.
# The CoffeeScript __extends implementation creates a new object as the
# Router instance and then replaces the constructor function with one of its own.
# The new constructor function invokes the Backbone.Router.prototype's constructor function.
# `_ref = Workspace.__super__.constructor.apply(this, arguments);`
# This means our replacement Backbone.Router constructor function (with the profiling bookends) is never invoked.
# class Workspace extends Backbone.Router
Workspace = Backbone.Router.extend
routes:
'*filter': 'setFilter'
setFilter: (param) ->
app.TodoFilter = param || ''
app.todos.trigger('filter')
app.TodoRouter = new Workspace()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment