Skip to content

Instantly share code, notes, and snippets.

@jcoglan
Last active March 28, 2016 12:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jcoglan/fa555de2e97923e87e58 to your computer and use it in GitHub Desktop.
Save jcoglan/fa555de2e97923e87e58 to your computer and use it in GitHub Desktop.
'use strict';
var Object_create, Object_assign, Module, Include, Kernel, Class;
var hasOwnProperty = Object.prototype.hasOwnProperty;
Object_create = Object.create || function(prototype) {
var f = function() {};
f.prototype = prototype;
return new f();
};
Object_assign = Object.assign || function(destination, source) {
for (var key in source) destination[key] = source[key];
return destination;
};
/*----------------------------------------------------------------------------*/
Module = function(name, methods) {
if (typeof name !== 'string') {
methods = name;
}
this.__id = ++ Module.__ID;
this.__name = name;
this.__super = this.__super || null;
this.__includes = {};
this.__methods = {};
this.__source = this;
this.include(methods);
};
Module.__ID = 0;
Object_assign(Module.prototype, {
__ancList: null,
__ancSet: null,
__id: null,
__includes: null,
__methods: null,
__name: null,
__source: null,
__super: null,
inspect: function() {
return this.__name;
},
ancestors: function() {
if (this.__ancList) return this.__ancList;
var rest = this.__super ? this.__super.ancestors() : [],
list = [this.__source].concat(rest);
return this.__ancList = list;
},
_ancestorSet: function() {
if (this.__ancSet) return this.__ancSet;
var rest = this.__super ? this.__super._ancestorSet() : null,
set = Object_create(rest);
set[this.__source.__id] = true;
return this.__ancSet = set;
},
define: function(name, method) {
this.__methods[name] = method;
this._propagate(name);
},
_propagate: function(name, method) {
for (var id in this.__includes)
this.__includes[id]._propagate(name, method);
},
include: function(methods) {
var mixins, name, value, i, n;
if (methods instanceof Module) return this._includeModule(methods);
if (typeof methods !== 'object') return;
mixins = methods.extend;
if (typeof mixins === 'object') {
mixins = [].concat(mixins);
for (i = 0, n = mixins.length; i < n; i++) this.extend(mixins[i]);
}
mixins = methods.include;
if (typeof mixins === 'object') {
mixins = [].concat(mixins);
for (i = 0, n = mixins.length; i < n; i++) this.include(mixins[i]);
}
for (name in methods) {
value = methods[name];
if (typeof value === 'function') this.define(name, value);
}
},
_includeModule: function(module) {
var ours = this._ancestorSet(),
theirs = module.ancestors(),
host = this.__host || this,
next = this,
prev = this.__super,
ancestor, include;
var i = 0, n = theirs.length;
while (n - i++) {
ancestor = theirs[i - 1];
if (ancestor.__id in ours) continue;
include = ancestor._createInclude(host);
include.__sub = next;
next.__super = include;
this._prune(ancestor);
include._cascade();
next = include;
}
next.__super = prev;
if (prev) prev._subclass(next);
for (var id in this.__includes)
this.__includes[id]._includeModule(module);
},
_createInclude: function(host) {
var include = new Include(this, host);
this.__includes[include.__id] = include;
return include;
},
_prune: function(module) {
this.__ancList = this.__ancSet = null;
}
});
/*----------------------------------------------------------------------------*/
Include = function(source, host) {
this.__id = ++ Module.__ID;
this.__source = source;
this.__methods = source.__methods;
this.__host = host;
};
Object_assign(Include.prototype, {
__ancList: null,
__ancSet: null,
__host: null,
__id: null,
__methods: null,
__source: null,
__sub: null,
__super: null,
ancestors: Module.prototype.ancestors,
_ancestorSet: Module.prototype._ancestorSet,
_includeModule: Module.prototype._includeModule,
_subclass: function(module) {
this.__sub = module;
},
_cascade: function() {
for (var name in this.__methods) this._propagate(name);
},
_propagate: function(name, method) {
if (this.__host.constructor === Module) return;
if (hasOwnProperty.call(this.__methods, name))
method = this.__methods[name];
this.__sub._propagate(name, method);
},
_prune: function(module) {
this.__ancList = this.__ancSet = null;
var sub = this.__sub;
if (module && sub.__source === module) {
this.__sub = sub.__sub;
this.__sub.__super = this;
delete sub.__source.__includes[sub.__id];
this.__sub._prune(null);
} else {
return sub._prune(module);
}
}
});
/*----------------------------------------------------------------------------*/
Kernel = new Module('Kernel', {
__eigen__: function() {
if (!this.__meta) {
var name = '#<Class:' + this.inspect() + '>',
parent = (this.superclass || {}).__meta || this.klass;
this.__meta = new Class(name, parent, {}, {_target: this, _virtual: true});
}
return this.__meta;
},
extend: function(module) {
this.__eigen__().include(module, {_extended: this});
}
});
/*----------------------------------------------------------------------------*/
Class = function(name, superclass, methods, options) {
if (typeof name !== 'string') {
options = methods; methods = superclass; superclass = name; name = '';
}
if (typeof superclass !== 'function') {
options = methods; methods = superclass; superclass = null;
}
if (typeof options === 'undefined') {
options = {};
}
var self = function() {
var value = this.initialize && this.initialize.apply(this, arguments);
return value || this;
};
Object_assign(self, this);
if (superclass) self.prototype = Object_create(superclass.prototype);
self.prototype.constructor = self.prototype.klass = self;
self.__super = self.superclass = superclass || null;
self.__subs = {};
self.__target = options._target || self.prototype;
self.__cache = {};
Module.call(self, name, methods);
self.include(Kernel);
if (options._virtual) {
superclass = self.__super;
for (name in superclass.__cache)
self._propagate(name, superclass.__cache[name]);
} else {
self.__eigen__();
}
return self;
};
Class.prototype = Object_assign(Object_create(Module.prototype), {
__cache: null,
__subs: null,
__target: null,
_subclass: function(module) {
var host = module.__host || module;
this.__subs[host.__id] = module;
},
_propagate: function(name, method) {
if (hasOwnProperty.call(this.__methods, name))
method = this.__methods[name];
this.__cache[name] = method;
if (this.__target[name] !== method)
this.__target[name] = method;
for (var id in this.__subs)
this.__subs[id]._propagate(name, method);
},
_prune: function(module) {
this.__ancList = this.__ancSet = null;
var id, sub;
for (id in this.__subs) {
sub = this.__subs[id];
if (module && sub.__source === module) {
this.__subs[sub.__host.__id] = sub.__sub;
sub.__sub.__super = this;
delete sub.__source.__includes[sub.__id];
sub.__sub._prune(null);
} else {
sub._prune(module);
}
}
}
});
/*----------------------------------------------------------------------------*/
(function() {
var Ruby = {Module: Module, Kernel: Kernel, Class: Class};
var bootstrap = function(name, parent) {
var klass = Ruby[name];
Object_assign(klass, Class.prototype);
klass.constructor = klass.klass = Class;
klass.__id = ++ Module.__ID;
klass.__name = name;
klass.__super = klass.superclass = parent || null;
klass.__subs = {};
klass.__includes = {};
klass.__methods = Object_assign({}, klass.prototype);
klass.__source = klass;
klass.__target = klass.prototype;
klass.__cache = {};
klass.include(Kernel);
klass.prototype.constructor = klass.prototype.klass = klass;
};
bootstrap('Module');
bootstrap('Class', Module);
var eigen = Kernel.__methods.__eigen__;
eigen.call(Module);
eigen.call(Class);
Module.__super._cascade();
module.exports = Ruby;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment