Skip to content

Instantly share code, notes, and snippets.

@nakamura-to
Last active December 20, 2015 18:09
Show Gist options
  • Save nakamura-to/6173630 to your computer and use it in GitHub Desktop.
Save nakamura-to/6173630 to your computer and use it in GitHub Desktop.
ECMA Script 6のgeneratorを使ってIoC (Inversion of Control)。node v0.11.4で--harmony-generatorsオプションをつけて実行。
/*
* IoC (Inversion o Control) Computation Expression
*/
function ioc(fn) {
assert(isGeneratorFunction(fn), 'The argument `fn` must be a GeneratorFunction.');
run.mflow_generatorFunction = fn;
return run;
function run(container) {
return function () {
var gen = fn.apply(this, arguments);
var ret = gen.next();
var component;
while (!ret.done) {
if (typeof ret.value === 'string') {
component = container[ret.value];
if (component != null) {
component = normalize(component, container);
ret = gen.next(component);
} else {
get.throw(new Error('The component is not found. key = "') + ret.value + '"');
}
} else {
get.throw(new Error('The yieldable value must be a string'));
}
}
return ret.value;
};
}
function normalize(obj, container) {
if (obj && obj.mflow_generatorFunction) obj = obj.mflow_generatorFunction;
if (isGeneratorFunction(obj)) {
obj = ioc(obj);
obj = obj(container);
} else if (isFunction(obj)) {
obj = obj();
}
return obj;
}
function assert(prerequisite, message) {
if (prerequisite) return;
throw new Error(message);
}
function isFunction(obj) {
return typeof obj === 'function';
}
function isGeneratorFunction(obj) {
return obj && obj.constructor && 'GeneratorFunction' === obj.constructor.name;
}
}
/*
* MAIN
*/
var flow1 = ioc(function * () {
var p = yield 'person';
return JSON.stringify(p);
});
var flow2 = ioc(function * (prefix, suffix) {
var m = yield 'message';
return prefix + m.content + suffix;
});
var container = {
// mutable state
count: 0,
// singleton
person: {
name: 'foo',
age: 20
},
// singleton
flow1: flow1,
// singleton
flow2: flow2,
// prototype
message : function () {
var count = container.count++;
return {
content: 'hello ' + count
};
},
};
var main= ioc(function * () {
var a = yield 'flow1';
var b = yield 'flow2';
var c = yield 'flow2';
console.log(a()); // {"name":"foo","age":20}
console.log(b('[', ']')); // [hello 0]
console.log(c('<', '>')); // <hello 1>
});
main(container)();
/*
* Test using mock
*/
var result = flow1({person: {name: 'test', age: 99}})();
console.log(result); // {"name":"test","age":99}
result = flow2({message: { content: 'dummy'}})('(', ')');
console.log(result); // (dummy)
@nakamura-to
Copy link
Author

いくつか改善案

yieldにコンポーネントの名前だけでなくパスを指定できるようにする

yield 'person.name';

コンポーネントが関数の場合にはそいつが属しているコンテナを引数で渡す

var container = {
  child: {
     fn: function (container, parent) {
        ...
     }
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment