Comparing version 0.10.4 to 0.11.0
## Change Log | ||
**0.11.0** <small>_Nov 15, 2017_</small> — [Diff](https://github.com/bookshelf/bookshelf/compare/0.10.4...0.11.0) | ||
- Moved `.babelrc` -> `src/.babelrc` [#1470](https://github.com/bookshelf/bookshelf/pull/1470) | ||
- Timestamp on save now utilizes a date option for timestamp updates on insert and update. [#1592](https://github.com/bookshelf/bookshelf/pull/1592) | ||
- Used in options on save like so: ```m.save({item: 'test'}, { date: dateInThePast })``` | ||
- Added `morphValues` for `morphTo` relation. [#1326](https://github.com/bookshelf/bookshelf/pull/1326) | ||
- Added ability to also set timestamps as model attributes in save. # | ||
- Removed non-production files from packaging / added them to .npmignore [#1679](https://github.com/bookshelf/bookshelf/pull/1679) | ||
- Development Facing | ||
- Oracle tests only run when oracle is installed. | ||
- Refactoring on the registry plugin | ||
- Updated a lot of documents related to repo organization. | ||
**0.10.4** - <small>_Jul 17, 2017_</small> — [Diff](https://github.com/bookshelf/bookshelf/compare/0.10.3...0.10.4) | ||
@@ -4,0 +17,0 @@ |
@@ -137,2 +137,7 @@ 'use strict'; | ||
* | ||
* 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 }` | ||
*/ | ||
@@ -152,2 +157,6 @@ ModelBase.prototype.idAttribute = 'id'; | ||
* column name. | ||
* You can pass values for the timestamps columns as parameter in the | ||
* {@link Model#save save} method. This will prevent the automatic | ||
* update of the timestamps columns values during the {@link Model#save save} method, | ||
* while the final columns values will be the values you have specified. | ||
* | ||
@@ -513,2 +522,5 @@ */ | ||
* update is for. | ||
* @param {string} [options.date] | ||
* Either a Date object or ms since the epoch. Specify what date is used for | ||
* the timestamps updated. | ||
* | ||
@@ -520,3 +532,3 @@ * @returns {Object} A hash of timestamp attributes that were set. | ||
var now = new Date(); | ||
var now = (options || {}).date ? new Date(options.date) : new Date(); | ||
var attributes = {}; | ||
@@ -526,2 +538,5 @@ var method = this.saveMethod(options); | ||
var canEditUpdatedAtKey = (options || {}).editUpdatedAt != undefined ? options.editUpdatedAt : true; | ||
var canEditCreatedAtKey = (options || {}).editCreatedAt != undefined ? options.editCreatedAt : true; | ||
var _keys = (0, _slicedToArray3.default)(keys, 2), | ||
@@ -531,8 +546,8 @@ createdAtKey = _keys[0], | ||
if (updatedAtKey) { | ||
attributes[updatedAtKey] = this.attributes[updatedAtKey] ? new Date(this.attributes[updatedAtKey]) : now; | ||
if (updatedAtKey && canEditUpdatedAtKey) { | ||
attributes[updatedAtKey] = now; | ||
} | ||
if (createdAtKey && method === 'insert') { | ||
attributes[createdAtKey] = this.attributes[createdAtKey] ? new Date(this.attributes[createdAtKey]) : now; | ||
if (createdAtKey && method === 'insert' && canEditCreatedAtKey) { | ||
attributes[createdAtKey] = now; | ||
} | ||
@@ -539,0 +554,0 @@ |
@@ -124,3 +124,3 @@ 'use strict'; | ||
return Target.query('whereIn', idAttribute, ids).sync(options).select().tap(function (response) { | ||
var clone = relatedData.instance('morphTo', Target, { morphName: morphName, columnNames: columnNames }); | ||
var clone = relatedData.instance('morphTo', Target, { morphName: morphName, columnNames: columnNames, morphValue: type }); | ||
return _this3._eagerLoadHelper(response, relationName, { relatedData: clone }, options); | ||
@@ -127,0 +127,0 @@ }); |
'use strict'; | ||
var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); | ||
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/* eslint no-console: 0 */ | ||
@@ -24,6 +30,12 @@ | ||
// an error if none is matched. | ||
morphCandidate: function morphCandidate(candidates, foreignTable) { | ||
var Target = _.find(candidates, function (Candidate) { | ||
return _.result(Candidate.prototype, 'tableName') === foreignTable; | ||
}); | ||
morphCandidate: function morphCandidate(candidates, morphValue) { | ||
var _$find = _.find(candidates, function (_ref) { | ||
var _ref2 = (0, _slicedToArray3.default)(_ref, 2), | ||
tableName = _ref2[1]; | ||
return tableName === morphValue; | ||
}), | ||
_$find2 = (0, _slicedToArray3.default)(_$find, 1), | ||
Target = _$find2[0]; | ||
if (!Target) { | ||
@@ -30,0 +42,0 @@ throw new Error('The target polymorphic model was not found'); |
'use strict'; | ||
var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); | ||
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); | ||
var _typeof2 = require('babel-runtime/helpers/typeof'); | ||
@@ -45,2 +49,4 @@ | ||
var DEFAULT_TIMESTAMP_KEYS = ['created_at', 'updated_at']; | ||
/** | ||
@@ -476,2 +482,16 @@ * @class Model | ||
* | ||
* And with custom morphValues, the inverse of morphValue of | ||
* {@link Model#morphOne morphOne} and {@link Model#morphMany morphMany}, | ||
* where the `morphValues` may be optionally set to check against a | ||
* different value in the `_type` column than the {@link Model#tableName}; | ||
* for example, a more descriptive name, or a name that betters adheres to | ||
* whatever standard you are using for models. | ||
* | ||
* let Photo = bookshelf.Model.extend({ | ||
* tableName: 'photos', | ||
* imageable: function() { | ||
* return this.morphTo('imageable', [Site, "favicon"], [Post, "cover_photo"]); | ||
* } | ||
* }); | ||
* | ||
* @method Model#morphTo | ||
@@ -492,9 +512,19 @@ * | ||
candidates = void 0; | ||
if (_lodash2.default.isArray(arguments[1])) { | ||
columnNames = arguments[1]; | ||
if (_lodash2.default.isNil(arguments[1]) || _lodash2.default.isArray(arguments[1]) && _lodash2.default.isString(arguments[1][0])) { | ||
columnNames = arguments[1] || null; // may be `null` or `undefined` | ||
candidates = _lodash2.default.drop(arguments, 2); | ||
} else { | ||
columnNames = null; | ||
candidates = _lodash2.default.drop(arguments); | ||
candidates = _lodash2.default.drop(arguments, 1); | ||
} | ||
candidates = _lodash2.default.map(candidates, function (target) { | ||
if (_lodash2.default.isArray(target)) { | ||
return target; | ||
} | ||
// Set up the morphValue by default as the tableName | ||
return [target, _lodash2.default.result(target.prototype, 'tableName')]; | ||
}); | ||
return this._relation('morphTo', null, { morphName: morphName, columnNames: columnNames, candidates: candidates }).init(this); | ||
@@ -991,6 +1021,23 @@ }, | ||
// Obtain the keys for the timestamp columns | ||
var keys = _lodash2.default.isArray(this.hasTimestamps) ? this.hasTimestamps : DEFAULT_TIMESTAMP_KEYS; | ||
var _keys = (0, _slicedToArray3.default)(keys, 2), | ||
createdAtKey = _keys[0], | ||
updatedAtKey = _keys[1]; | ||
// Now set timestamps if appropriate. Extend `attrs` so that the | ||
// timestamps will be provided for a patch operation. | ||
if (this.hasTimestamps) { | ||
_lodash2.default.extend(attrs, this.timestamp(_lodash2.default.extend(options, { silent: true }))); | ||
//If some of the new attributes are value for update_at or created_at columns disable the possibility for the timestamp function to update the columns | ||
var editUpdatedAt = attrs[updatedAtKey] ? false : true; | ||
var editCreatedAt = attrs[createdAtKey] ? false : true; | ||
var additionalOptions = { | ||
silent: true, | ||
editUpdatedAt: editUpdatedAt, | ||
editCreatedAt: editCreatedAt | ||
}; | ||
_lodash2.default.extend(attrs, this.timestamp(_lodash2.default.extend(options, additionalOptions))); | ||
} | ||
@@ -997,0 +1044,0 @@ |
'use strict'; | ||
var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); | ||
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); | ||
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); | ||
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); | ||
var _inherits2 = require('babel-runtime/helpers/inherits'); | ||
var _inherits3 = _interopRequireDefault(_inherits2); | ||
var _lodash = require('lodash'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// Registry Plugin - | ||
@@ -47,2 +65,13 @@ // Create a central registry of model/collection constructors to | ||
var ModelNotResolved = function (_Error) { | ||
(0, _inherits3.default)(ModelNotResolved, _Error); | ||
function ModelNotResolved() { | ||
(0, _classCallCheck3.default)(this, ModelNotResolved); | ||
return (0, _possibleConstructorReturn3.default)(this, (ModelNotResolved.__proto__ || Object.getPrototypeOf(ModelNotResolved)).apply(this, arguments)); | ||
} | ||
return ModelNotResolved; | ||
}(Error); | ||
// Check the collection or module caches for a Model or Collection constructor, | ||
@@ -52,6 +81,8 @@ // returning if the input is not an object. Check for a collection first, | ||
// registered model, throwing an error if none are found. | ||
function resolveModel(input) { | ||
if (typeof input === 'string') { | ||
return bookshelf.collection(input) || bookshelf.model(input) || function () { | ||
throw new Error('The model ' + input + ' could not be resolved from the registry plugin.'); | ||
throw new ModelNotResolved('The model ' + input + ' could not be resolved from the registry plugin.'); | ||
}(); | ||
@@ -69,14 +100,7 @@ } | ||
var _relation = Model.prototype._relation; | ||
Model.prototype._relation = function (method, Target) { | ||
Model.prototype._relation = function () { | ||
// The second argument is always a model, so resolve it and call the original method. | ||
return _relation.apply(this, [method, resolveModel(Target)].concat((0, _lodash.drop)(arguments, 2))); | ||
return _relation.apply(this, (0, _lodash.update)(arguments, 1, resolveModel)); | ||
}; | ||
// The `through` method doesn't use `_relation` beneath, so we have to | ||
// re-implement it specifically | ||
var through = Model.prototype.through; | ||
Model.prototype.through = function (Target) { | ||
return through.apply(this, [resolveModel(Target)].concat((0, _lodash.drop)(arguments))); | ||
}; | ||
// `morphTo` takes the relation name first, and then a variadic set of models so we | ||
@@ -86,12 +110,53 @@ // can't include it with the rest of the relational methods. | ||
Model.prototype.morphTo = function (relationName) { | ||
return morphTo.apply(this, [relationName].concat((0, _lodash.map)((0, _lodash.drop)(arguments), function (model) { | ||
return resolveModel(model); | ||
}))); | ||
var candidates = void 0, | ||
columnNames = void 0; | ||
if ((0, _lodash.isArray)(arguments[1]) || (0, _lodash.isNil)(arguments[1])) { | ||
columnNames = arguments[1]; // may be `null` or `undefined` | ||
candidates = (0, _lodash.drop)(arguments, 2); | ||
} else { | ||
columnNames = null; | ||
candidates = (0, _lodash.drop)(arguments, 1); | ||
} | ||
// try to use the columnNames as target instead | ||
if ((0, _lodash.isArray)(columnNames)) { | ||
try { | ||
columnNames[0] = resolveModel(columnNames[0]); | ||
} catch (err) { | ||
// if it did not work, they were real columnNames | ||
if (err instanceof ModelNotResolved) { | ||
throw err; | ||
} | ||
} | ||
} | ||
var models = (0, _lodash.map)(candidates, function (target) { | ||
if ((0, _lodash.isArray)(target)) { | ||
var _target = (0, _slicedToArray3.default)(target, 2), | ||
model = _target[0], | ||
morphValue = _target[1]; | ||
return [resolveModel(model), morphValue]; | ||
} else { | ||
return resolveModel(target); | ||
} | ||
}); | ||
return morphTo.apply(this, [relationName, columnNames].concat(models)); | ||
}; | ||
// The `through` method exists on the Collection as well, for `hasMany` / `belongsToMany` through relations. | ||
// The `through` method doesn't use `_relation` beneath, so we have to | ||
// re-implement it specifically | ||
var modelThrough = Model.prototype.through; | ||
Model.prototype.through = function () { | ||
// The first argument is the model | ||
return modelThrough.apply(this, (0, _lodash.update)(arguments, 0, resolveModel)); | ||
}; | ||
// The `through` method exists on the Collection as well, for | ||
// `hasMany` / `belongsToMany` through relations. | ||
var collectionThrough = Collection.prototype.through; | ||
Collection.prototype.through = function (Target) { | ||
return collectionThrough.apply(this, [resolveModel(Target)].concat((0, _lodash.drop)(arguments))); | ||
Collection.prototype.through = function () { | ||
// The first argument is the model | ||
return collectionThrough.apply(this, (0, _lodash.update)(arguments, 0, resolveModel)); | ||
}; | ||
}; |
@@ -143,3 +143,3 @@ 'use strict'; | ||
case 'morphValue': | ||
this[keyName] = this.parentTableName || this.targetTableName; | ||
this[keyName] = this.morphValue || this.parentTableName || this.targetTableName; | ||
break; | ||
@@ -146,0 +146,0 @@ } |
{ | ||
"name": "bookshelf", | ||
"version": "0.10.4", | ||
"version": "0.11.0", | ||
"description": "A lightweight ORM for PostgreSQL, MySQL, and SQLite3", | ||
@@ -12,3 +12,3 @@ "main": "bookshelf.js", | ||
"cover": "npm run lint && istanbul cover _mocha -- --check-leaks -t 10000 -b -R spec test/index.js", | ||
"test": "npm run lint && mocha --check-leaks -t 10000 -b test/index.js", | ||
"test": "npm run build && npm run lint && mocha --check-leaks -t 10000 -b test/index.js", | ||
"jsdoc": "./scripts/jsdoc.sh", | ||
@@ -44,3 +44,3 @@ "gh-pages": "./scripts/gh-pages.sh", | ||
"babel-cli": "^6.0.15", | ||
"babel-eslint": "^6.1.2", | ||
"babel-eslint": "^8.0.1", | ||
"babel-plugin-syntax-object-rest-spread": "^6.0.14", | ||
@@ -47,0 +47,0 @@ "babel-plugin-transform-object-rest-spread": "^6.0.14", |
@@ -13,2 +13,4 @@ # [bookshelf.js](http://bookshelfjs.org) | ||
## [Read the discussion about the future of bookshelf.js](https://github.com/bookshelf/bookshelf/issues/1600) | ||
## Introduction | ||
@@ -27,3 +29,3 @@ | ||
```js | ||
$ npm install knex --save | ||
$ npm install knex@0.13 --save | ||
$ npm install bookshelf --save | ||
@@ -125,2 +127,3 @@ | ||
* [bookshelf-advanced-serialization](https://github.com/sequiturs/bookshelf-advanced-serialization) - A more powerful visibility plugin, supporting serializing models and collections according to access permissions, application context, and after ensuring relations have been loaded. | ||
* [bookshelf-plugin-mode](https://github.com/popodidi/bookshelf-plugin-mode) - Plugin inspired by [Visibility](https://github.com/tgriesser/bookshelf/wiki/Plugin:-Visibility) plugin, providing functionality to specify different modes with corresponding visible/hidden fields of model. | ||
* [bookshelf-secure-password](https://github.com/venables/bookshelf-secure-password) - A plugin for easily securing passwords using bcrypt. | ||
@@ -153,7 +156,7 @@ | ||
Bookshelf uses its own copy of the "bluebird" promise library, you can read up here for more on debugging these promises... but in short, adding: | ||
process.stderr.on('data', function(data) { | ||
console.log(data); | ||
}); | ||
```js | ||
process.stderr.on('data', function(data) { | ||
console.log(data); | ||
}); | ||
``` | ||
At the start of your application code will catch any errors not otherwise caught in the normal promise chain handlers, which is very helpful in debugging. | ||
@@ -160,0 +163,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
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
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
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
180
2
255656
27
5469