Skip to content

Commit

Permalink
Merge pull request NancyFx#953 from grumpydev/ScanningChanges
Browse files Browse the repository at this point in the history
Scanning changes
  • Loading branch information
thecodejunkie committed Feb 19, 2013
2 parents 176359e + 75a5e0e commit b41515f
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 135 deletions.
8 changes: 6 additions & 2 deletions src/Nancy.Testing.Tests/ConfigurableBootstrapperFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,18 @@ public void Should_throw_exceptions_if_any_occur_in_route()
public void Should_run_application_startup_closure()
{
var date = new DateTime(2112,10,31);
var bootstrapper = new ConfigurableBootstrapper(with => with.ApplicationStartup((container, pipelines) =>

var bootstrapper = new ConfigurableBootstrapper(with =>
{
with.ApplicationStartup((container, pipelines) =>
{
pipelines.BeforeRequest += ctx =>
{
ctx.Items.Add("date", date);
return null;
};
}));
});
});

bootstrapper.Initialise();

Expand Down
40 changes: 26 additions & 14 deletions src/Nancy.Testing/ConfigurableBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class ConfigurableBootstrapper : NancyBootstrapperWithRequestContainerBas
/// </summary>
public static IList<string> TestAssemblySuffixes = new[] { "test", "tests", "unittests", "specs", "specifications" };

private bool allDiscoveredModules;

/// <summary>
/// Initializes a new instance of the <see cref="ConfigurableBootstrapper"/> class.
/// </summary>
Expand Down Expand Up @@ -223,7 +225,12 @@ protected override IEnumerable<ModuleRegistration> Modules
var moduleRegistrations =
this.GetModuleRegistrations().ToList();

return (moduleRegistrations.Any()) ? moduleRegistrations : base.Modules;
if (moduleRegistrations.Any())
{
return moduleRegistrations;
}

return this.allDiscoveredModules ? base.Modules : new ModuleRegistration[] { };
}
}

Expand Down Expand Up @@ -332,7 +339,16 @@ protected override TinyIoCContainer GetApplicationContainer()
/// <returns>INancyEngine implementation</returns>
protected override INancyEngine GetEngineInternal()
{
return this.ApplicationContainer.Resolve<INancyEngine>();
try
{
return this.ApplicationContainer.Resolve<INancyEngine>();
}
catch (InvalidOperationException ex)
{
throw new InvalidOperationException(
"Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and specified either a module to test, or set AllDiscoveredModules in the ConfigurableBootstrapper. Inspect the innerexception for more details.",
ex.InnerException);
}
}

/// <summary>
Expand Down Expand Up @@ -536,6 +552,13 @@ public ConfigurableBootstrapperConfigurator(ConfigurableBootstrapper bootstrappe
this.Diagnostics<DisabledDiagnostics>();
}

public ConfigurableBootstrapperConfigurator AllDiscoveredModules()
{
this.bootstrapper.allDiscoveredModules = true;

return this;
}

public ConfigurableBootstrapperConfigurator Binder(IBinder binder)
{
this.bootstrapper.registeredInstances.Add(
Expand Down Expand Up @@ -1620,17 +1643,6 @@ public ConfigurableBootstrapperConfigurator TrieNodeFactory<T>() where T : ITrie
return this;
}

/// <summary>
/// Configures the bootstrapper to add an assembly ignore predicate to the list
/// </summary>
/// <param name="ignoredPredicate">Ignore predicate</param>
/// <returns>A reference to the current <see cref="ConfigurableBootstrapperConfigurator"/>.</returns>
public ConfigurableBootstrapperConfigurator IgnoredAssembly(Func<Assembly, bool> ignoredPredicate)
{
this.bootstrapper.configuration.WithIgnoredAssembly(ignoredPredicate);
return this;
}

public ConfigurableBootstrapperConfigurator ApplicationStartup(Action<TinyIoCContainer, IPipelines> action)
{
this.bootstrapper.applicationStartupActions.Add(action);
Expand Down Expand Up @@ -1691,4 +1703,4 @@ public void RegisterModuleInstance(INancyModule module, string moduleKey)
}
}
}
}
}
10 changes: 8 additions & 2 deletions src/Nancy.Tests/Fakes/FakeDefaultNancyBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@ public FakeDefaultNancyBootstrapper()

}

protected override IEnumerable<Func<Assembly, bool>> AutoRegisterIgnoredAssemblies
{
get
{
return base.AutoRegisterIgnoredAssemblies.Union(new Func<Assembly, bool>[] { asm => asm.FullName.StartsWith("TestAssembly") });
}
}
public FakeDefaultNancyBootstrapper(NancyInternalConfiguration configuration)
{
this.configuration = configuration.WithIgnoredAssembly(asm => asm.FullName.StartsWith("TestAssembly"));

this.configuration = configuration;
this.RequestContainerInitialisations = new Dictionary<NancyContext, int>();
}

Expand Down
15 changes: 0 additions & 15 deletions src/Nancy.Tests/Unit/Bootstrapper/NancyBootstrapperBaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,21 +217,6 @@ public void Should_register_application_registration_instance_registrations_into
this.bootstrapper.InstanceRegistrations.ShouldBeSameAs(instanceRegistrations);
}

[Fact]
public void Should_ingore_assemblies_specified_in_AppDomainAssemblyTypeScanner()
{
// Given
// When
AppDomainAssemblyTypeScanner.IgnoredAssemblies =
new Func<Assembly, bool>[]
{
asm => asm.FullName.StartsWith("mscorlib")
};

// Then
AppDomainAssemblyTypeScanner.TypesOf<IEnumerable>().Where(t => t.Assembly.FullName.StartsWith("mscorlib")).Count().ShouldEqual(0);
}

[Fact]
public void Should_allow_favicon_override()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,5 @@ public void Should_not_be_valid_if_any_types_null()

result.ShouldBeFalse();
}

[Fact]
public void Should_allow_additional_ignored_assemblies()
{
Func<Assembly, bool> predicate = asm => asm.FullName.StartsWith("moo");
var config = NancyInternalConfiguration.Default.WithIgnoredAssembly(predicate);

var result = config.IgnoredAssemblies;

result.Any(p => p.Equals(predicate)).ShouldBeTrue();
}

[Fact]
public void Should_append_ignored_assembly_to_default()
{
Func<Assembly, bool> predicate = asm => asm.FullName.StartsWith("moo");
var config = NancyInternalConfiguration.Default.WithIgnoredAssembly(predicate);

var result = config.IgnoredAssemblies.Count();

result.ShouldEqual(NancyInternalConfiguration.DefaultIgnoredAssemblies.Count() + 1);
}
}
}
20 changes: 17 additions & 3 deletions src/Nancy.ViewEngines.Razor.Tests/RazorViewEngineFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;

using FakeItEasy;
using Xunit;

using Nancy.Bootstrapper;
using Nancy.Tests;
using Nancy.ViewEngines.Razor.Tests.Models;
using System.Threading;
using Nancy.Localization;

using Xunit;

