module PipeRpc class Hub class Responder def initialize(hub) @hub = hub @servers = {} end def add_server(servers) servers.each{ |name, server| @servers[name.to_sym] = server } end def rmv_server(server_name) @servers.delete(server_name.to_sym) end def on_request(&on_request) @on_request = on_request end def handle_notification(notification) handle_request(notification) {} end def handle_request(request, &on_result) id, server, method, args = request.id, @servers[request.server], request.method, request.arguments unless server @hub.send_error(id: id, code: -32604) return end async = proc do |result| on_result.call ResultResponse.new(id: id, result: result) end @on_request.call(server, method, args) if @on_request # methods returning the async block as result state that they are asynchronous! method_lineno = __LINE__+1 result = server.__send__(method, *args, &async) # directly return result for synchronous calls async.call(result) unless result == async rescue NoMethodError => e backtrace = e.backtrace.to_a code = itself_caused_no_method_error?(backtrace, method_lineno) ? -32601 : -32603 @hub.send_error(id: id, code: code, data: { message: e.message, method: e.name, args: e.args, backtrace: backtrace }) rescue ArgumentError => e backtrace = e.backtrace.to_a code = itself_caused_argument_error?(backtrace, method_lineno) ? -32602 : -32603 @hub.send_error(id: id, code: code, data: { message: e.message, backtrace: backtrace }) rescue InternalError => e # raised on the other side backtrace = e.backtrace.to_a @hub.send_error(id: id, code: -32605, data: { message: e.message, backtrace: backtrace }) rescue => e backtrace = e.backtrace.to_a @hub.send_error(id: id, code: -32603, data: { message: e.message, backtrace: backtrace }) end private def itself_caused_no_method_error?(backtrace, ref_lineno) if Object.const_defined?(:MRUBY_VERSION) !backtrace[0] else itself_caused_error?(backtrace[0], ref_lineno) end end def itself_caused_argument_error?(backtrace, ref_lineno) if Object.const_defined?(:MRUBY_VERSION) !backtrace[0] else itself_caused_error?(backtrace[1], ref_lineno) end end def itself_caused_error?(backtrace_loc, ref_lineno) file, lineno, = backtrace_loc.split(':') (file == __FILE__) and (lineno.to_i == ref_lineno) end end end end