Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

bookshelf

Package Overview
Dependencies
Maintainers
2
Versions
87
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bookshelf - npm Package Compare versions

Comparing version 0.9.4 to 0.9.5

src/plugins/pagination.js

2

bookshelf.js

@@ -7,5 +7,3 @@ /**

*
* version 0.9.4
*
*/
module.exports = require('./lib/bookshelf').default;
## Change Log
**0.9.5** — <small>_May 15, 2016_</small> — [Diff](https://github.com/tgriesser/bookshelf/compare/0.9.4...0.9.5)
* Add pagination plugin. #1183
* Fire {@link Model#event:fetched} on eagerly loaded relations. #1206
* Correct cloning of {@link Model#belongsToMany} decorated relations. #1222
* Update Knex to 0.11.x. #1227
* Update minimum lodash version. #1230
**0.9.4** — <small>_April 3, 2016_</small> — [Diff](https://github.com/tgriesser/bookshelf/compare/0.9.3...0.9.4)

@@ -4,0 +12,0 @@

@@ -92,3 +92,12 @@ 'use strict';

// be documented as a valid argument for consumer code.
var collectionProps = ['model', 'comparator', 'relatedData'];
//
// RE: 'attach', 'detach', 'updatePivot', 'withPivot', '_processPivot', '_processPlainPivot', '_processModelPivot'
// It's okay to whitelist also given method references to be copied when cloning
// a collection. These methods are present only when `relatedData` is present and
// its `type` is 'belongsToMany'. So it is safe to put them in the list and use them
// without any additional verification.
// These should not be documented as a valid arguments for consumer code.
var collectionProps = ['model', 'comparator', 'relatedData',
// `belongsToMany` pivotal collection properties
'attach', 'detach', 'updatePivot', 'withPivot', '_processPivot', '_processPlainPivot', '_processModelPivot'];

@@ -196,7 +205,7 @@ // Copied over from Backbone.

if (options.parse) models = this.parse(models, options);
var i = undefined,
l = undefined,
id = undefined,
model = undefined,
attrs = undefined;
var i = void 0,
l = void 0,
id = void 0,
model = void 0,
attrs = void 0;
var at = options.at;

@@ -203,0 +212,0 @@ var targetModel = this.model;

@@ -73,7 +73,7 @@ 'use strict';

var pendingDeferred = [];
for (var relationName in handled) {
pendingDeferred.push(this.eagerFetch(relationName, handled[relationName], _lodash2.default.extend({}, options, {
for (var _relationName in handled) {
pendingDeferred.push(this.eagerFetch(_relationName, handled[_relationName], _lodash2.default.extend({}, options, {
isEager: true,
withRelated: subRelated[relationName],
_beforeFn: withRelated[relationName] || _lodash.noop
withRelated: subRelated[_relationName],
_beforeFn: withRelated[_relationName] || _lodash.noop
})));

@@ -80,0 +80,0 @@ }

@@ -60,3 +60,3 @@ 'use strict';

var Events = (function (_EventEmitter) {
var Events = function (_EventEmitter) {
(0, _inherits3.default)(Events, _EventEmitter);

@@ -72,2 +72,3 @@

/**

@@ -220,5 +221,5 @@ * @method Events#on

return Events;
})(EventEmitter);
}(EventEmitter);
exports.default = Events;
exports.default = Events;

@@ -38,2 +38,3 @@ 'use strict';

var PIVOT_PREFIX = '_pivot_';

@@ -76,3 +77,3 @@ var DEFAULT_TIMESTAMP_KEYS = ['created_at', 'updated_at'];

* // Do something before the data is fetched from the database
* })
* });
*

@@ -86,4 +87,4 @@ * @see Events#on

*
* customer.off('fetched fetching')
* ship.off() // This will remove all event listeners
* customer.off('fetched fetching');
* ship.off(); // This will remove all event listeners
*

@@ -97,3 +98,3 @@ * @see Events#off

*
* ship.trigger('fetched')
* ship.trigger('fetched');
*

@@ -169,3 +170,3 @@ * @see Events#trigger

* @description Get the current value of an attribute from the model.
* @example note.get("title")
* @example note.get("title");
*

@@ -195,3 +196,3 @@ * @param {string} attribute - The name of the attribute to retrieve.

if (key == null) return this;
var attrs = undefined;
var attrs = void 0;

@@ -289,2 +290,3 @@ // Handle both `"key", value` and `{key: value}` -style arguments.

if (!shallow) {

@@ -372,3 +374,3 @@

* return _.reduce(attrs, function(memo, val, key) {
* memo[_.str.camelize(key)] = val;
* memo[_.camelCase(key)] = val;
* return memo;

@@ -529,2 +531,3 @@ * }, {});

if (updatedAtKey) {

@@ -659,3 +662,2 @@ attributes[updatedAtKey] = now;

* var Customer = bookshelf.Model.extend({
*
* initialize: function() {

@@ -672,15 +674,12 @@ * this.on('saving', this.validateSave);

* },
*
* }, {
*
* login: Promise.method(function(email, password) {
* if (!email || !password) throw new Error('Email and password are both required');
* return new this({email: email.toLowerCase().trim()}).fetch({require: true}).tap(function(customer) {
* return bcrypt.compareAsync(customer.get('password'), password)
* .then(function(res) {
* if (!res) throw new Error('Invalid password');
* });
* return bcrypt.compareAsync(password, customer.get('password'))
* .then(function(res) {
* if (!res) throw new Error('Invalid password');
* });
* });
* })
*
* });

@@ -705,5 +704,5 @@ *

* set: function() {
* ...
* // ...
* bookshelf.Model.prototype.set.apply(this, arguments);
* ...
* // ...
* }

@@ -710,0 +709,0 @@ * });

@@ -32,3 +32,3 @@ 'use strict';

var RelationBase = (function () {
var RelationBase = function () {
function RelationBase(type, Target, options) {

@@ -47,2 +47,3 @@ (0, _classCallCheck3.default)(this, RelationBase);

(0, _createClass3.default)(RelationBase, [{

@@ -73,3 +74,3 @@ key: 'instance',

return RelationBase;
})(); // Base Relation
}(); // Base Relation
// ---------------

@@ -79,2 +80,3 @@

(0, _lodash.assign)(RelationBase, { extend: _extend2.default });

@@ -49,6 +49,7 @@ 'use strict';

// All core modules required for the bookshelf instance.
function Bookshelf(knex) {
var bookshelf = {
VERSION: '0.9.4'
VERSION: require('../package.json').version
};

@@ -105,3 +106,3 @@

* // ...
* })
* });
*

@@ -116,2 +117,3 @@ * @param {(Model[])=} models

/**

@@ -138,2 +140,3 @@ * @method Model.count

/**

@@ -183,3 +186,3 @@ * @method Model.fetchAll

* {name: 'Person2'}
* ])
* ]);
*

@@ -232,3 +235,2 @@ * Promise.all(accounts.invoke('save')).then(function() {

* ], function(info) {
*
* // Some validation could take place here.

@@ -257,2 +259,3 @@ * return new Book(info).save({'shelf_id': model.id}, {transacting: t});

/**

@@ -351,2 +354,3 @@ * @callback Bookshelf~transactionCallback

// We've supplemented `Events` with a `triggerThen` method to allow for

@@ -353,0 +357,0 @@ // asynchronous event handling via promises. We also mix this into the

@@ -329,3 +329,5 @@ 'use strict';

* let qb = collection.query();
* qb.where({id: 1}).select().then(function(resp) {...
* qb.where({id: 1}).select().then(function(resp) {
* // ...
* });
*

@@ -335,3 +337,5 @@ * collection.query(function(qb) {

* }).fetch()
* .then(function(collection) {...
* .then(function(collection) {
* // ...
* });
*

@@ -342,3 +346,3 @@ * collection

* .then(function(collection) {
* ...
* // ...
* });

@@ -357,2 +361,35 @@ *

/**
* @method Collection#orderBy
* @since 0.9.3
* @description
*
* Specifies the column to sort on and sort order.
*
* The order parameter is optional, and defaults to 'ASC'. You may
* also specify 'DESC' order by prepending a hyphen to the sort column
* name. `orderBy("date", 'DESC')` is the same as `orderBy("-date")`.
*
* Unless specified using dot notation (i.e., "table.column"), the default
* table will be the table name of the model `orderBy` was called on.
*
* @example
*
* Cars.forge().orderBy('color', 'ASC').fetch()
* .then(function (rows) { // ...
*
* @param sort {string}
* Column to sort on
* @param order {string}
* Ascending ('ASC') or descending ('DESC') order
*/
orderBy: function orderBy() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _helpers2.default.orderBy.apply(_helpers2.default, [this].concat(args));
},
/**
* @method Collection#query

@@ -375,2 +412,3 @@ * @private

/**

@@ -377,0 +415,0 @@ * @method Collection#_handleResponse

@@ -52,2 +52,3 @@ 'use strict';

var getAttributeUnique = function getAttributeUnique(models, attribute) {

@@ -64,3 +65,3 @@ return (0, _lodash.uniq)((0, _lodash.map)(models, function (m) {

var EagerRelation = (function (_EagerBase) {
var EagerRelation = function (_EagerBase) {
(0, _inherits3.default)(EagerRelation, _EagerBase);

@@ -76,2 +77,3 @@

// Handles an eager loaded fetch, passing the name of the item we're fetching

@@ -115,2 +117,3 @@ // for, and any options needed for the current fetch.

var parentsByType = (0, _lodash.groupBy)(this.parent, function (model) {

@@ -140,18 +143,26 @@ return model.get(typeColumn);

value: function _eagerLoadHelper(response, relationName, handled, options) {
var _this4 = this;
var relatedModels = this.pushModels(relationName, handled, response);
var relatedData = handled.relatedData;
// If there is a response, fetch additional nested eager relations, if any.
if (response.length > 0 && options.withRelated) {
var relatedModel = relatedData.createModel();
return _promise2.default.try(function () {
// If there is a response, fetch additional nested eager relations, if any.
if (response.length > 0 && options.withRelated) {
var relatedModel = relatedData.createModel();
// If this is a `morphTo` relation, we need to do additional processing
// to ensure we don't try to load any relations that don't look to exist.
if (relatedData.type === 'morphTo') {
var withRelated = this._filterRelated(relatedModel, options);
if (withRelated.length === 0) return;
options = _lodash2.default.extend({}, options, { withRelated: withRelated });
// If this is a `morphTo` relation, we need to do additional processing
// to ensure we don't try to load any relations that don't look to exist.
if (relatedData.type === 'morphTo') {
var withRelated = _this4._filterRelated(relatedModel, options);
if (withRelated.length === 0) return;
options = _lodash2.default.extend({}, options, { withRelated: withRelated });
}
return new EagerRelation(relatedModels, response, relatedModel).fetch(options).return(response);
}
return new EagerRelation(relatedModels, response, relatedModel).fetch(options).return(response);
}
}).tap(function () {
return _promise2.default.map(relatedModels, function (model) {
return model.triggerThen('fetched', model, model.attributes, options);
});
});
}

@@ -178,4 +189,4 @@

return EagerRelation;
})(_eager2.default);
}(_eager2.default);
exports.default = EagerRelation;

@@ -84,6 +84,39 @@ 'use strict';

helpers.warn(a + ' has been deprecated, please use ' + b + ' instead');
},
orderBy: function orderBy(obj, sort, order) {
var tableName = void 0;
var idAttribute = void 0;
if (obj.model) {
tableName = obj.model.prototype.tableName;
idAttribute = obj.model.prototype.idAttribute ? obj.model.prototype.idAttribute : 'id';
} else {
tableName = obj.constructor.prototype.tableName;
idAttribute = obj.constructor.prototype.idAttribute ? obj.constructor.prototype.idAttribute : 'id';
}
var _sort = void 0;
if (sort && sort.indexOf('-') === 0) {
_sort = sort.slice(1);
} else if (sort) {
_sort = sort;
} else {
_sort = idAttribute;
}
var _order = order || (sort && sort.indexOf('-') === 0 ? 'DESC' : 'ASC');
if (_sort.indexOf('.') === -1) {
_sort = tableName + '.' + _sort;
}
return obj.query(function (qb) {
qb.orderBy(_sort, _order);
});
}
};
module.exports = helpers;

@@ -73,3 +73,3 @@ 'use strict';

*
* let Books = bookshelf.Model.extend({
* let Book = bookshelf.Model.extend({
* tableName: 'documents',

@@ -116,3 +116,3 @@ * constructor: function() {

* new Patient({id: 1}).related('record').fetch().then(function(model) {
* ...
* // ...
* });

@@ -122,3 +122,3 @@ *

* new Patient({id: 1}).record().fetch().then(function(model) {
* ...
* // ...
* });

@@ -145,2 +145,3 @@ *

/**

@@ -181,2 +182,3 @@ * The `hasMany` relation specifies that this model has one or more rows in

/**

@@ -225,2 +227,3 @@ * The `belongsTo` relationship is used when a model is a member of

/**

@@ -324,2 +327,3 @@ * Defines a many-to-many relation, where the current model is joined to one

/**

@@ -377,2 +381,3 @@ * The {@link Model#morphOne morphOne} is used to signify a {@link oneToOne

/**

@@ -430,2 +435,3 @@ * {@link Model#morphMany morphMany} is essentially the same as a {@link

/**

@@ -467,10 +473,10 @@ * The {@link Model#morphTo morphTo} relation is used to specify the inverse

if (!_lodash2.default.isString(morphName)) throw new Error('The `morphTo` name must be specified.');
var columnNames = undefined,
candidates = undefined;
var columnNames = void 0,
candidates = void 0;
if (_lodash2.default.isArray(arguments[1])) {
columnNames = arguments[1];
candidates = _lodash2.default.rest(arguments, 2);
candidates = _lodash2.default.drop(arguments, 2);
} else {
columnNames = null;
candidates = _lodash2.default.rest(arguments);
candidates = _lodash2.default.drop(arguments);
}

@@ -480,2 +486,3 @@ return this._relation('morphTo', null, { morphName: morphName, columnNames: columnNames, candidates: candidates }).init(this);

/**

@@ -492,3 +499,2 @@ * Helps to create dynamic relations between {@link Model models} and {@link

* let Book = bookshelf.Model.extend({
*
* tableName: 'books',

@@ -505,7 +511,5 @@ *

* }
*
* });
*
* let Chapter = bookshelf.Model.extend({
*
* tableName: 'chapters',

@@ -516,7 +520,5 @@ *

* }
*
* });
*
* let Paragraph = bookshelf.Model.extend({
*
* tableName: 'paragraphs',

@@ -532,3 +534,2 @@ *

* }
*
* });

@@ -561,2 +562,3 @@ *

/**

@@ -585,2 +587,3 @@ * @method Model#refresh

/**

@@ -652,3 +655,3 @@ * Fetches a {@link Model model} from the database, using any {@link

* @param {string|string[]} [options.columns='*']
* Specify columns to be retireved.
* Specify columns to be retrieved.
* @param {Transaction} [options.transacting]

@@ -676,2 +679,3 @@ * Optionally run the query in a transaction.

_doFetch: _promise2.default.method(function (attributes, options) {

@@ -735,2 +739,3 @@ options = options ? _lodash2.default.clone(options) : {};

/**

@@ -760,2 +765,3 @@ * @method Model#count

/**

@@ -823,2 +829,3 @@ * Fetches a collection of {@link Model models} from the database, using any

/**

@@ -935,3 +942,3 @@ * @method Model#load

save: _promise2.default.method(function (key, val, options) {
var attrs = undefined;
var attrs = void 0;

@@ -1135,3 +1142,2 @@ // Handle both `"key", value` and `{key: value}` -style arguments.

* @param {Model} model The model firing the event.
* @param {Object} attrs Model firing the event.
* @param {Object} options Options object passed to {@link Model#save save}.

@@ -1178,2 +1184,3 @@ * @returns {Promise}

/**

@@ -1207,6 +1214,10 @@ * The `query` method is used to tap into the underlying Knex query builder

* }).fetch()
* .then(function(model) { // ...
* .then(function(model) {
* // ...
* });
*
* let qb = model.query();
* qb.where({id: 1}).select().then(function(resp) { // ...
* qb.where({id: 1}).select().then(function(resp) {
* // ...
* });
*

@@ -1224,2 +1235,3 @@ * @method Model#query

/**

@@ -1259,2 +1271,36 @@ * The where method is used as convenience for the most common {@link

/**
* @method Model#orderBy
* @since 0.9.3
* @description
*
* Specifies the column to sort on and sort order.
*
* The order parameter is optional, and defaults to 'ASC'. You may
* also specify 'DESC' order by prepending a hyphen to the sort column
* name. `orderBy("date", 'DESC')` is the same as `orderBy("-date")`.
*
* Unless specified using dot notation (i.e., "table.column"), the default
* table will be the table name of the model `orderBy` was called on.
*
* @example
*
* Car.forge().orderBy('color', 'ASC').fetchAll()
* .then(function (rows) { // ...
*
* @param sort {string}
* Column to sort on
* @param order {string}
* Ascending ('ASC') or descending ('DESC') order
*/
orderBy: function orderBy() {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return _helpers2.default.orderBy.apply(_helpers2.default, [this].concat(args));
},
/* Ensure that QueryBuilder is copied on clone. */

@@ -1269,2 +1315,3 @@ clone: function clone() {

/**

@@ -1281,2 +1328,3 @@ * Creates and returns a new Bookshelf.Sync instance.

/**

@@ -1298,2 +1346,3 @@ * Helper for setting up the `morphOne` or `morphMany` relations.

/**

@@ -1319,2 +1368,3 @@ * @name Model#_handleResponse

/**

@@ -1321,0 +1371,0 @@ * @name Model#_handleEager

@@ -19,2 +19,6 @@ 'use strict';

var DEFAULT_LIMIT = 10;
var DEFAULT_OFFSET = 0;
var DEFAULT_PAGE = 1;
/**

@@ -46,199 +50,188 @@ * Exports a plugin to pass into the bookshelf instance, i.e.:

bookshelf.Model = bookshelf.Model.extend({
/**
* @method Model#fetchPage
* @belongsTo Model
*
* Similar to {@link Model#fetchAll}, but fetches a single page of results
* as specified by the limit (page size) and offset or page number.
*
* Any options that may be passed to {@link Model#fetchAll} may also be passed
* in the options to this method.
*
* To perform pagination, you may include *either* an `offset` and `limit`, **or**
* a `page` and `pageSize`.
*
* By default, with no parameters or missing parameters, `fetchPage` will use an
* options object of `{page: 1, pageSize: 10}`
*
*
* Below is an example showing the user of a JOIN query with sort/ordering,
* pagination, and related models.
*
* @example
*
* Car
* .query(function (qb) {
* qb.innerJoin('manufacturers', 'cars.manufacturer_id', 'manufacturers.id');
* qb.groupBy('cars.id');
* qb.where('manufacturers.country', '=', 'Sweden');
* })
* .orderBy('-productionYear') // Same as .orderBy('cars.productionYear', 'DESC')
* .fetchPage({
* pageSize: 15, // Defaults to 10 if not specified
* page: 3, // Defaults to 1 if not specified
*
* // OR
* // limit: 15,
* // offset: 30,
*
* withRelated: ['engine'] // Passed to Model#fetchAll
* })
* .then(function (results) {
* console.log(results); // Paginated results object with metadata example below
* })
*
* // Pagination results:
*
* {
* models: [<Car>], // Regular bookshelf Collection
* // other standard Collection attributes
* ...
* pagination: {
* rowCount: 53, // Total number of rows found for the query before pagination
* pageCount: 4, // Total number of pages of results
* page: 3, // The requested page number
* pageSze: 15, // The requested number of rows per page
*
* // OR, if limit/offset pagination is used instead of page/pageSize:
* // offset: 30, // The requested offset
* // limit: 15 // The requested limit
* }
* }
*
* @param options {object}
* The pagination options, plus any additional options that will be passed to
* {@link Model#fetchAll}
* @returns {Promise<Model|null>}
*/
function fetchPage() {
var _this = this;
/**
* @method Model#orderBy
* @since 0.9.3
* @description
*
* Specifies the column to sort on and sort order.
*
* The order parameter is optional, and defaults to 'ASC'. You may
* also specify 'DESC' order by prepending a hyphen to the sort column
* name. `orderBy("date", 'DESC')` is the same as `orderBy("-date")`.
*
* Unless specified using dot notation (i.e., "table.column"), the default
* table will be the table name of the model `orderBy` was called on.
*
* @example
*
* Cars.forge().orderBy('color', 'ASC').fetchAll()
* .then(function (rows) { // ...
*
* @param sort {string}
* Column to sort on
* @param order {string}
* Ascending ('ASC') or descending ('DESC') order
*/
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var page = options.page;
var pageSize = options.pageSize;
var limit = options.limit;
var offset = options.offset;
var fetchOptions = (0, _objectWithoutProperties3.default)(options, ['page', 'pageSize', 'limit', 'offset']);
orderBy: function orderBy(sort, order) {
var tableName = this.constructor.prototype.tableName;
var idAttribute = this.constructor.prototype.idAttribute ? this.constructor.prototype.idAttribute : 'id';
var _sort = undefined;
var usingPageSize = false; // usingPageSize = false means offset/limit, true means page/pageSize
var _page = void 0;
var _pageSize = void 0;
var _limit = void 0;
var _offset = void 0;
if (sort && sort.startsWith('-')) {
_sort = sort.slice(1);
} else if (sort) {
_sort = sort;
} else {
_sort = idAttribute;
}
function ensureIntWithDefault(val, def) {
if (!val) {
return def;
}
var _order = order || (sort && sort.startsWith('-') ? 'DESC' : 'ASC');
var _val = parseInt(val);
if (Number.isNaN(_val)) {
return def;
}
if (_sort.indexOf('.') === -1) {
_sort = tableName + '.' + _sort;
}
return _val;
}
return this.query(function (qb) {
qb.orderBy(_sort, _order);
});
},
if (!limit && !offset) {
usingPageSize = true;
/**
* @method Model#fetchPage
* @since 0.9.3
* @description
*
* Similar to {@link Model#fetchAll}, but fetches a single page of results
* as specified by the limit (page size) and offset or page number.
*
* Any options that may be passed to {@link Model#fetchAll} may also be passed
* in the options to this method.
*
* To perform pagination, include a `limit` and _either_ `offset` or `page`.
*
* The defaults are page 1 (offset 0) and limit 10 when no parameters or invalid
* parameters are passed.
*
* Below is an example showing the user of a JOIN query with sort/ordering,
* pagination, and related models.
*
* @example
*
* Car
* .query(function (qb) {
* qb.innerJoin('manufacturers', 'cars.manufacturer_id', 'manufacturers.id');
* qb.groupBy('cars.id');
* qb.where('manufacturers.country', '=', 'Sweden');
* })
* .orderBy('-productionYear') // Same as .orderBy('cars.productionYear', 'DESC')
* .fetchPage({
* limit: 15, // Defaults to 10 if not specified
* page: 3, // Defaults to 1 if not specified; same as {offset: 30} with limit of 15.
* withRelated: ['engine'] // Passed to Model#fetchAll
* })
* .then(function (results) {
* console.log(results); // Paginated results object with metadata example below
* })
*
* // Pagination results:
*
* {
* models: [<Car>], // Regular bookshelf Collection
* // other standard Collection attributes
* ...
* pagination: {
* rowCount: 15, // Would be less than 15 on the last page of results
* total: 53, // Total number of rows found for the query before pagination
* limit: 15, // The requested number of rows per page, same as rowCount except final page
* page: 3, // The requested page number
* offset: 30 // The requested offset, calculated from the page/limit if not provided
* }
* }
*
* @param options {object}
* The pagination options, plus any additional options that will be passed to
* {@link Model#fetchAll}
* @returns {Promise<Model|null>}
*/
fetchPage: function fetchPage() {
var _this = this;
_pageSize = ensureIntWithDefault(pageSize, DEFAULT_LIMIT);
_page = ensureIntWithDefault(page, DEFAULT_PAGE);
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var limit = options.limit;
var page = options.page;
var offset = options.offset;
var fetchOptions = (0, _objectWithoutProperties3.default)(options, ['limit', 'page', 'offset']);
_limit = _pageSize;
_offset = _limit * (_page - 1);
} else {
_pageSize = _limit; // not used, just for eslint `const` error
_limit = ensureIntWithDefault(limit, DEFAULT_LIMIT);
_offset = ensureIntWithDefault(offset, DEFAULT_OFFSET);
}
var _limit = parseInt(limit);
var _page = parseInt(page);
var _offset = parseInt(offset);
var tableName = this.constructor.prototype.tableName;
var idAttribute = this.constructor.prototype.idAttribute ? this.constructor.prototype.idAttribute : 'id';
if (Number.isNaN(_limit) || !Number.isInteger(_limit) || _limit < 0) {
_limit = 10;
}
var paginate = function paginate() {
// const pageQuery = clone(this.query());
var pager = _this.constructor.forge();
if (page && Number.isInteger(_page) && _page > 0) {
// Request by page number, calculate offset
_offset = _limit * (_page - 1);
} else if (offset && Number.isInteger(_offset) && _offset >= 0) {
// Request by offset, calculate page
_page = Math.floor(_offset / _limit) + 1;
} else {
// Defaults for erroneous or not defined page/offset
_page = 1;
_offset = 0;
}
return pager.query(function (qb) {
(0, _lodash.assign)(qb, _this.query().clone());
qb.limit.apply(qb, [_limit]);
qb.offset.apply(qb, [_offset]);
return null;
}).fetchAll(fetchOptions);
};
var tableName = this.constructor.prototype.tableName;
var idAttribute = this.constructor.prototype.idAttribute ? this.constructor.prototype.idAttribute : 'id';
var count = function count() {
var notNeededQueries = ['orderByBasic', 'orderByRaw', 'groupByBasic', 'groupByRaw'];
var counter = _this.constructor.forge();
var paginate = function paginate() {
// const pageQuery = clone(this.query());
var pager = _this.constructor.forge();
return counter.query(function (qb) {
(0, _lodash.assign)(qb, _this.query().clone());
return pager.query(function (qb) {
Object.assign(qb, _this.query().clone());
qb.limit.apply(qb, [_limit]);
qb.offset.apply(qb, [_offset]);
return null;
}).fetchAll(fetchOptions);
};
// Remove grouping and ordering. Ordering is unnecessary
// for a count, and grouping returns the entire result set
// What we want instead is to use `DISTINCT`
(0, _lodash.remove)(qb._statements, function (statement) {
return notNeededQueries.indexOf(statement.type) > -1 || statement.grouping === 'columns';
});
qb.countDistinct.apply(qb, [tableName + '.' + idAttribute]);
}).fetchAll().then(function (result) {
var count = function count() {
var notNeededQueries = ['orderByBasic', 'orderByRaw', 'groupByBasic', 'groupByRaw'];
var counter = _this.constructor.forge();
var metadata = usingPageSize ? { page: _page, pageSize: _limit } : { offset: _offset, limit: _limit };
return counter.query(function (qb) {
Object.assign(qb, _this.query().clone());
if (result && result.length == 1) {
// We shouldn't have to do this, instead it should be
// result.models[0].get('count')
// but SQLite uses a really strange key name.
var _count = result.models[0];
var keys = Object.keys(_count.attributes);
if (keys.length === 1) {
var key = Object.keys(_count.attributes)[0];
metadata.rowCount = parseInt(_count.attributes[key]);
}
}
// Remove grouping and ordering. Ordering is unnecessary
// for a count, and grouping returns the entire result set
// What we want instead is to use `DISTINCT`
qb.countDistinct.apply(qb, [tableName + '.' + idAttribute]);
(0, _lodash.remove)(qb._statements, function (statement) {
return notNeededQueries.indexOf(statement.type) > -1;
});
}).fetchAll().then(function (result) {
var metadata = { page: _page, limit: _limit, offset: _offset };
return metadata;
});
};
if (result && result.length == 1) {
// We shouldn't have to do this, instead it should be
// result.models[0].get('count')
// but SQLite uses a really strange key name.
var _count = result.models[0];
var keys = Object.keys(_count.attributes);
if (keys.length === 1) {
var key = Object.keys(_count.attributes)[0];
metadata.total = _count.attributes[key];
}
}
return _bluebird2.default.join(paginate(), count()).then(function (_ref) {
var _ref2 = (0, _slicedToArray3.default)(_ref, 2);
return metadata;
});
};
var rows = _ref2[0];
var metadata = _ref2[1];
return _bluebird2.default.join(paginate(), count()).then(function (_ref) {
var _ref2 = (0, _slicedToArray3.default)(_ref, 2);
var pageCount = Math.ceil(metadata.rowCount / _limit);
var pageData = (0, _lodash.assign)(metadata, { pageCount: pageCount });
return (0, _lodash.assign)(rows, { pagination: pageData });
});
}
var rows = _ref2[0];
var metadata = _ref2[1];
bookshelf.Model.prototype.fetchPage = fetchPage;
var pageData = Object.assign(metadata, { rowCount: rows.length });
return Object.assign(rows, { pagination: pageData });
});
}
});
bookshelf.Model.fetchPage = function () {
var _forge;
return (_forge = this.forge()).fetchPage.apply(_forge, arguments);
};
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.apply(fetchPage, [this.model.forge()].concat(args));
};
};

@@ -57,5 +57,5 @@ 'use strict';

if (typeof input === 'string') {
return bookshelf.collection(input) || bookshelf.model(input) || (function () {
return bookshelf.collection(input) || bookshelf.model(input) || function () {
throw new Error('The model ' + input + ' could not be resolved from the registry plugin.');
})();
}();
}

@@ -75,3 +75,3 @@ return input;

// The first argument is always a model, so resolve it and call the original method.
return original.apply(this, [resolveModel(Target)].concat(_lodash2.default.rest(arguments)));
return original.apply(this, [resolveModel(Target)].concat(_lodash2.default.drop(arguments)));
};

@@ -84,3 +84,3 @@ });

Model.prototype.morphTo = function (relationName) {
return morphTo.apply(this, [relationName].concat(_lodash2.default.map(_lodash2.default.rest(arguments), function (model) {
return morphTo.apply(this, [relationName].concat(_lodash2.default.map(_lodash2.default.drop(arguments), function (model) {
return resolveModel(model);

@@ -93,4 +93,4 @@ }, this)));

Collection.prototype.through = function (Target) {
return collectionThrough.apply(this, [resolveModel(Target)].concat(_lodash2.default.rest(arguments)));
return collectionThrough.apply(this, [resolveModel(Target)].concat(_lodash2.default.drop(arguments)));
};
};

@@ -34,4 +34,4 @@ 'use strict';

for (var virtualName in virtuals) {
var getter = undefined,
setter = undefined;
var getter = void 0,
setter = void 0;
if (virtuals[virtualName].get) {

@@ -112,3 +112,3 @@ getter = virtuals[virtualName].get;

save: function save(key, value, options) {
var attrs = undefined;
var attrs = void 0;

@@ -141,3 +141,3 @@ // Handle both `"key", value` and `{key: value}` -style arguments.

// `this.patchAttributes` instead of `this.attributes`.
_lodash2.default.each(attrs, (function (value, key) {
_lodash2.default.each(attrs, function (value, key) {

@@ -149,3 +149,3 @@ if (setVirtual.call(this, value, key)) {

}
}).bind(this));
}.bind(this));

@@ -152,0 +152,0 @@ // Now add any changes that occurred during the update.

@@ -224,3 +224,3 @@ 'use strict';

whereClauses: function whereClauses(knex, response) {
var key = undefined;
var key = void 0;

@@ -235,5 +235,5 @@ if (this.isJoined()) {

} else {
var column = this.isInverse() ? this.targetIdAttribute : this.key('foreignKey');
var _column = this.isInverse() ? this.targetIdAttribute : this.key('foreignKey');
key = this.targetTableName + '.' + column;
key = this.targetTableName + '.' + _column;
}

@@ -316,3 +316,3 @@

(0, _collection.each)(parentModels, function (model) {
var groupedKey = undefined;
var groupedKey = void 0;
if (!_this.isInverse()) {

@@ -339,2 +339,3 @@ groupedKey = model.id;

parsePivot: function parsePivot(models) {

@@ -399,3 +400,3 @@ var _this2 = this;

var singularMemo = (function () {
var singularMemo = function () {
var cache = Object.create(null);

@@ -408,3 +409,3 @@ return function (arg) {

};
})();
}();

@@ -459,2 +460,3 @@ // Specific to many-to-many relationships, these methods are mixed

/**

@@ -492,2 +494,3 @@ * Detach one or more related objects from their pivot tables. If a model or

/**

@@ -494,0 +497,0 @@ * The `updatePivot` method is used exclusively on {@link Model#belongsToMany

@@ -94,2 +94,15 @@ 'use strict';

}).then(function () {
options.query = knex;
/**
* Counting event.
*
* Fired before a `count` query. A promise may be
* returned from the event handler for async behaviour.
*
* @event Model#counting
* @param {Model} model The model firing the event.
* @param {Object} options Options object passed to {@link Model#count count}.
* @returns {Promise}
*/
return this.syncing.triggerThen('counting', this.syncing, options);

@@ -131,3 +144,3 @@ }).then(function () {

if (relatedData.isThrough()) {
var _ret = (function () {
var _ret = function () {
fks[relatedData.key('foreignKey')] = relatedData.parentFk;

@@ -141,3 +154,3 @@ var through = new relatedData.throughTarget(fks);

};
})();
}();

@@ -144,0 +157,0 @@ if ((typeof _ret === 'undefined' ? 'undefined' : (0, _typeof3.default)(_ret)) === "object") return _ret.v;

{
"name": "bookshelf",
"version": "0.9.4",
"version": "0.9.5",
"description": "A lightweight ORM for PostgreSQL, MySQL, and SQLite3",

@@ -35,3 +35,3 @@ "main": "bookshelf.js",

"inherits": "~2.0.1",
"lodash": "^3.7.0"
"lodash": "^3.8.0"
},

@@ -50,3 +50,3 @@ "devDependencies": {

"jsdoc": "^3.4.0",
"knex": "^0.10.0",
"knex": "^0.11.0",
"minimist": "^1.1.0",

@@ -63,3 +63,3 @@ "mocha": "^2.0.1",

"peerDependencies": {
"knex": ">=0.6.10 <0.11.0"
"knex": ">=0.6.10 <0.12.0"
},

@@ -66,0 +66,0 @@ "author": {

@@ -95,9 +95,5 @@ # [bookshelf.js](http://bookshelfjs.org) [![Build Status](https://travis-ci.org/tgriesser/bookshelf.svg?branch=master)](https://travis-ci.org/tgriesser/bookshelf)

User.where('id', 1).fetch({withRelated: ['posts.tags']}).then(function(user) {
console.log(user.related('posts').toJSON());
}).catch(function(err) {
console.error(err);
});

@@ -111,2 +107,3 @@ ```

- [Visibility](https://github.com/tgriesser/bookshelf/wiki/Plugin:-Visibility): Specify a whitelist/blacklist of model attributes when serialized toJSON.
- [Pagination](https://github.com/tgriesser/bookshelf/wiki/Plugin:-Pagination): Adds `fetchPage` methods to use for pagination in place of `fetch` and `fetchAll`

@@ -113,0 +110,0 @@ ## Support

@@ -69,3 +69,14 @@ // Base Collection

// be documented as a valid argument for consumer code.
const collectionProps = ['model', 'comparator', 'relatedData'];
//
// RE: 'attach', 'detach', 'updatePivot', 'withPivot', '_processPivot', '_processPlainPivot', '_processModelPivot'
// It's okay to whitelist also given method references to be copied when cloning
// a collection. These methods are present only when `relatedData` is present and
// its `type` is 'belongsToMany'. So it is safe to put them in the list and use them
// without any additional verification.
// These should not be documented as a valid arguments for consumer code.
const collectionProps = [
'model', 'comparator', 'relatedData',
// `belongsToMany` pivotal collection properties
'attach', 'detach', 'updatePivot', 'withPivot', '_processPivot', '_processPlainPivot', '_processModelPivot'
];

@@ -72,0 +83,0 @@ // Copied over from Backbone.

@@ -46,3 +46,3 @@ // Base Model

* // Do something before the data is fetched from the database
* })
* });
*

@@ -56,4 +56,4 @@ * @see Events#on

*
* customer.off('fetched fetching')
* ship.off() // This will remove all event listeners
* customer.off('fetched fetching');
* ship.off(); // This will remove all event listeners
*

@@ -67,3 +67,3 @@ * @see Events#off

*
* ship.trigger('fetched')
* ship.trigger('fetched');
*

@@ -139,3 +139,3 @@ * @see Events#trigger

* @description Get the current value of an attribute from the model.
* @example note.get("title")
* @example note.get("title");
*

@@ -337,3 +337,3 @@ * @param {string} attribute - The name of the attribute to retrieve.

* return _.reduce(attrs, function(memo, val, key) {
* memo[_.str.camelize(key)] = val;
* memo[_.camelCase(key)] = val;
* return memo;

@@ -611,3 +611,2 @@ * }, {});

* var Customer = bookshelf.Model.extend({
*
* initialize: function() {

@@ -624,15 +623,12 @@ * this.on('saving', this.validateSave);

* },
*
* }, {
*
* login: Promise.method(function(email, password) {
* if (!email || !password) throw new Error('Email and password are both required');
* return new this({email: email.toLowerCase().trim()}).fetch({require: true}).tap(function(customer) {
* return bcrypt.compareAsync(customer.get('password'), password)
* .then(function(res) {
* if (!res) throw new Error('Invalid password');
* });
* return bcrypt.compareAsync(password, customer.get('password'))
* .then(function(res) {
* if (!res) throw new Error('Invalid password');
* });
* });
* })
*
* });

@@ -657,5 +653,5 @@ *

* set: function() {
* ...
* // ...
* bookshelf.Model.prototype.set.apply(this, arguments);
* ...
* // ...
* }

@@ -662,0 +658,0 @@ * });

@@ -28,3 +28,3 @@ import _ from 'lodash';

const bookshelf = {
VERSION: '0.9.4'
VERSION: require('../package.json').version
};

@@ -83,3 +83,3 @@

* // ...
* })
* });
*

@@ -159,3 +159,3 @@ * @param {(Model[])=} models

* {name: 'Person2'}
* ])
* ]);
*

@@ -209,3 +209,2 @@ * Promise.all(accounts.invoke('save')).then(function() {

* ], function(info) {
*
* // Some validation could take place here.

@@ -212,0 +211,0 @@ * return new Book(info).save({'shelf_id': model.id}, {transacting: t});

@@ -311,3 +311,5 @@ import _ from 'lodash';

* let qb = collection.query();
* qb.where({id: 1}).select().then(function(resp) {...
* qb.where({id: 1}).select().then(function(resp) {
* // ...
* });
*

@@ -317,3 +319,5 @@ * collection.query(function(qb) {

* }).fetch()
* .then(function(collection) {...
* .then(function(collection) {
* // ...
* });
*

@@ -324,3 +328,3 @@ * collection

* .then(function(collection) {
* ...
* // ...
* });

@@ -339,2 +343,30 @@ *

/**
* @method Collection#orderBy
* @since 0.9.3
* @description
*
* Specifies the column to sort on and sort order.
*
* The order parameter is optional, and defaults to 'ASC'. You may
* also specify 'DESC' order by prepending a hyphen to the sort column
* name. `orderBy("date", 'DESC')` is the same as `orderBy("-date")`.
*
* Unless specified using dot notation (i.e., "table.column"), the default
* table will be the table name of the model `orderBy` was called on.
*
* @example
*
* Cars.forge().orderBy('color', 'ASC').fetch()
* .then(function (rows) { // ...
*
* @param sort {string}
* Column to sort on
* @param order {string}
* Ascending ('ASC') or descending ('DESC') order
*/
orderBy (...args) {
return Helpers.orderBy(this, ...args);
},
/**
* @method Collection#query

@@ -341,0 +373,0 @@ * @private

@@ -77,15 +77,19 @@ // EagerRelation

// If there is a response, fetch additional nested eager relations, if any.
if (response.length > 0 && options.withRelated) {
const relatedModel = relatedData.createModel();
return Promise.try(() => {
// If there is a response, fetch additional nested eager relations, if any.
if (response.length > 0 && options.withRelated) {
const relatedModel = relatedData.createModel();
// If this is a `morphTo` relation, we need to do additional processing
// to ensure we don't try to load any relations that don't look to exist.
if (relatedData.type === 'morphTo') {
const withRelated = this._filterRelated(relatedModel, options);
if (withRelated.length === 0) return;
options = _.extend({}, options, {withRelated: withRelated});
// If this is a `morphTo` relation, we need to do additional processing
// to ensure we don't try to load any relations that don't look to exist.
if (relatedData.type === 'morphTo') {
const withRelated = this._filterRelated(relatedModel, options);
if (withRelated.length === 0) return;
options = _.extend({}, options, {withRelated: withRelated});
}
return new EagerRelation(relatedModels, response, relatedModel).fetch(options).return(response);
}
return new EagerRelation(relatedModels, response, relatedModel).fetch(options).return(response);
}
}).tap(() => {
return Promise.map(relatedModels, (model) => model.triggerThen('fetched', model, model.attributes, options));
});
}

@@ -92,0 +96,0 @@

@@ -86,2 +86,40 @@ /* eslint no-console: 0 */

helpers.warn(a + ' has been deprecated, please use ' + b + ' instead')
},
orderBy (obj, sort, order) {
let tableName;
let idAttribute;
if (obj.model) {
tableName = obj.model.prototype.tableName;
idAttribute = obj.model.prototype.idAttribute ?
obj.model.prototype.idAttribute : 'id';
} else {
tableName = obj.constructor.prototype.tableName;
idAttribute = obj.constructor.prototype.idAttribute ?
obj.constructor.prototype.idAttribute : 'id';
}
let _sort;
if (sort && sort.indexOf('-') === 0) {
_sort = sort.slice(1);
} else if (sort) {
_sort = sort;
} else {
_sort = idAttribute;
}
const _order = order || (
(sort && sort.indexOf('-') === 0) ? 'DESC' : 'ASC'
);
if (_sort.indexOf('.') === -1) {
_sort = `${tableName}.${_sort}`;
}
return obj.query(qb => {
qb.orderBy(_sort, _order);
});
}

@@ -88,0 +126,0 @@

@@ -40,3 +40,3 @@ import _, { assign, isArray } from 'lodash';

*
* let Books = bookshelf.Model.extend({
* let Book = bookshelf.Model.extend({
* tableName: 'documents',

@@ -83,3 +83,3 @@ * constructor: function() {

* new Patient({id: 1}).related('record').fetch().then(function(model) {
* ...
* // ...
* });

@@ -89,3 +89,3 @@ *

* new Patient({id: 1}).record().fetch().then(function(model) {
* ...
* // ...
* });

@@ -430,6 +430,6 @@ *

columnNames = arguments[1];
candidates = _.rest(arguments, 2);
candidates = _.drop(arguments, 2);
} else {
columnNames = null;
candidates = _.rest(arguments);
candidates = _.drop(arguments);
}

@@ -450,3 +450,2 @@ return this._relation('morphTo', null, {morphName, columnNames, candidates}).init(this);

* let Book = bookshelf.Model.extend({
*
* tableName: 'books',

@@ -463,7 +462,5 @@ *

* }
*
* });
*
* let Chapter = bookshelf.Model.extend({
*
* tableName: 'chapters',

@@ -474,7 +471,5 @@ *

* }
*
* });
*
* let Paragraph = bookshelf.Model.extend({
*
* tableName: 'paragraphs',

@@ -490,3 +485,2 @@ *

* }
*
* });

@@ -610,3 +604,3 @@ *

* @param {string|string[]} [options.columns='*']
* Specify columns to be retireved.
* Specify columns to be retrieved.
* @param {Transaction} [options.transacting]

@@ -1102,3 +1096,2 @@ * Optionally run the query in a transaction.

* @param {Model} model The model firing the event.
* @param {Object} attrs Model firing the event.
* @param {Object} options Options object passed to {@link Model#save save}.

@@ -1173,6 +1166,10 @@ * @returns {Promise}

* }).fetch()
* .then(function(model) { // ...
* .then(function(model) {
* // ...
* });
*
* let qb = model.query();
* qb.where({id: 1}).select().then(function(resp) { // ...
* qb.where({id: 1}).select().then(function(resp) {
* // ...
* });
*

@@ -1220,2 +1217,30 @@ * @method Model#query

/**
* @method Model#orderBy
* @since 0.9.3
* @description
*
* Specifies the column to sort on and sort order.
*
* The order parameter is optional, and defaults to 'ASC'. You may
* also specify 'DESC' order by prepending a hyphen to the sort column
* name. `orderBy("date", 'DESC')` is the same as `orderBy("-date")`.
*
* Unless specified using dot notation (i.e., "table.column"), the default
* table will be the table name of the model `orderBy` was called on.
*
* @example
*
* Car.forge().orderBy('color', 'ASC').fetchAll()
* .then(function (rows) { // ...
*
* @param sort {string}
* Column to sort on
* @param order {string}
* Ascending ('ASC') or descending ('DESC') order
*/
orderBy(...args) {
return Helpers.orderBy(this, ...args);
},
/* Ensure that QueryBuilder is copied on clone. */

@@ -1222,0 +1247,0 @@ clone() {

@@ -67,3 +67,3 @@ import _ from 'lodash';

// The first argument is always a model, so resolve it and call the original method.
return original.apply(this, [resolveModel(Target)].concat(_.rest(arguments)));
return original.apply(this, [resolveModel(Target)].concat(_.drop(arguments)));
};

@@ -76,3 +76,3 @@ });

Model.prototype.morphTo = function(relationName) {
return morphTo.apply(this, [relationName].concat(_.map(_.rest(arguments), function(model) {
return morphTo.apply(this, [relationName].concat(_.map(_.drop(arguments), function(model) {
return resolveModel(model);

@@ -85,5 +85,5 @@ }, this)));

Collection.prototype.through = function(Target) {
return collectionThrough.apply(this, [resolveModel(Target)].concat(_.rest(arguments)));
return collectionThrough.apply(this, [resolveModel(Target)].concat(_.drop(arguments)));
};
};

@@ -81,2 +81,15 @@ // Sync

}).then(function() {
options.query = knex;
/**
* Counting event.
*
* Fired before a `count` query. A promise may be
* returned from the event handler for async behaviour.
*
* @event Model#counting
* @param {Model} model The model firing the event.
* @param {Object} options Options object passed to {@link Model#count count}.
* @returns {Promise}
*/
return this.syncing.triggerThen('counting', this.syncing, options);

@@ -83,0 +96,0 @@ }).then(function() {

@@ -17,4 +17,5 @@ var Promise = require('../lib/base/promise').default;

process.stderr.on('data', function(data) {
console.log(data);
// http://bluebirdjs.com/docs/api/error-management-configuration.html#global-rejection-events
process.on("unhandledRejection", function(reason, promise) {
console.error(reason);
});

@@ -21,0 +22,0 @@

@@ -52,3 +52,3 @@ var _ = require('lodash');

}
require('./integration/model')(bookshelf);

@@ -61,2 +61,3 @@ require('./integration/collection')(bookshelf);

require('./integration/plugins/registry')(bookshelf);
require('./integration/plugins/pagination')(bookshelf);
});

@@ -63,0 +64,0 @@

@@ -169,2 +169,24 @@ var Promise = global.testPromise;

describe('orderBy', function () {
it('orders the results by column', function () {
var asc = new Site({id: 1})
.authors()
.orderBy('first_name', 'ASC')
.fetch();
var desc = new Site({id: 1})
.authors()
.orderBy('first_name', 'DESC')
.fetch();
return Promise.join(asc, desc).then(function (result) {
var r0 = result[0].toJSON().reverse();
var r1 = result[1].toJSON();
expect(r0).to.eql(r1);
})
})
})
describe('sync', function() {

@@ -171,0 +193,0 @@

@@ -433,2 +433,27 @@ var _ = require('lodash');

describe('orderBy', function () {
it('returns results in the correct order', function () {
var asc = Models.Customer.forge().orderBy('id', 'ASC').fetchAll()
.then(function (result) {
return result.toJSON().map(function (row) { return row.id });
});
var desc = Models.Customer.forge().orderBy('id', 'DESC').fetchAll()
.then(function (result) {
return result.toJSON().map(function (row) { return row.id });
})
return Promise.join(asc, desc)
.then(function (results) {
expect(results[0].reverse()).to.eql(results[1]);
});
});
it('returns DESC order results with a minus sign', function () {
return Models.Customer.forge().orderBy('-id').fetchAll().then(function (results) {
expect(parseInt(results.models[0].get('id'))).to.equal(4);
});
})
});
describe('save', function() {

@@ -435,0 +460,0 @@

@@ -150,2 +150,49 @@ var _ = require('lodash');

describe('emits \'fetching\' and \'fetched\' events for eagerly loaded relations with', function () {
afterEach(function () {
delete Site.prototype.initialize;
});
it('withRelated option', function () {
var countFetching = 0;
var countFetched = 0;
Site.prototype.initialize = function () {
this.on('fetching', function () {
countFetching++;
});
this.on('fetched', function () {
countFetched++;
});
};
return Blog.forge({id: 1}).fetch({withRelated: ['site']})
.then(function() {
equal(countFetching, 1);
equal(countFetched, 1);
});
});
it('load() method', function () {
var countFetching = 0;
var countFetched = 0;
Site.prototype.initialize = function () {
this.on('fetching', function () {
countFetching++;
});
this.on('fetched', function () {
countFetched++;
});
};
return Blog.where({id: 1}).fetch()
.then(function (blog) {
return blog.load('site')
.then(function() {
equal(countFetching, 1);
equal(countFetched, 1);
});
})
});
});
});

@@ -420,2 +467,16 @@

it('keeps the pivotal helper methods when cloning a collection having `relatedData` with `type` "belongsToMany", #1197', function() {
var pivotalProps = ['attach', 'detach', 'updatePivot', 'withPivot', '_processPivot', '_processPlainPivot', '_processModelPivot'];
var author = new Author({id: 1});
var posts = author.related('posts');
pivotalProps.forEach(function (prop) {
expect(posts[prop]).to.be.an.instanceof(Function);
});
var clonedAuthor = author.clone()
var clonedPosts = clonedAuthor.related('posts');
pivotalProps.forEach(function (prop) {
expect(clonedPosts[prop]).to.equal(posts[prop]);
});
});
});

@@ -422,0 +483,0 @@

One-to-one associations can be created with {@link Model#belongsTo belongsTo}, {@link Model#hasOne hasOne}, and {@link Model#morphOne morphOne} relation types.
var Book, Summary;
Book = bookshelf.Model.extend({
var Book = bookshelf.Model.extend({
tableName: 'books',

@@ -12,3 +10,3 @@ summary: function() {

Summary = bookshelf.Model.extend({
var Summary = bookshelf.Model.extend({
tableName: 'summaries',

@@ -15,0 +13,0 @@ book: function() {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc