I have moved my blog to my own domain http://kazimanzurrashid.com. See you over there.

This article is divided into three parts. In the first, part I will show you how you can add Html5 forms in your ASP.NET MVC application with very minimum effort. In the second part, I will show you how to implement client side validation which will trigger automatically even when the browser does not have the html5 client side validation support and in the last part I will show widgetify the form in the client side with jQuery for the older browser that does not have support for Html5 form.

Let’s begin with the server side first. To add Html5 form I will utilize the ModelMetadata and DataAnnotation features that are available in ASP.NET MVC. The DataAnnotation has several attributes which we can use when generating the input types. Let’s say I have a dummy model which is fully decorated with the DataAnnotation attributes:

public class Html5FormModel
{
    [Display(Name = "String", Prompt = "Type a string"), Required]
    public string StringProperty { get; set; }

    [Display(Name = "Decimal", Prompt = "Type a decimal"), Range(1.0, 100.0), Required]
    public decimal? DecimalProperty { get; set; }

    [Display(Name = "Date", Prompt = "Type a date"), DataType(DataType.Date), Required]
    public DateTime? DateProperty { get; set; }

    [Display(Name = "Url", Prompt = "Type an url"), DataType(DataType.Url), Required]
    public string UrlProperty { get; set; }

    [Display(Name = "Email", Prompt = "Type an email"), DataType(DataType.EmailAddress), Required]
    public string EmailProperty { get; set; }

    [Display(Name = "Phone number", Prompt = "Type a phone number"), DataType(DataType.PhoneNumber), Required]
    public string PhoneProperty { get; set; }
}

And I am using the following code to generate the form:

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true, "Validation errors. Please correct the errors and try again.")
    <div>
        <fieldset>

            <legend>Html5 form</legend>

            <div class="editor-label">
                @Html.LabelFor(m => m.StringProperty)
            </div>
            <div class="editor-field">
                @Html.EditorFor(m => m.StringProperty)
                @Html.ValidationMessageFor(m => m.StringProperty)
            </div>

            <div class="editor-label">
                @Html.LabelFor(m => m.DecimalProperty)
            </div>
            <div class="editor-field">
                @Html.EditorFor(m => m.DecimalProperty)
                @Html.ValidationMessageFor(m => m.DecimalProperty)
            </div>

            <div class="editor-label">
                @Html.LabelFor(m => m.DateProperty)
            </div>
            <div class="editor-field">
                @Html.EditorFor(m => m.DateProperty)
                @Html.ValidationMessageFor(m => m.DateProperty)
            </div>

            <div class="editor-label">
                @Html.LabelFor(m => m.UrlProperty)
            </div>
            <div class="editor-field">
                @Html.EditorFor(m => m.UrlProperty)
                @Html.ValidationMessageFor(m => m.UrlProperty)
            </div>

            <div class="editor-label">
                @Html.LabelFor(m => m.EmailProperty)
            </div>
            <div class="editor-field">
                @Html.EditorFor(m => m.EmailProperty)
                @Html.ValidationMessageFor(m => m.EmailProperty)
            </div>

            <div class="editor-label">
                @Html.LabelFor(m => m.PhoneProperty)
            </div>
            <div class="editor-field">
                @Html.EditorFor(m => m.PhoneProperty)
                @Html.ValidationMessageFor(m => m.PhoneProperty)
            </div>

            <p>
                <input type="submit" value="Submit" />
            </p>

        </fieldset>
    </div>
}

Check that I am not using the TextBox of HtmlHelper as it will inject the old input type="text" instead I am using the EditorFor so that I can put my own template to render the inputs. Now, If you open the zip file that I have attached in the bottom of the post, you will find there are separate editor template for each type of DataAnnotation DataType attribute and Html5 form input type (both chstml and aspx templates are included). Some of the codes are something like these:

<input type="date"/>

@Html.Html5Form().InputDate()

<input type="email"/>

@Html.Html5Form().InputEmail()

<input type="number"/>

@Html.Html5Form().InputNumber()

Behind the scene there is class InputHelper which does the actual heavy lifting, here is the code:

public static class HtmlHelperExtension
{
    public static Html5Form Html5Form(this HtmlHelper instance)
    {
        return new Html5Form(instance);
    }
}

public class Html5Form
{
    private readonly HtmlHelper htmlHelper;

    public Html5Form(HtmlHelper htmlHelper)
    {
        this.htmlHelper = htmlHelper;
    }

    private static CultureInfo Culture { get { return CultureInfo.CurrentCulture; } }

    public virtual IHtmlString InputText()
    {
        return Build("text");
    }

    public virtual IHtmlString InputEmail()
    {
        return Build("email");
    }

    public virtual IHtmlString InputUrl()
    {
        return Build("url");
    }

    public virtual IHtmlString InputNumber()
    {
        return Build("number");
    }

    public virtual IHtmlString InputRange()
    {
        return Build("range");
    }

    public virtual IHtmlString InputPassword()
    {
        return Build("password");
    }

    public virtual IHtmlString InputTelephone()
    {
        return Build("tel");
    }

    public virtual IHtmlString InputDate()
    {
        return Build("date");
    }

    public virtual IHtmlString InputTime()
    {
        return Build("time");
    }

    public virtual IHtmlString InputDateTime()
    {
        return Build("datetime");
    }

    private IHtmlString Build(string type)
    {
        var viewData = htmlHelper.ViewData;
        var metadata = viewData.ModelMetadata;

        var placeHolder = !string.IsNullOrWhiteSpace(metadata.Watermark) ? "placeholder=\"" + metadata.Watermark + "\"" : string.Empty;
        var value = viewData.TemplateInfo.FormattedModelValue.ToString();
        var valueAttribute = !string.IsNullOrWhiteSpace(value) ? "value=\"" + value + "\"" : string.Empty;
        var id = viewData.TemplateInfo.GetFullHtmlFieldId(string.Empty);
        var name = viewData.TemplateInfo.GetFullHtmlFieldName(string.Empty);
        var cssClass = "text-box single-line";

        ModelState state;

        if (viewData.ModelState.TryGetValue(name, out state) && (state.Errors.Count > 0))
        {
            cssClass += " " + HtmlHelper.ValidationInputCssClassName;
        }

        var validators = GetValidators(metadata);

        string input = string.Format(Culture, "<input type=\"{0}\" id=\"{1}\" name=\"{2}\" class=\"{3}\" {4} {5} {6}/>", type, id, name, cssClass, valueAttribute, placeHolder, validators);

        return MvcHtmlString.Create(input);
    }

    private string GetValidators(ModelMetadata metadata)
    {
        var unobtrusiveJavaScriptEnabled = htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled;

        var rules = new StringBuilder();
        var validatorsDictionary = ModelValidatorProviders.Providers
                                                          .GetValidators(metadata, htmlHelper.ViewContext)
                                                          .SelectMany(v => v.GetClientValidationRules());

        foreach (var rule in validatorsDictionary)
        {
            if (rule is ModelClientValidationRequiredRule)
            {
                rules.Append(" required=\"required\"");

                if (unobtrusiveJavaScriptEnabled)
                {
                    rules.AppendFormat(Culture, " data-val-required=\"{0}\"", HttpUtility.HtmlEncode(rule.ErrorMessage ?? string.Empty));
                }

            }
            else if (rule is ModelClientValidationRegexRule)
            {
                rules.AppendFormat(Culture, " pattern=\"{0}\"", rule.ValidationParameters["pattern"]);

                if (unobtrusiveJavaScriptEnabled)
                {
                    rules.AppendFormat(Culture, " data-val-pattern=\"{0}\"", HttpUtility.HtmlEncode(rule.ErrorMessage ?? string.Empty));
                }
            }
            else if (rule is ModelClientValidationRangeRule)
            {
                rules.AppendFormat(Culture, " min=\"{0}\"", rule.ValidationParameters["min"]);
                rules.AppendFormat(Culture, " max=\"{0}\"", rule.ValidationParameters["max"]);

                if (unobtrusiveJavaScriptEnabled)
                {
                    rules.AppendFormat(Culture, " data-val-range=\"{0}\"", HttpUtility.HtmlEncode(rule.ErrorMessage ?? string.Empty));
                }
            }
            else
            {
                if (!unobtrusiveJavaScriptEnabled)
                {
                    continue;
                }

                var dictionaryKey = "data-val-" + rule.ValidationType;

                rules.AppendFormat(Culture, " {0}=\"{1}\"", dictionaryKey, HttpUtility.HtmlEncode(rule.ErrorMessage));

                dictionaryKey = dictionaryKey + "-";

                foreach (var pair in rule.ValidationParameters)
                {
                    rules.AppendFormat(Culture, " {0}=\"{1}\"", dictionaryKey + pair.Key, HttpUtility.HtmlEncode(pair.Value));
                }
            }
        }


        return rules.ToString();
    }
}

Here is the markup that is generated when it is running in the browser:

<form action="/" method="post">
<div>
    <fieldset>
        <legend>Html5 form</legend>
        <div class="editor-label">
            <label for="StringProperty">String</label>
        </div>
        <div class="editor-field">
            <input type="text" id="StringProperty" name="StringProperty" class="text-box single-line" placeholder="Type a string" required="required" />
        </div>
        <div class="editor-label">
            <label for="DecimalProperty">Decimal</label>
        </div>
        <div class="editor-field">
            <input type="number" id="DecimalProperty" name="DecimalProperty" class="text-box single-line" placeholder="Type a decimal" min="1" max="100" required="required" />
        </div>
        <div class="editor-label">
            <label for="DateProperty">Date</label>
        </div>
        <div class="editor-field">
            <input type="date" id="DateProperty" name="DateProperty" class="text-box single-line" placeholder="Type a date" required="required" />
        </div>
        <div class="editor-label">
            <label for="UrlProperty">Url</label>
        </div>
        <div class="editor-field">
            <input type="url" id="UrlProperty" name="UrlProperty" class="text-box single-line" placeholder="Type an url" required="required" />
        </div>
        <div class="editor-label">
            <label for="EmailProperty">Email</label>
        </div>
        <div class="editor-field">
            <input type="email" id="EmailProperty" name="EmailProperty" class="text-box single-line" placeholder="Type an email" required="required" />
        </div>
        <div class="editor-label">
            <label for="PhoneProperty">Phone number</label>
        </div>
        <div class="editor-field">
            <input type="tel" id="PhoneProperty" name="PhoneProperty" class="text-box single-line" placeholder="Type a phone number" required="required" />
        </div>
        <p>
            <input type="submit" value="Submit" />
        </p>
    </fieldset>
</div>
</form>

As you can see all the DataAnnotation attributes are generated with correct input attributes. Input type to DataAnnotation data type, required and range also prompt to placeholder.

In my previous post, I mentioned that most of the current desktop browsers does not have the full support of Html5 form but it will fallback to regular input even we are using the html5 form. Here are the screenshots of some of the major browsers:

Opera 10.6

opera

Chorme 8.0

webkit

FF 3.6

ff

IE9

ie

 

As you can see both chorme and opera are utilizing some of the Html5 form features but firefox and ie is rendering it as regular input elements.

That’s it for today, in the next post I will show the client side validation of html5 form, so stay tuned.

Download: Html5Form.zip

Shout it

This is a continuation of my previous post. In this post, I will do a side by side comparison of Microsoft WebMatrix/WebPage with Ruby Sinatra. The reason I picked Sinatra because both Sinatra and WebMatrix can be used to develop web sites with very minimal effort.

For your reference the Sinatra site is hosted over to heroku(cloud hosting for ruby application) and the source codes are in github.

Lets start with the development model, in MS WebPage, you start with a page and put your application logic in the page intermixed with the markup, very similar to classic asp or php. For example, the following shows codes of the home page of the bakery:

@{
    Page.Title = "Home";

    var db = Database.Open("bakery");
    var products = db.Query("SELECT * FROM PRODUCTS").ToList();
    var featured = products[new Random().Next(products.Count)];
}

<h1>Welcome to Fourth Coffee!</h1>

<div id="featuredProdcut">
    <img alt="Featured Product" src="@HrefAttribute("~/Images/Products/" + featured.ImageName)" />
    <div id="featuredProductInfo">
        <div id="productInfo">
            <h2>@featured.Name</h2>
            <p class="price">$@string.Format("{0:f}", featured.Price)</p>
            <p class="description">@featured.Description</p>
        </div>
        <div id="callToAction">
            <a class="order-button" href="@HrefAttribute("~/order", featured.Id)" title="Order @featured.Name">Order Now</a>
        </div>
    </div>
</div>

<ul id="products">
    @foreach(var p in products){
        <li class="product">
            <div class="productInfo">
                <h3>@p.Name</h3>
                <img class="product-image" src="@HrefAttribute("~/Images/Products/Thumbnails/"+ p.ImageName)" alt="Image of @p.Name" />
                <p class="description">@p.Description</p>                    
            </div>
            <div class="action">
                <p class="price">$@string.Format("{0:f}", p.Price)</p>
                <a class="order-button" href="@HrefAttribute("~/order", p.Id)" title="Order @p.Name">Order Now</a>
            </div>
        </li>
    }
</ul>

But in Sinatra you start with a route, but unlike the ASP.NET MVC or Rails you do not have to declare the route, they are inlined, let me show you the sinatra version of the above code:

# Home
get "/" do

    @title = "Home" # Page title

    # Get all the products
    @products = Product.all(:order => [:name.asc])

    # Picking up a random product as featured
    count = @products.length
    @featured = count > 0 ? @products[rand(count)] : nil

    erb :index # We are using erb as view engine

end

As you can see, in sinatra you start with a http method like Get, Post, Put, Delete, next you have to define the endpoint for which the associated code block will execute, in this case the above code will execute when the root of the site is requested. Now, in the code unlike the WebMatrix which uses plain sql, we are using Ruby DataMapper(there are various other alternates like ActiveRecord, Sequel, MongoDB, CouchDB for data access) to get all the products and among it we are picking up a random product as featured one. Once we are done we are using ERB as view engine to render the view. Like the data access there are also severeal alternates when it comes to view engines, among it Haml is the most popular one, but to make it simple I have decided to go with the ERB. Another nice thing of sinatra is that I can include my view in the same file where I am handling the route or I can even embed my view as inline like erb "Current Time: <%= Time.now =>".  If I include the view in the same file the code will look like the following:

# Home
get "/" do

    @title = "Home" # Page title

    # Get all the products
    @products = Product.all(:order => [:name.asc])

    # Picking up a random product as featured
    count = @products.length
    @featured = count > 0 ? @products[rand(count)] : nil

    erb :index # We are using erb as view engine

end

__END__

@@ index
<h1>Welcome to Sinatra Fourth Coffee!</h1>
<% if @featured %>
<section id="featured">
    <figure><img alt="Featured Product <%= @featured.name %>" src="http://weblogs.asp.net/images/products/<%= @featured.picture %>"/></figure>
    <div>
        <header>
            <h2><%= @featured.name %></h2>
        </header>
        <div>
            <p>$<%= money(@featured.price)  %></p>
            <p><%= @featured.description %></p>
        </div>
        <div>
            <a class="order-button" href="http://weblogs.asp.net/order/<%= @featured.id %>" title="Order <%= @featured.name %>">Order Now</a>
        </div>
    </div>
</section>
<% end %>
<% @products.each do |product| %>
<section class="product">
    <header>
        <h2><%= product.name %></h2>
    </header>
    <figure>
        <img class="thumbnail" src="http://weblogs.asp.net/images/products/thumbnails/<%= product.picture %>" alt="<%= product.name %>"/>
        <figcaption><%= product.description %></figcaption>
    </figure>
    <p>
        <span>$<%= money(product.price) %></span>
        <a class="order-button" href="http://weblogs.asp.net/order/<%= product.id %>" title="Order <%= product.name %>">Order Now</a>
    </p>
</section>
<% end %>

But in the GitHub you will find that I have used separated view files instead of including in the same code file.

In the above code I have mentioned that it is using DataMapper, but I did not show you how it has been configured, here is the configuration of DataMapper, in the WebMatrix example you will find there is only one database table and when an order is received it just sends an email, but in the sinatra version i have extended it a bit more so that it stores the order in the database instead of sending email.

require "rubygems"
require "dm-core"
require "dm-validations"
require 'dm-migrations'

DataMapper::setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/bakery.db")

class Product
    include DataMapper::Resource

    property :id, Serial
    property :name, String, :length => 64
    property :description, Text, :lazy => false
    property :price, Decimal, :precision => 12, :scale => 2
    property :picture, String, :length => 256
end

class Order
    include DataMapper::Resource

    property :id, Serial
    property :created_at, DateTime, :required => true
    property :email, String, :length => 256, :required => true, :format => :email_address
    property :address, String, :length => 1024, :required => true
    property :quantity, Integer, :required => true, :min => 1, :max => 100

    belongs_to :product

end

configure :development do
    # Only Upgrade in development mode
    DataMapper.auto_upgrade!
end

If you are familiar with Fluent NHibernate or Entity Framework Code First then the above is nothing different other than it is ruby code. Also check that we are embedding the validation rules in the property but I think the latest EF code first also has this feature via the Data Annotation. Since we have added all validation rules in the model when an order is received the form validation becomes absolutely easy:

post "/order/:id" do |id|

    product = Product.get(id)

    # Only save if product exists
    if product

        quantity = params[:quantity].empty? ? 0 : Float(params[:quantity])

        @order = Order.new(:product => product, :created_at => Time.now.utc, :email => params[:email], :address => params[:address], :quantity => quantity)

        if @order.save
            redirect("/success/#{@order.id}")
        else
            @title = "Place Your Order: #{product.name}"  # Page title
            erb :order
        end

    else
        # Show 404
        throw_not_found
    end
end

And the View:

and here is the WebMatrix version:

@{
    Page.Title = "Place Your Order";

    var db = Database.Open("bakery");
    var productId = UrlData[0].AsInt();
    var product = db.QuerySingle("SELECT * FROM PRODUCTS WHERE ID = @0", productId);

    if(product == null){
        Response.Redirect("~/");
    }

    if (IsPost) {
        var email = Request["orderEmail"];
        if (email.IsEmpty()) {
            ModelState.AddError("orderEmail", "You must specify an email address.");
        }

        var shipping = Request["orderShipping"];
        if (shipping.IsEmpty()) {
            ModelState.AddError("orderShipping", "You must specify a shipping address.");
        }

        //If there is no error try to process order
        if(ModelState.Count == 0){
        // Mail Sending Code
        }
    }
}

As you can see that in the Sinatra version we are not doing any kind of manual validation, instead the DataMapper is taking care of it, these are done based upon the rules that we have setup in the model. When the order is saved we taking the user to the successful otherwise showing the user the same order page.

Here is the view of the order page:

<progress class="orderProgress" value="2.0" max="3.0">
    <ol>
        <li><span>1</span>Choose Item</li>
        <li class="current"><span>2</span>Details &amp; Submit</li>
        <li><span>3</span>Receipt</li>
    </ol>
</progress>
<h1><%= @title %></h1>
<form id="orderForm" action="" method="post">
    <fieldset>
        <legend>Place Your Order</legend>
        <img class="thumbnail" src="http://weblogs.asp.net/images/products/thumbnails/<%= @order.product.picture %>" alt="<%= @order.product.name %>"/>
        <ul>
            <li>
                <label for="email">Email Address:</label>
                <input type="email" name="email" id="email" value="<%= @order.email %>" placeholder="Type your email" required="required" autofocus="autofocus"/>
                <%= field_validation(@order, :email) %>
            </li>
            <li>
                <label for="address">Shipping Address:</label>
                <textarea rows="4" cols="20" name="address" id="address" placeholder="Type your address" required="required"><%= @order.address %></textarea>
                <%= field_validation(@order, :address) %>
            </li>
            <li>
            <label for="quantity">Quantity:</label>
            <input type="number" name="quantity" id="quantity" min="1" step="1" max="100" value="<%= @order.quantity %>" required="required"/>
            x <span id="price"><%= money(@order.product.price)  %></span> = <output id="total" for="quantity,price">$<%= money(@order.product.price * @order.quantity) %></output>
            <%= field_validation(@order, :quantity) %>
            </li>
            <li><button type="submit" class="order-button">Place Order</button></li>
        </ul>
    </fieldset>
</form>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script>!window.jQuery && document.write('<script src="js/jquery-1.4.2.min.js"><\/script>')</script>
<script>
    $(function () {

        var price = parseFloat($("#price").text()).toFixed(2);
        var total = $("#total");
        var quantity = $("#quantity");

        quantity.change(function () {

            var newQuantity = parseInt($(this).val());
            if (!newQuantity || newQuantity < 1) {
                quantity.val(1);
                newQuantity = 1;
            }
            else if (newQuantity.toString() !== quantity.val()) {
                quantity.val(newQuantity);
            }

            total.text("$" + (price * newQuantity).toFixed(2));
        });
    });
</script>

Both in the handler and in the view you will find few methods like throw_not_found, field_validation which are not built-in methods, those are some helper methods, to create helper methods you just have to put those methods in a helper do and end block like the following:

helpers do

    def throw_not_found()
        raise Sinatra::NotFound
    end

    def money(value)
        sprintf("%.02f", value)
    end

    def field_validation(target, field)
        "<span class=\"field-validation-error\">#{target.errors[field][0]}</span>" unless target.errors[field].empty?
    end

end

I think I have shown most of the codes of this application and explains how it works, if the above interests you I would suggest you download the code from the github and check it side by side. Overall I think Sinatra is superior comparing to WebMatrix/WebPage when it comes to writing clean code and separating parts of your application and yet maintaining the simplicity and minimalism. I wonder if Microsoft did evaluate it before choosing the asp/php path and I would definitely like to see similar features baked into it so that real professional can consider it instead of only hobbyist.

Shout it

Unless you are living in a cave, I expect you heard Html5 and know that most of the modern browsers has the reasonable support to render html5 pages. In this post, I will take the tiny sample “Bakery” which comes with the Microsoft WebMatrix as a site template and truly upgrade it to html5. The reason I mentioned “truly” because it is already using html5 doctype but the markup is still in xhtml. You can click here if you want to see a screenshot of this website.

The following is the skeleton of this site:

WebMatrix Bakery Skeleton

Now, let us see the markup when it is rendered in the browser:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Fourth Coffee - Home</title>
    <link href="Styles/Site.css" rel="stylesheet" />
</head>
<body>
    <div id="page">
        <div id="header">
            <p class="site-title"><a href="./">Fourth Coffee</a></p>
            <ul id="menu">
                <li><a href="./">Home</a></li>
                <li><a href="About">About Us</a></li>
            </ul>
        </div>
        <div id="body">
            <h1>Welcome to Fourth Coffee!</h1>
            <div id="featuredProdcut">
                <img alt="Featured Product" src="Images/Products/bread.jpg" />
                <div id="featuredProductInfo">
                    <div id="productInfo">
                        <h2>Bread</h2>
                        <p class="price">$1.49</p>
                        <p class="description">Fresh baked French-style bread</p>
                    </div>
                    <div id="callToAction">
                        <a class="order-button" href="order/4" title="Order Bread">Order Now</a>
                    </div>
                </div>
            </div>
            <ul id="products">
                <li class="product">
                    <div class="productInfo">
                        <h3>Carrot Cake</h3>
                        <img class="product-image" src="Images/Products/Thumbnails/carrot_cake.jpg" alt="Image of Carrot Cake" />
                        <p class="description">A scrumptious mini-carrot cake encrusted with sliced almonds</p>
                    </div>
                    <div class="action">
                        <p class="price">$8.99</p>
                        <a class="order-button" href="order/1" title="Order Carrot Cake">Order Now</a>
                    </div>
                </li>
                <!-- More Products -->
            </ul>
        </div>
        <div id="footer">
            &copy;2010 - Fourth Coffee
        </div>
    </div>
</body>
</html>

As I mentioned earlier that it has some flavor of html5, for example, if you see the first line you will find that it does not use long declaration of doctype like <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">, in html5 it has trimmed down to <!DOCTYPE html>. Next, to specify the content type, it is not required to write <meta http-equiv="Content-Type" content="text/html"; charset="utf-8"/>, instead <meta charset="utf-8"/> is used and at last for script and stylesheet the type is no longer required, for example the stylesheet file has been included with <link href="Styles/Site.css" rel="stylesheet"/> without the type="text/css".

But other than the above, there are nothing more that are html5ish also the markup contains too many div and unnecessary css classes. In order to make it truly meaningful, we are going to use some of the new html tags that comes with the html5. First, let me show you the bare minimum of the layout:

<body>
    <header role="banner">
        <h1><a href="http://weblogs.asp.net/">Fourth Coffee</a></h1>
        <nav>
            <ul>
                <li><a href="http://weblogs.asp.net/" rel="home">Home</a></li>
                <li><a href="http://weblogs.asp.net/about">About Us</a></li>
            </ul>
        </nav>
    </header>
    <div role="main">
        <!-- Put your page content over here -->
    <div/>
    <footer role="contentinfo">
        <p>&copy;2010 - Fourth Coffee</p>
    </footer>
</body>

As you can see that we no longer using any container div (e.g. div id="page") since the body itself can act as the container and now it starts with the new header tag instead of the div id="header", the header also contains role="banner", it is not html5 specific instead it is called ARIA landmark role. Inside the header, rather than using p tag for the site title we are using the standard h1 and using the existing text replacing technique to replace it with the site banner. Next, for the top right navigation we are using the new nav element, but we are not using the ARIA role="navigation" as the nav itself stands for the navigation. The rest of the two element are pretty simple for the main content we are using the plain old div with an ARIA role="main" and the for the copyright we are using the new footer element of html5.

Next, for the products, the WebMatrix template is using div for the featured product and ul for the rest of the product listing. But in our case we are going to use another new element section. Initially, there was a misconception to replace div with section which was wrong, if it is only styling or DOM scripting purpose then you should stick with the div, but if want to group some thematically related content which are also self contained and independent part, you can use the section and in our case it is the product.

