By default, .NET objects accessed through a ScriptObject are automatically exposed with lowercase and _ names. It means that a property like MyMethodIsNice will be exposed as my_method_is_nice. This is the default convention, originally to match the behavior of liquid templates.
A renamer is simply a delegate that takes an input MemberInfo and return a new member name:
namespace Scriban.Runtime
{
public delegate string MemberRenamerDelegate(MemberInfo member);
}
The StandardMemberRenamer is used to convert string camel/pascal case strings to "ruby" like strings.
If you want to import a .NET object without changing the cases, you can use the simple nop member renamer member => member.Name.
Note that renaming can be changed at two levels:
When importing a .NET object into a ScriptObject by passing a renamer delegate, before passing an object to a TemplateContext:
var scriptObject1 = new ScriptObject();
// Here the renamer will just return a same member name as the original
// hence importing .NET member name as-is
scriptObject1.Import(new MyObject(), renamer: member => member.Name);
var context = new TemplateContext();
context.PushGlobal(scriptObject1);
var template = Template.Parse("This is Hello: `{{Hello}}`");
var result = template.Render(context);
// Prints This is MyFunctions.Hello: `hello from method!`
Console.WriteLine(result);
By setting the default member renamer on the TemplateContext
// Setup a default renamer at the `TemplateContext` level
var context = new TemplateContext {MemberRenamer = member => member.Name};
It is important to setup this on the TemplateContext for any .NET objects that might be accessed indirectly through another ScriptObject so that when a .NET object is exposed, it is exposed with the correct naming convention.
The method Template.Render(object, renamer) takes also a member renamer, imports the object model with the renamer and setup correctly the renamer on the underlying TemplateContext.
So you can rewrite the previous example with the shorter version:
var template = Template.Parse("This is Hello: `{{Hello}}`");
template.Render(new MyObject(), member => member.Name);
Similar to the member renamer, by default, .NET objects accessed through a ScriptObject are automatically exposing all public instance fields and properties of .NET objects.
A filter is simply a delegate that takes an input MemberInfo and return a boolean to indicate whether to expose the member (true) or discard the member (false)
namespace Scriban.Runtime
{
/// <summary>
/// Allows to filter a member while importing a .NET object into a ScriptObject
/// or while exposing a .NET instance through a ScriptObject,
/// by returning <c>true</c> to keep the member; or false to discard it.
/// </summary>
/// <param name="member">A member info</param>
/// <returns><c>true</c> to keep the member; otherwise <c>false</c> to remove the member</returns>
public delegate bool MemberFilterDelegate(MemberInfo member);
}
You can use a MemberFilter when importing a an instance:
var scriptObject1 = new ScriptObject();
// Imports only properties that contains the word "Yo"
scriptObject1.Import(new MyObject(), filter: member => member is PropertyInfo && member.Name.Contains("Yo"));
By setting the default member filter on the TemplateContext, so that .NET objects automatically exposed via a ScriptObject will follow the global filtering rules defined on the context:
// Setup a default filter at the `TemplateContext` level
var context = new TemplateContext {MemberFilter = member => member is PropertyInfo && member.Name.Contains("Yo") };
As for the member renamer, it is important to setup this on the TemplateContext for any .NET objects that might be accessed indirectly through another ScriptObject so that when a .NET object is exposed, it is exposed with the same filtering convention