Last active
July 6, 2016 00:35
-
-
Save softmonkeyjapan/c250c5c6192b4fe03bc6 to your computer and use it in GitHub Desktop.
Medium : Extend $resource with AngularJs WIP (https://medium.com/@loickartono/extends-resource-with-angularjs-9be9e41ab3e8)
This file contains hidden or 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
(function () { | |
'use strict'; | |
angular | |
.module('lk-active-resource', [ | |
'ngResource', | |
'platanus.inflector' | |
]) | |
.provider('ARSettings', function () { | |
this.apiUrl = undefined; | |
this.$get = function () { | |
return { | |
apiUrl: this.apiUrl, | |
}; | |
}; | |
this.configure = function (config) { | |
for (var key in config) { | |
this[key] = config[key]; | |
} | |
}; | |
}) | |
.factory('ActiveResource', function ($resource, ARSettings, inflector, $injector) { | |
/** | |
* Check whether an object is a number. | |
* | |
* @param {Object} object - Object to check numericallity on. | |
* @return {Boolean} True if number, false otherwise. | |
*/ | |
var isNumeric = function (object) { | |
return !isNaN(parseFloat(object)) && isFinite(object); | |
}; | |
/** | |
* Generate options based on arguments passed. | |
* If the object passed is : | |
* - An object : Use it directly. | |
* - A string : Inflect it and use the default config. | |
* - Something else : Throw an error. | |
* | |
* @param {Object} args - Javascript object or string as the name of the resource (singular). | |
* @return {Object} Options to pass to $resource. | |
*/ | |
var sanitizeOptions = function (args) { | |
if (args !== null && typeof args === 'string') { | |
var _resName = inflector.pluralize(args); | |
return { | |
url: '/' + _resName + '/:id/:action', | |
params: { id: '@id' }, | |
namespace: args | |
}; | |
} else if (args !== null && typeof args === 'object') { | |
return args; | |
} else { | |
throw new Error(args + ' is not a valid options'); | |
} | |
}; | |
/** | |
* ActiveResource core definition. | |
*/ | |
var Resource = function (options) { | |
options = sanitizeOptions(options); | |
options.params = options.params || {}; | |
options.methods = options.methods || {}; | |
/** | |
* Transform data before querying the server. | |
* In the case of Rails, will wrap the data with a resource namespace. | |
* | |
* @param {Object} data - Data to send. | |
* @return {String} Stringify data. | |
*/ | |
var transformRequest = function (data) { | |
if (!options.namespace) { | |
return JSON.stringify(data); | |
} | |
var datas = {}; | |
datas[options.namespace] = data; | |
return JSON.stringify(datas); | |
}; | |
/** | |
* Transform data after querying the server. | |
* If the response contains an object (instead of a query) with the resource namespace in plural : | |
* | |
* new ActiveResource('user') => Check for the key users | |
* | |
* then attach to each object the Resource object. This is a particular case | |
* mostly used in pagination scenario. | |
* | |
* @param {Object} data - Data to send. | |
* @return {String} Stringify data. | |
*/ | |
var transformResponse = function (data) { | |
data = JSON.parse(data); | |
if (options.namespace) { | |
var namespace = inflector.pluralize(options.namespace); | |
if (data[namespace]) { | |
var ClassObject = $injector.get( | |
inflector.camelize(inflector.singularize(namespace), true) | |
); | |
angular.forEach(data[namespace], function (object, index) { | |
var instance = new ClassObject(); | |
data[namespace][index] = angular.extend(instance, object); | |
}); | |
} | |
} | |
return data; | |
}; | |
var defaults = { | |
browse: { method: 'GET', transformResponse: transformResponse }, | |
query: { method: 'GET', transformResponse: transformResponse, isArray: true }, | |
get: { method: 'GET', transformResponse: transformResponse }, | |
create: { method: 'POST', transformRequest: transformRequest }, | |
update: { method: 'PATCH', transformRequest: transformRequest }, | |
destroy: { method: 'DELETE' } | |
}; | |
angular.extend(defaults, options.methods); | |
var resource = $resource(ARSettings.apiUrl + options.url, options.params, defaults); | |
/** | |
* Get an entire collection of objects. | |
* | |
* @param {Object} args - $resource.query arguments. | |
* @return {Promise} Promise | |
*/ | |
resource.all = function (args) { | |
var options = args || {}; | |
return this.query(options); | |
}; | |
/** | |
* Get an entire collection of objects. | |
* Since a search is often returning pagination type of data, | |
* the collection of object will be wrapped under a key within that response. | |
* See transformResponse for more information about that case. | |
* | |
* @param {Object} args - $resource.query arguments. | |
* @return {Promise} Promise | |
*/ | |
resource.search = function (args) { | |
var options = args || {}; | |
return this.browse(options); | |
}; | |
/** | |
* Find a specific object. | |
* | |
* @param {Object|Integer} args - $resource.get arguments, or { id: args } if numeric. | |
* @param {Function} callback - $resource.get callback function if any. | |
* @return {Promise} Promise | |
*/ | |
resource.find = function (args, callback) { | |
var options = isNumeric(args) ? { id: args } : args; | |
return this.get(options, callback); | |
}; | |
/** | |
* Mixin custom methods to instance. | |
* | |
* @param {Object} args - Set of properties to mixin the $resource object. | |
* @return {this} this. Chainable. | |
*/ | |
resource.instanceMethods = function (args) { | |
angular.extend(this.prototype, args); | |
return this; | |
}; | |
/** | |
* $resource's $save method override. | |
* Allow to use $save in order to create or update a resource based on it's id. | |
* | |
* @return {Promise} Promise | |
*/ | |
resource.prototype.save = function () { | |
var action = this.id ? '$update' : '$create'; | |
return this[action](); | |
}; | |
/** | |
* Delete instance object. | |
* | |
* @return {Promise} Promise | |
*/ | |
resource.prototype.delete = function () { | |
if (!this.id) { | |
throw new Error('Object must have an id to be deleted.'); | |
} | |
var options = { id: this.id }; | |
return this.$destroy(options); | |
}; | |
return resource; | |
}; | |
return Resource; | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment