Skip to content

Commit

Permalink
Merge branch 'master' into gh-pages
Browse files Browse the repository at this point in the history
* master:
  0.1.9
  return undefined if model isn't fetched, fixes #21
  allow an array to be passed to hasTimestamps, fixes #25
  adding a plugin for node callback interface exec method, fixes #20
  format on the first queries, fixes #26
  • Loading branch information
tgriesser committed Jun 20, 2013
2 parents 9ee4d4c + 47290ec commit 281bddd
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 32 deletions.
23 changes: 12 additions & 11 deletions bookshelf.js
@@ -1,4 +1,4 @@
// Bookshelf.js 0.1.8
// Bookshelf.js 0.1.9

// (c) 2013 Tim Griesser
// Bookshelf may be freely distributed under the MIT license.
Expand All @@ -25,7 +25,7 @@
require('trigger-then')(Backbone, when);

// Keep in sync with `package.json`.
Bookshelf.VERSION = '0.1.8';
Bookshelf.VERSION = '0.1.9';

// We're using `Backbone.Events` rather than `EventEmitter`,
// for consistency and portability.
Expand Down Expand Up @@ -340,15 +340,14 @@
return new EagerRelation(model, resp)
.fetch(options)
.then(function() { return resp; });
} else {
if (options.require) return when.reject(new Error('EmptyResponse'));
model.clear({silent: true})._reset();
return {};
}
if (options.require) return when.reject(new Error('EmptyResponse'));
})
.then(function(resp) {
model.trigger('fetched', model, resp, options);
return model;
if (resp && resp.length > 0) {
model.trigger('fetched', model, resp, options);
return model;
}
});
},

Expand Down Expand Up @@ -451,9 +450,10 @@
// Sets the timestamps before saving the model.
timestamp: function(options) {
var d = new Date();
var keys = (_.isArray(this.hasTimestamps) ? this.hasTimestamps : ['created_at', 'updated_at']);
var vals = {};
vals.updated_at = d;
if (this.isNew(options)) vals.created_at = d;
vals[keys[1]] = d;
if (this.isNew(options)) vals[keys[0]] = d;
return vals;
},

Expand Down Expand Up @@ -859,7 +859,8 @@

// Select the first item from the database - only used by models.
first: function() {
this.query.where(extendNull(this.syncing.attributes)).limit(1);
var syncing = this.syncing;
this.query.where(syncing.format(extendNull(syncing.attributes))).limit(1);
return this.select();
},

Expand Down
23 changes: 12 additions & 11 deletions docs/bookshelf.html
Expand Up @@ -27,7 +27,7 @@ <h1>bookshelf.js</h1>
<div class="pilwrap ">
<a class="pilcrow" href="#section-1">&#182;</a>
</div>
<pre><code>Bookshelf.js 0.1.8
<pre><code>Bookshelf.js 0.1.9

(c) 2013 Tim Griesser
Bookshelf may be freely distributed under the MIT license.
Expand Down Expand Up @@ -128,7 +128,7 @@ <h2>Initial Setup</h2>

</div>

<div class="content"><div class='highlight'><pre> Bookshelf.VERSION = <span class="string">'0.1.8'</span>;</pre></div></div>
<div class="content"><div class='highlight'><pre> Bookshelf.VERSION = <span class="string">'0.1.9'</span>;</pre></div></div>

</li>

Expand Down Expand Up @@ -824,15 +824,14 @@ <h2>Bookshelf.Model</h2>
<span class="keyword">return</span> <span class="keyword">new</span> EagerRelation(model, resp)
.fetch(options)
.then(<span class="keyword">function</span>() { <span class="keyword">return</span> resp; });
} <span class="keyword">else</span> {
<span class="keyword">if</span> (options.require) <span class="keyword">return</span> when.reject(<span class="keyword">new</span> Error(<span class="string">'EmptyResponse'</span>));
model.clear({silent: <span class="literal">true</span>})._reset();
<span class="keyword">return</span> {};
}
<span class="keyword">if</span> (options.require) <span class="keyword">return</span> when.reject(<span class="keyword">new</span> Error(<span class="string">'EmptyResponse'</span>));
})
.then(<span class="keyword">function</span>(resp) {
model.trigger(<span class="string">'fetched'</span>, model, resp, options);
<span class="keyword">return</span> model;
<span class="keyword">if</span> (resp &amp;&amp; resp.length &gt; <span class="number">0</span>) {
model.trigger(<span class="string">'fetched'</span>, model, resp, options);
<span class="keyword">return</span> model;
}
});
},</pre></div></div>

Expand Down Expand Up @@ -1067,9 +1066,10 @@ <h2>Bookshelf.Model</h2>

<div class="content"><div class='highlight'><pre> timestamp: <span class="keyword">function</span>(options) {
<span class="keyword">var</span> d = <span class="keyword">new</span> Date();
<span class="keyword">var</span> keys = (_.isArray(<span class="keyword">this</span>.hasTimestamps) ? <span class="keyword">this</span>.hasTimestamps : [<span class="string">'created_at'</span>, <span class="string">'updated_at'</span>]);
<span class="keyword">var</span> vals = {};
vals.updated_at = d;
<span class="keyword">if</span> (<span class="keyword">this</span>.isNew(options)) vals.created_at = d;
vals[keys[<span class="number">1</span>]] = d;
<span class="keyword">if</span> (<span class="keyword">this</span>.isNew(options)) vals[keys[<span class="number">0</span>]] = d;
<span class="keyword">return</span> vals;
},</pre></div></div>

Expand Down Expand Up @@ -1976,7 +1976,8 @@ <h2>Bookshelf.Sync</h2>
</div>

<div class="content"><div class='highlight'><pre> first: <span class="keyword">function</span>() {
<span class="keyword">this</span>.query.where(extendNull(<span class="keyword">this</span>.syncing.attributes)).limit(<span class="number">1</span>);
<span class="keyword">var</span> syncing = <span class="keyword">this</span>.syncing;
<span class="keyword">this</span>.query.where(syncing.format(extendNull(syncing.attributes))).limit(<span class="number">1</span>);
<span class="keyword">return</span> <span class="keyword">this</span>.select();
},</pre></div></div>

Expand Down
40 changes: 32 additions & 8 deletions index.html
Expand Up @@ -281,7 +281,7 @@
<div id="sidebar" class="interface">

<a class="toc_title" href="#">
Bookshelf.js <span class="version">(0.1.8)</span>
Bookshelf.js <span class="version">(0.1.9)</span>
</a>
<ul class="toc_section">
<li>&raquo; <a href="http://github.com/tgriesser/bookshelf">GitHub Repository</a></li>
Expand Down Expand Up @@ -429,7 +429,7 @@
as well as a full <a href="https://travis-ci.org/tgriesser/bookshelf">test suite</a>.
</p>

<h3>Latest Release: 0.1.8</h3>
<h3>Latest Release: 0.1.9</h3>

<p>
Current Develop &mdash;
Expand Down Expand Up @@ -718,7 +718,7 @@ <h2 id="Model">Bookshelf.Model</h2>
<br />
Fetches a model from the database, using any attributes currently set on the model
to form a <tt>select</tt> query. Returns a promise, which will resolve with the fetched Model,
or <tt>null</tt> if the model isn't fetched. If you wish to trigger an error if the fetched model
or <tt>undefined</tt> if the model isn't fetched. If you wish to trigger an error if the fetched model
is not found, pass <tt>{require: true}</tt> as one of the options to the fetch call. A <tt>"fetched"</tt>
event will be fired when a record is successfully retrieved.
</p>
Expand Down Expand Up @@ -1315,15 +1315,15 @@ <h3 id="Model-polymorphic-associations">Polymorphic Associations:</h3>
</p>

