Tuesday, March 30, 2010

Number of TFS checkins

I was trying to work out how many times I have checked code into TFS for each project that I've worked on in a given date range. I still haven't managed to work out how to break it down by project but did get the total count.

  • Ran Team Foundation Sidekicks.
  • On History tab select my User name in the drop down.
  • Clicked the "Save list to file" button.

Problem with this CSV list is that I have line breaks in many of the comments so it's not a true CSV file.

  • Using Notepad++
  • Ctrl+F for search and change the Search Mode to Regular Expression.
  • Enter "^\d+," without quotes in the Find what field.
  • Click the "Find all in Current Document" or the "Count" button to get the number of lines with that pattern.

That will be the number of items that you checked in.

The regex ^\d+, can be roughly interpreted as find all lines starting with (^) a string of one or more numbers (\d+) and ending with a comma (,).

This is probably one of the most unwieldy and ugliest solutions to a problem I have recently come up with. Also, I haven't managed to work out how to breakdown check-ins by project. Any ideas?

Wednesday, March 24, 2010

Mercurial Commit Push Pull Update

I'm just starting to wrap my head around Mercurial and feel that this is the best diagram that I've seen that explains the push/pull commit/update model.
 To get or retrieve changes from your working directory to the "server" directory is always a two step process.
To "check-in" you will commit your changes to your local repository and then push them to the server.
To "get latest" you will pull from the server and then update to your working directory.

 

Source: http://www.ivy.fr/mercurial/ref/v1.0/Mercurial-QuickStart-v1.0-300dpi.png

Wednesday, March 17, 2010

Make Better Software: The Training Series

The company that I work for recently bought my team the Make Better Software: The Training Series done by Joel Spolsky at Fog Creek Software. This is my report on this course.

I've listened to all the Stack Overflow podcasts with Jeff Atwood and Joel Spolsky and so I now "get" that Joel is only pretending to be arrogant and his manner is intended to provoke debate. He does it well.

The six one-hour videos in the course are very well done. They were easy to watch and packed with good content and extremely well edited. Every now and then you felt like it was teetering on the edge of a sales pitch for either the product that they sell or for candidates to apply for a job at their company but considering that it was all about working at Fog Creek and creating Fog Bugz it would have been very difficult to not do this now and then.

The manual (Student Guide) that comes with the videos was not as well prepared as the videos. It is essentially a collection of Joel's blog post over the last 10 years divided into six sections. I found at least two blog posts that were repeated verbatim in two different sections and there were paragraphs that were repeated and some of the blogs seemed to have text missing off the end. This was slightly unprofessional and a bit irksome. I'm not sure if anybody proofread or edited the compilation of these.

In the table of contents he has a "chapter" titled "Incentive Pay Considered Harful", italics and bold are mine. I've heard Joel criticize candidates for submitting resumes with spelling mistakes in them with comments like "there's no excuse for that, don't they know how to use a spell checker."

Despite my criticism of the spelling and lack-of-proof-reading I consider the content to be good. It duplicates what is said in the video and in some parts goes a bit deeper and has the names and references that you might miss when watching the video of that section.

The course starts off with The Joel Test. We score fairly high on the Joel test with a 10. We don't do point 3 (daily builds) but instead we build on every check-in which is more rigorous than he is proposing.

After the Joel Test the course is divided into 6 modules:

  1. Recruiting
  2. Team Members
  3. Environment
  4. Schedules
  5. Lifecycle
  6. Design of Software

From a management point of view all modules are relevant but more so modules 1 to 4. I felt that Program Managers were the main focus in the Team Members section but all members were covered and some myths dispelled.

Fog Creek appear to have the best environment for almost anybody to work in let alone developers. It's interesting that the offices given to developers are not only to increase productivity but are a huge sales tool in recruiting talent.

My point of view is that of a developer. As such I found the second half of the course more interesting. Joel did an excellent job of convincing me about the value in specifications and I no longer feel that they are a waste of time but rather something that needs to be continuously worked on while a project is alive.

Would I recommend this course? Absolutely.

 

Tuesday, March 2, 2010

LINQ secondary sort using extension method syntax

You have a list of objects that you want to sort using LINQ. You want to sort by a primary and secondary key from the fields (properties or members) of the object. How do you do this?

The answer is to do two sorts. The first by the secondary key and the second by the primary key. Here's a little test console app that you can run to verify.

class Widget
{
    public int First { get; set; }
    public int Second { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        List<Widget> widgets = new List<Widget>();
        widgets.Add(new Widget { First = 1, Second = 6 });
        widgets.Add(new Widget { First = 2, Second = 6 });
        widgets.Add(new Widget { First = 3, Second = 5 });
        widgets.Add(new Widget { First = 4, Second = 5 });
        widgets.Add(new Widget { First = 5, Second = 4 });
        widgets.Add(new Widget { First = 6, Second = 4 });

        var firstSort = widgets.
          OrderByDescending(a => a.First).
            OrderBy(a => a.Second);
        foreach (Widget widget in firstSort)
        {
            Console.WriteLine("First {0} Second {1}",
                widget.First, widget.Second);
        }

        Console.ReadLine();
    }
}

The results of this run are:

First 6 Second 4
First 5 Second 4
First 4 Second 5
First 3 Second 5
First 2 Second 6
First 1 Second 6
Hit return to continue...

So we initially reverse sort by the First property, this will be the secondary sort. We then sort by the Second property, this will be the primary sort.

Wednesday, February 10, 2010

HTML Submit button still submits even when JavaScript returns false

Here's something that's caught me out a couple of times in the past so I'm documenting it to remind myself.

My web page has a submit <input> tag that looks like this:

<input type="submit" value="Create" />

I want to do some client side validation before I POST the page back to the server so I modify it to call my client side JavaScript function by adding the onclick attribute:

<input type="submit" value="Create" onclick="SubmitCreate();" />

The JavaScript does a bit of validation:

function SubmitCreate() {
    var selectedNodes = tree.getSelectedNodes();
    if (selectedNodes.length < 1) {
        alert('Please select at least one node.');
        return false;
    }
}

The problem is that even though the SubmitCreate() function is returning false this is not preventing the page from being submitted.

The reason, when shown, is obvious. The onclick attribute should return the JavaScript and not just call it.

<input type="submit" value="Create" onclick="return SubmitCreate();" />


 

Friday, February 5, 2010

Viewing connections to a server

I keep on forgetting how to do this so here it is documented for my convenience.

Start Performance Monitor (PerfMon)

Click + to add new monitor.

Enter computer's name

To view connections for a web service

From Performance object drop down select "Web Service"

From Select counters from list drop down select "Current Connections"

From Select instances from list select "_Total"

To view connections for a web app

From Performance object drop down select "ASP.NET Apps v2.0.50727"

Tuesday, February 2, 2010

Combine, compress, and update your CSS file in ASP.NET MVC

After doing some analysis using Google's web master tools I discovered that I needed to make some improvements to how the CSS file(s) on a busy site were being delivered. This post follows on from and improves on Automatically keeping CSS file current on any web page.

  • The CSS files need to be combined into a single file.
  • It needs to be compressed.
  • It needs to be cached, but only until it's changed.
  • Image references need to be dynamic.

I'm using ASP.NET MVC so the first thing that I did was to add an Action to a Controller that was to be the new CSS file. Instead of referencing a physical file on disk the CSS file has now become a resource that follows this pattern:

~/Site/Css/20100201105959.css

Site is the controller, Css is the action, and 20100201105959.css is the single parameter that this action accepts.

This is what the SiteController class looks like:

[CompressFilter]
public class SiteController : Controller
{
    static ContentResult cr = null;

    [CacheFilter(Duration=9999999)]
    public ActionResult CSS(string fileName)
    {
        try
        {
            if (cr == null)
            {
                StringBuilder sb = new StringBuilder();
                foreach (string cssFile in Constants.cssFiles)
                {
                    string file = Request.PhysicalApplicationPath + cssFile;
                    sb.Append(System.IO.File.ReadAllText(file));
                    sb.Append("!!!!!");
                }

                sb.Replace("[IMAGE_URL]", StaticData.Instance.ImageUrl);

                cr = new ContentResult();
                cr.Content = sb.ToString();
                cr.ContentType = "text/css";
            }

            return cr;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Trace.TraceError(ex.Message);
            return View();
        }
    }
}

Notice that we have a couple of filters on the class and method. The [CompressFilter] on the class will check the browser's capability for gzip or deflate and activate compression for any action/method in this class. The [CacheFilter] will add a 3 month "cache this file" direction to the HTTP header of the HTML that's returned.

The Constants.cssFiles returns a list of root-relative CSS files that need to be combined into the single response.

The Replace() function on the StringBuilder allows us to put a tag in the CSS files so that any references to images can be dynamically determined at runtime. This allows us to run the images off a url such as http://localhost:1234/images/ during development and http://images.mysite.com/ in production. Storing images in a subdomain instead of the main domain will improve performance because more images will be able to be downloaded in parallel by the browser.

The single fileName parameter is a dummy parameter that is needed to make the URL different when one of the underlying CSS files is modified but still allow optimum caching in the browsers. i.e. by changing this value in the HTML we return we force the browser to re-fetch the CSS but only when one of the underlying CSS files change.

The ContentResult is a static variable so that the building of this CSS file is a single hit after the App Pool has been recycled.

The following snippet of code is placed in the code behind page for the Site.Master. Normally you would not have a code behind page in the MVC model but I have not been able to work out how to dynamically inject the CSS file name into the master file any other way.

protected void Page_Load(object sender, EventArgs e)
{
    HtmlLink css = new HtmlLink();
    css.Href = String.Format("/Site/CSS/{0}", StaticData.Instance.CssFileName);
    css.Attributes["rel"] = "stylesheet";
    css.Attributes["type"] = "text/css";
    css.Attributes["media"] = "all";
    Page.Header.Controls.Add(css);
}

The CSS file name is generated with the following snippet of code:

string _CssFileName = null;public string CssFileName
{
    get
    {
        if (String.IsNullOrEmpty(_CssFileName))
        {
            DateTime dt = new DateTime(2000,1,1);
            foreach (string cssFile in Constants.cssFiles)
            {
                string file = System.Web.HttpContext.Current.Server.MapPath(cssFile);
                FileInfo fi = new FileInfo(file);
                DateTime lastWriteTime = fi.LastWriteTime;
                if (lastWriteTime > dt)
                {
                    dt = lastWriteTime;
                }
            }
            _CssFileName = dt.ToString("yyyyMMddHHmmss") + ".css";
        }
        return _CssFileName;
    }
}

We iterate through each of the CSS files and extract the most recent modified date. Using this date, we generate the the CSS file name. That way a different file name will be injected into the HTML whenever one of the CSS files is modified and this will force the browser to reload the new CSS file keeping it always up-to-date.

It may seem like a lot of work for a CSS file at first glance but the benefits are enormous and the added flexibility will allow you to change it on a whim.

I haven't shown you the Constants.cssFiles but that's just an array of file names. I was also thinking of implementing this as a loop that found all the *.css files in a folder. That way if you added a new CSS file it would automatically be included without a code change. However, the disadvantage of this is that you cannot predetermine the order in which the CSS files are combined into the single file and the order is usually important.

If, however, you did want to use that all-css-in-one-folder approach you could adopt a naming convention such as 01_myfile.css, 02_myfile.css and then sort the file names before combining them.