Skip to content

Commit 3d3f6f5

Browse files
committed
Merge pull request #20 from jmdobry/feature-lifecycle
feature-lifecycle
2 parents a067045 + 82225b7 commit 3d3f6f5

File tree

21 files changed

+994
-349
lines changed

21 files changed

+994
-349
lines changed

dist/angular-data.js

Lines changed: 322 additions & 174 deletions
Large diffs are not rendered by default.

dist/angular-data.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

karma.start.js

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Setup global test variables
2-
var $rootScope, $q, $log, DS, app, $httpBackend, p1, p2, p3, p4;
2+
var $rootScope, $q, $log, DSProvider, DS, app, $httpBackend, p1, p2, p3, p4;
3+
4+
var lifecycle = {};
35

46
// Helper globals
57
var fail = function (msg) {
@@ -27,20 +29,66 @@ angular.module('app', ['ng', 'jmdobry.angular-data']);
2729

2830
// Setup before each test
2931
beforeEach(function (done) {
30-
module('app');
32+
lifecycle.beforeValidate = function (resourceName, attrs, cb) {
33+
lifecycle.beforeValidate.callCount += 1;
34+
cb(null, attrs);
35+
};
36+
lifecycle.validate = function (resourceName, attrs, cb) {
37+
lifecycle.validate.callCount += 1;
38+
cb(null, attrs);
39+
};
40+
lifecycle.afterValidate = function (resourceName, attrs, cb) {
41+
lifecycle.afterValidate.callCount += 1;
42+
cb(null, attrs);
43+
};
44+
lifecycle.beforeCreate = function (resourceName, attrs, cb) {
45+
lifecycle.beforeCreate.callCount += 1;
46+
cb(null, attrs);
47+
};
48+
lifecycle.afterCreate = function (resourceName, attrs, cb) {
49+
lifecycle.afterCreate.callCount += 1;
50+
cb(null, attrs);
51+
};
52+
lifecycle.beforeUpdate = function (resourceName, attrs, cb) {
53+
lifecycle.beforeUpdate.callCount += 1;
54+
cb(null, attrs);
55+
};
56+
lifecycle.afterUpdate = function (resourceName, attrs, cb) {
57+
lifecycle.afterUpdate.callCount += 1;
58+
cb(null, attrs);
59+
};
60+
lifecycle.beforeDestroy = function (resourceName, attrs, cb) {
61+
lifecycle.beforeDestroy.callCount += 1;
62+
cb(null, attrs);
63+
};
64+
lifecycle.afterDestroy = function (resourceName, attrs, cb) {
65+
lifecycle.afterDestroy.callCount += 1;
66+
cb(null, attrs);
67+
};
68+
module('app', function (_DSProvider_) {
69+
DSProvider = _DSProvider_;
70+
DSProvider.config({
71+
baseUrl: 'http://test.angular-cache.com',
72+
beforeValidate: lifecycle.beforeValidate,
73+
validate: lifecycle.validate,
74+
afterValidate: lifecycle.afterValidate,
75+
beforeCreate: lifecycle.beforeCreate,
76+
afterCreate: lifecycle.afterCreate,
77+
beforeUpdate: lifecycle.beforeUpdate,
78+
afterUpdate: lifecycle.afterUpdate,
79+
beforeDestroy: lifecycle.beforeDestroy,
80+
afterDestroy: lifecycle.afterDestroy
81+
});
82+
});
3183
inject(function (_$rootScope_, _$q_, _$httpBackend_, _DS_) {
3284
// Setup global mocks
3385
$q = _$q_;
3486
$rootScope = _$rootScope_;
3587
DS = _DS_;
3688
$httpBackend = _$httpBackend_;
37-
app = {
38-
baseUrl: 'http://test.angular-cache.com'
39-
};
4089
DS.defineResource({
4190
name: 'post',
42-
endpoint: '/posts',
43-
baseUrl: app.baseUrl
91+
endpoint: '/posts'
4492
});
4593
$log = {
4694
warn: function () {
@@ -61,6 +109,16 @@ beforeEach(function (done) {
61109
sinon.spy($log, 'info');
62110
sinon.spy($log, 'error');
63111
sinon.spy($log, 'debug');
112+
113+
lifecycle.beforeValidate.callCount = 0;
114+
lifecycle.validate.callCount = 0;
115+
lifecycle.afterValidate.callCount = 0;
116+
lifecycle.beforeCreate.callCount = 0;
117+
lifecycle.afterCreate.callCount = 0;
118+
lifecycle.beforeUpdate.callCount = 0;
119+
lifecycle.afterUpdate.callCount = 0;
120+
lifecycle.beforeDestroy.callCount = 0;
121+
lifecycle.afterDestroy.callCount = 0;
64122
});
65123

66124
p1 = { author: 'John', age: 30, id: 5 };
@@ -79,6 +137,7 @@ afterEach(function () {
79137
$log.info.restore();
80138
$log.error.restore();
81139
$log.debug.restore();
140+
82141
$httpBackend.verifyNoOutstandingExpectation();
83142
$httpBackend.verifyNoOutstandingRequest();
84143
});

src/datastore/async_methods/create/index.js

Lines changed: 33 additions & 32 deletions
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

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,31 +45,36 @@ var utils = require('utils'),
4545
* - `{UnhandledError}`
4646
*/
4747
function destroy(resourceName, id) {
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.isString(id) && !utils.isNumber(id)) {
5254
deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }));
53-
}
54-
55-
try {
55+
} else {
5656
var resource = services.store[resourceName],
57-
_this = this,
58-
url = utils.makePath(resource.baseUrl || services.config.baseUrl, resource.endpoint || resource.name, id);
57+
_this = this;
5958

60-
_this.DEL(url, null).then(function () {
61-
try {
59+
promise = promise
60+
.then(function (attrs) {
61+
return services.$q.promisify(resource.beforeDestroy)(resourceName, attrs);
62+
})
63+
.then(function () {
64+
return _this.DEL(utils.makePath(resource.baseUrl, resource.endpoint, id), null);
65+
})
66+
.then(function () {
67+
return services.$q.promisify(resource.afterDestroy)(resourceName, resource.index[id]);
68+
})
69+
.then(function () {
6270
_this.eject(resourceName, id);
63-
deferred.resolve(id);
64-
} catch (err) {
65-
deferred.reject(err);
66-
}
67-
}, deferred.reject);
68-
} catch (err) {
69-
deferred.reject(new errors.UnhandledError(err));
71+
return id;
72+
});
73+
74+
deferred.resolve(resource.index[id]);
7075
}
7176

72-
return deferred.promise;
77+
return promise;
7378
}
7479

7580
module.exports = destroy;

src/datastore/async_methods/find/index.js

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
var utils = require('utils'),
22
errors = require('errors'),
33
services = require('services'),
4-
GET = require('../../http').GET,
54
errorPrefix = 'DS.find(resourceName, id[, options]): ';
65

76
/**
@@ -49,7 +48,9 @@ var utils = require('utils'),
4948
* - `{UnhandledError}`
5049
*/
5150
function find(resourceName, id, options) {
52-
var deferred = $q.defer();
51+
var deferred = services.$q.defer(),
52+
promise = deferred.promise;
53+
5354
options = options || {};
5455

5556
if (!services.store[resourceName]) {
@@ -59,43 +60,35 @@ function find(resourceName, id, options) {
5960
} else if (!utils.isObject(options)) {
6061
deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }));
6162
} else {
62-
var _this = this;
63-
6463
try {
65-
var resource = services.store[resourceName];
64+
var resource = services.store[resourceName],
65+
_this = this;
6666

67-
if (id in resource.index && !options.bypassCache) {
68-
deferred.resolve(_this.get(resourceName, id));
69-
} else {
70-
var url = utils.makePath(resource.baseUrl || services.config.baseUrl, resource.endpoint || resource.name, id),
71-
config = null;
67+
if (options.bypassCache) {
68+
delete resource.completedQueries[id];
69+
}
7270

73-
if (options.bypassCache) {
74-
config = {
75-
headers: {
76-
'Last-Modified': new Date(resource.modified[id])
77-
}
78-
};
71+
if (!(id in resource.completedQueries)) {
72+
if (!(id in resource.pendingQueries)) {
73+
promise = resource.pendingQueries[id] = _this.GET(utils.makePath(resource.baseUrl, resource.endpoint, id), null)
74+
.then(function (data) {
75+
// Query is no longer pending
76+
delete resource.pendingQueries[id];
77+
resource.completedQueries[id] = new Date().getTime();
78+
return _this.inject(resourceName, data);
79+
});
7980
}
80-
GET(url, config).then(function (data) {
81-
try {
82-
_this.inject(resourceName, data);
83-
deferred.resolve(_this.get(resourceName, id));
84-
} catch (err) {
85-
deferred.reject(err);
86-
}
87-
}, deferred.reject);
88-
}
89-
} catch (err) {
90-
if (!(err instanceof errors.UnhandledError)) {
91-
deferred.reject(new errors.UnhandledError(err));
81+
82+
return resource.pendingQueries[id];
9283
} else {
93-
deferred.reject(err);
84+
deferred.resolve(_this.get(resourceName, id));
9485
}
86+
} catch (err) {
87+
deferred.reject(err);
9588
}
9689
}
9790

98-
return deferred.promise;
91+
return promise;
9992
}
10093

10194
module.exports = find;

0 commit comments

Comments
 (0)