diff --git a/Gruntfile.js b/Gruntfile.js index d52290e..9551cce 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -74,8 +74,6 @@ module.exports = function (grunt) { options: { alias: [ 'lib/observe-js.js:observejs', - 'src/datastore/adapters/http/index.js:HttpAdapter', - 'src/datastore/services/index.js:services', 'src/errors/index.js:errors', 'src/utils/index.js:utils' ], diff --git a/README.md b/README.md index c7b1225..d179406 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ __Data store for Angular.js.__ -__Current version:__ 0.5.0 +__Current version:__ 0.6.0 Angular-data is in a pre-1.0.0 development stage; the API is fluctuating, not a lot of tests yet, etc. diff --git a/bower.json b/bower.json index 6421beb..a1aa39c 100644 --- a/bower.json +++ b/bower.json @@ -2,7 +2,7 @@ "author": "Jason Dobry", "name": "angular-data", "description": "Data store for Angular.js.", - "version": "0.5.0", + "version": "0.6.0", "homepage": "http://jmdobry.github.io/angular-data/", "repository": { "type": "git", diff --git a/dist/angular-data.js b/dist/angular-data.js index 0c30ee7..9ea6c1d 100644 --- a/dist/angular-data.js +++ b/dist/angular-data.js @@ -1,7 +1,7 @@ /** * @author Jason Dobry * @file angular-data.js - * @version 0.5.0 - Homepage + * @version 0.6.0 - Homepage * @copyright (c) 2014 Jason Dobry * @license MIT * @@ -1067,245 +1067,502 @@ var toString = require('../lang/toString'); module.exports = upperCase; -},{"../lang/toString":19}],"clHM+W":[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'); +},{"../lang/toString":19}],27:[function(require,module,exports){ +function DSHttpAdapterProvider() { -function _$http(deferred, config) { - var start = new Date().getTime(); + this.$get = ['$http', '$log', 'DSUtils', function ($http, $log, DSUtils) { - services.$http(config).success(function (data, status, headers, config) { - services.$log.debug(config.method + ' request:' + config.url + ' Time taken: ' + (new Date().getTime() - start) + 'ms', arguments); - deferred.resolve(data); - }).error(function (data, status, headers, config) { - services.$log.error(arguments); - deferred.reject(data); - }); + var defaults = this.defaults = { + serialize: function (data) { + return data; + }, + deserialize: function (data) { + return data.data; + } + }; + + return { + defaults: defaults, + + /** + * @doc method + * @id DSHttpAdapter.methods:HTTP + * @name HTTP + * @description + * Wrapper for `$http()`. + * + * ## Signature: + * ```js + * DS.HTTP(config) + * ``` + * + * ## Example: + * + * ```js + * works the same as $http() + * ``` + * + * @param {object} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + HTTP: HTTP, + + /** + * @doc method + * @id DSHttpAdapter.methods:GET + * @name GET + * @description + * Wrapper for `$http.get()`. + * + * ## Signature: + * ```js + * DS.GET(url[, config]) + * ``` + * + * ## Example: + * + * ```js + * Works the same as $http.get() + * ``` + * + * @param {string} url The url of the request. + * @param {object=} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + GET: GET, + + /** + * @doc method + * @id DSHttpAdapter.methods:POST + * @name POST + * @description + * Wrapper for `$http.post()`. + * + * ## Signature: + * ```js + * DS.POST(url[, attrs][, config]) + * ``` + * + * ## Example: + * + * ```js + * Works the same as $http.post() + * ``` + * + * @param {string} url The url of the request. + * @param {object=} attrs Request payload. + * @param {object=} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + POST: POST, + + /** + * @doc method + * @id DSHttpAdapter.methods:PUT + * @name PUT + * @description + * Wrapper for `$http.put()`. + * + * ## Signature: + * ```js + * DS.PUT(url[, attrs][, config]) + * ``` + * + * ## Example: + * + * ```js + * Works the same as $http.put() + * ``` + * + * @param {string} url The url of the request. + * @param {object=} attrs Request payload. + * @param {object=} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + PUT: PUT, + + /** + * @doc method + * @id DSHttpAdapter.methods:DEL + * @name DEL + * @description + * Wrapper for `$http.delete()`. + * + * ## Signature: + * ```js + * DS.DEL(url[, config]) + * ``` + * + * ## Example: + * + * ```js + * Works the same as $http.delete + * ``` + * + * @param {string} url The url of the request. + * @param {object} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + DEL: DEL, + + find: find, + + findAll: findAll, + + create: create, + + createMany: function () { + throw new Error('Not yet implemented!'); + }, + + update: update, + + updateMany: function () { + throw new Error('Not yet implemented!'); + }, + + destroy: destroy, + + destroyMany: function () { + throw new Error('Not yet implemented!'); + } + }; + + function HTTP(config) { + var start = new Date().getTime(); + + return $http(config).then(function (data) { + $log.debug(data.config.method + ' request:' + data.config.url + ' Time taken: ' + (new Date().getTime() - start) + 'ms', arguments); + return defaults.deserialize(data); + }); + } + + function GET(url, config) { + config = config || {}; + return HTTP(DSUtils.deepMixIn(config, { + url: url, + method: 'GET' + })); + } + + function POST(url, attrs, config) { + config = config || {}; + return HTTP(DSUtils.deepMixIn(config, { + url: url, + data: attrs, + method: 'POST' + })); + } + + function PUT(url, attrs, config) { + config = config || {}; + return HTTP(DSUtils.deepMixIn(config, { + url: url, + data: attrs, + method: 'PUT' + })); + } + + function DEL(url, config) { + config = config || {}; + return this.HTTP(DSUtils.deepMixIn(config, { + url: url, + method: 'DELETE' + })); + } + + /** + * @doc method + * @id DSHttpAdapter.methods:find + * @name find + * @param {object} resourceConfig Properties: + * - `{string}` - `baseUrl` - Base url. + * - `{string=}` - `endpoint` - Endpoint path for the resource. + * @param {string|number} id + * @param {object=} options + * @returns {Promise} Promise. + */ + function find(resourceConfig, id, options) { + return this.GET( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint, id), + options + ); + } + + function findAll(resourceConfig, params, options) { + options = options || {}; + options.params = options.params || {}; + DSUtils.deepMixIn(options, params); + return this.GET( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint), + options + ); + } + + function create(resourceConfig, attrs, options) { + return this.POST( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint), + defaults.serialize(attrs), + options + ); + } + + function update(resourceConfig, attrs, options) { + return this.PUT( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint, attrs[resourceConfig.idAttribute]), + defaults.serialize(attrs), + options + ); + } + + function destroy(resourceConfig, id, options) { + return this.DEL( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint, id), + options + ); + } + }]; } -/** +module.exports = DSHttpAdapterProvider; + +},{}],28:[function(require,module,exports){ +/*! * @doc method - * @id DS.async_methods:HTTP - * @name HTTP - * @description - * Wrapper for `$http()`. - * - * ## Signature: - * ```js - * DS.HTTP(config) - * ``` - * - * ## Example: - * - * ```js - * works the same as $http() - * ``` - * - * @param {object} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. + * @id BinaryHeap.private_functions:bubbleUp + * @name bubbleUp + * @param {array} heap The heap. + * @param {function} weightFunc The weight function. + * @param {number} n The index of the element to bubble up. */ -function HTTP(config) { - var deferred = services.$q.defer(); - - try { - if (!services.$rootScope.$$phase) { - services.$rootScope.$apply(function () { - _$http(deferred, config); - }); +function bubbleUp(heap, weightFunc, n) { + var element = heap[n], + weight = weightFunc(element); + // When at 0, an element can not go up any further. + while (n > 0) { + // Compute the parent element's index, and fetch it. + var parentN = Math.floor((n + 1) / 2) - 1, + parent = heap[parentN]; + // If the parent has a lesser weight, things are in order and we + // are done. + if (weight >= weightFunc(parent)) { + break; } else { - _$http(deferred, config); + heap[parentN] = element; + heap[n] = parent; + n = parentN; } - } catch (err) { - deferred.reject(new errors.UnhandledError(err)); } +} + +/*! + * @doc method + * @id BinaryHeap.private_functions:bubbleDown + * @name bubbleDown + * @param {array} heap The heap. + * @param {function} weightFunc The weight function. + * @param {number} n The index of the element to sink down. + */ +function bubbleDown(heap, weightFunc, n) { + var length = heap.length, + node = heap[n], + nodeWeight = weightFunc(node); + + while (true) { + var child2N = (n + 1) * 2, + child1N = child2N - 1; + var swap = null; + if (child1N < length) { + var child1 = heap[child1N], + child1Weight = weightFunc(child1); + // If the score is less than our node's, we need to swap. + if (child1Weight < nodeWeight) { + swap = child1N; + } + } + // Do the same checks for the other child. + if (child2N < length) { + var child2 = heap[child2N], + child2Weight = weightFunc(child2); + if (child2Weight < (swap === null ? nodeWeight : weightFunc(heap[child1N]))) { + swap = child2N; + } + } - return deferred.promise; + if (swap === null) { + break; + } else { + heap[n] = heap[swap]; + heap[swap] = node; + n = swap; + } + } } /** - * @doc method - * @id DS.async_methods:GET - * @name GET + * @doc function + * @id BinaryHeap.class:constructor + * @name BinaryHeap * @description - * Wrapper for `$http.get()`. - * - * ## Signature: - * ```js - * DS.GET(url[, config]) - * ``` + * BinaryHeap implementation of a priority queue. * * ## Example: - * * ```js - * Works the same as $http.get() + * angular.module('app').controller(function (BinaryHeap) { + * var bHeap = new BinaryHeap(function (x) { + * return x.value; + * }); + * }); * ``` * - * @param {string} url The url of the request. - * @param {object=} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. + * @param {function=} weightFunc Function that returns the value that should be used for node value comparison. */ -function GET(url, config) { - config = config || {}; - return HTTP(utils.deepMixIn(config, { - url: url, - method: 'GET' - })); +function BinaryHeap(weightFunc) { + if (weightFunc && typeof weightFunc !== 'function') { + throw new Error('BinaryHeap(weightFunc): weightFunc: must be a function!'); + } + weightFunc = defaults.userProvidedDefaultWeightFunc || defaults.defaultWeightFunc; + this.weightFunc = weightFunc; + this.heap = []; } /** * @doc method - * @id DS.async_methods:PUT - * @name PUT + * @id BinaryHeap.instance_methods:push + * @name push(node) * @description - * Wrapper for `$http.put()`. - * - * ## Signature: - * ```js - * DS.PUT(url[, attrs][, config]) - * ``` - * - * ## Example: - * - * ```js - * Works the same as $http.put() - * ``` - * - * @param {string} url The url of the request. - * @param {object=} attrs Request payload. - * @param {object=} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. + * Push an element into the binary heap. + * @param {*} node The element to push into the binary heap. */ -function PUT(url, attrs, config) { - config = config || {}; - return HTTP(utils.deepMixIn(config, { - url: url, - data: attrs, - method: 'PUT' - })); -} +BinaryHeap.prototype.push = function (node) { + this.heap.push(node); + bubbleUp(this.heap, this.weightFunc, this.heap.length - 1); +}; /** * @doc method - * @id DS.async_methods:POST - * @name POST + * @id BinaryHeap.instance_methods:peek + * @name peek * @description - * Wrapper for `$http.post()`. - * - * ## Signature: - * ```js - * DS.POST(url[, attrs][, config]) - * ``` - * - * ## Example: - * - * ```js - * Works the same as $http.post() - * ``` - * - * @param {string} url The url of the request. - * @param {object=} attrs Request payload. - * @param {object=} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. + * Return, but do not remove, the minimum element in the binary heap. + * @returns {*} peeked node */ -function POST(url, attrs, config) { - config = config || {}; - return HTTP(utils.deepMixIn(config, { - url: url, - data: attrs, - method: 'POST' - })); -} +BinaryHeap.prototype.peek = function () { + return this.heap[0]; +}; /** * @doc method - * @id DS.async_methods:DEL - * @name DEL + * @id BinaryHeap.instance_methods:pop + * @name pop * @description - * Wrapper for `$http.delete()`. - * - * ## Signature: - * ```js - * DS.DEL(url[, config]) - * ``` - * - * ## Example: - * - * ```js - * Works the same as $http.delete - * ``` - * - * @param {string} url The url of the request. - * @param {object} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. + * Remove and return the minimum element in the binary heap. + * @returns {*} popped node */ -function DEL(url, config) { - config = config || {}; - return HTTP(utils.deepMixIn(config, { - url: url, - method: 'DELETE' - })); -} +BinaryHeap.prototype.pop = function () { + var front = this.heap[0], + end = this.heap.pop(); + if (this.heap.length > 0) { + this.heap[0] = end; + bubbleDown(this.heap, this.weightFunc, 0); + } + return front; +}; -module.exports = { - /** - * @doc method - * @id DS.async_methods:HTTP - * @name HTTP - * @methodOf DS - * @description - * See [DS.HTTP](/documentation/api/api/DS.async_methods:HTTP). - */ - HTTP: HTTP, +/** + * @doc method + * @id BinaryHeap.instance_methods:remove + * @name remove + * @description + * Remove the first node in the priority queue that satisfies angular.equals comparison with the given node. + * @param {*} node The node to remove. + * @returns {*} The removed node. + */ +BinaryHeap.prototype.remove = function (node) { + var length = this.heap.length; + for (var i = 0; i < length; i++) { + if (angular.equals(this.heap[i], node)) { + var removed = this.heap[i], + end = this.heap.pop(); + if (i !== length - 1) { + this.heap[i] = end; + bubbleUp(this.heap, this.weightFunc, i); + bubbleDown(this.heap, this.weightFunc, i); + } + return removed; + } + } + return null; +}; - /** - * @doc method - * @id DS.async_methods:GET - * @name GET - * @methodOf DS - * @description - * See [DS.GET](/documentation/api/api/DS.async_methods:GET). - */ - GET: GET, +/** + * @doc method + * @id BinaryHeap.instance_methods:removeAll + * @name removeAll + * @description + * Remove all nodes from this BinaryHeap. + */ +BinaryHeap.prototype.removeAll = function () { + this.heap = []; +}; - /** - * @doc method - * @id DS.async_methods:POST - * @name POST - * @methodOf DS - * @description - * See [DS.POST](/documentation/api/api/DS.async_methods:POST). - */ - POST: POST, +/** + * @doc method + * @id BinaryHeap.instance_methods:size + * @name size + * @description + * Return the size of the priority queue. + * @returns {number} The size of the priority queue. + */ +BinaryHeap.prototype.size = function () { + return this.heap.length; +}; + +/** + * @doc interface + * @id BinaryHeapProvider + * @name BinaryHeapProvider + */ +function BinaryHeapProvider() { + + var defaults = { + defaultWeightFunc: function (x) { + return x; + }, + userProvidedDefaultWeightFunc: null + }; /** * @doc method - * @id DS.async_methods:PUT - * @name PUT - * @methodOf DS - * @description - * See [DS.PUT](/documentation/api/api/DS.async_methods:PUT). + * @id BinaryHeapProvider.methods:setDefaultWeightFunction + * @name setDefaultWeightFunction + * @param {function} weightFunc New default weight function. */ - PUT: PUT, + function setDefaultWeightFunction(weightFunc) { + if (!angular.isFunction(weightFunc)) { + throw new Error('BinaryHeapProvider.setDefaultWeightFunction(weightFunc): weightFunc: Must be a function!'); + } + defaults.userProvidedDefaultWeightFunc = weightFunc; + } /** * @doc method - * @id DS.async_methods:DEL - * @name DEL - * @methodOf DS - * @description - * See [DS.DEL](/documentation/api/api/DS.async_methods:DEL). + * @id BinaryHeapProvider.methods:setDefaultWeightFunction + * @name setDefaultWeightFunction + * @methodOf BinaryHeapProvider + * @param {function} weightFunc New default weight function. */ - DEL: DEL -}; + this.setDefaultWeightFunction = setDefaultWeightFunction; + + this.$get = function () { + return BinaryHeap; + }; +} + +module.exports = BinaryHeapProvider; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],"HttpAdapter":[function(require,module,exports){ -module.exports=require('clHM+W'); },{}],29:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.create(resourceName, attrs): '; +var errorPrefix = 'DS.create(resourceName, attrs): '; /** * @doc method @@ -1336,6 +1593,7 @@ var utils = require('utils'), * @param {string} resourceName The resource type, e.g. 'user', 'comment', etc. * @param {object} attrs The attributes with which to update the item of the type specified by `resourceName` that has * the primary key specified by `id`. + * @param {object=} options Configuration options. * @returns {Promise} Promise produced by the `$q` service. * * ## Resolves with: @@ -1348,45 +1606,47 @@ var utils = require('utils'), * - `{RuntimeError}` * - `{UnhandledError}` */ -function create(resourceName, attrs) { - var deferred = services.$q.defer(), +function create(resourceName, attrs, options) { + var deferred = this.$q.defer(), promise = deferred.promise; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isObject(attrs)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object!', { attrs: { actual: typeof attrs, expected: 'object' } })); + options = options || {}; + + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isObject(attrs)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object!', { attrs: { actual: typeof attrs, expected: 'object' } })); } else { try { - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], _this = this; promise = promise .then(function (attrs) { - return services.$q.promisify(resource.beforeValidate)(resourceName, attrs); + return _this.$q.promisify(definition.beforeValidate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.validate)(resourceName, attrs); + return _this.$q.promisify(definition.validate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.afterValidate)(resourceName, attrs); + return _this.$q.promisify(definition.afterValidate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.beforeCreate)(resourceName, attrs); + return _this.$q.promisify(definition.beforeCreate)(resourceName, attrs); }) .then(function (attrs) { - return services.adapters[resource.defaultAdapter].POST.apply(_this, [utils.makePath(resource.baseUrl, resource.endpoint), attrs, null]); + return _this.adapters[options.adapter || definition.defaultAdapter].create(definition, attrs, options); }) .then(function (data) { - return services.$q.promisify(resource.afterCreate)(resourceName, data); + return _this.$q.promisify(definition.afterCreate)(resourceName, data); }) .then(function (data) { - return _this.inject(resource.name, data); + return _this.inject(definition.name, data); }); deferred.resolve(attrs); } catch (err) { - deferred.reject(new errors.UnhandledError(err)); + deferred.reject(new this.errors.UnhandledError(err)); } } @@ -1395,11 +1655,8 @@ function create(resourceName, attrs) { module.exports = create; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],30:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.destroy(resourceName, id): '; +},{}],30:[function(require,module,exports){ +var errorPrefix = 'DS.destroy(resourceName, id): '; /** * @doc method @@ -1430,6 +1687,7 @@ var utils = require('utils'), * * @param {string} resourceName The resource type, e.g. 'user', 'comment', etc. * @param {string|number} id The primary key of the item to remove. + * @param {object=} options Configuration options. * @returns {Promise} Promise produced by the `$q` service. * * ## Resolves with: @@ -1442,34 +1700,40 @@ var utils = require('utils'), * - `{RuntimeError}` * - `{UnhandledError}` */ -function destroy(resourceName, id) { - var deferred = services.$q.defer(), +function destroy(resourceName, id, options) { + var deferred = this.$q.defer(), promise = deferred.promise; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); + options = options || {}; + + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); } else { - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this; - promise = promise - .then(function (attrs) { - return services.$q.promisify(resource.beforeDestroy)(resourceName, attrs); - }) - .then(function () { - return services.adapters[resource.defaultAdapter].DEL(utils.makePath(resource.baseUrl, resource.endpoint, id), null); - }) - .then(function () { - return services.$q.promisify(resource.afterDestroy)(resourceName, resource.index[id]); - }) - .then(function () { - _this.eject(resourceName, id); - return id; - }); - - deferred.resolve(resource.index[id]); + if (id in resource.index) { + promise = promise + .then(function (attrs) { + return _this.$q.promisify(definition.beforeDestroy)(resourceName, attrs); + }) + .then(function () { + return _this.adapters[options.adapter || definition.defaultAdapter].destroy(definition, id, options); + }) + .then(function () { + return _this.$q.promisify(definition.afterDestroy)(resourceName, resource.index[id]); + }) + .then(function () { + _this.eject(resourceName, id); + return id; + }); + deferred.resolve(resource.index[id]); + } else { + deferred.resolve(); + } } return promise; @@ -1477,11 +1741,8 @@ function destroy(resourceName, id) { module.exports = destroy; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],31:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.find(resourceName, id[, options]): '; +},{}],31:[function(require,module,exports){ +var errorPrefix = 'DS.find(resourceName, id[, options]): '; /** * @doc method @@ -1528,20 +1789,21 @@ var utils = require('utils'), * - `{UnhandledError}` */ function find(resourceName, id, options) { - var deferred = services.$q.defer(), + var deferred = this.$q.defer(), promise = deferred.promise; options = options || {}; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); - } else if (!utils.isObject(options)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); + } else if (!this.utils.isObject(options)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); } else { try { - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this; if (options.bypassCache) { @@ -1550,7 +1812,7 @@ function find(resourceName, id, options) { if (!(id in resource.completedQueries)) { if (!(id in resource.pendingQueries)) { - promise = resource.pendingQueries[id] = services.adapters[resource.defaultAdapter].GET(utils.makePath(resource.baseUrl, resource.endpoint, id), null) + promise = resource.pendingQueries[id] = _this.adapters[options.adapter || definition.defaultAdapter].find(definition, id, options) .then(function (data) { // Query is no longer pending delete resource.pendingQueries[id]; @@ -1573,14 +1835,11 @@ function find(resourceName, id, options) { module.exports = find; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],32:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.findAll(resourceName, params[, options]): '; +},{}],32:[function(require,module,exports){ +var errorPrefix = 'DS.findAll(resourceName, params[, options]): '; -function processResults(data, resourceName, queryHash) { - var resource = services.store[resourceName]; +function processResults(utils, data, resourceName, queryHash) { + var resource = this.store[resourceName]; data = data || []; @@ -1594,15 +1853,16 @@ function processResults(data, resourceName, queryHash) { } // Update the data store's index for this resource - resource.index = utils.toLookup(resource.collection, resource.idAttribute); + resource.index = utils.toLookup(resource.collection, this.definitions[resourceName].idAttribute); // Update modified timestamp of collection resource.collectionModified = utils.updateTimestamp(resource.collectionModified); return data; } -function _findAll(resourceName, params, options) { - var resource = services.store[resourceName], +function _findAll(utils, resourceName, params, options) { + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this, queryHash = utils.toJson(params); @@ -1616,12 +1876,12 @@ function _findAll(resourceName, params, options) { if (!(queryHash in resource.pendingQueries)) { // This particular query has never even been made - resource.pendingQueries[queryHash] = services.adapters[resource.defaultAdapter].GET(utils.makePath(resource.baseUrl, resource.endpoint), { params: params }) + resource.pendingQueries[queryHash] = _this.adapters[options.adapter || definition.defaultAdapter].findAll(definition, { params: params }, options) .then(function (data) { try { - return processResults.apply(_this, [data, resourceName, queryHash]); + return processResults.apply(_this, [utils, data, resourceName, queryHash]); } catch (err) { - throw new errors.UnhandledError(err); + throw new _this.errors.UnhandledError(err); } }); } @@ -1700,26 +1960,26 @@ function _findAll(resourceName, params, options) { * - `{UnhandledError}` */ function findAll(resourceName, params, options) { - var deferred = services.$q.defer(), + var deferred = this.$q.defer(), promise = deferred.promise, _this = this; options = options || {}; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isObject(params)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } })); - } else if (!utils.isObject(options)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isObject(params)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } })); + } else if (!this.utils.isObject(options)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); } else { try { promise = promise.then(function () { - return _findAll.apply(_this, [resourceName, params, options]); + return _findAll.apply(_this, [_this.utils, resourceName, params, options]); }); deferred.resolve(); } catch (err) { - deferred.reject(new errors.UnhandledError(err)); + deferred.reject(new this.errors.UnhandledError(err)); } } @@ -1728,7 +1988,7 @@ function findAll(resourceName, params, options) { module.exports = findAll; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],33:[function(require,module,exports){ +},{}],33:[function(require,module,exports){ module.exports = { /** * @doc method @@ -1792,10 +2052,7 @@ module.exports = { }; },{"./create":29,"./destroy":30,"./find":31,"./findAll":32,"./refresh":34,"./save":35}],34:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.refresh(resourceName, id[, options]): '; +var errorPrefix = 'DS.refresh(resourceName, id[, options]): '; /** * @doc method @@ -1851,16 +2108,16 @@ var utils = require('utils'), function refresh(resourceName, id, options) { options = options || {}; - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); - } else if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + } else if (!this.utils.isObject(options)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); } else { options.bypassCache = true; - if (id in services.store[resourceName].index) { + if (id in this.store[resourceName].index) { return this.find(resourceName, id, options); } else { return false; @@ -1870,11 +2127,8 @@ function refresh(resourceName, id, options) { module.exports = refresh; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],35:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.save(resourceName, id[, options]): '; +},{}],35:[function(require,module,exports){ +var errorPrefix = 'DS.save(resourceName, id[, options]): '; /** * @doc method @@ -1920,46 +2174,47 @@ var utils = require('utils'), * - `{UnhandledError}` */ function save(resourceName, id, options) { - var deferred = services.$q.defer(), + var deferred = this.$q.defer(), promise = deferred.promise; options = options || {}; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); - } else if (!utils.isObject(options)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); - } else if (!(id in services.store[resourceName].index)) { - deferred.reject(new errors.RuntimeError(errorPrefix + 'id: "' + id + '" not found!')); + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); + } else if (!this.utils.isObject(options)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); + } else if (!(id in this.store[resourceName].index)) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + 'id: "' + id + '" not found!')); } else { - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this; promise = promise .then(function (attrs) { - return services.$q.promisify(resource.beforeValidate)(resourceName, attrs); + return _this.$q.promisify(definition.beforeValidate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.validate)(resourceName, attrs); + return _this.$q.promisify(definition.validate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.afterValidate)(resourceName, attrs); + return _this.$q.promisify(definition.afterValidate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.beforeUpdate)(resourceName, attrs); + return _this.$q.promisify(definition.beforeUpdate)(resourceName, attrs); }) .then(function (attrs) { - return services.adapters[resource.defaultAdapter].PUT(utils.makePath(resource.baseUrl, resource.endpoint, id), attrs, null); + return _this.adapters[options.adapter || definition.defaultAdapter].update(definition, attrs, options); }) .then(function (data) { - return services.$q.promisify(resource.afterUpdate)(resourceName, data); + return _this.$q.promisify(definition.afterUpdate)(resourceName, data); }) .then(function (data) { - var saved = _this.inject(resource.name, data, options); - resource.previous_attributes[id] = utils.deepMixIn({}, data); - resource.saved[id] = utils.updateTimestamp(resource.saved[id]); + var saved = _this.inject(definition.name, data, options); + resource.previousAttributes[id] = _this.utils.deepMixIn({}, data); + resource.saved[id] = _this.utils.updateTimestamp(resource.saved[id]); return saved; }); @@ -1971,129 +2226,30 @@ function save(resourceName, id, options) { module.exports = save; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],36:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - HttpAdapter = require('HttpAdapter'), - configErrorPrefix = 'DSProvider.config(options): ', - registerAdapterErrorPrefix = 'DSProvider.registerAdapter(name, adapter): '; - -/** - * @doc method - * @id DSProvider.methods:config - * @name config - * @description - * Configure the DS service. - * - * ## Signature: - * ```js - * DSProvider.config(options) - * ``` - * - * ## Example: - * ```js - * DSProvider.config({ - * baseUrl: 'http://myapp.com/api', - * idAttribute: '_id', - * validate: function (resourceName, attrs, cb) { - * console.log('looks good to me'); - * cb(null, attrs); - * } - * }); - * ``` - * - * ## Throws: - * - * - `{IllegalArgumentError}` - * - * @param {object} options Global configuration for the data store. Properties: - * - `{string=}` - `baseUrl` - The default base url to be used by the data store. Can be overridden via `DS.defineResource`. - * - `{string=}` - `idAttribute` - The default property that specifies the primary key of an object. Default: `"id"`. - * - `{function=}` - `beforeValidate` - Global lifecycle hook. Signature: `beforeValidate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `validate` - Global lifecycle hook. Signature: `validate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `afterValidate` - Global lifecycle hook. Signature: `afterValidate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `beforeCreate` - Global lifecycle hook. Signature: `beforeCreate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `afterCreate` - Global lifecycle hook. Signature: `afterCreate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `beforeUpdate` - Global lifecycle hook. Signature: `beforeUpdate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `afterUpdate` - Global lifecycle hook. Signature: `afterUpdate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `beforeDestroy` - Global lifecycle hook. Signature: `beforeDestroy(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `afterDestroy` - Global lifecycle hook. Signature: `afterDestroy(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - */ -function config(options) { - options = options || {}; - - if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options: Must be an object!', { actual: typeof options, expected: 'object' }); - } else if ('baseUrl' in options && !utils.isString(options.baseUrl)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.baseUrl: Must be a string!', { baseUrl: { actual: typeof options.baseUrl, expected: 'string' } }); - } else if ('idAttribute' in options && !utils.isString(options.idAttribute)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.idAttribute: Must be a string!', { idAttribute: { actual: typeof options.idAttribute, expected: 'string' } }); - } else if ('mergeStrategy' in options && !utils.isString(options.mergeStrategy)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.mergeStrategy: Must be a string!', { mergeStrategy: { actual: typeof options.mergeStrategy, expected: 'string' } }); - } else if ('beforeValidate' in options && !utils.isFunction(options.beforeValidate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.beforeValidate: Must be a function!', { beforeValidate: { actual: typeof options.beforeValidate, expected: 'function' } }); - } else if ('validate' in options && !utils.isFunction(options.validate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.validate: Must be a function!', { validate: { actual: typeof options.validate, expected: 'function' } }); - } else if ('afterValidate' in options && !utils.isFunction(options.afterValidate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.afterValidate: Must be a function!', { afterValidate: { actual: typeof options.afterValidate, expected: 'function' } }); - } else if ('beforeCreate' in options && !utils.isFunction(options.beforeCreate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.beforeCreate: Must be a function!', { beforeCreate: { actual: typeof options.beforeCreate, expected: 'function' } }); - } else if ('afterCreate' in options && !utils.isFunction(options.afterCreate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.afterCreate: Must be a function!', { afterCreate: { actual: typeof options.afterCreate, expected: 'function' } }); - } else if ('beforeUpdate' in options && !utils.isFunction(options.beforeUpdate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.beforeUpdate: Must be a function!', { beforeUpdate: { actual: typeof options.beforeUpdate, expected: 'function' } }); - } else if ('afterUpdate' in options && !utils.isFunction(options.afterUpdate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.afterUpdate: Must be a function!', { afterUpdate: { actual: typeof options.afterUpdate, expected: 'function' } }); - } else if ('beforeDestroy' in options && !utils.isFunction(options.beforeDestroy)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.beforeDestroy: Must be a function!', { beforeDestroy: { actual: typeof options.beforeDestroy, expected: 'function' } }); - } else if ('afterDestroy' in options && !utils.isFunction(options.afterDestroy)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.afterDestroy: Must be a function!', { afterDestroy: { actual: typeof options.afterDestroy, expected: 'function' } }); - } else if ('defaultAdapter' in options && !utils.isString(options.defaultAdapter)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.defaultAdapter: Must be a function!', { defaultAdapter: { actual: typeof options.defaultAdapter, expected: 'string' } }); - } +},{}],36:[function(require,module,exports){ +var errorPrefix = 'DSProvider.registerAdapter(name, adapter): '; - services.config = new services.BaseConfig(options); +function lifecycleNoop(resourceName, attrs, cb) { + cb(null, attrs); } -/** - * @doc method - * @id DSProvider.methods:registerAdapter - * @name registerAdapter - * @description - * Register a new adapter. - * - * ## Signature: - * ```js - * DSProvider.registerAdapter(name, adapter); - * ``` - * - * ## Example: - * ```js - * DSProvider.registerAdapter('IndexedDBAdapter', {...}); - * ``` - * - * ## Throws: - * - * - `{IllegalArgumentError}` - * - `{RuntimeError}` - * - * @param {string} name The name of the new adapter. - * @param {object} adapter New adapter. - */ -function registerAdapter(name, adapter) { - - if (!utils.isString(name)) { - throw new errors.IllegalArgumentError(registerAdapterErrorPrefix + 'name: Must be a string!', { actual: typeof name, expected: 'string' }); - } else if (!utils.isObject(adapter)) { - throw new errors.IllegalArgumentError(registerAdapterErrorPrefix + 'adapter: Must be an object!', { actual: typeof adapter, expected: 'object' }); - } else if (services.adapters[name]) { - throw new errors.RuntimeError(registerAdapterErrorPrefix + name + ' is already registered!'); - } - - services.adapters[name] = adapter; +function BaseConfig() { } +BaseConfig.prototype.idAttribute = 'id'; +BaseConfig.prototype.defaultAdapter = 'DSHttpAdapter'; +BaseConfig.prototype.baseUrl = ''; +BaseConfig.prototype.endpoint = ''; +BaseConfig.prototype.beforeValidate = lifecycleNoop; +BaseConfig.prototype.validate = lifecycleNoop; +BaseConfig.prototype.afterValidate = lifecycleNoop; +BaseConfig.prototype.beforeCreate = lifecycleNoop; +BaseConfig.prototype.afterCreate = lifecycleNoop; +BaseConfig.prototype.beforeUpdate = lifecycleNoop; +BaseConfig.prototype.afterUpdate = lifecycleNoop; +BaseConfig.prototype.beforeDestroy = lifecycleNoop; +BaseConfig.prototype.afterDestroy = lifecycleNoop; + /** * @doc interface * @id DSProvider @@ -2101,150 +2257,65 @@ function registerAdapter(name, adapter) { */ function DSProvider() { - /** - * @doc method - * @id DSProvider.methods:config - * @name config - * @methodOf DSProvider - * @description - * See [DSProvider.config](/documentation/api/api/DSProvider.methods:config). - */ - this.config = config; - - config({}); - - /** - * @doc method - * @id DSProvider.methods:registerAdapter - * @name config - * @methodOf DSProvider - * @description - * See [DSProvider.registerAdapter](/documentation/api/api/DSProvider.methods:registerAdapter). - */ - this.registerAdapter = registerAdapter; - - this.$get = ['$rootScope', '$log', '$http', '$q', function ($rootScope, $log, $http, $q) { - services.$rootScope = $rootScope; - services.$log = $log; - services.$http = $http; - services.$q = $q; - services.store = {}; - services.adapters = {}; + var defaults = this.defaults = new BaseConfig(), + adapters = this.adapters = {}; + + this.$get = [ + '$rootScope', '$log', '$q', 'DSHttpAdapter', 'DSUtils', 'DSErrors', + function ($rootScope, $log, $q, DSHttpAdapter, DSUtils, DSErrors) { + + var syncMethods = require('./sync_methods'), + asyncMethods = require('./async_methods'); + + adapters.DSHttpAdapter = DSHttpAdapter; + + /** + * @doc interface + * @id DS + * @name DS + * @description + * Data store + */ + var DS = { + $rootScope: $rootScope, + $log: $log, + $q: $q, + defaults: defaults, + store: {}, + definitions: {}, + adapters: adapters, + errors: DSErrors, + utils: DSUtils + }; - registerAdapter('HttpAdapter', HttpAdapter); - /** - * @doc interface - * @id DS - * @name DS - * @description - * Data store - */ - var DS = { - HttpAdapter: HttpAdapter, - errors: errors - }; + DSUtils.deepFreeze(syncMethods); + DSUtils.deepFreeze(asyncMethods); - utils.deepMixIn(DS, require('./sync_methods')); - utils.deepMixIn(DS, require('./async_methods')); + DSUtils.deepMixIn(DS, syncMethods); + DSUtils.deepMixIn(DS, asyncMethods); - utils.deepFreeze(DS); + DSUtils.deepFreeze(DS.errors); + DSUtils.deepFreeze(DS.utils); - var $dirtyCheckScope = $rootScope.$new(); + var $dirtyCheckScope = $rootScope.$new(); - $dirtyCheckScope.$watch(function () { - // Throttle angular-data's digest loop to tenths of a second - return new Date().getTime() / 100 | 0; - }, function () { - DS.digest(); - }); + $dirtyCheckScope.$watch(function () { + // Throttle angular-data's digest loop to tenths of a second + return new Date().getTime() / 100 | 0; + }, function () { + DS.digest(); + }); - return DS; - }]; + return DS; + }]; } module.exports = DSProvider; -},{"./async_methods":33,"./sync_methods":46,"HttpAdapter":"clHM+W","errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],"cX8q+p":[function(require,module,exports){ -function lifecycleNoop(resourceName, attrs, cb) { - cb(null, attrs); -} - -var services = module.exports = { - store: {}, - BaseConfig: function (options) { - if ('idAttribute' in options) { - this.idAttribute = options.idAttribute; - } - - if ('baseUrl' in options) { - this.baseUrl = options.baseUrl; - } - - if ('beforeValidate' in options) { - this.beforeValidate = options.beforeValidate; - } - - if ('validate' in options) { - this.validate = options.validate; - } - - if ('afterValidate' in options) { - this.afterValidate = options.afterValidate; - } - - if ('beforeCreate' in options) { - this.beforeCreate = options.beforeCreate; - } - - if ('afterCreate' in options) { - this.afterCreate = options.afterCreate; - } - - if ('beforeUpdate' in options) { - this.beforeUpdate = options.beforeUpdate; - } - - if ('afterUpdate' in options) { - this.afterUpdate = options.afterUpdate; - } - - if ('beforeDestroy' in options) { - this.beforeDestroy = options.beforeDestroy; - } - - if ('afterDestroy' in options) { - this.afterDestroy = options.afterDestroy; - } - - if ('defaultAdapter' in options) { - this.defaultAdapter = options.defaultAdapter; - } - } -}; - -services.BaseConfig.prototype.idAttribute = 'id'; -services.BaseConfig.prototype.defaultAdapter = 'HttpAdapter'; -services.BaseConfig.prototype.baseUrl = ''; -services.BaseConfig.prototype.endpoint = ''; -services.BaseConfig.prototype.beforeValidate = lifecycleNoop; -services.BaseConfig.prototype.validate = lifecycleNoop; -services.BaseConfig.prototype.afterValidate = lifecycleNoop; -services.BaseConfig.prototype.beforeCreate = lifecycleNoop; -services.BaseConfig.prototype.afterCreate = lifecycleNoop; -services.BaseConfig.prototype.beforeUpdate = lifecycleNoop; -services.BaseConfig.prototype.afterUpdate = lifecycleNoop; -services.BaseConfig.prototype.beforeDestroy = lifecycleNoop; -services.BaseConfig.prototype.afterDestroy = lifecycleNoop; - -},{}],"services":[function(require,module,exports){ -module.exports=require('cX8q+p'); -},{}],39:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.changes(resourceName, id): '; +},{"./async_methods":33,"./sync_methods":44}],37:[function(require,module,exports){ +var errorPrefix = 'DS.changes(resourceName, id): '; /** * @doc method @@ -2281,54 +2352,35 @@ var utils = require('utils'), * @returns {object} The changes of the item of the type specified by `resourceName` with the primary key specified by `id`. */ function changes(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { - return angular.copy(services.store[resourceName].changes[id]); + return angular.copy(this.store[resourceName].changes[id]); } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } module.exports = changes; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],40:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.defineResource(definition): '; +},{}],38:[function(require,module,exports){ +var errorPrefix = 'DS.defineResource(definition): '; -function Resource(options) { - services.BaseConfig.apply(this, [options]); +function Resource(utils, options) { - if ('name' in options) { - this.name = options.name; - } + utils.deepMixIn(this, options); if ('endpoint' in options) { this.endpoint = options.endpoint; } else { this.endpoint = this.name; } - - this.collection = []; - this.completedQueries = {}; - this.pendingQueries = {}; - this.index = {}; - this.modified = {}; - this.changes = {}; - this.previous_attributes = {}; - this.saved = {}; - this.observers = {}; - this.collectionModified = 0; } -Resource.prototype = services.config; - /** * @doc method * @id DS.sync_methods:defineResource @@ -2379,38 +2431,50 @@ Resource.prototype = services.config; * - `{function=}` - `afterDestroy` - Lifecycle hook. Overrides global. Signature: `afterDestroy(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. */ function defineResource(definition) { - if (utils.isString(definition)) { + if (this.utils.isString(definition)) { definition = { name: definition }; } - if (!utils.isObject(definition)) { - throw new errors.IllegalArgumentError(errorPrefix + 'definition: Must be an object!', { definition: { actual: typeof definition, expected: 'object' } }); - } else if (!utils.isString(definition.name)) { - throw new errors.IllegalArgumentError(errorPrefix + 'definition.name: Must be a string!', { definition: { name: { actual: typeof definition.name, expected: 'string' } } }); - } else if (definition.idAttribute && !utils.isString(definition.idAttribute)) { - throw new errors.IllegalArgumentError(errorPrefix + 'definition.idAttribute: Must be a string!', { definition: { idAttribute: { actual: typeof definition.idAttribute, expected: 'string' } } }); - } else if (definition.endpoint && !utils.isString(definition.endpoint)) { - throw new errors.IllegalArgumentError(errorPrefix + 'definition.endpoint: Must be a string!', { definition: { endpoint: { actual: typeof definition.endpoint, expected: 'string' } } }); - } else if (services.store[definition.name]) { - throw new errors.RuntimeError(errorPrefix + definition.name + ' is already registered!'); + if (!this.utils.isObject(definition)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'definition: Must be an object!', { definition: { actual: typeof definition, expected: 'object' } }); + } else if (!this.utils.isString(definition.name)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'definition.name: Must be a string!', { definition: { name: { actual: typeof definition.name, expected: 'string' } } }); + } else if (definition.idAttribute && !this.utils.isString(definition.idAttribute)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'definition.idAttribute: Must be a string!', { definition: { idAttribute: { actual: typeof definition.idAttribute, expected: 'string' } } }); + } else if (definition.endpoint && !this.utils.isString(definition.endpoint)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'definition.endpoint: Must be a string!', { definition: { endpoint: { actual: typeof definition.endpoint, expected: 'string' } } }); + } else if (this.store[definition.name]) { + throw new this.errors.RuntimeError(errorPrefix + definition.name + ' is already registered!'); } try { - services.store[definition.name] = new Resource(definition); + Resource.prototype = this.defaults; + this.definitions[definition.name] = new Resource(this.utils, definition); + + this.store[definition.name] = { + collection: [], + completedQueries: {}, + pendingQueries: {}, + index: {}, + changes: {}, + modified: {}, + saved: {}, + previousAttributes: {}, + observers: {}, + collectionModified: 0 + }; } catch (err) { - delete services.store[definition.name]; - throw new errors.UnhandledError(err); + delete this.definitions[definition.name]; + delete this.store[definition.name]; + throw new this.errors.UnhandledError(err); } } module.exports = defineResource; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],41:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - observe = require('observejs'); +},{}],39:[function(require,module,exports){ +var observe = require('observejs'); /** * @doc method @@ -2437,31 +2501,28 @@ var utils = require('utils'), */ function digest() { try { - if (!services.$rootScope.$$phase) { - services.$rootScope.$apply(function () { + if (!this.$rootScope.$$phase) { + this.$rootScope.$apply(function () { observe.Platform.performMicrotaskCheckpoint(); }); } else { observe.Platform.performMicrotaskCheckpoint(); } } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } module.exports = digest; -},{"errors":"hIh4e1","observejs":"u+GZEJ","services":"cX8q+p","utils":"uE/lJt"}],42:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.eject(resourceName, id): '; +},{"observejs":"u+GZEJ"}],40:[function(require,module,exports){ +var errorPrefix = 'DS.eject(resourceName, id): '; -function _eject(resource, id) { +function _eject(definition, resource, id) { if (id) { var found = false; for (var i = 0; i < resource.collection.length; i++) { - if (resource.collection[i][resource.idAttribute] == id) { + if (resource.collection[i][definition.idAttribute] == id) { found = true; break; } @@ -2472,7 +2533,7 @@ function _eject(resource, id) { delete resource.observers[id]; delete resource.index[id]; delete resource.changes[id]; - delete resource.previous_attributes[id]; + delete resource.previousAttributes[id]; delete resource.modified[id]; delete resource.saved[id]; } @@ -2482,9 +2543,8 @@ function _eject(resource, id) { resource.modified = {}; resource.saved = {}; resource.changes = {}; - resource.previous_attributes = {}; + resource.previousAttributes = {}; } - resource.collectionModified = utils.updateTimestamp(resource.collectionModified); } /** @@ -2531,33 +2591,35 @@ function _eject(resource, id) { * @param {string|number=} id The primary key of the item to eject. */ function eject(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (id && !utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (id && !this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } + var resource = this.store[resourceName], + _this = this; + try { - if (!services.$rootScope.$$phase) { - services.$rootScope.$apply(function () { - _eject(services.store[resourceName], id); + if (!this.$rootScope.$$phase) { + this.$rootScope.$apply(function () { + _eject(_this.definitions[resourceName], resource, id); + resource.collectionModified = _this.utils.updateTimestamp(resource.collectionModified); }); } else { - _eject(services.store[resourceName], id); + _eject(_this.definitions[resourceName], resource, id); + resource.collectionModified = _this.utils.updateTimestamp(resource.collectionModified); } } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } module.exports = eject; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],43:[function(require,module,exports){ +},{}],41:[function(require,module,exports){ /* jshint loopfunc: true */ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.filter(resourceName, params[, options]): '; +var errorPrefix = 'DS.filter(resourceName, params[, options]): '; /** * @doc method @@ -2602,21 +2664,22 @@ var utils = require('utils'), function filter(resourceName, params, options) { options = options || {}; - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isObject(params)) { - throw new errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } }); - } else if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isObject(params)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } }); + } else if (!this.utils.isObject(options)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); } try { - var resource = services.store[resourceName]; + var resource = this.store[resourceName], + _this = this; // Protect against null params.query = params.query || {}; - var queryHash = utils.toJson(params); + var queryHash = this.utils.toJson(params); if (!(queryHash in resource.completedQueries) && options.loadFromServer) { // This particular query has never been completed @@ -2628,16 +2691,16 @@ function filter(resourceName, params, options) { } // The query has been completed, so hit the cache with the query - var filtered = utils.filter(resource.collection, function (value) { + var filtered = this.utils.filter(resource.collection, function (value) { var keep = true; // Apply 'where' clauses if (params.query.where) { - if (!utils.isObject(params.query.where)) { - throw new errors.IllegalArgumentError(errorPrefix + 'params.query.where: Must be an object!', { params: { query: { where: { actual: typeof params.query.where, expected: 'object' } } } }); + if (!_this.utils.isObject(params.query.where)) { + throw new _this.errors.IllegalArgumentError(errorPrefix + 'params.query.where: Must be an object!', { params: { query: { where: { actual: typeof params.query.where, expected: 'object' } } } }); } - utils.forOwn(params.query.where, function (value2, key2) { - if (utils.isString(value2)) { + _this.utils.forOwn(params.query.where, function (value2, key2) { + if (_this.utils.isString(value2)) { value2 = { '===': value2 }; @@ -2657,7 +2720,7 @@ function filter(resourceName, params, options) { } else if ('<=' in value2) { keep = keep && (value[key2] <= value2['<=']); } else if ('in' in value2) { - keep = keep && utils.contains(value2['in'], value[key2]); + keep = keep && _this.utils.contains(value2['in'], value[key2]); } }); } @@ -2666,25 +2729,25 @@ function filter(resourceName, params, options) { // Apply 'orderBy' if (params.query.orderBy) { - if (utils.isString(params.query.orderBy)) { + if (this.utils.isString(params.query.orderBy)) { params.query.orderBy = [ [params.query.orderBy, 'ASC'] ]; } - if (utils.isArray(params.query.orderBy)) { + if (this.utils.isArray(params.query.orderBy)) { for (var i = 0; i < params.query.orderBy.length; i++) { - if (utils.isString(params.query.orderBy[i])) { + if (this.utils.isString(params.query.orderBy[i])) { params.query.orderBy[i] = [params.query.orderBy[i], 'ASC']; - } else if (!utils.isArray(params.query.orderBy[i])) { - throw new errors.IllegalArgumentError(errorPrefix + 'params.query.orderBy[' + i + ']: Must be a string or an array!', { params: { query: { 'orderBy[i]': { actual: typeof params.query.orderBy[i], expected: 'string|array' } } } }); + } else if (!this.utils.isArray(params.query.orderBy[i])) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'params.query.orderBy[' + i + ']: Must be a string or an array!', { params: { query: { 'orderBy[i]': { actual: typeof params.query.orderBy[i], expected: 'string|array' } } } }); } - filtered = utils.sort(filtered, function (a, b) { + filtered = this.utils.sort(filtered, function (a, b) { var cA = a[params.query.orderBy[i][0]], cB = b[params.query.orderBy[i][0]]; - if (utils.isString(cA)) { - cA = utils.upperCase(cA); + if (_this.utils.isString(cA)) { + cA = _this.utils.upperCase(cA); } - if (utils.isString(cB)) { - cB = utils.upperCase(cB); + if (_this.utils.isString(cB)) { + cB = _this.utils.upperCase(cB); } if (params.query.orderBy[i][1] === 'DESC') { if (cB < cA) { @@ -2706,36 +2769,33 @@ function filter(resourceName, params, options) { }); } } else { - throw new errors.IllegalArgumentError(errorPrefix + 'params.query.orderBy: Must be a string or an array!', { params: { query: { orderBy: { actual: typeof params.query.orderBy, expected: 'string|array' } } } }); + throw new this.errors.IllegalArgumentError(errorPrefix + 'params.query.orderBy: Must be a string or an array!', { params: { query: { orderBy: { actual: typeof params.query.orderBy, expected: 'string|array' } } } }); } } // Apply 'limit' and 'skip' - if (utils.isNumber(params.query.limit) && utils.isNumber(params.query.skip)) { - filtered = utils.slice(filtered, params.query.skip, params.query.skip + params.query.limit); - } else if (utils.isNumber(params.query.limit)) { - filtered = utils.slice(filtered, 0, params.query.limit); - } else if (utils.isNumber(params.query.skip)) { - filtered = utils.slice(filtered, params.query.skip); + if (this.utils.isNumber(params.query.limit) && this.utils.isNumber(params.query.skip)) { + filtered = this.utils.slice(filtered, params.query.skip, params.query.skip + params.query.limit); + } else if (this.utils.isNumber(params.query.limit)) { + filtered = this.utils.slice(filtered, 0, params.query.limit); + } else if (this.utils.isNumber(params.query.skip)) { + filtered = this.utils.slice(filtered, params.query.skip); } return filtered; } catch (err) { - if (err instanceof errors.IllegalArgumentError) { + if (err instanceof this.errors.IllegalArgumentError) { throw err; } else { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } } module.exports = filter; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],44:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.get(resourceName, id[, options]): '; +},{}],42:[function(require,module,exports){ +var errorPrefix = 'DS.get(resourceName, id[, options]): '; /** * @doc method @@ -2771,38 +2831,35 @@ var utils = require('utils'), function get(resourceName, id, options) { options = options || {}; - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); - } else if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + } else if (!this.utils.isObject(options)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); } try { // cache miss, request resource from server - if (!(id in services.store[resourceName].index) && options.loadFromServer) { + if (!(id in this.store[resourceName].index) && options.loadFromServer) { this.find(resourceName, id).then(null, function (err) { throw err; }); } // return resource from cache - return services.store[resourceName].index[id]; + return this.store[resourceName].index[id]; } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } module.exports = get; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],45:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.hasChanges(resourceName, id): '; +},{}],43:[function(require,module,exports){ +var errorPrefix = 'DS.hasChanges(resourceName, id): '; -function diffIsEmpty(diff) { +function diffIsEmpty(utils, diff) { return !(utils.isEmpty(diff.added) && utils.isEmpty(diff.removed) && utils.isEmpty(diff.changed)); @@ -2842,27 +2899,27 @@ function diffIsEmpty(diff) { * @returns {boolean} Whether the item of the type specified by `resourceName` with the primary key specified by `id` has changes. */ function hasChanges(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { // return resource from cache - if (id in services.store[resourceName].changes) { - return diffIsEmpty(services.store[resourceName].changes[id]); + if (id in this.store[resourceName].changes) { + return diffIsEmpty(this.utils, this.store[resourceName].changes[id]); } else { return false; } } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } module.exports = hasChanges; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],46:[function(require,module,exports){ +},{}],44:[function(require,module,exports){ module.exports = { /** * @doc method @@ -2974,50 +3031,47 @@ module.exports = { hasChanges: require('./hasChanges') }; -},{"./changes":39,"./defineResource":40,"./digest":41,"./eject":42,"./filter":43,"./get":44,"./hasChanges":45,"./inject":47,"./lastModified":48,"./lastSaved":49,"./previous":50}],47:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - observe = require('observejs'), +},{"./changes":37,"./defineResource":38,"./digest":39,"./eject":40,"./filter":41,"./get":42,"./hasChanges":43,"./inject":45,"./lastModified":46,"./lastSaved":47,"./previous":48}],45:[function(require,module,exports){ +var observe = require('observejs'), errorPrefix = 'DS.inject(resourceName, attrs[, options]): '; -function _inject(resource, attrs) { +function _inject(definition, resource, attrs) { var _this = this; function _react(added, removed, changed, getOldValueFn) { try { - var innerId = getOldValueFn(resource.idAttribute); + var innerId = getOldValueFn(definition.idAttribute); - resource.changes[innerId] = utils.diffObjectFromOldObject(resource.index[innerId], resource.previous_attributes[innerId]); - resource.modified[innerId] = utils.updateTimestamp(resource.modified[innerId]); - resource.collectionModified = utils.updateTimestamp(resource.collectionModified); + resource.changes[innerId] = _this.utils.diffObjectFromOldObject(resource.index[innerId], resource.previousAttributes[innerId]); + resource.modified[innerId] = _this.utils.updateTimestamp(resource.modified[innerId]); + resource.collectionModified = _this.utils.updateTimestamp(resource.collectionModified); - if (resource.idAttribute in changed) { - services.$log.error('Doh! You just changed the primary key of an object! ' + - 'I don\'t know how to handle this yet, so your data for the "' + resource.name + + if (definition.idAttribute in changed) { + $log.error('Doh! You just changed the primary key of an object! ' + + 'I don\'t know how to handle this yet, so your data for the "' + definition.name + '" resource is now in an undefined (probably broken) state.'); } } catch (err) { - throw new errors.UnhandledError(err); + throw new _this.errors.UnhandledError(err); } } - if (utils.isArray(attrs)) { + if (_this.utils.isArray(attrs)) { for (var i = 0; i < attrs.length; i++) { - _inject.call(_this, resource, attrs[i]); + _inject.call(_this, definition, resource, attrs[i]); } } else { - if (!(resource.idAttribute in attrs)) { - throw new errors.RuntimeError(errorPrefix + 'attrs: Must contain the property specified by `idAttribute`!'); + if (!(definition.idAttribute in attrs)) { + throw new _this.errors.RuntimeError(errorPrefix + 'attrs: Must contain the property specified by `idAttribute`!'); } else { - var id = attrs[resource.idAttribute]; + var id = attrs[definition.idAttribute]; if (!(id in resource.index)) { resource.index[id] = {}; - resource.previous_attributes[id] = {}; + resource.previousAttributes[id] = {}; - utils.deepMixIn(resource.index[id], attrs); - utils.deepMixIn(resource.previous_attributes[id], attrs); + _this.utils.deepMixIn(resource.index[id], attrs); + _this.utils.deepMixIn(resource.previousAttributes[id], attrs); resource.collection.push(resource.index[id]); @@ -3027,7 +3081,7 @@ function _inject(resource, attrs) { return id; }); } else { - utils.deepMixIn(resource.index[id], attrs); + _this.utils.deepMixIn(resource.index[id], attrs); resource.observers[id].deliver(); } } @@ -3083,29 +3137,30 @@ function _inject(resource, attrs) { function inject(resourceName, attrs, options) { options = options || {}; - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isObject(attrs) && !utils.isArray(attrs)) { - throw new errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object or an array!', { attrs: { actual: typeof attrs, expected: 'object|array' } }); - } else if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isObject(attrs) && !this.utils.isArray(attrs)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object or an array!', { attrs: { actual: typeof attrs, expected: 'object|array' } }); + } else if (!this.utils.isObject(options)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); } - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this; try { - if (!services.$rootScope.$$phase) { - services.$rootScope.$apply(function () { - _inject.apply(_this, [services.store[resourceName], attrs]); + if (!this.$rootScope.$$phase) { + this.$rootScope.$apply(function () { + _inject.apply(_this, [definition, resource, attrs]); }); } else { - _inject.apply(_this, [services.store[resourceName], attrs]); + _inject.apply(_this, [definition, resource, attrs]); } return attrs; } catch (err) { - if (!(err instanceof errors.RuntimeError)) { - throw new errors.UnhandledError(err); + if (!(err instanceof this.errors.RuntimeError)) { + throw new this.errors.UnhandledError(err); } else { throw err; } @@ -3114,11 +3169,8 @@ function inject(resourceName, attrs, options) { module.exports = inject; -},{"errors":"hIh4e1","observejs":"u+GZEJ","services":"cX8q+p","utils":"uE/lJt"}],48:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.lastModified(resourceName[, id]): '; +},{"observejs":"u+GZEJ"}],46:[function(require,module,exports){ +var errorPrefix = 'DS.lastModified(resourceName[, id]): '; /** * @doc method @@ -3155,31 +3207,28 @@ var utils = require('utils'), * `resourceName` with the given primary key was modified. */ function lastModified(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (id && !utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (id && !this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { if (id) { - if (!(id in services.store[resourceName].modified)) { - services.store[resourceName].modified[id] = 0; + if (!(id in this.store[resourceName].modified)) { + this.store[resourceName].modified[id] = 0; } - return services.store[resourceName].modified[id]; + return this.store[resourceName].modified[id]; } - return services.store[resourceName].collectionModified; + return this.store[resourceName].collectionModified; } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } module.exports = lastModified; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],49:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.lastSaved(resourceName[, id]): '; +},{}],47:[function(require,module,exports){ +var errorPrefix = 'DS.lastSaved(resourceName[, id]): '; /** * @doc method @@ -3223,31 +3272,28 @@ var utils = require('utils'), * `resourceName` with the given primary key was modified. */ function lastSaved(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (id && !utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (id && !this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { if (id) { - if (!(id in services.store[resourceName].saved)) { - services.store[resourceName].saved[id] = 0; + if (!(id in this.store[resourceName].saved)) { + this.store[resourceName].saved[id] = 0; } - return services.store[resourceName].saved[id]; + return this.store[resourceName].saved[id]; } - return services.store[resourceName].collectionModified; + return this.store[resourceName].collectionModified; } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } module.exports = lastSaved; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],50:[function(require,module,exports){ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.previous(resourceName, id): '; +},{}],48:[function(require,module,exports){ +var errorPrefix = 'DS.previous(resourceName, id): '; /** * @doc method @@ -3285,23 +3331,23 @@ var utils = require('utils'), * @returns {object} The previous attributes of the item of the type specified by `resourceName` with the primary key specified by `id`. */ function previous(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { // return resource from cache - return angular.copy(services.store[resourceName].previous_attributes[id]); + return angular.copy(this.store[resourceName].previousAttributes[id]); } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } module.exports = previous; -},{"errors":"hIh4e1","services":"cX8q+p","utils":"uE/lJt"}],"errors":[function(require,module,exports){ +},{}],"errors":[function(require,module,exports){ module.exports=require('hIh4e1'); },{}],"hIh4e1":[function(require,module,exports){ /** @@ -3407,52 +3453,6 @@ function IllegalArgumentError(message, errors) { IllegalArgumentError.prototype = Object.create(Error.prototype); IllegalArgumentError.prototype.constructor = IllegalArgumentError; -/** - * @doc function - * @id errors.types:ValidationError - * @name ValidationError - * @description Error that is thrown/returned when validation of a schema fails. - * @param {string=} message Error message. Default: `"Validation Error!"`. - * @param {object=} errors Object containing information about the error. - * @returns {ValidationError} A new instance of `ValidationError`. - */ -function ValidationError(message, errors) { - Error.call(this); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, this.constructor); - } - - /** - * @doc property - * @id errors.types:ValidationError.type - * @name type - * @propertyOf errors.types:ValidationError - * @description Name of error type. Default: `"ValidationError"`. - */ - this.type = this.constructor.name; - - /** - * @doc property - * @id errors.types:ValidationError.errors - * @name errors - * @propertyOf errors.types:ValidationError - * @description Object containing information about the error. - */ - this.errors = errors || {}; - - /** - * @doc property - * @id errors.types:ValidationError.message - * @name message - * @propertyOf errors.types:ValidationError - * @description Error message. Default: `"Validation Error!"`. - */ - this.message = message || 'Validation Error!'; -} - -ValidationError.prototype = Object.create(Error.prototype); -ValidationError.prototype.constructor = ValidationError; - /** * @doc function * @id errors.types:RuntimeError @@ -3504,171 +3504,141 @@ RuntimeError.prototype.constructor = RuntimeError; * @id errors * @name angular-data error types * @description - * `UnhandledError`, `IllegalArgumentError`, `RuntimeError` and `ValidationError`. + * `UnhandledError`, `IllegalArgumentError` and `RuntimeError`. * * References to the constructor functions of these errors can be found at `DS.errors`. */ -module.exports = { - UnhandledError: UnhandledError, - IllegalArgumentError: IllegalArgumentError, - ValidationError: ValidationError, - RuntimeError: RuntimeError -}; +module.exports = [function () { + return { + UnhandledError: UnhandledError, + IllegalArgumentError: IllegalArgumentError, + RuntimeError: RuntimeError + }; +}]; -},{}],53:[function(require,module,exports){ +},{}],51:[function(require,module,exports){ (function (window, angular, undefined) { 'use strict'; -// angular.module('jmdobry.binary-heap', []); -// -// /** -// * @doc interface -// * @id BinaryHeapProvider -// * @name BinaryHeapProvider -// */ -// function BinaryHeapProvider() { -// -// var defaults = require('./binaryHeap/defaults'); -// -// /** -// * @doc method -// * @id BinaryHeapProvider.methods:setDefaultWeightFunction -// * @name setDefaultWeightFunction -// * @param {function} weightFunc New default weight function. -// */ -// function setDefaultWeightFunction(weightFunc) { -// if (!angular.isFunction(weightFunc)) { -// throw new Error('BinaryHeapProvider.setDefaultWeightFunction(weightFunc): weightFunc: Must be a function!'); -// } -// defaults.userProvidedDefaultWeightFunc = weightFunc; -// } -// -// /** -// * @doc method -// * @id BinaryHeapProvider.methods:setDefaultWeightFunction -// * @name setDefaultWeightFunction -// * @methodOf BinaryHeapProvider -// * @param {function} weightFunc New default weight function. -// */ -// this.setDefaultWeightFunction = setDefaultWeightFunction; -// -// this.$get = function () { -// return require('./binaryHeap'); -// }; -// } -// angular.module('jmdobry.binary-heap').provider('BinaryHeap', BinaryHeapProvider); - - angular.module('jmdobry.angular-data', ['ng'/*, 'jmdobry.binary-heap'*/]).config(['$provide', function ($provide) { - $provide.decorator('$q', function ($delegate) { - // do whatever you you want - $delegate.promisify = function (fn, target) { - var _this = this; - return function () { - var deferred = _this.defer(), - args = Array.prototype.slice.apply(arguments); - - args.push(function (err, result) { - if (err) { + angular.module('angular-data.BinaryHeap', []) + .provider('BinaryHeap', require('./binaryHeap')); + angular.module('angular-data.DS', ['ng', 'angular-data.BinaryHeap']) + .service('DSUtils', require('./utils')) + .service('DSErrors', require('./errors')) + .provider('DSHttpAdapter', require('./adapters/http')) + .provider('DS', require('./datastore')) + .config(['$provide', function ($provide) { + $provide.decorator('$q', function ($delegate) { + // do whatever you you want + $delegate.promisify = function (fn, target) { + var _this = this; + return function () { + var deferred = _this.defer(), + args = Array.prototype.slice.apply(arguments); + + args.push(function (err, result) { + if (err) { + deferred.reject(err); + } else { + deferred.resolve(result); + } + }); + + try { + fn.apply(target || this, args); + } catch (err) { deferred.reject(err); - } else { - deferred.resolve(result); } - }); - try { - fn.apply(target || this, args); - } catch (err) { - deferred.reject(err); - } - - return deferred.promise; + return deferred.promise; + }; }; - }; - return $delegate; - }); - }]); - angular.module('jmdobry.angular-data').provider('DS', require('./datastore')); + return $delegate; + }); + }]); })(window, window.angular); -},{"./datastore":36}],"uE/lJt":[function(require,module,exports){ -module.exports = { - isString: angular.isString, - isArray: angular.isArray, - isObject: angular.isObject, - isNumber: angular.isNumber, - isFunction: angular.isFunction, - isEmpty: require('mout/lang/isEmpty'), - toJson: angular.toJson, - makePath: require('mout/string/makePath'), - upperCase: require('mout/string/upperCase'), - deepMixIn: require('mout/object/deepMixIn'), - forOwn: require('mout/object/forOwn'), - contains: require('mout/array/contains'), - filter: require('mout/array/filter'), - toLookup: require('mout/array/toLookup'), - slice: require('mout/array/slice'), - sort: require('mout/array/sort'), - updateTimestamp: function (timestamp) { - var newTimestamp = typeof Date.now === 'function' ? Date.now() : new Date().getTime(); - if (timestamp && newTimestamp <= timestamp) { - return timestamp + 1; - } else { - return newTimestamp; - } - }, - deepFreeze: function deepFreeze(o) { - if (typeof Object.freeze === 'function') { - var prop, propKey; - Object.freeze(o); // First freeze the object. - for (propKey in o) { - prop = o[propKey]; - if (!o.hasOwnProperty(propKey) || typeof prop !== 'object' || Object.isFrozen(prop)) { - // If the object is on the prototype, not an object, or is already frozen, - // skip it. Note that this might leave an unfrozen reference somewhere in the - // object if there is an already frozen object containing an unfrozen object. - continue; - } +},{"./adapters/http":27,"./binaryHeap":28,"./datastore":36,"./errors":"hIh4e1","./utils":"uE/lJt"}],"uE/lJt":[function(require,module,exports){ +module.exports = [function () { + return { + isString: angular.isString, + isArray: angular.isArray, + isObject: angular.isObject, + isNumber: angular.isNumber, + isFunction: angular.isFunction, + isEmpty: require('mout/lang/isEmpty'), + toJson: angular.toJson, + makePath: require('mout/string/makePath'), + upperCase: require('mout/string/upperCase'), + deepMixIn: require('mout/object/deepMixIn'), + forOwn: require('mout/object/forOwn'), + contains: require('mout/array/contains'), + filter: require('mout/array/filter'), + toLookup: require('mout/array/toLookup'), + slice: require('mout/array/slice'), + sort: require('mout/array/sort'), + updateTimestamp: function (timestamp) { + var newTimestamp = typeof Date.now === 'function' ? Date.now() : new Date().getTime(); + if (timestamp && newTimestamp <= timestamp) { + return timestamp + 1; + } else { + return newTimestamp; + } + }, + deepFreeze: function deepFreeze(o) { + if (typeof Object.freeze === 'function') { + var prop, propKey; + Object.freeze(o); // First freeze the object. + for (propKey in o) { + prop = o[propKey]; + if (!o.hasOwnProperty(propKey) || typeof prop !== 'object' || Object.isFrozen(prop)) { + // If the object is on the prototype, not an object, or is already frozen, + // skip it. Note that this might leave an unfrozen reference somewhere in the + // object if there is an already frozen object containing an unfrozen object. + continue; + } - deepFreeze(prop); // Recursively call deepFreeze. + deepFreeze(prop); // Recursively call deepFreeze. + } } - } - }, - diffObjectFromOldObject: function (object, oldObject) { - var added = {}; - var removed = {}; - var changed = {}; + }, + diffObjectFromOldObject: function (object, oldObject) { + var added = {}; + var removed = {}; + var changed = {}; - for (var prop in oldObject) { - var newValue = object[prop]; + for (var prop in oldObject) { + var newValue = object[prop]; - if (newValue !== undefined && newValue === oldObject[prop]) - continue; + if (newValue !== undefined && newValue === oldObject[prop]) + continue; - if (!(prop in object)) { - removed[prop] = undefined; - continue; + if (!(prop in object)) { + removed[prop] = undefined; + continue; + } + + if (newValue !== oldObject[prop]) + changed[prop] = newValue; } - if (newValue !== oldObject[prop]) - changed[prop] = newValue; - } + for (var prop2 in object) { + if (prop2 in oldObject) + continue; - for (var prop2 in object) { - if (prop2 in oldObject) - continue; + added[prop2] = object[prop2]; + } - added[prop2] = object[prop2]; + return { + added: added, + removed: removed, + changed: changed + }; } - - return { - added: added, - removed: removed, - changed: changed - }; - } -}; + }; +}]; },{"mout/array/contains":3,"mout/array/filter":4,"mout/array/slice":7,"mout/array/sort":8,"mout/array/toLookup":9,"mout/lang/isEmpty":14,"mout/object/deepMixIn":21,"mout/object/forOwn":23,"mout/string/makePath":25,"mout/string/upperCase":26}],"utils":[function(require,module,exports){ module.exports=require('uE/lJt'); -},{}]},{},[53]) +},{}]},{},[51]) diff --git a/dist/angular-data.min.js b/dist/angular-data.min.js index c309dce..ee309d7 100644 --- a/dist/angular-data.min.js +++ b/dist/angular-data.min.js @@ -1,11 +1,11 @@ /** * @author Jason Dobry * @file angular-data.min.js -* @version 0.5.0 - Homepage +* @version 0.6.0 - Homepage * @copyright (c) 2014 Jason Dobry * @license MIT * * @overview Data store for Angular.js. */ -require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gb&&a.check();)a.report(),b++}function e(a){for(var b in a)return!1;return!0}function f(a){return e(a.added)&&e(a.removed)&&e(a.changed)}function g(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var f in a)f in b||(c[f]=a[f]);return Array.isArray(a)&&a.length!==b.length&&(e.length=a.length),{added:c,removed:d,changed:e}}function h(a,b){var c=b||(Array.isArray(a)?[]:{});for(var d in a)c[d]=a[d];return Array.isArray(a)&&(c.length=a.length),c}function i(a,b,c,d){if(this.closed=!1,this.object=a,this.callback=b,this.target=c,this.token=d,this.reporting=!0,n){var e=this;this.boundInternalCallback=function(a){e.internalCallback(a)}}j(this),this.connect(),this.sync(!0)}function j(a){u&&(t.push(a),i._allObserversCount++)}function k(a,b,c,d){i.call(this,a,b,c,d)}function l(a){this.arr=[],this.callback=a,this.isObserved=!0}function m(a,b,c){for(var d={},e={},f=0;fa&&b.anyChanged);i._allObserversCount=t.length,v=!1}}},u&&(a.Platform.clearObservers=function(){t=[]}),k.prototype=r({__proto__:i.prototype,connect:function(){n&&Object.observe(this.object,this.boundInternalCallback)},sync:function(){n||(this.oldObject=h(this.object))},check:function(a){var b,c;if(n){if(!a)return!1;c={},b=m(this.object,a,c)}else c=this.oldObject,b=g(this.object,this.oldObject);return f(b)?!1:(this.reportArgs=[b.added||{},b.removed||{},b.changed||{}],this.reportArgs.push(function(a){return c[a]}),!0)},disconnect:function(){n?this.object&&Object.unobserve(this.object,this.boundInternalCallback):this.oldObject=void 0}});var x=Object.getPrototypeOf({}),y=Object.getPrototypeOf([]);l.prototype={reset:function(){this.isObserved=!this.isObserved},observe:function(a){if(c(a)&&a!==x&&a!==y){var b=this.arr.indexOf(a);b>=0&&this.arr[b+1]===this.isObserved||(0>b&&(b=this.arr.length,this.arr[b]=a,Object.observe(a,this.callback)),this.arr[b+1]=this.isObserved,this.observe(Object.getPrototypeOf(a)))}},cleanup:function(){for(var a=0,b=0,c=this.isObserved;ba&&(this.arr[a]=d,this.arr[a+1]=c),a+=2):Object.unobserve(d,this.callback),b+=2}this.arr.length=a}};var z={"new":!0,updated:!0,deleted:!0};a.Observer=i,a.Observer.hasObjectObserve=n,a.ObjectObserver=k}((c.Number={isNaN:window.isNaN})?c:c)},{}],observejs:[function(a,b){b.exports=a("u+GZEJ")},{}],3:[function(a,b){function c(a,b){return-1!==d(a,b)}var d=a("./indexOf");b.exports=c},{"./indexOf":5}],4:[function(a,b){function c(a,b,c){b=d(b,c);var e=[];if(null==a)return e;for(var f,g=-1,h=a.length;++gc?d+c:c;d>e;){if(a[e]===b)return e;e++}return-1}b.exports=c},{}],6:[function(a,b){function c(a){return null!=a&&""!==a}function d(a,b){return b=b||"",e(a,c).join(b)}var e=a("./filter");b.exports=d},{"./filter":4}],7:[function(a,b){function c(a,b,c){return d.call(a,b,c)}var d=Array.prototype.slice;b.exports=c},{}],8:[function(a,b){function c(a,b){if(null==a)return[];if(a.length<2)return a;null==b&&(b=d);var f,g,h;return f=~~(a.length/2),g=c(a.slice(0,f),b),h=c(a.slice(f,a.length),b),e(g,h,b)}function d(a,b){return b>a?-1:a>b?1:0}function e(a,b,c){for(var d=[];a.length&&b.length;)c(a[0],b[0])<=0?d.push(a.shift()):d.push(b.shift());return a.length&&d.push.apply(d,a),b.length&&d.push.apply(d,b),d}b.exports=c},{}],9:[function(a,b){function c(a,b){var c={};if(null==a)return c;var e,f=-1,g=a.length;if(d(b))for(;++f"in b?c=c&&a[e]>b[">"]:">="in b?c=c&&a[e]>=b[">="]:"<"in b?c=c&&a[e]f?-1:f>e?1:0:f>e?-1:e>f?1:0})}}return d.isNumber(b.query.limit)&&d.isNumber(b.query.skip)?j=d.slice(j,b.query.skip,b.query.skip+b.query.limit):d.isNumber(b.query.limit)?j=d.slice(j,0,b.query.limit):d.isNumber(b.query.skip)&&(j=d.slice(j,b.query.skip)),j}catch(l){throw l instanceof e.IllegalArgumentError?l:new e.UnhandledError(l)}}var d=a("utils"),e=a("errors"),f=a("services"),g="DS.filter(resourceName, params[, options]): ";b.exports=c},{errors:"hIh4e1",services:"cX8q+p",utils:"uE/lJt"}],44:[function(a,b){function c(a,b,c){if(c=c||{},!f.store[a])throw new e.RuntimeError(g+a+" is not a registered resource!");if(!d.isString(b)&&!d.isNumber(b))throw new e.IllegalArgumentError(g+"id: Must be a string or a number!",{id:{actual:typeof b,expected:"string|number"}});if(!d.isObject(c))throw new e.IllegalArgumentError(g+"options: Must be an object!",{options:{actual:typeof c,expected:"object"}});try{return b in f.store[a].index||!c.loadFromServer||this.find(a,b).then(null,function(a){throw a}),f.store[a].index[b]}catch(h){throw new e.UnhandledError(h)}}var d=a("utils"),e=a("errors"),f=a("services"),g="DS.get(resourceName, id[, options]): ";b.exports=c},{errors:"hIh4e1",services:"cX8q+p",utils:"uE/lJt"}],45:[function(a,b){function c(a){return!(e.isEmpty(a.added)&&e.isEmpty(a.removed)&&e.isEmpty(a.changed))}function d(a,b){if(!g.store[a])throw new f.RuntimeError(h+a+" is not a registered resource!");if(!e.isString(b)&&!e.isNumber(b))throw new f.IllegalArgumentError(h+"id: Must be a string or a number!",{id:{actual:typeof b,expected:"string|number"}});try{return b in g.store[a].changes?c(g.store[a].changes[b]):!1}catch(d){throw new f.UnhandledError(d)}}var e=a("utils"),f=a("errors"),g=a("services"),h="DS.hasChanges(resourceName, id): ";b.exports=d},{errors:"hIh4e1",services:"cX8q+p",utils:"uE/lJt"}],46:[function(a,b){b.exports={defineResource:a("./defineResource"),eject:a("./eject"),filter:a("./filter"),get:a("./get"),inject:a("./inject"),lastModified:a("./lastModified"),lastSaved:a("./lastSaved"),digest:a("./digest"),changes:a("./changes"),previous:a("./previous"),hasChanges:a("./hasChanges")}},{"./changes":39,"./defineResource":40,"./digest":41,"./eject":42,"./filter":43,"./get":44,"./hasChanges":45,"./inject":47,"./lastModified":48,"./lastSaved":49,"./previous":50}],47:[function(a,b){function c(a,b){function d(b,c,d,h){try{var i=h(a.idAttribute);a.changes[i]=e.diffObjectFromOldObject(a.index[i],a.previous_attributes[i]),a.modified[i]=e.updateTimestamp(a.modified[i]),a.collectionModified=e.updateTimestamp(a.collectionModified),a.idAttribute in d&&g.$log.error("Doh! You just changed the primary key of an object! I don't know how to handle this yet, so your data for the \""+a.name+'" resource is now in an undefined (probably broken) state.')}catch(j){throw new f.UnhandledError(j)}}var j=this;if(e.isArray(b))for(var k=0;k=b?a+1:b},deepFreeze:function c(a){if("function"==typeof Object.freeze){var b,d;Object.freeze(a);for(d in a)b=a[d],a.hasOwnProperty(d)&&"object"==typeof b&&!Object.isFrozen(b)&&c(b)}},diffObjectFromOldObject:function(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var h in a)h in b||(c[h]=a[h]);return{added:c,removed:d,changed:e}}}},{"mout/array/contains":3,"mout/array/filter":4,"mout/array/slice":7,"mout/array/sort":8,"mout/array/toLookup":9,"mout/lang/isEmpty":14,"mout/object/deepMixIn":21,"mout/object/forOwn":23,"mout/string/makePath":25,"mout/string/upperCase":26}],utils:[function(a,b){b.exports=a("uE/lJt")},{}]},{},[53]); \ No newline at end of file +require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gb&&a.check();)a.report(),b++}function e(a){for(var b in a)return!1;return!0}function f(a){return e(a.added)&&e(a.removed)&&e(a.changed)}function g(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var f in a)f in b||(c[f]=a[f]);return Array.isArray(a)&&a.length!==b.length&&(e.length=a.length),{added:c,removed:d,changed:e}}function h(a,b){var c=b||(Array.isArray(a)?[]:{});for(var d in a)c[d]=a[d];return Array.isArray(a)&&(c.length=a.length),c}function i(a,b,c,d){if(this.closed=!1,this.object=a,this.callback=b,this.target=c,this.token=d,this.reporting=!0,n){var e=this;this.boundInternalCallback=function(a){e.internalCallback(a)}}j(this),this.connect(),this.sync(!0)}function j(a){u&&(t.push(a),i._allObserversCount++)}function k(a,b,c,d){i.call(this,a,b,c,d)}function l(a){this.arr=[],this.callback=a,this.isObserved=!0}function m(a,b,c){for(var d={},e={},f=0;fa&&b.anyChanged);i._allObserversCount=t.length,v=!1}}},u&&(a.Platform.clearObservers=function(){t=[]}),k.prototype=r({__proto__:i.prototype,connect:function(){n&&Object.observe(this.object,this.boundInternalCallback)},sync:function(){n||(this.oldObject=h(this.object))},check:function(a){var b,c;if(n){if(!a)return!1;c={},b=m(this.object,a,c)}else c=this.oldObject,b=g(this.object,this.oldObject);return f(b)?!1:(this.reportArgs=[b.added||{},b.removed||{},b.changed||{}],this.reportArgs.push(function(a){return c[a]}),!0)},disconnect:function(){n?this.object&&Object.unobserve(this.object,this.boundInternalCallback):this.oldObject=void 0}});var x=Object.getPrototypeOf({}),y=Object.getPrototypeOf([]);l.prototype={reset:function(){this.isObserved=!this.isObserved},observe:function(a){if(c(a)&&a!==x&&a!==y){var b=this.arr.indexOf(a);b>=0&&this.arr[b+1]===this.isObserved||(0>b&&(b=this.arr.length,this.arr[b]=a,Object.observe(a,this.callback)),this.arr[b+1]=this.isObserved,this.observe(Object.getPrototypeOf(a)))}},cleanup:function(){for(var a=0,b=0,c=this.isObserved;ba&&(this.arr[a]=d,this.arr[a+1]=c),a+=2):Object.unobserve(d,this.callback),b+=2}this.arr.length=a}};var z={"new":!0,updated:!0,deleted:!0};a.Observer=i,a.Observer.hasObjectObserve=n,a.ObjectObserver=k}((c.Number={isNaN:window.isNaN})?c:c)},{}],observejs:[function(a,b){b.exports=a("u+GZEJ")},{}],3:[function(a,b){function c(a,b){return-1!==d(a,b)}var d=a("./indexOf");b.exports=c},{"./indexOf":5}],4:[function(a,b){function c(a,b,c){b=d(b,c);var e=[];if(null==a)return e;for(var f,g=-1,h=a.length;++gc?d+c:c;d>e;){if(a[e]===b)return e;e++}return-1}b.exports=c},{}],6:[function(a,b){function c(a){return null!=a&&""!==a}function d(a,b){return b=b||"",e(a,c).join(b)}var e=a("./filter");b.exports=d},{"./filter":4}],7:[function(a,b){function c(a,b,c){return d.call(a,b,c)}var d=Array.prototype.slice;b.exports=c},{}],8:[function(a,b){function c(a,b){if(null==a)return[];if(a.length<2)return a;null==b&&(b=d);var f,g,h;return f=~~(a.length/2),g=c(a.slice(0,f),b),h=c(a.slice(f,a.length),b),e(g,h,b)}function d(a,b){return b>a?-1:a>b?1:0}function e(a,b,c){for(var d=[];a.length&&b.length;)c(a[0],b[0])<=0?d.push(a.shift()):d.push(b.shift());return a.length&&d.push.apply(d,a),b.length&&d.push.apply(d,b),d}b.exports=c},{}],9:[function(a,b){function c(a,b){var c={};if(null==a)return c;var e,f=-1,g=a.length;if(d(b))for(;++f0;){var f=Math.floor((c+1)/2)-1,g=a[f];if(e>=b(g))break;a[f]=d,a[c]=g,c=f}}function d(a,b,c){for(var d=a.length,e=a[c],f=b(e);;){var g=2*(c+1),h=g-1,i=null;if(d>h){var j=a[h],k=b(j);f>k&&(i=h)}if(d>g){var l=a[g],m=b(l);m<(null===i?f:b(a[h]))&&(i=g)}if(null===i)break;a[c]=a[i],a[i]=e,c=i}}function e(a){if(a&&"function"!=typeof a)throw new Error("BinaryHeap(weightFunc): weightFunc: must be a function!");a=defaults.userProvidedDefaultWeightFunc||defaults.defaultWeightFunc,this.weightFunc=a,this.heap=[]}function f(){function a(a){if(!angular.isFunction(a))throw new Error("BinaryHeapProvider.setDefaultWeightFunction(weightFunc): weightFunc: Must be a function!");b.userProvidedDefaultWeightFunc=a}var b={defaultWeightFunc:function(a){return a},userProvidedDefaultWeightFunc:null};this.setDefaultWeightFunction=a,this.$get=function(){return e}}e.prototype.push=function(a){this.heap.push(a),c(this.heap,this.weightFunc,this.heap.length-1)},e.prototype.peek=function(){return this.heap[0]},e.prototype.pop=function(){var a=this.heap[0],b=this.heap.pop();return this.heap.length>0&&(this.heap[0]=b,d(this.heap,this.weightFunc,0)),a},e.prototype.remove=function(a){for(var b=this.heap.length,e=0;b>e;e++)if(angular.equals(this.heap[e],a)){var f=this.heap[e],g=this.heap.pop();return e!==b-1&&(this.heap[e]=g,c(this.heap,this.weightFunc,e),d(this.heap,this.weightFunc,e)),f}return null},e.prototype.removeAll=function(){this.heap=[]},e.prototype.size=function(){return this.heap.length},b.exports=f},{}],29:[function(a,b){function c(a,b,c){var e=this.$q.defer(),f=e.promise;if(c=c||{},this.definitions[a])if(this.utils.isObject(b))try{var g=this.definitions[a],h=this;f=f.then(function(b){return h.$q.promisify(g.beforeValidate)(a,b)}).then(function(b){return h.$q.promisify(g.validate)(a,b)}).then(function(b){return h.$q.promisify(g.afterValidate)(a,b)}).then(function(b){return h.$q.promisify(g.beforeCreate)(a,b)}).then(function(a){return h.adapters[c.adapter||g.defaultAdapter].create(g,a,c)}).then(function(b){return h.$q.promisify(g.afterCreate)(a,b)}).then(function(a){return h.inject(g.name,a)}),e.resolve(b)}catch(i){e.reject(new this.errors.UnhandledError(i))}else e.reject(new this.errors.IllegalArgumentError(d+"attrs: Must be an object!",{attrs:{actual:typeof b,expected:"object"}}));else e.reject(new this.errors.RuntimeError(d+a+" is not a registered resource!"));return f}var d="DS.create(resourceName, attrs): ";b.exports=c},{}],30:[function(a,b){function c(a,b,c){var e=this.$q.defer(),f=e.promise;if(c=c||{},this.definitions[a])if(this.utils.isString(b)||this.utils.isNumber(b)){var g=this.definitions[a],h=this.store[a],i=this;b in h.index?(f=f.then(function(b){return i.$q.promisify(g.beforeDestroy)(a,b)}).then(function(){return i.adapters[c.adapter||g.defaultAdapter].destroy(g,b,c)}).then(function(){return i.$q.promisify(g.afterDestroy)(a,h.index[b])}).then(function(){return i.eject(a,b),b}),e.resolve(h.index[b])):e.resolve()}else e.reject(new this.errors.IllegalArgumentError(d+"id: Must be a string or a number!",{id:{actual:typeof b,expected:"string|number"}}));else e.reject(new this.errors.RuntimeError(d+a+" is not a registered resource!"));return f}var d="DS.destroy(resourceName, id): ";b.exports=c},{}],31:[function(a,b){function c(a,b,c){var e=this.$q.defer(),f=e.promise;if(c=c||{},this.definitions[a])if(this.utils.isString(b)||this.utils.isNumber(b))if(this.utils.isObject(c))try{var g=this.definitions[a],h=this.store[a],i=this;if(c.bypassCache&&delete h.completedQueries[b],!(b in h.completedQueries))return b in h.pendingQueries||(f=h.pendingQueries[b]=i.adapters[c.adapter||g.defaultAdapter].find(g,b,c).then(function(c){return delete h.pendingQueries[b],h.completedQueries[b]=(new Date).getTime(),i.inject(a,c)})),h.pendingQueries[b];e.resolve(i.get(a,b))}catch(j){e.reject(j)}else e.reject(new this.errors.IllegalArgumentError(d+"options: Must be an object!",{options:{actual:typeof c,expected:"object"}}));else e.reject(new this.errors.IllegalArgumentError(d+"id: Must be a string or a number!",{id:{actual:typeof b,expected:"string|number"}}));else e.reject(new this.errors.RuntimeError(d+a+" is not a registered resource!"));return f}var d="DS.find(resourceName, id[, options]): ";b.exports=c},{}],32:[function(a,b){function c(a,b,c,d){var e=this.store[c];b=b||[],delete e.pendingQueries[d],e.completedQueries[d]=(new Date).getTime();for(var f=0;f"in b?c=c&&a[d]>b[">"]:">="in b?c=c&&a[d]>=b[">="]:"<"in b?c=c&&a[d]e?-1:e>d?1:0:e>d?-1:d>e?1:0})}}return this.utils.isNumber(b.query.limit)&&this.utils.isNumber(b.query.skip)?h=this.utils.slice(h,b.query.skip,b.query.skip+b.query.limit):this.utils.isNumber(b.query.limit)?h=this.utils.slice(h,0,b.query.limit):this.utils.isNumber(b.query.skip)&&(h=this.utils.slice(h,b.query.skip)),h}catch(j){throw j instanceof this.errors.IllegalArgumentError?j:new this.errors.UnhandledError(j)}}var d="DS.filter(resourceName, params[, options]): ";b.exports=c},{}],42:[function(a,b){function c(a,b,c){if(c=c||{},!this.definitions[a])throw new this.errors.RuntimeError(d+a+" is not a registered resource!");if(!this.utils.isString(b)&&!this.utils.isNumber(b))throw new this.errors.IllegalArgumentError(d+"id: Must be a string or a number!",{id:{actual:typeof b,expected:"string|number"}});if(!this.utils.isObject(c))throw new this.errors.IllegalArgumentError(d+"options: Must be an object!",{options:{actual:typeof c,expected:"object"}});try{return b in this.store[a].index||!c.loadFromServer||this.find(a,b).then(null,function(a){throw a}),this.store[a].index[b]}catch(e){throw new this.errors.UnhandledError(e)}}var d="DS.get(resourceName, id[, options]): ";b.exports=c},{}],43:[function(a,b){function c(a,b){return!(a.isEmpty(b.added)&&a.isEmpty(b.removed)&&a.isEmpty(b.changed))}function d(a,b){if(!this.definitions[a])throw new this.errors.RuntimeError(e+a+" is not a registered resource!");if(!this.utils.isString(b)&&!this.utils.isNumber(b))throw new this.errors.IllegalArgumentError(e+"id: Must be a string or a number!",{id:{actual:typeof b,expected:"string|number"}});try{return b in this.store[a].changes?c(this.utils,this.store[a].changes[b]):!1}catch(d){throw new this.errors.UnhandledError(d)}}var e="DS.hasChanges(resourceName, id): ";b.exports=d},{}],44:[function(a,b){b.exports={defineResource:a("./defineResource"),eject:a("./eject"),filter:a("./filter"),get:a("./get"),inject:a("./inject"),lastModified:a("./lastModified"),lastSaved:a("./lastSaved"),digest:a("./digest"),changes:a("./changes"),previous:a("./previous"),hasChanges:a("./hasChanges")}},{"./changes":37,"./defineResource":38,"./digest":39,"./eject":40,"./filter":41,"./get":42,"./hasChanges":43,"./inject":45,"./lastModified":46,"./lastSaved":47,"./previous":48}],45:[function(a,b){function c(a,b,d){function g(c,d,e,f){try{var g=f(a.idAttribute);b.changes[g]=h.utils.diffObjectFromOldObject(b.index[g],b.previousAttributes[g]),b.modified[g]=h.utils.updateTimestamp(b.modified[g]),b.collectionModified=h.utils.updateTimestamp(b.collectionModified),a.idAttribute in e&&$log.error("Doh! You just changed the primary key of an object! I don't know how to handle this yet, so your data for the \""+a.name+'" resource is now in an undefined (probably broken) state.')}catch(i){throw new h.errors.UnhandledError(i)}}var h=this;if(h.utils.isArray(d))for(var i=0;i=b?a+1:b},deepFreeze:function b(a){if("function"==typeof Object.freeze){var c,d;Object.freeze(a);for(d in a)c=a[d],a.hasOwnProperty(d)&&"object"==typeof c&&!Object.isFrozen(c)&&b(c)}},diffObjectFromOldObject:function(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var h in a)h in b||(c[h]=a[h]);return{added:c,removed:d,changed:e}}}}]},{"mout/array/contains":3,"mout/array/filter":4,"mout/array/slice":7,"mout/array/sort":8,"mout/array/toLookup":9,"mout/lang/isEmpty":14,"mout/object/deepMixIn":21,"mout/object/forOwn":23,"mout/string/makePath":25,"mout/string/upperCase":26}],utils:[function(a,b){b.exports=a("uE/lJt")},{}]},{},[51]); \ No newline at end of file diff --git a/karma.start.js b/karma.start.js index 5be18c8..5425ff7 100644 --- a/karma.start.js +++ b/karma.start.js @@ -27,7 +27,7 @@ var fail = function (msg) { }], TYPES_EXCEPT_FUNCTION = ['string', 123, 123.123, null, undefined, {}, [], true, false]; -angular.module('app', ['ng', 'jmdobry.angular-data']); +angular.module('app', ['ng', 'angular-data.DS']); // Setup before each test beforeEach(function (done) { @@ -69,18 +69,16 @@ beforeEach(function (done) { }; module('app', function (_DSProvider_) { DSProvider = _DSProvider_; - DSProvider.config({ - baseUrl: 'http://test.angular-cache.com', - beforeValidate: lifecycle.beforeValidate, - validate: lifecycle.validate, - afterValidate: lifecycle.afterValidate, - beforeCreate: lifecycle.beforeCreate, - afterCreate: lifecycle.afterCreate, - beforeUpdate: lifecycle.beforeUpdate, - afterUpdate: lifecycle.afterUpdate, - beforeDestroy: lifecycle.beforeDestroy, - afterDestroy: lifecycle.afterDestroy - }); + DSProvider.defaults.baseUrl = 'http://test.angular-cache.com'; + DSProvider.defaults.beforeValidate = lifecycle.beforeValidate; + DSProvider.defaults.validate = lifecycle.validate; + DSProvider.defaults.afterValidate = lifecycle.afterValidate; + DSProvider.defaults.beforeCreate = lifecycle.beforeCreate; + DSProvider.defaults.afterCreate = lifecycle.afterCreate; + DSProvider.defaults.beforeUpdate = lifecycle.beforeUpdate; + DSProvider.defaults.afterUpdate = lifecycle.afterUpdate; + DSProvider.defaults.beforeDestroy = lifecycle.beforeDestroy; + DSProvider.defaults.afterDestroy = lifecycle.afterDestroy; }); inject(function (_$rootScope_, _$q_, _$httpBackend_, _DS_, _$log_) { // Setup global mocks diff --git a/package.json b/package.json index 12c095d..6149a46 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "angular-data", "description": "Data store for Angular.js.", - "version": "0.5.0", + "version": "0.6.0", "homepage": "http://github.com/jmdobry/angular-data", "repository": { "type": "git", diff --git a/src/adapters/http/index.js b/src/adapters/http/index.js new file mode 100644 index 0000000..691455d --- /dev/null +++ b/src/adapters/http/index.js @@ -0,0 +1,257 @@ +function DSHttpAdapterProvider() { + + this.$get = ['$http', '$log', 'DSUtils', function ($http, $log, DSUtils) { + + var defaults = this.defaults = { + serialize: function (data) { + return data; + }, + deserialize: function (data) { + return data.data; + } + }; + + return { + defaults: defaults, + + /** + * @doc method + * @id DSHttpAdapter.methods:HTTP + * @name HTTP + * @description + * Wrapper for `$http()`. + * + * ## Signature: + * ```js + * DS.HTTP(config) + * ``` + * + * ## Example: + * + * ```js + * works the same as $http() + * ``` + * + * @param {object} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + HTTP: HTTP, + + /** + * @doc method + * @id DSHttpAdapter.methods:GET + * @name GET + * @description + * Wrapper for `$http.get()`. + * + * ## Signature: + * ```js + * DS.GET(url[, config]) + * ``` + * + * ## Example: + * + * ```js + * Works the same as $http.get() + * ``` + * + * @param {string} url The url of the request. + * @param {object=} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + GET: GET, + + /** + * @doc method + * @id DSHttpAdapter.methods:POST + * @name POST + * @description + * Wrapper for `$http.post()`. + * + * ## Signature: + * ```js + * DS.POST(url[, attrs][, config]) + * ``` + * + * ## Example: + * + * ```js + * Works the same as $http.post() + * ``` + * + * @param {string} url The url of the request. + * @param {object=} attrs Request payload. + * @param {object=} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + POST: POST, + + /** + * @doc method + * @id DSHttpAdapter.methods:PUT + * @name PUT + * @description + * Wrapper for `$http.put()`. + * + * ## Signature: + * ```js + * DS.PUT(url[, attrs][, config]) + * ``` + * + * ## Example: + * + * ```js + * Works the same as $http.put() + * ``` + * + * @param {string} url The url of the request. + * @param {object=} attrs Request payload. + * @param {object=} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + PUT: PUT, + + /** + * @doc method + * @id DSHttpAdapter.methods:DEL + * @name DEL + * @description + * Wrapper for `$http.delete()`. + * + * ## Signature: + * ```js + * DS.DEL(url[, config]) + * ``` + * + * ## Example: + * + * ```js + * Works the same as $http.delete + * ``` + * + * @param {string} url The url of the request. + * @param {object} config Configuration for the request. + * @returns {Promise} Promise produced by the `$q` service. + */ + DEL: DEL, + + find: find, + + findAll: findAll, + + create: create, + + createMany: function () { + throw new Error('Not yet implemented!'); + }, + + update: update, + + updateMany: function () { + throw new Error('Not yet implemented!'); + }, + + destroy: destroy, + + destroyMany: function () { + throw new Error('Not yet implemented!'); + } + }; + + function HTTP(config) { + var start = new Date().getTime(); + + return $http(config).then(function (data) { + $log.debug(data.config.method + ' request:' + data.config.url + ' Time taken: ' + (new Date().getTime() - start) + 'ms', arguments); + return defaults.deserialize(data); + }); + } + + function GET(url, config) { + config = config || {}; + return HTTP(DSUtils.deepMixIn(config, { + url: url, + method: 'GET' + })); + } + + function POST(url, attrs, config) { + config = config || {}; + return HTTP(DSUtils.deepMixIn(config, { + url: url, + data: attrs, + method: 'POST' + })); + } + + function PUT(url, attrs, config) { + config = config || {}; + return HTTP(DSUtils.deepMixIn(config, { + url: url, + data: attrs, + method: 'PUT' + })); + } + + function DEL(url, config) { + config = config || {}; + return this.HTTP(DSUtils.deepMixIn(config, { + url: url, + method: 'DELETE' + })); + } + + /** + * @doc method + * @id DSHttpAdapter.methods:find + * @name find + * @param {object} resourceConfig Properties: + * - `{string}` - `baseUrl` - Base url. + * - `{string=}` - `endpoint` - Endpoint path for the resource. + * @param {string|number} id + * @param {object=} options + * @returns {Promise} Promise. + */ + function find(resourceConfig, id, options) { + return this.GET( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint, id), + options + ); + } + + function findAll(resourceConfig, params, options) { + options = options || {}; + options.params = options.params || {}; + DSUtils.deepMixIn(options, params); + return this.GET( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint), + options + ); + } + + function create(resourceConfig, attrs, options) { + return this.POST( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint), + defaults.serialize(attrs), + options + ); + } + + function update(resourceConfig, attrs, options) { + return this.PUT( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint, attrs[resourceConfig.idAttribute]), + defaults.serialize(attrs), + options + ); + } + + function destroy(resourceConfig, id, options) { + return this.DEL( + DSUtils.makePath(resourceConfig.baseUrl, resourceConfig.endpoint, id), + options + ); + } + }]; +} + +module.exports = DSHttpAdapterProvider; diff --git a/src/binaryHeap/defaults.js b/src/binaryHeap/defaults.js deleted file mode 100644 index d06eb11..0000000 --- a/src/binaryHeap/defaults.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - defaultWeightFunc: function (x) { - return x; - }, - userProvidedDefaultWeightFunc: null -}; diff --git a/src/binaryHeap/index.js b/src/binaryHeap/index.js index 61e6d74..8749cf0 100644 --- a/src/binaryHeap/index.js +++ b/src/binaryHeap/index.js @@ -1,5 +1,3 @@ -var defaults = require('./defaults'); - /*! * @doc method * @id BinaryHeap.private_functions:bubbleUp @@ -193,9 +191,43 @@ BinaryHeap.prototype.size = function () { /** * @doc interface - * @id BinaryHeap - * @name BinaryHeap - * @description - * Binary heap implementation of a priority queue. + * @id BinaryHeapProvider + * @name BinaryHeapProvider */ -module.exports = BinaryHeap; +function BinaryHeapProvider() { + + var defaults = { + defaultWeightFunc: function (x) { + return x; + }, + userProvidedDefaultWeightFunc: null + }; + + /** + * @doc method + * @id BinaryHeapProvider.methods:setDefaultWeightFunction + * @name setDefaultWeightFunction + * @param {function} weightFunc New default weight function. + */ + function setDefaultWeightFunction(weightFunc) { + if (!angular.isFunction(weightFunc)) { + throw new Error('BinaryHeapProvider.setDefaultWeightFunction(weightFunc): weightFunc: Must be a function!'); + } + defaults.userProvidedDefaultWeightFunc = weightFunc; + } + + /** + * @doc method + * @id BinaryHeapProvider.methods:setDefaultWeightFunction + * @name setDefaultWeightFunction + * @methodOf BinaryHeapProvider + * @param {function} weightFunc New default weight function. + */ + this.setDefaultWeightFunction = setDefaultWeightFunction; + + this.$get = function () { + return BinaryHeap; + }; +} + +module.exports = BinaryHeapProvider; diff --git a/src/datastore/adapters/http/index.js b/src/datastore/adapters/http/index.js deleted file mode 100644 index 3f4fc81..0000000 --- a/src/datastore/adapters/http/index.js +++ /dev/null @@ -1,230 +0,0 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'); - -function _$http(deferred, config) { - var start = new Date().getTime(); - - services.$http(config).success(function (data, status, headers, config) { - services.$log.debug(config.method + ' request:' + config.url + ' Time taken: ' + (new Date().getTime() - start) + 'ms', arguments); - deferred.resolve(data); - }).error(function (data, status, headers, config) { - services.$log.error(arguments); - deferred.reject(data); - }); -} - -/** - * @doc method - * @id DS.async_methods:HTTP - * @name HTTP - * @description - * Wrapper for `$http()`. - * - * ## Signature: - * ```js - * DS.HTTP(config) - * ``` - * - * ## Example: - * - * ```js - * works the same as $http() - * ``` - * - * @param {object} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. - */ -function HTTP(config) { - var deferred = services.$q.defer(); - - try { - if (!services.$rootScope.$$phase) { - services.$rootScope.$apply(function () { - _$http(deferred, config); - }); - } else { - _$http(deferred, config); - } - } catch (err) { - deferred.reject(new errors.UnhandledError(err)); - } - - return deferred.promise; -} - -/** - * @doc method - * @id DS.async_methods:GET - * @name GET - * @description - * Wrapper for `$http.get()`. - * - * ## Signature: - * ```js - * DS.GET(url[, config]) - * ``` - * - * ## Example: - * - * ```js - * Works the same as $http.get() - * ``` - * - * @param {string} url The url of the request. - * @param {object=} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. - */ -function GET(url, config) { - config = config || {}; - return HTTP(utils.deepMixIn(config, { - url: url, - method: 'GET' - })); -} - -/** - * @doc method - * @id DS.async_methods:PUT - * @name PUT - * @description - * Wrapper for `$http.put()`. - * - * ## Signature: - * ```js - * DS.PUT(url[, attrs][, config]) - * ``` - * - * ## Example: - * - * ```js - * Works the same as $http.put() - * ``` - * - * @param {string} url The url of the request. - * @param {object=} attrs Request payload. - * @param {object=} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. - */ -function PUT(url, attrs, config) { - config = config || {}; - return HTTP(utils.deepMixIn(config, { - url: url, - data: attrs, - method: 'PUT' - })); -} - -/** - * @doc method - * @id DS.async_methods:POST - * @name POST - * @description - * Wrapper for `$http.post()`. - * - * ## Signature: - * ```js - * DS.POST(url[, attrs][, config]) - * ``` - * - * ## Example: - * - * ```js - * Works the same as $http.post() - * ``` - * - * @param {string} url The url of the request. - * @param {object=} attrs Request payload. - * @param {object=} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. - */ -function POST(url, attrs, config) { - config = config || {}; - return HTTP(utils.deepMixIn(config, { - url: url, - data: attrs, - method: 'POST' - })); -} - -/** - * @doc method - * @id DS.async_methods:DEL - * @name DEL - * @description - * Wrapper for `$http.delete()`. - * - * ## Signature: - * ```js - * DS.DEL(url[, config]) - * ``` - * - * ## Example: - * - * ```js - * Works the same as $http.delete - * ``` - * - * @param {string} url The url of the request. - * @param {object} config Configuration for the request. - * @returns {Promise} Promise produced by the `$q` service. - */ -function DEL(url, config) { - config = config || {}; - return HTTP(utils.deepMixIn(config, { - url: url, - method: 'DELETE' - })); -} - -module.exports = { - /** - * @doc method - * @id DS.async_methods:HTTP - * @name HTTP - * @methodOf DS - * @description - * See [DS.HTTP](/documentation/api/api/DS.async_methods:HTTP). - */ - HTTP: HTTP, - - /** - * @doc method - * @id DS.async_methods:GET - * @name GET - * @methodOf DS - * @description - * See [DS.GET](/documentation/api/api/DS.async_methods:GET). - */ - GET: GET, - - /** - * @doc method - * @id DS.async_methods:POST - * @name POST - * @methodOf DS - * @description - * See [DS.POST](/documentation/api/api/DS.async_methods:POST). - */ - POST: POST, - - /** - * @doc method - * @id DS.async_methods:PUT - * @name PUT - * @methodOf DS - * @description - * See [DS.PUT](/documentation/api/api/DS.async_methods:PUT). - */ - PUT: PUT, - - /** - * @doc method - * @id DS.async_methods:DEL - * @name DEL - * @methodOf DS - * @description - * See [DS.DEL](/documentation/api/api/DS.async_methods:DEL). - */ - DEL: DEL -}; diff --git a/src/datastore/async_methods/create/index.js b/src/datastore/async_methods/create/index.js index d06d62a..30fac21 100644 --- a/src/datastore/async_methods/create/index.js +++ b/src/datastore/async_methods/create/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.create(resourceName, attrs): '; +var errorPrefix = 'DS.create(resourceName, attrs): '; /** * @doc method @@ -32,6 +29,7 @@ var utils = require('utils'), * @param {string} resourceName The resource type, e.g. 'user', 'comment', etc. * @param {object} attrs The attributes with which to update the item of the type specified by `resourceName` that has * the primary key specified by `id`. + * @param {object=} options Configuration options. * @returns {Promise} Promise produced by the `$q` service. * * ## Resolves with: @@ -44,45 +42,47 @@ var utils = require('utils'), * - `{RuntimeError}` * - `{UnhandledError}` */ -function create(resourceName, attrs) { - var deferred = services.$q.defer(), +function create(resourceName, attrs, options) { + var deferred = this.$q.defer(), promise = deferred.promise; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isObject(attrs)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object!', { attrs: { actual: typeof attrs, expected: 'object' } })); + options = options || {}; + + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isObject(attrs)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object!', { attrs: { actual: typeof attrs, expected: 'object' } })); } else { try { - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], _this = this; promise = promise .then(function (attrs) { - return services.$q.promisify(resource.beforeValidate)(resourceName, attrs); + return _this.$q.promisify(definition.beforeValidate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.validate)(resourceName, attrs); + return _this.$q.promisify(definition.validate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.afterValidate)(resourceName, attrs); + return _this.$q.promisify(definition.afterValidate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.beforeCreate)(resourceName, attrs); + return _this.$q.promisify(definition.beforeCreate)(resourceName, attrs); }) .then(function (attrs) { - return services.adapters[resource.defaultAdapter].POST.apply(_this, [utils.makePath(resource.baseUrl, resource.endpoint), attrs, null]); + return _this.adapters[options.adapter || definition.defaultAdapter].create(definition, attrs, options); }) .then(function (data) { - return services.$q.promisify(resource.afterCreate)(resourceName, data); + return _this.$q.promisify(definition.afterCreate)(resourceName, data); }) .then(function (data) { - return _this.inject(resource.name, data); + return _this.inject(definition.name, data); }); deferred.resolve(attrs); } catch (err) { - deferred.reject(new errors.UnhandledError(err)); + deferred.reject(new this.errors.UnhandledError(err)); } } diff --git a/src/datastore/async_methods/destroy/index.js b/src/datastore/async_methods/destroy/index.js index cafce4a..ed86555 100644 --- a/src/datastore/async_methods/destroy/index.js +++ b/src/datastore/async_methods/destroy/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.destroy(resourceName, id): '; +var errorPrefix = 'DS.destroy(resourceName, id): '; /** * @doc method @@ -32,6 +29,7 @@ var utils = require('utils'), * * @param {string} resourceName The resource type, e.g. 'user', 'comment', etc. * @param {string|number} id The primary key of the item to remove. + * @param {object=} options Configuration options. * @returns {Promise} Promise produced by the `$q` service. * * ## Resolves with: @@ -44,34 +42,40 @@ var utils = require('utils'), * - `{RuntimeError}` * - `{UnhandledError}` */ -function destroy(resourceName, id) { - var deferred = services.$q.defer(), +function destroy(resourceName, id, options) { + var deferred = this.$q.defer(), promise = deferred.promise; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); + options = options || {}; + + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); } else { - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this; - promise = promise - .then(function (attrs) { - return services.$q.promisify(resource.beforeDestroy)(resourceName, attrs); - }) - .then(function () { - return services.adapters[resource.defaultAdapter].DEL(utils.makePath(resource.baseUrl, resource.endpoint, id), null); - }) - .then(function () { - return services.$q.promisify(resource.afterDestroy)(resourceName, resource.index[id]); - }) - .then(function () { - _this.eject(resourceName, id); - return id; - }); - - deferred.resolve(resource.index[id]); + if (id in resource.index) { + promise = promise + .then(function (attrs) { + return _this.$q.promisify(definition.beforeDestroy)(resourceName, attrs); + }) + .then(function () { + return _this.adapters[options.adapter || definition.defaultAdapter].destroy(definition, id, options); + }) + .then(function () { + return _this.$q.promisify(definition.afterDestroy)(resourceName, resource.index[id]); + }) + .then(function () { + _this.eject(resourceName, id); + return id; + }); + deferred.resolve(resource.index[id]); + } else { + deferred.resolve(); + } } return promise; diff --git a/src/datastore/async_methods/find/index.js b/src/datastore/async_methods/find/index.js index cc4d98b..6487162 100644 --- a/src/datastore/async_methods/find/index.js +++ b/src/datastore/async_methods/find/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.find(resourceName, id[, options]): '; +var errorPrefix = 'DS.find(resourceName, id[, options]): '; /** * @doc method @@ -48,20 +45,21 @@ var utils = require('utils'), * - `{UnhandledError}` */ function find(resourceName, id, options) { - var deferred = services.$q.defer(), + var deferred = this.$q.defer(), promise = deferred.promise; options = options || {}; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); - } else if (!utils.isObject(options)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); + } else if (!this.utils.isObject(options)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); } else { try { - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this; if (options.bypassCache) { @@ -70,7 +68,7 @@ function find(resourceName, id, options) { if (!(id in resource.completedQueries)) { if (!(id in resource.pendingQueries)) { - promise = resource.pendingQueries[id] = services.adapters[resource.defaultAdapter].GET(utils.makePath(resource.baseUrl, resource.endpoint, id), null) + promise = resource.pendingQueries[id] = _this.adapters[options.adapter || definition.defaultAdapter].find(definition, id, options) .then(function (data) { // Query is no longer pending delete resource.pendingQueries[id]; diff --git a/src/datastore/async_methods/findAll/index.js b/src/datastore/async_methods/findAll/index.js index 74296b0..83e56af 100644 --- a/src/datastore/async_methods/findAll/index.js +++ b/src/datastore/async_methods/findAll/index.js @@ -1,10 +1,7 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.findAll(resourceName, params[, options]): '; +var errorPrefix = 'DS.findAll(resourceName, params[, options]): '; -function processResults(data, resourceName, queryHash) { - var resource = services.store[resourceName]; +function processResults(utils, data, resourceName, queryHash) { + var resource = this.store[resourceName]; data = data || []; @@ -18,15 +15,16 @@ function processResults(data, resourceName, queryHash) { } // Update the data store's index for this resource - resource.index = utils.toLookup(resource.collection, resource.idAttribute); + resource.index = utils.toLookup(resource.collection, this.definitions[resourceName].idAttribute); // Update modified timestamp of collection resource.collectionModified = utils.updateTimestamp(resource.collectionModified); return data; } -function _findAll(resourceName, params, options) { - var resource = services.store[resourceName], +function _findAll(utils, resourceName, params, options) { + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this, queryHash = utils.toJson(params); @@ -40,12 +38,12 @@ function _findAll(resourceName, params, options) { if (!(queryHash in resource.pendingQueries)) { // This particular query has never even been made - resource.pendingQueries[queryHash] = services.adapters[resource.defaultAdapter].GET(utils.makePath(resource.baseUrl, resource.endpoint), { params: params }) + resource.pendingQueries[queryHash] = _this.adapters[options.adapter || definition.defaultAdapter].findAll(definition, { params: params }, options) .then(function (data) { try { - return processResults.apply(_this, [data, resourceName, queryHash]); + return processResults.apply(_this, [utils, data, resourceName, queryHash]); } catch (err) { - throw new errors.UnhandledError(err); + throw new _this.errors.UnhandledError(err); } }); } @@ -124,26 +122,26 @@ function _findAll(resourceName, params, options) { * - `{UnhandledError}` */ function findAll(resourceName, params, options) { - var deferred = services.$q.defer(), + var deferred = this.$q.defer(), promise = deferred.promise, _this = this; options = options || {}; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isObject(params)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } })); - } else if (!utils.isObject(options)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isObject(params)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } })); + } else if (!this.utils.isObject(options)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); } else { try { promise = promise.then(function () { - return _findAll.apply(_this, [resourceName, params, options]); + return _findAll.apply(_this, [_this.utils, resourceName, params, options]); }); deferred.resolve(); } catch (err) { - deferred.reject(new errors.UnhandledError(err)); + deferred.reject(new this.errors.UnhandledError(err)); } } diff --git a/src/datastore/async_methods/refresh/index.js b/src/datastore/async_methods/refresh/index.js index c4e0a25..c4948b9 100644 --- a/src/datastore/async_methods/refresh/index.js +++ b/src/datastore/async_methods/refresh/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.refresh(resourceName, id[, options]): '; +var errorPrefix = 'DS.refresh(resourceName, id[, options]): '; /** * @doc method @@ -57,16 +54,16 @@ var utils = require('utils'), function refresh(resourceName, id, options) { options = options || {}; - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); - } else if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + } else if (!this.utils.isObject(options)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); } else { options.bypassCache = true; - if (id in services.store[resourceName].index) { + if (id in this.store[resourceName].index) { return this.find(resourceName, id, options); } else { return false; diff --git a/src/datastore/async_methods/save/index.js b/src/datastore/async_methods/save/index.js index 194621a..a819284 100644 --- a/src/datastore/async_methods/save/index.js +++ b/src/datastore/async_methods/save/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.save(resourceName, id[, options]): '; +var errorPrefix = 'DS.save(resourceName, id[, options]): '; /** * @doc method @@ -47,46 +44,47 @@ var utils = require('utils'), * - `{UnhandledError}` */ function save(resourceName, id, options) { - var deferred = services.$q.defer(), + var deferred = this.$q.defer(), promise = deferred.promise; options = options || {}; - if (!services.store[resourceName]) { - deferred.reject(new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); - } else if (!utils.isObject(options)) { - deferred.reject(new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); - } else if (!(id in services.store[resourceName].index)) { - deferred.reject(new errors.RuntimeError(errorPrefix + 'id: "' + id + '" not found!')); + if (!this.definitions[resourceName]) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!')); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } })); + } else if (!this.utils.isObject(options)) { + deferred.reject(new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } })); + } else if (!(id in this.store[resourceName].index)) { + deferred.reject(new this.errors.RuntimeError(errorPrefix + 'id: "' + id + '" not found!')); } else { - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this; promise = promise .then(function (attrs) { - return services.$q.promisify(resource.beforeValidate)(resourceName, attrs); + return _this.$q.promisify(definition.beforeValidate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.validate)(resourceName, attrs); + return _this.$q.promisify(definition.validate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.afterValidate)(resourceName, attrs); + return _this.$q.promisify(definition.afterValidate)(resourceName, attrs); }) .then(function (attrs) { - return services.$q.promisify(resource.beforeUpdate)(resourceName, attrs); + return _this.$q.promisify(definition.beforeUpdate)(resourceName, attrs); }) .then(function (attrs) { - return services.adapters[resource.defaultAdapter].PUT(utils.makePath(resource.baseUrl, resource.endpoint, id), attrs, null); + return _this.adapters[options.adapter || definition.defaultAdapter].update(definition, attrs, options); }) .then(function (data) { - return services.$q.promisify(resource.afterUpdate)(resourceName, data); + return _this.$q.promisify(definition.afterUpdate)(resourceName, data); }) .then(function (data) { - var saved = _this.inject(resource.name, data, options); - resource.previous_attributes[id] = utils.deepMixIn({}, data); - resource.saved[id] = utils.updateTimestamp(resource.saved[id]); + var saved = _this.inject(definition.name, data, options); + resource.previousAttributes[id] = _this.utils.deepMixIn({}, data); + resource.saved[id] = _this.utils.updateTimestamp(resource.saved[id]); return saved; }); diff --git a/src/datastore/index.js b/src/datastore/index.js index 192563c..2b3496a 100644 --- a/src/datastore/index.js +++ b/src/datastore/index.js @@ -1,125 +1,26 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - HttpAdapter = require('HttpAdapter'), - configErrorPrefix = 'DSProvider.config(options): ', - registerAdapterErrorPrefix = 'DSProvider.registerAdapter(name, adapter): '; +var errorPrefix = 'DSProvider.registerAdapter(name, adapter): '; -/** - * @doc method - * @id DSProvider.methods:config - * @name config - * @description - * Configure the DS service. - * - * ## Signature: - * ```js - * DSProvider.config(options) - * ``` - * - * ## Example: - * ```js - * DSProvider.config({ - * baseUrl: 'http://myapp.com/api', - * idAttribute: '_id', - * validate: function (resourceName, attrs, cb) { - * console.log('looks good to me'); - * cb(null, attrs); - * } - * }); - * ``` - * - * ## Throws: - * - * - `{IllegalArgumentError}` - * - * @param {object} options Global configuration for the data store. Properties: - * - `{string=}` - `baseUrl` - The default base url to be used by the data store. Can be overridden via `DS.defineResource`. - * - `{string=}` - `idAttribute` - The default property that specifies the primary key of an object. Default: `"id"`. - * - `{function=}` - `beforeValidate` - Global lifecycle hook. Signature: `beforeValidate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `validate` - Global lifecycle hook. Signature: `validate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `afterValidate` - Global lifecycle hook. Signature: `afterValidate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `beforeCreate` - Global lifecycle hook. Signature: `beforeCreate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `afterCreate` - Global lifecycle hook. Signature: `afterCreate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `beforeUpdate` - Global lifecycle hook. Signature: `beforeUpdate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `afterUpdate` - Global lifecycle hook. Signature: `afterUpdate(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `beforeDestroy` - Global lifecycle hook. Signature: `beforeDestroy(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - * - `{function=}` - `afterDestroy` - Global lifecycle hook. Signature: `afterDestroy(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. - */ -function config(options) { - options = options || {}; - - if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options: Must be an object!', { actual: typeof options, expected: 'object' }); - } else if ('baseUrl' in options && !utils.isString(options.baseUrl)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.baseUrl: Must be a string!', { baseUrl: { actual: typeof options.baseUrl, expected: 'string' } }); - } else if ('idAttribute' in options && !utils.isString(options.idAttribute)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.idAttribute: Must be a string!', { idAttribute: { actual: typeof options.idAttribute, expected: 'string' } }); - } else if ('mergeStrategy' in options && !utils.isString(options.mergeStrategy)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.mergeStrategy: Must be a string!', { mergeStrategy: { actual: typeof options.mergeStrategy, expected: 'string' } }); - } else if ('beforeValidate' in options && !utils.isFunction(options.beforeValidate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.beforeValidate: Must be a function!', { beforeValidate: { actual: typeof options.beforeValidate, expected: 'function' } }); - } else if ('validate' in options && !utils.isFunction(options.validate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.validate: Must be a function!', { validate: { actual: typeof options.validate, expected: 'function' } }); - } else if ('afterValidate' in options && !utils.isFunction(options.afterValidate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.afterValidate: Must be a function!', { afterValidate: { actual: typeof options.afterValidate, expected: 'function' } }); - } else if ('beforeCreate' in options && !utils.isFunction(options.beforeCreate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.beforeCreate: Must be a function!', { beforeCreate: { actual: typeof options.beforeCreate, expected: 'function' } }); - } else if ('afterCreate' in options && !utils.isFunction(options.afterCreate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.afterCreate: Must be a function!', { afterCreate: { actual: typeof options.afterCreate, expected: 'function' } }); - } else if ('beforeUpdate' in options && !utils.isFunction(options.beforeUpdate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.beforeUpdate: Must be a function!', { beforeUpdate: { actual: typeof options.beforeUpdate, expected: 'function' } }); - } else if ('afterUpdate' in options && !utils.isFunction(options.afterUpdate)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.afterUpdate: Must be a function!', { afterUpdate: { actual: typeof options.afterUpdate, expected: 'function' } }); - } else if ('beforeDestroy' in options && !utils.isFunction(options.beforeDestroy)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.beforeDestroy: Must be a function!', { beforeDestroy: { actual: typeof options.beforeDestroy, expected: 'function' } }); - } else if ('afterDestroy' in options && !utils.isFunction(options.afterDestroy)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.afterDestroy: Must be a function!', { afterDestroy: { actual: typeof options.afterDestroy, expected: 'function' } }); - } else if ('defaultAdapter' in options && !utils.isString(options.defaultAdapter)) { - throw new errors.IllegalArgumentError(configErrorPrefix + 'options.defaultAdapter: Must be a function!', { defaultAdapter: { actual: typeof options.defaultAdapter, expected: 'string' } }); - } - - services.config = new services.BaseConfig(options); +function lifecycleNoop(resourceName, attrs, cb) { + cb(null, attrs); } -/** - * @doc method - * @id DSProvider.methods:registerAdapter - * @name registerAdapter - * @description - * Register a new adapter. - * - * ## Signature: - * ```js - * DSProvider.registerAdapter(name, adapter); - * ``` - * - * ## Example: - * ```js - * DSProvider.registerAdapter('IndexedDBAdapter', {...}); - * ``` - * - * ## Throws: - * - * - `{IllegalArgumentError}` - * - `{RuntimeError}` - * - * @param {string} name The name of the new adapter. - * @param {object} adapter New adapter. - */ -function registerAdapter(name, adapter) { - - if (!utils.isString(name)) { - throw new errors.IllegalArgumentError(registerAdapterErrorPrefix + 'name: Must be a string!', { actual: typeof name, expected: 'string' }); - } else if (!utils.isObject(adapter)) { - throw new errors.IllegalArgumentError(registerAdapterErrorPrefix + 'adapter: Must be an object!', { actual: typeof adapter, expected: 'object' }); - } else if (services.adapters[name]) { - throw new errors.RuntimeError(registerAdapterErrorPrefix + name + ' is already registered!'); - } - - services.adapters[name] = adapter; +function BaseConfig() { } +BaseConfig.prototype.idAttribute = 'id'; +BaseConfig.prototype.defaultAdapter = 'DSHttpAdapter'; +BaseConfig.prototype.baseUrl = ''; +BaseConfig.prototype.endpoint = ''; +BaseConfig.prototype.beforeValidate = lifecycleNoop; +BaseConfig.prototype.validate = lifecycleNoop; +BaseConfig.prototype.afterValidate = lifecycleNoop; +BaseConfig.prototype.beforeCreate = lifecycleNoop; +BaseConfig.prototype.afterCreate = lifecycleNoop; +BaseConfig.prototype.beforeUpdate = lifecycleNoop; +BaseConfig.prototype.afterUpdate = lifecycleNoop; +BaseConfig.prototype.beforeDestroy = lifecycleNoop; +BaseConfig.prototype.afterDestroy = lifecycleNoop; + /** * @doc interface * @id DSProvider @@ -127,67 +28,59 @@ function registerAdapter(name, adapter) { */ function DSProvider() { - /** - * @doc method - * @id DSProvider.methods:config - * @name config - * @methodOf DSProvider - * @description - * See [DSProvider.config](/documentation/api/api/DSProvider.methods:config). - */ - this.config = config; - - config({}); - - /** - * @doc method - * @id DSProvider.methods:registerAdapter - * @name config - * @methodOf DSProvider - * @description - * See [DSProvider.registerAdapter](/documentation/api/api/DSProvider.methods:registerAdapter). - */ - this.registerAdapter = registerAdapter; - - this.$get = ['$rootScope', '$log', '$http', '$q', function ($rootScope, $log, $http, $q) { - - services.$rootScope = $rootScope; - services.$log = $log; - services.$http = $http; - services.$q = $q; - services.store = {}; - services.adapters = {}; - - registerAdapter('HttpAdapter', HttpAdapter); - - /** - * @doc interface - * @id DS - * @name DS - * @description - * Data store - */ - var DS = { - HttpAdapter: HttpAdapter, - errors: errors - }; - - utils.deepMixIn(DS, require('./sync_methods')); - utils.deepMixIn(DS, require('./async_methods')); - - utils.deepFreeze(DS); - - var $dirtyCheckScope = $rootScope.$new(); - - $dirtyCheckScope.$watch(function () { - // Throttle angular-data's digest loop to tenths of a second - return new Date().getTime() / 100 | 0; - }, function () { - DS.digest(); - }); - - return DS; - }]; + + var defaults = this.defaults = new BaseConfig(), + adapters = this.adapters = {}; + + this.$get = [ + '$rootScope', '$log', '$q', 'DSHttpAdapter', 'DSUtils', 'DSErrors', + function ($rootScope, $log, $q, DSHttpAdapter, DSUtils, DSErrors) { + + var syncMethods = require('./sync_methods'), + asyncMethods = require('./async_methods'); + + adapters.DSHttpAdapter = DSHttpAdapter; + + /** + * @doc interface + * @id DS + * @name DS + * @description + * Data store + */ + var DS = { + $rootScope: $rootScope, + $log: $log, + $q: $q, + defaults: defaults, + store: {}, + definitions: {}, + adapters: adapters, + errors: DSErrors, + utils: DSUtils + }; + + + DSUtils.deepFreeze(syncMethods); + DSUtils.deepFreeze(asyncMethods); + + DSUtils.deepMixIn(DS, syncMethods); + DSUtils.deepMixIn(DS, asyncMethods); + + DSUtils.deepFreeze(DS.errors); + DSUtils.deepFreeze(DS.utils); + + var $dirtyCheckScope = $rootScope.$new(); + + $dirtyCheckScope.$watch(function () { + // Throttle angular-data's digest loop to tenths of a second + return new Date().getTime() / 100 | 0; + }, function () { + DS.digest(); + }); + + return DS; + }]; } module.exports = DSProvider; diff --git a/src/datastore/services/index.js b/src/datastore/services/index.js deleted file mode 100644 index 02b4854..0000000 --- a/src/datastore/services/index.js +++ /dev/null @@ -1,70 +0,0 @@ -function lifecycleNoop(resourceName, attrs, cb) { - cb(null, attrs); -} - -var services = module.exports = { - store: {}, - BaseConfig: function (options) { - if ('idAttribute' in options) { - this.idAttribute = options.idAttribute; - } - - if ('baseUrl' in options) { - this.baseUrl = options.baseUrl; - } - - if ('beforeValidate' in options) { - this.beforeValidate = options.beforeValidate; - } - - if ('validate' in options) { - this.validate = options.validate; - } - - if ('afterValidate' in options) { - this.afterValidate = options.afterValidate; - } - - if ('beforeCreate' in options) { - this.beforeCreate = options.beforeCreate; - } - - if ('afterCreate' in options) { - this.afterCreate = options.afterCreate; - } - - if ('beforeUpdate' in options) { - this.beforeUpdate = options.beforeUpdate; - } - - if ('afterUpdate' in options) { - this.afterUpdate = options.afterUpdate; - } - - if ('beforeDestroy' in options) { - this.beforeDestroy = options.beforeDestroy; - } - - if ('afterDestroy' in options) { - this.afterDestroy = options.afterDestroy; - } - - if ('defaultAdapter' in options) { - this.defaultAdapter = options.defaultAdapter; - } - } -}; - -services.BaseConfig.prototype.idAttribute = 'id'; -services.BaseConfig.prototype.defaultAdapter = 'HttpAdapter'; -services.BaseConfig.prototype.baseUrl = ''; -services.BaseConfig.prototype.endpoint = ''; -services.BaseConfig.prototype.beforeValidate = lifecycleNoop; -services.BaseConfig.prototype.validate = lifecycleNoop; -services.BaseConfig.prototype.afterValidate = lifecycleNoop; -services.BaseConfig.prototype.beforeCreate = lifecycleNoop; -services.BaseConfig.prototype.afterCreate = lifecycleNoop; -services.BaseConfig.prototype.beforeUpdate = lifecycleNoop; -services.BaseConfig.prototype.afterUpdate = lifecycleNoop; -services.BaseConfig.prototype.beforeDestroy = lifecycleNoop; -services.BaseConfig.prototype.afterDestroy = lifecycleNoop; diff --git a/src/datastore/sync_methods/changes/index.js b/src/datastore/sync_methods/changes/index.js index ddcbdc6..db2eb38 100644 --- a/src/datastore/sync_methods/changes/index.js +++ b/src/datastore/sync_methods/changes/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.changes(resourceName, id): '; +var errorPrefix = 'DS.changes(resourceName, id): '; /** * @doc method @@ -38,16 +35,16 @@ var utils = require('utils'), * @returns {object} The changes of the item of the type specified by `resourceName` with the primary key specified by `id`. */ function changes(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { - return angular.copy(services.store[resourceName].changes[id]); + return angular.copy(this.store[resourceName].changes[id]); } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } diff --git a/src/datastore/sync_methods/defineResource/index.js b/src/datastore/sync_methods/defineResource/index.js index e78bdd2..26d2917 100644 --- a/src/datastore/sync_methods/defineResource/index.js +++ b/src/datastore/sync_methods/defineResource/index.js @@ -1,35 +1,16 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.defineResource(definition): '; +var errorPrefix = 'DS.defineResource(definition): '; -function Resource(options) { - services.BaseConfig.apply(this, [options]); +function Resource(utils, options) { - if ('name' in options) { - this.name = options.name; - } + utils.deepMixIn(this, options); if ('endpoint' in options) { this.endpoint = options.endpoint; } else { this.endpoint = this.name; } - - this.collection = []; - this.completedQueries = {}; - this.pendingQueries = {}; - this.index = {}; - this.modified = {}; - this.changes = {}; - this.previous_attributes = {}; - this.saved = {}; - this.observers = {}; - this.collectionModified = 0; } -Resource.prototype = services.config; - /** * @doc method * @id DS.sync_methods:defineResource @@ -80,28 +61,43 @@ Resource.prototype = services.config; * - `{function=}` - `afterDestroy` - Lifecycle hook. Overrides global. Signature: `afterDestroy(resourceName, attrs, cb)`. Callback signature: `cb(err, attrs)`. */ function defineResource(definition) { - if (utils.isString(definition)) { + if (this.utils.isString(definition)) { definition = { name: definition }; } - if (!utils.isObject(definition)) { - throw new errors.IllegalArgumentError(errorPrefix + 'definition: Must be an object!', { definition: { actual: typeof definition, expected: 'object' } }); - } else if (!utils.isString(definition.name)) { - throw new errors.IllegalArgumentError(errorPrefix + 'definition.name: Must be a string!', { definition: { name: { actual: typeof definition.name, expected: 'string' } } }); - } else if (definition.idAttribute && !utils.isString(definition.idAttribute)) { - throw new errors.IllegalArgumentError(errorPrefix + 'definition.idAttribute: Must be a string!', { definition: { idAttribute: { actual: typeof definition.idAttribute, expected: 'string' } } }); - } else if (definition.endpoint && !utils.isString(definition.endpoint)) { - throw new errors.IllegalArgumentError(errorPrefix + 'definition.endpoint: Must be a string!', { definition: { endpoint: { actual: typeof definition.endpoint, expected: 'string' } } }); - } else if (services.store[definition.name]) { - throw new errors.RuntimeError(errorPrefix + definition.name + ' is already registered!'); + if (!this.utils.isObject(definition)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'definition: Must be an object!', { definition: { actual: typeof definition, expected: 'object' } }); + } else if (!this.utils.isString(definition.name)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'definition.name: Must be a string!', { definition: { name: { actual: typeof definition.name, expected: 'string' } } }); + } else if (definition.idAttribute && !this.utils.isString(definition.idAttribute)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'definition.idAttribute: Must be a string!', { definition: { idAttribute: { actual: typeof definition.idAttribute, expected: 'string' } } }); + } else if (definition.endpoint && !this.utils.isString(definition.endpoint)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'definition.endpoint: Must be a string!', { definition: { endpoint: { actual: typeof definition.endpoint, expected: 'string' } } }); + } else if (this.store[definition.name]) { + throw new this.errors.RuntimeError(errorPrefix + definition.name + ' is already registered!'); } try { - services.store[definition.name] = new Resource(definition); + Resource.prototype = this.defaults; + this.definitions[definition.name] = new Resource(this.utils, definition); + + this.store[definition.name] = { + collection: [], + completedQueries: {}, + pendingQueries: {}, + index: {}, + changes: {}, + modified: {}, + saved: {}, + previousAttributes: {}, + observers: {}, + collectionModified: 0 + }; } catch (err) { - delete services.store[definition.name]; - throw new errors.UnhandledError(err); + delete this.definitions[definition.name]; + delete this.store[definition.name]; + throw new this.errors.UnhandledError(err); } } diff --git a/src/datastore/sync_methods/digest/index.js b/src/datastore/sync_methods/digest/index.js index e4fc2ca..55aad43 100644 --- a/src/datastore/sync_methods/digest/index.js +++ b/src/datastore/sync_methods/digest/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - observe = require('observejs'); +var observe = require('observejs'); /** * @doc method @@ -28,15 +25,15 @@ var utils = require('utils'), */ function digest() { try { - if (!services.$rootScope.$$phase) { - services.$rootScope.$apply(function () { + if (!this.$rootScope.$$phase) { + this.$rootScope.$apply(function () { observe.Platform.performMicrotaskCheckpoint(); }); } else { observe.Platform.performMicrotaskCheckpoint(); } } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } diff --git a/src/datastore/sync_methods/eject/index.js b/src/datastore/sync_methods/eject/index.js index 6b473bc..1043476 100644 --- a/src/datastore/sync_methods/eject/index.js +++ b/src/datastore/sync_methods/eject/index.js @@ -1,13 +1,10 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.eject(resourceName, id): '; +var errorPrefix = 'DS.eject(resourceName, id): '; -function _eject(resource, id) { +function _eject(definition, resource, id) { if (id) { var found = false; for (var i = 0; i < resource.collection.length; i++) { - if (resource.collection[i][resource.idAttribute] == id) { + if (resource.collection[i][definition.idAttribute] == id) { found = true; break; } @@ -18,7 +15,7 @@ function _eject(resource, id) { delete resource.observers[id]; delete resource.index[id]; delete resource.changes[id]; - delete resource.previous_attributes[id]; + delete resource.previousAttributes[id]; delete resource.modified[id]; delete resource.saved[id]; } @@ -28,9 +25,8 @@ function _eject(resource, id) { resource.modified = {}; resource.saved = {}; resource.changes = {}; - resource.previous_attributes = {}; + resource.previousAttributes = {}; } - resource.collectionModified = utils.updateTimestamp(resource.collectionModified); } /** @@ -77,22 +73,27 @@ function _eject(resource, id) { * @param {string|number=} id The primary key of the item to eject. */ function eject(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (id && !utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (id && !this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } + var resource = this.store[resourceName], + _this = this; + try { - if (!services.$rootScope.$$phase) { - services.$rootScope.$apply(function () { - _eject(services.store[resourceName], id); + if (!this.$rootScope.$$phase) { + this.$rootScope.$apply(function () { + _eject(_this.definitions[resourceName], resource, id); + resource.collectionModified = _this.utils.updateTimestamp(resource.collectionModified); }); } else { - _eject(services.store[resourceName], id); + _eject(_this.definitions[resourceName], resource, id); + resource.collectionModified = _this.utils.updateTimestamp(resource.collectionModified); } } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } diff --git a/src/datastore/sync_methods/filter/index.js b/src/datastore/sync_methods/filter/index.js index e941c9c..6dcd689 100644 --- a/src/datastore/sync_methods/filter/index.js +++ b/src/datastore/sync_methods/filter/index.js @@ -1,8 +1,5 @@ /* jshint loopfunc: true */ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.filter(resourceName, params[, options]): '; +var errorPrefix = 'DS.filter(resourceName, params[, options]): '; /** * @doc method @@ -47,21 +44,22 @@ var utils = require('utils'), function filter(resourceName, params, options) { options = options || {}; - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isObject(params)) { - throw new errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } }); - } else if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isObject(params)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'params: Must be an object!', { params: { actual: typeof params, expected: 'object' } }); + } else if (!this.utils.isObject(options)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); } try { - var resource = services.store[resourceName]; + var resource = this.store[resourceName], + _this = this; // Protect against null params.query = params.query || {}; - var queryHash = utils.toJson(params); + var queryHash = this.utils.toJson(params); if (!(queryHash in resource.completedQueries) && options.loadFromServer) { // This particular query has never been completed @@ -73,16 +71,16 @@ function filter(resourceName, params, options) { } // The query has been completed, so hit the cache with the query - var filtered = utils.filter(resource.collection, function (value) { + var filtered = this.utils.filter(resource.collection, function (value) { var keep = true; // Apply 'where' clauses if (params.query.where) { - if (!utils.isObject(params.query.where)) { - throw new errors.IllegalArgumentError(errorPrefix + 'params.query.where: Must be an object!', { params: { query: { where: { actual: typeof params.query.where, expected: 'object' } } } }); + if (!_this.utils.isObject(params.query.where)) { + throw new _this.errors.IllegalArgumentError(errorPrefix + 'params.query.where: Must be an object!', { params: { query: { where: { actual: typeof params.query.where, expected: 'object' } } } }); } - utils.forOwn(params.query.where, function (value2, key2) { - if (utils.isString(value2)) { + _this.utils.forOwn(params.query.where, function (value2, key2) { + if (_this.utils.isString(value2)) { value2 = { '===': value2 }; @@ -102,7 +100,7 @@ function filter(resourceName, params, options) { } else if ('<=' in value2) { keep = keep && (value[key2] <= value2['<=']); } else if ('in' in value2) { - keep = keep && utils.contains(value2['in'], value[key2]); + keep = keep && _this.utils.contains(value2['in'], value[key2]); } }); } @@ -111,25 +109,25 @@ function filter(resourceName, params, options) { // Apply 'orderBy' if (params.query.orderBy) { - if (utils.isString(params.query.orderBy)) { + if (this.utils.isString(params.query.orderBy)) { params.query.orderBy = [ [params.query.orderBy, 'ASC'] ]; } - if (utils.isArray(params.query.orderBy)) { + if (this.utils.isArray(params.query.orderBy)) { for (var i = 0; i < params.query.orderBy.length; i++) { - if (utils.isString(params.query.orderBy[i])) { + if (this.utils.isString(params.query.orderBy[i])) { params.query.orderBy[i] = [params.query.orderBy[i], 'ASC']; - } else if (!utils.isArray(params.query.orderBy[i])) { - throw new errors.IllegalArgumentError(errorPrefix + 'params.query.orderBy[' + i + ']: Must be a string or an array!', { params: { query: { 'orderBy[i]': { actual: typeof params.query.orderBy[i], expected: 'string|array' } } } }); + } else if (!this.utils.isArray(params.query.orderBy[i])) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'params.query.orderBy[' + i + ']: Must be a string or an array!', { params: { query: { 'orderBy[i]': { actual: typeof params.query.orderBy[i], expected: 'string|array' } } } }); } - filtered = utils.sort(filtered, function (a, b) { + filtered = this.utils.sort(filtered, function (a, b) { var cA = a[params.query.orderBy[i][0]], cB = b[params.query.orderBy[i][0]]; - if (utils.isString(cA)) { - cA = utils.upperCase(cA); + if (_this.utils.isString(cA)) { + cA = _this.utils.upperCase(cA); } - if (utils.isString(cB)) { - cB = utils.upperCase(cB); + if (_this.utils.isString(cB)) { + cB = _this.utils.upperCase(cB); } if (params.query.orderBy[i][1] === 'DESC') { if (cB < cA) { @@ -151,25 +149,25 @@ function filter(resourceName, params, options) { }); } } else { - throw new errors.IllegalArgumentError(errorPrefix + 'params.query.orderBy: Must be a string or an array!', { params: { query: { orderBy: { actual: typeof params.query.orderBy, expected: 'string|array' } } } }); + throw new this.errors.IllegalArgumentError(errorPrefix + 'params.query.orderBy: Must be a string or an array!', { params: { query: { orderBy: { actual: typeof params.query.orderBy, expected: 'string|array' } } } }); } } // Apply 'limit' and 'skip' - if (utils.isNumber(params.query.limit) && utils.isNumber(params.query.skip)) { - filtered = utils.slice(filtered, params.query.skip, params.query.skip + params.query.limit); - } else if (utils.isNumber(params.query.limit)) { - filtered = utils.slice(filtered, 0, params.query.limit); - } else if (utils.isNumber(params.query.skip)) { - filtered = utils.slice(filtered, params.query.skip); + if (this.utils.isNumber(params.query.limit) && this.utils.isNumber(params.query.skip)) { + filtered = this.utils.slice(filtered, params.query.skip, params.query.skip + params.query.limit); + } else if (this.utils.isNumber(params.query.limit)) { + filtered = this.utils.slice(filtered, 0, params.query.limit); + } else if (this.utils.isNumber(params.query.skip)) { + filtered = this.utils.slice(filtered, params.query.skip); } return filtered; } catch (err) { - if (err instanceof errors.IllegalArgumentError) { + if (err instanceof this.errors.IllegalArgumentError) { throw err; } else { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } } diff --git a/src/datastore/sync_methods/get/index.js b/src/datastore/sync_methods/get/index.js index b313f89..626a932 100644 --- a/src/datastore/sync_methods/get/index.js +++ b/src/datastore/sync_methods/get/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.get(resourceName, id[, options]): '; +var errorPrefix = 'DS.get(resourceName, id[, options]): '; /** * @doc method @@ -37,26 +34,26 @@ var utils = require('utils'), function get(resourceName, id, options) { options = options || {}; - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); - } else if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + } else if (!this.utils.isObject(options)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); } try { // cache miss, request resource from server - if (!(id in services.store[resourceName].index) && options.loadFromServer) { + if (!(id in this.store[resourceName].index) && options.loadFromServer) { this.find(resourceName, id).then(null, function (err) { throw err; }); } // return resource from cache - return services.store[resourceName].index[id]; + return this.store[resourceName].index[id]; } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } diff --git a/src/datastore/sync_methods/hasChanges/index.js b/src/datastore/sync_methods/hasChanges/index.js index 4c33549..6bcfd26 100644 --- a/src/datastore/sync_methods/hasChanges/index.js +++ b/src/datastore/sync_methods/hasChanges/index.js @@ -1,9 +1,6 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.hasChanges(resourceName, id): '; +var errorPrefix = 'DS.hasChanges(resourceName, id): '; -function diffIsEmpty(diff) { +function diffIsEmpty(utils, diff) { return !(utils.isEmpty(diff.added) && utils.isEmpty(diff.removed) && utils.isEmpty(diff.changed)); @@ -43,21 +40,21 @@ function diffIsEmpty(diff) { * @returns {boolean} Whether the item of the type specified by `resourceName` with the primary key specified by `id` has changes. */ function hasChanges(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { // return resource from cache - if (id in services.store[resourceName].changes) { - return diffIsEmpty(services.store[resourceName].changes[id]); + if (id in this.store[resourceName].changes) { + return diffIsEmpty(this.utils, this.store[resourceName].changes[id]); } else { return false; } } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } diff --git a/src/datastore/sync_methods/inject/index.js b/src/datastore/sync_methods/inject/index.js index fc960b0..ee5f9fd 100644 --- a/src/datastore/sync_methods/inject/index.js +++ b/src/datastore/sync_methods/inject/index.js @@ -1,46 +1,43 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - observe = require('observejs'), +var observe = require('observejs'), errorPrefix = 'DS.inject(resourceName, attrs[, options]): '; -function _inject(resource, attrs) { +function _inject(definition, resource, attrs) { var _this = this; function _react(added, removed, changed, getOldValueFn) { try { - var innerId = getOldValueFn(resource.idAttribute); + var innerId = getOldValueFn(definition.idAttribute); - resource.changes[innerId] = utils.diffObjectFromOldObject(resource.index[innerId], resource.previous_attributes[innerId]); - resource.modified[innerId] = utils.updateTimestamp(resource.modified[innerId]); - resource.collectionModified = utils.updateTimestamp(resource.collectionModified); + resource.changes[innerId] = _this.utils.diffObjectFromOldObject(resource.index[innerId], resource.previousAttributes[innerId]); + resource.modified[innerId] = _this.utils.updateTimestamp(resource.modified[innerId]); + resource.collectionModified = _this.utils.updateTimestamp(resource.collectionModified); - if (resource.idAttribute in changed) { - services.$log.error('Doh! You just changed the primary key of an object! ' + - 'I don\'t know how to handle this yet, so your data for the "' + resource.name + + if (definition.idAttribute in changed) { + $log.error('Doh! You just changed the primary key of an object! ' + + 'I don\'t know how to handle this yet, so your data for the "' + definition.name + '" resource is now in an undefined (probably broken) state.'); } } catch (err) { - throw new errors.UnhandledError(err); + throw new _this.errors.UnhandledError(err); } } - if (utils.isArray(attrs)) { + if (_this.utils.isArray(attrs)) { for (var i = 0; i < attrs.length; i++) { - _inject.call(_this, resource, attrs[i]); + _inject.call(_this, definition, resource, attrs[i]); } } else { - if (!(resource.idAttribute in attrs)) { - throw new errors.RuntimeError(errorPrefix + 'attrs: Must contain the property specified by `idAttribute`!'); + if (!(definition.idAttribute in attrs)) { + throw new _this.errors.RuntimeError(errorPrefix + 'attrs: Must contain the property specified by `idAttribute`!'); } else { - var id = attrs[resource.idAttribute]; + var id = attrs[definition.idAttribute]; if (!(id in resource.index)) { resource.index[id] = {}; - resource.previous_attributes[id] = {}; + resource.previousAttributes[id] = {}; - utils.deepMixIn(resource.index[id], attrs); - utils.deepMixIn(resource.previous_attributes[id], attrs); + _this.utils.deepMixIn(resource.index[id], attrs); + _this.utils.deepMixIn(resource.previousAttributes[id], attrs); resource.collection.push(resource.index[id]); @@ -50,7 +47,7 @@ function _inject(resource, attrs) { return id; }); } else { - utils.deepMixIn(resource.index[id], attrs); + _this.utils.deepMixIn(resource.index[id], attrs); resource.observers[id].deliver(); } } @@ -106,29 +103,30 @@ function _inject(resource, attrs) { function inject(resourceName, attrs, options) { options = options || {}; - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isObject(attrs) && !utils.isArray(attrs)) { - throw new errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object or an array!', { attrs: { actual: typeof attrs, expected: 'object|array' } }); - } else if (!utils.isObject(options)) { - throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isObject(attrs) && !this.utils.isArray(attrs)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'attrs: Must be an object or an array!', { attrs: { actual: typeof attrs, expected: 'object|array' } }); + } else if (!this.utils.isObject(options)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { options: { actual: typeof options, expected: 'object' } }); } - var resource = services.store[resourceName], + var definition = this.definitions[resourceName], + resource = this.store[resourceName], _this = this; try { - if (!services.$rootScope.$$phase) { - services.$rootScope.$apply(function () { - _inject.apply(_this, [services.store[resourceName], attrs]); + if (!this.$rootScope.$$phase) { + this.$rootScope.$apply(function () { + _inject.apply(_this, [definition, resource, attrs]); }); } else { - _inject.apply(_this, [services.store[resourceName], attrs]); + _inject.apply(_this, [definition, resource, attrs]); } return attrs; } catch (err) { - if (!(err instanceof errors.RuntimeError)) { - throw new errors.UnhandledError(err); + if (!(err instanceof this.errors.RuntimeError)) { + throw new this.errors.UnhandledError(err); } else { throw err; } diff --git a/src/datastore/sync_methods/lastModified/index.js b/src/datastore/sync_methods/lastModified/index.js index 49a3035..3896769 100644 --- a/src/datastore/sync_methods/lastModified/index.js +++ b/src/datastore/sync_methods/lastModified/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.lastModified(resourceName[, id]): '; +var errorPrefix = 'DS.lastModified(resourceName[, id]): '; /** * @doc method @@ -38,21 +35,21 @@ var utils = require('utils'), * `resourceName` with the given primary key was modified. */ function lastModified(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (id && !utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (id && !this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { if (id) { - if (!(id in services.store[resourceName].modified)) { - services.store[resourceName].modified[id] = 0; + if (!(id in this.store[resourceName].modified)) { + this.store[resourceName].modified[id] = 0; } - return services.store[resourceName].modified[id]; + return this.store[resourceName].modified[id]; } - return services.store[resourceName].collectionModified; + return this.store[resourceName].collectionModified; } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } diff --git a/src/datastore/sync_methods/lastSaved/index.js b/src/datastore/sync_methods/lastSaved/index.js index e626560..b244b1f 100644 --- a/src/datastore/sync_methods/lastSaved/index.js +++ b/src/datastore/sync_methods/lastSaved/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.lastSaved(resourceName[, id]): '; +var errorPrefix = 'DS.lastSaved(resourceName[, id]): '; /** * @doc method @@ -45,21 +42,21 @@ var utils = require('utils'), * `resourceName` with the given primary key was modified. */ function lastSaved(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (id && !utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (id && !this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { if (id) { - if (!(id in services.store[resourceName].saved)) { - services.store[resourceName].saved[id] = 0; + if (!(id in this.store[resourceName].saved)) { + this.store[resourceName].saved[id] = 0; } - return services.store[resourceName].saved[id]; + return this.store[resourceName].saved[id]; } - return services.store[resourceName].collectionModified; + return this.store[resourceName].collectionModified; } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } diff --git a/src/datastore/sync_methods/previous/index.js b/src/datastore/sync_methods/previous/index.js index 79aae82..6655dc2 100644 --- a/src/datastore/sync_methods/previous/index.js +++ b/src/datastore/sync_methods/previous/index.js @@ -1,7 +1,4 @@ -var utils = require('utils'), - errors = require('errors'), - services = require('services'), - errorPrefix = 'DS.previous(resourceName, id): '; +var errorPrefix = 'DS.previous(resourceName, id): '; /** * @doc method @@ -39,17 +36,17 @@ var utils = require('utils'), * @returns {object} The previous attributes of the item of the type specified by `resourceName` with the primary key specified by `id`. */ function previous(resourceName, id) { - if (!services.store[resourceName]) { - throw new errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); - } else if (!utils.isString(id) && !utils.isNumber(id)) { - throw new errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); + if (!this.definitions[resourceName]) { + throw new this.errors.RuntimeError(errorPrefix + resourceName + ' is not a registered resource!'); + } else if (!this.utils.isString(id) && !this.utils.isNumber(id)) { + throw new this.errors.IllegalArgumentError(errorPrefix + 'id: Must be a string or a number!', { id: { actual: typeof id, expected: 'string|number' } }); } try { // return resource from cache - return angular.copy(services.store[resourceName].previous_attributes[id]); + return angular.copy(this.store[resourceName].previousAttributes[id]); } catch (err) { - throw new errors.UnhandledError(err); + throw new this.errors.UnhandledError(err); } } diff --git a/src/errors/index.js b/src/errors/index.js index 74a1b85..1c1278d 100644 --- a/src/errors/index.js +++ b/src/errors/index.js @@ -101,52 +101,6 @@ function IllegalArgumentError(message, errors) { IllegalArgumentError.prototype = Object.create(Error.prototype); IllegalArgumentError.prototype.constructor = IllegalArgumentError; -/** - * @doc function - * @id errors.types:ValidationError - * @name ValidationError - * @description Error that is thrown/returned when validation of a schema fails. - * @param {string=} message Error message. Default: `"Validation Error!"`. - * @param {object=} errors Object containing information about the error. - * @returns {ValidationError} A new instance of `ValidationError`. - */ -function ValidationError(message, errors) { - Error.call(this); - if (typeof Error.captureStackTrace === 'function') { - Error.captureStackTrace(this, this.constructor); - } - - /** - * @doc property - * @id errors.types:ValidationError.type - * @name type - * @propertyOf errors.types:ValidationError - * @description Name of error type. Default: `"ValidationError"`. - */ - this.type = this.constructor.name; - - /** - * @doc property - * @id errors.types:ValidationError.errors - * @name errors - * @propertyOf errors.types:ValidationError - * @description Object containing information about the error. - */ - this.errors = errors || {}; - - /** - * @doc property - * @id errors.types:ValidationError.message - * @name message - * @propertyOf errors.types:ValidationError - * @description Error message. Default: `"Validation Error!"`. - */ - this.message = message || 'Validation Error!'; -} - -ValidationError.prototype = Object.create(Error.prototype); -ValidationError.prototype.constructor = ValidationError; - /** * @doc function * @id errors.types:RuntimeError @@ -198,13 +152,14 @@ RuntimeError.prototype.constructor = RuntimeError; * @id errors * @name angular-data error types * @description - * `UnhandledError`, `IllegalArgumentError`, `RuntimeError` and `ValidationError`. + * `UnhandledError`, `IllegalArgumentError` and `RuntimeError`. * * References to the constructor functions of these errors can be found at `DS.errors`. */ -module.exports = { - UnhandledError: UnhandledError, - IllegalArgumentError: IllegalArgumentError, - ValidationError: ValidationError, - RuntimeError: RuntimeError -}; +module.exports = [function () { + return { + UnhandledError: UnhandledError, + IllegalArgumentError: IllegalArgumentError, + RuntimeError: RuntimeError + }; +}]; diff --git a/src/index.js b/src/index.js index 977190d..c30d176 100644 --- a/src/index.js +++ b/src/index.js @@ -1,74 +1,41 @@ (function (window, angular, undefined) { 'use strict'; -// angular.module('jmdobry.binary-heap', []); -// -// /** -// * @doc interface -// * @id BinaryHeapProvider -// * @name BinaryHeapProvider -// */ -// function BinaryHeapProvider() { -// -// var defaults = require('./binaryHeap/defaults'); -// -// /** -// * @doc method -// * @id BinaryHeapProvider.methods:setDefaultWeightFunction -// * @name setDefaultWeightFunction -// * @param {function} weightFunc New default weight function. -// */ -// function setDefaultWeightFunction(weightFunc) { -// if (!angular.isFunction(weightFunc)) { -// throw new Error('BinaryHeapProvider.setDefaultWeightFunction(weightFunc): weightFunc: Must be a function!'); -// } -// defaults.userProvidedDefaultWeightFunc = weightFunc; -// } -// -// /** -// * @doc method -// * @id BinaryHeapProvider.methods:setDefaultWeightFunction -// * @name setDefaultWeightFunction -// * @methodOf BinaryHeapProvider -// * @param {function} weightFunc New default weight function. -// */ -// this.setDefaultWeightFunction = setDefaultWeightFunction; -// -// this.$get = function () { -// return require('./binaryHeap'); -// }; -// } -// angular.module('jmdobry.binary-heap').provider('BinaryHeap', BinaryHeapProvider); + angular.module('angular-data.BinaryHeap', []) + .provider('BinaryHeap', require('./binaryHeap')); + angular.module('angular-data.DS', ['ng', 'angular-data.BinaryHeap']) + .service('DSUtils', require('./utils')) + .service('DSErrors', require('./errors')) + .provider('DSHttpAdapter', require('./adapters/http')) + .provider('DS', require('./datastore')) + .config(['$provide', function ($provide) { + $provide.decorator('$q', function ($delegate) { + // do whatever you you want + $delegate.promisify = function (fn, target) { + var _this = this; + return function () { + var deferred = _this.defer(), + args = Array.prototype.slice.apply(arguments); - angular.module('jmdobry.angular-data', ['ng'/*, 'jmdobry.binary-heap'*/]).config(['$provide', function ($provide) { - $provide.decorator('$q', function ($delegate) { - // do whatever you you want - $delegate.promisify = function (fn, target) { - var _this = this; - return function () { - var deferred = _this.defer(), - args = Array.prototype.slice.apply(arguments); + args.push(function (err, result) { + if (err) { + deferred.reject(err); + } else { + deferred.resolve(result); + } + }); - args.push(function (err, result) { - if (err) { + try { + fn.apply(target || this, args); + } catch (err) { deferred.reject(err); - } else { - deferred.resolve(result); } - }); - try { - fn.apply(target || this, args); - } catch (err) { - deferred.reject(err); - } - - return deferred.promise; + return deferred.promise; + }; }; - }; - return $delegate; - }); - }]); - angular.module('jmdobry.angular-data').provider('DS', require('./datastore')); + return $delegate; + }); + }]); })(window, window.angular); diff --git a/src/utils/index.js b/src/utils/index.js index 8491f3e..d46789c 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,76 +1,78 @@ -module.exports = { - isString: angular.isString, - isArray: angular.isArray, - isObject: angular.isObject, - isNumber: angular.isNumber, - isFunction: angular.isFunction, - isEmpty: require('mout/lang/isEmpty'), - toJson: angular.toJson, - makePath: require('mout/string/makePath'), - upperCase: require('mout/string/upperCase'), - deepMixIn: require('mout/object/deepMixIn'), - forOwn: require('mout/object/forOwn'), - contains: require('mout/array/contains'), - filter: require('mout/array/filter'), - toLookup: require('mout/array/toLookup'), - slice: require('mout/array/slice'), - sort: require('mout/array/sort'), - updateTimestamp: function (timestamp) { - var newTimestamp = typeof Date.now === 'function' ? Date.now() : new Date().getTime(); - if (timestamp && newTimestamp <= timestamp) { - return timestamp + 1; - } else { - return newTimestamp; - } - }, - deepFreeze: function deepFreeze(o) { - if (typeof Object.freeze === 'function') { - var prop, propKey; - Object.freeze(o); // First freeze the object. - for (propKey in o) { - prop = o[propKey]; - if (!o.hasOwnProperty(propKey) || typeof prop !== 'object' || Object.isFrozen(prop)) { - // If the object is on the prototype, not an object, or is already frozen, - // skip it. Note that this might leave an unfrozen reference somewhere in the - // object if there is an already frozen object containing an unfrozen object. - continue; - } +module.exports = [function () { + return { + isString: angular.isString, + isArray: angular.isArray, + isObject: angular.isObject, + isNumber: angular.isNumber, + isFunction: angular.isFunction, + isEmpty: require('mout/lang/isEmpty'), + toJson: angular.toJson, + makePath: require('mout/string/makePath'), + upperCase: require('mout/string/upperCase'), + deepMixIn: require('mout/object/deepMixIn'), + forOwn: require('mout/object/forOwn'), + contains: require('mout/array/contains'), + filter: require('mout/array/filter'), + toLookup: require('mout/array/toLookup'), + slice: require('mout/array/slice'), + sort: require('mout/array/sort'), + updateTimestamp: function (timestamp) { + var newTimestamp = typeof Date.now === 'function' ? Date.now() : new Date().getTime(); + if (timestamp && newTimestamp <= timestamp) { + return timestamp + 1; + } else { + return newTimestamp; + } + }, + deepFreeze: function deepFreeze(o) { + if (typeof Object.freeze === 'function') { + var prop, propKey; + Object.freeze(o); // First freeze the object. + for (propKey in o) { + prop = o[propKey]; + if (!o.hasOwnProperty(propKey) || typeof prop !== 'object' || Object.isFrozen(prop)) { + // If the object is on the prototype, not an object, or is already frozen, + // skip it. Note that this might leave an unfrozen reference somewhere in the + // object if there is an already frozen object containing an unfrozen object. + continue; + } - deepFreeze(prop); // Recursively call deepFreeze. + deepFreeze(prop); // Recursively call deepFreeze. + } } - } - }, - diffObjectFromOldObject: function (object, oldObject) { - var added = {}; - var removed = {}; - var changed = {}; + }, + diffObjectFromOldObject: function (object, oldObject) { + var added = {}; + var removed = {}; + var changed = {}; + + for (var prop in oldObject) { + var newValue = object[prop]; - for (var prop in oldObject) { - var newValue = object[prop]; + if (newValue !== undefined && newValue === oldObject[prop]) + continue; - if (newValue !== undefined && newValue === oldObject[prop]) - continue; + if (!(prop in object)) { + removed[prop] = undefined; + continue; + } - if (!(prop in object)) { - removed[prop] = undefined; - continue; + if (newValue !== oldObject[prop]) + changed[prop] = newValue; } - if (newValue !== oldObject[prop]) - changed[prop] = newValue; - } + for (var prop2 in object) { + if (prop2 in oldObject) + continue; - for (var prop2 in object) { - if (prop2 in oldObject) - continue; + added[prop2] = object[prop2]; + } - added[prop2] = object[prop2]; + return { + added: added, + removed: removed, + changed: changed + }; } - - return { - added: added, - removed: removed, - changed: changed - }; - } -}; + }; +}]; diff --git a/test/integration/datastore/index.test.js b/test/integration/datastore/index.test.js deleted file mode 100644 index 39479bc..0000000 --- a/test/integration/datastore/index.test.js +++ /dev/null @@ -1,115 +0,0 @@ -describe('DSProvider.config(options)', function () { - var errorPrefix = 'DSProvider.config(options): '; - - it('should throw an error when method pre-conditions are not met', function (done) { - angular.forEach(TYPES_EXCEPT_OBJECT, function (key) { - if (key) { - assert.throws(function () { - DSProvider.config(key); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options: Must be an object!'); - } else { - assert.doesNotThrow(function () { - DSProvider.config(key); - }, 'Should not fail'); - } - }); - - angular.forEach(TYPES_EXCEPT_STRING, function (key) { - assert.throws(function () { - DSProvider.config({ - baseUrl: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.baseUrl: Must be a string!'); - }); - - angular.forEach(TYPES_EXCEPT_STRING, function (key) { - assert.throws(function () { - DSProvider.config({ - idAttribute: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.idAttribute: Must be a string!'); - }); - - angular.forEach(TYPES_EXCEPT_STRING, function (key) { - assert.throws(function () { - DSProvider.config({ - mergeStrategy: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.mergeStrategy: Must be a string!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - beforeValidate: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.beforeValidate: Must be a function!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - validate: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.validate: Must be a function!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - afterValidate: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.afterValidate: Must be a function!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - beforeCreate: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.beforeCreate: Must be a function!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - afterCreate: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.afterCreate: Must be a function!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - beforeUpdate: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.beforeUpdate: Must be a function!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - afterUpdate: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.afterUpdate: Must be a function!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - beforeDestroy: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.beforeDestroy: Must be a function!'); - }); - - angular.forEach(TYPES_EXCEPT_FUNCTION, function (key) { - assert.throws(function () { - DSProvider.config({ - afterDestroy: key - }); - }, DS.errors.IllegalArgumentError, errorPrefix + 'options.afterDestroy: Must be a function!'); - }); - - done(); - }); -});