Skip to content

Commit

Permalink
Fixing benchmarks and introducing MemberAccessStrategy (sebastienros#143
Browse files Browse the repository at this point in the history
)
  • Loading branch information
sebastienros authored Aug 19, 2019
1 parent 2408a59 commit b7bd73b
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 9 deletions.
18 changes: 16 additions & 2 deletions Fluid.Benchmarks/DotLiquidBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using BenchmarkDotNet.Attributes;
using DotLiquid;
using System.Linq;

namespace Fluid.Benchmarks
{
Expand All @@ -14,7 +15,20 @@ public DotLiquidBenchmarks()
{
_dotLiquidTemplate = Template.Parse(TextTemplate);
_dotLiquidTemplate.MakeThreadSafe();
_products = Hash.FromAnonymousObject(new { products = Products });
_products = MakeProducts();
}

private Hash MakeProducts()
{
return Hash.FromAnonymousObject(new
{
products = Products.Select(product => new Hash()
{
["name"] = product.Name,
["price"] = product.Price,
["description"] = product.Description
}).ToList()
});
}

[Benchmark]
Expand All @@ -34,7 +48,7 @@ public override string Render()
public override string ParseAndRender()
{
var template = Template.Parse(TextTemplate);
var products = Hash.FromAnonymousObject(new { products = Products });
var products = MakeProducts();
return template.Render(products);
}
}
Expand Down
2 changes: 2 additions & 0 deletions Fluid.Benchmarks/FluidBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class FluidBenchmarks : BaseBenchmarks

public FluidBenchmarks()
{
TemplateContext.GlobalMemberAccessStrategy.MemberNameStrategy = MemberNameStrategies.CamelCase;
TemplateContext.GlobalMemberAccessStrategy.Register<Product>();
FluidTemplate.TryParse(TextTemplate, out _fluidTemplate, out var errors);
_context = new TemplateContext().SetValue("products", Products);
}
Expand Down
2 changes: 2 additions & 0 deletions Fluid/ConcurrentMemberAccessStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public ConcurrentMemberAccessStrategy()
_map = new ConcurrentDictionary<Type, ConcurrentDictionary<string, IMemberAccessor>>();
}

public MemberNameStrategy MemberNameStrategy { get; set; } = MemberNameStrategies.Default;

public IMemberAccessor GetAccessor(Type type, string name)
{
IMemberAccessor accessor = null;
Expand Down
2 changes: 2 additions & 0 deletions Fluid/IMemberAccessStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public interface IMemberAccessStrategy
IMemberAccessor GetAccessor(Type type, string name);

void Register(Type type, string name, IMemberAccessor getter);

MemberNameStrategy MemberNameStrategy { get; set; }
}
}
3 changes: 3 additions & 0 deletions Fluid/MemberAccessStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class MemberAccessStrategy : IMemberAccessStrategy
private Dictionary<Type, Dictionary<string, IMemberAccessor>> _map;
private readonly IMemberAccessStrategy _parent;

public MemberNameStrategy MemberNameStrategy { get; set; } = MemberNameStrategies.Default;

public MemberAccessStrategy()
{
_map = new Dictionary<Type, Dictionary<string, IMemberAccessor>>();
Expand All @@ -17,6 +19,7 @@ public MemberAccessStrategy()
public MemberAccessStrategy(IMemberAccessStrategy parent) : this()
{
_parent = parent;
MemberNameStrategy = _parent.MemberNameStrategy;
}

public IMemberAccessor GetAccessor(Type type, string name)
Expand Down
14 changes: 7 additions & 7 deletions Fluid/MemberAccessStrategyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class MemberAccessStrategyExtensions
// A cache of accessors so we don't rebuild them once they are added to global or contextual access strategies
internal static ConcurrentDictionary<Type, Dictionary<string, IMemberAccessor>> _typeMembers = new ConcurrentDictionary<Type, Dictionary<string, IMemberAccessor>>();

internal static Dictionary<string, IMemberAccessor> GetTypeMembers(Type type)
internal static Dictionary<string, IMemberAccessor> GetTypeMembers(Type type, MemberNameStrategy memberNameStrategy)
{
return _typeMembers.GetOrAdd(type, t =>
{
Expand All @@ -28,21 +28,21 @@ internal static Dictionary<string, IMemberAccessor> GetTypeMembers(Type type)
continue;
}

list[propertyInfo.Name] = new PropertyInfoAccessor(propertyInfo);
list[memberNameStrategy(propertyInfo)] = new PropertyInfoAccessor(propertyInfo);
}

foreach (var fieldInfo in t.GetTypeInfo().GetFields(BindingFlags.Public | BindingFlags.Instance))
{
list[fieldInfo.Name] = new DelegateAccessor((o, n) => fieldInfo.GetValue(o));
list[memberNameStrategy(fieldInfo)] = new DelegateAccessor((o, n) => fieldInfo.GetValue(o));
}

return list;
});
}

