UNPKG

tiger

Version:

A full port of Spine.js MVC framework to Titanium Mobile, with enhancements

374 lines (308 loc) 11.9 kB
// Generated by CoffeeScript 1.6.3 /* Version b2 Rev. 1 July 2012 Version a02 Rev. 15th Oct 2008 TigerDB by Charles Phillips <charles@doublerebel.com> Designed for Tiger for Spine on Titanium 0.0.2+ Persistent storage through Titanium's SQLite DB Based on javascript-client-side-sqlite3-wrapper http://code.google.com/p/javascript-client-side-sqlite3-wrapper/ Tested working on Safari 5525.20.1 (should work in Chrome as well?) and will not work in Firefox cause Gears interface has not been implemented. Only INSERT and FIND are working. USE AS FOLLOW: To create a new local database and then a table on the client's machine: storage = LocalStorage('DatabaseName') storage.createTable('Phonebook','Name','Number') Note that TABLES MUST START with a CAPITAL letter. When a table is created, it is automatically mapped as a method/property of the storage that created it. Hence we can write: storage.Phonebook To get to that table and perform CRUD operations on it. ------ INSERT ------ INSERTS can take two forms. storage.Phonebook.insert('Girl Next Door','555-000-001') Or, the second form allows us to stuff a hash (interchangeably an object in Javascript) into the table. We declare an object/hash. person = {} person['Name'] = "Girl Next Door" person['Number'] = "555-000-001" storage.Phonebook.insert(person) Similarly, we could have also declared the object as: person = { Name: "Girl Next Door", Number: "555-000-001" } Note: NO ERROR CHECKING has currently been implemented, if you spell the field names wrong, or give it too many/few parameters, the operation will fail silently! (In JS, but Titanium will throw an SQLite error) Sidenote on allowing this "object form" is that it could be possible to persist arbitrary Javascript objects with the database#without knowing beforehand* their properties since SQLite can add more columns when needed on the fly! ------ FIND ------ #find takes two forms (and a 1/2!). To retrieve all rows in a table: storage.Phonebook.find() All SQLite tables have an implicit rowid column which is unique. We can therefore -- like in Rails -- pass #find an array of rowid's: storage.Phonebook.find([1,2,3,4,5]) The more customary approach is to query using one/multiple conditions (e.g. the WHERE SQL expression). To do this we pass #find a hash. storage.Phonebook.find( { Name:'Girl Next Door',Number:'555' } ) Here the "contains" clause is implied, as in the field contains the string (e.g. SQLite's#like* keyword). If the column contains numeric data, then we would query it like this. storage.Players.find( {Wins:'>10'} ) The conditionals are passed in as strings and plugged into the SQL statement. There are six million things wrong with doing this. Further TO DO's for #find include searching DATETIME and possibly pagination (as in return how many rows per page and how many pages). ------- RESTORING ON PAGE RELOAD ------- When the page is reloaded, restoring the mapping of the storage to its tables is achieved by calling: storage.restoreState() Hence, this statement should almost always be called right after the storage is instantiated. */ (function() { var Model, Tiger, TigerDB, TigerTable, dbName, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __slice = [].slice, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Tiger = this.Tiger || require('./tiger'); Model = Tiger.Model; TigerDB = (function(_super) { var mapTable, mapTables; __extends(TigerDB, _super); TigerDB.include(Tiger.Log); function TigerDB(dbName) { var err; try { this.debug("Opening Database " + dbName); if (Ti.Database) { this.db = Ti.Database.open(dbName); } else { this.debug("No Ti.Database."); } } catch (_error) { err = _error; this.debug("Couldn't open the database."); } } mapTable = function(tableName) { this[tableName] = new TigerTable(this.db, tableName, this.executeQuery); return this.debug(this[tableName].name); }; mapTables = function(row) { if (row.name.match(/^[A-Z]/)) { return mapTable(row.name); } }; TigerDB.prototype.executeQuery = function(string, args, callback) { var arg, error, fields, i, rawValue, resultArray, resultSet, row, value, _i, _ref; args = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = args.length; _i < _len; _i++) { arg = args[_i]; _results.push(/^[0-9]+\.?[0-9]*$/.exec(arg) && ("\"" + (String(arg)) + "\"") || arg); } return _results; })(); this.debug("SQL: " + string + " | " + (args.join())); resultSet = this.db.execute(string, args); resultArray = []; if (resultSet) { fields = Ti.Platform.osname === 'android' ? resultSet.fieldCount : resultSet.fieldCount(); while (resultSet.isValidRow()) { row = {}; for (i = _i = 0, _ref = fields - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { rawValue = resultSet.field(i); try { value = JSON.parse(rawValue); } catch (_error) { error = _error; value = rawValue; } row[resultSet.fieldName(i)] = value; } this.debug("Result: " + (JSON.stringify(row))); resultArray.push(row); resultSet.next(); } resultSet.close(); } if (callback) { callback(); } return resultArray; }; TigerDB.prototype.restoreState = function() { this.executeQuery("SELECT name FROM sqlite_master WHERE type='table'", [], mapTables); return this; }; TigerDB.prototype.createTable = function(name, columns) { this[name] = new TigerTable(this.db, name, this.executeQuery); return this[name].commit(columns); }; TigerDB.prototype.destroyTable = function(name) { this.executeQuery("DROP TABLE IF EXISTS " + name, [], null); return delete this[name]; }; return TigerDB; })(Tiger.Class); TigerTable = (function(_super) { __extends(TigerTable, _super); TigerTable.include(Tiger.Log); function TigerTable(db, name, executeQuery) { this.db = db != null ? db : ""; this.name = name != null ? name : ""; this.executeQuery = executeQuery; } TigerTable.prototype.commit = function(attributes) { var columns, i, sendStr; columns = Tiger.makeArray(attributes); for (i in columns) { if (!(columns[i] === "id")) { continue; } columns[i] = "id TEXT NOT NULL PRIMARY KEY"; break; } sendStr = "CREATE TABLE IF NOT EXISTS " + this.name + "(" + columns + ")"; return this.executeQuery(sendStr, [], null); }; TigerTable.prototype.insert = function() { var args, cols, i, qStr, sendStr, _i, _j, _len, _len1, _ref; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; qStr = []; sendStr = ""; if (typeof args[0] === "string" || typeof args[0] === "number") { for (_i = 0, _len = args.length; _i < _len; _i++) { i = args[_i]; qStr.push("?"); } sendStr = "INSERT OR REPLACE INTO " + this.name + " VALUES (" + qStr + ")"; } else { _ref = this.deconstruct(args[0]), cols = _ref[0], args = _ref[1]; for (_j = 0, _len1 = args.length; _j < _len1; _j++) { i = args[_j]; qStr.push("?"); } sendStr = "INSERT OR REPLACE INTO " + this.name + " (" + cols + ") VALUES (" + qStr + ")"; } return this.executeQuery(sendStr, args); }; TigerTable.prototype.remove = function() { var args, cols, i, qStr, sendStr, _i, _j, _len, _len1, _ref; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; qStr = []; sendStr = ""; if (typeof args[0] === "string" || typeof args[0] === "number") { for (_i = 0, _len = args.length; _i < _len; _i++) { i = args[_i]; qStr.push("?"); } sendStr = "DELETE FROM " + this.name + " WHERE ID = (" + qStr + ")"; } else { _ref = this.deconstruct(args[0]), cols = _ref[0], args = _ref[1]; for (_j = 0, _len1 = args.length; _j < _len1; _j++) { i = args[_j]; qStr.push("?"); } sendStr = "DELETE FROM " + this.name + " WHERE (" + cols + ") = (" + qStr + ")"; } return this.executeQuery(sendStr, args); }; TigerTable.prototype.all = function() { return this.executeQuery("select * from " + this.name, []); }; TigerTable.prototype.find = function() { var args, key, lastStr, sendStr, val; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; if (!args.length) { return this.all(); } if (Tiger.isArray(args[0])) { sendStr = "select * from " + this.name + " where id in (" + (args[0].join()) + ")"; return this.executeQuery(sendStr, []); } else { args = args[0]; sendStr = []; for (key in args) { val = args[key]; if (val.match(/^[\d\>\<]/)) { sendStr.push("" + key + " " + val); } else { sendStr.push("" + key + " like '%" + val + "%'"); } } lastStr = ("select * from " + this.name + " where ") + sendStr.join(" and "); return this.executeQuery(lastStr, []); } }; TigerTable.prototype.deconstruct = function(obj) { var key, keys, val, vals; keys = []; vals = []; for (key in obj) { val = obj[key]; keys.push(key); vals.push(typeof val === 'object' ? JSON.stringify(val) : val); } return [keys, vals]; }; return TigerTable; })(Tiger.Class); dbName = Ti.App.name.replace(/[^a-zA-Z0-9]/g, ""); Model.TigerDB = { db: new TigerDB(dbName), install: function() { if (__indexOf.call(this.attributes, "id") < 0) { this.attributes.push("id"); } this.db.createTable(this.name, this.attributes); this.installed = true; return this; }, uninstall: function() { this.db.destroyTable(this.name); this.installed = false; return this; }, extended: function() { Tiger.Log.debug("extending Tiger.DB"); this.change(this.updateTigerDB); return this.fetch(this.loadTigerDB); }, updateTigerDB: function(record, method) { Tiger.Log.debug("Update DB: " + method, record.attributes()); switch (method) { case "create": case "update": this.db[this.name].insert(record.attributes()); break; case "destroy": this.db[this.name].remove(record.id); break; } return this; }, loadTigerDB: function(filter) { var result; Tiger.Log.debug("Loading Database Table " + this.name); if (!this.installed) { this.install(); } result = filter ? this.db[this.name].find(filter) : this.db[this.name].all(); if (!result) { return result; } else { return this.refresh(result); } } }; Tiger.DB = TigerDB; if (typeof module !== "undefined" && module !== null) { module.exports = TigerDB; } }).call(this);