<pre>
var qb = model.query();
qb.where({id: 1}).select().then(function(resp) {...

model
.query('where', 'other_id', '=', '5')
.fetch()
.then(function(model) {
...
});

var qb = model.query();
qb.where({id: 1}).select().then(function(resp) {...
</pre>

<p id="Model-resetQuery">
Expand All @@ -1336,10 +1336,18 @@ <h3 id="Model-polymorphic-associations">Polymorphic Associations:</h3>
<p id="Model-hasTimestamps">
<b class="header">hasTimestamps</b><code>model.hasTimestamps</code>
<br />
A boolean property on the model, determining whether the <a href="#Model-timestamp">timestamp</a> method will
be called on a <tt>model.save()</tt> call &mdash; the default value is false.
If this value is set, the <a href="#Model-timestamp">timestamp</a> method will
be called on a <tt>model.save()</tt> to set the <tt>created_at</tt> and <tt>updated_at</tt>
values on save. If this value is an array, the first value will be used as the model's
key for the "created at" value and the second for the "updated at" value.
</p>

<pre>
var PostModel = Bookshelf.Model.extend({
hasTimestamps: ['createdAt', 'updatedAt']
});
</pre>

<p id="Model-timestamp">
<b class="header">timestamp</b><code>model.timestamp(options)</code>
<br />
Expand Down Expand Up @@ -1831,6 +1839,14 @@ <h2 id="Utility">Utility</h2>

<h2 id="faq">F.A.Q.</h2>

<p id="faq-debug">
<b class="header">Can I use standard node.js style callbacks.</b><br />
Yes - you can call <tt>require('bookshelf/plugins/exec')</tt> after loading Bookshelf, and
any models/collections created from that point forward will contain both <tt>then</tt> and
<tt>exec</tt> methods, where the <tt>exec</tt> method takes the standard <tt>(err, result)</tt>
callback interface.
</p>

<p id="faq-debug">
<b class="header">How do I debug?</b><br />
If you pass <tt>{debug: true}</tt> as one of the options in your initialize settings, you can see
Expand Down Expand Up @@ -1868,6 +1884,14 @@ <h2 id="faq">F.A.Q.</h2>

<h2 id="changelog">Change Log</h2>

<p>
<b class="header">0.1.9</b> &mdash; <small><i>June 19, 2013</i></small><br />
Resolve Model's <tt>fetch</tt> promise with <tt>undefined</tt> if no model was returned. An array of
"created at" and "updated at" values may be used for <tt>hasTimestamps</tt>. Format is called on the
<tt>Model#fetch</tt> method. Added an <tt>exec</tt> plugin to provide a node callback style interface
for any of the promise methods.
</p>

<p>
<b class="header">0.1.8</b> &mdash; <small><i>June 18, 2013</i></small><br />
Added support for polymorphic associations, with <tt>morphOne</tt>, <tt>morphMany</tt>, and
Expand Down
4 changes: 2 additions & 2 deletions package.json
@@ -1,7 +1,7 @@
{
"name": "bookshelf",
"version": "0.1.8",
"description": "A lightweight ORM for Postgres, MySQL, and SQLite3 with some Backbone",
"version": "0.1.9",
"description": "A lightweight Active Record ORM for PostgreSQL, MySQL, and SQLite3, influenced by Backbone.js",
"main": "bookshelf.js",
"scripts": {
"test": "mocha -b -R spec test/index.js"
Expand Down
38 changes: 38 additions & 0 deletions plugins/exec.js
@@ -0,0 +1,38 @@
var Bookshelf = require('../bookshelf');
var _ = require('underscore');

// Used to optionally add `exec` support for those who prefer node-style callbacks.
Bookshelf.wrapExec = function(target, method) {
var targetMethod = target[method];
target[method] = function() {
var result, args = arguments;
var ctx = this;
return {
then: function(onFulfilled, onRejected) {
result || (result = targetMethod.apply(ctx, args));
return result.then(onFulfilled, onRejected);
},
exec: function(callback) {
result || (result = targetMethod.apply(ctx, args));
return targetMethod.apply(ctx, args).then(function(resp) {
callback(null, resp);
}, function(err) {
callback(err, null);
}).then(null, function(err) {
setTimeout(function() { throw err; }, 0);
});
}
};
};
};

_.each(['load', 'fetch', 'save', 'destroy'], function(method) {
Bookshelf.wrapExec(Bookshelf.Model.prototype, method);
});

_.each(['load', 'fetch'], function(method) {
Bookshelf.wrapExec(Bookshelf.Collection.prototype, method);
});

// Export the `Bookshelf` object.
module.exports = Bookshelf;
31 changes: 31 additions & 0 deletions test/index.js
Expand Up @@ -27,4 +27,35 @@ describe('Bookshelf', function() {
require('./regular')(Postgres, 'postgres');
require('./regular')(Sqlite3, 'sqlite3');

});

describe('Plugins', function() {

describe('exec', function() {

it('adds `then` and `exec` to all sync methods', function() {

require('../plugins/exec');

var model = new Bookshelf.Model();
var collection = new Bookshelf.Collection();

_.each(['load', 'fetch', 'save', 'destroy'], function(method) {
var fn = model[method]();
if (!_.isFunction(fn.then) || !_.isFunction(fn.exec)) {
throw new Error('then and exec are not both defined');
}
});

_.each(['load', 'fetch'], function(method) {
var fn = collection[method]();
if (!_.isFunction(fn.then) || !_.isFunction(fn.exec)) {
throw new Error('then and exec are not both defined');
}
});

});

});

});
13 changes: 13 additions & 0 deletions test/lib/model.js
Expand Up @@ -382,6 +382,19 @@ module.exports = function(Bookshelf, handler) {
};
m.save({item: 'test'});
});

it('allows custom keys for the created at & update at values', function(ok) {
var m = new Bookshelf.Model(null, {hasTimestamps: ['createdAt', 'updatedAt']});
m.sync = function() {
equal(this.get('item'), 'test');
equal(_.isDate(this.get('createdAt')), true);
equal(_.isDate(this.get('updatedAt')), true);
ok();
return stubSync;
};
m.save({item: 'test'});
});

});

describe('timestamp', function() {
Expand Down

0 comments on commit 281bddd

Please sign in to comment.