středa 17. prosince 2014

Poznámky z code review 3

Další nedostatkem, se kterým se setkávám, je buď neznalost používaných technologiích a nebo nevyužívání jejich možností. To lze určitě pochopit, pokud kodér nepracuje s danou technologii určitý čas a nebo je jasné, že není expertem. Code review by ho  v takovém případě mělo upozornit na jiné možnosti a tedy tak vlastně přispívá k jeho odbornému růstu - a nebo může sloužit k vyvolání diskuze, který přístup je lepší a proč.

Například mi byl předán takovýto Razor kód:

@model OurModel

@{
   var mainClass = "main";
   var hasProperty = SomePropertyProvider.HasProperty("Name");

   if (Model.IsReadOnly)
   {
      mainClass += " readonly";
   }

   if(Model.Values.Any(v=> IsWrong(v))
   {
      mainClass += " error";
   }

   var alertId = Guid.NewGuid().ToString();
}
   
<div class="@mainClass">
    <table class="Header">
        <tr>
            <td class="modelType">
.....

Je jasné, že se kodér snažil sestavit  jména stylu - a dopustil se chyb - například v bloku kódu střídá několik věcí - jednak sestavuje jména stylů, mezitím ale provádí ale volání s touto akcí nijak nespjatých. Správně by měl blok vypadat asi takto:

@model OurModel

@{
   var alertId = Guid.NewGuid().ToString();
   var hasProperty = SomePropertyProvider.HasProperty("Name");

   var mainClass = "main";

   if (Model.IsReadOnly)
   {
      mainClass += " readonly";
   }

   if(Model.Values.Any(v=> IsWrong(v))
   {
      mainClass += " error";
   }
}
   
<div class="@mainClass">
    <table class="Header">
        <tr>
            <td class="modelType">
.....

To už vypadá lépe, ale stále to není ono. Kombinování řetězců výše uvedeným způsobem není příliš štastné a i když uvedený příklad by asi nevedl k velké paměťové zátěži, je lepší uvedené "sčítání" řetězců nepoužívat. Jinou možnost nabízí MVC helper:

@model OurModel

@helper GetMainClasses()
{
   @:message

   if(Model.IsReadOnly())
   {
      @:readonly
   }

   if(Model.Values.Any(v=> IsWrong(v))
   {
      @:error
   }
}
   
<div class="@GetMainClasses()">
    <table class="Header">
        <tr>
            <td class="modelType">
.....

a nebo při rozepsání na více helperů:

@model OurModel

@functions
{
    string IsReadonly()
    {
        if (Model.IsReadOnly)
            return "readonly";
        
        return string.Empty;
    }
    
    string IsInError()
    {
        if(Model.Values.Any(v=> IsWrong(v))
            return "error";

        return string.Empty;       
    }
}
   
<div class="message @IsReadonly() @IsInError()">
    <table class="Header">
        <tr>
            <td class="modelType">
.....


a nebo alternativně:

@model OurModel

@helper IsInError()
{
    @(Model.Values.Any(v=> IsWrong(v) ? "error" : string.Empty)
}

@helper IsReadonly()
{
    @(Model.IsReadOnly() ? "readonly" : string.Empty)
}
   
<div class="message @IsReadonly() @IsInError()">
    <table class="Header">
        <tr>
            <td class="modelType">
.....

popřípadě in-line kódem:

@model OurModel

<div class="message @(Model.IsReadOnly() ? "" : string.Empty) @(Model.Values.Any(v=> IsWrong(v) ? "" : string.Empty)">
    <table class="Header">
        <tr>
            <td class="modelType">
.....

a nebo pomocí funkce:

@model OurModel

@functions
{
   string AddClass(bool add, string name)
   {
      return add ? name : null;
   }
}

<div class="message @(AddClass(Model.IsReadOnly(), "readonly")) @(AddClass(Model.Values.Any(v=> IsWrong(v), "error"))">
    <table class="Header">
        <tr>
            <td class="modelType">
.....

Chápu, že na daný příklad a řešení mohou být různé názory, kód nebyl vysloveně špatný a nefunkční, ale jak jsem již napsal, lze code review pojmout i jako možnost vzdělávání se či prostor k diskuzi. Helpery z uvedených příkladů lze přesunout na konec stránky a mít tak  na začátku téměř čisté HTML. Čitelnost ukázek v VS je lepší, nějak mám problém vkládat ukázky, která mají odpovídající barevné zvýraznění.

pondělí 15. prosince 2014

Poznámky z code review 2

Při programování, ale vlastně asi při každé činnosti, je dobré se řídit pár zásadami. Jendou z nich je alespoň v případě programování něco, co se anglicky označuje jako DRY. Ne, neznamená to, že je sucho a je nutno zajít do hospody na jedno, ale jsou to první písmena Don't Repeat Yourself.

Je mnoho způsobů jak tento přístup využít a jedním z nich je i nepsat zbytečný kód. Při code review často ovšem narazím na to, že vývojář tento princip nedodrží a v kódu se objeví něco podobného následujícímu zjednodušenému příkladu: 

public bool CanCreateNew()
{
    if (this.allowedActions == null)
        return false;

    return this.allowedActions.Any(x => x.ActionCode == ActionCode.CreateNew);
}

public bool CanUpdate()
{
    if (this.allowedActions == null)
        return false;

    return this.allowedActions.Any(x => x.ActionCode == ActionCode.Update);
}

public bool CanCancel()
{
    if (this.allowedActions == null)
        return false;

    return this.allowedActions.Any(x => x.ActionCode == ActionCode.Cancel);
}

Když se podíváte na těla funkcí, tak se neustále opakuje ten samý postup a všechny metody lze vyjádřit pomocí jedné:

public bool CanDoAction(ActionCode actionCode)
{
    if (this.allowedActions == null)
        return false;

    return this.allowedActions.Any(x => x.ActionCode == actionCode);
}

Uvedenému přístupu je vhodné se vyvarovat - výjimkou snad mohou být pouze případy, kdy jste placeni od řádku kódu :-). Je dobré si uvědomit, že čím méně kódu napíšete, tím méně v něm může být chyb a kód se bude snadněji udržovat.

sobota 13. prosince 2014

Poznámky z code review 1

Občas dělám i code review hotového kódu a některé věci se neustále opakují či se vyskytují v kódu poměrně často a tak mne napadlo, že by nebylo špatné na ně upozornit a nabídnout k zamyšlení či diskuzi. První příspěvek bude věnován kódu, který provádí rozhodnutí dle výčtové hodnoty, tedy enumu.

Například vznikne takovýto výčtový typ:

public enum Option
{
    One = 1,
    Two = 2
}

Výše uvedené hodnoty pak často vedou k programátora k rozhodování se pomocí následujícího kódu, kdy předpokládá, že buď má proměnná jednu určitou hodnotu a pokud ne, tak musí mít druhou.

if (userOption == Option.One)
    Console.WriteLine("One");
else
    Console.WriteLine("Two");

Což ovšem platí jen do okamžiku, než někdo přidá další hodnoty, například doplní definici enum takto:

public enum Option
{
    Unknown = 0,
    One = 1,
    Two = 2,
    Three = 3
}

Podobné chyby se ale my programátoři často dopouštíme, i pokud má výčtový typ více hodnot než dvě a my se z nějakého důvodu domníváme, že stejně může nabývat jen dvou námi vybraných hodnot. Takto napsaný kód není příliš odolný proti změnám ve výčtovém typu a ve výsledku se pak program často chová "podivně".

Jak to tedy napsat správně? Podle mne takto:

switch (userOption)
{
    case Option.One: Console.WriteLine("One"); break;
    case Option.Two: Console.WriteLine("Two"); break;
    default: throw new NotImplementedException();
}

Pokud se objeví hodnota, s kterou kód nepočítal, vyhodí se výjimka. A nebo se může provést úprava hodnoty  a zalogování chyby, aby šlo později daný kód upravit, tedy například napsat kód takto:

switch (userOption)
{
    case Option.One: Console.WriteLine("One"); break;
    case Option.Two: Console.WriteLine("Two"); break;

    default: 
        Console.WriteLine("Not implemented value {0}, processing as {1}"
                             userOption, 
                             Option.One); 
        goto case Option.One;
}

Upozornění: kód je napsán jen pro účely tohoto článku, má jen ilustrovat problém. K podobným chybám může dojít i při nepoužití výčtu, jen se to na něm dobře ilustruje.

středa 10. prosince 2014

Feraty u jezera Garda 3 - Cima Rocca

Výprava na horu Rocca byla  krásným celodenním výletem - v této oblasti jsou hned čtyři feraty:
  • F.Susati,
  • M.Foletti
  • Sent.dei Camminamenti
  • Sent.delle Lasta

úterý 9. prosince 2014

Feraty u jezera Garda 2 - jezero Idro a Sasse

Další ferátkou byla cesta kolem horského jezera Idro. Má jméno Sasse a více informací lze zde. To je necelou hodinku jízdy od severního konce Gardského jezera  a leží i výše - a je tam tedy i o poznání chladněji. A zatímco u Gardy to koncem října ještě žije, tady sezona už skončila. Velké parkoviště bylo zcela  prázdné a na celé feratě jsme potkali jen čtyři další lidi. A panovalo zde ticho.

neděle 7. prosince 2014

Vybaveni na cesty autem - chlazení

Převoz potravin autem a v chladu byla vždy výzva. Na kratší vzdálenosti stačí polystyrénová krabice, zmražené potraviny a chladící vložky - tenhle systém používáme už roky a je to spolehlivý způsob přepravy většího množství potravin na kratší vzdálenosti. Ale pokud se jede někam na více dnů a není možné využívat lednice, nelze se spolehnout na  box, respektive ten není moc použitelný.

pátek 5. prosince 2014

Razor a email

V přechozím příspěvku jsem ukázal, jak využít Razor i mimo web aplikace. Nyní popíši, jak tohoto přístupu využít v případě, že chceme v aplikaci vytvořit email a ten poslat uživateli. Email bude obsahovat i obrázky a přílohu.

Vytvoření šablony pro email

Šablony jsou vlastně dvě. V první je vlastní obsah mailu a tato šablona využívá jinou jako svoji obálku - to je vhodné pro případy, kdy bude potřeba více emailů a všechny by měly mít například jednotný vzhled.


email.cshtml
@* Generator: Template *@

@functions{
    public string Recipient { get; set; }

    public string Sender { get; set; }

    public string Content { get; set; }
}

@{ Layout = new emailLayout();}

<img alt="Logo" src="cid:logo" />

<table width="550" class="headerText">
    <tr><td>Hello Mr.@Recipient</td></tr>
    <tr><td>This is my message for you: @Content</td></tr>
    <tr><td>Regards, @Sender</td></tr>
</table>

emailLayout.cshtml
@* Generator: Template *@

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
    <style type="text/css">
        body {
            font-family: "Lucida Grande","Helvetica Neue",sans-serif;
            font-size: 12px;
            color: #333333;
            background: White;
        }
    </style>
</head>
<body>
    @RenderBody()
</body>
</html>

Vygenerování emailu

Získání HTML mailu je jednoduché - jak ze šablony vyrobit text už bylo ukázáno v předchozím díle, nyní jen použijeme metodu třídy AlternateView k získání HTML obsahu:

Code:
MailMessage mail = new MailMessage();

var htmlContent = (new email { Sender = "Martin", Recipient = "John", Content = "You win!!!" }).TransformText().Trim();
var htmlView = AlternateView.CreateAlternateViewFromString(htmlContent, new ContentType("text/html"));

mail.AlternateViews.Add(htmlView);

Vkládání obrázků

V kódu šablon si můžete všimnout i značek pro zobrazení obrázků. Pokud má email obsahovat obrázky, máte tyto tři možnosti:

  • v tagu img odkázát na existující, veřejně dostupný obrázek na sítí 
  • vložit obrázek do emailu  a odkázat na něj v tagu img pomoci cid:název 
  • vložit obrázek jako base64

Z textu šablon vyplývá, že použiji druhou možnost - jako hodnotu ContentId použijeme hodnotu zadanou za cid: v src atributu příslušeného img tagu:

var attachResource = new LinkedResource(new MemoryStream(<byte[]>));
attachResource.ContentId = <resourceID>;
htmlView.LinkedResources.Add(attachResource);

Vkládání příloh

Pokud chceme k emailu přiložit i přílohu, jde to podobně jednoduše - nejprve si vytvoříme příslušný objekt a pak je přidáme do kolekce příloh mailu:


Attachment att = new Attachment(new MemoryStream(<byte[]>), attachmentName);

mail.Attachments.Add(att); 

Tipy

Pro testování posílání emailů lze použít utilitku Smtp4Dev a    do config souboru aplikace přidáme následující nastavení:

<system.net>
   <mailSettings>
      <smtp from="mail@test.com">
         <network host="localhost" port="25"/>
      </smtp>
   </mailSettings>
</system.net>

Výsledek

Po spuštění pak obdržíme  email, který nás informuje o výhře:



A na závěr snad jen upozornění na možnost podívat a získat celý (zahrnuje i kód z prvního příspěvku na toto téma) funkční kód na Codeplex a jako v předchozím díle je zde i poster (stránka ve formátu A4 v pdf), který stručně shrnuje vše z tohoto příspěvku.