Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #4060: base.js.5.diff

File base.js.5.diff, 5.5 kB (added by newmanb@stanford.edu, 2 years ago)

Great news. The size of the patch just dropped again, and I've actually added the method privacy feature back in! New simplifications are just leaping out at me. For those scoring at home, this still is the only proposed inheritance system that pretends to be backwards compatible with the current version of Prototype. It trivially supports the extend() syntax which makes Dean Edwards' system attractive, it has seen more active use than Base.js, and, frankly, the code is easier to read (i.e. more extensible & maintainable). These might sound like bold claims, but I'm prepared to stand behind them. The call stack dinosaur is long gone. What's left is growing harder and harder to dislike. If you're reading this, Sam, you might have more of a decision to make than you thought.

  • src/base.js

    old new  
    1 var Class = { 
    2   create: function() { 
    3     return function() {  
    4       this.initialize.apply(this, arguments); 
     1var Class = new (function(vocab) { 
     2   
     3  this.AUTHOR = "Ben Newman (newmanb@stanford.edu)"; 
     4 
     5  var $doNotInit = {}; 
     6   
     7  this.create = function(declarations) { 
     8     
     9    var constructor = function() { 
     10      if (arguments[0] !== $doNotInit && this[vocab['init']]) 
     11        this[vocab['init']].apply(this, arguments); 
     12    }; 
     13 
     14    if (declarations) { 
     15      var ownPrototype = function() { 
     16        for (var name in declarations) { 
     17          declarations[name].ancestor = baseInstance[name]; 
     18          __attachClassProperty(name, declarations[name], this); 
     19        } 
     20        __ensureDefaults(this, declarations); 
     21      }; 
     22 
     23      var base = declarations.extending || {}; 
     24      var baseInstance = __inherit(ownPrototype, base);       
     25       
     26      __setupAttachDelete(declarations); 
     27      __inherit(constructor, ownPrototype); 
     28      __enableMimicry(constructor); // experimental 
     29 
     30      constructor['extend'] = function(declarations) { 
     31        declarations['extending'] = constructor; 
     32        return Class.create(declarations); 
     33      } // a la Dean Edwards' Base.js 
    534    } 
     35     
     36    return constructor; 
     37  }; 
     38 
     39  var __enableMimicry = function(__ctor) { 
     40    var __old = __ctor.prototype; 
     41    __ctor['mimic'] = function(obj) { 
     42      var __new = Object.extend(obj, __old); 
     43      __ensureDefaults(__new, __old); 
     44      return Object.extend(function() { 
     45        return __ctor.apply(this, arguments); 
     46      }, { 'prototype': __new }); 
     47    }; 
     48  }; 
     49 
     50  var __inherit = function(subclass, superclass) { 
     51    switch (typeof(superclass)) { 
     52      case 'object': default: 
     53        superclass[vocab['init']] = superclass.constructor; 
     54        subclass.prototype = superclass; break; 
     55      case 'function': 
     56        superclass.getInstance = superclass.getInstance || 
     57          Class.encap(new superclass($doNotInit)); 
     58        subclass.prototype = superclass.getInstance(); 
     59    } 
     60    return subclass.prototype; 
     61  }; 
     62 
     63  var __setupAttachDelete = function(declarations) { 
     64    return Object.extend(declarations, { 
     65      'attachMethod': function(fnName, func) { 
     66        func.ancestor = this[fnName]; 
     67        __attachClassProperty(fnName, func, this); 
     68      }, 
     69      'deleteMethod': function(fnName) { 
     70        delete this[fnName]; 
     71      } 
     72    }); 
     73  }; 
     74 
     75  var __validOverride = function(propName) { 
     76    switch (propName) { 
     77    case vocab['super']: case 'valueOf': 
     78    case 'attachMethod': case 'deleteMethod': 
     79    case 'getBaseInstance': return false; 
     80    default: return true; 
     81    } 
     82  }; 
     83 
     84  // last receiving object 
     85  var __context = window; 
     86   
     87  var __attachClassProperty = function(propName, prop, obj) { 
     88     
     89    if (typeof(obj[propName] = prop) == 'function') { 
     90       
     91      if (__validOverride(propName)) { 
     92        var __ancestor = prop.ancestor; 
     93           
     94        obj[propName] = function() { 
     95          __ensurePrivacy(propName, this); // optional 
     96          // save this object reference globally 
     97          var __savedContext = __context; 
     98          __context = this; 
     99          // save the current version of the super function 
     100          var __superSaver = this[vocab['super']]; 
     101          this[vocab['super']] = __ancestor; 
     102           
     103          try { var result = prop.apply(this, arguments); } 
     104          catch (ex) { throw ex; } 
     105          finally { // restore super and object reference 
     106            this[vocab['super']] = __superSaver; 
     107            __context = __savedContext; 
     108          }   
     109          return result; 
     110        }; 
     111       
     112        obj[propName].valueOf = Class.encap(prop); 
     113      } 
     114 
     115      var __origStr = prop.toString(); 
     116      prop.toString = obj[propName].toString = function() { 
     117        return __origStr.replace(/^\s*function/, propName); 
     118      }; 
     119    } 
     120  }; 
     121 
     122  var __ensurePrivacy = function(propName, obj) { 
     123    if ((!__context || obj.constructor !== __context.constructor) 
     124        && propName[0] == '_') // private naming convention 
     125          alert("Tried to call private method " + propName + 
     126                " from foreign object " + __context + "."); 
    6127  } 
    7 } 
    8128 
     129  // all the functionality of bind(), plus context-sensitivity 
     130  Function.prototype.grant = function(obj) { 
     131    var __func = this; 
     132    var __originalContext = __context; 
     133    return function() { 
     134      __savedContext = __context; 
     135      __context = __originalContext; 
     136      try { var result = 
     137              __func.apply(obj || this, arguments); } 
     138      catch (ex) { throw ex; } 
     139      finally { __context = __savedContext; } 
     140      return result; 
     141    } 
     142  }; 
     143 
     144  var __ensureDefaults = function(ownPrototype, declarations) { 
     145    ['toString', 'toLocaleString', 'valueOf'].each(function(propName) { 
     146      var prop = declarations[propName]; 
     147      if (!!prop) { 
     148        prop.ancestor = ownPrototype[propName]; 
     149        __attachClassProperty(propName, prop, ownPrototype); 
     150      } 
     151    }); 
     152  }; 
     153 
     154  // convenient mechanism for creating accessor functions 
     155  this.encap = function(__val, mutable) { 
     156    if (!mutable) return function() { return __val; } 
     157    else return function(/* optional new value */) { 
     158      if (arguments.length > 0) __val = arguments[0]; 
     159      return __val; 
     160    } 
     161  }; 
     162   
     163})( // the vocabulary is arbitrary: 
     164{'super': 'sup', 'init': 'initialize'}); 
     165 
    9166var Abstract = new Object(); 
    10167 
    11168Object.extend = function(destination, source) {