Commit 558c110
Changed files (21)
trunk
product
MyMoney
Infrastructure
caching
Container
proxies
Interceptors
Testing
MetaData
trunk/product/MyMoney/Infrastructure/caching/IdentityMapSpecs.cs
@@ -0,0 +1,91 @@
+using jpboodhoo.bdd.contexts;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.caching
+{
+ [Concern(typeof (IdentityMap<,>))]
+ public class behaves_like_identity_map : concerns_for<IIdentityMap<int, string>, IdentityMap<int, string>>
+ {
+ public override IIdentityMap<int, string> create_sut()
+ {
+ return new IdentityMap<int, string>();
+ }
+ }
+
+ public class when_getting_an_item_from_the_identity_map_for_an_item_that_has_been_added : behaves_like_identity_map
+ {
+ it should_return_the_item_that_was_added_for_the_given_key = () => result.should_be_equal_to("1");
+
+ because b = () =>
+ {
+ sut.Add(1, "1");
+ result = sut.ItemThatBelongsTo(1);
+ };
+
+ static string result;
+ }
+
+ public class when_getting_an_item_from_the_identity_map_that_has_not_been_added : behaves_like_identity_map
+ {
+ it should_return_the_default_value_for_that_type = () => result.should_be_equal_to(null);
+
+ because b = () => { result = sut.ItemThatBelongsTo(2); };
+
+ static string result;
+ }
+
+ public class when_checking_if_an_item_has_been_added_to_the_identity_map_that_has_been_added :
+ behaves_like_identity_map
+ {
+ it should_return_true = () => result.should_be_true();
+
+ because b = () =>
+ {
+ sut.Add(10, "10");
+ result = sut.ContainsAnItemFor(10);
+ };
+
+ static bool result;
+ }
+
+ public class when_checking_if_an_item_has_been_added_to_the_identity_map_that_has_not_been_added :
+ behaves_like_identity_map
+ {
+ it should_return_false = () => result.should_be_false();
+
+ because b = () => { result = sut.ContainsAnItemFor(9); };
+
+ static bool result;
+ }
+
+ public class when_updating_the_value_for_a_key_that_has_already_been_added_to_the_identity_map :
+ behaves_like_identity_map
+ {
+ it should_replace_the_old_item_with_the_new_one = () => result.should_be_equal_to("7");
+
+ because b = () =>
+ {
+ sut.Add(6, "6");
+ sut.UpdateTheItemFor(6, "7");
+ result = sut.ItemThatBelongsTo(6);
+ };
+
+ static string result;
+ }
+
+ public class when_updating_the_value_for_a_key_that_has_not_been_added_to_the_identity_map :
+ behaves_like_identity_map
+ {
+ it should_add_the_new_item = () => result.should_be_equal_to("3");
+
+ because b = () =>
+ {
+ sut.UpdateTheItemFor(3, "3");
+ result = sut.ItemThatBelongsTo(3);
+ };
+
+ static string result;
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/caching/IIdentityMap.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+
+namespace MoMoney.Infrastructure.caching
+{
+ public interface IIdentityMap<TKey, TValue>
+ {
+ void Add(TKey key, TValue value);
+ void UpdateTheItemFor(TKey key, TValue newValue);
+ bool ContainsAnItemFor(TKey key);
+ TValue ItemThatBelongsTo(TKey key);
+ }
+
+ public class IdentityMap<TKey, TValue> : IIdentityMap<TKey, TValue>
+ {
+ readonly IDictionary<TKey, TValue> itemsInMap;
+
+ public IdentityMap() : this(new Dictionary<TKey, TValue>())
+ {
+ }
+
+ public IdentityMap(IDictionary<TKey, TValue> itemsInMap)
+ {
+ this.itemsInMap = itemsInMap;
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ itemsInMap.Add(key, value);
+ }
+
+ public void UpdateTheItemFor(TKey key, TValue newValue)
+ {
+ if (ContainsAnItemFor(key))
+ {
+ itemsInMap[key] = newValue;
+ }
+ else
+ {
+ Add(key, newValue);
+ }
+ }
+
+ public bool ContainsAnItemFor(TKey key)
+ {
+ return itemsInMap.ContainsKey(key);
+ }
+
+ public TValue ItemThatBelongsTo(TKey key)
+ {
+ return ContainsAnItemFor(key) ? itemsInMap[key] : default(TValue);
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/Container/Windsor/WindsorContainerFactory.cs
@@ -1,3 +1,4 @@
+using System;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using MoMoney.Infrastructure.Container.Windsor.configuration;
@@ -51,11 +52,31 @@ namespace MoMoney.Infrastructure.Container.Windsor
.Pick()
.FromAssembly(GetType().Assembly)
.WithService
- .FirstInterface()
+ .LastInterface()
+ //.FirstInterface()
.Unless(criteria_to_satisfy.is_satisfied_by)
.Configure(x => configuration.configure(x))
);
return the_container;
}
}
+
+ public static class e
+ {
+ public static BasedOnDescriptor LastInterface(this ServiceDescriptor descriptor)
+ {
+ return descriptor.Select(delegate(Type type, Type baseType)
+ {
+ Type first = null;
+ var interfaces = type.GetInterfaces();
+
+ if (interfaces.Length > 0)
+ {
+ first = interfaces[0];
+ }
+
+ return (first != null) ? new Type[] {first} : null;
+ });
+ }
+ }
}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/Container/Windsor/WindsorDependencyRegistry.cs
@@ -37,7 +37,7 @@ namespace MoMoney.Infrastructure.Container.Windsor
implementation_type);
}
- public void register_instance_of<Interface>(Interface instanceOfTheInterface)
+ public void singleton<Interface>(Interface instanceOfTheInterface)
{
underlying_container.Kernel.AddComponentInstance<Interface>(instanceOfTheInterface);
}
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/IMethodCallTracker.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using Castle.Core.Interceptor;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+ public interface IMethodCallTracker<TypeToProxy> : IInterceptor
+ {
+ TypeToProxy target { get; }
+ IEnumerable<string> methods_to_intercept();
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/MethodCallTracker.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using Castle.Core.Interceptor;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+ public class MethodCallTracker<TypeToProxy> : IMethodCallTracker<TypeToProxy>
+ {
+ readonly IList<string> the_name_of_each_method_to_intercept;
+
+ public MethodCallTracker() : this(new List<string>())
+ {
+ }
+
+ public MethodCallTracker(IList<string> the_name_of_each_method_to_intercept)
+ {
+ this.the_name_of_each_method_to_intercept = the_name_of_each_method_to_intercept;
+ }
+
+ public TypeToProxy target { get; set; }
+
+ public void Intercept(IInvocation invocation)
+ {
+ set_return_value_for(invocation);
+ if (the_name_of_each_method_to_intercept.Contains(invocation.Method.Name))
+ {
+ return;
+ }
+ the_name_of_each_method_to_intercept.Add(invocation.Method.Name);
+ }
+
+ public IEnumerable<string> methods_to_intercept()
+ {
+ return the_name_of_each_method_to_intercept;
+ }
+
+ static void set_return_value_for(IInvocation invocation)
+ {
+ var return_type = invocation.Method.ReturnType;
+ if (return_type == typeof (void)) return;
+ invocation.ReturnValue = (return_type.IsValueType ? Activator.CreateInstance(return_type) : null);
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/MethodCallTrackerSpecs.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Linq;
+using Castle.Core.Interceptor;
+using jpboodhoo.bdd.contexts;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+ [Concern(typeof (MethodCallTracker<IAnInterface>))]
+ public class behaves_like_method_call_tracker :
+ concerns_for<IMethodCallTracker<IAnInterface>, MethodCallTracker<IAnInterface>>
+ {
+ public override IMethodCallTracker<IAnInterface> create_sut()
+ {
+ return new MethodCallTracker<IAnInterface>();
+ }
+ }
+
+ public class when_tracking_the_calls_to_intercept_on_a_type : behaves_like_method_call_tracker
+ {
+ static IInvocation invocation;
+ static IEnumerable<string> result;
+
+ context c = () =>
+ {
+ invocation = an<IInvocation>();
+ invocation
+ .is_told_to(s => s.Method)
+ .it_will_return(typeof (IAnInterface).GetMethod("ValueReturningMethodWithAnArgument"));
+ };
+
+ because b = () =>
+ {
+ sut.Intercept(invocation);
+ result = sut.methods_to_intercept();
+ };
+
+ it should_return_all_the_methods_that_are_specified_for_interception =
+ () => result.should_contain(typeof (IAnInterface).GetMethod("ValueReturningMethodWithAnArgument").Name);
+
+ it should_only_contain_the_methods_specified_for_interception = () => result.Count().should_be_equal_to(1);
+
+ it should_specify_the_default_return_value_for_the_method_to_intercept =
+ //() => invocation.was_told_to(i => i.ReturnValue = 0);
+ () => invocation.ReturnValue.should_be_equal_to(0);
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/RunOnBackgroundThreadInterceptor.cs
@@ -0,0 +1,25 @@
+using Castle.Core.Interceptor;
+using MoMoney.Infrastructure.Threading;
+using MoMoney.Utility.Core;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+ public class RunOnBackgroundThreadInterceptor<CommandToExecute> : IInterceptor
+ where CommandToExecute : IDisposableCommand
+ {
+ readonly IBackgroundThreadFactory thread_factory;
+
+ public RunOnBackgroundThreadInterceptor(IBackgroundThreadFactory thread_factory)
+ {
+ this.thread_factory = thread_factory;
+ }
+
+ public virtual void Intercept(IInvocation invocation)
+ {
+ using (thread_factory.create_for<CommandToExecute>())
+ {
+ invocation.Proceed();
+ }
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/RunOnBackgrounThreadInterceptorSpecs.cs
@@ -0,0 +1,46 @@
+using Castle.Core.Interceptor;
+using jpboodhoo.bdd.contexts;
+using MoMoney.Infrastructure.Threading;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+using MoMoney.Utility.Core;
+
+namespace MoMoney.Infrastructure.proxies.Interceptors
+{
+ [Concern(typeof (RunOnBackgroundThreadInterceptor<>))]
+ public class behaves_like_background_thread_interceptor :
+ concerns_for<IInterceptor, RunOnBackgroundThreadInterceptor<IDisposableCommand>>
+ {
+ context c = () => { thread_factory = the_dependency<IBackgroundThreadFactory>(); };
+
+ protected static IBackgroundThreadFactory thread_factory;
+ }
+
+ public class when_intercepting_a_call_to_a_method_that_takes_a_long_time_to_complete :
+ behaves_like_background_thread_interceptor
+ {
+ static IInvocation invocation;
+ static IBackgroundThread background_thread;
+
+ context c = () =>
+ {
+ invocation = an<IInvocation>();
+ background_thread = an<IBackgroundThread>();
+ thread_factory
+ .is_told_to(f => f.create_for<IDisposableCommand>())
+ .it_will_return(background_thread);
+ };
+
+ because b = () => sut.Intercept(invocation);
+
+ it should_display_a_progress_bar_on_a_background_thread =
+ () => thread_factory.was_told_to(f => f.create_for<IDisposableCommand>());
+
+ it should_proceed_with_the_orginal_invocation_on_the_actual_object =
+ () => invocation.was_told_to(i => i.Proceed());
+
+ it should_hide_the_progress_bar_when_the_invocation_is_completed =
+ () => background_thread.was_told_to(b => b.Dispose());
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/Interceptors/SelectiveInterceptor.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using Castle.Core.Interceptor;
+
+namespace Ec.AuditTool.Infrastructure.Proxies.Interceptors
+{
+ public class SelectiveInterceptor : IInterceptor
+ {
+ private readonly IList<string> methods_to_intercept;
+ private readonly IInterceptor underlying_interceptor;
+
+ public SelectiveInterceptor(IEnumerable<string> methods_to_intercept, IInterceptor underlying_interceptor)
+ {
+ this.methods_to_intercept = new List<string>(methods_to_intercept);
+ this.underlying_interceptor = underlying_interceptor;
+ }
+
+ public void Intercept(IInvocation invocation)
+ {
+ if (methods_to_intercept.Contains(invocation.Method.Name))
+ {
+ underlying_interceptor.Intercept(invocation);
+ return;
+ }
+ invocation.Proceed();
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IConstraintSelector.cs
@@ -0,0 +1,7 @@
+namespace MoMoney.Infrastructure.proxies
+{
+ public interface IConstraintSelector<TypeToPutConstraintOn>
+ {
+ TypeToPutConstraintOn InterceptOn { get; }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IInterceptorConstraint.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using MoMoney.Infrastructure.proxies.Interceptors;
+
+namespace MoMoney.Infrastructure.proxies
+{
+ public interface IInterceptorConstraint<TypeToPutConstraintOn> : IConstraintSelector<TypeToPutConstraintOn>
+ {
+ IEnumerable<string> methods_to_intercept();
+ }
+
+ public class InterceptorConstraint<TypeToPutConstraintOn> : IInterceptorConstraint<TypeToPutConstraintOn>
+ {
+ private readonly IMethodCallTracker<TypeToPutConstraintOn> call_tracker;
+
+ public InterceptorConstraint(IMethodCallTracker<TypeToPutConstraintOn> call_tracker)
+ {
+ this.call_tracker = call_tracker;
+ }
+
+ public TypeToPutConstraintOn InterceptOn
+ {
+ get { return call_tracker.target; }
+ }
+
+ public IEnumerable<string> methods_to_intercept()
+ {
+ return call_tracker.methods_to_intercept();
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IInterceptorConstraintFactory.cs
@@ -0,0 +1,26 @@
+namespace MoMoney.Infrastructure.proxies
+{
+ public interface IInterceptorConstraintFactory
+ {
+ IInterceptorConstraint<Type> CreateFor<Type>();
+ }
+
+ public class InterceptorConstraintFactory : IInterceptorConstraintFactory
+ {
+ readonly IMethodCallTrackerFactory factory;
+
+ public InterceptorConstraintFactory() : this(new MethodCallTrackerFactory())
+ {
+ }
+
+ public InterceptorConstraintFactory(IMethodCallTrackerFactory factory)
+ {
+ this.factory = factory;
+ }
+
+ public IInterceptorConstraint<Type> CreateFor<Type>()
+ {
+ return new InterceptorConstraint<Type>(factory.create_for<Type>());
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IMethodCallTrackerFactory.cs
@@ -0,0 +1,32 @@
+using Castle.DynamicProxy;
+using MoMoney.Infrastructure.proxies.Interceptors;
+
+namespace MoMoney.Infrastructure.proxies
+{
+ public interface IMethodCallTrackerFactory
+ {
+ IMethodCallTracker<TypeToProxy> create_for<TypeToProxy>();
+ }
+
+ public class MethodCallTrackerFactory : IMethodCallTrackerFactory
+ {
+ private readonly ProxyGenerator generator;
+
+ public MethodCallTrackerFactory() : this(new ProxyGenerator())
+ {
+ }
+
+ public MethodCallTrackerFactory(ProxyGenerator generator)
+ {
+ this.generator = generator;
+ }
+
+ public IMethodCallTracker<TypeToProxy> create_for<TypeToProxy>()
+ {
+ var call_tracker_interceptor = new MethodCallTracker<TypeToProxy>();
+ var target = generator.CreateInterfaceProxyWithoutTarget<TypeToProxy>(call_tracker_interceptor);
+ call_tracker_interceptor.target = target;
+ return call_tracker_interceptor;
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/InterceptorConstraintFactorySpecs.cs
@@ -0,0 +1,29 @@
+using jpboodhoo.bdd.contexts;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.proxies
+{
+ [Concern(typeof (InterceptorConstraintFactory))]
+ public class behaves_like_constraint_factory :
+ concerns_for<IInterceptorConstraintFactory, InterceptorConstraintFactory>
+ {
+ context c = () => { method_call_tracker_factory = the_dependency<IMethodCallTrackerFactory>(); };
+
+ protected static IMethodCallTrackerFactory method_call_tracker_factory;
+ }
+
+ public class when_creating_a_constraint_for_a_type_to_intercept_on : behaves_like_constraint_factory
+ {
+ static IInterceptorConstraint<string> result;
+
+ it should_create_a_method_call_tracker_for_the_type_to_place_a_constraint_on =
+ () => method_call_tracker_factory.was_told_to(f => f.create_for<string>());
+
+ it should_return_an_instance_of_an_interceptor_constraint =
+ () => result.should_be_an_instance_of<InterceptorConstraint<string>>();
+
+ because b = () => { result = sut.CreateFor<string>(); };
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/InterceptorConstraintSpecs.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using jpboodhoo.bdd.contexts;
+using MoMoney.Infrastructure.proxies.Interceptors;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.proxies
+{
+ [Concern(typeof (InterceptorConstraint<string>))]
+ public class behaves_like_constraint : concerns_for<IInterceptorConstraint<string>, InterceptorConstraint<string>>
+ {
+ context c = () => { method_call_tracker = the_dependency<IMethodCallTracker<string>>(); };
+
+ protected static IMethodCallTracker<string> method_call_tracker;
+ }
+
+ public class when_asking_for_all_the_methods_to_intercept : behaves_like_constraint
+ {
+ static IEnumerable<string> result;
+ static IList<string> methods_to_intercept;
+
+ it should_return_all_the_recorded_method_calls_from_the_call_tracker =
+ () => result.should_be_equal_to(methods_to_intercept);
+
+ context c = () =>
+ {
+ methods_to_intercept = new List<string>();
+ method_call_tracker
+ .is_told_to(t => t.methods_to_intercept())
+ .it_will_return(methods_to_intercept);
+ };
+
+ because b = () => { result = sut.methods_to_intercept(); };
+ }
+
+ [Concern(typeof (InterceptorConstraint<int>))]
+ public class when_asking_for_the_target_of_the_interception_constrain :
+ concerns_for<IInterceptorConstraint<int>, InterceptorConstraint<int>>
+ {
+ static IMethodCallTracker<int> method_call_tracker;
+ static int result;
+ const int target_of_interception = 7;
+
+ context c = () =>
+ {
+ method_call_tracker = the_dependency<IMethodCallTracker<int>>();
+ method_call_tracker.is_told_to(t => t.target).it_will_return(target_of_interception);
+ };
+
+ because b = () => { result = sut.InterceptOn; };
+
+ it should_return_the_target_of_the_method_call_tracker =
+ () => result.should_be_equal_to(target_of_interception);
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/IProxyFactory.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using System.Linq;
+using Castle.Core.Interceptor;
+using Castle.DynamicProxy;
+
+namespace Ec.AuditTool.Infrastructure.Proxies
+{
+ public interface IProxyFactory
+ {
+ TypeToProxy CreateProxyFor<TypeToProxy>(TypeToProxy implementation, IEnumerable<IInterceptor> interceptors);
+ }
+
+ public class ProxyFactory : IProxyFactory
+ {
+ private readonly ProxyGenerator generator;
+
+ public ProxyFactory() : this(new ProxyGenerator())
+ {
+ }
+
+ public ProxyFactory(ProxyGenerator generator)
+ {
+ this.generator = generator;
+ }
+
+ public TypeToProxy CreateProxyFor<TypeToProxy>(TypeToProxy implementation,
+ IEnumerable<IInterceptor> interceptors)
+ {
+ return generator
+ .CreateInterfaceProxyWithTarget<TypeToProxy>(implementation, interceptors.ToArray());
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/ProxyBuilder.cs
@@ -0,0 +1,63 @@
+using System.Collections.Generic;
+using System.Linq;
+using Castle.Core.Interceptor;
+using Ec.AuditTool.Infrastructure.Proxies;
+using Ec.AuditTool.Infrastructure.Proxies.Interceptors;
+
+namespace MoMoney.Infrastructure.proxies
+{
+ public interface IProxyBuilder<TypeToProxy>
+ {
+ IConstraintSelector<TypeToProxy> AddInterceptor<Interceptor>() where Interceptor : IInterceptor, new();
+ TypeToProxy CreateProxyFor(TypeToProxy an_implementation_of_the_interface);
+ }
+
+ public class ProxyBuilder<TypeToProxy> : IProxyBuilder<TypeToProxy>
+ {
+ readonly IDictionary<IInterceptor, IInterceptorConstraint<TypeToProxy>> constraints;
+ readonly IProxyFactory proxy_factory;
+ readonly IInterceptorConstraintFactory constraint_factory;
+
+ public ProxyBuilder() : this(new ProxyFactory(), new InterceptorConstraintFactory())
+ {
+ }
+
+ public ProxyBuilder(IProxyFactory proxy_factory, IInterceptorConstraintFactory constraint_factory)
+ {
+ this.proxy_factory = proxy_factory;
+ this.constraint_factory = constraint_factory;
+ constraints = new Dictionary<IInterceptor, IInterceptorConstraint<TypeToProxy>>();
+ }
+
+
+ public IConstraintSelector<TypeToProxy> AddInterceptor<Interceptor>() where Interceptor : IInterceptor, new()
+ {
+ var constraint = constraint_factory.CreateFor<TypeToProxy>();
+ constraints.Add(new Interceptor(), constraint);
+ return constraint;
+ }
+
+ public TypeToProxy CreateProxyFor(TypeToProxy an_implementation_of_the_interface)
+ {
+ return proxy_factory.CreateProxyFor(an_implementation_of_the_interface,
+ AllInterceptorsWithTheirConstraints());
+ }
+
+ IEnumerable<IInterceptor> AllInterceptorsWithTheirConstraints()
+ {
+ foreach (var pair in constraints)
+ {
+ var constraint = pair.Value;
+ var interceptor = pair.Key;
+ if (constraint.methods_to_intercept().Count() > 0)
+ {
+ yield return new SelectiveInterceptor(constraint.methods_to_intercept(), interceptor);
+ }
+ else
+ {
+ yield return interceptor;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Infrastructure/proxies/ProxyBuilderSpecs.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Castle.Core.Interceptor;
+using Ec.AuditTool.Infrastructure.Proxies;
+using jpboodhoo.bdd.contexts;
+using MoMoney.Testing.MetaData;
+using MoMoney.Testing.spechelpers.contexts;
+using MoMoney.Testing.spechelpers.core;
+
+namespace MoMoney.Infrastructure.proxies
+{
+ [Concern(typeof (ProxyBuilder<IAnInterface>))]
+ public class behaves_like_proxy_builder : concerns_for<IProxyBuilder<IAnInterface>, ProxyBuilder<IAnInterface>>
+ {
+ public override IProxyBuilder<IAnInterface> create_sut()
+ {
+ return new ProxyBuilder<IAnInterface>();
+ }
+ }
+
+ public class when_building_a_proxy_for_a_type : behaves_like_proxy_builder
+ {
+ it should_make_sure_the_original_call_gets_forwarded_to_the_item_to_proxy =
+ () =>
+ {
+ an_implementation_of_the_interface.was_told_to(i => i.OneMethod());
+ an_implementation_of_the_interface.was_told_to(i => i.SecondMethod());
+ };
+
+ it should_allow_each_intercepter_to_intercept_the_call =
+ () =>
+ {
+ SomeInterceptor.MethodsCalled.Count().should_be_equal_to(2);
+ AnotherInterceptor.MethodsCalled.Count().should_be_equal_to(2);
+ };
+
+ context c = () => { an_implementation_of_the_interface = an<IAnInterface>(); };
+
+ because b = () =>
+ {
+ sut.AddInterceptor<SomeInterceptor>();
+ sut.AddInterceptor<AnotherInterceptor>();
+ var proxy = sut.CreateProxyFor(an_implementation_of_the_interface);
+ proxy.OneMethod();
+ proxy.SecondMethod();
+ };
+
+ after_each_observation ae = () =>
+ {
+ SomeInterceptor.Cleanup();
+ AnotherInterceptor.Cleanup();
+ };
+
+ static IAnInterface an_implementation_of_the_interface;
+ }
+
+ [Integration]
+ public class when_building_a_proxy_to_target_certain_methods_on_a_type : behaves_like_proxy_builder
+ {
+ it should_only_intercept_calls_on_the_method_that_was_specified =
+ () =>
+ {
+ SomeInterceptor.MethodsCalled.Count().should_be_equal_to(1);
+ SomeInterceptor.MethodsCalled.First().Name.should_be_equal_to("OneMethod");
+ };
+
+ context c = () => { an_implementation = an<IAnInterface>(); };
+
+ because b = () =>
+ {
+ var constraint = sut.AddInterceptor<SomeInterceptor>();
+ constraint.InterceptOn.OneMethod();
+
+ var proxy = sut.CreateProxyFor(an_implementation);
+ proxy.OneMethod();
+ proxy.SecondMethod();
+ };
+
+ after_each_observation ae = () =>
+ {
+ SomeInterceptor.Cleanup();
+ AnotherInterceptor.Cleanup();
+ };
+
+ static IAnInterface an_implementation;
+ }
+
+ public interface IAnInterface
+ {
+ string GetterAndSetterProperty { get; set; }
+ void OneMethod();
+ void SecondMethod();
+ int FirstValueReturningMethod();
+ int ValueReturningMethodWithAnArgument(int number);
+ }
+
+ public class SomeInterceptor : IInterceptor
+ {
+ public static bool WasCalled;
+ public static IList<MethodInfo> MethodsCalled;
+
+ static SomeInterceptor()
+ {
+ MethodsCalled = new List<MethodInfo>();
+ }
+
+ public void Intercept(IInvocation invocation)
+ {
+ WasCalled = true;
+ MethodsCalled.Add(invocation.Method);
+ invocation.Proceed();
+ }
+
+ public static void Cleanup()
+ {
+ WasCalled = false;
+ MethodsCalled.Clear();
+ }
+ }
+
+ public class AnotherInterceptor : IInterceptor
+ {
+ public static bool WasCalled;
+ public static IList<MethodInfo> MethodsCalled;
+
+ static AnotherInterceptor()
+ {
+ MethodsCalled = new List<MethodInfo>();
+ }
+
+ public void Intercept(IInvocation invocation)
+ {
+ WasCalled = true;
+ MethodsCalled.Add(invocation.Method);
+ invocation.Proceed();
+ }
+
+ public static void Cleanup()
+ {
+ WasCalled = false;
+ MethodsCalled.Clear();
+ }
+ }
+
+ public class SomeImplementation : IAnInterface
+ {
+ public string GetterAndSetterProperty { get; set; }
+
+ public void OneMethod()
+ {
+ }
+
+ public void SecondMethod()
+ {
+ }
+
+ public int FirstValueReturningMethod()
+ {
+ return 1;
+ }
+
+ public int ValueReturningMethodWithAnArgument(int number)
+ {
+ return number + 1;
+ }
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/Testing/MetaData/IntegrationAttribute.cs
@@ -0,0 +1,8 @@
+using System.ComponentModel;
+
+namespace MoMoney.Testing.MetaData
+{
+ public class IntegrationAttribute : CategoryAttribute
+ {
+ }
+}
\ No newline at end of file
trunk/product/MyMoney/MyMoney.csproj
@@ -189,6 +189,8 @@
<Compile Include="Domain\Core\range.cs" />
<Compile Include="Domain\Core\range_specs.cs" />
<Compile Include="Domain\repositories\company_repository_extensions.cs" />
+ <Compile Include="Infrastructure\caching\IdentityMapSpecs.cs" />
+ <Compile Include="Infrastructure\caching\IIdentityMap.cs" />
<Compile Include="Infrastructure\cloning\BinarySerializer.cs" />
<Compile Include="Infrastructure\cloning\BinarySerializerSpecs.cs" />
<Compile Include="Infrastructure\cloning\ISerializer.cs" />
@@ -219,6 +221,21 @@
<Compile Include="Infrastructure\interceptors\UnitOfWorkInterceptor.cs" />
<Compile Include="Infrastructure\interceptors\UnitOfWorkInterceptorSpecs.cs" />
<Compile Include="Infrastructure\Logging\ILoggable.cs" />
+ <Compile Include="Infrastructure\proxies\IConstraintSelector.cs" />
+ <Compile Include="Infrastructure\proxies\IInterceptorConstraint.cs" />
+ <Compile Include="Infrastructure\proxies\IInterceptorConstraintFactory.cs" />
+ <Compile Include="Infrastructure\proxies\IMethodCallTrackerFactory.cs" />
+ <Compile Include="Infrastructure\proxies\InterceptorConstraintFactorySpecs.cs" />
+ <Compile Include="Infrastructure\proxies\InterceptorConstraintSpecs.cs" />
+ <Compile Include="Infrastructure\proxies\Interceptors\IMethodCallTracker.cs" />
+ <Compile Include="Infrastructure\proxies\Interceptors\MethodCallTracker.cs" />
+ <Compile Include="Infrastructure\proxies\Interceptors\MethodCallTrackerSpecs.cs" />
+ <Compile Include="Infrastructure\proxies\Interceptors\RunOnBackgroundThreadInterceptor.cs" />
+ <Compile Include="Infrastructure\proxies\Interceptors\RunOnBackgrounThreadInterceptorSpecs.cs" />
+ <Compile Include="Infrastructure\proxies\Interceptors\SelectiveInterceptor.cs" />
+ <Compile Include="Infrastructure\proxies\IProxyFactory.cs" />
+ <Compile Include="Infrastructure\proxies\ProxyBuilder.cs" />
+ <Compile Include="Infrastructure\proxies\ProxyBuilderSpecs.cs" />
<Compile Include="Infrastructure\registries\default_registry.cs" />
<Compile Include="Infrastructure\registries\default_registry_specs.cs" />
<Compile Include="Domain\accounting\billing\Bill.cs" />
@@ -473,6 +490,7 @@
<Compile Include="Tasks\infrastructure\FileSystemTasks.cs" />
<Compile Include="Tasks\application\IncomeTasks.cs" />
<Compile Include="Tasks\infrastructure\UpdateTasks.cs" />
+ <Compile Include="Testing\MetaData\IntegrationAttribute.cs" />
<Compile Include="Testing\spechelpers\contexts\concerns_for.cs" />
<Compile Include="Testing\spechelpers\contexts\behaves_like_a_repository.cs" />
<Compile Include="Testing\spechelpers\core\empty_fixture.cs" />