'use strict';
var request = require('superagent');
var Promise = require('es6-promise').Promise;
function CollectionFactory(collectionOptions) {
if (!collectionOptions.resource) {
throw new Error('"resource" not specified. Construct the CollectionFactory as: new CollectionFactory({resource: MyResource})');
}
/**
* Collection
*
* Collection of resources returned by {@link Resource#query}.
*
* @class
*/
function Collection() {
var collection = Object.create(Array.prototype);
// Initialize the array.
collection = Array.apply(collection, arguments) || collection;
// Add all the class methods to the collection.
for (var method in Collection.prototype) {
if (Collection.prototype.hasOwnProperty(method)) {
collection[method] = Collection.prototype[method];
}
}
// Return the new collection object.
return collection;
}
Collection.prototype = {
/**
* Fetch previous page of collection and prepend items to current instance of collection.
*
* @example
* var collection = Resource.query();
* collection.then(function() {
* if (collection.hasPrev()) {
* var promise = collection.prevPage();
* promise.then(function(data) {
* // previous page has been fetched
* // `data` contains the newly fetched resources
* // `collection` now contains all resources
* });
* }
* });
*
* @example
* // Don't attach `then` to the `collection` because that was already resolved earlier as a response to {@link #query}.
* var promise = collection.prevPage();
* collection.then(function() {
* // WRONG. Already resolved.
* });
* promise.then(function() {
* // CORRECT. Will be resolved when previous page is fetched.
* });
*
* @return {Promise} promise
* @throws {RangeError} When previous page not available. You should check {@link #hasPrev} before calling `prevPage()`.
*/
prevPage: function() {
if (!this.prev_page) {
throw new RangeError("No previous page.");
}
return new Promise(function(resolve, reject) {
request
.get(this.prev_page)
.accept('json')
.end(function(response) {
if (response.error) {
var error = (response.body || JSON.parse(response.text)).error || response.error;
reject({error: error});
} else {
var body = response.body || JSON.parse(response.text);
var resources = [];
body.data.forEach(function(data) {
var resource = new collectionOptions.resource(data);
resources.push(resource);
}.bind(this));
this.unshift.apply(this, resources);
var newCollection = new Collection();
newCollection.push.apply(newCollection, this);
if (body.pagination && body.pagination.previous_page) {
this.prev_page = body.pagination.previous_page;
newCollection.prev_page = body.pagination.previous_page;
} else {
delete this.prev_page;
}
newCollection.next_page = this.next_page;
resolve(newCollection);
}
}.bind(this));
}.bind(this));
},
/**
* @alias prevPage
*/
previousPage: function() {
return this.prevPage.apply(this, arguments);
},
/**
* Fetch next page of collection and append resources to current instance of collection.
*
* @example
* var collection = Resource.query();
* collection.then(function() {
* if (collection.hasNext()) {
* var promise = collection.nextPage();
* promise.then(function(data) {
* // next page has been fetched
* // `data` contains the newly fetched resources
* // `collection` now contains all resources
* });
* }
* });
*
* @example
* // Don't attach `then` to the `collection` because that was already resolved earlier as a response to {@link #query}.
* var promise = collection.nextPage();
* collection.then(function() {
* // WRONG. Already resolved.
* });
* promise.then(function() {
* // CORRECT. Will be resolved when next page is fetched.
* });
*
* @return {Promise} promise
* @throws {RangeError} When next page not available. You should check {@link #hasNext} before calling `nextPage()`.
*/
nextPage: function() {
if (!this.next_page) {
throw new RangeError("No next page.");
}
return new Promise(function(resolve, reject) {
request
.get(this.next_page)
.accept('json')
.end(function(response) {
if (response.error) {
var error = (response.body || JSON.parse(response.text)).error || response.error;
reject({error: error});
} else {
var body = response.body || JSON.parse(response.text);
var newCollection = new Collection();
body.data.forEach(function(data) {
var resource = new collectionOptions.resource(data);
this.push(resource);
}.bind(this));
newCollection.push.apply(newCollection, this);
if (body.pagination && body.pagination.next_page) {
this.next_page = body.pagination.next_page;
newCollection.next_page = body.pagination.next_page;
} else {
delete this.next_page;
}
newCollection.prev_page = this.prev_page;
resolve(newCollection);
}
}.bind(this));
}.bind(this));
},
/**
* Check if there is previous page available.
*
* This method doesn't trigger any external request. The resolution is based on the result of the last query.
*
* @example
* var collection = Resource.query();
* if (collection.hasPrev()) {
* collection.prevPage();
* }
*
* @return {Boolean} has previous page
*/
hasPrev: function() {
return !! this.prev_page;
},
/**
* @alias hasPrev
*/
hasPrevious: function() {
return this.hasPrev.apply(this, arguments);
},
/**
* Check if there is next page available.
*
* This method doesn't trigger any external request. The resolution is based on the result of the last query.
*
* @example
* var collection = Resource.query();
* if (collection.hasNext()) {
* collection.nextPage();
* }
*
* @return {Boolean} has next page
*/
hasNext: function() {
return !! this.next_page;
}
};
return Collection;
}
module.exports = CollectionFactory;