Support Missing Key Handlers
A request was made to allow missing keys to be handled in a custom manner. I created an event that can be registered with the Generator instances. I also fixed a deployment issue causing debug versions of the library to be packaged and released to NuGet. This should boost performance considerably.
This commit is contained in:
parent
bfc579be22
commit
ac09c8fc38
|
@ -1,3 +1,4 @@
|
||||||
nuget pack ../mustache-sharp/mustache-sharp.csproj -Prop Configuration=Release -Build
|
msbuild ../mustache-sharp.sln /p:Configuration=Release
|
||||||
|
nuget pack ../mustache-sharp/mustache-sharp.csproj -Properties Configuration=Release
|
||||||
nuget push *.nupkg
|
nuget push *.nupkg
|
||||||
del *.nupkg
|
del *.nupkg
|
|
@ -110,6 +110,47 @@ namespace mustache.test
|
||||||
generator.Render(new object());
|
generator.Render(new object());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If we try to print a key that doesn't exist, we can provide a
|
||||||
|
/// handler to provide a substitute.
|
||||||
|
/// </summary>
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCompile_MissingKey_CallsKeyNotFoundHandler()
|
||||||
|
{
|
||||||
|
FormatCompiler compiler = new FormatCompiler();
|
||||||
|
const string format = @"Hello, {{Name}}!!!";
|
||||||
|
Generator generator = compiler.Compile(format);
|
||||||
|
generator.KeyNotFound += (obj, args) =>
|
||||||
|
{
|
||||||
|
args.Substitute = "Unknown";
|
||||||
|
args.Handled = true;
|
||||||
|
};
|
||||||
|
string actual = generator.Render(new object());
|
||||||
|
string expected = "Hello, Unknown!!!";
|
||||||
|
Assert.AreEqual(expected, actual, "The wrong message was generated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the key is the parent object, the search will go up the hierarchy.
|
||||||
|
/// </summary>
|
||||||
|
[TestMethod]
|
||||||
|
public void TestCompile_KeyInParent_LooksUpKeyInParent()
|
||||||
|
{
|
||||||
|
FormatCompiler compiler = new FormatCompiler();
|
||||||
|
const string format = @"{{#with Address}}{{FirstName}} from {{City}}{{/with}}";
|
||||||
|
Generator generator = compiler.Compile(format);
|
||||||
|
string actual = generator.Render(new
|
||||||
|
{
|
||||||
|
FirstName = "Bob",
|
||||||
|
Address = new
|
||||||
|
{
|
||||||
|
City = "Philadelphia",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
string expected = "Bob from Philadelphia";
|
||||||
|
Assert.AreEqual(expected, actual, "The wrong message was generated.");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If we specify an alignment with a key, the alignment should
|
/// If we specify an alignment with a key, the alignment should
|
||||||
/// be used when rending the value.
|
/// be used when rending the value.
|
||||||
|
|
|
@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
||||||
//
|
//
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
[assembly: AssemblyVersion("0.0.3.0")]
|
[assembly: AssemblyVersion("0.0.4.0")]
|
||||||
[assembly: AssemblyFileVersion("0.0.3.0")]
|
[assembly: AssemblyFileVersion("0.0.4.0")]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ namespace mustache
|
||||||
public sealed class Generator
|
public sealed class Generator
|
||||||
{
|
{
|
||||||
private readonly IGenerator _generator;
|
private readonly IGenerator _generator;
|
||||||
|
private readonly List<EventHandler<MissingKeyEventArgs>> _handlers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a Generator.
|
/// Initializes a new instance of a Generator.
|
||||||
|
@ -18,6 +20,16 @@ namespace mustache
|
||||||
internal Generator(IGenerator generator)
|
internal Generator(IGenerator generator)
|
||||||
{
|
{
|
||||||
_generator = generator;
|
_generator = generator;
|
||||||
|
_handlers = new List<EventHandler<MissingKeyEventArgs>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a key/property is not found in the object graph.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<MissingKeyEventArgs> KeyNotFound
|
||||||
|
{
|
||||||
|
add { _handlers.Add(value); }
|
||||||
|
remove { _handlers.Remove(value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -48,6 +60,10 @@ namespace mustache
|
||||||
private string render(IFormatProvider provider, object source)
|
private string render(IFormatProvider provider, object source)
|
||||||
{
|
{
|
||||||
KeyScope scope = new KeyScope(source);
|
KeyScope scope = new KeyScope(source);
|
||||||
|
foreach (EventHandler<MissingKeyEventArgs> handler in _handlers)
|
||||||
|
{
|
||||||
|
scope.KeyNotFound += handler;
|
||||||
|
}
|
||||||
StringWriter writer = new StringWriter(provider);
|
StringWriter writer = new StringWriter(provider);
|
||||||
_generator.GetText(scope, writer);
|
_generator.GetText(scope, writer);
|
||||||
return writer.ToString();
|
return writer.ToString();
|
||||||
|
|
|
@ -33,6 +33,11 @@ namespace mustache
|
||||||
_source = source;
|
_source = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a key/property is not found in the object graph.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<MissingKeyEventArgs> KeyNotFound;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a child scope that searches for keys in the given object.
|
/// Creates a child scope that searches for keys in the given object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -41,6 +46,7 @@ namespace mustache
|
||||||
public KeyScope CreateChildScope(object source)
|
public KeyScope CreateChildScope(object source)
|
||||||
{
|
{
|
||||||
KeyScope scope = new KeyScope(source, this);
|
KeyScope scope = new KeyScope(source, this);
|
||||||
|
scope.KeyNotFound = KeyNotFound;
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +83,15 @@ namespace mustache
|
||||||
}
|
}
|
||||||
if (_parent == null)
|
if (_parent == null)
|
||||||
{
|
{
|
||||||
|
MissingKeyEventArgs args = new MissingKeyEventArgs(name);
|
||||||
|
if (KeyNotFound != null)
|
||||||
|
{
|
||||||
|
KeyNotFound(this, args);
|
||||||
|
}
|
||||||
|
if (args.Handled)
|
||||||
|
{
|
||||||
|
return args.Substitute;
|
||||||
|
}
|
||||||
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, name);
|
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, name);
|
||||||
throw new KeyNotFoundException(message);
|
throw new KeyNotFoundException(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace mustache
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Holds the information needed to handle a missing key.
|
||||||
|
/// </summary>
|
||||||
|
public class MissingKeyEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of a MissingKeyEventArgs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="missingKey">The key that had no match.</param>
|
||||||
|
internal MissingKeyEventArgs(string missingKey)
|
||||||
|
{
|
||||||
|
MissingKey = missingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the key that could not be found.
|
||||||
|
/// </summary>
|
||||||
|
public string MissingKey { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether to use the substitute.
|
||||||
|
/// </summary>
|
||||||
|
public bool Handled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the object to use as the substitute.
|
||||||
|
/// </summary>
|
||||||
|
public object Substitute { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,6 @@ using System.Runtime.CompilerServices;
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("0.0.3.0")]
|
[assembly: AssemblyVersion("0.0.4.0")]
|
||||||
[assembly: AssemblyFileVersion("0.0.3.0")]
|
[assembly: AssemblyFileVersion("0.0.4.0")]
|
||||||
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
|
@ -49,6 +49,7 @@
|
||||||
<Compile Include="InlineGenerator.cs" />
|
<Compile Include="InlineGenerator.cs" />
|
||||||
<Compile Include="KeyGenerator.cs" />
|
<Compile Include="KeyGenerator.cs" />
|
||||||
<Compile Include="MasterTagDefinition.cs" />
|
<Compile Include="MasterTagDefinition.cs" />
|
||||||
|
<Compile Include="MissingKeyEventArgs.cs" />
|
||||||
<Compile Include="NestedContext.cs" />
|
<Compile Include="NestedContext.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
|
Loading…
Reference in New Issue