Skip to content

Commit

Permalink
Reworked setting up of delegation removing unnecessary code paths and…
Browse files Browse the repository at this point in the history
… generally refactoring.
  • Loading branch information
joshweinstein committed Jan 16, 2013
1 parent eb683dc commit 1f53a4e
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 61 deletions.
92 changes: 35 additions & 57 deletions backbone.associations.js
@@ -1,8 +1,7 @@
Backbone.AssociativeModel = Backbone.Model.extend({
constructor: function(attributes, options) {
var delegatedAttrNames = _(_(this.delegateAttributes).chain().keys().map(function(k) {
return k.split(/\s+/);
}).flatten().value());
this.adaptDelegatedAttributesIntoMap();
var delegatedAttrNames = _(_(this.delegatedAttrsMap).keys());

// sort attributes, regular attributes before attributes to be delegated, so that
// the latter can be delegated to the former, if need be
Expand Down Expand Up @@ -42,7 +41,7 @@ Backbone.AssociativeModel = Backbone.Model.extend({
this.prepForAssociations();
this.associations(); //executes the `associations` function in user's model class
}
this._delegateAttributes(this.delegateAttributes);
this.setupDelegatedAttributes(); //over-writes this#get and this#set

return Backbone.Model.prototype.set.apply(this, arguments);
},
Expand Down Expand Up @@ -331,68 +330,47 @@ Backbone.AssociativeModel = Backbone.Model.extend({
};
},

delegateAttribute: function(attrName) {
return this.delegateAttributesInternal(attrName);
},

_delegateAttributes: function(delegatedAttrMap) {
/*
looks like:
{
'attr1 attr2 attr3': 'delegateModel'
}
*/
_(delegatedAttrMap).each(function(delegateModel, delegatedAttributes) {
this.delegateAttributesInternal(delegatedAttributes).toAttribute(delegateModel);
adaptDelegatedAttributesIntoMap: function() {
// adapt delegatedAttributes into delegatedAttrName -> delegateModelName map
this.delegatedAttrsMap = {};
_(this.delegateAttributes).each(function(delegateModelAttrName, delegatedAttrNames) {
var attributeNameList = delegatedAttrNames.split(/\s+/);
_.each(attributeNameList, function(attributeName) {
this.delegatedAttrsMap[attributeName] = delegateModelAttrName;
}, this );
}, this );
},

delegateAttributesInternal: function(attributeNames) {
var self = this,
delegatedAttrsMap = this.delegatedAttrsMap || this.setupDelegatedAttributes();

return {
toAttribute: function(delegateModelAttributeName) {
var delegateModel = self.get(delegateModelAttributeName),
delegatedAttrChanged;

var attributeNameList = attributeNames.split(/\s+/);
_.each(attributeNameList, function(attributeName) {
delegatedAttrsMap[attributeName] = delegateModelAttributeName;
setupDelegatedAttributes: function() {
this.get = this.getWithDelegates;
this.set = this.setWithDelegates;

delegatedAttrChanged = function(delegateModel, changedAttrVal, options) {
this.trigger('change:'+attributeName, delegateModel, changedAttrVal, options);
};
// bind to change events once the delegate models are set
var self = this;
_(this.delegatedAttrsMap).each(function(delegateModelAttrName, delegatedAttrName) {
var delegatedAttrChanged = function(delegateModel, changedAttrVal, options) {
this.trigger('change:'+delegatedAttrName, delegateModel, changedAttrVal, options);
},

var bindDelegatedAttrChanged = function(delegateModel) {
delegateModel.on('change:'+attributeName, delegatedAttrChanged, self);
},
unbindDelegatedAttrChanged = function(delegateModel) {
delegateModel.off('change:'+attributeName, bindDelegatedAttrChanged);
};
bindDelegatedAttrChanged = function(delegateModel) {
delegateModel.on('change:'+delegatedAttrName, delegatedAttrChanged, self);
},

if (delegateModel) {
bindDelegatedAttrChanged(delegateModel);
} else {
self.on('change:'+delegateModelAttributeName, function(delegatingModel, delegateModel, options) {
if (delegateModel) bindDelegatedAttrChanged(delegateModel);
});
}
unbindDelegatedAttrChanged = function(delegateModel) {
delegateModel.off('change:'+delegatedAttrName, bindDelegatedAttrChanged);
};

self.on('change:'+delegateModelAttributeName, function(delegatingModel, delegateModel, options) {
var formerDelegateModel = delegatingModel.previous(attributeName);
if (!delegateModel && formerDelegateModel) unbindDelegatedAttrChanged(formerDelegateModel);
});
self.on('change:'+delegateModelAttrName, function(delegatingModel, delegateModel, options) {
if (delegateModel) bindDelegatedAttrChanged(delegateModel);
// TODO trigger a change:attrName if delegateModel has this attribute to bring
// delegatingModel in sync with delegateModel
});
}
};
},

setupDelegatedAttributes: function() {
this.delegatedAttrsMap = {};
this.get = this.getWithDelegates;
this.set = this.setWithDelegates;
return this.delegatedAttrsMap;
self.on('change:'+delegateModelAttrName, function(delegatingModel, delegateModel, options) {
var formerDelegateModel = delegatingModel.previous(delegateModelAttrName);
if (!delegateModel && formerDelegateModel) unbindDelegatedAttrChanged(formerDelegateModel);
});
});
},

getWithDelegates: function(attrName) {
Expand Down
9 changes: 5 additions & 4 deletions spec/delegation.js
Expand Up @@ -137,8 +137,8 @@ describe("#delegateAttributesInternal", function() {

ModelWithDelegatedAttr = Backbone.AssociativeModel.extend({
associations: function() { this.hasOne('delegateModel'); },
initialize: function() {
this.delegateAttributesInternal("delegatedAttrName1 delegatedAttrName2").toAttribute("delegateModel");
delegateAttributes: {
'delegatedAttrName1 delegatedAttrName2': 'delegateModel'
}
});

Expand Down Expand Up @@ -194,8 +194,9 @@ describe("#delegateAttribute", function() {

ModelWithDelegatedAttr = Backbone.AssociativeModel.extend({
associations: function() { this.hasOne('delegateModel'); },
initialize: function() {
this.delegateAttribute('delegatedAttrName').toAttribute('delegateModel');

delegateAttributes: {
'delegatedAttrName': 'delegateModel'
}
});

Expand Down

0 comments on commit 1f53a4e

Please sign in to comment.