🪡
VitalRouterのmruby拡張をVContainerと合わせて使う際の注意点
VitalRouter.MRuby
VitalRouterには動的にRubyのスクリプトを読み込んでVitalRouterで定義されたコマンドを操作できる拡張実装があります。
導入方法など説明はGitHubリポジトリのreadmeに詳しいです。
VContainer と使う上での注意
色々わかってる人向けに先に結論を書きますが、Rubyスクリプトを起点に送られてくるコマンドを[Routes]
を付けたクラスで受け取るためには、MRubyContext.Router
にはreadmeにあるような Router.Default
でなく、VContainer 経由で取得される Router
のインスタンスを渡す必要があります。
LifetimeScopeの実装内で IContainerBuilder.RegisterVitalRouter
を実行すると先に Router
クラスのインスタンスが登録されるので、そのあとに以下のように書いてもRubyスクリプト起点のコマンドは別Routerに送られるようなので MRubyScript.RunAsync
実行後もコマンドが飛んでこない現象に遭遇しました。
ダメな例
public class MyLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
// 以下のように送信先を設定した場合、RegisterVitalRouterの実装内部で
// Router のインスタンスが登録されるので、
builder.RegisterVitalRouter(routing =>
{
routing.Map<MyPresenter>();
});
var context = MRubyContext.Create();
// ここで設定される Router.Default とは別物になってしまう。
context.Router = Router.Default;
context.CommandPreset = new MyCommandPreset();
var rubySource = "cmd :speak, id: 'Bob', text: 'Hello'";
using MRubyScript script = context.CompileScript(rubySource);
script.RunAsync();
// そもそも LifetimeScope でやるなという話だが、とりあえず試したい場合はやってしまいがちでは
// (私のように)
}
}
VCointainerの流儀に倣って別 EntryPoint などから Router
を取得してrubyスクリプトを実行しましょう。
修正後
public class MyLifetimeScope : LifetimeScope
{
protected override void Configure(IContainerBuilder builder)
{
builder.RegisterVitalRouter(routing =>
{
routing.Map<MyPresenter>();
});
// 実行は MyEntryPoint へ回す
builder.RegisterEntryPoint<MyEntryPoint>();
}
}
public class MyEntryPoint : IAsyncStartable
{
[Inject] readonly Router _router;
public async Awaitable StartAsync(CancellationToken cancellation)
{
var context = MRubyContext.Create();
// [Inject]で取得されるRouterを使う
context.Router = _router;
context.CommandPreset = new MyCommandPreset();
var rubySource = "cmd :speak, id: 'Bob', text: 'Hello'";
using MRubyScript script = context.CompileScript(rubySource);
await script.RunAsync(cancellation);
}
}
// 一応残りの実装も
[MRubyObject]
public partial struct CharacterSpeakCommand : ICommand
{
public string Id;
public string Text;
}
[MRubyCommand("speak", typeof(CharacterSpeakCommand))]
partial class MyCommandPreset : MRubyCommandPreset { }
[Routes]
public partial class MyPresenter
{
public void On(CharacterSpeakCommand cmd)
{
Debug.Log($"{cmd.Id}: {cmd.Text}");
}
}
Discussion