Comparing version 0.0.1 to 0.1.0
401
bookshelf.js
@@ -7,3 +7,2 @@ // Bookshelf.js 0.1.0 | ||
// http://bookshelfjs.org | ||
(function() { | ||
@@ -16,17 +15,18 @@ | ||
// Keep a reference to our own copy of Backbone, in case we want to use | ||
// this specific instance of Backbone elsewhere in the application. | ||
// this specific instance elsewhere in the application. | ||
var Backbone = Bookshelf.Backbone = require('backbone'); | ||
// Local dependency references. | ||
var _ = require('underscore'); | ||
var Q = require('q'); | ||
var Knex = require('knex'); | ||
var _ = require('underscore'); | ||
var When = require('when'); | ||
var WhenFn = require('when/function'); | ||
var Knex = require('knex'); | ||
var Inflection = require('inflection'); | ||
// Keep in sync with package.json. | ||
Bookshelf.VERSION = '0.0.0'; | ||
// Keep in sync with `package.json`. | ||
Bookshelf.VERSION = '0.1.0'; | ||
// We're using `Backbone.Events` rather than `EventEmitter`, | ||
// for API consistency and portability, but adding some | ||
// methods to make the API feel a bit more like Node's. | ||
// for consistency and portability, adding a few | ||
// functions to make the API feel a bit more like Node. | ||
var Events = Bookshelf.Events = Backbone.Events; | ||
@@ -39,2 +39,4 @@ Events.emit = function() { this.trigger.apply(this, arguments); }; | ||
var push = Array.prototype.push; | ||
// Shared functions which are mixed-in to the | ||
@@ -46,9 +48,8 @@ // `Model`, `Collection`, and `EagerRelation` prototypes. | ||
builder: function(table) { | ||
return Bookshelf.Knex(table); | ||
return Knex(table); | ||
}, | ||
// If there are no arguments, return the current object's | ||
// query builder (or create a new one). If there are arguments, | ||
// call the query builder with the first argument, applying the | ||
// rest. | ||
// query builder (or create and return a new one). If there are arguments, | ||
// call the query builder with the first argument, applying the rest. | ||
query: function() { | ||
@@ -63,3 +64,3 @@ this._builder || (this._builder = this.builder(_.result(this, 'tableName'))); | ||
// Reset the query builder, called internally | ||
// after a query completes. | ||
// each time a query is run. | ||
resetQuery: function() { | ||
@@ -89,5 +90,3 @@ delete this._builder; | ||
.processRelated(options) | ||
.then(function() { | ||
return model; | ||
}); | ||
.yield(model); | ||
}, | ||
@@ -103,3 +102,17 @@ | ||
return this.relations[item]; | ||
}, | ||
// Helper for attaching query constraints on related | ||
// `models` or `collections` as necessary. | ||
_addConstraints: function(resp) { | ||
var relation = this._relation; | ||
if (relation && relation.fkValue || resp) { | ||
if (relation.type !== 'belongsToMany') { | ||
constraints.call(this, resp); | ||
} else { | ||
belongsToMany.call(this, resp); | ||
} | ||
} | ||
} | ||
}; | ||
@@ -115,13 +128,14 @@ | ||
// Create a new model with the specified attributes. A client id (`cid`) | ||
// is automatically generated and assigned for you. | ||
// A unique `cid` property is also added to each created model, similar to | ||
// `Backbone` models, and is useful checking the identity of two models. | ||
var Model = Bookshelf.Model = function(attributes, options) { | ||
var attrs = attributes || {}; | ||
options || (options = {}); | ||
this.cid = _.uniqueId('c'); | ||
this.attributes = {}; | ||
this.relations = {}; | ||
this._configure(options); | ||
if (options.parse) attrs = this.parse(attrs, options) || {}; | ||
options.protect || (options.protect = false); | ||
this.cid = _.uniqueId('c'); | ||
if (options) { | ||
_.extend(this, _.pick(options, modelProps)); | ||
if (options.parse) attrs = this.parse(attrs, options) || {}; | ||
} | ||
this.set(attrs, options); | ||
@@ -133,7 +147,7 @@ this.changed = {}; | ||
// A list of properties that are omitted from the `Backbone.Model.prototype`, since we're not | ||
// handling validations, or tracking changes in the same way as Backbone, we can drop all of | ||
// these related features. | ||
// handling validations, or tracking changes in the same fashion as `Backbone`, we can drop all of | ||
// these related keys. | ||
var modelOmitted = ['isValid', 'hasChanged', 'changedAttributes', 'previous', 'previousAttributes']; | ||
// List of attributes attached directly from the constructor's options object. | ||
// List of attributes attached directly from the `options` passed to the constructor. | ||
var modelProps = ['tableName', 'hasTimestamps']; | ||
@@ -143,14 +157,2 @@ | ||
// The database table associated with the model. | ||
tableName: null, | ||
// Indicates if the model should be timestamped. | ||
hasTimestamps: false, | ||
// Ensures the options sent to the model are properly attached. | ||
_configure: function(options) { | ||
if (this.options) options = _.extend({}, _.result(this, 'options'), options); | ||
_.extend(this, _.pick(options, modelProps)); | ||
}, | ||
// The `hasOne` relation specifies that this table has exactly one of | ||
@@ -163,3 +165,3 @@ // another type of object, specified by a foreign key in the other table. The foreign key is assumed | ||
type: 'hasOne', | ||
foreignKey: foreignKey || Inflection.singularize(_.result(this, 'tableName')) + '_id' | ||
foreignKey: foreignKey || singularMemo(_.result(this, 'tableName')) + '_id' | ||
}); | ||
@@ -175,3 +177,3 @@ }, | ||
type: 'hasMany', | ||
foreignKey: foreignKey || Inflection.singularize(_.result(this, 'tableName')) + '_id' | ||
foreignKey: foreignKey || singularMemo(_.result(this, 'tableName')) + '_id' | ||
}); | ||
@@ -186,3 +188,3 @@ }, | ||
foreignKey: Target.prototype.idAttribute, | ||
otherKey: otherKey || Inflection.singularize(_.result(Target.prototype, 'tableName')) + '_id' | ||
otherKey: otherKey || singularMemo(_.result(Target.prototype, 'tableName')) + '_id' | ||
}); | ||
@@ -197,4 +199,4 @@ }, | ||
type: 'belongsToMany', | ||
otherKey: otherKey || Inflection.singularize(_.result(this, 'tableName')) + '_id', | ||
foreignKey: foreignKey || Inflection.singularize(_.result(Target.prototype, 'tableName')) + '_id', | ||
otherKey: otherKey || singularMemo(_.result(this, 'tableName')) + '_id', | ||
foreignKey: foreignKey || singularMemo(_.result(Target.prototype, 'tableName')) + '_id', | ||
joinTableName: joinTableName || [ | ||
@@ -214,7 +216,8 @@ _.result(this, 'tableName'), | ||
// Sets and saves the hash of model attributes, | ||
// If the server returns an attributes hash that differs, | ||
// the model's state will be `set` again. | ||
// Sets and saves the hash of model attributes, triggering | ||
// a "creating" or "updating" event on the model, as well as a "saving" event, | ||
// to bind listeners for any necessary validation, logging, etc. | ||
// If an error is thrown during these events, the model will not be saved. | ||
save: function(key, val, options) { | ||
var id, attrs, success; | ||
var id, attrs; | ||
@@ -232,3 +235,3 @@ // Handle both `"key", value` and `{key: value}` -style arguments. | ||
// set them as attributes on the model, even | ||
// if the method is set to "patch". | ||
// if the "partial" option is specified. | ||
if (this.hasTimestamps) { | ||
@@ -250,27 +253,34 @@ _.extend(attrs, this.timestamp(options)); | ||
// Trigger a "beforeSave" event on the model, so we can bind listeners to do any | ||
// validation, mutating, logging, etc. | ||
this.trigger('beforeSave', model, method, options); | ||
return sync[method](attrs, options).then(function(resp) { | ||
// After a successful database save, the id is updated | ||
// if the model was created, otherwise the success function is called | ||
model.resetQuery(); | ||
if (resp.insertId) model.set(model.idAttribute, resp.insertId); | ||
if (success) success(model, resp, options); | ||
model.trigger((method === 'insert' ? 'create' : 'update'), model, resp, options); | ||
return WhenFn.call(function() { | ||
model.trigger((method === 'insert' ? 'creating' : 'updating'), model, attrs, options); | ||
model.trigger('saving', model, attrs, options); | ||
}) | ||
.then(function() { return sync[method](attrs, options); }) | ||
.then(function(resp) { | ||
// After a successful database save, the id is updated if the model was created | ||
if (method === 'insert' && resp) model.set(model.idAttribute, resp[0]); | ||
model.trigger((method === 'insert' ? 'created' : 'updated'), model, resp, options); | ||
model.trigger('saved', model, resp, options); | ||
return model; | ||
}); | ||
}) | ||
.ensure(function() { model.resetQuery(); }); | ||
}, | ||
// Destroy a model, calling a delete based on its `idAttribute`. | ||
// Destroy a model, calling a "delete" based on its `idAttribute`. | ||
// A "destroying" and "destroyed" are triggered on the model before | ||
// and after the model is destroyed, respectively. If an error is thrown | ||
// during the "destroying" event, the model will not be destroyed. | ||
destroy: function(options) { | ||
options || (options = {}); | ||
var model = this; | ||
this.trigger('beforeDestroy', model, options); | ||
return this.sync(this, options).del().then(function(resp) { | ||
model.trigger('destroy', model, resp, options); | ||
return WhenFn.call(function() { | ||
model.trigger('destroying', model, options); | ||
}) | ||
.then(function() { return model.sync(model, options).del(options); }) | ||
.then(function(resp) { | ||
model.trigger('destroyed', model, resp, options); | ||
return resp; | ||
}).ensure(function() { | ||
model.resetQuery(); | ||
}); | ||
@@ -308,3 +318,3 @@ }, | ||
// Create a new model with identical attributes to this one, | ||
// including any appropriate relations for the model. | ||
// including any relations on the current model. | ||
clone: function() { | ||
@@ -331,9 +341,12 @@ var model = new this.constructor(this.attributes); | ||
} else if (Target.prototype instanceof Model) { | ||
Target = Bookshelf.Collection.extend({model: Target}); | ||
Target = Bookshelf.Collection.extend({ | ||
model: Target, | ||
builder: Target.prototype.builder | ||
}); | ||
} | ||
// If we're handling an eager loaded related model, | ||
// we need to keep a reference to the original constructor, | ||
// to assemble the correct object once the eager matching is finished. | ||
// Otherwise, we need to grab the `foreignKey` value for building the query. | ||
// keep a reference to the original constructor to assemble | ||
// the correct object once the eager matching is finished. | ||
// Otherwise, just grab the `foreignKey` value for building the query. | ||
if (this._isEager) { | ||
@@ -368,4 +381,4 @@ if (multi) { | ||
// Validation can be complicated, and is better left to be | ||
// handled on its own and not mixed in with database logic. | ||
// Validation can be complicated, and is better handled | ||
// on its own and not mixed in with database logic. | ||
_validate: function() { | ||
@@ -415,3 +428,2 @@ return true; | ||
collection.add(model, options); | ||
if (options.success) options.success(model, resp, options); | ||
return model; | ||
@@ -452,38 +464,2 @@ }); | ||
// Fetch the nested related items, and handle the responses. | ||
// Returns a deferred object, with the cumulative handling of | ||
// multiple (potentially nested) relations. | ||
fetch: function(options) { | ||
var current = this; | ||
var models = this.models = []; | ||
var opts = this._relation; | ||
Bookshelf.addConstraints(opts.type, this, opts.parentResponse); | ||
return this.query().select(opts.columns).then(function(resp) { | ||
current.resetQuery(); | ||
// Only find additional related items & process if | ||
// there is a response from the query. | ||
if (resp && resp.length > 0) { | ||
var filteredResp = skim(resp); | ||
// We can just push the models onto the collection, rather than resetting. | ||
for (var i = 0, l = filteredResp.length; i < l; i++) { | ||
models.push(new opts.modelCtor(filteredResp[i], {parse: true})); | ||
} | ||
if (options.withRelated) { | ||
var model = new opts.modelCtor(); | ||
return new EagerRelation(current, model, resp).processRelated(options); | ||
} | ||
} | ||
return models; | ||
}); | ||
}, | ||
// This helper function is used internally to determine which relations | ||
@@ -517,3 +493,3 @@ // are necessary for fetching based on the `model.load` or `withRelated` option. | ||
relation = target[name](); | ||
target._isEager = null; | ||
delete target['_isEager']; | ||
@@ -530,13 +506,12 @@ // Set the parent's response, for purposes of setting query constraints. | ||
// Fetch all eager loaded models. | ||
// Fetch all eager loaded models, loading them onto | ||
// an array of pending deferred objects, so we easily | ||
// re-organize the responses once all of the queries complete. | ||
var pendingDeferred = []; | ||
var pendingNames = this.pendingNames = []; | ||
// TODO: look at refactoring the `.call` on the fetch | ||
// below, it's a bit confusing when debugging. | ||
for (name in handled) { | ||
pendingNames.push(name); | ||
pendingDeferred.push(this.fetch.call(handled[name], { | ||
transacting: options.transacting, | ||
withRelated: subRelated[name] | ||
pendingDeferred.push(eagerFetch.call(handled[name], { | ||
transacting: options.transacting, | ||
withRelated: subRelated[name] | ||
})); | ||
@@ -547,3 +522,3 @@ } | ||
// returning the original response when these syncs are complete. | ||
return Q.all(pendingDeferred).spread(_.bind(this.matchResponses, this)); | ||
return When.all(pendingDeferred).spread(_.bind(this.matchResponses, this)); | ||
}, | ||
@@ -574,7 +549,6 @@ | ||
// Attach the appropriate related items onto the parent model. | ||
// TODO: optimize this section. | ||
for (var i2 = 0, l2 = models.length; i2 < l2; i2++) { | ||
var m = models[i2]; | ||
var id = (type === 'belongsTo' ? m.get(relation._relation.otherKey) : m.id); | ||
var result = Bookshelf.eagerRelated(type, relation, relatedModels, id); | ||
var result = eagerRelated(type, relation, relatedModels, id); | ||
m.relations[name] = result; | ||
@@ -586,3 +560,3 @@ } | ||
if (type === 'hasOne' || type === 'belongsTo') { | ||
parent.relations[name] = relation.models[0]; | ||
parent.relations[name] = relation.models[0] || new relation._relation.modelCtor(); | ||
} else { | ||
@@ -605,27 +579,15 @@ parent.relations[name] = new relation._relation.collectionCtor(relation.models, {parse: true}); | ||
// Adds the relation constraints onto the query. | ||
Bookshelf.addConstraints = function(type, target, resp) { | ||
if (type !== 'belongsToMany') { | ||
return constraints(target, resp); | ||
} else { | ||
return belongsToMany(target, resp); | ||
} | ||
}; | ||
// Handles the "eager related" relationship matching. | ||
Bookshelf.eagerRelated = function(type, target, eager, id) { | ||
var eagerRelated = function(type, target, eager, id) { | ||
var relation = target._relation; | ||
var where = {}; | ||
switch (type) { | ||
case "hasOne": | ||
case "belongsTo": | ||
where[relation.foreignKey] = id; | ||
// TODO: Should this return an empty model otherwise? | ||
return eager.findWhere(where); | ||
case "hasMany": | ||
where[relation.foreignKey] = id; | ||
return new relation.collectionCtor(eager.where(where), {parse: true}); | ||
case "belongsToMany": | ||
where['_pivot_' + relation.otherKey] = id; | ||
return new relation.collectionCtor(eager.where(where), {parse: true}); | ||
if (type === 'hasOne' || type === 'belongsTo') { | ||
where[relation.foreignKey] = id; | ||
return eager.findWhere(where) || new relation.modelCtor(); | ||
} else if (type === 'hasMany') { | ||
where[relation.foreignKey] = id; | ||
return new relation.collectionCtor(eager.where(where), {parse: true}); | ||
} else { | ||
where['_pivot_' + relation.otherKey] = id; | ||
return new relation.collectionCtor(eager.where(where), {parse: true}); | ||
} | ||
@@ -635,21 +597,20 @@ }; | ||
// Standard constraints for regular or eager loaded relations. | ||
var constraints = function(target, resp) { | ||
var relation = target._relation; | ||
var constraints = function(resp) { | ||
var relation = this._relation; | ||
if (resp) { | ||
target.query('whereIn', relation.foreignKey, _.pluck(resp, relation.parentIdAttr)); | ||
this.query('whereIn', relation.foreignKey, _.pluck(resp, relation.parentIdAttr)); | ||
} else { | ||
target.query('where', relation.foreignKey, '=', relation.fkValue); | ||
this.query('where', relation.foreignKey, '=', relation.fkValue); | ||
} | ||
return target; | ||
}; | ||
// Helper function for adding the constraints needed on a eager load. | ||
var belongsToMany = function(target, resp) { | ||
var belongsToMany = function(resp) { | ||
var | ||
relation = target._relation, | ||
relation = this._relation, | ||
columns = relation.columns || (relation.columns = []), | ||
builder = target.query(), | ||
builder = this.query(), | ||
tableName = _.result(target, 'tableName'), | ||
idAttribute = _.result(target, 'idAttribute'), | ||
tableName = _.result(this, 'tableName'), | ||
idAttribute = _.result(this, 'idAttribute'), | ||
@@ -670,3 +631,3 @@ otherKey = relation.otherKey, | ||
if (pivotColumns) columns.push.apply(columns, pivotColumns); | ||
if (pivotColumns) push.apply(columns, pivotColumns); | ||
@@ -680,7 +641,42 @@ builder.join(joinTableName, tableName + '.' + idAttribute, '=', joinTableName + '.' + foreignKey); | ||
} | ||
return target; | ||
}; | ||
// Called from `EagerRelation.processRelated` with the context | ||
// of an eager-loading model or collection, this function | ||
// fetches the nested related items, and returns a deferred object, | ||
// with the cumulative handling of multiple (potentially nested) relations. | ||
var eagerFetch = function(options) { | ||
var current = this; | ||
var models = this.models = []; | ||
var relation = this._relation; | ||
this._addConstraints(relation.parentResponse); | ||
return this.query().select(relation.columns).then(function(resp) { | ||
// Only find additional related items & process if | ||
// there is a response from the query. | ||
if (resp && resp.length > 0) { | ||
// We can just push the models onto the collection, rather than resetting. | ||
for (var i = 0, l = resp.length; i < l; i++) { | ||
models.push(new relation.modelCtor(resp[i], {parse: true})); | ||
} | ||
if (options.withRelated) { | ||
var model = new relation.modelCtor(); | ||
return new EagerRelation(current, model, resp).processRelated(options); | ||
} | ||
} | ||
return models; | ||
}).ensure(function() { | ||
current.resetQuery(); | ||
}); | ||
}; | ||
// Set up inheritance for the model and collection. | ||
Model.extend = Collection.extend = Bookshelf.Backbone.Model.extend; | ||
Model.extend = Collection.extend = EagerRelation.extend = Bookshelf.Backbone.Model.extend; | ||
@@ -711,6 +707,3 @@ // The `forge` function properly instantiates a new Model or Collection | ||
this.query = model.query(); | ||
var relation = model._relation; | ||
if (relation && relation.fkValue) { | ||
Bookshelf.addConstraints(relation.type, model); | ||
} | ||
model._addConstraints(); | ||
if (options.transacting) this.query.transacting(options.transacting); | ||
@@ -737,15 +730,18 @@ }; | ||
if (!_.isArray(columns)) columns = columns ? [columns] : ['*']; | ||
if (model._relation && model._relation.columns) { | ||
columns = model._relation.columns; | ||
} | ||
return this.query.select(columns).then(function(resp) { | ||
var target, filteredResp; | ||
model.resetQuery(); | ||
var target; | ||
if (resp && resp.length > 0) { | ||
filteredResp = skim(resp); | ||
// If this is a model fetch, then we set the parsed attributes | ||
// on the model, otherwise, we reset the collection. | ||
if (model instanceof Model) { | ||
model.set(model.parse(filteredResp[0], options), options); | ||
model.set(model.parse(resp[0], options), options); | ||
} else { | ||
model.reset(filteredResp, {silent: true, parse: true}); | ||
model.reset(resp, {silent: true, parse: true}); | ||
} | ||
@@ -759,7 +755,5 @@ | ||
target = (model instanceof Collection ? new model.model() : model); | ||
return new EagerRelation(model, target, filteredResp) | ||
return new EagerRelation(model, target, resp) | ||
.processRelated(options) | ||
.then(function() { | ||
return resp; | ||
}); | ||
.yield(resp); | ||
} | ||
@@ -772,3 +766,3 @@ | ||
// a failure if the model comes up blank. | ||
if (options.require) return Q.reject('EmptyResponse'); | ||
if (options.require) return When.reject(new Error('EmptyResponse')); | ||
@@ -785,6 +779,7 @@ if (model instanceof Model) { | ||
if (resp.length > 0) { | ||
if (options.success) options.success(model, resp, options); | ||
model.trigger('fetched', model, resp, options); | ||
} | ||
return model; | ||
}).ensure(function() { | ||
model.resetQuery(); | ||
}); | ||
@@ -795,3 +790,5 @@ }, | ||
insert: function() { | ||
return this.query.insert(this.model.format(_.extend({}, this.model.attributes))); | ||
return this.query | ||
.idAttribute(_.result(this.model, 'idAttribute')) | ||
.insert(this.model.format(_.extend({}, this.model.attributes))); | ||
}, | ||
@@ -802,3 +799,4 @@ | ||
attrs = (attrs && options.patch ? attrs : this.model.attributes); | ||
return this.query.where(this.model.idAttribute, this.model.id) | ||
return this.query | ||
.where(this.model.idAttribute, this.model.id) | ||
.update(this.model.format(_.extend({}, this.model.attributes))); | ||
@@ -815,3 +813,3 @@ }, | ||
if (!wheres && this.query.wheres.length === 0) { | ||
return Q.reject('A model cannot be destroyed without a "where" clause or an idAttribute.'); | ||
return When.reject(new Error('A model cannot be destroyed without a "where" clause or an idAttribute.')); | ||
} | ||
@@ -831,7 +829,7 @@ return this.query.where(wheres).del(); | ||
_handler: function(method, ids, options) { | ||
if (ids == void 0 && method === 'insert') return Q.resolve(); | ||
if (ids == void 0 && method === 'insert') return When.resolve(); | ||
if (!_.isArray(ids)) ids = ids ? [ids] : []; | ||
var pivot = this._relation; | ||
var context = this; | ||
return Q.all(_.map(ids, function(item) { | ||
return When.all(_.map(ids, function(item) { | ||
var data = {}; | ||
@@ -900,8 +898,13 @@ data[pivot.otherKey] = pivot.fkValue; | ||
// Filters an array of objects, cleaning out any nested properties. | ||
var skim = function(data) { | ||
return _.map(data, function(obj) { | ||
return _.pick(obj, _.keys(obj)); | ||
}); | ||
}; | ||
// Simple memoization of the singularize call. | ||
var singularMemo = (function(value) { | ||
var cache = {}; | ||
return function(arg) { | ||
if (arg in cache) { | ||
return cache[arg]; | ||
} else { | ||
return cache[arg] = Inflection.singularize(arg); | ||
} | ||
}; | ||
}()); | ||
@@ -917,4 +920,3 @@ // References to the default `Knex` and `Knex.Transaction`, overwritten | ||
// Configure the `Bookshelf` settings (database adapter, etc.) once, | ||
// so it is ready on first model initialization. Optionally, provide | ||
// a `name` so a named instance of | ||
// so it is ready on first model initialization. | ||
Bookshelf.Initialize = function(name, options) { | ||
@@ -924,3 +926,3 @@ var Target; | ||
options = name; | ||
name = 'default'; | ||
name = 'main'; | ||
} | ||
@@ -935,9 +937,9 @@ if (Bookshelf.Instances[name]) { | ||
if (name === 'default') { | ||
Target = Bookshelf.Instances['default'] = Bookshelf; | ||
if (name === 'main') { | ||
Target = Bookshelf.Instances['main'] = Bookshelf; | ||
} else { | ||
Target = Bookshelf.Instances[name] = {}; | ||
// Create a new `Bookshelf` instance for this database. | ||
_.extend(Target, _.omit(Bookshelf, 'Instances', 'Initialize', 'Knex', 'Transaction'), { | ||
// Create a new `Bookshelf` instance for this database connection. | ||
_.extend(Target, _.omit(Bookshelf, 'Instances', 'Initialize', 'Knex', 'Transaction', 'VERSION'), { | ||
Knex: Builder, | ||
@@ -949,8 +951,13 @@ Transaction: Builder.Transaction | ||
_.each(['Model', 'Collection', 'EagerRelation'], function(item) { | ||
Target[item].prototype.builder = function(table) { | ||
return Target(table); | ||
}; | ||
Target[item] = Bookshelf[item].extend({ | ||
builder: function(table) { | ||
return Builder(table); | ||
} | ||
}); | ||
}); | ||
} | ||
// Set the instanceName, so we know what Bookshelf we're using. | ||
Target.instanceName = name; | ||
// Return the initialized instance. | ||
@@ -962,7 +969,11 @@ return Target; | ||
// options, to initialize different databases. | ||
// The main instance being named "default"... | ||
// The main instance being named "main"... | ||
Bookshelf.Instances = {}; | ||
// The main Bookshelf `instanceName`... incase we're using Bookshelf | ||
// after `Knex` has been initialized, for consistency. | ||
Bookshelf.instanceName = 'main'; | ||
module.exports = Bookshelf; | ||
}).call(this); |
{ | ||
"name": "bookshelf", | ||
"author": { | ||
"name": "Tim Griesser", | ||
"url": "http://tgriesser.com" | ||
}, | ||
"version": "0.0.1", | ||
"description": "A promise based ORM in the style of Backbone.js", | ||
"author": "Tim Griesser", | ||
"version": "0.1.0", | ||
"description": "An ORM with some Backbone", | ||
"main": "bookshelf.js", | ||
"scripts": { | ||
"test": "mocha -R spec test/index.js" | ||
"test": "mocha -b -R spec test/index.js" | ||
}, | ||
@@ -26,6 +23,6 @@ "homepage": "http://bookshelfjs.org", | ||
"backbone": "1.0.x", | ||
"knex": ">=0.0.1", | ||
"underscore": "1.4.4", | ||
"q": "0.9.x", | ||
"inflection": "1.2.x" | ||
"knex": "0.1.x", | ||
"underscore": "~1.4.4", | ||
"inflection": "~1.2.x", | ||
"when": "~2.1.0" | ||
}, | ||
@@ -36,7 +33,7 @@ "devDependencies": { | ||
"pg": "~0.15.1", | ||
"sqlite3": "~2.1.7" | ||
"sqlite3": "~2.1.7", | ||
"objectdump": "~0.3.0" | ||
}, | ||
"author": "Tim Griesser", | ||
"license": "MIT", | ||
"readmeFilename": "README.md" | ||
} |
@@ -1,3 +0,21 @@ | ||
# Bookshelf | ||
``` | ||
._. ._. | ||
| | ______ _ _ _ __ | | | ||
| | | ___ \ | | | | | |/ _| | | | ||
| | | |_/ / ___ ___ | | _____| |__ ___| | |_ | | | ||
| | | ___ \/ _ \ / _ \| |/ / __| '_ \ / _ \ | _| | | | ||
| | | |_/ / (_) | (_) | <\__ \ | | | __/ | | | | | ||
| | \____/ \___/ \___/|_|\_\___/_| |_|\___|_|_| | | | ||
|----------------------------------------------------------| | ||
\--------\ /----------------------------------\ /--------/ | ||
\/ \/ | ||
``` | ||
[![Build Status](https://secure.travis-ci.org/tgriesser/bookshelf.png)](https://travis-ci.org/tgriesser/bookshelf) | ||
Bookshelf is a node.js ORM with support for postgreSQL, mySQL, and SQLite3. | ||
It is built atop the <a href="http://knexjs.org">Knex Query Builder</a>, | ||
and is strongly influenced by the Model and Collection foundations of Backbone.js. | ||
It features transaction support, one-to-one, one-to-many, and many-to-many relations. | ||
For Docs, License, Tests, FAQ, and other information, see: http://bookshelfjs.org. | ||
To suggest a feature, report a bug, or general discussion: http://github.com/tgriesser/bookshelf/issues/ |
@@ -0,31 +1,30 @@ | ||
var _ = require('underscore'); | ||
var _ = require('underscore'); | ||
var Bookshelf = require('../bookshelf'); | ||
var conn = require(process.env.BOOKSHELF_TEST || './shared/config'); | ||
before(function(ok) { | ||
// The output goes here. | ||
exports.output = {}; | ||
Bookshelf.Initialize({ | ||
client: 'mysql', | ||
connection: { | ||
host : '127.0.0.1', | ||
user : 'root', | ||
password : '', | ||
database : 'myapp_test', | ||
charset : 'utf8' | ||
} | ||
}); | ||
Bookshelf.Initialize({ | ||
client: 'mysql', | ||
connection: conn.mysql | ||
}); | ||
// Load all of the tables and | ||
// data for the tests. | ||
require('./data/migration') | ||
.then(function() { | ||
return require('./data/inserts'); | ||
}) | ||
.then(function() { | ||
ok(); | ||
}).done(); | ||
var Postgres = Bookshelf.Initialize('pg', { | ||
client: 'postgres', | ||
connection: conn.postgres | ||
}); | ||
require('./model'); | ||
require('./collection'); | ||
require('./relations'); | ||
var Sqlite3 = Bookshelf.Initialize('sqlite', { | ||
client: 'sqlite', | ||
connection: conn.sqlite3 | ||
}); | ||
describe('Bookshelf', function() { | ||
require('./regular')(Bookshelf, 'mysql'); | ||
require('./regular')(Postgres, 'postgres'); | ||
require('./regular')(Sqlite3, 'sqlite3'); | ||
}); |
Sorry, the diff of this file is not supported yet
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
130293
18
3492
21
5
5
1
+ Addedwhen@~2.1.0
+ Addedgeneric-pool@2.0.4(transitive)
+ Addedknex@0.1.8(transitive)
+ Addedwhen@2.1.1(transitive)
- Removedq@0.9.x
- Removedcolorette@2.0.19(transitive)
- Removedcommander@10.0.1(transitive)
- Removeddebug@4.3.4(transitive)
- Removedescalade@3.2.0(transitive)
- Removedesm@3.2.25(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedget-package-type@0.1.0(transitive)
- Removedgetopts@2.3.0(transitive)
- Removedhasown@2.0.2(transitive)
- Removedinterpret@2.2.0(transitive)
- Removedis-core-module@2.15.1(transitive)
- Removedknex@3.1.0(transitive)
- Removedlodash@4.17.21(transitive)
- Removedms@2.1.2(transitive)
- Removedpath-parse@1.0.7(transitive)
- Removedpg-connection-string@2.6.2(transitive)
- Removedq@0.9.7(transitive)
- Removedrechoir@0.8.0(transitive)
- Removedresolve@1.22.8(transitive)
- Removedresolve-from@5.0.0(transitive)
- Removedsupports-preserve-symlinks-flag@1.0.0(transitive)
- Removedtarn@3.0.2(transitive)
- Removedtildify@2.0.0(transitive)
Updatedinflection@~1.2.x
Updatedknex@0.1.x
Updatedunderscore@~1.4.4