Now lets see the new markup:

<div role="main">
    <h1>Welcome to Fourth Coffee!</h1>
    <section id="featured">
        <figure>
            <img src="http://weblogs.asp.net/Images/Products/bread.jpg" alt="Featured Product Bread"/>
        </figure>
        <div>
            <header>
                <h2>Bread</h2>
            </header>
            <div>
                <p>$1.49</p>
                <p>Fresh baked French-style bread</p>
            </div>
            <div>
                <a title="Order Bread" href="http://weblogs.asp.net/order/4" class="order-button">Order Now</a>
            </div>
        </div>
    </section>
    <section class="product">
        <header>
            <h2>Carrot Cake</h2>
        </header>
        <figure>
            <img class="thumbnail" src="http://weblogs.asp.net/Images/Products/Thumbnails/carrot_cake.jpg" alt="Carrot Cake"/>
            <figcaption>A scrumptious mini-carrot cake encrusted with sliced almonds</figcaption>
        </figure>
        <p>
            <span>$8.99</span>
            <a class="order-button" href="http://weblogs.asp.net/order/1" title="Order Carrot Cake">Order Now</a>
        </p>
    </section>
    <!-- More Products -->
<div/>

As you can see that other than using section we are also using the new figure and we are putting the product description in the figcaption also check that each section has its own header like the main document. Now if are wondering what are the css style that are responsible for the styling, here you go:

article, aside, header, figcaption, figure, footer, nav, section
{
    display: block;
}

.thumbnail
{
    background-color: #edece8;
    border: 1px #e6e3d8 solid;
    height: 200px;
    margin: 0 0 7px 0;
    padding: 6px;
    width: 200px;
}

.order-button
{
    background-color: #a52f09;
    border: 1px solid #712107;
    color: #fdfcf7 !important;
    float: right;
    padding: 2px 7px; 
    /*CSS3 properties*/
    -moz-border-radius: 4px;
    -webkit-border-radius: 4px;
    border-radius: 4px;
    -webkit-box-shadow: 1px 2px 4px rgba(136,136,136, 0.6);
    -moz-box-shadow: 1px 2px 5px #b4b4b4;
    box-shadow: 1px 2px 5px #b4b4b4;
}

header[role="banner"]
{
    height: 80px;
}

header[role="banner"] > h1 > a
{
    background: transparent url(../images/brand.png) no-repeat;
    float: left;
    height: 50px;
    margin: 15px;
    padding: 0;
    text-indent: -9999px;
    width: 340px;
}

header[role="banner"] > nav
{
    float: right;
    font-size: 1.4em;
    list-style: none;
}

header[role="banner"] > nav ul
{
    margin-top: 50px;
    list-style: none outside none;
}

header[role="banner"] > nav li
{
    float: left;
    margin-left: 30px;
}

div[role="main"]
{
    border-top: 1px dotted #5d5a53;
    clear: both;
    margin-bottom: 20px;
    padding-top: 20px;
    overflow: hidden;
}

#featured
{
    background-color: #fdfcf7;
    border: 4px solid #e6e3d8;
    height: 300px;
    margin: 20px auto;
    width: 920px;
    /*CSS3 properties*/
    -moz-border-radius: 6px;
    -webkit-border-radius: 6px;
    border-radius: 6px;
    -webkit-box-shadow: 0px 2px 5px #888;
    -moz-box-shadow: 0px 2px 5px #888;
    box-shadow: 0px 2px 5px #888;
}

#featured > figure
{
    float: left;
    /*CSS3 properties*/
    -moz-border-radius: 3px 0px 0px 3px;
    -webkit-border-radius: 3px 0px 0px 3px;
    border-radius: 3px 0px 0px 3px;
}

#featured > div
{
    float: right;
    height: 100%;
    padding: 0 10px;
    position: relative;
    width: 230px;
}

#featured > div h2
{
    font-size: 2.5em;
    font-weight: normal;
    margin: 5px 0;
}

#featured > div header + div p
{
    font-size: 1.75em;
    margin: 5px 0;
}

#featured > div header + div p + p
{
    font-size: 1.2em;
    margin: 0;
}

#featured > div header + div + div
{
    position: absolute;
    left: 5px;
    right: 5px;
    bottom: 10px;
    width: 230px;
}

#featured > div header + div + div a.order-button
{
    font-size: 2em;
    padding: 5px 0;
    text-align: center;
    width: 220px;
}

.product
{
    float: left;
    margin: 10px 12px;
    width: 215px;
}

.product h2
{
    font-size: 1.75em;
    font-weight: normal;
    margin: 0 0 5px 0;
    padding: 0;
}

.product > figure
{
    height: 250px;
}

.product > p
{
    margin-top: 2em;
}

.product > p > span
{
    float: left;
    font-size: 1.5em;
}

One of the thing that I find incorrect in the WebMatrix template Css3 styling is the ordering, rather than calling it incorrect let me call it as backward looking rather than forward looking, for example when declaring the styles they decide to put the standard declaration first then the browser extension, but the correct order should be the browser extension first then the standard declaration:

Backward looking:

border-radius: 4px; 
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
box-shadow: 1px 2px 5px #b4b4b4;
-webkit-box-shadow: 1px 2px 4px rgba(136,136,136, 0.6);
-moz-box-shadow: 1px 2px 5px #b4b4b4;

Forward looking:

-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: 1px 2px 4px rgba(136,136,136, 0.6);
-moz-box-shadow: 1px 2px 5px #b4b4b4;
box-shadow: 1px 2px 5px #b4b4b4;

Now, lets see the other two pages, first the order page which show an order form, but the form does not have any html5 input types and attributes. The following shows the WebMatrix template version:

<form action="" method="post">
    <fieldset class="no-legend">
        <legend>Place Your Order</legend>
        <img class="product-image order-image" src="../Images/Products/Thumbnails/cupcakes.jpg" alt="Image of Cupcakes"/>
        <ol>
            <li class="email">
                <label for="orderEmail">Your Email Address</label>
                <input type="text" id="orderEmail" name="orderEmail"/>
            </li>
            <li class="shiping">
                <label for="orderShipping">Shipping Address</label>
                <textarea rows="4" cols="20" id="orderShipping" name="orderShipping"></textarea>
            </li>
            <li class="quantity">
                <label for="orderQty">Quantity</label>
                <input type="text" id="orderQty" name="orderQty" value="1"/>
                x
                <span id="orderPrice">5.99</span>
                =
                <span id="orderTotal">5.99</span>
            </li>
        </ol>
        <p class="actions">
            <input type="hidden" name="ProductId" value="3" />
            <input type="submit" value="Place Order"/>
        </p>
    </fieldset>
</form>

and here is the revised html5 version:

<form id="order" action="" method="post">
    <fieldset>
        <legend>Place Your Order</legend>
        <img class="thumbnail" src="http://weblogs.asp.net/images/products/thumbnails/cupcakes.jpg" alt="Cupcakes"/>
        <ul>
            <li>
                <label for="email">Email Address:</label>
                <input type="email" name="email" id="email" placeholder="Type your email" required="required" autofocus="autofocus"/>
            </li>
            <li>
                <label for="address">Shipping Address:</label>
                <textarea rows="4" cols="20" name="address" id="address" placeholder="Type your address" required="required"></textarea>
            </li>
            <li>
                <label for="quantity">Quantity:</label>
                <input type="number" name="quantity" id="quantity" min="1" step="1" max="100" value="1" required="required"/>
                x <span id="price">9.00</span> = <output id="total" for="quantity,price">$9.00</output>
            </li>
            <li><button type="submit" class="order-button">Place Order</button></li>
        </ul>
    </fieldset>
</form>

As you can see that our revised version has some new input types like <input="email"/> and <input type="number"/> and attributes like placeholder, required, autofocus etc etc, but currently only few browsers has the support to take advantages of it, for example if I run it in Opera latest and click the place order with some invalid data it will show the following screen:

Opera html5 form

As you can see Opera does have the built-in support to validate the form, also it is showing the quantity input in an range input rather than plain textbox. I think the iPhone Safari also takes the advantages of html5 form. Though not all the browser has the support of these new values but the good news is it will automatically fallback to the plain old input type="text" even if these new values are used. Maybe in future I will show you how can use some intelligent scripting to trigger these client side features depending upon the browser it is running. One last thing I want to highlight in this form is that to show the total amount we are also using the new output element and to associate with the other elements that are involves in the calculation we are using the for attribute with comma separated values.

At the top of Order and Successful page some kind of wizard like interface is shown which indicates the current steps, I think the best way to code it if we use the new progress element. I this case, we will just wrap the ol with the progress. For example:

Order:

<progress value="2.0" max="3.0">
    <ol>
        <li><span>1</span>Choose Item</li>
        <li class="current"><span>2</span>Details &amp; Submit</li>
        <li><span>3</span>Receipt</li>
    </ol>
