Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

About this user

Ed Frederick www.edwardfrederick.com

« Newer Snippets
Older Snippets »
Showing 1-1 of 1 total  RSS 

derive.js 0.10 - a prototype.js extension for object derivation, mixins, and method chaining

http://revolutiononrails.blogspot.com/2007/02/derivejs-010-prototypejs-extension-for.html

Prototype.js is perfectly sufficient 99% of the time, so this is definitely overkill unless you want to build a big app in Javascript.

It has a strong Ruby 'feel' to it, but I'm definitely not trying to hide Javascript behind a Ruby to JS compiler or JS generator like RJS with this.

Scenerio: You've got a JS app with 20-30 Classes, each looking like:


   1  
   2  var Foo = Class.create();
   3  Object.extend(Foo.prototype, {
   4    initialize: {},
   5    foo: function(){
   6      // ...
   7    }
   8  });
   9  Object.extend(Foo, {
  10    classMethod: {}
  11  });
  12  
  13  var Bar = Class.create();
  14  Object.extend(Foo.prototype, Bar.prototype);
  15  Object.extend(Foo, Bar);
  16  Object.extend(Foo.prototype, {
  17    initialize: {},
  18    foo: function(){
  19      // ...
  20    }
  21  });
  22  Object.extend(Foo, {
  23    fooClassmethod: function(){}
  24  });
  25  
  26  // ...


It gets a little verbose, not to mention that you've got to manually apply 'superclass' methods to objects to call 'super' variants, etc. There's also no support for mixing things in 'horizontally' like you can in Ruby (where the message path goes out to modules and then up the inheritance stack, zigzag like.) When writing GUIs, this mixin capability is really useful for stacking event listeners.

Example of usage, with derivation and mixin:

   1  
   2  var SomeMixin = Mixin.create({
   3    one: function (teststr){
   4      console.debug('Entering (SomeMixin instance mthd one) with argument ' + teststr);
   5      console.debug('Calling super...');          
   6      this.sup('one', teststr);        
   7      console.debug('Exiting (SomeMixin instance mthd one)'); 
   8    },
   9    self: {
  10      included: function(mixin){
  11        console.debug("Something mixed-in SomeMixin")
  12      }
  13    }
  14  })
  15  
  16  var Foo = Class.derive(Object,{
  17    initialize: function(){},
  18    one: function(teststr){
  19      console.debug('Entering (Foo instance mthd one) with argument ' + teststr);
  20      console.debug('Exiting (Foo instance mthd one)'); 
  21    },
  22    self: {
  23      one: function(){
  24        console.debug('Entering (Foo.one) class method');
  25      },
  26      two: function(){
  27        console.debug('Entering (Foo.two) class method');
  28    	},
  29    	inherited: function(){
  30    	  console.debug("Someone inherited Foo!");
  31    	}
  32    }
  33  });
  34  
  35  Foo.include(SomeMixin);
  36  
  37  var Bar = Class.derive(Foo,{
  38    initialize: function(teststr){},
  39    one: function(teststr){
  40      console.debug('Entering (Bar instance method one) with argument ' + teststr);     
  41      console.debug('Calling super...');
  42      this.sup('one', teststr);
  43      console.debug('Exiting with (Bar instance method one)');
  44    },
  45    self: {
  46      one: function(){
  47        console.debug('Entering (Bar.one) class method');
  48      }
  49    }
  50  });
  51  
  52  // Singleton is a Mixin defined for you in derive.js
  53  Bar.extend(Singleton);
  54  
  55  b = Bar.instance();
  56  b.one('test');
  57  Bar.one();
  58  Bar.two();



