Skip to content

Commit 079a9d9

Browse files
committed
Fixes #19.
Added lifecycle hooks to DS.save and DS.create. #7
1 parent a067045 commit 079a9d9

File tree

12 files changed

+478
-216
lines changed

12 files changed

+478
-216
lines changed

dist/angular-data.js

+239-108
Large diffs are not rendered by default.

dist/angular-data.min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/datastore/async_methods/create/index.js

+33-32
Original file line numberDiff line numberDiff line change
@@ -45,47 +45,48 @@ var utils = require('utils'),
4545
* - `{UnhandledError}`
4646
*/
4747
function create(resourceName, attrs) {
48-
var deferred = $q.defer();
48+
var deferred = services.$q.defer(),
49+
promise = deferred.promise;
50+
4951
if (!services.store[resourceName]) {
5052
deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'));
5153
} else if (!utils.isObject(attrs)) {
5254
deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object!', { attrs: { actual: typeof attrs, expected: 'object' } }));
53-
}
54-
55-
try {
56-
var resource = services.store[resourceName],
57-
_this = this,
58-
url = utils.makePath(resource.baseUrl || services.config.baseUrl, resource.endpoint || resource.name);
55+
} else {
56+
try {
57+
var resource = services.store[resourceName],
58+
_this = this;
5959

60-
if (resource.validate) {
61-
resource.validate(attrs, null, function (err) {
62-
if (err) {
63-
deferred.reject(err);
64-
} else {
60+
promise = promise
61+
.then(function (attrs) {
62+
return services.$q.promisify(resource.beforeValidate)(resourceName, attrs);
63+
})
64+
.then(function (attrs) {
65+
return services.$q.promisify(resource.validate)(resourceName, attrs);
66+
})
67+
.then(function (attrs) {
68+
return services.$q.promisify(resource.afterValidate)(resourceName, attrs);
69+
})
70+
.then(function (attrs) {
71+
return services.$q.promisify(resource.beforeCreate)(resourceName, attrs);
72+
})
73+
.then(function (attrs) {
74+
return _this.POST(utils.makePath(resource.baseUrl, resource.endpoint), attrs, null);
75+
})
76+
.then(function (data) {
77+
return services.$q.promisify(resource.afterCreate)(resourceName, data);
78+
})
79+
.then(function (data) {
80+
return _this.inject(resource.name, data);
81+
});
6582

66-
_this.POST(url, attrs, null).then(function (data) {
67-
try {
68-
deferred.resolve(_this.inject(resource.name, data));
69-
} catch (err) {
70-
deferred.reject(err);
71-
}
72-
}, deferred.reject);
73-
}
74-
});
75-
} else {
76-
_this.POST(url, attrs, null).then(function (data) {
77-
try {
78-
deferred.resolve(_this.inject(resource.name, data));
79-
} catch (err) {
80-
deferred.reject(err);
81-
}
82-
}, deferred.reject);
83+
deferred.resolve(attrs);
84+
} catch (err) {
85+
deferred.reject(new errors.UnhandledError(err));
8386
}
84-
} catch (err) {
85-
deferred.reject(new errors.UnhandledError(err));
8687
}
8788

88-
return deferred.promise;
89+
return promise;
8990
}
9091

9192
module.exports = create;

src/datastore/async_methods/destroy/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ var utils = require('utils'),
4545
* - `{UnhandledError}`
4646
*/
4747
function destroy(resourceName, id) {
48-
var deferred = $q.defer();
48+
var deferred = service.$q.defer();
4949
if (!services.store[resourceName]) {
5050
deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'));
5151
} else if (!utils.isString(id) && !utils.isNumber(id)) {

src/datastore/async_methods/find/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ var utils = require('utils'),
4949
* - `{UnhandledError}`
5050
*/
5151
function find(resourceName, id, options) {
52-
var deferred = $q.defer();
52+
var deferred = services.$q.defer();
5353
options = options || {};
5454

5555
if (!services.store[resourceName]) {

src/datastore/async_methods/findAll/index.js

+20-14
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function processResults(data, resourceName, queryHash) {
2626
return data;
2727
}
2828

29-
function _findAll(deferred, resourceName, params, options) {
29+
function _findAll(resourceName, params, options) {
3030
var resource = services.store[resourceName],
3131
_this = this;
3232

@@ -41,18 +41,19 @@ function _findAll(deferred, resourceName, params, options) {
4141

4242
if (!resource.pendingQueries[queryHash]) {
4343

44-
// This particular query has never even been started
45-
var url = utils.makePath(resource.baseUrl || services.config.baseUrl, resource.endpoint || resource.name);
46-
resource.pendingQueries[queryHash] = GET(url, { params: params }).then(function (data) {
47-
try {
48-
deferred.resolve(processResults.apply(_this, [data, resourceName, queryHash]));
49-
} catch (err) {
50-
deferred.reject(new errors.UnhandledError(err));
51-
}
52-
}, deferred.reject);
44+
// This particular query has never even been made
45+
resource.pendingQueries[queryHash] = GET(utils.makePath(resource.baseUrl, resource.endpoint), { params: params })
46+
.then(function (data) {
47+
try {
48+
return processResults.apply(_this, [data, resourceName, queryHash]);
49+
} catch (err) {
50+
throw new errors.UnhandledError(err);
51+
}
52+
});
5353
}
54+
return resource.pendingQueries[queryHash];
5455
} else {
55-
deferred.resolve(this.filter(resourceName, params, options));
56+
return this.filter(resourceName, params, options);
5657
}
5758
}
5859

@@ -124,7 +125,9 @@ function _findAll(deferred, resourceName, params, options) {
124125
* - `{UnhandledError}`
125126
*/
126127
function findAll(resourceName, params, options) {
127-
var deferred = services.$q.defer();
128+
var deferred = services.$q.defer(),
129+
promise = deferred.promise,
130+
_this = this;
128131

129132
options = options || {};
130133

@@ -136,13 +139,16 @@ function findAll(resourceName, params, options) {
136139
deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }));
137140
} else {
138141
try {
139-
_findAll.apply(this, [deferred, resourceName, params, options]);
142+
promise = promise.then(function () {
143+
return _findAll.apply(_this, [resourceName, params, options]);
144+
});
145+
deferred.resolve();
140146
} catch (err) {
141147
deferred.reject(new errors.UnhandledError(err));
142148
}
143149
}
144150

145-
return deferred.promise;
151+
return promise;
146152
}
147153

148154
module.exports = findAll;

src/datastore/async_methods/save/index.js

+32-34
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,8 @@
11
var utils = require('utils'),
22
errors = require('errors'),
33
services = require('services'),
4-
PUT = require('../../http').PUT,
54
errorPrefix = 'DS.save(resourceName, id[, options]): ';
65

7-
function _save(deferred, resource, id, options) {
8-
var _this = this;
9-
var url = utils.makePath(resource.baseUrl || services.config.baseUrl, resource.endpoint || resource.name, id);
10-
PUT(url, resource.index[id], null).then(function (data) {
11-
var saved = _this.inject(resource.name, data, options);
12-
resource.saved[id] = utils.updateTimestamp(resource.saved[id]);
13-
deferred.resolve(saved);
14-
}, deferred.reject);
15-
}
16-
176
/**
187
* @doc method
198
* @id DS.async_methods:save
@@ -58,7 +47,8 @@ function _save(deferred, resource, id, options) {
5847
* - `{UnhandledError}`
5948
*/
6049
function save(resourceName, id, options) {
61-
var deferred = $q.defer();
50+
var deferred = services.$q.defer(),
51+
promise = deferred.promise;
6252

6353
options = options || {};
6454

@@ -68,33 +58,41 @@ function save(resourceName, id, options) {
6858
deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }));
6959
} else if (!utils.isObject(options)) {
7060
deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be an object!', { options: { actual: typeof options, expected: 'object' } }));
61+
} else if (!(id in services.store[resourceName].index)) {
62+
deferred.reject(new errors.RuntimeError(errorPrefix + 'id: "' + id + '" not found!'));
7163
} else {
72-
var _this = this;
64+
var resource = services.store[resourceName],
65+
_this = this;
7366

74-
try {
75-
var resource = services.store[resourceName];
67+
promise = promise
68+
.then(function (attrs) {
69+
return services.$q.promisify(resource.beforeValidate)(resourceName, attrs);
70+
})
71+
.then(function (attrs) {
72+
return services.$q.promisify(resource.validate)(resourceName, attrs);
73+
})
74+
.then(function (attrs) {
75+
return services.$q.promisify(resource.afterValidate)(resourceName, attrs);
76+
})
77+
.then(function (attrs) {
78+
return services.$q.promisify(resource.beforeUpdate)(resourceName, attrs);
79+
})
80+
.then(function (attrs) {
81+
return _this.PUT(utils.makePath(resource.baseUrl, resource.endpoint, id), attrs, null);
82+
})
83+
.then(function (data) {
84+
return services.$q.promisify(resource.afterUpdate)(resourceName, data);
85+
})
86+
.then(function (data) {
87+
var saved = _this.inject(resource.name, data, options);
88+
resource.saved[id] = utils.updateTimestamp(resource.saved[id]);
89+
return saved;
90+
});
7691

77-
if (resource.schema) {
78-
resource.schema.validate(resource.index[id], function (err) {
79-
if (err) {
80-
deferred.reject(err);
81-
} else {
82-
_save.call(_this, deferred, resource, id, options);
83-
}
84-
});
85-
} else {
86-
_save.call(_this, deferred, resource, id, options);
87-
}
88-
} catch (err) {
89-
if (!(err instanceof errors.UnhandledError)) {
90-
deferred.reject(new errors.UnhandledError(err));
91-
} else {
92-
deferred.reject(err);
93-
}
94-
}
92+
deferred.resolve(resource.index[id]);
9593
}
9694

97-
return deferred.promise;
95+
return promise;
9896
}
9997

10098
module.exports = save;

src/datastore/index.js

+28-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
var utils = require('utils'),
22
errors = require('errors'),
3-
IllegalArgumentError = errors.IllegalArgumentError,
43
services = require('services'),
54
errorPrefix = 'DSProvider.config(options): ';
65

@@ -19,7 +18,8 @@ var utils = require('utils'),
1918
* ## Example:
2019
* ```js
2120
* DSProvider.config({
22-
* baseUrl: 'http://myapp.com/api'
21+
* baseUrl: 'http://myapp.com/api',
22+
* idAttribute: '_id'
2323
* });
2424
* ```
2525
*
@@ -33,12 +33,34 @@ function config(options) {
3333
options = options || {};
3434

3535
if (!utils.isObject(options)) {
36-
throw new IllegalArgumentError(errorPrefix + 'options: Must be an object!', { actual: typeof options, expected: 'object' });
37-
} else if (!utils.isString(options.baseUrl)) {
38-
throw new IllegalArgumentError(errorPrefix + 'options: Must be an object!', { baseUrl: { actual: typeof options, expected: 'object' } });
36+
throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { actual: typeof options, expected: 'object' });
37+
} else if ('baseUrl' in options && !utils.isString(options.baseUrl)) {
38+
throw new errors.IllegalArgumentError(errorPrefix + 'options.baseUrl: Must be a string!', { baseUrl: { actual: typeof options.baseUrl, expected: 'string' } });
39+
} else if ('idAttribute' in options && !utils.isString(options.idAttribute)) {
40+
throw new errors.IllegalArgumentError(errorPrefix + 'options.idAttribute: Must be a string!', { idAttribute: { actual: typeof options.idAttribute, expected: 'string' } });
41+
} else if ('mergeStrategy' in options && !utils.isString(options.mergeStrategy)) {
42+
throw new errors.IllegalArgumentError(errorPrefix + 'options.mergeStrategy: Must be a string!', { mergeStrategy: { actual: typeof options.mergeStrategy, expected: 'string' } });
43+
} else if ('beforeValidate' in options && !utils.isFunction(options.beforeValidate)) {
44+
throw new errors.IllegalArgumentError(errorPrefix + 'options.beforeValidate: Must be a function!', { beforeValidate: { actual: typeof options.beforeValidate, expected: 'function' } });
45+
} else if ('validate' in options && !utils.isFunction(options.validate)) {
46+
throw new errors.IllegalArgumentError(errorPrefix + 'options.validate: Must be a function!', { validate: { actual: typeof options.validate, expected: 'function' } });
47+
} else if ('afterValidate' in options && !utils.isFunction(options.afterValidate)) {
48+
throw new errors.IllegalArgumentError(errorPrefix + 'options.afterValidate: Must be a function!', { afterValidate: { actual: typeof options.afterValidate, expected: 'function' } });
49+
} else if ('beforeCreate' in options && !utils.isFunction(options.beforeCreate)) {
50+
throw new errors.IllegalArgumentError(errorPrefix + 'options.beforeCreate: Must be a function!', { beforeCreate: { actual: typeof options.beforeCreate, expected: 'function' } });
51+
} else if ('afterCreate' in options && !utils.isFunction(options.afterCreate)) {
52+
throw new errors.IllegalArgumentError(errorPrefix + 'options.afterCreate: Must be a function!', { afterCreate: { actual: typeof options.afterCreate, expected: 'function' } });
53+
} else if ('beforeUpdate' in options && !utils.isFunction(options.beforeUpdate)) {
54+
throw new errors.IllegalArgumentError(errorPrefix + 'options.beforeUpdate: Must be a function!', { beforeUpdate: { actual: typeof options.beforeUpdate, expected: 'function' } });
55+
} else if ('afterUpdate' in options && !utils.isFunction(options.afterUpdate)) {
56+
throw new errors.IllegalArgumentError(errorPrefix + 'options.afterUpdate: Must be a function!', { afterUpdate: { actual: typeof options.afterUpdate, expected: 'function' } });
57+
} else if ('beforeDestroy' in options && !utils.isFunction(options.beforeDestroy)) {
58+
throw new errors.IllegalArgumentError(errorPrefix + 'options.beforeDestroy: Must be a function!', { beforeDestroy: { actual: typeof options.beforeDestroy, expected: 'function' } });
59+
} else if ('afterDestroy' in options && !utils.isFunction(options.afterDestroy)) {
60+
throw new errors.IllegalArgumentError(errorPrefix + 'options.afterDestroy: Must be a function!', { afterDestroy: { actual: typeof options.afterDestroy, expected: 'function' } });
3961
}
4062

41-
utils.deepMixIn(services.config, options);
63+
services.config = new services.BaseConfig(options);
4264
}
4365

