Support firing event when keys/variables used as arguments.
This commit is contained in:
parent
6e74fa1fcc
commit
6a272230af
|
@ -349,6 +349,63 @@ Content";
|
|||
CollectionAssert.AreEqual(expected, actual, "Not all placeholders were found.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We can track all of the keys that appear in a template by
|
||||
/// registering with the PlaceholderFound event.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCompile_FindsVariables_RecordsVariables()
|
||||
{
|
||||
FormatCompiler compiler = new FormatCompiler();
|
||||
HashSet<string> variables = new HashSet<string>();
|
||||
compiler.VariableFound += (o, e) =>
|
||||
{
|
||||
variables.Add(e.Name);
|
||||
};
|
||||
compiler.Compile(@"{{@FirstName}}{{@LastName}}");
|
||||
string[] expected = new string[] { "FirstName", "LastName" };
|
||||
string[] actual = variables.OrderBy(s => s).ToArray();
|
||||
CollectionAssert.AreEqual(expected, actual, "Not all variables were found.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We can track all of the keys that appear in a template by
|
||||
/// registering with the PlaceholderFound event.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCompile_FindsPlaceholdersInIf_RecordsPlaceholders()
|
||||
{
|
||||
FormatCompiler compiler = new FormatCompiler();
|
||||
HashSet<string> keys = new HashSet<string>();
|
||||
compiler.PlaceholderFound += (o, e) =>
|
||||
{
|
||||
keys.Add(e.Key);
|
||||
};
|
||||
compiler.Compile(@"{{#if FirstName}}{{/if}}");
|
||||
string[] expected = new string[] { "FirstName" };
|
||||
string[] actual = keys.OrderBy(s => s).ToArray();
|
||||
CollectionAssert.AreEqual(expected, actual, "Not all placeholders were found.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We can track all of the keys that appear in a template by
|
||||
/// registering with the PlaceholderFound event.
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void TestCompile_FindsVariablesInIf_RecordsVariables()
|
||||
{
|
||||
FormatCompiler compiler = new FormatCompiler();
|
||||
HashSet<string> variables = new HashSet<string>();
|
||||
compiler.VariableFound += (o, e) =>
|
||||
{
|
||||
variables.Add(e.Name);
|
||||
};
|
||||
compiler.Compile(@"{{#if @FirstName}}{{/if}}");
|
||||
string[] expected = new string[] { "FirstName" };
|
||||
string[] actual = variables.OrderBy(s => s).ToArray();
|
||||
CollectionAssert.AreEqual(expected, actual, "Not all placeholders were found.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We can determine the context in which a placeholder is found by looking at the provided context array.
|
||||
/// </summary>
|
||||
|
|
|
@ -48,6 +48,11 @@ namespace Mustache
|
|||
/// </summary>
|
||||
public event EventHandler<PlaceholderFoundEventArgs> PlaceholderFound;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a variable is found in the template.
|
||||
/// </summary>
|
||||
public event EventHandler<VariableFoundEventArgs> VariableFound;
|
||||
|
||||
/// <summary>
|
||||
/// Registers the given tag definition with the parser.
|
||||
/// </summary>
|
||||
|
@ -140,7 +145,7 @@ namespace Mustache
|
|||
|
||||
private static string getKeyRegex()
|
||||
{
|
||||
return @"((?<key>" + RegexHelper.CompoundKey + @")(,(?<alignment>(\+|-)?[\d]+))?(:(?<format>.*?))?)";
|
||||
return @"((?<key>@?" + RegexHelper.CompoundKey + @")(,(?<alignment>(\+|-)?[\d]+))?(:(?<format>.*?))?)";
|
||||
}
|
||||
|
||||
private static string getTagRegex(TagDefinition definition)
|
||||
|
@ -198,12 +203,29 @@ namespace Mustache
|
|||
string key = match.Groups["key"].Value;
|
||||
string alignment = match.Groups["alignment"].Value;
|
||||
string formatting = match.Groups["format"].Value;
|
||||
PlaceholderFoundEventArgs args = new PlaceholderFoundEventArgs(key, alignment, formatting, context.ToArray());
|
||||
if (PlaceholderFound != null)
|
||||
if (key.StartsWith("@"))
|
||||
{
|
||||
PlaceholderFound(this, args);
|
||||
VariableFoundEventArgs args = new VariableFoundEventArgs(key.Substring(1), alignment, formatting, context.ToArray());
|
||||
if (VariableFound != null)
|
||||
{
|
||||
VariableFound(this, args);
|
||||
key = "@" + args.Name;
|
||||
alignment = args.Alignment;
|
||||
formatting = args.Formatting;
|
||||
}
|
||||
}
|
||||
KeyGenerator keyGenerator = new KeyGenerator(args.Key, args.Alignment, args.Formatting);
|
||||
else
|
||||
{
|
||||
PlaceholderFoundEventArgs args = new PlaceholderFoundEventArgs(key, alignment, formatting, context.ToArray());
|
||||
if (PlaceholderFound != null)
|
||||
{
|
||||
PlaceholderFound(this, args);
|
||||
key = args.Key;
|
||||
alignment = args.Alignment;
|
||||
formatting = args.Formatting;
|
||||
}
|
||||
}
|
||||
KeyGenerator keyGenerator = new KeyGenerator(key, alignment, formatting);
|
||||
generator.AddGenerator(keyGenerator);
|
||||
}
|
||||
else if (match.Groups["open"].Success)
|
||||
|
@ -216,10 +238,12 @@ namespace Mustache
|
|||
string message = String.Format(Resources.UnknownTag, tagName);
|
||||
throw new FormatException(message);
|
||||
}
|
||||
|
||||
generator.AddGenerator(new StaticGenerator(leading));
|
||||
ArgumentCollection arguments = getArguments(nextDefinition, match, context);
|
||||
|
||||
if (nextDefinition.HasContent)
|
||||
{
|
||||
generator.AddGenerator(new StaticGenerator(leading));
|
||||
ArgumentCollection arguments = getArguments(nextDefinition, match);
|
||||
CompoundGenerator compoundGenerator = new CompoundGenerator(nextDefinition, arguments);
|
||||
IEnumerable<TagParameter> contextParameters = nextDefinition.GetChildContextParameters();
|
||||
bool hasContext = contextParameters.Any();
|
||||
|
@ -237,8 +261,6 @@ namespace Mustache
|
|||
}
|
||||
else
|
||||
{
|
||||
generator.AddGenerator(new StaticGenerator(leading));
|
||||
ArgumentCollection arguments = getArguments(nextDefinition, match);
|
||||
InlineGenerator inlineGenerator = new InlineGenerator(nextDefinition, arguments);
|
||||
generator.AddGenerator(inlineGenerator);
|
||||
}
|
||||
|
@ -270,9 +292,9 @@ namespace Mustache
|
|||
return formatIndex;
|
||||
}
|
||||
|
||||
private static ArgumentCollection getArguments(TagDefinition definition, Match match)
|
||||
private ArgumentCollection getArguments(TagDefinition definition, Match match, List<Context> context)
|
||||
{
|
||||
ArgumentCollection collection = new ArgumentCollection();
|
||||
// make sure we don't have too many arguments
|
||||
List<Capture> captures = match.Groups["argument"].Captures.Cast<Capture>().ToList();
|
||||
List<TagParameter> parameters = definition.Parameters.ToList();
|
||||
if (captures.Count > parameters.Count)
|
||||
|
@ -280,25 +302,60 @@ namespace Mustache
|
|||
string message = String.Format(Resources.WrongNumberOfArguments, definition.Name);
|
||||
throw new FormatException(message);
|
||||
}
|
||||
|
||||
// provide default values for missing arguments
|
||||
if (captures.Count < parameters.Count)
|
||||
{
|
||||
captures.AddRange(Enumerable.Repeat((Capture)null, parameters.Count - captures.Count));
|
||||
}
|
||||
|
||||
// pair up the parameters to the given arguments
|
||||
// provide default for parameters with missing arguments
|
||||
// throw an error if a missing argument is for a required parameter
|
||||
Dictionary<TagParameter, string> arguments = new Dictionary<TagParameter, string>();
|
||||
foreach (var pair in parameters.Zip(captures, (p, c) => new { Capture = c, Parameter = p }))
|
||||
{
|
||||
if (pair.Capture == null)
|
||||
string value = null;
|
||||
if (pair.Capture != null)
|
||||
{
|
||||
if (pair.Parameter.IsRequired)
|
||||
{
|
||||
string message = String.Format(Resources.WrongNumberOfArguments, definition.Name);
|
||||
throw new FormatException(message);
|
||||
}
|
||||
collection.AddArgument(pair.Parameter, null);
|
||||
value = pair.Capture.Value;
|
||||
}
|
||||
else
|
||||
else if (pair.Parameter.IsRequired)
|
||||
{
|
||||
collection.AddArgument(pair.Parameter, pair.Capture.Value);
|
||||
}
|
||||
string message = String.Format(Resources.WrongNumberOfArguments, definition.Name);
|
||||
throw new FormatException(message);
|
||||
}
|
||||
arguments.Add(pair.Parameter, value);
|
||||
}
|
||||
|
||||
// indicate that a key/variable has been encountered
|
||||
// update the key/variable name
|
||||
ArgumentCollection collection = new ArgumentCollection();
|
||||
foreach (var pair in arguments)
|
||||
{
|
||||
string placeholder = pair.Value;
|
||||
if (placeholder != null)
|
||||
{
|
||||
if (placeholder.StartsWith("@"))
|
||||
{
|
||||
VariableFoundEventArgs args = new VariableFoundEventArgs(placeholder.Substring(1), String.Empty, String.Empty, context.ToArray());
|
||||
if (VariableFound != null)
|
||||
{
|
||||
VariableFound(this, args);
|
||||
placeholder = "@" + args.Name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PlaceholderFoundEventArgs args = new PlaceholderFoundEventArgs(placeholder, String.Empty, String.Empty, context.ToArray());
|
||||
if (PlaceholderFound != null)
|
||||
{
|
||||
PlaceholderFound(this, args);
|
||||
placeholder = args.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
collection.AddArgument(pair.Key, placeholder);
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Mustache
|
|||
{
|
||||
private readonly string _key;
|
||||
private readonly string _format;
|
||||
private readonly bool _isVariable;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a KeyGenerator.
|
||||
|
@ -20,7 +21,16 @@ namespace Mustache
|
|||
/// <param name="formatting">The format specifier.</param>
|
||||
public KeyGenerator(string key, string alignment, string formatting)
|
||||
{
|
||||
_key = key;
|
||||
if (key.StartsWith("@"))
|
||||
{
|
||||
_key = key.Substring(1);
|
||||
_isVariable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_key = key;
|
||||
_isVariable = false;
|
||||
}
|
||||
_format = getFormat(alignment, formatting);
|
||||
}
|
||||
|
||||
|
@ -44,7 +54,7 @@ namespace Mustache
|
|||
|
||||
void IGenerator.GetText(Scope scope, TextWriter writer, Scope context)
|
||||
{
|
||||
object value = scope.Find(_key);
|
||||
object value = _isVariable ? context.Find(_key) : scope.Find(_key);
|
||||
writer.Write(_format, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using Mustache.Properties;
|
||||
|
||||
namespace Mustache
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the information descibing a variable that is found in a template.
|
||||
/// </summary>
|
||||
public class VariableFoundEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a VariableFoundEventArgs.
|
||||
/// </summary>
|
||||
/// <param name="key">The key that was found.</param>
|
||||
/// <param name="alignment">The alignment that will be applied to the substitute value.</param>
|
||||
/// <param name="formatting">The formatting that will be applied to the substitute value.</param>
|
||||
/// <param name="context">The context where the placeholder was found.</param>
|
||||
internal VariableFoundEventArgs(string name, string alignment, string formatting, Context[] context)
|
||||
{
|
||||
Name = name;
|
||||
Alignment = alignment;
|
||||
Formatting = formatting;
|
||||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the key that was found.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alignment that will be applied to the substitute value.
|
||||
/// </summary>
|
||||
public string Alignment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the formatting that will be applied to the substitute value.
|
||||
/// </summary>
|
||||
public string Formatting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the context where the placeholder was found.
|
||||
/// </summary>
|
||||
public Context[] Context { get; private set; }
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@
|
|||
<Compile Include="ContentTagDefinition.cs" />
|
||||
<Compile Include="Context.cs" />
|
||||
<Compile Include="ContextParameter.cs" />
|
||||
<Compile Include="VariableFoundEventArgs.cs" />
|
||||
<Compile Include="SetTagDefinition.cs" />
|
||||
<Compile Include="NewlineTagDefinition.cs" />
|
||||
<Compile Include="IndexTagDefinition.cs" />
|
||||
|
|
Loading…
Reference in New Issue