redux-orm
Advanced tools
Comparing version 0.9.0-rc.0 to 0.9.0-rc.1
@@ -23,6 +23,2 @@ 'use strict'; | ||
var _createClass2 = require('babel-runtime/helpers/createClass'); | ||
var _createClass3 = _interopRequireDefault(_createClass2); | ||
var _reject = require('lodash/reject'); | ||
@@ -115,157 +111,136 @@ | ||
(0, _createClass3.default)(Table, [{ | ||
key: 'accessId', | ||
value: function accessId(branch, id) { | ||
return branch[this.mapName][id]; | ||
} | ||
}, { | ||
key: 'idExists', | ||
value: function idExists(branch, id) { | ||
return branch[this.mapName].hasOwnProperty(id); | ||
} | ||
}, { | ||
key: 'accessIdList', | ||
value: function accessIdList(branch) { | ||
return branch[this.arrName]; | ||
} | ||
}, { | ||
key: 'accessList', | ||
value: function accessList(branch) { | ||
var _this = this; | ||
Table.prototype.accessId = function accessId(branch, id) { | ||
return branch[this.mapName][id]; | ||
}; | ||
return branch[this.arrName].map(function (id) { | ||
return _this.accessId(branch, id); | ||
}); | ||
} | ||
}, { | ||
key: 'getMaxId', | ||
value: function getMaxId(branch) { | ||
return this.getMeta(branch, 'maxId'); | ||
} | ||
}, { | ||
key: 'setMaxId', | ||
value: function setMaxId(tx, branch, newMaxId) { | ||
return this.setMeta(tx, branch, 'maxId', newMaxId); | ||
} | ||
}, { | ||
key: 'nextId', | ||
value: function nextId(id) { | ||
return id + 1; | ||
} | ||
}, { | ||
key: 'query', | ||
value: function query(branch, clauses) { | ||
var _this2 = this; | ||
Table.prototype.idExists = function idExists(branch, id) { | ||
return branch[this.mapName].hasOwnProperty(id); | ||
}; | ||
return clauses.reduce(function (rows, _ref) { | ||
var type = _ref.type, | ||
payload = _ref.payload; | ||
Table.prototype.accessIdList = function accessIdList(branch) { | ||
return branch[this.arrName]; | ||
}; | ||
switch (type) { | ||
case _constants.FILTER: | ||
{ | ||
if (payload.hasOwnProperty(_this2.idAttribute) && payload[_this2.idAttribute]) { | ||
// Payload specified a primary key; Since that is unique, we can directly | ||
// return that. | ||
var id = payload[_this2.idAttribute]; | ||
return _this2.idExists(branch, id) ? [_this2.accessId(branch, payload[_this2.idAttribute])] : []; | ||
} | ||
return (0, _filter2.default)(rows, payload); | ||
} | ||
case _constants.EXCLUDE: | ||
{ | ||
return (0, _reject2.default)(rows, payload); | ||
} | ||
case _constants.ORDER_BY: | ||
{ | ||
var _payload = (0, _slicedToArray3.default)(payload, 2), | ||
iteratees = _payload[0], | ||
orders = _payload[1]; | ||
Table.prototype.accessList = function accessList(branch) { | ||
var _this = this; | ||
return (0, _orderBy2.default)(rows, iteratees, orders); | ||
} | ||
default: | ||
return rows; | ||
} | ||
}, this.accessList(branch)); | ||
} | ||
return branch[this.arrName].map(function (id) { | ||
return _this.accessId(branch, id); | ||
}); | ||
}; | ||
/** | ||
* Returns the default state for the data structure. | ||
* @return {Object} The default state for this {@link Backend} instance's data structure | ||
*/ | ||
Table.prototype.getMaxId = function getMaxId(branch) { | ||
return this.getMeta(branch, 'maxId'); | ||
}; | ||
}, { | ||
key: 'getEmptyState', | ||
value: function getEmptyState() { | ||
var _ref2; | ||
Table.prototype.setMaxId = function setMaxId(tx, branch, newMaxId) { | ||
return this.setMeta(tx, branch, 'maxId', newMaxId); | ||
}; | ||
return _ref2 = {}, (0, _defineProperty3.default)(_ref2, this.arrName, []), (0, _defineProperty3.default)(_ref2, this.mapName, {}), (0, _defineProperty3.default)(_ref2, 'meta', {}), _ref2; | ||
} | ||
}, { | ||
key: 'setMeta', | ||
value: function setMeta(tx, branch, key, value) { | ||
var batchToken = tx.batchToken, | ||
withMutations = tx.withMutations; | ||
Table.prototype.nextId = function nextId(id) { | ||
return id + 1; | ||
}; | ||
if (withMutations) { | ||
var res = _immutableOps2.default.mutable.setIn(['meta', key], value, branch); | ||
return res; | ||
Table.prototype.query = function query(branch, clauses) { | ||
var _this2 = this; | ||
return clauses.reduce(function (rows, _ref) { | ||
var type = _ref.type, | ||
payload = _ref.payload; | ||
switch (type) { | ||
case _constants.FILTER: | ||
{ | ||
if (payload.hasOwnProperty(_this2.idAttribute) && payload[_this2.idAttribute]) { | ||
// Payload specified a primary key; Since that is unique, we can directly | ||
// return that. | ||
var id = payload[_this2.idAttribute]; | ||
return _this2.idExists(branch, id) ? [_this2.accessId(branch, payload[_this2.idAttribute])] : []; | ||
} | ||
return (0, _filter2.default)(rows, payload); | ||
} | ||
case _constants.EXCLUDE: | ||
{ | ||
return (0, _reject2.default)(rows, payload); | ||
} | ||
case _constants.ORDER_BY: | ||
{ | ||
var _payload = (0, _slicedToArray3.default)(payload, 2), | ||
iteratees = _payload[0], | ||
orders = _payload[1]; | ||
return (0, _orderBy2.default)(rows, iteratees, orders); | ||
} | ||
default: | ||
return rows; | ||
} | ||
}, this.accessList(branch)); | ||
}; | ||
return _immutableOps2.default.batch.setIn(batchToken, ['meta', key], value, branch); | ||
/** | ||
* Returns the default state for the data structure. | ||
* @return {Object} The default state for this {@link Backend} instance's data structure | ||
*/ | ||
Table.prototype.getEmptyState = function getEmptyState() { | ||
var _ref2; | ||
return _ref2 = {}, (0, _defineProperty3.default)(_ref2, this.arrName, []), (0, _defineProperty3.default)(_ref2, this.mapName, {}), (0, _defineProperty3.default)(_ref2, 'meta', {}), _ref2; | ||
}; | ||
Table.prototype.setMeta = function setMeta(tx, branch, key, value) { | ||
var batchToken = tx.batchToken, | ||
withMutations = tx.withMutations; | ||
if (withMutations) { | ||
var res = _immutableOps2.default.mutable.setIn(['meta', key], value, branch); | ||
return res; | ||
} | ||
}, { | ||
key: 'getMeta', | ||
value: function getMeta(branch, key) { | ||
return branch.meta[key]; | ||
} | ||
/** | ||
* Returns the data structure including a new object `entry` | ||
* @param {Object} tx - transaction info | ||
* @param {Object} branch - the data structure state | ||
* @param {Object} entry - the object to insert | ||
* @return {Object} an object with two keys: `state` and `created`. | ||
* `state` is the new table state and `created` is the | ||
* row that was created. | ||
*/ | ||
return _immutableOps2.default.batch.setIn(batchToken, ['meta', key], value, branch); | ||
}; | ||
}, { | ||
key: 'insert', | ||
value: function insert(tx, branch, entry) { | ||
var _ops$batch$merge2; | ||
Table.prototype.getMeta = function getMeta(branch, key) { | ||
return branch.meta[key]; | ||
}; | ||
var batchToken = tx.batchToken, | ||
withMutations = tx.withMutations; | ||
/** | ||
* Returns the data structure including a new object `entry` | ||
* @param {Object} tx - transaction info | ||
* @param {Object} branch - the data structure state | ||
* @param {Object} entry - the object to insert | ||
* @return {Object} an object with two keys: `state` and `created`. | ||
* `state` is the new table state and `created` is the | ||
* row that was created. | ||
*/ | ||
var hasId = entry.hasOwnProperty(this.idAttribute); | ||
Table.prototype.insert = function insert(tx, branch, entry) { | ||
var _ops$batch$merge2; | ||
var workingState = branch; | ||
var batchToken = tx.batchToken, | ||
withMutations = tx.withMutations; | ||
// This will not affect string id's. | ||
var _idSequencer = idSequencer(this.getMaxId(branch), entry[this.idAttribute]), | ||
_idSequencer2 = (0, _slicedToArray3.default)(_idSequencer, 2), | ||
newMaxId = _idSequencer2[0], | ||
id = _idSequencer2[1]; | ||
var hasId = entry.hasOwnProperty(this.idAttribute); | ||
workingState = this.setMaxId(tx, branch, newMaxId); | ||
var workingState = branch; | ||
var finalEntry = hasId ? entry : _immutableOps2.default.batch.set(batchToken, this.idAttribute, id, entry); | ||
// This will not affect string id's. | ||
if (withMutations) { | ||
_immutableOps2.default.mutable.push(id, workingState[this.arrName]); | ||
_immutableOps2.default.mutable.set(id, finalEntry, workingState[this.mapName]); | ||
return { | ||
state: workingState, | ||
created: finalEntry | ||
}; | ||
} | ||
var _idSequencer = idSequencer(this.getMaxId(branch), entry[this.idAttribute]), | ||
_idSequencer2 = (0, _slicedToArray3.default)(_idSequencer, 2), | ||
newMaxId = _idSequencer2[0], | ||
id = _idSequencer2[1]; | ||
var nextState = _immutableOps2.default.batch.merge(batchToken, (_ops$batch$merge2 = {}, (0, _defineProperty3.default)(_ops$batch$merge2, this.arrName, _immutableOps2.default.batch.push(batchToken, id, workingState[this.arrName])), (0, _defineProperty3.default)(_ops$batch$merge2, this.mapName, _immutableOps2.default.batch.merge(batchToken, (0, _defineProperty3.default)({}, id, finalEntry), workingState[this.mapName])), _ops$batch$merge2), workingState); | ||
workingState = this.setMaxId(tx, branch, newMaxId); | ||
var finalEntry = hasId ? entry : _immutableOps2.default.batch.set(batchToken, this.idAttribute, id, entry); | ||
if (withMutations) { | ||
_immutableOps2.default.mutable.push(id, workingState[this.arrName]); | ||
_immutableOps2.default.mutable.set(id, finalEntry, workingState[this.mapName]); | ||
return { | ||
state: nextState, | ||
state: workingState, | ||
created: finalEntry | ||
@@ -275,78 +250,84 @@ }; | ||
/** | ||
* Returns the data structure with objects where `rows` | ||
* are merged with `mergeObj`. | ||
* | ||
* @param {Object} tx - transaction info | ||
* @param {Object} branch - the data structure state | ||
* @param {Object[]} rows - rows to update | ||
* @param {Object} mergeObj - The object to merge with each row. | ||
* @return {Object} | ||
*/ | ||
var nextState = _immutableOps2.default.batch.merge(batchToken, (_ops$batch$merge2 = {}, (0, _defineProperty3.default)(_ops$batch$merge2, this.arrName, _immutableOps2.default.batch.push(batchToken, id, workingState[this.arrName])), (0, _defineProperty3.default)(_ops$batch$merge2, this.mapName, _immutableOps2.default.batch.merge(batchToken, (0, _defineProperty3.default)({}, id, finalEntry), workingState[this.mapName])), _ops$batch$merge2), workingState); | ||
}, { | ||
key: 'update', | ||
value: function update(tx, branch, rows, mergeObj) { | ||
var _this3 = this; | ||
return { | ||
state: nextState, | ||
created: finalEntry | ||
}; | ||
}; | ||
var batchToken = tx.batchToken, | ||
withMutations = tx.withMutations; | ||
var mapName = this.mapName; | ||
/** | ||
* Returns the data structure with objects where `rows` | ||
* are merged with `mergeObj`. | ||
* | ||
* @param {Object} tx - transaction info | ||
* @param {Object} branch - the data structure state | ||
* @param {Object[]} rows - rows to update | ||
* @param {Object} mergeObj - The object to merge with each row. | ||
* @return {Object} | ||
*/ | ||
var mapFunction = function mapFunction(row) { | ||
var merge = withMutations ? _immutableOps2.default.mutable.merge : _immutableOps2.default.batch.merge(batchToken); | ||
return merge(mergeObj, row); | ||
}; | ||
Table.prototype.update = function update(tx, branch, rows, mergeObj) { | ||
var _this3 = this; | ||
var set = withMutations ? _immutableOps2.default.mutable.set : _immutableOps2.default.batch.set(batchToken); | ||
var batchToken = tx.batchToken, | ||
withMutations = tx.withMutations; | ||
var mapName = this.mapName; | ||
var newMap = rows.reduce(function (map, row) { | ||
var result = mapFunction(row); | ||
return set(result[_this3.idAttribute], result, map); | ||
}, branch[mapName]); | ||
return _immutableOps2.default.batch.set(batchToken, mapName, newMap, branch); | ||
} | ||
/** | ||
* Returns the data structure without rows `rows`. | ||
* @param {Object} tx - transaction info | ||
* @param {Object} branch - the data structure state | ||
* @param {Object[]} rows - rows to update | ||
* @return {Object} the data structure without ids in `idsToDelete`. | ||
*/ | ||
var mapFunction = function mapFunction(row) { | ||
var merge = withMutations ? _immutableOps2.default.mutable.merge : _immutableOps2.default.batch.merge(batchToken); | ||
return merge(mergeObj, row); | ||
}; | ||
}, { | ||
key: 'delete', | ||
value: function _delete(tx, branch, rows) { | ||
var _this4 = this, | ||
_ops$batch$merge3; | ||
var set = withMutations ? _immutableOps2.default.mutable.set : _immutableOps2.default.batch.set(batchToken); | ||
var batchToken = tx.batchToken, | ||
withMutations = tx.withMutations; | ||
var arrName = this.arrName, | ||
mapName = this.mapName; | ||
var newMap = rows.reduce(function (map, row) { | ||
var result = mapFunction(row); | ||
return set(result[_this3.idAttribute], result, map); | ||
}, branch[mapName]); | ||
return _immutableOps2.default.batch.set(batchToken, mapName, newMap, branch); | ||
}; | ||
var arr = branch[arrName]; | ||
/** | ||
* Returns the data structure without rows `rows`. | ||
* @param {Object} tx - transaction info | ||
* @param {Object} branch - the data structure state | ||
* @param {Object[]} rows - rows to update | ||
* @return {Object} the data structure without ids in `idsToDelete`. | ||
*/ | ||
var idsToDelete = rows.map(function (row) { | ||
return row[_this4.idAttribute]; | ||
Table.prototype.delete = function _delete(tx, branch, rows) { | ||
var _this4 = this, | ||
_ops$batch$merge3; | ||
var batchToken = tx.batchToken, | ||
withMutations = tx.withMutations; | ||
var arrName = this.arrName, | ||
mapName = this.mapName; | ||
var arr = branch[arrName]; | ||
var idsToDelete = rows.map(function (row) { | ||
return row[_this4.idAttribute]; | ||
}); | ||
if (withMutations) { | ||
idsToDelete.forEach(function (id) { | ||
var idx = arr.indexOf(id); | ||
if (idx !== -1) { | ||
_immutableOps2.default.mutable.splice(idx, 1, [], arr); | ||
} | ||
_immutableOps2.default.mutable.omit(id, branch[mapName]); | ||
}); | ||
if (withMutations) { | ||
idsToDelete.forEach(function (id) { | ||
var idx = arr.indexOf(id); | ||
if (idx !== -1) { | ||
_immutableOps2.default.mutable.splice(idx, 1, [], arr); | ||
} | ||
return branch; | ||
} | ||
_immutableOps2.default.mutable.omit(id, branch[mapName]); | ||
}); | ||
return branch; | ||
} | ||
return _immutableOps2.default.batch.merge(batchToken, (_ops$batch$merge3 = {}, (0, _defineProperty3.default)(_ops$batch$merge3, arrName, _immutableOps2.default.batch.filter(batchToken, function (id) { | ||
return !(0, _utils.includes)(idsToDelete, id); | ||
}, branch[arrName])), (0, _defineProperty3.default)(_ops$batch$merge3, mapName, _immutableOps2.default.batch.omit(batchToken, idsToDelete, branch[mapName])), _ops$batch$merge3), branch); | ||
}; | ||
return _immutableOps2.default.batch.merge(batchToken, (_ops$batch$merge3 = {}, (0, _defineProperty3.default)(_ops$batch$merge3, arrName, _immutableOps2.default.batch.filter(batchToken, function (id) { | ||
return !(0, _utils.includes)(idsToDelete, id); | ||
}, branch[arrName])), (0, _defineProperty3.default)(_ops$batch$merge3, mapName, _immutableOps2.default.batch.omit(batchToken, idsToDelete, branch[mapName])), _ops$batch$merge3), branch); | ||
} | ||
}]); | ||
return Table; | ||
@@ -353,0 +334,0 @@ }(); |
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.manyToManyDescriptor = exports.backwardManyToOneDescriptor = exports.backwardOneToOneDescriptor = exports.forwardOneToOneDescriptor = exports.forwardManyToOneDescriptor = undefined; | ||
exports.manyToManyDescriptor = exports.backwardManyToOneDescriptor = exports.backwardOneToOneDescriptor = exports.forwardOneToOneDescriptor = exports.forwardManyToOneDescriptor = exports.attrDescriptor = undefined; | ||
@@ -21,2 +21,17 @@ var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); | ||
function attrDescriptor(fieldName) { | ||
return { | ||
get: function get() { | ||
return this._fields[fieldName]; | ||
}, | ||
set: function set(value) { | ||
return this.set(fieldName, value); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}; | ||
} | ||
// Forwards side a Foreign Key: returns one object. | ||
@@ -191,2 +206,3 @@ // Also works as forwardsOneToOneDescriptor. | ||
exports.attrDescriptor = attrDescriptor; | ||
exports.forwardManyToOneDescriptor = forwardManyToOneDescriptor; | ||
@@ -193,0 +209,0 @@ exports.forwardOneToOneDescriptor = forwardOneToOneDescriptor; |
@@ -12,10 +12,6 @@ 'use strict'; | ||
var _defineProperty = require('babel-runtime/core-js/object/define-property'); | ||
var _getOwnPropertyDescriptor = require('babel-runtime/core-js/object/get-own-property-descriptor'); | ||
var _defineProperty2 = _interopRequireDefault(_defineProperty); | ||
var _getOwnPropertyDescriptor2 = _interopRequireDefault(_getOwnPropertyDescriptor); | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); | ||
@@ -33,2 +29,6 @@ | ||
var _defineProperty = require('babel-runtime/core-js/object/define-property'); | ||
var _defineProperty2 = _interopRequireDefault(_defineProperty); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
@@ -38,6 +38,2 @@ | ||
var _createClass2 = require('babel-runtime/helpers/createClass'); | ||
var _createClass3 = _interopRequireDefault(_createClass2); | ||
exports.attr = attr; | ||
@@ -73,6 +69,6 @@ exports.fk = fk; | ||
(0, _createClass3.default)(Attribute, [{ | ||
key: 'install', | ||
value: function install() {} | ||
}]); | ||
Attribute.prototype.install = function install(model, fieldName, orm) { | ||
(0, _defineProperty2.default)(model.prototype, fieldName, (0, _descriptors.attrDescriptor)(fieldName)); | ||
}; | ||
return Attribute; | ||
@@ -96,8 +92,6 @@ }(); | ||
(0, _createClass3.default)(RelationalField, [{ | ||
key: 'getClass', | ||
value: function getClass() { | ||
return this.constructor; | ||
} | ||
}]); | ||
RelationalField.prototype.getClass = function getClass() { | ||
return this.constructor; | ||
}; | ||
return RelationalField; | ||
@@ -111,29 +105,28 @@ }(); | ||
(0, _classCallCheck3.default)(this, ForeignKey); | ||
return (0, _possibleConstructorReturn3.default)(this, (ForeignKey.__proto__ || (0, _getPrototypeOf2.default)(ForeignKey)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _RelationalField.apply(this, arguments)); | ||
} | ||
(0, _createClass3.default)(ForeignKey, [{ | ||
key: 'install', | ||
value: function install(model, fieldName, orm) { | ||
var toModelName = this.toModelName; | ||
var toModel = toModelName === 'this' ? model : orm.get(toModelName); | ||
ForeignKey.prototype.install = function install(model, fieldName, orm) { | ||
var toModelName = this.toModelName; | ||
var toModel = toModelName === 'this' ? model : orm.get(toModelName); | ||
// Forwards. | ||
(0, _defineProperty2.default)(model.prototype, fieldName, (0, _descriptors.forwardManyToOneDescriptor)(fieldName, toModel.modelName)); | ||
model.definedProperties[fieldName] = true; | ||
// Forwards. | ||
(0, _defineProperty2.default)(model.prototype, fieldName, (0, _descriptors.forwardManyToOneDescriptor)(fieldName, toModel.modelName)); | ||
// Backwards. | ||
var backwardsFieldName = this.relatedName ? this.relatedName : (0, _utils.reverseFieldName)(model.modelName); | ||
// Backwards. | ||
var backwardsFieldName = this.relatedName ? this.relatedName : (0, _utils.reverseFieldName)(model.modelName); | ||
if (toModel.definedProperties[backwardsFieldName]) { | ||
var errorMsg = (0, _utils.reverseFieldErrorMessage)(model.modelName, fieldName, toModel.modelName, backwardsFieldName); | ||
throw new Error(errorMsg); | ||
} | ||
var backwardsDescriptor = (0, _getOwnPropertyDescriptor2.default)(toModel.prototype, backwardsFieldName); | ||
(0, _defineProperty2.default)(toModel.prototype, backwardsFieldName, (0, _descriptors.backwardManyToOneDescriptor)(fieldName, model.modelName)); | ||
toModel.definedProperties[backwardsFieldName] = true; | ||
var ThisField = this.getClass(); | ||
toModel.virtualFields[backwardsFieldName] = new ThisField(model.modelName, fieldName); | ||
if (backwardsDescriptor) { | ||
var errorMsg = (0, _utils.reverseFieldErrorMessage)(model.modelName, fieldName, toModel.modelName, backwardsFieldName); | ||
throw new Error(errorMsg); | ||
} | ||
}]); | ||
(0, _defineProperty2.default)(toModel.prototype, backwardsFieldName, (0, _descriptors.backwardManyToOneDescriptor)(fieldName, model.modelName)); | ||
var ThisField = this.getClass(); | ||
toModel.virtualFields[backwardsFieldName] = new ThisField(model.modelName, fieldName); | ||
}; | ||
return ForeignKey; | ||
@@ -147,80 +140,78 @@ }(RelationalField); | ||
(0, _classCallCheck3.default)(this, ManyToMany); | ||
return (0, _possibleConstructorReturn3.default)(this, (ManyToMany.__proto__ || (0, _getPrototypeOf2.default)(ManyToMany)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _RelationalField2.apply(this, arguments)); | ||
} | ||
(0, _createClass3.default)(ManyToMany, [{ | ||
key: 'install', | ||
value: function install(model, fieldName, orm) { | ||
var toModelName = this.toModelName; | ||
var toModel = toModelName === 'this' ? model : orm.get(toModelName); | ||
ManyToMany.prototype.install = function install(model, fieldName, orm) { | ||
var toModelName = this.toModelName; | ||
var toModel = toModelName === 'this' ? model : orm.get(toModelName); | ||
// Forwards. | ||
// Forwards. | ||
var throughModelName = this.through || (0, _utils.m2mName)(model.modelName, fieldName); | ||
var throughModelName = this.through || (0, _utils.m2mName)(model.modelName, fieldName); | ||
var throughModel = orm.get(throughModelName); | ||
var throughModel = orm.get(throughModelName); | ||
var throughFields = void 0; | ||
if (!this.throughFields) { | ||
var toFieldName = (0, _findKey2.default)(throughModel.fields, function (field) { | ||
return field instanceof ForeignKey && field.toModelName === toModel.modelName; | ||
}); | ||
var fromFieldName = (0, _findKey2.default)(throughModel.fields, function (field) { | ||
return field instanceof ForeignKey && field.toModelName === model.modelName; | ||
}); | ||
var throughFields = void 0; | ||
if (!this.throughFields) { | ||
var toFieldName = (0, _findKey2.default)(throughModel.fields, function (field) { | ||
return field instanceof ForeignKey && field.toModelName === toModel.modelName; | ||
}); | ||
var fromFieldName = (0, _findKey2.default)(throughModel.fields, function (field) { | ||
return field instanceof ForeignKey && field.toModelName === model.modelName; | ||
}); | ||
throughFields = { | ||
to: toFieldName, | ||
from: fromFieldName | ||
}; | ||
} else { | ||
var _throughFields = throughFields, | ||
_throughFields2 = (0, _slicedToArray3.default)(_throughFields, 2), | ||
fieldAName = _throughFields2[0], | ||
fieldBName = _throughFields2[1]; | ||
var fieldA = throughModel.fields[fieldAName]; | ||
if (fieldA.toModelName === toModel.modelName) { | ||
throughFields = { | ||
to: toFieldName, | ||
from: fromFieldName | ||
to: fieldAName, | ||
from: fieldBName | ||
}; | ||
} else { | ||
var _throughFields = throughFields, | ||
_throughFields2 = (0, _slicedToArray3.default)(_throughFields, 2), | ||
fieldAName = _throughFields2[0], | ||
fieldBName = _throughFields2[1]; | ||
var fieldA = throughModel.fields[fieldAName]; | ||
if (fieldA.toModelName === toModel.modelName) { | ||
throughFields = { | ||
to: fieldAName, | ||
from: fieldBName | ||
}; | ||
} else { | ||
throughFields = { | ||
to: fieldBName, | ||
from: fieldAName | ||
}; | ||
} | ||
throughFields = { | ||
to: fieldBName, | ||
from: fieldAName | ||
}; | ||
} | ||
} | ||
(0, _defineProperty2.default)(model.prototype, fieldName, (0, _descriptors.manyToManyDescriptor)(model.modelName, toModel.modelName, throughModelName, throughFields, false)); | ||
model.definedProperties[fieldName] = true; | ||
model.virtualFields[fieldName] = new ManyToMany({ | ||
to: toModel.modelName, | ||
relatedName: fieldName, | ||
through: this.through | ||
}); | ||
(0, _defineProperty2.default)(model.prototype, fieldName, (0, _descriptors.manyToManyDescriptor)(model.modelName, toModel.modelName, throughModelName, throughFields, false)); | ||
// Backwards. | ||
var backwardsFieldName = this.relatedName ? this.relatedName : (0, _utils.reverseFieldName)(model.modelName); | ||
model.virtualFields[fieldName] = new ManyToMany({ | ||
to: toModel.modelName, | ||
relatedName: fieldName, | ||
through: this.through | ||
}); | ||
if (toModel.definedProperties[backwardsFieldName]) { | ||
// Backwards field was already defined on toModel. | ||
var errorMsg = (0, _utils.reverseFieldErrorMessage)(model.modelName, fieldName, toModel.modelName, backwardsFieldName); | ||
throw new Error(errorMsg); | ||
} | ||
// Backwards. | ||
var backwardsFieldName = this.relatedName ? this.relatedName : (0, _utils.reverseFieldName)(model.modelName); | ||
(0, _defineProperty2.default)(toModel.prototype, backwardsFieldName, (0, _descriptors.manyToManyDescriptor)(model.modelName, toModel.modelName, throughModelName, throughFields, true)); | ||
toModel.definedProperties[backwardsFieldName] = true; | ||
toModel.virtualFields[backwardsFieldName] = new ManyToMany({ | ||
to: model.modelName, | ||
relatedName: fieldName, | ||
through: throughModelName | ||
}); | ||
var backwardsDescriptor = (0, _getOwnPropertyDescriptor2.default)(toModel.prototype, backwardsFieldName); | ||
if (backwardsDescriptor) { | ||
// Backwards field was already defined on toModel. | ||
var errorMsg = (0, _utils.reverseFieldErrorMessage)(model.modelName, fieldName, toModel.modelName, backwardsFieldName); | ||
throw new Error(errorMsg); | ||
} | ||
}, { | ||
key: 'getDefault', | ||
value: function getDefault() { | ||
return []; | ||
} | ||
}]); | ||
(0, _defineProperty2.default)(toModel.prototype, backwardsFieldName, (0, _descriptors.manyToManyDescriptor)(model.modelName, toModel.modelName, throughModelName, throughFields, true)); | ||
toModel.virtualFields[backwardsFieldName] = new ManyToMany({ | ||
to: model.modelName, | ||
relatedName: fieldName, | ||
through: throughModelName | ||
}); | ||
}; | ||
ManyToMany.prototype.getDefault = function getDefault() { | ||
return []; | ||
}; | ||
return ManyToMany; | ||
@@ -234,28 +225,26 @@ }(RelationalField); | ||
(0, _classCallCheck3.default)(this, OneToOne); | ||
return (0, _possibleConstructorReturn3.default)(this, (OneToOne.__proto__ || (0, _getPrototypeOf2.default)(OneToOne)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _RelationalField3.apply(this, arguments)); | ||
} | ||
(0, _createClass3.default)(OneToOne, [{ | ||
key: 'install', | ||
value: function install(model, fieldName, orm) { | ||
var toModelName = this.toModelName; | ||
var toModel = toModelName === 'this' ? model : orm.get(toModelName); | ||
OneToOne.prototype.install = function install(model, fieldName, orm) { | ||
var toModelName = this.toModelName; | ||
var toModel = toModelName === 'this' ? model : orm.get(toModelName); | ||
// Forwards. | ||
(0, _defineProperty2.default)(model.prototype, fieldName, (0, _descriptors.forwardOneToOneDescriptor)(fieldName, toModel.modelName)); | ||
model.definedProperties[fieldName] = true; | ||
// Forwards. | ||
(0, _defineProperty2.default)(model.prototype, fieldName, (0, _descriptors.forwardOneToOneDescriptor)(fieldName, toModel.modelName)); | ||
// Backwards. | ||
var backwardsFieldName = this.relatedName ? this.relatedName : model.modelName.toLowerCase(); | ||
// Backwards. | ||
var backwardsFieldName = this.relatedName ? this.relatedName : model.modelName.toLowerCase(); | ||
if (toModel.definedProperties[backwardsFieldName]) { | ||
var errorMsg = (0, _utils.reverseFieldErrorMessage)(model.modelName, fieldName, toModel.modelName, backwardsFieldName); | ||
throw new Error(errorMsg); | ||
} | ||
var backwardsDescriptor = (0, _getOwnPropertyDescriptor2.default)(toModel.prototype, backwardsFieldName); | ||
(0, _defineProperty2.default)(toModel.prototype, backwardsFieldName, (0, _descriptors.backwardOneToOneDescriptor)(fieldName, model.modelName)); | ||
toModel.definedProperties[backwardsFieldName] = true; | ||
toModel.virtualFields[backwardsFieldName] = new OneToOne(model.modelName, fieldName); | ||
if (backwardsDescriptor) { | ||
var errorMsg = (0, _utils.reverseFieldErrorMessage)(model.modelName, fieldName, toModel.modelName, backwardsFieldName); | ||
throw new Error(errorMsg); | ||
} | ||
}]); | ||
(0, _defineProperty2.default)(toModel.prototype, backwardsFieldName, (0, _descriptors.backwardOneToOneDescriptor)(fieldName, model.modelName)); | ||
toModel.virtualFields[backwardsFieldName] = new OneToOne(model.modelName, fieldName); | ||
}; | ||
return OneToOne; | ||
@@ -266,3 +255,5 @@ }(RelationalField); | ||
* Defines a value attribute on the model. | ||
* You need to define this for each non-foreign key you wish to use. | ||
* Though not required, it is recommended to define this for each non-foreign key you wish to use. | ||
* Getters and setters need to be defined on each Model | ||
* instantiation for undeclared data fields, which is slower. | ||
* You can use the optional `getDefault` parameter to fill in unpassed values | ||
@@ -269,0 +260,0 @@ * to {@link Model#create}, such as for generating ID's with UUID: |
785
lib/Model.js
@@ -107,480 +107,452 @@ 'use strict'; | ||
(0, _createClass3.default)(Model, [{ | ||
key: '_initFields', | ||
value: function _initFields(props) { | ||
var _this = this; | ||
Model.prototype._initFields = function _initFields(props) { | ||
var _this = this; | ||
var ModelClass = this.getClass(); | ||
this._fields = (0, _assign2.default)({}, props); | ||
var fieldsDef = this.getClass().fields; | ||
this._fields = (0, _assign2.default)({}, props); | ||
var ThisModel = this.getClass(); | ||
(0, _forOwn2.default)(props, function (fieldValue, fieldName) { | ||
if (!fieldsDef.hasOwnProperty(fieldName)) { | ||
throw new Error('Unexpected field given to ' + ModelClass.modelName + ' constructor: ' + fieldName + '. ' + ('If ' + ModelClass.modelName + ' should accept this field, ') + 'add an attr() field to it.'); | ||
} | ||
(0, _forOwn2.default)(props, function (fieldValue, fieldName) { | ||
// In this case, we got a prop that wasn't defined as a field. | ||
// Assuming it's an arbitrary data field, making an instance-specific | ||
// descriptor for it. | ||
// Using the in operator as the property could be defined anywhere | ||
// on the prototype chain. | ||
if (!(fieldName in _this)) { | ||
(0, _defineProperty3.default)(_this, fieldName, { | ||
get: function get() { | ||
return _this._fields[fieldName]; | ||
}, | ||
set: function set(value) { | ||
return _this.set(fieldName, value); | ||
}, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
} | ||
}); | ||
}; | ||
_this._fields[fieldName] = fieldValue; | ||
Model.toString = function toString() { | ||
return 'ModelClass: ' + this.modelName; | ||
}; | ||
// If the field has not already been defined on the | ||
// prototype for a relation. | ||
if (!ModelClass.definedProperties[fieldName]) { | ||
(0, _defineProperty3.default)(_this, fieldName, { | ||
get: function get() { | ||
return _this._fields[fieldName]; | ||
}, | ||
set: function set(value) { | ||
return _this.set(fieldName, value); | ||
}, | ||
configurable: true | ||
}); | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'getClass', | ||
/** | ||
* Returns the options object passed to the database for the table that represents | ||
* this Model class. | ||
* | ||
* Returns an empty object by default, which means the database | ||
* will use default options. You can either override this function to return the options | ||
* you want to use, or assign the options object as a static property of the same name to the | ||
* Model class. | ||
* | ||
* @return {Object} the options object passed to the database for the table | ||
* representing this Model class. | ||
*/ | ||
/** | ||
* Gets the {@link Model} class or subclass constructor (the class that | ||
* instantiated this instance). | ||
* | ||
* @return {Model} The {@link Model} class or subclass constructor used to instantiate | ||
* this instance. | ||
*/ | ||
value: function getClass() { | ||
return this.constructor; | ||
Model.options = function options() { | ||
return {}; | ||
}; | ||
Model._getTableOpts = function _getTableOpts() { | ||
if (typeof this.options === 'function') { | ||
return this.options(); | ||
} | ||
return this.options; | ||
}; | ||
/** | ||
* Gets the id value of the current instance by looking up the id attribute. | ||
* @return {*} The id value of the current instance. | ||
*/ | ||
Model.markAccessed = function markAccessed() { | ||
this.session.markAccessed(this); | ||
}; | ||
}, { | ||
key: 'getId', | ||
value: function getId() { | ||
return this._fields[this.getClass().idAttribute]; | ||
} | ||
/** | ||
* Returns the id attribute of this {@link Model}. | ||
* | ||
* @return {string} The id attribute of this {@link Model}. | ||
*/ | ||
/** | ||
* Returns a reference to the plain JS object in the store. | ||
* Make sure to not mutate this. | ||
* | ||
* @return {Object} a reference to the plain JS object in the store | ||
*/ | ||
}, { | ||
key: 'toString', | ||
/** | ||
* Connect the model class to a {@link Session}. | ||
* | ||
* @private | ||
* @param {Session} session - The session to connect to. | ||
*/ | ||
Model.connect = function connect(session) { | ||
if (!session instanceof _Session2.default) { | ||
throw Error('A model can only connect to a Session instance.'); | ||
} | ||
this._session = session; | ||
}; | ||
/** | ||
* Get the current {@link Session} instance. | ||
* | ||
* @private | ||
* @return {Session} The current {@link Session} instance. | ||
*/ | ||
/** | ||
* Returns a string representation of the {@link Model} instance. | ||
* | ||
* @return {string} A string representation of this {@link Model} instance. | ||
*/ | ||
value: function toString() { | ||
var _this2 = this; | ||
var ThisModel = this.getClass(); | ||
var className = ThisModel.modelName; | ||
var fieldNames = (0, _keys2.default)(ThisModel.fields); | ||
var fields = fieldNames.map(function (fieldName) { | ||
var field = ThisModel.fields[fieldName]; | ||
if (field instanceof _fields.ManyToMany) { | ||
var ids = _this2[fieldName].toModelArray().map(function (model) { | ||
return model.getId(); | ||
}); | ||
return fieldName + ': [' + ids.join(', ') + ']'; | ||
} | ||
var val = _this2._fields[fieldName]; | ||
return fieldName + ': ' + val; | ||
}).join(', '); | ||
return className + ': {' + fields + '}'; | ||
} | ||
Model.getQuerySet = function getQuerySet() { | ||
var QuerySetClass = this.querySetClass; | ||
return new QuerySetClass(this); | ||
}; | ||
/** | ||
* Returns a boolean indicating if `otherModel` equals this {@link Model} instance. | ||
* Equality is determined by shallow comparing their attributes. | ||
* | ||
* @param {Model} otherModel - a {@link Model} instance to compare | ||
* @return {Boolean} a boolean indicating if the {@link Model} instance's are equal. | ||
*/ | ||
Model.invalidateClassCache = function invalidateClassCache() { | ||
this.isSetUp = undefined; | ||
this.virtualFields = {}; | ||
}; | ||
}, { | ||
key: 'equals', | ||
value: function equals(otherModel) { | ||
return (0, _utils.objectShallowEquals)(this._fields, otherModel._fields); | ||
} | ||
/** | ||
* Returns a {@link QuerySet} containing all {@link Model} instances. | ||
* @return {QuerySet} a QuerySet containing all {@link Model} instances | ||
*/ | ||
Model.all = function all() { | ||
return this.getQuerySet(); | ||
}; | ||
/** | ||
* Updates a property name to given value for this {@link Model} instance. | ||
* The values are immediately committed to the database. | ||
* | ||
* @param {string} propertyName - name of the property to set | ||
* @param {*} value - value assigned to the property | ||
* @return {undefined} | ||
*/ | ||
/** | ||
* Creates a new record in the database, instantiates a {@link Model} and returns it. | ||
* | ||
* If you pass values for many-to-many fields, instances are created on the through | ||
* model as well. | ||
* | ||
* @param {props} props - the new {@link Model}'s properties. | ||
* @return {Model} a new {@link Model} instance. | ||
*/ | ||
}, { | ||
key: 'set', | ||
value: function set(propertyName, value) { | ||
this.update((0, _defineProperty5.default)({}, propertyName, value)); | ||
} | ||
/** | ||
* Assigns multiple fields and corresponding values to this {@link Model} instance. | ||
* The updates are immediately committed to the database. | ||
* | ||
* @param {Object} userMergeObj - an object that will be merged with this instance. | ||
* @return {undefined} | ||
*/ | ||
Model.create = function create(userProps) { | ||
var _this2 = this; | ||
}, { | ||
key: 'update', | ||
value: function update(userMergeObj) { | ||
var ThisModel = this.getClass(); | ||
var relFields = ThisModel.fields; | ||
var mergeObj = (0, _assign2.default)({}, userMergeObj); | ||
var props = (0, _assign2.default)({}, userProps); | ||
// If an array of entities or id's is supplied for a | ||
// many-to-many related field, clear the old relations | ||
// and add the new ones. | ||
for (var mergeKey in mergeObj) { | ||
// eslint-disable-line no-restricted-syntax | ||
if (relFields.hasOwnProperty(mergeKey)) { | ||
var field = relFields[mergeKey]; | ||
if (field instanceof _fields.ManyToMany) { | ||
var currentIds = this[mergeKey].toRefArray().map(function (row) { | ||
return row[ThisModel.idAttribute]; | ||
}); | ||
var m2mVals = {}; | ||
var normalizedNewIds = mergeObj[mergeKey].map(_utils.normalizeEntity); | ||
var diffActions = (0, _utils.arrayDiffActions)(currentIds, normalizedNewIds); | ||
if (diffActions) { | ||
var idsToDelete = diffActions.delete; | ||
var idsToAdd = diffActions.add; | ||
if (idsToDelete.length > 0) { | ||
var _mergeKey; | ||
var declaredFieldNames = (0, _keys2.default)(this.fields); | ||
(_mergeKey = this[mergeKey]).remove.apply(_mergeKey, (0, _toConsumableArray3.default)(idsToDelete)); | ||
} | ||
if (idsToAdd.length > 0) { | ||
var _mergeKey2; | ||
declaredFieldNames.forEach(function (key) { | ||
var field = _this2.fields[key]; | ||
var valuePassed = userProps.hasOwnProperty(key); | ||
if (!valuePassed && !(field instanceof _fields.ManyToMany)) { | ||
if (field.getDefault) { | ||
props[key] = field.getDefault(); | ||
} | ||
} else { | ||
var value = userProps[key]; | ||
props[key] = (0, _utils.normalizeEntity)(value); | ||
(_mergeKey2 = this[mergeKey]).add.apply(_mergeKey2, (0, _toConsumableArray3.default)(idsToAdd)); | ||
} | ||
} | ||
delete mergeObj[mergeKey]; | ||
} else if (field instanceof _fields.ForeignKey || field instanceof _fields.OneToOne) { | ||
mergeObj[mergeKey] = (0, _utils.normalizeEntity)(mergeObj[mergeKey]); | ||
// If a value is supplied for a ManyToMany field, | ||
// discard them from props and save for later processing. | ||
if ((0, _isArray2.default)(value)) { | ||
if (_this2.fields.hasOwnProperty(key) && _this2.fields[key] instanceof _fields.ManyToMany) { | ||
m2mVals[key] = value; | ||
delete props[key]; | ||
} | ||
} | ||
} | ||
}); | ||
this._initFields((0, _assign2.default)({}, this._fields, mergeObj)); | ||
var newEntry = this.session.applyUpdate({ | ||
action: _constants.CREATE, | ||
table: this.modelName, | ||
payload: props | ||
}); | ||
ThisModel.session.applyUpdate({ | ||
action: _constants.UPDATE, | ||
query: getByIdQuery(this), | ||
payload: mergeObj | ||
}); | ||
} | ||
var ModelClass = this; | ||
var instance = new ModelClass(newEntry); | ||
/** | ||
* Updates {@link Model} instance attributes to reflect the | ||
* database state in the current session. | ||
* @return {undefined} | ||
*/ | ||
(0, _forOwn2.default)(m2mVals, function (value, key) { | ||
var _instance$key; | ||
}, { | ||
key: 'refreshFromState', | ||
value: function refreshFromState() { | ||
this._initFields(this.ref); | ||
} | ||
var ids = value.map(_utils.normalizeEntity); | ||
var uniqueIds = (0, _uniq2.default)(ids); | ||
/** | ||
* Deletes the record for this {@link Model} instance. | ||
* You'll still be able to access fields and values on the instance. | ||
* | ||
* @return {undefined} | ||
*/ | ||
}, { | ||
key: 'delete', | ||
value: function _delete() { | ||
this._onDelete(); | ||
this.getClass().session.applyUpdate({ | ||
action: _constants.DELETE, | ||
query: getByIdQuery(this) | ||
}); | ||
} | ||
}, { | ||
key: '_onDelete', | ||
value: function _onDelete() { | ||
var virtualFields = this.getClass().virtualFields; | ||
for (var key in virtualFields) { | ||
// eslint-disable-line | ||
var field = virtualFields[key]; | ||
if (field instanceof _fields.ManyToMany) { | ||
// Delete any many-to-many rows the entity is included in. | ||
this[key].clear(); | ||
} else if (field instanceof _fields.ForeignKey) { | ||
var relatedQs = this[key]; | ||
if (relatedQs.exists()) { | ||
relatedQs.update((0, _defineProperty5.default)({}, field.relatedName, null)); | ||
} | ||
} else if (field instanceof _fields.OneToOne) { | ||
// Set null to any foreign keys or one to ones pointed to | ||
// this instance. | ||
if (this[key] !== null) { | ||
this[key][field.relatedName] = null; | ||
} | ||
} | ||
if (ids.length !== uniqueIds.length) { | ||
var idsString = ids; | ||
throw new Error('Found duplicate id(s) when passing "' + idsString + '" to ' + _this2.modelName + '.' + key + ' value on create'); | ||
} | ||
} | ||
}, { | ||
key: 'ref', | ||
get: function get() { | ||
var ModelClass = this.getClass(); | ||
return ModelClass._findDatabaseRows((0, _defineProperty5.default)({}, ModelClass.idAttribute, this.getId()))[0]; | ||
} | ||
}], [{ | ||
key: 'toString', | ||
value: function toString() { | ||
return 'ModelClass: ' + this.modelName; | ||
} | ||
(_instance$key = instance[key]).add.apply(_instance$key, (0, _toConsumableArray3.default)(ids)); | ||
}); | ||
/** | ||
* Returns the options object passed to the database for the table that represents | ||
* this Model class. | ||
* | ||
* Returns an empty object by default, which means the database | ||
* will use default options. You can either override this function to return the options | ||
* you want to use, or assign the options object as a static property of the same name to the | ||
* Model class. | ||
* | ||
* @return {Object} the options object passed to the database for the table | ||
* representing this Model class. | ||
*/ | ||
return instance; | ||
}; | ||
}, { | ||
key: 'options', | ||
value: function options() { | ||
return {}; | ||
/** | ||
* Returns a {@link Model} instance for the object with id `id`. | ||
* This throws if the `id` doesn't exist. Use {@link Model#hasId} | ||
* to check for existence first if you're not certain. | ||
* | ||
* @param {*} id - the `id` of the object to get | ||
* @throws If object with id `id` doesn't exist | ||
* @return {Model} {@link Model} instance with id `id` | ||
*/ | ||
Model.withId = function withId(id) { | ||
var ModelClass = this; | ||
var rows = this._findDatabaseRows((0, _defineProperty5.default)({}, ModelClass.idAttribute, id)); | ||
if (rows.length === 0) { | ||
throw new Error(ModelClass.modelName + ' instance with id ' + id + ' not found'); | ||
} | ||
}, { | ||
key: '_getTableOpts', | ||
value: function _getTableOpts() { | ||
if (typeof this.options === 'function') { | ||
return this.options(); | ||
} | ||
return this.options; | ||
} | ||
}, { | ||
key: 'markAccessed', | ||
value: function markAccessed() { | ||
this.session.markAccessed(this); | ||
} | ||
/** | ||
* Returns the id attribute of this {@link Model}. | ||
* | ||
* @return {string} The id attribute of this {@link Model}. | ||
*/ | ||
return new ModelClass(rows[0]); | ||
}; | ||
}, { | ||
key: 'connect', | ||
/** | ||
* Returns a boolean indicating if an entity with the id `id` exists | ||
* in the state. | ||
* | ||
* @param {*} id - a value corresponding to the id attribute of the {@link Model} class. | ||
* @return {Boolean} a boolean indicating if entity with `id` exists in the state | ||
*/ | ||
/** | ||
* Connect the model class to a {@link Session}. | ||
* | ||
* @private | ||
* @param {Session} session - The session to connect to. | ||
*/ | ||
value: function connect(session) { | ||
if (!session instanceof _Session2.default) { | ||
throw Error('A model can only connect to a Session instance.'); | ||
} | ||
this._session = session; | ||
} | ||
Model.hasId = function hasId(id) { | ||
var rows = this._findDatabaseRows((0, _defineProperty5.default)({}, this.idAttribute, id)); | ||
return rows.length === 1; | ||
}; | ||
/** | ||
* Get the current {@link Session} instance. | ||
* | ||
* @private | ||
* @return {Session} The current {@link Session} instance. | ||
*/ | ||
Model._findDatabaseRows = function _findDatabaseRows(lookupObj) { | ||
var ModelClass = this; | ||
return ModelClass.session.query({ | ||
table: ModelClass.modelName, | ||
clauses: [{ | ||
type: _constants.FILTER, | ||
payload: lookupObj | ||
}] | ||
}).rows; | ||
}; | ||
}, { | ||
key: 'getQuerySet', | ||
value: function getQuerySet() { | ||
var QuerySetClass = this.querySetClass; | ||
return new QuerySetClass(this); | ||
} | ||
}, { | ||
key: 'invalidateClassCache', | ||
value: function invalidateClassCache() { | ||
this.isSetUp = undefined; | ||
this.definedProperties = {}; | ||
this.virtualFields = {}; | ||
} | ||
}, { | ||
key: 'all', | ||
/** | ||
* Gets the {@link Model} instance that matches properties in `lookupObj`. | ||
* Throws an error if {@link Model} is not found, or multiple records match | ||
* the properties. | ||
* | ||
* @param {Object} lookupObj - the properties used to match a single entity. | ||
* @return {Model} a {@link Model} instance that matches `lookupObj` properties. | ||
*/ | ||
/** | ||
* Returns a {@link QuerySet} containing all {@link Model} instances. | ||
* @return {QuerySet} a QuerySet containing all {@link Model} instances | ||
*/ | ||
value: function all() { | ||
return this.getQuerySet(); | ||
Model.get = function get(lookupObj) { | ||
var ModelClass = this; | ||
var rows = this._findDatabaseRows(lookupObj); | ||
if (rows.length === 0) { | ||
throw new Error('Model instance not found when calling get method'); | ||
} else if (rows.length > 1) { | ||
throw new Error('Expected to find a single row in Model.get. Found ' + rows.length + '.'); | ||
} | ||
/** | ||
* Creates a new record in the database, instantiates a {@link Model} and returns it. | ||
* | ||
* If you pass values for many-to-many fields, instances are created on the through | ||
* model as well. | ||
* | ||
* @param {props} props - the new {@link Model}'s properties. | ||
* @return {Model} a new {@link Model} instance. | ||
*/ | ||
return new ModelClass(rows[0]); | ||
}; | ||
}, { | ||
key: 'create', | ||
value: function create(userProps) { | ||
var _this3 = this; | ||
/** | ||
* Gets the {@link Model} class or subclass constructor (the class that | ||
* instantiated this instance). | ||
* | ||
* @return {Model} The {@link Model} class or subclass constructor used to instantiate | ||
* this instance. | ||
*/ | ||
var props = (0, _assign2.default)({}, userProps); | ||
var m2mVals = {}; | ||
Model.prototype.getClass = function getClass() { | ||
return this.constructor; | ||
}; | ||
var allowedFieldNames = (0, _keys2.default)(this.fields); | ||
/** | ||
* Gets the id value of the current instance by looking up the id attribute. | ||
* @return {*} The id value of the current instance. | ||
*/ | ||
// We don't check for extra field values passed here; | ||
// the constructor will throw in that case. So we | ||
// only go through the defined fields. | ||
allowedFieldNames.forEach(function (key) { | ||
var field = _this3.fields[key]; | ||
var valuePassed = userProps.hasOwnProperty(key); | ||
if (!valuePassed && !(field instanceof _fields.ManyToMany)) { | ||
if (field.getDefault) { | ||
props[key] = field.getDefault(); | ||
} | ||
} else { | ||
var value = userProps[key]; | ||
props[key] = (0, _utils.normalizeEntity)(value); | ||
// If a value is supplied for a ManyToMany field, | ||
// discard them from props and save for later processing. | ||
if ((0, _isArray2.default)(value)) { | ||
if (_this3.fields.hasOwnProperty(key) && _this3.fields[key] instanceof _fields.ManyToMany) { | ||
m2mVals[key] = value; | ||
delete props[key]; | ||
} | ||
} | ||
} | ||
}); | ||
Model.prototype.getId = function getId() { | ||
return this._fields[this.getClass().idAttribute]; | ||
}; | ||
var newEntry = this.session.applyUpdate({ | ||
action: _constants.CREATE, | ||
table: this.modelName, | ||
payload: props | ||
}); | ||
/** | ||
* Returns a reference to the plain JS object in the store. | ||
* Make sure to not mutate this. | ||
* | ||
* @return {Object} a reference to the plain JS object in the store | ||
*/ | ||
var ModelClass = this; | ||
var instance = new ModelClass(newEntry); | ||
(0, _forOwn2.default)(m2mVals, function (value, key) { | ||
var _instance$key; | ||
/** | ||
* Returns a string representation of the {@link Model} instance. | ||
* | ||
* @return {string} A string representation of this {@link Model} instance. | ||
*/ | ||
Model.prototype.toString = function toString() { | ||
var _this3 = this; | ||
var ids = value.map(_utils.normalizeEntity); | ||
var uniqueIds = (0, _uniq2.default)(ids); | ||
var ThisModel = this.getClass(); | ||
var className = ThisModel.modelName; | ||
var fieldNames = (0, _keys2.default)(ThisModel.fields); | ||
var fields = fieldNames.map(function (fieldName) { | ||
var field = ThisModel.fields[fieldName]; | ||
if (field instanceof _fields.ManyToMany) { | ||
var ids = _this3[fieldName].toModelArray().map(function (model) { | ||
return model.getId(); | ||
}); | ||
return fieldName + ': [' + ids.join(', ') + ']'; | ||
} | ||
var val = _this3._fields[fieldName]; | ||
return fieldName + ': ' + val; | ||
}).join(', '); | ||
return className + ': {' + fields + '}'; | ||
}; | ||
if (ids.length !== uniqueIds.length) { | ||
var idsString = ids; | ||
throw new Error('Found duplicate id(s) when passing "' + idsString + '" to ' + _this3.modelName + '.' + key + ' value on create'); | ||
} | ||
(_instance$key = instance[key]).add.apply(_instance$key, (0, _toConsumableArray3.default)(ids)); | ||
}); | ||
/** | ||
* Returns a boolean indicating if `otherModel` equals this {@link Model} instance. | ||
* Equality is determined by shallow comparing their attributes. | ||
* | ||
* @param {Model} otherModel - a {@link Model} instance to compare | ||
* @return {Boolean} a boolean indicating if the {@link Model} instance's are equal. | ||
*/ | ||
return instance; | ||
} | ||
/** | ||
* Returns a {@link Model} instance for the object with id `id`. | ||
* This throws if the `id` doesn't exist. Use {@link Model#hasId} | ||
* to check for existence first if you're not certain. | ||
* | ||
* @param {*} id - the `id` of the object to get | ||
* @throws If object with id `id` doesn't exist | ||
* @return {Model} {@link Model} instance with id `id` | ||
*/ | ||
Model.prototype.equals = function equals(otherModel) { | ||
return (0, _utils.objectShallowEquals)(this._fields, otherModel._fields); | ||
}; | ||
}, { | ||
key: 'withId', | ||
value: function withId(id) { | ||
var ModelClass = this; | ||
var rows = this._findDatabaseRows((0, _defineProperty5.default)({}, ModelClass.idAttribute, id)); | ||
if (rows.length === 0) { | ||
throw new Error(ModelClass.modelName + ' instance with id ' + id + ' not found'); | ||
} | ||
/** | ||
* Updates a property name to given value for this {@link Model} instance. | ||
* The values are immediately committed to the database. | ||
* | ||
* @param {string} propertyName - name of the property to set | ||
* @param {*} value - value assigned to the property | ||
* @return {undefined} | ||
*/ | ||
return new ModelClass(rows[0]); | ||
} | ||
/** | ||
* Returns a boolean indicating if an entity with the id `id` exists | ||
* in the state. | ||
* | ||
* @param {*} id - a value corresponding to the id attribute of the {@link Model} class. | ||
* @return {Boolean} a boolean indicating if entity with `id` exists in the state | ||
*/ | ||
Model.prototype.set = function set(propertyName, value) { | ||
this.update((0, _defineProperty5.default)({}, propertyName, value)); | ||
}; | ||
}, { | ||
key: 'hasId', | ||
value: function hasId(id) { | ||
var rows = this._findDatabaseRows((0, _defineProperty5.default)({}, this.idAttribute, id)); | ||
return rows.length === 1; | ||
/** | ||
* Assigns multiple fields and corresponding values to this {@link Model} instance. | ||
* The updates are immediately committed to the database. | ||
* | ||
* @param {Object} userMergeObj - an object that will be merged with this instance. | ||
* @return {undefined} | ||
*/ | ||
Model.prototype.update = function update(userMergeObj) { | ||
var ThisModel = this.getClass(); | ||
var relFields = ThisModel.fields; | ||
var mergeObj = (0, _assign2.default)({}, userMergeObj); | ||
// If an array of entities or id's is supplied for a | ||
// many-to-many related field, clear the old relations | ||
// and add the new ones. | ||
for (var mergeKey in mergeObj) { | ||
// eslint-disable-line no-restricted-syntax | ||
if (relFields.hasOwnProperty(mergeKey)) { | ||
var field = relFields[mergeKey]; | ||
if (field) { | ||
if (field instanceof _fields.ManyToMany) { | ||
var currentIds = this[mergeKey].toRefArray().map(function (row) { | ||
return row[ThisModel.idAttribute]; | ||
}); | ||
var normalizedNewIds = mergeObj[mergeKey].map(_utils.normalizeEntity); | ||
var diffActions = (0, _utils.arrayDiffActions)(currentIds, normalizedNewIds); | ||
if (diffActions) { | ||
var idsToDelete = diffActions.delete; | ||
var idsToAdd = diffActions.add; | ||
if (idsToDelete.length > 0) { | ||
var _mergeKey; | ||
(_mergeKey = this[mergeKey]).remove.apply(_mergeKey, (0, _toConsumableArray3.default)(idsToDelete)); | ||
} | ||
if (idsToAdd.length > 0) { | ||
var _mergeKey2; | ||
(_mergeKey2 = this[mergeKey]).add.apply(_mergeKey2, (0, _toConsumableArray3.default)(idsToAdd)); | ||
} | ||
} | ||
delete mergeObj[mergeKey]; | ||
} else if (field instanceof _fields.ForeignKey || field instanceof _fields.OneToOne) { | ||
mergeObj[mergeKey] = (0, _utils.normalizeEntity)(mergeObj[mergeKey]); | ||
} | ||
} | ||
} | ||
} | ||
}, { | ||
key: '_findDatabaseRows', | ||
value: function _findDatabaseRows(lookupObj) { | ||
var ModelClass = this; | ||
return ModelClass.session.query({ | ||
table: ModelClass.modelName, | ||
clauses: [{ | ||
type: _constants.FILTER, | ||
payload: lookupObj | ||
}] | ||
}).rows; | ||
} | ||
/** | ||
* Gets the {@link Model} instance that matches properties in `lookupObj`. | ||
* Throws an error if {@link Model} is not found, or multiple records match | ||
* the properties. | ||
* | ||
* @param {Object} lookupObj - the properties used to match a single entity. | ||
* @return {Model} a {@link Model} instance that matches `lookupObj` properties. | ||
*/ | ||
this._initFields((0, _assign2.default)({}, this._fields, mergeObj)); | ||
}, { | ||
key: 'get', | ||
value: function get(lookupObj) { | ||
var ModelClass = this; | ||
ThisModel.session.applyUpdate({ | ||
action: _constants.UPDATE, | ||
query: getByIdQuery(this), | ||
payload: mergeObj | ||
}); | ||
}; | ||
var rows = this._findDatabaseRows(lookupObj); | ||
/** | ||
* Updates {@link Model} instance attributes to reflect the | ||
* database state in the current session. | ||
* @return {undefined} | ||
*/ | ||
if (rows.length === 0) { | ||
throw new Error('Model instance not found when calling get method'); | ||
} else if (rows.length > 1) { | ||
throw new Error('Expected to find a single row in Model.get. Found ' + rows.length + '.'); | ||
Model.prototype.refreshFromState = function refreshFromState() { | ||
this._initFields(this.ref); | ||
}; | ||
/** | ||
* Deletes the record for this {@link Model} instance. | ||
* You'll still be able to access fields and values on the instance. | ||
* | ||
* @return {undefined} | ||
*/ | ||
Model.prototype.delete = function _delete() { | ||
this._onDelete(); | ||
this.getClass().session.applyUpdate({ | ||
action: _constants.DELETE, | ||
query: getByIdQuery(this) | ||
}); | ||
}; | ||
Model.prototype._onDelete = function _onDelete() { | ||
var virtualFields = this.getClass().virtualFields; | ||
for (var key in virtualFields) { | ||
// eslint-disable-line | ||
var field = virtualFields[key]; | ||
if (field instanceof _fields.ManyToMany) { | ||
// Delete any many-to-many rows the entity is included in. | ||
this[key].clear(); | ||
} else if (field instanceof _fields.ForeignKey) { | ||
var relatedQs = this[key]; | ||
if (relatedQs.exists()) { | ||
relatedQs.update((0, _defineProperty5.default)({}, field.relatedName, null)); | ||
} | ||
} else if (field instanceof _fields.OneToOne) { | ||
// Set null to any foreign keys or one to ones pointed to | ||
// this instance. | ||
if (this[key] !== null) { | ||
this[key][field.relatedName] = null; | ||
} | ||
} | ||
} | ||
}; | ||
return new ModelClass(rows[0]); | ||
// DEPRECATED AND REMOVED METHODS | ||
Model.prototype.getNextState = function getNextState() { | ||
throw new Error('Model.prototype.getNextState is removed. See the 0.9 ' + 'migration guide on the GitHub repo.'); | ||
}; | ||
(0, _createClass3.default)(Model, [{ | ||
key: 'ref', | ||
get: function get() { | ||
var ModelClass = this.getClass(); | ||
return ModelClass._findDatabaseRows((0, _defineProperty5.default)({}, ModelClass.idAttribute, this.getId()))[0]; | ||
} | ||
}, { | ||
}], [{ | ||
key: '_sessionData', | ||
@@ -613,3 +585,2 @@ get: function get() { | ||
}; | ||
Model.definedProperties = {}; | ||
Model.virtualFields = {}; | ||
@@ -616,0 +587,0 @@ Model.querySetClass = _QuerySet2.default; |
373
lib/ORM.js
@@ -12,6 +12,2 @@ 'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); | ||
@@ -33,6 +29,2 @@ | ||
var _createClass2 = require('babel-runtime/helpers/createClass'); | ||
var _createClass3 = _interopRequireDefault(_createClass2); | ||
exports.DeprecatedSchema = DeprecatedSchema; | ||
@@ -111,230 +103,209 @@ | ||
(0, _createClass3.default)(ORM, [{ | ||
key: 'register', | ||
value: function register() { | ||
var _this = this; | ||
ORM.prototype.register = function register() { | ||
var _this = this; | ||
for (var _len = arguments.length, models = Array(_len), _key = 0; _key < _len; _key++) { | ||
models[_key] = arguments[_key]; | ||
} | ||
for (var _len = arguments.length, models = Array(_len), _key = 0; _key < _len; _key++) { | ||
models[_key] = arguments[_key]; | ||
} | ||
models.forEach(function (model) { | ||
model.invalidateClassCache(); | ||
models.forEach(function (model) { | ||
model.invalidateClassCache(); | ||
_this.registerManyToManyModelsFor(model); | ||
_this.registry.push(model); | ||
}); | ||
} | ||
}, { | ||
key: 'registerManyToManyModelsFor', | ||
value: function registerManyToManyModelsFor(model) { | ||
var _this3 = this; | ||
_this.registerManyToManyModelsFor(model); | ||
_this.registry.push(model); | ||
}); | ||
}; | ||
var fields = model.fields; | ||
var thisModelName = model.modelName; | ||
ORM.prototype.registerManyToManyModelsFor = function registerManyToManyModelsFor(model) { | ||
var _this3 = this; | ||
(0, _forOwn2.default)(fields, function (fieldInstance, fieldName) { | ||
if (fieldInstance instanceof _fields.ManyToMany && !fieldInstance.through) { | ||
var _Through$fields; | ||
var fields = model.fields; | ||
var thisModelName = model.modelName; | ||
var toModelName = void 0; | ||
if (fieldInstance.toModelName === 'this') { | ||
toModelName = thisModelName; | ||
} else { | ||
toModelName = fieldInstance.toModelName; | ||
(0, _forOwn2.default)(fields, function (fieldInstance, fieldName) { | ||
if (fieldInstance instanceof _fields.ManyToMany && !fieldInstance.through) { | ||
var _Through$fields; | ||
var toModelName = void 0; | ||
if (fieldInstance.toModelName === 'this') { | ||
toModelName = thisModelName; | ||
} else { | ||
toModelName = fieldInstance.toModelName; | ||
} | ||
var fromFieldName = (0, _utils.m2mFromFieldName)(thisModelName); | ||
var toFieldName = (0, _utils.m2mToFieldName)(toModelName); | ||
var Through = function (_Model) { | ||
(0, _inherits3.default)(ThroughModel, _Model); | ||
function ThroughModel() { | ||
(0, _classCallCheck3.default)(this, ThroughModel); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model.apply(this, arguments)); | ||
} | ||
var fromFieldName = (0, _utils.m2mFromFieldName)(thisModelName); | ||
var toFieldName = (0, _utils.m2mToFieldName)(toModelName); | ||
return ThroughModel; | ||
}(_Model3.default); | ||
var Through = function (_Model) { | ||
(0, _inherits3.default)(ThroughModel, _Model); | ||
Through.modelName = (0, _utils.m2mName)(thisModelName, fieldName); | ||
function ThroughModel() { | ||
(0, _classCallCheck3.default)(this, ThroughModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (ThroughModel.__proto__ || (0, _getPrototypeOf2.default)(ThroughModel)).apply(this, arguments)); | ||
} | ||
Through.fields = (_Through$fields = { | ||
id: (0, _fields.attr)() | ||
}, (0, _defineProperty3.default)(_Through$fields, fromFieldName, new _fields.ForeignKey(thisModelName)), (0, _defineProperty3.default)(_Through$fields, toFieldName, new _fields.ForeignKey(toModelName)), _Through$fields); | ||
return ThroughModel; | ||
}(_Model3.default); | ||
Through.invalidateClassCache(); | ||
_this3.implicitThroughModels.push(Through); | ||
} | ||
}); | ||
}; | ||
Through.modelName = (0, _utils.m2mName)(thisModelName, fieldName); | ||
/** | ||
* Gets a {@link Model} class by its name from the registry. | ||
* @param {string} modelName - the name of the {@link Model} class to get | ||
* @throws If {@link Model} class is not found. | ||
* @return {Model} the {@link Model} class, if found | ||
*/ | ||
Through.fields = (_Through$fields = { | ||
id: (0, _fields.attr)() | ||
}, (0, _defineProperty3.default)(_Through$fields, fromFieldName, new _fields.ForeignKey(thisModelName)), (0, _defineProperty3.default)(_Through$fields, toFieldName, new _fields.ForeignKey(toModelName)), _Through$fields); | ||
Through.invalidateClassCache(); | ||
_this3.implicitThroughModels.push(Through); | ||
} | ||
}); | ||
ORM.prototype.get = function get(modelName) { | ||
var found = (0, _find2.default)(this.registry.concat(this.implicitThroughModels), function (model) { | ||
return model.modelName === modelName; | ||
}); | ||
if (typeof found === 'undefined') { | ||
throw new Error('Did not find model ' + modelName + ' from registry.'); | ||
} | ||
return found; | ||
}; | ||
/** | ||
* Gets a {@link Model} class by its name from the registry. | ||
* @param {string} modelName - the name of the {@link Model} class to get | ||
* @throws If {@link Model} class is not found. | ||
* @return {Model} the {@link Model} class, if found | ||
*/ | ||
ORM.prototype.getModelClasses = function getModelClasses() { | ||
this._setupModelPrototypes(this.registry); | ||
this._setupModelPrototypes(this.implicitThroughModels); | ||
return this.registry.concat(this.implicitThroughModels); | ||
}; | ||
}, { | ||
key: 'get', | ||
value: function get(modelName) { | ||
var found = (0, _find2.default)(this.registry.concat(this.implicitThroughModels), function (model) { | ||
return model.modelName === modelName; | ||
}); | ||
ORM.prototype._attachQuerySetMethods = function _attachQuerySetMethods(model) { | ||
var querySetClass = model.querySetClass; | ||
if (typeof found === 'undefined') { | ||
throw new Error('Did not find model ' + modelName + ' from registry.'); | ||
} | ||
return found; | ||
} | ||
}, { | ||
key: 'getModelClasses', | ||
value: function getModelClasses() { | ||
this._setupModelPrototypes(this.registry); | ||
this._setupModelPrototypes(this.implicitThroughModels); | ||
return this.registry.concat(this.implicitThroughModels); | ||
} | ||
}, { | ||
key: '_attachQuerySetMethods', | ||
value: function _attachQuerySetMethods(model) { | ||
var querySetClass = model.querySetClass; | ||
(0, _utils.attachQuerySetMethods)(model, querySetClass); | ||
}; | ||
(0, _utils.attachQuerySetMethods)(model, querySetClass); | ||
ORM.prototype.isFieldInstalled = function isFieldInstalled(modelName, fieldName) { | ||
return this.installedFields.hasOwnProperty(modelName) ? !!this.installedFields[modelName][fieldName] : false; | ||
}; | ||
ORM.prototype.setFieldInstalled = function setFieldInstalled(modelName, fieldName) { | ||
if (!this.installedFields.hasOwnProperty(modelName)) { | ||
this.installedFields[modelName] = {}; | ||
} | ||
}, { | ||
key: 'isFieldInstalled', | ||
value: function isFieldInstalled(modelName, fieldName) { | ||
return this.installedFields.hasOwnProperty(modelName) ? !!this.installedFields[modelName][fieldName] : false; | ||
} | ||
}, { | ||
key: 'setFieldInstalled', | ||
value: function setFieldInstalled(modelName, fieldName) { | ||
if (!this.installedFields.hasOwnProperty(modelName)) { | ||
this.installedFields[modelName] = {}; | ||
} | ||
this.installedFields[modelName][fieldName] = true; | ||
} | ||
}, { | ||
key: '_setupModelPrototypes', | ||
value: function _setupModelPrototypes(models) { | ||
var _this4 = this; | ||
this.installedFields[modelName][fieldName] = true; | ||
}; | ||
models.forEach(function (model) { | ||
if (!model.isSetUp) { | ||
var fields = model.fields; | ||
(0, _forOwn2.default)(fields, function (fieldInstance, fieldName) { | ||
if (!_this4.isFieldInstalled(model.modelName, fieldName)) { | ||
fieldInstance.install(model, fieldName, _this4); | ||
_this4.setFieldInstalled(model.modelName, fieldName); | ||
} | ||
}); | ||
_this4._attachQuerySetMethods(model); | ||
model.isSetUp = true; | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'generateSchemaSpec', | ||
value: function generateSchemaSpec() { | ||
var models = this.getModelClasses(); | ||
var tables = models.reduce(function (spec, modelClass) { | ||
var tableName = modelClass.modelName; | ||
var tableSpec = modelClass._getTableOpts(); | ||
spec[tableName] = (0, _assign2.default)({}, { fields: modelClass.fields }, tableSpec); | ||
return spec; | ||
}, {}); | ||
return { tables: tables }; | ||
} | ||
}, { | ||
key: 'getDatabase', | ||
value: function getDatabase() { | ||
if (!this.db) { | ||
this.db = this.createDatabase(this.generateSchemaSpec()); | ||
ORM.prototype._setupModelPrototypes = function _setupModelPrototypes(models) { | ||
var _this4 = this; | ||
models.forEach(function (model) { | ||
if (!model.isSetUp) { | ||
var fields = model.fields; | ||
(0, _forOwn2.default)(fields, function (fieldInstance, fieldName) { | ||
if (!_this4.isFieldInstalled(model.modelName, fieldName)) { | ||
fieldInstance.install(model, fieldName, _this4); | ||
_this4.setFieldInstalled(model.modelName, fieldName); | ||
} | ||
}); | ||
_this4._attachQuerySetMethods(model); | ||
model.isSetUp = true; | ||
} | ||
return this.db; | ||
} | ||
}); | ||
}; | ||
/** | ||
* Returns the empty database state. | ||
* @return {Object} the empty state | ||
*/ | ||
ORM.prototype.generateSchemaSpec = function generateSchemaSpec() { | ||
var models = this.getModelClasses(); | ||
var tables = models.reduce(function (spec, modelClass) { | ||
var tableName = modelClass.modelName; | ||
var tableSpec = modelClass._getTableOpts(); | ||
spec[tableName] = (0, _assign2.default)({}, { fields: modelClass.fields }, tableSpec); | ||
return spec; | ||
}, {}); | ||
return { tables: tables }; | ||
}; | ||
}, { | ||
key: 'getEmptyState', | ||
value: function getEmptyState() { | ||
return this.getDatabase().getEmptyState(); | ||
ORM.prototype.getDatabase = function getDatabase() { | ||
if (!this.db) { | ||
this.db = this.createDatabase(this.generateSchemaSpec()); | ||
} | ||
return this.db; | ||
}; | ||
/** | ||
* Begins an immutable database session. | ||
* | ||
* @param {Object} state - the state the database manages | ||
* @return {Session} a new {@link Session} instance | ||
*/ | ||
/** | ||
* Returns the empty database state. | ||
* @return {Object} the empty state | ||
*/ | ||
}, { | ||
key: 'session', | ||
value: function session(state) { | ||
return new _Session2.default(this, this.getDatabase(), state); | ||
} | ||
/** | ||
* Begins a mutable database session. | ||
* | ||
* @param {Object} state - the state the database manages | ||
* @return {Session} a new {@link Session} instance | ||
*/ | ||
ORM.prototype.getEmptyState = function getEmptyState() { | ||
return this.getDatabase().getEmptyState(); | ||
}; | ||
}, { | ||
key: 'mutableSession', | ||
value: function mutableSession(state) { | ||
return new _Session2.default(this, this.getDatabase(), state, true); | ||
} | ||
/** | ||
* Begins an immutable database session. | ||
* | ||
* @param {Object} state - the state the database manages | ||
* @return {Session} a new {@link Session} instance | ||
*/ | ||
// DEPRECATED AND REMOVED METHODS | ||
}, { | ||
key: 'withMutations', | ||
value: function withMutations(state) { | ||
(0, _utils.warnDeprecated)('ORM.prototype.withMutations is deprecated. ' + 'Use ORM.prototype.mutableSession instead.'); | ||
ORM.prototype.session = function session(state) { | ||
return new _Session2.default(this, this.getDatabase(), state); | ||
}; | ||
return this.mutableSession(state); | ||
/** | ||
* Begins a mutable database session. | ||
* | ||
* @param {Object} state - the state the database manages | ||
* @return {Session} a new {@link Session} instance | ||
*/ | ||
ORM.prototype.mutableSession = function mutableSession(state) { | ||
return new _Session2.default(this, this.getDatabase(), state, true); | ||
}; | ||
// DEPRECATED AND REMOVED METHODS | ||
ORM.prototype.withMutations = function withMutations(state) { | ||
(0, _utils.warnDeprecated)('ORM.prototype.withMutations is deprecated. ' + 'Use ORM.prototype.mutableSession instead.'); | ||
return this.mutableSession(state); | ||
}; | ||
ORM.prototype.from = function from(state) { | ||
(0, _utils.warnDeprecated)('ORM.prototype.from function is deprecated. ' + 'Use ORM.prototype.session instead.'); | ||
return this.session(state); | ||
}; | ||
ORM.prototype.reducer = function reducer() { | ||
(0, _utils.warnDeprecated)('ORM.prototype.reducer is deprecated. Access ' + 'the Session.prototype.state property instead.'); | ||
return (0, _redux.createReducer)(this); | ||
}; | ||
ORM.prototype.createSelector = function createSelector() { | ||
(0, _utils.warnDeprecated)('ORM.prototype.createSelector is deprecated. ' + 'Import `createSelector` from Redux-ORM instead.'); | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
} | ||
}, { | ||
key: 'from', | ||
value: function from(state) { | ||
(0, _utils.warnDeprecated)('ORM.prototype.from function is deprecated. ' + 'Use ORM.prototype.session instead.'); | ||
return this.session(state); | ||
} | ||
}, { | ||
key: 'reducer', | ||
value: function reducer() { | ||
(0, _utils.warnDeprecated)('ORM.prototype.reducer is deprecated. Access ' + 'the Session.prototype.state property instead.'); | ||
return (0, _redux.createReducer)(this); | ||
} | ||
}, { | ||
key: 'createSelector', | ||
value: function createSelector() { | ||
(0, _utils.warnDeprecated)('ORM.prototype.createSelector is deprecated. ' + 'Import `createSelector` from Redux-ORM instead.'); | ||
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
} | ||
return _redux.createSelector.apply(undefined, [this].concat(args)); | ||
}; | ||
return _redux.createSelector.apply(undefined, [this].concat(args)); | ||
} | ||
}, { | ||
key: 'getDefaultState', | ||
value: function getDefaultState() { | ||
(0, _utils.warnDeprecated)('ORM.prototype.getDefaultState is deprecated. Use ' + 'the ORM.prototype.getEmptyState instead.'); | ||
return this.getEmptyState(); | ||
} | ||
}, { | ||
key: 'define', | ||
value: function define() { | ||
throw new Error('ORM.prototype.define is removed. Please define a Model class.'); | ||
} | ||
}]); | ||
ORM.prototype.getDefaultState = function getDefaultState() { | ||
(0, _utils.warnDeprecated)('ORM.prototype.getDefaultState is deprecated. Use ' + 'the ORM.prototype.getEmptyState instead.'); | ||
return this.getEmptyState(); | ||
}; | ||
ORM.prototype.define = function define() { | ||
throw new Error('ORM.prototype.define is removed. Please define a Model class.'); | ||
}; | ||
return ORM; | ||
@@ -341,0 +312,0 @@ }(); |
@@ -74,258 +74,243 @@ 'use strict'; | ||
(0, _createClass3.default)(QuerySet, [{ | ||
key: '_new', | ||
value: function _new(clauses, userOpts) { | ||
var opts = (0, _assign2.default)({}, this._opts, userOpts); | ||
return new this.constructor(this.modelClass, clauses, opts); | ||
} | ||
}, { | ||
key: 'toString', | ||
value: function toString() { | ||
var _this = this; | ||
QuerySet.addSharedMethod = function addSharedMethod(methodName) { | ||
this.sharedMethods = this.sharedMethods.concat(methodName); | ||
}; | ||
this._evaluate(); | ||
var contents = this.rows.map(function (id) { | ||
return _this.modelClass.withId(id).toString(); | ||
}).join('\n - '); | ||
return 'QuerySet contents: \n - ' + contents; | ||
} | ||
QuerySet.prototype._new = function _new(clauses, userOpts) { | ||
var opts = (0, _assign2.default)({}, this._opts, userOpts); | ||
return new this.constructor(this.modelClass, clauses, opts); | ||
}; | ||
/** | ||
* Returns an array of the plain objects represented by the QuerySet. | ||
* The plain objects are direct references to the store. | ||
* | ||
* @return {Object[]} references to the plain JS objects represented by | ||
* the QuerySet | ||
*/ | ||
QuerySet.prototype.toString = function toString() { | ||
var _this = this; | ||
}, { | ||
key: 'toRefArray', | ||
value: function toRefArray() { | ||
this._evaluate(); | ||
return this.rows; | ||
} | ||
this._evaluate(); | ||
var contents = this.rows.map(function (id) { | ||
return _this.modelClass.withId(id).toString(); | ||
}).join('\n - '); | ||
return 'QuerySet contents: \n - ' + contents; | ||
}; | ||
/** | ||
* Returns an array of {@link Model} instances represented by the QuerySet. | ||
* @return {Model[]} model instances represented by the QuerySet | ||
*/ | ||
/** | ||
* Returns an array of the plain objects represented by the QuerySet. | ||
* The plain objects are direct references to the store. | ||
* | ||
* @return {Object[]} references to the plain JS objects represented by | ||
* the QuerySet | ||
*/ | ||
}, { | ||
key: 'toModelArray', | ||
value: function toModelArray() { | ||
this._evaluate(); | ||
var ModelClass = this.modelClass; | ||
return this.rows.map(function (props) { | ||
return new ModelClass(props); | ||
}); | ||
} | ||
/** | ||
* Returns the number of {@link Model} instances represented by the QuerySet. | ||
* | ||
* @return {number} length of the QuerySet | ||
*/ | ||
QuerySet.prototype.toRefArray = function toRefArray() { | ||
this._evaluate(); | ||
return this.rows; | ||
}; | ||
}, { | ||
key: 'count', | ||
value: function count() { | ||
this._evaluate(); | ||
return this.rows.length; | ||
} | ||
/** | ||
* Returns an array of {@link Model} instances represented by the QuerySet. | ||
* @return {Model[]} model instances represented by the QuerySet | ||
*/ | ||
/** | ||
* Checks if the {@link QuerySet} instance has any records matching the query | ||
* in the database. | ||
* | ||
* @return {Boolean} `true` if the {@link QuerySet} instance contains entities, else `false`. | ||
*/ | ||
}, { | ||
key: 'exists', | ||
value: function exists() { | ||
return Boolean(this.count()); | ||
} | ||
QuerySet.prototype.toModelArray = function toModelArray() { | ||
this._evaluate(); | ||
var ModelClass = this.modelClass; | ||
return this.rows.map(function (props) { | ||
return new ModelClass(props); | ||
}); | ||
}; | ||
/** | ||
* Returns the {@link Model} instance at index `index` in the {@link QuerySet} instance if | ||
* `withRefs` flag is set to `false`, or a reference to the plain JavaScript | ||
* object in the model state if `true`. | ||
* | ||
* @param {number} index - index of the model instance to get | ||
* @return {Model|Object} a {@link Model} instance or a plain JavaScript | ||
* object at index `index` in the {@link QuerySet} instance | ||
*/ | ||
/** | ||
* Returns the number of {@link Model} instances represented by the QuerySet. | ||
* | ||
* @return {number} length of the QuerySet | ||
*/ | ||
}, { | ||
key: 'at', | ||
value: function at(index) { | ||
this._evaluate(); | ||
var ModelClass = this.modelClass; | ||
return new ModelClass(this.rows[index]); | ||
} | ||
/** | ||
* Returns the {@link Model} instance at index 0 in the {@link QuerySet} instance. | ||
* @return {Model} | ||
*/ | ||
QuerySet.prototype.count = function count() { | ||
this._evaluate(); | ||
return this.rows.length; | ||
}; | ||
}, { | ||
key: 'first', | ||
value: function first() { | ||
return this.at(0); | ||
} | ||
/** | ||
* Checks if the {@link QuerySet} instance has any records matching the query | ||
* in the database. | ||
* | ||
* @return {Boolean} `true` if the {@link QuerySet} instance contains entities, else `false`. | ||
*/ | ||
/** | ||
* Returns the {@link Model} instance at index `QuerySet.count() - 1` | ||
* @return {Model} | ||
*/ | ||
}, { | ||
key: 'last', | ||
value: function last() { | ||
this._evaluate(); | ||
return this.at(this.rows.length - 1); | ||
} | ||
QuerySet.prototype.exists = function exists() { | ||
return Boolean(this.count()); | ||
}; | ||
/** | ||
* Returns a new {@link QuerySet} instance with the same entities. | ||
* @return {QuerySet} a new QuerySet with the same entities. | ||
*/ | ||
/** | ||
* Returns the {@link Model} instance at index `index` in the {@link QuerySet} instance if | ||
* `withRefs` flag is set to `false`, or a reference to the plain JavaScript | ||
* object in the model state if `true`. | ||
* | ||
* @param {number} index - index of the model instance to get | ||
* @return {Model|Object} a {@link Model} instance or a plain JavaScript | ||
* object at index `index` in the {@link QuerySet} instance | ||
*/ | ||
}, { | ||
key: 'all', | ||
value: function all() { | ||
return this._new(this.clauses); | ||
} | ||
/** | ||
* Returns a new {@link QuerySet} instance with entities that match properties in `lookupObj`. | ||
* | ||
* @param {Object} lookupObj - the properties to match objects with. | ||
* @return {QuerySet} a new {@link QuerySet} instance with objects that passed the filter. | ||
*/ | ||
QuerySet.prototype.at = function at(index) { | ||
this._evaluate(); | ||
var ModelClass = this.modelClass; | ||
return new ModelClass(this.rows[index]); | ||
}; | ||
}, { | ||
key: 'filter', | ||
value: function filter(lookupObj) { | ||
var normalizedLookupObj = (typeof lookupObj === 'undefined' ? 'undefined' : (0, _typeof3.default)(lookupObj)) === 'object' ? (0, _mapValues2.default)(lookupObj, _utils.normalizeEntity) : lookupObj; | ||
var filterDescriptor = { type: _constants.FILTER, payload: normalizedLookupObj }; | ||
return this._new(this.clauses.concat(filterDescriptor)); | ||
} | ||
/** | ||
* Returns the {@link Model} instance at index 0 in the {@link QuerySet} instance. | ||
* @return {Model} | ||
*/ | ||
/** | ||
* Returns a new {@link QuerySet} instance with entities that do not match | ||
* properties in `lookupObj`. | ||
* | ||
* @param {Object} lookupObj - the properties to unmatch objects with. | ||
* @return {QuerySet} a new {@link QuerySet} instance with objects that passed the filter. | ||
*/ | ||
}, { | ||
key: 'exclude', | ||
value: function exclude(lookupObj) { | ||
var normalizedLookupObj = (typeof lookupObj === 'undefined' ? 'undefined' : (0, _typeof3.default)(lookupObj)) === 'object' ? (0, _mapValues2.default)(lookupObj, _utils.normalizeEntity) : lookupObj; | ||
var excludeDescriptor = { type: _constants.EXCLUDE, payload: normalizedLookupObj }; | ||
return this._new(this.clauses.concat(excludeDescriptor)); | ||
} | ||
}, { | ||
key: '_evaluate', | ||
value: function _evaluate() { | ||
if (!this._evaluated) { | ||
var session = this.modelClass.session; | ||
var querySpec = { | ||
table: this.modelClass.modelName, | ||
clauses: this.clauses | ||
}; | ||
QuerySet.prototype.first = function first() { | ||
return this.at(0); | ||
}; | ||
var _session$query = session.query(querySpec), | ||
rows = _session$query.rows; | ||
/** | ||
* Returns the {@link Model} instance at index `QuerySet.count() - 1` | ||
* @return {Model} | ||
*/ | ||
this.rows = rows; | ||
this._evaluated = true; | ||
} | ||
} | ||
/** | ||
* Returns a new {@link QuerySet} instance with entities ordered by `iteratees` in ascending | ||
* order, unless otherwise specified. Delegates to `lodash.orderBy`. | ||
* | ||
* @param {string[]|Function[]} iteratees - an array where each item can be a string or a | ||
* function. If a string is supplied, it should | ||
* correspond to property on the entity that will | ||
* determine the order. If a function is supplied, | ||
* it should return the value to order by. | ||
* @param {Boolean[]} [orders] - the sort orders of `iteratees`. If unspecified, all iteratees | ||
* will be sorted in ascending order. `true` and `'asc'` | ||
* correspond to ascending order, and `false` and `'desc` | ||
* to descending order. | ||
* @return {QuerySet} a new {@link QuerySet} with objects ordered by `iteratees`. | ||
*/ | ||
QuerySet.prototype.last = function last() { | ||
this._evaluate(); | ||
return this.at(this.rows.length - 1); | ||
}; | ||
}, { | ||
key: 'orderBy', | ||
value: function orderBy(iteratees, orders) { | ||
var orderByDescriptor = { type: _constants.ORDER_BY, payload: [iteratees, orders] }; | ||
return this._new(this.clauses.concat(orderByDescriptor)); | ||
} | ||
/** | ||
* Returns a new {@link QuerySet} instance with the same entities. | ||
* @return {QuerySet} a new QuerySet with the same entities. | ||
*/ | ||
/** | ||
* Records an update specified with `mergeObj` to all the objects | ||
* in the {@link QuerySet} instance. | ||
* | ||
* @param {Object} mergeObj - an object to merge with all the objects in this | ||
* queryset. | ||
* @return {undefined} | ||
*/ | ||
}, { | ||
key: 'update', | ||
value: function update(mergeObj) { | ||
this.modelClass.session.applyUpdate({ | ||
action: _constants.UPDATE, | ||
query: { | ||
table: this.modelClass.modelName, | ||
clauses: this.clauses | ||
}, | ||
payload: mergeObj | ||
}); | ||
this._evaluated = false; | ||
} | ||
QuerySet.prototype.all = function all() { | ||
return this._new(this.clauses); | ||
}; | ||
/** | ||
* Records a deletion of all the objects in this {@link QuerySet} instance. | ||
* @return {undefined} | ||
*/ | ||
/** | ||
* Returns a new {@link QuerySet} instance with entities that match properties in `lookupObj`. | ||
* | ||
* @param {Object} lookupObj - the properties to match objects with. | ||
* @return {QuerySet} a new {@link QuerySet} instance with objects that passed the filter. | ||
*/ | ||
}, { | ||
key: 'delete', | ||
value: function _delete() { | ||
this.toModelArray().forEach(function (model) { | ||
return model._onDelete(); | ||
}); | ||
this.modelClass.session.applyUpdate({ | ||
action: _constants.DELETE, | ||
query: { | ||
table: this.modelClass.modelName, | ||
clauses: this.clauses | ||
} | ||
}); | ||
QuerySet.prototype.filter = function filter(lookupObj) { | ||
var normalizedLookupObj = (typeof lookupObj === 'undefined' ? 'undefined' : (0, _typeof3.default)(lookupObj)) === 'object' ? (0, _mapValues2.default)(lookupObj, _utils.normalizeEntity) : lookupObj; | ||
var filterDescriptor = { type: _constants.FILTER, payload: normalizedLookupObj }; | ||
return this._new(this.clauses.concat(filterDescriptor)); | ||
}; | ||
this._evaluated = false; | ||
/** | ||
* Returns a new {@link QuerySet} instance with entities that do not match | ||
* properties in `lookupObj`. | ||
* | ||
* @param {Object} lookupObj - the properties to unmatch objects with. | ||
* @return {QuerySet} a new {@link QuerySet} instance with objects that passed the filter. | ||
*/ | ||
QuerySet.prototype.exclude = function exclude(lookupObj) { | ||
var normalizedLookupObj = (typeof lookupObj === 'undefined' ? 'undefined' : (0, _typeof3.default)(lookupObj)) === 'object' ? (0, _mapValues2.default)(lookupObj, _utils.normalizeEntity) : lookupObj; | ||
var excludeDescriptor = { type: _constants.EXCLUDE, payload: normalizedLookupObj }; | ||
return this._new(this.clauses.concat(excludeDescriptor)); | ||
}; | ||
QuerySet.prototype._evaluate = function _evaluate() { | ||
if (!this._evaluated) { | ||
var session = this.modelClass.session; | ||
var querySpec = { | ||
table: this.modelClass.modelName, | ||
clauses: this.clauses | ||
}; | ||
var _session$query = session.query(querySpec), | ||
rows = _session$query.rows; | ||
this.rows = rows; | ||
this._evaluated = true; | ||
} | ||
}; | ||
// DEPRECATED AND REMOVED METHODS | ||
/** | ||
* Returns a new {@link QuerySet} instance with entities ordered by `iteratees` in ascending | ||
* order, unless otherwise specified. Delegates to `lodash.orderBy`. | ||
* | ||
* @param {string[]|Function[]} iteratees - an array where each item can be a string or a | ||
* function. If a string is supplied, it should | ||
* correspond to property on the entity that will | ||
* determine the order. If a function is supplied, | ||
* it should return the value to order by. | ||
* @param {Boolean[]} [orders] - the sort orders of `iteratees`. If unspecified, all iteratees | ||
* will be sorted in ascending order. `true` and `'asc'` | ||
* correspond to ascending order, and `false` and `'desc` | ||
* to descending order. | ||
* @return {QuerySet} a new {@link QuerySet} with objects ordered by `iteratees`. | ||
*/ | ||
}, { | ||
key: 'map', | ||
value: function map() { | ||
throw new Error('QuerySet.prototype.map is removed. ' + 'Call .toModelArray() or .toRefArray() first to map.'); | ||
} | ||
}, { | ||
key: 'forEach', | ||
value: function forEach() { | ||
throw new Error('QuerySet.prototype.forEach is removed. ' + 'Call .toModelArray() or .toRefArray() first to iterate.'); | ||
} | ||
}, { | ||
QuerySet.prototype.orderBy = function orderBy(iteratees, orders) { | ||
var orderByDescriptor = { type: _constants.ORDER_BY, payload: [iteratees, orders] }; | ||
return this._new(this.clauses.concat(orderByDescriptor)); | ||
}; | ||
/** | ||
* Records an update specified with `mergeObj` to all the objects | ||
* in the {@link QuerySet} instance. | ||
* | ||
* @param {Object} mergeObj - an object to merge with all the objects in this | ||
* queryset. | ||
* @return {undefined} | ||
*/ | ||
QuerySet.prototype.update = function update(mergeObj) { | ||
this.modelClass.session.applyUpdate({ | ||
action: _constants.UPDATE, | ||
query: { | ||
table: this.modelClass.modelName, | ||
clauses: this.clauses | ||
}, | ||
payload: mergeObj | ||
}); | ||
this._evaluated = false; | ||
}; | ||
/** | ||
* Records a deletion of all the objects in this {@link QuerySet} instance. | ||
* @return {undefined} | ||
*/ | ||
QuerySet.prototype.delete = function _delete() { | ||
this.toModelArray().forEach(function (model) { | ||
return model._onDelete(); | ||
}); | ||
this.modelClass.session.applyUpdate({ | ||
action: _constants.DELETE, | ||
query: { | ||
table: this.modelClass.modelName, | ||
clauses: this.clauses | ||
} | ||
}); | ||
this._evaluated = false; | ||
}; | ||
// DEPRECATED AND REMOVED METHODS | ||
QuerySet.prototype.map = function map() { | ||
throw new Error('QuerySet.prototype.map is removed. ' + 'Call .toModelArray() or .toRefArray() first to map.'); | ||
}; | ||
QuerySet.prototype.forEach = function forEach() { | ||
throw new Error('QuerySet.prototype.forEach is removed. ' + 'Call .toModelArray() or .toRefArray() first to iterate.'); | ||
}; | ||
(0, _createClass3.default)(QuerySet, [{ | ||
key: 'withModels', | ||
@@ -340,7 +325,2 @@ get: function get() { | ||
} | ||
}], [{ | ||
key: 'addSharedMethod', | ||
value: function addSharedMethod(methodName) { | ||
this.sharedMethods = this.sharedMethods.concat(methodName); | ||
} | ||
}]); | ||
@@ -347,0 +327,0 @@ return QuerySet; |
@@ -11,6 +11,2 @@ 'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); | ||
@@ -74,3 +70,3 @@ | ||
(0, _classCallCheck3.default)(this, SessionBoundModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (SessionBoundModel.__proto__ || (0, _getPrototypeOf2.default)(SessionBoundModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _modelClass.apply(this, arguments)); | ||
} | ||
@@ -91,67 +87,60 @@ | ||
(0, _createClass3.default)(Session, [{ | ||
key: 'markAccessed', | ||
value: function markAccessed(modelName) { | ||
this.getDataForModel(modelName).accessed = true; | ||
Session.prototype.markAccessed = function markAccessed(modelName) { | ||
this.getDataForModel(modelName).accessed = true; | ||
}; | ||
Session.prototype.getDataForModel = function getDataForModel(modelName) { | ||
if (!this.modelData[modelName]) { | ||
this.modelData[modelName] = {}; | ||
} | ||
}, { | ||
key: 'getDataForModel', | ||
value: function getDataForModel(modelName) { | ||
if (!this.modelData[modelName]) { | ||
this.modelData[modelName] = {}; | ||
} | ||
return this.modelData[modelName]; | ||
} | ||
return this.modelData[modelName]; | ||
}; | ||
/** | ||
* Applies update to a model state. | ||
* | ||
* @private | ||
* @param {Object} update - the update object. Must have keys | ||
* `type`, `payload`. | ||
*/ | ||
/** | ||
* Applies update to a model state. | ||
* | ||
* @private | ||
* @param {Object} update - the update object. Must have keys | ||
* `type`, `payload`. | ||
*/ | ||
}, { | ||
key: 'applyUpdate', | ||
value: function applyUpdate(updateSpec) { | ||
var batchToken = this.batchToken, | ||
withMutations = this.withMutations; | ||
var tx = { batchToken: batchToken, withMutations: withMutations }; | ||
var result = this.db.update(updateSpec, tx, this.state); | ||
var status = result.status, | ||
state = result.state; | ||
Session.prototype.applyUpdate = function applyUpdate(updateSpec) { | ||
var batchToken = this.batchToken, | ||
withMutations = this.withMutations; | ||
var tx = { batchToken: batchToken, withMutations: withMutations }; | ||
var result = this.db.update(updateSpec, tx, this.state); | ||
var status = result.status, | ||
state = result.state; | ||
if (status === _constants.SUCCESS) { | ||
this.state = state; | ||
} else { | ||
throw new Error('Applying update failed: ' + result.toString()); | ||
} | ||
return result.payload; | ||
if (status === _constants.SUCCESS) { | ||
this.state = state; | ||
} else { | ||
throw new Error('Applying update failed: ' + result.toString()); | ||
} | ||
}, { | ||
key: 'query', | ||
value: function query(querySpec) { | ||
var table = querySpec.table; | ||
this.markAccessed(table); | ||
return this.db.query(querySpec, this.state); | ||
} | ||
return result.payload; | ||
}; | ||
// DEPRECATED AND REMOVED METHODS | ||
Session.prototype.query = function query(querySpec) { | ||
var table = querySpec.table; | ||
}, { | ||
key: 'getNextState', | ||
value: function getNextState() { | ||
(0, _utils.warnDeprecated)('Session.prototype.getNextState function is deprecated. Access ' + 'the Session.prototype.state property instead.'); | ||
return this.state; | ||
} | ||
}, { | ||
key: 'reduce', | ||
value: function reduce() { | ||
throw new Error('Session.prototype.reduce is removed. The Redux integration API ' + 'is now decoupled from ORM and Session - see the 0.9 migration guide ' + 'in the GitHub repo.'); | ||
} | ||
}, { | ||
this.markAccessed(table); | ||
return this.db.query(querySpec, this.state); | ||
}; | ||
// DEPRECATED AND REMOVED METHODS | ||
Session.prototype.getNextState = function getNextState() { | ||
(0, _utils.warnDeprecated)('Session.prototype.getNextState function is deprecated. Access ' + 'the Session.prototype.state property instead.'); | ||
return this.state; | ||
}; | ||
Session.prototype.reduce = function reduce() { | ||
throw new Error('Session.prototype.reduce is removed. The Redux integration API ' + 'is now decoupled from ORM and Session - see the 0.9 migration guide ' + 'in the GitHub repo.'); | ||
}; | ||
(0, _createClass3.default)(Session, [{ | ||
key: 'accessedModels', | ||
@@ -158,0 +147,0 @@ get: function get() { |
'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
@@ -341,3 +337,3 @@ | ||
(0, _classCallCheck3.default)(this, DefaultFieldModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (DefaultFieldModel.__proto__ || (0, _getPrototypeOf2.default)(DefaultFieldModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model.apply(this, arguments)); | ||
} | ||
@@ -446,3 +442,3 @@ | ||
(0, _classCallCheck3.default)(this, Item); | ||
return (0, _possibleConstructorReturn3.default)(this, (Item.__proto__ || (0, _getPrototypeOf2.default)(Item)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model2.apply(this, arguments)); | ||
} | ||
@@ -449,0 +445,0 @@ |
'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
@@ -59,3 +55,3 @@ | ||
(0, _classCallCheck3.default)(this, TestModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (TestModel.__proto__ || (0, _getPrototypeOf2.default)(TestModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _BaseModel.apply(this, arguments)); | ||
} | ||
@@ -68,2 +64,14 @@ | ||
it('make sure intsance methods are enumerable', function () { | ||
// See #29. | ||
var enumerableProps = {}; | ||
for (var propName in Model) { | ||
// eslint-disable-line | ||
enumerableProps[propName] = true; | ||
} | ||
expect(enumerableProps.create).to.be.true; | ||
}); | ||
it('session getter works correctly', function () { | ||
@@ -93,3 +101,3 @@ expect(Model.session).to.be.undefined; | ||
(0, _classCallCheck3.default)(this, TestModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (TestModel.__proto__ || (0, _getPrototypeOf2.default)(TestModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _BaseModel2.apply(this, arguments)); | ||
} | ||
@@ -96,0 +104,0 @@ |
@@ -7,6 +7,2 @@ 'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
@@ -58,3 +54,3 @@ | ||
(0, _classCallCheck3.default)(this, A); | ||
return (0, _possibleConstructorReturn3.default)(this, (A.__proto__ || (0, _getPrototypeOf2.default)(A)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model.apply(this, arguments)); | ||
} | ||
@@ -72,3 +68,3 @@ | ||
(0, _classCallCheck3.default)(this, B); | ||
return (0, _possibleConstructorReturn3.default)(this, (B.__proto__ || (0, _getPrototypeOf2.default)(B)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model2.apply(this, arguments)); | ||
} | ||
@@ -97,3 +93,3 @@ | ||
(0, _classCallCheck3.default)(this, A); | ||
return (0, _possibleConstructorReturn3.default)(this, (A.__proto__ || (0, _getPrototypeOf2.default)(A)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model3.apply(this, arguments)); | ||
} | ||
@@ -111,3 +107,3 @@ | ||
(0, _classCallCheck3.default)(this, B); | ||
return (0, _possibleConstructorReturn3.default)(this, (B.__proto__ || (0, _getPrototypeOf2.default)(B)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model4.apply(this, arguments)); | ||
} | ||
@@ -136,3 +132,3 @@ | ||
(0, _classCallCheck3.default)(this, A); | ||
return (0, _possibleConstructorReturn3.default)(this, (A.__proto__ || (0, _getPrototypeOf2.default)(A)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model5.apply(this, arguments)); | ||
} | ||
@@ -150,3 +146,3 @@ | ||
(0, _classCallCheck3.default)(this, B); | ||
return (0, _possibleConstructorReturn3.default)(this, (B.__proto__ || (0, _getPrototypeOf2.default)(B)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model6.apply(this, arguments)); | ||
} | ||
@@ -206,3 +202,3 @@ | ||
var initialState = {}; | ||
var session = orm.from(initialState); | ||
var session = orm.session(initialState); | ||
expect(session).to.be.instanceOf(_.Session); | ||
@@ -209,0 +205,0 @@ }); |
'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
@@ -11,6 +7,2 @@ | ||
var _createClass2 = require('babel-runtime/helpers/createClass'); | ||
var _createClass3 = _interopRequireDefault(_createClass2); | ||
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); | ||
@@ -174,13 +166,11 @@ | ||
(0, _classCallCheck3.default)(this, CustomQuerySet); | ||
return (0, _possibleConstructorReturn3.default)(this, (CustomQuerySet.__proto__ || (0, _getPrototypeOf2.default)(CustomQuerySet)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _QuerySet.apply(this, arguments)); | ||
} | ||
(0, _createClass3.default)(CustomQuerySet, [{ | ||
key: 'unreleased', | ||
value: function unreleased() { | ||
return this.filter(function (book) { | ||
return book.releaseYear > currentYear; | ||
}); | ||
} | ||
}]); | ||
CustomQuerySet.prototype.unreleased = function unreleased() { | ||
return this.filter(function (book) { | ||
return book.releaseYear > currentYear; | ||
}); | ||
}; | ||
return CustomQuerySet; | ||
@@ -187,0 +177,0 @@ }(_.QuerySet); |
@@ -56,3 +56,3 @@ 'use strict'; | ||
var session = orm.from(emptyState); | ||
var session = orm.session(emptyState); | ||
@@ -76,3 +76,3 @@ expect(session.Book.session).to.equal(session); | ||
it('marks accessed models', function () { | ||
var session = orm.from(emptyState); | ||
var session = orm.session(emptyState); | ||
expect(session.accessedModels).to.have.length(0); | ||
@@ -79,0 +79,0 @@ |
@@ -8,6 +8,2 @@ 'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
@@ -114,3 +110,3 @@ | ||
(0, _classCallCheck3.default)(this, BookModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (BookModel.__proto__ || (0, _getPrototypeOf2.default)(BookModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model.apply(this, arguments)); | ||
} | ||
@@ -142,3 +138,3 @@ | ||
(0, _classCallCheck3.default)(this, AuthorModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (AuthorModel.__proto__ || (0, _getPrototypeOf2.default)(AuthorModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model2.apply(this, arguments)); | ||
} | ||
@@ -169,3 +165,3 @@ | ||
(0, _classCallCheck3.default)(this, CoverModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (CoverModel.__proto__ || (0, _getPrototypeOf2.default)(CoverModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model3.apply(this, arguments)); | ||
} | ||
@@ -186,3 +182,3 @@ | ||
(0, _classCallCheck3.default)(this, GenreModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (GenreModel.__proto__ || (0, _getPrototypeOf2.default)(GenreModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model4.apply(this, arguments)); | ||
} | ||
@@ -203,3 +199,3 @@ | ||
(0, _classCallCheck3.default)(this, PublisherModel); | ||
return (0, _possibleConstructorReturn3.default)(this, (PublisherModel.__proto__ || (0, _getPrototypeOf2.default)(PublisherModel)).apply(this, arguments)); | ||
return (0, _possibleConstructorReturn3.default)(this, _Model5.apply(this, arguments)); | ||
} | ||
@@ -206,0 +202,0 @@ |
{ | ||
"name": "redux-orm", | ||
"version": "0.9.0-rc.0", | ||
"version": "0.9.0-rc.1", | ||
"description": "Simple ORM to manage and query your state trees", | ||
@@ -25,2 +25,3 @@ "main": "lib/index.js", | ||
"babel-plugin-transform-runtime": "^6.8.0", | ||
"babel-plugin-transform-es2015-classes": "6.18.0", | ||
"babel-preset-es2015": "^6.6.0", | ||
@@ -27,0 +28,0 @@ "babel-preset-stage-2": "^6.5.0", |
@@ -31,3 +31,3 @@ redux-orm | ||
You can declare your models with the ES6 class syntax, extending from `Model`. You should declare all fields you want to persist for a model, including non-relational fields. `redux-orm` supports one-to-one and many-to-many relations in addition to foreign keys (`oneToOne`, `many` and `fk` imports respectively). Non-related properties can be accessed like in normal JavaScript objects. | ||
You can declare your models with the ES6 class syntax, extending from `Model`. You need to declare all your non-relational fields on the Model, and declaring all data fields is recommended as the library doesn't have to redefine getters and setters when instantiating Models. `redux-orm` supports one-to-one and many-to-many relations in addition to foreign keys (`oneToOne`, `many` and `fk` imports respectively). Non-related properties can be accessed like in normal JavaScript objects. | ||
@@ -48,3 +48,3 @@ ```javascript | ||
Book.fields = { | ||
id: attr(), // non-relational field for any value | ||
id: attr(), // non-relational field for any value; optional but highly recommended | ||
name: attr(), | ||
@@ -51,0 +51,0 @@ authors: many('Author', 'books'), |
@@ -7,2 +7,18 @@ import difference from 'lodash/difference'; | ||
function attrDescriptor(fieldName) { | ||
return { | ||
get() { | ||
return this._fields[fieldName]; | ||
}, | ||
set(value) { | ||
return this.set(fieldName, value); | ||
}, | ||
enumerable: true, | ||
configurable: true, | ||
}; | ||
} | ||
// Forwards side a Foreign Key: returns one object. | ||
@@ -181,2 +197,3 @@ // Also works as forwardsOneToOneDescriptor. | ||
export { | ||
attrDescriptor, | ||
forwardManyToOneDescriptor, | ||
@@ -183,0 +200,0 @@ forwardOneToOneDescriptor, |
import findKey from 'lodash/findKey'; | ||
import { | ||
attrDescriptor, | ||
forwardManyToOneDescriptor, | ||
@@ -31,3 +32,9 @@ backwardManyToOneDescriptor, | ||
install() {} | ||
install(model, fieldName, orm) { | ||
Object.defineProperty( | ||
model.prototype, | ||
fieldName, | ||
attrDescriptor(fieldName) | ||
); | ||
} | ||
}; | ||
@@ -64,3 +71,2 @@ | ||
); | ||
model.definedProperties[fieldName] = true; | ||
@@ -72,3 +78,8 @@ // Backwards. | ||
if (toModel.definedProperties[backwardsFieldName]) { | ||
const backwardsDescriptor = Object.getOwnPropertyDescriptor( | ||
toModel.prototype, | ||
backwardsFieldName | ||
); | ||
if (backwardsDescriptor) { | ||
const errorMsg = reverseFieldErrorMessage( | ||
@@ -88,3 +99,3 @@ model.modelName, | ||
); | ||
toModel.definedProperties[backwardsFieldName] = true; | ||
const ThisField = this.getClass(); | ||
@@ -154,3 +165,3 @@ toModel.virtualFields[backwardsFieldName] = new ThisField(model.modelName, fieldName); | ||
); | ||
model.definedProperties[fieldName] = true; | ||
model.virtualFields[fieldName] = new ManyToMany({ | ||
@@ -167,3 +178,8 @@ to: toModel.modelName, | ||
if (toModel.definedProperties[backwardsFieldName]) { | ||
const backwardsDescriptor = Object.getOwnPropertyDescriptor( | ||
toModel.prototype, | ||
backwardsFieldName | ||
); | ||
if (backwardsDescriptor) { | ||
// Backwards field was already defined on toModel. | ||
@@ -190,3 +206,2 @@ const errorMsg = reverseFieldErrorMessage( | ||
); | ||
toModel.definedProperties[backwardsFieldName] = true; | ||
toModel.virtualFields[backwardsFieldName] = new ManyToMany({ | ||
@@ -216,3 +231,2 @@ to: model.modelName, | ||
); | ||
model.definedProperties[fieldName] = true; | ||
@@ -224,3 +238,8 @@ // Backwards. | ||
if (toModel.definedProperties[backwardsFieldName]) { | ||
const backwardsDescriptor = Object.getOwnPropertyDescriptor( | ||
toModel.prototype, | ||
backwardsFieldName | ||
); | ||
if (backwardsDescriptor) { | ||
const errorMsg = reverseFieldErrorMessage( | ||
@@ -240,3 +259,2 @@ model.modelName, | ||
); | ||
toModel.definedProperties[backwardsFieldName] = true; | ||
toModel.virtualFields[backwardsFieldName] = new OneToOne(model.modelName, fieldName); | ||
@@ -248,3 +266,5 @@ } | ||
* Defines a value attribute on the model. | ||
* You need to define this for each non-foreign key you wish to use. | ||
* Though not required, it is recommended to define this for each non-foreign key you wish to use. | ||
* Getters and setters need to be defined on each Model | ||
* instantiation for undeclared data fields, which is slower. | ||
* You can use the optional `getDefault` parameter to fill in unpassed values | ||
@@ -251,0 +271,0 @@ * to {@link Model#create}, such as for generating ID's with UUID: |
@@ -69,25 +69,18 @@ import forOwn from 'lodash/forOwn'; | ||
_initFields(props) { | ||
const ModelClass = this.getClass(); | ||
const fieldsDef = this.getClass().fields; | ||
this._fields = Object.assign({}, props); | ||
const ThisModel = this.getClass(); | ||
forOwn(props, (fieldValue, fieldName) => { | ||
if (!fieldsDef.hasOwnProperty(fieldName)) { | ||
throw new Error( | ||
`Unexpected field given to ${ModelClass.modelName} constructor: ${fieldName}. ` + | ||
`If ${ModelClass.modelName} should accept this field, ` + | ||
'add an attr() field to it.' | ||
); | ||
} | ||
this._fields[fieldName] = fieldValue; | ||
// If the field has not already been defined on the | ||
// prototype for a relation. | ||
if (!ModelClass.definedProperties[fieldName]) { | ||
// In this case, we got a prop that wasn't defined as a field. | ||
// Assuming it's an arbitrary data field, making an instance-specific | ||
// descriptor for it. | ||
// Using the in operator as the property could be defined anywhere | ||
// on the prototype chain. | ||
if (!(fieldName in this)) { | ||
Object.defineProperty(this, fieldName, { | ||
get: () => this._fields[fieldName], | ||
set: (value) => this.set(fieldName, value), | ||
set: value => this.set(fieldName, value), | ||
configurable: true, | ||
enumerable: true, | ||
}); | ||
@@ -173,3 +166,2 @@ } | ||
this.isSetUp = undefined; | ||
this.definedProperties = {}; | ||
this.virtualFields = {}; | ||
@@ -204,8 +196,5 @@ } | ||
const allowedFieldNames = Object.keys(this.fields); | ||
const declaredFieldNames = Object.keys(this.fields); | ||
// We don't check for extra field values passed here; | ||
// the constructor will throw in that case. So we | ||
// only go through the defined fields. | ||
allowedFieldNames.forEach(key => { | ||
declaredFieldNames.forEach(key => { | ||
const field = this.fields[key]; | ||
@@ -419,21 +408,23 @@ const valuePassed = userProps.hasOwnProperty(key); | ||
const field = relFields[mergeKey]; | ||
if (field instanceof ManyToMany) { | ||
const currentIds = this[mergeKey].toRefArray() | ||
.map(row => row[ThisModel.idAttribute]); | ||
if (field) { | ||
if (field instanceof ManyToMany) { | ||
const currentIds = this[mergeKey].toRefArray() | ||
.map(row => row[ThisModel.idAttribute]); | ||
const normalizedNewIds = mergeObj[mergeKey].map(normalizeEntity); | ||
const diffActions = arrayDiffActions(currentIds, normalizedNewIds); | ||
if (diffActions) { | ||
const idsToDelete = diffActions.delete; | ||
const idsToAdd = diffActions.add; | ||
if (idsToDelete.length > 0) { | ||
this[mergeKey].remove(...idsToDelete); | ||
const normalizedNewIds = mergeObj[mergeKey].map(normalizeEntity); | ||
const diffActions = arrayDiffActions(currentIds, normalizedNewIds); | ||
if (diffActions) { | ||
const idsToDelete = diffActions.delete; | ||
const idsToAdd = diffActions.add; | ||
if (idsToDelete.length > 0) { | ||
this[mergeKey].remove(...idsToDelete); | ||
} | ||
if (idsToAdd.length > 0) { | ||
this[mergeKey].add(...idsToAdd); | ||
} | ||
} | ||
if (idsToAdd.length > 0) { | ||
this[mergeKey].add(...idsToAdd); | ||
} | ||
delete mergeObj[mergeKey]; | ||
} else if (field instanceof ForeignKey || field instanceof OneToOne) { | ||
mergeObj[mergeKey] = normalizeEntity(mergeObj[mergeKey]); | ||
} | ||
delete mergeObj[mergeKey]; | ||
} else if (field instanceof ForeignKey || field instanceof OneToOne) { | ||
mergeObj[mergeKey] = normalizeEntity(mergeObj[mergeKey]); | ||
} | ||
@@ -497,2 +488,11 @@ } | ||
} | ||
// DEPRECATED AND REMOVED METHODS | ||
getNextState() { | ||
throw new Error( | ||
'Model.prototype.getNextState is removed. See the 0.9 ' + | ||
'migration guide on the GitHub repo.' | ||
); | ||
} | ||
}; | ||
@@ -503,3 +503,2 @@ | ||
}; | ||
Model.definedProperties = {}; | ||
Model.virtualFields = {}; | ||
@@ -506,0 +505,0 @@ Model.querySetClass = QuerySet; |
@@ -27,2 +27,13 @@ import chai from 'chai'; | ||
it('make sure intsance methods are enumerable', () => { | ||
// See #29. | ||
const enumerableProps = {}; | ||
for (const propName in Model) { // eslint-disable-line | ||
enumerableProps[propName] = true; | ||
} | ||
expect(enumerableProps.create).to.be.true; | ||
}); | ||
it('session getter works correctly', () => { | ||
@@ -29,0 +40,0 @@ expect(Model.session).to.be.undefined; |
@@ -106,3 +106,3 @@ import chai from 'chai'; | ||
const initialState = {}; | ||
const session = orm.from(initialState); | ||
const session = orm.session(initialState); | ||
expect(session).to.be.instanceOf(Session); | ||
@@ -109,0 +109,0 @@ }); |
@@ -45,3 +45,3 @@ import chai from 'chai'; | ||
const session = orm.from(emptyState); | ||
const session = orm.session(emptyState); | ||
@@ -65,3 +65,3 @@ expect(session.Book.session).to.equal(session); | ||
it('marks accessed models', () => { | ||
const session = orm.from(emptyState); | ||
const session = orm.session(emptyState); | ||
expect(session.accessedModels).to.have.length(0); | ||
@@ -68,0 +68,0 @@ |
Sorry, the diff of this file is not supported yet
4978055
19
27901