Proste template’y z DotLiquid

Po raz kolejny chciałbym podzielić się znaleziskiem z Nuget. Zdarzyło się, że potrzebowałem dostarczyć temlate’y wiadomości tak aby możliwe było ich przeredagowanie przez osoby bardziej twórcze. W sumie udało się odnaleźć parę rozwiązań, które mogłyby spełnić to wymaganie. Między innymi

Te i parę innych znalazłem na liście Top 20 NuGet packages for Templating

Po wstępnym rozpoznaniu tematu zdecydowałem się na DotLiquid. Jest to zportowana .Net’towa wersja popularnego (podobno) Ruby Liquid temlating language.
Zdecydowałem się głównie z braku czasu na konkretną weryfikację ale wyboru nie żałuję i na pewno rozwiązanie to będzie moim faworytem w przyszłości. Główne zalety, argumenty które do mnie przemówiły to:

  • Dostępność na nuget
  • Przyzwoita dokumentacja
  • Czytelność
  • Bezpieczeństwo – możliwość zachowania kontroli nad mapowaniem parametrów temlate’u.

Aby tym razem nie powtarzać przykładów z dokumentacji założyłem sobie takiego example case’a:
Chcemy udostępnić użytkownikowi możliwość otrzymywania mailingu z wybranych kategorii. Mamy więc klasy.

    [LiquidType("Name","Surname", "MailingCategories", "Weight", "Height", "Birthdate")]
    public class Mailing
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
        public float Height { get; set; }
        public float Weight { get; set; }
        public DateTime Birthdate { get; set; }
        public string Email { get; set; }
        public string Secret { get; set; }
        public Guid ConfirmationGuid { get; set; }
        public Guid RemoveGuid { get; set; }
        public List<MailingCategory> MailingCategories { get; set; }
    }
    [LiquidType("Name")]
    public class MailingCategory
    {
        public int Id { get; set; }
        public String Name { get; set; }
    }

Chcemy aby po wpisaniu się do subskrypcji użytkownik potwierdził swój wybór. Tworzymy więc temlate’a maila i metodę dostarczającą treść maila.

Witaj, 

Aby otrzymywać mailing ze strony spamsender.com potwierdź swoje zgłoszenie:

Twoje dane:
Imię:  {{mailing.Name | Upcase }}
Nazwisko: {{mailing.Surname | Upcase }}
Wzrost/Waga: {{mailing.Weight}} / {{mailing.Height}}
Data urodzenia: {{ mailing.Birthdate | date:"MM/dd/yyyy" }}

Dopisałeś się do grup:
{% assign categories = mailing.MailingCategories | Sort: 'Name' %}{% for category in categories -%}
{{category.Name}}
{% endfor -%}

Aby potwierdzić zgłoszenie kliknij poniższy link:
{{ConfirmationLink}}

Aby anulować swoje uczestnictwo kliknij poniższy link:
{{RemoveLink}}

{{Secret}}

Powodzenia,
spamsender.com
    public class MailingMailContentProvider : IMailContentProvider<Mailing>
    {
        private readonly String templateText;
        private readonly String linkRootPath;

        static MailingMailContentProvider()
        {
            Template.NamingConvention = new CSharpNamingConvention();
            Liquid.UseRubyDateFormat = false;
        }

        public MailingMailContentProvider(string templateText, string linkRootPath)
        {
            this.templateText = templateText;
            this.linkRootPath = linkRootPath;
        }

        public string GetMailContent(Mailing item)
        {
            Template template = Template.Parse(templateText);
            Hash hash = Hash.FromAnonymousObject(new
            {
                mailing = item,
                ConfirmationLink = $"{linkRootPath}/confirm/{item.ConfirmationGuid}",
                RemoveLink = $"{linkRootPath}/remove/{item.RemoveGuid}"
            });
            String result = template.Render(hash);
            return result;
        }
    }

Samego działania możemy się oczywiście spodziewać, ale dla pewności zweryfikujmy to prostym testem.

    [TestFixture]
    public class MailingMailContentProviderTests
    {
        private IMailContentProvider<Mailing> provider;
        private readonly string linkPath = "www.moromind.pl";
        [SetUp]
        public void Init()
        {
            string temlateText = File.ReadAllText(@"Templates\MailingConfirmationTemlate.txt");
            provider = new MailingMailContentProvider(temlateText, linkPath);
        }

        [Test]
        public void TestGetEmailContentTestShouldReturnContentWithPropertiesIncludes()
        {
            Mailing mailing = new Mailing()
            {
                Id = 1,
                Name = "Grzegorz",
                Surname = "Morawski",
                Email = "testemail@domain.com",
                Birthdate = new DateTime(1985,1,31),
                ConfirmationGuid = Guid.NewGuid(),
                RemoveGuid = Guid.NewGuid(),
                Height = 175F,
                Weight = 78F,
                Secret = "my secret text"
            };
            mailing.MailingCategories = new List<MailingCategory>()
            {
                new MailingCategory() {Id=1, Name = "Humor"},
                new MailingCategory() {Id=2, Name = "Programming"},
                new MailingCategory() {Id=2, Name = "Extreme sports"}
            };
            string confirmationLink = $"{linkPath}/confirm/{mailing.ConfirmationGuid}";
            string removeLink = $"{linkPath}/remove/{mailing.RemoveGuid}";

            string mailContent = provider.GetMailContent(mailing);
            StringAssert.Contains(mailing.Name.ToUpper(), mailContent);
            StringAssert.Contains(mailing.Surname.ToUpper(), mailContent);

            StringAssert.Contains(mailing.MailingCategories[0].Name, mailContent);
            StringAssert.Contains(mailing.MailingCategories[1].Name, mailContent);
            StringAssert.Contains(mailing.MailingCategories[2].Name, mailContent);

            StringAssert.Contains(confirmationLink, mailContent);
            StringAssert.Contains(removeLink, mailContent);

            StringAssert.DoesNotContain(mailing.Secret, mailContent);

            Console.Out.Write(mailContent);
        }
    }

Output testu wygląda następująco:

Witaj, 

Aby otrzymywać mailing ze strony spamsender.com potwierdź swoje zgłoszenie:

Twoje dane:
Imię:  GRZEGORZ
Nazwisko: MORAWSKI
Wzrost/Waga: 78 / 175
Data urodzenia: 1985-01-31 00:00:00

Dopisałeś się do grup:
Extreme sports
Humor
Programming

Aby potwierdzić zgłoszenie kliknij poniższy link:
www.moromind.pl/confirm/1d12c918-2c25-44c2-bad7-4d028888fc8e

Aby anulować swoje uczestnictwo kliknij poniższy link:
www.moromind.pl/remove/4c4bf2a9-39cd-4ef5-8ae5-cf4e9b804e9d

Powodzenia,
spamsender.com

Na tym prostym przykładzie widzimy, że możemy mieć kontrolę nad:

  • Dostępem do properties
  • Listami
  • Tagami
  • Formatem danych

Oraz paroma innymi rzeczami. Oczywiście to nie wszystko z podstawowych funkcjonalności, a  jako ambitni programiści możemy całość rozszerzyć o swoje filtry, tagi, block tagi, ale o tym innym razem (lub w dokumentacji).

Napisano w .net Tagi: , , ,