login

ProxyPattern (Ruby)

HomePage | RecentChanges | Preferences | Wikis | RubyGarden | Feed-icon-16x16

"Provide a surrogate or placeholder for another object to control access to it." [1][2]

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


Also see ExampleDesignPatternsInRuby

HomePage | RecentChanges | Preferences | Wikis | RubyGarden
Edit text of this page | View other revisions
Rev 12, Last edited at December 04, 2006 15:54 pm by anonymous / 129.170.30.207 (diff)
Approved by pate at December 04, 2006 14:54 pm
Find: