diff --git a/.gitignore b/.gitignore index c58e7ed..fd46a8c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,6 @@ TestResults _UpgradeReport_Files UpgradeLog.XML -RazorPad.Web.Website/App_Data/Database packages -*ncrunch* \ No newline at end of file +*.DotSettings +*.crunchsolution.cache \ No newline at end of file diff --git a/RazorPad.Contracts/IModelBuilder.cs b/RazorPad.Contracts/IModelBuilder.cs new file mode 100644 index 0000000..64be773 --- /dev/null +++ b/RazorPad.Contracts/IModelBuilder.cs @@ -0,0 +1,6 @@ +namespace RazorPad.UI +{ + public interface IModelBuilder + { + } +} \ No newline at end of file diff --git a/RazorPad.Contracts/IModelBuilderFactory.cs b/RazorPad.Contracts/IModelBuilderFactory.cs new file mode 100644 index 0000000..52f41c9 --- /dev/null +++ b/RazorPad.Contracts/IModelBuilderFactory.cs @@ -0,0 +1,8 @@ +namespace RazorPad.UI +{ + public interface IModelBuilderFactory + { + bool CanBuild(IModelProvider provider); + IModelBuilder Build(IModelProvider provider = null); + } +} \ No newline at end of file diff --git a/RazorPad.Contracts/IModelProvider.cs b/RazorPad.Contracts/IModelProvider.cs new file mode 100644 index 0000000..a88a384 --- /dev/null +++ b/RazorPad.Contracts/IModelProvider.cs @@ -0,0 +1,16 @@ +using System; + +namespace RazorPad +{ + public interface IModelProvider + { + event EventHandler Error; + + event EventHandler ModelChanged; + + dynamic GetModel(); + + string Serialize(); + void Deserialize(string serialized); + } +} diff --git a/RazorPad.Contracts/IModelProviderFactory.cs b/RazorPad.Contracts/IModelProviderFactory.cs new file mode 100644 index 0000000..a7aca96 --- /dev/null +++ b/RazorPad.Contracts/IModelProviderFactory.cs @@ -0,0 +1,7 @@ +namespace RazorPad +{ + public interface IModelProviderFactory + { + IModelProvider Create(); + } +} \ No newline at end of file diff --git a/RazorPad.Contracts/IRazorDocumentLoader.cs b/RazorPad.Contracts/IRazorDocumentLoader.cs new file mode 100644 index 0000000..d5c1f1c --- /dev/null +++ b/RazorPad.Contracts/IRazorDocumentLoader.cs @@ -0,0 +1,12 @@ +using System.IO; + +namespace RazorPad.Persistence +{ + public interface IRazorDocumentLoader + { + RazorDocument Parse(string content); + + RazorDocument Load(string uri); + RazorDocument Load(Stream stream); + } +} \ No newline at end of file diff --git a/RazorPad.Contracts/IRazorDocumentSaver.cs b/RazorPad.Contracts/IRazorDocumentSaver.cs new file mode 100644 index 0000000..11b2907 --- /dev/null +++ b/RazorPad.Contracts/IRazorDocumentSaver.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace RazorPad.Persistence +{ + public interface IRazorDocumentSaver + { + void Save(RazorDocument document, Stream stream); + } + + public static class RazorDocumentSaverExtensions + { + public static void Save(this IRazorDocumentSaver saver, RazorDocument document, string filename = null) + { + var destination = filename ?? document.Filename; + + if (string.IsNullOrWhiteSpace(destination)) + throw new ApplicationException("No filename specified!"); + + document.DocumentKind = RazorDocument.GetDocumentKind(filename); + + using (var stream = File.Open(destination, FileMode.Truncate, FileAccess.Write)) + { + saver.Save(document, stream); + + try { stream.Flush(); } + catch(Exception ex) + { + Trace.WriteLine("Error flushing file stream: " + ex); + } + } + } + } + +} \ No newline at end of file diff --git a/RazorPad.Contracts/ITemplateExecutor.cs b/RazorPad.Contracts/ITemplateExecutor.cs new file mode 100644 index 0000000..31f8913 --- /dev/null +++ b/RazorPad.Contracts/ITemplateExecutor.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace RazorPad.Compilation +{ + public interface ITemplateExecutor + { + string Execute(string templateText, dynamic model, IEnumerable assemblyReferences); + } + + public static class TemplateExecutorExtensions + { + public static string Execute(this ITemplateExecutor executor, RazorDocument document) + { + Contract.Requires(document != null); + return executor.Execute(document.Template, document.GetModel(), document.References); + } + } +} \ No newline at end of file diff --git a/RazorPad.Contracts/Properties/AssemblyInfo.cs b/RazorPad.Contracts/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..81c48d8 --- /dev/null +++ b/RazorPad.Contracts/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RazorPad.Contracts")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("RazorPad.Contracts")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9a7d59e2-858d-4c3e-9673-cf82aec9a184")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RazorPad.Contracts/RazorDocument.cs b/RazorPad.Contracts/RazorDocument.cs new file mode 100644 index 0000000..0c118e7 --- /dev/null +++ b/RazorPad.Contracts/RazorDocument.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace RazorPad +{ + public class RazorDocument + { + public const string DefaultTemplateBaseClassName = "RazorPad.Compilation.TemplateBase"; + + public static readonly IEnumerable SimpleTemplateExtensions = + new[] { ".txt", ".cshtml", ".vbhtml" }; + + public static volatile Func GetDocumentKind = + GetDocumentKindInternal; + + public string Filename { get; set; } + + public IDictionary Metadata { get; private set; } + + public IModelProvider ModelProvider { get; set; } + + public string Template { get; set; } + + public string TemplateBaseClassName { get; set; } + + public IEnumerable References { get; set; } + + public RazorDocumentKind DocumentKind + { + get { return _documentKind.GetValueOrDefault(GetDocumentKind(Filename)); } + set { _documentKind = value; } + } + private RazorDocumentKind? _documentKind; + + public RazorDocument() + : this(null) + { + } + + public RazorDocument(string template, + IEnumerable references = null, + IModelProvider modelProvider = null, + IDictionary metadata = null + ) + { + Metadata = new Dictionary(metadata ?? new Dictionary()); + ModelProvider = modelProvider; + References = references ?? Enumerable.Empty(); + Template = template ?? string.Empty; + TemplateBaseClassName = DefaultTemplateBaseClassName; + } + + public dynamic GetModel() + { + if (ModelProvider == null) + return null; + + return ModelProvider.GetModel(); + } + + private static RazorDocumentKind GetDocumentKindInternal(string filename) + { + if (!string.IsNullOrWhiteSpace(filename)) + { + var extension = Path.GetExtension(filename); + if (SimpleTemplateExtensions.Contains(extension)) + return RazorDocumentKind.TemplateOnly; + } + + return RazorDocumentKind.Full; + } + } +} diff --git a/RazorPad.Contracts/RazorDocumentKind.cs b/RazorPad.Contracts/RazorDocumentKind.cs new file mode 100644 index 0000000..ca49f16 --- /dev/null +++ b/RazorPad.Contracts/RazorDocumentKind.cs @@ -0,0 +1,8 @@ +namespace RazorPad +{ + public enum RazorDocumentKind + { + TemplateOnly, + Full, + } +} \ No newline at end of file diff --git a/RazorPad.Contracts/RazorPad.Contracts.csproj b/RazorPad.Contracts/RazorPad.Contracts.csproj new file mode 100644 index 0000000..9daa200 --- /dev/null +++ b/RazorPad.Contracts/RazorPad.Contracts.csproj @@ -0,0 +1,72 @@ + + + + + Debug + AnyCPU + {92E04BBB-A022-41BA-A13C-1EB6C47DFFAE} + Library + Properties + RazorPad + RazorPad.Contracts + v4.0 + 512 + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + ..\assets\RazorPad.snk + + + + + + + + + + + + + + + + + + + + + + RazorPad.snk + + + + + + \ No newline at end of file diff --git a/RazorPad.Contracts/RazorPad.Contracts.ncrunchproject b/RazorPad.Contracts/RazorPad.Contracts.ncrunchproject new file mode 100644 index 0000000..50845ce --- /dev/null +++ b/RazorPad.Contracts/RazorPad.Contracts.ncrunchproject @@ -0,0 +1,16 @@ + + false + false + false + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/RazorPad.Contracts/RazorPadError.cs b/RazorPad.Contracts/RazorPadError.cs new file mode 100644 index 0000000..6126450 --- /dev/null +++ b/RazorPad.Contracts/RazorPadError.cs @@ -0,0 +1,41 @@ +using System; + +namespace RazorPad +{ + public class RazorPadError + { + public int Line { get; set; } + + public int Column { get; set; } + + public string Message { get; set; } + + public RazorPadError(string message = null) + { + Message = message; + } + + public RazorPadError(Exception exception) + { + if (exception == null) + return; + + Message = string.Format("EXCEPTION: {0}", exception.Message); + } + + public override string ToString() + { + return string.Format("[{0}:{1}] {2}", Line, Column, Message); + } + } + + public class RazorPadErrorEventArgs : EventArgs + { + public RazorPadError Error { get; private set; } + + public RazorPadErrorEventArgs(RazorPadError error) + { + Error = error; + } + } +} \ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests.cs b/RazorPad.Core.Tests/AcceptanceTests.cs deleted file mode 100644 index 859daf3..0000000 --- a/RazorPad.Core.Tests/AcceptanceTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.CodeDom.Compiler; -using System.Globalization; -using System.IO; -using System.Reflection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using RazorPad.Compilation; - -namespace RazorPad.Core.Tests -{ - [TestClass] - public class AcceptanceTests - { - private TemplateCompiler _templateCompiler; - - [TestInitialize] - public void TestInitialize() - { - _templateCompiler = new TemplateCompiler(); - } - - [TestMethod] - public void ShouldSupportFunctions() - { - const string template = @"@functions { int MyValue = 1; } @MyValue"; - - var results = _templateCompiler.Execute(template); - - Assert.AreEqual(results.Trim(), "1"); - } - - - [TestMethod] - public void ShouldSupportHelpers() - { - const string template = @" -@helper DisplayCurrency(decimal value) { - @value.ToString(""C2"") -} - -@DisplayCurrency(3.4m) -"; - var code = _templateCompiler.GenerateCode(template); - var results = _templateCompiler.Execute(template); - - Assert.AreEqual(results.Trim(), 3.4m.ToString("C2")); - } - - - } -} diff --git a/RazorPad.Core.Tests/AcceptanceTests/AcceptanceTests.cs b/RazorPad.Core.Tests/AcceptanceTests/AcceptanceTests.cs new file mode 100644 index 0000000..bd12469 --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/AcceptanceTests.cs @@ -0,0 +1,102 @@ +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using RazorPad.Compilation; +using RazorPad.Persistence; + +namespace RazorPad.Core.Tests +{ + [TestClass] + public class AcceptanceTests + { + private TemplateCompiler _templateCompiler; + private RazorDocumentManager _loader; + + [TestInitialize] + public void TestInitialize() + { + _loader = new RazorDocumentManager(new XmlRazorDocumentSource()); + _templateCompiler = new TemplateCompiler(); + } + + + [TestMethod] + public void ShouldSupportFunctions() + { + var document = LoadDocument("AcceptanceTests.Functions.cshtml"); + + var results = _templateCompiler.Execute(document); + + string output = LoadResource("AcceptanceTests.Functions.cshtml.output"); + Assert.AreEqual(output, results.Trim()); + } + + + [TestMethod] + public void ShouldSupportHelpers() + { + var document = LoadDocument("AcceptanceTests.Helpers.cshtml"); + + var results = _templateCompiler.Execute(document); + + string output = LoadResource("AcceptanceTests.Helpers.cshtml.output"); + Assert.AreEqual(output, results.Trim()); + } + + + [TestMethod] + public void ShouldSupportSimpleRendering() + { + var document = LoadDocument("AcceptanceTests.SimpleRendering.cshtml"); + + var results = _templateCompiler.Execute(document); + + string output = LoadResource("AcceptanceTests.SimpleRendering.cshtml.output"); + Assert.AreEqual(output, results.Trim()); + } + + + [TestMethod] + public void ShouldSupportAssemblyReferences() + { + var document = LoadDocument("AcceptanceTests.AssemblyReferences.razorpad"); + + var results = _templateCompiler.Execute(document); + + string output = LoadResource("AcceptanceTests.AssemblyReferences.razorpad.output"); + Assert.AreEqual(output, results.Trim()); + } + + + [TestMethod] + public void ShouldSupportBasicRazorDocument() + { + var document = LoadDocument("AcceptanceTests.BasicRazorDocument.razorpad"); + + var results = _templateCompiler.Execute(document); + + string output = LoadResource("AcceptanceTests.BasicRazorDocument.razorpad.output"); + Assert.AreEqual(output, results.Trim()); + } + + + + private RazorDocument LoadDocument(string name) + { + using (var reader = GetResourceStream(name)) + return _loader.Load(reader); + } + + private string LoadResource(string name) + { + using (var stream = GetResourceStream(name)) + using (var reader = new StreamReader(stream)) + return reader.ReadToEnd(); + } + + private Stream GetResourceStream(string resourceName) + { + var type = GetType(); + return type.Assembly.GetManifestResourceStream(type, resourceName); + } + } +} diff --git a/RazorPad.Core.Tests/AcceptanceTests/AcceptanceTests.tt b/RazorPad.Core.Tests/AcceptanceTests/AcceptanceTests.tt new file mode 100644 index 0000000..81057d2 --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/AcceptanceTests.tt @@ -0,0 +1,75 @@ +<#@ template language="C#" hostspecific="true" #> +<#@ output extension=".cs" #> +<#@ import namespace="System.IO" #> +<#@ import namespace="System.Collections.Generic" #> +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using RazorPad.Compilation; +using RazorPad.Persistence; + +namespace RazorPad.Core.Tests +{ + [TestClass] + public class AcceptanceTests + { + private TemplateCompiler _templateCompiler; + private RazorDocumentManager _loader; + + [TestInitialize] + public void TestInitialize() + { + _loader = new RazorDocumentManager(new XmlRazorDocumentSource()); + _templateCompiler = new TemplateCompiler(); + } + +<# + var directoryInfo = new DirectoryInfo(Host.ResolvePath("")); + var directory = directoryInfo.Name; + + var razorDocuments = new List(); + razorDocuments.AddRange(directoryInfo.GetFiles("*.cshtml")); + razorDocuments.AddRange(directoryInfo.GetFiles("*.razorpad")); + + foreach(var file in razorDocuments) + { + string resourceName = "AcceptanceTests." + file.Name.Replace("\\", "."); + string outputResourceName = resourceName + ".output"; + string displayName = Path.GetFileNameWithoutExtension(file.Name); +#> + + [TestMethod] + public void ShouldSupport<#= displayName #>() + { + var document = LoadDocument("<#= resourceName #>"); + + var results = _templateCompiler.Execute(document); + + string output = LoadResource("<#= outputResourceName #>"); + Assert.AreEqual(output, results.Trim()); + } + +<# + } +#> + + + private RazorDocument LoadDocument(string name) + { + using (var reader = GetResourceStream(name)) + return _loader.Load(reader); + } + + private string LoadResource(string name) + { + using (var stream = GetResourceStream(name)) + using (var reader = new StreamReader(stream)) + return reader.ReadToEnd(); + } + + private Stream GetResourceStream(string resourceName) + { + var type = GetType(); + return type.Assembly.GetManifestResourceStream(type, resourceName); + } + } +} diff --git a/RazorPad.Core.Tests/AcceptanceTests/AssemblyReferences.razorpad b/RazorPad.Core.Tests/AcceptanceTests/AssemblyReferences.razorpad new file mode 100644 index 0000000..16863ba --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/AssemblyReferences.razorpad @@ -0,0 +1,17 @@ + + + + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.XML.dll + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Xml.Linq.dll + C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.Web.dll + + + + \ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/AssemblyReferences.razorpad.output b/RazorPad.Core.Tests/AcceptanceTests/AssemblyReferences.razorpad.output new file mode 100644 index 0000000..c0704aa --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/AssemblyReferences.razorpad.output @@ -0,0 +1 @@ +Awesome \ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/BasicRazorDocument.razorpad b/RazorPad.Core.Tests/AcceptanceTests/BasicRazorDocument.razorpad new file mode 100644 index 0000000..e043a3c --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/BasicRazorDocument.razorpad @@ -0,0 +1,20 @@ + + + Hello World! + 2012-04-19 12:42:19 + 2012-04-19 20:12:50 + + + + + + + + + \ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/BasicRazorDocument.razorpad.output b/RazorPad.Core.Tests/AcceptanceTests/BasicRazorDocument.razorpad.output new file mode 100644 index 0000000..b0dc386 --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/BasicRazorDocument.razorpad.output @@ -0,0 +1 @@ +

Hello, RazorPad!

\ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/Functions.cshtml b/RazorPad.Core.Tests/AcceptanceTests/Functions.cshtml new file mode 100644 index 0000000..7173a45 --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/Functions.cshtml @@ -0,0 +1,15 @@ +@functions { + + private const int firstNumber = 10; + + int Add(int x, int y) + { + return x + y; + } +} + +@{ + const int secondNumber = 5; +} + +@firstNumber plus @secondNumber equals @Add(firstNumber, secondNumber) \ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/Functions.cshtml.output b/RazorPad.Core.Tests/AcceptanceTests/Functions.cshtml.output new file mode 100644 index 0000000..70afc81 --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/Functions.cshtml.output @@ -0,0 +1 @@ +10 plus 5 equals 15 \ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/Helpers.cshtml b/RazorPad.Core.Tests/AcceptanceTests/Helpers.cshtml new file mode 100644 index 0000000..b982fb6 --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/Helpers.cshtml @@ -0,0 +1,5 @@ +@helper DisplayCurrency(decimal value) { + @value.ToString("C2", new System.Globalization.CultureInfo("en-us")) +} + +@DisplayCurrency(3.4m) \ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/Helpers.cshtml.output b/RazorPad.Core.Tests/AcceptanceTests/Helpers.cshtml.output new file mode 100644 index 0000000..6fe4f0a --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/Helpers.cshtml.output @@ -0,0 +1 @@ +$3.40 \ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/SimpleRendering.cshtml b/RazorPad.Core.Tests/AcceptanceTests/SimpleRendering.cshtml new file mode 100644 index 0000000..d59be12 --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/SimpleRendering.cshtml @@ -0,0 +1,5 @@ +@{ + var name = "RazorPad"; +} + +

Hello, @name!

\ No newline at end of file diff --git a/RazorPad.Core.Tests/AcceptanceTests/SimpleRendering.cshtml.output b/RazorPad.Core.Tests/AcceptanceTests/SimpleRendering.cshtml.output new file mode 100644 index 0000000..173b6f8 --- /dev/null +++ b/RazorPad.Core.Tests/AcceptanceTests/SimpleRendering.cshtml.output @@ -0,0 +1 @@ +

Hello, RazorPad!

\ No newline at end of file diff --git a/RazorPad.Core.Tests/Persistence/RazorDocumentLoaderTests.cs b/RazorPad.Core.Tests/Persistence/RazorDocumentLoaderTests.cs new file mode 100644 index 0000000..240d435 --- /dev/null +++ b/RazorPad.Core.Tests/Persistence/RazorDocumentLoaderTests.cs @@ -0,0 +1,34 @@ +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using RazorPad.Persistence; + +namespace RazorPad.Core.Tests +{ + [TestClass] + public class RazorDocumentLoaderTests + { + private RazorDocumentManager _manager; + + [TestInitialize] + public void TestInitialize() + { + _manager = new RazorDocumentManager(new XmlRazorDocumentSource()); + } + + [TestMethod] + public void ShouldLoadXmlRazorDocument() + { + var document = _manager.Parse("System.Awesome"); + + Assert.AreEqual("

Hello, World!

", document.Template); + Assert.AreEqual("System.Awesome", document.References.Single()); + } + + [TestMethod] + public void ShouldLoadCSharpRazorTemplate() + { + var document = _manager.Parse(@"

Hello, World!

"); + Assert.AreEqual("

Hello, World!

", document.Template.Trim()); + } + } +} \ No newline at end of file diff --git a/RazorPad.Core.Tests/Persistence/XmlRazorDocumentSourceTests.cs b/RazorPad.Core.Tests/Persistence/XmlRazorDocumentSourceTests.cs new file mode 100644 index 0000000..7577ca1 --- /dev/null +++ b/RazorPad.Core.Tests/Persistence/XmlRazorDocumentSourceTests.cs @@ -0,0 +1,111 @@ +using System.IO; +using System.Xml.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using RazorPad.Persistence; +using RazorPad.Providers; + +namespace RazorPad.Core.Tests +{ + [TestClass] + public class XmlRazorDocumentSourceTests + { + private XmlRazorDocumentSource _documentSource; + private const string DemoDocument = @" + + + Hello World! + 2012-04-19 12:42:19 + 2012-04-19 20:12:50 + + + + + + + + + "; + + [TestInitialize] + public void TestInitialize() + { + _documentSource = new XmlRazorDocumentSource(); + } + + [TestMethod] + public void ShouldLoadEmptyDocument() + { + var document = _documentSource.Parse(""); + + Assert.IsNotNull(document); + } + + [TestMethod] + public void ShouldLoadRazorDocument() + { + var document = _documentSource.Parse(DemoDocument); + + Assert.AreEqual("

Hello, @Model.Name!

", document.Template.Trim()); + Assert.AreEqual("Hello World!", document.Metadata["Title"]); + Assert.IsInstanceOfType(document.ModelProvider, typeof(JsonModelProvider)); + + dynamic model = document.GetModel(); + Assert.AreEqual("RazorPad", model.Name); + } + + [TestMethod] + public void ShouldSaveRazorDocument() + { + const string expectedModel = "{Name:'RazorPad'}"; + var document = new RazorDocument + { + ModelProvider = new JsonModelProvider { Json = expectedModel }, + Template = "

Test!

", + }; + document.Metadata.Add("Title", "Test Document"); + + XDocument savedXml; + using (var stream = new MemoryStream()) + { + _documentSource.Save(document, stream); + stream.Seek(0, SeekOrigin.Begin); + savedXml = XDocument.Load(stream); + } + + var modelElement = savedXml.Root.Element("Model"); + Assert.AreEqual("Json", modelElement.Attribute("Provider").Value); + Assert.AreEqual(expectedModel, modelElement.Value); + } + + [TestMethod] + public void ShouldSaveAndLoadRazorDocument() + { + const string expectedModel = "{Name:'RazorPad'}"; + var originalDocument = new RazorDocument + { + ModelProvider = new JsonModelProvider { Json = expectedModel }, + Template = "

Test!

", + }; + originalDocument.Metadata.Add("Title", "Test Document"); + + RazorDocument rehydratedDocument; + using (var stream = new MemoryStream()) + { + _documentSource.Save(originalDocument, stream); + stream.Seek(0, SeekOrigin.Begin); + rehydratedDocument = _documentSource.Load(stream); + } + + Assert.AreEqual(originalDocument.Metadata["Title"], rehydratedDocument.Metadata["Title"]); + Assert.AreEqual(originalDocument.Template, rehydratedDocument.Template); + Assert.AreEqual(((JsonModelProvider)originalDocument.ModelProvider).Json, + ((JsonModelProvider)rehydratedDocument.ModelProvider).Json); + } + } +} diff --git a/RazorPad.Core.Tests/Properties/AssemblyInfo.cs b/RazorPad.Core.Tests/Properties/AssemblyInfo.cs index 3bf046b..2c3a181 100644 --- a/RazorPad.Core.Tests/Properties/AssemblyInfo.cs +++ b/RazorPad.Core.Tests/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/RazorPad.Core.Tests/RazorPad.Core.Tests.csproj b/RazorPad.Core.Tests/RazorPad.Core.Tests.csproj index 47c6096..0a968a1 100644 --- a/RazorPad.Core.Tests/RazorPad.Core.Tests.csproj +++ b/RazorPad.Core.Tests/RazorPad.Core.Tests.csproj @@ -45,6 +45,8 @@ False ..\..\..\..\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\System.Web.Razor.dll + + @@ -52,15 +54,44 @@ - + + True + True + AcceptanceTests.tt + + + + + TextTemplatingFileGenerator + AcceptanceTests.cs + + + + + + + {92E04BBB-A022-41BA-A13C-1EB6C47DFFAE} + RazorPad.Contracts + {511D144C-F4C7-40A3-B3B4-80D891FCE2F5} RazorPad.Core + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/RazorPad.UI.Application/Properties/Settings.Designer.cs b/RazorPad.UI.Application/Properties/Settings.Designer.cs new file mode 100644 index 0000000..e1e3aea --- /dev/null +++ b/RazorPad.UI.Application/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RazorPad.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/RazorPad.UI.Application/Properties/Settings.settings b/RazorPad.UI.Application/Properties/Settings.settings new file mode 100644 index 0000000..8f2fd95 --- /dev/null +++ b/RazorPad.UI.Application/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/RazorPad.UI.Application/RazorPad.UI.Application.csproj b/RazorPad.UI.Application/RazorPad.UI.Application.csproj new file mode 100644 index 0000000..f847b21 --- /dev/null +++ b/RazorPad.UI.Application/RazorPad.UI.Application.csproj @@ -0,0 +1,216 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {A635596E-2215-4FEF-BC10-92F63EAAAA97} + WinExe + Properties + RazorPad + RazorPad + v4.0 + + + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + ..\..\RazorPad\ + true + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + razor.ico + + + true + + + + ..\packages\AvalonDock.2.0.0345\lib\net40\AvalonDock.dll + + + ..\packages\AvalonDock.2.0.0345\lib\net40\AvalonDock.Themes.VS2010.dll + + + + False + ..\packages\AvalonEdit.4.1.0.8000\lib\Net40\ICSharpCode.AvalonEdit.dll + + + ..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + BrowserView.xaml + + + SyntaxTreeView.xaml + + + ErrorsView.xaml + + + GeneratedCodeView.xaml + + + MessagesView.xaml + + + SourceView.xaml + + + ReferencesDialogWindow.xaml + + + AssemblyReferences.xaml + + + ListViewFilterText.xaml + + + MainWindow.xaml + + + ModelBuilder.xaml + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + {92E04BBB-A022-41BA-A13C-1EB6C47DFFAE} + RazorPad.Contracts + + + {511D144C-F4C7-40A3-B3B4-80D891FCE2F5} + RazorPad.Core + + + {147DFCE8-A33F-42F4-A8F1-7E999C02D1DC} + RazorPad.UI + + + + + + + + + + xcopy /Y "$(SolutionDir)Assets\Themes" "$(TargetDir)Themes\" + + + \ No newline at end of file diff --git a/RazorPad.UI.Application/RazorPad.UI.Application.ncrunchproject b/RazorPad.UI.Application/RazorPad.UI.Application.ncrunchproject new file mode 100644 index 0000000..043e7f0 --- /dev/null +++ b/RazorPad.UI.Application/RazorPad.UI.Application.ncrunchproject @@ -0,0 +1,16 @@ + + false + false + true + false + false + true + true + false + true + true + 60000 + + + AutoDetect + \ No newline at end of file diff --git a/RazorPad.UI.Application/ServiceLocator.cs b/RazorPad.UI.Application/ServiceLocator.cs new file mode 100644 index 0000000..ce4a5f8 --- /dev/null +++ b/RazorPad.UI.Application/ServiceLocator.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.ComponentModel.Composition.Hosting; +using System.IO; +using System.Linq; +using System.Reflection; +using NLog; + +namespace RazorPad +{ + public static class ServiceLocator + { + static readonly Logger Log = LogManager.GetCurrentClassLogger(); + + internal static CompositionContainer Container { get; private set; } + + public static void Initialize() + { + if(Container != null) + return; + + Log.Info("Initializing Service Locator..."); + + var assemblies = new AggregateCatalog( + new AssemblyCatalog(Assembly.Load("RazorPad.Core")), + new AssemblyCatalog(Assembly.Load("RazorPad.UI")), + new AssemblyCatalog(Assembly.Load("RazorPad")) + ); + + var plugins = GetPluginsCatalog(); + + Container = new CompositionContainer(new AggregateCatalog(assemblies, plugins)); + + Log.Info("Service Locator initialized"); + } + + public static TService Get(string name = null) + { + if (name != null) + return Container.GetExportedValue(name); + + return Container.GetExportedValue(); + } + + public static IEnumerable GetMany(string name = null) + { + if (name != null) + return Container.GetExportedValues(name); + + return Container.GetExportedValues(); + } + + public static void Inject(object target) + { + Container.ComposeParts(target); + } + + private static DirectoryCatalog GetPluginsCatalog() + { + string pluginPath = Path.Combine(Environment.CurrentDirectory, "plugins"); + + Log.Info("Looking for plugins in " + pluginPath); + + if (!Directory.Exists(pluginPath)) + { + Log.Debug("Plugin directory {0} doesn't exist - creating...", pluginPath); + Directory.CreateDirectory(pluginPath); + } + + var plugins = new DirectoryCatalog(pluginPath); + + Log.Info(string.Format("Found {0} parts in {1} plugin assemblies", + plugins.Parts.Count(), plugins.LoadedFiles.Count)); + + return plugins; + } + } +} diff --git a/RazorPad.UI.Application/Views/ActiveDocumentConverter.cs b/RazorPad.UI.Application/Views/ActiveDocumentConverter.cs new file mode 100644 index 0000000..0271aa3 --- /dev/null +++ b/RazorPad.UI.Application/Views/ActiveDocumentConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Windows.Data; +using System.Windows.Markup; +using RazorPad.ViewModels; + +namespace RazorPad.Views +{ + public class ActiveDocumentConverter : MarkupExtension, IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return RazorEditorOrDoNothing(value); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return RazorEditorOrDoNothing(value); + } + + private static object RazorEditorOrDoNothing(object value) + { + if (value is RazorTemplateEditorViewModel) + return value; + + return Binding.DoNothing; + } + + + private static volatile ActiveDocumentConverter _instance; + + public override object ProvideValue(IServiceProvider serviceProvider) + { + return _instance = _instance ?? new ActiveDocumentConverter(); + } + } +} \ No newline at end of file diff --git a/RazorPad.UI.Application/Views/AssemblyReferences.xaml b/RazorPad.UI.Application/Views/AssemblyReferences.xaml new file mode 100644 index 0000000..7a7c82a --- /dev/null +++ b/RazorPad.UI.Application/Views/AssemblyReferences.xaml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RazorPad.UI.Application/Views/AssemblyReferences.xaml.cs b/RazorPad.UI.Application/Views/AssemblyReferences.xaml.cs new file mode 100644 index 0000000..eea72e3 --- /dev/null +++ b/RazorPad.UI.Application/Views/AssemblyReferences.xaml.cs @@ -0,0 +1,10 @@ +namespace RazorPad.Views +{ + public partial class AssemblyReferences + { + public AssemblyReferences() + { + InitializeComponent(); + } + } +} diff --git a/RazorPad.UI.Application/Views/ListViewFilterText.xaml b/RazorPad.UI.Application/Views/ListViewFilterText.xaml new file mode 100644 index 0000000..e5c470b --- /dev/null +++ b/RazorPad.UI.Application/Views/ListViewFilterText.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + +