Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Move ES5 stuff out for a separate module ("configure"), use Object.cr…

…eate when available,
  • Loading branch information...
commit 617153d13fa69455eab9d095930cd0b04c513b7e 1 parent d368898
@kriszyp authored
Showing with 86 additions and 106 deletions.
  1. +86 −93 lib/compose.js
  2. +0 −13 test/compose.js
View
179 lib/compose.js
@@ -5,111 +5,111 @@
*/
"use strict";
(function(define){
-define([], function(exports){
+define([], function(){
// function for creating instances from a prototype
function Create(){
}
- var defineProperty = Object.defineProperty || function(object, key, descriptor){
- object[key] = descriptor.value;
- };
- function delegate(proto){
- Create.prototype = typeof proto == "function" ? proto.prototype : proto;
- return new Create();
- }
+ var delegate = Object.create ?
+ function(proto){
+ return Object.create(typeof proto == "function" ? proto.prototype : proto);
+ } :
+ function(proto){
+ Create.prototype = typeof proto == "function" ? proto.prototype : proto;
+ var instance = new Create();
+ Create.prototype = null;
+ return instance;
+ };
// this does the work of combining mixins/prototypes
function mixin(instance, args, i){
// use prototype inheritance for first arg
- var argsLength = args.length;
+ var value, argsLength = args.length;
for(; i < argsLength; i++){
var arg = args[i];
if(typeof arg == "function"){
// the arg is a function, use the prototype for the properties
arg = arg.prototype;
for(var key in arg){
- var value = arg[key];
+ value = arg[key];
if(typeof value == "function" && key in instance && value !== instance[key]){
- // we have potentially conflicting methods
- if(value == required){
- // it is a required value, and we have satisfied it
- continue;
- }
- else if(arg.hasOwnProperty(key)){
- // if it is own property, it is considered an explicit override
- if(!value.overrides){
- // record the override hierarchy
- value.overrides = instance[key];
- }
- }else{
- // still possible conflict, see if either value is in the other value's override chain
- var overriden = value, existing = instance[key];
- while((overriden = overriden.overrides) != existing){
- if(!overriden){
- // couldn't find existing in the provided value's override chain
- overriden = existing;
- while((overriden = overriden.overrides) != value){
- if(!overriden){
- // couldn't find value in the provided existing's override chain
- // we have a real conflict now
- existing = function(){
- throw new Error("Conflicted method, final composer must explicitly override with correct method.");
- }
- break;
- }
- }
- // use existing, since it overrides value
- value = existing;
- break;
- }
- }
-
- }
- if(value.install){
- // apply modifier
- value.install.call(instance, key);
- continue;
- }
+ value = resolvePrototype(value, key, instance[key], arg.hasOwnProperty(key), instance);
}
- // apply the value from this arg's property
- instance[key] = value;
+ if(value && value.install){
+ // apply modifier
+ value.install.call(instance, key);
+ }else{
+ instance[key] = value;
+ }
}
}else{
// it is an object, copy properties, looking for modifiers
for(var key in arg){
var value = arg[key];
- if(typeof value == "object" && value){
- if(value.value || value.get || value.set){
- // support for ES5 property descriptors
- defineProperty(instance, key, value);
- }else{
- // normal value
- instance[key] = value;
+ if(typeof value == "function"){
+ if(value.install){
+ // apply modifier
+ value.install.call(instance, key);
+ continue;
}
- }else{
- if(typeof value == "function"){
- if(value.install){
- // apply modifier
- value.install.call(instance, key);
+ if(key in instance){
+ if(value == required){
+ // required requirement met
continue;
- }
- if(key in instance){
- if(value == required){
- // required requirement met
- continue;
- }
- if(!value.overrides){
- // add the overrides chain
- value.overrides = instance[key];
- }
+ }
+ if(!value.overrides){
+ // add the overrides chain
+ value.overrides = instance[key];
}
}
- // add it to the instance
- instance[key] = value;
}
+ // add it to the instance
+ instance[key] = value;
}
}
}
return instance;
}
+ // allow for override (by es5 module)
+ Compose._setMixin = function(newMixin){
+ mixin = newMixin;
+ };
+ function resolvePrototype(value, key, existing, own, instance){
+ if(value == required){
+ // it is a required value, and we have satisfied it
+ return existing;
+ }
+ else if(own){
+ // if it is own property, it is considered an explicit override
+ if(!value.overrides){
+ // record the override hierarchy
+ value.overrides = instance[key];
+ }
+ }else{
+ // still possible conflict, see if either value is in the other value's override chain
+ var overriden = value;
+ while((overriden = overriden.overrides) != existing){
+ if(!overriden){
+ // couldn't find existing in the provided value's override chain
+ overriden = existing;
+ while((overriden = overriden.overrides) != value){
+ if(!overriden){
+ // couldn't find value in the provided existing's override chain
+ // we have a real conflict now
+ existing = function(){
+ throw new Error("Conflicted method, final composer must explicitly override with correct method.");
+ }
+ break;
+ }
+ }
+ // use existing, since it overrides value
+ value = existing;
+ break;
+ }
+ }
+
+ }
+ return value;
+ }
+ Compose._resolvePrototype = resolvePrototype;
// Decorator branding
function Decorator(install){
@@ -175,6 +175,9 @@ define([], function(exports){
// rename Decorator for calling super methods
Compose.from = function(trait, fromKey){
+ if(fromKey){
+ return (typeof trait == "function" ? trait.prototype : trait)[fromKey];
+ }
return Decorator(function(key){
if(!(this[key] = (typeof trait == "string" ? this[trait] :
(typeof trait == "function" ? trait.prototype : trait)[fromKey || key]))){
@@ -182,17 +185,7 @@ define([], function(exports){
}
});
};
- // dontEnum Decorator
- Compose.dontEnum = function(value){
- return Decorator(function(key){
- defineProperty(this, key, {
- value: value,
- enumerable: false,
- writable: true,
- configurable: true
- });
- });
- };
+
// Composes an instance
Compose.create = function(base){
// create the instance
@@ -229,7 +222,7 @@ define([], function(exports){
mixin(delegate(base), arguments, 1); // normally create a delegate to start with
function Constructor(){
var instance;
- if(this == undefinedThis){
+ if(this === undefinedThis){
// we allow for direct calls without a new operator, in this case we need to
// create the instance ourself.
Create.prototype = prototype;
@@ -239,8 +232,8 @@ define([], function(exports){
}
// call all the constructors with the given arguments
for(var i = 0; i < argsLength; i++){
- var arg = args[i];
- if(typeof arg == "function"){
+ var arg;
+ if(typeof (arg = args[i]) == "function"){
instance = arg.apply(instance, arguments) || instance;
}
}
@@ -255,10 +248,10 @@ define([], function(exports){
})(typeof define != "undefined" ?
define: // AMD/RequireJS format if available
function(deps, factory){
- var exports = factory(); // execute the factory
if(typeof module !="undefined"){
- module.exports = exports; // CommonJS environment, like NodeJS
+ module.exports = factory(); // CommonJS environment, like NodeJS
+ // require("./configure");
}else{
- Compose = exports; // raw script, assign to Compose global
+ Compose = factory(); // raw script, assign to Compose global
}
});
View
13 test/compose.js
@@ -92,19 +92,6 @@ exports.testAround = function() {
widget.render();
assert.equal(node.innerHTML, "<hi>Title</h1><div>Hello, World</div>");
};
-exports.testDontEnum = function() {
- var DontEnumWidget = Compose(MessageWidget, {
- message: "Hello, World",
- dontEnum: Compose.dontEnum(3)
- });
- var widget = new DontEnumWidget({});
- assert.equal(widget.dontEnum, 3);
- if(Object.defineProperty){
- for(var i in widget){
- assert.notEqual(i, "dontEnum");
- }
- }
-};
exports.testRequired = function() {
var logged;
var Logger = Compose({
Please sign in to comment.
Something went wrong with that request. Please try again.