Proxy
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
{
var method = registry.get(methodToCall);
if (method == null) throw "Error, method is not defined";
return method();
}
}
then you define once in your code
class MyInterfaceDynamic extends haxe.remoting.Proxy<MyInterface>, implements MyInterface;
And now you can do things like that:
var res = new Resolver();
// define your functions
res.addFunction("getString", function () return "a new String");
// create an Implemention whenever you want
var dynamicImpl = new MyInterfaceDynamic(res);
// call your dynamic interface functions
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;
var conn:AsyncConnection;
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 is delegated to the function secureCall, which checks authentication on the server side
proxy.getString("tim", function (r) trace(r)); // hello tim
The ServerSide
class ServerGateway implements IServerApi
{
var authenticated:Bool;
public function new ()
{
authenticated = false;
}
public function getString (name:String):String
{
checkAuthentication();
return "hello " + name;
}
function checkAuthentication ()
{
if (!authenticated) {
throw new Error "Error you're not authorized";
} else {
authenticated = false; // reset Authentication
}
}
public function secureCall (userName:String, passWord:String, func:String, args:Array<Dynamic>):Dynamic
{
if (userName == "tim" && passWord == "pass") {
authenticated = true;
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