Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
351 lines (231 sloc) 7.09 KB


Weave the object fabric of your app together with methods for constructing, merging, appending, and mutating objects and their methods.

LoomJS is a simple and powerful four method API for differential inheritance and AOP. (Mostly taken from SnackJS.)

Environments & Installation


Install with npm:

$ npm install loom

Require like anything else:

var loom = require('loom')

Ender - Client-Side

Integrates with Ender. When building with Ender, the global loom object is returned to its rightful owner and the methods are assigned to the Ender global (usually $). So loom.punch becomes $.punch.

$ ender build loom


You can simply include src/loom.js into your web page.

License & Copyright

(c) Ryan Florence, MIT License



Mutates an object method (duck punching, AOP) or simply connects one function to another.


loom.punch(obj, method, fn [, auto])


  1. obj (object) - The object who's method is getting punched.

  2. method (string) - The name of the object's method.

  3. fn (function) - The new function. Signature function ([old [, argN]])


    1. old (function) - The old version of the method, (with context already bound to the object). Note: This argument is passed by default, if the auto argument is used then this argument will not be passed
    2. argN (mixed) - Any arguments passed when the method is called.
  4. auto (boolean: optional, defaults to false) - If true, will automatically call the old method before calling the new one.




var widget = {
  a: 0,
  b: 0,
  add: function (amt){
    this.a += amt

widget.a //> 1
widget.b //> 0

loom.punch(widget, 'add', function (old, amt){
  // old is the old version of this method, already bound to widget
  // call it wherever you want inside of here
  this.b += amt

widget.a //> 2
widget.b //> 1

loom.punch(widget, 'add', function (){
  // the last arg (`auto`) is `true`, so old will be called before
  // this stuff happens.  Effectively the same as calling `old`
  // on the first line of this new function.
  console.log("I'm adding stuff!")
}, true)

widget.add(1) // console logs "I'm adding stuff!"
widget.a //> 3
widget.b //> 2

loom.punch(widget, 'add', function (old){
  return this.a + this.b
  // since `auto` is not true, and old is not called
  // this method has been completely overwritten

var res = widget.add()
res //> 5
widget.a //> 3 ... unchanged
widget.b //> 2 ... unchanged


When using the auto argument, this is extremely similar to dojo.connect (minus the DOM events). It's also not unlike Extends and this.parent in a MooTools class. Think of the old argument as this.parent.


Similar to Object.create, in that it sets the prototype of an object to another. It also allows for an extension object to merge into the new object. When methods are redefined in the extension object, they are automatically punched providing a reference to the old (or parent) method. Useful for object templating and prototypal inheritance.


loom.construct(proto [, ext [, mixins]])


  1. proto (object) - The object use as the prototype.
  2. ext (object: optional) - An object to merge into the new object. Note: if a method is redefined then it will be automatically punched, providing as the first argument of the method the old method. Please see the examples.
  3. mixins (array: optional) - An array of objects to append to the new object.


Object - A new object with proto as its prototype, ext merged into it, and mixins appended to it.

// construct a generic object
var point = {
  coords: {
    x: 0,
    y: 0

  translate: function (x, y){
    this.coords.x += x
    this.coords.y += y

  sum: function (){
    var sum = 0
    for (key in this.coords)
      sum += this.coords[key]
    return sum

// construct a generic object that inherits from the other
var point3d = loom.construct(point, {
  // new property
  z: 0,
  // redefined method
  translate: function (old, x, y, z){
    // if a method exists in the prototype object
    // then the method here is automatically punched
    // providing the old method as the first argument
    this.coords.z += z

// construct objects that inherit from the generic objects (instances, if you will)
var p = loom.construct(point)
p.coords.x //> 5
p.coords.y //> 10
p.sum() //> 15
p.__proto__ === point //> true

var p3d = loom.construct(point3d)
p3d.translate(1,2,3) // punched method, note that the signature does not
                     // include the `old` argument when being called
p.coords //> {x: 1, y: 2, z: 3}
p.sum() //> 6, inherited
p3d.__proto__ === point3d //> true


Merges one or more objects into the first, overwriting previous properties.


loom.merge(target, obj [, objN...])


  1. target (object) - The object to receive new properties.
  2. obj (object) - The object to merge into the first.
  3. objN (object) - Additional objects to merge into the first, each one overwriting the properties of the previous.


Object - The target obj, with the other objects merged into it.


var coords = {
  x: 1,
  y: 1

loom.merge(coords, {
  translateY: function (amt){
    this.y += amt
  translateX: function (amt){
    this.x += amt

coords.y //> 11


Appends the properties of one object onto another, ignoring properties that already exist.


loom.append(obj, ext)


  1. obj (object) - The object to receive new properties.
  2. ext (object) - The object to append to the first.


Object - the obj, with new properties.


var obj1 = {
	a: 'foo'

var obj2 = {
	a: 'exists already',
	b: 'bar',
	c: 'baz'

loom.append(obj1, obj2)
obj1.a //> 'foo', unchanged since it exists
obj1.b //> 'bar', appended because it didn't exist


Returns the loom to it's previous definition and returns a loom object.




Object - a loom object.


Assigning to a new object

var loom = 'foo'

// pretend like loom.js is included here

typeof loom //> object

var o = loom.noConflict()

loom //> 'foo' back to its rightful owner
o //> new reference for loom

Assigning to global Object

Some like to put things in places that make semantic sense.

loom.merge(Object, loom.noConflict())
// loom is gone
Object.punch //> function
Object.append //> function, etc.