-
-
Save aaronksaunders/aae1962d3b4076763ab2 to your computer and use it in GitHub Desktop.
using backbone associate for simple relationships with Appcelerator Titanium Alloy https://github.com/rjz/backbone.associate
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.associate.js v0.0.6 | |
* (c) 2013, RJ Zaworski | |
* | |
* Presumptionless model relations for Backbone.js | |
* Released under the MIT License | |
* | |
* https://github.com/rjz/backbone.associate | |
*/ | |
(function (root, factory) { | |
// CommonJS compatibilty | ADDED CODE HERE SO ALLOY UNDERSCORE AND BACKBONE WOULD BE USED | |
if (typeof window == 'undefined' && !Ti) { | |
factory(require('underscore'), require('backbone')); | |
} | |
// AMD compat would go here, but requirejs `shim` config is a better option | |
else { | |
factory(root._, root.Backbone); | |
} | |
})(this, function (_, Backbone) { | |
var | |
// Sift through a map of attributes and initialize any | |
// known associations | |
_filterAssociates = function (attributes) { | |
var self = this, | |
current = self.attributes, | |
key, association, associations = self._associations, | |
omit = []; | |
for (key in associations) { | |
association = associations[key]; | |
if (current[key] instanceof association.type) { | |
if (attributes[key] instanceof association.type) { | |
current[key] = attributes[key]; | |
omit.push(key); | |
} | |
else if (_.has(attributes, key)) | |
{ | |
if (current[key] instanceof Backbone.Model) { | |
current[key].set(attributes[key]); | |
omit.push(key); | |
} | |
else if (current[key] instanceof Backbone.Collection) { | |
current[key].reset(attributes[key]); | |
omit.push(key); | |
} | |
} | |
} | |
else if (!(attributes[key] instanceof association.type)) { | |
attributes[key] = new (association.type)(attributes[key]); | |
} | |
} | |
return _.omit(attributes, omit); | |
}, | |
// Wraps a method, exposing an "unwrap" method for reverting it later | |
_wrapMethod = function (wrapper, key) { | |
var self = this, | |
original = self[key], | |
wrapped = _.wrap(original, wrapper); | |
wrapped.unwrap = function () { | |
self[key] = original; | |
}; | |
self[key] = wrapped; | |
}, | |
// Extensions to Backbone.Model for filtering associate data, etc, etc | |
_extensions = { | |
// Updates `set` to handle supplied attributes | |
set: function (original, key, val, options) { | |
var self = this, | |
attributes = {}; | |
if (_.isObject(key)) { | |
_.extend(attributes, key); | |
} | |
else { | |
attributes[key] = val; | |
} | |
if (_.isObject(val) && (typeof options === "undefined" || options === null)) { | |
options = val; | |
} | |
return original.call(self, _filterAssociates.call(self, attributes), options); | |
}, | |
// Updates `toJSON` to serialize associated objects | |
toJSON: function (original, options) { | |
var self = this, | |
key, associations = self._associations, | |
attributes = original.call(self, options); | |
for (key in associations) { | |
if (attributes[key] instanceof associations[key]['type']) { | |
attributes[key] = attributes[key].toJSON(); | |
} | |
} | |
return attributes; | |
} | |
}, | |
// Patch initialize method to setup associations and filter initial attributes | |
_initialize = function (original, attrs, options) { | |
var self = this, | |
extensions = _.clone(_extensions); | |
// Provide associate accessors | |
for (key in self._associations) { | |
extensions[key] = _.partial(self.get, key); | |
} | |
// Wrap extensions around existing class methods | |
_.each(extensions, _wrapMethod, self); | |
// Filter any attributes that slipped by without parsing | |
_filterAssociates.call(self, self.attributes); | |
// Pass control back to the original initialize method | |
return original.call(self, attrs, options); | |
}; | |
// Define associations for a model | |
Backbone.associate = function (klass, associations) { | |
var proto = klass.prototype; | |
if (!proto._associations) { | |
// Patch initialize method in prototype | |
_wrapMethod.call(proto, _initialize, 'initialize'); | |
// Add namespace for associations | |
proto._associations = {}; | |
} | |
_.extend(proto._associations, associations); | |
}; | |
// Remove model associations | |
Backbone.dissociate = function (klass) { | |
var proto = klass.prototype; | |
proto.initialize.unwrap(); | |
proto._associations = null; | |
}; | |
}); |
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
// | |
// EXAM MODEL OBJECT | |
// | |
// twitter: @aaronksaunders | |
// blog.clearlyinnovative.com | |
// | |
exports.definition = { | |
config : { | |
columns : { | |
}, | |
adapter : { | |
type : "properties", | |
collection_name : 'exams', | |
} | |
}, | |
extendModel : function(Model) { | |
_.extend(Model.prototype, { | |
// extended functions and properties go here | |
/** | |
* need this function to return the actual Class for the Model | |
* there currently is no other way that I know of to do this | |
*/ | |
Klass : function() { | |
return Model; | |
} | |
}); | |
return Model; | |
}, | |
extendCollection : function(Collection) { | |
_.extend(Collection.prototype, { | |
/** | |
* need this function to return the actual Class for the Collection | |
* there currently is no other way that I know of to do this | |
*/ | |
Klass : function() { | |
return Collection; | |
}, | |
/** | |
* cleanup function to remove all of the objects. | |
* | |
* added this for testing purposes | |
*/ | |
cleanup : function() { | |
var regex = new RegExp("^(" + this.config.adapter.collection_name + ")\\-(.+)$"); | |
var TAP = Ti.App.Properties; | |
_.each(TAP.listProperties(), function(prop) { | |
var match = prop.match(regex); | |
if (match) { | |
TAP.removeProperty(prop); | |
Ti.API.info('deleting old model ' + prop); | |
} | |
}); | |
} | |
}); | |
return Collection; | |
} | |
} | |
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
// | |
// INDEX.JS | |
// | |
// twitter: @aaronksaunders | |
// blog.clearlyinnovative.com | |
// | |
require('backbone.associate'); | |
$.index.open(); | |
// | |
// if true then delete existing data and create new models and dump output | |
// if false just dump the existing output... this is to test that the associations | |
// are actually being persisted | |
// | |
var CREATE_SAMPLE_EXAM = true; | |
// create the variables | |
var question, midtermExam; | |
var Exam = Alloy.createModel('exam'); | |
var Exams = Alloy.createCollection('exam'); | |
var Questions = Alloy.createCollection('question'); | |
// create the association between the Exam and the questions. | |
// notice the new function on the extended Alloy model object. | |
// it is used to return the class so backbone.associate can | |
// extend it | |
Backbone.associate(Exam.Klass(), { | |
questions : { | |
type : Questions.Klass() | |
} | |
}) | |
// CLEAN UP OLD DATA - uncomment this code to clean up the models and | |
// run the test all over again | |
if ( CREATE_SAMPLE_EXAM === true ) { | |
Alloy.createCollection('exam').cleanup(); | |
Alloy.createCollection('question').cleanup(); | |
// create a new exam | |
midtermExam = createANewExam(); | |
// dump the exam results | |
fetchExams(); | |
} | |
/** | |
* query the exams collections and dump the contents | |
*/ | |
function fetchExams() { | |
Alloy.createCollection('exam').fetch({ | |
success : function(_model, _response) { | |
Ti.API.info("EXAM: " + JSON.stringify(_model.models[0].toJSON(), null, 2)); | |
Ti.API.info("QUESTIONS: " + JSON.stringify(_model.models[0].questions().toJSON(), null, 2)); | |
}, | |
error : function() { | |
} | |
}); | |
} | |
/** | |
* create an exam and associated the question objects | |
*/ | |
function createANewExam() { | |
var midtermExam = Alloy.createModel('exam', { | |
name : "Midterm2013" | |
}); | |
midtermExam.save(); | |
_.each(['one', 'two', 'three', 'four'], function(_item, _index) { | |
question = Alloy.createModel('question', { | |
exam_id : midtermExam.id, | |
title : "Question number " + _item | |
}); | |
midtermExam.questions().add(question); | |
}); | |
midtermExam.save(); | |
return midtermExam; | |
} |
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
// | |
// QUESTION MODEL OBJECT | |
// | |
// twitter: @aaronksaunders | |
// blog.clearlyinnovative.com | |
// | |
exports.definition = { | |
config : { | |
columns : {}, | |
adapter : { | |
type : "properties", | |
collection_name : 'questions', | |
} | |
}, | |
extendModel : function(Model) { | |
_.extend(Model.prototype, { | |
// extended functions and properties go here | |
/** | |
* need this function to return the actual Class for the Model | |
* there currently is no other way that I know of to do this | |
*/ | |
Klass : function() { | |
return Model; | |
} | |
}); | |
return Model; | |
}, | |
extendCollection : function(Collection) { | |
_.extend(Collection.prototype, { | |
/** | |
* need this function to return the actual Class for the Collection | |
* there currently is no other way that I know of to do this | |
*/ | |
Klass : function() { | |
return Collection; | |
}, | |
/** | |
* cleanup function to remove all of the objects. | |
* | |
* added this for testing purposes | |
*/ | |
cleanup : function() { | |
var regex = new RegExp("^(" + this.config.adapter.collection_name + ")\\-(.+)$"); | |
var TAP = Ti.App.Properties; | |
_.each(TAP.listProperties(), function(prop) { | |
var match = prop.match(regex); | |
if (match) { | |
TAP.removeProperty(prop); | |
Ti.API.info('deleting old model ' + prop); | |
} | |
}); | |
} | |
}); | |
return Collection; | |
} | |
} | |
@Marza you need to use a new version of underscore
https://twitter.com/aaronksaunders/status/351016886974152709
Aaron,
Does this work well with tableview databinding in Alloy?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Which version of Titanium does this work on? Im running into this error with 3.1.1