Skip to content

Instantly share code, notes, and snippets.

@ahoward
Last active December 27, 2015 11:49
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 ahoward/7320900 to your computer and use it in GitHub Desktop.
Save ahoward/7320900 to your computer and use it in GitHub Desktop.
RAILS_ROOT/lib/rpc.rb
if(!window.RPC){
RPC = function(url){
this.url = url || '/rpc';
};
RPC.prototype.call = function(){
var rpc = this;
var args = Array.prototype.slice.call(arguments);
var ajax;
var method;
if(args.length == 1 && typeof(args[0]) == 'object'){
ajax = args.shift();
} else {
ajax = {};
for(var i = 0; i < args.length; i++){
var arg = args[i];
switch (typeof(arg)) {
case 'object':
ajax['data'] = ajax['data'] || arg;
break;
case 'function':
ajax['success'] = ajax['success'] || arg;
break;
case 'string':
method = method || arg;
break;
};
}
};
ajax['url'] = ajax['url'] || rpc['url'] || (window.location.pathname + '/rpc');
method = method || ajax['method'];
delete ajax['method'];
ajax['data'] = ajax['data'] || {};
ajax['data']['method'] = method;
ajax['async'] = true;
ajax['type'] = 'GET';
ajax['dataType'] = 'json';
ajax['cache'] = false;
ajax['contentType'] = 'application/json; charset = utf-8';
var result = ajax;
if(!ajax['success']){
ajax['async'] = false;
ajax['success'] = function(response){ result = response };
};
jQuery.ajax(ajax);
return(result);
};
};
module RPC
Actions =
Hash.new{|hash, controller| hash[controller] = Hash.new}
module ClassMethods
def rpc(method = nil, &block)
case
when method.nil? && block.nil?
Actions
else
controller = self
method = method.to_s
Actions[controller][method] = block
end
end
end
module InstanceMethods
def rpc
method = params.delete(:method) || params.delete(:call)
if method
controllers = self.class.ancestors.select{|controller| controller <= ::ApplicationController}
controllers.each do |controller|
action = Actions[controller][method]
if action
result = action.call(params)
render(:json => result.to_json)
return
end
end
render(:nothing => true, :status => :not_found, :layout => false)
else
render(:json => RPC.index)
end
end
end
def RPC.index
Hash.new.tap do |methods|
Actions.each do |controller, actions|
controller = controller.name.underscore
methods[controller] = []
actions.each do |method, block|
methods[controller].push(method)
end
end
end
end
def RPC.included(other)
super
ensure
other.send(:extend, ClassMethods)
other.send(:include, InstanceMethods)
end
end
=begin
ref: https://gist.github.com/ahoward/5752290
web apps nearly always end up needing a plethora of little javascript helper methods: you know, auto-complete forms, populating defaults, validations, etc. these aren't API calls properly, just little tidbits of functionality needed to make the views work.
there is always a question of which controller to hang these methods off of. do you make a global helper controller for all this stuff? do you hang them off the controller in question? how to do share the backend of javascript helper methods across controllers?
step one
--------
include the RPC module in your ApplicationController
````ruby
class ApplicationController < ::ActionController::Base
include RPC
end
````
step two
--------
in controllers, declare rpc helper methods
````ruby
class PostsController < ApplicationController
rpc(:defaults) do |params|
post = Post.new(params)
post.set_defaults!
post.as_json
end
end
````
step three
----------
setup js to be able to call rpc methods *relative* to the current controller
````javascript
// in file: app/views/layouts/application.html.erb
//
// notice the *relative* url rpc endpoint
//
// <script>
jQuery(function(){
var rpc = function(){
// parse args
//
var args = Array.prototype.slice.call(arguments);
var ajax;
var method;
if(args.length == 1 && typeof(args[0]) == 'object'){
ajax = args.shift();
} else {
ajax = {};
for(var i = 0; i < args.length; i++){
var arg = args[i];
switch (typeof(arg)) {
case 'object':
ajax['data'] = arg;
break;
case 'function':
ajax['success'] = arg;
break;
case 'string':
method = arg;
break;
};
}
};
// normalize ajax options
//
ajax['url'] = ajax['url'] || rpc['url'];
method = method || ajax['method'];
delete ajax['method'];
ajax['data'] = ajax['data'] || {};
ajax['data']['method'] = method;
ajax['async'] = true;
ajax['type'] = 'GET';
ajax['dataType'] = 'json';
ajax['cache'] = false;
ajax['contentType'] = 'application/json; charset = utf-8';
var result = ajax;
// ajax request - using callback or returning the result sync style
//
if(!ajax['success']){
ajax['async'] = false;
ajax['success'] = function(response){ result = response };
};
jQuery.ajax(ajax);
return(result);
};
rpc['url'] = <%= raw url_for(:action => :rpc).to_json %>;
window.rpc = rpc;
});
// </script>
````
step four
---------
enable the rpc endpoints in config/routes.rb
````ruby
resources :posts do
collection do
get 'rpc'
end
end
````
step five
---------
now you can use relative js rpc helpers at will in your views
````ruby
var title = $('#title');
var slug = $('#slug');
title.change(function(){
var value = title.val();
var params = {'title' : value};
var success = function(defaults){
slug.val(defaults['slug']);
};
rpc('defaults', params, success);
});
````
this organization keeps your code clean by avoiding a proliferation of controllers and prevents the need to litter clean resourceful controllers with loads of fugly js helper actions
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment