lib/netzke/core/railz/controller_extensions.rb
module Netzke
module Railz
# Before each request, Netzke::Base.controller and Netzke::Base.session are set, to be accessible from components.
module ControllerExtensions
class DirectRequest
def initialize(params)
@params = params
end
def cmp_path
@params[:path]
end
def endpoint
@params[:endpoint].underscore
end
# arguments for endpoint call
def args
res = remoting_args.has_key?("args") ? remoting_args["args"] : remoting_args
res.is_a?(Array) ? res : [res].compact # need to wrap into array to normalize
end
def client_configs
remoting_args["configs"] || []
end
def tid
@params[:tid]
end
private
# raw arguments from the client
def remoting_args
@_remoting_args ||= HashWithIndifferentAccess.new(data: @params[:data])[:data]
end
end
extend ActiveSupport::Concern
included do
send(:before_action, :set_controller_and_session)
end
module ClassMethods
# inform AbstractController::Base that methods direct, ext, and dispatcher are actually actions
def action_methods
super.merge(%w[ext direct dispatcher].to_set)
end
end
# Handles Ext.Direct RPC calls
def direct
if params['_json'] # this is a batched request
response = []
params['_json'].each do |batch|
direct_request = DirectRequest.new(batch)
response << direct_response(direct_request, invoke_endpoint(direct_request))
end
else # this is a single request
direct_request = DirectRequest.new(params)
response = direct_response(direct_request, invoke_endpoint(direct_request))
end
render plain: response.to_json, layout: false
end
# On-the-fly generation of public/netzke/ext.[js|css]
def ext
respond_to do |format|
format.js {
render js: Netzke::Core::DynamicAssets.ext_js(form_authenticity_token)
}
format.css {
render plain: Netzke::Core::DynamicAssets.ext_css, content_type: 'text/css'
}
end
end
# Old-way action used at multi-part form submission (endpointUrl)
def dispatcher
endpoint_dispatch(params[:address])
end
protected
# Receives DirectRequest and result of invoke_endpoint, returns hash understood by client-side's Direct function
def direct_response(request, endpoint_response)
component_name, *sub_components = request.cmp_path.split('__')
# We render text/plain, so that the browser never modifies our response
response.headers["Content-Type"] = "text/plain; charset=utf-8"
{ type: :rpc,
tid: request.tid,
action: component_name,
method: request.endpoint,
result: endpoint_response.netzke_jsonify
}
end
# Receives DirectRequest, returns an array/hash of methods for the client side (consumed by netzkeBulkExecute)
def invoke_endpoint(request)
component_name, *sub_components = request.cmp_path.split('__')
if cmp_config = config_in_session(component_name)
cmp_config[:client_config] = request.client_configs.shift || {}
component_instance = Netzke::Base.instance_by_config(cmp_config)
component_instance.invoke_endpoint((sub_components + [request.endpoint]).join("__"), request.args, request.client_configs)
else
{ netzke_on_session_expired: [] }
end
end
# The dispatcher for the old-style requests (used for multi-part form submission). The URL contains the name of the component,
# as well as the method of this component to be called, according to the double underscore notation.
# E.g.: some_grid__post_grid_data.
def endpoint_dispatch(endpoint_path)
component_name, *sub_components = endpoint_path.split('__')
if cmp_config = config_in_session(component_name)
cmp_config[:client_config] = ActiveSupport::JSON.decode(params[:configs]).shift || {}
component_instance = Netzke::Base.instance_by_config(cmp_config)
render plain: component_instance.invoke_endpoint(sub_components.join("__"), [params]).netzke_jsonify.to_json, layout: false
else
render plain: { netzke_on_session_expired: [] }.to_json, layout: false
end
end
def set_controller_and_session
Netzke::Base.controller = self
Netzke::Base.session = session
end
def config_in_session(component_name)
components_in_session = (session[:netzke_components] || {}).symbolize_keys
components_in_session[component_name.to_sym].try(:symbolize_keys)
end
end
end
end