Created
February 4, 2011 02:26
-
-
Save sr3d/810643 to your computer and use it in GitHub Desktop.
My own version of ActiveRecord that actually works on Titanium.
This file contains hidden or 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
(function() { | |
App.Models.Task = App.Models.Base.createModel('Task', 'tasks', { | |
name: 'TEXT', | |
note: 'TEXT', | |
due_at: 'DATE', | |
is_completed: 'TEXT', | |
completed_at: 'TEXT', | |
category_id: 'TEXT', | |
event_id: 'INTEGER', | |
has_reminders: 'TEXT', | |
reminder_type_ids: 'TEXT', | |
vendor_id: 'INTEGER', | |
updated_at: 'DATE', | |
created_at: 'DATE', | |
created: 'DATE' | |
}, { | |
// Instance Methods | |
/* return a list of reminders in CSV format */ | |
getRemindersLabel: function() { | |
if( !this.reminder_type_ids ) { return ''; } | |
var ids = this.reminder_type_ids.split(','); | |
var value = []; | |
for(var i = 0; i < ids.length; i++ ) { | |
value.push( REMINDER_TYPES[ +ids[i] ] ); | |
} | |
return value.join(', '); | |
} | |
}, { | |
// CLASS Methods | |
refreshLocal: function(options) { | |
options = _.extend({ onData: function(){} }, options || {} ); | |
App.Models.refreshLocal( App.Models.Task, API_BASE + '/tasks.json', 'task', options); | |
} | |
,complete: function(taskIds, options) { | |
options = _.extend( {onData: function() {}}, options || {} ) ; | |
if( taskIds.length > 0 ) { | |
Request( API_BASE + '/tasks/complete.json', { | |
async: false, | |
method: 'POST', | |
parameters: { task_ids: taskIds.join(','), app_token: APP_TOKEN, api_token: API_TOKEN, email: EMAIL }, | |
onSuccess: function(request) { | |
log('updating'); | |
App.Models.executeNonSelect('UPDATE tasks SET is_completed = ? WHERE id IN (' + taskIds.join(',') + ')', true); | |
options.onData(request); | |
} | |
}); | |
}; | |
} | |
}); | |
App.Models.Task.validatesPresenceOf('name','Name is required'); | |
})(); |
This file contains hidden or 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
(function() { | |
App.Models.Base = {}; | |
App.Models.Base.InstanceMethods = { | |
getAttributes: function() { | |
var attr = {}; | |
var model = App.Models[this.className]; | |
var self = this; | |
_.each(model.fields, function(v,k) { | |
attr[ k ] = self[ k ]; | |
}); | |
return attr; | |
} | |
,setAttributes: function( attrs ) { | |
_.extend(this, attrs); | |
return this; | |
} | |
,updateAttributes: function( attrs) { | |
this.setAttributes(attrs); | |
this.save(); | |
} | |
,save: function( skipValidation ) { | |
if( skipValidation === null ) { skipValidation = false; } | |
if( !skipValidation && !this.validate() ) { return false; }; | |
var db = App.Models.connect(); | |
var sql; | |
var fields = App.Models[ this.className ].fields; | |
var self = this; | |
if( this.persisted ) { | |
if(fields['updated']) { self.updated = (new Date()).toString(); }; | |
sql = 'UPDATE '+this.tableName + ' SET '; | |
var values = [], cols = []; | |
_.each(fields, function(v, k) { | |
cols.push( '"' + k + '" = ?' ); | |
values.push( fields[k] == 'DATE' ? ( _.isDate( self[ k ] ) ? self[k].valueOf() : Date.parse(self[k]).valueOf() ) : self[k] ); | |
}); | |
sql += cols.join(', ') + ' WHERE id = ' + this.id; | |
log(sql + ' ' + JSON.stringify(values) ); | |
db.execute.apply(db, [sql].concat(values) ); | |
} else { | |
sql = 'INSERT INTO ' + this.tableName; // + _.keys(fields).join(',') + ') VALUES ' + ; | |
var values = [], cols = [], placeHolders = [], self = this; | |
// add "created" magic column | |
if(fields['created']) { self.created = (new Date()).toString(); }; | |
_.each(fields, function(v, k) { | |
cols.push('"' + k + '"' ); | |
values.push( fields[k] == 'DATE' ? ( _.isDate( self[ k ] ) ? self[k].valueOf() : Date.parse(self[k]).valueOf() ) : self[k] ); | |
placeHolders.push('?'); | |
} ); | |
sql += ' (' + cols.join(',') + ') '; | |
sql += ' VALUES ( ' + placeHolders.join(',') +' )'; | |
log(sql + '[' + JSON.stringify(values) + ']' ); | |
db.execute.apply(db, [sql].concat(values)); | |
var id = db.lastInsertRowId; | |
this.id = id; | |
this.persisted = true; | |
} | |
this.errors = []; | |
return true; | |
} | |
,destroy: function() { | |
var sql = 'DELETE FROM '+ this.tableName + ' WHERE id = ? LIMIT 1'; | |
log( sql + ' [' + this.id + ']'); | |
(App.Models.connect()).execute(sql, this.id); | |
} | |
,validate: function() { | |
this.errors = {}; | |
var validators = App.Models[this.className].validators; | |
for(var i = 0; i < validators.length; i++ ) { | |
if( !validators[i](this) ) { | |
return false; | |
} | |
} | |
return true; | |
} | |
,toString: function() { | |
return JSON.stringify(this.getAttributes()); | |
} | |
,toRailsParamsHash: function(baseName) { | |
if( !baseName ) { baseName = this.className }; | |
var hash = {}; | |
var self = this; | |
_.each(App.Models[this.className].fields, function(v, k) { | |
// autoconvert date/ date in integer formats to Date object, then make sure it's in RailsISOString | |
hash[baseName + '[' + k +']' ] = ( v == 'DATE' ? ( _.isDate(self[k]) ? self[k].toRailsISOString() : (new Date(self[k])).toRailsISOString() ) : self[k] ); | |
}); | |
return hash; | |
} | |
}; | |
// All methods in here receives the Model as the first argument | |
App.Models.Base.ClassMethods = { | |
fetchedAt: function(model) { | |
var lastFetch = DB.get(model.className + '_fetched_at'); | |
if(!lastFetch){ return null; }; | |
return new Date(lastFetch); | |
} | |
,setFetchedAt: function(model, date) { | |
DB.set( model.className + '_fetched_at', date || (new Date()) ); | |
} | |
,find: function(model, id) { | |
var result; | |
var db = App.Models.connect(); | |
var sql; | |
var fields = _.keys(model.fields); | |
if( _.isString(id) || _.isNumber(id) ) { | |
var rs = db.execute('SELECT * FROM '+ model.tableName +' WHERE id = ?', id); | |
if (rs.isValidRow()) { | |
result = new model(); | |
result.persisted = true; | |
for(var i = 0; i < fields.length; i++ ) { | |
result[ fields[i] ] = rs.fieldByName( fields[i] ); | |
}; | |
} | |
} else { // isArray or null | |
var rs; | |
if( id == null ) { | |
sql ='SELECT * FROM '+ model.tableName; | |
log(sql); | |
rs = db.execute(sql); | |
} else { // find in specific id | |
sql = 'SELECT * FROM '+ model.tableName +' WHERE id IN (?)'; | |
log( sql + '[' + id + ']' ); | |
rs = db.execute(sql, id); | |
}; | |
result = []; | |
while (rs.isValidRow()) { | |
var obj = new model(); | |
obj.persisted = true; | |
for( var i = 0; i < fields.length; i++ ) { | |
obj[ fields[i] ] = rs.fieldByName( fields[i] ); | |
}; | |
result.push(obj); | |
rs.next(); | |
}; | |
} | |
rs.close(); | |
return result; | |
} | |
/* WIP */ | |
,findBySQL: function( model, /* String or []*/ sql, options ) { | |
options = _.extend({ | |
all: false | |
}, options || {} ); | |
var db = App.Models.connect(); | |
var rs, result = [], fields = _.keys(model.fields); | |
log(sql); | |
if(_.isString(sql)) { | |
rs = db.execute(sql); | |
} else { | |
rs = db.execute.apply(db, sql); | |
}; | |
result = []; | |
while (rs.isValidRow()) { | |
// log(rs); | |
var obj = new model(); | |
obj.persisted = true; | |
for( var i = 0; i < fields.length; i++ ) { | |
obj[ fields[i] ] = rs.fieldByName( fields[i] ); | |
}; | |
result.push(obj); | |
if(!options.all) { break; }; | |
rs.next(); | |
}; | |
return options.all ? result : result[0]; | |
} | |
,create: function( model, json ) { | |
var obj = new model( json ); | |
obj.save(); | |
return obj; | |
} | |
,createTable: function(model) { | |
var sql = 'CREATE TABLE IF NOT EXISTS '+ model.tableName + ' (id INTEGER PRIMARY KEY'; | |
var cols = []; | |
_.each(model.fields, function(v,k) { | |
cols.push( '"' + k + '" ' + (v == 'DATE' ? 'FLOAT' : v) ); | |
}); | |
sql += ', ' + cols.join(', ') + ')'; | |
log(sql); | |
App.Models.db.execute(sql); | |
log('Done creating table ' + model.tableName); | |
} | |
,truncate: function(model) { | |
var sql = 'DELETE FROM ' + model.tableName; | |
log(sql); | |
(App.Models.connect()).execute(sql); | |
// log('Done truncating'); | |
} | |
,addValidator: function( model, validator ) { | |
model.validators.push(validator); | |
} | |
,validatesPresenceOf: function(model, field, message) { | |
model.addValidator(function(instance) { | |
var value = (instance.getAttributes())[field]; | |
if(!value || /^\s*$/.test(value)) { | |
instance.errors[ field ] = message || (field + ' ' + ' is required'); | |
return false; | |
}; | |
return true; | |
}); | |
} | |
,validatesFormatOf: function(model, field, format, message) { | |
model.addValidator(function(instance) { | |
var value = instance.getAttributes()[field]; | |
if(value && format.test(value)) { return true; }; | |
instance.errors[ field ] = message || ('Wrong format for ' + field); | |
return false; | |
}); | |
} | |
}; | |
App.Models.Base.createModel = function(modelName, tableName, fields, instanceMethods, classMethods) { | |
var model = function( /* string or json object */ json, options) { | |
if( json ) { | |
if( _.isString(json) ) { | |
json = JSON.parse(json); | |
} | |
_.extend( this, json ); | |
}; | |
this.className = modelName; | |
this.tableName = tableName; | |
this.persisted = false; | |
this.errors = []; | |
} | |
_.extend(model.prototype, App.Models.Base.InstanceMethods); | |
_.extend(model.prototype, instanceMethods || {} ); | |
_.each( _.extend({ | |
tableName: tableName, | |
className: modelName, | |
validators: [], | |
fields: fields, | |
}, App.Models.Base.ClassMethods), function(v,k) { | |
var value = v; | |
// attach the class func to the model, appending the model as the first arg | |
if( _.isFunction(v) ) { | |
value = _.bind(v, model, model); | |
} | |
model[k] = value; | |
}); | |
_.extend(model, classMethods); | |
// model.createTable(); | |
if( !MIGRATE ) { model.createTable() ;} | |
// add the id column to the field list | |
model.fields['id'] = 'INTEGER'; | |
return model; | |
}; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment