Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Backbone + Titanium - part 2
//Customise Backbone.sync to work with Titanium rather than jQuery
var getUrl = function(object) {
if (!(object && object.url)) return null;
return _.isFunction(object.url) ? object.url() : object.url;
};
Backbone.sync = (function() {
var methodMap = {
'create': 'POST',
'read' : 'GET',
'update': 'PUT',
'delete': 'DELETE'
};
return function(method, model, options) {
var xhr = Ti.Network.createHTTPClient({ timeout: 35000 });
var type = methodMap[method],
params = _.extend({}, options);
//==== Start standard Backbone.sync code ====
// Ensure that we have a URL
if (!params.url) params.url = getUrl(model) || urlError();
// Ensure that we have the appropriate request data.
if (!params.data && model && (method == 'create' || method == 'update')) {
params.data = model.toParams();
}
// For older servers, emulate JSON by encoding the request into an HTML-form.
if (Backbone.emulateJSON) {
params.contentType = 'application/x-www-form-urlencoded';
params.processData = true;
params.data = params.data ? {model : params.data} : {};
}
// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if (Backbone.emulateHTTP) {
if (type === 'PUT' || type === 'DELETE') {
if (Backbone.emulateJSON) params.data._method = type;
params.type = 'POST';
params.beforeSend = function(xhr) {
xhr.setRequestHeader('X-HTTP-Method-Override', type);
};
}
}
//==== End standard Backbone.sync code ====
//Handle success
xhr.onload = function() {
var cookies = this.getResponseHeader('Set-Cookie');
if(cookies != null && Ti.Android)
Ti.App.Properties.setString('cookies', cookies);
params.success(JSON.parse(this.responseText));
};
//Handle error
xhr.onerror = params.error;
//Prepare the request
xhr.open(type, params.url);
//Add request headers etc.
if(params.contentType)
xhr.setRequestHeader('Content-Type', params.contentType);
xhr.setRequestHeader('Accept', 'application/json');
if (params.beforeSend) params.beforeSend(xhr);
if(Ti.Android && Ti.App.Properties.getString('cookies'))
xhr.setRequestHeader('Cookie', Ti.App.Properties.getString('cookies'));
//Make the request
xhr.send(params.data);
};
})();
class Post extends Backbone.Model
urlRoot: app.endpoint + "/posts"
toParams: ->
# assuming you've stored an instance of the previous QueryStringBuilder class...
app.query_string_builder.stringify(this.attributes, "post")
class Posts extends Backbone.Collection
url: app.endpoint + "/posts"
model: Post
app.models.Post = Post
app.models.Posts = Posts
class PostViewController
initialize: (@post) ->
# create @window and @tableView
data = []
data.push @createTitleRow()
data.push @createBodyRow()
data.push @createAuthorRow()
@tableView.data = data
createTitleRow: ->
row = Ti.UI.createTableViewRow
title: @post.get('title')
@post.bind 'change:title', (e) =>
row.title = @post.get('title')
row
# ...
class PostsViewController
# assumming an existing @tableView and @window
reload: ->
@posts = new app.models.Posts()
@posts.fetch
success: =>
@prepareData(@posts.models)
error: (e) =>
Ti.API.error JSON.stringify(e)
prepareData: (models) ->
@tableView.data = _.map models, (model) ->
{ title: model.get('title'), model: model, hasChild: true }
class QueryStringBuilder
stringify: (obj, prefix, accum = {}) ->
if _.isArray(obj)
this.stringifyArray obj, prefix, accum
else if _.isString(obj) || _.isNumber(obj) || _.isDate(obj) || "#{obj}" == "[object TiBlob]"
this.stringifyString obj, prefix, accum
else if _.isBoolean(obj)
this.stringifyBoolean obj, prefix, accum
else if obj?
if obj.attributes?
this.stringifyObject obj.attributes, prefix, accum
else
this.stringifyObject obj, prefix, accum
else
return prefix
accum
stringifyBoolean: (bool, prefix, accum) ->
unless prefix
throw new TypeError("Stringify expects an object")
accum[prefix] = if bool then 1 else 0
stringifyString: (str, prefix, accum) ->
unless prefix
throw new TypeError("Stringify expects an object")
accum[prefix] = str
stringifyArray: (arr, prefix, accum) ->
unless prefix
throw new TypeError("Stringify expects an object")
i = 0
for item in arr
this.stringify(item, "#{prefix}[#{i++}]", accum)
stringifyObject: (obj, prefix, accum) ->
for key, value of obj
continue if key.match(/_preview$/)
new_key = key
if _.isArray(value)
new_key = "#{key}_attributes"
new_prefix = ''
if prefix
new_prefix = "#{prefix}[#{encodeURIComponent(new_key)}]"
else
new_prefix = encodeURIComponent new_key
this.stringify value, new_prefix, accum
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment