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 {
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