### Start Local Development Server Source: https://github.com/hadashia/vitalrouter/blob/main/website/README.md Starts a local development server for live previewing changes. Changes are reflected without restarting. ```bash yarn start ``` -------------------------------- ### Install Dependencies Source: https://github.com/hadashia/vitalrouter/blob/main/website/README.md Installs project dependencies using Yarn. ```bash yarn ``` -------------------------------- ### Install VitalRouter.MRuby v1 Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Add this URL to the Unity Package Manager to install VitalRouter.MRuby v1. Ensure VitalRouter v1 series is also installed. ```bash https://github.com/hadashiA/VitalRouter.git?path=/src/VitalRouter.Unity/Assets/VitalRouter.MRuby#1.6.4 ``` -------------------------------- ### Ruby Command Execution Example Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Example of how a Ruby script can publish a command. The 'cmd' method in Ruby delegates processing to C# and resumes execution after C# async methods complete. ```ruby cmd :speak, id: 'Yogoroza', text: 'What a strange cat.' ``` -------------------------------- ### Define FooCommand using Class Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/icommand.md Example of defining a command using a C# class with init-only properties, implementing the ICommand interface. Useful for commands with more complex state or identity. ```cs public class FooCommand : ICommand { public Guid Id { get; init; } public Vector3 Destination { get; init; } } ``` -------------------------------- ### Install VitalRouter Unity Extension Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/installation.md This is an optional Unity extension for VitalRouter. Ensure UniTask is installed for optimized GC-free code. ```bash https://github.com/hadashiA/VitalRouter.git?path=/src/VitalRouter.Unity/Assets/VitalRouter#2.0.0 ``` -------------------------------- ### MRubyConstructor Example Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Shows how to use the MRubyConstructor attribute to specify which constructor should be used when creating an object from mruby. This allows for custom object initialization logic. ```csharp [MRubyObject] partial class Foo { public int X { ge; } [MRubyConstructor] public Foo(int x) { X = x; } } ``` -------------------------------- ### Define FooCommand using Struct Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/icommand.md Example of defining a command using a standard C# struct with init-only properties, implementing the ICommand interface. Suitable for data-oriented types. ```cs public readonly struct FooCommand : ICommand { public int X { get; init; } public string Y { get; init; } } ``` -------------------------------- ### Define Serializable CharacterSpawnCommand Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/icommand.md Example of a command struct implementing ICommand and decorated with attributes for serialization using Unity's scene serialization, MessagePack, and VYaml. Demonstrates defining data-oriented types with various serialization options. ```cs [Serializable] [MessagePackObject] [YamlObject] public readonly struct CharacterSpawnCommand : ICommand { public long Id { get; init; } public CharacterType Type { get; init; } public Vector3 Position { get; init; } } ``` -------------------------------- ### Migration Example: v1 vs v2 CommandPreset Definition Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Compares the definition of custom commands in VitalRouter v1 and v2, showing the transition from MRubyCommand attributes to MRubyState.DefineVitalRouter. ```csharp // v1 (old) [MRubyCommand("move", typeof(CharacterMoveCommand))] // < Your custom command name and type list here [MRubyCommand("speak", typeof(CharacterSpeakCommand))] partial class MyCommandPreset : MRubyCommandPreset { } var context = MRubyContext.Create(); context.Router = Router.Default context.CommandPreset = new MyCommandPreset()); using var script = context.CompileScript(rubySource); await script.RunAsync(); ``` ```csharp // v2 var mrb = MRubyState.Create(); mrb.DefineVitalRouter(x => { x.AddCommand("move"); x.AddCommand("speak"); }); using var compilation = compiler.Compile(rubySource); await mrb.ExecuteAsync(Router.Default, compilation.ToIrep()); ``` -------------------------------- ### Generated RBS Signature Example Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md An example of the generated RBS file content, showing how command signatures and their parameters are defined for type checking and code completion in Ruby scripts. ```rbs # # This file is generated by VitalRouter.MRuby. Do not edit manually. ``` -------------------------------- ### Implementing an Error Handling Interceptor Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/error-handling.md Provides an example of an ICommandInterceptor that catches exceptions during command processing and logs them. This interceptor can be used for centralized error handling. ```cs class ExceptionHandling : ICommandInterceptor { public async ValueTask InvokeAsync( T command, CancellationToken cancellation, Func next) where T : ICommand { try { await next(command, cancellation); } catch (Exception ex) { // Error tracking you like UnityEngine.Debug.Log($"oops! {ex.Message}"); } } } ``` -------------------------------- ### Register VitalRouter with Generic Host Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/di/microsoft-extensions.md Register VitalRouter services using the Generic Host builder. This example shows how to map all routes from an assembly or a specific presenter class. ```csharp using VitalRouter; // Example of using Generic Host var builder = Host.CreateApplicationBuilder(); builder.Services.AddVitalRouter(routing => { // Map all `[Routes]` targets in the specified assembly. routing.MapAll(GetType().Assembly); // Map specific class. routing.Map(); }); ``` -------------------------------- ### MRubyObject Serialization Example Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Demonstrates how to define C# types that can be serialized to and deserialized from mruby using the MRubyObject attribute. It shows which members are convertible and how to ignore specific members. ```csharp [MRubyObject] partial struct SerializeExample { // this is serializable members public string Id { get; private set; } public string Foo { get; init; } public Vector3 To; [MRubyMember] public int Z; // ignore members [MRubyIgnore] public float Foo; } ``` -------------------------------- ### Define FooCommand using Record Struct Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/icommand.md Example of defining a command using a C# record struct, implementing the ICommand interface. This is a concise way to define data-oriented types. ```cs public readonly record struct FooCommand(int X, string Y) : ICommand; ``` -------------------------------- ### Auto-generated Interceptor Determination Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/intro.mdx These examples demonstrate how source generators are used to determine interceptors for commands, facilitating interception logic. ```csharp // Auto-generated example, Determination of interceptor MethodTable.InterceptorFinder = static self => self.interceptorStackDefault; MethodTable.InterceptorFinder = static self => self.interceptorStackDefault; ``` -------------------------------- ### MRubyMember Alias Example Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Illustrates how to use MRubyMember with an alias to map a different name from Ruby to a C# member. This allows for flexible naming conventions between the two environments. ```csharp [MRubyObject] partial class Foo { [MRubyMember("alias_y")] public int Y; } ``` -------------------------------- ### Automatic Unsubscription for MonoBehaviour Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md When using a presenter that inherits from MonoBehaviour, routes are automatically unsubscribed upon destruction. MapTo in Start() is recommended. ```csharp [Routes] // < If routing as a MonoBehaviour public class FooPresenter : MonoBehaviour { void Start() { MapTo(Router.Default); // < Start command handling here } } ``` -------------------------------- ### Create a Filtering Interceptor Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/interceptor.md Implements a custom ICommandInterceptor to conditionally allow or deny command processing based on command properties. This example filters commands where FooCommand.X is greater than 100. ```cs class MyFilter : ICommandInterceptor { public async ValueTask InvokeAsync( T command, PublishContext context, PublishContinuation next) where T : ICommand { if (command is FooCommand { X: > 100 } cmd) { // Deny. Skip the rest of the subscribers. return; } // Allow. await next(command, context); } } ``` -------------------------------- ### Auto-generated Subscriber Determination Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/intro.mdx These examples show how VitalRouter uses source generators to pre-determine subscribers for commands, optimizing command dispatching. ```csharp // Auto-generated example, Determination of subscriber MethodTable.Value = static (source, command, cancellation) => source.On(command); MethodTable.Value = static (source, command, cancellation) => source.On(command); ``` -------------------------------- ### Creating and Using Multiple Router Instances Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md Shows how to instantiate a new Router and use its ISuscribable and IPublisher interfaces for mapping routes and publishing commands. The cost of instantiation is minimal. ```csharp var router = new Router(); // Router has some interfaces. ISubscribable subscribable = router; IPublisher publisher = router; p.MapTo(subscribable); await publisher.PublishAsync(new FooCommand()); ``` -------------------------------- ### Subscribing with PublishContext Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/publish-context.md Shows how to use PublishContext when subscribing to commands, both synchronously and asynchronously. ```cs subscribable.Subscribe((FooCommand cmd, PublishContext ctx) => { /* ... */ }); subscribable.SubscribeAwait(async (FooCommand cmd, PublishContext ctx) => { /* ... */ }); ``` -------------------------------- ### Create and Use a Separate Router Instance Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/event-handler-pattern.md Instantiate a new Router and use its subscribable and publisher interfaces. This is a lightweight alternative to events. ```cs var router = new Router(); // Router has some interfaces. ISubscribable subscribable = router; IPublisher publisher = router; subscribable.Subscribe(cmd => { }) await publisher.PublishAsync(new FooCommand()); ``` -------------------------------- ### Catching Exceptions with try/catch Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/error-handling.md Shows how to wrap a command publication in a try/catch block to handle exceptions thrown by route handlers. ```cs try { await Router.PublishAsync(new FooCommand()); } catch (Exception ex) { Console.WriteLine(ex.Message); // => "OOPS!!" } ``` -------------------------------- ### FanOutInterceptor with Sequential Routers Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/fan-out.md Demonstrates how to create and configure a FanOutInterceptor to distribute commands to two sequential groups (groupA and groupB). Commands are delivered in parallel to the groups, but processed sequentially within each group. ```cs var fanOut = new FanOutInterceptor(); var groupA = new Router(CommandOrdering.Sequential) var groupB = new Router(CommandOrdering.Sequential); fanOut.Add(groupA); fanOut.Add(groupB); Router.Default.Filter(fanOut); // Map routes per group presenter1.MapTo(groupA); presenter2.MapTo(groupA); presente3.MapTo(groupB); presente4.MapTo(groupB); ``` -------------------------------- ### Create a Handler with [Routes] attribute Source: https://github.com/hadashia/vitalrouter/blob/main/README.md Use the [Routes] attribute on a partial class to define handlers for commands. Use ValueTask for pure .NET or UniTask for Unity-specific async handling. ```csharp [Routes] public partial class PlayerPresenter { // Use ValueTask for performance in pure .NET projects [Route] public async ValueTask On(MoveCommand cmd) { await MoveToAsync(cmd.Destination); } // Use UniTask for Unity-specific async handling [Route] public async UniTask On(SomeAsyncCommand cmd) { await DoSomethingAsync(); } } ``` -------------------------------- ### Fan-out Configuration with Microsoft.Extensions.DependencyInjection Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/fan-out.md Shows how to configure fan-out routing using the AddVitalRouter extension method with Microsoft.Extensions.DependencyInjection. This allows defining sequential fan-out groups and mapping commands to presenters within each group. ```cs builder.AddVitalRouter(routing => { routing .FanOut(groupA => { groupA.Ordering = CommandOrdering.Sequential; groupA.Map(); groupA.Map(); }) .FanOut(groupB => { groupB.Ordering = CommandOrdering.Sequential; groupB.Map(); groupB.Map(); }) }); ``` -------------------------------- ### Compile and Execute Ruby Source Code Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Demonstrates compiling Ruby source code to bytecode, executing it directly, and saving/loading the compiled bytecode. This is useful for build pipelines or runtime environments where Ruby code needs to be processed. ```cs using ChibiRuby.Compiler; var source = """ def f(a) 1 * a end f 100 """u8; var mrb = MRubyState.Create(); var compiler = MRubyCompiler.Create(mrb); // Compile to bytecode (a CompilationResult holds the native byte buffer) using var compilation = compiler.Compile(source); // You can run it via irep (internal executable representation) var irep = compilation.ToIrep(); var result = mrb.Execute(irep); // => 100 // Or save the compiled bytecode (mruby calls this format "Rite") to a file or any other storage File.WriteAllBytes("compiled.mrb", compilation.AsBytecode().ToArray()); // Can be used later from file mrb.LoadBytecode(File.ReadAllBytes("compiled.mrb")); //=> 100 // or, you can evaluate source code directly result = compiler.LoadSourceCode("f(100)"u8); result = compiler.LoadSourceCode("f(100)"); ``` -------------------------------- ### Build Static Content Source: https://github.com/hadashia/vitalrouter/blob/main/website/README.md Generates static website content into the 'build' directory for hosting. ```bash yarn build ``` -------------------------------- ### Registering a Handler with PublishContext Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/publish-context.md Demonstrates how to define a handler that accepts a PublishContext for receiving published commands. ```cs [Routes] partial class FooHandler { [Route] async ValueTask On(FooCommand cmd, PublishContext ctx) { // ... } } ``` -------------------------------- ### Create and Configure MRubyContext Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Instantiate an MRubyContext and set its Router and CommandPreset. The Router handles incoming commands, and CommandPreset defines commands publishable from mruby. ```csharp var context = MRubyContext.Create(); context.Router = Router.Default; // ... 1 context.CommandPreset = new MyCommandPreset()); // ... 2 ``` -------------------------------- ### Sequential Command Ordering Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md Configure CommandOrdering.Sequential on a [Routes] class to ensure commands are delivered and processed one after another, preventing race conditions and enabling sequential scenarios. ```csharp [Routes(CommandOrdering.Sequential)] // < Order control of the commands delivered to this type. public partial class FooPresenter { async ValueTask On(FooCommand cmd) { // ... } } ``` -------------------------------- ### Execute mruby Scripts with MRubyContext Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Demonstrates loading, evaluating, and compiling mruby scripts using MRubyContext. Includes error handling for syntax and runtime exceptions. ```cs using var context = MRubyContext.Create(Router.Default, new MyCommandPreset()); // Evaluate arbitrary ruby script context.Load( "def mymethod(v) " + " v * 100 " + "end "); // Evaluates any ruby script and returns the deserialized result of the last value. var result = context.Evaluate("mymethod(7)"); // => 700 // Syntax error and runtime error on the Ruby side can be supplemented with try/catch. try { context.Evaluate("raise 'ERRRO!'"); } catch (Exception ex) { // ... } // Execute scripts, including the async method including VitalRouter, such as command publishing. var script = context.CompileScript("3.times { |i| cmd :text, body: \"Hello Hello #{i}\" }"); await script.RunAsync(); // When a syntax error is detected, CompileScript throws an exception. try { context.CompileScript("invalid invalid invalid"); } catch (Exception ex) { } // The completed script can be reused. await script.RunAsync(); // You can supplement Ruby runtime errors by try/catch RunAsync. try { await script.RunAsync(); } catch (Exception ex) { // ... } script.Dispose(); ``` -------------------------------- ### Setting Global Command Ordering Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/sequential-control.mdx Configure the default command ordering for all routers globally or by creating a new router instance with a specific ordering. ```cs // Set sequential constraint to the globally. Router.Default.AddFilter(CommandOrdering.Sequential); // Or var fifoRouter = new Router(CommandOrdering.Sequential); ``` -------------------------------- ### Simple Pub/Sub with Lambdas Source: https://github.com/hadashia/vitalrouter/blob/main/README.md Subscribe to commands using lambda expressions for simple publish-subscribe patterns. Supports async subscriptions with ordering and inline interceptors (filters). ```csharp // Simple subscription router.Subscribe(cmd => { /* ... */ }); // Async subscription with ordering router.SubscribeAwait(async (cmd, ct) => { await DoSomethingAsync(); }, CommandOrdering.Sequential); // Inline interceptors (Filters) // // `WithFilter(...)` returns a derived child router that owns the given filter. // Subscribers registered on the child receive commands published on the parent // (with the filter applied), just like an Rx `Where` chain forwards items from // its source. router .WithFilter(async (cmd, context, next) => { Console.WriteLine("Before"); await next(cmd, context); Console.WriteLine("After"); }) .Subscribe(cmd => { /* ... */ }); // Publishing on the parent triggers the filter for subscribers on the child: await router.PublishAsync(new MoveCommand(...)); // → "Before" → handler → "After" ``` -------------------------------- ### Deploy Website using SSH Source: https://github.com/hadashia/vitalrouter/blob/main/website/README.md Deploys the website using SSH, typically for GitHub Pages hosting. ```bash USE_SSH=true yarn deploy ``` -------------------------------- ### Advanced MRuby Scripting with Classes and `instance_eval` Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/intro.md Illustrates an advanced mruby scripting technique using class definitions and `instance_eval` to create context-specific methods, making scripts more organized and readable. ```ruby # Making full use of class definitions and instance_eval... class CharacterContext def initialize(id) @id = id end def move(to) cmd :move, id: @id, to: to end end def with(id, &block) CharacterContext.new(id).instance_eval(block) end ``` ```ruby # It can be written like this. with(:Bob) do move [5, 5] end ``` -------------------------------- ### Map Handler to Router and Publish Command Source: https://github.com/hadashia/vitalrouter/blob/main/README.md Map a presenter's routes to a router using MapTo, which returns a Subscription. Then, publish commands asynchronously to the router. ```csharp var router = new Router(); var presenter = new PlayerPresenter(); // MapTo returns a Subscription (IDisposable) using var subscription = presenter.MapTo(router); // Publish a message await router.PublishAsync(new MoveCommand(new Vector3(1, 0, 0))); ``` -------------------------------- ### Setting Command Ordering per Class Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/sequential-control.mdx Apply a specific command ordering strategy to all methods within a presenter class using the `Routes` attribute. ```cs // Command ordering per class level [Routes(CommandOrdering.Sequential)] public FooPresenter { public async UniTask On(FooCommand cmd) { } } ``` -------------------------------- ### Fan-out Configuration with VContainer (Unity) Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/fan-out.md Illustrates how to set up fan-out routing within a Unity project using VContainer. The RegisterVitalRouter extension method is used to define sequential fan-out groups and map commands to presenters. ```cs public class SampleLifetimeScope : LifetimeScope { public override void Configure(IContainerBuilder builder) { builder.RegisterVitalRouter(routing => { routing .FanOut(groupA => { groupA.Ordering = CommandOrdering.Sequential; groupA.Map(); groupA.Map(); }) .FanOut(groupB => { groupB.Ordering = CommandOrdering.Sequential; groupB.Map(); groupB.Map(); }) }); } } ``` -------------------------------- ### Apply Interceptors with DI Container (Microsoft.Extensions.DependencyInjection) Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/interceptor.md Demonstrates registering interceptors with Microsoft's dependency injection framework. Interceptors are added to the routing configuration. ```cs builder.AddVitalRouter(routing => { routing.Filters.Add(); routing.Filters.Add(); }); ``` -------------------------------- ### Publish a Command Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/event-handler-pattern.md Issue a command using PublishAsync. This will trigger registered handlers. Awaiting PublishAsync ensures all handlers complete. ```cs await Router.Default.PublishAsync(new FooCommand()); ``` -------------------------------- ### Setting Command Ordering per Method Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/sequential-control.mdx Define the command ordering strategy for an individual method within a presenter class using the `Route` attribute. ```cs [Routes] public FooPresenter { // Command ordering per method level [Route(CommandOrdering.Sequential)] public async UniTask On(FooCommand cmd) { } } ``` -------------------------------- ### Rent a Command from the Pool Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/command-pooling.md Use `CommandPool.Shared.Rent` to obtain a command instance. Provide a lambda expression for instantiation if the command is not already in the pool. Arguments can be passed to the lambda. ```cs // To publish, use CommandPool for instantiation. var cmd = CommandPool.Shared.Rent(() => new MyBoxedCommand()); ``` ```cs // Lambda expressions are used to instantiate objects that are not in the pool. Any number of arguments can be passed from outside. var cmd = CommandPool.Shared.Rent(arg1 => new MyBoxedCommand(arg1), extraArg); ``` ```cs var cmd = CommandPool.Shared.Rent((arg1, arg2) => new MyBoxedCommand(arg1, arg2), extraArg1, extraArg2); // ... ``` ```cs // Configure value cmd.ResourceA = resourceA; // Use it publisher.PublishAsync(cmd); ``` -------------------------------- ### Configuring Command Ordering via DI Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/sequential-control.mdx Set the command ordering strategy during the VitalRouter configuration in a Dependency Injection container. ```cs // Or Configure sequential routing via DI builder.RegisterVitalRouter(routing => { routing.CommandOrdering = CommandOrdering.Sequential; }); ``` -------------------------------- ### Publishing Commands with MRuby `cmd` Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/intro.md Demonstrates how to publish commands to C# handlers using the `cmd` method in mruby. This is a non-blocking operation similar to C#'s `await PublishAsync`. ```ruby # Publish to C# handlers. (Non-blocking) S # This line is same as `await PublishAsync(new CharacterMoveCommand { Id = "Bob", To = new Vector3(5, 5) })" cmd :move, id: "Bob", to: [5, 5] # You can set any variable you want from if state[:flag_1] cmd :speak, id: "Bob", body: "Flag 1 is on!" end ``` -------------------------------- ### Map Routes to a Router Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md Use the generated MapTo method to subscribe a presenter to a router. This initiates the command handling process. ```csharp var p = new FooPresenter(); p.MapTo(Router.Default); ``` -------------------------------- ### Applying Filters to Classes and Methods Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md Demonstrates how to apply filters at both the class and method level using the [Filter] attribute. Filters intercept command processing before and after method execution. ```csharp [Routes] [Filter(typeof(Filter1))] // < Class-wide filter public partial class FooPresenter { [Route] [Filter(typeof(Filter2))] // < Filter by each method async ValueTask On(FooCommand) { // ... } } ``` -------------------------------- ### Valid Handler Signatures Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md Demonstrates various valid method signatures for command handlers, including different return types, method names, and optional arguments like CancellationToken and PublishContext. ```csharp // public accessibility instead of `[Route]` public void On(FooCommand cmd) { /* .. */ } ``` ```csharp // Method name can be anything [Route] void Handle(FooCommand cmd) { /* ... */ } ``` ```csharp // With CancellationToken [Route] async ValueTask On(FooCommand cmd, CancellationToken cancellation) { /* .. */ } ``` ```csharp // With PublishContext [Route] void On(FooCommand cmd, PublishContext context) { /* .. */ } ``` ```csharp // With UniTask [Route] async UniTask On(FooCommand cmd) { /* .. */ } ``` -------------------------------- ### Create and Define MRubyState Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Initialize an MRubyState to manage mruby execution. Register ICommand implementations that you want to make available to mruby scripts using the DefineVitalRouter extension method. ```csharp using ChibiRuby; var mrb = MRubyState.Create(); mrb.DefineVitalRouter(x => { x.AddCommand("move"); x.AddCommand("speak"); }); ``` -------------------------------- ### Defining Custom MRuby Command Methods Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/intro.md Shows how to define custom methods in mruby that wrap the `cmd` call, simplifying scripting by creating a more natural Ruby interface. ```ruby # When the method is defined... def move(id, to) = cmd :move, id:, to: # It can be written like this move "Bob", [5, 5] ``` -------------------------------- ### Adding Custom Data with an Interceptor Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/publish-context.md Demonstrates how to add custom data to the PublishContext using an ICommandInterceptor. ```cs class MyFilter : ICommandInterceptor { public async ValueTask InvokeAsync( T command, PublishContext context, PublishContinuation next) where T : ICommand { context.Extensions.TryAdd("CustomData", 123456789); await next(command, context); } } ``` -------------------------------- ### Define a Poolable Command Class Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/command-pooling.md Define a class that implements `IPoolableCommand` for use with the command pool. Implement `OnReturnToPool` to reset any resource references. ```cs public class MyBoxedCommmand : IPoolableCommand { public ResourceA ResourceA { ge; set; } void IPoolableCommand.OnReturnToPool() { ResourceA = null!; } } ``` -------------------------------- ### Configure Sequential Command Ordering Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/di/microsoft-extensions.md Set the command ordering to Sequential for the registered routers. This ensures commands are processed in the order they are registered. ```csharp builder.Services.AddVitalRouter(routing => { // ... routing.Ordering = CommandOrdering.Sequential; // ... }); ``` -------------------------------- ### Using Existing Router Instance Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/di/vcontainer.md If you need to use an existing Router instance, such as Router.Default, register it explicitly before calling RegisterVitalRouter. ```csharp builder.RegisterInstance(Router.Default); builder.RegisterVitalRouter(routing => { // ... }); ``` -------------------------------- ### Call Ruby 'log' Method from Ruby Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Demonstrates how to call the C#-defined 'log' method from a Ruby script. ```ruby log 'This is a log' ``` -------------------------------- ### Define a Class for Routes Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md Use the [Routes] attribute on a partial class to enable compile-time parsing for command reception. The class must be partial. ```csharp [Routes] public partial class FooPresenter { // ... } ``` -------------------------------- ### Execute MRuby Script Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Load and execute mruby bytecode from a file asynchronously. The Router.Default is used for command publishing, and the mruby script is parsed before execution. ```csharp var bytecode = File.ReadAllBytes("your_script.mrb"); await mrb.ExecuteAsync(Router.Default, mrb.ParseBytecode(bytecode)); ``` -------------------------------- ### Create a Logging Interceptor Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/interceptor.md Implements a custom ICommandInterceptor to log messages before and after a command is processed. Useful for debugging and tracing. ```cs class Logging : ICommandInterceptor { public async ValueTask InvokeAsync( T command, PublishContext context, PublishCOntinuation next) where T : ICommand { UnityEngine.Debug.Log($"Start {typeof(T)}"); // Execute subsequent routes. await next(command, context); UnityEngine.Debug.Log($"End {typeof(T)}"); } } ``` -------------------------------- ### Apply Interceptors Globally to Router Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/interceptor.md Demonstrates applying interceptors globally to the router instance or its default configuration. This affects all publish and subscribe operations. ```cs Router.Default.AddFilter(new YourInterceptor()); var router = new Router(); router.AddFilter(new YourInterceptor()); IPublisher pubilsher = router; publisher.AddFilter(new YourInterceptor()); ISubscribable subscribable = router; subscribable.AddFilter(new YourInterceptor()); ``` -------------------------------- ### Adding Interceptors to the Router Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/error-handling.md Illustrates how to add an error handling interceptor globally to the router or to a specific handler using attributes. ```cs // Add error handler globally Router.Default.AddFilter(new ErrorHandling()); // Add error handler to specific handler [Routes] [Filter(typeof(ErrorHandling))] partial class FooPresenter { // ... } ``` -------------------------------- ### Deploy Website without SSH Source: https://github.com/hadashia/vitalrouter/blob/main/website/README.md Deploys the website without using SSH, requiring a GitHub username. ```bash GIT_USER= yarn deploy ``` -------------------------------- ### Call Ruby 'wait' Command from Ruby Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Demonstrates how to call the C#-defined 'wait' command from a Ruby script, specifying the duration in seconds. ```ruby def wait(secs) = cmd :wait, seconds: secs wait 1 ``` -------------------------------- ### Apply Interceptors to Type and Method Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/interceptor.md Illustrates applying interceptors at the class level using the [Routes] attribute and at the method level using the [Filter] attribute. ```cs [Routes] [Filter(typeof(Logging))] // 2. Apply to the type public partial class FooPresenter { [Filter(typeof(ExtraInterceptor))] // 3. Apply to the method public void On(CommandA cmd) { // ... } } ``` -------------------------------- ### Subscribe to Commands with a Synchronous Lambda Handler Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/event-handler-pattern.md Register a synchronous lambda expression to handle commands. The first argument is the command, and the second is the PublishContext. ```cs Router.Default.Subscribe((cmd, context) => { // sync handler }); ``` -------------------------------- ### Throwing an Exception in a Route Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/error-handling.md Demonstrates how a route handler can throw an exception, which can then be caught by a surrounding try/catch block. ```cs [Route] void On(FooCommand cmd) { throw new InvalidOperationException("OOPS!!!"); } ``` -------------------------------- ### Apply Interceptors with DI Container (VContainer) Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/interceptor.md Shows how to register interceptors using VContainer's dependency injection. Interceptors are added to the routing configuration. ```cs builder.RegisterVitalRouter(routing => { routing.Filters.Add(); routing.Filters.Add(); }); ``` -------------------------------- ### Configure Serializer Options with Custom Formatters Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Set up `MrbValueSerializerOptions` to include your custom formatters alongside default behaviors. This involves adding your formatter instance to `StaticCompositeResolver` and setting the context's serializer options. ```csharp StaticCompositeResolver.Instance .AddFormatters(UserIdFormatter.Instance) // < Yoru custom formatters .AddResolvers(StandardResolver.Instance); // < Default behaviours // Set serializer options to context. var context = MRubyContext.Create(...); context.SerializerOptions = new MrbValueSerializerOptions { Resolver = StaticCompositeResolver }; ``` -------------------------------- ### Define Command Handlers with [Route] Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md Append the [Route] attribute to methods within a [Routes] class to define command handlers. Handlers can be synchronous or asynchronous. ```csharp [Routes] public partial class FooPresenter { [Route] void On(FooCommand cmd) { // Do something ... } [Route] async ValueTask On(BarCommand cmd, CancellationToken cancellation) { await DoSomethingAwait(); } } ``` -------------------------------- ### Set Shared State from C# to mruby Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Demonstrates how to set various data types (int, bool, float, string, symbol) from C# into the mruby shared state. ```cs var context = MRubyScript.CreateContext(...); context.SharedState.Set("int_value", 1234); context.SharedState.Set("bool_value", true); context.SharedState.Set("float_value", 678.9); context.SharedState.Set("string_value", "Hoge Hoge"); context.SharedState.Set("symbol_value", "fuga_fuga", asSymbol: true); ``` -------------------------------- ### Setting Command Ordering per Lambda Expression Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/sequential-control.mdx Specify the command ordering strategy directly when subscribing to commands using lambda expressions. ```cs // Command ordering per per lambda expressions router.SubscribeAwait(async (cmd, ctx) => { /* ... */ }, CommandOrdering.Sequential); ``` -------------------------------- ### Compile and Run MRuby Script Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Compile an mruby script source into an executable script object and then run it asynchronously. The script uses the 'cmd' method to invoke registered commands. ```csharp // Your ruby script source here var rubySource = "cmd :speak, id: 'Bob', text: 'Hello'" using MRubyScript script = context.CompileScript(rubySource); await script.RunAsync(); ``` -------------------------------- ### Define Ruby 'wait' Command from C# Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Defines a custom Ruby command 'wait' from C# using a record struct that implements ICommand. ```csharp [MRubyObject] public readonly partial record struct WaitCommand(int Seconds) : ICommand; ``` ```csharp mrb.DefineVitalRouter(x => { x.AddCommand("wait"); }); ``` -------------------------------- ### Build VitalRouter MRuby Native Library Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Builds the VitalRouter MRuby native library using the mruby build system. Ensure you have prepared the appropriate MRUBY_CONFIG file for your target platform. ```bash cd VitalRouter/src/vitalrouter-mruby MRUBY_CONFIG=/path/to/build_config.rb rake ``` -------------------------------- ### Subscribe and Bind Publisher with R3 Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/r3.md Subscribe to an R3 Observable and bind it to a VitalRouter publisher. This snippet shows how to subscribe and bind a publisher, with options to forget the async operation or await publishing. ```csharp // Subscibe and bind publisher. observable .Select(x => new FooCommand { X = x } .SubscribeToPublish(Router.Default); // Subscibe and bind publisher and await for publishing all. (PublishAsync().Forget()) await observable .Select(x => new FooCommand { X = x } .ForEachPublishAndForgetAsync(Router.Default); // Subscibe and bind publisher and await for publishing all. (await PublishAsync() one by one) await observable .Select(x => new FooCommand { X = x } .ForEachPublishAndAwaitAsync(Router.Default); ``` -------------------------------- ### Apply a Filter to a Router Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/event-handler-pattern.md Use WithFilter to add common processing before commands are delivered to handlers. Filters are applied cumulatively. ```cs Router.Default .WithFilter(async (cmd, context, next) => { if (condition) await next(cmd, context); }) .Subscribe((cmd, context) => { /* ... */ }); ``` -------------------------------- ### Define a Command with record struct Source: https://github.com/hadashia/vitalrouter/blob/main/README.md Define commands as record structs for zero-allocation messaging. Ensure AOT metadata is correctly generated for environments like iOS if using tools like HybridCLR. ```csharp public readonly record struct MoveCommand(Vector3 Destination) : ICommand; ``` -------------------------------- ### Ruby API for Time and Commands Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Provides Ruby extensions for non-blocking waits based on time or frames, and for publishing VitalRouter commands. ```ruby # Wait for the number of seconds. (Non-blocking) # It is equivalent to `await UniTask.Delay(TimeSpan.FromSeconds(1))`) wait 1.0.sec wait 2.5.secs # Wait for the number of fames. (Non-blocking) # It is equivalent to `await UniTask.DelayFrame(1)`) wait 1.frame wait 2.frames # Send logs to the Unity side. Default implementation is `UnityEngine.Debug.Log` log "Log to Unity !" # Publish VitalRouter command cmd :your_command_name, prop1: 123, prop2: "bra bra" ``` -------------------------------- ### Return a Command to the Pool Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/command-pooling.md Commands can be returned to the pool automatically using the `CommandPooling` Interceptor or manually by calling `CommandPool.Shard.Return`. ```cs // It is convenient to use the `CommandPooling` Interceptor to return to pool automatically. Router.Default.Filter(CommandPooling.Instance); ``` ```cs // Or, return to pool manually. CommandPool.Shard.Return(cmd); ``` -------------------------------- ### CommandOrdering Enum Definition Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/sequential-control.mdx Defines the possible strategies for handling simultaneous command publications: Parallel, Sequential, Drop, and Switch. ```cs public enum CommandOrdering { /// /// If commands are published simultaneously, subscribers are called in parallel. /// Parallel, /// /// If commands are published simultaneously, wait until the subscriber has processed the first command. /// Sequential, /// /// If commands are published simultaneously, ignore commands that come later. /// Drop, /// /// If the previous asynchronous method is running, it is cancelled and the next asynchronous method is executed. /// Switch, } ``` -------------------------------- ### Define Ruby 'log' Method from C# Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Defines a Ruby 'log' method in C# that uses Unity's Debug.Log to output messages. ```csharp mrb.DefineMethod(mrb.ObjectClass, mrb.Intern("log"), (mrb, self) => { var message = mrb.GetArgumentAt(0); UnityEngine.Debug.Log(mrb.Stringify(message).ToString()); return default; }); ``` -------------------------------- ### Subscribe to Commands with an Asynchronous Lambda Handler Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/event-handler-pattern.md Register an asynchronous lambda expression to handle commands using SubscribeAwait. Specify CommandOrdering for parallel execution during await. ```cs Router.Default.SubscribeAwait(async (cmd, context) => { await DoSomeThingAsync(); }, CommandOrdering.Parallel); ``` -------------------------------- ### Define Custom Command Preset Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Define a custom command preset by inheriting from MRubyCommandPreset and decorating command types with MRubyCommand attributes. This specifies the command names and their corresponding C# types. ```csharp [MRubyCommand("move", typeof(CharacterMoveCommand))] // < Your custom command name and type list here [MRubyCommand("speak", typeof(CharacterSpeakCommand))] partial class MyCommandPreset : MRubyCommandPreset { } ``` -------------------------------- ### Resolve ICommandPublisher in a Controller Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/di/microsoft-extensions.md Demonstrates how to resolve and use ICommandPublisher within a controller class. The publisher instance is managed by the DI container. ```csharp class HogeController { readonly ICommandPublisher publisher; // Resolve `ICommandPublisher` public HogeController(ICommandPublisher publisher) { this.publisher = publisher; } public void DoSomething() { publisher.PublishAsync(new FooCommand { X = 1, Y = 2 }).Forget(); } } ``` -------------------------------- ### Checking Cancellation in a Handler Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/publish-context.md Illustrates how to check the CancellationToken from PublishContext within a handler to respect pipeline cancellation. ```cs [Routes] partial class FooHandler { [Route] async ValueTask On(FooCommand cmd, PublishContext ctx) { if (ctx.CancellationToken.IsCancelRequested) return; } } ``` -------------------------------- ### Setting Command Ordering Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/di/vcontainer.md Control the execution order of commands by setting the Ordering property within the RegisterVitalRouter configuration. ```csharp builder.RegisterVitalRouter(routing => { // ... routing.Ordering = CommandOrdering.Sequential; // ... }); ``` -------------------------------- ### Accessing Custom Data in a Handler Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/publish-context.md Shows how to retrieve custom data previously added to the PublishContext's Extensions in a handler. ```cs [Routes] partial class FooHandler { [Route] async ValueTask On(FooCommand cmd, PublishContext context) { context.Extensions["CustomData"] // => 123456789 } } ``` -------------------------------- ### Handle mruby Logs in Unity Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Configures a global log handler to capture messages sent from the mruby side, directing them to Unity's debug log. ```cs MRubyContext.GlobalLogHandler = message => { UnityEngine.Debug.Log(messae); }; ``` -------------------------------- ### Subscribe to Messages with Lambda Handlers Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/intro.mdx Register event handlers using lambda expressions for simple message subscriptions. Unsubscribe using the returned IDisposable. ```csharp // Lambda handlers var subscription = router.Subscribe(cmd => { /* ... */ }); // Unsubscribe with `IDisposable`. subscription.Dispose(); ``` -------------------------------- ### Register Interceptors Manually without DI Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/pipeline/interceptor.md Shows how to register interceptor instances directly when not using a dependency injection container, typically during router mapping. ```cs MapTo(Router.Default, new Logging(), new ErrorHandling()); ``` ```cs // auto-generated public Subscription MapTo(ICommandSubscribable subscribable, Logging interceptor1, ErrorHandling interceptor2) { /* ... */ } ``` -------------------------------- ### Resolve ICommandSubscribable in a Presenter Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/di/microsoft-extensions.md Shows how to resolve ICommandSubscribable in a presenter class to subscribe to commands. The subscribable instance is provided by the DI container. ```csharp public class HogePresenter { // Resolve `ICommandSubscribable` public FooController(ICommandSubscribable subscribable) { subscribable.Subscribe((cmd, ctx) => { /* ... */ }); } } ``` -------------------------------- ### Declare Custom Commands Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Declare custom commands as partial structs or classes decorated with MRubyObject. These commands can be passed between mruby and C#. ```csharp [MRubyObject] partial struct CharacterMoveCommand : ICommand { public string Id; public Vector3 To; } [MRubyObject] partial struct CharacterSpeakCommand : ICommand { public string Id; public string Text; } ``` -------------------------------- ### Subscribe to Async Messages with Lambda Interceptors Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/intro.mdx Subscribe to asynchronous messages using lambda expressions and apply filters using WithFilter. Supports sequential execution. ```csharp // Subscribe with async var subscription = router.SubscribeAwait(async (cmd, cancellationToken) => { /* ... */ }, CommandOrdering.Sequential); // lambda interceptors // // `WithFilter(...)` returns a derived child router that owns the filter. // Subscribers on the child receive commands published on the parent with // the filter applied (Rx `Where`-style chaining). router .WithFilter(async (x, context) => { if (condition) await next(x, context); }) .Subscribe(cmd => { /* .. */ }); ``` -------------------------------- ### Define MRubyObject Commands Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v2.md Declare custom commands with the [MRubyObject] attribute to enable mutual conversion between C# and Ruby objects. These commands can be executed from mruby scripts. ```csharp // Your custom command decralations [MRubyObject] partial struct CharacterMoveCommand : ICommand { public string Id; public Vector3 To; } [MRubyObject] partial struct CharacterSpeakCommand : ICommand { public string Id; public string Text; } ``` -------------------------------- ### Convert Publisher to R3 Observable Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/r3.md Convert a publisher to an R3 Observable for use with R3 streams. This is the initial step for integrating R3 with VitalRouter. ```csharp Observable o = publisher.AsObservable() // Convert pub/sub to R3 Observable ``` -------------------------------- ### Unsubscribe Routes Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/getting-started/declarative-routing-pattern.md Unsubscribe routes by disposing the subscription object returned by MapTo, or by calling UnmapRoutes() to unsubscribe all routes from the presenter. ```csharp Subscription s = p.MapTo(); s.Dispose(); // Unmap // Or, unmap all routes p.UnmapRoutes(); ``` -------------------------------- ### Access Shared State from mruby Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/extensions/mruby/v1.md Illustrates how to read variables set from C# within an mruby script using the `state` object, including type casting and fuzzy matching. ```ruby state[:int_value] #=> 1234 state[:bool_value] #=> true state[:float_value] #=> 678.9 state[:string_value] #=> "Hoge Hoge" state[:symbol_value] #=> :fuga_fuga # A somewhat fuzzy matcher, the `is?` method, is available for shared states. state[:a] #=> 'buz' state[:a].is?('buz') #=> true state[:a].is?(:buz) #=> true ``` -------------------------------- ### Creating an Isolated Router for Child Scope Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/di/vcontainer.md To create a dedicated, isolated Router for a child scope, set the 'Isolated' property to true within the RegisterVitalRouter configuration. ```csharp builder.RegisterVitalRouter(routing => { routing.Isolated = true; routing.Map(); }); ``` -------------------------------- ### Declarative Event Handlers with Attributes Source: https://github.com/hadashia/vitalrouter/blob/main/website/docs/intro.mdx Define message handlers using attributes on class declarations. Supports filters and async handlers. ```csharp [Routes] [Filter(typeof(Logging))] [Filter(typeof(ExceptionHandling))] [Filter(typeof(GameStateUpdating))] public partial class ExamplePresenter { // Declare event handler [Route] void On(FooCommand cmd) { // Do something ... } // Declare event handler (async) [Route] async UniTask On(BarCommand cmd) { // Do something for await ... } // Declare event handler with extra filter [Route] [Filter(typeof(ExtraFilter))] async UniTask On(BuzCommand cmd, CancellationToken cancellation = default) { // Do something after all filters runs on. } // Declare event handler with specifies behavior when async handlers are executed concurrently [Route(CommandOrdering.Sequential)] async UniTask On(BuzCommand cmd, CancellationToken cancellation = default) { // Do something after all filters runs on. } } ```