Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "4.4"
- "5.10"
branches:
only:
- master
- dev
- dev
3 changes: 3 additions & 0 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ myComponent: {
// and shave off some cpu cycles (No guessing will be done)
// at the cost of being more verbose.
isConstructor: true, // or false
// Optional: If object is instantiated using a named constructor,
// you can specify its name here, so that it will be called.
namedConstructor: "create",
}
}
```
Expand Down
12 changes: 8 additions & 4 deletions lib/plugin/basePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ define(function(require) {
* @param {function} wire
*/
function instanceFactory(resolver, componentDef, wire) {
var create, args, isConstructor, module, instance;
var create, args, isConstructor, module, instance, constructorName;

create = componentDef.options;

Expand All @@ -228,12 +228,14 @@ define(function(require) {
} else if(wire.resolver.isRef(create)) {
module = wire(create);
args = getArgs(create, wire);
constructorName = create.constructorName;
} else if(object.isObject(create) && create.module) {
module = wire.resolver.isRef(create.module)
? wire(create.module)
: wire.loadModule(create.module);
args = getArgs(create, wire);
isConstructor = create.isConstructor;
constructorName = create.constructorName;
} else {
module = create;
}
Expand All @@ -246,9 +248,11 @@ define(function(require) {
function createInstance(module, args) {
// We'll either use the module directly, or we need
// to instantiate/invoke it.
return typeof module == 'function'
? instantiate(module, args, isConstructor)
: Object.create(module);
return constructorName
? module[constructorName].apply(module, args)
: typeof module == 'function'
? instantiate(module, args, isConstructor)
: Object.create(module);
}
}

Expand Down
114 changes: 114 additions & 0 deletions test/node/lib/plugin/basePlugin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,35 @@ function factory1(v1) {
};
}

function ClassWithNamedConstructor() {

}

ClassWithNamedConstructor.createInstance = function(){
var instance = new ClassWithNamedConstructor();
instance.createInstanceCalled = true;
instance.createInstanceArgs = Array.prototype.slice.call(arguments);
instance.thisSetInCreateInstance = this;

return instance;
};

ClassWithNamedConstructor.createInstancePromise = function(){
return {
then: function(f) {
f(ClassWithNamedConstructor.createInstance());
}
};
};

ClassWithNamedConstructor.createInstanceReject = function(){
return {
then: function(f, reject) {
reject(new Error('test error'));
}
};
};

buster.testCase('lib/plugin/basePlugin', {
'module factory': {
'should use module exports value as component': function() {
Expand Down Expand Up @@ -506,6 +535,91 @@ buster.testCase('lib/plugin/basePlugin', {
},
fail
);
},
'constructorName': {
'should use static named constructor if constructorName is set': function() {
return createContext({
test: {
create: {
module: ClassWithNamedConstructor,
constructorName: 'createInstance'
}
}
}).then(function(wired){
assert('test' in wired);
assert(wired.test.createInstanceCalled);
});
},
'should pass arguments to named constructor': function() {
return createContext({
test: {
create: {
module: ClassWithNamedConstructor,
constructorName: 'createInstance',
args: ['foo', 'bar', 1, 1.2]
}
}
}).then(function(wired){
assert('test' in wired);
assert.equals(wired.test.createInstanceArgs, ['foo', 'bar', 1, 1.2]);
});
},
'should work with module loaded by reference': function() {
return createContext({
reference: {
module: ClassWithNamedConstructor,
},
test: {
create: {
module: {$ref: 'reference'},
constructorName: 'createInstance'
}
}
}).then(function(wired){
assert('test' in wired);
assert(wired.test.createInstanceCalled);
});
},
'this in named constructor should be the constructor on which it is called': function() {
return createContext({
test: {
create: {
module: ClassWithNamedConstructor,
constructorName: 'createInstance',
}
}
}).then(function(wired){
assert('test' in wired);
assert.equals(wired.test.thisSetInCreateInstance, ClassWithNamedConstructor);
});
},
'should support constructors returning promises or promise-like objects': function() {
return createContext({
test: {
create: {
module: ClassWithNamedConstructor,
constructorName: 'createInstancePromise',
}
}
}).then(function(wired){
assert('test' in wired);
assert(wired.test instanceof ClassWithNamedConstructor);
});
},
'should reject if named constructor rejects': function() {
return createContext({
test: {
create: {
module: ClassWithNamedConstructor,
constructorName: 'createInstanceReject',
}
}
}).then(function(){
throw new Error('Unexpected success');
}, function(err){
assert.equals(err.message, 'test error');
});
}
}
},

Expand Down