</progress>

Successful:

<progress value="3.0" max="3.0">
    <ol>
        <li><span>1</span>Choose Item</li>
        <li><span>2</span>Details &amp; Submit</li>
        <li class="current"><span>3</span>Receipt</li>
    </ol>
</progress>

The last page in the site is the About Us page which basically contains the history of the company and in the side bar a Twitter widgets that shows the result with bakery hash tag. The company history is the prime candidate for the html5 new article element and side bar as aside as it is showing related content with bakery, so the markup becomes:

<article id="about">
    <header>
        <h1>A little bit about Fourth Coffee</h1>
    </header>
    <p>
        Fourth Coffee was founded in 2010 and delivers coffee and fresh baked goods right to your door. 
        In another life, Bill Baker was a developer by day and pastry chef by night.  
        But soon Bill's innate skills with all things involving butter, flour and sugar put him 
        even more in demand than his programming talents and what started out as a way to satisfy 
        his own sweet tooth became all-consuming.  Fourth Coffee is not only a candy-coated wonderland 
        of coffee, pastries, cookies and cakes, it also honors his tech background by employing a state 
        of the art online ordering system that makes it easy for anybody with internet access to 
        order his all natural, locally-sourced confections and have them delivered to their 
        door within 24 hours.
    </p>
</article>
<aside>
<!-- Twitter Widgets goes here -->
</aside>

That’s it for today. As you can see that writing meaningful and clean markup is not only a fun but also an art. In my next post, I will discuss about the Microsoft Webpage that are bundled in the WebMatrix and alternate frameworks that are available in the other platforms, so stay tuned.

Shout it

Since the release of Razor I could not manage time to give a deep dive into this new framework. Last night, I started to explore, the idea was to create ASP.NET MVC View Engine, yes I know the official version is coming up end of this month, but it will give me enough details of the Razor internals in the mean time. Part of the goal was to port the default ASP.NET MVC application in Razor. And here is the basic version:

_Master.cshtml

<!DOCTYPE html>
<head>
    <title>
        @RenderSection("title")
    </title>
    <link href="@Href("~/Content/Site.css")" rel="stylesheet" type="text/css"/>
</head>
<body>
    <div class="page">
        <div id="header">
            <div id="title">
                <h1>My ASP.NET MVC Razor Application</h1>
            </div>
            <div id="logindisplay">
                @RenderPage("~/Views/Shared/_LogOnUserControl.cshtml")
            </div> 
            <div id="menucontainer">
                <ul id="menu">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                </ul>
            </div>
        </div>
        <div id="main">
            @RenderBody()
            <div id="footer"></div>
        </div>
    </div>
</body>
</html>

Home/Index.cshtml

@{ LayoutPage = "~/Views/Shared/_Master.cshtml"; }
@section title {
    Home Page
}
<h2>@ViewData["Message"]</h2>
<p>
    To learn more about ASP.NET MVC Razor Integration visit <a href="http://weblogs.asp.net/rashid">http://weblogs.asp.net/rashid</a>.
</p>

Account/Logon.cshtml

@{ LayoutPage = "~/Views/Shared/_Master.cshtml"; }
@section title {
    Log On
}
<h2>Log On</h2>
<p>
    Please enter your username and password. @Html.ActionLink("Register", "Register") if you don't have an account.
</p>
<form method="post">
    @Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.")
    <div>
        <fieldset>
            <legend>Account Information</legend>
            <div class="editor-label">
                @Html.Label("UserName")
            </div>
            <div class="editor-field">
                @Html.TextBox("UserName")
                @Html.ValidationMessage("UserName")
            </div>
            <div class="editor-label">
                @Html.Label("Password")
            </div>
            <div class="editor-field">
                @Html.Password("Password")
                @Html.ValidationMessage("Password")
            </div>
            <div class="editor-label">
                @Html.CheckBox("RememberMe")
                @Html.Label("RememberMe")
            </div>
            <p>
                <input type="submit" value="Log On"/>
            </p>
        </fieldset>
    </div>
</form>

You will find rest of the view markups in the attached zip file at bottom of this post.

There are quite a few features that it currently does not support:

  1. Strongly typed HtmlHelper (Razor WebPage does not have the support of generic model type).
  2. Render Partial is not working, so you have to use the Razor @RenderPage.
  3. None of the Form Extension is working.
  4. Does not support Razor Start and Init page.

The view engine does not have anything special, except a hack, It turns out the WebPageContext which drives the Razor page rendering has all the member as internal. So I have to use a bit of reflection to set those properties. I wish when can we create a framework without using the internal scope.

To run the attached solution, just open it in the VS2010 and press f5, you do not have to run the IISExpress, it will use the VS built-in web server, Currently it uses C#, but if you want to use VB, just register the ViewEngine in the application_start like the following:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);
    ViewEngines.Engines.Add(new RazorViewEngine(("vb")));
}

That's it for today. Enjoy!!!

Download: AspNetMvcRazor.zip

Shout it

With the release of Iron Ruby we are no more tide up with the xml based build script like NAnt and MSBuild, we can now use Rake with Iron Ruby to build our .NET based projects. In this post, I will show you a very basic build script in rake that will integrate StyleCop, Simian, FxCop, MSpec and NCover. I will use the same fund transfer project that I used in my previous post.

Before moving to the build script, let me give a brief description of the above tools:

  1. StyleCop: It works on source code and ensures that all of rules that you have defined earlier has been meet. These rules can include ode structure, naming Convention, documentation rules etc, you can even create your own custom rules if you want. It has been recently open sourced by Microsoft and can be download from http://stylecop.codeplex.com.
  2. Simian is another excellent tool which also works on source code. It detects the duplicate codes among the given source code files. Though it is a commercial product but it is free to use in non commercial or open source projects. It supports a lot of languages and you can download it from http://www.redhillconsulting.com.au/products/simian.
  3. FxCop is one of the tool that is available from the early days of .NET, similar to StyleCop but it enforce the rules on compiled outputs rather than source codes. The latest version of FxCop is included in the Windows 7 SDK which you can download from the Microsoft Download Center. In this post, I will be using the last standalone version 1.36.
  4. MSpec is my BDD Style Test Framework of choice. Currently there is no official binary version but you can download it and compile it yourself.
  5. NCover is my another favorite tool which reports the code coverage. Usually it is used with the test runner to capture the coverage. In this post I will be using the community edition of NCover which is free. You can download it from http://www.ncover.com/download/current.

Now we know the basics of the these tools, lets define the steps, since both StyleCop and Simian works on source codes we will invoke these before compiling our source codes and rest of the steps after the compilation:

  1. Run StyleCop
  2. Run Simian
  3. Compile
  4. Run FxCop
  5. Run Test And Coverage.
  6. Copy the build artifacts in the Drop location.

This is the skeleton of the rake script for the above steps:

task :style_cop do
	puts "Running StyleCop"
end

task :simian do
	puts "Running Simian"
end

task :compile do
	puts "Compiling projects"
end

task :fx_cop do
	puts "Running FxCop"
end

task :test_and_coverage do
	puts "Running Test and capturing the code coverage"
end

task :drop do
	puts "Preparing the build artifacts"
end

task :default => [:style_cop, :simian, :compile, :fx_cop, :test_and_coverage, :drop] do
	puts "Done"
end

Consider task as MSBuild Target and it has an associated name (ruby symbol) denoted as :name which is same as MSBuild Target name. To make a task depended on other tasks you have to specify the task names in square brackets like the last task of in above script. To invoke one or more specific tasks you have to pass the task name as argument, for multiple tasks use space as separator. For example:

rake simian fx_cop # The rake command by default executes rakefile.rb
will show output:
Running Simian
Running FxCop

If none of the task is passed, it will execute the default task.

Now, lets add the codes in the above skeleton, but before that lets see the directory structure of the Fund Transfer Project:

RakeFS

Though we have six main tasks but there few more things that we have to add. First the initialization

MSBUILD = File.join(ENV["windir"] || ENV["WINDIR"], "Microsoft.NET", "Framework", "v3.5", "msbuild.exe")

CONFIG = ENV["config"] || ENV["CONFIG"] || "Debug"

CURRENT_PATH = File.dirname(__FILE__)
ARTIFACT_PATH = File.join(CURRENT_PATH, "Artifacts")
REPORT_PATH = File.join(ARTIFACT_PATH, "Reports")
REFERENCES_PATH = File.join(CURRENT_PATH, "References")
TOOLS_PATH = File.join(CURRENT_PATH, "Tools")
TEST_SUFFIX = "Specs"

projects = []
app_projects = []
app_files = []
app_dependency_files = []
test_projects = []
test_files = []
test_dependency_files = []
referenced_files = []

