Skip to content

Instantly share code, notes, and snippets.

@mkuklis
Last active December 14, 2015 01:38
Show Gist options
  • Save mkuklis/5007219 to your computer and use it in GitHub Desktop.
Save mkuklis/5007219 to your computer and use it in GitHub Desktop.
backbone.record.js
// backbone.record
(function (Backbone) {
"use strict";
var api = {
initialize: function () {},
removeAll: function () {
var promise = new Promise(this);
var sql = "DELETE FROM " + this.name;
var seqSql = "DELETE FROM sqlite_sequence WHERE name='" + this.name + "'";
this.db.transaction(function (t) {
t.executeSql(sql, []);
t.executeSql(seqSql, [], function () {}, error);
promise.resolve();
});
return promise;
},
save: function (record, options) {
var self = this;
options || (options = {});
var promise = new Promise(options.ctx || this);
var sql = (record._id) ? this._update(record) : this._insert(record);
this.db.transaction(function (tx) {
tx.executeSql(sql, _(record).values(), function (tx, results) {
record._id = results.insertId;
self.trigger('save', record);
promise.resolve(record);
});
});
return promise;
},
batch: function (rows) {
var column, isArray, keys;
var sql = 'INSERT INTO ' + this.name;
var values = "";
var columns = "";
if (!rows) return;
var promise = new Promise(this);
isArray = _(rows[0]).isArray();
keys = (isArray) ? _(this.columns).keys() : _(rows[0]).keys();
_(keys).each(function (column) {
if (column != '_id') {
columns += (columns) ? ', ' + column : column;
values += (values) ? ', ?' : '?';
}
});
sql += '(' + columns + ') VALUES ' + '(' + values + ')';
this.db.transaction(function (tx) {
_(rows).each(function (row) {
tx.executeSql(sql, (isArray) ? row : _(row).values());
});
}, error, _.bind(function () {
this.trigger('batch', rows);
promise.resolve(rows);
}, this));
return promise;
},
find: function (criteria, options) {
var record, i, l, column;
var self = this;
var sql = "SELECT * FROM " + this.name;
var values = [];
var records = [];
options || (options = {});
var promise = new Promise(options.ctx || this);
if (criteria) {
sql += ' WHERE ';
for (column in criteria) {
if (_.isArray(criteria[column])) {
sql += column + " " + criteria[column][0] + ' ? ';
values.push(criteria[column][1]);
}
else {
sql += column + ' = ? ';
values.push(criteria[column]);
}
}
}
if (options.limit) {
sql += " LIMIT " + limit;
}
this.db.readTransaction(function (t) {
t.executeSql(sql, values, function (err, result) {
if (result.rows.length != 0) {
for (i = 0, l = result.rows.length; i < l; i++) {
record = result.rows.item(i);
records.push(record);
}
}
self.trigger('found', records);
promise.resolve(records);
}, error);
});
return promise;
},
all: function (options) {
var record, i, l, column;
var self = this;
var sql = "SELECT * FROM " + this.name;
var values = [];
var records = [];
options || (options = {});
var promise = new Promise(options.ctx || this);
this.db.readTransaction(function (t) {
t.executeSql(sql, values, function (err, result) {
if (result.rows.length != 0) {
for (i = 0, l = result.rows.length; i < l; i++) {
record = result.rows.item(i);
records.push(record);
}
}
self.trigger('all', records);
promise.resolve(records);
}, error);
});
return promise;
},
get: function (id, options) {
var item;
var self = this;
var sql = "SELECT * FROM " + this.name + ' WHERE _id = ' + id;
options || (options = {});
var promise = new Promise(options.ctx || this);
this.db.readTransaction(function (t) {
t.executeSql(sql, [], function (err, result) {
item = result.rows.item(0);
self.trigger('get', item);
promise.resolve(item);
}, error);
});
return promise;
},
count: function () {
var promise = new Promise(this);
var size;
var sql = "SELECT count(*) as rowsize FROM " + this.name;
this.db.readTransaction(function (t) {
t.executeSql(sql, [], _.bind(function (err, result) {
size = result.rows.item(0).rowsize;
this.trigger('count', size);
promise.resolve(size);
}, this), error);
});
return promise;
},
// private
_insert: function (record) {
var values = '';
var columns = '';
var sql = 'INSERT INTO ' + this.name;
_(record).each(function (value, column) {
columns += (columns) ? ', ' + column : column;
values += (values) ? ', ?' : '?';
});
sql += '(' + columns + ') VALUES ' + '(' + values + ')';
return sql;
},
_update: function (record) {
var columns = '';
var sql = 'UPDATE ' + this.name + ' SET ';
_(record).each(function (value, column) {
columns += (columns) ? ', ' + column + ' = ?' : column + ' = ?';
});
sql += columns + ' WHERE _id = ' + record._id;
return sql;
},
_create: function () {
var column, keys = '';
var create = 'CREATE TABLE IF NOT EXISTS ' + this.name +
'(_id INTEGER PRIMARY KEY AUTOINCREMENT';
for (column in this.columns) {
keys += ', ' + column + ' ' + this.columns[column];
}
create += keys + ')';
this.db.transaction(function (tx) {
tx.executeSql(create, [], function () {}, error);
});
},
_open: function () {
this.db = openDatabase(this.name, "1", this.name, this.size);
},
drop: function () {
var sql = "DROP table " + this.name;
this.db.transaction(function (t) {
t.executeSql(sql, []);
});
}
};
// helpers
function error(tx, e) {
throw new Error(e.message);
}
// Record constructor
var Record = function (options) {
if (!this.name || !this.columns) throw new Error('database name or columns not defined');
options || (options = {});
this.size = options.size || 5 * 1024 * 1024;
this.initialize.apply(this, arguments);
this._open();
this._create();
}
_.extend(Record.prototype, Backbone.Events, api);
Record.extend = Backbone.Model.extend;
Backbone.Record = Record;
// simple promise implementation
function Promise(context) {
this.context = context || this;
this.success = [];
this.error = [];
}
Promise.prototype = {
constructor: Promise,
then: function (success, error) {
if (success) {
if (this.resolved) {
success.apply(this.context, this.resolved);
}
else {
this.success.push(success);
}
}
if (error) {
if (this.rejected) {
error.apply(this.context, this.rejected);
}
else {
this.error.push(error);
}
}
return this;
},
resolve: function () {
var callback;
this.resolved = arguments;
this.error = [];
while (callback = this.success.shift()) {
callback.apply(this.context, this.resolved);
}
},
reject: function () {
var callback;
this.rejected = arguments;
this.success = [];
while (callback = this.error.shift()) {
callback.apply(this.context, this.rejected);
}
}
};
})(Backbone);
@mkuklis
Copy link
Author

mkuklis commented Feb 21, 2013

Usage:

var Todos = Backbone.Record.extend({
  name: 'todos',
  columns: {
    title: 'NVARCHAR(120)',
    completed: 'BOOLEAN default false'
  }
});

var todos = new Todos();

todos.batch([
  { title: 'todo1', completed: true }, 
  { title: 'todo2', completed: true } 
]).then(function (records) {
 // do your thing
});

todos.save({ title: 'todo1', completed: true }).then(function (record) {
 // do your thing
});

todos.all().then(function (records) {
  // do your thing
});


todos.get(1).then(function (record) {
  // do your thing
});

todos.find({ title: 'todo 1' }).then(function (record) {
  // do your thing
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment