kriszyp / compose Goto Github PK
View Code? Open in Web Editor NEWComposeJS is a lightweight JavaScript library for object composition
Home Page: http://dojotoolkit.org/
ComposeJS is a lightweight JavaScript library for object composition
Home Page: http://dojotoolkit.org/
The code below creates a new object that extends HTMLButtonElement, and the new object has the appendChild method:
function SuperButton(){};
SuperButton.prototype = Object.create(HTMLButtonElement.prototype, { } )
(new SuperButton).appendChild // --> returns appendChild
It works fine on both Chrome/desktop and iOS/Safari.
However, using compose() to do the same thing doesn't work on iOS Safari:
var myClass = compose(HTMLButtonElement, {
});
console.log("appendChild in class", myClass.prototype.appendChild);
It works fine on Chrome/desktop, but on iOS7 Safari the final console.log() prints undefined.
Strangely, I can see the method there if I trace down the prototype chain myself
> myClass.prototype.prototype.appendChild
function appendChild() {
[native code]
}
> myClass.prototype.appendChild
undefined
This is from Eugene; adding here so it doesn't get lost. All the constructors in the example should get called, but some aren't.
Cloned the repo and used npm install
with Node v0.6.10. Run node test/compose.js
and I get this:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Cannot find module 'patr/assert'
at Function._resolveFilename (module.js:332:11)
at Function._load (module.js:279:25)
at Module.require (module.js:354:17)
at require (module.js:370:17)
at /Users/mark/Code/promised-io/compose/test/compose.js:4:14
at /Users/mark/Code/promised-io/compose/test/compose.js:383:3
at /Users/mark/Code/promised-io/compose/test/compose.js:2:1
at Object.<anonymous> (/Users/mark/Code/promised-io/compose/test/compose.js:380:2)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
Changing the references to patr/lib/
fixed things, but I had to do the same in Patr itself where it references promised-io
.
After hacking around to get the tests working (see #17), three tests fail:
Running tests
testCompose: passed
testComposeWithConstruct: passed
testInheritance: passed
testInheritanceViaExtend: passed
testInheritance2: passed
testMultipleInheritance: failed
AssertionError: got <div>hi</div>, expected <div>Hello, World</div>
at Object.equal (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/assert.js:15:16)
at Object.test (/Users/mark/Code/promised-io/compose/test/compose.js:93:9)
at runIteration (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:56:24)
at doTest (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:71:11)
at testCompleted (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:78:14)
at /Users/mark/Code/promised-io/compose/node_modules/patr/node_modules/promised-io/lib/promise.js:342:29
at runIteration (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:60:15)
at doTest (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:71:11)
at testCompleted (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:78:14)
at /Users/mark/Code/promised-io/compose/node_modules/patr/node_modules/promised-io/lib/promise.js:342:29
testAround: passed
testRequired: failed
Error: This method is required and no implementation has been provided
at Constructor.required [as render] (/Users/mark/Code/promised-io/compose/compose.js:192:9)
at Constructor.logAndRender (/Users/mark/Code/promised-io/compose/test/compose.js:115:9)
at Object.test (/Users/mark/Code/promised-io/compose/test/compose.js:128:9)
at runIteration (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:56:24)
at doTest (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:71:11)
at testCompleted (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:78:14)
at /Users/mark/Code/promised-io/compose/node_modules/patr/node_modules/promised-io/lib/promise.js:342:29
at runIteration (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:60:15)
at doTest (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:71:11)
at testFailed (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:89:17)
testCreate: passed
testInheritanceCreate: passed
testNestedCompose: passed
testFromAlias: passed
testFromExclude: passed
testComplexHierarchy: passed
testExtendError: passed
testAfterNothing: passed
testDiamond: failed
AssertionError: got 2, expected 1
at Object.equal (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/assert.js:15:16)
at Object.test (/Users/mark/Code/promised-io/compose/test/compose.js:316:9)
at runIteration (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:56:24)
at doTest (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:71:11)
at testCompleted (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:78:14)
at /Users/mark/Code/promised-io/compose/node_modules/patr/node_modules/promised-io/lib/promise.js:342:29
at runIteration (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:60:15)
at doTest (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:71:11)
at testCompleted (/Users/mark/Code/promised-io/compose/node_modules/patr/lib/runner.js:78:14)
at /Users/mark/Code/promised-io/compose/node_modules/patr/node_modules/promised-io/lib/promise.js:342:29
testNull: passed
testAdvice: passed
passed: 16/19
I thought ComposeJS supports just loading the compose.js file, without having an AMD framework. But when I try it in firefox (version 10, beta) or the latest chrome, I get an error about the Compose global being not defined. Test file is:
<html>
<head>
<script src="compose.js"></script>
<script>
console.log("Compose global is", Compose);
</script>
</head>
</html>
Hi!
Compose.after() is great, but in promised environment would be greater to have promise-aware .after(). The use case is, say, to impose limitations on recordset returned from an async datastore
--Vladimir
Hi!
What is the recommended pattern to extend an object/constructor with an object which is a promise? (e.g. a Store can provide closures or bound methods which are only usable when the store is connected/initialized)
TIA,
--Vladimir
Please run this test on REAL IE8 and compare it with the other modern browser:
Caveat: Do not use IE9's IE8 mode for testing, it CANNOT reproduce the issue!
Expected Result:
"a" in the result window
Actual Result in IE 8:
"[Object object]"
The cause of this issue is that if the member name of an object is same as the one of the bulti-in method name of Object.prototype.
it became un-enumerable!!
run below code in IE8 to see the bug:
var test = {toString:function(){}, foobar: function(){}};
for(var key in test){console.log(key)}
I have a TextBox
widget extending dojo/on#Evented
. It emits a change
event every time its value changes. I'm subclassing it into a ValidationTextBox
widget, where I'd like to listen to change
events to run validation code. I'd like to use a decorator to set up a _validate
function which is automatically listening to the change
event:
define([
"./TextBox",
"decorators"
], function(TextBox, decorators){
return TextBox.extend({
validate: function(){
return this._validate(this.get("value"));
},
isValid: function(value){
return true;
},
_validate: decorators.on("change", function(value){
return this.isValid(value);
})
});
});
Initially, my decorator looked like this:
define([
"dojo",
"compose"
], function(dojo, Compose){
return {
on: function(type, listener){
return new Compose.Decorator(function(key){
this[key] = listener;
this.on(type, dojo.hitch(this, key));
});
}
};
});
But this won't work, since inside the decorator, this
refers to the prototype. My workaround is to use after-advice around the postCreate
method:
define([
"dojo",
"compose"
], function(dojo, Compose){
return {
on: function(type, listener){
return new Compose.Decorator(function(key){
this[key] = listener;
Compose.after(function(){
this.on(type, dojo.hitch(this, key));
}).install.call(this, "postCreate");
});
}
};
});
That works, but is a bit cumbersome. It'd be nice if Composed classes could signal when their constructors have finished running to provide a reference to the created instance, for example:
define([
"dojo",
"compose"
], function(dojo, Compose){
return {
on: function(type, listener){
return new Compose.Decorator(function(key){
this[key] = listener;
this["-created-"](function(){
this.on(type, dojo.hitch(this, key));
});
});
}
};
});
Thoughts?
foo = new Application.Model()
foo.set({date: new Date()})
foo.get("date")
=> Thu May 24 2012 11:07:02 GMT+0200 (CEST)
foo.set({date: (new Date()).increment("day",1)})
foo.get("date")
=> Thu May 24 2012 11:07:02 GMT+0200 (CEST)
Expected: Fri May 25 2012 11:07:02 GMT+0200 (CEST)
some background... with dojo's loader, if you have 2 different module ids that map to the same url, the AMD module factory will be called separately for each unique module id. so, if you map the module ids "foo/compose"
and "bar/compose"
to the url "my/libs/compose"
, the module at "my/libs/compose"
will be loaded separately for each module id (ie twice).
if i compose a constructor using one version of compose
// Foo.js
define(['foo/compose'], function (compose) {
return compose(function Foo() {
console.log('Foo called');
});
});
and then compose another constructor that depends on Foo
but use the other version of compose
// Bar.js
define(['bar/compose', './Foo'], function (compose, Foo) {
return compose(Foo, function Bar() {
console.log('Bar called');
});
});
when i create an instance of Bar
, the Foo
constructor is not called. this is because of the reference to currentConstructors
via closure. i've found the following patch seems to fix it but i'm mostly including it here to try and show where the problem is.
diff --git a/support/compose.js b/support/compose.js
index 4bb86cd..a74e898 100644
--- a/support/compose.js
+++ b/support/compose.js
@@ -196,7 +196,7 @@ define([], function(){
return this; // this depends on strict mode
})();
- var currentConstructors;
function extend(){
var args = [this];
args.push.apply(args, arguments);
@@ -240,33 +240,33 @@ define([], function(){
return instance;
}
Constructor._register = function(){
- register(constructors);
+ return register(constructors);
};
- currentConstructors = [];
- register(arguments);
- var constructors = currentConstructors,
+ var constructors = register(arguments),
constructorsLength = constructors.length;
Constructor.extend = extend;
Constructor.prototype = prototype;
return Constructor;
};
function register(args){
+ var constructors = [];
outer:
for(var i = 0; i < args.length; i++){
var arg = args[i];
if(typeof arg == "function"){
if(arg._register){
- arg._register();
+ constructors.push.apply(constructors, arg._register());
}else{
- for(var j = 0; j < currentConstructors.length; j++){
- if(arg == currentConstructors[j]){
+ for(var j = 0; j < constructors.length; j++){
+ if(arg == constructors[j]){
continue outer;
}
}
- currentConstructors.push(arg);
+ constructors.push(arg);
}
}
}
+ return constructors;
}
// returning the export of the module
return Compose;
i didn't open a pull request since i'm not sure if you want to support this use case and also wasn't sure if you had a different solution in mind. i also haven't run the tests to see if this breaks anything.
A google console tour:
var p = C.create({foo: 'bar'}, {m1: function(){return 'IAMM1'}});
undefined
var u = C.create(p, {m2: function(){return 'IAMM2'}, foo:'baz'});
undefined
function PFacet(model){var f={};['m1','m2'].forEach(function(m){f[m]=C.from(model,m).bind(model);});return f;}
undefined
PFacet(u).m1()
compose.js:117 Object Error: Decorator not applied
PFacet(u).m2()
compose.js:117 Object Error: Decorator not applied
p.m1()
"IAMM1"
p.m2()
TypeError: Object #<an Object> has no method 'm2'
u.m2()
"IAMM2"
u.m1()
"IAMM1"
What I did wrong?
TIA,
--Vladimir
is it possible to support subclassing Error so that this can be done
var CustomError = Compose(Error, function (msg) {
this.message = 'custom ' + this.foo + msg;
}, {
foo: 'foo'
});
var err = new CustomError('bar');
console.log(err);
console.log(err instanceof CustomError ? 'yes' : 'no');
currently, instanceof
returns false and foo
is not mixed in. i found this blog post (http://blog.getify.com/2010/02/howto-custom-error-types-in-javascript/) about subclassing Error if it's useful for reference.
when trying to make decorators work, i produced the following code:
var log = console.log;
var Logged = function( method ){
return new Compose.Decorator( function( method_name ) {
log( "decorating " + method_name );
this[ method_name ] = function() {
log( "entering " + method_name );
R = method.apply( this, arguments );
log( "leaving " + method_name );
return R;
}
});
};
var C = Compose( {
'frob': Logged( function() { log( "performing" + frob ) } ) } );
c = new C()
c.frob()
which, i believe, keeps pretty close to the example given in the documentation. however, running the above gives
Error: Decorator not applied
is this a bug or is my own code buggy?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.