From 7de6466588b09b3158dbadc329a97f0b666419bd Mon Sep 17 00:00:00 2001 From: bounav Date: Tue, 23 Jan 2024 11:53:13 +0000 Subject: [PATCH] Using the Microsoft.Extensions.DependencyInjection apis to resolved dependencies - Now using constructor injection instead of auto-initializing properties - Removed ISparkServiceInitialize and ISparkServiceContainer (and it's implementation) - New IBatchCompiler interface so that we can use different compilers - ~2x performance improvement when compiling views with Roslyn - CodeDom compilation can still be used at the moment (class marked as obsolete) - CastleMonoRail still using codedom (rosylin doesn't like the assembly name when compiling in that project) --- .../SparkBatchCompilerTester.cs | 11 +- .../SparkViewDataTests.cs | 4 +- ...parkViewFactoryStrictNullBehaviourTests.cs | 9 +- .../SparkViewFactoryTests.cs | 50 ++- .../ViewComponents/BaseViewComponentTests.cs | 11 +- .../ViewComponentRenderViewTests.cs | 3 +- .../Install/PrecompileInstaller.cs | 5 +- src/Castle.MonoRail.Views.Spark/SparkView.cs | 8 +- .../SparkViewFactory.cs | 55 ++- .../ViewComponentContext.cs | 7 +- src/Spark.JsTests/Generate.ashx.cs | 36 +- .../PythonViewCompilerTests.cs | 3 +- .../ScriptingLanguageFactoryTests.cs | 17 +- .../Compiler/PythonViewCompiler.cs | 5 +- src/Spark.Python/PythonLanguageFactory.cs | 4 + src/Spark.Ruby.Tests/RubyViewCompilerTests.cs | 10 +- src/Spark.Ruby/Compiler/RubyViewCompiler.cs | 8 +- src/Spark.Ruby/RubyLanguageFactory.cs | 3 + src/Spark.Tests/CompiledViewHolderTester.cs | 56 ++- src/Spark.Tests/Parser/ViewLoaderTester.cs | 133 +++++- .../PdfViewResultTests.cs | 27 +- .../PythonLanguageFactoryWithExtensions.cs | 5 + .../ServiceCollectionExtensions.cs | 25 ++ .../SparkPythonEngineStarter.cs | 134 ------ .../HtmlHelperExtensionsTester.cs | 28 +- .../RubyLanguageFactoryWithExtensions.cs | 6 + .../ServiceCollectionExtensions.cs | 25 ++ .../SparkRubyEngineStarter.cs | 133 ------ .../DescriptorBuildingTester.cs | 83 +++- .../SparkBatchCompilerTester.cs | 59 ++- .../SparkViewFactoryTester.cs | 147 +++++-- src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs | 36 +- .../Descriptors/DescriptorFilterExtensions.cs | 7 +- .../Extensions/ServiceCollectionExtensions.cs | 73 ++++ .../Install/PrecompileInstaller.cs | 17 +- src/Spark.Web.Mvc/LanguageKit.cs | 10 +- src/Spark.Web.Mvc/Spark.Web.Mvc.csproj | 3 +- src/Spark.Web.Mvc/SparkEngineStarter.cs | 137 ------ src/Spark.Web.Mvc/SparkViewFactory.cs | 173 +++----- src/Spark.Web.Tests/BatchCompilationTester.cs | 41 +- .../Bindings/BindingExecutionTester.cs | 20 +- .../Caching/CacheElementTester.cs | 22 +- .../ClientsideCompilerTester.cs | 29 +- .../Compiler/CSharpViewCompilerTester.cs | 79 ++-- .../Compiler/SourceMappingTester.cs | 24 +- .../Compiler/VisualBasicViewCompilerTester.cs | 46 +- .../SparkSectionHandlerTester.cs | 11 +- .../Extensions/ServiceCollectionExtensions.cs | 69 +++ .../FileSystem/InMemoryViewFolderTester.cs | 45 +- .../FileSystem/ViewFolderSettingsTester.cs | 37 +- src/Spark.Web.Tests/ImportAndIncludeTester.cs | 12 +- .../Parser/AutomaticEncodingTester.cs | 10 +- .../Parser/CSharpSyntaxProviderTester.cs | 34 +- src/Spark.Web.Tests/PartialProviderTester.cs | 39 +- src/Spark.Web.Tests/PrefixSupportTester.cs | 37 +- src/Spark.Web.Tests/SparkExtensionTester.cs | 14 +- .../SparkServiceContainerTester.cs | 162 ------- src/Spark.Web.Tests/SparkViewFactoryTester.cs | 31 +- src/Spark.Web.Tests/ViewActivatorTester.cs | 17 +- .../Visitors/DetectCodeExpressionTester.cs | 94 ++++- src/Spark.Web.Tests/VisualBasicViewTester.cs | 21 +- src/Spark.sln.DotSettings | 3 + src/Spark/Compiler/AssemblyExtensions.cs | 44 ++ src/Spark/Compiler/BatchCompilerException.cs | 14 - .../Compiler/CSharp/CSharpViewCompiler.cs | 83 ++-- .../CodeDomBatchCompiler.cs} | 395 ++++++++---------- .../CodeDom/CodeDomCompilerException.cs | 9 + src/Spark/Compiler/CompilerException.cs | 5 + src/Spark/Compiler/IBatchCompiler.cs | 18 + src/Spark/Compiler/Roslyn/CSharpLink.cs | 57 +++ .../Compiler/Roslyn/IRoslynCompilationLink.cs | 12 + .../Compiler/Roslyn/RoslynBatchCompiler.cs | 89 ++++ .../Roslyn/RoslynCompilerException.cs | 9 + src/Spark/Compiler/Roslyn/VisualBasicLink.cs | 57 +++ .../VisualBasic/VisualBasicViewCompiler.cs | 14 +- src/Spark/DefaultLanguageFactory.cs | 8 +- src/Spark/ISparkServiceContainer.cs | 25 -- src/Spark/ISparkServiceInitialize.cs | 26 -- src/Spark/ISparkViewEngine.cs | 11 +- src/Spark/Parser/ViewLoader.cs | 122 ++---- src/Spark/Spark.csproj | 2 + src/Spark/SparkServiceContainer.cs | 128 ------ src/Spark/SparkViewEngine.cs | 245 ++++------- src/Xpark/Program.cs | 48 ++- 84 files changed, 2005 insertions(+), 1879 deletions(-) create mode 100644 src/Spark.Web.Mvc.Python/ServiceCollectionExtensions.cs delete mode 100644 src/Spark.Web.Mvc.Python/SparkPythonEngineStarter.cs create mode 100644 src/Spark.Web.Mvc.Ruby/ServiceCollectionExtensions.cs delete mode 100644 src/Spark.Web.Mvc.Ruby/SparkRubyEngineStarter.cs create mode 100644 src/Spark.Web.Mvc/Extensions/ServiceCollectionExtensions.cs delete mode 100644 src/Spark.Web.Mvc/SparkEngineStarter.cs create mode 100644 src/Spark.Web.Tests/Extensions/ServiceCollectionExtensions.cs delete mode 100644 src/Spark.Web.Tests/SparkServiceContainerTester.cs create mode 100644 src/Spark.sln.DotSettings create mode 100644 src/Spark/Compiler/AssemblyExtensions.cs delete mode 100644 src/Spark/Compiler/BatchCompilerException.cs rename src/Spark/Compiler/{BatchCompiler.cs => CodeDom/CodeDomBatchCompiler.cs} (70%) create mode 100644 src/Spark/Compiler/CodeDom/CodeDomCompilerException.cs create mode 100644 src/Spark/Compiler/IBatchCompiler.cs create mode 100644 src/Spark/Compiler/Roslyn/CSharpLink.cs create mode 100644 src/Spark/Compiler/Roslyn/IRoslynCompilationLink.cs create mode 100644 src/Spark/Compiler/Roslyn/RoslynBatchCompiler.cs create mode 100644 src/Spark/Compiler/Roslyn/RoslynCompilerException.cs create mode 100644 src/Spark/Compiler/Roslyn/VisualBasicLink.cs delete mode 100644 src/Spark/ISparkServiceContainer.cs delete mode 100644 src/Spark/ISparkServiceInitialize.cs delete mode 100644 src/Spark/SparkServiceContainer.cs diff --git a/src/Castle.MonoRail.Views.Spark.Tests/SparkBatchCompilerTester.cs b/src/Castle.MonoRail.Views.Spark.Tests/SparkBatchCompilerTester.cs index 90936835..b0932118 100644 --- a/src/Castle.MonoRail.Views.Spark.Tests/SparkBatchCompilerTester.cs +++ b/src/Castle.MonoRail.Views.Spark.Tests/SparkBatchCompilerTester.cs @@ -35,11 +35,12 @@ public class SparkBatchCompilerTester [SetUp] public void Init() { - var settings = new SparkSettings(); + var settings = new SparkSettings() + .SetPageBaseType(typeof(SparkView)); var services = new StubMonoRailServices(); + services.AddService(typeof(ISparkSettings), settings); services.AddService(typeof(IViewSourceLoader), new FileAssemblyViewSourceLoader("MonoRail.Tests.Views")); - services.AddService(typeof(ISparkViewEngine), new SparkViewEngine(settings)); services.AddService(typeof(IControllerDescriptorProvider), services.ControllerDescriptorProvider); _factory = new SparkViewFactory(); _factory.Service(services); @@ -59,7 +60,7 @@ public void CompileBatchDescriptor() var assembly = _factory.Precompile(batch); Assert.IsNotNull(assembly); - Assert.AreEqual(3, assembly.GetTypes().Length); + Assert.AreEqual(3, assembly.GetTypes().Count(x => x.BaseType == typeof(SparkView))); } [Test] @@ -98,7 +99,7 @@ public void MultipleLayoutFiles() var assembly = _factory.Precompile(batch); Assert.IsNotNull(assembly); - Assert.AreEqual(4, assembly.GetTypes().Length); + Assert.AreEqual(4, assembly.GetTypes().Count(x => x.BaseType == typeof(SparkView))); } [Test] @@ -131,7 +132,7 @@ public void WildcardIncludeRules() var assembly = _factory.Precompile(batch); Assert.IsNotNull(assembly); - Assert.AreEqual(3, assembly.GetTypes().Length); + Assert.AreEqual(3, assembly.GetTypes().Count(x => x.BaseType == typeof(SparkView))); } [Test] diff --git a/src/Castle.MonoRail.Views.Spark.Tests/SparkViewDataTests.cs b/src/Castle.MonoRail.Views.Spark.Tests/SparkViewDataTests.cs index 77b8c423..9a06af17 100644 --- a/src/Castle.MonoRail.Views.Spark.Tests/SparkViewDataTests.cs +++ b/src/Castle.MonoRail.Views.Spark.Tests/SparkViewDataTests.cs @@ -46,7 +46,7 @@ public void PropertyBagAvailable() controllerContext.PropertyBag.Add("foo", "bar"); mocks.ReplayAll(); - view.Contextualize(engineContext, controllerContext, null, null); + view.Contextualize(engineContext, controllerContext, null, null, null); Assert.AreEqual("bar", view.ViewData["foo"]); } @@ -71,7 +71,7 @@ public void MergingCollectionsLikeVelocity() engineContext.Request.Params.Add("contextParamsKey", "contextParamsValue"); controllerContext.Resources.Add("controllerResourcesKey", resource); - view.Contextualize(engineContext, controllerContext, null, null); + view.Contextualize(engineContext, controllerContext, null, null, null); Assert.AreEqual("controllerPropertyBagValue", view.ViewData["controllerPropertyBagKey"]); Assert.AreEqual("contextFlashValue", view.ViewData["contextFlashKey"]); diff --git a/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryStrictNullBehaviourTests.cs b/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryStrictNullBehaviourTests.cs index 13f4c57f..1f75a9c4 100644 --- a/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryStrictNullBehaviourTests.cs +++ b/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryStrictNullBehaviourTests.cs @@ -27,10 +27,13 @@ public class SparkViewFactoryStrictNullBehaviourTests : SparkViewFactoryTestsBas { protected override void Configure() { - var settings = new SparkSettings(); + var settings = + new SparkSettings() + .SetPageBaseType(typeof(SparkView)); + settings.SetNullBehaviour(NullBehaviour.Strict); - var sparkViewEngine = new SparkViewEngine(settings); - serviceProvider.AddService(typeof(ISparkViewEngine), sparkViewEngine); + + serviceProvider.AddService(typeof(ISparkSettings), settings); factory = new SparkViewFactory(); factory.Service(serviceProvider); diff --git a/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryTests.cs b/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryTests.cs index e6bdc997..b969bcb3 100644 --- a/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryTests.cs +++ b/src/Castle.MonoRail.Views.Spark.Tests/SparkViewFactoryTests.cs @@ -27,21 +27,25 @@ namespace Castle.MonoRail.Views.Spark.Tests [TestFixture] public class SparkViewFactoryTests : SparkViewFactoryTestsBase - { - protected override void Configure() - { - factory = new SparkViewFactory(); - factory.Service(serviceProvider); - - manager = new DefaultViewEngineManager(); - manager.Service(serviceProvider); - serviceProvider.ViewEngineManager = manager; - serviceProvider.AddService(typeof(IViewEngineManager), manager); - serviceProvider.AddService(typeof(ISparkSettings), new SparkSettings()); + { + protected override void Configure() + { + var settings = new SparkSettings() + .SetPageBaseType(typeof(SparkView)); + + serviceProvider.AddService(typeof(ISparkSettings), settings); + + factory = new SparkViewFactory(); + factory.Service(serviceProvider); - manager.RegisterEngineForExtesionLookup(factory); - manager.RegisterEngineForView(factory); - } + manager = new DefaultViewEngineManager(); + manager.Service(serviceProvider); + serviceProvider.ViewEngineManager = manager; + serviceProvider.AddService(typeof(IViewEngineManager), manager); + + manager.RegisterEngineForExtesionLookup(factory); + manager.RegisterEngineForView(factory); + } [Test] public void ExtensionIsSpark() @@ -69,7 +73,7 @@ public void ContextAndControllerContextAvailable() descriptor.Templates.Add(string.Format("Shared{0}default.spark", Path.DirectorySeparatorChar)); var entry = factory.Engine.GetEntry(descriptor); var view = (SparkView)entry.CreateInstance(); - view.Contextualize(engineContext, controllerContext, factory, null); + view.Contextualize(engineContext, controllerContext, serviceProvider.GetService(), factory, null); var result = new StringWriter(); view.RenderView(result); @@ -114,14 +118,14 @@ public void NullBehaviourConfiguredToLenient() { mocks.ReplayAll(); manager.Process(string.Format("Home{0}NullBehaviourConfiguredToLenient", Path.DirectorySeparatorChar), output, engineContext, controller, controllerContext); - var content = output.ToString(); - Assert.IsFalse(content.Contains("default")); - - ContainsInOrder(content, - "

name kaboom *${user.Name}*

", - "

name silently **

", - "

name fixed *fred*

"); - } + var content = output.ToString(); + Assert.IsFalse(content.Contains("default")); + + ContainsInOrder(content, + "

name kaboom *${user.Name}*

", + "

name silently **

", + "

name fixed *fred*

"); + } [Test] public void TerseHtmlEncode() diff --git a/src/Castle.MonoRail.Views.Spark.Tests/ViewComponents/BaseViewComponentTests.cs b/src/Castle.MonoRail.Views.Spark.Tests/ViewComponents/BaseViewComponentTests.cs index 226e183a..e813af53 100644 --- a/src/Castle.MonoRail.Views.Spark.Tests/ViewComponents/BaseViewComponentTests.cs +++ b/src/Castle.MonoRail.Views.Spark.Tests/ViewComponents/BaseViewComponentTests.cs @@ -29,8 +29,7 @@ public class BaseViewComponentTests protected StubEngineContext engineContext; protected SparkViewFactory factory; protected IController controller; - protected SparkViewEngine engine; - + [SetUp] public virtual void Init() { @@ -45,9 +44,11 @@ public virtual void Init() services.AddService(typeof(IViewComponentFactory), viewComponentFactory); services.AddService(typeof(IViewComponentRegistry), viewComponentFactory.Registry); - var settings = new SparkSettings(); - engine = new SparkViewEngine(settings); - services.AddService(typeof(ISparkViewEngine), engine); + var settings = new SparkSettings() + .SetPageBaseType(typeof(SparkView)); + services.AddService(typeof(ISparkSettings), settings); + + services.AddService(typeof(IResourcePathManager), new DefaultResourcePathManager(settings)); factory = new SparkViewFactory(); factory.Service(services); diff --git a/src/Castle.MonoRail.Views.Spark.Tests/ViewComponents/ViewComponentRenderViewTests.cs b/src/Castle.MonoRail.Views.Spark.Tests/ViewComponents/ViewComponentRenderViewTests.cs index 3b163180..c4dbf1dd 100644 --- a/src/Castle.MonoRail.Views.Spark.Tests/ViewComponents/ViewComponentRenderViewTests.cs +++ b/src/Castle.MonoRail.Views.Spark.Tests/ViewComponents/ViewComponentRenderViewTests.cs @@ -16,6 +16,7 @@ using System.Reflection; using Castle.MonoRail.Framework; using NUnit.Framework; +using Spark; using Spark.FileSystem; namespace Castle.MonoRail.Views.Spark.Tests.ViewComponents @@ -81,7 +82,7 @@ public void ComponentRenderViewFromEmbeddedResource() Assembly.Load("Castle.MonoRail.Views.Spark.Tests"), "Castle.MonoRail.Views.Spark.Tests.EmbeddedViews"); - engine.ViewFolder = engine.ViewFolder.Append(embeddedViewFolder); + this.factory.Engine.ViewFolder = this.factory.Engine.ViewFolder.Append(embeddedViewFolder); mocks.ReplayAll(); diff --git a/src/Castle.MonoRail.Views.Spark/Install/PrecompileInstaller.cs b/src/Castle.MonoRail.Views.Spark/Install/PrecompileInstaller.cs index 138f6233..e604106b 100644 --- a/src/Castle.MonoRail.Views.Spark/Install/PrecompileInstaller.cs +++ b/src/Castle.MonoRail.Views.Spark/Install/PrecompileInstaller.cs @@ -77,11 +77,12 @@ public override void Install(IDictionary stateSaver) // Attempt to get the configuration from settings, otherwise use default settings var settings = (ISparkSettings)config.GetSection("spark") ?? - new SparkSettings(); + new SparkSettings() + .SetPageBaseType(typeof(SparkView)); var services = new StubMonoRailServices(); + services.AddService(typeof(ISparkSettings), settings); services.AddService(typeof(IViewSourceLoader), new FileAssemblyViewSourceLoader(viewsLocation)); - services.AddService(typeof(ISparkViewEngine), new SparkViewEngine(settings)); services.AddService(typeof(IControllerDescriptorProvider), services.ControllerDescriptorProvider); var factory = new SparkViewFactory(); diff --git a/src/Castle.MonoRail.Views.Spark/SparkView.cs b/src/Castle.MonoRail.Views.Spark/SparkView.cs index 8c5a1f16..6dc6c525 100644 --- a/src/Castle.MonoRail.Views.Spark/SparkView.cs +++ b/src/Castle.MonoRail.Views.Spark/SparkView.cs @@ -35,6 +35,7 @@ protected SparkView() private IEngineContext _context; private IControllerContext _controllerContext; + private IResourcePathManager _resourcePathManager; private SparkViewFactory _viewEngine; private IDictionary _contextVars; @@ -54,7 +55,7 @@ protected SparkView() public string SiteRoot { get { return _context.ApplicationPath; } } public string SiteResource(string path) { - return _viewEngine.Engine.ResourcePathManager.GetResourcePath(SiteRoot, path); + return this._resourcePathManager.GetResourcePath(SiteRoot, path); } public IDictionary PropertyBag { get { return _contextVars ?? _controllerContext.PropertyBag; } } @@ -91,10 +92,11 @@ public string Eval(string expression, string format) public T Helper() where T : class { return ControllerContext.Helpers[typeof(T).Name] as T; } public T Helper(string name) where T : class { return ControllerContext.Helpers[name] as T; } - public virtual void Contextualize(IEngineContext context, IControllerContext controllerContext, SparkViewFactory viewEngine, SparkView outerView) + public virtual void Contextualize(IEngineContext context, IControllerContext controllerContext, IResourcePathManager resourcePathManager, SparkViewFactory viewEngine, SparkView outerView) { _context = context; _controllerContext = controllerContext; + _resourcePathManager = resourcePathManager; _viewEngine = viewEngine; if (_viewEngine != null && _viewEngine.CacheServiceProvider != null) @@ -132,7 +134,7 @@ public void RenderComponent( var service = (IViewComponentFactory)_context.GetService(typeof(IViewComponentFactory)); var component = service.Create(name); - IViewComponentContext viewComponentContext = new ViewComponentContext(this, _viewEngine, name, parameters, body, sections); + IViewComponentContext viewComponentContext = new ViewComponentContext(this, _resourcePathManager, _viewEngine, name, parameters, body, sections); var oldContextVars = _contextVars; try diff --git a/src/Castle.MonoRail.Views.Spark/SparkViewFactory.cs b/src/Castle.MonoRail.Views.Spark/SparkViewFactory.cs index 53cf70ac..6ffb7099 100644 --- a/src/Castle.MonoRail.Views.Spark/SparkViewFactory.cs +++ b/src/Castle.MonoRail.Views.Spark/SparkViewFactory.cs @@ -26,7 +26,12 @@ using Castle.MonoRail.Framework.Resources; using Castle.MonoRail.Framework.Routing; using Castle.MonoRail.Views.Spark.Wrappers; +using Spark.Bindings; using Spark.Compiler; +using Spark.Compiler.CodeDom; +using Spark.Compiler.Roslyn; +using Spark.Parser; +using Spark.Parser.Syntax; namespace Castle.MonoRail.Views.Spark { @@ -36,6 +41,7 @@ public class SparkViewFactory : ViewEngineBase, IViewSourceLoaderContainer { private IControllerDescriptorProvider _controllerDescriptorProvider; private IViewActivatorFactory _viewActivatorFactory; + private IResourcePathManager _resourcePathManager; public override void Service(IServiceProvider provider) { @@ -43,9 +49,10 @@ public override void Service(IServiceProvider provider) _controllerDescriptorProvider = (IControllerDescriptorProvider)provider.GetService(typeof(IControllerDescriptorProvider)); _viewActivatorFactory = (IViewActivatorFactory)provider.GetService(typeof(IViewActivatorFactory)); + _resourcePathManager = (IResourcePathManager)provider.GetService(typeof(IResourcePathManager)); _cacheServiceProvider = (ICacheServiceProvider)provider.GetService(typeof(ICacheServiceProvider)); - SetEngine((ISparkViewEngine)provider.GetService(typeof(ISparkViewEngine))); + Engine = (ISparkViewEngine)provider.GetService(typeof(ISparkViewEngine)); } private ISparkViewEngine _engine; @@ -55,29 +62,35 @@ public ISparkViewEngine Engine { if (_engine == null) { - SetEngine(new SparkViewEngine(new SparkSettings())); + var settings = + (ISparkSettings)this.serviceProvider.GetService(typeof(ISparkSettings)) + ?? new SparkSettings() + .SetPageBaseType(typeof(SparkView)); + + var partialProvider = new DefaultPartialProvider(); + + var viewFolder = new ViewSourceLoaderWrapper(this); + + var batchCompiler = new CodeDomBatchCompiler(); + + this._engine = + new SparkViewEngine( + settings, + new DefaultSyntaxProvider(settings), + this._viewActivatorFactory ?? new DefaultViewActivator(), + new DefaultLanguageFactory(batchCompiler), + new CompiledViewHolder(), + viewFolder, + batchCompiler, + partialProvider, + new DefaultPartialReferenceProvider(partialProvider), + new DefaultBindingProvider(), + new ViewComponentExtensionFactory(this.serviceProvider)); } return _engine; } - set - { - SetEngine(value); - } - } - - private void SetEngine(ISparkViewEngine engine) - { - _engine = engine; - if (_engine == null) - return; - - _engine.ViewFolder = new ViewSourceLoaderWrapper(this); - _engine.ExtensionFactory = new ViewComponentExtensionFactory(serviceProvider); - _engine.DefaultPageBaseType = typeof(SparkView).FullName; - - if (_viewActivatorFactory != null) - _engine.ViewActivatorFactory = _viewActivatorFactory; + set => this._engine = value; } private ICacheServiceProvider _cacheServiceProvider; @@ -156,7 +169,7 @@ public override void Process(string templateName, TextWriter output, IEngineCont var entry = Engine.CreateEntry(descriptor); var view = (SparkView)entry.CreateInstance(); - view.Contextualize(context, controllerContext, this, null); + view.Contextualize(context, controllerContext, _resourcePathManager, this, null); if (view.Logger == null || view.Logger == NullLogger.Instance) view.Logger = Logger; view.RenderView(output); diff --git a/src/Castle.MonoRail.Views.Spark/ViewComponentContext.cs b/src/Castle.MonoRail.Views.Spark/ViewComponentContext.cs index df8430a0..1b0e7d20 100644 --- a/src/Castle.MonoRail.Views.Spark/ViewComponentContext.cs +++ b/src/Castle.MonoRail.Views.Spark/ViewComponentContext.cs @@ -26,6 +26,7 @@ namespace Castle.MonoRail.Views.Spark public class ViewComponentContext : IViewComponentContext { private readonly SparkView _view; + private readonly IResourcePathManager _resourcePathManager; private readonly SparkViewFactory _viewEngine; private readonly string _name; private readonly IDictionary _componentParameters; @@ -33,10 +34,10 @@ public class ViewComponentContext : IViewComponentContext private readonly IDictionary _sections; private readonly IDictionary _contextVarsAdapter; - - public ViewComponentContext(SparkView view, SparkViewFactory viewEngine, string name, IDictionary componentParameters, Action body, IDictionary sections) + public ViewComponentContext(SparkView view, IResourcePathManager resourcePathManager, SparkViewFactory viewEngine, string name, IDictionary componentParameters, Action body, IDictionary sections) { _view = view; + _resourcePathManager = resourcePathManager; _viewEngine = viewEngine; _name = name; _componentParameters = componentParameters; @@ -68,7 +69,7 @@ public void RenderView(string name, TextWriter writer) try { - componentView.Contextualize(_view.Context, _view.ControllerContext, _viewEngine, _view); + componentView.Contextualize(_view.Context, _view.ControllerContext, _resourcePathManager, _viewEngine, _view); componentView.RenderView(writer); } finally diff --git a/src/Spark.JsTests/Generate.ashx.cs b/src/Spark.JsTests/Generate.ashx.cs index f4cc82f2..299b1bd4 100644 --- a/src/Spark.JsTests/Generate.ashx.cs +++ b/src/Spark.JsTests/Generate.ashx.cs @@ -16,7 +16,11 @@ using System.IO; using System.Web; using System.Web.Services; +using Spark.Bindings; +using Spark.Compiler.Roslyn; using Spark.FileSystem; +using Spark.Parser; +using Spark.Parser.Syntax; namespace Spark.JsTests { @@ -29,13 +33,31 @@ public class Generate : IHttpHandler { public void ProcessRequest(HttpContext context) { - var engine = new SparkViewEngine - { - ViewFolder = new VirtualPathProviderViewFolder("~/Views") - }; - var entry = engine.CreateEntry(new SparkViewDescriptor() - .SetLanguage(LanguageType.Javascript) - .AddTemplate(context.Request.PathInfo.TrimStart('/', Path.DirectorySeparatorChar) + ".spark")); + var settings = new SparkSettings().SetDefaultLanguage(LanguageType.Javascript); + + var viewFolder = new VirtualPathProviderViewFolder("~/Views"); + + var partialProvider = new DefaultPartialProvider(); + + var batchCompiler = new RoslynBatchCompiler(); + + var engine = new SparkViewEngine( + settings, + new DefaultSyntaxProvider(settings), + new DefaultViewActivator(), + new DefaultLanguageFactory(batchCompiler), + new CompiledViewHolder(), + viewFolder, + batchCompiler, + partialProvider, + new DefaultPartialReferenceProvider(partialProvider), + new DefaultBindingProvider(), + null); + + var entry = engine.CreateEntry( + new SparkViewDescriptor() + .SetLanguage(LanguageType.Javascript) + .AddTemplate(context.Request.PathInfo.TrimStart('/', Path.DirectorySeparatorChar) + ".spark")); //Spark.Simple._LiteralHtml({foo:'asoi'}) context.Response.ContentType = "text/javascript"; diff --git a/src/Spark.Python.Tests/PythonViewCompilerTests.cs b/src/Spark.Python.Tests/PythonViewCompilerTests.cs index ceec7215..c3720f99 100644 --- a/src/Spark.Python.Tests/PythonViewCompilerTests.cs +++ b/src/Spark.Python.Tests/PythonViewCompilerTests.cs @@ -17,6 +17,7 @@ using System.IO; using NUnit.Framework; using Spark.Compiler; +using Spark.Compiler.Roslyn; using Spark.Parser; using Spark.Python.Compiler; using Spark.Tests.Models; @@ -37,7 +38,7 @@ public void Init() { BaseClass = typeof(StubSparkView).FullName }; - _languageFactory = new PythonLanguageFactory(); + _languageFactory = new PythonLanguageFactory(new RoslynBatchCompiler()); // Load up assemblies IronPython.Hosting.Python.CreateEngine(); diff --git a/src/Spark.Python.Tests/ScriptingLanguageFactoryTests.cs b/src/Spark.Python.Tests/ScriptingLanguageFactoryTests.cs index 37b5e7d3..e4f90f4d 100644 --- a/src/Spark.Python.Tests/ScriptingLanguageFactoryTests.cs +++ b/src/Spark.Python.Tests/ScriptingLanguageFactoryTests.cs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. // + +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.Python.Compiler; using Spark.Tests.Stubs; @@ -26,11 +29,15 @@ public class ScriptingLanguageFactoryTests [SetUp] public void Init() { - _engine = new SparkViewEngine(new SparkSettings()) - { - LanguageFactory = new PythonLanguageFactory(), - DefaultPageBaseType = typeof(StubSparkView).FullName - }; + var settings = new SparkSettings().SetPageBaseType(typeof(StubSparkView)); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton() + .BuildServiceProvider();; + + _engine = (SparkViewEngine)sp.GetService(); + IronPython.Hosting.Python.CreateEngine(); } diff --git a/src/Spark.Python/Compiler/PythonViewCompiler.cs b/src/Spark.Python/Compiler/PythonViewCompiler.cs index 04ed8448..b60418f8 100644 --- a/src/Spark.Python/Compiler/PythonViewCompiler.cs +++ b/src/Spark.Python/Compiler/PythonViewCompiler.cs @@ -16,6 +16,7 @@ using System.Linq; using System.Text; using Spark.Compiler; +using Spark.Compiler.CodeDom; using Spark.Compiler.CSharp.ChunkVisitors; using GeneratedCodeVisitor=Spark.Python.Compiler.ChunkVisitors.GeneratedCodeVisitor; using GlobalFunctionsVisitor=Spark.Python.Compiler.ChunkVisitors.GlobalFunctionsVisitor; @@ -29,8 +30,8 @@ public override void CompileView(IEnumerable> viewTemplates, IEnume { GenerateSourceCode(viewTemplates, allResources); - var compiler = new BatchCompiler(); - var assembly = compiler.Compile(Debug, "csharp", SourceCode); + var compiler = new CodeDomBatchCompiler(); + var assembly = compiler.Compile(Debug, "csharp", null, new[] { SourceCode }); CompiledType = assembly.GetType(ViewClassFullName); } diff --git a/src/Spark.Python/PythonLanguageFactory.cs b/src/Spark.Python/PythonLanguageFactory.cs index 318a277e..21aa6705 100644 --- a/src/Spark.Python/PythonLanguageFactory.cs +++ b/src/Spark.Python/PythonLanguageFactory.cs @@ -20,6 +20,10 @@ namespace Spark.Python { public class PythonLanguageFactory : DefaultLanguageFactory { + public PythonLanguageFactory(IBatchCompiler batchCompiler) : base(batchCompiler) + { + } + private PythonEngineManager _PythonEngineManager; public PythonEngineManager PythonEngineManager diff --git a/src/Spark.Ruby.Tests/RubyViewCompilerTests.cs b/src/Spark.Ruby.Tests/RubyViewCompilerTests.cs index 7c9df532..7099520d 100644 --- a/src/Spark.Ruby.Tests/RubyViewCompilerTests.cs +++ b/src/Spark.Ruby.Tests/RubyViewCompilerTests.cs @@ -17,6 +17,7 @@ using System.IO; using NUnit.Framework; using Spark.Compiler; +using Spark.Compiler.Roslyn; using Spark.Parser; using Spark.Ruby.Compiler; using Spark.Tests.Models; @@ -34,10 +35,11 @@ public class RubyViewCompilerTests public void Init() { _compiler = new RubyViewCompiler - { - BaseClass = typeof(StubSparkView).FullName,Debug = true - }; - _languageFactory = new RubyLanguageFactory(); + { + BaseClass = typeof(StubSparkView).FullName, + Debug = true + }; + _languageFactory = new RubyLanguageFactory(new RoslynBatchCompiler()); //load assemblies global::IronRuby.Ruby.CreateEngine(); diff --git a/src/Spark.Ruby/Compiler/RubyViewCompiler.cs b/src/Spark.Ruby/Compiler/RubyViewCompiler.cs index 03123925..72e210a7 100644 --- a/src/Spark.Ruby/Compiler/RubyViewCompiler.cs +++ b/src/Spark.Ruby/Compiler/RubyViewCompiler.cs @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // -using System; + using System.Collections.Generic; using System.Linq; using System.Text; using Spark.Compiler; -using Spark.Ruby.Compiler; +using Spark.Compiler.CodeDom; using Spark.Ruby.Compiler.ChunkVisitors; using BaseClassVisitor = Spark.Compiler.CSharp.ChunkVisitors.BaseClassVisitor; @@ -31,8 +31,8 @@ public override void CompileView(IEnumerable> viewTemplates, IEnume { GenerateSourceCode(viewTemplates, allResources); - var compiler = new BatchCompiler(); - var assembly = compiler.Compile(Debug, "csharp", SourceCode); + var compiler = new CodeDomBatchCompiler(); + var assembly = compiler.Compile(Debug, "csharp", null, new[] { SourceCode }); CompiledType = assembly.GetType(ViewClassFullName); } diff --git a/src/Spark.Ruby/RubyLanguageFactory.cs b/src/Spark.Ruby/RubyLanguageFactory.cs index c8b339b1..5917fd62 100644 --- a/src/Spark.Ruby/RubyLanguageFactory.cs +++ b/src/Spark.Ruby/RubyLanguageFactory.cs @@ -20,6 +20,9 @@ namespace Spark.Ruby { public class RubyLanguageFactory : DefaultLanguageFactory { + public RubyLanguageFactory(IBatchCompiler batchCompiler) : base(batchCompiler) + { + } private RubyEngineManager _RubyEngineManager; diff --git a/src/Spark.Tests/CompiledViewHolderTester.cs b/src/Spark.Tests/CompiledViewHolderTester.cs index 39b271dc..4361dac6 100644 --- a/src/Spark.Tests/CompiledViewHolderTester.cs +++ b/src/Spark.Tests/CompiledViewHolderTester.cs @@ -17,6 +17,9 @@ using Spark.Parser; using NUnit.Framework; using Rhino.Mocks; +using Spark.Bindings; +using Spark.FileSystem; +using Spark.Parser.Syntax; namespace Spark.Tests { @@ -51,7 +54,21 @@ public void LookupNonExistentReturnsNull() public void LookupReturnsStoredInstance() { var key = BuildKey(Path.Combine("c", "v"), Path.Combine("shared", "m")); - var entry = new CompiledViewEntry { Descriptor = key, Loader = new ViewLoader() }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var viewLoader = new ViewLoader( + settings, + MockRepository.GenerateMock(), + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); + + var entry = new CompiledViewEntry { Descriptor = key, Loader = viewLoader }; Assert.IsNull(holder.Lookup(key)); holder.Store(entry); Assert.AreSame(entry, holder.Lookup(key)); @@ -77,24 +94,43 @@ public void VariousKeyEqualities() Assert.That(!Equals(null, key1)); } - bool isCurrent; + public class FakeViewLoader : ViewLoader + { + public FakeViewLoader() : base(null, null, null, null, null, null, null) + { + } + + public bool IsCurrentValue { get; set; } + + public override bool IsCurrent() + { + return IsCurrentValue; + } + } [Test] public void ExpiredEntryReturnsNull() { - var loader = MockRepository.GenerateMock(); - - isCurrent = true; - Func foo = () => isCurrent; - loader.Stub(x => x.IsCurrent()).Do(foo); + var loader = new FakeViewLoader + { + IsCurrentValue = true + }; var key = BuildKey(Path.Combine("c", "v"), Path.Combine("shared", "m")); - var entry = new CompiledViewEntry { Descriptor = key, Loader = loader }; + + var entry = new CompiledViewEntry + { + Descriptor = key, + Loader = loader + }; + holder.Store(entry); + Assert.AreSame(entry, holder.Lookup(key)); - isCurrent = false; - Assert.IsNull(holder.Lookup(key)); + loader.IsCurrentValue = false; + + Assert.IsNull(holder.Lookup(key)); } [Test] diff --git a/src/Spark.Tests/Parser/ViewLoaderTester.cs b/src/Spark.Tests/Parser/ViewLoaderTester.cs index 1becdc8d..f0445afd 100644 --- a/src/Spark.Tests/Parser/ViewLoaderTester.cs +++ b/src/Spark.Tests/Parser/ViewLoaderTester.cs @@ -47,17 +47,36 @@ public void Init() viewFolder.Stub(x => x.ListViews(Arg.Is.Anything)).IgnoreArguments().Return(Array.Empty()); syntaxProvider = MockRepository.GenerateMock(); + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); - loader = new ViewLoader { ViewFolder = viewFolder, SyntaxProvider = syntaxProvider }; + loader = new ViewLoader(settings, viewFolder, partialProvider, new DefaultPartialReferenceProvider(partialProvider), null, syntaxProvider, null); } + private static int syntaxCallbacks = 0; + IViewFile ExpectGetChunks(string path, params Chunk[] chunks) { var source = MockRepository.GenerateMock(); - viewFolder.Expect(x => x.GetViewSource(path)).Return(source); - source.Expect(x => x.LastModified).Return(0); - syntaxProvider.Expect(x => x.GetChunks(Arg.Is.Anything, Arg.Is.Anything)).Return(chunks); + viewFolder + .Expect(x => x.GetViewSource(path)) + .Return(source); + + source + .Expect(x => x.LastModified) + .Return(0); + + syntaxProvider + .Expect(x => x.GetChunks(Arg.Is.Anything, Arg.Is.Anything)) + .WhenCalled( + call => + { + syntaxCallbacks++; + }) + .Return(chunks); return source; } @@ -145,14 +164,21 @@ public void FileNotFoundException() [Test] public void ExpiresWhenFilesChange() { - var viewFolder = new StubViewFolder { Path = Path.Combine("home", "changing.spark"), LastModified = 4 }; + var settings = new SparkSettings(); - var viewLoader = new ViewLoader - { - ViewFolder = viewFolder, - SyntaxProvider = MockRepository.GenerateStub() - }; - viewLoader.SyntaxProvider + var viewFolder = new StubViewFolder + { + Path = Path.Combine("home", "changing.spark"), + LastModified = 4 + }; + + this.syntaxProvider = MockRepository.GenerateStub(); + + var partialProvider = new DefaultPartialProvider(); + + var viewLoader = new ViewLoader(settings, viewFolder, partialProvider, new DefaultPartialReferenceProvider(partialProvider), null, this.syntaxProvider, null); + + this.syntaxProvider .Expect(x => x.GetChunks(null, null)) .IgnoreArguments() .Return(Array.Empty()); @@ -181,12 +207,13 @@ public void BindingProviderIsCalledUsingTheCorrectBindingRequest() syntaxProvider.Stub(x => x.GetChunks(null, null)) .IgnoreArguments() .Return(new List()); - var viewLoader = new ViewLoader - { - ViewFolder = folder, - SyntaxProvider = syntaxProvider, - BindingProvider = bindingProvider - }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var viewLoader = new ViewLoader(settings, folder, partialProvider, new DefaultPartialReferenceProvider(partialProvider), null, syntaxProvider, bindingProvider); + viewLoader.Load(viewPath); bindingProvider.VerifyAllExpectations(); @@ -234,7 +261,20 @@ public void LoadingPartialInsideNamedSection() {Path.Combine("home", "_Guts.spark"), "
"}, {Path.Combine("home", "_Another.spark"), "

hello world

"} }; - var viewLoader = new ViewLoader { SyntaxProvider = new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), ViewFolder = viewFolder }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var viewLoader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); + var chunks = viewLoader.Load(Path.Combine("home", "index.spark")); var everything = viewLoader.GetEverythingLoaded(); @@ -254,7 +294,19 @@ public void PartialsInSameFolderAreDiscovered() {Path.Combine("invoice", "five.spark"), ""}, }; - var viewLoader = new ViewLoader { ViewFolder = viewFolder }; + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var viewLoader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); + var zero = viewLoader.FindPartialFiles(Path.Combine("home", "zero.spark")); var product = viewLoader.FindPartialFiles(Path.Combine("product", "two.spark")); var invoice = viewLoader.FindPartialFiles(Path.Combine("invoice", "five.spark")); @@ -299,7 +351,18 @@ public void PartialsInCascadingBaseFoldersAndSharedFoldersAreDiscovered() {Path.Combine("area2","Shared","_dontfind3.spark"), ""}, }; - var viewLoader = new ViewLoader { ViewFolder = viewFolder }; + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var viewLoader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); var partials = viewLoader.FindPartialFiles(Path.Combine("area1","controller2","view3.spark")); @@ -321,7 +384,20 @@ public void LoadingEmptyFile() { { Path.Combine("home", "empty.spark"), "" }, }; - var viewLoader = new ViewLoader { SyntaxProvider = new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), ViewFolder = viewFolder }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var viewLoader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); + var chunks = viewLoader.Load(Path.Combine("home", "empty.spark")); var everything = viewLoader.GetEverythingLoaded(); @@ -335,7 +411,20 @@ public void LoadingEmptyShadeFile() { { Path.Combine("home", "empty.shade"), "" }, }; - var viewLoader = new ViewLoader { SyntaxProvider = new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), ViewFolder = viewFolder }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var viewLoader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); + var chunks = viewLoader.Load(Path.Combine("home", "empty.shade")); var everything = viewLoader.GetEverythingLoaded(); diff --git a/src/Spark.Web.Mvc.Pdf.Tests/PdfViewResultTests.cs b/src/Spark.Web.Mvc.Pdf.Tests/PdfViewResultTests.cs index 674f8b0a..1bd653f2 100644 --- a/src/Spark.Web.Mvc.Pdf.Tests/PdfViewResultTests.cs +++ b/src/Spark.Web.Mvc.Pdf.Tests/PdfViewResultTests.cs @@ -2,9 +2,11 @@ using System.IO; using System.Web.Mvc; using System.Web.Routing; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using Rhino.Mocks; using Spark.FileSystem; +using Spark.Web.Mvc.Extensions; using Spark.Web.Mvc.Tests; namespace Spark.Web.Mvc.Pdf.Tests @@ -62,17 +64,20 @@ private static IViewEngine MockViewEngine(ControllerContext controllerContext, o public void PdfResultShouldWriteToOutputStream() { var settings = new SparkSettings(); - var viewFolder = new InMemoryViewFolder - { - { - "foo/bar.spark", - HelloWorldXml - } - }; - var factory = new SparkViewFactory(settings) - { - ViewFolder = viewFolder - }; + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton( + new InMemoryViewFolder + { + { + "foo/bar.spark", + HelloWorldXml + } + }) + .BuildServiceProvider(); + + var factory = sp.GetService(); var stream = new MemoryStream(); var controllerContext = GetControllerContext(stream); diff --git a/src/Spark.Web.Mvc.Python/PythonLanguageFactoryWithExtensions.cs b/src/Spark.Web.Mvc.Python/PythonLanguageFactoryWithExtensions.cs index 6013fa35..71adfd2e 100644 --- a/src/Spark.Web.Mvc.Python/PythonLanguageFactoryWithExtensions.cs +++ b/src/Spark.Web.Mvc.Python/PythonLanguageFactoryWithExtensions.cs @@ -15,6 +15,7 @@ using System.Web.Mvc; using Microsoft.Scripting.Runtime; using Spark.Compiler; +using Spark.Compiler.Roslyn; using Spark.Python; [assembly: ExtensionType(typeof(HtmlHelper), typeof(System.Web.Mvc.Html.FormExtensions))] @@ -31,6 +32,10 @@ namespace Spark.Web.Mvc.Python { public class PythonLanguageFactoryWithExtensions : PythonLanguageFactory { + public PythonLanguageFactoryWithExtensions(IBatchCompiler batchCompiler) : base(batchCompiler) + { + } + private bool _initialized; public override ViewCompiler CreateViewCompiler(ISparkViewEngine engine, SparkViewDescriptor descriptor) diff --git a/src/Spark.Web.Mvc.Python/ServiceCollectionExtensions.cs b/src/Spark.Web.Mvc.Python/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..a8bbba34 --- /dev/null +++ b/src/Spark.Web.Mvc.Python/ServiceCollectionExtensions.cs @@ -0,0 +1,25 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Spark.Web.Mvc.Extensions; + +namespace Spark.Web.Mvc.Python +{ + public static class ServiceCollectionExtensions + { + /// + /// Registers spark dependencies in the service collection. + /// + /// + /// + /// + public static IServiceCollection AddSparkPython(this IServiceCollection services, ISparkSettings settings = null) + { + services.AddSpark(settings); + + // Override ISparkLanguageFactory + services.AddSingleton(); + + return services; + } + } +} diff --git a/src/Spark.Web.Mvc.Python/SparkPythonEngineStarter.cs b/src/Spark.Web.Mvc.Python/SparkPythonEngineStarter.cs deleted file mode 100644 index 8f01db2a..00000000 --- a/src/Spark.Web.Mvc.Python/SparkPythonEngineStarter.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2008-2009 Louis DeJardin - http://whereslou.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System.Collections.Generic; -using System.Web.Mvc; -using Spark.Web.Mvc.Python; - -namespace Spark.Web.Mvc.Python -{ - public static class SparkPythonEngineStarter - { - /// - /// Adds Asp.Net Mvc specific IViewEngine implementation and the IronPython - /// view language factory to a spark service container. - /// - /// An instance of the spark service container to modify - public static void ConfigureContainer(ISparkServiceContainer container) - { - container.SetServiceBuilder(c => new SparkViewFactory(c.GetService())); - container.SetServiceBuilder(c => new PythonLanguageFactoryWithExtensions()); - } - - /// - /// Create an IronPython enabled Spark service container - /// - /// A configured service container. Additional service builders may - /// me added. - public static ISparkServiceContainer CreateContainer() - { - var container = new SparkServiceContainer(); - ConfigureContainer(container); - return container; - } - - /// - /// Create an IronPython enabled service container with explicit spark settings. - /// - /// Typically an instance of SparkSettings object - /// A configured service container. Additional service builders may - /// me added. May be passed to RegisterViewEngine. - public static ISparkServiceContainer CreateContainer(ISparkSettings settings) - { - var container = new SparkServiceContainer(settings); - ConfigureContainer(container); - return container; - } - - /// - /// Creates a spark IViewEngine with IronPython as the default language. - /// Settings come from config or are defaulted. - /// - /// An IViewEngine interface of the SparkViewFactory - public static IViewEngine CreateViewEngine() - { - return CreateContainer().GetService(); - } - - /// - /// Creates a spark IViewEngine with IronPython as the default language. - /// - /// Typically an instance of SparkSettings object - /// An IViewEngine interface of the SparkViewFactory - public static IViewEngine CreateViewEngine(ISparkSettings settings) - { - return CreateContainer(settings).GetService(); - } - - /// - /// Installs the Spark+IronPython view engine. Settings come from config or are defaulted. - /// - public static void RegisterViewEngine() - { - ViewEngines.Engines.Add(CreateViewEngine()); - } - - /// - /// Installs the Spark+IronPython view engine. Settings passed in. - /// - public static void RegisterViewEngine(ISparkSettings settings) - { - ViewEngines.Engines.Add(CreateViewEngine(settings)); - } - - /// - /// Installs the Spark view engine. Service container passed in. - /// - public static void RegisterViewEngine(ISparkServiceContainer container) - { - ViewEngines.Engines.Add(container.GetService()); - } - - /// - /// Installs the Spark+IronPython view engine. Settings come from config or are defaulted. - /// - /// Typically in the ViewEngines.Engines collection - public static void RegisterViewEngine(ICollection engines) - { - engines.Add(CreateViewEngine()); - } - - /// - /// Installs the Spark+IronPython view engine. Settings passed in. - /// - /// Typically in the ViewEngines.Engines collection - /// Typically an instance of SparkSettings object - public static void RegisterViewEngine(ICollection engines, ISparkSettings settings) - { - engines.Add(CreateViewEngine(settings)); - } - - /// - /// Install the view engine from the container. Typical usage is to call CreateContainer, - /// provide additinal service builder functors to override certain classes, then call this - /// method. - /// - /// Typically the ViewEngines.Engines collection - /// A service container, often created with CreateContainer - public static void RegisterViewEngine(ICollection engines, ISparkServiceContainer container) - { - engines.Add(container.GetService()); - } - } -} \ No newline at end of file diff --git a/src/Spark.Web.Mvc.Ruby.Tests/HtmlHelperExtensionsTester.cs b/src/Spark.Web.Mvc.Ruby.Tests/HtmlHelperExtensionsTester.cs index a0d7b6b0..a4d3c1ea 100644 --- a/src/Spark.Web.Mvc.Ruby.Tests/HtmlHelperExtensionsTester.cs +++ b/src/Spark.Web.Mvc.Ruby.Tests/HtmlHelperExtensionsTester.cs @@ -12,17 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. // -using System; -using System.Collections.Generic; + using System.IO; -using System.Linq; -using System.Text; using System.Web; using System.Web.Caching; using System.Web.Mvc; using System.Web.Routing; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Compiler.Roslyn; using Spark.FileSystem; +using Spark.Web.Mvc.Extensions; namespace Spark.Web.Mvc.Ruby.Tests { @@ -41,7 +41,10 @@ public class StubController : Controller [Test] public void BuildingScriptHeader() { - var languageFactory = new RubyLanguageFactoryWithExtensions(); + var batchCompiler = new RoslynBatchCompiler(); + + var languageFactory = new RubyLanguageFactoryWithExtensions(batchCompiler); + var header = languageFactory.BuildScriptHeader(languageFactory.GetType().Assembly); Assert.That(header.Contains("ActionLink")); @@ -51,15 +54,18 @@ public void BuildingScriptHeader() private static ViewContext CompileView(string viewContents) { - var settings = new SparkSettings(); - var container = SparkRubyEngineStarter.CreateContainer(settings); + var services = new ServiceCollection() + .AddSpark(new SparkSettings()); + + var viewFolder = new InMemoryViewFolder { { $"stub{Path.DirectorySeparatorChar}index.spark", viewContents } }; - var viewFolder = new InMemoryViewFolder { { string.Format("stub{0}index.spark", Path.DirectorySeparatorChar), viewContents } }; - container.SetServiceBuilder(c => viewFolder); - var viewEngine = container.GetService(); + services.AddSingleton(viewFolder); - var httpContext = new StubHttpContext(); + var serviceProvider = services.BuildServiceProvider(); + + var viewEngine = serviceProvider.GetService(); + var httpContext = new StubHttpContext(); var routeData = new RouteData(); routeData.Values.Add("controller", "stub"); diff --git a/src/Spark.Web.Mvc.Ruby/RubyLanguageFactoryWithExtensions.cs b/src/Spark.Web.Mvc.Ruby/RubyLanguageFactoryWithExtensions.cs index 56d5a388..b427e760 100644 --- a/src/Spark.Web.Mvc.Ruby/RubyLanguageFactoryWithExtensions.cs +++ b/src/Spark.Web.Mvc.Ruby/RubyLanguageFactoryWithExtensions.cs @@ -22,6 +22,7 @@ using IronRuby.Runtime; using Microsoft.Scripting.Runtime; using Spark.Compiler; +using Spark.Compiler.Roslyn; using Spark.Ruby; using Spark.Ruby.Compiler; @@ -39,7 +40,12 @@ namespace Spark.Web.Mvc.Ruby { public class RubyLanguageFactoryWithExtensions : RubyLanguageFactory { + public RubyLanguageFactoryWithExtensions(IBatchCompiler batchCompiler) : base(batchCompiler) + { + } + private bool _initialized; + private string _scriptHeader; public override ViewCompiler CreateViewCompiler(ISparkViewEngine engine, SparkViewDescriptor descriptor) diff --git a/src/Spark.Web.Mvc.Ruby/ServiceCollectionExtensions.cs b/src/Spark.Web.Mvc.Ruby/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..f746e85e --- /dev/null +++ b/src/Spark.Web.Mvc.Ruby/ServiceCollectionExtensions.cs @@ -0,0 +1,25 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Spark.Web.Mvc.Extensions; + +namespace Spark.Web.Mvc.Ruby +{ + public static class ServiceCollectionExtensions + { + /// + /// Registers spark dependencies in the service collection. + /// + /// + /// + /// + public static IServiceCollection AddSparkRuby(this IServiceCollection services, ISparkSettings settings = null) + { + services.AddSpark(settings); + + // Override ISparkLanguageFactory + services.AddSingleton(); + + return services; + } + } +} diff --git a/src/Spark.Web.Mvc.Ruby/SparkRubyEngineStarter.cs b/src/Spark.Web.Mvc.Ruby/SparkRubyEngineStarter.cs deleted file mode 100644 index 2b9579b8..00000000 --- a/src/Spark.Web.Mvc.Ruby/SparkRubyEngineStarter.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2008-2009 Louis DeJardin - http://whereslou.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System.Collections.Generic; -using System.Web.Mvc; - -namespace Spark.Web.Mvc.Ruby -{ - public static class SparkRubyEngineStarter - { - /// - /// Adds Asp.Net Mvc specific IViewEngine implementation and the IronPython - /// view language factory to a spark service container. - /// - /// An instance of the spark service container to modify - public static void ConfigureContainer(ISparkServiceContainer container) - { - container.SetServiceBuilder(c => new SparkViewFactory(c.GetService())); - container.SetServiceBuilder(c => new RubyLanguageFactoryWithExtensions()); - } - - /// - /// Create an IronPython enabled Spark service container - /// - /// A configured service container. Additional service builders may - /// me added. - public static ISparkServiceContainer CreateContainer() - { - var container = new SparkServiceContainer(); - ConfigureContainer(container); - return container; - } - - /// - /// Create an IronPython enabled service container with explicit spark settings. - /// - /// Typically an instance of SparkSettings object - /// A configured service container. Additional service builders may - /// me added. May be passed to RegisterViewEngine. - public static ISparkServiceContainer CreateContainer(ISparkSettings settings) - { - var container = new SparkServiceContainer(settings); - ConfigureContainer(container); - return container; - } - - /// - /// Creates a spark IViewEngine with IronPython as the default language. - /// Settings come from config or are defaulted. - /// - /// An IViewEngine interface of the SparkViewFactory - public static IViewEngine CreateViewEngine() - { - return CreateContainer().GetService(); - } - - /// - /// Creates a spark IViewEngine with IronPython as the default language. - /// - /// Typically an instance of SparkSettings object - /// An IViewEngine interface of the SparkViewFactory - public static IViewEngine CreateViewEngine(ISparkSettings settings) - { - return CreateContainer(settings).GetService(); - } - - /// - /// Installs the Spark+IronPython view engine. Settings come from config or are defaulted. - /// - public static void RegisterViewEngine() - { - ViewEngines.Engines.Add(CreateViewEngine()); - } - - /// - /// Installs the Spark+IronPython view engine. Settings passed in. - /// - public static void RegisterViewEngine(ISparkSettings settings) - { - ViewEngines.Engines.Add(CreateViewEngine(settings)); - } - - /// - /// Installs the Spark view engine. Service container passed in. - /// - public static void RegisterViewEngine(ISparkServiceContainer container) - { - ViewEngines.Engines.Add(container.GetService()); - } - - /// - /// Installs the Spark+IronPython view engine. Settings come from config or are defaulted. - /// - /// Typically in the ViewEngines.Engines collection - public static void RegisterViewEngine(ICollection engines) - { - engines.Add(CreateViewEngine()); - } - - /// - /// Installs the Spark+IronPython view engine. Settings passed in. - /// - /// Typically in the ViewEngines.Engines collection - /// Typically an instance of SparkSettings object - public static void RegisterViewEngine(ICollection engines, ISparkSettings settings) - { - engines.Add(CreateViewEngine(settings)); - } - - /// - /// Install the view engine from the container. Typical usage is to call CreateContainer, - /// provide additinal service builder functors to override certain classes, then call this - /// method. - /// - /// Typically the ViewEngines.Engines collection - /// A service container, often created with CreateContainer - public static void RegisterViewEngine(ICollection engines, ISparkServiceContainer container) - { - engines.Add(container.GetService()); - } - } -} \ No newline at end of file diff --git a/src/Spark.Web.Mvc.Tests/DescriptorBuildingTester.cs b/src/Spark.Web.Mvc.Tests/DescriptorBuildingTester.cs index 088da660..f160edcd 100644 --- a/src/Spark.Web.Mvc.Tests/DescriptorBuildingTester.cs +++ b/src/Spark.Web.Mvc.Tests/DescriptorBuildingTester.cs @@ -16,16 +16,17 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Web; using System.Web.Mvc; using System.Web.Routing; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using Rhino.Mocks; using Spark.FileSystem; using Spark.Parser; using Spark.Web.Mvc.Descriptors; +using Spark.Web.Mvc.Extensions; namespace Spark.Web.Mvc.Tests { @@ -37,13 +38,35 @@ public class DescriptorBuildingTester private RouteData _routeData; private ControllerContext _controllerContext; + public static IServiceProvider SetupServiceProvider(ISparkSettings settings, Action serviceOverrides = null) + { + IServiceCollection services = new ServiceCollection() + .AddSpark(settings); + + if (serviceOverrides != null) + { + serviceOverrides.Invoke(services); + } + + return services.BuildServiceProvider(); + } + [SetUp] public void Init() { - _factory = new SparkViewFactory(); + var settings = new SparkSettings(); + _viewFolder = new InMemoryViewFolder(); - _factory.ViewFolder = _viewFolder; + var sp = SetupServiceProvider( + settings, + services => + { + services.AddSingleton(this._viewFolder); + }); + + _factory = sp.GetService(); + var httpContext = MockRepository.GenerateStub(); _routeData = new RouteData(); var controller = MockRepository.GenerateStub(); @@ -573,7 +596,17 @@ private static IDictionary Dict(IEnumerable values) [Test] public void CustomParameterAddedToViewSearchPath() { - _factory.DescriptorBuilder = new ExtendingDescriptorBuilderWithInheritance(_factory.Engine); + var settings = new SparkSettings(); + + var sp = SetupServiceProvider(settings, + s => + { + s.AddSingleton(this._viewFolder); + s.AddSingleton(); + }); + + var factory = sp.GetService(); + _routeData.Values.Add("controller", "Home"); _routeData.Values.Add("language", "en-us"); _viewFolder.Add(@"Home\Index.en-us.spark", ""); @@ -584,7 +617,8 @@ public void CustomParameterAddedToViewSearchPath() _viewFolder.Add(@"Layouts\Application.spark", ""); var searchedLocations = new List(); - var result = _factory.CreateDescriptor(_controllerContext, "Index", null, true, searchedLocations); + var result = factory.CreateDescriptor(_controllerContext, "Index", null, true, searchedLocations); + AssertDescriptorTemplates( result, searchedLocations, @@ -594,12 +628,8 @@ public void CustomParameterAddedToViewSearchPath() public class ExtendingDescriptorBuilderWithInheritance : DefaultDescriptorBuilder { - public ExtendingDescriptorBuilderWithInheritance() - { - } - - public ExtendingDescriptorBuilderWithInheritance(ISparkViewEngine engine) - : base(engine) + public ExtendingDescriptorBuilderWithInheritance(ISparkSettings settings, IViewFolder viewFolder) + : base(settings, viewFolder) { } @@ -649,16 +679,30 @@ static IEnumerable Merge(IEnumerable locations, string region) [Test] public void CustomDescriptorBuildersCantUseDescriptorFilters() { - _factory.DescriptorBuilder = MockRepository.GenerateStub(); - Assert.That(() => - _factory.AddFilter(MockRepository.GenerateStub()), - Throws.TypeOf()); + var settings = new SparkSettings(); + + var factory = new SparkViewFactory( + settings, + null, + MockRepository.GenerateStub(), + null, + null); + + Assert.That( + () => + factory.AddFilter(MockRepository.GenerateStub()), + Throws.TypeOf()); } [Test] public void SimplifiedUseMasterGrammarDetectsElementCorrectly() { - var builder = new DefaultDescriptorBuilder(); + var settings = new SparkSettings + { + Prefix = null + }; + + var builder = new DefaultDescriptorBuilder(settings, null); var a = builder.ParseUseMaster(new Position(new SourceContext(""))); var b = builder.ParseUseMaster(new Position(new SourceContext(""))); @@ -678,7 +722,12 @@ public void SimplifiedUseMasterGrammarDetectsElementCorrectly() [Test] public void SimplifiedUseMasterGrammarWithPrefixDetectsElementCorrectly() { - var builder = new DefaultDescriptorBuilder("s"); + var settings = new SparkSettings + { + Prefix = "s" + }; + + var builder = new DefaultDescriptorBuilder(settings, null); var a = builder.ParseUseMaster(new Position(new SourceContext(""))); var b = builder.ParseUseMaster(new Position(new SourceContext(""))); diff --git a/src/Spark.Web.Mvc.Tests/SparkBatchCompilerTester.cs b/src/Spark.Web.Mvc.Tests/SparkBatchCompilerTester.cs index 6052f04e..0933dadc 100644 --- a/src/Spark.Web.Mvc.Tests/SparkBatchCompilerTester.cs +++ b/src/Spark.Web.Mvc.Tests/SparkBatchCompilerTester.cs @@ -17,8 +17,12 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Compiler; +using Spark.Compiler.CodeDom; using Spark.FileSystem; +using Spark.Web.Mvc.Extensions; using Spark.Web.Mvc.Tests.Controllers; namespace Spark.Web.Mvc.Tests @@ -28,12 +32,30 @@ public class SparkBatchCompilerTester { #region Setup/Teardown + public static IServiceProvider SetupServiceProvider(Action serviceOverrides = null) + { + var services = new ServiceCollection(); + + services.AddSpark(new SparkSettings()); + + if (serviceOverrides != null) + { + serviceOverrides.Invoke(services); + } + + return services.BuildServiceProvider(); + } + [SetUp] public void Init() { - var settings = new SparkSettings(); + var serviceProvider = SetupServiceProvider( + s => + { + s.AddSingleton(new FileSystemViewFolder("AspNetMvc.Tests.Views")); + }); - _factory = new SparkViewFactory(settings) { ViewFolder = new FileSystemViewFolder("AspNetMvc.Tests.Views") }; + _factory = serviceProvider.GetService(); } #endregion @@ -52,7 +74,7 @@ public void CompileBatchDescriptor() var assembly = _factory.Precompile(batch); Assert.IsNotNull(assembly); - Assert.AreEqual(3, assembly.GetTypes().Length); + Assert.AreEqual(3, assembly.GetTypes().Count(x => x.BaseType == typeof(SparkView))); } [Test] @@ -67,8 +89,11 @@ public void CanHandleCSharpV3SyntaxWhenLoadedInAppDomainWithoutConfig() try { sandbox = AppDomain.CreateDomain("sandbox", null, appDomainSetup); - var remoteRunner = (PrecompileRunner) sandbox.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, - typeof(PrecompileRunner).FullName); + + var remoteRunner = (PrecompileRunner)sandbox.CreateInstanceAndUnwrap( + Assembly.GetExecutingAssembly().FullName, + typeof(PrecompileRunner).FullName); + remoteRunner.Precompile(); } finally @@ -84,9 +109,16 @@ public class PrecompileRunner : MarshalByRefObject { public void Precompile() { - var settings = new SparkSettings(); + var serviceProvider = SetupServiceProvider( + s => + { + // Don't know why but the roslin compiler complains + // in CanHandleCSharpV3SyntaxWhenLoadedInAppDomainWithoutConfig test + s.AddSingleton(); + s.AddSingleton(new FileSystemViewFolder("AspNetMvc.Tests.Views")); + }); - var factory = new SparkViewFactory(settings) { ViewFolder = new FileSystemViewFolder("AspNetMvc.Tests.Views") }; + var factory = serviceProvider.GetService(); var batch = new SparkBatchDescriptor(); @@ -143,7 +175,7 @@ public void MultipleLayoutFiles() var assembly = _factory.Precompile(batch); Assert.IsNotNull(assembly); - Assert.AreEqual(4, assembly.GetTypes().Length); + Assert.AreEqual(4, assembly.GetTypes().Count(x => x.BaseType == typeof(SparkView))); } [Test] @@ -170,7 +202,7 @@ public void WildcardIncludeRules() var assembly = _factory.Precompile(batch); Assert.IsNotNull(assembly); - Assert.AreEqual(3, assembly.GetTypes().Length); + Assert.AreEqual(3, assembly.GetTypes().Count(x => x.BaseType == typeof(SparkView))); } [Test] @@ -182,6 +214,15 @@ public void FileWithoutSparkExtensionAreIgnored() { string.Format("Stub{0}Helper.cs", Path.DirectorySeparatorChar), "// this is a code file" }, { string.Format("Layouts{0}Stub.spark", Path.DirectorySeparatorChar), "

layout

" }, }; + + var sp = SetupServiceProvider( + s => + { + s.AddSingleton(viewFolder); + }); + + _factory = sp.GetService(); + var batch = new SparkBatchDescriptor(); batch.For(); diff --git a/src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs b/src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs index 80e2e0af..e541eef8 100644 --- a/src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs +++ b/src/Spark.Web.Mvc.Tests/SparkViewFactoryTester.cs @@ -21,9 +21,11 @@ using System.Web.Mvc.Html; using System.Web.Routing; using System.Web.SessionState; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using Rhino.Mocks; using Spark.FileSystem; +using Spark.Web.Mvc.Extensions; using Spark.Web.Mvc.Tests.Controllers; using Spark.Web.Mvc.Tests.Models; @@ -34,6 +36,20 @@ public class SparkViewFactoryTester { #region Setup/Teardown + public static IServiceProvider SetupServiceProvider(ISparkSettings settings, Action serviceOverrides = null) + { + var services = new ServiceCollection(); + + services.AddSpark(settings); + + if (serviceOverrides != null) + { + serviceOverrides.Invoke(services); + } + + return services.BuildServiceProvider(); + } + [SetUp] public void Init() { @@ -78,8 +94,17 @@ public void Init() controllerContext = new ControllerContext(httpContext, routeData, controller); - var settings = new SparkSettings().AddNamespace("System.Web.Mvc.Html").SetAutomaticEncoding(true); - factory = new SparkViewFactory(settings) { ViewFolder = new FileSystemViewFolder("AspNetMvc.Tests.Views") }; + var settings = new SparkSettings().AddNamespace("System.Web.Mvc.Html") + .SetAutomaticEncoding(true); + + var serviceProvider = SetupServiceProvider( + settings, + s => + { + s.AddSingleton(new FileSystemViewFolder("AspNetMvc.Tests.Views")); + }); + + factory = serviceProvider.GetService(); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory()); @@ -150,7 +175,8 @@ private static void ContainsInOrder(string content, params string[] values) foreach (var value in values) { var nextIndex = content.IndexOf(value, index); - Assert.GreaterOrEqual(nextIndex, 0, string.Format("Looking for {0}", value)); + + Assert.GreaterOrEqual(nextIndex, 0, $"Looking for {value}"); index = nextIndex + value.Length; } } @@ -241,14 +267,20 @@ public void HtmlHelperWorksOnItsOwn() [Test] public void MasterApplicationIfPresent() { - factory.ViewFolder = new InMemoryViewFolder - { - {string.Format("Foo{0}Baaz.spark", Path.DirectorySeparatorChar), ""}, - {string.Format("Shared{0}Application.spark", Path.DirectorySeparatorChar), ""} - }; - + var viewFolder = new InMemoryViewFolder + { + { $"Foo{Path.DirectorySeparatorChar}Baaz.spark", "" }, + { $"Shared{Path.DirectorySeparatorChar}Application.spark", "" } + }; + var sp = SetupServiceProvider( + new SparkSettings(), + s => + { + s.AddSingleton(viewFolder); + }); + factory = sp.GetService(); routeData.Values["controller"] = "Foo"; routeData.Values["action"] = "NotBaaz"; @@ -258,17 +290,26 @@ public void MasterApplicationIfPresent() //mocks.VerifyAll(); Assert.AreEqual(2, descriptor.Templates.Count); - Assert.AreEqual(string.Format("Foo{0}Baaz.spark", Path.DirectorySeparatorChar), descriptor.Templates[0]); - Assert.AreEqual(string.Format("Shared{0}Application.spark", Path.DirectorySeparatorChar), descriptor.Templates[1]); + Assert.AreEqual($"Foo{Path.DirectorySeparatorChar}Baaz.spark", descriptor.Templates[0]); + Assert.AreEqual($"Shared{Path.DirectorySeparatorChar}Application.spark", descriptor.Templates[1]); } [Test] public void MasterEmptyByDefault() { - factory.ViewFolder = new InMemoryViewFolder - { - {string.Format("Foo{0}Baaz.spark", Path.DirectorySeparatorChar), ""} - }; + var viewFolder = new InMemoryViewFolder + { + { $"Foo{Path.DirectorySeparatorChar}Baaz.spark", "" } + }; + + var sp = SetupServiceProvider( + new SparkSettings(), + s => + { + s.AddSingleton(viewFolder); + }); + + factory = sp.GetService(); routeData.Values["controller"] = "Foo"; routeData.Values["action"] = "NotBaaz"; @@ -276,21 +317,27 @@ public void MasterEmptyByDefault() var descriptor = factory.CreateDescriptor(controllerContext, "Baaz", null, true, null); Assert.AreEqual(1, descriptor.Templates.Count); - Assert.AreEqual(string.Format("Foo{0}Baaz.spark", Path.DirectorySeparatorChar), descriptor.Templates[0]); + Assert.AreEqual($"Foo{Path.DirectorySeparatorChar}Baaz.spark", descriptor.Templates[0]); } [Test] public void MasterForControllerIfPresent() { - factory.ViewFolder = new InMemoryViewFolder - { - {string.Format("Foo{0}Baaz.spark", Path.DirectorySeparatorChar), ""}, - {string.Format("Shared{0}Foo.spark", Path.DirectorySeparatorChar),""} - }; - - - - + var viewFolder = new InMemoryViewFolder + { + { $"Foo{Path.DirectorySeparatorChar}Baaz.spark", "" }, + { $"Shared{Path.DirectorySeparatorChar}Foo.spark", "" } + }; + + var sp = SetupServiceProvider( + new SparkSettings(), + s => + { + s.AddSingleton(viewFolder); + }); + + factory = sp.GetService(); + routeData.Values["controller"] = "Foo"; routeData.Values["action"] = "NotBaaz"; @@ -355,16 +402,23 @@ public void RenderPlainView() [Test] public void TargetNamespaceFromController() { - factory.ViewFolder = new InMemoryViewFolder - { - {string.Format("Home{0}Baaz.spark", Path.DirectorySeparatorChar), ""}, - {string.Format("Layouts{0}Home.spark", Path.DirectorySeparatorChar),""} - }; + var viewFolder = new InMemoryViewFolder + { + { $"Home{Path.DirectorySeparatorChar}Baaz.spark", "" }, + { $"Layouts{Path.DirectorySeparatorChar}Home.spark", "" } + }; - controller = new StubController(); - controllerContext = new ControllerContext(httpContext, routeData, controller); + var sp = SetupServiceProvider( + new SparkSettings(), + s => + { + s.AddSingleton(viewFolder); + }); + factory = sp.GetService(); + controller = new StubController(); + controllerContext = new ControllerContext(httpContext, routeData, controller); var descriptor = factory.CreateDescriptor(controllerContext, "Baaz", null, true, null); //mocks.VerifyAll(); @@ -440,35 +494,36 @@ public void ViewSourceLoaderCanBeChanged() { var replacement = MockRepository.GenerateStub(); + var existing = factory.Engine.ViewFolder; - - var existing = factory.ViewFolder; Assert.AreNotSame(existing, replacement); - Assert.AreSame(existing, factory.ViewFolder); + Assert.AreSame(existing, factory.Engine.ViewFolder); - factory.ViewFolder = replacement; - Assert.AreSame(replacement, factory.ViewFolder); - Assert.AreNotSame(existing, factory.ViewFolder); + factory.Engine.ViewFolder = replacement; + + Assert.AreSame(replacement, factory.Engine.ViewFolder); + Assert.AreNotSame(existing, factory.Engine.ViewFolder); } [Test] public void CreatingViewEngineWithSimpleContainer() { var settings = new SparkSettings().AddNamespace("System.Web.Mvc.Html"); - var container = SparkEngineStarter.CreateContainer(settings); - var viewFactory = (SparkViewFactory)container.GetService(); - var viewEngine = container.GetService(); - var viewFolder = container.GetService(); - var descriptorBuilder = container.GetService(); - var cacheServiceProvider = container.GetService(); - var viewActivatorFactory = container.GetService(); + var sp = SetupServiceProvider(settings); + + var viewFactory = sp.GetService(); + var viewEngine = sp.GetService(); + var viewFolder = sp.GetService(); + var descriptorBuilder = sp.GetService(); + var cacheServiceProvider = sp.GetService(); + var viewActivatorFactory = sp.GetService(); Assert.AreSame(settings, viewFactory.Settings); Assert.AreSame(settings, viewEngine.Settings); Assert.AreSame(viewEngine, viewFactory.Engine); Assert.AreSame(viewFolder, viewEngine.ViewFolder); - Assert.AreSame(viewFolder, viewFactory.ViewFolder); + Assert.AreSame(viewFolder, viewFactory.Engine.ViewFolder); Assert.AreSame(descriptorBuilder, viewFactory.DescriptorBuilder); Assert.AreSame(cacheServiceProvider, viewFactory.CacheServiceProvider); Assert.AreSame(viewActivatorFactory, viewFactory.ViewActivatorFactory); diff --git a/src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs b/src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs index cb6c6749..8ab0e37e 100644 --- a/src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs +++ b/src/Spark.Web.Mvc/DefaultDescriptorBuilder.cs @@ -2,43 +2,27 @@ using System.IO; using System.Linq; using System.Web.Mvc; -using Spark.Compiler; -using Spark.Compiler.NodeVisitors; +using Spark.FileSystem; using Spark.Parser; using Spark.Parser.Syntax; using Spark.Web.Mvc.Descriptors; namespace Spark.Web.Mvc { - public class DefaultDescriptorBuilder : IDescriptorBuilder, ISparkServiceInitialize + public class DefaultDescriptorBuilder : IDescriptorBuilder { - private ISparkViewEngine _engine; + private IViewFolder _viewFolder; - public DefaultDescriptorBuilder() - : this((string)null) + public DefaultDescriptorBuilder(ISparkSettings settings, IViewFolder viewFolder) { - } - - public DefaultDescriptorBuilder(string _prefix) - { - Filters = new List + this.Filters = new List { new AreaDescriptorFilter() }; - _grammar = new UseMasterGrammar(_prefix); - } - public DefaultDescriptorBuilder(ISparkViewEngine engine) - : this() - { - _engine = engine; - _grammar = new UseMasterGrammar(_engine.Settings.Prefix); - } + this._grammar = new UseMasterGrammar(settings.Prefix); - public virtual void Initialize(ISparkServiceContainer container) - { - _engine = container.GetService(); - _grammar = new UseMasterGrammar(_engine.Settings.Prefix); + this._viewFolder = viewFolder; } public IList Filters { get; set; } @@ -153,12 +137,12 @@ public UseMasterGrammar(string _prefix) } private UseMasterGrammar _grammar; - public ParseAction ParseUseMaster { get { return _grammar.ParseUseMaster; } } + public ParseAction ParseUseMaster => _grammar.ParseUseMaster; public string TrailingUseMasterName(SparkViewDescriptor descriptor) { var lastTemplate = descriptor.Templates.Last(); - var sourceContext = AbstractSyntaxProvider.CreateSourceContext(lastTemplate, _engine.ViewFolder); + var sourceContext = AbstractSyntaxProvider.CreateSourceContext(lastTemplate, _viewFolder); if (sourceContext == null) { return null; @@ -173,7 +157,7 @@ private bool LocatePotentialTemplate( ICollection descriptorTemplates, ICollection searchedLocations) { - var template = potentialTemplates.FirstOrDefault(t => _engine.ViewFolder.HasView(t)); + var template = potentialTemplates.FirstOrDefault(t => _viewFolder.HasView(t)); if (template != null) { descriptorTemplates.Add(template); diff --git a/src/Spark.Web.Mvc/Descriptors/DescriptorFilterExtensions.cs b/src/Spark.Web.Mvc/Descriptors/DescriptorFilterExtensions.cs index d4e08f3c..d8a10919 100644 --- a/src/Spark.Web.Mvc/Descriptors/DescriptorFilterExtensions.cs +++ b/src/Spark.Web.Mvc/Descriptors/DescriptorFilterExtensions.cs @@ -4,11 +4,6 @@ namespace Spark.Web.Mvc.Descriptors { public static class DescriptorFilterExtensions { - public static void AddFilter(this ISparkServiceContainer target, IDescriptorFilter filter) - { - target.GetService().AddFilter(filter); - } - public static void AddFilter(this SparkViewFactory target, IDescriptorFilter filter) { target.DescriptorBuilder.AddFilter(filter); @@ -17,7 +12,7 @@ public static void AddFilter(this SparkViewFactory target, IDescriptorFilter fil public static void AddFilter(this IDescriptorBuilder target, IDescriptorFilter filter) { if (!(target is DefaultDescriptorBuilder)) - throw new InvalidCastException("IDescriptorFilters may only be added to DefaultDescriptorBuilder"); + throw new InvalidCastException($"IDescriptorFilters may only be added to {nameof(DefaultDescriptorBuilder)}"); ((DefaultDescriptorBuilder) target).AddFilter(filter); } diff --git a/src/Spark.Web.Mvc/Extensions/ServiceCollectionExtensions.cs b/src/Spark.Web.Mvc/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..fcbd8052 --- /dev/null +++ b/src/Spark.Web.Mvc/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,73 @@ +using System; +using System.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Spark.Bindings; +using Spark.Compiler; +using Spark.Compiler.CodeDom; +using Spark.Compiler.Roslyn; +using Spark.FileSystem; +using Spark.Parser; +using Spark.Parser.Syntax; + +namespace Spark.Web.Mvc.Extensions +{ + public static class ServiceCollectionExtensions + { + internal static string MissingSparkSettingsConfigurationErrorExceptionMessage + = "Spark setting not configured. Missing spark section app configuration or no ISparkSetting instance registered in IoC container."; + + /// + /// Registers spark dependencies in the service collection. + /// + /// + /// + /// + public static IServiceCollection AddSpark(this IServiceCollection services, ISparkSettings settings = null) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (settings == null) + { + settings = (ISparkSettings)ConfigurationManager.GetSection("spark"); + + if (settings == null) + { + throw new ConfigurationErrorsException(MissingSparkSettingsConfigurationErrorExceptionMessage); + } + } + + // Roslyn + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + + services + .AddSingleton(settings) + .AddSingleton(settings) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(settings.CreateDefaultViewFolder()) + .AddSingleton() + .AddSingleton() + .AddSingleton(); + + services.AddSingleton(c => null); + + services + .AddSingleton() + .AddSingleton() + .AddSingleton(); + + return services; + } + } +} diff --git a/src/Spark.Web.Mvc/Install/PrecompileInstaller.cs b/src/Spark.Web.Mvc/Install/PrecompileInstaller.cs index 69d80903..819e6f6a 100644 --- a/src/Spark.Web.Mvc/Install/PrecompileInstaller.cs +++ b/src/Spark.Web.Mvc/Install/PrecompileInstaller.cs @@ -19,11 +19,14 @@ using System.Configuration.Install; using System.IO; using System.Reflection; +using Microsoft.Extensions.DependencyInjection; using Spark.FileSystem; +using Spark.Web.Mvc.Extensions; namespace Spark.Web.Mvc.Install { [RunInstaller(true)] + [Obsolete("Is Spark MVC ever 'installed'?")] public partial class PrecompileInstaller : Installer { public PrecompileInstaller() @@ -68,13 +71,15 @@ public override void Install(IDictionary stateSaver) settings = (ISparkSettings) config.GetSection("spark"); } - // Finally create an engine with the settings from the web.config - var factory = new SparkViewFactory(settings) - { - ViewFolder = new FileSystemViewFolder(viewsLocation) - }; + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(new FileSystemViewFolder(viewsLocation)) + .BuildServiceProvider(); - // And generate all of the known view/master templates into the target assembly + // Create an engine with the settings from the web.config + var factory = sp.GetService(); + + // And generate all the known view/master templates into the target assembly var batch = new SparkBatchDescriptor(targetPath); // create entries for controller attributes in the parent installer's assembly diff --git a/src/Spark.Web.Mvc/LanguageKit.cs b/src/Spark.Web.Mvc/LanguageKit.cs index caa2eee6..5f7f2086 100644 --- a/src/Spark.Web.Mvc/LanguageKit.cs +++ b/src/Spark.Web.Mvc/LanguageKit.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Web.Mvc; using Spark.FileSystem; using Spark.Web.Mvc.Descriptors; @@ -11,16 +10,11 @@ namespace Spark.Web.Mvc { public static class LanguageKit { - public static void Install(ISparkServiceContainer services, Func selector) - { - services.AddFilter(new Filter(selector)); - services.GetService().ViewFolder = new Folder(services.GetService()); - } - public static void Install(SparkViewFactory factory, Func selector) { factory.AddFilter(new Filter(selector)); - factory.ViewFolder = new Folder(factory.ViewFolder); + + factory.Engine.ViewFolder = new Folder(factory.Engine.ViewFolder); } public static void Install(IEnumerable engines, Func selector) diff --git a/src/Spark.Web.Mvc/Spark.Web.Mvc.csproj b/src/Spark.Web.Mvc/Spark.Web.Mvc.csproj index b5e59c73..c83d9dee 100644 --- a/src/Spark.Web.Mvc/Spark.Web.Mvc.csproj +++ b/src/Spark.Web.Mvc/Spark.Web.Mvc.csproj @@ -1,4 +1,4 @@ - + Library net48 @@ -55,5 +55,6 @@ + \ No newline at end of file diff --git a/src/Spark.Web.Mvc/SparkEngineStarter.cs b/src/Spark.Web.Mvc/SparkEngineStarter.cs deleted file mode 100644 index a5ac1171..00000000 --- a/src/Spark.Web.Mvc/SparkEngineStarter.cs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2008-2009 Louis DeJardin - http://whereslou.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Mvc; - -namespace Spark.Web.Mvc -{ - public static class SparkEngineStarter - { - /// - /// Adds Asp.Net Mvc specific IViewEngine implementation. - /// - /// An instance of the spark service container to modify - public static void ConfigureContainer(ISparkServiceContainer container) - { - container.SetServiceBuilder(c => new SparkViewFactory(c.GetService())); - container.SetServiceBuilder(c => new DefaultDescriptorBuilder()); - container.SetServiceBuilder(c => new DefaultCacheServiceProvider()); - } - - /// - /// Create a CSharp enabled Spark service container - /// - /// A configured service container. Additional service builders may - /// me added. - public static ISparkServiceContainer CreateContainer() - { - var container = new SparkServiceContainer(); - ConfigureContainer(container); - return container; - } - - /// - /// Create a CSharp enabled service container with explicit spark settings. - /// - /// Typically an instance of SparkSettings object - /// A configured service container. Additional service builders may - /// me added. May be passed to RegisterViewEngine. - public static ISparkServiceContainer CreateContainer(ISparkSettings settings) - { - var container = new SparkServiceContainer(settings); - ConfigureContainer(container); - return container; - } - - /// - /// Creates a spark IViewEngine with CSharp as the default language. - /// Settings come from config or are defaulted. - /// - /// An IViewEngine interface of the SparkViewFactory - public static IViewEngine CreateViewEngine() - { - return CreateContainer().GetService(); - } - - /// - /// Creates a spark IViewEngine with CSharp as the default language. - /// - /// Typically an instance of SparkSettings object - /// An IViewEngine interface of the SparkViewFactory - public static IViewEngine CreateViewEngine(ISparkSettings settings) - { - return CreateContainer(settings).GetService(); - } - - - /// - /// Installs the Spark view engine. Settings come from config or are defaulted. - /// - public static void RegisterViewEngine() - { - ViewEngines.Engines.Add(CreateViewEngine()); - } - - /// - /// Installs the Spark view engine. Settings passed in. - /// - public static void RegisterViewEngine(ISparkSettings settings) - { - ViewEngines.Engines.Add(CreateViewEngine(settings)); - } - - /// - /// Installs the Spark view engine. Container passed in. - /// - public static void RegisterViewEngine(ISparkServiceContainer container) - { - ViewEngines.Engines.Add(container.GetService()); - } - - /// - /// Installs the Spark view engine. Settings come from config or are defaulted. - /// - /// Typically in the ViewEngines.Engines collection - public static void RegisterViewEngine(ICollection engines) - { - engines.Add(CreateViewEngine()); - } - - /// - /// Installs the Spark view engine. Settings passed in. - /// - /// Typically in the ViewEngines.Engines collection - /// Typically an instance of SparkSettings object - public static void RegisterViewEngine(ICollection engines, ISparkSettings settings) - { - engines.Add(CreateViewEngine(settings)); - } - - /// - /// Install the view engine from the container. Typical usage is to call CreateContainer, - /// provide additinal service builder functors to override certain classes, then call this - /// method. - /// - /// Typically the ViewEngines.Engines collection - /// A service container, often created with CreateContainer - public static void RegisterViewEngine(ICollection engines, ISparkServiceContainer container) - { - engines.Add(container.GetService()); - } - } -} diff --git a/src/Spark.Web.Mvc/SparkViewFactory.cs b/src/Spark.Web.Mvc/SparkViewFactory.cs index 1ac07f1d..700d5911 100644 --- a/src/Spark.Web.Mvc/SparkViewFactory.cs +++ b/src/Spark.Web.Mvc/SparkViewFactory.cs @@ -14,11 +14,9 @@ // using System; using System.Collections.Generic; -using System.Configuration; using System.IO; using System.Linq; using System.Reflection; -using System.Threading; using System.Web.Mvc; using System.Web.Routing; using Spark.Compiler; @@ -27,92 +25,40 @@ namespace Spark.Web.Mvc { - public class SparkViewFactory : IViewEngine, IViewFolderContainer, ISparkServiceInitialize + public class SparkViewFactory : IViewEngine, IViewFolderContainer { - private ISparkViewEngine _engine; - private IDescriptorBuilder _descriptorBuilder; - private ICacheServiceProvider _cacheServiceProvider; - - - public SparkViewFactory() - : this(null) - { - } - - public SparkViewFactory(ISparkSettings settings) - { - Settings = settings ?? (ISparkSettings)ConfigurationManager.GetSection("spark") ?? new SparkSettings(); - } - - - public virtual void Initialize(ISparkServiceContainer container) - { - Settings = container.GetService(); - Engine = container.GetService(); - DescriptorBuilder = container.GetService(); - CacheServiceProvider = container.GetService(); - } - - public ISparkSettings Settings { get; set; } - - public ISparkViewEngine Engine + public ISparkSettings Settings { get; protected set; } + public ISparkViewEngine Engine { get; protected set; } + public IDescriptorBuilder DescriptorBuilder { get; protected set; } + public IResourcePathManager ResourcePathManager { get; protected set; } + public ICacheServiceProvider CacheServiceProvider { get; protected set; } + + private readonly Dictionary _cache; + private readonly ViewEngineResult _cacheMissResult; + + public SparkViewFactory(ISparkSettings settings, + ISparkViewEngine viewEngine, + IDescriptorBuilder descriptorBuilder, + IResourcePathManager resourcePathManager, + ICacheServiceProvider cacheServiceProvider) { - get - { - if (_engine == null) - SetEngine(new SparkViewEngine(Settings)); + Settings = settings; - return _engine; - } - set + if (string.IsNullOrEmpty(settings.PageBaseType)) { - SetEngine(value); + settings.PageBaseType = typeof(SparkView).FullName; } - } - public void SetEngine(ISparkViewEngine engine) - { - _descriptorBuilder = null; - _engine = engine; - if (_engine != null) - { - _engine.DefaultPageBaseType = typeof(SparkView).FullName; - } - } + Engine = viewEngine; + DescriptorBuilder = descriptorBuilder; + ResourcePathManager = resourcePathManager; + CacheServiceProvider = cacheServiceProvider; - public IViewActivatorFactory ViewActivatorFactory - { - get { return Engine.ViewActivatorFactory; } - set { Engine.ViewActivatorFactory = value; } + _cache = new Dictionary(); + _cacheMissResult = new ViewEngineResult(Array.Empty()); } - public IViewFolder ViewFolder - { - get { return Engine.ViewFolder; } - set { Engine.ViewFolder = value; } - } - - public IDescriptorBuilder DescriptorBuilder - { - get - { - return _descriptorBuilder ?? - Interlocked.CompareExchange(ref _descriptorBuilder, new DefaultDescriptorBuilder(Engine), null) ?? - _descriptorBuilder; - } - set { _descriptorBuilder = value; } - } - - public ICacheServiceProvider CacheServiceProvider - { - get - { - return _cacheServiceProvider ?? - Interlocked.CompareExchange(ref _cacheServiceProvider, new DefaultCacheServiceProvider(), null) ?? - _cacheServiceProvider; - } - set { _cacheServiceProvider = value; } - } + public IViewActivatorFactory ViewActivatorFactory => Engine.ViewActivatorFactory; public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName) { @@ -136,16 +82,12 @@ public virtual ViewEngineResult FindPartialView(ControllerContext controllerCont public virtual void ReleaseView(ControllerContext controllerContext, IView view) { - var sparkView = view as ISparkView; - if (sparkView != null) + if (view is ISparkView sparkView) + { Engine.ReleaseInstance(sparkView); + } } - private readonly Dictionary _cache = - new Dictionary(); - - private readonly ViewEngineResult _cacheMissResult = new ViewEngineResult(new string[0]); - private ViewEngineResult FindViewInternal(ControllerContext controllerContext, string viewName, string masterName, bool findDefaultMaster, bool useCache) { var searchedLocations = new List(); @@ -198,16 +140,16 @@ private void SetCacheValue(BuildDescriptorParams descriptorParams, ISparkViewEnt lock (_cache) _cache[descriptorParams] = entry; } - private ViewEngineResult BuildResult(RequestContext requestContext, ISparkViewEntry entry) { var view = (IView)entry.CreateInstance(); - if (view is SparkView) + + if (view is SparkView sparkView) { - var sparkView = (SparkView)view; - sparkView.ResourcePathManager = Engine.ResourcePathManager; + sparkView.ResourcePathManager = ResourcePathManager; sparkView.CacheService = CacheServiceProvider.GetCacheService(requestContext); } + return new ViewEngineResult(view, this); } @@ -233,17 +175,23 @@ public SparkViewDescriptor CreateDescriptor( searchedLocations); } - public SparkViewDescriptor CreateDescriptor(string targetNamespace, string controllerName, string viewName, - string masterName, bool findDefaultMaster) + public SparkViewDescriptor CreateDescriptor( + string targetNamespace, + string controllerName, + string viewName, + string masterName, + bool findDefaultMaster) { var searchedLocations = new List(); + var descriptor = DescriptorBuilder.BuildDescriptor( new BuildDescriptorParams( targetNamespace /*areaName*/, controllerName, viewName, masterName, - findDefaultMaster, null), + findDefaultMaster, + null), searchedLocations); if (descriptor == null) @@ -291,7 +239,7 @@ public IList CreateDescriptors(SparkBatchEntry entry) { if (include.EndsWith("*")) { - foreach (var fileName in ViewFolder.ListViews(controllerName)) + foreach (var fileName in Engine.ViewFolder.ListViews(controllerName)) { if (!string.Equals(Path.GetExtension(fileName), ".spark", StringComparison.InvariantCultureIgnoreCase)) { @@ -332,23 +280,25 @@ public IList CreateDescriptors(SparkBatchEntry entry) { if (entry.LayoutNames.Count == 0) { - descriptors.Add(CreateDescriptor( - entry.ControllerType.Namespace, - controllerName, - viewName, - null /*masterName*/, - true)); + descriptors.Add( + CreateDescriptor( + entry.ControllerType.Namespace, + controllerName, + viewName, + null /*masterName*/, + true)); } else { foreach (var masterName in entry.LayoutNames) { - descriptors.Add(CreateDescriptor( - entry.ControllerType.Namespace, - controllerName, - viewName, - string.Join(" ", masterName.ToArray()), - false)); + descriptors.Add( + CreateDescriptor( + entry.ControllerType.Namespace, + controllerName, + viewName, + string.Join(" ", masterName.ToArray()), + false)); } } } @@ -402,17 +352,6 @@ void IViewEngine.ReleaseView(ControllerContext controllerContext, IView view) #endregion - - #region ISparkServiceInitialize Members - - void ISparkServiceInitialize.Initialize(ISparkServiceContainer container) - { - Initialize(container); - } - - #endregion - - #region IViewFolderContainer Members IViewFolder IViewFolderContainer.ViewFolder diff --git a/src/Spark.Web.Tests/BatchCompilationTester.cs b/src/Spark.Web.Tests/BatchCompilationTester.cs index 4ed80c2e..ad18ffba 100644 --- a/src/Spark.Web.Tests/BatchCompilationTester.cs +++ b/src/Spark.Web.Tests/BatchCompilationTester.cs @@ -21,6 +21,8 @@ using Spark.FileSystem; using Spark.Tests.Precompiled; using System.IO; +using Microsoft.Extensions.DependencyInjection; +using Spark.Extensions; using Spark.Tests; namespace Spark @@ -36,14 +38,17 @@ public void Init() var settings = new SparkSettings() .SetPageBaseType(typeof(Tests.Stubs.StubSparkView)); - engine = new SparkViewEngine(settings) - { - ViewFolder = new InMemoryViewFolder - { - {Path.Combine("Home","Index.spark"), "

Hello world

"}, - {Path.Combine("Home","List.spark"), "
  1. one
  2. two
"} - } - }; + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton( + new InMemoryViewFolder + { + { Path.Combine("Home", "Index.spark"), "

Hello world

" }, + { Path.Combine("Home", "List.spark"), "
  1. one
  2. two
" } + }) + .BuildServiceProvider(); + + engine = (SparkViewEngine)sp.GetService(); } [Test] @@ -57,7 +62,11 @@ public void CompileMultipleDescriptors() var assembly = engine.BatchCompilation(descriptors); - var types = assembly.GetTypes(); + var types = + assembly + .GetTypes() + .Where(x => x.BaseType == typeof(Tests.Stubs.StubSparkView)); + Assert.AreEqual(2, types.Count()); var entry0 = engine.GetEntry(descriptors[0]); @@ -84,7 +93,12 @@ public void DescriptorsAreEqual() var assembly = engine.BatchCompilation(new[] { descriptor }); - var types = assembly.GetTypes(); + var types = + assembly + .GetTypes() + .Where(x => x.BaseType == typeof(Tests.Stubs.StubSparkView)) + .ToArray(); + Assert.AreEqual(1, types.Count()); var attribs = types[0].GetCustomAttributes(typeof(SparkViewAttribute), false); @@ -103,7 +117,12 @@ public void DescriptorsWithNoTargetNamespace() var assembly = engine.BatchCompilation(new[] { descriptor }); - var types = assembly.GetTypes(); + var types = + assembly + .GetTypes() + .Where(x => x.BaseType == typeof(Tests.Stubs.StubSparkView)) + .ToArray(); + Assert.AreEqual(1, types.Count()); var attribs = types[0].GetCustomAttributes(typeof(SparkViewAttribute), false); diff --git a/src/Spark.Web.Tests/Bindings/BindingExecutionTester.cs b/src/Spark.Web.Tests/Bindings/BindingExecutionTester.cs index 688ea456..be896215 100644 --- a/src/Spark.Web.Tests/Bindings/BindingExecutionTester.cs +++ b/src/Spark.Web.Tests/Bindings/BindingExecutionTester.cs @@ -1,7 +1,9 @@ using System; using System.IO; using System.Text; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.FileSystem; using Spark.Tests.Stubs; @@ -16,16 +18,20 @@ public class BindingExecutionTester [SetUp] public void Init() { - this._viewFolder = new InMemoryViewFolder(); + var settings = new SparkSettings().SetPageBaseType(typeof(StubSparkView)); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton() + .BuildServiceProvider(); + + this._viewFolder = (InMemoryViewFolder)sp.GetService(); + + var viewEngine = sp.GetService(); this._factory = new StubViewFactory { - Engine = new SparkViewEngine( - new SparkSettings() - .SetPageBaseType(typeof(StubSparkView))) - { - ViewFolder = this._viewFolder - } + Engine = viewEngine }; } diff --git a/src/Spark.Web.Tests/Caching/CacheElementTester.cs b/src/Spark.Web.Tests/Caching/CacheElementTester.cs index 93e29480..269f49c9 100644 --- a/src/Spark.Web.Tests/Caching/CacheElementTester.cs +++ b/src/Spark.Web.Tests/Caching/CacheElementTester.cs @@ -23,7 +23,9 @@ using System.IO; using System.Linq; using System.Text; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.FileSystem; using Spark.Tests.Stubs; @@ -39,16 +41,20 @@ public class CacheElementTester [SetUp] public void Init() { - this._viewFolder = new InMemoryViewFolder(); - this._cacheService = new StubCacheService(); + var settings = new SparkSettings().SetPageBaseType(typeof(StubSparkView)); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton() + .AddSingleton() + .BuildServiceProvider(); + + this._viewFolder = (InMemoryViewFolder) sp.GetService(); + this._cacheService = (StubCacheService) sp.GetService(); + this._factory = new StubViewFactory { - Engine = new SparkViewEngine( - new SparkSettings() - .SetPageBaseType(typeof(StubSparkView))) - { - ViewFolder = this._viewFolder - }, + Engine = (SparkViewEngine)sp.GetService(), CacheService = this._cacheService }; } diff --git a/src/Spark.Web.Tests/ClientsideCompilerTester.cs b/src/Spark.Web.Tests/ClientsideCompilerTester.cs index e85c0bb2..c5ad7641 100644 --- a/src/Spark.Web.Tests/ClientsideCompilerTester.cs +++ b/src/Spark.Web.Tests/ClientsideCompilerTester.cs @@ -14,7 +14,9 @@ // using System.IO; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.FileSystem; namespace Spark @@ -22,14 +24,30 @@ namespace Spark [TestFixture] public class ClientsideCompilerTester { + private static ServiceProvider CreateServiceProvider(ISparkSettings settings, IViewFolder viewFolder) + { + return new ServiceCollection() + .AddSpark(settings) + .AddSingleton(viewFolder) + .BuildServiceProvider(); + } + [Test] public void GenerateSimpleTemplate() { + var settings = new SparkSettings(); + + var viewFolder = new FileSystemViewFolder("Spark.Tests.Views"); + + var sp = CreateServiceProvider(settings, viewFolder); + + var engine = sp.GetService(); + var descriptor = new SparkViewDescriptor() .SetLanguage(LanguageType.Javascript) .AddTemplate(Path.Combine("Clientside","simple.spark")); - var engine = new SparkViewEngine { ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") }; + var entry = engine.CreateEntry(descriptor); Assert.IsNotNull(entry.SourceCode); @@ -39,11 +57,18 @@ public void GenerateSimpleTemplate() [Test] public void AnonymousTypeBecomesHashLikeObject() { + var settings = new SparkSettings(); + + var viewFolder = new FileSystemViewFolder("Spark.Tests.Views"); + + var sp = CreateServiceProvider(settings, viewFolder); + + var engine = sp.GetService(); + var descriptor = new SparkViewDescriptor() .SetLanguage(LanguageType.Javascript) .AddTemplate(Path.Combine("Clientside","AnonymousTypeBecomesHashLikeObject.spark")); - var engine = new SparkViewEngine { ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") }; var entry = engine.CreateEntry(descriptor); Assert.IsNotNull(entry.SourceCode); diff --git a/src/Spark.Web.Tests/Compiler/CSharpViewCompilerTester.cs b/src/Spark.Web.Tests/Compiler/CSharpViewCompilerTester.cs index 2a8afc86..61b5dbbe 100644 --- a/src/Spark.Web.Tests/Compiler/CSharpViewCompilerTester.cs +++ b/src/Spark.Web.Tests/Compiler/CSharpViewCompilerTester.cs @@ -15,7 +15,9 @@ using System.Collections.Generic; using NUnit.Framework; +using Spark.Compiler.CodeDom; using Spark.Compiler.CSharp; +using Spark.Compiler.Roslyn; using Spark.Tests; using Spark.Tests.Models; using Spark.Tests.Stubs; @@ -25,10 +27,12 @@ namespace Spark.Compiler [TestFixture] public class CSharpViewCompilerTester { + private IBatchCompiler batchCompiler; [SetUp] public void Init() { + this.batchCompiler = new RoslynBatchCompiler(); } private static void DoCompileView(ViewCompiler compiler, IList chunks) @@ -39,7 +43,7 @@ private static void DoCompileView(ViewCompiler compiler, IList chunks) [Test] public void MakeAndCompile() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; DoCompileView(compiler, new[] { new SendLiteralChunk { Text = "hello world" } }); @@ -54,7 +58,7 @@ public void MakeAndCompile() public void UnsafeLiteralCharacters() { var text = "hello\t\r\n\"world"; - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; DoCompileView(compiler, new[] { new SendLiteralChunk { Text = text } }); Assert.That(compiler.SourceCode.Contains("Write(\"hello\\t\\r\\n\\\"world\")")); @@ -68,7 +72,7 @@ public void UnsafeLiteralCharacters() [Test] public void SimpleOutput() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; DoCompileView(compiler, new[] { new SendExpressionChunk { Code = "3 + 4" } }); var instance = compiler.CreateInstance(); string contents = instance.RenderView(); @@ -79,7 +83,7 @@ public void SimpleOutput() [Test] public void LocalVariableDecl() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; DoCompileView(compiler, new Chunk[] { new LocalVariableChunk { Name = "i", Value = "5" }, @@ -94,7 +98,7 @@ public void LocalVariableDecl() [Test] public void ForEachLoop() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; DoCompileView(compiler, new Chunk[] { new LocalVariableChunk {Name = "data", Value = "new[]{3,4,5}"}, @@ -120,7 +124,7 @@ public void ForEachLoop() [Test] public void GlobalVariables() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; DoCompileView(compiler, new Chunk[] { new SendExpressionChunk{Code="title"}, @@ -139,11 +143,12 @@ public void GlobalVariables() [Test] public void TargetNamespace() { - var compiler = new CSharpViewCompiler - { - BaseClass = "Spark.SparkViewBase", - Descriptor = new SparkViewDescriptor { TargetNamespace = "Testing.Target.Namespace" } - }; + var compiler = new CSharpViewCompiler(this.batchCompiler) + { + BaseClass = "Spark.SparkViewBase", + Descriptor = new SparkViewDescriptor { TargetNamespace = "Testing.Target.Namespace" } + }; + DoCompileView(compiler, new Chunk[] { new SendLiteralChunk { Text = "Hello" } }); var instance = compiler.CreateInstance(); @@ -154,7 +159,7 @@ public void TargetNamespace() [Test] public void ProvideFullException() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; Assert.That( () => @@ -170,7 +175,7 @@ public void ProvideFullException() [Test] public void IfTrueCondition() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; @@ -193,7 +198,7 @@ public void IfTrueCondition() [Test] public void IfFalseCondition() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; @@ -216,7 +221,7 @@ public void IfFalseCondition() [Test] public void IfElseFalseCondition() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; var falseChunks = new Chunk[] { new SendLiteralChunk { Text = "wasfalse" } }; @@ -241,7 +246,7 @@ public void IfElseFalseCondition() [Test] public void UnlessTrueCondition() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; @@ -264,7 +269,7 @@ public void UnlessTrueCondition() [Test] public void UnlessFalseCondition() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; @@ -287,7 +292,7 @@ public void UnlessFalseCondition() [Test] public void LenientSilentNullDoesNotCauseWarningCS0168() { - var compiler = new CSharpViewCompiler() + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.Tests.Stubs.StubSparkView", NullBehaviour = NullBehaviour.Lenient @@ -307,7 +312,7 @@ public void LenientSilentNullDoesNotCauseWarningCS0168() [Test] public void LenientOutputNullDoesNotCauseWarningCS0168() { - var compiler = new CSharpViewCompiler() + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.Tests.Stubs.StubSparkView", NullBehaviour = NullBehaviour.Lenient @@ -326,7 +331,7 @@ public void LenientOutputNullDoesNotCauseWarningCS0168() [Test] public void StrictNullUsesException() { - var compiler = new CSharpViewCompiler() + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.Tests.Stubs.StubSparkView", NullBehaviour = NullBehaviour.Strict @@ -348,11 +353,12 @@ public void StrictNullUsesException() [Test] public void PageBaseTypeOverridesBaseClass() { - var compiler = new CSharpViewCompiler() - { - BaseClass = "Spark.Tests.Stubs.StubSparkView", - NullBehaviour = NullBehaviour.Strict - }; + var compiler = new CSharpViewCompiler(this.batchCompiler) + { + BaseClass = "Spark.Tests.Stubs.StubSparkView", + NullBehaviour = NullBehaviour.Strict + }; + DoCompileView(compiler, new Chunk[] { new PageBaseTypeChunk { BaseClass="Spark.Tests.Stubs.StubSparkView2"}, @@ -368,11 +374,11 @@ public void PageBaseTypeOverridesBaseClass() [Test] public void PageBaseTypeWorksWithOptionalModel() { - var compiler = new CSharpViewCompiler() - { - BaseClass = "Spark.Tests.Stubs.StubSparkView", - NullBehaviour = NullBehaviour.Strict - }; + var compiler = new CSharpViewCompiler(this.batchCompiler) + { + BaseClass = "Spark.Tests.Stubs.StubSparkView", + NullBehaviour = NullBehaviour.Strict + }; DoCompileView( compiler, @@ -392,11 +398,12 @@ public void PageBaseTypeWorksWithOptionalModel() [Test] public void PageBaseTypeWorksWithGenericParametersIncluded() { - var compiler = new CSharpViewCompiler() - { - BaseClass = "Spark.Tests.Stubs.StubSparkView", - NullBehaviour = NullBehaviour.Strict - }; + var compiler = new CSharpViewCompiler(this.batchCompiler) + { + BaseClass = "Spark.Tests.Stubs.StubSparkView", + NullBehaviour = NullBehaviour.Strict + }; + DoCompileView(compiler, new Chunk[] { new PageBaseTypeChunk {BaseClass = "Spark.Tests.Stubs.StubSparkView3"}, @@ -413,7 +420,7 @@ public void PageBaseTypeWorksWithGenericParametersIncluded() [Test] public void Markdown() { - var compiler = new CSharpViewCompiler { BaseClass = "Spark.SparkViewBase" }; + var compiler = new CSharpViewCompiler(this.batchCompiler) { BaseClass = "Spark.SparkViewBase" }; var innerChunks = new Chunk[] { new SendLiteralChunk { Text = "*test*" } }; diff --git a/src/Spark.Web.Tests/Compiler/SourceMappingTester.cs b/src/Spark.Web.Tests/Compiler/SourceMappingTester.cs index f3b39d41..59b31871 100644 --- a/src/Spark.Web.Tests/Compiler/SourceMappingTester.cs +++ b/src/Spark.Web.Tests/Compiler/SourceMappingTester.cs @@ -17,6 +17,10 @@ using Spark.FileSystem; using Spark.Tests.Stubs; using System.IO; +using Spark.Bindings; +using Spark.Compiler.Roslyn; +using Spark.Parser; +using Spark.Parser.Syntax; using Spark.Tests; namespace Spark.Compiler @@ -33,13 +37,25 @@ public void Init() { var settings = new SparkSettings() .SetPageBaseType(typeof(StubSparkView)); - var container = new SparkServiceContainer(settings); - _viewFolder = new InMemoryViewFolder(); + var partialProvider = new DefaultPartialProvider(); - container.SetServiceBuilder(c => _viewFolder); + _viewFolder = new InMemoryViewFolder(); - _engine = container.GetService(); + var batchCompiler = new RoslynBatchCompiler(); + + _engine = new SparkViewEngine( + settings, + new DefaultSyntaxProvider(settings), + new DefaultViewActivator(), + new DefaultLanguageFactory(batchCompiler), + new CompiledViewHolder(), + _viewFolder, + batchCompiler, + partialProvider, + new DefaultPartialReferenceProvider(partialProvider), + new DefaultBindingProvider(), + null); } private string RenderView(SparkViewDescriptor descriptor) diff --git a/src/Spark.Web.Tests/Compiler/VisualBasicViewCompilerTester.cs b/src/Spark.Web.Tests/Compiler/VisualBasicViewCompilerTester.cs index 7b567a00..3d87f227 100644 --- a/src/Spark.Web.Tests/Compiler/VisualBasicViewCompilerTester.cs +++ b/src/Spark.Web.Tests/Compiler/VisualBasicViewCompilerTester.cs @@ -16,6 +16,8 @@ using System; using System.Collections.Generic; using NUnit.Framework; +using Spark.Compiler.CodeDom; +using Spark.Compiler.Roslyn; using Spark.Compiler.VisualBasic; using Spark.Tests; using Spark.Tests.Models; @@ -26,10 +28,13 @@ namespace Spark.Compiler [TestFixture] public class VisualBasicViewCompilerTester { + private IBatchCompiler batchCompiler; [SetUp] public void Init() { + this.batchCompiler = + new RoslynBatchCompiler(); } private static void DoCompileView(ViewCompiler compiler, IList chunks) @@ -53,7 +58,7 @@ public void MakeAndCompile() [Test] public void StronglyTypedBase() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.Tests.Stubs.StubSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.Tests.Stubs.StubSparkView" }; DoCompileView(compiler, new Chunk[] { @@ -82,9 +87,9 @@ public void UnsafeLiteralCharacters() Assert.That(contents, Is.EqualTo(text)); } - private static VisualBasicViewCompiler CreateCompiler() + private VisualBasicViewCompiler CreateCompiler() { - return new VisualBasicViewCompiler + return new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView", UseAssemblies = new[] { "Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" }, @@ -95,7 +100,7 @@ private static VisualBasicViewCompiler CreateCompiler() [Test] public void SimpleOutput() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; DoCompileView(compiler, new[] { new SendExpressionChunk { Code = "3 + 4" } }); var instance = compiler.CreateInstance(); string contents = instance.RenderView(); @@ -142,7 +147,7 @@ public void RethrowNullBehavior() [Test] public void LocalVariableDecl() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; DoCompileView(compiler, new Chunk[] { new LocalVariableChunk { Name = "i", Value = "5" }, @@ -157,7 +162,7 @@ public void LocalVariableDecl() [Test] public void ForEachLoop() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; DoCompileView(compiler, new Chunk[] { new LocalVariableChunk {Name = "data", Value = "new Integer(){3,4,5}"}, @@ -183,7 +188,7 @@ public void ForEachLoop() [Test] public void ForEachAutoVariables() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; DoCompileView(compiler, new Chunk[] { new LocalVariableChunk {Name = "data", Value = "new Integer(){3,4,5}"}, @@ -213,7 +218,7 @@ public void ForEachAutoVariables() [Test] public void GlobalVariables() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; DoCompileView(compiler, new Chunk[] { new SendExpressionChunk{Code="title"}, @@ -233,7 +238,7 @@ public void GlobalVariables() [Platform(Exclude = "Mono", Reason = "Problems with Mono-2.10+/Linux and the VB compiler prevent this from running.")] public void TargetNamespace() { - var compiler = new VisualBasicViewCompiler + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView", Descriptor = new SparkViewDescriptor { TargetNamespace = "Testing.Target.Namespace" } @@ -248,19 +253,20 @@ public void TargetNamespace() [Test] public void ProvideFullException() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; + Assert.That(() => DoCompileView(compiler, new Chunk[] { new SendExpressionChunk {Code = "NoSuchVariable"} }), - Throws.TypeOf()); + Throws.TypeOf().Or.TypeOf()); } [Test] public void IfTrueCondition() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; @@ -280,7 +286,7 @@ public void IfTrueCondition() [Test] public void IfFalseCondition() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; @@ -300,7 +306,7 @@ public void IfFalseCondition() [Test] public void IfElseFalseCondition() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; var falseChunks = new Chunk[] { new SendLiteralChunk { Text = "wasfalse" } }; @@ -322,7 +328,7 @@ public void IfElseFalseCondition() [Test] public void UnlessTrueCondition() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; @@ -342,7 +348,7 @@ public void UnlessTrueCondition() [Test] public void UnlessFalseCondition() { - var compiler = new VisualBasicViewCompiler { BaseClass = "Spark.AbstractSparkView" }; + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.AbstractSparkView" }; var trueChunks = new Chunk[] { new SendLiteralChunk { Text = "wastrue" } }; @@ -362,7 +368,7 @@ public void UnlessFalseCondition() [Test] public void StrictNullUsesException() { - var compiler = new VisualBasicViewCompiler() + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.Tests.Stubs.StubSparkView", NullBehaviour = NullBehaviour.Strict @@ -382,7 +388,7 @@ public void StrictNullUsesException() [Test] public void PageBaseTypeOverridesBaseClass() { - var compiler = new VisualBasicViewCompiler() + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.Tests.Stubs.StubSparkView", NullBehaviour = NullBehaviour.Strict @@ -401,7 +407,7 @@ public void PageBaseTypeOverridesBaseClass() [Test] public void PageBaseTypeWorksWithOptionalModel() { - var compiler = new VisualBasicViewCompiler() + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.Tests.Stubs.StubSparkView", NullBehaviour = NullBehaviour.Strict @@ -421,7 +427,7 @@ public void PageBaseTypeWorksWithOptionalModel() [Test] public void PageBaseTypeWorksWithGenericParametersIncluded() { - var compiler = new VisualBasicViewCompiler() + var compiler = new VisualBasicViewCompiler(this.batchCompiler) { BaseClass = "Spark.Tests.Stubs.StubSparkView", NullBehaviour = NullBehaviour.Strict diff --git a/src/Spark.Web.Tests/Configuration/SparkSectionHandlerTester.cs b/src/Spark.Web.Tests/Configuration/SparkSectionHandlerTester.cs index a5b1eb03..0ae88148 100644 --- a/src/Spark.Web.Tests/Configuration/SparkSectionHandlerTester.cs +++ b/src/Spark.Web.Tests/Configuration/SparkSectionHandlerTester.cs @@ -16,7 +16,9 @@ using System.Configuration; using System.IO; using System.Linq; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.FileSystem; using Spark.Tests; using Spark.Tests.Stubs; @@ -79,12 +81,17 @@ public void UseAssemblyAndNamespaceFromSettings() .AddAssembly("System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") .SetPageBaseType(typeof(StubSparkView)); - var views = new InMemoryViewFolder + var viewFolder = new InMemoryViewFolder { { Path.Combine("home", "index.spark"), "
${ProcessStatus.Alive}
" } }; - var engine = new SparkViewEngine(settings) { ViewFolder = views }; + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(viewFolder) + .BuildServiceProvider(); + + var engine = (SparkViewEngine)sp.GetService(); var descriptor = new SparkViewDescriptor(); descriptor.Templates.Add(Path.Combine("home", "index.spark")); diff --git a/src/Spark.Web.Tests/Extensions/ServiceCollectionExtensions.cs b/src/Spark.Web.Tests/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..b5f996e1 --- /dev/null +++ b/src/Spark.Web.Tests/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,69 @@ +using System; +using System.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Spark.Bindings; +using Spark.Compiler; +using Spark.Compiler.Roslyn; +using Spark.FileSystem; +using Spark.Parser; +using Spark.Parser.Syntax; + +namespace Spark.Extensions +{ + // TODO: File duplicated in System.Web.Mvc + public static class ServiceCollectionExtensions + { + internal static string MissingSparkSettingsConfigurationErrorExceptionMessage + = "Spark setting not configured. Missing spark section app configuration or no ISparkSetting instance registered in IoC container."; + + /// + /// Registers spark dependencies in the service collection. + /// + /// + /// + /// + public static IServiceCollection AddSpark(this IServiceCollection services, ISparkSettings settings = null) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (settings == null) + { + settings = (ISparkSettings)ConfigurationManager.GetSection("spark"); + + if (settings == null) + { + throw new ConfigurationErrorsException(MissingSparkSettingsConfigurationErrorExceptionMessage); + } + } + + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + + services + .AddSingleton(settings) + .AddSingleton(settings) + .AddSingleton() + + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(settings.CreateDefaultViewFolder()) + .AddSingleton() + .AddSingleton() + .AddSingleton(); + + services + .AddSingleton(c => null); + + return services; + } + } +} diff --git a/src/Spark.Web.Tests/FileSystem/InMemoryViewFolderTester.cs b/src/Spark.Web.Tests/FileSystem/InMemoryViewFolderTester.cs index 4a9647ef..d93144dd 100644 --- a/src/Spark.Web.Tests/FileSystem/InMemoryViewFolderTester.cs +++ b/src/Spark.Web.Tests/FileSystem/InMemoryViewFolderTester.cs @@ -15,7 +15,9 @@ using System.IO; using System.Linq; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.Tests; using Spark.Tests.Stubs; @@ -119,9 +121,19 @@ public void LastModifiedChanges() [Test] public void InMemoryViewFolderUsedByEngine() { - var folder = new InMemoryViewFolder(); - folder.Add(Path.Combine("home", "index.spark"), "

Hello world

"); - var engine = new SparkViewEngine(new SparkSettings().SetPageBaseType(typeof (StubSparkView))){ViewFolder = folder}; + var viewFolder = new InMemoryViewFolder + { + { Path.Combine("home", "index.spark"), "

Hello world

" } + }; + + var settings = new SparkSettings().SetPageBaseType(typeof(StubSparkView)); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(viewFolder) + .BuildServiceProvider(); + + var engine = sp.GetService(); var descriptor = new SparkViewDescriptor(); descriptor.Templates.Add(Path.Combine("home", "index.spark")); @@ -154,17 +166,26 @@ private static string RenderView(ISparkViewEngine engine, string path) [Test] public void UnicodeCharactersSurviveConversionToByteArrayAndBack() { - var folder = new InMemoryViewFolder(); - folder.Add(Path.Combine("Home", "fr.spark"), "Fran\u00E7ais"); - folder.Add(Path.Combine("Home", "ru.spark"), "\u0420\u0443\u0441\u0441\u043A\u0438\u0439"); - folder.Add(Path.Combine("Home", "ja.spark"), "\u65E5\u672C\u8A9E"); - - Assert.That(ReadToEnd(folder, Path.Combine("Home", "fr.spark")), Is.EqualTo("Français")); - Assert.That(ReadToEnd(folder, Path.Combine("Home", "ru.spark")), Is.EqualTo("Русский")); - Assert.That(ReadToEnd(folder, Path.Combine("Home", "ja.spark")), Is.EqualTo("日本語")); + var viewFolder = new InMemoryViewFolder + { + { Path.Combine("Home", "fr.spark"), "Fran\u00E7ais" }, + { Path.Combine("Home", "ru.spark"), "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" }, + { Path.Combine("Home", "ja.spark"), "\u65E5\u672C\u8A9E" } + }; + + Assert.That(ReadToEnd(viewFolder, Path.Combine("Home", "fr.spark")), Is.EqualTo("Français")); + Assert.That(ReadToEnd(viewFolder, Path.Combine("Home", "ru.spark")), Is.EqualTo("Русский")); + Assert.That(ReadToEnd(viewFolder, Path.Combine("Home", "ja.spark")), Is.EqualTo("日本語")); var settings = new SparkSettings().SetPageBaseType(typeof(StubSparkView)); - var engine = new SparkViewEngine(settings) { ViewFolder = folder }; + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(viewFolder) + .BuildServiceProvider(); + + var engine = sp.GetService(); + Assert.That(RenderView(engine, Path.Combine("Home", "fr.spark")), Is.EqualTo("Français")); Assert.That(RenderView(engine, Path.Combine("Home", "ru.spark")), Is.EqualTo("Русский")); Assert.That(RenderView(engine, Path.Combine("Home", "ja.spark")), Is.EqualTo("日本語")); diff --git a/src/Spark.Web.Tests/FileSystem/ViewFolderSettingsTester.cs b/src/Spark.Web.Tests/FileSystem/ViewFolderSettingsTester.cs index ad536625..ad661c86 100644 --- a/src/Spark.Web.Tests/FileSystem/ViewFolderSettingsTester.cs +++ b/src/Spark.Web.Tests/FileSystem/ViewFolderSettingsTester.cs @@ -25,9 +25,13 @@ public class ViewFolderSettingsTester [Test] public void ApplySettings() { - var settings = new SparkSettings() - .AddViewFolder(typeof(VirtualPathProviderViewFolder), new Dictionary { { "virtualBaseDir", "~/MoreViews/" } }); - var engine = new SparkViewEngine(settings); + var settings = + new SparkSettings() + .AddViewFolder( + typeof(VirtualPathProviderViewFolder), + new Dictionary { { "virtualBaseDir", "~/MoreViews/" } }); + + var engine = new SparkViewEngine(settings, null, null, null, null, null, null, null, null, null, null); var folder = engine.ViewFolder; @@ -44,8 +48,11 @@ public void ApplySettings() public void CustomViewFolder() { var settings = new SparkSettings() - .AddViewFolder(typeof(MyViewFolder), new Dictionary { { "foo", "quux" }, { "bar", "42" } }); - var engine = new SparkViewEngine(settings); + .AddViewFolder( + typeof(MyViewFolder), + new Dictionary { { "foo", "quux" }, { "bar", "42" } }); + + var engine = new SparkViewEngine(settings, null, null, null, null, null, null, null, null, null, null); var folder = engine.ViewFolder; Assert.IsAssignableFrom(typeof(CombinedViewFolder), folder); @@ -60,9 +67,12 @@ public void CustomViewFolder() public void AssemblyParameter() { var settings = new SparkSettings() - .AddViewFolder(typeof(EmbeddedViewFolder), new Dictionary { { "assembly", "Spark.Tests" }, { "resourcePath", "Spark.Tests.Views" } }); + .AddViewFolder( + typeof(EmbeddedViewFolder), + new Dictionary + { { "assembly", "Spark.Tests" }, { "resourcePath", "Spark.Tests.Views" } }); - var engine = new SparkViewEngine(settings); + var engine = new SparkViewEngine(settings, null, null, null, null, null, null, null, null, null, null); var folder = engine.ViewFolder; Assert.IsAssignableFrom(typeof(CombinedViewFolder), folder); @@ -76,11 +86,14 @@ public void AssemblyParameter() public void TypeFileSystemCreatesFileSystemViewFolder() { var settings = new SparkSettings() - .AddViewFolder(typeof(FileSystemViewFolder), new Dictionary - { - { "basePath", @"e:\no\such\path" } - }); - var engine = new SparkViewEngine(settings); + .AddViewFolder( + typeof(FileSystemViewFolder), + new Dictionary + { + { "basePath", @"e:\no\such\path" } + }); + + var engine = new SparkViewEngine(settings, null, null, null, null, null, null, null, null, null, null); var folder = engine.ViewFolder; Assert.IsAssignableFrom(typeof(CombinedViewFolder), folder); var combined = (CombinedViewFolder)folder; diff --git a/src/Spark.Web.Tests/ImportAndIncludeTester.cs b/src/Spark.Web.Tests/ImportAndIncludeTester.cs index 04875575..b4b6d0b6 100644 --- a/src/Spark.Web.Tests/ImportAndIncludeTester.cs +++ b/src/Spark.Web.Tests/ImportAndIncludeTester.cs @@ -14,8 +14,10 @@ // using System.IO; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using Spark.Compiler; +using Spark.Extensions; using Spark.FileSystem; using Spark.Tests; using Spark.Tests.Stubs; @@ -29,10 +31,12 @@ private ISparkView CreateView(IViewFolder viewFolder, string template) { var settings = new SparkSettings().SetPageBaseType(typeof(StubSparkView)); - var engine = new SparkViewEngine(settings) - { - ViewFolder = viewFolder - }; + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(viewFolder) + .BuildServiceProvider(); + + var engine = (SparkViewEngine)sp.GetService(); return engine.CreateInstance(new SparkViewDescriptor().AddTemplate(template)); } diff --git a/src/Spark.Web.Tests/Parser/AutomaticEncodingTester.cs b/src/Spark.Web.Tests/Parser/AutomaticEncodingTester.cs index 73f6f99e..2674bddb 100644 --- a/src/Spark.Web.Tests/Parser/AutomaticEncodingTester.cs +++ b/src/Spark.Web.Tests/Parser/AutomaticEncodingTester.cs @@ -15,7 +15,9 @@ using System.IO; using System.Linq; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.FileSystem; using Spark.Parser.Markup; using Spark.Tests; @@ -42,13 +44,15 @@ public void Init(bool automaticEncoding) this._settings = new SparkSettings() .SetPageBaseType(typeof(StubSparkView)) .SetAutomaticEncoding(automaticEncoding); - var container = new SparkServiceContainer(this._settings); this._viewFolder = new InMemoryViewFolder(); - container.SetServiceBuilder(c => this._viewFolder); + var sp = new ServiceCollection() + .AddSpark(_settings) + .AddSingleton(_viewFolder) + .BuildServiceProvider(); - this._engine = container.GetService(); + _engine = sp.GetService(); } private string RenderView(SparkViewDescriptor descriptor) diff --git a/src/Spark.Web.Tests/Parser/CSharpSyntaxProviderTester.cs b/src/Spark.Web.Tests/Parser/CSharpSyntaxProviderTester.cs index 00bc0b4b..5adeec30 100644 --- a/src/Spark.Web.Tests/Parser/CSharpSyntaxProviderTester.cs +++ b/src/Spark.Web.Tests/Parser/CSharpSyntaxProviderTester.cs @@ -14,8 +14,10 @@ // using System.IO; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using Spark.Compiler.NodeVisitors; +using Spark.Extensions; using Spark.FileSystem; using Spark.Parser.Syntax; using Spark.Tests; @@ -31,7 +33,11 @@ public class CSharpSyntaxProviderTester [Test] public void CanParseSimpleFile() { - var context = new VisitorContext { ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") }; + var context = new VisitorContext + { + ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") + }; + var result = this._syntax.GetChunks(context, Path.Combine("Home", "childview.spark")); Assert.IsNotNull(result); @@ -40,12 +46,14 @@ public void CanParseSimpleFile() [Test] public void UsingCSharpSyntaxInsideEngine() { - // engine takes base class and IViewFolder - var engine = new SparkViewEngine(new SparkSettings().SetPageBaseType("Spark.Tests.Stubs.StubSparkView")) - { - SyntaxProvider = this._syntax, - ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") - }; + var settings = new SparkSettings().SetPageBaseType("Spark.Tests.Stubs.StubSparkView"); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(new FileSystemViewFolder("Spark.Tests.Views")) + .BuildServiceProvider(); + + var engine = sp.GetService(); // describe and instantiate view var descriptor = new SparkViewDescriptor(); @@ -62,9 +70,15 @@ public void UsingCSharpSyntaxInsideEngine() [Test] public void StatementAndExpressionInCode() { - // engine takes base class and IViewFolder - var engine = new SparkViewEngine( - new SparkSettings().SetPageBaseType("Spark.Tests.Stubs.StubSparkView")) { SyntaxProvider = this._syntax, ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") }; + var settings = new SparkSettings().SetPageBaseType("Spark.Tests.Stubs.StubSparkView"); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(new FileSystemViewFolder("Spark.Tests.Views")) + .AddSingleton(this._syntax) + .BuildServiceProvider(); + + var engine = sp.GetService(); // describe and instantiate view var descriptor = new SparkViewDescriptor(); diff --git a/src/Spark.Web.Tests/PartialProviderTester.cs b/src/Spark.Web.Tests/PartialProviderTester.cs index 0374715c..6ac97664 100644 --- a/src/Spark.Web.Tests/PartialProviderTester.cs +++ b/src/Spark.Web.Tests/PartialProviderTester.cs @@ -1,13 +1,15 @@ +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; using Rhino.Mocks; +using Spark.Extensions; namespace Spark { [TestFixture] public class PartialProviderTester { - private SparkViewEngine _engine; private IPartialProvider _partialProvider; + private IPartialReferenceProvider _partialReferenceProvider; private string _viewPath; private string[] _result; @@ -17,10 +19,14 @@ public void Init() this._viewPath = "fake/path"; this._partialProvider = MockRepository.GenerateMock(); - this._engine = new SparkViewEngine(new SparkSettings()) - { - PartialProvider = this._partialProvider - }; + + var sp = new ServiceCollection() + .AddSpark(new SparkSettings()) + .AddSingleton(this._partialProvider) + .BuildServiceProvider(); + + _partialReferenceProvider = sp.GetService(); + this._result = new[] {"output"}; } @@ -28,26 +34,11 @@ public void Init() public void DefaultPartialReferenceProviderWrapsPartialProvider() { this._partialProvider.Expect(x => x.GetPaths(this._viewPath)).Return(this._result); - var output = this._engine.PartialReferenceProvider.GetPaths(this._viewPath, true); - this._partialProvider.VerifyAllExpectations(); - Assert.AreEqual(this._result, output); - } - - [Test] - public void SettingNewPartialProviderPropogatesToDefaultPartialProvider() - { - var differentPartialProvider = MockRepository.GenerateMock(); - this._engine.PartialProvider = differentPartialProvider; - - //should not call the original - this._partialProvider.Expect(x => x.GetPaths(this._viewPath)).Repeat.Never(); - - //should call the newly provided instance - differentPartialProvider.Expect(x => x.GetPaths(this._viewPath)).Return(this._result); - - var output = this._engine.PartialReferenceProvider.GetPaths(this._viewPath, true); + + var output = _partialReferenceProvider.GetPaths(this._viewPath, true); + this._partialProvider.VerifyAllExpectations(); - differentPartialProvider.VerifyAllExpectations(); + Assert.AreEqual(this._result, output); } } diff --git a/src/Spark.Web.Tests/PrefixSupportTester.cs b/src/Spark.Web.Tests/PrefixSupportTester.cs index 24bd9379..12c6cadc 100644 --- a/src/Spark.Web.Tests/PrefixSupportTester.cs +++ b/src/Spark.Web.Tests/PrefixSupportTester.cs @@ -14,7 +14,9 @@ // using System.IO; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.FileSystem; using Spark.Tests.Stubs; @@ -32,10 +34,12 @@ public void Init() _settings = new SparkSettings() .SetPageBaseType(typeof(StubSparkView)); - _engine = new SparkViewEngine(_settings) - { - ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") - }; + var sp = new ServiceCollection() + .AddSpark(_settings) + .AddSingleton(new FileSystemViewFolder("Spark.Tests.Views")) + .BuildServiceProvider(); + + _engine = (SparkViewEngine)sp.GetService(); } static void ContainsInOrder(string content, params string[] values) @@ -56,10 +60,12 @@ public void PrefixFromSettings() .SetPageBaseType(typeof(StubSparkView)) .SetPrefix("s"); - var engine = new SparkViewEngine(settings) - { - ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") - }; + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(new FileSystemViewFolder("Spark.Tests.Views")) + .BuildServiceProvider(); + + var engine = (SparkViewEngine)sp.GetService(); var view = (StubSparkView)engine.CreateInstance(new SparkViewDescriptor().AddTemplate(Path.Combine("Prefix", "prefix-from-settings.spark"))); view.ViewData["Names"] = new[] { "alpha", "beta", "gamma" }; @@ -177,13 +183,15 @@ public void SegmentAndRenderPrefixes() public void SectionAsSegmentAndRenderPrefixes() { var settings = new SparkSettings() - .SetPageBaseType(typeof (StubSparkView)) + .SetPageBaseType(typeof(StubSparkView)) .SetParseSectionTagAsSegment(true); - var engine = new SparkViewEngine(settings) - { - ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") - }; + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(new FileSystemViewFolder("Spark.Tests.Views")) + .BuildServiceProvider(); + + var engine = (SparkViewEngine)sp.GetService(); var view = (StubSparkView) @@ -206,8 +214,7 @@ public void SectionAsSegmentAndRenderPrefixes() [Test] public void MacroAndContentPrefixesFromSettings() { - _engine.Settings = new SparkSettings() - .SetPageBaseType(typeof(StubSparkView)) + this._settings.SetPageBaseType(typeof(StubSparkView)) .SetPrefix("s"); var view = diff --git a/src/Spark.Web.Tests/SparkExtensionTester.cs b/src/Spark.Web.Tests/SparkExtensionTester.cs index 91e4f437..c490c59b 100644 --- a/src/Spark.Web.Tests/SparkExtensionTester.cs +++ b/src/Spark.Web.Tests/SparkExtensionTester.cs @@ -21,6 +21,8 @@ using Spark.FileSystem; using Spark.Parser.Markup; using System.IO; +using Microsoft.Extensions.DependencyInjection; +using Spark.Extensions; namespace Spark { @@ -32,9 +34,15 @@ public class SparkExtensionTester [SetUp] public void Init() { - engine = new SparkViewEngine(new SparkSettings().SetPageBaseType("Spark.Tests.Stubs.StubSparkView")) - {ViewFolder = new FileSystemViewFolder("Spark.Tests.Views")}; - engine.ExtensionFactory = new StubExtensionFactory(); + var settings = new SparkSettings().SetPageBaseType("Spark.Tests.Stubs.StubSparkView"); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(new FileSystemViewFolder("Spark.Tests.Views")) + .AddSingleton(new StubExtensionFactory()) + .BuildServiceProvider(); + + engine = (SparkViewEngine)sp.GetService(); } [Test] diff --git a/src/Spark.Web.Tests/SparkServiceContainerTester.cs b/src/Spark.Web.Tests/SparkServiceContainerTester.cs deleted file mode 100644 index 6cb74cbc..00000000 --- a/src/Spark.Web.Tests/SparkServiceContainerTester.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2008-2009 Louis DeJardin - http://whereslou.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System; -using System.Collections.Generic; -using System.Configuration; -using NUnit.Framework; -using Spark.Bindings; -using Spark.FileSystem; -using System.IO; - -namespace Spark -{ - [TestFixture] - public class SparkServiceContainerTester - { - [Test] - public void ContainerCreatesDefaultServices() - { - var container = new SparkServiceContainer(); - - var langauageFactory = container.GetService(); - Assert.IsInstanceOf(typeof(DefaultLanguageFactory), langauageFactory); - - var resourcePathManager = container.GetService(); - Assert.IsInstanceOf(typeof(DefaultResourcePathManager), resourcePathManager); - - var bindingProvider = container.GetService(); - Assert.IsInstanceOf(typeof(DefaultBindingProvider), bindingProvider); - - var partialProvider = container.GetService(); - Assert.IsInstanceOf(typeof(DefaultPartialProvider), partialProvider); - - var partialReferenceProvider = container.GetService(); - Assert.IsInstanceOf(typeof(DefaultPartialReferenceProvider), partialReferenceProvider); - } - - [Test] - public void ConfigSettingsUsedByDefault() - { - var container = new SparkServiceContainer(); - - var settings = container.GetService().Settings; - Assert.AreSame(ConfigurationManager.GetSection("spark"), settings); - } - - [Test] - public void CreatedSettingsUsedWhenProvided() - { - var settings = new SparkSettings().SetPrefix("foo"); - var container = new SparkServiceContainer(settings); - - var settings2 = container.GetService().Settings; - Assert.AreSame(settings, settings2); - } - - [Test] - public void SettingsServiceReplacesType() - { - var container = new SparkServiceContainer(); - container.SetService(new StubExtensionFactory()); - Assert.IsInstanceOf(typeof(StubExtensionFactory), container.GetService()); - } - - [Test] - public void AddingServiceInstanceCallsInitialize() - { - var container = new SparkServiceContainer(); - var service = new TestService(); - Assert.IsFalse(service.Initialized); - container.SetService(service); - Assert.IsTrue(service.Initialized); - Assert.AreSame(service, container.GetService()); - } - - [Test] - public void AddingServiceBuilderCallsInitialize() - { - var container = new SparkServiceContainer(); - container.SetServiceBuilder(typeof(ITestService), c => new TestService()); - var service = container.GetService(); - Assert.IsInstanceOf(typeof(TestService), service); - Assert.IsTrue(((TestService)service).Initialized); - } - - [Test] - public void EngineGetsCustomServiceAndViewFolderSettings() - { - var settings = new SparkSettings(); - settings.AddViewFolder(typeof(TestViewFolder), - new Dictionary { { "testpath", Path.Combine("hello", "world.spark") } }); - - var container = new SparkServiceContainer(settings); - container.SetServiceBuilder(c=>new TestActivatorFactory()); - - var engine = container.GetService(); - Assert.IsInstanceOf(typeof(TestActivatorFactory), engine.ViewActivatorFactory); - - Assert.IsTrue(engine.ViewFolder.HasView(Path.Combine("hello", "world.spark"))); - } - } - - public interface ITestService - { - - } - public class TestService : ITestService, ISparkServiceInitialize - { - public bool Initialized { get; set; } - public void Initialize(ISparkServiceContainer container) - { - Initialized = true; - } - } - public class TestViewFolder : IViewFolder - { - private readonly string _testpath; - - public TestViewFolder(string testpath) - { - _testpath = testpath; - } - - public IViewFile GetViewSource(string path) - { - throw new System.NotImplementedException(); - } - - public IList ListViews(string path) - { - throw new System.NotImplementedException(); - } - - public bool HasView(string path) - { - return path == _testpath; - } - } - public class TestActivatorFactory : IViewActivatorFactory - { - public IViewActivator Register(Type type) - { - throw new System.NotImplementedException(); - } - - public void Unregister(Type type, IViewActivator activator) - { - throw new System.NotImplementedException(); - } - } -} diff --git a/src/Spark.Web.Tests/SparkViewFactoryTester.cs b/src/Spark.Web.Tests/SparkViewFactoryTester.cs index a7373d62..fe734fb5 100644 --- a/src/Spark.Web.Tests/SparkViewFactoryTester.cs +++ b/src/Spark.Web.Tests/SparkViewFactoryTester.cs @@ -20,18 +20,20 @@ // John Gietzen //------------------------------------------------------------------------- +using Microsoft.Extensions.DependencyInjection; +using Spark.Extensions; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; + +using Rhino.Mocks; +using Spark.Compiler; +using Spark.FileSystem; +using Spark.Tests.Models; +using Spark.Tests.Stubs; + namespace Spark.Tests { - using System.Collections.Generic; - using System.Text; - using NUnit.Framework; - - using Rhino.Mocks; - using Spark.Compiler; - using Spark.FileSystem; - using Spark.Tests.Models; - using Spark.Tests.Stubs; - [TestFixture, Category("SparkViewEngine")] public class SparkViewFactoryTester { @@ -46,7 +48,14 @@ public class SparkViewFactoryTester public void Init() { settings = new SparkSettings().SetPageBaseType("Spark.Tests.Stubs.StubSparkView"); - engine = new SparkViewEngine(settings) { ViewFolder = new FileSystemViewFolder("Spark.Tests.Views") }; + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton(new FileSystemViewFolder("Spark.Tests.Views")) + .BuildServiceProvider(); + + engine = (SparkViewEngine)sp.GetService(); + factory = new StubViewFactory { Engine = engine }; sb = new StringBuilder(); diff --git a/src/Spark.Web.Tests/ViewActivatorTester.cs b/src/Spark.Web.Tests/ViewActivatorTester.cs index ccf4ac22..d1ced9e8 100644 --- a/src/Spark.Web.Tests/ViewActivatorTester.cs +++ b/src/Spark.Web.Tests/ViewActivatorTester.cs @@ -15,7 +15,9 @@ using System; using System.Diagnostics; using System.IO; +using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using Spark.Extensions; using Spark.FileSystem; using Spark.Tests.Stubs; @@ -88,12 +90,15 @@ public void FastCreateViewInstance() [Test] public void CustomViewActivator() { - var engine = new SparkViewEngine( - new SparkSettings().SetPageBaseType(typeof(StubSparkView))) - { - ViewActivatorFactory = new CustomFactory(), - ViewFolder = new InMemoryViewFolder { { "hello/world.spark", "

hello world

" } } - }; + var settings = new SparkSettings().SetPageBaseType(typeof(StubSparkView)); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton() + .AddSingleton(new InMemoryViewFolder { { "hello/world.spark", "

hello world

" } }) + .BuildServiceProvider(); + + var engine = (SparkViewEngine) sp.GetService(); var descriptor = new SparkViewDescriptor().AddTemplate("hello/world.spark"); var view = engine.CreateInstance(descriptor); diff --git a/src/Spark.Web.Tests/Visitors/DetectCodeExpressionTester.cs b/src/Spark.Web.Tests/Visitors/DetectCodeExpressionTester.cs index e1a4a460..3b3dba30 100644 --- a/src/Spark.Web.Tests/Visitors/DetectCodeExpressionTester.cs +++ b/src/Spark.Web.Tests/Visitors/DetectCodeExpressionTester.cs @@ -33,8 +33,10 @@ public void FindLoopParameters() { SyntaxProvider = new DefaultSyntaxProvider(new SparkSettings()) }; - var nodes = ParseNodes("${xIndex}${xIsLast}", - new SpecialNodeVisitor(context)); + + var nodes = ParseNodes( + "${xIndex}${xIsLast}", + new SpecialNodeVisitor(context)); var visitor = new ChunkBuilderVisitor(context); visitor.Accept(nodes); @@ -56,11 +58,23 @@ public void FindLoopParameters() public void ParametersInPartial() { var viewFolder = new InMemoryViewFolder - { - {Path.Combine("home", "index.spark"), ""}, - {Path.Combine("home", "_Guts.spark"), "

${xIndex}

"} - }; - var loader = new ViewLoader { SyntaxProvider = new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), ViewFolder = viewFolder }; + { + { Path.Combine("home", "index.spark"), "" }, + { Path.Combine("home", "_Guts.spark"), "

${xIndex}

" } + }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var loader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); var chunks = loader.Load(Path.Combine("home", "index.spark")); @@ -78,11 +92,23 @@ public void ParametersInPartial() public void ParametersInCallerBody() { var viewFolder = new InMemoryViewFolder - { - {Path.Combine("home", "index.spark"), "${xIndex}"}, - {Path.Combine("home", "_Guts.spark"), "

"} - }; - var loader = new ViewLoader { SyntaxProvider = new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), ViewFolder = viewFolder }; + { + { Path.Combine("home", "index.spark"), "${xIndex}" }, + { Path.Combine("home", "_Guts.spark"), "

" } + }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var loader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); var chunks = loader.Load(Path.Combine("home", "index.spark")); @@ -99,11 +125,23 @@ public void ParametersInCallerBody() public void ParametersInNamedSegment() { var viewFolder = new InMemoryViewFolder - { - {Path.Combine("home", "index.spark"), "${xIndex}"}, - {Path.Combine("home", "_Guts.spark"), "

"} - }; - var loader = new ViewLoader { SyntaxProvider = new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), ViewFolder = viewFolder }; + { + { Path.Combine("home", "index.spark"), "${xIndex}" }, + { Path.Combine("home", "_Guts.spark"), "

" } + }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var loader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); var chunks = loader.Load(Path.Combine("home", "index.spark")); @@ -120,11 +158,23 @@ public void ParametersInNamedSegment() public void IterationInPartialFile() { var viewFolder = new InMemoryViewFolder - { - {Path.Combine("home", "index.spark"), "${xIndex}"}, - {Path.Combine("home", "_Guts.spark"), ""} - }; - var loader = new ViewLoader { SyntaxProvider = new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), ViewFolder = viewFolder }; + { + { Path.Combine("home", "index.spark"), "${xIndex}" }, + { Path.Combine("home", "_Guts.spark"), "" } + }; + + var settings = new SparkSettings(); + + var partialProvider = new DefaultPartialProvider(); + + var loader = new ViewLoader( + settings, + viewFolder, + new DefaultPartialProvider(), + new DefaultPartialReferenceProvider(partialProvider), + null, + new DefaultSyntaxProvider(ParserSettings.DefaultBehavior), + null); var chunks = loader.Load(Path.Combine("home", "index.spark")); diff --git a/src/Spark.Web.Tests/VisualBasicViewTester.cs b/src/Spark.Web.Tests/VisualBasicViewTester.cs index 667bc85f..44a8097f 100644 --- a/src/Spark.Web.Tests/VisualBasicViewTester.cs +++ b/src/Spark.Web.Tests/VisualBasicViewTester.cs @@ -4,6 +4,8 @@ using Spark.Tests.Models; using Spark.Tests.Stubs; using System.IO; +using Microsoft.Extensions.DependencyInjection; +using Spark.Extensions; namespace Spark { @@ -17,16 +19,19 @@ public class VisualBasicViewTester [SetUp] public void Init() { - _viewFolder = new InMemoryViewFolder(); + var settings = new SparkSettings() + .SetDefaultLanguage(LanguageType.VisualBasic) + .SetPageBaseType(typeof(StubSparkView)); + + var sp = new ServiceCollection() + .AddSpark(settings) + .AddSingleton() + .BuildServiceProvider(); + + _viewFolder = (InMemoryViewFolder)sp.GetService(); _factory = new StubViewFactory { - Engine = new SparkViewEngine( - new SparkSettings() - .SetDefaultLanguage(LanguageType.VisualBasic) - .SetPageBaseType(typeof(StubSparkView))) - { - ViewFolder = _viewFolder - } + Engine = (SparkViewEngine)sp.GetService() }; } diff --git a/src/Spark.sln.DotSettings b/src/Spark.sln.DotSettings new file mode 100644 index 00000000..acf0b017 --- /dev/null +++ b/src/Spark.sln.DotSettings @@ -0,0 +1,3 @@ + + DO_NOT_SHOW + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> \ No newline at end of file diff --git a/src/Spark/Compiler/AssemblyExtensions.cs b/src/Spark/Compiler/AssemblyExtensions.cs new file mode 100644 index 00000000..45b89a40 --- /dev/null +++ b/src/Spark/Compiler/AssemblyExtensions.cs @@ -0,0 +1,44 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace Spark.Compiler +{ + public static class AssemblyExtensions + { + public static bool IsDynamic(this Assembly assembly) + { +#if NETFRAMEWORK || NET + if (assembly is AssemblyBuilder) + { + return true; + } +#endif + +#if NETFRAMEWORK + if (assembly.ManifestModule.GetType().Namespace == "System.Reflection.Emit" /* .Net 4 specific */) + { + return true; + } +#endif + + return assembly.HasNoLocation(); + } + + private static bool HasNoLocation(this Assembly assembly) + { + bool result; + + try + { + result = string.IsNullOrEmpty(assembly.Location); + } + catch (NotSupportedException) + { + return true; + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/Spark/Compiler/BatchCompilerException.cs b/src/Spark/Compiler/BatchCompilerException.cs deleted file mode 100644 index de8a2527..00000000 --- a/src/Spark/Compiler/BatchCompilerException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.CodeDom.Compiler; - -namespace Spark.Compiler -{ - public class BatchCompilerException : CompilerException - { - public BatchCompilerException(string message, CompilerResults results) : base(message) - { - Results = results; - } - - public CompilerResults Results { get; set; } - } -} \ No newline at end of file diff --git a/src/Spark/Compiler/CSharp/CSharpViewCompiler.cs b/src/Spark/Compiler/CSharp/CSharpViewCompiler.cs index 2edbbdaf..76abcec8 100644 --- a/src/Spark/Compiler/CSharp/CSharpViewCompiler.cs +++ b/src/Spark/Compiler/CSharp/CSharpViewCompiler.cs @@ -12,28 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. // + using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using Spark.Compiler.CSharp.ChunkVisitors; namespace Spark.Compiler.CSharp { - public class CSharpViewCompiler : ViewCompiler + public class CSharpViewCompiler(IBatchCompiler compiler) : ViewCompiler { public override void CompileView(IEnumerable> viewTemplates, IEnumerable> allResources) { GenerateSourceCode(viewTemplates, allResources); - var batchCompiler = new BatchCompiler(); - var assembly = batchCompiler.Compile(Debug, "csharp", SourceCode); + var assembly = compiler.Compile(Debug, "csharp", null, new[] { SourceCode }); + CompiledType = assembly.GetType(ViewClassFullName); } - - public override void GenerateSourceCode(IEnumerable> viewTemplates, IEnumerable> allResources) + public override void GenerateSourceCode( + IEnumerable> viewTemplates, + IEnumerable> allResources) { var globalSymbols = new Dictionary(); @@ -77,7 +77,7 @@ public override void GenerateSourceCode(IEnumerable> viewTemplates, source .WriteLine() - .WriteLine(string.Format("namespace {0}", TargetNamespace)) + .Write("namespace ").WriteLine(TargetNamespace) .WriteLine("{").AddIndent(); } @@ -88,11 +88,32 @@ public override void GenerateSourceCode(IEnumerable> viewTemplates, // [SparkView] attribute source.WriteLine("[global::Spark.SparkViewAttribute("); if (TargetNamespace != null) - source.WriteFormat(" TargetNamespace=\"{0}\",", TargetNamespace).WriteLine(); + { + source + .Write(" TargetNamespace=\"") + .Write(TargetNamespace) + .WriteLine("\","); + } + source.WriteLine(" Templates = new string[] {"); - source.Write(" ").WriteLine(string.Join(",\r\n ", - Descriptor.Templates.Select( - t => "\"" + SparkViewAttribute.ConvertToAttributeFormat(t) + "\"").ToArray())); + + source.Write(" "); + + for (var i = 0; i < Descriptor.Templates.Count; i++) + { + var template = Descriptor.Templates[i]; + + source + .Write("\"") + .Write(SparkViewAttribute.ConvertToAttributeFormat(template)) + .Write("\""); + + if (i < Descriptor.Templates.Count - 1) + { + source.WriteLine(","); + } + } + source.WriteLine(" })]"); } @@ -107,7 +128,11 @@ public override void GenerateSourceCode(IEnumerable> viewTemplates, source.WriteLine(); EditorBrowsableStateNever(source, 4); - source.WriteLine("private static System.Guid _generatedViewId = new System.Guid(\"{0:n}\");", GeneratedViewId); + + source + .Write("private static System.Guid _generatedViewId = new System.Guid(\"") + .Write(GeneratedViewId.ToString("n")) + .WriteLine("\");"); source.WriteLine("public override System.Guid GeneratedViewId"); source.WriteLine("{ get { return _generatedViewId; } }"); @@ -127,13 +152,15 @@ public override void GenerateSourceCode(IEnumerable> viewTemplates, globalsGenerator.Accept(resource); } - // public void RenderViewLevelx() + // private void RenderViewLevelx() int renderLevel = 0; foreach (var viewTemplate in viewTemplates) { source.WriteLine(); - EditorBrowsableStateNever(source, 4); - source.WriteLine(string.Format("private void RenderViewLevel{0}()", renderLevel)); + EditorBrowsableStateNever(source, 4); + + source.Write("private void RenderViewLevel").Write(renderLevel.ToString()).WriteLine("()"); + source.WriteLine("{").AddIndent(); var viewGenerator = new GeneratedCodeVisitor(source, globalSymbols, NullBehaviour); viewGenerator.Accept(viewTemplate); @@ -142,9 +169,8 @@ public override void GenerateSourceCode(IEnumerable> viewTemplates, } // public void RenderView() - source.WriteLine(); - EditorBrowsableStateNever(source, 4); + EditorBrowsableStateNever(source, 4); source.WriteLine("public override void Render()"); source.WriteLine("{").AddIndent(); for (var invokeLevel = 0; invokeLevel != renderLevel; ++invokeLevel) @@ -152,18 +178,27 @@ public override void GenerateSourceCode(IEnumerable> viewTemplates, { if (invokeLevel != renderLevel - 1) { - source.WriteLine("using (OutputScope()) {{DelegateFirstRender(RenderViewLevel{0}); Content[\"view\"] = Output;}}", invokeLevel); + source + .Write("using (OutputScope()) {{DelegateFirstRender(RenderViewLevel") + .Write(invokeLevel.ToString()) + .WriteLine("); Content[\"view\"] = Output;}}"); } else { if (renderLevel <= 1) { - source.WriteLine(" DelegateFirstRender(RenderViewLevel{0});", invokeLevel); + source + .Write(" DelegateFirstRender(RenderViewLevel") + .Write(invokeLevel.ToString()) + .WriteLine(");"); + } + else + { + source + .Write(" RenderViewLevel") + .Write(invokeLevel.ToString()) + .WriteLine("();"); } - else - { - source.WriteLine(" RenderViewLevel{0}();", invokeLevel); - } } } source.RemoveIndent().WriteLine("}"); diff --git a/src/Spark/Compiler/BatchCompiler.cs b/src/Spark/Compiler/CodeDom/CodeDomBatchCompiler.cs similarity index 70% rename from src/Spark/Compiler/BatchCompiler.cs rename to src/Spark/Compiler/CodeDom/CodeDomBatchCompiler.cs index cd6b239a..cbca4fe5 100644 --- a/src/Spark/Compiler/BatchCompiler.cs +++ b/src/Spark/Compiler/CodeDom/CodeDomBatchCompiler.cs @@ -1,210 +1,185 @@ -// Copyright 2008-2009 Louis DeJardin - http://whereslou.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System; -using System.CodeDom.Compiler; -using System.Collections.Generic; -using System.Configuration; -using System.IO; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using Microsoft.CSharp; - -namespace Spark.Compiler -{ - public class BatchCompiler - { - public string OutputAssembly { get; set; } - - public Assembly Compile(bool debug, string languageOrExtension, params string[] sourceCode) - { - var language = languageOrExtension; - if (CodeDomProvider.IsDefinedLanguage(languageOrExtension) == false && - CodeDomProvider.IsDefinedExtension(languageOrExtension)) - { - language = CodeDomProvider.GetLanguageFromExtension(languageOrExtension); - } - - CodeDomProvider codeProvider; - CompilerParameters compilerParameters; - - if (ConfigurationManager.GetSection("system.codedom") != null) - { - var compilerInfo = CodeDomProvider.GetCompilerInfo(language); - codeProvider = compilerInfo.CreateProvider(); - compilerParameters = compilerInfo.CreateDefaultCompilerParameters(); - } - else - { - if (!language.Equals("c#", StringComparison.OrdinalIgnoreCase) && - !language.Equals("cs", StringComparison.OrdinalIgnoreCase) && - !language.Equals("csharp", StringComparison.OrdinalIgnoreCase)) - { - throw new CompilerException( - string.Format("When running the {0} in an AppDomain without a system.codedom config section only the csharp language is supported. This happens if you are precompiling your views.", - typeof(BatchCompiler).FullName)); - } - - var compilerVersion = GetCompilerVersion(); - - var providerOptions = new Dictionary { { "CompilerVersion", compilerVersion } }; - codeProvider = new CSharpCodeProvider(providerOptions); - compilerParameters = new CompilerParameters(); - } - - compilerParameters.TreatWarningsAsErrors = false; - var extension = codeProvider.FileExtension; - - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - if (assembly.IsDynamic()) - { - continue; - } - - compilerParameters.ReferencedAssemblies.Add(assembly.Location); - } - - CompilerResults compilerResults; - - var dynamicBase = string.Empty; - -#if NETFRAMEWORK - dynamicBase = AppDomain.CurrentDomain.SetupInformation.DynamicBase; -#else - dynamicBase = AppDomain.CurrentDomain.DynamicDirectory; -#endif - - var basePath = !string.IsNullOrEmpty(dynamicBase) ? dynamicBase : Path.GetTempPath(); - compilerParameters.TempFiles = new TempFileCollection(basePath); //Without this, the generated code throws Access Denied exception with Impersonate mode on platforms like SharePoint - if (debug) - { - compilerParameters.IncludeDebugInformation = true; - - var baseFile = Path.Combine(basePath, Guid.NewGuid().ToString("n")); - - var codeFiles = new List(); - int fileCount = 0; - foreach (string sourceCodeItem in sourceCode) - { - ++fileCount; - var codeFile = baseFile + "-" + fileCount + "." + extension; - using (var stream = new FileStream(codeFile, FileMode.Create, FileAccess.Write)) - { - using (var writer = new StreamWriter(stream)) - { - writer.Write(sourceCodeItem); - } - } - codeFiles.Add(codeFile); - } - - if (!string.IsNullOrEmpty(OutputAssembly)) - { - compilerParameters.OutputAssembly = Path.Combine(basePath, OutputAssembly); - } - else - { - compilerParameters.OutputAssembly = baseFile + ".dll"; - } - compilerResults = codeProvider.CompileAssemblyFromFile(compilerParameters, codeFiles.ToArray()); - } - else - { - if (!string.IsNullOrEmpty(OutputAssembly)) - { - compilerParameters.OutputAssembly = Path.Combine(basePath, OutputAssembly); - } - else - { - // This should result in the assembly being loaded without keeping the file on disk - compilerParameters.GenerateInMemory = true; - } - - compilerResults = codeProvider.CompileAssemblyFromSource(compilerParameters, sourceCode); - } - - if (compilerResults.Errors.HasErrors) - { - var sb = new StringBuilder(); - sb.AppendLine("Dynamic view compilation failed."); - - foreach (CompilerError err in compilerResults.Errors) - { - sb.AppendFormat("{4}({0},{1}): {2} {3}: ", err.Line, err.Column, err.IsWarning ? "warning" : "error", err.ErrorNumber, err.FileName); - sb.AppendLine(err.ErrorText); - } - - sb.AppendLine(); - foreach (var sourceCodeItem in sourceCode) - { - using (var reader = new StringReader(sourceCodeItem)) - { - for (int lineNumber = 1; ; ++lineNumber) - { - var line = reader.ReadLine(); - if (line == null) - break; - sb.Append(lineNumber).Append(' ').AppendLine(line); - } - } - } - throw new BatchCompilerException(sb.ToString(), compilerResults); - } - - return compilerResults.CompiledAssembly; - } - - private static string GetCompilerVersion() - { - return "v4.0"; - } - } - - public static class AssemblyExtensions - { - public static bool IsDynamic(this Assembly assembly) - { - if (assembly is AssemblyBuilder) - { - return true; - } - - if (assembly.ManifestModule.GetType().Namespace == "System.Reflection.Emit" /* .Net 4 specific */) - { - return true; - } - - return assembly.HasNoLocation(); - } - - private static bool HasNoLocation(this Assembly assembly) - { - bool result; - - try - { - result = string.IsNullOrEmpty(assembly.Location); - } - catch (NotSupportedException) - { - return true; - } - - return result; - } - } - -} +// Copyright 2008-2009 Louis DeJardin - http://whereslou.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Microsoft.CSharp; + +namespace Spark.Compiler.CodeDom +{ + [Obsolete("To be replaced with RoslynBatchCompiler")] + public class CodeDomBatchCompiler : IBatchCompiler + { + /// + /// Compiles the in the specified . + /// + /// When true the source is compiled in debug mode. + /// E.g. "csharp" or "visualbasic" + /// E.g. "File.Name.dll" (optional) + /// The source code to compile. + /// + /// + /// + public Assembly Compile(bool debug, string languageOrExtension, string outputAssembly, IEnumerable sourceCode) + { + var language = languageOrExtension; + if (CodeDomProvider.IsDefinedLanguage(languageOrExtension) == false && + CodeDomProvider.IsDefinedExtension(languageOrExtension)) + { + language = CodeDomProvider.GetLanguageFromExtension(languageOrExtension); + } + + CodeDomProvider codeProvider; + CompilerParameters compilerParameters; + + if (ConfigurationManager.GetSection("system.codedom") != null) + { + var compilerInfo = CodeDomProvider.GetCompilerInfo(language); + codeProvider = compilerInfo.CreateProvider(); + compilerParameters = compilerInfo.CreateDefaultCompilerParameters(); + } + else + { + if (!language.Equals("c#", StringComparison.OrdinalIgnoreCase) && + !language.Equals("cs", StringComparison.OrdinalIgnoreCase) && + !language.Equals("csharp", StringComparison.OrdinalIgnoreCase)) + { + throw new CompilerException( + $"When running the {typeof(CodeDomBatchCompiler).FullName} in an AppDomain without a system.codedom config section only the csharp language is supported. This happens if you are precompiling your views."); + } + + var compilerVersion = GetCompilerVersion(); + + var providerOptions = new Dictionary { { "CompilerVersion", compilerVersion } }; + codeProvider = new CSharpCodeProvider(providerOptions); + compilerParameters = new CompilerParameters(); + } + + compilerParameters.TreatWarningsAsErrors = false; + var extension = codeProvider.FileExtension; + + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (assembly.IsDynamic()) + { + continue; + } + + compilerParameters.ReferencedAssemblies.Add(assembly.Location); + } + + CompilerResults compilerResults; + + // ReSharper disable once RedundantAssignment + var dynamicBase = string.Empty; + +#if NETFRAMEWORK + dynamicBase = AppDomain.CurrentDomain.SetupInformation.DynamicBase; +#else + dynamicBase = AppDomain.CurrentDomain.DynamicDirectory; +#endif + + var basePath = !string.IsNullOrEmpty(dynamicBase) ? dynamicBase : Path.GetTempPath(); + compilerParameters.TempFiles = new TempFileCollection(basePath); //Without this, the generated code throws Access Denied exception with Impersonate mode on platforms like SharePoint + if (debug) + { + compilerParameters.IncludeDebugInformation = true; + + var baseFile = Path.Combine(basePath, Guid.NewGuid().ToString("n")); + + var codeFiles = new List(); + int fileCount = 0; + foreach (string sourceCodeItem in sourceCode) + { + ++fileCount; + var codeFile = baseFile + "-" + fileCount + "." + extension; + using (var stream = new FileStream(codeFile, FileMode.Create, FileAccess.Write)) + { + using (var writer = new StreamWriter(stream)) + { + writer.Write(sourceCodeItem); + } + } + codeFiles.Add(codeFile); + } + + if (!string.IsNullOrEmpty(outputAssembly)) + { + compilerParameters.OutputAssembly = Path.Combine(basePath, outputAssembly); + } + else + { + compilerParameters.OutputAssembly = baseFile + ".dll"; + } + compilerResults = codeProvider.CompileAssemblyFromFile(compilerParameters, codeFiles.ToArray()); + } + else + { + if (!string.IsNullOrEmpty(outputAssembly)) + { + compilerParameters.OutputAssembly = Path.Combine(basePath, outputAssembly); + } + else + { + // This should result in the assembly being loaded without keeping the file on disk + compilerParameters.GenerateInMemory = true; + } + + compilerResults = codeProvider.CompileAssemblyFromSource(compilerParameters, sourceCode.ToArray()); + } + + if (compilerResults.Errors.HasErrors) + { + var sb = new StringBuilder().AppendLine("Dynamic view compilation failed."); + + foreach (CompilerError err in compilerResults.Errors) + { + sb.AppendFormat("{4}({0},{1}): {2} {3}: ", err.Line, err.Column, err.IsWarning ? "warning" : "error", err.ErrorNumber, err.FileName); + sb.AppendLine(err.ErrorText); + } + + sb.AppendLine(); + foreach (var sourceCodeItem in sourceCode) + { + using var reader = new StringReader(sourceCodeItem); + + for (int lineNumber = 1; ; ++lineNumber) + { + var line = reader.ReadLine(); + if (line == null) + { + break; + } + + sb.Append(lineNumber).Append(' ').AppendLine(line); + } + } + throw new CodeDomCompilerException(sb.ToString(), compilerResults); + } + + return compilerResults.CompiledAssembly; + } + + private static string GetCompilerVersion() + { + return "v4.0"; + } + } +} diff --git a/src/Spark/Compiler/CodeDom/CodeDomCompilerException.cs b/src/Spark/Compiler/CodeDom/CodeDomCompilerException.cs new file mode 100644 index 00000000..a3b3eb3a --- /dev/null +++ b/src/Spark/Compiler/CodeDom/CodeDomCompilerException.cs @@ -0,0 +1,9 @@ +using System.CodeDom.Compiler; + +namespace Spark.Compiler.CodeDom +{ + public class CodeDomCompilerException(string message, CompilerResults results) : CompilerException(message) + { + public CompilerResults Results { get; set; } = results; + } +} \ No newline at end of file diff --git a/src/Spark/Compiler/CompilerException.cs b/src/Spark/Compiler/CompilerException.cs index 4f45e535..49a64787 100644 --- a/src/Spark/Compiler/CompilerException.cs +++ b/src/Spark/Compiler/CompilerException.cs @@ -17,8 +17,13 @@ namespace Spark.Compiler { + [Serializable] public class CompilerException : SystemException { + public CompilerException() + { + } + public CompilerException(string message) : base(message) { diff --git a/src/Spark/Compiler/IBatchCompiler.cs b/src/Spark/Compiler/IBatchCompiler.cs new file mode 100644 index 00000000..79ac9bf5 --- /dev/null +++ b/src/Spark/Compiler/IBatchCompiler.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace Spark.Compiler +{ + public interface IBatchCompiler + { + /// + /// Compiles the in the specified . + /// + /// When true the source is compiled in debug mode. + /// E.g. "csharp" or "visualbasic" + /// E.g. "File.Name.dll" (optional) + /// The source code to compile. + /// + Assembly Compile(bool debug, string languageOrExtension, string outputAssembly, IEnumerable sourceCode); + } +} diff --git a/src/Spark/Compiler/Roslyn/CSharpLink.cs b/src/Spark/Compiler/Roslyn/CSharpLink.cs new file mode 100644 index 00000000..42513c63 --- /dev/null +++ b/src/Spark/Compiler/Roslyn/CSharpLink.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace Spark.Compiler.Roslyn; + +public class CSharpLink : IRoslynCompilationLink +{ + public bool ShouldVisit(string languageOrExtension) => languageOrExtension is "c#" or "cs" or "csharp"; + + public Assembly Compile(bool debug, string assemblyName, List references, IEnumerable sourceCode) + { + var syntaxTrees = sourceCode.Select(source => CSharpSyntaxTree.ParseText(source)); + + var optimizationLevel = debug ? OptimizationLevel.Debug : OptimizationLevel.Release; + + var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) + .WithOptimizationLevel(optimizationLevel) + .WithPlatform(Platform.AnyCpu); + + var compilation = CSharpCompilation.Create( + assemblyName, + syntaxTrees: syntaxTrees, + references: references, + options: options); + + using var ms = new MemoryStream(); + + var result = compilation.Emit(ms); + + if (!result.Success) + { + var failures = result.Diagnostics.Where(diagnostic => + diagnostic.IsWarningAsError || + diagnostic.Severity == DiagnosticSeverity.Error); + + var sb = new StringBuilder(); + + foreach (var diagnostic in failures) + { + sb.Append(diagnostic.Id).Append(":").AppendLine(diagnostic.GetMessage()); + } + + throw new RoslynCompilerException(sb.ToString(), result); + } + + ms.Seek(0, SeekOrigin.Begin); + + var assembly = Assembly.Load(ms.ToArray()); + + return assembly; + } +} \ No newline at end of file diff --git a/src/Spark/Compiler/Roslyn/IRoslynCompilationLink.cs b/src/Spark/Compiler/Roslyn/IRoslynCompilationLink.cs new file mode 100644 index 00000000..1180b7ee --- /dev/null +++ b/src/Spark/Compiler/Roslyn/IRoslynCompilationLink.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Reflection; +using Microsoft.CodeAnalysis; + +namespace Spark.Compiler.Roslyn; + +public interface IRoslynCompilationLink +{ + bool ShouldVisit(string languageOrExtension); + + Assembly Compile(bool debug, string assemblyName, List references, IEnumerable sourceCode); +} \ No newline at end of file diff --git a/src/Spark/Compiler/Roslyn/RoslynBatchCompiler.cs b/src/Spark/Compiler/Roslyn/RoslynBatchCompiler.cs new file mode 100644 index 00000000..28678ff9 --- /dev/null +++ b/src/Spark/Compiler/Roslyn/RoslynBatchCompiler.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Reflection; +using Microsoft.CodeAnalysis; + +namespace Spark.Compiler.Roslyn +{ + public class RoslynBatchCompiler : IBatchCompiler + { + private readonly IEnumerable links; + + public RoslynBatchCompiler() + { + this.links = new IRoslynCompilationLink[] { new CSharpLink(), new VisualBasicLink() }; + } + + public RoslynBatchCompiler(IEnumerable links) + { + this.links = links; + } + + /// + /// Compiles the in the specified . + /// + /// When true the source is compiled in debug mode. + /// E.g. "csharp" or "visualbasic" + /// E.g. "File.Name.dll" (optional) + /// The source code to compile. + /// + public Assembly Compile(bool debug, string languageOrExtension, string outputAssembly, IEnumerable sourceCode) + { + Assembly assembly = null; + + if (!this.links.Any()) + { + throw new ConfigurationErrorsException("No IRoslynCompilationLink links"); + } + + string assemblyName; + if (!string.IsNullOrEmpty(outputAssembly)) + { + var basePath = Path.GetTempPath(); + assemblyName = Path.Combine(basePath, outputAssembly); + } + else + { + assemblyName = Path.GetRandomFileName(); + } + + var references = new List(); + + foreach (var currentAssembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (currentAssembly.IsDynamic()) + { + continue; + } + + var reference = MetadataReference.CreateFromFile(currentAssembly.Location); + + references.Add(reference); + } + + var match = false; + foreach (var visitor in this.links) + { + if (visitor.ShouldVisit(languageOrExtension)) + { + match = true; + + assembly = visitor.Compile(debug, assemblyName, references, sourceCode); + + // Chain of responsibility pattern + break; + } + } + + if (!match) + { + throw new ArgumentOutOfRangeException(nameof(languageOrExtension), languageOrExtension, "Un-handled value"); + } + + return assembly; + } + } +} \ No newline at end of file diff --git a/src/Spark/Compiler/Roslyn/RoslynCompilerException.cs b/src/Spark/Compiler/Roslyn/RoslynCompilerException.cs new file mode 100644 index 00000000..7314cb29 --- /dev/null +++ b/src/Spark/Compiler/Roslyn/RoslynCompilerException.cs @@ -0,0 +1,9 @@ +using Microsoft.CodeAnalysis.Emit; + +namespace Spark.Compiler.Roslyn +{ + public class RoslynCompilerException(string message, EmitResult emitResult) : CompilerException(message) + { + public EmitResult EmitResult { get; set; } = emitResult; + } +} \ No newline at end of file diff --git a/src/Spark/Compiler/Roslyn/VisualBasicLink.cs b/src/Spark/Compiler/Roslyn/VisualBasicLink.cs new file mode 100644 index 00000000..eebbae0c --- /dev/null +++ b/src/Spark/Compiler/Roslyn/VisualBasicLink.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.VisualBasic; + +namespace Spark.Compiler.Roslyn; + +public class VisualBasicLink : IRoslynCompilationLink +{ + public bool ShouldVisit(string languageOrExtension) => languageOrExtension is "vb" or "vbs" or "visualbasic" or "vbscript"; + + public Assembly Compile(bool debug, string assemblyName, List references, IEnumerable sourceCode) + { + var syntaxTrees = sourceCode.Select(source => VisualBasicSyntaxTree.ParseText(source)); + + var optimizationLevel = debug ? OptimizationLevel.Debug : OptimizationLevel.Release; + + var options = new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary) + .WithOptimizationLevel(optimizationLevel) + .WithPlatform(Platform.AnyCpu); + + var compilation = VisualBasicCompilation.Create( + assemblyName, + syntaxTrees: syntaxTrees, + references: references, + options: options); + + using var ms = new MemoryStream(); + + var result = compilation.Emit(ms); + + if (!result.Success) + { + var failures = result.Diagnostics.Where(diagnostic => + diagnostic.IsWarningAsError || + diagnostic.Severity == DiagnosticSeverity.Error); + + var sb = new StringBuilder(); + + foreach (var diagnostic in failures) + { + sb.Append(diagnostic.Id).Append(":").AppendLine(diagnostic.GetMessage()); + } + + throw new RoslynCompilerException(sb.ToString(), result); + } + + ms.Seek(0, SeekOrigin.Begin); + + var assembly = Assembly.Load(ms.ToArray()); + + return assembly; + } +} \ No newline at end of file diff --git a/src/Spark/Compiler/VisualBasic/VisualBasicViewCompiler.cs b/src/Spark/Compiler/VisualBasic/VisualBasicViewCompiler.cs index 8f1233d4..8ac9cf75 100644 --- a/src/Spark/Compiler/VisualBasic/VisualBasicViewCompiler.cs +++ b/src/Spark/Compiler/VisualBasic/VisualBasicViewCompiler.cs @@ -12,22 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. // + +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; +using Spark.Compiler.CodeDom; using Spark.Compiler.VisualBasic.ChunkVisitors; namespace Spark.Compiler.VisualBasic { - public class VisualBasicViewCompiler : ViewCompiler + public class VisualBasicViewCompiler(IBatchCompiler batchCompiler) : ViewCompiler { public override void CompileView(IEnumerable> viewTemplates, IEnumerable> allResources) { GenerateSourceCode(viewTemplates, allResources); - var batchCompiler = new BatchCompiler(); - var assembly = batchCompiler.Compile(Debug, "visualbasic", SourceCode); + var assembly = batchCompiler.Compile(Debug, "visualbasic", null, new[] { SourceCode }); CompiledType = assembly.GetType(ViewClassFullName); } @@ -100,8 +101,11 @@ public override void GenerateSourceCode(IEnumerable> viewTemplates, .AddIndent(); source.WriteLine(); - source.WriteLine(string.Format(" Private Shared ReadOnly _generatedViewId As Global.System.Guid = New Global.System.Guid(\"{0:n}\")", GeneratedViewId)); + source + .Write(" Private Shared ReadOnly _generatedViewId As Global.System.Guid = New Global.System.Guid(\"") + .Write(GeneratedViewId.ToString("n")) + .WriteLine("\")"); source .WriteLine(" Public Overrides ReadOnly Property GeneratedViewId() As Global.System.Guid") diff --git a/src/Spark/DefaultLanguageFactory.cs b/src/Spark/DefaultLanguageFactory.cs index 276dabd8..7800896a 100644 --- a/src/Spark/DefaultLanguageFactory.cs +++ b/src/Spark/DefaultLanguageFactory.cs @@ -19,7 +19,7 @@ namespace Spark { - public class DefaultLanguageFactory : ISparkLanguageFactory + public class DefaultLanguageFactory(IBatchCompiler batchCompiler) : ISparkLanguageFactory { public virtual ViewCompiler CreateViewCompiler(ISparkViewEngine engine, SparkViewDescriptor descriptor) { @@ -40,16 +40,16 @@ public virtual ViewCompiler CreateViewCompiler(ISparkViewEngine engine, SparkVie { case LanguageType.Default: case LanguageType.CSharp: - viewCompiler = new CSharpViewCompiler(); + viewCompiler = new CSharpViewCompiler(batchCompiler); break; case LanguageType.VisualBasic: - viewCompiler = new VisualBasicViewCompiler(); + viewCompiler = new VisualBasicViewCompiler(batchCompiler); break; case LanguageType.Javascript: viewCompiler = new JavascriptViewCompiler(); break; default: - throw new CompilerException(string.Format("Unknown language type {0}", descriptor.Language)); + throw new CompilerException($"Unknown language type {descriptor.Language}"); } viewCompiler.BaseClass = pageBaseType; diff --git a/src/Spark/ISparkServiceContainer.cs b/src/Spark/ISparkServiceContainer.cs deleted file mode 100644 index 6d5a268d..00000000 --- a/src/Spark/ISparkServiceContainer.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2008-2009 Louis DeJardin - http://whereslou.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System; - -namespace Spark -{ - public interface ISparkServiceContainer : IServiceProvider - { - T GetService(); - void SetService(TServiceInterface service); - void SetServiceBuilder(Func serviceBuilder); - } -} diff --git a/src/Spark/ISparkServiceInitialize.cs b/src/Spark/ISparkServiceInitialize.cs deleted file mode 100644 index 0bca4899..00000000 --- a/src/Spark/ISparkServiceInitialize.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2008-2009 Louis DeJardin - http://whereslou.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Spark -{ - public interface ISparkServiceInitialize - { - void Initialize(ISparkServiceContainer container); - } -} diff --git a/src/Spark/ISparkViewEngine.cs b/src/Spark/ISparkViewEngine.cs index c077f782..4d140b6a 100644 --- a/src/Spark/ISparkViewEngine.cs +++ b/src/Spark/ISparkViewEngine.cs @@ -14,7 +14,6 @@ // using System.Collections.Generic; using System.Reflection; -using Spark; using Spark.FileSystem; namespace Spark @@ -22,12 +21,12 @@ namespace Spark public interface ISparkViewEngine { ISparkSettings Settings { get; } + IViewFolder ViewFolder { get; set; } - IResourcePathManager ResourcePathManager { get; set; } - ISparkExtensionFactory ExtensionFactory { get; set; } - IViewActivatorFactory ViewActivatorFactory { get; set; } - string DefaultPageBaseType { get; set; } - ISparkSyntaxProvider SyntaxProvider { get; set; } + + IViewActivatorFactory ViewActivatorFactory { get; } + string DefaultPageBaseType { get; } + ISparkSyntaxProvider SyntaxProvider { get; } ISparkViewEntry GetEntry(SparkViewDescriptor descriptor); ISparkViewEntry CreateEntry(SparkViewDescriptor descriptor); diff --git a/src/Spark/Parser/ViewLoader.cs b/src/Spark/Parser/ViewLoader.cs index 8921a3b9..fab231be 100644 --- a/src/Spark/Parser/ViewLoader.cs +++ b/src/Spark/Parser/ViewLoader.cs @@ -29,58 +29,24 @@ namespace Spark.Parser using Spark.Compiler.CSharp.ChunkVisitors; using Spark.Compiler.NodeVisitors; using Spark.FileSystem; - using Spark.Parser.Markup; - public class ViewLoader + public class ViewLoader( + ISparkSettings settings, + IViewFolder viewFolder, + IPartialProvider partialProvider, + IPartialReferenceProvider partialReferenceProvider, + ISparkExtensionFactory extensionFactory, + ISparkSyntaxProvider syntaxProvider, + IBindingProvider bindingProvider) { - private readonly Dictionary entries = new Dictionary(); + private readonly Dictionary entries = new(); + private readonly List pending = new(); - private readonly List pending = new List(); + public string Prefix => settings.Prefix; - private IPartialProvider partialProvider; - private IPartialReferenceProvider partialReferenceProvider; + public bool ParseSectionTagAsSegment => settings.ParseSectionTagAsSegment; - public IViewFolder ViewFolder { get; set; } - - //public ParseAction> Parser { get; set; } - - public ISparkExtensionFactory ExtensionFactory { get; set; } - - public ISparkSyntaxProvider SyntaxProvider { get; set; } - - public string Prefix { get; set; } - - public bool ParseSectionTagAsSegment { get; set; } - - public AttributeBehaviour AttributeBehaviour { get; set; } - - public IBindingProvider BindingProvider { get; set; } - - public IPartialProvider PartialProvider - { - get - { - if (partialProvider == null) - { - partialProvider = new DefaultPartialProvider(); - }; - return partialProvider; - } - set { partialProvider = value; } - } - - public IPartialReferenceProvider PartialReferenceProvider - { - get - { - if (partialReferenceProvider == null) - { - partialReferenceProvider = new DefaultPartialReferenceProvider(() => PartialProvider); - }; - return partialReferenceProvider; - } - set { partialReferenceProvider = value; } - } + public AttributeBehaviour AttributeBehaviour => settings.AttributeBehaviour; /// /// Returns a value indicating whether this view loader is current. @@ -93,8 +59,7 @@ public virtual bool IsCurrent() { // The view is current if all entries' last modified value is the // same as when it was created. - return this.entries.All( - entry => entry.Value.ViewFile.LastModified == entry.Value.LastModified); + return this.entries.All(entry => entry.Value.ViewFile.LastModified == entry.Value.LastModified); } public IList Load(string viewPath) @@ -112,13 +77,13 @@ public IList Load(string viewPath) // import _global.spark files from template path and shared path var perFolderGlobal = Path.Combine(Path.GetDirectoryName(viewPath), Constants.GlobalSpark); - if (this.ViewFolder.HasView(perFolderGlobal)) + if (viewFolder.HasView(perFolderGlobal)) { this.BindEntry(perFolderGlobal); } var sharedGlobal = Path.Combine(Constants.Shared, Constants.GlobalSpark); - if (this.ViewFolder.HasView(sharedGlobal)) + if (viewFolder.HasView(sharedGlobal)) { this.BindEntry(sharedGlobal); } @@ -165,11 +130,11 @@ private IEnumerable PartialViewFolderPaths(string viewPath, bool allowCu { if (allowCustomReferencePath) { - return this.PartialReferenceProvider.GetPaths(viewPath, allowCustomReferencePath); + return partialReferenceProvider.GetPaths(viewPath, allowCustomReferencePath); } else { - return this.PartialProvider.GetPaths(viewPath); + return partialProvider.GetPaths(viewPath); } } @@ -209,7 +174,7 @@ private Entry BindEntry(string referencePath) return this.entries[referencePath]; } - var viewSource = this.ViewFolder.GetViewSource(referencePath); + var viewSource = viewFolder.GetViewSource(referencePath); var newEntry = new Entry { @@ -233,15 +198,16 @@ private void LoadInternal(string viewPath) var context = new VisitorContext { - ViewFolder = this.ViewFolder, + ViewFolder = viewFolder, Prefix = this.Prefix, - ExtensionFactory = this.ExtensionFactory, + ExtensionFactory = extensionFactory, PartialFileNames = this.FindPartialFiles(viewPath), Bindings = this.FindBindings(viewPath), ParseSectionTagAsSegment = this.ParseSectionTagAsSegment, AttributeBehaviour = this.AttributeBehaviour, }; - newEntry.Chunks = this.SyntaxProvider.GetChunks(context, viewPath); + + newEntry.Chunks = syntaxProvider.GetChunks(context, viewPath); var fileReferenceVisitor = new FileReferenceVisitor(); fileReferenceVisitor.Accept(newEntry.Chunks); @@ -269,7 +235,7 @@ private IEnumerable FindAllPartialFiles(IEnumerable folderPaths) { foreach (var folderPath in folderPaths.Distinct()) { - foreach (var view in this.ViewFolder.ListViews(folderPath)) + foreach (var view in viewFolder.ListViews(folderPath)) { var baseName = Path.GetFileNameWithoutExtension(view); if (baseName.StartsWith("_")) @@ -282,12 +248,12 @@ private IEnumerable FindAllPartialFiles(IEnumerable folderPaths) private IEnumerable FindBindings(string viewPath) { - if (this.BindingProvider == null) + if (bindingProvider == null) { - return new Binding[0]; + return Array.Empty(); } - return this.BindingProvider.GetBindings(new BindingRequest(this.ViewFolder) { ViewPath = viewPath }); + return bindingProvider.GetBindings(new BindingRequest(viewFolder) { ViewPath = viewPath }); } private string ResolveReference(string existingViewPath, string viewName) @@ -302,11 +268,11 @@ private string ResolveReference(string existingViewPath, string viewName) Path.Combine(x, viewNameWithShadeExtension) }); - var partialViewLocation = partialPaths.FirstOrDefault(x => this.ViewFolder.HasView(x)); + var partialViewLocation = partialPaths.FirstOrDefault(x => viewFolder.HasView(x)); if (partialViewLocation == null) { - var message = string.Format("Unable to locate {0} in {1}", viewName, string.Join(", ", partialPaths.ToArray())); + var message = $"Unable to locate {viewName} in {string.Join(", ", partialPaths.ToArray())}"; throw new FileNotFoundException(message, viewName); } @@ -319,37 +285,17 @@ private class Entry public string ViewPath { - get - { - return this.fileContext.ViewSourcePath; - } - - set - { - this.fileContext.ViewSourcePath = value; - } + get => this.fileContext.ViewSourcePath; + set => this.fileContext.ViewSourcePath = value; } public IList Chunks { - get - { - return this.fileContext.Contents; - } - - set - { - this.fileContext.Contents = value; - } + get => this.fileContext.Contents; + set => this.fileContext.Contents = value; } - public FileContext FileContext - { - get - { - return this.fileContext; - } - } + public FileContext FileContext => this.fileContext; public long LastModified { get; set; } diff --git a/src/Spark/Spark.csproj b/src/Spark/Spark.csproj index 6fe83070..7831a61b 100644 --- a/src/Spark/Spark.csproj +++ b/src/Spark/Spark.csproj @@ -26,6 +26,8 @@ + + diff --git a/src/Spark/SparkServiceContainer.cs b/src/Spark/SparkServiceContainer.cs deleted file mode 100644 index b6109299..00000000 --- a/src/Spark/SparkServiceContainer.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2008-2009 Louis DeJardin - http://whereslou.com -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -using System; -using System.Collections.Generic; -using System.Configuration; -using Spark.Bindings; -using Spark.FileSystem; -using Spark.Parser.Syntax; - -namespace Spark -{ - public class SparkServiceContainer : ISparkServiceContainer - { - internal static string MissingSparkSettingsConfigurationErrorExceptionMessage - = "Spark setting not configured. Missing spark section app configuration or no ISparkSetting instance registered in IoC container."; - - public SparkServiceContainer() - { - } - - public SparkServiceContainer(ISparkSettings settings) - { - _services[typeof(ISparkSettings)] = settings; - } - - readonly Dictionary _services = new Dictionary(); - - private readonly Dictionary> _defaults = - new Dictionary> - { - { - typeof(ISparkSettings), - c => ConfigurationManager.GetSection("spark") ?? - c.GetService() ?? - throw new ConfigurationErrorsException(MissingSparkSettingsConfigurationErrorExceptionMessage) - }, - { typeof(ISparkViewEngine), c => new SparkViewEngine(c.GetService()) }, - { typeof(ISparkLanguageFactory), c => new DefaultLanguageFactory() }, - { typeof(ISparkSyntaxProvider), c => new DefaultSyntaxProvider(c.GetService()) }, - { typeof(IViewActivatorFactory), c => new DefaultViewActivator() }, - { typeof(IResourcePathManager), c => new DefaultResourcePathManager(c.GetService()) }, - { typeof(ITemplateLocator), c => new DefaultTemplateLocator() }, - { typeof(IBindingProvider), c => new DefaultBindingProvider() }, - { typeof(IViewFolder), c => c.GetService().CreateDefaultViewFolder() }, - { typeof(ICompiledViewHolder), c => new CompiledViewHolder() }, - { typeof(IPartialProvider), c => new DefaultPartialProvider() }, - { typeof(IPartialReferenceProvider), c => new DefaultPartialReferenceProvider(c.GetService()) }, - }; - - - public T GetService() - { - return (T)GetService(typeof(T)); - } - - // This implementation throws stack overflow exceptions when a type is not found - public object GetService(Type serviceType) - { - lock (_services) - { - object service; - if (_services.TryGetValue(serviceType, out service)) - return service; - - Func serviceBuilder; - if (_defaults.TryGetValue(serviceType, out serviceBuilder)) - { - service = serviceBuilder(this); - _services.Add(serviceType, service); - if (service is ISparkServiceInitialize) - ((ISparkServiceInitialize)service).Initialize(this); - return service; - } - return null; - } - } - - public void SetService(TService service) - { - SetService(typeof(TService), service); - } - - public void SetService(Type serviceType, object service) - { - if (_services.ContainsKey(serviceType)) - throw new ApplicationException($"A service of type {serviceType} has already been created"); - if (!serviceType.IsInterface) - throw new ApplicationException($"Only an interface may be used as service type. {serviceType}"); - - lock (_services) - { - _services[serviceType] = service; - if (service is ISparkServiceInitialize) - ((ISparkServiceInitialize)service).Initialize(this); - } - } - - public void SetServiceBuilder(Func serviceBuilder) - { - SetServiceBuilder(typeof(TService), serviceBuilder); - } - - public void SetServiceBuilder(Type serviceType, Func serviceBuilder) - { - if (_services.ContainsKey(serviceType)) - throw new ApplicationException($"A service of type {serviceType} has already been created"); - if (!serviceType.IsInterface) - throw new ApplicationException($"Only an interface may be used as service type. {serviceType}"); - - lock (_services) - { - _defaults[serviceType] = serviceBuilder; - } - } - } -} diff --git a/src/Spark/SparkViewEngine.cs b/src/Spark/SparkViewEngine.cs index 1c81403f..eb005f93 100644 --- a/src/Spark/SparkViewEngine.cs +++ b/src/Spark/SparkViewEngine.cs @@ -15,119 +15,70 @@ using System; using System.Linq; using System.Collections.Generic; -using System.Configuration; using System.Reflection; using Spark.Bindings; using Spark.Compiler; using Spark.Compiler.CSharp; using Spark.Parser; using Spark.FileSystem; -using Spark.Parser.Syntax; namespace Spark { - public class SparkViewEngine : ISparkViewEngine, ISparkServiceInitialize + public class SparkViewEngine : ISparkViewEngine { - public SparkViewEngine() - : this(null) + public ISparkSettings Settings { get; } + public IViewFolder ViewFolder { get; set; } + public readonly ISparkLanguageFactory LanguageFactory; + public IViewActivatorFactory ViewActivatorFactory { get; } + public readonly ICompiledViewHolder CompiledViewHolder; + + public ISparkSyntaxProvider SyntaxProvider { get; } + + private readonly IBatchCompiler BatchCompiler; + private readonly IPartialProvider PartialProvider; + private readonly IPartialReferenceProvider PartialReferenceProvider; + private readonly IBindingProvider BindingProvider; + private readonly ISparkExtensionFactory SparkExtensionFactory; + + public SparkViewEngine( + ISparkSettings settings, + ISparkSyntaxProvider syntaxProvider, + IViewActivatorFactory viewActivatorFactory, + ISparkLanguageFactory languageFactory, + ICompiledViewHolder compiledViewHolder, + IViewFolder viewFolder, + IBatchCompiler batchCompiler, + IPartialProvider partialProvider, + IPartialReferenceProvider partialReferenceProvider, + IBindingProvider bindingProvider, + ISparkExtensionFactory sparkExtensionFactory) { - } - - public SparkViewEngine(ISparkSettings settings) - { - Settings = settings ?? - (ISparkSettings)ConfigurationManager.GetSection("spark") ?? - throw new ConfigurationErrorsException(SparkServiceContainer.MissingSparkSettingsConfigurationErrorExceptionMessage); - SyntaxProvider = new DefaultSyntaxProvider(Settings); - ViewActivatorFactory = new DefaultViewActivator(); - } - - public void Initialize(ISparkServiceContainer container) - { - Settings = container.GetService(); - SyntaxProvider = container.GetService(); - ViewActivatorFactory = container.GetService(); - LanguageFactory = container.GetService(); - BindingProvider = container.GetService(); - ResourcePathManager = container.GetService(); - TemplateLocator = container.GetService(); - CompiledViewHolder = container.GetService(); - PartialProvider = container.GetService(); - PartialReferenceProvider = container.GetService(); - SetViewFolder(container.GetService()); - } - - private IViewFolder _viewFolder; - public IViewFolder ViewFolder - { - get - { - if (_viewFolder == null) - { - SetViewFolder(Settings.CreateDefaultViewFolder()); - } - - return _viewFolder; - } - set { SetViewFolder(value); } - } - - private ISparkLanguageFactory _langaugeFactory; - public ISparkLanguageFactory LanguageFactory - { - get - { - if (_langaugeFactory == null) - _langaugeFactory = new DefaultLanguageFactory(); - return _langaugeFactory; - } - set { _langaugeFactory = value; } - } + Settings = settings; + SyntaxProvider = syntaxProvider; + ViewActivatorFactory = viewActivatorFactory; + LanguageFactory = languageFactory; + + CompiledViewHolder = compiledViewHolder; + + ViewFolder = this.InitialiseAggregateViewFolder(settings, viewFolder); - private IBindingProvider _bindingProvider; - public IBindingProvider BindingProvider - { - get - { - if (_bindingProvider == null) - _bindingProvider = new DefaultBindingProvider(); - return _bindingProvider; - } - set { _bindingProvider = value; } + BatchCompiler = batchCompiler; + PartialProvider = partialProvider; + PartialReferenceProvider = partialReferenceProvider; + BindingProvider = bindingProvider; + SparkExtensionFactory = sparkExtensionFactory; } - private IPartialProvider _partialProvider; - public IPartialProvider PartialProvider - { - get - { - if (_partialProvider == null) - _partialProvider = new DefaultPartialProvider(); - return _partialProvider; - } - set { _partialProvider = value; } - } + public string DefaultPageBaseType => Settings.PageBaseType; - private IPartialReferenceProvider _partialReferenceProvider; - public IPartialReferenceProvider PartialReferenceProvider - { - get - { - if (_partialReferenceProvider == null) - _partialReferenceProvider = new DefaultPartialReferenceProvider(() => PartialProvider); - return _partialReferenceProvider; - } - set { _partialReferenceProvider = value; } - } - - private void SetViewFolder(IViewFolder value) + private IViewFolder InitialiseAggregateViewFolder(ISparkSettings settings, IViewFolder value) { var aggregateViewFolder = value; - if (this.Settings.ViewFolders != null) + if (settings.ViewFolders != null) { - foreach (var viewFolderSettings in this.Settings.ViewFolders) + foreach (var viewFolderSettings in settings.ViewFolders) { IViewFolder viewFolder = this.ActivateViewFolder(viewFolderSettings); @@ -140,7 +91,7 @@ private void SetViewFolder(IViewFolder value) } } - _viewFolder = aggregateViewFolder; + return aggregateViewFolder; } private IViewFolder ActivateViewFolder(IViewFolderSettings viewFolderSettings) @@ -165,7 +116,7 @@ private IViewFolder ActivateViewFolder(IViewFolderSettings viewFolderSettings) } var args = bestConstructor.GetParameters() - .Select(param => ChangeType(viewFolderSettings, param)) + .Select(param => this.ChangeType(viewFolderSettings, param)) .ToArray(); return (IViewFolder)Activator.CreateInstance(type, args); @@ -174,55 +125,13 @@ private IViewFolder ActivateViewFolder(IViewFolderSettings viewFolderSettings) private object ChangeType(IViewFolderSettings viewFolderSettings, ParameterInfo param) { if (param.ParameterType == typeof(Assembly)) - return Assembly.Load(viewFolderSettings.Parameters[param.Name]); - - return Convert.ChangeType(viewFolderSettings.Parameters[param.Name], param.ParameterType); - } - - private IResourcePathManager _resourcePathManager; - public IResourcePathManager ResourcePathManager - { - get { - if (_resourcePathManager == null) - _resourcePathManager = new DefaultResourcePathManager(Settings); - return _resourcePathManager; - } - set { _resourcePathManager = value; } - } - - public ISparkExtensionFactory ExtensionFactory { get; set; } - public IViewActivatorFactory ViewActivatorFactory { get; set; } - - private ITemplateLocator _templateLocator; - public ITemplateLocator TemplateLocator - { - get - { - if (_templateLocator == null) - _templateLocator = new DefaultTemplateLocator(); - return _templateLocator; + return Assembly.Load(viewFolderSettings.Parameters[param.Name]); } - set { _templateLocator = value; } - } - private ICompiledViewHolder _compiledViewHolder; - public ICompiledViewHolder CompiledViewHolder - { - get - { - if (_compiledViewHolder == null) - _compiledViewHolder = new CompiledViewHolder(); - return _compiledViewHolder; - } - set { _compiledViewHolder = value; } + return Convert.ChangeType(viewFolderSettings.Parameters[param.Name], param.ParameterType); } - public ISparkSyntaxProvider SyntaxProvider { get; set; } - - public ISparkSettings Settings { get; set; } - public string DefaultPageBaseType { get; set; } - public ISparkViewEntry GetEntry(SparkViewDescriptor descriptor) { return CompiledViewHolder.Lookup(descriptor); @@ -235,13 +144,15 @@ public ISparkView CreateInstance(SparkViewDescriptor descriptor) public void ReleaseInstance(ISparkView view) { - if (view == null) throw new ArgumentNullException("view"); + if (view == null) + { + throw new ArgumentNullException(nameof(view)); + } var entry = CompiledViewHolder.Lookup(view.GeneratedViewId); - if (entry != null) - entry.ReleaseInstance(view); - } + entry?.ReleaseInstance(view); + } public ISparkViewEntry CreateEntry(SparkViewDescriptor descriptor) { @@ -301,25 +212,25 @@ void LoadTemplates(ViewLoader loader, IEnumerable templates, ICollection } } - private ViewLoader CreateViewLoader() + public Assembly BatchCompilation(IList descriptors) { - return new ViewLoader - { - ViewFolder = ViewFolder, - SyntaxProvider = SyntaxProvider, - ExtensionFactory = ExtensionFactory, - Prefix = Settings.Prefix, - BindingProvider = BindingProvider, - ParseSectionTagAsSegment = Settings.ParseSectionTagAsSegment, - AttributeBehaviour = Settings.AttributeBehaviour, - PartialProvider = PartialProvider, - PartialReferenceProvider = PartialReferenceProvider - }; + return BatchCompilation(null /*outputAssembly*/, descriptors); } - public Assembly BatchCompilation(IList descriptors) + /// + /// ViewLoader must be transient (due to its dictionary and list). + /// + /// + private ViewLoader CreateViewLoader() { - return BatchCompilation(null /*outputAssembly*/, descriptors); + return new ViewLoader( + this.Settings, + this.ViewFolder, + this.PartialProvider, + this.PartialReferenceProvider, + this.SparkExtensionFactory, + this.SyntaxProvider, + this.BindingProvider); } public Assembly BatchCompilation(string outputAssembly, IList descriptors) @@ -332,7 +243,7 @@ public Assembly BatchCompilation(string outputAssembly, IList LoadBatchCompilation(Assembly assembly) var descriptor = ((SparkViewAttribute)attributes[0]).BuildDescriptor(); var entry = new CompiledViewEntry - { - Descriptor = descriptor, - Loader = new ViewLoader { PartialProvider = PartialProvider, PartialReferenceProvider = PartialReferenceProvider }, - Compiler = new CSharpViewCompiler { CompiledType = type }, - Activator = ViewActivatorFactory.Register(type) - }; + { + Descriptor = descriptor, + Loader = this.CreateViewLoader(), + Compiler = new CSharpViewCompiler(this.BatchCompiler) { CompiledType = type }, + Activator = ViewActivatorFactory.Register(type) + }; + CompiledViewHolder.Store(entry); descriptors.Add(descriptor); diff --git a/src/Xpark/Program.cs b/src/Xpark/Program.cs index 0ddac8e0..47611459 100644 --- a/src/Xpark/Program.cs +++ b/src/Xpark/Program.cs @@ -1,12 +1,14 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using System.Xml; using System.Xml.Linq; using Spark; +using Spark.Bindings; +using Spark.Compiler.Roslyn; using Spark.FileSystem; +using Spark.Parser; +using Spark.Parser.Syntax; namespace Xpark { @@ -41,29 +43,45 @@ The Model in the template is an XDocument loaded from the source. var templateName = Path.GetFileName(templatePath); var templateDirPath = Path.GetDirectoryName(templatePath); + // Look for views in the root and shared location var viewFolder = new FileSystemViewFolder(templateDirPath); + viewFolder.Append(new SubViewFolder(viewFolder, "Shared")); - // Create an engine using the templates path as the root location - // as well as the shared location - var engine = new SparkViewEngine(new SparkSettings()) - { - DefaultPageBaseType = typeof(SparkView).FullName, - ViewFolder = viewFolder.Append(new SubViewFolder(viewFolder, "Shared")) - }; + var settings = new SparkSettings() + .SetPageBaseType(typeof(SparkView)); - SparkView view; - // compile and instantiate the template - view = (SparkView)engine.CreateInstance( - new SparkViewDescriptor() - .AddTemplate(templateName)); + var partialProvider = new DefaultPartialProvider(); + + var batchCompiler = new RoslynBatchCompiler(); + var engine = new SparkViewEngine( + settings, + new DefaultSyntaxProvider(settings), + new DefaultViewActivator(), + new DefaultLanguageFactory(batchCompiler), + new CompiledViewHolder(), + viewFolder, + batchCompiler, + partialProvider, + new DefaultPartialReferenceProvider(partialProvider), + new DefaultBindingProvider(), + null); + + // compile and instantiate the template + var view = + (SparkView)engine.CreateInstance( + new SparkViewDescriptor() + .AddTemplate(templateName)); // load the second argument, or default to reading stdin if (args.Length >= 2) + { view.Model = XDocument.Load(args[1]); + } else + { view.Model = XDocument.Load(XmlReader.Create(Console.OpenStandardInput())); - + } // write out to the third argument, or default to writing stdout if (args.Length >= 3)