Created
August 1, 2010 23:15
-
-
Save jejacks0n/503887 to your computer and use it in GitHub Desktop.
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
// Custom Rails routing/resource handling for ajax requests.. | |
(function() { | |
var methods = ['get', 'put', 'post', 'delete'], | |
actions = ['index', 'show', 'new', 'create', 'edit', 'update', 'destroy']; | |
function parseUrl(url) { | |
var urlParts = url.match(/^((http[s]?|ftp):\/\/)?(((.+)@)?([^:\/\?#\s]+)(:(\d+))?)?(\/?[^\?#\.]+)?(\.([^\?#]+))?(\?([^#]?))?(#(.*))?$/i) || []; | |
var pathParts = (urlParts[9]) ? urlParts[9].match(/(\/.*)\/+(\w+)$/i) || [] : []; | |
return {scheme: urlParts[2], credentials: urlParts[5], host: urlParts[6], port: urlParts[8], path: urlParts[9], action: pathParts[2] || '', format: urlParts[11], query: urlParts[13], hash: urlParts[15]}; | |
} | |
function observeAjax(original, url) { | |
var method = this.options.method.toLowerCase(), | |
urlParts = parseUrl(url), | |
path = urlParts.path, | |
action = urlParts.action, | |
handled = false; | |
for (var mapping in context.resources) { | |
var resource = context.resources[mapping], | |
actions = resource.__actions__, | |
f = false; | |
var matches = (path + '/').match(resource.__path__); | |
if (matches) { | |
var match1 = !matches[matches.length - 1], | |
match2 = !matches[matches.length - 2]; | |
switch (method) { | |
case 'get': | |
if (match2) action = ''; | |
switch (action) { | |
case '': f = actions['index']; break; | |
case 'new': f = actions['new']; break; | |
case 'edit': f = actions['edit']; break; | |
default: f = (match1) ? actions['show'] : actions['get'][action]; | |
} | |
break; | |
case 'put': f = (match1) ? actions['update'] : actions['put'][action]; break; | |
case 'post': f = (match2) ? actions['create'] : actions['post'][action]; break; | |
case 'delete': f = (match1) ? actions['destroy'] : actions['delete'][action]; break; | |
} | |
var called = false; | |
var proceed = function(options) { | |
called = true; | |
this.options = Object.extend(this.options, options || {}); | |
return original(url); | |
}.bind(this); // TODO: remove prototype dependancy | |
var result; | |
if (f) result = f(proceed, urlParts); | |
if (result !== false && !called) proceed(result); | |
handled = true; | |
} | |
} | |
if (!handled) return original(url); | |
} | |
function currentResource(path) { | |
if (this.resources[path]) return this.resources[path]; | |
var resource = this.resources[path] = { | |
__actions__: {}, __path__: new RegExp('^/' + path + '(/(\\w+))?/?(\\w+)?/?$'), | |
add: function(actionOrActions, callback, method) { | |
if (typeof(actionOrActions) == 'string' && callback) { | |
var action = actionOrActions.split(':'); | |
if (action.length > 1) this.__actions__[action[0]][action[1]] = callback; | |
else this.__actions__[action[0]] = callback; | |
} else if (typeof(actionOrActions) == 'object') { | |
var i; | |
for (i = 0; i < methods.length; i += 1) { this.__actions__[methods[i]] = actionOrActions[methods[i]] || this.__actions__[methods[i]] || {}; } | |
for (i = 0; i < actions.length; i += 1) { this.__actions__[actions[i]] = actionOrActions[actions[i]] || this.__actions__[actions[i]]; } | |
} else { | |
throw('To add a resource you must provide an action or method with a function, or an object of actions/methods'); | |
} | |
} | |
}; | |
var i; | |
for (i = 0; i < methods.length; i += 1) { | |
resource[methods[i]] = (function(method) { | |
return function(action, callback) { | |
resource.add(method + ':' + action, callback); | |
} | |
})(methods[i]); | |
} | |
for (i = 0; i < actions.length; i += 1) { | |
resource[actions[i]] = (function(action) { | |
return function(callback) { resource.add(action, callback) } | |
})(actions[i]); | |
} | |
return resource; | |
} | |
// TODO: add support for jQuery, mootools, YUI, Dojo, and ?? | |
Ajax.Request.prototype.request = Ajax.Request.prototype.request.wrap(observeAjax); | |
// TODO: let's have a way to accomplish adding nicely to a window.Rails variable | |
// methods to add/manage resources for Rails | |
var context = window.Rails = { | |
resources: {}, | |
resource: function(path, actions) { | |
var resource = currentResource.call(this, path); | |
resource.add(actions || {}); | |
return resource; | |
} | |
}; | |
})(); | |
// GET /posts(.:format) index | |
// posts POST /posts(.:format) create | |
// new_post GET /posts/new(.:format) new | |
// GET /posts/:id(.:format) show | |
// PUT /posts/:id(.:format) update | |
// post DELETE /posts/:id(.:format) destroy | |
// edit_post GET /posts/:id/edit(.:format) edit | |
//--------------------------------------------------------------------------- | |
// reorder_posts POST /posts/reorder(.:format) | |
// publish_post PUT /posts/:id/publish(.:format) | |
//deactivate_post_comment PUT /posts/:id/deactivate(.:format) | |
// activate_post_comment PUT /posts/:id/activate(.:format) | |
// approve_post_comment PUT /posts/:id/comment/approve(.:format) | |
// reject_post_comment PUT /posts/:id/comment/reject(.:format) | |
// | |
var RailsResourceTestClass = Class.create({ | |
initialize: function() { | |
Rails.resource('namespace1/namespace2/posts', this.resourcePosts); | |
// TODO: it could be more like "blogs/:blog_id/posts" | |
Rails.resource('blogs/(\\w+)/posts', this.resourcesBlogs); | |
console.debug('----------- TESTING BASIC RESOURCES'); | |
this.testResource('/posts'); | |
console.debug('----------- TESTING NAMESPACED RESOURCES'); | |
this.testResource('/namespace1/namespace2/posts'); | |
console.debug('----------- TESTING NESTED RESOURCES'); | |
this.testResource('/blogs/1/posts'); | |
this.testResource('/blogs/2/posts'); | |
console.debug('----------- TESTING TIMED CALLBACK / RESOURCE ADDITIONS'); | |
this.testResource('/comments'); | |
}, | |
testResource: function(path) { | |
// default resource actions | |
new Ajax.Request(path, {method: 'get'}); // index | |
new Ajax.Request(path, {method: 'post'}); // create | |
new Ajax.Request(path+'/new', {method: 'get'}); // new | |
new Ajax.Request(path+'/1', {method: 'get'}); // show | |
new Ajax.Request(path+'/1', {method: 'put'}); // update | |
new Ajax.Request(path+'/1', {method: 'delete'}); // destroy | |
new Ajax.Request(path+'/1/edit', {method: 'get'}); // edit | |
// resource members | |
new Ajax.Request(path+'/reorder', {method: 'post'}); | |
new Ajax.Request(path+'/1/publish', {method: 'put'}); | |
new Ajax.Request(path+'/1/unset', {method: 'put'}); | |
new Ajax.Request(path+'/1/comments', {method: 'get'}); | |
new Ajax.Request(path+'/1/unset', {method: 'get'}); | |
new Ajax.Request(path+'/1/comments', {method: 'delete'}); | |
// unrelated resource -- shouldn't be handled | |
new Ajax.Request(path+'/1/comment/approve', {method: 'put'}); | |
new Ajax.Request(path+'/1/comment/approve', {method: 'get'}); | |
new Ajax.Request(path+'/1/comment/approve', {method: 'post'}); | |
new Ajax.Request(path+'/1/comment/approve', {method: 'delete'}); | |
new Ajax.Request(path+'/1/comment/reject', {method: 'put'}); | |
}, | |
resourcePosts: { | |
index: function() { | |
console.debug('Namespace::PostsController#index'); | |
}, | |
show: function() { | |
console.debug('Namespace::PostsController#show'); | |
}, | |
'new': function() { | |
console.debug('Namespace::PostsController#new'); | |
}, | |
create: function() { | |
console.debug('Namespace::PostsController#create'); | |
}, | |
edit: function() { | |
console.debug('Namespace::PostsController#edit'); | |
}, | |
update: function() { | |
console.debug('Namespace::PostsController#update'); | |
}, | |
destroy: function() { | |
console.debug('Namespace::PostsController#destroy'); | |
}, | |
post: { | |
reorder: function() { | |
console.debug('Namespace::PostsController#reorder:POST'); | |
} | |
}, | |
'delete': { | |
comments: function() { | |
console.debug('Namespace::PostsController#comments:DELETE'); | |
} | |
}, | |
get: { | |
comments: function() { | |
console.debug('Namespace::PostsController#comments:GET'); | |
} | |
}, | |
put: { | |
publish: function() { | |
console.debug('Namespace::PostsController#publish:PUT'); | |
return { | |
onSuccess: function(response) { | |
console.debug('Namespace::PostsController#publish:success', response); | |
}, | |
onFailure: function(response) { | |
console.debug('Namespace::PostsController#publish:failure', response); | |
} | |
} | |
} | |
} | |
}, | |
resourcesBlogs: { | |
index: function() { | |
console.debug('Blogs::PostsController#index'); | |
} | |
} | |
}); | |
Rails.resource('posts', { | |
index: function() { | |
console.debug('PostsController#index'); | |
}, | |
show: function() { | |
console.debug('PostsController#show'); | |
}, | |
'new': function(proceed) { | |
console.debug('PostsController#new'); | |
proceed(); | |
}, | |
create: function() { | |
console.debug('PostsController#create'); | |
}, | |
edit: function() { | |
console.debug('PostsController#edit'); | |
}, | |
update: function() { | |
console.debug('PostsController#update'); | |
}, | |
destroy: function() { | |
console.debug('PostsController#destroy'); | |
}, | |
post: { | |
reorder: function() { | |
console.debug('PostsController#reorder:POST'); | |
} | |
}, | |
'delete': { | |
comments: function() { | |
console.debug('PostsController#comments:DELETE'); | |
} | |
}, | |
get: { | |
comments: function() { | |
console.debug('PostsController#comments:GET'); | |
} | |
}, | |
put: { | |
publish: function() { | |
console.debug('PostsController#publish:PUT'); | |
return { | |
onSuccess: function(response) { | |
console.debug('PostsController#publish:PUT:success', response); | |
}, | |
onFailure: function(response) { | |
console.debug('PostsController#publish:PUT:failure', response); | |
} | |
} | |
} | |
} | |
}); | |
Rails.resource('comments').add({ | |
index: function(proceed) { | |
setTimeout(function() { | |
console.debug('CommentsController#index(in a setTimeout)'); | |
proceed(); | |
}, 10000); | |
return false; | |
}, | |
destroy: function(proceed) { | |
console.debug('CommentsController#destroy'); | |
proceed(); | |
return true; | |
}, | |
edit: function() { | |
console.debug('CommentsController#edit'); | |
}, | |
update: function() { | |
console.debug('CommentsController#update'); | |
}, | |
create: function() { | |
console.debug('CommentsController#create'); | |
} | |
}); | |
var comments = Rails.resource('comments', { | |
edit: function() { | |
console.debug('CommentsController#edit'); | |
}, | |
create: function() { | |
console.debug('CommentsController#create:override'); | |
} | |
}); | |
comments.add('update', function() { | |
console.debug('CommentsController#update:override'); | |
}); | |
comments.add('put:reject', function() { | |
console.debug('CommentsController#reject'); | |
}); | |
comments.edit(function() { | |
console.debug('CommentsController#edit:override'); | |
}); | |
comments.put('approve', function() { | |
console.debug('CommentsController#approve'); | |
}); | |
console.debug(Rails.resources); | |
new RailsResourceTestClass(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment