Skip to content

Commit

Permalink
#31 : Added configurable behaviour for bad model references from the …
Browse files Browse the repository at this point in the history
…document template.

Options are, the module handler either throws an exception, ignores the match it's processing, or removes the match. Default is exception.
  • Loading branch information
CPonty committed Dec 8, 2015
1 parent 0b75a68 commit dd36598
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 30 deletions.
3 changes: 2 additions & 1 deletion Examples/ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class Program
{
public static string root = @"..\..\";
public static bool debug = true;
public static HandleFailAction modelEntryFailAction = HandleFailAction.exception;

static void Main(string[] args)
{
Expand All @@ -40,7 +41,7 @@ static void Generate(string name, object model)

Console.WriteLine("Generating " + name);

var doc = Templ.Load(file).Build(model, debug).SaveAs(output);
var doc = Templ.Load(file).Build(model, debug, modelEntryFailAction).SaveAs(output);
if (debug) doc.Debugger.SaveAs(output);
}
}
Expand Down
51 changes: 36 additions & 15 deletions Templ.NET/ModelEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@

namespace TemplNET
{
public class ModelEntryException : Exception
{
public ModelEntryException(string message, Exception innerException) : base(message, innerException)
{
}
}

/// <summary>
/// List of ways in which an exception in a module handler can be handled
/// </summary>
/// Example: {txt:user.name} // user.name is not a property in the model
public enum HandleFailAction
{
exception, ignore, remove
};

/// <summary>
/// Represents a reference-by-reflection to a Entry (a field/property/member) within an object.
/// The entry's nested Path is defined with a string.
Expand Down Expand Up @@ -189,23 +205,28 @@ private static MemberValue GetPrimitive(object model, string path)
/// <param name="path"></param>
public static TemplModelEntry Get(object model, string path)
{
MemberValue propVal;
var primitive = GetPrimitive(model, path);
if (primitive == null)
{
propVal = MemberValue.FindPath(model, path.Trim());
try {
MemberValue propVal;
var primitive = GetPrimitive(model, path);
if (primitive == null)
{
propVal = MemberValue.FindPath(model, path.Trim());
}
else
{
propVal = primitive;
}
return new TemplModelEntry()
{
Model = model,
Path = path.Trim(),
Info = propVal.Info,
Value = propVal.Value
};
}
else
{
propVal = primitive;
catch (Exception ex) {
throw new ModelEntryException($"Failed to retrieve entry from the model at path '{path}'", ex);
}
return new TemplModelEntry()
{
Model = model,
Path = path.Trim(),
Info = propVal.Info,
Value = propVal.Value
};
}

/// <summary>
Expand Down
38 changes: 27 additions & 11 deletions Templ.NET/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ protected TemplModule AddPrefix(string prefix)
/// Applies all changes to the document.
/// <see cref="Templ.Build"/> automatically executes it for all <see cref="Templ.ActiveModules"/>.
/// </summary>
public abstract void Build(DocX doc, object model);
public abstract void Build(DocX doc, object model, HandleFailAction modelEntryFailAction);
}

/// <summary>
Expand Down Expand Up @@ -127,7 +127,6 @@ private bool RemoveExpired(T m)
/// Verifies the number of fields in the supplied Match's placeholder is within the min/max expected for this Module.
/// <para/> Throws exception if problems are found.
/// </summary>
/// <param name="m"></param>
private T CheckFieldCount(T m)
{
var l = m.Fields.Length;
Expand All @@ -141,10 +140,11 @@ private T CheckFieldCount(T m)
}
return m;
}

/// <summary>
/// Handle and/or remove a collection of Matches from the document
/// </summary>
public void BuildFromScope(DocX doc, object model, IEnumerable<T> scope)
public void BuildFromScope(DocX doc, object model, IEnumerable<T> scope, HandleFailAction modelEntryFailAction)
{
var watch = Stopwatch.StartNew();
// Mark module instance as "used" if any matches are being processed
Expand All @@ -154,35 +154,51 @@ public void BuildFromScope(DocX doc, object model, IEnumerable<T> scope)
// Note how we are constantly "committing" the changes using ToList().
// This ensures order is preserved (e.g. all "finds" happen before all "handler"s)
scope.Select( m => CheckFieldCount(m) ).ToList()
.Select( m => Handler(doc, model, m)).ToList()
.Select( m => TryHandler(doc, model, m, modelEntryFailAction)).ToList()
.Where( m =>!RemoveExpired(m)).ToList()
.Select( m => CustomHandler(doc, model, m)).ToList()
.ForEach(m => RemoveExpired(m));
Statistics.millis = watch.ElapsedMilliseconds;
}

private T TryHandler(DocX doc, object model, T m, HandleFailAction modelEntryFailAction)
{
try
{
return Handler(doc, model, m);
}
catch (ModelEntryException)
{
switch (modelEntryFailAction)
{
case HandleFailAction.exception:
throw;
case HandleFailAction.remove:
m.Expired = true;
break;
}
return m;
}
}

/// <summary>
/// Find and build all content from the document
/// </summary>
public override void Build(DocX doc, object model)
public override void Build(DocX doc, object model, HandleFailAction modelEntryFailAction)
{
BuildFromScope(doc, model, Regexes.SelectMany(rxp => FindAll(doc, rxp)));
BuildFromScope(doc, model, Regexes.SelectMany(rxp => FindAll(doc, rxp)), modelEntryFailAction);
}

/// <summary>
/// Module-specific Match handler.
/// Implementations should modify the Matched content, or mark expired to delete
/// </summary>
/// <param name="doc"></param>
/// <param name="model"></param>
/// <param name="m"></param>
public abstract T Handler(DocX doc, object model, T m);

/// <summary>
/// Module-specific finder.
/// Implementations should retrieve all regex-matching content from the document.
/// </summary>
/// <param name="doc"></param>
/// <param name="rxp"></param>
public abstract IEnumerable<T> FindAll(DocX doc, TemplRegex rxp);
}

Expand Down
2 changes: 1 addition & 1 deletion Templ.NET/Modules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void BuildFromScope(DocX doc, object model, IEnumerable<Paragraph> paragr
{
Path = path;
Paragraphs = paragraphs;
Build(doc, model);
Build(doc, model, HandleFailAction.exception);
Path = "";
paragraphs = new List<Paragraph>();
}
Expand Down
4 changes: 2 additions & 2 deletions Templ.NET/Templ.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,15 @@ public static Templ Load(string templatePath, bool useDefaultModules = true)
/// </summary>
/// <seealso cref="Debugger"/>
///
public Templ Build(object model, bool debug = TemplConst.Debug)
public Templ Build(object model, bool debug = TemplConst.Debug, HandleFailAction modelEntryFailAction = HandleFailAction.exception)
{
if (debug)
{
Debugger.AddState(Document, "Init");
}
ActiveModules.ForEach(module =>
{
module.Build(Document.Docx, model);
module.Build(Document.Docx, model, modelEntryFailAction);
if (debug && module.Used)
{
Debugger.AddState(Document, module.Name);
Expand Down

0 comments on commit dd36598

Please sign in to comment.