-
Notifications
You must be signed in to change notification settings - Fork 42
/
model_instance_methods.js
142 lines (119 loc) · 3.61 KB
/
model_instance_methods.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
Model.InstanceMethods = {
attr: function(name, value) {
if (arguments.length === 0) {
// Combined attributes/changes object.
return jQuery.extend({}, this.attributes, this.changes);
} else if (arguments.length === 2) {
// Don't write to attributes yet, store in changes for now.
if (_.isEqual(this.attributes[name], value)) {
// Clean up any stale changes.
delete this.changes[name];
} else {
this.changes[name] = value;
}
return this;
} else if (typeof name === "object") {
// Mass-assign attributes.
for (var key in name) {
this.attr(key, name[key]);
}
return this;
} else {
// Changes take precedent over attributes.
return (name in this.changes) ?
this.changes[name] :
this.attributes[name];
}
},
bind: function(event, callback) {
this.callbacks[event] = this.callbacks[event] || [];
this.callbacks[event].push(callback);
return this;
},
callPersistMethod: function(method, callback) {
var self = this;
// Automatically manage adding and removing from the model's Collection.
var manageCollection = function() {
if (method === "create") {
self.constructor.add(self);
} else if (method === "destroy") {
self.constructor.remove(self.id());
}
};
// Wrap the existing callback in this function so we always manage the
// collection and trigger events from here rather than relying on the
// persistence adapter to do it for us. The persistence adapter is
// only required to execute the callback with a single argument - a
// boolean to indicate whether the call was a success - though any
// other arguments will also be forwarded to the original callback.
var wrappedCallback = function(success) {
if (success) {
// Merge any changes into attributes and clear changes.
self.merge(self.changes).reset();
// Add/remove from collection if persist was successful.
manageCollection();
// Trigger the event before executing the callback.
self.trigger(method);
}
// Store the return value of the callback.
var value;
// Run the supplied callback.
if (callback) value = callback.apply(self, arguments);
return value;
};
if (this.constructor.persistence) {
this.constructor.persistence[method](this, wrappedCallback);
} else {
wrappedCallback.call(this, true);
}
},
destroy: function(callback) {
this.callPersistMethod("destroy", callback);
return this;
},
id: function() {
return this.attributes.id || null;
},
merge: function(attributes) {
jQuery.extend(this.attributes, attributes);
return this;
},
newRecord: function() {
return this.id() === null;
},
reset: function() {
this.errors.clear();
this.changes = {};
return this;
},
save: function(callback) {
if (this.valid()) {
var method = this.newRecord() ? "create" : "update";
this.callPersistMethod(method, callback);
} else {
if (callback) callback(false);
}
return this;
},
trigger: function(name, data) {
var callbacks = this.callbacks[name];
if (callbacks) {
for (var i = 0; i < callbacks.length; i++) {
callbacks[i].apply(this, data || []);
}
}
return this;
},
update: function(attributes) {
this.merge(attributes).trigger("update");
return this;
},
valid: function() {
this.errors.clear();
this.validate();
return this.errors.size() === 0;
},
validate: function() {
return this;
}
};