Skip to content

Instantly share code, notes, and snippets.

@nadako
Created April 16, 2015 19:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nadako/69d833f7e9f031fa692c to your computer and use it in GitHub Desktop.
Save nadako/69d833f7e9f031fa692c to your computer and use it in GitHub Desktop.
Simple macro-based dependency injection system based on factory functions
import haxe.macro.Expr;
import haxe.Constraints.Function;
#if macro
import haxe.macro.Context;
import haxe.macro.Type;
using haxe.macro.Tools;
#end
class DI {
var instances:Map<String,Dynamic>;
var factories:Map<String,Void->Dynamic>;
public function new() {
instances = new Map();
factories = new Map();
}
public macro function add(self:Expr, factory:ExprOf<Function>):Expr {
switch (Context.typeof(factory).follow()) {
case TFun(args, ret):
var id = ret.toString();
var callArgs = [];
for (arg in args) {
var id = arg.t.toString();
callArgs.push(macro (@:privateAccess $self._resolve)($v{id}));
}
return macro (@:privateAccess $self._add)($v{id}, function() return ($factory)($a{callArgs}));
default:
throw new Error("Factory should be a funtion", factory.pos);
}
}
public macro function run(self:Expr, fn:ExprOf<Function>):Expr {
switch (Context.typeof(fn).follow()) {
case TFun(args, ret):
var callArgs = [];
for (arg in args) {
var id = arg.t.toString();
callArgs.push(macro (@:privateAccess $self._resolve)($v{id}));
}
return macro $fn($a{callArgs});
default:
throw new Error("fn should be a funtion", fn.pos);
}
}
inline function _add(id:String, factory:Void->Dynamic):Void {
if (factories.exists(id))
throw 'Duplicate factory declaration for $id';
factories[id] = factory;
}
inline function _resolve(id:String):Dynamic {
var inst = instances[id];
if (inst == null) {
var factory = factories[id];
if (factory == null)
throw 'No factory defined for type $id';
inst = instances[id] = factory();
}
return inst;
}
}
class Config {
public var appName:String;
public function new(appName) this.appName = appName;
}
class App {
var cfg:Config;
public function new(cfg:Config) {
this.cfg = cfg;
}
public function startup() {
trace("Hello from " + cfg.appName);
}
}
class Main {
static function main () {
var di = new DI();
di.add(Config.new.bind("MyApp"));
di.add(App.new);
di.run(function(cfg:Config, app:App) {
app.startup();
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment