This repository has been archived by the owner on Jul 7, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ampersand-io-model.js
162 lines (137 loc) · 4.46 KB
/
ampersand-io-model.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*$AMPERSAND_VERSION*/
var _ = require('underscore');
var AmpersandState = require('ampersand-state');
var AmpersandIO = require('ampersand-io');
var events = {
onFetch: 'fetch-response',
create: 'model-create',
update: 'model-update',
fetch: 'model-fetch',
remove: 'model-remove'
};
var AmpersandIOModel = function (attrs, options){
options || (options = {});
Base.call(this, attrs, options);
AmpersandIO.call(this, options.socket, options);
};
var IOMixin = AmpersandIO.extend({
events: events,
save: function (key, val, options) {
var attrs, event;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (key === null || typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
options = _.extend({validate: true}, options);
// If we're not waiting and attributes exist, save acts as
// `set(attr).save(null, opts)` with validation. Otherwise, check if
// the model will be valid when the attributes, if any, are set.
if (attrs && !options.wait) {
if (!this.set(attrs, options)){
return false;
}
} else {
if (!this._validate(attrs, options)){
return false;
}
}
// Set the event type
event = this.isNew() ? 'create' : 'update';
if (options.parse === void 0){
options.parse = true;
}
var model = this;
options.cb = options.callback;
options.callback = function cb(err, resp){
var serverAttrs = model.parse(resp, options);
if (err){
return callback(err, model, resp, options);
}
if (options.wait){
serverAttrs = _.extend(attrs || {}, serverAttrs);
}
if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
return callback(true, model, resp, options);
}
callback(null, model, resp, options);
};
this.emit(this.events[event], this, options);
return model;
},
// Fetch the model from the server. If the server's representation of the
// model differs from its current attributes, they will be overridden
fetch: function (options) {
options = options ? _.clone(options) : {};
if (options.parse === void 0){
options.parse = true;
}
var model = this;
options.cb = options.callback;
options.callback = function (err, resp){
callback(err, model, resp, options);
};
options.respCallback = function cb(response, serverCb){
model.removeListeners([model.events.onFetch]);
if (response.err){
return callback(response.err, model, response.data, options, serverCb);
}
if (!model.set(model.parse(response.data, options), options)) {
return callback(true, model, response.data, options, serverCb);
}
callback(null, model, response.data, options, serverCb);
};
var listener = {};
listener[this.events.onFetch] = { fn: options.respCallback, active: true};
this.addListeners(listener);
this.emit(this.events.fetch, this, options);
return model;
},
// Destroy this model on the server if it was already persisted.
// Optimistically removes the model from its collection, if it has one.
// If `wait: true` is passed, waits for the server to respond before removal.
destroy: function (options) {
options = options ? _.clone(options) : {};
var model = this;
var destroy = function () {
model.trigger('destroy', model, model.collection, options);
};
options.cb = options.callback;
options.callback = function cb(err, resp){
if (err){
return callback(err, model, resp, options);
}
if (options.wait || model.isNew()){
destroy();
}
callback(null, model, resp, options);
};
if (this.isNew()) {
options.callback();
return;
}
this.emit(this.events.remove, this, options);
if (!options.wait){
destroy();
}
return model;
}
});
// Aux func used to trigger errors if they exist, use the optional
// callback function if given and call the server ack callback if exists
var callback = function(err, model, resp, options, serverCb){
!serverCb || serverCb();
if (options.cb){
options.cb(err, model, resp);
}
if (err){
model.trigger('error', err, model, options);
}
};
var Base = AmpersandState.extend();
AmpersandIOModel.prototype = Object.create(Base.prototype);
_.extend(AmpersandIOModel.prototype, IOMixin.prototype);
AmpersandIOModel.extend = Base.extend;
module.exports = AmpersandIOModel;