diff --git a/src/Spark.Web/NullCacheService.cs b/src/Castle.MonoRail.Views.Spark/NullCacheService.cs similarity index 100% rename from src/Spark.Web/NullCacheService.cs rename to src/Castle.MonoRail.Views.Spark/NullCacheService.cs diff --git a/src/Castle.MonoRail.Views.Spark/Wrappers/HybridCacheService.cs b/src/Castle.MonoRail.Views.Spark/Wrappers/HybridCacheService.cs index f9bab1a3..a8a36ab0 100644 --- a/src/Castle.MonoRail.Views.Spark/Wrappers/HybridCacheService.cs +++ b/src/Castle.MonoRail.Views.Spark/Wrappers/HybridCacheService.cs @@ -32,7 +32,7 @@ public HybridCacheService(IEngineContext context) } else { - _fallbackCacheService = new DefaultCacheService(context.UnderlyingContext.Cache); + _fallbackCacheService = new WebCacheService(context.UnderlyingContext.Cache); } } diff --git a/src/Spark.Tests/InMemoryServiceTest.cs b/src/Spark.Tests/InMemoryServiceTest.cs new file mode 100644 index 00000000..8599b4dd --- /dev/null +++ b/src/Spark.Tests/InMemoryServiceTest.cs @@ -0,0 +1,88 @@ +using System; +using System.Threading; +using Microsoft.Extensions.Caching.Memory; +using NUnit.Framework; + +namespace Spark.Tests +{ + [TestFixture] + public class InMemoryServiceTest + { + [Test] + public void TestStoreValueThenRetrieveIt() + { + var service = new InMemoryCacheService(new MemoryCache(new MemoryCacheOptions())); + + var item = new { }; + + service.Store("identifier", CacheExpires.Empty, null, item); + + var retrieved = service.Get("identifier"); + + Assert.AreSame(item, retrieved); + } + + [Test] + public void TestStoreValueThenRetrieveItAfterAbsoluteExpiration() + { + var service = new InMemoryCacheService(new MemoryCache(new MemoryCacheOptions())); + + var item = new { }; + + service.Store("identifier", new CacheExpires(DateTime.UtcNow.AddMilliseconds(50)), null, item); + + Thread.Sleep(100); + + var retrieved = service.Get("identifier"); + + Assert.IsNull(retrieved); + } + + [Test] + public void TestStoreValueThenRetrieveItWhenExpirationSlides() + { + var service = new InMemoryCacheService(new MemoryCache(new MemoryCacheOptions())); + + var item = new { }; + + service.Store("identifier", new CacheExpires(TimeSpan.FromMilliseconds(75)), null, item); + + object retrieved; + + for (var i = 0; i < 3; i++) + { + Thread.Sleep(50); + + retrieved = service.Get("identifier"); + } + + retrieved = service.Get("identifier"); + + Assert.IsNotNull(retrieved); + + Assert.AreSame(item, retrieved); + } + + [Test] + public void TestStoreValueWithSignal() + { + var service = new InMemoryCacheService(new MemoryCache(new MemoryCacheOptions())); + + var item = new { }; + + var signal = new CacheSignal(); + + service.Store("identifier", null, signal, item); + + var retrieved = service.Get("identifier"); + + Assert.AreSame(item, retrieved); + + signal.FireChanged(); + + retrieved = service.Get("identifier"); + + Assert.IsNull(retrieved); + } + } +} diff --git a/src/Spark.Web.Mvc/Extensions/ServiceCollectionExtensions.cs b/src/Spark.Web.Mvc/Extensions/ServiceCollectionExtensions.cs index a99a77bb..fd33e2cc 100644 --- a/src/Spark.Web.Mvc/Extensions/ServiceCollectionExtensions.cs +++ b/src/Spark.Web.Mvc/Extensions/ServiceCollectionExtensions.cs @@ -69,7 +69,7 @@ public static IServiceCollection AddSpark(this IServiceCollection services, ISpa { if (HttpContext.Current != null && HttpContext.Current.Cache != null) { - return new DefaultCacheService(HttpContext.Current.Cache); + return new WebCacheService(HttpContext.Current.Cache); } return null; diff --git a/src/Spark.Web.Tests/Caching/CacheElementTester.cs b/src/Spark.Web.Tests/Caching/CacheElementTester.cs index 8624b3c1..d51b261e 100644 --- a/src/Spark.Web.Tests/Caching/CacheElementTester.cs +++ b/src/Spark.Web.Tests/Caching/CacheElementTester.cs @@ -553,7 +553,6 @@ public void SignalWillExpireOutputCachingEntry()

2

")); Assert.That(calls, Is.EqualTo(2)); - } } } diff --git a/src/Spark.Web/CacheExpires.cs b/src/Spark.Web/CacheExpires.cs deleted file mode 100644 index ba91cbca..00000000 --- a/src/Spark.Web/CacheExpires.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; - -namespace Spark -{ - public class CacheExpires - { - private static CacheExpires _empty = new CacheExpires(); - - public CacheExpires() - { - Absolute = NoAbsoluteExpiration; - Sliding = NoSlidingExpiration; - } - - public CacheExpires(DateTime absolute) - { - Absolute = absolute; - Sliding = NoSlidingExpiration; - } - - public CacheExpires(TimeSpan sliding) - { - Absolute = NoAbsoluteExpiration; - Sliding = sliding; - } - - public CacheExpires(double sliding) - : this(TimeSpan.FromSeconds(sliding)) - { - } - - public DateTime Absolute { get; set; } - public TimeSpan Sliding { get; set; } - - public static DateTime NoAbsoluteExpiration { get { return System.Web.Caching.Cache.NoAbsoluteExpiration; } } - public static TimeSpan NoSlidingExpiration { get { return System.Web.Caching.Cache.NoSlidingExpiration; } } - public static CacheExpires Empty { get { return _empty; } } - } -} \ No newline at end of file diff --git a/src/Spark.Web/Caching/SpoolWriterOriginator.cs b/src/Spark.Web/Caching/SpoolWriterOriginator.cs deleted file mode 100644 index f2055cfb..00000000 --- a/src/Spark.Web/Caching/SpoolWriterOriginator.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Linq; -using Spark.Spool; - -namespace Spark.Caching -{ - public class SpoolWriterOriginator : TextWriterOriginator - { - private readonly SpoolWriter _writer; - private int _priorStringCount; - - public SpoolWriterOriginator(SpoolWriter writer) - { - _writer = writer; - } - - public override TextWriterMemento CreateMemento() - { - return new TextWriterMemento { Written = _writer.ToArray() }; - } - - public override void BeginMemento() - { - _priorStringCount = _writer.Count(); - } - - public override TextWriterMemento EndMemento() - { - return new TextWriterMemento { Written = _writer.Skip(_priorStringCount).ToArray() }; - } - - public override void DoMemento(TextWriterMemento memento) - { - foreach (var written in memento.Written) - _writer.Write(written); - } - } -} \ No newline at end of file diff --git a/src/Spark.Web/Caching/StringWriterOriginator.cs b/src/Spark.Web/Caching/StringWriterOriginator.cs deleted file mode 100644 index 421d0ce3..00000000 --- a/src/Spark.Web/Caching/StringWriterOriginator.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.IO; - -namespace Spark.Caching -{ - public class StringWriterOriginator : TextWriterOriginator - { - private readonly StringWriter _writer; - private int _priorLength; - - public StringWriterOriginator(StringWriter writer) - { - _writer = writer; - } - - public override TextWriterMemento CreateMemento() - { - return new TextWriterMemento {Written = new[] {_writer.ToString()}}; - } - - public override void BeginMemento() - { - _priorLength = _writer.GetStringBuilder().Length; - } - - public override TextWriterMemento EndMemento() - { - var currentLength = _writer.GetStringBuilder().Length; - var written = _writer.GetStringBuilder().ToString(_priorLength, currentLength - _priorLength); - return new TextWriterMemento { Written = new[] { written } }; - } - - public override void DoMemento(TextWriterMemento memento) - { - foreach(var written in memento.Written) - _writer.Write(written); - } - } -} diff --git a/src/Spark.Web/Spark.Web.csproj b/src/Spark.Web/Spark.Web.csproj index d76470b1..136e0f29 100644 --- a/src/Spark.Web/Spark.Web.csproj +++ b/src/Spark.Web/Spark.Web.csproj @@ -28,9 +28,6 @@ - - - diff --git a/src/Spark.Web/Caching/DefaultCacheService.cs b/src/Spark.Web/WebCacheService.cs similarity index 83% rename from src/Spark.Web/Caching/DefaultCacheService.cs rename to src/Spark.Web/WebCacheService.cs index 2a1a4785..bf31bc1f 100644 --- a/src/Spark.Web/Caching/DefaultCacheService.cs +++ b/src/Spark.Web/WebCacheService.cs @@ -1,25 +1,25 @@ using System; using System.Web.Caching; -namespace Spark.Caching +namespace Spark { - public class DefaultCacheService : ICacheService + public class WebCacheService : ICacheService { - private readonly Cache _cache; + private readonly Cache cache; - public DefaultCacheService(Cache cache) + public WebCacheService(Cache cache) { - _cache = cache; + this.cache = cache; } public object Get(string identifier) { - return _cache.Get(identifier); + return this.cache.Get(identifier); } public void Store(string identifier, CacheExpires expires, ICacheSignal signal, object item) { - _cache.Insert( + this.cache.Insert( identifier, item, SignalDependency.For(signal), diff --git a/src/Spark.Web/AbstractSparkView.cs b/src/Spark/AbstractSparkView.cs similarity index 100% rename from src/Spark.Web/AbstractSparkView.cs rename to src/Spark/AbstractSparkView.cs diff --git a/src/Spark/CacheExpires.cs b/src/Spark/CacheExpires.cs new file mode 100644 index 00000000..a9ed50cb --- /dev/null +++ b/src/Spark/CacheExpires.cs @@ -0,0 +1,60 @@ +using System; + +namespace Spark +{ + /// + /// Represents when a cached entry should expire. + /// + public class CacheExpires + { + /// + /// Constructor for a non expiring cached entry. + /// + public CacheExpires() + { + Absolute = NoAbsoluteExpiration; + Sliding = NoSlidingExpiration; + } + + /// + /// Constructor for a non cached entry expiring at a specified time. + /// + /// The time when to invalidate the cached entry. + public CacheExpires(DateTime absolute) + { + Absolute = absolute; + Sliding = NoSlidingExpiration; + } + + /// + /// Constructor for a cached entry that stays cached as long as it keeps being used. + /// + /// The timespan of sliding expirations. + public CacheExpires(TimeSpan sliding) + { + Absolute = NoAbsoluteExpiration; + Sliding = sliding; + } + + /// + /// Constructor for a cached entry that stays cached as long as it keeps being used. + /// + /// The number of seconds of sliding expirations. + public CacheExpires(double sliding) + : this(TimeSpan.FromSeconds(sliding)) + { + } + + public DateTime Absolute { get; set; } + public TimeSpan Sliding { get; set; } + + public static DateTime NoAbsoluteExpiration => DateTime.MaxValue; + + public static TimeSpan NoSlidingExpiration => TimeSpan.Zero; + + /// + /// Cached entry never to expire. + /// + public static CacheExpires Empty { get; } = new(); + } +} \ No newline at end of file diff --git a/src/Spark.Web/CacheSignal.cs b/src/Spark/CacheSignal.cs similarity index 92% rename from src/Spark.Web/CacheSignal.cs rename to src/Spark/CacheSignal.cs index 5e00d3ab..f4db2bd5 100644 --- a/src/Spark.Web/CacheSignal.cs +++ b/src/Spark/CacheSignal.cs @@ -19,7 +19,7 @@ public event EventHandler Changed lock (this) { _changed += value; - if (_enabled) + if (_enabled) return; Enable(); @@ -31,7 +31,7 @@ public event EventHandler Changed lock (this) { _changed -= value; - if (_enabled != true || ChangedIsEmpty() == false) + if (_enabled != true || ChangedIsEmpty() == false) return; Disable(); @@ -42,7 +42,7 @@ public event EventHandler Changed private bool ChangedIsEmpty() { - return _changed == null || + return _changed == null || _changed.GetInvocationList().Length == 0; } @@ -52,7 +52,7 @@ private bool ChangedIsEmpty() /// to call FireChanged. /// protected virtual void Enable() - { + { } /// @@ -60,7 +60,7 @@ protected virtual void Enable() /// when no cache dependencies remain listenning to the signal. /// protected virtual void Disable() - { + { } /// @@ -69,7 +69,7 @@ protected virtual void Disable() /// public void FireChanged() { - if (_changed != null) + if (_changed != null) _changed(this, EventArgs.Empty); } } diff --git a/src/Spark.Web/Caching/CacheMemento.cs b/src/Spark/Caching/CacheMemento.cs similarity index 60% rename from src/Spark.Web/Caching/CacheMemento.cs rename to src/Spark/Caching/CacheMemento.cs index 18c92a74..4e475e0f 100644 --- a/src/Spark.Web/Caching/CacheMemento.cs +++ b/src/Spark/Caching/CacheMemento.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using Spark.Spool; @@ -6,14 +5,10 @@ namespace Spark.Caching { public class CacheMemento { - public CacheMemento() - { - Content = new Dictionary(); - OnceTable = new Dictionary(); - } - public SpoolWriter SpoolOutput { get; set; } - public Dictionary Content { get; set;} - public Dictionary OnceTable { get; set; } + + public Dictionary Content { get; set; } = new(); + + public Dictionary OnceTable { get; set; } = new(); } } diff --git a/src/Spark.Web/Caching/CacheOriginator.cs b/src/Spark/Caching/CacheOriginator.cs similarity index 71% rename from src/Spark.Web/Caching/CacheOriginator.cs rename to src/Spark/Caching/CacheOriginator.cs index c0b1511d..93aa3d26 100644 --- a/src/Spark.Web/Caching/CacheOriginator.cs +++ b/src/Spark/Caching/CacheOriginator.cs @@ -5,42 +5,35 @@ namespace Spark.Caching { - public class CacheOriginator + public class CacheOriginator(SparkViewContext state) { - private readonly SparkViewContext _state; - private TextWriter _priorOutput; private SpoolWriter _spoolOutput; - private readonly Dictionary _priorContent = new Dictionary(); + private readonly Dictionary _priorContent = new(); private Dictionary _priorOnceTable; - public CacheOriginator(SparkViewContext state) - { - _state = state; - } - /// /// Establishes original state for memento capturing purposes /// public void BeginMemento() { - foreach (var content in _state.Content) + foreach (var content in state.Content) { var writerOriginator = TextWriterOriginator.Create(content.Value); _priorContent.Add(content.Key, writerOriginator); writerOriginator.BeginMemento(); } - _priorOnceTable = _state.OnceTable.ToDictionary(kv=>kv.Key, kv=>kv.Value); + _priorOnceTable = state.OnceTable.ToDictionary(kv=>kv.Key, kv=>kv.Value); // capture current output also if it's not locked into a named output at the moment // this could be a case in view's output, direct to network, or various macro or content captures - if (_state.Content.Any(kv => ReferenceEquals(kv.Value, _state.Output)) == false) + if (state.Content.Any(kv => ReferenceEquals(kv.Value, state.Output)) == false) { - _priorOutput = _state.Output; + _priorOutput = state.Output; _spoolOutput = new SpoolWriter(); - _state.Output = _spoolOutput; + state.Output = _spoolOutput; } } @@ -57,7 +50,7 @@ public CacheMemento EndMemento() if (_priorOutput != null) { _spoolOutput.WriteTo(_priorOutput); - _state.Output = _priorOutput; + state.Output = _priorOutput; memento.SpoolOutput = _spoolOutput; } @@ -69,15 +62,15 @@ public CacheMemento EndMemento() memento.Content.Add(content.Key, textMemento); } - // also save any named content in it's entirety that added created after BeginMemento was called - foreach (var content in _state.Content.Where(kv => _priorContent.ContainsKey(kv.Key) == false)) + // also save any named content in its entirety that added created after BeginMemento was called + foreach (var content in state.Content.Where(kv => _priorContent.ContainsKey(kv.Key) == false)) { var originator = TextWriterOriginator.Create(content.Value); memento.Content.Add(content.Key, originator.CreateMemento()); } // capture anything from the oncetable that was added after BeginMemento was called - var newItems = _state.OnceTable.Where(once => _priorOnceTable.ContainsKey(once.Key) == false); + var newItems = state.OnceTable.Where(once => _priorOnceTable.ContainsKey(once.Key) == false); memento.OnceTable = newItems.ToDictionary(once => once.Key, once => once.Value); return memento; } @@ -88,16 +81,16 @@ public CacheMemento EndMemento() /// memento captured in previous begin/end calls public void DoMemento(CacheMemento memento) { - memento.SpoolOutput.WriteTo(_state.Output); + memento.SpoolOutput.WriteTo(state.Output); foreach (var content in memento.Content) { // create named content if it doesn't exist TextWriter writer; - if (_state.Content.TryGetValue(content.Key, out writer) == false) + if (state.Content.TryGetValue(content.Key, out writer) == false) { writer = new SpoolWriter(); - _state.Content.Add(content.Key, writer); + state.Content.Add(content.Key, writer); } // and in any case apply the delta @@ -106,10 +99,10 @@ public void DoMemento(CacheMemento memento) } // add recorded once deltas that were not yet in this subject's table - var newItems = memento.OnceTable.Where(once => _state.OnceTable.ContainsKey(once.Key) == false); + var newItems = memento.OnceTable.Where(once => state.OnceTable.ContainsKey(once.Key) == false); foreach (var once in newItems) { - _state.OnceTable.Add(once.Key, once.Value); + state.OnceTable.Add(once.Key, once.Value); } } } diff --git a/src/Spark/Caching/SpoolWriterOriginator.cs b/src/Spark/Caching/SpoolWriterOriginator.cs new file mode 100644 index 00000000..dd044591 --- /dev/null +++ b/src/Spark/Caching/SpoolWriterOriginator.cs @@ -0,0 +1,31 @@ +using System.Linq; +using Spark.Spool; + +namespace Spark.Caching +{ + public class SpoolWriterOriginator(SpoolWriter writer) : TextWriterOriginator + { + private int _priorStringCount; + + public override TextWriterMemento CreateMemento() + { + return new TextWriterMemento { Written = writer.ToArray() }; + } + + public override void BeginMemento() + { + _priorStringCount = writer.Count(); + } + + public override TextWriterMemento EndMemento() + { + return new TextWriterMemento { Written = writer.Skip(_priorStringCount).ToArray() }; + } + + public override void DoMemento(TextWriterMemento memento) + { + foreach (var written in memento.Written) + writer.Write(written); + } + } +} \ No newline at end of file diff --git a/src/Spark/Caching/StringWriterOriginator.cs b/src/Spark/Caching/StringWriterOriginator.cs new file mode 100644 index 00000000..619a7ace --- /dev/null +++ b/src/Spark/Caching/StringWriterOriginator.cs @@ -0,0 +1,32 @@ +using System.IO; + +namespace Spark.Caching +{ + public class StringWriterOriginator(StringWriter writer) : TextWriterOriginator + { + private int _priorLength; + + public override TextWriterMemento CreateMemento() + { + return new TextWriterMemento {Written = new[] {writer.ToString()}}; + } + + public override void BeginMemento() + { + _priorLength = writer.GetStringBuilder().Length; + } + + public override TextWriterMemento EndMemento() + { + var currentLength = writer.GetStringBuilder().Length; + var written = writer.GetStringBuilder().ToString(_priorLength, currentLength - _priorLength); + return new TextWriterMemento { Written = new[] { written } }; + } + + public override void DoMemento(TextWriterMemento memento) + { + foreach(var written in memento.Written) + writer.Write(written); + } + } +} diff --git a/src/Spark.Web/Caching/TextWriterOriginator.cs b/src/Spark/Caching/TextWriterOriginator.cs similarity index 94% rename from src/Spark.Web/Caching/TextWriterOriginator.cs rename to src/Spark/Caching/TextWriterOriginator.cs index b9885705..9727df17 100644 --- a/src/Spark.Web/Caching/TextWriterOriginator.cs +++ b/src/Spark/Caching/TextWriterOriginator.cs @@ -10,9 +10,15 @@ public abstract class TextWriterOriginator public static TextWriterOriginator Create(TextWriter writer) { if (writer is SpoolWriter) + { return new SpoolWriterOriginator((SpoolWriter) writer); + } + if (writer is StringWriter) + { return new StringWriterOriginator((StringWriter)writer); + } + throw new InvalidCastException("writer is unknown type " + writer.GetType().FullName); } diff --git a/src/Spark/Compiler/CSharp/ChunkVisitors/GeneratedCodeVisitor.cs b/src/Spark/Compiler/CSharp/ChunkVisitors/GeneratedCodeVisitor.cs index bff1a342..207a1641 100644 --- a/src/Spark/Compiler/CSharp/ChunkVisitors/GeneratedCodeVisitor.cs +++ b/src/Spark/Compiler/CSharp/ChunkVisitors/GeneratedCodeVisitor.cs @@ -503,6 +503,7 @@ protected override void Visit(CacheChunk chunk) .RemoveIndent().WriteLine("}") .RemoveIndent().WriteLine("}"); } + protected override void Visit(MarkdownChunk chunk) { CodeIndent(chunk).WriteLine("using(MarkdownOutputScope())"); diff --git a/src/Spark/Compiler/ChunkVisitors/DetectCodeExpressionVisitor.cs b/src/Spark/Compiler/ChunkVisitors/DetectCodeExpressionVisitor.cs index 95307a8d..3a00578a 100644 --- a/src/Spark/Compiler/ChunkVisitors/DetectCodeExpressionVisitor.cs +++ b/src/Spark/Compiler/ChunkVisitors/DetectCodeExpressionVisitor.cs @@ -12,11 +12,8 @@ // 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.ChunkVisitors; using Spark.Parser.Code; namespace Spark.Compiler.ChunkVisitors @@ -62,7 +59,7 @@ void Examine(Snippets code) protected override void Visit(UseImportChunk chunk) { - + //no-op } protected override void Visit(ContentSetChunk chunk) diff --git a/src/Spark.Web/ICacheService.cs b/src/Spark/ICacheService.cs similarity index 93% rename from src/Spark.Web/ICacheService.cs rename to src/Spark/ICacheService.cs index e4937080..2c9816aa 100644 --- a/src/Spark.Web/ICacheService.cs +++ b/src/Spark/ICacheService.cs @@ -1,5 +1,3 @@ -using System; - namespace Spark { public interface ICacheService diff --git a/src/Spark/InMemoryCacheService.cs b/src/Spark/InMemoryCacheService.cs new file mode 100644 index 00000000..b60c8d9e --- /dev/null +++ b/src/Spark/InMemoryCacheService.cs @@ -0,0 +1,71 @@ +using System; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Primitives; + +namespace Spark; + +public class InMemoryCacheService(IMemoryCache cache) : ICacheService +{ + public object Get(string identifier) + { + return cache.Get(identifier); + } + + public void Store(string identifier, CacheExpires expires, ICacheSignal signal, object item) + { + var option = new MemoryCacheEntryOptions(); + + if (expires != null) + { + if (expires.Sliding > CacheExpires.NoSlidingExpiration) + { + option.SlidingExpiration = expires.Sliding; + } + else + { + option.AbsoluteExpiration = expires.Absolute; + } + } + + if (signal != null) + { + option.AddExpirationToken(new SignalChangeToken(signal)); + } + + cache.Set(identifier, item, option); + } + + private class SignalChangeToken : IChangeToken + { + private readonly ICacheSignal signal; + private bool hasChanged; + + public SignalChangeToken(ICacheSignal signal) + { + this.signal = signal; + this.signal.Changed += this.SignalOnChanged; + } + + private void SignalOnChanged(object sender, EventArgs e) + { + this.hasChanged = true; + } + + public bool HasChanged => this.hasChanged; + + public bool ActiveChangeCallbacks => true; + + public IDisposable RegisterChangeCallback(Action callback, object state) + { + return new StopListeningToSignal(this); + } + + private class StopListeningToSignal(SignalChangeToken signalChangeToken) : IDisposable + { + public void Dispose() + { + signalChangeToken.signal.Changed -= signalChangeToken.SignalOnChanged; + } + } + } +} \ No newline at end of file diff --git a/src/Spark/Spark.csproj b/src/Spark/Spark.csproj index 81e988fe..f060c4e1 100644 --- a/src/Spark/Spark.csproj +++ b/src/Spark/Spark.csproj @@ -1,4 +1,4 @@ - + Library net48;net8.0 @@ -25,9 +25,11 @@ Spark is a view engine allowing the HTML to dominate the flow and any code to fit seamlessly. - + + + diff --git a/src/Spark.Web/SparkViewBase.cs b/src/Spark/SparkViewBase.cs similarity index 100% rename from src/Spark.Web/SparkViewBase.cs rename to src/Spark/SparkViewBase.cs diff --git a/src/Spark.Web/SparkViewDecorator.cs b/src/Spark/SparkViewDecorator.cs similarity index 100% rename from src/Spark.Web/SparkViewDecorator.cs rename to src/Spark/SparkViewDecorator.cs