Comparing version 0.12.1 to 0.13.0
## Change Log | ||
**0.13.0** <small>_Mar 18, 2018_</small> - [Diff](https://github.com/bookshelf/bookshelf/compare/0.12.1...0.13.0) | ||
#### Breaking changes | ||
- Make `require: true` the default when deleting models: [#1779](https://github.com/bookshelf/bookshelf/pull/1779) | ||
- Remove the second argument to the model's destroyed event handler: [#1777](https://github.com/bookshelf/bookshelf/pull/1777) | ||
- Events are now triggered sequentially even when the handlers execute asynchronous code: [#1768](https://github.com/bookshelf/bookshelf/pull/1768) | ||
- Drop support for Node versions older than 4: [#1696](https://github.com/bookshelf/bookshelf/pull/1696) | ||
- Reorder `saving` and `creating` events to reflect the documentation: [#1142](https://github.com/bookshelf/bookshelf/pull/1142) | ||
#### Enhancements | ||
- Only request `returning` attribute if client supports `returning`: [#1770](https://github.com/bookshelf/bookshelf/pull/1770) | ||
- Throw error if user doesn't pass a valid Knex instance on initialize: [#1756](https://github.com/bookshelf/bookshelf/pull/1756) | ||
- Add parameterized virtual properties to virtuals plugin: [#1755](https://github.com/bookshelf/bookshelf/pull/1755) | ||
- Add individual attribute processor plugin to core: [#1741](https://github.com/bookshelf/bookshelf/pull/1741) | ||
- Format `idAttribute` on save and delete: [#1680](https://github.com/bookshelf/bookshelf/pull/1680) | ||
- Add `withSchema` option to all database operations: [#1638](https://github.com/bookshelf/bookshelf/pull/1638) | ||
- Add a case converter plugin to core: [#1093](https://github.com/bookshelf/bookshelf/pull/1093) | ||
#### Bug fixes | ||
- Fix inconsistent timestamp values between save and fetch: [#1784](https://github.com/bookshelf/bookshelf/pull/1784) | ||
- Set `model.id` if attributes being `.set()` contain a parsed version of `idAttribute`: [#1760](https://github.com/bookshelf/bookshelf/pull/1760) | ||
- Fix pagination plugin's `fetchPage()` ignoring or hanging with transactions: [#1625](https://github.com/bookshelf/bookshelf/pull/1625) | ||
- Fix `fetchPage()` from pagination plugin not working for relation collections: [#1561](https://github.com/bookshelf/bookshelf/pull/1561) | ||
- Don't try to update `idAttribute` if it hasn't changed: [#1260](https://github.com/bookshelf/bookshelf/pull/1260) | ||
#### Test suite | ||
- Increase timeout of the large arrays test: [#1778](https://github.com/bookshelf/bookshelf/pull/1778) | ||
- Add test to verify that `parentId` is not undefined when using `fetchAll` with relations: [#1769](https://github.com/bookshelf/bookshelf/pull/1769) | ||
- Fixes and general improvements to the test suite: [#1753](https://github.com/bookshelf/bookshelf/pull/1753) | ||
- Remove OracleDB tests: [#1744](https://github.com/bookshelf/bookshelf/pull/1744) | ||
- Fix invalid test related to dirty attributes: [#1312](https://github.com/bookshelf/bookshelf/pull/1312) | ||
#### Documentation | ||
- Improve docs about running tests: [#1761](https://github.com/bookshelf/bookshelf/pull/1761) | ||
- Fix typo on parse-and-format tutorial: [#1748](https://github.com/bookshelf/bookshelf/pull/1748) | ||
- Add Bookshelf Manager to list of community plugins: [#1747](https://github.com/bookshelf/bookshelf/pull/1747) | ||
#### Dependencies | ||
- Update some dependencies: [#1787](https://github.com/bookshelf/bookshelf/pull/1787), [#1782](https://github.com/bookshelf/bookshelf/pull/1782), [#1780](https://github.com/bookshelf/bookshelf/pull/1780), [#1767](https://github.com/bookshelf/bookshelf/pull/1767) [#1746](https://github.com/bookshelf/bookshelf/pull/1746), [#1730](https://github.com/bookshelf/bookshelf/pull/1730) | ||
**0.12.1** <small>_Jan 8, 2018_</small> - [Diff](https://github.com/bookshelf/bookshelf/compare/0.12.0...0.12.1) | ||
@@ -4,0 +48,0 @@ |
@@ -613,3 +613,8 @@ 'use strict'; | ||
CollectionBase.prototype.clone = function () { | ||
return new this.constructor(this.models, _lodash2.default.pick(this, collectionProps)); | ||
// Iterate over the selected list of collection properties and invoke `clone` for | ||
// each property that has a method for that porpose. | ||
var clonedProps = (0, _lodash2.default)(this).pick(collectionProps).mapValues(function (val) { | ||
return val && typeof val.clone === 'function' ? val.clone() : val; | ||
}).value(); | ||
return new this.constructor(this.models, clonedProps); | ||
}; | ||
@@ -616,0 +621,0 @@ |
@@ -175,3 +175,3 @@ 'use strict'; | ||
}); | ||
return _promise2.default.map(listeners, function (listener) { | ||
return _promise2.default.mapSeries(listeners, function (listener) { | ||
return listener.apply(_this5, args); | ||
@@ -178,0 +178,0 @@ }); |
@@ -15,2 +15,6 @@ 'use strict'; | ||
var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); | ||
var _defineProperty3 = _interopRequireDefault(_defineProperty2); | ||
var _lodash = require('lodash'); | ||
@@ -28,10 +32,9 @@ | ||
var _constants = require('../constants'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var PIVOT_PREFIX = '_pivot_'; // Base Model | ||
// List of attributes attached directly from the `options` passed to the constructor. | ||
// Base Model | ||
// --------------- | ||
var DEFAULT_TIMESTAMP_KEYS = ['created_at', 'updated_at']; | ||
// List of attributes attached directly from the `options` passed to the constructor. | ||
var modelProps = ['tableName', 'hasTimestamps']; | ||
@@ -141,12 +144,16 @@ | ||
* for each database row (typically an auto-incrementing primary key named | ||
* `"id"`). Note that if you are using {@link Model#parse parse} and {@link | ||
* `'id'`). Note that if you are using {@link Model#parse parse} and {@link | ||
* Model#format format} (to have your model's attributes in `camelCase`, | ||
* but your database's columns in `snake_case`, for example) this refers to | ||
* the name returned by parse (`myId`), not the database column (`my_id`). | ||
* the name returned by parse (`myId`), not the actual database column | ||
* (`my_id`). | ||
* | ||
* If the table you're working with does not have an Primary-Key in the form | ||
* of a single column - you'll have to override it with a getter that returns | ||
* null. (overriding with undefined does not cascade the default behavior of | ||
* the value `'id'`. | ||
* Such a getter in ES6 would look like `get idAttribute() { return null }` | ||
* You can also get the parsed id attribute value by using the model's | ||
* {@link Model#parsedIdAttribute parsedIdAttribute} method. | ||
* | ||
* If the table you're working with does not have a Primary-Key in the form | ||
* of a single column you'll have to override it with a getter that returns | ||
* `null`. Overriding with `undefined` does not cascade the default behavior of | ||
* the value `'id'`. Such a getter in ES6 would look like | ||
* `get idAttribute() { return null }`. | ||
*/ | ||
@@ -176,2 +183,29 @@ ModelBase.prototype.idAttribute = 'id'; | ||
* @method | ||
* @private | ||
* @description | ||
* | ||
* Converts the timestamp keys to actual Date objects. This will not run if the | ||
* model doesn't have {@link Model#hasTimestamps hasTimestamps} set to either | ||
* `true` or an array of key names. | ||
* This method is run internally when reading data from the database to ensure | ||
* data consistency between the several database implementations. | ||
* It returns the model instance that called it, so it allows chaining of other | ||
* model methods. | ||
* | ||
* @returns {Model} The model that called this. | ||
*/ | ||
ModelBase.prototype.formatTimestamps = function formatTimestamps() { | ||
var _this = this; | ||
if (!this.hasTimestamps) return this; | ||
this.getTimestampKeys().forEach(function (key) { | ||
_this.set(key, new Date(_this.get(key))); | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* @method | ||
* @description Get the current value of an attribute from the model. | ||
@@ -189,2 +223,31 @@ * @example note.get("title"); | ||
* @method | ||
* @private | ||
* @description | ||
* | ||
* Returns the model's {@link Model#idAttribute idAttribute} after applying the | ||
* model's {@link Model#parse parse} method to it. Doesn't mutate the original | ||
* value of {@link Model#idAttribute idAttribute} in any way. | ||
* | ||
* @example | ||
* | ||
* var Customer = bookshelf.Model.extend({ | ||
* idAttribute: 'id', | ||
* parse: function(attrs) { | ||
* return _.mapKeys(attrs, function(value, key) { | ||
* return 'parsed_' + key; | ||
* }); | ||
* } | ||
* }); | ||
* | ||
* customer.parsedIdAttribute() // 'parsed_id' | ||
* | ||
* @returns {mixed} Whatever value the parse method returns. | ||
*/ | ||
ModelBase.prototype.parsedIdAttribute = function () { | ||
var parsedAttributes = this.parse((0, _defineProperty3.default)({}, this.idAttribute, null)); | ||
return parsedAttributes && Object.keys(parsedAttributes)[0]; | ||
}; | ||
/** | ||
* @method | ||
* @description Set a hash of attributes (one or many) on the model. | ||
@@ -221,3 +284,3 @@ * @example | ||
// Check for changes of `id`. | ||
if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; | ||
if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];else if (this.parsedIdAttribute() in attrs) this.id = attrs[this.parsedIdAttribute()]; | ||
@@ -315,3 +378,3 @@ // For each `set` attribute, update or delete the current value. | ||
var pivotAttributes = (0, _lodash.mapKeys)(pivot, function (value, key) { | ||
return '' + PIVOT_PREFIX + key; | ||
return '' + _constants.PIVOT_PREFIX + key; | ||
}); | ||
@@ -390,12 +453,12 @@ | ||
* @example | ||
* | ||
* // Example of a "parse" to convert snake_case to camelCase, using `underscore.string` | ||
* // Example of a parser to convert snake_case to camelCase, using lodash | ||
* // This is just an example. You can use the built-in case-converter plugin | ||
* // to achieve the same functionality. | ||
* model.parse = function(attrs) { | ||
* return _.reduce(attrs, function(memo, val, key) { | ||
* memo[_.camelCase(key)] = val; | ||
* return memo; | ||
* }, {}); | ||
* return _.mapKeys(attrs, function(value, key) { | ||
* return _.camelCase(key); | ||
* }); | ||
* }; | ||
* | ||
* @param {Object} response Hash of attributes to parse. | ||
* @param {Object} attributes Hash of attributes to parse. | ||
* @returns {Object} Parsed attributes. | ||
@@ -537,3 +600,19 @@ */ | ||
* @method | ||
* @private | ||
* @description | ||
* | ||
* Returns the automatic timestamp key names set on this model. Note that this | ||
* will always return a value even if the model has {@link Model#hasTimestamps | ||
* hasTimestamps} set to `false`. In this case and when set to `true` the | ||
* return value will be the default names of `created_at` and `updated_at`. | ||
* | ||
* @returns {Array<string>} The two timestamp key names. | ||
*/ | ||
ModelBase.prototype.getTimestampKeys = function () { | ||
return Array.isArray(this.hasTimestamps) ? this.hasTimestamps : _constants.DEFAULT_TIMESTAMP_KEYS; | ||
}; | ||
/** | ||
* @method | ||
* @description | ||
* Sets the timestamp attributes on the model, if {@link Model#hasTimestamps | ||
@@ -563,10 +642,9 @@ * hasTimestamps} is set to `true` or an array. Check if the model {@link | ||
var method = this.saveMethod(options); | ||
var keys = _lodash2.default.isArray(this.hasTimestamps) ? this.hasTimestamps : DEFAULT_TIMESTAMP_KEYS; | ||
var canEditUpdatedAtKey = (options || {}).editUpdatedAt != undefined ? options.editUpdatedAt : true; | ||
var canEditCreatedAtKey = (options || {}).editCreatedAt != undefined ? options.editCreatedAt : true; | ||
var _keys = (0, _slicedToArray3.default)(keys, 2), | ||
createdAtKey = _keys[0], | ||
updatedAtKey = _keys[1]; | ||
var _getTimestampKeys = this.getTimestampKeys(), | ||
_getTimestampKeys2 = (0, _slicedToArray3.default)(_getTimestampKeys, 2), | ||
createdAtKey = _getTimestampKeys2[0], | ||
updatedAtKey = _getTimestampKeys2[1]; | ||
@@ -573,0 +651,0 @@ if (updatedAtKey && canEditUpdatedAtKey) { |
@@ -62,2 +62,10 @@ 'use strict'; | ||
// Clones a relation. Required by Pagination plugin. | ||
}, { | ||
key: 'clone', | ||
value: function clone() { | ||
return new this.constructor(null, null, this); | ||
} | ||
// Eager pair the models. | ||
@@ -64,0 +72,0 @@ |
@@ -50,2 +50,5 @@ 'use strict'; | ||
function Bookshelf(knex) { | ||
if (!knex || knex.name !== 'knex') { | ||
throw new Error('Invalid knex instance'); | ||
} | ||
var bookshelf = { | ||
@@ -56,3 +59,2 @@ VERSION: require('../package.json').version | ||
var Model = bookshelf.Model = _model2.default.extend({ | ||
_builder: builderFn, | ||
@@ -266,5 +268,52 @@ | ||
// Provides a nice, tested, standardized way of adding plugins to a | ||
// `Bookshelf` instance, injecting the current instance into the plugin, | ||
// which should be a module.exports. | ||
/** | ||
* @method Bookshelf#plugin | ||
* @memberOf Bookshelf | ||
* @description | ||
* | ||
* This method provides a nice, tested, standardized way of adding plugins | ||
* to a `Bookshelf` instance, injecting the current instance into the | ||
* plugin, which should be a `module.exports`. | ||
* | ||
* You can add a plugin by specifying a string with the name of the plugin | ||
* to load. In this case it will try to find a module. It will first check | ||
* for a match within the `bookshelf/plugins` directory. If nothing is | ||
* found it will pass the string to `require()`, so you can either require | ||
* an npm dependency by name or one of your own modules by relative path: | ||
* | ||
* bookshelf.plugin('./bookshelf-plugins/my-favourite-plugin'); | ||
* bookshelf.plugin('plugin-from-npm'); | ||
* | ||
* There are a few built-in plugins already, along with many independently | ||
* developed ones. See [the list of available plugins](#plugins). | ||
* | ||
* You can also provide an array of strings or functions, which is the same | ||
* as calling `bookshelf.plugin()` multiple times. In this case the same | ||
* options object will be reused: | ||
* | ||
* bookshelf.plugin(['registry', './my-plugins/special-parse-format']); | ||
* | ||
* Example plugin: | ||
* | ||
* // Converts all string values to lower case when setting attributes on a model | ||
* module.exports = function(bookshelf) { | ||
* bookshelf.Model = bookshelf.Model.extend({ | ||
* set: function(key, value, options) { | ||
* if (!key) return this; | ||
* if (typeof value === 'string') value = value.toLowerCase(); | ||
* return bookshelf.Model.prototype.set.call(this, key, value, options); | ||
* } | ||
* }); | ||
* } | ||
* | ||
* @param {string|array|Function} plugin | ||
* The plugin or plugins to add. If you provide a string it can | ||
* represent a built-in plugin, an npm package or a file somewhere on | ||
* your project. You can also pass a function as argument to add it as a | ||
* plugin. Finally, it's also possible to pass an array of strings or | ||
* functions to add them all at once. | ||
* @param {mixed} options | ||
* This can be anything you want and it will be passed directly to the | ||
* plugin as the second argument when loading it. | ||
*/ | ||
plugin: function plugin(_plugin, options) { | ||
@@ -271,0 +320,0 @@ var _this = this; |
@@ -446,3 +446,6 @@ 'use strict'; | ||
this.set(response, { silent: true, parse: true }).invokeMap('_reset'); | ||
this.set(response, { silent: true, parse: true }).invokeMap('formatTimestamps'); | ||
this.invokeMap('_reset'); | ||
if (relatedData && relatedData.isJoined()) { | ||
@@ -449,0 +452,0 @@ relatedData.parsePivot(this.models); |
@@ -6,2 +6,3 @@ 'use strict'; | ||
}); | ||
var PIVOT_PREFIX = exports.PIVOT_PREFIX = '_pivot_'; | ||
var PIVOT_PREFIX = exports.PIVOT_PREFIX = '_pivot_'; | ||
var DEFAULT_TIMESTAMP_KEYS = exports.DEFAULT_TIMESTAMP_KEYS = ['created_at', 'updated_at']; |
@@ -49,4 +49,2 @@ 'use strict'; | ||
var DEFAULT_TIMESTAMP_KEYS = ['created_at', 'updated_at']; | ||
/** | ||
@@ -1019,9 +1017,7 @@ * @class Model | ||
// Obtain the keys for the timestamp columns | ||
var keys = _lodash2.default.isArray(this.hasTimestamps) ? this.hasTimestamps : DEFAULT_TIMESTAMP_KEYS; | ||
var _getTimestampKeys = this.getTimestampKeys(), | ||
_getTimestampKeys2 = (0, _slicedToArray3.default)(_getTimestampKeys, 2), | ||
createdAtKey = _getTimestampKeys2[0], | ||
updatedAtKey = _getTimestampKeys2[1]; | ||
var _keys = (0, _slicedToArray3.default)(keys, 2), | ||
createdAtKey = _keys[0], | ||
updatedAtKey = _keys[1]; | ||
// Now set timestamps if appropriate. Extend `attrs` so that the | ||
@@ -1119,3 +1115,3 @@ // timestamps will be provided for a patch operation. | ||
*/ | ||
return this.triggerThen(method === 'insert' ? 'creating saving' : 'updating saving', this, attrs, options).bind(this).then(function () { | ||
return this.triggerThen(method === 'insert' ? 'saving creating' : 'saving updating', this, attrs, options).bind(this).then(function () { | ||
return sync[options.method](method === 'update' && options.patch ? attrs : this.attributes); | ||
@@ -1201,3 +1197,4 @@ }).then(function (resp) { | ||
* @param {bool} [options.require=true] | ||
* Throw a {@link Model.NoRowsDeletedError} if no records are affected by destroy. | ||
* Throw a {@link Model.NoRowsDeletedError} if no records are affected by destroy. This is | ||
* the default behavior as of version 0.13.0. | ||
* | ||
@@ -1242,3 +1239,3 @@ * @example | ||
}).then(function (affectedRows) { | ||
if (options.require && affectedRows === 0) { | ||
if (options.require !== false && affectedRows === 0) { | ||
throw new this.constructor.NoRowsDeletedError('No Rows Deleted'); | ||
@@ -1256,7 +1253,6 @@ } | ||
* @param {Model} model The model firing the event. | ||
* @param {Object} affectedRows Number of affected rows. | ||
* @param {Object} options Options object passed to {@link Model#destroy destroy}. | ||
* @returns {Promise} | ||
*/ | ||
return this.triggerThen('destroyed', this, affectedRows, options); | ||
return this.triggerThen('destroyed', this, options); | ||
}).then(this._reset); | ||
@@ -1452,3 +1448,5 @@ }), | ||
var relatedData = this.relatedData; | ||
this.set(this.parse(response[0]), { silent: true })._reset(); | ||
this.set(this.parse(response[0]), { silent: true }).formatTimestamps()._reset(); | ||
if (relatedData && relatedData.isJoined()) { | ||
@@ -1455,0 +1453,0 @@ relatedData.parsePivot([this]); |
@@ -48,3 +48,3 @@ 'use strict'; | ||
module.exports = function paginationPlugin(bookshelf) { | ||
var Model = bookshelf.Model; | ||
/** | ||
@@ -125,2 +125,3 @@ * @method Model#fetchPage | ||
fetchOptions = (0, _objectWithoutProperties3.default)(options, ['page', 'pageSize', 'limit', 'offset']); | ||
var transacting = fetchOptions.transacting; | ||
@@ -161,8 +162,19 @@ | ||
var tableName = this.constructor.prototype.tableName; | ||
var idAttribute = this.constructor.prototype.idAttribute ? this.constructor.prototype.idAttribute : 'id'; | ||
var isModel = this instanceof Model; | ||
var _ref = isModel ? ['fetchAll', this.constructor] : ['fetch', this.target || this.model], | ||
_ref2 = (0, _slicedToArray3.default)(_ref, 2), | ||
fetchMethodName = _ref2[0], | ||
targetModel = _ref2[1]; | ||
var _targetModel$prototyp = targetModel.prototype, | ||
tableName = _targetModel$prototyp.tableName, | ||
_targetModel$prototyp2 = _targetModel$prototyp.idAttribute, | ||
idAttribute = _targetModel$prototyp2 === undefined ? 'id' : _targetModel$prototyp2; | ||
var targetIdColumn = tableName + '.' + idAttribute; | ||
var paginate = function paginate() { | ||
// const pageQuery = clone(this.query()); | ||
var pager = _this.constructor.forge(); | ||
var pager = _this.clone(); | ||
@@ -174,3 +186,3 @@ return pager.query(function (qb) { | ||
return null; | ||
}).fetchAll(fetchOptions); | ||
})[fetchMethodName](fetchOptions); | ||
}; | ||
@@ -180,3 +192,3 @@ | ||
var notNeededQueries = ['orderByBasic', 'orderByRaw', 'groupByBasic', 'groupByRaw']; | ||
var counter = _this.constructor.forge(); | ||
var counter = _this.clone(); | ||
@@ -192,4 +204,9 @@ return counter.query(function (qb) { | ||
}); | ||
qb.countDistinct.apply(qb, [tableName + '.' + idAttribute]); | ||
}).fetchAll().then(function (result) { | ||
if (!isModel && counter.relatedData) { | ||
// Remove joining columns that break COUNT operation. | ||
// eg. pivotal coulmns for belongsToMany relation. | ||
counter.relatedData.joinColumns = _lodash.noop; | ||
} | ||
qb.countDistinct.apply(qb, [targetIdColumn]); | ||
})[fetchMethodName]({ transacting: transacting }).then(function (result) { | ||
@@ -214,6 +231,6 @@ var metadata = usingPageSize ? { page: _page, pageSize: _limit } : { offset: _offset, limit: _limit }; | ||
return _bluebird2.default.join(paginate(), count()).then(function (_ref) { | ||
var _ref2 = (0, _slicedToArray3.default)(_ref, 2), | ||
rows = _ref2[0], | ||
metadata = _ref2[1]; | ||
return _bluebird2.default.join(paginate(), count()).then(function (_ref3) { | ||
var _ref4 = (0, _slicedToArray3.default)(_ref3, 2), | ||
rows = _ref4[0], | ||
metadata = _ref4[1]; | ||
@@ -234,9 +251,3 @@ var pageCount = Math.ceil(metadata.rowCount / _limit); | ||
bookshelf.Collection.prototype.fetchPage = function () { | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return fetchPage.apply(this.model.forge(), args); | ||
}; | ||
bookshelf.Collection.prototype.fetchPage = fetchPage; | ||
}; |
@@ -61,3 +61,3 @@ 'use strict'; | ||
if (options && options.virtuals === true || this.outputVirtuals) { | ||
attrs = _lodash2.default.extend(attrs, getVirtuals(this)); | ||
attrs = _lodash2.default.extend(attrs, getVirtuals(this, options && options.virtualParams)); | ||
} | ||
@@ -70,6 +70,10 @@ } | ||
get: function get(attr) { | ||
for (var _len = arguments.length, params = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
params[_key - 1] = arguments[_key]; | ||
} | ||
var virtuals = this.virtuals; | ||
if (_lodash2.default.isObject(virtuals) && virtuals[attr]) { | ||
return getVirtual(this, attr); | ||
return getVirtual.apply(undefined, [this, attr].concat(params)); | ||
} | ||
@@ -81,3 +85,2 @@ return proto.get.apply(this, arguments); | ||
set: function set(key, value, options) { | ||
if (key == null) { | ||
@@ -168,3 +171,3 @@ return this; | ||
// Underscore methods that we want to implement on the Model. | ||
// Lodash methods that we want to implement on the Model. | ||
var modelMethods = ['keys', 'values', 'toPairs', 'invert', 'pick', 'omit']; | ||
@@ -183,7 +186,13 @@ | ||
if (_lodash2.default.isObject(virtuals) && virtuals[virtualName]) { | ||
return virtuals[virtualName].get ? virtuals[virtualName].get.call(model) : virtuals[virtualName].call(model); | ||
var _virtuals$virtualName, _virtuals$virtualName2; | ||
for (var _len2 = arguments.length, params = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { | ||
params[_key2 - 2] = arguments[_key2]; | ||
} | ||
return virtuals[virtualName].get ? (_virtuals$virtualName = virtuals[virtualName].get).call.apply(_virtuals$virtualName, [model].concat(params)) : (_virtuals$virtualName2 = virtuals[virtualName]).call.apply(_virtuals$virtualName2, [model].concat(params)); | ||
} | ||
} | ||
function getVirtuals(model) { | ||
function getVirtuals(model, params) { | ||
var virtuals = model.virtuals; | ||
@@ -194,3 +203,4 @@ | ||
for (var virtualName in virtuals) { | ||
attrs[virtualName] = getVirtual(model, virtualName); | ||
var paramsForVirtual = (typeof params === 'undefined' ? 'undefined' : (0, _typeof3.default)(params)) === 'object' && params !== null ? params[virtualName] : undefined; | ||
attrs[virtualName] = getVirtual(model, virtualName, paramsForVirtual); | ||
} | ||
@@ -197,0 +207,0 @@ } |
@@ -431,3 +431,3 @@ 'use strict'; | ||
model.attributes = model.parse(model.attributes); | ||
model._reset(); | ||
model.formatTimestamps()._reset(); | ||
}); | ||
@@ -434,0 +434,0 @@ |
'use strict'; | ||
var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); | ||
var _defineProperty3 = _interopRequireDefault(_defineProperty2); | ||
var _lodash = require('lodash'); | ||
@@ -13,2 +17,8 @@ | ||
// Sync | ||
// --------------- | ||
var supportsReturning = function supportsReturning(client) { | ||
return _lodash2.default.includes(['postgresql', 'postgres', 'pg', 'oracle', 'mssql'], client); | ||
}; | ||
// Sync is the dispatcher for any database queries, | ||
@@ -19,4 +29,2 @@ // taking the "syncing" `model` or `collection` being queried, along with | ||
// part of a transaction, and this information is passed along to `Knex`. | ||
// Sync | ||
// --------------- | ||
var Sync = function Sync(syncing, options) { | ||
@@ -29,2 +37,3 @@ options = options || {}; | ||
if (options.transacting) this.query.transacting(options.transacting); | ||
if (options.withSchema) this.query.withSchema(options.withSchema); | ||
}; | ||
@@ -202,3 +211,3 @@ | ||
var syncing = this.syncing; | ||
return this.query.insert(syncing.format(_lodash2.default.extend(Object.create(null), syncing.attributes)), syncing.idAttribute); | ||
return this.query.insert(syncing.format(_lodash2.default.extend(Object.create(null), syncing.attributes)), supportsReturning(this.query.client.config.client) ? syncing.idAttribute : null); | ||
}), | ||
@@ -210,7 +219,11 @@ | ||
query = this.query; | ||
if (syncing.id != null) query.where(syncing.idAttribute, syncing.id); | ||
if (syncing.id != null) query.where(syncing.format((0, _defineProperty3.default)({}, syncing.idAttribute, syncing.id))); | ||
if (_lodash2.default.filter(query._statements, { grouping: 'where' }).length === 0) { | ||
throw new Error('A model cannot be updated without a "where" clause or an idAttribute.'); | ||
} | ||
return query.update(syncing.format(_lodash2.default.extend(Object.create(null), attrs))); | ||
var updating = syncing.format(_lodash2.default.extend(Object.create(null), attrs)); | ||
if (syncing.id === updating[syncing.idAttribute]) { | ||
delete updating[syncing.idAttribute]; | ||
} | ||
return query.update(updating); | ||
}), | ||
@@ -222,3 +235,3 @@ | ||
syncing = this.syncing; | ||
if (syncing.id != null) query.where(syncing.idAttribute, syncing.id); | ||
if (syncing.id != null) query.where(syncing.format((0, _defineProperty3.default)({}, syncing.idAttribute, syncing.id))); | ||
if (_lodash2.default.filter(query._statements, { grouping: 'where' }).length === 0) { | ||
@@ -225,0 +238,0 @@ throw new Error('A model cannot be destroyed without a "where" clause or an idAttribute.'); |
{ | ||
"name": "bookshelf", | ||
"version": "0.12.1", | ||
"version": "0.13.0", | ||
"description": "A lightweight ORM for PostgreSQL, MySQL, and SQLite3", | ||
@@ -46,4 +46,4 @@ "main": "bookshelf.js", | ||
"bookshelf-jsdoc-theme": "^0.2.0", | ||
"chai": "^3.5.0", | ||
"eslint": "4.15.0", | ||
"chai": "4.0.2", | ||
"eslint": "^4.17.0", | ||
"istanbul": "^0.4.5", | ||
@@ -53,9 +53,9 @@ "jsdoc": "^3.4.0", | ||
"minimist": "^1.1.0", | ||
"mocha": "^3.0.2", | ||
"mocha": "^5.0.0", | ||
"mysql": "^2.5.2", | ||
"pg": "^6.1.0", | ||
"pg": "^7.4.1", | ||
"semver": "^5.0.3", | ||
"sinon": "^4.1.3", | ||
"sinon-chai": "^2.6.0", | ||
"sqlite3": "^3.0.5", | ||
"sinon-chai": "^3.0.0", | ||
"sqlite3": "^4.0.0", | ||
"uuid": "^3.1.0" | ||
@@ -79,3 +79,6 @@ }, | ||
"license": "MIT", | ||
"readmeFilename": "README.md" | ||
"readmeFilename": "README.md", | ||
"engines": { | ||
"node": ">=4" | ||
} | ||
} |
@@ -116,2 +116,4 @@ # [bookshelf.js](http://bookshelfjs.org) | ||
* [Pagination](https://github.com/bookshelf/bookshelf/wiki/Plugin:-Pagination): Adds `fetchPage` methods to use for pagination in place of `fetch` and `fetchAll`. | ||
* [Case Converter](https://github.com/bookshelf/bookshelf/wiki/Plugin:-Case-Converter): Handles the conversion between the database's snake_cased and a model's camelCased properties automatically. | ||
* [Processor](https://github.com/bookshelf/bookshelf/wiki/Plugin:-Processor): Allows defining custom processor functions that handle transformation of values whenever they are `.set()` on a model. | ||
@@ -133,2 +135,3 @@ ## Community plugins | ||
* [bookshelf-ez-fetch](https://github.com/DJAndries/bookshelf-ez-fetch) - Convenient fetching methods which allow for compact filtering, relation selection and error handling. | ||
* [bookshelf-manager](https://github.com/ericclemmons/bookshelf-manager) - Model & Collection manager to make it easy to create & save deep, nested JSON structures from API requests. | ||
@@ -169,6 +172,5 @@ ## Support | ||
The test suite looks for an environment variable called `BOOKSHELF_TEST` for the path to the database configuration. If you run the following command: `$ export BOOKSHELF_TEST='/path/to/your/bookshelf_config.js'`, replacing with the path to your config file, and the config file is valid, the test suite should run with npm test. | ||
See the [CONTRIBUTING](https://github.com/bookshelf/bookshelf/blob/master/.github/CONTRIBUTING.md#running-the-tests) | ||
document on GitHub. | ||
Also note that you will have to create the appropriate database(s) for the test suite to run. For example, with MySQL, you'll need to run the command `create database bookshelf_test;` in addition to exporting the correct test settings prior to running the test suite. | ||
### Can I use Bookshelf outside of Node.js? | ||
@@ -175,0 +177,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
285221
31
6019
187
0