Design and implement a template engine–part 2

In the Design and implement a template engine – part 1 I mentioned about a better solution: Reuse Razor Engine. In the post, I go in detail of how to implement it. But, first, we need to know some concepts:

Concepts

  1. Template Engine: Use to render a template into string
  2. Template Model Builder: Build the Model object which is used to fill in the data in template
  3. Template Model Expander: Expand the Model object with more properties
  4. And Template: the template

Implementation

The implementation assume that you already know about dynamic object and Razor Syntax (which is used in MVC – Views)

public interface ITemplateModelExpander
{
    /// <summary>
    /// Expand a dynamic object with more properties. The input object is sharing between expanders. Each expander will expand the properties that most make sense to them
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    dynamic Expand(dynamic obj);
    /// <summary>
    /// True if want to tell the <see cref="ITemplateModelBuilder"/> that, use the result of Expand method as the input for the next expander.
    /// The implementation should be very careful when setting true. Most of the time, it should be false
    /// </summary>
    bool UseResultForNextExpander { get; }
}

ITemplateModelExpander is the one that will be implemented in many classes. As its purpose, we want to expand the dynamic object to build up a real Model for the template.

Next we will see how we define a Template Model Builder.

/// <summary>
/// Builder is for building the object model using the inner expanders
/// </summary>
public interface ITemplateModelBuilder
{
    /// <summary>
    /// return the dynamic object that can be used in template
    /// </summary>
    /// <returns></returns>
    object Build();
    /// <summary>
    /// Append expander
    /// </summary>
    /// <param name="expander"></param>
    void AppendExpander(ITemplateModelExpander expander);
    void AppendExpanders(IEnumerable<ITemplateModelExpander> expanders);
    /// <summary>
    /// Clear all expanders. It is useful when you want to empty the object model and start adding new expander.
    /// Mostly you will use it if there is a single <see cref="ITemplateModelBuilder"/> in the system (Singleton lifestyle)
    /// </summary>
    void ClearExpanders();
}

It does 1 main thing: Build; which builds the Model object (of course, dynamic object) using expanders. That’s why you see 3 more methods related to Expander.

Then I make a simple implementation for the Model Builder:

public class StandardTemplateModelBuilder : ITemplateModelBuilder
{
    private readonly List<ITemplateModelExpander> _expanders = new List<ITemplateModelExpander>();

    public object Build()
    {
        var model = new ExpandoObject();
        foreach (var expander in _expanders)
        {
            var result = expander.Expand(model);
            if (expander.UseResultForNextExpander)
            {
                model = result;
            }
        }
        return model;
    }

    public void AppendExpander(ITemplateModelExpander expander)
    {
        _expanders.Add(expander);
    }

    public void AppendExpanders(IEnumerable<ITemplateModelExpander> expanders)
    {
        _expanders.AddRange(expanders);
    }

    public void ClearExpanders()
    {
        _expanders.Clear();
    }
}

Pretty simple right Smile

Let’s talk about Template, I have this simple interface:

/// <summary>
/// Define a template entry.
/// </summary>
public interface ITemplate
{
    /// <summary>
    /// The template, which tell <see cref="ITemplateEngine"/> how to render output
    /// </summary>
    string Template { get; set; }
    /// <summary>
    /// The builder which will build the model to populate data for the template
    /// </summary>
    ITemplateModelBuilder ModelBuilder { get; }
}

Define it as interface gives me power to change the template type. Even though, I just have Razor template for now:

public class RazorTemplate : ITemplate
{
    public RazorTemplate(ITemplateModelBuilder modelBuilder)
    {
        if(modelBuilder == null)
            throw new ArgumentNullException("modelBuilder");
        ModelBuilder = modelBuilder;
    }
    public string Template { get; set; }
    public ITemplateModelBuilder ModelBuilder { get; private set; }
}

You might wander why do I not inject template property directly in the constructor? Because, I want to resolve the ITemplate instance using an IoC container, such as Windsor and wire up with correct ITemplateModelBuilder which is StandardTemplateModelBuilder in this case. (I will show you how to use it later)

The last part, the Template Engine:

/// <summary>
/// Engine to render output base on template
/// </summary>
public interface ITemplateEngine
{
    string Render(ITemplate template);
}

Very simple. It just render an ITemplate instance.

The implementation is based on Razor Engine

/// <summary>
/// Simple implementation using Razor engine
/// </summary>
public class RazorBasedTemplateEngine : ITemplateEngine
{
    public string Render(ITemplate template)
    {
        return Razor.Parse(template.Template, template.ModelBuilder.Build());
    }
}

It asks the ModelBuilder from Template to build up the dynamic model object which is used to populate the template.

Usage

Have a look at this Unit Test:

[Test]
public void Test_simple_render_engine()
{
    // Arrange
    var engine = new RazorBasedTemplateEngine();
    var modelBuilder = new StandardTemplateModelBuilder();
    var template = new RazorTemplate(modelBuilder)
    {
        Template = @"Hello @Model.Name"
    };
    modelBuilder.AppendExpander(new SampleByNameExpander("Thai Anh Duc"));
    // Act
    var result = engine.Render(template);
    // Assert
    Console.WriteLine(result);
    Assert.AreEqual("Hello Thai Anh Duc", result);
}

I setup Engine, ModelBuilder, and Razor Template. These objects can be wire up automatically by IoC, see below

public class TestIoCWireUp
{
    private readonly ITemplateEngine _engine;
    private readonly ITemplate _template;

    public TestIoCWireUp(ITemplateEngine engine, ITemplate template)
    {
        if (engine == null)
            throw new ArgumentNullException("engine");
        if (template == null)
            throw new ArgumentNullException("template");
        _engine = engine;
        _template = template;
    }

    public string RenderNameTemplate(string template)
    {
        _template.Template = template;
        _template.ModelBuilder.AppendExpander(new SampleByNameExpander("Thai Anh Duc"));
        return _engine.Render(_template);
    }
}

And very simple implementation for SampleByNameExpander:

public class SampleByNameExpander : ITemplateModelExpander
{
    private readonly string _name;

    public SampleByNameExpander(string name)
    {
        _name = name;
    }
    public dynamic Expand(dynamic obj)
    {
        obj.Name = _name;
        return obj;
    }

    public bool UseResultForNextExpander { get {return false;} }
}

If you want to expand the Model with more properties that make sense to your application, you can add as many as possible. Just make sure you add the expander for the ModelBuilder of the Template you want. Or you can create a big giant Expander which will expose lot of properties at once. However, I will not encourage that. It is much more maintainable with simple small classes.

And, that’s it. Hope it help Smile

Can we code when age?

I am a developer living in Vietnam. I got many friends from the university, from companies I worked. When I was under 30; just a number to say that when I was young developer; my friends usually say:

You cannot code when you are age; assuming over 30 as they said. You have to do project manager, learn about economic. You become a project manager and let young developer do the coding task.

My first reaction: WHY?

Pretty much I got the same answer from them:

  • You are old, so you cannot think fast. Technology changes frequently and you cannot keep up with
  • Young developer is better than you
  • Being a project manager will give you more income with less work

At that time, I still did not understand why they thought that. They were a good programmer or at least did good job. And they thought of giving it up, and dreamed of another title, another job – believe me, some of them took economic class or MBA course (local MBA course).

Time flies. And now I am over 30 (31 precisely). And I am a developer. Actually, I am a technical manager, project manager. I have a team of more than 10 people with both developers and testers. But the main point is:

I do the coding. I still code and I love code. And I make a living with my coding job

Then I started to find out the reason: Why do they think that? What are the root causes?

Due to the expansion of the team, and of my new startup company, I made interview with some candidates. They are good programmers and might take many roles in their company. I asked them:

What books have you read since university? Which blog post do you follow? Do you know any famous programmer names?

To my surprise: NO. They have not read any technical books or books that can help them in coding life. They do not know Uncle Bob, Ayende, …

The answer is obvious:

They have not learned anything since university. They think they are good. They do the normal every day task and get bored. Their mind are slowing down.

In term of science, there is no evident for being stupid when you are age. Except you are over 70 or the like. Your brain needs food. Knowledge is the food. You do not give brain enough food.

Young developer and developer in general, I just want to tell you:

Foster your brain with knowledge. Learning everyday. Read more books. Do more code

Happy coding Smile

Design and implement a template engine – part 1

Recently, I got a task which is typical in many systems: Template with merge fields. The common usage is for email.

Given that an employee is created, the system will notify the manager by email with template:

Dear Manager,

New employee {Name} has been created on {DateTime.Now}.

From the above statement, we have template with 2 merged fields: Employee Name and Created Date. There are some solutions. But I name the old way of doing it here for reference:

Replace merge fields using string.Replace or Regex.

You can imagine it and how to do it. Or you event did it some times.

A better approach: Take advantages of Razor Engine.

The template will be in razor syntax

Dear Manager,

New employee @Model.EmployeeName has been created on @DateTime.Now.ToString(“s”).

In the next post, I will discuss about implementation.

Things i should pay attention and improve immediately

There are so many tittle but important things that I have not paid any attention to for 30 years. For the past couple of months, I started reading more books about self-improvement, and due to the fact that I am father of my daughter, and I want to be a good father, a good husband. I started to look around for improvement.

In this post, I will just list out all the things I want to improve. And for each section, I plan to write many posts about it.

Personal lifestyle

  1. Keep on gym exercise, focus a lot on running
  2. Improve sitting posture. I am a developer but I created a very bad posture which hurts me a lot in long term
  3. Read more book

Career

  1. Expand knowledge to other area such as: Node.js, ReactiveUI, ReactiveX (Rx). Can be anything, will design later
  2. Focus time for business, work smart and hard with 180-200 hours/month

Family

It is the most important thing. I place it last since I do not want to change it or to emphasize it. It is a fact. I always want to create a fun – healthy – wealthy family. All my goals, my plan, my to-do are for it.

Get started – again

It has been a while I have not written anything. And I almost forgot that I do have a blog. Fortunately, from this August, I set some big goals for my life, targeting in next 2 years. As I learn from “Getting things done”, I better write them down and get started. Goals are not goals without writing them down.

Here they are:

  1. Work for 180-200 hours per month
  2. Running 5 KM within 30 minutes
  3. Spend time with my family – this is always a priority for me
  4. Improve English – just by writing blog is a good way and a good start
  5. Quit smoking completely (I have done this at the time of writing, I have not smoked for 3 weeks)

I love drinking and I love hanging around with friends. To archive those goals, things will have a slightly change. I need time and power for my week days. Therefore, here are rules:

  1. No drinking in the week days, except: wedding party
  2. Drink only on Friday, Saturday nights
  3. Avoid drink on Sunday (can drink but not much). The purpose is to have energy for next week.