Comparing version 3.0.0-beta.6 to 3.0.0-beta.7
@@ -0,1 +1,17 @@ | ||
##### 3.0.0-beta.7 - 05 June 2016 | ||
###### Backwards compatible changes | ||
- #336 - refactor(Relation): move Relation class into separate file by @stalniy | ||
- #337 - refactor(LinkedCollection) by @stalniy | ||
- #342 - Allow definition of custom getter/setter for schema properties by @jmdobry | ||
- #344 - Schema and utils tests by @MatthewOverall | ||
- #345 - refactor(Container): move defineRelations method to Mapper by @stalniy | ||
- Turned off minification of function names in js-data.min.js | ||
###### Bug fixes | ||
- #332 - records should "commit" when they are saved fixed by @jmdobry | ||
###### Other | ||
- Upgraded dependencies | ||
##### 3.0.0-beta.6 - 16 May 2016 | ||
@@ -2,0 +18,0 @@ |
@@ -91,3 +91,2 @@ export const version: { | ||
hasChanges(opts?: any): boolean | ||
hashCode(): string|number | ||
previous(key: any): any | ||
@@ -99,2 +98,3 @@ revert(opts?: any): this | ||
unset(key: string, opts?: any): void | ||
validate(opts?: any): any | ||
} | ||
@@ -227,3 +227,3 @@ export class Mapper extends Component { | ||
linkRelations: boolean | ||
as(name: string): Mapper|LinkedCollection | ||
as(name: string): Mapper|LinkedCollection|any | ||
constructor(opts?: any) | ||
@@ -230,0 +230,0 @@ add(mapperName: string, records: any[]|any, opts?: any): any[]|any |
{ | ||
"name": "js-data", | ||
"description": "Robust, framework-agnostic in-memory data store.", | ||
"version": "3.0.0-beta.6", | ||
"version": "3.0.0-beta.7", | ||
"homepage": "http://www.js-data.io", | ||
@@ -60,3 +60,3 @@ "repository": { | ||
"bundle": "npm run bundle:es5 && npm run bundle:next && repo-tools write-version dist/js-data.js dist/js-data.es2015.js", | ||
"min": "uglifyjs dist/js-data.js -o dist/js-data.min.js --source-map dist/js-data.min.map --source-map-url js-data.min.map -v -m -c --screw-ie8", | ||
"min": "uglifyjs dist/js-data.js -o dist/js-data.min.js --source-map dist/js-data.min.map --source-map-url js-data.min.map -v -m -c --keep-fnames --screw-ie8", | ||
"banner": "node scripts/banner.js", | ||
@@ -67,3 +67,3 @@ "gzip": "echo gzipped size: $(cat dist/js-data.min.js | gzip -f9 | wc -c)kb", | ||
"mocha": "mocha --recursive -t 20000 -R dot -r babel-core/register -r babel-polyfill", | ||
"cover": "nyc --require babel-core/register --require babel-polyfill --cache mocha --recursive -t 20000 -R dot && nyc report --reporter=html", | ||
"cover": "nyc --require babel-core/register --require babel-polyfill --cache mocha --recursive -t 20000 -R dot && nyc report --reporter=html", | ||
"test": "npm run build && npm run cover && npm run karma", | ||
@@ -75,6 +75,4 @@ "release": "npm test && npm run doc && repo-tools updates && repo-tools changelog && repo-tools authors" | ||
"babel-plugin-transform-es2015-modules-umd": "6.8.0", | ||
"babel-plugin-transform-regenerator": "6.8.0", | ||
"babel-polyfill": "6.8.0", | ||
"babel-preset-es2015-rollup": "1.1.1", | ||
"js-data-repo-tools": "0.5.1", | ||
"babel-plugin-transform-regenerator": "6.9.0", | ||
"js-data-repo-tools": "0.5.2", | ||
"karma": "0.13.22", | ||
@@ -88,8 +86,5 @@ "karma-babel-preprocessor": "6.0.1", | ||
"karma-sinon": "1.0.5", | ||
"nyc": "6.4.4", | ||
"phantomjs-prebuilt": "2.1.7", | ||
"rollup": "0.26.3", | ||
"rollup-plugin-babel": "2.4.0", | ||
"uglify-js": "2.6.2" | ||
} | ||
} |
@@ -10,2 +10,12 @@ import utils from './utils' | ||
/** | ||
* Whether to call {@link Record#commit} on records that are added to the | ||
* collection and already exist in the collection. | ||
* | ||
* @name Collection#commitOnMerge | ||
* @type {boolean} | ||
* @default true | ||
*/ | ||
commitOnMerge: true, | ||
/** | ||
* Field to be used as the unique identifier for records in this collection. | ||
@@ -26,4 +36,4 @@ * Defaults to `"id"` unless {@link Collection#mapper} is set, in which case | ||
* Possible values: | ||
* - merge | ||
* - replace | ||
* merge | ||
* replace | ||
* | ||
@@ -64,96 +74,101 @@ * Merge: | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string} [opts.commitOnMerge] See {@link Collection#commitOnMerge}. | ||
* @param {string} [opts.idAttribute] See {@link Collection#idAttribute}. | ||
* @param {string} [opts.onConflict="merge"] See {@link Collection#onConflict}. | ||
* @param {string} [opts.mapper] See {@link Collection#mapper}. | ||
* @since 3.0.0 | ||
*/ | ||
export default Component.extend({ | ||
constructor: function Collection (records, opts) { | ||
utils.classCallCheck(this, Collection) | ||
Collection.__super__.call(this) | ||
function Collection (records, opts) { | ||
utils.classCallCheck(this, Collection) | ||
Collection.__super__.call(this) | ||
if (records && !utils.isArray(records)) { | ||
opts = records | ||
records = [] | ||
if (records && !utils.isArray(records)) { | ||
opts = records | ||
records = [] | ||
} | ||
if (utils.isString(opts)) { | ||
opts = { idAttribute: opts } | ||
} | ||
// Default values for arguments | ||
records || (records = []) | ||
opts || (opts = {}) | ||
/** | ||
* Default Mapper for this collection. Optional. If a Mapper is provided, then | ||
* the collection will use the {@link Mapper#idAttribute} setting, and will | ||
* wrap records in {@link Mapper#recordClass}. | ||
* | ||
* @example | ||
* import {Collection, Mapper} from 'js-data' | ||
* | ||
* class MyMapperClass extends Mapper { | ||
* foo () { return 'bar' } | ||
* } | ||
* const myMapper = new MyMapperClass() | ||
* const collection = new Collection(null, { mapper: myMapper }) | ||
* | ||
* @name Collection#mapper | ||
* @type {Mapper} | ||
* @default null | ||
* @since 3.0.0 | ||
*/ | ||
Object.defineProperties(this, { | ||
mapper: { | ||
value: undefined, | ||
writable: true | ||
}, | ||
// Query class used by this collection | ||
queryClass: { | ||
value: undefined, | ||
writable: true | ||
} | ||
if (utils.isString(opts)) { | ||
opts = { idAttribute: opts } | ||
} | ||
}) | ||
// Default values for arguments | ||
records || (records = []) | ||
opts || (opts = {}) | ||
// Apply user-provided configuration | ||
utils.fillIn(this, opts) | ||
// Fill in any missing options with the defaults | ||
utils.fillIn(this, utils.copy(COLLECTION_DEFAULTS)) | ||
if (!this.queryClass) { | ||
this.queryClass = Query | ||
} | ||
const idAttribute = this.recordId() | ||
Object.defineProperties(this, { | ||
/** | ||
* Default Mapper for this collection. Optional. If a Mapper is provided, then | ||
* the collection will use the {@link Mapper#idAttribute} setting, and will | ||
* wrap records in {@link Mapper#recordClass}. | ||
* The main index, which uses @{link Collection#recordId} as the key. | ||
* | ||
* @example | ||
* import {Collection, Mapper} from 'js-data' | ||
* @name Collection#index | ||
* @type {Index} | ||
*/ | ||
index: { | ||
value: new Index([idAttribute], { | ||
hashCode (obj) { | ||
return utils.get(obj, idAttribute) | ||
} | ||
}) | ||
}, | ||
/** | ||
* Object that holds the secondary indexes of this collection. | ||
* | ||
* class MyMapperClass extends Mapper { | ||
* foo () { return 'bar' } | ||
* } | ||
* const myMapper = new MyMapperClass() | ||
* const collection = new Collection(null, { mapper: myMapper }) | ||
* | ||
* @name Collection#mapper | ||
* @type {Mapper} | ||
* @default null | ||
* @name Collection#indexes | ||
* @type {Object.<string, Index>} | ||
*/ | ||
Object.defineProperties(this, { | ||
mapper: { | ||
value: undefined, | ||
writable: true | ||
}, | ||
// Query class used by this collection | ||
queryClass: { | ||
value: undefined, | ||
writable: true | ||
} | ||
}) | ||
// Apply user-provided configuration | ||
utils.fillIn(this, opts) | ||
// Fill in any missing options with the defaults | ||
utils.fillIn(this, utils.copy(COLLECTION_DEFAULTS)) | ||
if (!this.queryClass) { | ||
this.queryClass = Query | ||
indexes: { | ||
value: {} | ||
} | ||
}) | ||
const idAttribute = this.recordId() | ||
// Insert initial data into the collection | ||
if (records) { | ||
this.add(records) | ||
} | ||
} | ||
Object.defineProperties(this, { | ||
/** | ||
* The main index, which uses @{link Collection#recordId} as the key. | ||
* | ||
* @name Collection#index | ||
* @type {Index} | ||
*/ | ||
index: { | ||
value: new Index([idAttribute], { | ||
hashCode (obj) { | ||
return utils.get(obj, idAttribute) | ||
} | ||
}) | ||
}, | ||
export default Component.extend({ | ||
constructor: Collection, | ||
/** | ||
* Object that holds the secondary indexes of this collection. | ||
* | ||
* @name Collection#indexes | ||
* @type {Object.<string, Index>} | ||
*/ | ||
indexes: { | ||
value: {} | ||
} | ||
}) | ||
// Insert initial data into the collection | ||
if (records) { | ||
this.add(records) | ||
} | ||
}, | ||
/** | ||
@@ -185,4 +200,4 @@ * Used to bind to events emitted by records in this Collection. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string} [opts.onConflict] What to do when a record is already in | ||
* the collection. Possible values are `merge` or `replace`. | ||
* @param {boolean} [opts.commitOnMerge=true] See {@link Collection#commitOnMerge}. | ||
* @param {string} [opts.onConflict] See {@link Collection#onConflict}. | ||
* @returns {(Object|Object[]|Record|Record[])} The added record or records. | ||
@@ -244,2 +259,5 @@ */ | ||
record = existing | ||
if (opts.commitOnMerge && utils.isFunction(record.commit)) { | ||
record.commit() | ||
} | ||
// Update all indexes in the collection | ||
@@ -379,4 +397,4 @@ this.updateIndexes(record) | ||
* @since 3.0.0 | ||
* @param {string} name - The name of the new secondary index. | ||
* @param {string[]} [fieldList] - Array of field names to use as the key or | ||
* @param {string} name The name of the new secondary index. | ||
* @param {string[]} [fieldList] Array of field names to use as the key or | ||
* compound key of the new secondary index. If no fieldList is provided, then | ||
@@ -422,5 +440,5 @@ * the name will also be the field that is used to index the collection. | ||
* @since 3.0.0 | ||
* @param {(Object|Function)} [queryOrFn={}] - Selection query or filter | ||
* @param {(Object|Function)} [queryOrFn={}] Selection query or filter | ||
* function. | ||
* @param {Object} [thisArg] - Context to which to bind `queryOrFn` if | ||
* @param {Object} [thisArg] Context to which to bind `queryOrFn` if | ||
* `queryOrFn` is a function. | ||
@@ -443,4 +461,4 @@ * @returns {Array} The result. | ||
* @since 3.0.0 | ||
* @param {Function} forEachFn - Iteration function. | ||
* @param {*} [thisArg] - Context to which to bind `forEachFn`. | ||
* @param {Function} forEachFn Iteration function. | ||
* @param {*} [thisArg] Context to which to bind `forEachFn`. | ||
* @returns {Array} The result. | ||
@@ -457,3 +475,3 @@ */ | ||
* @since 3.0.0 | ||
* @param {(string|number)} id - The primary key of the record to get. | ||
* @param {(string|number)} id The primary key of the record to get. | ||
* @returns {(Object|Record)} The record with the given id. | ||
@@ -479,7 +497,7 @@ */ | ||
* @since 3.0.0 | ||
* @param {...Array} [keyList] - Provide one or more keyLists, and all | ||
* @param {...Array} [keyList] Provide one or more keyLists, and all | ||
* records matching each keyList will be retrieved. If no keyLists are | ||
* provided, all records will be returned. | ||
* @param {Object} [opts] - Configuration options. | ||
* @param {string} [opts.index] - Name of the secondary index to use in the | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string} [opts.index] Name of the secondary index to use in the | ||
* query. If no index is specified, the main index is used. | ||
@@ -518,3 +536,3 @@ * @returns {Array} The result. | ||
* @since 3.0.0 | ||
* @param {number} num - The maximum number of records to keep in the result. | ||
* @param {number} num The maximum number of records to keep in the result. | ||
* @returns {Array} The result. | ||
@@ -536,4 +554,4 @@ */ | ||
* @since 3.0.0 | ||
* @param {Function} mapFn - Mapping function. | ||
* @param {*} [thisArg] - Context to which to bind `mapFn`. | ||
* @param {Function} mapFn Mapping function. | ||
* @param {*} [thisArg] Context to which to bind `mapFn`. | ||
* @returns {Array} The result of the mapping. | ||
@@ -555,4 +573,4 @@ */ | ||
* @since 3.0.0 | ||
* @param {string} funcName - Name of function to call | ||
* @parama {...*} [args] - Remaining arguments to be passed to the function. | ||
* @param {string} funcName Name of function to call | ||
* @parama {...*} [args] Remaining arguments to be passed to the function. | ||
* @returns {Array} The result. | ||
@@ -616,4 +634,4 @@ */ | ||
* @since 3.0.0 | ||
* @param {Function} cb - Reduction callback. | ||
* @param {*} initialValue - Initial value of the reduction. | ||
* @param {Function} cb Reduction callback. | ||
* @param {*} initialValue Initial value of the reduction. | ||
* @returns {*} The result. | ||
@@ -631,4 +649,4 @@ */ | ||
* @since 3.0.0 | ||
* @param {(string|number)} id - The primary key of the record to be removed. | ||
* @param {Object} [opts] - Configuration options. | ||
* @param {(string|number)} id The primary key of the record to be removed. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {Object|Record} The removed record, if any. | ||
@@ -661,8 +679,8 @@ */ | ||
* @since 3.0.0 | ||
* @param {Object} [query={}] - Selection query. | ||
* @param {Object} [query.where] - Filtering criteria. | ||
* @param {number} [query.skip] - Number to skip. | ||
* @param {number} [query.limit] - Number to limit to. | ||
* @param {Array} [query.orderBy] - Sorting criteria. | ||
* @param {Object} [opts] - Configuration options. | ||
* @param {Object} [query={}] Selection query. See {@link query}. | ||
* @param {Object} [query.where] See {@link query.where}. | ||
* @param {number} [query.offset] See {@link query.offset}. | ||
* @param {number} [query.limit] See {@link query.limit}. | ||
* @param {string|Array[]} [query.orderBy] See {@link query.orderBy}. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(Object[]|Record[])} The removed records, if any. | ||
@@ -693,3 +711,3 @@ */ | ||
* @since 3.0.0 | ||
* @param {number} num - The number of records to skip. | ||
* @param {number} num The number of records to skip. | ||
* @returns {Array} The result. | ||
@@ -707,4 +725,4 @@ */ | ||
* @since 3.0.0 | ||
* @param {Object} [opts] - Configuration options. | ||
* @param {string[]} [opts.with] - Array of relation names or relation fields | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string[]} [opts.with] Array of relation names or relation fields | ||
* to include in the representation. | ||
@@ -724,4 +742,4 @@ * @returns {Array} The records. | ||
* @since 3.0.0 | ||
* @param {Object} record - The record to update. | ||
* @param {Object} [opts] - Configuration options. | ||
* @param {Object} record The record to update. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string} [opts.index] The index in which to update the record's | ||
@@ -741,4 +759,4 @@ * position. If you don't specify an index then the record will be updated | ||
* @since 3.0.0 | ||
* @param {Object} record - TODO | ||
* @param {Object} [opts] - Configuration options. | ||
* @param {Object} record TODO | ||
* @param {Object} [opts] Configuration options. | ||
*/ | ||
@@ -752,1 +770,27 @@ updateIndexes (record) { | ||
}) | ||
/** | ||
* Create a subclass of this Collection. | ||
* | ||
* @example <caption>Extend the class in a cross-browser manner.</caption> | ||
* import {Collection} from 'js-data' | ||
* const CustomCollectionClass = Collection.extend({ | ||
* foo () { return 'bar' } | ||
* }) | ||
* const customCollection = new CustomCollectionClass() | ||
* console.log(customCollection.foo()) // "bar" | ||
* | ||
* @example <caption>Extend the class using ES2015 class syntax.</caption> | ||
* class CustomCollectionClass extends Collection { | ||
* foo () { return 'bar' } | ||
* } | ||
* const customCollection = new CustomCollectionClass() | ||
* console.log(customCollection.foo()) // "bar" | ||
* | ||
* @method Collection.extend | ||
* @param {Object} [props={}] Properties to add to the prototype of the | ||
* subclass. | ||
* @param {Object} [classProps={}] Static properties to add to the subclass. | ||
* @returns {Constructor} Subclass of this Collection class. | ||
* @since 3.0.0 | ||
*/ |
import utils from './utils' | ||
import Component from './Component' | ||
import { | ||
belongsToType, | ||
hasManyType, | ||
hasOneType | ||
} from './decorators' | ||
import Mapper from './Mapper' | ||
@@ -436,45 +431,47 @@ | ||
const props = { | ||
constructor: function Container (opts) { | ||
utils.classCallCheck(this, Container) | ||
Container.__super__.call(this) | ||
opts || (opts = {}) | ||
export function Container (opts) { | ||
utils.classCallCheck(this, Container) | ||
Container.__super__.call(this) | ||
opts || (opts = {}) | ||
Object.defineProperties(this, { | ||
// Holds the adapters, shared by all mappers in this container | ||
_adapters: { | ||
value: {} | ||
}, | ||
// The the mappers in this container | ||
_mappers: { | ||
value: {} | ||
} | ||
}) | ||
Object.defineProperties(this, { | ||
// Holds the adapters, shared by all mappers in this container | ||
_adapters: { | ||
value: {} | ||
}, | ||
// The the mappers in this container | ||
_mappers: { | ||
value: {} | ||
} | ||
}) | ||
// Apply options provided by the user | ||
utils.fillIn(this, opts) | ||
// Apply options provided by the user | ||
utils.fillIn(this, opts) | ||
/** | ||
* Defaults options to pass to {@link Container#mapperClass} when creating a | ||
* new {@link Mapper}. | ||
* | ||
* @default {} | ||
* @name Container#mapperDefaults | ||
* @since 3.0.0 | ||
* @type {Object} | ||
*/ | ||
this.mapperDefaults = this.mapperDefaults || {} | ||
/** | ||
* Defaults options to pass to {@link Container#mapperClass} when creating a | ||
* new {@link Mapper}. | ||
* | ||
* @default {} | ||
* @name Container#mapperDefaults | ||
* @since 3.0.0 | ||
* @type {Object} | ||
*/ | ||
this.mapperDefaults = this.mapperDefaults || {} | ||
/** | ||
* Constructor function to use in {@link Container#defineMapper} to create a | ||
* new mapper. | ||
* | ||
* {@link Mapper} | ||
* @name Container#mapperClass | ||
* @since 3.0.0 | ||
* @type {Constructor} | ||
*/ | ||
this.mapperClass = this.mapperClass || Mapper | ||
}, | ||
/** | ||
* Constructor function to use in {@link Container#defineMapper} to create a | ||
* new mapper. | ||
* | ||
* {@link Mapper} | ||
* @name Container#mapperClass | ||
* @since 3.0.0 | ||
* @type {Constructor} | ||
*/ | ||
this.mapperClass = this.mapperClass || Mapper | ||
} | ||
const props = { | ||
constructor: Container, | ||
/** | ||
@@ -604,24 +601,4 @@ * Register a new event listener on this Container. | ||
mapper.on('all', (...args) => this._onMapperEvent(name, ...args)) | ||
mapper.defineRelations() | ||
// Setup the mapper's relations, including generating Mapper#relationList | ||
// and Mapper#relationFields | ||
utils.forOwn(mapper.relations, (group, type) => { | ||
utils.forOwn(group, (relations, _name) => { | ||
if (utils.isObject(relations)) { | ||
relations = [relations] | ||
} | ||
relations.forEach((def) => { | ||
def.getRelation = () => this.getMapper(_name) | ||
const relatedMapper = this._mappers[_name] || _name | ||
if (type === belongsToType) { | ||
mapper.belongsTo(relatedMapper, def) | ||
} else if (type === hasOneType) { | ||
mapper.hasOne(relatedMapper, def) | ||
} else if (type === hasManyType) { | ||
mapper.hasMany(relatedMapper, def) | ||
} | ||
}) | ||
}) | ||
}) | ||
return mapper | ||
@@ -691,2 +668,3 @@ }, | ||
* UserMapper === container.as('user').getMapper() // true | ||
* container.getMapper('profile') // throws Error, there is no mapper with name "profile" | ||
* | ||
@@ -699,3 +677,3 @@ * @method Container#getMapper | ||
getMapper (name) { | ||
const mapper = this._mappers[name] | ||
const mapper = this.getMapperByName(name) | ||
if (!mapper) { | ||
@@ -708,2 +686,24 @@ throw utils.err(`${DOMAIN}#getMapper`, name)(404, 'mapper') | ||
/** | ||
* Return the mapper registered under the specified name. | ||
* Doesn't throw error if mapper doesn't exist. | ||
* | ||
* @example | ||
* import {Container} from 'js-data' | ||
* const container = new Container() | ||
* // Container#defineMapper returns a direct reference to the newly created | ||
* // Mapper. | ||
* const UserMapper = container.defineMapper('user') | ||
* UserMapper === container.getMapperByName('user') // true | ||
* container.getMapperByName('profile') // undefined | ||
* | ||
* @method Container#getMapperByName | ||
* @param {string} name {@link Mapper#name}. | ||
* @returns {Mapper} | ||
* @since 3.0.0 | ||
*/ | ||
getMapperByName (name) { | ||
return this._mappers[name] | ||
}, | ||
/** | ||
* Register an adapter on this container under the given name. Adapters | ||
@@ -746,76 +746,3 @@ * registered on a container are shared by all mappers in the container. | ||
/** | ||
* ```javascript | ||
* import {Container} from 'js-data' | ||
* ``` | ||
* | ||
* The `Container` class is a place to store {@link Mapper} instances. | ||
* | ||
* Without a container, you need to manage mappers yourself, including resolving | ||
* circular dependencies among relations. All mappers in a container share the | ||
* same adapters, so you don't have to add each adapter to all of your mappers. | ||
* | ||
* @example <caption>Without Container</caption> | ||
* import {Mapper} from 'js-data' | ||
* import HttpAdapter from 'js-data-http' | ||
* const adapter = new HttpAdapter() | ||
* const userMapper = new Mapper({ name: 'user' }) | ||
* userMapper.registerAdapter('http', adapter, { default: true }) | ||
* const commentMapper = new Mapper({ name: 'comment' }) | ||
* commentMapper.registerAdapter('http', adapter, { default: true }) | ||
* | ||
* // This might be more difficult if the mappers were defined in different | ||
* // modules. | ||
* userMapper.hasMany(commentMapper, { | ||
* localField: 'comments', | ||
* foreignKey: 'userId' | ||
* }) | ||
* commentMapper.belongsTo(userMapper, { | ||
* localField: 'user', | ||
* foreignKey: 'userId' | ||
* }) | ||
* | ||
* @example <caption>With Container</caption> | ||
* import {Container} from 'js-data' | ||
* import HttpAdapter from 'js-data-http' | ||
* const container = new Container() | ||
* // All mappers in container share adapters | ||
* container.registerAdapter('http', new HttpAdapter(), { default: true }) | ||
* | ||
* // These could be defined in separate modules without a problem. | ||
* container.defineMapper('user', { | ||
* relations: { | ||
* hasMany: { | ||
* comment: { | ||
* localField: 'comments', | ||
* foreignKey: 'userId' | ||
* } | ||
* } | ||
* } | ||
* }) | ||
* container.defineMapper('comment', { | ||
* relations: { | ||
* belongsTo: { | ||
* user: { | ||
* localField: 'user', | ||
* foreignKey: 'userId' | ||
* } | ||
* } | ||
* } | ||
* }) | ||
* | ||
* @class Container | ||
* @extends Component | ||
* @param {Object} [opts] Configuration options. | ||
* @param {Function} [opts.mapperClass] Constructor function to use in | ||
* {@link Container#defineMapper} to create a new mapper. | ||
* @param {Object} [opts.mapperDefaults] Defaults options to pass to | ||
* {@link Container#mapperClass} when creating a new mapper. | ||
* @returns {Container} | ||
* @since 3.0.0 | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/components-of-jsdata#container","Components of JSData: Container"] | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/jsdata-and-the-browser","Notes on using JSData in the Browser"] | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/jsdata-and-nodejs","Notes on using JSData in Node.js"] | ||
*/ | ||
export const Container = Component.extend(props) | ||
Component.extend(props) | ||
@@ -822,0 +749,0 @@ /** |
@@ -32,3 +32,3 @@ import utils from './utils' | ||
if (record && record._set) { | ||
record._set(field, value) | ||
record._set(`props.${field}`, value) | ||
} else { | ||
@@ -47,14 +47,66 @@ utils.set(record, field, value) | ||
function DataStore (opts) { | ||
utils.classCallCheck(this, DataStore) | ||
DataStore.__super__.call(this, opts) | ||
this.collectionClass = this.collectionClass || LinkedCollection | ||
this._collections = {} | ||
this._pendingQueries = {} | ||
this._completedQueries = {} | ||
return this | ||
} | ||
/** | ||
* The `DataStore` class is an extension of {@link Container}. Not only does | ||
* `DataStore` manage mappers, but also collections. `DataStore` implements the | ||
* asynchronous {@link Mapper} methods, such as {@link Mapper#find} and | ||
* {@link Mapper#create}. If you use the asynchronous `DataStore` methods | ||
* instead of calling them directly on the mappers, then the results of the | ||
* method calls will be inserted into the store's collections. You can think of | ||
* a `DataStore` as an [Identity Map](https://en.wikipedia.org/wiki/Identity_map_pattern) | ||
* for the [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) | ||
* (the Mappers). | ||
* | ||
* ```javascript | ||
* import {DataStore} from 'js-data' | ||
* ``` | ||
* | ||
* @example | ||
* import {DataStore} from 'js-data' | ||
* import HttpAdapter from 'js-data-http' | ||
* const store = new DataStore() | ||
* | ||
* // DataStore#defineMapper returns a direct reference to the newly created | ||
* // Mapper. | ||
* const UserMapper = store.defineMapper('user') | ||
* | ||
* // DataStore#as returns the store scoped to a particular Mapper. | ||
* const UserStore = store.as('user') | ||
* | ||
* // Call "find" on "UserMapper" (Stateless ORM) | ||
* UserMapper.find(1).then((user) => { | ||
* // retrieved a "user" record via the http adapter, but that's it | ||
* | ||
* // Call "find" on "store" targeting "user" (Stateful DataStore) | ||
* return store.find('user', 1) // same as "UserStore.find(1)" | ||
* }).then((user) => { | ||
* // not only was a "user" record retrieved, but it was added to the | ||
* // store's "user" collection | ||
* const cachedUser = store.getCollection('user').get(1) | ||
* console.log(user === cachedUser) // true | ||
* }) | ||
* | ||
* @class DataStore | ||
* @extends Container | ||
* @param {Object} [opts] Configuration options. See {@link Container}. | ||
* @returns {DataStore} | ||
* @see Container | ||
* @since 3.0.0 | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/components-of-jsdata#datastore","Components of JSData: DataStore"] | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/working-with-the-datastore","Working with the DataStore"] | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/jsdata-and-the-browser","Notes on using JSData in the Browser"] | ||
*/ | ||
const props = { | ||
constructor: function DataStore (opts) { | ||
utils.classCallCheck(this, DataStore) | ||
DataStore.__super__.call(this, opts) | ||
constructor: DataStore, | ||
this.collectionClass = this.collectionClass || LinkedCollection | ||
this._collections = {} | ||
this._pendingQueries = {} | ||
this._completedQueries = {} | ||
return this | ||
}, | ||
_callSuper (method, ...args) { | ||
@@ -67,4 +119,3 @@ return this.constructor.__super__.prototype[method].apply(this, args) | ||
* | ||
* @name DataStore#_end | ||
* @method | ||
* @method DataStore#_end | ||
* @private | ||
@@ -78,9 +129,9 @@ * @param {string} name Name of the {@link LinkedCollection} to which to | ||
_end (name, result, opts) { | ||
let _data = opts.raw ? result.data : result | ||
if (_data && utils.isFunction(this.addToCache)) { | ||
_data = this.addToCache(name, _data, opts) | ||
let data = opts.raw ? result.data : result | ||
if (data && utils.isFunction(this.addToCache)) { | ||
data = this.addToCache(name, data, opts) | ||
if (opts.raw) { | ||
result.data = _data | ||
result.data = data | ||
} else { | ||
result = _data | ||
result = data | ||
} | ||
@@ -98,4 +149,3 @@ } | ||
* | ||
* @name DataStore#on | ||
* @method | ||
* @method DataStore#on | ||
* @param {string} event Name of event to subsribe to. | ||
@@ -109,4 +159,3 @@ * @param {Function} listener Listener function to handle the event. | ||
* | ||
* @name DataStore#_onCollectionEvent | ||
* @method | ||
* @method DataStore#_onCollectionEvent | ||
* @private | ||
@@ -124,7 +173,6 @@ * @param {string} name Name of the collection that emitted the event. | ||
* | ||
* @name DataStore#addToCache | ||
* @method | ||
* @param {string} name - Name of the {@link Mapper} to target. | ||
* @param {*} data - Data from which data should be selected for add. | ||
* @param {Object} [opts] - Configuration options. | ||
* @method DataStore#addToCache | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
* @param {*} data Data from which data should be selected for add. | ||
* @param {Object} [opts] Configuration options. | ||
*/ | ||
@@ -196,7 +244,7 @@ addToCache (name, data, opts) { | ||
* | ||
* @name DataStore#cachedFind | ||
* @method | ||
* @method DataStore#cachedFind | ||
* @param {string} name The `name` argument passed to {@link DataStore#find}. | ||
* @param {(string|number)} id The `id` argument passed to {@link DataStore#find}. | ||
* @param {Object} opts The `opts` argument passed to {@link DataStore#find}. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -208,4 +256,3 @@ cachedFind: cachedFn, | ||
* | ||
* @name DataStore#cachedFindAll | ||
* @method | ||
* @method DataStore#cachedFindAll | ||
* @param {string} name The `name` argument passed to {@link DataStore#findAll}. | ||
@@ -215,2 +262,3 @@ * @param {string} hash The result of calling {@link DataStore#hashQuery} on | ||
* @param {Object} opts The `opts` argument passed to {@link DataStore#findAll}. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -229,4 +277,3 @@ cachedFindAll: cachedFn, | ||
* | ||
* @name DataStore#cacheFind | ||
* @method | ||
* @method DataStore#cacheFind | ||
* @param {string} name The `name` argument passed to {@link DataStore#find}. | ||
@@ -236,2 +283,3 @@ * @param {*} data The result to cache. | ||
* @param {Object} opts The `opts` argument passed to {@link DataStore#find}. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -252,4 +300,3 @@ cacheFind (name, data, id, opts) { | ||
* | ||
* @name DataStore#cacheFindAll | ||
* @method | ||
* @method DataStore#cacheFindAll | ||
* @param {string} name The `name` argument passed to {@link DataStore#findAll}. | ||
@@ -260,2 +307,3 @@ * @param {*} data The result to cache. | ||
* @param {Object} opts The `opts` argument passed to {@link DataStore#findAll}. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -275,6 +323,5 @@ cacheFindAll (name, data, hash, opts) { | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#create}. Adds the created to the store. | ||
* | ||
* @name DataStore#create | ||
* @method | ||
* @method DataStore#create | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
@@ -284,3 +331,4 @@ * @param {Object} record Passed to {@link Mapper#create}. | ||
* {@link Mapper#create} for more configuration options. | ||
* @returns {Promise} | ||
* @returns {Promise} Resolves with the result of the create. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -294,6 +342,6 @@ create (name, record, opts) { | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#createMany}. Adds the created records to the | ||
* store. | ||
* | ||
* @name DataStore#createMany | ||
* @method | ||
* @method DataStore#createMany | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
@@ -303,3 +351,4 @@ * @param {Array} records Passed to {@link Mapper#createMany}. | ||
* {@link Mapper#createMany} for more configuration options. | ||
* @returns {Promise} | ||
* @returns {Promise} Resolves with the result of the create. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -423,31 +472,32 @@ createMany (name, records, opts) { | ||
if (mapper.recordClass.prototype.hasOwnProperty(foreignKey)) { | ||
const superClass = mapper.recordClass | ||
mapper.recordClass = superClass.extend({ | ||
constructor: (function () { | ||
var subClass = function Record (props, opts) { | ||
utils.classCallCheck(this, subClass) | ||
superClass.call(this, props, opts) | ||
} | ||
return subClass | ||
})() | ||
}) | ||
let foreignKeyDescriptor = Object.getOwnPropertyDescriptor(mapper.recordClass.prototype, foreignKey) | ||
if (!foreignKeyDescriptor) { | ||
foreignKeyDescriptor = { | ||
enumerable: true | ||
} | ||
} | ||
Object.defineProperty(mapper.recordClass.prototype, foreignKey, { | ||
enumerable: true, | ||
get () { return this._get(foreignKey) }, | ||
set (value) { | ||
const _self = this | ||
if (utils.isUndefined(value)) { | ||
// Unset locals | ||
utils.set(_self, localField, undefined) | ||
} else { | ||
safeSet(_self, foreignKey, value) | ||
let storeRecord = self.get(relation, value) | ||
if (storeRecord) { | ||
utils.set(_self, localField, storeRecord) | ||
} | ||
const originalGet = foreignKeyDescriptor.get | ||
foreignKeyDescriptor.get = function () { | ||
if (originalGet) { | ||
return originalGet.call(this) | ||
} | ||
return this._get(`props.${foreignKey}`) | ||
} | ||
const originalSet = foreignKeyDescriptor.set | ||
foreignKeyDescriptor.set = function (value) { | ||
if (originalSet) { | ||
originalSet.call(this, value) | ||
} | ||
if (utils.isUndefined(value)) { | ||
// Unset locals | ||
utils.set(this, localField, undefined) | ||
} else { | ||
safeSet(this, foreignKey, value) | ||
let storeRecord = self.get(relation, value) | ||
if (storeRecord) { | ||
utils.set(this, localField, storeRecord) | ||
} | ||
} | ||
}) | ||
} | ||
Object.defineProperty(mapper.recordClass.prototype, foreignKey, foreignKeyDescriptor) | ||
} else if (type === hasManyType) { | ||
@@ -625,11 +675,12 @@ const localKeys = def.localKeys | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#destroy}. Removes any destroyed record from the | ||
* store. | ||
* | ||
* @name DataStore#destroy | ||
* @method | ||
* @param {string} name - Name of the {@link Mapper} to target. | ||
* @param {(string|number)} id - Passed to {@link Mapper#destroy}. | ||
* @param {Object} [opts] - Passed to {@link Mapper#destroy}. See | ||
* @method DataStore#destroy | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
* @param {(string|number)} id Passed to {@link Mapper#destroy}. | ||
* @param {Object} [opts] Passed to {@link Mapper#destroy}. See | ||
* {@link Mapper#destroy} for more configuration options. | ||
* @returns {Promise} | ||
* @returns {Promise} Resolves when the delete completes. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -651,11 +702,12 @@ destroy (name, id, opts) { | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#destroyAll}. Removes any destroyed records from | ||
* the store. | ||
* | ||
* @name DataStore#destroyAll | ||
* @method | ||
* @param {string} name - Name of the {@link Mapper} to target. | ||
* @param {Object} [query] - Passed to {@link Mapper#destroyAll}. | ||
* @param {Object} [opts] - Passed to {@link Mapper#destroyAll}. See | ||
* @method DataStore#destroyAll | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
* @param {Object} [query] Passed to {@link Mapper#destroyAll}. | ||
* @param {Object} [opts] Passed to {@link Mapper#destroyAll}. See | ||
* {@link Mapper#destroyAll} for more configuration options. | ||
* @returns {Promise} | ||
* @returns {Promise} Resolves when the delete completes. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -688,10 +740,10 @@ destroyAll (name, query, opts) { | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#find}. Adds any found record to the store. | ||
* | ||
* @name DataStore#find | ||
* @method | ||
* @param {string} name - Name of the {@link Mapper} to target. | ||
* @param {(string|number)} id - Passed to {@link Mapper#find}. | ||
* @param {Object} [opts] - Passed to {@link Mapper#find}. | ||
* @returns {Promise} | ||
* @method DataStore#find | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
* @param {(string|number)} id Passed to {@link Mapper#find}. | ||
* @param {Object} [opts] Passed to {@link Mapper#find}. | ||
* @returns {Promise} Resolves with the result, if any. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -727,10 +779,10 @@ find (name, id, opts) { | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#findAll}. Adds any found records to the store. | ||
* | ||
* @name DataStore#findAll | ||
* @method | ||
* @param {string} name - Name of the {@link Mapper} to target. | ||
* @param {Object} [query] - Passed to {@link Model.findAll}. | ||
* @param {Object} [opts] - Passed to {@link Model.findAll}. | ||
* @returns {Promise} | ||
* @method DataStore#findAll | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
* @param {Object} [query] Passed to {@link Model.findAll}. | ||
* @param {Object} [opts] Passed to {@link Model.findAll}. | ||
* @returns {Promise} Resolves with the result, if any. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -768,8 +820,10 @@ findAll (name, query, opts) { | ||
/** | ||
* TODO | ||
* Return the {@link LinkedCollection} with the given name. | ||
* | ||
* @name DataStore#getCollection | ||
* @method | ||
* @method DataStore#getCollection | ||
* @param {string} name Name of the {@link LinkedCollection} to retrieve. | ||
* @returns {LinkedCollection} | ||
* @since 3.0.0 | ||
* @throws {Error} Thrown if the specified {@link LinkedCollection} does not | ||
* exist. | ||
*/ | ||
@@ -784,2 +838,17 @@ getCollection (name) { | ||
/** | ||
* Hashing function used to cache {@link DataStore#find} and | ||
* {@link DataStore#findAll} requests. This method simply JSONifies the | ||
* `query` argument passed to {@link DataStore#find} or | ||
* {@link DataStore#findAll}. | ||
* | ||
* Override this method for custom hashing behavior. | ||
* @method DataStore#hashQuery | ||
* @param {string} name The `name` argument passed to {@link DataStore#find} | ||
* or {@link DataStore#findAll}. | ||
* @param {Object} query The `query` argument passed to {@link DataStore#find} | ||
* or {@link DataStore#findAll}. | ||
* @returns {string} The JSONified `query`. | ||
* @since 3.0.0 | ||
*/ | ||
hashQuery (name, query, opts) { | ||
@@ -794,2 +863,15 @@ return utils.toJson(query) | ||
/** | ||
* Wrapper for {@link LinkedCollection#remove}. Removes the specified | ||
* {@link Record} from the store. | ||
* | ||
* @method DataStore#remove | ||
* @param {string} name The name of the {@link LinkedCollection} to target. | ||
* @param {string|number} id The primary key of the {@link Record} to remove. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string[]} [opts.with] Relations of the {@link Record} to also | ||
* remove from the store. | ||
* @returns {Record} The removed {@link Record}, if any. | ||
* @since 3.0.0 | ||
*/ | ||
remove (name, id, opts) { | ||
@@ -803,2 +885,19 @@ const record = this.getCollection(name).remove(id, opts) | ||
/** | ||
* Wrapper for {@link LinkedCollection#removeAll}. Removes the selected | ||
* {@link Record}s from the store. | ||
* | ||
* @method DataStore#removeAll | ||
* @param {string} name The name of the {@link LinkedCollection} to target. | ||
* @param {Object} [query={}] Selection query. See {@link query}. | ||
* @param {Object} [query.where] See {@link query.where}. | ||
* @param {number} [query.offset] See {@link query.offset}. | ||
* @param {number} [query.limit] See {@link query.limit}. | ||
* @param {string|Array[]} [query.orderBy] See {@link query.orderBy}. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string[]} [opts.with] Relations of the {@link Record} to also | ||
* remove from the store. | ||
* @returns {Record} The removed {@link Record}s, if any. | ||
* @since 3.0.0 | ||
*/ | ||
removeAll (name, query, opts) { | ||
@@ -812,3 +911,19 @@ const records = this.getCollection(name).removeAll(query, opts) | ||
/** | ||
* Remove from the store {@link Record}s that are related to the provided | ||
* {@link Record}(s). | ||
* | ||
* @method DataStore#removeRelated | ||
* @param {string} name The name of the {@link LinkedCollection} to target. | ||
* @param {Record|Record[]} records {@link Record}s whose relations are to be | ||
* removed. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string[]} [opts.with] Relations of the {@link Record}(s) to remove | ||
* from the store. | ||
* @since 3.0.0 | ||
*/ | ||
removeRelated (name, records, opts) { | ||
if (!utils.isArray(records)) { | ||
records = [records] | ||
} | ||
utils.forEachRelation(this.getMapper(name), opts, (def, optsCopy) => { | ||
@@ -856,12 +971,13 @@ records.forEach((record) => { | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#update}. Adds the updated {@link Record} to the | ||
* store. | ||
* | ||
* @name DataStore#update | ||
* @method | ||
* @param {string} name - Name of the {@link Mapper} to target. | ||
* @param {(string|number)} id - Passed to {@link Mapper#update}. | ||
* @param {Object} record - Passed to {@link Mapper#update}. | ||
* @param {Object} [opts] - Passed to {@link Mapper#update}. See | ||
* @method DataStore#update | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
* @param {(string|number)} id Passed to {@link Mapper#update}. | ||
* @param {Object} record Passed to {@link Mapper#update}. | ||
* @param {Object} [opts] Passed to {@link Mapper#update}. See | ||
* {@link Mapper#update} for more configuration options. | ||
* @returns {Promise} | ||
* @returns {Promise} Resolves with the result of the update. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -875,12 +991,13 @@ update (name, id, record, opts) { | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#updateAll}. Adds the updated {@link Record}s to | ||
* the store. | ||
* | ||
* @name DataStore#updateAll | ||
* @method | ||
* @param {string} name - Name of the {@link Mapper} to target. | ||
* @param {Object} props - Passed to {@link Mapper#updateAll}. | ||
* @param {Object} [query] - Passed to {@link Mapper#updateAll}. | ||
* @param {Object} [opts] - Passed to {@link Mapper#updateAll}. See | ||
* @method DataStore#updateAll | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
* @param {Object} props Passed to {@link Mapper#updateAll}. | ||
* @param {Object} [query] Passed to {@link Mapper#updateAll}. | ||
* @param {Object} [opts] Passed to {@link Mapper#updateAll}. See | ||
* {@link Mapper#updateAll} for more configuration options. | ||
* @returns {Promise} | ||
* @returns {Promise} Resolves with the result of the update. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -894,6 +1011,6 @@ updateAll (name, props, query, opts) { | ||
/** | ||
* TODO | ||
* Wrapper for {@link Mapper#updateMany}. Adds the updated {@link Record}s to | ||
* the store. | ||
* | ||
* @name DataStore#updateMany | ||
* @method | ||
* @method DataStore#updateMany | ||
* @param {string} name Name of the {@link Mapper} to target. | ||
@@ -903,3 +1020,4 @@ * @param {(Object[]|Record[])} records Passed to {@link Mapper#updateMany}. | ||
* {@link Mapper#updateMany} for more configuration options. | ||
* @returns {Promise} | ||
* @returns {Promise} Resolves with the result of the update. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -919,52 +1037,2 @@ updateMany (name, records, opts) { | ||
/** | ||
* The `DataStore` class is an extension of {@link Container}. Not only does | ||
* `DataStore` manage mappers, but also collections. `DataStore` implements the | ||
* asynchronous {@link Mapper} methods, such as {@link Mapper#find} and | ||
* {@link Mapper#create}. If you use the asynchronous `DataStore` methods | ||
* instead of calling them directly on the mappers, then the results of the | ||
* method calls will be inserted into the store's collections. You can think of | ||
* a `DataStore` as an [Identity Map](https://en.wikipedia.org/wiki/Identity_map_pattern) | ||
* for the [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) | ||
* (the Mappers). | ||
* | ||
* ```javascript | ||
* import {DataStore} from 'js-data' | ||
* ``` | ||
* | ||
* @example | ||
* import {DataStore} from 'js-data' | ||
* import HttpAdapter from 'js-data-http' | ||
* const store = new DataStore() | ||
* | ||
* // DataStore#defineMapper returns a direct reference to the newly created | ||
* // Mapper. | ||
* const UserMapper = store.defineMapper('user') | ||
* | ||
* // DataStore#as returns the store scoped to a particular Mapper. | ||
* const UserStore = store.as('user') | ||
* | ||
* // Call "find" on "UserMapper" (Stateless ORM) | ||
* UserMapper.find(1).then((user) => { | ||
* // retrieved a "user" record via the http adapter, but that's it | ||
* | ||
* // Call "find" on "store" targeting "user" (Stateful DataStore) | ||
* return store.find('user', 1) // same as "UserStore.find(1)" | ||
* }).then((user) => { | ||
* // not only was a "user" record retrieved, but it was added to the | ||
* // store's "user" collection | ||
* const cachedUser = store.getCollection('user').get(1) | ||
* console.log(user === cachedUser) // true | ||
* }) | ||
* | ||
* @class DataStore | ||
* @extends Container | ||
* @param {Object} [opts] Configuration options. See {@link Container}. | ||
* @returns {DataStore} | ||
* @see Container | ||
* @since 3.0.0 | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/components-of-jsdata#datastore","Components of JSData: DataStore"] | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/working-with-the-datastore","Working with the DataStore"] | ||
* @tutorial ["http://www.js-data.io/v3.0/docs/jsdata-and-the-browser","Notes on using JSData in the Browser"] | ||
*/ | ||
export default Container.extend(props) | ||
@@ -971,0 +1039,0 @@ |
@@ -1,114 +0,4 @@ | ||
import utils from './utils' | ||
import { Relation } from './relations' | ||
export const belongsToType = 'belongsTo' | ||
export const hasManyType = 'hasMany' | ||
export const hasOneType = 'hasOne' | ||
const DOMAIN = 'Relation' | ||
function Relation (related, opts) { | ||
const DOMAIN_ERR = `new ${DOMAIN}` | ||
opts || (opts = {}) | ||
const localField = opts.localField | ||
if (!localField) { | ||
throw utils.err(DOMAIN_ERR, 'opts.localField')(400, 'string', localField) | ||
} | ||
const foreignKey = opts.foreignKey = opts.foreignKey || opts.localKey | ||
if (!foreignKey && (opts.type === belongsToType || opts.type === hasOneType)) { | ||
throw utils.err(DOMAIN_ERR, 'opts.foreignKey')(400, 'string', foreignKey) | ||
} | ||
const localKeys = opts.localKeys | ||
const foreignKeys = opts.foreignKeys | ||
if (!foreignKey && !localKeys && !foreignKeys && opts.type === hasManyType) { | ||
throw utils.err(DOMAIN_ERR, 'opts.<foreignKey|localKeys|foreignKeys>')(400, 'string', foreignKey) | ||
} | ||
if (utils.isString(related)) { | ||
opts.relation = related | ||
if (!utils.isFunction(opts.getRelation)) { | ||
throw utils.err(DOMAIN_ERR, 'opts.getRelation')(400, 'function', opts.getRelation) | ||
} | ||
} else if (related) { | ||
opts.relation = related.name | ||
Object.defineProperty(this, 'relatedMapper', { | ||
value: related | ||
}) | ||
} else { | ||
throw utils.err(DOMAIN_ERR, 'related')(400, 'Mapper or string', related) | ||
} | ||
Object.defineProperty(this, 'inverse', { | ||
value: undefined, | ||
writable: true | ||
}) | ||
utils.fillIn(this, opts) | ||
} | ||
utils.addHiddenPropsToTarget(Relation.prototype, { | ||
getRelation () { | ||
return this.relatedMapper | ||
}, | ||
getForeignKey (record) { | ||
if (this.type === belongsToType) { | ||
return utils.get(record, this.foreignKey) | ||
} | ||
return utils.get(record, this.mapper.idAttribute) | ||
}, | ||
setForeignKey (record, relatedRecord) { | ||
if (!record || !relatedRecord) { | ||
return | ||
} | ||
if (this.type === belongsToType) { | ||
utils.set(record, this.foreignKey, utils.get(relatedRecord, this.getRelation().idAttribute)) | ||
} else { | ||
const idAttribute = this.mapper.idAttribute | ||
if (utils.isArray(relatedRecord)) { | ||
relatedRecord.forEach((relatedRecordItem) => { | ||
utils.set(relatedRecordItem, this.foreignKey, utils.get(record, idAttribute)) | ||
}) | ||
} else { | ||
utils.set(relatedRecord, this.foreignKey, utils.get(record, idAttribute)) | ||
} | ||
} | ||
}, | ||
getLocalField (record) { | ||
return utils.get(record, this.localField) | ||
}, | ||
setLocalField (record, data) { | ||
return utils.set(record, this.localField, data) | ||
}, | ||
getInverse (mapper) { | ||
if (this.inverse) { | ||
return this.inverse | ||
} | ||
this.getRelation().relationList.forEach((def) => { | ||
if (def.getRelation() === mapper) { | ||
if (def.foreignKey && def.foreignKey !== this.foreignKey) { | ||
return | ||
} | ||
this.inverse = def | ||
return false | ||
} | ||
}) | ||
return this.inverse | ||
} | ||
}) | ||
const relatedTo = function (mapper, related, opts) { | ||
opts.name = mapper.name | ||
const relation = new Relation(related, opts) | ||
Object.defineProperty(relation, 'mapper', { | ||
value: mapper | ||
}) | ||
mapper.relationList || Object.defineProperty(mapper, 'relationList', { value: [] }) | ||
mapper.relationFields || Object.defineProperty(mapper, 'relationFields', { value: [] }) | ||
mapper.relationList.push(relation) | ||
mapper.relationFields.push(relation.localField) | ||
} | ||
export { belongsToType, hasManyType, hasOneType } from './relations' | ||
/** | ||
@@ -129,6 +19,4 @@ * TODO | ||
export const belongsTo = function (related, opts) { | ||
opts || (opts = {}) | ||
opts.type = belongsToType | ||
return function (target) { | ||
relatedTo(target, related, opts) | ||
return function (mapper) { | ||
Relation.belongsTo(related, opts).assignTo(mapper) | ||
} | ||
@@ -152,6 +40,4 @@ } | ||
export const hasMany = function (related, opts) { | ||
opts || (opts = {}) | ||
opts.type = hasManyType | ||
return function (target) { | ||
relatedTo(target, related, opts) | ||
return function (mapper) { | ||
Relation.hasMany(related, opts).assignTo(mapper) | ||
} | ||
@@ -175,7 +61,5 @@ } | ||
export const hasOne = function (related, opts) { | ||
opts || (opts = {}) | ||
opts.type = hasOneType | ||
return function (target) { | ||
relatedTo(target, related, opts) | ||
return function (mapper) { | ||
Relation.hasOne(related, opts).assignTo(mapper) | ||
} | ||
} |
import utils from './utils' | ||
import { | ||
belongsToType, | ||
hasManyType, | ||
hasOneType | ||
} from './decorators' | ||
import './decorators' | ||
import Collection from './Collection' | ||
@@ -25,25 +21,27 @@ | ||
*/ | ||
export default Collection.extend({ | ||
constructor: function LinkedCollection (records, opts) { | ||
utils.classCallCheck(this, LinkedCollection) | ||
// Make sure this collection has somewhere to store "added" timestamps | ||
Object.defineProperties(this, { | ||
_added: { | ||
value: {} | ||
}, | ||
datastore: { | ||
writable: true, | ||
value: undefined | ||
} | ||
}) | ||
function LinkedCollection (records, opts) { | ||
utils.classCallCheck(this, LinkedCollection) | ||
// Make sure this collection has somewhere to store "added" timestamps | ||
Object.defineProperties(this, { | ||
_added: { | ||
value: {} | ||
}, | ||
datastore: { | ||
writable: true, | ||
value: undefined | ||
} | ||
}) | ||
LinkedCollection.__super__.call(this, records, opts) | ||
LinkedCollection.__super__.call(this, records, opts) | ||
// Make sure this collection has a reference to a datastore | ||
if (!this.datastore) { | ||
throw utils.err(`new ${DOMAIN}`, 'opts.datastore')(400, 'DataStore', this.datastore) | ||
} | ||
return this | ||
}, | ||
// Make sure this collection has a reference to a datastore | ||
if (!this.datastore) { | ||
throw utils.err(`new ${DOMAIN}`, 'opts.datastore')(400, 'DataStore', this.datastore) | ||
} | ||
return this | ||
} | ||
export default Collection.extend({ | ||
constructor: LinkedCollection, | ||
_onRecordEvent (...args) { | ||
@@ -60,12 +58,7 @@ utils.getSuper(this).prototype._onRecordEvent.apply(this, args) | ||
add (records, opts) { | ||
const datastore = this.datastore | ||
const mapper = this.mapper | ||
const relationList = mapper.relationList | ||
const timestamp = new Date().getTime() | ||
const usesRecordClass = !!mapper.recordClass | ||
const idAttribute = mapper.idAttribute | ||
let singular | ||
const singular = utils.isObject(records) && !utils.isArray(records) | ||
if (utils.isObject(records) && !utils.isArray(records)) { | ||
singular = true | ||
if (singular) { | ||
records = [records] | ||
@@ -76,118 +69,12 @@ } | ||
if (relationList.length && records.length) { | ||
if (mapper.relationList.length && records.length) { | ||
// Check the currently visited record for relations that need to be | ||
// inserted into their respective collections. | ||
mapper.relationList.forEach(function (def) { | ||
const relationName = def.relation | ||
// A reference to the Mapper that this Mapper is related to | ||
const relatedMapper = datastore.getMapper(relationName) | ||
// The field used by the related Mapper as the primary key | ||
const relationIdAttribute = relatedMapper.idAttribute | ||
// Grab the foreign key in this relationship, if there is one | ||
const foreignKey = def.foreignKey | ||
// A lot of this is an optimization for being able to insert a lot of | ||
// data as quickly as possible | ||
const relatedCollection = datastore.getCollection(relationName) | ||
const type = def.type | ||
const isHasMany = type === hasManyType | ||
const shouldAdd = utils.isUndefined(def.add) ? true : !!def.add | ||
let relatedData | ||
records.forEach(function (record) { | ||
// Grab a reference to the related data attached or linked to the | ||
// currently visited record | ||
relatedData = def.getLocalField(record) | ||
const id = utils.get(record, idAttribute) | ||
if (utils.isFunction(def.add)) { | ||
relatedData = def.add(datastore, def, record) | ||
} else if (relatedData) { | ||
// Otherwise, if there is something to be added, add it | ||
if (isHasMany) { | ||
// Handle inserting hasMany relations | ||
relatedData = relatedData.map(function (toInsertItem) { | ||
// Check that this item isn't the same item that is already in the | ||
// store | ||
if (toInsertItem !== relatedCollection.get(relatedCollection.recordId(toInsertItem))) { | ||
// Make sure this item has its foreignKey | ||
if (foreignKey) { | ||
// TODO: slow, could be optimized? But user loses hook | ||
def.setForeignKey(record, toInsertItem) | ||
} | ||
// Finally add this related item | ||
if (shouldAdd) { | ||
toInsertItem = relatedCollection.add(toInsertItem) | ||
} | ||
} | ||
return toInsertItem | ||
}) | ||
} else { | ||
const relatedDataId = utils.get(relatedData, relationIdAttribute) | ||
// Handle inserting belongsTo and hasOne relations | ||
if (relatedData !== relatedCollection.get(relatedDataId)) { | ||
// Make sure foreignKey field is set | ||
def.setForeignKey(record, relatedData) | ||
// Finally insert this related item | ||
if (shouldAdd) { | ||
relatedData = relatedCollection.add(relatedData) | ||
} | ||
} | ||
} | ||
} | ||
if (!relatedData || (utils.isArray(relatedData) && !relatedData.length)) { | ||
if (type === belongsToType) { | ||
const relatedId = utils.get(record, foreignKey) | ||
if (!utils.isUndefined(relatedId)) { | ||
relatedData = relatedCollection.get(relatedId) | ||
} | ||
} else if (type === hasOneType) { | ||
const _records = relatedCollection.filter({ | ||
[foreignKey]: id | ||
}) | ||
relatedData = _records.length ? _records[0] : undefined | ||
} else if (type === hasManyType) { | ||
if (foreignKey) { | ||
const _records = relatedCollection.filter({ | ||
[foreignKey]: id | ||
}) | ||
relatedData = _records.length ? _records : undefined | ||
} else if (def.localKeys && utils.get(record, def.localKeys)) { | ||
const _records = relatedCollection.filter({ | ||
where: { | ||
[relationIdAttribute]: { | ||
'in': utils.get(record, def.localKeys) | ||
} | ||
} | ||
}) | ||
relatedData = _records.length ? _records : undefined | ||
} else if (def.foreignKeys) { | ||
const _records = relatedCollection.filter({ | ||
where: { | ||
[def.foreignKeys]: { | ||
'contains': id | ||
} | ||
} | ||
}) | ||
relatedData = _records.length ? _records : undefined | ||
} | ||
} | ||
} | ||
if (relatedData) { | ||
def.setLocalField(record, relatedData) | ||
} else { | ||
} | ||
}) | ||
def.linkRecords(mapper, records) | ||
}) | ||
} | ||
records.forEach((record) => { | ||
// Track when this record was added | ||
this._added[this.recordId(record)] = timestamp | ||
records.forEach((record) => this._addMeta(record, timestamp)) | ||
if (usesRecordClass) { | ||
record._set('$', timestamp) | ||
} | ||
}) | ||
return singular ? records[0] : records | ||
@@ -200,6 +87,3 @@ }, | ||
if (record) { | ||
delete this._added[id] | ||
if (mapper.recordClass) { | ||
record._set('$') // unset | ||
} | ||
this._clearMeta(record) | ||
} | ||
@@ -212,10 +96,47 @@ return record | ||
const records = utils.getSuper(this).prototype.removeAll.call(this, query, opts) | ||
records.forEach((record) => { | ||
delete this._added[this.recordId(record)] | ||
if (mapper.recordClass) { | ||
record._set('$') // unset | ||
} | ||
}) | ||
records.forEach(this._clearMeta, this) | ||
return records | ||
}, | ||
_clearMeta (record) { | ||
delete this._added[this.recordId(record)] | ||
if (this.mapper.recordClass) { | ||
record._set('$') // unset | ||
} | ||
}, | ||
_addMeta (record, timestamp) { | ||
// Track when this record was added | ||
this._added[this.recordId(record)] = timestamp | ||
if (this.mapper.recordClass) { | ||
record._set('$', timestamp) | ||
} | ||
} | ||
}) | ||
/** | ||
* Create a subclass of this LinkedCollection. | ||
* | ||
* @example <caption>Extend the class in a cross-browser manner.</caption> | ||
* import {LinkedCollection} from 'js-data' | ||
* const CustomLinkedCollectionClass = LinkedCollection.extend({ | ||
* foo () { return 'bar' } | ||
* }) | ||
* const customLinkedCollection = new CustomLinkedCollectionClass() | ||
* console.log(customLinkedCollection.foo()) // "bar" | ||
* | ||
* @example <caption>Extend the class using ES2015 class syntax.</caption> | ||
* class CustomLinkedCollectionClass extends LinkedCollection { | ||
* foo () { return 'bar' } | ||
* } | ||
* const customLinkedCollection = new CustomLinkedCollectionClass() | ||
* console.log(customLinkedCollection.foo()) // "bar" | ||
* | ||
* @method LinkedCollection.extend | ||
* @param {Object} [props={}] Properties to add to the prototype of the | ||
* subclass. | ||
* @param {Object} [classProps={}] Static properties to add to the subclass. | ||
* @returns {Constructor} Subclass of this LinkedCollection class. | ||
* @since 3.0.0 | ||
*/ |
@@ -43,25 +43,27 @@ import utils from './utils' | ||
*/ | ||
export default Component.extend({ | ||
constructor: function Query (collection) { | ||
utils.classCallCheck(this, Query) | ||
function Query (collection) { | ||
utils.classCallCheck(this, Query) | ||
/** | ||
* The {@link Collection} on which this query operates. | ||
* | ||
* @name Query#collection | ||
* @since 3.0.0 | ||
* @type {Collection} | ||
*/ | ||
this.collection = collection | ||
/** | ||
* The {@link Collection} on which this query operates. | ||
* | ||
* @name Query#collection | ||
* @since 3.0.0 | ||
* @type {Collection} | ||
*/ | ||
this.collection = collection | ||
/** | ||
* The current data result of this query. | ||
* | ||
* @name Query#data | ||
* @since 3.0.0 | ||
* @type {Array} | ||
*/ | ||
this.data = null | ||
}, | ||
/** | ||
* The current data result of this query. | ||
* | ||
* @name Query#data | ||
* @since 3.0.0 | ||
* @type {Array} | ||
*/ | ||
this.data = null | ||
} | ||
export default Component.extend({ | ||
constructor: Query, | ||
_applyWhereFromObject (where) { | ||
@@ -818,4 +820,4 @@ const fields = [] | ||
* @param {Object} [classProps={}] Static properties to add to the subclass. | ||
* @returns {Constructor} Subclass of this Query. | ||
* @returns {Constructor} Subclass of this Query class. | ||
* @since 3.0.0 | ||
*/ |
@@ -29,33 +29,36 @@ import utils from './utils' | ||
* initial properties. | ||
* @since 3.0.0 | ||
*/ | ||
const Record = Component.extend({ | ||
constructor: function Record (props, opts) { | ||
utils.classCallCheck(this, Record) | ||
function Record (props, opts) { | ||
utils.classCallCheck(this, Record) | ||
props || (props = {}) | ||
opts || (opts = {}) | ||
const _props = {} | ||
Object.defineProperties(this, { | ||
_get: { value (key) { return utils.get(_props, key) } }, | ||
_set: { value (key, value) { return utils.set(_props, key, value) } }, | ||
_unset: { value (key) { return utils.unset(_props, key) } } | ||
}) | ||
const _set = this._set | ||
// TODO: Optimize these strings | ||
_set('creating', true) | ||
if (opts.noValidate) { | ||
_set('noValidate', true) | ||
} | ||
utils.fillIn(this, props) | ||
_set('creating', false) | ||
_set('noValidate', false) | ||
_set('previous', utils.plainCopy(props)) | ||
} | ||
props || (props = {}) | ||
opts || (opts = {}) | ||
const _props = {} | ||
Object.defineProperties(this, { | ||
_get: { value (key) { return utils.get(_props, key) } }, | ||
_set: { value (key, value) { return utils.set(_props, key, value) } }, | ||
_unset: { value (key) { return utils.unset(_props, key) } } | ||
}) | ||
const _set = this._set | ||
// TODO: Optimize these strings | ||
_set('creating', true) | ||
if (opts.noValidate) { | ||
_set('noValidate', true) | ||
} | ||
utils.fillIn(this, props) | ||
_set('creating', false) | ||
_set('noValidate', false) | ||
_set('previous', utils.copy(props)) | ||
}, | ||
export default Component.extend({ | ||
constructor: Record, | ||
/** | ||
* TODO | ||
* Returns the {@link Mapper} paired with this record's class, if any. | ||
* | ||
* @name Record#_mapper | ||
* @method | ||
* @ignore | ||
* @private | ||
* @method Record#_mapper | ||
* @returns {Mapper} The {@link Mapper} paired with this record's class, if any. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -71,8 +74,8 @@ _mapper () { | ||
/** | ||
* TODO | ||
* Lifecycle hook. | ||
* | ||
* @name Record#afterLoadRelations | ||
* @method | ||
* @param {string[]} relations TODO | ||
* @param {Object} opts TODO | ||
* @method Record#afterLoadRelations | ||
* @param {string[]} relations The `relations` argument passed to {@link Record#loadRelations}. | ||
* @param {Object} opts The `opts` argument passed to {@link Record#loadRelations}. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -82,8 +85,8 @@ afterLoadRelations () {}, | ||
/** | ||
* TODO | ||
* Lifecycle hook. | ||
* | ||
* @name Record#beforeLoadRelations | ||
* @method | ||
* @param {string[]} relations TODO | ||
* @param {Object} opts TODO | ||
* @method Record#beforeLoadRelations | ||
* @param {string[]} relations The `relations` argument passed to {@link Record#loadRelations}. | ||
* @param {Object} opts The `opts` argument passed to {@link Record#loadRelations}. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -96,7 +99,9 @@ beforeLoadRelations () {}, | ||
* | ||
* @name Record#changes | ||
* @method | ||
* @method Record#changes | ||
* @param [opts] Configuration options. | ||
* @param {Function} [opts.equalsFn] Equality function. Default uses `===`. | ||
* @param {Array} [opts.ignore] Array of strings or RegExp of fields to ignore. | ||
* @param {Function} [opts.equalsFn={@link utils.deepEqual}] Equality function. | ||
* @param {Array} [opts.ignore=[]] Array of strings or RegExp of fields to ignore. | ||
* @returns {Object} Object describing the changes to this record since it was | ||
* instantiated or its {@link Record#commit} method was last called. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -109,11 +114,11 @@ changes (opts) { | ||
/** | ||
* TODO | ||
* Make the record's current in-memory state it's only state, with any | ||
* previous property values being set to current values. | ||
* | ||
* @name Record#commit | ||
* @method | ||
* @method Record#commit | ||
* @since 3.0.0 | ||
*/ | ||
commit () { | ||
this._set('changed') // unset | ||
this._set('previous', utils.copy(this)) | ||
return this | ||
this._set('previous', utils.plainCopy(this)) | ||
}, | ||
@@ -124,6 +129,7 @@ | ||
* | ||
* @name Record#destroy | ||
* @method | ||
* @method Record#destroy | ||
* @param {Object} [opts] Configuration options passed to {@link Mapper#destroy}. | ||
* @returns {Promise} The result of calling {@link Mapper#destroy}. | ||
* @returns {Promise} The result of calling {@link Mapper#destroy} with the | ||
* primary key of this record. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -139,6 +145,6 @@ destroy (opts) { | ||
* | ||
* @name Record#get | ||
* @method | ||
* @param {string} key - Path of value to retrieve. | ||
* @method Record#get | ||
* @param {string} key Path of value to retrieve. | ||
* @returns {*} Value at path. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -153,7 +159,9 @@ 'get' (key) { | ||
* | ||
* @name Record#hasChanges | ||
* @method | ||
* @method Record#hasChanges | ||
* @param [opts] Configuration options. | ||
* @param {Function} [opts.equalsFn] Equality function. Default uses `===`. | ||
* @param {Array} [opts.ignore] Array of strings or RegExp of fields to ignore. | ||
* @param {Function} [opts.equalsFn={@link utils.deepEqual}] Equality function. | ||
* @param {Array} [opts.ignore=[]] Array of strings or RegExp of fields to ignore. | ||
* @returns {boolean} Return whether the record has changed since it was | ||
* instantiated or since its {@link Record#commit} method was called. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -166,11 +174,10 @@ hasChanges (opts) { | ||
/** | ||
* TODO | ||
* Return whether the record in its current state passes validation. | ||
* | ||
* @name Record#hashCode | ||
* @method | ||
* @method Record#isValid | ||
* @param {Object} [opts] Configuration options. Passed to {@link Mapper#validate}. | ||
* @returns {boolean} Whether the record in its current state passes | ||
* validation. | ||
* @since 3.0.0 | ||
*/ | ||
hashCode () { | ||
return utils.get(this, this._mapper().idAttribute) | ||
}, | ||
isValid (opts) { | ||
@@ -181,8 +188,11 @@ return !this._mapper().validate(this, opts) | ||
/** | ||
* TODO | ||
* Lazy load relations of this record, to be attached to the record once their | ||
* loaded. | ||
* | ||
* @name Record#loadRelations | ||
* @method | ||
* @param {string[]} [relations] TODO | ||
* @param {Object} [opts] TODO | ||
* @method Record#loadRelations | ||
* @param {string[]} [relations] List of relations to load. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {Promise} Resolves with the record, with the loaded relations now | ||
* attached. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -267,7 +277,9 @@ loadRelations (relations, opts) { | ||
/** | ||
* TODO | ||
* Return the properties with which this record was instantiated. | ||
* | ||
* @name Record#previous | ||
* @method | ||
* @param {string} [key] TODO | ||
* @method Record#previous | ||
* @param {string} [key] If specified, return just the initial value of the | ||
* given key. | ||
* @returns {Object} The initial properties of this record. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -282,7 +294,10 @@ previous (key) { | ||
/** | ||
* TODO | ||
* Revert changes to this record back to the properties it had when it was | ||
* instantiated. | ||
* | ||
* @name Record#revert | ||
* @method | ||
* @method Record#revert | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string[]} [opts.preserve] Array of strings or Regular Expressions | ||
* denoting properties that should not be reverted. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -304,3 +319,2 @@ revert (opts) { | ||
this.commit() | ||
return this | ||
}, | ||
@@ -311,13 +325,13 @@ | ||
* | ||
* @name Record#save | ||
* @method | ||
* @param {Object} [opts] Configuration options. See {@link Mapper#create}. | ||
* @param [opts] Configuration options. | ||
* @method Record#save | ||
* @param {Object} [opts] Configuration options. See {@link Mapper#create} and | ||
* {@link Mapper#update}. | ||
* @param {boolean} [opts.changesOnly] Equality function. Default uses `===`. | ||
* @param {Function} [opts.equalsFn] Passed to {@link Record#changes} when | ||
* `changesOnly` is `true`. | ||
* `opts.changesOnly` is `true`. | ||
* @param {Array} [opts.ignore] Passed to {@link Record#changes} when | ||
* `changesOnly` is `true`. | ||
* `opts.changesOnly` is `true`. | ||
* @returns {Promise} The result of calling {@link Mapper#create} or | ||
* {@link Mapper#update}. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -338,3 +352,10 @@ save (opts) { | ||
} | ||
return superMethod(mapper, 'update')(id, props, opts) | ||
return superMethod(mapper, 'update')(id, props, opts).then((result) => { | ||
const record = opts.raw ? result.data : result | ||
if (record) { | ||
utils.deepMixIn(this, record) | ||
this.commit() | ||
} | ||
return result | ||
}) | ||
}, | ||
@@ -346,8 +367,8 @@ | ||
* | ||
* @name Record#set | ||
* @method | ||
* @param {(string|Object)} key - Key to set or hash of key-value pairs to set. | ||
* @param {*} [value] - Value to set for the given key. | ||
* @param {Object} [opts] - Optional configuration. | ||
* @param {boolean} [opts.silent=false] - Whether to trigger change events. | ||
* @method Record#set | ||
* @param {(string|Object)} key Key to set or hash of key-value pairs to set. | ||
* @param {*} [value] Value to set for the given key. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {boolean} [opts.silent=false] Whether to trigger change events. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -368,16 +389,15 @@ 'set' (key, value, opts) { | ||
// TODO: move logic for single-item async operations onto the instance. | ||
/** | ||
* Return a plain object representation of this record. If the class from | ||
* which this record was created has a mapper, then {@link Mapper#toJSON} will | ||
* be called instead. | ||
* which this record was created has a Mapper, then {@link Mapper#toJSON} will | ||
* be called with this record instead. | ||
* | ||
* @name Record#toJSON | ||
* @method | ||
* @method Record#toJSON | ||
* @param {Object} [opts] Configuration options. | ||
* @param {string[]} [opts.with] Array of relation names or relation fields | ||
* to include in the representation. Only available as an option if the class | ||
* from which this record was created has a mapper. | ||
* from which this record was created has a Mapper and this record resides in | ||
* an instance of {@link DataStore}. | ||
* @returns {Object} Plain object representation of this record. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -391,3 +411,3 @@ toJSON (opts) { | ||
utils.forOwn(this, function (prop, key) { | ||
json[key] = utils.copy(prop) | ||
json[key] = utils.plainCopy(prop) | ||
}) | ||
@@ -401,7 +421,7 @@ return json | ||
* | ||
* @name Record#unset | ||
* @method | ||
* @param {string} key - Key to unset. | ||
* @param {Object} [opts] - Optional configuration. | ||
* @param {boolean} [opts.silent=false] - Whether to trigger change events. | ||
* @method Record#unset | ||
* @param {string} key Key to unset. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {boolean} [opts.silent=false] Whether to trigger change events. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -412,2 +432,10 @@ unset (key, opts) { | ||
/** | ||
* Validate this record based on its current properties. | ||
* | ||
* @method Record#validate | ||
* @param {Object} [opts] Configuration options. Passed to {@link Mapper#validate}. | ||
* @returns {*} Array of errors or `undefined` if no errors. | ||
* @since 3.0.0 | ||
*/ | ||
validate (opts) { | ||
@@ -433,2 +461,26 @@ return this._mapper().validate(this, opts) | ||
export default Record | ||
/** | ||
* Create a subclass of this Record. | ||
* | ||
* @example <caption>Extend the class in a cross-browser manner.</caption> | ||
* import {Record} from 'js-data' | ||
* const CustomRecordClass = Record.extend({ | ||
* foo () { return 'bar' } | ||
* }) | ||
* const customRecord = new CustomRecordClass() | ||
* console.log(customRecord.foo()) // "bar" | ||
* | ||
* @example <caption>Extend the class using ES2015 class syntax.</caption> | ||
* class CustomRecordClass extends Record { | ||
* foo () { return 'bar' } | ||
* } | ||
* const customRecord = new CustomRecordClass() | ||
* console.log(customRecord.foo()) // "bar" | ||
* | ||
* @method Record.extend | ||
* @param {Object} [props={}] Properties to add to the prototype of the | ||
* subclass. | ||
* @param {Object} [classProps={}] Static properties to add to the subclass. | ||
* @returns {Constructor} Subclass of this Record class. | ||
* @since 3.0.0 | ||
*/ |
@@ -7,4 +7,9 @@ import utils from './utils' | ||
/** | ||
* TODO | ||
* | ||
* A function map for each of the seven primitive JSON types defined by the core specification. | ||
* Each function will check a given value and return true or false if the value is an instance of that type. | ||
* ``` | ||
* types.integer(1) // returns true | ||
* types.string({}) // returns false | ||
* ``` | ||
* http://json-schema.org/latest/json-schema-core.html#anchor8 | ||
* @name Schema.types | ||
@@ -93,4 +98,3 @@ * @type {Object} | ||
/** | ||
* TODO | ||
* | ||
* A map of all object member validation functions for each keyword defined in the JSON Schema. | ||
* @name Schema.validationKeywords | ||
@@ -101,9 +105,16 @@ * @type {Object} | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor82 | ||
* Validates the provided value against all schemas defined in the Schemas `allOf` keyword. | ||
* The instance is valid against if and only if it is valid against all the schemas declared in the Schema's value. | ||
* | ||
* The value of this keyword MUST be an array. This array MUST have at least one element. | ||
* Each element of this array MUST be a valid JSON Schema. | ||
* | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor82 | ||
* | ||
* @name Schema.validationKeywords.allOf | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value Value to be validated. | ||
* @param {Object} [schema] Schema containing the `allOf` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -119,9 +130,16 @@ allOf (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor85 | ||
* Validates the provided value against all schemas defined in the Schemas `anyOf` keyword. | ||
* The instance is valid against this keyword if and only if it is valid against | ||
* at least one of the schemas in this keyword's value. | ||
* | ||
* The value of this keyword MUST be an array. This array MUST have at least one element. | ||
* Each element of this array MUST be an object, and each object MUST be a valid JSON Schema. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor85 | ||
* | ||
* @name Schema.validationKeywords.anyOf | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value Value to be validated. | ||
* @param {Object} [schema] Schema containing the `anyOf` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -156,13 +174,16 @@ anyOf (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor76 | ||
* Validates the provided value against an array of possible values defined by the Schema's `enum` keyword | ||
* Validation succeeds if the value is deeply equal to one of the values in the array. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor76 | ||
* | ||
* @name Schema.validationKeywords.enum | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value Value to validate | ||
* @param {Object} [schema] Schema containing the `enum` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
enum (value, schema, opts) { | ||
const possibleValues = schema['enum'] | ||
if (possibleValues.indexOf(value) === -1) { | ||
if (utils.findIndex(possibleValues, (item) => utils.deepEqual(item, value)) === -1) { | ||
return makeError(value, `one of (${possibleValues.join(', ')})`, opts) | ||
@@ -173,9 +194,11 @@ } | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor37 | ||
* Validates each of the provided array values against a schema or an array of schemas defined by the Schema's `items` keyword | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor37 for validation rules. | ||
* | ||
* @name Schema.validationKeywords.items | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [Array] Array to be validated. | ||
* @param {Object} [schema] Schema containing the items keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -202,2 +225,4 @@ items (value, schema, opts) { | ||
/** | ||
* Validates the provided number against a maximum value defined by the Schema's `maximum` keyword | ||
* Validation succeeds if the value is a number, and is less than, or equal to, the value of this keyword. | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor17 | ||
@@ -207,5 +232,6 @@ * | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [Number] number to validate against the keyword. | ||
* @param {Object} schema [schema] Schema containing the `maximum` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -219,5 +245,6 @@ maximum (value, schema, opts) { | ||
const exclusiveMaximum = schema.exclusiveMaximum | ||
if (typeof value === typeof maximum && (exclusiveMaximum ? maximum < value : maximum <= value)) { | ||
// TODO: Account for value of exclusiveMaximum in messaging | ||
return makeError(value, `no more than ${maximum}`, opts) | ||
if (typeof value === typeof maximum && !(exclusiveMaximum ? maximum > value : maximum >= value)) { | ||
return exclusiveMaximum | ||
? makeError(value, `no more than nor equal to ${maximum}`, opts) | ||
: makeError(value, `no more than ${maximum}`, opts) | ||
} | ||
@@ -227,22 +254,30 @@ }, | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor42 | ||
* Validates the length of the provided array against a maximum value defined by the Schema's `maxItems` keyword. | ||
* Validation succeeds if the length of the array is less than, or equal to the value of this keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor42 | ||
* | ||
* @name Schema.validationKeywords.maxItems | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [array] Array to be validated. | ||
* @param {Object} [schema] Schema containing the `maxItems` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
maxItems (value, schema, opts) { | ||
return maxLengthCommon('maxItems', value, schema, opts) | ||
if (utils.isArray(value)) { | ||
return maxLengthCommon('maxItems', value, schema, opts) | ||
} | ||
}, | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor26 | ||
* Validates the length of the provided string against a maximum value defined in the Schema's `maxLength` keyword. | ||
* Validation succeeds if the length of the string is less than, or equal to the value of this keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor26 | ||
* | ||
* @name Schema.validationKeywords.maxLength | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [string] String to be validated. | ||
* @param {Object} [schema] Schema containing the `maxLength` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -254,11 +289,16 @@ maxLength (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor54 | ||
* Validates the count of the provided object's properties against a maximum value defined in the Schema's `maxProperties` keyword. | ||
* Validation succeeds if the object's property count is less than, or equal to the value of this keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor54 | ||
* | ||
* @name Schema.validationKeywords.maxProperties | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [Object] Object to be validated. | ||
* @param {Object} [schema] Schema containing the `maxProperties` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
maxProperties (value, schema, opts) { | ||
// validate only objects | ||
if (!utils.isObject(value)) return | ||
const maxProperties = schema.maxProperties | ||
@@ -272,2 +312,4 @@ const length = Object.keys(value).length | ||
/** | ||
* Validates the provided value against a minimum value defined by the Schema's `minimum` keyword | ||
* Validation succeeds if the value is a number and is greater than, or equal to, the value of this keyword. | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor21 | ||
@@ -277,5 +319,6 @@ * | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [number] number to validate against the keyword. | ||
* @param {Object} [schema] Schema containing the `minimum` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -289,5 +332,6 @@ minimum (value, schema, opts) { | ||
const exclusiveMinimum = schema.exclusiveMinimum | ||
if (typeof value === typeof minimum && (exclusiveMinimum ? minimum > value : minimum >= value)) { | ||
// TODO: Account for value of exclusiveMinimum in messaging | ||
return makeError(value, `no less than ${minimum}`, opts) | ||
if (typeof value === typeof minimum && !(exclusiveMinimum ? value > minimum : value >= minimum)) { | ||
return exclusiveMinimum | ||
? makeError(value, `no less than nor equal to ${minimum}`, opts) | ||
: makeError(value, `no less than ${minimum}`, opts) | ||
} | ||
@@ -297,22 +341,30 @@ }, | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor42 | ||
* Validates the length of the provided array against a minimum value defined by the Schema's `minItems` keyword. | ||
* Validation succeeds if the length of the array is greater than, or equal to the value of this keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor45 | ||
* | ||
* @name Schema.validationKeywords.minItems | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [array] Array to be validated. | ||
* @param {Object} [schema] Schema containing the `minItems` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
minItems (value, schema, opts) { | ||
return minLengthCommon('minItems', value, schema, opts) | ||
if (utils.isArray(value)) { | ||
return minLengthCommon('minItems', value, schema, opts) | ||
} | ||
}, | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor29 | ||
* Validates the length of the provided string against a minimum value defined in the Schema's `minLength` keyword. | ||
* Validation succeeds if the length of the string is greater than, or equal to the value of this keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor29 | ||
* | ||
* @name Schema.validationKeywords.minLength | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [string] String to be validated. | ||
* @param {Object} [schema] Schema containing the `minLength` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -324,11 +376,16 @@ minLength (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor57 | ||
* Validates the count of the provided object's properties against a minimum value defined in the Schema's `minProperties` keyword. | ||
* Validation succeeds if the object's property count is greater than, or equal to the value of this keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor57 | ||
* | ||
* @name Schema.validationKeywords.minProperties | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [Object] Object to be validated. | ||
* @param {Object} [schema] Schema containing the `minProperties` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
minProperties (value, schema, opts) { | ||
// validate only objects | ||
if (!utils.isObject(value)) return | ||
const minProperties = schema.minProperties | ||
@@ -342,22 +399,33 @@ const length = Object.keys(value).length | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor14 | ||
* Validates the provided number is a multiple of the number defined in the Schema's `multipleOf` keyword. | ||
* Validation succeeds if the number can be divided equally into the value of this keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor14 | ||
* | ||
* @name Schema.validationKeywords.multipleOf | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [number] Number to be validated. | ||
* @param {Object} [schema] Schema containing the `multipleOf` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
multipleOf (value, schema, opts) { | ||
// TODO | ||
const multipleOf = schema.multipleOf | ||
if (utils.isNumber(value)) { | ||
if ((value / multipleOf) % 1 !== 0) { | ||
return makeError(value, `multipleOf ${multipleOf}`, opts) | ||
} | ||
} | ||
}, | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor91 | ||
* Validates the provided value is not valid with any of the schemas defined in the Schema's `not` keyword. | ||
* An instance is valid against this keyword if and only if it is NOT valid against the schemas in this keyword's value. | ||
* | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor91 | ||
* @name Schema.validationKeywords.not | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value to be checked. | ||
* @param {Object} [schema] Schema containing the not keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -372,9 +440,12 @@ not (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor88 | ||
* Validates the provided value is valid with one and only one of the schemas defined in the Schema's `oneOf` keyword. | ||
* An instance is valid against this keyword if and only if it is valid against a single schemas in this keyword's value. | ||
* | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor88 | ||
* @name Schema.validationKeywords.oneOf | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value to be checked. | ||
* @param {Object} [schema] Schema containing the `oneOf` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -400,9 +471,12 @@ oneOf (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor33 | ||
* Validates the provided string matches a pattern defined in the Schema's `pattern` keyword. | ||
* Validation succeeds if the string is a match of the regex value of this keyword. | ||
* | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor33 | ||
* @name Schema.validationKeywords.pattern | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [string] String to be validated. | ||
* @param {Object} [schema] Schema containing the `pattern` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -417,9 +491,14 @@ pattern (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor64 | ||
* Validates the provided object's properties against a map of values defined in the Schema's `properties` keyword. | ||
* Validation succeeds if the object's property are valid with each of the schema's in the provided map. | ||
* Validation also depends on the additionalProperties and or patternProperties. | ||
* | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor64 for more info. | ||
* | ||
* @name Schema.validationKeywords.properties | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [Object] Object to be validated. | ||
* @param {Object} [schema] Schema containing the `properties` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -483,11 +562,15 @@ properties (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor61 | ||
* Validates the provided object's has all properties listed in the Schema's `properties` keyword array. | ||
* Validation succeeds if the object contains all properties provided in the array value of this keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor61 | ||
* | ||
* @name Schema.validationKeywords.required | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [Object] Object to be validated. | ||
* @param {Object} [schema] Schema containing the `required` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
required (value, schema, opts) { | ||
opts || (opts = {}) | ||
const required = schema.required | ||
@@ -509,9 +592,11 @@ let errors = [] | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor79 | ||
* Validates the provided value's type is equal to the type, or array of types, defined in the Schema's `type` keyword. | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor79 | ||
* | ||
* @name Schema.validationKeywords.type | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value Value to be validated. | ||
* @param {Object} [schema] Schema containing the `type` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -547,9 +632,12 @@ type (value, schema, opts) { | ||
/** | ||
* http://json-schema.org/latest/json-schema-validation.html#anchor49 | ||
* Validates the provided array values are unique. | ||
* Validation succeeds if the items in the array are unique, but only if the value of this keyword is true | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor49 | ||
* | ||
* @name Schema.validationKeywords.uniqueItems | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [array] Array to be validated. | ||
* @param {Object} [schema] Schema containing the `uniqueItems` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -566,3 +654,3 @@ uniqueItems (value, schema, opts) { | ||
// Found a duplicate | ||
if (item === value[j]) { | ||
if (utils.deepEqual(item, value[j])) { | ||
return makeError(item, 'no duplicates', opts) | ||
@@ -609,9 +697,10 @@ } | ||
/** | ||
* TODO | ||
* Validates the provided value against a given Schema according to the http://json-schema.org/ v4 specification. | ||
* | ||
* @name Schema.validate | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} [schema] TODO | ||
* @param {*} value Value to be validated. | ||
* @param {Object} [schema] Valid Schema according to the http://json-schema.org/ v4 specification. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -659,2 +748,3 @@ const validate = function (value, schema, opts) { | ||
} | ||
errors = errors.concat(validateAny(value, schema, opts) || []) | ||
@@ -695,2 +785,4 @@ if (shouldPop) { | ||
const descriptor = { | ||
// Better to allow configurability, but at the user's own risk | ||
configurable: true, | ||
// These properties are enumerable by default, but regardless of their | ||
@@ -707,3 +799,13 @@ // enumerability, they won't be "own" properties of individual records | ||
descriptor.get = function () { return this._get(keyPath) } | ||
descriptor.get = function () { | ||
return this._get(keyPath) | ||
} | ||
if (utils.isFunction(schema.get)) { | ||
const originalGet = descriptor.get | ||
descriptor.get = function () { | ||
return schema.get.call(this, originalGet) | ||
} | ||
} | ||
descriptor.set = function (value) { | ||
@@ -790,2 +892,9 @@ // These are accessed a lot | ||
if (utils.isFunction(schema.set)) { | ||
const originalSet = descriptor.set | ||
descriptor.set = function (value) { | ||
return schema.set.call(this, value, originalSet) | ||
} | ||
} | ||
return descriptor | ||
@@ -795,3 +904,3 @@ } | ||
/** | ||
* TODO | ||
* A map of validation functions grouped by type. | ||
* | ||
@@ -803,9 +912,15 @@ * @name Schema.typeGroupValidators | ||
/** | ||
* TODO | ||
* Validates the provided value against the schema using all of the validation keywords specific to instances of an array. | ||
* The validation keywords for the type `array` are: | ||
*``` | ||
* ['items', 'maxItems', 'minItems', 'uniqueItems'] | ||
*``` | ||
* see http://json-schema.org/latest/json-schema-validation.html#anchor25 | ||
* | ||
* @name Schema.typeGroupValidators.array | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [array] Array to be validated. | ||
* @param {Object} [schema] Schema containing at least one array keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -817,9 +932,13 @@ array: function (value, schema, opts) { | ||
/** | ||
* TODO | ||
* | ||
* Validates the provided value against the schema using all of the validation keywords specific to instances of an integer. | ||
* The validation keywords for the type `integer` are: | ||
*``` | ||
* ['multipleOf', 'maximum', 'minimum'] | ||
*``` | ||
* @name Schema.typeGroupValidators.integer | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [number] Number to be validated. | ||
* @param {Object} [schema] Schema containing at least one `integer` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -832,9 +951,13 @@ integer: function (value, schema, opts) { | ||
/** | ||
* TODO | ||
* | ||
* Validates the provided value against the schema using all of the validation keywords specific to instances of an number. | ||
* The validation keywords for the type `number` are: | ||
*``` | ||
* ['multipleOf', 'maximum', 'minimum'] | ||
*``` | ||
* @name Schema.typeGroupValidators.number | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [number] Number to be validated. | ||
* @param {Object} [schema] Schema containing at least one `number` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -847,4 +970,7 @@ number: function (value, schema, opts) { | ||
/** | ||
* TODO | ||
* | ||
* Validates the provided value against the schema using all of the validation keywords specific to instances of a number or integer. | ||
* The validation keywords for the type `numeric` are: | ||
*``` | ||
* ['multipleOf', 'maximum', 'minimum'] | ||
*``` | ||
* See http://json-schema.org/latest/json-schema-validation.html#anchor13. | ||
@@ -854,5 +980,6 @@ * | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [number] Number to be validated. | ||
* @param {Object} [schema] Schema containing at least one `numeric` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -864,4 +991,7 @@ numeric: function (value, schema, opts) { | ||
/** | ||
* TODO | ||
* | ||
* Validates the provided value against the schema using all of the validation keywords specific to instances of an object. | ||
* The validation keywords for the type `object` are: | ||
*``` | ||
* ['maxProperties', 'minProperties', 'required', 'properties', 'dependencies'] | ||
*``` | ||
* See http://json-schema.org/latest/json-schema-validation.html#anchor53. | ||
@@ -871,5 +1001,6 @@ * | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [number] Object to be validated. | ||
* @param {Object} [schema] Schema containing at least one `object` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -881,4 +1012,7 @@ object: function (value, schema, opts) { | ||
/** | ||
* TODO | ||
* | ||
* Validates the provided value against the schema using all of the validation keywords specific to instances of an string. | ||
* The validation keywords for the type `string` are: | ||
*``` | ||
* ['maxLength', 'minLength', 'pattern'] | ||
*``` | ||
* See http://json-schema.org/latest/json-schema-validation.html#anchor25. | ||
@@ -888,5 +1022,6 @@ * | ||
* @method | ||
* @param {*} value TODO | ||
* @param {Object} schema TODO | ||
* @param {Object} opts TODO | ||
* @param {*} value [number] String to be validated. | ||
* @param {Object} [schema] Schema containing at least one `string` keyword. | ||
* @param {Object} [opts] Configuration options. | ||
* @returns {(array|undefined)} Array of errors or `undefined` if valid. | ||
*/ | ||
@@ -909,18 +1044,20 @@ string: function (value, schema, opts) { | ||
*/ | ||
function Schema (definition) { | ||
definition || (definition = {}) | ||
// TODO: schema validation | ||
utils.fillIn(this, definition) | ||
// TODO: rework this to make sure all possible keywords are converted | ||
if (definition.properties) { | ||
utils.forOwn(definition.properties, function (_definition, prop) { | ||
if (!(_definition instanceof Schema)) { | ||
definition.properties[prop] = new Schema(_definition) | ||
} | ||
}) | ||
} | ||
} | ||
export default Component.extend({ | ||
constructor: function Schema (definition) { | ||
definition || (definition = {}) | ||
// TODO: schema validation | ||
utils.fillIn(this, definition) | ||
constructor: Schema, | ||
// TODO: rework this to make sure all possible keywords are converted | ||
if (definition.properties) { | ||
utils.forOwn(definition.properties, function (_definition, prop) { | ||
if (!(_definition instanceof Schema)) { | ||
definition.properties[prop] = new Schema(_definition) | ||
} | ||
}) | ||
} | ||
}, | ||
/** | ||
@@ -927,0 +1064,0 @@ * This adds ES5 getters/setters to the target based on the "properties" in |
515
src/utils.js
@@ -91,3 +91,3 @@ /** | ||
* | ||
* @name utils._ | ||
* @method utils._ | ||
* @param {Object} dest Destination object. | ||
@@ -107,8 +107,13 @@ * @param {Object} src Source object. | ||
/** | ||
* TODO | ||
* Recursively iterates over relations found in `opts.with`. | ||
* | ||
* @name utils._forRelation | ||
* @method utils._forRelation | ||
* @param {Object} opts Configuration options. | ||
* @param {Relation} def Relation definition. | ||
* @param {Function} fn Callback function. | ||
* @param {*} [thisArg] Execution context for the callback function. | ||
* @private | ||
* @since 3.0.0 | ||
*/ | ||
_forRelation (opts, def, fn, ctx) { | ||
_forRelation (opts, def, fn, thisArg) { | ||
const relationName = def.relation | ||
@@ -127,3 +132,3 @@ let containedName = null | ||
if (opts.withAll) { | ||
fn.call(ctx, def, {}) | ||
fn.call(thisArg, def, {}) | ||
return | ||
@@ -145,10 +150,13 @@ } else if (!containedName) { | ||
}) | ||
fn.call(ctx, def, optsCopy) | ||
fn.call(thisArg, def, optsCopy) | ||
}, | ||
/** | ||
* TODO | ||
* Find the index of a relation in the given list | ||
* | ||
* @name utils._getIndex | ||
* @method utils._getIndex | ||
* @param {string[]} list List to search. | ||
* @param {string} relation Relation to find. | ||
* @private | ||
* @returns {number} | ||
*/ | ||
@@ -175,13 +183,25 @@ _getIndex (list, relation) { | ||
* | ||
* @name utils.addHiddenPropsToTarget | ||
* @example | ||
* import {utils} from 'js-data' | ||
* function Cat () {} | ||
* utils.addHiddenPropsToTarget(Cat.prototype, { | ||
* say () { | ||
* console.log('meow') | ||
* } | ||
* }) | ||
* const cat = new Cat() | ||
* cat.say() // "meow" | ||
* | ||
* @method utils.addHiddenPropsToTarget | ||
* @param {Object} target That to which `props` should be added. | ||
* @param {Object} props Properties to be added to `target`. | ||
* @since 3.0.0 | ||
*/ | ||
addHiddenPropsToTarget (target, props) { | ||
const map = {} | ||
utils.forOwn(props, function (value, key) { | ||
map[key] = { | ||
writable: true, | ||
value | ||
} | ||
Object.keys(props).forEach(function (propName) { | ||
const descriptor = Object.getOwnPropertyDescriptor(props, propName) | ||
descriptor.enumerable = false | ||
map[propName] = descriptor | ||
}) | ||
@@ -192,9 +212,23 @@ Object.defineProperties(target, map) | ||
/** | ||
* TODO | ||
* Return whether the two objects are deeply different. | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* utils.areDifferent({}, {}) // false | ||
* utils.areDifferent({ a: 1 }, { a: 1 }) // false | ||
* utils.areDifferent({ foo: 'bar' }, {}) // true | ||
* | ||
* @method utils.areDifferent | ||
* @param {Object} a Base object. | ||
* @param {Object} b Comparison object. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {Function} [opts.equalsFn={@link utils.deepEqual}] Equality function. | ||
* @param {Array} [opts.ignore=[]] Array of strings or RegExp of fields to ignore. | ||
* @returns {boolean} Whether the two objects are deeply different. | ||
* @see utils.diffObjects | ||
* @since 3.0.0 | ||
*/ | ||
areDifferent (a, b, opts) { | ||
areDifferent (newObject, oldObject, opts) { | ||
opts || (opts = {}) | ||
const diff = utils.diffObjects(a, b, opts) | ||
const diff = utils.diffObjects(newObject, oldObject, opts) | ||
const diffCount = Object.keys(diff.added).length + | ||
@@ -207,5 +241,20 @@ Object.keys(diff.removed).length + | ||
/** | ||
* TODO | ||
* Verified that the given constructor is being invoked via `new`, as opposed | ||
* to just being called like a normal function. | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* function Cat () { | ||
* utils.classCallCheck(this, Cat) | ||
* } | ||
* const cat = new Cat() // this is ok | ||
* Cat() // this throws an error | ||
* | ||
* @method utils.classCallCheck | ||
* @param {*} instance Instance that is being constructed. | ||
* @param {Constructor} ctor Constructor function used to construct the | ||
* instance. | ||
* @since 3.0.0 | ||
* @throws {Error} Throws an error if the constructor is being improperly | ||
* invoked. | ||
*/ | ||
@@ -221,5 +270,19 @@ classCallCheck (instance, ctor) { | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* const a = { foo: { bar: 'baz' } } | ||
* const b = utils.copy(a) | ||
* a === b // false | ||
* utils.areDifferent(a, b) // false | ||
* | ||
* @param {*} from Value to deep copy. | ||
* @param {*} [to] Destination object for the copy operation. | ||
* @param {*} [stackFrom] For internal use. | ||
* @param {*} [stackTo] For internal use. | ||
* @param {string[]|RegExp[]} [blacklist] List of strings or RegExp of | ||
* properties to skip. | ||
* @param {boolean} [plain] Whether to make a plain copy (don't try to use | ||
* original prototype). | ||
* @returns {*} Deep copy of `from`. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -302,7 +365,18 @@ copy (from, to, stackFrom, stackTo, blacklist, plain) { | ||
/** | ||
* Recursively shallow fill in own enumberable properties from `source` to `dest`. | ||
* Recursively shallow fill in own enumerable properties from `source` to | ||
* `dest`. | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* const a = { foo: { bar: 'baz' }, beep: 'boop' } | ||
* const b = { beep: 'bip' } | ||
* utils.deepFillIn(b, a) | ||
* console.log(b) // {"foo":{"bar":"baz"},"beep":"bip"} | ||
* | ||
* @method utils.deepFillIn | ||
* @param {Object} dest The destination object. | ||
* @param {Object} source The source object. | ||
* @see utils.fillIn | ||
* @see utils.deepMixIn | ||
* @since 3.0.0 | ||
*/ | ||
@@ -324,7 +398,17 @@ deepFillIn (dest, source) { | ||
/** | ||
* Recursively shallow copy own enumberable properties from `source` to `dest`. | ||
* Recursively shallow copy own enumerable properties from `source` to `dest`. | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* const a = { foo: { bar: 'baz' }, beep: 'boop' } | ||
* const b = { beep: 'bip' } | ||
* utils.deepFillIn(b, a) | ||
* console.log(b) // {"foo":{"bar":"baz"},"beep":"boop"} | ||
* | ||
* @method utils.deepMixIn | ||
* @param {Object} dest The destination object. | ||
* @param {Object} source The source object. | ||
* @see utils.fillIn | ||
* @see utils.deepFillIn | ||
* @since 3.0.0 | ||
*/ | ||
@@ -346,10 +430,27 @@ deepMixIn (dest, source) { | ||
/** | ||
* @param {Object} a Base object. | ||
* @param {Object} b Comparison object. | ||
* @returns {Object} Diff. | ||
* Return a diff of the base object to the comparison object. | ||
* | ||
* @example | ||
* import {utils} from 'js-data' | ||
* const oldObject = { foo: 'bar', a: 1234 } | ||
* const newObject = { beep: 'boop', a: 5678 } | ||
* const diff = utils.diffObjects(oldObject, newObject) | ||
* console.log(diff.added) // {"beep":"boop"} | ||
* console.log(diff.changed) // {"a":5678} | ||
* console.log(diff.removed) // {"foo":undefined} | ||
* | ||
* @method utils.diffObjects | ||
* @param {Object} newObject Comparison object. | ||
* @param {Object} oldObject Base object. | ||
* @param {Object} [opts] Configuration options. | ||
* @param {Function} [opts.equalsFn={@link utils.deepEqual}] Equality function. | ||
* @param {Array} [opts.ignore=[]] Array of strings or RegExp of fields to ignore. | ||
* @returns {Object} The diff from the base object to the comparison object. | ||
* @see utils.areDifferent | ||
* @since 3.0.0 | ||
*/ | ||
diffObjects (a, b, opts) { | ||
diffObjects (newObject, oldObject, opts) { | ||
opts || (opts = {}) | ||
let equalsFn = opts.equalsFn | ||
let bl = opts.ignore | ||
let blacklist = opts.ignore | ||
const diff = { | ||
@@ -364,12 +465,19 @@ added: {}, | ||
utils.forOwn(b, function (oldValue, key) { | ||
const newValue = a[key] | ||
const newKeys = Object.keys(newObject).filter(function (key) { | ||
return !utils.isBlacklisted(key, blacklist) | ||
}) | ||
const oldKeys = Object.keys(oldObject).filter(function (key) { | ||
return !utils.isBlacklisted(key, blacklist) | ||
}) | ||
if (utils.isBlacklisted(key, bl) || equalsFn(newValue, oldValue)) { | ||
// Check for properties that were added or changed | ||
newKeys.forEach(function (key) { | ||
const oldValue = oldObject[key] | ||
const newValue = newObject[key] | ||
if (equalsFn(oldValue, newValue)) { | ||
return | ||
} | ||
if (utils.isUndefined(newValue)) { | ||
diff.removed[key] = undefined | ||
} else if (!equalsFn(newValue, oldValue)) { | ||
if (utils.isUndefined(oldValue)) { | ||
diff.added[key] = newValue | ||
} else { | ||
diff.changed[key] = newValue | ||
@@ -379,7 +487,9 @@ } | ||
utils.forOwn(a, function (newValue, key) { | ||
if (!utils.isUndefined(b[key]) || utils.isBlacklisted(key, bl)) { | ||
return | ||
// Check for properties that were removed | ||
oldKeys.forEach(function (key) { | ||
const oldValue = oldObject[key] | ||
const newValue = newObject[key] | ||
if (utils.isUndefined(newValue) && !utils.isUndefined(oldValue)) { | ||
diff.removed[key] = undefined | ||
} | ||
diff.added[key] = newValue | ||
}) | ||
@@ -391,3 +501,9 @@ | ||
/** | ||
* TODO | ||
* Return whether the two values are equal according to the `==` operator. | ||
* | ||
* @method utils.equal | ||
* @param {*} a First value in the comparison. | ||
* @param {*} b Second value in the comparison. | ||
* @returns {boolean} Whether the two values are equal according to `==`. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -399,3 +515,10 @@ equal (a, b) { | ||
/** | ||
* TODO | ||
* Produce a factory function for making Error objects with the provided | ||
* metadata. Used throughout the various js-data components. | ||
* | ||
* @method utils.err | ||
* @param {string} domain Namespace. | ||
* @param {string} target Target. | ||
* @returns {Function} Factory function. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -414,3 +537,3 @@ err (domain, target) { | ||
* | ||
* @ignore | ||
* @method utils.eventify | ||
* @param {Object} target Target object. | ||
@@ -421,2 +544,3 @@ * @param {Function} [getter] Custom getter for retrieving the object's event | ||
* listeners. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -466,3 +590,3 @@ eventify (target, getter, setter) { | ||
on: { | ||
value (type, func, ctx) { | ||
value (type, func, thisArg) { | ||
if (!getter.call(this)) { | ||
@@ -474,3 +598,3 @@ setter.call(this, {}) | ||
events[type].push({ | ||
c: ctx, | ||
c: thisArg, | ||
f: func | ||
@@ -484,5 +608,26 @@ }) | ||
/** | ||
* TODO | ||
* Used for sublcassing. Invoke this method in the context of a superclass to | ||
* to produce a subclass based on `props` and `classProps`. | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* function Animal () {} | ||
* Animal.extend = utils.extend | ||
* const Cat = Animal.extend({ | ||
* say () { | ||
* console.log('meow') | ||
* } | ||
* }) | ||
* const cat = new Cat() | ||
* cat instanceof Animal // true | ||
* cat instanceof Cat // true | ||
* cat.say() // "meow" | ||
* | ||
* @method utils.extend | ||
* @param {Object} props Instance properties for the subclass. | ||
* @param {Object} [props.constructor] Provide a custom constructor function | ||
* to use as the subclass. | ||
* @param {Object} props Static properties for the subclass. | ||
* @returns {Constructor} A new subclass. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -541,8 +686,18 @@ extend (props, classProps) { | ||
/** | ||
* Shallow copy own enumerable properties from `src` to `dest` that are on `src` | ||
* but are missing from `dest. | ||
* Shallow copy own enumerable properties from `src` to `dest` that are on | ||
* `src` but are missing from `dest. | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* const a = { foo: 'bar', beep: 'boop' } | ||
* const b = { beep: 'bip' } | ||
* utils.fillIn(b, a) | ||
* console.log(b) // {"foo":"bar","beep":"bip"} | ||
* | ||
* @method utils.fillIn | ||
* @param {Object} dest The destination object. | ||
* @param {Object} source The source object. | ||
* @see utils.deepFillIn | ||
* @see utils.deepMixIn | ||
* @since 3.0.0 | ||
*/ | ||
@@ -555,3 +710,2 @@ fillIn (dest, src) { | ||
}) | ||
return dest | ||
}, | ||
@@ -562,6 +716,7 @@ | ||
* | ||
* @ignore | ||
* @method utils.findIndex | ||
* @param {Array} array The array to search. | ||
* @param {Function} fn Checker function. | ||
* @param {number} Index if found or -1 if not found. | ||
* @returns {number} Index if found or -1 if not found. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -583,7 +738,13 @@ findIndex (array, fn) { | ||
/** | ||
* TODO | ||
* Recursively iterate over a {@link Mapper}'s relations according to | ||
* `opts.with`. | ||
* | ||
* @ignore | ||
* @method utils.forEachRelation | ||
* @param {Mapper} mapper Mapper. | ||
* @param {Object} opts Configuration options. | ||
* @param {Function} fn Callback function. | ||
* @param {*} thisArg Execution context for the callback function. | ||
* @since 3.0.0 | ||
*/ | ||
forEachRelation (mapper, opts, fn, ctx) { | ||
forEachRelation (mapper, opts, fn, thisArg) { | ||
const relationList = mapper.relationList || [] | ||
@@ -594,3 +755,3 @@ if (!relationList.length) { | ||
relationList.forEach(function (def) { | ||
utils._forRelation(opts, def, fn, ctx) | ||
utils._forRelation(opts, def, fn, thisArg) | ||
}) | ||
@@ -602,6 +763,16 @@ }, | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* const a = { b: 1, c: 4 } | ||
* let sum = 0 | ||
* utils.forOwn(a, function (value, key) { | ||
* sum += value | ||
* }) | ||
* console.log(sum) // 5 | ||
* | ||
* @method utils.forOwn | ||
* @param {Object} object The object whose properties are to be enumerated. | ||
* @param {Function} fn Iteration function. | ||
* @param {Object} [thisArg] Content to which to bind `fn`. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -620,5 +791,7 @@ forOwn (obj, fn, thisArg) { | ||
* | ||
* @ignore | ||
* @method utils.fromJson | ||
* @param {string} json JSON to parse. | ||
* @returns {Object} Parsed object. | ||
* @see utils.toJson | ||
* @since 3.0.0 | ||
*/ | ||
@@ -630,5 +803,17 @@ fromJson (json) { | ||
/** | ||
* TODO | ||
* Retrieve the specified property from the given object. Supports retrieving | ||
* nested properties. | ||
* | ||
* @ignore | ||
* @example | ||
* import {utils} from 'js-data' | ||
* const a = { foo: { bar: 'baz' }, beep: 'boop' } | ||
* console.log(utils.get(a, 'beep')) // "boop" | ||
* console.log(utils.get(a, 'foo.bar')) // "bar" | ||
* | ||
* @method utils.get | ||
* @param {Object} object Object from which to retrieve a property's value. | ||
* @param {string} prop Property to retrieve. | ||
* @returns {*} Value of the specified property. | ||
* @see utils.set | ||
* @since 3.0.0 | ||
*/ | ||
@@ -653,5 +838,10 @@ 'get': function (object, prop) { | ||
/** | ||
* TODO | ||
* Return the superclass for the given instance or subclass. If an instance is | ||
* provided, then finds the parent class of the instance's constructor. | ||
* | ||
* @ignore | ||
* @method utils.getSuper | ||
* @param {Object|Function} instance Instance or constructor. | ||
* @param {boolean} [isCtor=false] Whether `instance` is a constructor. | ||
* @returns {Constructor} The superclass (grandparent constructor). | ||
* @since 3.0.0 | ||
*/ | ||
@@ -669,6 +859,7 @@ getSuper (instance, isCtor) { | ||
* | ||
* @ignore | ||
* @method utils.intersection | ||
* @param {Array} array1 First array. | ||
* @param {Array} array2 Second array. | ||
* @returns {Array} Array of elements common to both arrays. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -696,5 +887,8 @@ intersection (array1, array2) { | ||
/** | ||
* TODO | ||
* Proxy for `Array.isArray`. | ||
* | ||
* @ignore | ||
* @method utils.isArray | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is an array. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -704,16 +898,18 @@ isArray: Array.isArray, | ||
/** | ||
* Return whether `prop` is matched by any string or regular expression in `bl`. | ||
* Return whether `prop` is matched by any string or regular expression in | ||
* `blacklist`. | ||
* | ||
* @ignore | ||
* @param {string} prop The name of a property. | ||
* @param {Array} bl Array of strings and regular expressions. | ||
* @method utils.isBlacklisted | ||
* @param {string} prop The name of a property to check. | ||
* @param {Array} blacklist Array of strings and regular expressions. | ||
* @returns {boolean} Whether `prop` was matched. | ||
* @since 3.0.0 | ||
*/ | ||
isBlacklisted (prop, bl) { | ||
if (!bl || !bl.length) { | ||
isBlacklisted (prop, blacklist) { | ||
if (!blacklist || !blacklist.length) { | ||
return false | ||
} | ||
let matches | ||
for (var i = 0; i < bl.length; i++) { | ||
if ((toStr(bl[i]) === REGEXP_TAG && bl[i].test(prop)) || bl[i] === prop) { | ||
for (var i = 0; i < blacklist.length; i++) { | ||
if ((toStr(blacklist[i]) === REGEXP_TAG && blacklist[i].test(prop)) || blacklist[i] === prop) { | ||
matches = prop | ||
@@ -727,5 +923,8 @@ return matches | ||
/** | ||
* TODO | ||
* Return whether the provided value is a boolean. | ||
* | ||
* @ignore | ||
* @method utils.isBoolean | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is a boolean. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -737,13 +936,9 @@ isBoolean (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is a date. | ||
* | ||
* @ignore | ||
* @method utils.isDate | ||
* @param {*} value The value to test. | ||
* @returns {Date} Whether the provided value is a date. | ||
* @since 3.0.0 | ||
*/ | ||
isBrowser: false, | ||
/** | ||
* TODO | ||
* | ||
* @ignore | ||
*/ | ||
isDate (value) { | ||
@@ -754,5 +949,8 @@ return (value && typeof value === 'object' && toStr(value) === DATE_TAG) | ||
/** | ||
* TODO | ||
* Return whether the provided value is a function. | ||
* | ||
* @ignore | ||
* @method utils.isFunction | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is a function. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -764,5 +962,8 @@ isFunction (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is an integer. | ||
* | ||
* @ignore | ||
* @method utils.isInteger | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is an integer. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -774,5 +975,8 @@ isInteger (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is `null`. | ||
* | ||
* @ignore | ||
* @method utils.isNull | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is `null`. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -784,5 +988,8 @@ isNull (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is a number. | ||
* | ||
* @ignore | ||
* @method utils.isNumber | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is a number. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -795,5 +1002,8 @@ isNumber (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is an object. | ||
* | ||
* @ignore | ||
* @method utils.isObject | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is an object. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -805,5 +1015,8 @@ isObject (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is a regular expression. | ||
* | ||
* @ignore | ||
* @method utils.isRegExp | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is a regular expression. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -815,5 +1028,8 @@ isRegExp (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is a string or a number. | ||
* | ||
* @ignore | ||
* @method utils.isSorN | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is a string or a number. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -825,5 +1041,8 @@ isSorN (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is a string. | ||
* | ||
* @ignore | ||
* @method utils.isString | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is a string. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -835,5 +1054,8 @@ isString (value) { | ||
/** | ||
* TODO | ||
* Return whether the provided value is a `undefined`. | ||
* | ||
* @ignore | ||
* @method utils.isUndefined | ||
* @param {*} value The value to test. | ||
* @returns {boolean} Whether the provided value is a `undefined`. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -845,5 +1067,7 @@ isUndefined (value) { | ||
/** | ||
* TODO | ||
* Mix in logging capabilities to the target. | ||
* | ||
* @ignore | ||
* @method utils.logify | ||
* @param {*} target The target. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -874,5 +1098,10 @@ logify (target) { | ||
/** | ||
* TODO | ||
* Adds the given record to the provided array only if it's not already in the | ||
* array. | ||
* | ||
* @ignore | ||
* @method utils.noDupeAdd | ||
* @param {Array} array The array. | ||
* @param {*} record The value to add. | ||
* @param {Function} fn Callback function passed to {@link utils.findIndex}. | ||
* @since 3.0.0 | ||
*/ | ||
@@ -890,8 +1119,12 @@ noDupeAdd (array, record, fn) { | ||
/** | ||
* TODO | ||
* Return a shallow copy of the provided object, minus the properties | ||
* specified in `keys`. | ||
* | ||
* @ignore | ||
* @method utils.omit | ||
* @param {Object} props The object to copy. | ||
* @param {string[]} keys Array of strings, representing properties to skip. | ||
* @returns {Object} Shallow copy of `props`, minus `keys`. | ||
* @since 3.0.0 | ||
*/ | ||
omit (props, keys) { | ||
// Remove relations | ||
const _props = {} | ||
@@ -906,2 +1139,12 @@ utils.forOwn(props, function (value, key) { | ||
/** | ||
* Return a shallow copy of the provided object, but only include the | ||
* properties specified in `keys`. | ||
* | ||
* @method utils.pick | ||
* @param {Object} props The object to copy. | ||
* @param {string[]} keys Array of strings, representing properties to keep. | ||
* @returns {Object} Shallow copy of `props`, but only including `keys`. | ||
* @since 3.0.0 | ||
*/ | ||
pick (props, keys) { | ||
@@ -918,16 +1161,22 @@ const _props = {} | ||
/** | ||
* TODO | ||
* Return a plain copy of the given value. | ||
* | ||
* @ignore | ||
* @method utils.plainCopy | ||
* @param {*} value The value to copy. | ||
* @returns {*} Plain copy of `value`. | ||
* @see utils.copy | ||
* @since 3.0.0 | ||
*/ | ||
plainCopy (from) { | ||
return utils.copy(from, undefined, undefined, undefined, undefined, true) | ||
plainCopy (value) { | ||
return utils.copy(value, undefined, undefined, undefined, undefined, true) | ||
}, | ||
/** | ||
* Proxy for `Promise.reject`. | ||
* Shortcut for `utils.Promise.reject(value)`. | ||
* | ||
* @ignore | ||
* @method utils.reject | ||
* @param {*} [value] Value with which to reject the Promise. | ||
* @returns {Promise} Promise reject with `value`. | ||
* @see utils.Promise | ||
* @since 3.0.0 | ||
*/ | ||
@@ -941,3 +1190,3 @@ reject (value) { | ||
* | ||
* @ignore | ||
* @method utils.remove | ||
* @param {Array} array The array to search. | ||
@@ -957,3 +1206,3 @@ * @param {Function} fn Checker function. | ||
/** | ||
* Proxy for `Promise.resolve`. | ||
* Shortcut for `utils.Promise.resolve(value)`. | ||
* | ||
@@ -963,2 +1212,4 @@ * @ignore | ||
* @returns {Promise} Promise resolved with `value`. | ||
* @see utils.Promise | ||
* @since 3.0.0 | ||
*/ | ||
@@ -972,3 +1223,3 @@ resolve (value) { | ||
* | ||
* @ignore | ||
* @method utils.set | ||
* @param {Object} object The object on which to set a property. | ||
@@ -996,5 +1247,10 @@ * @param {(string|Object)} path The key or path to the property. Can also | ||
/** | ||
* TODO | ||
* Check whether the two provided objects are deeply equal. | ||
* | ||
* @ignore | ||
* @method utils.deepEqual | ||
* @param {Object} a First object in the comparison. | ||
* @param {Object} b Second object in the comparison. | ||
* @returns {boolean} Whether the two provided objects are deeply equal. | ||
* @see utils.equal | ||
* @since 3.0.0 | ||
*/ | ||
@@ -1032,5 +1288,7 @@ deepEqual (a, b) { | ||
* | ||
* @ignore | ||
* @method utils.toJson | ||
* @param {*} value Value to serialize to JSON. | ||
* @returns {string} JSON string. | ||
* @see utils.fromJson | ||
* @since 3.0.0 | ||
*/ | ||
@@ -1042,5 +1300,7 @@ toJson: JSON.stringify, | ||
* | ||
* @ignore | ||
* @method utils.unset | ||
* @param {Object} object The object from which to delete the property. | ||
* @param {string} path The key or path to the property. | ||
* @see utils.set | ||
* @since 3.0.0 | ||
*/ | ||
@@ -1062,9 +1322,2 @@ unset (object, path) { | ||
// Attempt to detect whether we are in the browser. | ||
try { | ||
utils.isBrowser = !!window | ||
} catch (e) { | ||
utils.isBrowser = false | ||
} | ||
export default utils |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
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
1989247
14
34
26665