desc "Initilizing build"
task :init do

	# This will create the Artifacts directory if it does not exist
	FileUtils.mkdir_p ARTIFACT_PATH

	# Delete the existing files/folders of the Artifacts directory
	Dir.foreach(ARTIFACT_PATH) do | entry |
		FileUtils.remove_entry(File.join(ARTIFACT_PATH, entry)) if (entry != ".") && (entry != "..")
	end

	# Create the Report folder
	FileUtils.mkdir_p REPORT_PATH

	# Get all the CSharp Project files that exists under the current path
	projects = Dir.glob(File.join(CURRENT_PATH, "/**/*.csproj"))

	# Now we have to indentify the Test/Spec project and the regular project
	projects.each do | project |

		project_name = File.basename(project, ".csproj")

		# If Project has the special suffix we will treat it as Test/Spec Project
		# otherwise it is a regular project
		if /#{TEST_SUFFIX}$/.match(project_name)
			test_projects << project
		elsif
			app_projects << project
		end
	end

end

In the above we are first declaring few constants(please note that in Ruby Constants are started with capital letters) and few variables which we are going to use in the tasks. The File.Join is same as .NET Path.Combine. The ENV[YOUR_KEY] is used get the value of Environment variables. In the init task we are making sure the Artifacts and its subfolder Reports exists, next we are scanning all the projects and storing it in the two array’s for later usages. By default, the script assumes that the MSpec projects ends with “Specs”. Next, we will add one more helper task which will clear the build outputs:

desc "Cleaning up outputs"
task :clean do

	# Iterate all the project and pass to MSBuild for cleaning up the build outputs
	projects.each do | project |
		sh "\"#{MSBUILD}\" \"#{project}\" /p:Configuration=#{CONFIG} /t:Clean /tv:3.5"
	end

end

The sh stands for shell, we are using it to execute the MSBuild.exe and passing the required parameters.

Now, lets modify the default task’s dependencies so that the above two tasks are executed prior the others:

desc "Default"
task :default => [:init, :clean, :style_cop, :simian, :compile, :fx_cop, :test_and_coverage, :drop] do

	puts "Build Completed."

end

Among the tasks most of the tasks are invoking external processes, only the style_cop task requires bit of interaction with the clr types. Although StyleCop comes with MSBuild task but as we are not running in MSBuild environment, we cannot use it directly, instead we have to call the StyleCop directly from our rake script and this is the true power of Iron Ruby, we can easily call any clr type and vice versa. The following shows the code which runs the StyleCop analysis:

desc "Running StyleCop"
task :style_cop do

	# We will need the reference of StyleCop
	require File.join(TOOLS_PATH, "stylecop", "Microsoft.StyleCop.dll")

	# Create a List<StyleCopProject> to store the regular projects
	style_cop_projects = System::Collections::Generic::List[Microsoft::StyleCop::CodeProject].new
	
	# Build the File path of StyleCop Report
	report_file = File.join(REPORT_PATH, "StyleCop.xml")

	# Create a StyleCop console to apply the StyleCop rules
	style_cop_console = Microsoft::StyleCop::StyleCopConsole.new(nil, false, report_file, nil, true)
	
	# Create a StyleCop Configuration
	style_cop_configuration = Microsoft::StyleCop::Configuration.new(nil)

	# We will only invoke StyleCop for the regular project, not for the Test/Specs.
	app_projects.each do | project |

		style_cop_project = Microsoft::StyleCop::CodeProject.new(project.hash, project, style_cop_configuration)
		
		# Add all the .cs files that resides in this project
		source_files = Dir.glob(File.join(File.dirname(project), "/**/*.cs"))

		source_files.each do | source_file |
			style_cop_console.core.environment.add_source_code(style_cop_project, source_file, nil)
		end

		style_cop_projects.add(style_cop_project)
	end

	# Start applying the rules
	style_cop_console.start(style_cop_projects, true)
	
	# Now release all the associated resources of StyleCop
	style_cop_console.dispose()

end

The next two tasks are simple external program execution, it just prepares the required parameters and invokes the program:

desc "Running Simian"
task :simian do

	simian_path = File.join(TOOLS_PATH, "simian")
	simian_exe = File.join(simian_path, "simian-2.2.24.exe")
	simian_report = File.join(REPORT_PATH, "Simian.xml")

	app_projects_paths = app_projects.map { | project | "\"" + File.dirname(project) + "\""}.join(" ")

	# Copy the xsl file in the report folder so that we can view the xml as html
	FileUtils.copy(File.join(simian_path, "Simian.xsl"), REPORT_PATH)

	sh "\"#{simian_exe}\" #{app_projects_paths} -formatter=xml:\"#{simian_report}\" -failOnDuplication- -reportDuplicateText+ -includes=\"**/*.cs\""

end

desc "Compiling source"
task :compile do

	projects.each do | project |
		sh "\"#{MSBUILD}\" \"#{project}\" /p:Configuration=#{CONFIG} /t:Build /tv:3.5"
	end

end

Now we add one last helper task, this will ensure that all the project outputs and referenced components are copied into the build Artifacts folder, so that we can use the folder as Working Directory to run the FxCop and MSpec specifications.

desc "Copy compiled files"
task :copy_files do
	
	app_projects.each do | project |

		project_directory = File.dirname(project)
		project_name = File.basename(project, ".csproj")

		project_outputs = Dir.glob(File.join(File.join(project_directory, "bin", CONFIG), "/**/*.*"))

		project_outputs.each do | file |
		
			base_name = File.basename(file)

			if File.basename(base_name, File.extname(file)).eql?(project_name)
				app_files << base_name
			else
				app_dependency_files << base_name
			end
			
			FileUtils.copy(file, ARTIFACT_PATH);

		end

	end

	test_projects.each do | project |

		project_directory = File.dirname(project)
		project_name = File.basename(project, ".csproj")

		project_outputs = Dir.glob(File.join(File.join(project_directory, "bin", CONFIG), "/**/*.*"))

		project_outputs.each do | file |

			base_name = File.basename(file)

			if File.basename(base_name, File.extname(file)).eql?(project_name)
				test_files << base_name
			else
				test_dependency_files << base_name if !app_files.include?(base_name) && !app_dependency_files.include?(base_name)
			end

			FileUtils.copy(file, ARTIFACT_PATH);

		end

	end

	Dir.glob(File.join(REFERENCES_PATH, "/**/*.*")).each do | file |

		referenced_files << File.basename(file)

		FileUtils.copy(file, ARTIFACT_PATH);

	end

end

Depending upon the configuration (usually Debug/Release) we are copying the build outputs from the bin directory of each project and putting it into the Artifacts folder, when copying we are also populating few file list variables so that we can later use it in the FxCop and MSpec tasks. This tasks will be invoked after the compiling the projects. So we will again modify the dependencies of the default task.

desc "Default"
task :default => [:init, :clean, :style_cop, :simian, :compile, :copy_files, :fx_cop, :test_and_coverage, :drop] do

	puts "Build Completed."

end

The next two tasks are also invoking the external processes and building the required parameters:

desc "Running FxCop"
task :fx_cop do

	fxcop_path = File.join(TOOLS_PATH, "fxcop")
	fxcop_exe = File.join(fxcop_path, "FxCopCmd.exe")
	fxcop_report = File.join(REPORT_PATH, "FxCop.xml")

	dlls = "/f:" + app_files.select{ |file| File.extname(file).eql?(".dll")}.map { | dll | "\"" + File.join(ARTIFACT_PATH, dll) + "\""}.join(" /f:")

	FileUtils.copy(File.join(fxcop_path, "Xml", "FxCopReport.xsl"), REPORT_PATH)
	
	sh "\"#{fxcop_exe}\" #{dlls} /d:\"#{ARTIFACT_PATH}\" /o:\"#{fxcop_report}\" /oxsl:\"FxCopReport.xsl\" /to:0 /fo /gac /igc /q"

end

desc "Running tests"
task :test_and_coverage do

	test_runner_exe = File.join(TOOLS_PATH, "mspec", "mspec.exe")

	app_dlls = app_files.select{ |file| File.extname(file).eql?(".dll")}.map { | dll | File.basename(File.basename(dll), File.extname(dll)) }.join(";")
	test_dlls = test_files.select{ |file| File.extname(file).eql?(".dll")}.map { | dll | File.join(ARTIFACT_PATH, dll) }.join(" ")

	test_runner_argument = "\"#{test_runner_exe}\" --html \"#{REPORT_PATH}\" \"#{test_dlls}\""

	exclude_attributes = "System.Runtime.CompilerServices.CompilerGeneratedAttribute;System.CodeDom.Compiler.GeneratedCodeAttribute;System.Diagnostics.DebuggerNonUserCodeAttribute"

	ncover_coverage = File.join(REPORT_PATH, "NCover.Console.xml")
	ncover_path = File.join(TOOLS_PATH, "ncover")
	ncover_console_exe = File.join(ncover_path, "NCover.Console.exe")
	ncover_console_argument = "#{test_runner_argument} //a \"#{app_dlls}\" //x \"#{ncover_coverage}\" //ea \"#{exclude_attributes}\" //w \"#{ARTIFACT_PATH}\" //q"

	FileUtils.copy(File.join(ncover_path, "Coverage.xsl"), REPORT_PATH)

	sh "RegSvr32 \"#{File.join(ncover_path, "CoverLib.dll")}\" /s"

	sh "\"#{ncover_console_exe}\" #{ncover_console_argument}"

	sh "RegSvr32 \"#{File.join(ncover_path, "CoverLib.dll")}\" -u /s"
	
	ncover_explorer_exe = File.join(ncover_path, "NCoverExplorer.Console.exe")
	ncover_report = File.join(REPORT_PATH, "NCoverExplorer.Console.xml")

	FileUtils.copy(File.join(ncover_path, "CoverageReport.xsl"), REPORT_PATH)

	sh "\"#{ncover_explorer_exe}\" \"#{ncover_coverage}\" /r:ModuleClassFunctionSummary /x:\"#{ncover_report}\" /q"

end

And in the last task we will do some cleanup so that the artifacts directory only contains the application dll/pdb/xml docs and all the xml/html reports of the above tools.

desc "Preparing drop"
task :drop do

	app_dependency_files.concat(test_files).concat(test_dependency_files).concat(referenced_files).uniq().each do | file |
		FileUtils.remove_file(File.join(ARTIFACT_PATH, file), true)
	end
	
end

If you open the Artifacts and the Reports folder after running the rake scripts, you will see:

artifacts

As mentioned that it is a very basic rake example which you can use for building relatively simple applications as it does not have any customization support (though you can always modify the source code). But the good news is there is already rake task library for the .NET applications called Albacore which I highly recommend you to check.

That’s it for today. You can download the complete source code of this post from the following link.

Download: RakeIntegration.zip

 

Shout it

[Note: This is not a continuation of my previous post, treat it as an experiment out in the wild. ]

Lets consider the following class, a fictitious Fund Transfer Service:

public class FundTransferService : IFundTransferService
{
	private readonly ICurrencyConversionService currencyConversionService;

	public FundTransferService(ICurrencyConversionService currencyConversionService)
	{
		this.currencyConversionService = currencyConversionService;
	}

	public void Transfer(Account fromAccount, Account toAccount, decimal amount)
	{
		decimal conversionRate = currencyConversionService.GetConversionRate(fromAccount.Currency, toAccount.Currency);
		decimal convertedAmount = conversionRate * amount;

		fromAccount.Withdraw(amount);
		toAccount.Deposit(convertedAmount);
	}
}

public class Account
{
    public Account(string currency, decimal balance)
    {
        Currency = currency;
        Balance = balance;
    }

    public string Currency { get; private set; }

    public decimal Balance { get; private set; }

    public void Deposit(decimal amount)
    {
        Balance += amount;
    }

    public void Withdraw(decimal amount)
    {
        Balance -= amount;
    }
}

We can write the spec with MSpec + Moq like the following:

public class When_fund_is_transferred
{
	const decimal ConversionRate = 1.029m;
	const decimal TransferAmount = 10.0m;
	const decimal InitialBalance = 100.0m;

	static Account fromAccount;
	static Account toAccount;
	static FundTransferService fundTransferService;

	Establish context = () =>
	{
		fromAccount = new Account("USD", InitialBalance);
		toAccount = new Account("CAD", InitialBalance);

		var currencyConvertionService = new Moq.Mock<ICurrencyConversionService>();
		currencyConvertionService.Setup(ccv => ccv.GetConversionRate(Moq.It.IsAny<string>(), Moq.It.IsAny<string>())).Returns(ConversionRate);

		fundTransferService = new FundTransferService(currencyConvertionService.Object);
	};

	Because of = () => fundTransferService.Transfer(fromAccount, toAccount, TransferAmount);

	It should_decrease_from_account_balance = () => fromAccount.Balance.ShouldBeLessThan(InitialBalance);

	It should_increase_to_account_balance = () => toAccount.Balance.ShouldBeGreaterThan(InitialBalance);
}

and if you run the spec it will give you a nice little output like the following:

When fund is transferred
» should decrease from account balance
» should increase to account balance

2 passed, 0 failed, 0 skipped, took 1.14 seconds (MSpec).

Now, lets see how we can write the exact spec in RSpec.

require File.dirname(__FILE__)  + "/../FundTransfer/bin/Debug/FundTransfer"
require "spec"
require "caricature"

describe "When fund is transferred" do

	Conversion_Rate = 1.029
	Transfer_Amount = 10.0
	Initial_Balance = 100.0

	before(:all) do
	
		@from_account = FundTransfer::Account.new("USD", Initial_Balance)
		@to_account = FundTransfer::Account.new("CAD", Initial_Balance)
	
		currency_conversion_service = Caricature::Isolation.for(FundTransfer::ICurrencyConversionService)
		currency_conversion_service.when_receiving(:get_Conversion_Rate).with(:any, :any).return(Conversion_Rate)

		fund_transfer_service = FundTransfer::FundTransferService.new(currency_conversion_service)
				
		fund_transfer_service.transfer(@from_account, @to_account, Transfer_Amount)
		
	end
	
	it "should decrease from account balance" do
	
		@from_account.balance.should be < Initial_Balance
		
	end
	
	it "should increase to account balance" do
	
		@to_account.balance.should be > Initial_Balance
		
	end
		
end

I think the above code is self explanatory, treat the require(line 1- 4) statements as the add reference of our visual studio projects, we are adding all the required libraries with this statement. Next, the describe which is a RSpec keyword. The before does exactly the same as NUnit's Setup or MsTest’s TestInitialize attribute, but in the above we are using before(:all) which acts as ClassInitialize of MsTest, that means it will be executed only once before all the test methods. In the before(:all) we are first instantiating the from and to accounts, it is same as creating with the full name (including namespace)  like fromAccount = new FundTransfer.Account(.., ..), next, we are creating a mock object of ICurrencyConversionService, check that for creating the mock we are not using the Moq like the MSpec version. This is somewhat an interesting issue of IronRuby or maybe the DLR, it seems that it is not possible to use the lambda expression that most of the mocking tools uses in arrange phase in Iron Ruby, like:

currencyConvertionService.Setup(ccv => ccv.GetConvertionRate(Moq.It.IsAny<string>(), Moq.It.IsAny<string>())).Returns(ConvertionRate);

But the good news is, there is already an excellent mocking tool called Caricature written completely in IronRuby which we can use to mock the .NET classes. May be all the mocking tool providers should give some thought to add the support for the DLR, so that we can use the tool that we are already familiar with. I think the rest of the code is too simple, so I am skipping the explanation.

Now, the last thing, how we are going to run it with RSpec, lets first install the required gems. Open you command prompt and type the following:

igem sources -a http://gems.github.com

This will add the GitHub as gem source.

Next type:
igem install uuidtools caricature rspec

and at last we have to create a batch file so that we can execute it in the Notepad++, create a batch like in the IronRuby bin directory like my previous post and put the following in that batch file:

@echo off
cls
call spec %1 --format specdoc
pause

Next, add a run menu and shortcut in the Notepad++ like my previous post. Now, when we run it, it will show the following output:

When fund is transferred
- should decrease from account balance
- should increase to account balance

Finished in 0.332042 seconds

2 examples, 0 failures
Press any key to continue . . .

You will get the complete code of this post in the following link.

That's it for today.

Download: RSpecIntegration.zip

Shout it

Recently I have decided to learn Ruby and for last few days I am playing with IronRuby. Learning a new thing is always been a fun and when it comes to adorable language like Ruby it becomes more entertaining.

Like any other language, first we have to create the development environment. In order to run IronRuby we have to download the binaries form the IronRuby CodePlex project. IronRuby supports both .NET 2.0 and .NET 4, but .NET 4 is the recommended version, you can download either the installation or the zip file. If you download the zip file make sure you added the bin directory in the environment path variable. Once you are done, open up the command prompt and type :

ir –v

It should print message like:

IronRuby 1.0.0.0 on .NET 4.0.30319.1

The ir is 32bit version of IronRuby, if you want to use 64bit you can try ir64.

Next, we have to find a editor where we can write our Ruby code as there is currently no integration story of IronRuby with Visual Studio like its twin Iron Python. Among the free IDEs only SharpDevelop has the IronRuby support but it does not have auto complete or debugging built into it, only thing that it supports is the syntax highlighting, so using a text editor which has the same features is nothing different comparing to it. To play with the IronRuby we will be using Notepad++, which can be downloaded from its sourceforge download page. The Notepad++ does have a nice syntax highlighting support :

