Skip to content

Instantly share code, notes, and snippets.

@jamesarosen
Forked from mattetti/sc-simple-ds.js
Created August 24, 2011 01:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamesarosen/1167122 to your computer and use it in GitHub Desktop.
Save jamesarosen/1167122 to your computer and use it in GitHub Desktop.
Shed.Resource
(function(window) {
Shed.Resource = SC.Object.extend({
fetch: function() {
if (!this.hasFetched) {
this.hasFetched = true;
var self = this;
this.deferedFetch = $.getJSON(this.resourceURL())
.done(function(json) {
self.set('data', self.constructor.parse(json));
});
}
return this.deferedFetch;
},
resourceURL: function() {
return this.constructor.resourceURL(this);
},
// Turn this resource into a JSON object to be saved via AJAX. Override
// this method to produce different syncing behavior.
toJSON: function() {
return this.get('data');
},
isNew: function() {
return !this.get('id');
},
save: function() {
var ajaxOptions = {
data: this.toJSON()
};
if (this.isNew()) {
ajaxOptions.type = 'POST';
ajaxOptions.url = this.constructor.resourceURL();
} else {
ajaxOptions.type = 'PUT';
ajaxOptions.url = this.resourceURL();
}
return $.ajax(ajaxOptions);
}
});
Shed.Resource.reopenClass({
// Create an instance of this resource. If `options` includes an
// `id`, first check the identity map and return the existing resource
// with that ID if found.
create: function(options) {
var instance;
if (options && options.id) {
var id = options.id.toString();
this.identityMap = this.identityMap || {};
instance = this.identityMap[id];
if (!instance) {
this.identityMap[id] = instance = this._super.call(this);
instance.set('data', options);
}
} else {
instance = this._super.call(this);
instance.set('data', options);
}
return instance;
},
// Define a property of a resource.
property: function(transforms) {
var from = transforms && transforms.from,
to = transforms && transforms.to;
return function(name, value) {
var data = SC.get(this, 'data'), val;
if (!data || !data.hasOwnProperty(name)) {
this.fetch();
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');
},
// Parse JSON -- likely returned from an AJAX call -- into the
// properties for an instance of this resource. Override this method
// to produce different parsing behavior.
parse: function(json) {
return json;
},
// Define a resource class.
//
// Parameters:
//
// * `schema` -- the properties schema for the resource class
// * `url` -- either a function that returns the URL for syncing
// resources or a string. If the latter, a string of the form
// "/widgets" is turned into a function that returns "/widgets" if
// the Widget's ID is not present and "/widgets/{id}" if it is.
// * `parse` -- the function used to parse JSON returned from AJAX
// calls into the resource properties. By default, simply returns
// the JSON.
define: function(options) {
options = options || {};
var klass = this.extend(options.schema);
var classOptions = {
url: options.url
};
if (options.parse) {
classOptions.parse = options.parse;
}
klass.reopenClass(classOptions);
return klass;
},
resourceURL: function(instance) {
if ($.isFunction(this.url)) {
return this.url(instance);
} else {
if (instance) {
return this.url + '/' + instance.get('id');
} else {
return this.url;
}
}
}
});
Shed.Resource.property.string = Shed.Resource.property();
Shed.Resource.property.hash = Shed.Resource.property();
Shed.Resource.property.integer = Shed.Resource.property({
from: function(raw) {
return Number(raw);
},
to: function(number) {
return String(number);
}
});
Shed.Resource.property.date = Shed.Resource.property({
from: function(raw) {
return new Date(raw);
},
to: function(date) {
return JSON.stringify(date);
}
});
Shed.Resource.property.boolean = Shed.Resource.property({
from: function(raw) {
return raw === true || raw === 'true';
},
to: function(bool) {
return !!bool;
}
});
Shed.Resource.resource = function(options) {
options = options || {};
return function(name, value) {
var klass = SC.getPath(options.className);
var obj = klass.create({
id: this.get(options.property)
});
if (value !== undefined) {
SC.set(this, options.property, value.get('id'));
return;
} else {
return obj;
}
}.property(options.property).cacheable();
};
Shed.Resource.resources = function(options) {
options = options || {};
return function(name, value) {
var collectionOptions = {
type: SC.getPath(options.className),
url: options.url
};
if ($.isFunction(options.url)) {
collectionOptions.url = options.url(this);
} else if('string' === typeof options.url) {
collectionOptions.url = options.url.fmt(this.get('id'));
}
if (options.parse) {
collectionOptions.parse = options.parse;
}
var collection = Shed.ResourceCollection.create(collectionOptions);
return collection;
}.property('id').cacheable();
};
Shed.ResourceCollection = SC.ArrayProxy.extend({
type: SC.Required,
fetch: function() {
if (!this.hasFetched && ! this.prePopulated) {
this.hasFetched = true;
var content = [],
self = this;
this.deferedFetch = $.getJSON(this.url || this.type.resourceURL()).done(function(json) {
_.each(self.parse(json), function(itemAttributes) {
content.push(self.type.create(itemAttributes));
});
self.set('content', content);
});
}
return this.deferedFetch;
},
parse: function(json) {
return _.map(json, this.type.parse);
},
content: function(name, value) {
if (value === void(0)) {
this.fetch();
return this.realContent;
} else {
this.realContent = value;
return value;
}
}.property()
});
Shed.ResourceCollection.reopenClass({
create: function(options) {
options = options || {};
var content = options.content;
delete options.content;
options.prePopulated = !!content;
var instance = this._super.call(this, options);
if (content) {
instance.set('content', content);
}
return instance;
}
});
}(this));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment