diff --git a/README.md b/README.md index 8483024..f59191b 100755 --- a/README.md +++ b/README.md @@ -359,6 +359,11 @@ var Mixed = object.Constructor('Mixed', UtilityMixin(Base), { var m = Mixed() ``` +**Notes:** +- It is not recommended to `.mixin(..)` into constructors directly, use + `.mixinFlat(..)` instead. + + ## Advanced usage @@ -664,6 +669,9 @@ keeping the prototype visibility the same. This will copy the content of each input object without touching the objects themselves, making them fully reusable. +It is not recommended to `.mixin(..)` into constructors directly, use +`.mixinFlat(..)` instead. + ### `mixins(..)` diff --git a/object.js b/object.js index 8458e5a..36cd38a 100755 --- a/object.js +++ b/object.js @@ -495,6 +495,18 @@ function(base, ...objects){ // // // NOTE: this will only mix in non-empty objects... +// NOTE: mixing into a constructor will break object creation via new... +// Example: +// class A {} +// class B extends A {} +// +// mixin(B, {x: 123}) +// +// var b = new B() // will break... +// +// This does not affect object.Constructor(..) chains... +// NOTE: mixin(Object.prototype, ..) will fail because Object.prototype.__proto__ +// is imutable... var mixin = module.mixin = function(base, ...objects){ diff --git a/test.js b/test.js index 0a4b5ab..8970159 100755 --- a/test.js +++ b/test.js @@ -115,15 +115,20 @@ var instances = function(obj){ var makeAssert = function(pre, stats){ - return function(e, msg, ...args){ - stats - && (stats.assertions += 1) - && !e - && (stats.failures += 1) - module.VERBOSE - && console.log(pre +': '+ msg.bold, ...args) - console.assert(e, pre.bold +': '+ msg.bold.yellow, ...args) - return e } } + return Object.assign( + function(e, msg, ...args){ + stats + && (stats.assertions += 1) + && !e + && (stats.failures += 1) + module.VERBOSE + && console.log(pre +': '+ msg.bold, ...args) + console.assert(e, pre.bold +': '+ msg.bold.yellow, ...args) + return e }, + { + // XXX should have a real path... + path: pre + }) } @@ -595,11 +600,18 @@ module.modifiers = { : mode == 'raw' ? assert(O.__rawinstance__(), `.__rawinstance__()`, k) : assert(new O(), `new:`, k) + assert(o instanceof O, `instanceof:`, k) + O.__proto__ instanceof Function - && assert(o instanceof O.__proto__, `instanceof-nested:`, k) + // XXX need to test this for constructor mixins too... + && !(O.__mixin_constructors && !O.__mixin_flat) + && assert(o instanceof o.constructor.__proto__, `instanceof-nested:`, k) + assert(o.constructor === O, `.constructor:`, k) + assert(o.__proto__ === O.prototype, `.__proto__:`, k) + return res }, {}) }, instance_no_new: function(assert, setup){ return this.instance(assert, setup, 'no_new') }, @@ -616,17 +628,24 @@ module.modifiers = { return res }, // mixins... - mixin_instance: function(assert, setup, flat){ - var mixin = setup.__mixin_instance = { - __mixin_instance: true, + mixin_instance: function(assert, setup, flat, filter, get){ + filter = filter || instances + // XXX might be a good idea to get the method name from the context... + var attr = '__mixin_' + filter.name + + var mixin = setup[attr] = { + [attr]: true, + __mixin_flat: !!flat, // XXX } - mixin.__mixin_instance = mixin - instances(setup) + + mixin[attr] = mixin + filter(setup) .forEach(function([n, o]){ + o = get ? get(o) : o // mixin once per chain... - if(o.__mixin_instance){ + if(!o || o[attr]){ return } assert(!object.hasMixin(o, mixin), 'pre mixin test', n) assert(flat ? @@ -641,33 +660,15 @@ module.modifiers = { mixin_instance_flat: function(assert, setup){ return this.mixin_instance(assert, setup, true) }, mixin_constructor: function(assert, setup, flat){ - var mixin = setup.__mixin_constructor = { - __mixin_constructor: true, - - // XXX - } - mixin.__mixin_constructor = mixin - // XXX do we care about order??? - constructors(setup) - .forEach(function([n, o]){ - // special case: can't non-flat mixin into an Object... - if(!flat && o === Object){ - return } - // mixin once per chain... - if(o.prototype.__mixin_constructor){ - return } - assert(!object.hasMixin(o.prototype, mixin), 'pre mixin test', n) - assert(flat ? - object.mixinFlat(o.prototype, mixin) - : object.mixin(o.prototype, mixin), - flat ? - 'mixin (flat)' - : 'mixin', n) - assert(object.hasMixin(o.prototype, mixin), 'mixin test', n) - }) - return setup }, + return this.mixin_instance(assert, setup, false, constructors) }, + mixin_constructor_proto: function(assert, setup, flat){ + return this.mixin_instance(assert, setup, false, constructors, + function(o){ + // skip mixing into Object.prototype... + return o !== Object + && o.prototype }) }, mixin_constructor_flat: function(assert, setup){ - return this.mixin_constructor(assert, setup, true) }, + return this.mixin_constructor_proto(assert, setup, true) }, /*/ XXX mixout: function(assert, setup){ return {}