From 9cd6d1f8dde97bea3d6cef7546d497f1bfdaf036 Mon Sep 17 00:00:00 2001 From: Joel Kemp Date: Thu, 2 Jan 2014 18:42:28 -0500 Subject: [PATCH] Fixes #22 --- Cocktail.js | 4 ++++ Cocktail.min.js | 2 +- README.md | 5 ++++- spec/spec/CocktailSpec.js | 28 ++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Cocktail.js b/Cocktail.js index d2e327e..a0b57a6 100644 --- a/Cocktail.js +++ b/Cocktail.js @@ -30,6 +30,10 @@ } _(mixin).each(function(value, key) { if (_.isFunction(value)) { + // If the mixer already has that exact function reference + // Note: this would occur on an accidental mixin of the same base + if (obj[key] === value) return; + if (obj[key]) { collisions[key] = collisions[key] || [obj[key]]; collisions[key].push(value); diff --git a/Cocktail.min.js b/Cocktail.min.js index fcea40d..0106d1e 100644 --- a/Cocktail.min.js +++ b/Cocktail.min.js @@ -1 +1 @@ -(function(){var e={};typeof exports!="undefined"?e=exports:typeof define=="function"?define(function(t){return e}):this.Cocktail=e;e.mixins={};e.mixin=function(n){var r=_.chain(arguments).toArray().rest().flatten().value(),i=n.prototype||n,s={};_(r).each(function(t){_.isString(t)&&(t=e.mixins[t]);_(t).each(function(e,t){if(_.isFunction(e)){if(i[t]){s[t]=s[t]||[i[t]];s[t].push(e)}i[t]=e}else _.isObject(e)?i[t]=_.extend({},e,i[t]||{}):t in i||(i[t]=e)})});_(s).each(function(e,t){i[t]=function(){var t=this,n=arguments,r;_(e).each(function(e){var i=_.isFunction(e)?e.apply(t,n):e;r=typeof i=="undefined"?r:i});return r}})};var t;e.patch=function(r){t=r.Model.extend;var i=function(n,r){var i=t.call(this,n,r),s=i.prototype.mixins;s&&i.prototype.hasOwnProperty("mixins")&&e.mixin(i,s);return i};_([r.Model,r.Collection,r.Router,r.View]).each(function(t){t.mixin=function(){e.mixin(this,_.toArray(arguments))};t.extend=i})};e.unpatch=function(n){_([n.Model,n.Collection,n.Router,n.View]).each(function(e){e.mixin=undefined;e.extend=t})}})(); \ No newline at end of file +(function(){var e={};typeof exports!="undefined"?e=exports:typeof define=="function"?define(function(t){return e}):this.Cocktail=e;e.mixins={};e.mixin=function(n){var r=_.chain(arguments).toArray().rest().flatten().value(),i=n.prototype||n,s={};_(r).each(function(t){_.isString(t)&&(t=e.mixins[t]);_(t).each(function(e,t){if(_.isFunction(e)){if(i[t]===e)return;if(i[t]){s[t]=s[t]||[i[t]];s[t].push(e)}i[t]=e}else _.isObject(e)?i[t]=_.extend({},e,i[t]||{}):t in i||(i[t]=e)})});_(s).each(function(e,t){i[t]=function(){var t=this,n=arguments,r;_(e).each(function(e){var i=_.isFunction(e)?e.apply(t,n):e;r=typeof i=="undefined"?r:i});return r}})};var t;e.patch=function(r){t=r.Model.extend;var i=function(n,r){var i=t.call(this,n,r),s=i.prototype.mixins;s&&i.prototype.hasOwnProperty("mixins")&&e.mixin(i,s);return i};_([r.Model,r.Collection,r.Router,r.View]).each(function(t){t.mixin=function(){e.mixin(this,_.toArray(arguments))};t.extend=i})};e.unpatch=function(n){_([n.Model,n.Collection,n.Router,n.View]).each(function(e){e.mixin=undefined;e.extend=t})}})(); \ No newline at end of file diff --git a/README.md b/README.md index a5a0bbd..a64dafd 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,10 @@ Whether or not you're monkey patching Backbone, you can also use named mixins by In the example above, both `MyView` and `SelectMixin` both defined `initialize`, and `render`. What happens with these colliding methods? -Cocktail automatically ensures that methods defined in your mixins do not obliterate the corresponding methods in your classes. This is accomplished by wrapping all colliding methods into a new method that is then assigned to the final composite object. +Cocktail automatically ensures that methods defined in your mixins do not obliterate the corresponding methods in your classes. +This is accomplished by wrapping all colliding methods into a new method that is then assigned to the final composite object. + +Note: Cocktail will ensure that if you accidentally try to mix in the same method, it will not result in a collision and will do nothing. ### How are colliding functions called? diff --git a/spec/spec/CocktailSpec.js b/spec/spec/CocktailSpec.js index 64f77b6..12886c5 100644 --- a/spec/spec/CocktailSpec.js +++ b/spec/spec/CocktailSpec.js @@ -162,6 +162,34 @@ describe('Cocktail', function() { view.fooBar(); expect(calls).toEqual(['fooBarOriginal', 'fooBarInclude']); }); + + it("should not mixin the same function reference more than once", function () { + var A = { + foo: function () { + console.log('foo'); + } + }; + + var B = {}; + + var C = { + foo: function () { + console.log('foo'); + } + }; + + Cocktail.mixin(B, A); + expect(B.foo === A.foo).toBeTruthy(); + + // An accidental mixin of the same base + Cocktail.mixin(B, A); + expect(B.foo === A.foo).toBeTruthy(); + + // Expect the collision wrapper + Cocktail.mixin(B, C); + expect(B.foo === A.foo).toBeFalsy(); + expect(B.foo === C.foo).toBeFalsy(); + }); }); describe("when patching backbone", function() {