public class RazorViewEngineFixture
{
Expand Down Expand Up @@ -156,6 +160,8 @@ public void RenderView_csharp_should_use_model_directive_for_strongly_typed_view
[Fact]
public void RenderView_csharp_should_be_able_to_use_a_model_from_another_assembly()
{
AppDomainAssemblyTypeScanner.AddAssembliesToScan("Nancy.ViewEngines.Razor.Tests.Models.dll");

// Given
var view = new StringBuilder()
.AppendLine("@model Nancy.ViewEngines.Razor.Tests.Models.Person")
Expand Down Expand Up @@ -183,6 +189,8 @@ public void RenderView_csharp_should_be_able_to_use_a_model_from_another_assembl
[Fact]
public void RenderView_csharp_should_be_able_to_use_a_using_statement()
{
AppDomainAssemblyTypeScanner.AddAssembliesToScan("Nancy.ViewEngines.Razor.Tests.Models");

// Given
var view = new StringBuilder()
.AppendLine("@model Nancy.ViewEngines.Razor.Tests.Models.Person")
Expand Down Expand Up @@ -213,6 +221,12 @@ public void RenderView_csharp_should_be_able_to_use_a_using_statement()
public void RenderView_csharp_should_be_able_to_find_the_model_when_a_null_model_is_passed()
{
// Given
AppDomainAssemblyTypeScanner.AssembliesToScan =
AppDomainAssemblyTypeScanner.DefaultAssembliesToScan.Union(new Func<Assembly, bool>[]
{
x =>
x.GetName().Name.StartsWith("Nancy")
});
var view = new StringBuilder()
.AppendLine("@model Nancy.ViewEngines.Razor.Tests.Models.Person")
.AppendLine(@"@{ var hobby = new Hobby { Name = ""Music"" }; }")
Expand Down
92 changes: 84 additions & 8 deletions src/Nancy/Bootstrapper/AppDomainAssemblyTypeScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,35 @@ static AppDomainAssemblyTypeScanner()
/// </summary>
private static bool nancyAssembliesLoaded;

private static IEnumerable<Func<Assembly, bool>> assembliesToScan;

/// <summary>
///
/// The default assemblies for scanning.
/// Includes the nancy assembly and anythign referencing a nancy assembly
/// </summary>
private static IEnumerable<Func<Assembly, bool>> ignoredAssemblies;
public static Func<Assembly, bool>[] DefaultAssembliesToScan = new Func<Assembly, bool>[]
{
x => x == nancyAssembly,
x => x.GetReferencedAssemblies().Any(r => r.Name.StartsWith("Nancy", StringComparison.OrdinalIgnoreCase))
};

/// <summary>
/// Gets or sets a set of rules for ignoring assemblies while scanning through them.
/// Gets or sets a set of rules for which assemblies are scanned
/// Defaults to just assemblies that have references to nancy, and nancy
/// itself.
/// Each item in the enumerable is a delegate that takes the assembly and
/// returns true if it is to be included. Returning false doesn't mean it won't
/// be included as a true from another delegate will take precedence.
/// </summary>
public static IEnumerable<Func<Assembly, bool>> IgnoredAssemblies
public static IEnumerable<Func<Assembly, bool>> AssembliesToScan
{
private get
{
return ignoredAssemblies;
return assembliesToScan ?? (assembliesToScan = DefaultAssembliesToScan);
}
set
{
ignoredAssemblies = value;
assembliesToScan = value;
UpdateTypes ();
}
}
Expand All @@ -81,6 +93,54 @@ public static IEnumerable<Assembly> Assemblies
}
}

/// <summary>
/// Add assemblies to the list of assemblies to scan for Nancy types
/// </summary>
/// <param name="assemblyNames">One or more assembly names</param>
public static void AddAssembliesToScan(params string[] assemblyNames)
{
var normalisedNames = GetNormalisedAssemblyNames(assemblyNames).ToArray();

foreach (var assemblyName in normalisedNames)
{
LoadAssemblies(assemblyName + ".dll");
LoadAssemblies(assemblyName + ".exe");
}

var scanningPredicates = normalisedNames.Select(s =>
{
return (Func<Assembly, bool>)(a => a.GetName().Name == s);
});

AssembliesToScan = AssembliesToScan.Union(scanningPredicates);
}

/// <summary>
/// Add assemblies to the list of assemblies to scan for Nancy types
/// </summary>
/// <param name="assemblies">One of more assemblies</param>
public static void AddAssembliesToScan(params Assembly[] assemblies)
{
foreach (var assembly in assemblies)
{
LoadAssemblies(assembly.GetName() + ".dll");
LoadAssemblies(assembly.GetName() + ".exe");
}

var scanningPredicates = assemblies.Select(an => (Func<Assembly, bool>)(a => a == an));

AssembliesToScan = AssembliesToScan.Union(scanningPredicates);
}

/// <summary>
/// Add predicates for determining which assemblies to scan for Nancy types
/// </summary>
/// <param name="predicates">One or more predicates</param>
public static void AddAssembliesToScan(params Func<Assembly, bool>[] predicates)
{
AssembliesToScan = AssembliesToScan.Union(predicates);
}

/// <summary>
/// Load assemblies from a the app domain base directory matching a given wildcard.
/// Assemblies will only be loaded if they aren't already in the appdomain.
Expand Down Expand Up @@ -108,7 +168,8 @@ public static void LoadAssemblies(string containingDirectory, string wildcardFil

var unloadedAssemblies =
Directory.GetFiles(containingDirectory, wildcardFilename).Where(
f => !existingAssemblyPaths.Contains(f, StringComparer.InvariantCultureIgnoreCase));
f => !existingAssemblyPaths.Contains(f, StringComparer.InvariantCultureIgnoreCase)).ToArray();


foreach (var unloadedAssembly in unloadedAssemblies)
{
Expand Down Expand Up @@ -138,7 +199,7 @@ from type in assembly.SafeGetExportedTypes()
private static void UpdateAssemblies()
{
assemblies = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
where IgnoredAssemblies != null ? !IgnoredAssemblies.Any(asm => asm(assembly)) : true
where AssembliesToScan.Any(asm => asm(assembly))
where !assembly.IsDynamic
where !assembly.ReflectionOnly
select assembly).ToArray();
Expand Down Expand Up @@ -255,6 +316,21 @@ private static IEnumerable<string> GetAssemblyDirectories()
yield return AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
}
}

private static IEnumerable<string> GetNormalisedAssemblyNames(string[] assemblyNames)
{
foreach (var assemblyName in assemblyNames)
{
if (assemblyName.EndsWith(".dll") || assemblyName.EndsWith(".exe"))
{
yield return Path.GetFileNameWithoutExtension(assemblyName);
}
else
{
yield return assemblyName;
}
}
}
}

public static class AppDomainAssemblyTypeScannerExtensions
Expand Down
1 change: 0 additions & 1 deletion src/Nancy/Bootstrapper/NancyBootstrapperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ protected virtual DiagnosticsConfiguration DiagnosticsConfiguration
/// </summary>
public void Initialise()
{
AppDomainAssemblyTypeScanner.IgnoredAssemblies = this.InternalConfiguration.IgnoredAssemblies;
AppDomainAssemblyTypeScanner.LoadNancyAssemblies();

if (this.InternalConfiguration == null)
Expand Down
Loading

0 comments on commit b41515f

Please sign in to comment.