Skip to content
This repository was archived by the owner on Apr 22, 2021. It is now read-only.

Commit 69578a9

Browse files
committed
Merge branch 'master-2' into localRel
# Conflicts: # supermodel.js # supermodel.min.js # test/has.js
2 parents d4840ba + c42fb1c commit 69578a9

3 files changed

Lines changed: 268 additions & 2 deletions

File tree

supermodel.js

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,12 @@
165165

166166
replace: function(model, other) {
167167
var other = this.alter(model, other)
168-
if( other ) this.associate(other, model)
168+
if( other ) {
169+
this.associate(other, model);
170+
171+
// Triggers the association replace.
172+
model.trigger("replace:"+this.name, model, other);
173+
}
169174
},
170175

171176
// Replace the current association with `other`, taking care to remove the
@@ -198,6 +203,7 @@
198203
// Tear down the current association.
199204
if (!other) model.unset(this.id);
200205
if (current) {
206+
current.off('all', null, this);
201207
delete model[this.store];
202208
this.dissociate(current, model);
203209
}
@@ -206,6 +212,29 @@
206212
// Set up the new association.
207213
model.set(this.id, other.id);
208214
model[this.store] = other;
215+
216+
// Listening association events.
217+
other
218+
.on('all', function(eventName, object) {
219+
var splittedEvent = eventName.split(":");
220+
221+
// Avoids a loop generated by two-way associations.
222+
var reservedNamespace = splittedEvent[1];
223+
if(this.inverse == reservedNamespace)
224+
return;
225+
226+
var headEvent = splittedEvent[0];
227+
var tailEvents = _.rest(splittedEvent).join(":");
228+
229+
// Building the new event to trigger (namespace, association name).
230+
var newEvent = headEvent+":"+this.name;
231+
if(tailEvents) newEvent += ":"+tailEvents;
232+
233+
// Trigger new event from the concrete object.
234+
arguments[0] = newEvent;
235+
model.trigger.apply(model, arguments);
236+
}, this);
237+
209238
return other;
210239
}
211240
}
@@ -247,6 +276,28 @@
247276
// handle events that it triggers.
248277
collection.owner = model;
249278

279+
// Listening association events.
280+
collection
281+
.on('all', function(eventName, object) {
282+
var splittedEvents = eventName.split(":");
283+
284+
// Avoids a loop generated by two-way associations
285+
var reservedNamespace = splittedEvents[1];
286+
if(this.inverse == reservedNamespace)
287+
return;
288+
289+
var headEvent = splittedEvents[0];
290+
var tailEvents = _.rest(splittedEvents).join(":");
291+
292+
// Building the new event to trigger (namespace, association name).
293+
var newEvent = headEvent+":"+this.name;
294+
if(tailEvents) newEvent += ":"+tailEvents;
295+
296+
// Trigger new event from the concrete object
297+
arguments[0] = newEvent;
298+
model.trigger.apply(model, arguments);
299+
}, this);
300+
250301
return collection;
251302
},
252303

@@ -378,6 +429,35 @@
378429
.on('associate:' + this.source, this._associate)
379430
.on('dissociate:' + this.source, this._dissociate));
380431

432+
// Listening association events.
433+
collection
434+
.on('all', function(eventName) {
435+
var splittedEvents = eventName.split(":");
436+
437+
// Avoids a loop generated by two-way associations
438+
var reservedNamespace = [splittedEvents[1], splittedEvents[2]];
439+
if(this.through === reservedNamespace[0] ||
440+
_.indexOf(reservedNamespace, this.name) > -1)
441+
return;
442+
443+
var headEvent = splittedEvents[0];
444+
var tailEvents = _.rest(splittedEvents).join(":");
445+
446+
// Building a new event to trigger (namespace, association name).
447+
var newEventName = headEvent+":"+this.name;
448+
if(tailEvents) newEventName += ":"+tailEvents;
449+
450+
arguments[0] = newEventName;
451+
model.trigger.apply(model, arguments);
452+
453+
// Building a new event to trigger (namespace, association through).
454+
var newEventThrough = headEvent+":"+this.through;
455+
if(tailEvents) newEventThrough += ":"+tailEvents;
456+
457+
arguments[0] = newEventThrough;
458+
model.trigger.apply(model, arguments);
459+
}, this);
460+
381461
return collection;
382462
},
383463

@@ -539,6 +619,33 @@
539619
getId: function() {
540620
if(this.id) return this.id;
541621
else return this.cid;
622+
},
623+
624+
// Alters clone method to prepare a model copy ready to work as a Supermodel
625+
// but without associations
626+
clone: function() {
627+
var attrsCopy = this.cloneAttributes();
628+
return new this.constructor(attrsCopy);
629+
},
630+
631+
// Returns attributes to be cloned
632+
cloneAttributes: function() {
633+
// Attributes to copy
634+
var attrsCopy = _.extend({}, this.attributes);
635+
636+
var ctor = this.constructor;
637+
638+
// Remove id attributes
639+
delete attrsCopy[ctor.prototype.cidAttribute];
640+
delete attrsCopy[ctor.prototype.idAttribute];
641+
642+
// Remove associations
643+
var allAssociations = ctor.allAssociations();
644+
for(var assoc in allAssociations) {
645+
delete attrsCopy[allAssociations[assoc].id];
646+
}
647+
648+
return attrsCopy;
542649
}
543650

544651
}, {
@@ -617,6 +724,18 @@
617724
return this._associations || (this._associations = {});
618725
},
619726

727+
// Return a hash of all associations for each constructor in its prototype chain.
728+
allAssociations: function() {
729+
var allAssociations = {};
730+
731+
var ctor = this;
732+
do {
733+
_.extend(allAssociations, ctor._associations);
734+
} while (ctor = ctor.parent);
735+
736+
return allAssociations;
737+
},
738+
620739
// Models and associations are tracked via `all` and `associations`,
621740
// respectively. `reset` removes all model references to allow garbage
622741
// collection.

0 commit comments

Comments
 (0)