npplusplus-ruby

I am using the Vibrant Ink with some little modification.

The next thing we have to do is configure the Notepad++ that we can run the Ruby script in IronRuby inside the Notepad++. Lets create a batch(.bat) file in the IronRuby bin directory, which will have the following content:

@echo off 
cls
call ir %1
pause

This will make sure that the console will be paused once we run the script.

Now click Run->Run in the Notepad++, it will bring up the run dialog and put the following command in the textbox (The riir.bat is the batch file which we have saved in the above):

riir.bat "$(FULL_CURRENT_PATH)"

npplusplus-ruby-run

Click the save which will bring another dialog.

Type Iron Ruby and assign the shortcut to ctrl + f5 (Same as Visual Studio Start without Debugging) and click ok.

npplusplus-ruby-shortcut

Once you are done you will find the IronRuby in the Run menu. Now press ctrl + f5, we will find the ruby script running in the IronRuby.

npplusplus-ruby-output

Now there are one last thing that we would like to add which is poor man’s context sensitive help. First, download the ruby language help file from the Ruby Installer site and extract into a directory. Next we will have to install the Language Help Plug-in of Notepad++, click Plugins->Plugin Manger –>Show Plugin Manager and scroll down until you find the plug-in the list, now check the plug-in and click install. Once it is installed it will prompt you to restart the Notepad++, click yes.

npplusplus-ruby-plugin

When the Notepad++ restarts, click the Plugins –> Language Help –> Options –> add and enter the following details and click ok:

npplusplus-ruby-help

The chm file location can be different depending upon where you extracted it.

Now when you put your in any of ruby keyword and press ctrl + f1 it will take you to the help topic of that keyword.

For example, when my caret is in the each of the following code and I press ctrl + f1, it will take me to the each api doc of Array.

def loop_demo
	(1..10).each{ |n| puts n}
end

loop_demo

That’s it for today.

Happy Ruby coding.

Shout it

When using the DisplayFor/EditorFor there has been built-in support in ASP.NET MVC to show localized validation messages, but no support to show the associate label in localized text, unless you are using the .NET 4.0 with Mvc Future. Lets a say you are creating a create form for Product where you have support both English and German like the following.

English

create-en

German

create-de

I have recently added few helpers for localization in the MvcExtensions, lets see how we can use it to localize the form. As mentioned in the past that I am not a big fan when it comes to decorate class with attributes which is the recommended way in ASP.NET MVC. Instead, we will use the fluent configuration (Similar to FluentNHibernate or EF CodeFirst) of MvcExtensions to configure our View Models. For example for the above we will using:

public class ProductEditModelConfiguration : ModelMetadataConfiguration<ProductEditModel>
{
    public ProductEditModelConfiguration()
    {
        Configure(model => model.Id).Hide();

        Configure(model => model.Name).DisplayName(() => LocalizedTexts.Name)
                                      .Required(() => LocalizedTexts.NameCannotBeBlank)
                                      .MaximumLength(64, () => LocalizedTexts.NameCannotBeMoreThanSixtyFourCharacters);

        Configure(model => model.Category).DisplayName(() => LocalizedTexts.Category)
                                          .Required(() => LocalizedTexts.CategoryMustBeSelected)
                                          .AsDropDownList("categories", () => LocalizedTexts.SelectCategory);

        Configure(model => model.Supplier).DisplayName(() => LocalizedTexts.Supplier)
                                          .Required(() => LocalizedTexts.SupplierMustBeSelected)
                                          .AsListBox("suppliers");

        Configure(model => model.Price).DisplayName(() => LocalizedTexts.Price)
                                       .FormatAsCurrency()
                                       .Required(() => LocalizedTexts.PriceCannotBeBlank)
                                       .Range(10.00m, 1000.00m, () => LocalizedTexts.PriceMustBeBetweenTenToThousand);
    }
}

As you can we are using Func<string> to set the localized text, this is just an overload with the regular string method. There are few more methods in the ModelMetadata which accepts this Func<string> where localization can applied like Description, Watermark, ShortDisplayName etc. The LocalizedTexts is just a regular resource, we have both English and German:

mvc-localization-se

 

Now lets see the view markup:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Demo.Web.ProductEditModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    <%= LocalizedTexts.Create %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%= LocalizedTexts.Create %></h2>
    <%= Html.ValidationSummary(false, LocalizedTexts.CreateValidationSummary)%>
    <% Html.EnableClientValidation(); %>
    <% using (Html.BeginForm()) {%>
        <fieldset>
            <%= Html.EditorForModel() %>
            <p>
                <input type="submit" value="<%= LocalizedTexts.Create %>" />
            </p>
        </fieldset>
    <% } %>
    <div>
        <%= Html.ActionLink(LocalizedTexts.BackToList, "Index")%>
    </div>
</asp:Content>

As we can see that we are using the same LocalizedTexts for the other parts of the view which is not included in the ModelMetadata like the Page title, button text etc. We are also using EditorForModel instead of EditorFor for individual field and both are supported.

One of the added benefit of the fluent syntax based configuration is that we will get full compile type checking for our resource as we are not depending upon the string based resource name like the ASP.NET MVC.

You will find the complete localized CRUD example in the MvcExtensions sample folder [Available in the Trunk].

That’s it for today.

Shout it

One of the thing that people often complains is dependency injection in Action Filters. Since the standard way of applying action filters is to either decorate the Controller or the Action methods, there is no way you can inject dependencies in the action filter constructors. There are quite a few posts on this subject, which shows the property injection with a custom action invoker, but all of them suffers from the same small bug (you will find the BuildUp is called more than once if the filter implements multiple interface e.g. both IActionFilter and IResultFilter). The MvcExtensions supports both property injection as well as fluent filter configuration api. There are a number of benefits of this fluent filter configuration api over the regular attribute based filter decoration.

You can pass your dependencies in the constructor rather than property. Lets say, you want to create an action filter which will update the User Last Activity Date, you can create a filter like the following:

public class UpdateUserLastActivityAttribute : FilterAttribute, IResultFilter
{
    public UpdateUserLastActivityAttribute(IUserService userService)
    {
        Check.Argument.IsNotNull(userService, "userService");

        UserService = userService;
    }

    public IUserService UserService
    {
        get;
        private set;
    }

    public void OnResultExecuting(ResultExecutingContext filterContext)
    {
        // Do nothing, just sleep.
    }

    public void OnResultExecuted(ResultExecutedContext filterContext)
    {
        Check.Argument.IsNotNull(filterContext, "filterContext");

        string userName = filterContext.HttpContext.User.Identity.IsAuthenticated ?
                          filterContext.HttpContext.User.Identity.Name :
                          null;

        if (!string.IsNullOrEmpty(userName))
        {
            UserService.UpdateLastActivity(userName);
        }
    }
}

As you can see, it is nothing different than a regular filter except that we are passing the dependency in the constructor. Next, we have to configure this filter for which Controller/Action methods will execute:

public class ConfigureFilters : ConfigureFiltersBase
{
    protected override void Configure(IFilterRegistry registry)
    {
        registry.Register<HomeController, UpdateUserLastActivityAttribute>();
    }
}

You can register more than one filter for the same Controller/Action Methods:

registry.Register<HomeController, UpdateUserLastActivityAttribute, CompressAttribute>();

You can register the filters for a specific Action method instead of the whole controller:

registry.Register<HomeController, UpdateUserLastActivityAttribute, CompressAttribute>(c => c.Index());

You can even set various properties of the filter:

registry.Register<ControlPanelController, CustomAuthorizeAttribute>( attribute => { attribute.AllowedRole = Role.Administrator; });

The Fluent Filter registration also reduces the number of base controllers in your application. It is very common that we create a base controller and decorate it with action filters and then we create concrete controller(s) so that the base controllers action filters are also executed in the concrete controller. You can do the  same with a single line statement with the fluent filter registration:

Registering the Filters for All Controllers:

registry.Register<ElmahHandleErrorAttribute>(new TypeCatalogBuilder().Add(GetType().Assembly).Include(type => typeof(Controller).IsAssignableFrom(type)));

Registering Filters for selected Controllers:

registry.Register<ElmahHandleErrorAttribute>(new TypeCatalogBuilder().Add(GetType().Assembly).Include(type => typeof(Controller).IsAssignableFrom(type) && (type.Name.StartsWith("Home") || type.Name.StartsWith("Post"))));

You can also use the built-in filters in the fluent registration, for example:

registry.Register<HomeController, OutputCacheAttribute>(attribute => { attribute.Duration = 60; });

With the fluent filter configuration you can even apply filters to controllers that source code is not available to you (may be the controller is a part of a third party component).

That’s it for today, in the next post we will discuss about the Model binding support in MvcExtensions. So stay tuned.

Shout it
More Posts Next page »