Skip to content

Instantly share code, notes, and snippets.

@mattetti
Created August 22, 2011 22:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mattetti/1163733 to your computer and use it in GitHub Desktop.
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
*/
@jamesarosen
Copy link

If DS.saveRecord were to return the $.ajax call, consumers could do .done and .fail for callbacks. No need to accept extra params.

@jamesarosen
Copy link

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.

@jamesarosen
Copy link

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.

@mattetti
Copy link
Author

@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.

@jamesarosen
Copy link

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.

@jamesarosen
Copy link

Some updates & fixes by the above-mentioned @staugaard and @shajith are available here: https://gist.github.com/1167122

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment