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:
parent
ac09c8fc38
commit
a6c7933bab
|
@ -151,6 +151,31 @@ namespace mustache.test
|
||||||
Assert.AreEqual(expected, actual, "The wrong message was generated.");
|
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>
|
/// <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.4.0")]
|
[assembly: AssemblyVersion("0.0.5.0")]
|
||||||
[assembly: AssemblyFileVersion("0.0.4.0")]
|
[assembly: AssemblyFileVersion("0.0.5.0")]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using mustache.Properties;
|
using mustache.Properties;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
|
@ -63,27 +64,37 @@ namespace mustache
|
||||||
object nextLevel = _source;
|
object nextLevel = _source;
|
||||||
if (member != "this")
|
if (member != "this")
|
||||||
{
|
{
|
||||||
nextLevel = find(member);
|
nextLevel = find(name, member);
|
||||||
}
|
}
|
||||||
for (int index = 1; index < names.Length; ++index)
|
for (int index = 1; index < names.Length; ++index)
|
||||||
{
|
{
|
||||||
IDictionary<string, object> context = toLookup(nextLevel);
|
IDictionary<string, object> context = toLookup(nextLevel);
|
||||||
member = names[index];
|
member = names[index];
|
||||||
nextLevel = context[member];
|
if (!context.TryGetValue(member, out nextLevel))
|
||||||
|
{
|
||||||
|
nextLevel = handleKeyNotFound(name, member);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nextLevel;
|
return nextLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private object find(string name)
|
private object find(string fullName, string memberName)
|
||||||
{
|
{
|
||||||
IDictionary<string, object> lookup = toLookup(_source);
|
IDictionary<string, object> lookup = toLookup(_source);
|
||||||
if (lookup.ContainsKey(name))
|
if (lookup.ContainsKey(memberName))
|
||||||
{
|
{
|
||||||
return lookup[name];
|
return lookup[memberName];
|
||||||
}
|
}
|
||||||
if (_parent == null)
|
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)
|
if (KeyNotFound != null)
|
||||||
{
|
{
|
||||||
KeyNotFound(this, args);
|
KeyNotFound(this, args);
|
||||||
|
@ -92,11 +103,9 @@ namespace mustache
|
||||||
{
|
{
|
||||||
return args.Substitute;
|
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);
|
throw new KeyNotFoundException(message);
|
||||||
}
|
}
|
||||||
return _parent.find(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IDictionary<string, object> toLookup(object value)
|
private static IDictionary<string, object> toLookup(object value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,16 +10,23 @@ namespace mustache
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of a MissingKeyEventArgs.
|
/// Initializes a new instance of a MissingKeyEventArgs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="missingKey">The key that had no match.</param>
|
/// <param name="key">The fully-qualified key.</param>
|
||||||
internal MissingKeyEventArgs(string missingKey)
|
/// <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>
|
/// <summary>
|
||||||
/// Gets the key that could not be found.
|
/// Gets the fully-qualified key.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets or sets whether to use the substitute.
|
/// Gets or sets whether to use the substitute.
|
||||||
|
|
|
@ -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.4.0")]
|
[assembly: AssemblyVersion("0.0.5.0")]
|
||||||
[assembly: AssemblyFileVersion("0.0.4.0")]
|
[assembly: AssemblyFileVersion("0.0.4.0")]
|
||||||
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
Loading…
Reference in New Issue