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, it's 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(); } }
Add this line:
class MyInterfaceDynamic extends haxe.remoting.Proxy<MyInterface>, implements MyInterface;
declare your Interface-implementations on the fly
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"
You're now able to create an Interface Implementation on the fly and you get also auto-completion in your IDE.
I explored this, while creating a Remoting connection that needs authentication.
This is really great stuff, you can implement an AsyncConnection, and handle the function calls in your favoured 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"); // you can use it in the same way as 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 "Error you are not authorized"; } else { authenticated = false; // reset Authentication } } public function secureCall (userName:String, passWord:String, func:String, args:Array<Dynamic>):Dynamic { if (userName == "user" && 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