Sharpy was designed with extensibility in mind. This means that developers can write their own functions and modifiers and use them in views. To allow extensibility Sharpy uses
MEF.
Sharpy exposes 4 different contracts for developers to implement. Keep in mind that the built-in functions in Sharpy have been built within the exactly the same framework - the same functionality is available to internal and external functions. All exported functions and modifiers must be decorated with the MEF
Export attribute contained in the
System.ComponentModel.Composition namespace.
Once you have implemented a contract you will need to copy the assembly to a folder your website has access to and
configure the view engine to look at this folder. You can also look at this
example.
Contracts
- Block functions - contain both a start and end tag with content between the tags, eg. foreach
- Expression functions - similar to block functions, but the entire opening tag is treated as an expression, eg. if
- Inline functions - contain a single tag, eg. assign
- Variable modifiers - can be applied to any expression, eg. escape
The following code contracts (interfaces) reside in the
Sharpy.ViewEngine namespace.
public interface IBlockFunction
{
string Name { get; }
IList<string> TagsToIgnore();
string Evaluate(IDictionary<string, object> attributes, IFunctionEvaluator evaluator, string content);
}
public interface IExpressionFunction
{
string Name { get; }
IList<string> TagsToIgnore();
string Evaluate(string functionDetails, IFunctionEvaluator evaluator, string content);
}
public interface IInlineFunction
{
string Name { get; }
string Evaluate(IDictionary<string, object> attributes, IFunctionEvaluator evaluator);
}
public interface IVariableModifier
{
string Name { get; }
object Evaluate(object input, IEvaluator evaluator, params object[] parameters);
}
The same concept applies for implementing any of the contracts - any input values are evaluated before they are passed to the function/modifier. Every function/modifier also has access to an
Evaluator which exposes the functionality such as accessing the
ViewData or evaluating expressions.
public interface IEvaluator
{
IDictionary<string, object> ViewData { get; }
IDictionary<string, object> LocalData { get; }
object Model { get; }
string Evaluate(string content);
string EvaluateUrl(string contentPath);
string EvaluateTemplate(string templatePath);
string EvaluateTemplate(string templatePath, object model);
string EvaluateTemplate(string templatePath, object model, IDictionary<string, object> viewData);
string EvaluateTemplate(string templatePath, IDictionary<string, object> viewData);
object EvaluateExpression(string expression);
string Encode(string content);
}
Most of these members are reasonably straightforward - for example,
ViewData and
Model are available in regular ASP.NET views.
LocalData is similar to
ViewData, but contains variables declared in the views themselves. This is similar to declaring variables within a regular ASP.NET view - the scope of these variables are confined to the view itself. For example, the
assign function stores variables in
LocalData.
The
FunctionEvaluator available to functions is very similar.
public interface IFunctionEvaluator : IEvaluator
{
IDictionary<string, object> FunctionData { get; }
}
FunctionData is another dictionary, similar to
LocalData and
ViewData, but is only available to same
instance of a function. For example, the
cycle function stores variables in
FunctionData. Since
FunctionData is specific to a certain instance we can have multiple instances of the
cycle function within a loop and each will access their own dictionary. This is to deal with the following special case.
<ul>
{foreach from=$list item='int'}
<li bgcolor='{cycle values='#eeeeee,#d0d0d0'}'>{$int}</li>
{/foreach}
</ul>
<ul>
{foreach from=$list item='int'}
<li bgcolor='{cycle values='#eeeeee,#d0d0d0'}'>{$int}</li>
{/foreach}
</ul>