4466
/**

src/datastore/services/index.js

+65-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,69 @@
1-
module.exports = {
1+
function lifecycleNoop(resourceName, attrs, cb) {
2+
cb(null, attrs);
3+
}
4+
5+
var services = module.exports = {
26
config: {
37
idAttribute: 'id'
48
},
5-
store: {}
9+
store: {},
10+
BaseConfig: function (options) {
11+
if ('idAttribute' in options) {
12+
this.idAttribute = options.idAttribute;
13+
}
14+
15+
if ('baseUrl' in options) {
16+
this.baseUrl = options.baseUrl;
17+
}
18+
19+
if ('beforeValidate' in options) {
20+
this.beforeValidate = options.beforeValidate;
21+
}
22+
23+
if ('validate' in options) {
24+
this.validate = options.validate;
25+
}
26+
27+
if ('afterValidate' in options) {
28+
this.afterValidate = options.afterValidate;
29+
}
30+
31+
if ('beforeCreate' in options) {
32+
this.beforeCreate = options.beforeCreate;
33+
}
34+
35+
if ('afterCreate' in options) {
36+
this.afterCreate = options.afterCreate;
37+
}
38+
39+
if ('beforeUpdate' in options) {
40+
this.beforeUpdate = options.beforeUpdate;
41+
}
42+
43+
if ('afterUpdate' in options) {
44+
this.afterUpdate = options.afterUpdate;
45+
}
46+
47+
if ('beforeDestroy' in options) {
48+
this.beforeDestroy = options.beforeDestroy;
49+
}
50+
51+
if ('afterDestroy' in options) {
52+
this.afterDestroy = options.afterDestroy;
53+
}
54+
}
655
};
56+
57+
58+
services.BaseConfig.prototype.idAttribute = 'id';
59+
services.BaseConfig.prototype.baseUrl = '';
60+
services.BaseConfig.prototype.endpoint = '';
61+
services.BaseConfig.prototype.beforeValidate = lifecycleNoop;
62+
services.BaseConfig.prototype.validate = lifecycleNoop;
63+
services.BaseConfig.prototype.afterValidate = lifecycleNoop;
64+
services.BaseConfig.prototype.beforeCreate = lifecycleNoop;
65+
services.BaseConfig.prototype.afterCreate = lifecycleNoop;
66+
services.BaseConfig.prototype.beforeUpdate = lifecycleNoop;
67+
services.BaseConfig.prototype.afterUpdate = lifecycleNoop;
68+
services.BaseConfig.prototype.beforeDestroy = lifecycleNoop;
69+
services.BaseConfig.prototype.afterDestroy = lifecycleNoop;

0 commit comments

Comments
 (0)