Ruby's method_missing method provides a way of implementing generic Proxy objects. Method_missing is called when an object receives a message that it does not have a method for. The method_missing method can forward the message on to another object and wrap additional behaviour around the forwarded call.
Here is an example proxy that forwards calls to an object in another process using TCP/IP.
require 'socket'
class Proxy
def initialize( host, port )
@host = host
@port = port
end
def type
@target.type
end
def method_missing( name, *args )
socket = TCPSocket.new( @host, @port )
begin
# Send request to server
Marshal.dump( name, socket )
Marshal.dump( args.length, socket )
args.each { |a| Marshal.dump( a, socket ) }
socket.flush
# Get reply from server
is_ok = Marshal.load( socket ) # will return a boolean
result = Marshal.load( socket )
if is_ok
# The server has returned the result of the remote method
return result
else
# The server has returned an exception
raise result
end
ensure
socket.close
end
end
end
For good measure, here is the server program:
require 'socket'
class Accumulator
def accumulate( *args )
args.inject { |total,a| total += a }
end
end
def dispatch_call( object, socket )
begin
method = Marshal.load( socket )
args = Array.new( Marshal.load( socket ) )
args.each_index { |i| args[i] = Marshal.load( socket ) }
result = object.__send__( method, *args )
Marshal.dump( true, socket )
Marshal.dump( result, socket )
rescue => ex
Marshal.dump( false, socket )
Marshal.dump( ex, socket )
ensure
socket.close
end
end
acc = Accumulator.new
server = TCPServer.new( '127.0.0.1', 54321 )
puts "waiting for connections on host 127.0.0.1, port 54321"
loop do
dispatch_call( acc, server.accept )
end
Here's a client program that uses the proxy to call a remote object:
proxy = Proxy.new( '127.0.0.1', 54321 ) puts proxy.accumulate( 1,2,3,4,5,6,7,8,9,10 )
Running the server and client programs results in this output from the client:
55
Distributed object invocation in 57 lines of code!
-- NatPryce