Skip to content

Instantly share code, notes, and snippets.

@egonelbre
Last active August 29, 2015 14:21
Show Gist options
  • Save egonelbre/47e0eecb796ba47e44d4 to your computer and use it in GitHub Desktop.
Save egonelbre/47e0eecb796ba47e44d4 to your computer and use it in GitHub Desktop.
injection based
DCI = {};
if(typeof window !== 'undefined'){ window.DCI = DCI; }
if(typeof global !== 'undefined'){ global.DCI = DCI; }
(function(DCI){
DCI.Context = function(init, roles){
var sfn = "";
sfn += "\tvar $C = {};\n";
sfn += "\tvar " + Object.keys(roles).join(", ") + ";\n";
var fixMethod = function(fn){
fn = fn.toString();
for(var roleName in roles){
var role = roles[roleName];
for(var fnName in role){
// replaces Role.method(arg1,arg2,arg3) with
// with DCI.bindings(Role).get($C).Role.method.call(Role, arg1, arg2, arg3);
var rx = new RegExp( "(?!\\.)" + roleName + "\\s*\\.\\s*" + fnName + "\\s*\\(", "g");
fn = fn.replace(rx, "DCI.bindings("+ roleName + ").get($C)." + roleName + "." + fnName + ".call("+ roleName + ",");
}
var rx = new RegExp("(?!\\.)(" + roleName + "\\s*=\\s*[^;]+;)", "g");
var binding = "" +
"DCI.unbind(" + roleName + ", $C, '" + roleName + "');" +
"$1" +
"DCI.bind(" + roleName + ", $C, '" + roleName + "', $C" + roleName + ");";
fn = fn.replace(rx, binding);
}
// hack-fix Role$method.call(Arg,)
fn = fn.replace(/,\)/g, ")");
return fn;
};
for(var roleName in roles){
var decl = "{\n";
if(!roles.hasOwnProperty(roleName)) continue;
var roleDecl = roles[roleName];
var fns = [];
for(var fnName in roleDecl){
if(!roleDecl.hasOwnProperty(fnName)) continue;
fns.push("\t" + fnName + ":" + fixMethod(roleDecl[fnName]));
}
sfn += "\tvar $C" + roleName + " = {\n\t" + fns.join(",\n\t") + "\t\n\t};\n";
}
sfn += " return (" + fixMethod(init) + ").apply(null, arguments);";
if(true){
console.info("GENERATED", "\n" + sfn);
}
return (new Function(sfn));
}
DCI.bindings = function(player){
if(typeof player.___ === "undefined"){
player['___'] = new WeakMap();
}
return player['___'];
}
DCI.bind = function(player, context, roleName, roleDecl){
if(player == null){ return; }
if(typeof player !== 'object'){
if(Object.keys(roleDecl).length != 0){
throw new Error('Only objects may have role methods.');
}
return;
}
var bindings = DCI.bindings(player);
//TODO: check for naming conflicts
var roles = bindings.get(context) || {};
roles[roleName] = roleDecl;
bindings.set(context, roles);
};
DCI.unbind = function(player, context, roleName){
if(player == null){ return; }
if(typeof player !== 'object'){ return; }
var bindings = DCI.bindings(player);
if(!bindings.has(context)){
return;
}
var roles = bindings.get(context);
delete(roles[roleName]);
bindings.set(context, roles);
};
})(DCI);
<script src="dci.js"></script>
<script src="transfer.js"></script>
function Account(code, balance) {
return {
get code(){ return code; },
get balance(){ return balance; },
increase: function(by){ balance += by; },
decrease: function(by){ balance -= by; }
};
}
function Bank(){
var accounts = {};
return {
openAccount: function(code, initialBalance){
accounts[code] = Account(code, initialBalance);
},
accountByCode: function(code){
return accounts[code];
}
};
}
Transfer = DCI.Context(
function(bank, from, to, amount) {
Amount = amount;
Source = bank.accountByCode(from);
Destination = bank.accountByCode(to);
Source.give();
}, {
Amount: {},
Source: {
give: function() {
if(Source.balance < Amount){
throw new Error("Insufficient funds.");
}
Source.decrease(Amount);
Destination.receive();
}
},
Destination: {
receive: function() {
Destination.increase(Amount);
}
}
});
var bank = Bank();
bank.openAccount("alice", 132);
bank.openAccount("bob", 23);
console.log("Before:");
console.log("alice ", bank.accountByCode("alice").balance);
console.log("bob ", bank.accountByCode("bob").balance);
Transfer(bank, "alice", "bob", 100);
console.log("After:");
console.log("alice", bank.accountByCode("alice").balance);
console.log("bob", bank.accountByCode("bob").balance);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment