Last active
December 14, 2015 01:38
-
-
Save mkuklis/5007219 to your computer and use it in GitHub Desktop.
backbone.record.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: