Skip to content

Commit

Permalink
Added lazy mixing. With new tests and passing old tests. Readme updated
Browse files Browse the repository at this point in the history
  • Loading branch information
mrjoelkemp committed Oct 26, 2013
1 parent d7eff66 commit ad3b283
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 12 deletions.
26 changes: 14 additions & 12 deletions Cocktail.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
} else if (typeof define === 'function') {
define(function(require) {
return Cocktail;
})
});
} else {
this.Cocktail = Cocktail;
}
Expand All @@ -19,6 +19,8 @@

Cocktail.mixin = function mixin(klass) {
var mixins = _.chain(arguments).toArray().rest().flatten().value();
// Allows mixing into the constructor's prototype or the dynamic instance
var obj = klass.prototype || klass;

var collisions = {};

Expand All @@ -28,32 +30,32 @@
}
_(mixin).each(function(value, key) {
if (_.isFunction(value)) {
if (klass.prototype[key]) {
collisions[key] = collisions[key] || [klass.prototype[key]];
if (obj[key]) {
collisions[key] = collisions[key] || [obj[key]];
collisions[key].push(value);
}
klass.prototype[key] = value;
obj[key] = value;
} else if (_.isObject(value)) {
klass.prototype[key] = _.extend({}, value, klass.prototype[key] || {});
} else if (!(key in klass.prototype)) {
klass.prototype[key] = value;
obj[key] = _.extend({}, value, obj[key] || {});
} else if (!(key in obj)) {
obj[key] = value;
}
});
});

_(collisions).each(function(propertyValues, propertyName) {
klass.prototype[propertyName] = function() {
obj[propertyName] = function() {
var that = this,
args = arguments,
returnValue = undefined;
returnValue;

_(propertyValues).each(function(value) {
var returnedValue = _.isFunction(value) ? value.apply(that, args) : value;
returnValue = (returnedValue === undefined ? returnValue : returnedValue);
returnValue = (typeof returnedValue === 'undefined' ? returnValue : returnedValue);
});

return returnValue;
}
};
});
};

Expand All @@ -76,7 +78,7 @@
_([Backbone.Model, Backbone.Collection, Backbone.Router, Backbone.View]).each(function(klass) {
klass.mixin = function mixin() {
Cocktail.mixin(this, _.toArray(arguments));
}
};

klass.extend = extend;
});
Expand Down
1 change: 1 addition & 0 deletions Cocktail.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,29 @@ Now all instances of `MyView` will have the selection behavior defined in the `S
var view = new MyView(...);
view.toggleSelect(); //works!


**Alternatively**, you can lazily mix into your views/models like so:

var MyView = Backbone.View.extend({
events: {
'click .myChild': 'myCustomHandler'
}

initialize: function() {
Cocktail.mixin(this, MyMixins.SelectMixin, MyMixins.SomeOtherMixin);
},

render: function() {
...
},

etc...
});

This looks a bit cleaner if you can't monkeypatch (described below). In addition, all instances of `MyView` still share the
same mixin function objects – as they did with the previous constructor-based mixin strategy.


### If you don't mind monkeypatching

By default, as of 0.2.0 Cocktail no longer messes with Backbone's built-in extend method. However, if you don't mind some monkey patching then running:
Expand Down
23 changes: 23 additions & 0 deletions example/PaginatedViewAlt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
(function (Cocktail) {
'use strict';

// Showcases the instance-based mixin method
// as opposed to the constructor extension
window.MyPaginatedViewAlt = Backbone.View.extend({
initialize: function () {
Cocktail.mixin(this, window.MyMixins.PaginateMixin);
},

render: function() {
this.$el.append('<div class="the-number"></div>');
},

renderCurrentPage: function(page) {
this.$('.the-number').text(page + 1);
},

numberOfPages: function() {
return 10;
}
});
})(window.Cocktail);
18 changes: 18 additions & 0 deletions example/PaginatedViewSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,21 @@ describe("MyPaginatedView", function() {

MyMixinSpecs.PaginateMixinSpec(context);
});


describe("MyPaginatedViewAlt", function() {
var context = {};

beforeEach(function() {
context.view = new MyPaginatedViewAlt();
context.numberOfPages = 10;
context.ensureOnPage = function(page) {
expect($('.the-number').text()).toEqual(page + 1 + '');
}

$('#content').append(context.view.$el);
context.view.render();
});

MyMixinSpecs.PaginateMixinSpec(context);
});
1 change: 1 addition & 0 deletions example/SpecRunner.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<!-- include source files here... -->
<script type="text/javascript" src="PaginateMixin.js"></script>
<script type="text/javascript" src="PaginatedView.js"></script>
<script type="text/javascript" src="PaginatedViewAlt.js"></script>

<!-- include spec files here... -->
<script type="text/javascript" src="PaginateMixinSpec.js"></script>
Expand Down
20 changes: 20 additions & 0 deletions spec/spec/CocktailSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ describe('Cocktail', function() {
}

Cocktail.mixin(ViewClass, aMixin, 'fooBar');

ViewClass2 = Backbone.View.extend({
initialize: function () {
Cocktail.mixin(this, aMixin, 'fooBar');
},

//should be ignored as the patch is not installed
fooBar: function() {
calls.push('fooBarOriginal');
}
});

});

it("should allow mixing in mixins after the sub class has been built (useful for coffeescript)", function() {
Expand All @@ -142,6 +154,14 @@ describe('Cocktail', function() {
view.fooBar();
expect(calls).toEqual(['fooBarOriginal', 'fooBarInclude']);
});

it("should allow mixing into the instance after the subclass has been instantiated", function() {
var view = new ViewClass2();
expect(view.mixinmethod()).toEqual('mixin');
expect(view.otherMixinMethod()).toEqual('other');
view.fooBar();
expect(calls).toEqual(['fooBarOriginal', 'fooBarInclude']);
});
});

describe("when patching backbone", function() {
Expand Down

0 comments on commit ad3b283

Please sign in to comment.