Full source:

   1  
   2  // derive.js: edward.frederick@revolution.com, 2/07
   3  // By: Revolution Dev Team
   4  // Questions/Resources: rails-trunk@revolution.com, revolutiononrails.blogspot.com, www.edwardfrederick.com
   5  
   6  Object.chain = function(dest,source){			
   7    for (var property in source) {
   8  	  var chainprop = '__' + property + '_chain';
   9  		if (!property.match(/__.*_chain$/)){
  10  			if (!dest[chainprop]){
  11  				dest[chainprop] = new Array();
  12  			}
  13  			if (dest[property]){
  14  				dest[chainprop].unshift(dest[property]);
  15  			}
  16  			if (source[chainprop]){
  17  				dest[chainprop] = source[chainprop].concat(dest[chainprop]);
  18  			}
  19  			dest[property] = source[property];
  20  		}
  21    }
  22    return dest;	
  23  }
  24  
  25  Object.extend(Class, {
  26    derive: function(superclass, body){
  27  		var ctr = Class.create();		
  28  		if (superclass){
  29  			ctr.superclass = superclass;
  30  			Object.chain(ctr.prototype,superclass.prototype);			
  31  			Object.chain(ctr,superclass);
  32  			if (superclass.inherited)
  33  			  superclass.inherited(ctr);
  34  		}		
  35  		if (body){
  36  			if (body.self)
  37  				Object.chain(ctr,body.self);		
  38  			body.self = undefined;
  39  			Object.chain(ctr.prototype,body);
  40  		}
  41  		Object.extend(ctr,Class._deriveClassExtensions);
  42  		Object.extend(ctr.prototype,Class._deriveInstanceExtensions);		
  43  		return ctr;			
  44    },
  45  	_deriveClassExtensions: {	
  46  		include: function(mixin){
  47  			if (!mixin._mixin)
  48  				throw "Can only include a mixin!";				
  49  			Object.chain(this.prototype,mixin.methods);
  50  			if (mixin.self && mixin.self.included)
  51  				mixin.self.included(this);
  52  		},
  53  		extend: function(mixin){
  54  			if (!mixin._mixin)
  55  				throw "Can only extend a mixin!";
  56  			Object.chain(this,mixin.methods);
  57  			if (mixin.self && mixin.self.extended)
  58  				mixin.self.extended(this);
  59  		}
  60  	},
  61  	_deriveInstanceExtensions: {
  62  		sup: function(method){
  63  			var currentLinkVar = '__' + method + '_current_chain_link';
  64  			var chainVar = '__' + method + '_chain';
  65  
  66  			if (!this[currentLinkVar])
  67  				this[currentLinkVar] = 0;
  68  			if (this[currentLinkVar] && this[currentLinkVar] >= this[chainVar].size())
  69  				throw "NoPreviousMethod: " + method;	
  70  	
  71  			var mine = this[chainVar][this[currentLinkVar]];
  72  			this[currentLinkVar]++;
  73  			
  74  			var shiftedArguments = new Array();
  75  			for (var i = 1; i < arguments.length; i++)
  76  				shiftedArguments.push(arguments[i]);
  77  				
  78  			var result = mine.apply(this,shiftedArguments);
  79  			this[currentLinkVar] = undefined;
  80  			return result;
  81  		}
  82  	}	
  83  });
  84  
  85  /* Can also mix things in horizontally, as the derive heirarchies are 
  86  	 intended to be singly-rooted */
  87  var Mixin = {
  88  	create: function(object){
  89  		var mixin = {};
  90  		var methods = Object.extend({},object);
  91  		Object.extend(mixin,{
  92  			self: methods.self,
  93  			_mixin: true,
  94  			methods: methods
  95  		});
  96  		mixin.methods.self = undefined;
  97  		Object.extend(mixin, Mixin._classMethods);
  98  		return mixin;
  99  	},
 100  	_classMethods: {
 101  		included: Prototype.emptyFunction,
 102  		extended: Prototype.emptyFunction
 103  	}
 104  }
 105  
 106  
 107  /* Singleton mixin provided as an example (albeit a useful one) */
 108  var Singleton = Mixin.create({
 109  	instance: function(){
 110  		if (this._instance)
 111  			return this._instance;
 112  		else
 113  			return this._instance = new this(arguments);
 114  	},
 115  	self: {
 116  		included: function(klass){
 117  			// nothing here, but could be
 118  		},
 119  		extended: function(klass){
 120  			// nothing here, but could be
 121  		}
 122  	}
 123  });
« Newer Snippets
Older Snippets »
Showing 1-1 of 1 total  RSS