Proxy

You are viewing an old version of this entry, click here to see latest version.

Just want to share a nice trick, maybe you know it already.
It's a misuse of the Proxy Class, but very helpful.
Especially for creating Stubs and Mocks in TDD.

You have an Interface

interface MyInterface
{
   public function getString ():String;
}

Then you need a Resolver like this, important to implement Connection:

package;
import haxe.remoting.Connection;

class Resolver implements Connection
{
   var methodToCall:String;
     var registry:Hash<Void -> Dynamic>;
     public function new ()
   {
       registry = new Hash();
   }
     public function addFunction (method:String, func:Void -> Dynamic) {
       registry.set(method, func);
   }
     public function resolve( method: String ) : Connection {
       methodToCall = method;
       return this;
         }
   public function call( params : Array<Dynamic> ) : Dynamic {
       return registry.get(methodToCall)();
   }
}

then you define once in your code

class MyInterfaceDynamic extends haxe.remoting.Proxy<MyInterface>, implements I

And now you can do things like that:

var res = new Resolver();
res.addFunction("getString", function () return "a new String"); // define your functions
var dynamicImpl = new MyInterfaceDynamic(res); // create an Implemention whenever you want
trace(dynamicImpl.getString()) // returns "a new String"

Now you can create an Interface Implementation on the fly and you have auto-completion, too.

I just explored this, while trying to create a Remoting connection that needs authentication.
This is really great, you can implement an AsyncConnection, and handle the function calls in your
appropriate way.

My Remote Interface:

interface IServerApi:
{
   public function getString (name:String):String;
}

My Async Connection that delegates every call to the secureCall (not really secure ;) ) to check authentication:

package;
import haxe.remoting.AsyncConnection;

class SecureAsyncConnection implements AsyncConnection
{
     var username:String;
   var password:String;
   var methodToCall:String;
     private var conn:AsyncConnection;
   public var secureCall:String -> String -> String -> Array<Dynamic> -> Dynamic;
     public function new (conn:AsyncConnection) {
       this.conn = conn;
   }
     public function resolve( method : String ) : AsyncConnection {
       methodToCall = method;
       return this;
   }
   public function call( params : Array<Dynamic>, ?result : Dynamic -> Void ) : Void
   {          params = [username, password, methodToCall, params];
       return conn.secureCall.call(params, result);
   }
   public function setErrorHandler( error : Dynamic -> Void ) : Void {
       conn.setErrorHandler(error);
   }
     public function setCredentials (username:String, password:String) {
       this.username = username;
       this.password = password;
   }

}

and usage:

declare Proxy:

class MyProxy extends haxe.remoting.AsyncProxy <IServerApi> {}

use it:

var conn = HttpAsyncConnection.urlConnect("http://anyhost/gateway.php");
conn.setErrorHandler(function (e) trace(e));

var sec = new SecureAsyncConnection(conn.api);
var proxy = new MyProxy(sec);

sec.setCredentials("user", "pass");
      // use it the same way like before
// but every call calls the function secureCall, which checks authentication on the server side
// and delegates to the desired function

proxy.getString("tim", function (r) trace(r));

The ServerSide

class ServerGateway implements IServerApi
{

   public function new () {}
     public function getString (name:String):String {
       if (NotAuthenticated()) throw "Error you're not authenticated"
       return "hallo " + name;
   }
     public function secureCall (userName:String, passWord:String, func:String, args:Array<Dynamic>):Dynamic {
       if (userName == "tim" && passWord == "pass") {
           return Reflect.callMethod(this, func, args);
       } else {
           throw "Error you're not authorized";
       }
   }

   public static function main ():Void
   {
             var ctx = new haxe.remoting.Context();
       ctx.addObject("api", new ServerGateway());
             if( haxe.remoting.HttpConnection.handleRequest(ctx) )
           return;

       trace("this is a remoting server");
   }
}

That's it, maybe someone can create useful stuff with this.

Edited on 24.08.2009
fixed a few naming mistakes

version #6868, modified 2009-08-24 14:26:44 by hhoelzer