Provide more details when keys not found.

A request was made to provide the original key as well as the member
that was not found. In compound keys {{Customer.Address.ZipCode}}, any
of the names could be invalid, but knowing the fully-qualified name is
useful for error handling.

I also detected a bug where the code wasn't handling the case that a
sub-key didn't exist. This needs to be handled using the KeyNotFound
event.
This commit is contained in:
Travis Parks 2013-04-19 08:56:21 -04:00
parent ac09c8fc38
commit a6c7933bab
5 changed files with 66 additions and 25 deletions

View File

@ -151,6 +151,31 @@ namespace mustache.test
Assert.AreEqual(expected, actual, "The wrong message was generated.");
}
/// <summary>
/// If part of a key is wrong, the full details should be provided.
/// </summary>
[TestMethod]
public void TestCompile_MultipartKey_PartMissing_ProvidesFullDetail()
{
FormatCompiler compiler = new FormatCompiler();
const string format = @"{{Customer.Name}}";
Generator generator = compiler.Compile(format);
generator.KeyNotFound += (obj, args) =>
{
args.Substitute = args.Key + "," + args.MissingMember;
args.Handled = true;
};
string actual = generator.Render(new
{
Customer = new
{
FirstName = "Bob"
}
});
string expected = "Customer.Name,Name";
Assert.AreEqual(expected, actual, "The wrong message was generated.");
}
/// <summary>
/// If we specify an alignment with a key, the alignment should
/// be used when rending the value.

View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("0.0.4.0")]
[assembly: AssemblyFileVersion("0.0.4.0")]
[assembly: AssemblyVersion("0.0.5.0")]
[assembly: AssemblyFileVersion("0.0.5.0")]

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using mustache.Properties;
namespace mustache
@ -63,27 +64,37 @@ namespace mustache
object nextLevel = _source;
if (member != "this")
{
nextLevel = find(member);
nextLevel = find(name, member);
}
for (int index = 1; index < names.Length; ++index)
{
IDictionary<string, object> context = toLookup(nextLevel);
member = names[index];
nextLevel = context[member];
if (!context.TryGetValue(member, out nextLevel))
{
nextLevel = handleKeyNotFound(name, member);
}
}
return nextLevel;
}
private object find(string name)
private object find(string fullName, string memberName)
{
IDictionary<string, object> lookup = toLookup(_source);
if (lookup.ContainsKey(name))
if (lookup.ContainsKey(memberName))
{
return lookup[name];
return lookup[memberName];
}
if (_parent == null)
{
MissingKeyEventArgs args = new MissingKeyEventArgs(name);
return handleKeyNotFound(fullName, memberName);
}
return _parent.find(fullName, memberName);
}
private object handleKeyNotFound(string fullName, string memberName)
{
MissingKeyEventArgs args = new MissingKeyEventArgs(fullName, memberName);
if (KeyNotFound != null)
{
KeyNotFound(this, args);
@ -92,11 +103,9 @@ namespace mustache
{
return args.Substitute;
}
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, name);
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, memberName);
throw new KeyNotFoundException(message);
}
return _parent.find(name);
}
private static IDictionary<string, object> toLookup(object value)
{

View File

@ -10,16 +10,23 @@ namespace mustache
/// <summary>
/// Initializes a new instance of a MissingKeyEventArgs.
/// </summary>
/// <param name="missingKey">The key that had no match.</param>
internal MissingKeyEventArgs(string missingKey)
/// <param name="key">The fully-qualified key.</param>
/// <param name="missingMember">The part of the key that could not be found.</param>
internal MissingKeyEventArgs(string key, string missingMember)
{
MissingKey = missingKey;
Key = key;
MissingMember = missingMember;
}
/// <summary>
/// Gets the key that could not be found.
/// Gets the fully-qualified key.
/// </summary>
public string MissingKey { get; private set; }
public string Key { get; private set; }
/// <summary>
/// Gets the part of the key that could not be found.
/// </summary>
public string MissingMember { get; private set; }
/// <summary>
/// Gets or sets whether to use the substitute.

View File

@ -34,6 +34,6 @@ using System.Runtime.CompilerServices;
// 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("0.0.4.0")]
[assembly: AssemblyVersion("0.0.5.0")]
[assembly: AssemblyFileVersion("0.0.4.0")]
[assembly: InternalsVisibleTo("mustache-sharp.test")]