Code Kata – Numbers to Words, using javascript and TDD

This code kata is based on the KataNumbersInWords kata – convert numbers into words:

  • 3: “three”
  • 159: “one hundred and fifty-nine”
  • 2400: “two thousand and four hundred”

Katas are a good way to learn/practice TDD and/or learn more about a particular technology – in my particular case I decided to to this kata to improve my javascript skills.

The purpose of the exercise is to write the code incrementally, one step at a time – starting with the simplest case scenario (1 digit numbers) and then adding the code to handle the other scenarios (2 digit numbers, 3 digit numbers, etc).

Assume that the input is valid (number).

Test cases:

  • 1 digit numbers
    • 0 should return “zero”
    • 3 should return “three”
    • 7 should return “seven”
  • 2 digit numbers
    • 10 should return “ten”
    • 14 should return “fourteen”
    • 20 should return “twenty”
    • 21 should return “twenty-one”
    • 56 should return “fifty-six”
  • 3 digit numbers
    • 209 should return “two hundred and nine”
    • 300 should return “three hundred”
    • 417 should return “four hundred and seventeen”
    • 560 should return “five hundred and sixty”
    • 698 should return “six hundred and ninety-eight”
  • 4 digit numbers
    • 3004 should return “three thousand and four”
    • 4000 should return “four thousand”
    • 5020 should return “five thousand and twenty”
    • 6300 should return “six thousand and three hundred”
    • 7111 should return “seven thousand and one hundred and eleven”
  • 5 digit numbers
    • 40000 should return “forty thousand”
    • 70393 should return “seventy thousand and three hundred and ninety-three”
    • 87654 should return “eighty-seven thousand and six hundred and fifty-four”
  • 6 digit numbers
    • 500000 should return “five hundred thousand”
    • 803308 should return “eight hundred and three thousand and three hundred and eight”
    • 999999 should return “nine hundred and ninety-nine thousand and nine hundred and ninety-nine”

You can see my implementation here (I’m using WebStorm v8 and JS Test Driver):

Download the code (WebStorm project): NumbersToWords.zip

ASP.NET: Downloading files from a UNC share

Implementing a cross-browser solution for downloading files from a UNC share in ASP.NET applications

Table of contents

The scenario

I was working recently in an intranet application that had a download page. The output HTML was similar to the following:

<li><a class="download" href="\\MYSERVER\reports 2011\report1.zip" title="Report 1">Report 1</a></li>
<li><a class="download" href="\\MYSERVER\reports 2011\report2.zip" title="Report 2">Report 2</a></li>
<li><a class="download" href="\\MYSERVER\reports 2011\report 3.zip" title="Report 3">Report 3</a></li>
<li><a class="download" href="\\MYSERVER\reports 2011\report 4.zip" title="Report 4">Report 4</a></li>
<li><a class="download" href="\\MYSERVER\reports 2011\report 5&6.zip" title="Report 5&6">Report 5&6</a></li>

This was working fine in IE9, but not in other browsers. There was no action using Google Chrome, and using Firefox there was an error (HTTP Error 400 – Bad Request).

I tried to convert the file path to a file URI but it didn’t fix it. It continued to work on IE only.

<li><a class="download" href="file://MYSERVER/reports 2011/report1.zip" title="Report 1">Report 1</a></li>
<li><a class="download" href="file://MYSERVER/reports 2011/report2.zip" title="Report 2">Report 2</a></li>
<li><a class="download" href="file://MYSERVER/reports 2011/report 3.zip" title="Report 3">Report 3</a></li>
<li><a class="download" href="file://MYSERVER/reports 2011/report 4.zip" title="Report 4">Report 4</a></li>
<li><a class="download" href="file://MYSERVER/reports 2011/report 5&6.zip" title="Report 5&6">Report 5&6</a></li>

The solution was to create a custom ASP.NET download page. I used also jquery on the client side.

Step 1: Using jquery on the client side

The first step was to add an event handler to the download links. The request URI is encoded and is sent as a parameter to the download page. Creating an hidden iframe and setting the src attribute with the download link allows the file to be downloaded asynchronously.

$("a.download").bind("click", function (e) {
    e.preventDefault();
    var requestedFile = encodeURIComponent($(this).attr('href'));

    var iframe = document.createElement("iframe");
    iframe.src = 'Download.aspx?file=' + requestedFile;
    iframe.style.display = "none";
    document.body.appendChild(iframe); // triggers download page
});

Make sure you use encodeURIComponent function to encode special characters in the filename.

Step 2: Create an ASP.NET download page

This is the source code of the download page:

protected void Page_Load(object sender, EventArgs e)
{
    try
    {
        string requestFile = Request.QueryString["file"];

        if(string.IsNullOrEmpty(requestFile))
        {
            throw new FileNotFoundException("File to download cannot be null or empty");
        }

        // Get file name from URI string in C#
        // http://stackoverflow.com/a/1105614
        var uri = new Uri(requestFile);
        string filename = Path.GetFullPath(uri.LocalPath);
        var fileInfo = new FileInfo(filename);

        if(!fileInfo.Exists)
        {
            throw new FileNotFoundException("File to download was not found", filename);
        }


        // get content type based on file extension. Example:
		// http://stackoverflow.com/a/691599
        Response.ContentType = GetContentType(fileInfo.Extension);

        Response.AddHeader("Content-Disposition", 
                           "attachment; filename=\"" + fileInfo.Name + "\"");
        Response.WriteFile(fileInfo.FullName);
        Response.End();
    }
    catch(ThreadAbortException)
    {
        // ignore exception
    }
    catch(FileNotFoundException ex)
    {
        Response.StatusCode = (int) System.Net.HttpStatusCode.NotFound;
        Response.StatusDescription = ex.Message;
    }
    catch(Exception ex)
    {
        Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError;
        Response.StatusDescription = string.Format("Error downloading file: {0}", ex.Message);
    }
}

Some notes:

This is necessary in order to make the download work with a UNC share (\\MYSERVER\….) or a file URI (file://….)

    var uri = new Uri(requestFile);
    string filename = Path.GetFullPath(uri.LocalPath);

To avoid filename truncating, it’s necessary to wrap the filename with quotes

    Response.AddHeader("Content-Disposition", 
                       "attachment; filename=\"" + fileInfo.Name + "\"");

References

Technorati Tags: , , ,