1 var comb = require("comb"),
  2         hitch = comb.hitch,
  3         Promise = comb.Promise,
  4         PromiseList = comb.PromiseList,
  5         OneToMany = require("./oneToMany");
  6 
  7 var LOGGER = comb.logging.Logger.getLogger("comb.associations.ManyToMany");
  8 /**
  9  * @class Class to define a manyToMany association.
 10  *
 11  * </br>
 12  * <b>NOT to be instantiated directly</b>
 13  * Its just documented for reference.
 14  *
 15  * @name ManyToMany
 16  * @augments OneToMany
 17  *
 18  * @param {String} options.joinTable the joinTable of the association.
 19  *
 20  *
 21  * @property {String} joinTable the join table used in the relation.
 22  * */
 23 module.exports = exports = comb.define(OneToMany, {
 24     instance : {
 25 
 26         _fetchMethod : "all",
 27 
 28         //constructor
 29         constructor : function(options) {
 30             if (!options.joinTable) {
 31                 throw new Error("Join table required for a manyToManyRelationship");
 32             }
 33             this._joinTable = options.joinTable;
 34             this.super(arguments);
 35         },
 36 
 37 
 38         _filter : function(parent) {
 39             if (!this.filter) {
 40                 if (!this.orderBy) {
 41                     this.orderBy = this.model.primaryKey;
 42                 }
 43                 var q = {};
 44                 var jq = {};
 45                 jq[this.leftKey] = parent[parent.primaryKey];
 46                 q[this.model.table.pk] = {"in" : this.joinTable.dataset.select(this.rightKey).find(jq).sql};
 47                 return this.model.filter(q).order(this.orderBy);
 48             } else {
 49                 return this.super(arguments);
 50             }
 51         },
 52 
 53         diff : function(next, self) {
 54             if (!this[self.loadedKey]) {
 55                 if (!self.filter || (self.filter && self.isEager())) {
 56                     this[self.name].then(hitch(this, self.diff, next, self));
 57                 } else {
 58                     next();
 59                 }
 60 
 61             } else if (!this.filter) {
 62                 var values = this[self.addedKey];
 63                 var removeValues = this[self.removedKey];
 64                 var pl = [];
 65                 if (values.length) {
 66                     pl = pl.concat(values.map(function(v) {
 67                         var p = new Promise();
 68                         v.save().then(hitch(this, function(child) {
 69                             var q = {};
 70                             q[self.leftKey] = this[this.primaryKey];
 71                             q[self.rightKey] = v[v.primaryKey];
 72                             self.joinTable.save(q).then(hitch(p, 'callback'), hitch(p, "errback"));
 73                         }), hitch(p, "errback"));
 74                         return p;
 75                     }, this));
 76                 }
 77                 if (removeValues.length) {
 78                     var ids = removeValues.map(function(v) {
 79                         return v.primaryKeyValue;
 80                     });
 81                     var q = {};
 82                     q[self.rightKey] = {"in" : ids};
 83                     pl.push(self.joinTable.remove(q));
 84                 }
 85                 if (pl.length) {
 86                     new PromiseList(pl).then(hitch(this, function(r) {
 87                         this[self.addedKey].length = 0;
 88                         this[self.removedKey].length = 0;
 89                         this[self.loadedKey] = true;
 90                         next();
 91                     }), function(err) {
 92                         err.forEach(function(e) {
 93                             LOGGER.error(util.inspect(err[0][1][1]));
 94                         });
 95                         next();
 96                     });
 97                 } else {
 98                     this[self.loadedKey] = true;
 99                     next();
100                 }
101             } else {
102                 next();
103             }
104         },
105 
106         _preRemove : function(next, self) {
107             if (this.filter) next();
108             if (!this[self.loadedKey]) {
109                 this[self.name].then(hitch(this, function(values) {
110                     this[self.removedKey] = values;
111                     self.diff.call(this, next, self);
112                 }));
113             } else {
114                 this[self.removedKey] = this[self.name];
115                 self.diff.call(this, next, self);
116             }
117 
118         },
119 
120         getters : {
121 
122             //returns our join table model
123             joinTable : function() {
124                 return this.moose.getModel(this._joinTable);
125             }
126         }
127     }
128 });
129 
130