## # Ext::Direct::RemotingProvider # @mixin # This is a mixin containing just one action, "rpc", which you can include into any Merb controller to turn it into an # Ext.Direct router # @usage include Ext::Direct::RemotingProvider # @author Chris Scott # @TODO before_actions and forms # module Merb::Ext::Direct module RemotingProvider # standard ruby method called when some class does: # include Ext::Direct::RemotingProvider def self.included(base) # must explicity specify controller-actions to make callable. security-wise, this is a *good* thing. # just one action is added. base.show_action(:rpc) end ## # rpc # remote procedure call handler for Ext.direct requests. # def rpc if !request.ajax? && !params["extAction"] return "Ext::Direct::RemotingProvider#rpc -- This method is an ajax-handler only" end is_form = false is_upload = false if params["extAction"] && params["extMethod"] # create fake response here. is_form = true is_upload = params.delete("extUpload") == 'true' request = { "xcontroller" => params.delete("extAction"), "xaction" => params.delete("extMethod"), "type" => "rpc", "id" => params.delete("id"), "tid" => params.delete("extTID"), "format" => params.delete("format"), "data" => params } params.delete('controller') params.delete('action') res = "" Merb.logger.info('OUTPUT: ' + res) res elsif (params[:inflated_object]) # multiple requests found. I'm not sure how this "inflated_object" mechanism works. This is Merb magic? responses = [] params[:inflated_object].each do |req| responses << handle_request(req) end # controllers always return a string, so each response has already been json-encoded. # since we're dealing with multiple requests here, we have to manually string-concat-wrap the retured # string-of-json. # return "[" + responses.join(',') + "]" else return handle_request(params) end end ## # protected # ## ## # handle_request # # @throws XException when anything goes wrong to gracefully handle on client. # @see Merb::Controller#part method, google: "merb parts". A Merb "part" is a kind of sub-controller # with its onwn view capabilites but cannot be directly routed-to. Using part() method here is similar # to using Ext.getCmp(id), eg: part(req.controller => req.action) ~ Ext.getCmp('thing').doSomething(req); # However, unlike Ext.getCmp(), part(controller => action) returns a string to be added to the render stread # and not a component instance, just as all merb render actions. # Finally, we append the string "Part" at the end of the controller name in order to prevent having # to suffix our javascript names accordingly. "Part" here is necessarily needed in order to prevent # namespace clash with related parent-controller or model. In Merb, given a controller named Products, # we'll have a model named Product along with a part named ProductPart, ProductViewPart, # ProductGridPart, etc # def handle_request(req) req = XRequest.new(req) begin # here be your Ext.direct.Transaction as a ruby Hash. # turn it into an offical ruby XRequest instance. # now constantize the "controller" and send "action" to it. return part(Extlib::Inflection.constantize(req.controller) => req.action, :xrequest => req) rescue XException => e # caught an XException...return an Ext.Direct-friendly XExceptionResponse return XExceptionResponse.new(req, e).to_json rescue StandardError => e # might be an invalid controller and/or action if we got here. # we can be more specific here and trap on NameError if we wish, intead of base StandardError return XExceptionResponse.new(req, e).to_json end end end end