-
-
Save mattetti/1163733 to your computer and use it in GitHub Desktop.
DS = SC.Namespace.create(); | |
DS.property = function(transforms) { | |
return SC.computed(function(key, value) { | |
var data = get(this, 'data'), value, val, | |
name = transforms.keyName || key; | |
var from = transforms && transforms.from; | |
var to = transforms && transforms.to; | |
// if the data hasn't been populated yet, return immediately | |
if (!data) { return; } | |
if (value !== undefined) { | |
val = to ? to(value) : value; | |
SC.set(data, name, val); | |
} else { | |
value = SC.get(data, name); | |
if (from) { value = from(value); } | |
} | |
return value; | |
}).property('data') | |
}; | |
DS.property.string = DS.property; | |
DS.property.integer = function(keyName) { | |
return DS.property({ | |
keyName: keyName, | |
from: function(serialized) { | |
return Number(serialized); | |
}, | |
to: function(deserialized) { | |
return String(deserialized); | |
} | |
}); | |
}; | |
DS.property.date = function(keyName) { | |
return DS.property({ | |
keyName: keyName, | |
from: function(raw) { | |
return new Date(raw); | |
}, | |
to: function(date) { | |
return JSON.stringify(date); | |
} | |
}) | |
}; | |
DS.hasMany = function(other, klass) { | |
return function() { | |
var keys = SC.get(this.get('data'), other); | |
if (keys) { | |
var recordArray = SC.ArrayProxy.create(), content = []; | |
keys.forEach(function(key) { | |
content.push(klass.load(key)); | |
}) | |
recordArray.set('content', content); | |
return recordArray; | |
} else { | |
return; | |
} | |
}.property('data.' + other + '.@each').cacheable() | |
}; | |
DS.RecordArray = SC.Object.extend(SC.Array, { | |
keys: null, | |
objectAt: function(idx) { | |
var keys = this.get('keys'), | |
klass = this.get('klass'); | |
if (!keys) { return; } | |
var id = keys.objectAt(idx); | |
return klass.loadRecord(id); | |
} | |
}); | |
DS.Record = SC.Object.extend({ | |
unknownProperty: function(key, value) { | |
var data = this.get('data'); | |
if (value !== undefined) { | |
SC.set(data, key, value); | |
} else { | |
value = SC.get(data, key); | |
} | |
return value; | |
}, | |
save: function() { | |
DS.saveRecord(this); | |
} | |
}); | |
DS.RECORDS = {}; | |
DS.recordHashFor = function(klass, id) { | |
var recordsHash = DS.RECORDS[SC.guidFor(klass)]; | |
if (!recordsHash) { DS.RECORDS[SC.guidFor(klass)] = {} } | |
return recordsHash[id]; | |
}; | |
DS.setRecordHash = function(klass, id, hash) { | |
var recordsHash = DS.RECORDS[SC.guidFor(klass)]; | |
if (!recordsHash) { DS.RECORDS[SC.guidFor(klass)] = {}; recordsHash = {} } | |
recordsHash[id] = hash; | |
}; | |
// default method to look up a record by class and id. | |
// a record class can define its own load method for special behavior | |
// | |
// you can override this method to change the global loading behavior | |
DS.loadRecord = function(klass, id, force) { | |
var url = klass['instanceUrl'].fmt(id), | |
resource = klass['resource'], | |
instance = klass.create(); | |
if (!force) { | |
var recordHash = DS.recordHashFor(klass, id); | |
if (recordHash) { return instance.set('data', recordHash); } | |
} | |
$.ajax({ | |
url: url, | |
dataType: 'json', | |
success: function(json) { | |
DS.setRecordHash(klass, id, json[resource]); | |
instance.set('data', json[resource]); | |
} | |
}); | |
return instance; | |
}; | |
// params can be a Hash or a URL-encoded String | |
DS.loadRecords = function(klass, params) { | |
var url = klass['collectionUrl'], | |
resource = klass['resource'], | |
recordArray = SC.ArrayProxy.create(); | |
$.ajax({ | |
url: url, | |
data: params, | |
dataType: 'json', | |
success: function(json) { | |
var rawArray = json[resource], array = []; | |
rawArray.forEach(function(item) { | |
DS.setRecordHash(klass, item.id, item); | |
array.push(klass.create({ data: item })); | |
}); | |
recordArray.set('content', array); | |
} | |
}); | |
// Return the recordArray right away. It will asynchronously | |
// be populated with data when the data comes in via Ajax | |
return recordArray; | |
} | |
DS.saveRecord = function(record) { | |
var url = SC.get(this.constructor, 'instanceUrl'); | |
url = url.fmt(this.get('id')); | |
// TODO: Something on success vs. failure | |
$.ajax({ | |
type: 'PUT', | |
url: url, | |
data: SC.get(this, 'data'), | |
dataType: 'json', | |
}); | |
} | |
DS.Record.reopenClass({ | |
// force forces the record to be loaded even if it's already | |
// been fetched | |
load: function(id, force) { | |
return DS.loadRecord(this, id, force); | |
}, | |
// fetches all records. You can specify params as a Hash or | |
// URL-encoded string to narrow down the results. Returned | |
// records will override existing records | |
// | |
// TODO: Make sure instantiated objects get the updated | |
// data hashes. | |
loadAll: function(params) { | |
return DS.loadRecords(this, params); | |
} | |
}); | |
/** | |
USAGE | |
// TODO: Convention over configuration for instance URL, resource | |
// name and class URL | |
Person = DS.Record.extend({ | |
firstName: DS.property.string(), | |
lastName: DS.property.string(), | |
fullName: function() { | |
return this.get('firstName') + ' ' + this.get('lastName'); | |
}.property('firstName', 'lastName') | |
}); | |
Person.reopenClass({ | |
collectionUrl: "/people", | |
instanceUrl: "/people/%@", | |
resource: "people" | |
}); | |
var person = Person.load(1); | |
var people = Person.loadAll(); // can use this wherever an | |
// Array is expected, like in | |
// an ArrayController | |
*/ |
DS. loadRecord
doesn't actually use an identity map. If you load Person
14 twice, there are two different instances. It's just that the second call doesn't cause another AJAX call.
I'm pondering how to implement something like Person.allByTag(tagID)
(which would GET from /tags/:tagID/people
) and Person.allInGroup(groupID)
(which would gET from /groups/:groupID/people
). Perhaps Person
could override loadRecords
and switch on some params. If so, it might be nice to have that extracted out so clients don't have to re-implement the whole method just to change some behavior.
@jamesarosen this is a very rudimental prototype that @wycats and I have been working on for a proof of concept. Feel free to modify and improve as you need it.
Totally understood, and thanks for posting this!
I'm mostly thinking out loud here so we can collectively converge. @staugaard and @shajith have been working on a similar library today and will be stealing the best ideas from here. @wycats and I have planned to reconvene later this week to polish and release.
Some updates & fixes by the above-mentioned @staugaard and @shajith are available here: https://gist.github.com/1167122
If
DS.saveRecord
were to return the$.ajax
call, consumers could do.done
and.fail
for callbacks. No need to accept extra params.