internal static IMemberAccessor GetNamedAccessor(Type type, string name)
internal static IMemberAccessor GetNamedAccessor(Type type, string name, MemberNameStrategy strategy)
{
var typeMembers = GetTypeMembers(type);
var typeMembers = GetTypeMembers(type, strategy);

if (typeMembers.TryGetValue(name, out var result))
{
Expand All @@ -68,7 +68,7 @@ public static void Register<T>(this IMemberAccessStrategy strategy)
/// <param name="type">The type to register.</param>
public static void Register(this IMemberAccessStrategy strategy, Type type)
{
foreach (var entry in GetTypeMembers(type))
foreach (var entry in GetTypeMembers(type, strategy.MemberNameStrategy))
{
strategy.Register(type, entry.Key, entry.Value);
}
Expand Down Expand Up @@ -103,7 +103,7 @@ public static void Register(this IMemberAccessStrategy strategy, Type type, para
{
foreach (var name in names)
{
strategy.Register(type, name, GetNamedAccessor(type, name));
strategy.Register(type, name, GetNamedAccessor(type, name, strategy.MemberNameStrategy));
}
}

Expand Down
54 changes: 54 additions & 0 deletions Fluid/MemberNameStrategies.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Reflection;
using System.Text;

namespace Fluid
{
public class MemberNameStrategies
{
public static readonly MemberNameStrategy Default = RenameDefault;
public static readonly MemberNameStrategy CamelCase = RenameCamelCase;
public static readonly MemberNameStrategy SnakeCase = RenameCamelCase;

private static string RenameDefault(MemberInfo member) => member.Name;

public static string RenameCamelCase(MemberInfo member)
{
var name = member.Name;
var firstChar = name[0];

if (firstChar == char.ToLowerInvariant(firstChar))
{
return name;
}

return char.ToLowerInvariant(firstChar) + name.Substring(1);
}

public static string RenameSnake(MemberInfo member)
{
var builder = new StringBuilder();
var name = member.Name;
var previousUpper = false;

for (var i = 0; i < name.Length; i++)
{
var c = name[i];
if (char.IsUpper(c))
{
if (i > 0 && !previousUpper)
{
builder.Append("_");
}
builder.Append(char.ToLowerInvariant(c));
previousUpper = true;
}
else
{
builder.Append(c);
previousUpper = false;
}
}
return builder.ToString();
}
}
}
6 changes: 6 additions & 0 deletions Fluid/MemberNameStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using System.Reflection;

namespace Fluid
{
public delegate string MemberNameStrategy(MemberInfo member);
}
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,19 @@ a member of the base class is accessed.

<br>

### Object members casing

By default, the properties of a registered object are case sensitive and registered as they are in their source code. For instance,
the property `FirstName` would be access using the `{{ p.FirstName }}` tag.

However it can be necessary to register these properties with different cases, like __Camel case__ (`firstName`), or __Snake case__ (`first_name`).

The following example configures the templates to use Camel casing.

```csharp
TemplateContext.GlobalMemberAccessStrategy.MemberNameStrategy = MemberNameStrategies.CamelCase;
```

## Execution limits

### Limiting templates recursion
Expand Down

0 comments on commit b7bd73b

Please sign in to comment.