Support public fields for keys

This commit is contained in:
Travis Parks 2014-03-01 23:11:52 -05:00
parent 599dc7a78f
commit 517e38a6db
8 changed files with 139 additions and 120 deletions

View File

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="Local" id="2bc42439-1bb6-4112-9c20-eca1ffcae064" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010"> <TestSettings name="Local" id="2bc42439-1bb6-4112-9c20-eca1ffcae064" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Description>These are default test settings for a local test run.</Description> <Description>These are default test settings for a local test run.</Description>
<Execution> <Execution>
<TestTypeSpecific> <TestTypeSpecific>
<UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b"> <UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b">
<AssemblyResolution> <AssemblyResolution>
<TestDirectory useLoadContext="true" /> <TestDirectory useLoadContext="true" />
</AssemblyResolution> </AssemblyResolution>
</UnitTestRunConfig> </UnitTestRunConfig>
</TestTypeSpecific> </TestTypeSpecific>
<AgentRule name="LocalMachineDefaultRole"> <AgentRule name="LocalMachineDefaultRole">
<DataCollectors> <DataCollectors>
<DataCollector uri="datacollector://microsoft/CodeCoverage/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TestTools.CodeCoverage.CoveragePlugIn, Microsoft.VisualStudio.QualityTools.Plugins.CodeCoverage, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="Code Coverage"> <DataCollector uri="datacollector://microsoft/CodeCoverage/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TestTools.CodeCoverage.CoveragePlugIn, Microsoft.VisualStudio.QualityTools.Plugins.CodeCoverage, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="Code Coverage">
<Configuration> <Configuration>
<CodeCoverage xmlns=""> <CodeCoverage xmlns="">
<Regular> <Regular>
<CodeCoverageItem binaryFile="mustache-sharp\bin\Debug\mustache-sharp.dll" pdbFile="mustache-sharp\bin\Debug\mustache-sharp.pdb" instrumentInPlace="true" /> <CodeCoverageItem binaryFile="mustache-sharp\bin\Debug\mustache-sharp.dll" pdbFile="mustache-sharp\bin\Debug\mustache-sharp.pdb" instrumentInPlace="true" />
</Regular> </Regular>
</CodeCoverage> </CodeCoverage>
</Configuration> </Configuration>
</DataCollector> </DataCollector>
</DataCollectors> </DataCollectors>
</AgentRule> </AgentRule>
</Execution> </Execution>
</TestSettings> </TestSettings>

View File

@ -1,21 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="Trace and Test Impact" id="535ebf31-4d23-42a7-a823-ecb179ff7886" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010"> <TestSettings name="Trace and Test Impact" id="535ebf31-4d23-42a7-a823-ecb179ff7886" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Description>These are test settings for Trace and Test Impact.</Description> <Description>These are test settings for Trace and Test Impact.</Description>
<Execution> <Execution>
<TestTypeSpecific /> <TestTypeSpecific />
<AgentRule name="Execution Agents"> <AgentRule name="Execution Agents">
<DataCollectors> <DataCollectors>
<DataCollector uri="datacollector://microsoft/SystemInfo/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TestTools.DataCollection.SystemInfo.SystemInfoDataCollector, Microsoft.VisualStudio.TestTools.DataCollection.SystemInfo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="System Information"> <DataCollector uri="datacollector://microsoft/SystemInfo/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TestTools.DataCollection.SystemInfo.SystemInfoDataCollector, Microsoft.VisualStudio.TestTools.DataCollection.SystemInfo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="System Information">
</DataCollector> </DataCollector>
<DataCollector uri="datacollector://microsoft/ActionLog/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TestTools.ManualTest.ActionLog.ActionLogPlugin, Microsoft.VisualStudio.TestTools.ManualTest.ActionLog, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="Actions"> <DataCollector uri="datacollector://microsoft/ActionLog/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TestTools.ManualTest.ActionLog.ActionLogPlugin, Microsoft.VisualStudio.TestTools.ManualTest.ActionLog, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="Actions">
</DataCollector> </DataCollector>
<DataCollector uri="datacollector://microsoft/HttpProxy/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TraceCollector.HttpProxyCollector, Microsoft.VisualStudio.TraceCollector, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="ASP.NET Client Proxy for IntelliTrace and Test Impact"> <DataCollector uri="datacollector://microsoft/HttpProxy/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TraceCollector.HttpProxyCollector, Microsoft.VisualStudio.TraceCollector, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="ASP.NET Client Proxy for IntelliTrace and Test Impact">
</DataCollector> </DataCollector>
<DataCollector uri="datacollector://microsoft/TestImpact/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TraceCollector.TestImpactDataCollector, Microsoft.VisualStudio.TraceCollector, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="Test Impact"> <DataCollector uri="datacollector://microsoft/TestImpact/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TraceCollector.TestImpactDataCollector, Microsoft.VisualStudio.TraceCollector, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="Test Impact">
</DataCollector> </DataCollector>
<DataCollector uri="datacollector://microsoft/TraceDebugger/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TraceCollector.TraceDebuggerDataCollector, Microsoft.VisualStudio.TraceCollector, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="IntelliTrace"> <DataCollector uri="datacollector://microsoft/TraceDebugger/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TraceCollector.TraceDebuggerDataCollector, Microsoft.VisualStudio.TraceCollector, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="IntelliTrace">
</DataCollector> </DataCollector>
</DataCollectors> </DataCollectors>
</AgentRule> </AgentRule>
</Execution> </Execution>
</TestSettings> </TestSettings>

View File

@ -1,36 +1,36 @@
 
Microsoft Visual Studio Solution File, Format Version 11.00 Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010 # Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mustache-sharp", "mustache-sharp\mustache-sharp.csproj", "{D71B378F-A4BA-4263-A4F0-07A49A0C528D}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mustache-sharp", "mustache-sharp\mustache-sharp.csproj", "{D71B378F-A4BA-4263-A4F0-07A49A0C528D}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mustache-sharp.test", "mustache-sharp.test\mustache-sharp.test.csproj", "{7F607362-0680-4751-B1DC-621219294AE3}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mustache-sharp.test", "mustache-sharp.test\mustache-sharp.test.csproj", "{7F607362-0680-4751-B1DC-621219294AE3}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{25414E49-67E6-4B8D-8AD8-78C70F8992A7}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{25414E49-67E6-4B8D-8AD8-78C70F8992A7}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
Local.testsettings = Local.testsettings Local.testsettings = Local.testsettings
mustache-sharp.vsmdi = mustache-sharp.vsmdi mustache-sharp.vsmdi = mustache-sharp.vsmdi
TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
EndProjectSection EndProjectSection
EndProject EndProject
Global Global
GlobalSection(TestCaseManagementSettings) = postSolution GlobalSection(TestCaseManagementSettings) = postSolution
CategoryFile = mustache-sharp.vsmdi CategoryFile = mustache-sharp.vsmdi
EndGlobalSection EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D71B378F-A4BA-4263-A4F0-07A49A0C528D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D71B378F-A4BA-4263-A4F0-07A49A0C528D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D71B378F-A4BA-4263-A4F0-07A49A0C528D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D71B378F-A4BA-4263-A4F0-07A49A0C528D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D71B378F-A4BA-4263-A4F0-07A49A0C528D}.Release|Any CPU.ActiveCfg = Release|Any CPU {D71B378F-A4BA-4263-A4F0-07A49A0C528D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D71B378F-A4BA-4263-A4F0-07A49A0C528D}.Release|Any CPU.Build.0 = Release|Any CPU {D71B378F-A4BA-4263-A4F0-07A49A0C528D}.Release|Any CPU.Build.0 = Release|Any CPU
{7F607362-0680-4751-B1DC-621219294AE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7F607362-0680-4751-B1DC-621219294AE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F607362-0680-4751-B1DC-621219294AE3}.Debug|Any CPU.Build.0 = Debug|Any CPU {7F607362-0680-4751-B1DC-621219294AE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F607362-0680-4751-B1DC-621219294AE3}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F607362-0680-4751-B1DC-621219294AE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F607362-0680-4751-B1DC-621219294AE3}.Release|Any CPU.Build.0 = Release|Any CPU {7F607362-0680-4751-B1DC-621219294AE3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -452,6 +452,25 @@ Content";
Assert.AreEqual(String.Empty, context[0].TagName, "The top-most context had the wrong tag type."); Assert.AreEqual(String.Empty, context[0].TagName, "The top-most context had the wrong tag type.");
} }
/// <summary>
/// If a key refers to a public field, its value should be substituted in the output.
/// </summary>
[TestMethod]
public void TestGenerate_KeyRefersToPublicField_SubstitutesValue()
{
FormatCompiler compiler = new FormatCompiler();
const string format = @"Hello, {{Field}}!!!";
Generator generator = compiler.Compile(format);
ClassWithPublicField instance = new ClassWithPublicField() { Field = "Bob" };
string result = generator.Render(instance);
Assert.AreEqual("Hello, Bob!!!", result, "The wrong text was generated.");
}
public class ClassWithPublicField
{
public string Field;
}
#endregion #endregion
#region Comment #region Comment

View File

@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("mustache-sharp.test")] [assembly: AssemblyTitle("mustache-sharp.test")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("Truncon")]
[assembly: AssemblyProduct("mustache-sharp.test")] [assembly: AssemblyProduct("mustache-sharp.test")]
[assembly: AssemblyCopyright("Copyright © 2013")] [assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
@ -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.2.2.0")] [assembly: AssemblyVersion("0.0.0.0")]
[assembly: AssemblyFileVersion("0.2.2.0")] [assembly: AssemblyFileVersion("0.0.0.0")]

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010"> <TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6"> <TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
<RunConfiguration id="2bc42439-1bb6-4112-9c20-eca1ffcae064" name="Local" storage="local.testsettings" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <RunConfiguration id="2bc42439-1bb6-4112-9c20-eca1ffcae064" name="Local" storage="local.testsettings" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</TestList> </TestList>
</TestLists> </TestLists>

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 // 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.2.2.0")] [assembly: AssemblyVersion("0.2.3.0")]
[assembly: AssemblyFileVersion("0.2.2.0")] [assembly: AssemblyFileVersion("0.2.3.0")]
[assembly: InternalsVisibleTo("mustache-sharp.test")] [assembly: InternalsVisibleTo("mustache-sharp.test")]

View File

@ -11,10 +11,10 @@ namespace Mustache
/// </summary> /// </summary>
internal sealed class PropertyDictionary : IDictionary<string, object> internal sealed class PropertyDictionary : IDictionary<string, object>
{ {
private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _cache = new Dictionary<Type, Dictionary<string, PropertyInfo>>(); private static readonly Dictionary<Type, Dictionary<string, Func<object, object>>> _cache = new Dictionary<Type, Dictionary<string, Func<object, object>>>();
private readonly object _instance; private readonly object _instance;
private readonly Dictionary<string, PropertyInfo> _typeCache; private readonly Dictionary<string, Func<object, object>> _typeCache;
/// <summary> /// <summary>
/// Initializes a new instance of a PropertyDictionary. /// Initializes a new instance of a PropertyDictionary.
@ -25,7 +25,7 @@ namespace Mustache
_instance = instance; _instance = instance;
if (instance == null) if (instance == null)
{ {
_typeCache = new Dictionary<string, PropertyInfo>(); _typeCache = new Dictionary<string, Func<object, object>>();
} }
else else
{ {
@ -33,21 +33,25 @@ namespace Mustache
} }
} }
private static Dictionary<string, PropertyInfo> getCacheType(object instance) private static Dictionary<string, Func<object, object>> getCacheType(object instance)
{ {
Type type = instance.GetType(); Type type = instance.GetType();
Dictionary<string, PropertyInfo> typeCache; Dictionary<string, Func<object, object>> typeCache;
if (!_cache.TryGetValue(type, out typeCache)) if (!_cache.TryGetValue(type, out typeCache))
{ {
typeCache = new Dictionary<string, PropertyInfo>(); typeCache = new Dictionary<string, Func<object, object>>();
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy; BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy;
foreach (PropertyInfo propertyInfo in type.GetProperties(flags)) foreach (PropertyInfo propertyInfo in type.GetProperties(flags))
{ {
if (!propertyInfo.IsSpecialName) if (!propertyInfo.IsSpecialName)
{ {
typeCache.Add(propertyInfo.Name, propertyInfo); typeCache.Add(propertyInfo.Name, i => propertyInfo.GetValue(i, null));
} }
} }
foreach (FieldInfo fieldInfo in type.GetFields(flags))
{
typeCache.Add(fieldInfo.Name, i => fieldInfo.GetValue(i));
}
_cache.Add(type, typeCache); _cache.Add(type, typeCache);
} }
return typeCache; return typeCache;
@ -100,13 +104,13 @@ namespace Mustache
/// <exception cref="System.ArgumentNullException">The name of the property was null.</exception> /// <exception cref="System.ArgumentNullException">The name of the property was null.</exception>
public bool TryGetValue(string key, out object value) public bool TryGetValue(string key, out object value)
{ {
PropertyInfo propertyInfo; Func<object, object> getter;
if (!_typeCache.TryGetValue(key, out propertyInfo)) if (!_typeCache.TryGetValue(key, out getter))
{ {
value = null; value = null;
return false; return false;
} }
value = getValue(propertyInfo); value = getter(_instance);
return true; return true;
} }
@ -117,11 +121,11 @@ namespace Mustache
{ {
get get
{ {
ICollection<PropertyInfo> propertyInfos = _typeCache.Values; ICollection<Func<object, object>> getters = _typeCache.Values;
List<object> values = new List<object>(); List<object> values = new List<object>();
foreach (PropertyInfo propertyInfo in propertyInfos) foreach (Func<object, object> getter in getters)
{ {
object value = getValue(propertyInfo); object value = getter(_instance);
values.Add(value); values.Add(value);
} }
return values.AsReadOnly(); return values.AsReadOnly();
@ -143,8 +147,8 @@ namespace Mustache
{ {
get get
{ {
PropertyInfo propertyInfo = _typeCache[key]; Func<object, object> getter = _typeCache[key];
return getValue(propertyInfo); return getter(_instance);
} }
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
set set
@ -167,23 +171,23 @@ namespace Mustache
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{ {
PropertyInfo propertyInfo; Func<object, object> getter;
if (!_typeCache.TryGetValue(item.Key, out propertyInfo)) if (!_typeCache.TryGetValue(item.Key, out getter))
{ {
return false; return false;
} }
object value = getValue(propertyInfo); object value = getter(_instance);
return Equals(item.Value, value); return Equals(item.Value, value);
} }
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{ {
List<KeyValuePair<string, object>> pairs = new List<KeyValuePair<string, object>>(); List<KeyValuePair<string, object>> pairs = new List<KeyValuePair<string, object>>();
ICollection<KeyValuePair<string, PropertyInfo>> collection = _typeCache; ICollection<KeyValuePair<string, Func<object, object>>> collection = _typeCache;
foreach (KeyValuePair<string, PropertyInfo> pair in collection) foreach (KeyValuePair<string, Func<object, object>> pair in collection)
{ {
PropertyInfo propertyInfo = pair.Value; Func<object, object> getter = pair.Value;
object value = getValue(propertyInfo); object value = getter(_instance);
pairs.Add(new KeyValuePair<string, object>(pair.Key, value)); pairs.Add(new KeyValuePair<string, object>(pair.Key, value));
} }
pairs.CopyTo(array, arrayIndex); pairs.CopyTo(array, arrayIndex);
@ -217,9 +221,10 @@ namespace Mustache
/// <returns></returns> /// <returns></returns>
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{ {
foreach (KeyValuePair<string, PropertyInfo> pair in _typeCache) foreach (KeyValuePair<string, Func<object, object>> pair in _typeCache)
{ {
object value = getValue(pair.Value); Func<object, object> getter = pair.Value;
object value = getter(_instance);
yield return new KeyValuePair<string, object>(pair.Key, value); yield return new KeyValuePair<string, object>(pair.Key, value);
} }
} }
@ -228,10 +233,5 @@ namespace Mustache
{ {
return GetEnumerator(); return GetEnumerator();
} }
private object getValue(PropertyInfo propertyInfo)
{
return propertyInfo.GetValue(_instance, null);
}
} }
} }