MethodChain

MethodChain provides a mechanism for storing a sequence of method calls and then executing that sequence on any object you like.

// In the browser
JS.require('JS.MethodChain', function(MethodChain) { ... });

// In CommonJS
var MethodChain = require('jsclass/src/method_chain').MethodChain;

Here’s a quick example:

var chain = new MethodChain();

chain.map(function(s) { return s.toUpperCase() })
     .join(', ')
     .replace(/[aeiou]/ig, '_');

chain.__exec__(['foo', 'bar'])    // -> "F__, B_R"

Here, we create a new MethodChain object called chain. We then call three methods on it, map(), join() and replace(). chain remembers the names of these methods and the arguments you used, then calls the stored chain on any object you pass to its __exec__() method.

If you call further methods on chain, they get added to the list:

chain.split(/_+/);

chain.__exec__(['foo', 'bar'])    // -> ["F", ", B", "R"]

How it works

When you call a method like map() on a MethodChain instance, the instance stores the name of the method and any arguments you pass to it. All of chain’s methods return chain again, allowing you to chain methods together as shown above.

Any method you want to store in a chain has to exist in MethodChain’s list of method names in advance (my kingdom for a method_missing feature in JavaScript!), or you will get an error. MethodChain comes with over 300 method names in place from the JavaScript core API to get you started. If you find that a method is missing, you can add it like so:

// Add all methods from a class or object
MethodChain.addMethods(jQuery);

// Add methods by name
MethodChain.addMethods(['methodName1', 'someOtherMethod']);

All MethodChain instances will then have the methods you’ve added.

There are a few reserved methods on MethodChain objects that you cannot use for chaining:

Changing scope using __()

All chains have a method called __() (that’s two underscores) that you can use to change the scope of the chain. By default, each method in the chain is called on the return value of the previous method, but __() lets you insert objects into the chain so that subsequent methods get called on said object.

__() also lets you insert anonymous functions into the chain. Within each function, this refers to the return value of the previous method. Putting all this together, you could do:

var chain = new MethodChain();

chain.__(document).getElementsByTagName('p')
     .__(function() { console.log(this) });

When we execute chain, the net effect is the same as:

console.log(document.getElementsByTagName('p'));

If you insert a function into the chain, its return value will be used as the receiving object for the next method call in the chain.

toFunction()

The toFunction method returns a function that executes the chain against its argument. For example, taking the first chain we created at the top of this page:

var func = chain.toFunction();
func(['foo', 'bar'])    // -> "F__, B_R"

The wait() method

MethodChain adds a method called wait() as an instance method and a class method to all classes and objects created with jsclass. This method allows you to delay execution of a chain against an object for a given about of time.

var Fiddle = new Class({
    initialize: function(quantity) {
        this.size = quantity;
    },

    proclaim: function(thing) {
        console.log('I have ' + this.size + ' ' + thing + 's!');
    }
});

var fid = new Fiddle(99);

// Call proclaim() after 5 seconds
// prints "I have 99 problems!" 
fid.wait(5).proclaim('problem');

Further reading

For some background and further usage examples, refer to the articles on ChainCollector on my blog. ChainCollector is the name I originally gave the MethodChain class.