Created
January 7, 2015 23:54
-
-
Save geddski/bc850e1102431e9d5659 to your computer and use it in GitHub Desktop.
model.js
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
/** | |
* Feedback from real use: | |
* 1. Need to be able to transform the entire request before it even arrives to the model, | |
* not just transform each item (but want that too) | |
* 2. Declaring which fields you want is kinda lame, I wasted time trying to track down | |
* why the "children" field wasn't showing up. | |
* 3. Caching most definitely needs to be optional | |
* 4. transformOut CAN'T modify the original object right? Or the UI will hork? It's safe when using toJSON and fromJSON since those make new objects I think. Will need to add a test for this. | |
* 5. model.json() and model.rest() APIs seem wonky, should just be combined. Can still make cache separete or make it an option. | |
*/ | |
function($http, $q){ | |
/** | |
* add CRUD functionality to a model | |
*/ | |
var model = function(){}; | |
model.rest = function(obj, args){ | |
model.mixin(obj, 'get', args); | |
model.mixin(obj, 'getAll', args); | |
model.mixin(obj.prototype, 'delete', args); | |
model.mixin(obj.prototype, 'save', args); | |
}; | |
/** | |
* add toJSON and fromJSON functions | |
*/ | |
model.json = function(contructor, fields){ | |
contructor.prototype.toJSON = function(){ | |
var json = {}; | |
_.each(fields, function(field){ | |
json[field] = this[field]; | |
}, this); | |
return json; | |
}; | |
// add only the fields from the object that exist in the fields array | |
contructor.prototype.fromJSON = function(obj){ | |
_.each(fields, function(field){ | |
if (!obj[field]) { return; } | |
this[field] = obj[field]; | |
}, this); | |
}; | |
} | |
/** | |
* return just the data from a succesful response | |
*/ | |
model.getData = function(obj){ | |
return obj.data; | |
}; | |
/** | |
* update the model with the results from the service call (like ngResource) | |
*/ | |
model.updatesModel = function(instance, promise, shouldUpdate){ | |
if (shouldUpdate === true){ | |
promise.then(function(data){ | |
update(instance, data.data); | |
}); | |
} | |
}; | |
// NOT USING CACHING YET | |
// function cache(key){ | |
// return function(obj){ | |
// var cache = $angularCacheFactory.get('UrlCache') || $angularCacheFactory('UrlCache'); | |
// cache.put(key, obj); | |
// return obj; | |
// } | |
// } | |
// NOT USING CACHING YET | |
// function checkCache(key){ | |
// var deferred = $q.defer(); | |
// var cache = $angularCacheFactory.get('UrlCache'); | |
// if (cache){ | |
// var item = cache.get(key); | |
// if (item){ | |
// deferred.resolve(item); | |
// } | |
// else{ | |
// deferred.reject(); | |
// } | |
// } | |
// else{ | |
// deferred.reject(); | |
// } | |
// return deferred.promise; | |
// } | |
function update(a, b){ | |
Object.getOwnPropertyNames(b).forEach(function(prop){ | |
a[prop] = b[prop]; | |
}); | |
} | |
function noTransform(data){ | |
return data; | |
} | |
function wrap(obj){ | |
return function(data){ | |
return new obj(data); | |
} | |
} | |
function all(fn){ | |
return function(items){ | |
return items.map(fn); | |
} | |
} | |
/** | |
* TODO: flesh this out more | |
* Assumptions for now: | |
* - If the paramVals is not an object, then we'll replace every | |
* propName with the paramVals value. | |
* - If the paramVals is falsey, we'll replace propNames with an empty string | |
* - If the parsedUrl ends with a slash, we'll strip it. | |
* @param url - the URL with /:propNames to replace with values | |
* @param paramVals - the values to replace the /:propNames with | |
* @returns {string} - The parsed URL | |
*/ | |
function buildUrl(url, paramVals) { | |
var isObject = angular.isObject(paramVals); | |
var isEmpty = !paramVals; | |
var parsedUrl = url.replace(/:(\w+)/g, function(match, param) { | |
if (isEmpty) { | |
return ''; | |
} else if (isObject) { | |
return paramVals[param] || ''; | |
} else { | |
return paramVals; | |
} | |
}); | |
return parsedUrl.replace(/\/$/, ''); | |
} | |
//expose utilities | |
model.all = all; | |
model.wrap = wrap; | |
model.udpate = update; | |
// model.cache = cache; | |
//------mixins-------// | |
model.mixin = function(obj, mixin, args){ | |
obj[mixin] = model[mixin](args, obj); | |
}; | |
//TODO: conditional caching | |
model.get = function(args, obj){ | |
return function(options){ | |
var url = buildUrl(args.url, options); | |
return $http({method: 'GET', url: url}) | |
.then(model.getData) | |
.then(args.transformIn || obj.transformIn || noTransform) | |
.then(wrap(obj)) | |
} | |
}; | |
model.getAll = function(args, obj){ | |
return function(){ | |
var url = buildUrl(args.url); | |
return $http({method: 'GET', url: url }) | |
.then(model.getData) | |
.then(all(args.transformIn || obj.transformIn || noTransform)) | |
.then(all(wrap(obj))) | |
} | |
}; | |
model.delete = function(args){ | |
return function(){ | |
// var url = args.url + '/' + this.id; | |
var url = buildUrl(args.url, this); | |
return $http({method: 'DELETE', url: url}).then(model.getData); | |
}; | |
}; | |
model.save = function(args){ | |
return function(options){ | |
options = options || {}; | |
var url = buildUrl(args.url, options); | |
var method = 'POST'; | |
if (this.id){ | |
//if has an id, do an update rather than a create | |
url += '/' + this.id; | |
method = 'PUT'; | |
} | |
var data = this; | |
if (_.isFunction(this.toJSON)){ | |
data = this.toJSON(); | |
} | |
var transformOut = args.transformOut || this.constructor.transformOut || noTransform; | |
var httpPromise = $http({method: method, url: url, data: transformOut(data) }); | |
model.updatesModel(this, httpPromise, options.update); | |
return httpPromise.then(model.getData); | |
}; | |
} | |
return model; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment