angular-data
Advanced tools
Comparing version 1.0.0-rc.1 to 1.0.0-rc.2
@@ -0,1 +1,26 @@ | ||
##### 1.0.0-rc.2 - 25 September 2014 | ||
###### Backwards compatible API changes | ||
- #145 - Add "useClass" option to inject, find, findAll, create | ||
- #155 - Allow deserialize and serialize to be configured per-method as well | ||
- #159 - Find which items from collection have changed with lastModified | ||
- #161 - Allow the http method of DSHttpAdapter methods to be configured | ||
- #166 - Add ID Resolver | ||
- #167 - Default params argument of bindAll to empty object | ||
- #169 - Added eager inject and eager eject options | ||
- #170 - Global callbacks | ||
- #171 - "not in" query | ||
- #175 - Explore adding support for collection-based methods to the DSLocalStorageAdapter | ||
- #177 - Allow promises to be returned in lifecycle hooks | ||
- #182 - option to force trailing slash (DSHttpAdapterProvider.defaults.forceTrailingSlash) | ||
- #185 - eagerInject/eagerEject should default to definition's options | ||
- #186 - eagerInject doesn't add the model to the list after HTTP success | ||
- #187 - Proxy some static methods to instance methods | ||
- Made the `notify` option configurable globally and per-resource | ||
###### Backwards compatible bug fixes | ||
- #156 - cached findAll pending query doesn't get removed sometimes | ||
- #163 - loadRelations shouldn't try to load a relation if the id for it is missing | ||
- #165 - DS.hasChanges() reports changes after loading relations | ||
##### 1.0.0-rc.1 - 03 September 2014 | ||
@@ -2,0 +27,0 @@ |
{ | ||
"name": "angular-data", | ||
"description": "Data store for Angular.js.", | ||
"version": "1.0.0-rc.1", | ||
"version": "1.0.0-rc.2", | ||
"homepage": "http://angular-data.pseudobry.com", | ||
@@ -6,0 +6,0 @@ "repository": { |
@@ -1,2 +0,2 @@ | ||
## angular-data | ||
## angular-data [![Stories in Backlog](https://badge.waffle.io/jmdobry/angular-data.svg?label=backlog&title=Backlog)](http://waffle.io/jmdobry/angular-data) [![Stories in Ready](https://badge.waffle.io/jmdobry/angular-data.svg?label=ready&title=Ready)](http://waffle.io/jmdobry/angular-data) [![Stories in progress](https://badge.waffle.io/jmdobry/angular-data.svg?label=in%20progress&title=In%20Progress)](http://waffle.io/jmdobry/angular-data) | ||
@@ -12,3 +12,3 @@ Inspired by [Ember Data](https://github.com/emberjs/data), Angular-data is the model layer Angular is missing. It consists of a convenient in-memory cache for managing your data, and several adapters for communicating with various persistence layers. | ||
__Latest Release:__ [1.0.0-rc.1](http://angular-data.pseudobry.com/) | ||
__master:__ [1.0.0-rc.1](http://angular-data-next.pseudobry.com/) | ||
__master:__ [1.0.0-rc.2](http://angular-data-next.pseudobry.com/) | ||
@@ -82,15 +82,15 @@ Angular-data is in a 1.0.0 Beta. The API is rather stable and angular-data is well tested. | ||
## Guide | ||
- [Overview](/documentation/guide/angular-data/index) | ||
- [Basics](/documentation/guide/angular-data/overview) | ||
- [Defining Resources](/documentation/guide/angular-data-resource/basic) | ||
- [Asynchronous Methods](/documentation/guide/angular-data/asynchronous) | ||
- [Synchronous Methods](/documentation/guide/angular-data/synchronous) | ||
- [Queries & Filtering](/documentation/guide/angular-data/queries) | ||
- [Adapters](/documentation/guide/angular-data/adapters) | ||
- [How do I...?](/documentation/guide/angular-data/how) | ||
- [Overview](http://angular-data.pseudobry.com/documentation/guide/angular-data/index) | ||
- [Basics](http://angular-data.pseudobry.com/documentation/guide/angular-data/overview) | ||
- [Defining Resources](http://angular-data.pseudobry.com/documentation/guide/angular-data-resource/basic) | ||
- [Asynchronous Methods](http://angular-data.pseudobry.com/documentation/guide/angular-data/asynchronous) | ||
- [Synchronous Methods](http://angular-data.pseudobry.com/documentation/guide/angular-data/synchronous) | ||
- [Queries & Filtering](http://angular-data.pseudobry.com/documentation/guide/angular-data/queries) | ||
- [Adapters](http://angular-data.pseudobry.com/documentation/guide/angular-data/adapters) | ||
- [How do I...?](http://angular-data.pseudobry.com/documentation/guide/angular-data/how) | ||
## API | ||
- [Overview](/documentation/api/angular-data/angular-data) | ||
- [DS](/documentation/api/angular-data/DS) | ||
- [DSHttpAdapter](/documentation/api/angular-data/DSHttpAdapter) | ||
- [Overview](http://angular-data.pseudobry.com/documentation/api/angular-data/angular-data) | ||
- [DS](http://angular-data.pseudobry.com/documentation/api/angular-data/DS) | ||
- [DSHttpAdapter](http://angular-data.pseudobry.com/documentation/api/angular-data/DSHttpAdapter) | ||
@@ -97,0 +97,0 @@ ## Changelog |
@@ -46,2 +46,4 @@ /** | ||
forceTrailingSlash: false, | ||
/** | ||
@@ -107,2 +109,5 @@ * @doc property | ||
if (this.defaults.forceTrailingSlash && config.url[config.url.length] !== '/') { | ||
config.url += '/'; | ||
} | ||
config = DSUtils.deepMixIn(config, defaults.$httpConfig); | ||
@@ -133,5 +138,7 @@ return $http(config).then(function (data) { | ||
config = config || {}; | ||
if (!('method' in config)) { | ||
config.method = 'GET'; | ||
} | ||
return this.HTTP(DSUtils.deepMixIn(config, { | ||
url: url, | ||
method: 'GET' | ||
url: url | ||
})); | ||
@@ -159,6 +166,8 @@ }, | ||
config = config || {}; | ||
if (!('method' in config)) { | ||
config.method = 'POST'; | ||
} | ||
return this.HTTP(DSUtils.deepMixIn(config, { | ||
url: url, | ||
data: attrs, | ||
method: 'POST' | ||
data: attrs | ||
})); | ||
@@ -186,6 +195,8 @@ }, | ||
config = config || {}; | ||
if (!('method' in config)) { | ||
config.method = 'PUT'; | ||
} | ||
return this.HTTP(DSUtils.deepMixIn(config, { | ||
url: url, | ||
data: attrs || {}, | ||
method: 'PUT' | ||
data: attrs || {} | ||
})); | ||
@@ -212,5 +223,7 @@ }, | ||
config = config || {}; | ||
if (!('method' in config)) { | ||
config.method = 'DELETE'; | ||
} | ||
return this.HTTP(DSUtils.deepMixIn(config, { | ||
url: url, | ||
method: 'DELETE' | ||
url: url | ||
})); | ||
@@ -217,0 +230,0 @@ }, |
@@ -8,3 +8,3 @@ /*! | ||
this.$get = ['$q', 'DSUtils', 'DSErrors', function ($q, DSUtils, DSErrors) { | ||
this.$get = ['$q', 'DSUtils', 'DSErrors', function ($q, DSUtils) { | ||
@@ -21,2 +21,32 @@ /** | ||
getIds: function (name, options) { | ||
var ids; | ||
var idsPath = DSUtils.makePath(options.baseUrl, 'DSKeys', name); | ||
var idsJson = localStorage.getItem(idsPath); | ||
if (idsJson) { | ||
ids = DSUtils.fromJson(idsJson); | ||
} else { | ||
localStorage.setItem(idsPath, DSUtils.toJson({})); | ||
ids = {}; | ||
} | ||
return ids; | ||
}, | ||
saveKeys: function (ids, name, options) { | ||
var keysPath = DSUtils.makePath(options.baseUrl, 'DSKeys', name); | ||
localStorage.setItem(keysPath, DSUtils.toJson(ids)); | ||
}, | ||
ensureId: function (id, name, options) { | ||
var ids = this.getIds(name, options); | ||
ids[id] = 1; | ||
this.saveKeys(ids, name, options); | ||
}, | ||
removeId: function (id, name, options) { | ||
var ids = this.getIds(name, options); | ||
delete ids[id]; | ||
this.saveKeys(ids, name, options); | ||
}, | ||
/** | ||
@@ -132,3 +162,9 @@ * @doc method | ||
options = options || {}; | ||
return this.GET(DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint, id)); | ||
return this.GET(DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.endpoint, id)).then(function (item) { | ||
if (!item) { | ||
return $q.reject(new Error('Not Found!')); | ||
} else { | ||
return item; | ||
} | ||
}); | ||
}, | ||
@@ -141,6 +177,34 @@ | ||
* @description | ||
* Not supported. | ||
* Retrieve a collections of entities from localStorage. | ||
* | ||
* ## Signature: | ||
* ```js | ||
* DSLocalStorageAdapter.findAll(resourceConfig, params[, options]) | ||
* ``` | ||
* | ||
* @param {object} resourceConfig DS resource definition object: | ||
* @param {object=} params Query parameters. | ||
* @param {object=} options Optional configuration. Properties: | ||
* | ||
* - `{string=}` - `baseUrl` - Base path to use. | ||
* | ||
* @returns {Promise} Promise. | ||
*/ | ||
findAll: function () { | ||
throw new Error('DSLocalStorageAdapter.findAll is not supported!'); | ||
findAll: function (resourceConfig, params, options) { | ||
var _this = this; | ||
var deferred = $q.defer(); | ||
options = options || {}; | ||
if (!('allowSimpleWhere' in options)) { | ||
options.allowSimpleWhere = true; | ||
} | ||
var items = []; | ||
var ids = DSUtils.keys(_this.getIds(resourceConfig.name, options)); | ||
DSUtils.forEach(ids, function (id) { | ||
var itemJson = localStorage.getItem(DSUtils.makePath(options.baseUrl || resourceConfig.baseUrl, resourceConfig.getEndpoint(id, options), id)); | ||
if (itemJson) { | ||
items.push(DSUtils.fromJson(itemJson)); | ||
} | ||
}); | ||
deferred.resolve(_this.DS.defaults.defaultFilter.call(_this.DS, items, resourceConfig.name, params, options)); | ||
return deferred.promise; | ||
}, | ||
@@ -181,5 +245,4 @@ | ||
create: function (resourceConfig, attrs, options) { | ||
if (!(resourceConfig.idAttribute in attrs)) { | ||
throw new DSErrors.IA('DSLocalStorageAdapter.create: You must provide a primary key in the attrs object!'); | ||
} | ||
var _this = this; | ||
attrs[resourceConfig.idAttribute] = attrs[resourceConfig.idAttribute] || DSUtils.guid(); | ||
options = options || {}; | ||
@@ -189,3 +252,6 @@ return this.PUT( | ||
attrs | ||
); | ||
).then(function (item) { | ||
_this.ensureId(item[resourceConfig.idAttribute], resourceConfig.name, options); | ||
return item; | ||
}); | ||
}, | ||
@@ -235,6 +301,27 @@ | ||
* @description | ||
* Not supported. | ||
* Update a collections of entities in localStorage. | ||
* | ||
* ## Signature: | ||
* ```js | ||
* DSLocalStorageAdapter.updateAll(resourceConfig, attrs, params[, options]) | ||
* ``` | ||
* | ||
* @param {object} resourceConfig DS resource definition object: | ||
* @param {object} attrs Attributes with which to update the items. | ||
* @param {object=} params Query parameters. | ||
* @param {object=} options Optional configuration. Properties: | ||
* | ||
* - `{string=}` - `baseUrl` - Base path to use. | ||
* | ||
* @returns {Promise} Promise. | ||
*/ | ||
updateAll: function () { | ||
throw new Error('DSLocalStorageAdapter.updateAll is not supported!'); | ||
updateAll: function (resourceConfig, attrs, params, options) { | ||
var _this = this; | ||
return this.findAll(resourceConfig, params, options).then(function (items) { | ||
var tasks = []; | ||
DSUtils.forEach(items, function (item) { | ||
tasks.push(_this.update(resourceConfig, item[resourceConfig.idAttribute], attrs, options)); | ||
}); | ||
return $q.all(tasks); | ||
}); | ||
}, | ||
@@ -283,6 +370,26 @@ | ||
* @description | ||
* Not supported. | ||
* Destroy a collections of entities from localStorage. | ||
* | ||
* ## Signature: | ||
* ```js | ||
* DSLocalStorageAdapter.destroyAll(resourceConfig, params[, options]) | ||
* ``` | ||
* | ||
* @param {object} resourceConfig DS resource definition object: | ||
* @param {object=} params Query parameters. | ||
* @param {object=} options Optional configuration. Properties: | ||
* | ||
* - `{string=}` - `baseUrl` - Base path to use. | ||
* | ||
* @returns {Promise} Promise. | ||
*/ | ||
destroyAll: function () { | ||
throw new Error('Not supported!'); | ||
destroyAll: function (resourceConfig, params, options) { | ||
var _this = this; | ||
return this.findAll(resourceConfig, params, options).then(function (items) { | ||
var tasks = []; | ||
DSUtils.forEach(items, function (item) { | ||
tasks.push(_this.destroy(resourceConfig, item[resourceConfig.idAttribute], options)); | ||
}); | ||
return $q.all(tasks); | ||
}); | ||
} | ||
@@ -289,0 +396,0 @@ }; |
@@ -35,4 +35,6 @@ function errorPrefix(resourceName) { | ||
* | ||
* - `{boolean=}` - `useClass` - Whether to wrap the injected item with the resource's instance constructor. | ||
* - `{boolean=}` - `cacheResponse` - Inject the data returned by the adapter into the data store. Default: `true`. | ||
* - `{boolean=}` - `upsert` - If `attrs` already contains a primary key, then attempt to call `DS.update` instead. Default: `true`. | ||
* - `{boolean=}` - `eagerInject` - Eagerly inject the attributes into the store without waiting for a successful response from the adapter. Default: `false`. | ||
* - `{function=}` - `beforeValidate` - Override the resource or global lifecycle hook. | ||
@@ -61,2 +63,3 @@ * - `{function=}` - `validate` - Override the resource or global lifecycle hook. | ||
var definition = DS.definitions[resourceName]; | ||
var injected; | ||
@@ -78,2 +81,10 @@ options = options || {}; | ||
if (!('eagerInject' in options)) { | ||
options.eagerInject = definition.eagerInject; | ||
} | ||
if (!('notify' in options)) { | ||
options.notify = definition.notify; | ||
} | ||
deferred.resolve(attrs); | ||
@@ -102,13 +113,37 @@ | ||
.then(function (attrs) { | ||
return DS.adapters[options.adapter || definition.defaultAdapter].create(definition, definition.serialize(resourceName, attrs), options); | ||
if (options.notify) { | ||
DS.emit(definition, 'beforeCreate', DS.utils.merge({}, attrs)); | ||
} | ||
if (options.eagerInject && options.cacheResponse) { | ||
attrs[definition.idAttribute] = attrs[definition.idAttribute] || DS.utils.guid(); | ||
injected = DS.inject(resourceName, attrs); | ||
} | ||
return DS.adapters[options.adapter || definition.defaultAdapter].create(definition, options.serialize ? options.serialize(resourceName, attrs) : definition.serialize(resourceName, attrs), options); | ||
}) | ||
.then(function (res) { | ||
var func = options.afterCreate ? DS.$q.promisify(options.afterCreate) : definition.afterCreate; | ||
var attrs = definition.deserialize(resourceName, res); | ||
var attrs = options.deserialize ? options.deserialize(resourceName, res) : definition.deserialize(resourceName, res); | ||
return func.call(attrs, resourceName, attrs); | ||
}) | ||
.then(function (data) { | ||
.then(function (attrs) { | ||
if (options.notify) { | ||
DS.emit(definition, 'afterCreate', DS.utils.merge({}, attrs)); | ||
} | ||
if (options.cacheResponse) { | ||
var resource = DS.store[resourceName]; | ||
var created = DS.inject(definition.name, data, options); | ||
if (options.eagerInject) { | ||
var newId = attrs[definition.idAttribute]; | ||
var prevId = injected[definition.idAttribute]; | ||
var prev = DS.get(resourceName, prevId); | ||
resource.previousAttributes[newId] = resource.previousAttributes[prevId]; | ||
resource.changeHistories[newId] = resource.changeHistories[prevId]; | ||
resource.observers[newId] = resource.observers[prevId]; | ||
resource.modified[newId] = resource.modified[prevId]; | ||
resource.saved[newId] = resource.saved[prevId]; | ||
resource.index.put(newId, prev); | ||
DS.eject(resourceName, prevId, { notify: false }); | ||
prev[definition.idAttribute] = newId; | ||
resource.collection.push(prev); | ||
} | ||
var created = DS.inject(resourceName, attrs, options); | ||
var id = created[definition.idAttribute]; | ||
@@ -118,6 +153,12 @@ resource.completedQueries[id] = new Date().getTime(); | ||
resource.saved[id] = DS.utils.updateTimestamp(resource.saved[id]); | ||
return DS.get(definition.name, id); | ||
return DS.get(resourceName, id); | ||
} else { | ||
return data; | ||
return DS.createInstance(resourceName, attrs, options); | ||
} | ||
}) | ||
.catch(function (err) { | ||
if (options.eagerInject && options.cacheResponse) { | ||
DS.eject(resourceName, injected[definition.idAttribute], { notify: false }); | ||
} | ||
return DS.$q.reject(err); | ||
}); | ||
@@ -124,0 +165,0 @@ } |
@@ -35,2 +35,3 @@ function errorPrefix(resourceName, id) { | ||
* - `{function=}` - `afterDestroy` - Override the resource or global lifecycle hook. | ||
* - `{boolean=}` - `eagerEject` - If `true` eagerly eject the item from the store without waiting for the adapter's response, the item will be re-injected if the adapter operation fails. Default: `false`. | ||
* | ||
@@ -58,2 +59,3 @@ * @returns {Promise} Promise produced by the `$q` service. | ||
id = DS.utils.resolveId(definition, id); | ||
if (!definition) { | ||
@@ -72,2 +74,10 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); | ||
if (!('eagerEject' in options)) { | ||
options.eagerEject = definition.eagerEject; | ||
} | ||
if (!('notify' in options)) { | ||
options.notify = definition.notify; | ||
} | ||
return deferred.promise | ||
@@ -78,3 +88,9 @@ .then(function (attrs) { | ||
}) | ||
.then(function () { | ||
.then(function (attrs) { | ||
if (options.notify) { | ||
DS.emit(definition, 'beforeDestroy', DS.utils.merge({}, attrs)); | ||
} | ||
if (options.eagerEject) { | ||
DS.eject(resourceName, id); | ||
} | ||
return DS.adapters[options.adapter || definition.defaultAdapter].destroy(definition, id, options); | ||
@@ -87,4 +103,12 @@ }) | ||
.then(function () { | ||
if (options.notify) { | ||
DS.emit(definition, 'afterDestroy', DS.utils.merge({}, item)); | ||
} | ||
DS.eject(resourceName, id); | ||
return id; | ||
}).catch(function (err) { | ||
if (options.eagerEject && item) { | ||
DS.inject(resourceName, item); | ||
} | ||
return DS.$q.reject(err); | ||
}); | ||
@@ -91,0 +115,0 @@ } catch (err) { |
@@ -34,2 +34,3 @@ function errorPrefix(resourceName, id) { | ||
* | ||
* - `{boolean=}` - `useClass` - Whether to wrap the injected item with the resource's instance constructor. | ||
* - `{boolean=}` - `bypassCache` - Bypass the cache. Default: `false`. | ||
@@ -81,3 +82,3 @@ * - `{boolean=}` - `cacheResponse` - Inject the data returned by the adapter into the data store. Default: `true`. | ||
.then(function (res) { | ||
var data = definition.deserialize(resourceName, res); | ||
var data = options.deserialize ? options.deserialize(resourceName, res) : definition.deserialize(resourceName, res); | ||
if (options.cacheResponse) { | ||
@@ -89,3 +90,3 @@ // Query is no longer pending | ||
} else { | ||
return data; | ||
return DS.createInstance(resourceName, data, options); | ||
} | ||
@@ -92,0 +93,0 @@ }, function (err) { |
@@ -56,3 +56,4 @@ function errorPrefix(resourceName) { | ||
.then(function (res) { | ||
var data = definition.deserialize(resourceName, res); | ||
delete resource.pendingQueries[queryHash]; | ||
var data = options.deserialize ? options.deserialize(resourceName, res) : definition.deserialize(resourceName, res); | ||
if (options.cacheResponse) { | ||
@@ -65,2 +66,5 @@ try { | ||
} else { | ||
DS.utils.forEach(data, function (item, i) { | ||
data[i] = DS.createInstance(resourceName, item, options); | ||
}); | ||
return data; | ||
@@ -126,2 +130,3 @@ } | ||
* | ||
* - `{boolean=}` - `useClass` - Whether to wrap the injected item with the resource's instance constructor. | ||
* - `{boolean=}` - `bypassCache` - Bypass the cache. Default: `false`. | ||
@@ -128,0 +133,0 @@ * - `{boolean=}` - `cacheResponse` - Inject the data returned by the adapter into the data store. Default: `true`. |
@@ -104,3 +104,3 @@ function errorPrefix(resourceName) { | ||
if (def.type === 'hasMany') { | ||
if (def.type === 'hasMany' && params[def.foreignKey]) { | ||
task = DS.findAll(relationName, params, options); | ||
@@ -110,6 +110,8 @@ } else if (def.type === 'hasOne') { | ||
task = DS.find(relationName, instance[def.localKey], options); | ||
} else if (def.foreignKey) { | ||
task = DS.findAll(relationName, params, options); | ||
} else if (def.foreignKey && params[def.foreignKey]) { | ||
task = DS.findAll(relationName, params, options).then(function (hasOnes) { | ||
return hasOnes.length ? hasOnes[0] : null; | ||
}); | ||
} | ||
} else { | ||
} else if (instance[def.localKey]) { | ||
task = DS.find(relationName, instance[def.localKey], options); | ||
@@ -116,0 +118,0 @@ } |
@@ -60,2 +60,3 @@ function errorPrefix(resourceName, id) { | ||
id = DS.utils.resolveId(DS.definitions[resourceName], id); | ||
if (!DS.definitions[resourceName]) { | ||
@@ -62,0 +63,0 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); |
@@ -64,2 +64,3 @@ function errorPrefix(resourceName, id) { | ||
id = DS.utils.resolveId(definition, id); | ||
if (!definition) { | ||
@@ -82,2 +83,6 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); | ||
if (!('notify' in options)) { | ||
options.notify = definition.notify; | ||
} | ||
deferred.resolve(item); | ||
@@ -103,2 +108,5 @@ | ||
.then(function (attrs) { | ||
if (options.notify) { | ||
DS.emit(definition, 'beforeUpdate', DS.utils.merge({}, attrs)); | ||
} | ||
if (options.changesOnly) { | ||
@@ -124,13 +132,16 @@ var resource = DS.store[resourceName]; | ||
} | ||
return DS.adapters[options.adapter || definition.defaultAdapter].update(definition, id, definition.serialize(resourceName, attrs), options); | ||
return DS.adapters[options.adapter || definition.defaultAdapter].update(definition, id, options.serialize ? options.serialize(resourceName, attrs) : definition.serialize(resourceName, attrs), options); | ||
}) | ||
.then(function (res) { | ||
var func = options.afterUpdate ? DS.$q.promisify(options.afterUpdate) : definition.afterUpdate; | ||
var attrs = definition.deserialize(resourceName, res); | ||
var attrs = options.deserialize ? options.deserialize(resourceName, res) : definition.deserialize(resourceName, res); | ||
return func.call(attrs, resourceName, attrs); | ||
}) | ||
.then(function (data) { | ||
.then(function (attrs) { | ||
if (options.notify) { | ||
DS.emit(definition, 'afterUpdate', DS.utils.merge({}, attrs)); | ||
} | ||
if (options.cacheResponse) { | ||
var resource = DS.store[resourceName]; | ||
var saved = DS.inject(definition.name, data, options); | ||
var saved = DS.inject(definition.name, attrs, options); | ||
resource.previousAttributes[id] = DS.utils.deepMixIn({}, saved); | ||
@@ -141,3 +152,3 @@ resource.saved[id] = DS.utils.updateTimestamp(resource.saved[id]); | ||
} else { | ||
return data; | ||
return attrs; | ||
} | ||
@@ -144,0 +155,0 @@ }); |
@@ -66,2 +66,3 @@ function errorPrefix(resourceName, id) { | ||
id = DS.utils.resolveId(definition, id); | ||
if (!definition) { | ||
@@ -81,2 +82,6 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); | ||
if (!('notify' in options)) { | ||
options.notify = definition.notify; | ||
} | ||
deferred.resolve(attrs); | ||
@@ -102,13 +107,19 @@ | ||
.then(function (attrs) { | ||
return DS.adapters[options.adapter || definition.defaultAdapter].update(definition, id, definition.serialize(resourceName, attrs), options); | ||
if (options.notify) { | ||
DS.emit(definition, 'beforeUpdate', DS.utils.merge({}, attrs)); | ||
} | ||
return DS.adapters[options.adapter || definition.defaultAdapter].update(definition, id, options.serialize ? options.serialize(resourceName, attrs) : definition.serialize(resourceName, attrs), options); | ||
}) | ||
.then(function (res) { | ||
var func = options.afterUpdate ? DS.$q.promisify(options.afterUpdate) : definition.afterUpdate; | ||
var attrs = definition.deserialize(resourceName, res); | ||
var attrs = options.deserialize ? options.deserialize(resourceName, res) : definition.deserialize(resourceName, res); | ||
return func.call(attrs, resourceName, attrs); | ||
}) | ||
.then(function (data) { | ||
.then(function (attrs) { | ||
if (options.notify) { | ||
DS.emit(definition, 'afterUpdate', DS.utils.merge({}, attrs)); | ||
} | ||
if (options.cacheResponse) { | ||
var resource = DS.store[resourceName]; | ||
var updated = DS.inject(definition.name, data, options); | ||
var updated = DS.inject(definition.name, attrs, options); | ||
var id = updated[definition.idAttribute]; | ||
@@ -120,3 +131,3 @@ resource.previousAttributes[id] = DS.utils.deepMixIn({}, updated); | ||
} else { | ||
return data; | ||
return attrs; | ||
} | ||
@@ -123,0 +134,0 @@ }); |
@@ -92,2 +92,6 @@ function errorPrefix(resourceName) { | ||
if (!('notify' in options)) { | ||
options.notify = definition.notify; | ||
} | ||
deferred.resolve(attrs); | ||
@@ -113,14 +117,20 @@ | ||
.then(function (attrs) { | ||
return DS.adapters[options.adapter || definition.defaultAdapter].updateAll(definition, definition.serialize(resourceName, attrs), params, options); | ||
if (options.notify) { | ||
DS.emit(definition, 'beforeUpdate', DS.utils.merge({}, attrs)); | ||
} | ||
return DS.adapters[options.adapter || definition.defaultAdapter].updateAll(definition, options.serialize ? options.serialize(resourceName, attrs) : definition.serialize(resourceName, attrs), params, options); | ||
}) | ||
.then(function (res) { | ||
var func = options.afterUpdate ? DS.$q.promisify(options.afterUpdate) : definition.afterUpdate; | ||
var attrs = definition.deserialize(resourceName, res); | ||
var attrs = options.deserialize ? options.deserialize(resourceName, res) : definition.deserialize(resourceName, res); | ||
return func.call(attrs, resourceName, attrs); | ||
}) | ||
.then(function (data) { | ||
.then(function (attrs) { | ||
if (options.notify) { | ||
DS.emit(definition, 'afterUpdate', DS.utils.merge({}, attrs)); | ||
} | ||
if (options.cacheResponse) { | ||
return DS.inject(definition.name, data, options); | ||
return DS.inject(definition.name, attrs, options); | ||
} else { | ||
return data; | ||
return attrs; | ||
} | ||
@@ -127,0 +137,0 @@ }); |
@@ -32,3 +32,3 @@ var utils = require('../utils')[0](); | ||
if (options.allowSimpleWhere) { | ||
this.utils.forOwn(params, function (value, key) { | ||
this.utils.forEach(params, function (value, key) { | ||
if (!(key in reserved) && !(key in where)) { | ||
@@ -50,3 +50,3 @@ where[key] = { | ||
var keep = true; | ||
_this.utils.forOwn(where, function (clause, field) { | ||
_this.utils.forEach(where, function (clause, field) { | ||
if (_this.utils.isString(clause)) { | ||
@@ -62,3 +62,3 @@ clause = { | ||
if (_this.utils.isObject(clause)) { | ||
_this.utils.forOwn(clause, function (val, op) { | ||
_this.utils.forEach(clause, function (val, op) { | ||
if (op === '==') { | ||
@@ -82,2 +82,4 @@ keep = first ? (attrs[field] == val) : keep && (attrs[field] == val); | ||
keep = first ? _this.utils.contains(val, attrs[field]) : keep && _this.utils.contains(val, attrs[field]); | ||
} else if (op === 'notIn') { | ||
keep = first ? !_this.utils.contains(val, attrs[field]) : keep && !_this.utils.contains(val, attrs[field]); | ||
} else if (op === '|==') { | ||
@@ -101,2 +103,4 @@ keep = first ? (attrs[field] == val) : keep || (attrs[field] == val); | ||
keep = first ? _this.utils.contains(val, attrs[field]) : keep || _this.utils.contains(val, attrs[field]); | ||
} else if (op === '|notIn') { | ||
keep = first ? !_this.utils.contains(val, attrs[field]) : keep || !_this.utils.contains(val, attrs[field]); | ||
} | ||
@@ -192,3 +196,8 @@ first = false; | ||
Defaults.prototype.endpoint = ''; | ||
Defaults.prototype.useClass = false; | ||
Defaults.prototype.useClass = true; | ||
Defaults.prototype.keepChangeHistory = false; | ||
Defaults.prototype.resetHistoryOnInject = true; | ||
Defaults.prototype.eagerInject = false; | ||
Defaults.prototype.eagerEject = false; | ||
Defaults.prototype.notify = true; | ||
/** | ||
@@ -572,3 +581,3 @@ * @doc property | ||
Defaults.prototype.deserialize = function (resourceName, data) { | ||
return data.data; | ||
return data ? (data.data ? data.data : data) : data; | ||
}; | ||
@@ -660,6 +669,7 @@ | ||
var DS = { | ||
notify: function (definition, event) { | ||
emit: function (definition, event) { | ||
var args = Array.prototype.slice.call(arguments, 2); | ||
args.unshift(definition.name); | ||
args.unshift('DS.' + event); | ||
definition.emit.apply(definition, args); | ||
if (definition.events === 'broadcast') { | ||
@@ -745,2 +755,5 @@ $rootScope.$broadcast.apply($rootScope, args); | ||
DSHttpAdapter.DS = DS; | ||
DSLocalStorageAdapter.DS = DS; | ||
if (typeof Object.observe !== 'function' || | ||
@@ -747,0 +760,0 @@ typeof Array.observe !== 'function') { |
@@ -52,2 +52,4 @@ function errorPrefix(resourceName) { | ||
params = params || {}; | ||
if (!DS.utils.isObject(scope)) { | ||
@@ -54,0 +56,0 @@ throw new IA(errorPrefix(resourceName) + 'scope: Must be an object!'); |
@@ -40,2 +40,3 @@ function errorPrefix(resourceName) { | ||
id = DS.utils.resolveId(DS.definitions[resourceName], id); | ||
if (!DS.utils.isObject(scope)) { | ||
@@ -42,0 +43,0 @@ throw new IA(errorPrefix(resourceName) + 'scope: Must be an object!'); |
@@ -42,2 +42,4 @@ function errorPrefix(resourceName) { | ||
var DS = this; | ||
id = DS.utils.resolveId(DS.definitions[resourceName], id); | ||
if (!DS.definitions[resourceName]) { | ||
@@ -53,5 +55,5 @@ throw new DS.errors.NER(errorPrefix(resourceName) + resourceName); | ||
var diff = DS.utils.diffObjectFromOldObject(item, DS.store[resourceName].previousAttributes[id]); | ||
DS.utils.forOwn(diff, function (changeset, name) { | ||
DS.utils.forEach(diff, function (changeset, name) { | ||
var toKeep = []; | ||
DS.utils.forOwn(changeset, function (value, field) { | ||
DS.utils.forEach(changeset, function (value, field) { | ||
if (!angular.isFunction(value)) { | ||
@@ -63,2 +65,7 @@ toKeep.push(field); | ||
}); | ||
DS.utils.forEach(DS.definitions[resourceName].relationFields, function (field) { | ||
delete diff.added[field]; | ||
delete diff.removed[field]; | ||
delete diff.changed[field]; | ||
}); | ||
return diff; | ||
@@ -65,0 +72,0 @@ } |
@@ -74,2 +74,3 @@ function errorPrefix(resourceName) { | ||
instance = DS.utils.resolveItem(DS.store[resourceName], instance); | ||
if (!definition) { | ||
@@ -85,3 +86,3 @@ throw new DS.errors.NER(errorPrefix(resourceName) + resourceName); | ||
DS.utils.forOwn(definition.computed, function (fn, field) { | ||
DS.utils.forEach(definition.computed, function (fn, field) { | ||
_compute.call(instance, fn, field); | ||
@@ -88,0 +89,0 @@ }); |
@@ -60,3 +60,3 @@ function errorPrefix(resourceName) { | ||
* | ||
* - `{boolean=}` - `useClass` - Whether to use the resource's wrapper class. Default: `true`. | ||
* - `{boolean=}` - `useClass` - Whether to wrap the injected item with the resource's instance constructor. | ||
* | ||
@@ -82,3 +82,3 @@ * @returns {object} The new instance. | ||
if (!('useClass' in options)) { | ||
options.useClass = true; | ||
options.useClass = definition.useClass; | ||
} | ||
@@ -85,0 +85,0 @@ |
@@ -19,2 +19,3 @@ /*jshint evil:true*/ | ||
'changes', | ||
'changeHistory', | ||
'create', | ||
@@ -84,2 +85,4 @@ 'createInstance', | ||
* - `{boolean=}` - `useClass` - Whether to use a wrapper class created from the ProperCase name of the resource. The wrapper will always be used for resources that have `methods` defined. | ||
* - `{boolean=}` - `keepChangeHistory` - Whether to keep a history of changes for items in the data store. Default: `false`. | ||
* - `{boolean=}` - `resetHistoryOnInject` - Whether to reset the history of changes for items when they are injected of re-injected into the data store. This will also reset an item's previous attributes. Default: `true`. | ||
* - `{function=}` - `defaultFilter` - Override the filtering used internally by `DS.filter` with you own function here. | ||
@@ -109,6 +112,7 @@ * - `{*=}` - `meta` - A property reserved for developer use. This will never be used by the API. | ||
var DS = this; | ||
var DSUtils = DS.utils; | ||
var definitions = DS.definitions; | ||
var IA = DS.errors.IA; | ||
if (DS.utils.isString(definition)) { | ||
if (DSUtils.isString(definition)) { | ||
definition = definition.replace(/\s/gi, ''); | ||
@@ -119,9 +123,9 @@ definition = { | ||
} | ||
if (!DS.utils.isObject(definition)) { | ||
if (!DSUtils.isObject(definition)) { | ||
throw new IA(errorPrefix + 'definition: Must be an object!'); | ||
} else if (!DS.utils.isString(definition.name)) { | ||
} else if (!DSUtils.isString(definition.name)) { | ||
throw new IA(errorPrefix + 'definition.name: Must be a string!'); | ||
} else if (definition.idAttribute && !DS.utils.isString(definition.idAttribute)) { | ||
} else if (definition.idAttribute && !DSUtils.isString(definition.idAttribute)) { | ||
throw new IA(errorPrefix + 'definition.idAttribute: Must be a string!'); | ||
} else if (definition.endpoint && !DS.utils.isString(definition.endpoint)) { | ||
} else if (definition.endpoint && !DSUtils.isString(definition.endpoint)) { | ||
throw new IA(errorPrefix + 'definition.endpoint: Must be a string!'); | ||
@@ -135,3 +139,3 @@ } else if (DS.store[definition.name]) { | ||
Resource.prototype = DS.defaults; | ||
definitions[definition.name] = new Resource(DS.utils, definition); | ||
definitions[definition.name] = new Resource(DSUtils, definition); | ||
@@ -144,8 +148,8 @@ var def = definitions[definition.name]; | ||
def.relationFields = []; | ||
DS.utils.forOwn(def.relations, function (relatedModels, type) { | ||
DS.utils.forOwn(relatedModels, function (defs, relationName) { | ||
if (!DS.utils.isArray(defs)) { | ||
DSUtils.forEach(def.relations, function (relatedModels, type) { | ||
DSUtils.forEach(relatedModels, function (defs, relationName) { | ||
if (!DSUtils.isArray(defs)) { | ||
relatedModels[relationName] = [defs]; | ||
} | ||
DS.utils.forEach(relatedModels[relationName], function (d) { | ||
DSUtils.forEach(relatedModels[relationName], function (d) { | ||
d.type = type; | ||
@@ -160,4 +164,4 @@ d.relation = relationName; | ||
if (def.relations.belongsTo) { | ||
DS.utils.forOwn(def.relations.belongsTo, function (relatedModel, modelName) { | ||
DS.utils.forEach(relatedModel, function (relation) { | ||
DSUtils.forEach(def.relations.belongsTo, function (relatedModel, modelName) { | ||
DSUtils.forEach(relatedModel, function (relation) { | ||
if (relation.parent) { | ||
@@ -170,4 +174,4 @@ def.parent = modelName; | ||
} | ||
DS.utils.deepFreeze(def.relations); | ||
DS.utils.deepFreeze(def.relationList); | ||
DSUtils.deepFreeze(def.relations); | ||
DSUtils.deepFreeze(def.relationList); | ||
} | ||
@@ -185,13 +189,13 @@ | ||
if (parent && parentKey && definitions[parent] && options.params[parentKey] !== false) { | ||
if (DS.utils.isNumber(attrs) || DS.utils.isString(attrs)) { | ||
if (DSUtils.isNumber(attrs) || DSUtils.isString(attrs)) { | ||
item = DS.get(this.name, attrs); | ||
} | ||
if (DS.utils.isObject(attrs) && parentKey in attrs) { | ||
if (DSUtils.isObject(attrs) && parentKey in attrs) { | ||
delete options.params[parentKey]; | ||
endpoint = DS.utils.makePath(definitions[parent].getEndpoint(attrs, options), attrs[parentKey], thisEndpoint); | ||
endpoint = DSUtils.makePath(definitions[parent].getEndpoint(attrs, options), attrs[parentKey], thisEndpoint); | ||
} else if (item && parentKey in item) { | ||
delete options.params[parentKey]; | ||
endpoint = DS.utils.makePath(definitions[parent].getEndpoint(attrs, options), item[parentKey], thisEndpoint); | ||
endpoint = DSUtils.makePath(definitions[parent].getEndpoint(attrs, options), item[parentKey], thisEndpoint); | ||
} else if (options && options.params[parentKey]) { | ||
endpoint = DS.utils.makePath(definitions[parent].getEndpoint(attrs, options), options.params[parentKey], thisEndpoint); | ||
endpoint = DSUtils.makePath(definitions[parent].getEndpoint(attrs, options), options.params[parentKey], thisEndpoint); | ||
delete options.params[parentKey]; | ||
@@ -221,3 +225,3 @@ } | ||
var item = DS.eject(def.name, id); | ||
if (DS.utils.isFunction(def.onExpire)) { | ||
if (DSUtils.isFunction(def.onExpire)) { | ||
def.onExpire(id, item); | ||
@@ -234,3 +238,3 @@ } | ||
// Create the wrapper class for the new resource | ||
def.class = DS.utils.pascalCase(definition.name); | ||
def.class = DSUtils.pascalCase(definition.name); | ||
eval('function ' + def.class + '() {}'); | ||
@@ -241,3 +245,3 @@ def[def.class] = eval(def.class); | ||
if (def.methods) { | ||
DS.utils.deepMixIn(def[def.class].prototype, def.methods); | ||
DSUtils.deepMixIn(def[def.class].prototype, def.methods); | ||
} | ||
@@ -247,3 +251,3 @@ | ||
if (def.computed) { | ||
DS.utils.forOwn(def.computed, function (fn, field) { | ||
DSUtils.forEach(def.computed, function (fn, field) { | ||
if (angular.isFunction(fn)) { | ||
@@ -270,3 +274,3 @@ def.computed[field] = [fn]; | ||
}); | ||
fn.deps = DS.utils.filter(deps, function (dep) { | ||
fn.deps = DSUtils.filter(deps, function (dep) { | ||
return !!dep; | ||
@@ -281,2 +285,16 @@ }); | ||
def[def.class].prototype.DSUpdate = function () { | ||
var args = Array.prototype.slice.call(arguments); | ||
args.unshift(this[def.idAttribute]); | ||
args.unshift(def.name); | ||
return DS.update.apply(DS, args); | ||
}; | ||
def[def.class].prototype.DSSave = function () { | ||
var args = Array.prototype.slice.call(arguments); | ||
args.unshift(this[def.idAttribute]); | ||
args.unshift(def.name); | ||
return DS.save.apply(DS, args); | ||
}; | ||
// Initialize store data for the new resource | ||
@@ -292,2 +310,4 @@ DS.store[def.name] = { | ||
observers: {}, | ||
changeHistories: {}, | ||
changeHistory: [], | ||
collectionModified: 0 | ||
@@ -323,2 +343,5 @@ }; | ||
// Mix-in events | ||
DSUtils.Events(def); | ||
return def; | ||
@@ -325,0 +348,0 @@ } catch (err) { |
@@ -5,3 +5,3 @@ function errorPrefix(resourceName, id) { | ||
function _eject(definition, resource, id) { | ||
function _eject(definition, resource, id, options) { | ||
var item; | ||
@@ -25,2 +25,7 @@ var found = false; | ||
delete resource.completedQueries[id]; | ||
delete resource.pendingQueries[id]; | ||
DS.utils.forEach(resource.changeHistories[id], function (changeRecord) { | ||
DS.utils.remove(resource.changeHistory, changeRecord); | ||
}); | ||
delete resource.changeHistories[id]; | ||
delete resource.modified[id]; | ||
@@ -30,3 +35,5 @@ delete resource.saved[id]; | ||
this.notify(definition, 'eject', item); | ||
if (options.notify) { | ||
this.emit(definition, 'eject', item); | ||
} | ||
@@ -71,7 +78,11 @@ return item; | ||
* @param {string|number} id The primary key of the item to eject. | ||
* @param {object=} options Optional configuration. | ||
* @returns {object} A reference to the item that was ejected from the data store. | ||
*/ | ||
function eject(resourceName, id) { | ||
function eject(resourceName, id, options) { | ||
var DS = this; | ||
var definition = DS.definitions[resourceName]; | ||
options = options || {}; | ||
id = DS.utils.resolveId(definition, id); | ||
if (!definition) { | ||
@@ -85,8 +96,12 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); | ||
if (!('notify' in options)) { | ||
options.notify = definition.notify; | ||
} | ||
if (!DS.$rootScope.$$phase) { | ||
DS.$rootScope.$apply(function () { | ||
ejected = _eject.call(DS, definition, resource, id); | ||
ejected = _eject.call(DS, definition, resource, id, options); | ||
}); | ||
} else { | ||
ejected = _eject.call(DS, definition, resource, id); | ||
ejected = _eject.call(DS, definition, resource, id, options); | ||
} | ||
@@ -93,0 +108,0 @@ |
@@ -5,3 +5,3 @@ function errorPrefix(resourceName) { | ||
function _ejectAll(definition, resource, params) { | ||
function _ejectAll(definition, resource, params, options) { | ||
var DS = this; | ||
@@ -19,3 +19,5 @@ var queryHash = DS.utils.toJson(params); | ||
DS.notify(definition, 'eject', items); | ||
if (options.notify) { | ||
DS.emit(definition, 'eject', items); | ||
} | ||
@@ -84,8 +86,11 @@ return items; | ||
* | ||
* @param {object=} options Optional configuration. | ||
* | ||
* @returns {array} The items that were ejected from the data store. | ||
*/ | ||
function ejectAll(resourceName, params) { | ||
function ejectAll(resourceName, params, options) { | ||
var DS = this; | ||
var definition = DS.definitions[resourceName]; | ||
params = params || {}; | ||
options = options || {}; | ||
@@ -104,8 +109,12 @@ if (!definition) { | ||
if (!('notify' in options)) { | ||
options.notify = definition.notify; | ||
} | ||
if (!DS.$rootScope.$$phase) { | ||
DS.$rootScope.$apply(function () { | ||
ejected = _ejectAll.call(DS, definition, resource, params); | ||
ejected = _ejectAll.call(DS, definition, resource, params, options); | ||
}); | ||
} else { | ||
ejected = _ejectAll.call(DS, definition, resource, params); | ||
ejected = _ejectAll.call(DS, definition, resource, params, options); | ||
} | ||
@@ -112,0 +121,0 @@ |
@@ -47,2 +47,4 @@ function errorPrefix(resourceName, id) { | ||
var DS = this; | ||
id = DS.utils.resolveId(DS.definitions[resourceName], id); | ||
if (!DS.definitions[resourceName]) { | ||
@@ -49,0 +51,0 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); |
@@ -35,2 +35,12 @@ module.exports = { | ||
* @doc method | ||
* @id DS.sync methods:changeHistory | ||
* @name changeHistory | ||
* @methodOf DS | ||
* @description | ||
* See [DS.changeHistory](/documentation/api/api/DS.sync methods:changeHistory). | ||
*/ | ||
changeHistory: require('./changeHistory'), | ||
/** | ||
* @doc method | ||
* @id DS.sync methods:compute | ||
@@ -37,0 +47,0 @@ * @name compute |
var observe = require('../../../lib/observe-js/observe-js'); | ||
var _compute = require('./compute')._compute; | ||
var stack = 0; | ||
var data = { | ||
injectedSoFar: {} | ||
}; | ||
@@ -12,4 +8,20 @@ function errorPrefix(resourceName) { | ||
function _inject(definition, resource, attrs) { | ||
function _injectRelations(definition, injected, options) { | ||
var DS = this; | ||
DS.utils.forEach(definition.relationList, function (def) { | ||
var relationName = def.relation; | ||
var relationDef = DS.definitions[relationName]; | ||
if (relationDef && injected[def.localField]) { | ||
try { | ||
injected[def.localField] = DS.inject(relationName, injected[def.localField], options); | ||
} catch (err) { | ||
DS.$log.error(errorPrefix(definition.name) + 'Failed to inject ' + def.type + ' relation: "' + relationName + '"!', err); | ||
} | ||
} | ||
}); | ||
} | ||
function _inject(definition, resource, attrs, options) { | ||
var DS = this; | ||
var $log = DS.$log; | ||
@@ -29,9 +41,22 @@ | ||
if (!DS.utils.isEmpty(added) || !DS.utils.isEmpty(removed) || !DS.utils.isEmpty(changed) || firstTime) { | ||
item = DS.get(definition.name, innerId); | ||
resource.modified[innerId] = DS.utils.updateTimestamp(resource.modified[innerId]); | ||
resource.collectionModified = DS.utils.updateTimestamp(resource.collectionModified); | ||
if (definition.keepChangeHistory) { | ||
var changeRecord = { | ||
resourceName: definition.name, | ||
target: item, | ||
added: added, | ||
removed: removed, | ||
changed: changed, | ||
timestamp: resource.modified[innerId] | ||
}; | ||
resource.changeHistories[innerId].push(changeRecord); | ||
resource.changeHistory.push(changeRecord); | ||
} | ||
} | ||
if (definition.computed) { | ||
item = DS.get(definition.name, innerId); | ||
DS.utils.forOwn(definition.computed, function (fn, field) { | ||
item = item || DS.get(definition.name, innerId); | ||
DS.utils.forEach(definition.computed, function (fn, field) { | ||
var compute = false; | ||
@@ -52,3 +77,3 @@ // check if required fields changed | ||
if (definition.relations) { | ||
item = DS.get(definition.name, innerId); | ||
item = item || DS.get(definition.name, innerId); | ||
DS.utils.forEach(definition.relationList, function (def) { | ||
@@ -72,3 +97,3 @@ if (item[def.localField] && (def.localKey in added || def.localKey in removed || def.localKey in changed)) { | ||
for (var i = 0; i < attrs.length; i++) { | ||
injected.push(_inject.call(DS, definition, resource, attrs[i])); | ||
injected.push(_inject.call(DS, definition, resource, attrs[i], options)); | ||
} | ||
@@ -97,3 +122,3 @@ } else { | ||
if (!item) { | ||
if (definition.methods || definition.useClass) { | ||
if (options.useClass) { | ||
if (attrs instanceof definition[definition.class]) { | ||
@@ -114,2 +139,3 @@ item = attrs; | ||
resource.changeHistories[id] = []; | ||
resource.observers[id] = new observe.ObjectObserver(item); | ||
@@ -120,4 +146,18 @@ resource.observers[id].open(_react, item); | ||
_react.call(item, {}, {}, {}, null, true); | ||
if (definition.relations) { | ||
_injectRelations.call(DS, definition, item, options); | ||
} | ||
} else { | ||
DS.utils.deepMixIn(item, attrs); | ||
if (definition.resetHistoryOnInject) { | ||
resource.previousAttributes[id] = {}; | ||
DS.utils.deepMixIn(resource.previousAttributes[id], attrs); | ||
if (resource.changeHistories[id].length) { | ||
DS.utils.forEach(resource.changeHistories[id], function (changeRecord) { | ||
DS.utils.remove(resource.changeHistory, changeRecord); | ||
}); | ||
resource.changeHistories[id].splice(0, resource.changeHistories[id].length); | ||
} | ||
} | ||
if (typeof resource.index.touch === 'function') { | ||
@@ -142,41 +182,11 @@ resource.index.touch(id); | ||
function _injectRelations(definition, injected, options) { | ||
function _link(definition, injected, options) { | ||
var DS = this; | ||
function _process(def, relationName, injected) { | ||
var relationDef = DS.definitions[relationName]; | ||
if (relationDef && injected[def.localField] && !data.injectedSoFar[relationName + injected[def.localField][relationDef.idAttribute]]) { | ||
try { | ||
data.injectedSoFar[relationName + injected[def.localField][relationDef.idAttribute]] = 1; | ||
injected[def.localField] = DS.inject(relationName, injected[def.localField], options); | ||
} catch (err) { | ||
DS.$log.error(errorPrefix(definition.name) + 'Failed to inject ' + def.type + ' relation: "' + relationName + '"!', err); | ||
} | ||
} else if (options.findBelongsTo && def.type === 'belongsTo') { | ||
if (DS.utils.isArray(injected)) { | ||
DS.utils.forEach(injected, function (injectedItem) { | ||
DS.link(definition.name, injectedItem[definition.idAttribute], [relationName]); | ||
}); | ||
} else { | ||
DS.link(definition.name, injected[definition.idAttribute], [relationName]); | ||
} | ||
DS.utils.forEach(definition.relationList, function (def) { | ||
if (options.findBelongsTo && def.type === 'belongsTo' && injected[definition.idAttribute]) { | ||
DS.link(definition.name, injected[definition.idAttribute], [def.relation]); | ||
} else if ((options.findHasMany && def.type === 'hasMany') || (options.findHasOne && def.type === 'hasOne')) { | ||
if (DS.utils.isArray(injected)) { | ||
DS.utils.forEach(injected, function (injectedItem) { | ||
DS.link(definition.name, injectedItem[definition.idAttribute], [relationName]); | ||
}); | ||
} else { | ||
DS.link(definition.name, injected[definition.idAttribute], [relationName]); | ||
} | ||
DS.link(definition.name, injected[definition.idAttribute], [def.relation]); | ||
} | ||
} | ||
DS.utils.forEach(definition.relationList, function (def) { | ||
if (DS.utils.isArray(injected)) { | ||
DS.utils.forEach(injected, function (injectedI) { | ||
_process(def, def.relation, injectedI); | ||
}); | ||
} else { | ||
_process(def, def.relation, injected); | ||
} | ||
}); | ||
@@ -232,2 +242,3 @@ } | ||
* | ||
* - `{boolean=}` - `useClass` - Whether to wrap the injected item with the resource's instance constructor. | ||
* - `{boolean=}` - `findBelongsTo` - Find and attach any existing "belongsTo" relationships to the newly injected item. Potentially expensive if enabled. Default: `false`. | ||
@@ -258,36 +269,37 @@ * - `{boolean=}` - `findHasMany` - Find and attach any existing "hasMany" relationships to the newly injected item. Potentially expensive if enabled. Default: `false`. | ||
stack++; | ||
if (!('useClass' in options)) { | ||
options.useClass = definition.useClass; | ||
} | ||
if (!('notify' in options)) { | ||
options.notify = definition.notify; | ||
} | ||
if (!DS.$rootScope.$$phase) { | ||
DS.$rootScope.$apply(function () { | ||
injected = _inject.call(DS, definition, resource, attrs, options); | ||
}); | ||
} else { | ||
injected = _inject.call(DS, definition, resource, attrs, options); | ||
} | ||
try { | ||
if (!DS.$rootScope.$$phase) { | ||
DS.$rootScope.$apply(function () { | ||
injected = _inject.call(DS, definition, resource, attrs); | ||
}); | ||
if (options.linkInverse) { | ||
if (DS.utils.isArray(injected) && injected.length) { | ||
DS.linkInverse(definition.name, injected[0][definition.idAttribute]); | ||
} else { | ||
injected = _inject.call(DS, definition, resource, attrs); | ||
DS.linkInverse(definition.name, injected[definition.idAttribute]); | ||
} | ||
if (definition.relations) { | ||
_injectRelations.call(DS, definition, injected, options); | ||
} | ||
} | ||
if (options.linkInverse) { | ||
if (DS.utils.isArray(injected) && injected.length) { | ||
DS.linkInverse(definition.name, injected[0][definition.idAttribute]); | ||
} else { | ||
DS.linkInverse(definition.name, injected[definition.idAttribute]); | ||
} | ||
} | ||
DS.notify(definition, 'inject', injected); | ||
stack--; | ||
} catch (err) { | ||
stack--; | ||
throw err; | ||
if (DS.utils.isArray(injected)) { | ||
DS.utils.forEach(injected, function (injectedI) { | ||
_link.call(DS, definition, injectedI, options); | ||
}); | ||
} else { | ||
_link.call(DS, definition, injected, options); | ||
} | ||
if (!stack) { | ||
data.injectedSoFar = {}; | ||
if (options.notify) { | ||
DS.emit(definition, 'inject', injected); | ||
} | ||
return injected; | ||
@@ -294,0 +306,0 @@ } |
@@ -41,2 +41,4 @@ function errorPrefix(resourceName, id) { | ||
var resource = DS.store[resourceName]; | ||
id = DS.utils.resolveId(DS.definitions[resourceName], id); | ||
if (!DS.definitions[resourceName]) { | ||
@@ -43,0 +45,0 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); |
@@ -49,2 +49,4 @@ function errorPrefix(resourceName, id) { | ||
var resource = DS.store[resourceName]; | ||
id = DS.utils.resolveId(DS.definitions[resourceName], id); | ||
if (!DS.definitions[resourceName]) { | ||
@@ -51,0 +53,0 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); |
@@ -77,2 +77,3 @@ function errorPrefix(resourceName) { | ||
id = DS.utils.resolveId(definition, id); | ||
if (!definition) { | ||
@@ -79,0 +80,0 @@ throw new DS.errors.NER(errorPrefix(resourceName) + resourceName); |
@@ -7,5 +7,5 @@ function errorPrefix(resourceName) { | ||
var DS = this; | ||
DS.utils.forOwn(DS.definitions, function (d) { | ||
DS.utils.forOwn(d.relations, function (relatedModels) { | ||
DS.utils.forOwn(relatedModels, function (defs, relationName) { | ||
DS.utils.forEach(DS.definitions, function (d) { | ||
DS.utils.forEach(d.relations, function (relatedModels) { | ||
DS.utils.forEach(relatedModels, function (defs, relationName) { | ||
if (relations.length && !DS.utils.contains(relations, d.name)) { | ||
@@ -73,2 +73,4 @@ return; | ||
id = DS.utils.resolveId(definition, id); | ||
if (!definition) { | ||
@@ -75,0 +77,0 @@ throw new DS.errors.NER(errorPrefix(resourceName) + resourceName); |
@@ -43,2 +43,4 @@ function errorPrefix(resourceName, id) { | ||
var DS = this; | ||
id = DS.utils.resolveId(DS.definitions[resourceName], id); | ||
if (!DS.definitions[resourceName]) { | ||
@@ -45,0 +47,0 @@ throw new DS.errors.NER(errorPrefix(resourceName, id) + resourceName); |
@@ -7,5 +7,5 @@ function errorPrefix(resourceName) { | ||
var DS = this; | ||
DS.utils.forOwn(DS.definitions, function (d) { | ||
DS.utils.forOwn(d.relations, function (relatedModels) { | ||
DS.utils.forOwn(relatedModels, function (defs, relationName) { | ||
DS.utils.forEach(DS.definitions, function (d) { | ||
DS.utils.forEach(d.relations, function (relatedModels) { | ||
DS.utils.forEach(relatedModels, function (defs, relationName) { | ||
if (definition.name === relationName) { | ||
@@ -75,2 +75,3 @@ DS.utils.forEach(defs, function (def) { | ||
id = DS.utils.resolveId(definition, id); | ||
if (!definition) { | ||
@@ -77,0 +78,0 @@ throw new DS.errors.NER(errorPrefix(resourceName) + resourceName); |
@@ -74,3 +74,6 @@ (function (window, angular, undefined) { | ||
try { | ||
fn.apply(target || this, args); | ||
var promise = fn.apply(target || this, args); | ||
if (promise && promise.then) { | ||
promise.then(deferred.resolve, deferred.reject); | ||
} | ||
} catch (err) { | ||
@@ -77,0 +80,0 @@ deferred.reject(err); |
@@ -0,1 +1,45 @@ | ||
function Events(target) { | ||
var events = {}; | ||
target = target || this; | ||
/** | ||
* On: listen to events | ||
*/ | ||
target.on = function (type, func, ctx) { | ||
events[type] = events[type] || []; | ||
events[type].push({ | ||
f: func, | ||
c: ctx | ||
}); | ||
}; | ||
/** | ||
* Off: stop listening to event / specific callback | ||
*/ | ||
target.off = function (type, func) { | ||
var listeners = events[type]; | ||
if (!listeners) { | ||
events = {}; | ||
} else if (func) { | ||
for (var i = 0; i < listeners.length; i++) { | ||
if (listeners[i] === func) { | ||
listeners.splice(i, 1); | ||
break; | ||
} | ||
} | ||
} else { | ||
listeners.splice(0, listeners.length); | ||
} | ||
}; | ||
target.emit = function () { | ||
var args = Array.prototype.slice.call(arguments); | ||
var listeners = events[args.shift()] || []; | ||
if (listeners) { | ||
for (var i = 0; i < listeners.length; i++) { | ||
listeners[i].f.apply(listeners[i].c, args); | ||
} | ||
} | ||
}; | ||
} | ||
module.exports = [function () { | ||
@@ -11,2 +55,3 @@ return { | ||
toJson: angular.toJson, | ||
fromJson: angular.fromJson, | ||
makePath: require('mout/string/makePath'), | ||
@@ -16,11 +61,30 @@ upperCase: require('mout/string/upperCase'), | ||
deepMixIn: require('mout/object/deepMixIn'), | ||
forOwn: require('mout/object/forOwn'), | ||
forEach: angular.forEach, | ||
pick: require('mout/object/pick'), | ||
set: require('mout/object/set'), | ||
merge: require('mout/object/merge'), | ||
contains: require('mout/array/contains'), | ||
filter: require('mout/array/filter'), | ||
toLookup: require('mout/array/toLookup'), | ||
remove: require('mout/array/remove'), | ||
slice: require('mout/array/slice'), | ||
sort: require('mout/array/sort'), | ||
guid: require('mout/random/guid'), | ||
keys: require('mout/object/keys'), | ||
resolveItem: function (resource, idOrInstance) { | ||
if (resource && (this.isString(idOrInstance) || this.isNumber(idOrInstance))) { | ||
return resource.index[idOrInstance] || idOrInstance; | ||
} else { | ||
return idOrInstance; | ||
} | ||
}, | ||
resolveId: function (definition, idOrInstance) { | ||
if (this.isString(idOrInstance) || this.isNumber(idOrInstance)) { | ||
return idOrInstance; | ||
} else if (idOrInstance && definition) { | ||
return idOrInstance[definition.idAttribute] || idOrInstance; | ||
} else { | ||
return idOrInstance; | ||
} | ||
}, | ||
updateTimestamp: function (timestamp) { | ||
@@ -83,4 +147,5 @@ var newTimestamp = typeof Date.now === 'function' ? Date.now() : new Date().getTime(); | ||
}; | ||
} | ||
}, | ||
Events: Events | ||
}; | ||
}]; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
560812
53
13316