Skip to content

Commit

Permalink
Built all infrastructure to support arbitrary subscriber instances. N…
Browse files Browse the repository at this point in the history
…ot yet accessible via IBus
  • Loading branch information
flq committed Nov 21, 2010
1 parent f67d694 commit 21de4f8
Show file tree
Hide file tree
Showing 18 changed files with 471 additions and 3 deletions.
28 changes: 28 additions & 0 deletions MemBus.Tests/Help/IClassicIHandleStuffI.cs
@@ -0,0 +1,28 @@
namespace MemBus.Tests.Help
{
public interface IClassicIHandleStuffI<in T>
{
void Gimme(T theThing);
}

public interface IInvalidHandlerInterfaceBecauseNoParameter
{
void Gimme();
}

public interface IInvalidHandlerInterfaceBecauseTwoMethodsOfrequestedPattern
{
void Gimme(object thing);
void Gamme(object thang);
}

public interface IInvalidHandlerInterfaceBecauseTwoParams
{
void Gimme(object thing, object thang);
}

public interface IInvalidHandlerInterfaceBecauseReturnType
{
int Gimme(object thing);
}
}
19 changes: 19 additions & 0 deletions MemBus.Tests/Help/ItfNonGenericForHandles.cs
@@ -0,0 +1,19 @@
using System;

namespace MemBus.Tests.Help
{
public interface ItfNonGenericForHandles
{
void Handle(MessageA msg);
}

class AHandlerThroughSimpleInterface : ItfNonGenericForHandles
{
public int MsgCount;

public void Handle(MessageA msg)
{
MsgCount++;
}
}
}
37 changes: 37 additions & 0 deletions MemBus.Tests/Help/SomeCrazyHandler.cs
@@ -0,0 +1,37 @@
using System;

namespace MemBus.Tests.Help
{
public class SomeCrazyHandler : IClassicIHandleStuffI<MessageC>, IClassicIHandleStuffI<MessageB>
{
public int MessageCCount;
public int MessageACount;
public int MessageBCount;
public int MsgSpecialACount;

public void Gimme(MessageC theThing)
{
MessageCCount++;
}

public void Gimme(MessageB theThing)
{
MessageBCount++;
}

public void Handle(MessageA msg)
{
MessageACount++;
}

public void Handle(MessageB msg)
{
MessageBCount++;
}

public void Schmandle(MessageASpecialization msg)
{
MsgSpecialACount++;
}
}
}
12 changes: 12 additions & 0 deletions MemBus.Tests/Help/SomeHandler.cs
@@ -0,0 +1,12 @@
namespace MemBus.Tests.Help
{
public class SomeHandler
{
public int MsgACalls;

public void Handle(MessageA msg)
{
MsgACalls++;
}
}
}
5 changes: 5 additions & 0 deletions MemBus.Tests/MemBus.Tests.csproj
Expand Up @@ -56,6 +56,11 @@
<Link>Frame\AssertExtensions.cs</Link> <Link>Frame\AssertExtensions.cs</Link>
</Compile> </Compile>
<Compile Include="Cloning_a_bus.cs" /> <Compile Include="Cloning_a_bus.cs" />
<Compile Include="Help\IClassicIHandleStuffI.cs" />
<Compile Include="Help\ItfNonGenericForHandles.cs" />
<Compile Include="Help\SomeCrazyHandler.cs" />
<Compile Include="Help\SomeHandler.cs" />
<Compile Include="Using_SubscriptionAdapter_Service.cs" />
<Compile Include="When_using_caching_resolver.cs" /> <Compile Include="When_using_caching_resolver.cs" />
<Compile Include="Documentation_tests.cs" /> <Compile Include="Documentation_tests.cs" />
<Compile Include="Frame\PublishPipelineTester.cs" /> <Compile Include="Frame\PublishPipelineTester.cs" />
Expand Down
111 changes: 111 additions & 0 deletions MemBus.Tests/Using_SubscriptionAdapter_Service.cs
@@ -0,0 +1,111 @@
using System;
using System.Linq;
using MemBus.Setup;
using MemBus.Subscribing;
using MemBus.Tests.Help;
using Moq;
using NUnit.Framework;
using MemBus.Tests.Frame;
using MemBus.Support;

namespace MemBus.Tests
{
[TestFixture]
public class Using_SubscriptionAdapter_Service
{
[Test]
public void Unconfigured_setup_will_throw_invalid_op()
{
var setup = new FlexibleSubscribeAdapter();
Assert.Throws<InvalidOperationException>(() => ((ISetup<IConfigurableBus>)setup).Accept(null));
}

[Test]
public void When_having_some_configuration_adapter_adds_itself_as_service()
{
var setup = new FlexibleSubscribeAdapter();
setup.ByMethodName("Handle");

var bus = new Mock<IConfigurableBus>();
((ISetup<IConfigurableBus>)setup).Accept(bus.Object);

bus.Verify(c=>c.AddService<IAdapterServices>(setup));
}

[Test]
public void Integrative_test_of_finding_all_handlers_in_complex_scenario()
{
var setup = new FlexibleSubscribeAdapter();
setup
.ByInterface(typeof(IClassicIHandleStuffI<>))
.ByMethodName("Handle")
.ByMethodName("Schmandle");

var handler = new SomeCrazyHandler();
var subs = ((IAdapterServices) setup).SubscriptionsFor(handler);

subs.ShouldHaveCount(5);

subs.Where(s=>s.Handles(typeof(MessageASpecialization))).Each(s=>s.Push(new MessageASpecialization()));
handler.MessageACount.ShouldBeEqualTo(1);
handler.MsgSpecialACount.ShouldBeEqualTo(1);

subs.Where(s => s.Handles(typeof(MessageB))).Each(s => s.Push(new MessageB()));
handler.MessageBCount.ShouldBeEqualTo(2); //There are 2 handle methods for MsgB :)

subs.Where(s => s.Handles(typeof(MessageC))).Each(s => s.Push(new MessageC()));
handler.MessageCCount.ShouldBeEqualTo(1);
}

[Test]
public void Subscriptions_are_built_for_object_method_based()
{
var builder = new MethodBasedBuilder("Handle");
var subs = builder.BuildSubscriptions(new SomeHandler());
subs.ShouldNotBeNull();
subs.ShouldHaveCount(1);
}

[Test]
public void Subscriptions_for_object_method_based_work_correctly()
{
var builder = new MethodBasedBuilder("Handle");
var handler = new SomeHandler();
var subs = builder.BuildSubscriptions(handler);
var subscription = subs.First();
subscription.Handles(typeof(MessageA)).ShouldBeTrue();
subscription.Push(new MessageA());
handler.MsgACalls.ShouldBeEqualTo(1);
}

[TestCase(typeof(IInvalidHandlerInterfaceBecauseNoParameter))]
[TestCase(typeof(IInvalidHandlerInterfaceBecauseTwoMethodsOfrequestedPattern))]
[TestCase(typeof(IInvalidHandlerInterfaceBecauseReturnType))]
[TestCase(typeof(IInvalidHandlerInterfaceBecauseTwoParams))]
public void These_handler_interfaces_are_invalid_for_usage(Type interfaceType)
{
Assert.Throws<InvalidOperationException>(() => { new InterfaceBasedBuilder(interfaceType); });
}

[Test]
public void Non_generic_interface_is_properly_handled()
{
var builder = new InterfaceBasedBuilder(typeof(ItfNonGenericForHandles));
var targetToAdapt = new AHandlerThroughSimpleInterface();
var subs = builder.BuildSubscriptions(targetToAdapt);
subs.ShouldHaveCount(1);
var s = subs.First();
s.Handles(typeof(MessageA)).ShouldBeTrue();
s.Push(new MessageA());
targetToAdapt.MsgCount.ShouldBeEqualTo(1);
}

[Test]
public void Two_subscriptions_expected_from_aquainting_crazy_handler()
{
var builder = new InterfaceBasedBuilder(typeof (IClassicIHandleStuffI<>));
var subs = builder.BuildSubscriptions(new SomeCrazyHandler());
subs.ShouldHaveCount(2);
}
}
}
2 changes: 1 addition & 1 deletion MemBus.Tests/When_subscribing.cs
Expand Up @@ -7,7 +7,7 @@
namespace MemBus.Tests namespace MemBus.Tests
{ {
[TestFixture] [TestFixture]
public class sWhen_subscribing public class When_subscribing
{ {
[Test] [Test]
public void Conventions_allow_changing_the_shape() public void Conventions_allow_changing_the_shape()
Expand Down
6 changes: 6 additions & 0 deletions MemBus/MemBus.csproj
Expand Up @@ -75,6 +75,12 @@
<Compile Include="Configurators\RichClientFrontend.cs" /> <Compile Include="Configurators\RichClientFrontend.cs" />
<Compile Include="Setup\ISetup.cs" /> <Compile Include="Setup\ISetup.cs" />
<Compile Include="Setup\IConfigurableBus.cs" /> <Compile Include="Setup\IConfigurableBus.cs" />
<Compile Include="Subscribing\Adapter\ConstructSubscriptionExtension.cs" />
<Compile Include="Subscribing\Adapter\FlexibleSubscribeAdapter.cs" />
<Compile Include="Subscribing\Adapter\IAdapterServices.cs" />
<Compile Include="Subscribing\Adapter\InterfaceBasedBuilder.cs" />
<Compile Include="Subscribing\Adapter\ISubscriptionBuilder.cs" />
<Compile Include="Subscribing\Adapter\MethodBasedBuilder.cs" />
<Compile Include="Subscribing\ShapeToPassthrough.cs" /> <Compile Include="Subscribing\ShapeToPassthrough.cs" />
<Compile Include="Subscribing\SubscriptionPipeline.cs" /> <Compile Include="Subscribing\SubscriptionPipeline.cs" />
<Compile Include="Subscribing\DisposableSubscription.cs" /> <Compile Include="Subscribing\DisposableSubscription.cs" />
Expand Down
9 changes: 9 additions & 0 deletions MemBus/Setup/BusSetup.cs
Expand Up @@ -9,6 +9,8 @@ public class BusSetup
{ {
private readonly List<ISetup<IConfigurableBus>> configurators = new List<ISetup<IConfigurableBus>>(); private readonly List<ISetup<IConfigurableBus>> configurators = new List<ISetup<IConfigurableBus>>();


private BusSetup() { }

public BusSetup Apply(params ISetup<IConfigurableBus>[] configurators) public BusSetup Apply(params ISetup<IConfigurableBus>[] configurators)
{ {
this.configurators.AddRange(configurators); this.configurators.AddRange(configurators);
Expand All @@ -21,6 +23,13 @@ public BusSetup Apply<T>(params ISetup<IConfigurableBus>[] configurators) where
return Apply(configurators); return Apply(configurators);
} }


public BusSetup Apply<T>(Action<T> additionalConfig) where T : ISetup<IConfigurableBus>, new()
{
var t = new T();
additionalConfig(t);
return Apply(t);
}

public IBus Construct() public IBus Construct()
{ {
var bus = new Bus(); var bus = new Bus();
Expand Down
28 changes: 28 additions & 0 deletions MemBus/Subscribing/Adapter/ConstructSubscriptionExtension.cs
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;

namespace MemBus.Subscribing
{
public static class ConstructSubscriptionExtension
{
public static ISubscription ConstructSubscription(this MethodInfo info, object target)
{
var parameterType = info.GetParameters()[0].ParameterType;

var fittingDelegateType = typeof(Action<>).MakeGenericType(parameterType);
var @delegate = Delegate.CreateDelegate(fittingDelegateType, target, info);

var fittingMethodSubscription = typeof(MethodInvocation<>).MakeGenericType(parameterType);
var sub = Activator.CreateInstance(fittingMethodSubscription, @delegate);

return (ISubscription)sub;
}

public static IEnumerable<ISubscription> ConstructSubscriptions(this IEnumerable<MethodInfo> infos, object target)
{
return infos.Select(i => i.ConstructSubscription(target));
}
}
}
53 changes: 53 additions & 0 deletions MemBus/Subscribing/Adapter/FlexibleSubscribeAdapter.cs
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using MemBus.Setup;
using System.Linq;

namespace MemBus.Subscribing
{
public class FlexibleSubscribeAdapter : ISetup<IConfigurableBus>, IAdapterServices
{
private bool configurationAvailable;
private readonly List<ISubscriptionBuilder> builders = new List<ISubscriptionBuilder>();


void ISetup<IConfigurableBus>.Accept(IConfigurableBus setup)
{
if (!configurationAvailable)
throw new InvalidOperationException("No adapter rules were set up.");
setup.AddService<IAdapterServices>(this);
}

/// <summary>
/// Look at an object and look for methods with the provided name. The method must be void
/// and accept a single parameter
/// </summary>
public FlexibleSubscribeAdapter ByMethodName(string methodName)
{
addToBuilders(new MethodBasedBuilder(methodName));
return this;
}

/// <summary>
/// Look at an object and check it for interfaces. An interface should adhere to the following rules:
/// Interface should define only one void method with a single parameter.
/// The interface may be generic and can be implemented multiple times.
/// </summary>
public FlexibleSubscribeAdapter ByInterface(Type interfaceType)
{
addToBuilders(new InterfaceBasedBuilder(interfaceType));
return this;
}

IEnumerable<ISubscription> IAdapterServices.SubscriptionsFor(object subscriber)
{
return builders.SelectMany(b => b.BuildSubscriptions(subscriber));
}

private void addToBuilders(ISubscriptionBuilder builder)
{
builders.Add(builder);
configurationAvailable = true;
}
}
}
9 changes: 9 additions & 0 deletions MemBus/Subscribing/Adapter/IAdapterServices.cs
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace MemBus.Subscribing
{
public interface IAdapterServices
{
IEnumerable<ISubscription> SubscriptionsFor(object subscriber);
}
}
9 changes: 9 additions & 0 deletions MemBus/Subscribing/Adapter/ISubscriptionBuilder.cs
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace MemBus.Subscribing
{
public interface ISubscriptionBuilder
{
IEnumerable<ISubscription> BuildSubscriptions(object targetToAdapt);
}
}

0 comments on commit 21de4f8

Please sign in to comment.