Archives

Archives / 2008 / November
  • Edmonton Code Camp bound this Saturday

    What do 3 geeks do at 4AM in the parking lot of an IKEA in Calgary on a Saturday morning? Plot to overthrow Future Shop in the hopes of obtaining a rare shipment of WiiFits? Maybe. Test the cold-tolerance level of Dell laptops using Canadian Tire car batteries as their only source of power? Could be. Make cheap jokes about doing drag and drop presentations then have t-shirts made up to mock Microsoft employees?

    Nah, they get together to drive to Edmonton and attend the Edmonton Code Camp silly.

    That's what me and 2 of my very tired friends will be doing this Saturday (note, Tim Hortons will be our cuddly buddies come Saturday) as we head up to Edmonton, home of a hockey team that leaves their players quivering on the ice, questioning their sexuality.

    I'll be presenting two fun-filled-uber-cool-extra-special-director-cut-edition-limited-time-only-before-they-go-back-into-the-vault sessions:

    How to Win Friends and Influence People on Facebook, in .NET
    Yes, building Facebook apps isn't only restricted to PHP and procedural code. We'll go through building apps using the newly released 2.0 Facebook Toolkit for .NET and talk about restrictions that social networking sites put on how much information you can (and cannot) farm out of their API. And yeah, we'll build some cool Facebook games too (in .NET).

    Building Really Cool Apps with WPF
    We'll do some cool stuff. It's a code camp. Bring yer laptops and fire up Visual Studio and we'll make XAML stand on it's head and do somersaults over any ORM anyday. jQuery? Bah. This is WPF baby and it your user experience doesn't get any richer than this.

    My sessions are deemed a PowerPoint Free Zone to help make the planet a little nicer place to live in. Rock on big Al.

    See you there!

  • Visual Debian Installation Walkthrough using Virtual PC

    I had to work on some code inside of a “real” nix system recently so I though I would give everyone a visual walkthrough of setting up the operating system. I need to pave a new image so I figured I would just share with the rest of the class setting up a *nix system from scratch using Virtual PC. You can accomplish the same with VMWare, I just happen to be using Virtual PC for this.

    For the OS I chose Debian. Actually I have several unix images for this type of work (Linux, NetBSD, etc.) but I’ve always liked Debian. It’s a slick text based install and works quite well, right down to the part of being able to select only what I need. I found the other Unixes to be cumbersome getting setup sometimes and not very visually appealing (installing NetBSD is like watching Justice Gray do his hair).

    So here’s the visual walkthrough of creating your own Debian install using Virtual PC. This install was done with Debian 4.0R5.

    Pre-Install

    You’ll need a couple of things to get started. First your Virtual PC (or VMWare if you choose) to run the guest operating system. You’ll also need an ISO image of Debian. I used the 180mb netinst ISO image file to start. It’s larger than the 40mb version but the 40mb version doesn’t even include the base system so I saved the download by getting the larger image. You can get the image from this page here. Choose the i386 ISO from the first list of options on the page.

    Once you’ve got the ISO ready you can setup your Virtual PC image and start. Here we go.

    The Walkthrough

    From Virtual PC, select the “New Virtual Machine Wizard” from the File Menu:

    image 

    Click Next

    image

    Select “Create a virtual machine” from the options and click Next:

    image

    Give the virtual machine a name and optionally choose a location. I keep all my VMs on a portable USB drive:

    image

    Leave the operating system as “Other” and click Next:

    image

    You can adjust the ram if desired. I’m doing console development so 128mb of RAM is fine. If you want to install X-Windows or something then you might want to bump this up. It can be changed later so you can leave it for now and click Next:

    image

    Change the option to create a new virtual hard disk for you and click Next:

    image

    By default the new virtual hard disk is created where your virtual machine is created. This is fine and you can leave the default then click Next:

    image

    The confirmation screen will show you the options. Click Finish to create the new virtual machine. One more setting that you may need to change. Select the network adapter for the virtual machine to bridge to your host adapter. This allows Debian to obtain and IP and access the internet to download modules.

    image

    Your Virtual PC Console will show you the new virtual machine ready to start. Select it and click Start:

    image

    The new VM will boot but you need to capture the ISO image you downloaded earlier. Select CD from the menu and select Capture ISO Image:

    image

    Now browse to where your netimage ISO is located and select it then click Open:

    image

    From the Action menu in Virtual PC select  Ctrl+Alt+Delete:

    image

    Debian will now boot. Press enter to continue:

    image

    A few hundred lines of gobbly-gook will flash by until you arrive at this screen. Pick your language and press enter:

    image

    Choose your country next and press enter:

    image 

    Next choose a keyboard layout to use:

    image

    After a few minutes of loading screens you’ll be asked to provide a hostname for the system. Enter one and click continue:

    image

    By default the next screen will probably grab your ISP domain name but you can change it to whatever you want as this is going to be a local machine for testing. Enter a name and click continue:

    image

    More hardware detection and the disk partitioning starts. As this is a VM you can just select the first option and let Debian guide you through using the entire disk. Select that option and press enter:

    image

    Debian will find the 16gb hard drive the VM is showing (but it’s not really taking up 16gb on disk, well, not yet anyways). Select it and press enter:

    image

    The next option is how you configure the partitions. For *most nix installs I choose the 3rd option and split up my /home, /usr, and /var directories. However that’s usually on systems with multiple disks and again this is a test system right? Pick the first option to put all the files in a single partition and press enter:

    image

    You’ll be given one final chance to back out. Go ahead and press enter to write those changes:

    image 

    Tee hee. Fooled ya. There’s one more confirmation. They *really don’t want you to destroy data (like I did years ago and lost the contents of a book I was writing, tell you about that over beers one day). Select Yes and go for it.

    image

    Partitions get formatted, the world is a better place and we’re onto the next step. Congratulations! You’ve reformatted your hard drive (well, your virtual one). Select a time zone so Debian knows when to move clocks around on you:

    image

    Next is the all important “root” password. This is a test system so choose the uber-secure password of “password”.

    image

    And you’ll be asked to enter it again:

    image

    Next you’re asked to setup a real user (rather than using root). While this is a test system, it’s like logging into SQL Server using “sa” and kind of feels dirty. Besides in the unix world you can actually accomplish things as regular users without being nagged by a UAP dialog and a simple “su” command let’s you become “root” again. Enter the name of your real user here and press enter:

    image

    Next you’re asked for an account for the user. Give it a simple name (no spaces) that’s easy to remember. For the hackers reading this, I always use “bsimser”:

    image

    Enter a password for your new user. Again, I chose to use “password” feeling that nobody would be able to guess it (I also considered “love”, “sex”, “secret” and “god” but thought was too over the top):

    image

    Debian now installs the base system. Go for a coffee. It’ll be a few minutes. Go on. I’ll wait.

    When you get back Debian will be asking you about mirrors. The netinst version has a base system, but it can (and should) leverage internet mirrors for files. It’ll do checks against the mirrors to get the latest versions and updated packages for you so it’s a good idea to say Yes to this option.

    image

    Once you’ve committed your soul to the installer, you’ll be asked to pick a mirror. Choose one in your own country to reduce the traffic sent around the world and press enter:

    image

    A list of mirror sites will come up based on your country choice (you did choose YOUR country right?). I’m not sure if the mirror list is sorted by access speed but whatever. Pick one as they’re all near you anyway. You might find a mirror doesn’t work during the install (nothing in life is guaranteed, including this walkthrough) so if you find it doesn’t work, Debian will bump you back to the mirror selection screen (this one). Pick another and try again. One of them is bound to work, eventually.

    image

    If you’re behind a firewall or proxy you need to let the package manager know this information in order for it to do your bidding. Enter it in the next screen:

    image

    Debian then downloads and configures the package selector. Next step is the dreaded “tasksel”. This is sort of Linux for Dummies where you tell it you want a desktop, or web server, or SQL server and it picks the packages for you. Frankly, I always avoid this step. I like to get a clean system up and running then decide what I need to install on it. Also, as this is a VM you can just copy your clean system and have lots of little VMs running around with different purposes. If you choose a configuration here you are on your own, I cannot follow you on that path. However I recommend deselecting everything here and pressing Continue. We can install packages later.

    image

    Next Debian will ask you if you want to install the GRUB boot loader. As this is the only OS on the system let GRUB do it’s thing and install to the master boot record.

    image

    That’s it for this part. Before you reboot, release the CD ISO image by choosing the option in the CD menu of Virtual PC. Now press Continue and hold your breath.

    image

    More gobbly-gook and you’ll be at your Debian login prompt. Login as your regular user you created previously:

    image

    If you’re really lonesome for that DOS feeling type ‘alias dir=”ls –l”’ at the command prompt after you login. You’ll be able to type “dir” now and it’ll look kind of like your old world.

    Now we have a bare-bones system. You don’t have an ftp client or a telnet client or any developer tools! Let’s fix that.

    All (most?) unix systems these days have some kind of packaging system that allows you to get all these extras. It’s like a big warehouse of software just waiting to install to your desktop. Debians is called APT. There’s an entire HOWTO here on it if you want to get all down and dirty with it. APT is your friend. Learn it, live it, love it.

    For example type this at the command prompt:

    su<ENTER>

    Enter your root password

    apt-get update<ENTER>

    You should see something like this:

    image

    The contents will vary as these are from my mirror but basically it just updates the catalog of “stuff” you can get. Now (while still impersonating the root account) type this:

    apt-get install ftp<ENTER>

    You’ll see a result similar to this:

    image

    Remeber when Trinity needed a pilot program for a military M-109 helicopter and in a few seconds Tank downloaded it to her? Well, you can now type in “ftp my.favorite.porn.site” and get there instantly (or as fast as your internet connection will take you). Cool huh?

    I prefer the APT command line but it takes some getting used to. There’s also a tool called “dselect”. While still running as root, type “dselect” and you’ll eventually get to a screen like this:

    image

    It let’s you pick a package to install, provides a description of it, and let’s you install it but just selecting it. Type it in using the apt tool or pick it using this one. Your choice.

    That’s pretty much it kids. By now you have a fully working Debian image you can clone. Use the APT tool to install your favorite tools and get to work and hope this lengthy blog post filler helped.

    Enjoy!

  • Transforming Tree Surgeon using the Adaptive Console Framework

    I'm a command line app junkie as I love working with it. I guess it's my DOS/Linux roots but I find things go faster when you're not dealing with a GUI and a mouse. Command line tools like NAnt and MSBuild have all sorts of options and syntax. Some of it discoverable, some of it not so much. NAnt for example will try to find a buildfile to run and execute it. It also will display the name and version of the app (which is useful in build logs so you know what's going on). There are other things like trying to find out how to run a command line tool. For example if you type "nant /?" you'll get this:

    NAnt 0.86 (Build 0.86.3075.0; nightly; 02/06/2008)
    Copyright (C) 2001-2008 Gerry Shaw
    http://nant.sourceforge.net

    Unknown argument '/?'

    Try 'nant -help' for more information

    Entering the proper syntax of "nant -help" displays this:

    NAnt 0.86 (Build 0.86.3075.0; nightly; 02/06/2008)
    Copyright (C) 2001-2008 Gerry Shaw
    http://nant.sourceforge.net

    NAnt comes with ABSOLUTELY NO WARRANTY.
    This is free software, and you are welcome to redistribute it under certain
    conditions set out by the GNU General Public License.  A copy of the license
    is available in the distribution package and from the NAnt web site.

    Usage : NAnt [options] <target> <target> ...
    Options :

      -t[argetframework]:<text>      Specifies the framework to target
      -defaultframework:<text>       Specifies the framework to target (Short format: /k)
      -buildfile:<text>              Use given buildfile (Short format: /f)
      -v[erbose][+|-]                Displays more information during build process
      -debug[+|-]                    Displays debug information during build process

      -q[uiet][+|-]                  Displays only error or warning messages during
    build process
      -e[macs][+|-]                  Produce logging information without adornments
      -find[+|-]                     Search parent directories for build file
      -indent:<number>               Indentation level of build output
      -D:<name>=<value>              Use value for given property
      -logger:<text>                 Use given type as logger
      -l[ogfile]:<filename>          Use value as name of log output file
      -listener:<text>               Add an instance of class as a project listener
      -ext[ension]:<text>            Load NAnt extensions from the specified assembly
      -projecthelp[+|-]              Prints project help information
      -nologo[+|-]                   Suppresses display of the logo banner
      -h[elp][+|-]                   Prints this message
      @<file>                        Insert command-line settings from a text file.

    A file ending in .build will be used if no buildfile is specified.

    A lot of options there but pretty standard fare for a console application. And a lot of work to parse the options, validate them, display help messages, etc. I had a link to this thing called the Adaptive Console Framework sitting in my Action folder in Outlook and finally got around to looking at it. It's a library by Sunny Chen that takes the pain of command line junk by doing most of the heavy lifiting for you.

    This is what the console framework provides for you. A nice, simple way of not having to write a lot of code to deal with complex command line options and something that gives you a few other benefits along the way like automatic help text generation and easy access to command line options. Notice that the syntax for displaying NAnt help was "nant -help" but it wouldn't allow variations like "nant /?" or "nant -?". The framework as we'll see let's us make it easy to just add variations to command line syntax without doing a lot of work.

    The framework is a little gem of a library that I didn't think much about before but now after spending an entire hour of my hard earned time I think it's pretty slick. Here's a transformation of the console version of Tree Surgeon.

    The old version of the Tree Surgeon console (betcha didn't even think there was one!) was a little boring and actually broken. If you ran it without any arguments you got this:

    TreeSurgeon version 1.1
    Copyright (C) 2007 - 2008 Bil Simser
    Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc

    Creates a .NET Development tree

    TreeSurgeon projectName

    Please note - project name must not contain spaces. We recommend you use CamelCase for project names.

    You could probably surmise you need to provide a project name at least. But what about those other options like version and what unit test framework to use? And frankly this is wrong since it's not version 1.1, this output was from the 2.0 version. Lots of little problems here.

    Here's the source for the command line runner:

    [STAThread]

    private static int Main(string[] args)

    {

        try

        {

            return RunApp(args);

        }

        catch (Exception e)

        {

            Console.WriteLine("Unhandled Exception thrown. Details follow: ");

            Console.WriteLine(e.Message);

            Console.WriteLine(e.StackTrace);

            return -1;

        }

    }

    And here's the RunApp method:

    private static int RunApp(string[] args)

    {

        Console.WriteLine("TreeSurgeon version 1.1");

        Console.WriteLine("Copyright (C) 2007 - 2008 Bil Simser");

        Console.WriteLine("Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc");

        Console.WriteLine();

        if (args.Length != 2)

        {

            Usage();

            return -1;

        }

        Console.WriteLine("Starting Tree Generation for " + args[0]);

        Console.WriteLine();

        string outputDirectory = new TreeSurgeonFrontEnd(

            Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), args[1]).

            GenerateDevelopmentTree(args[0], "NUnit");

        Console.WriteLine("Tree Generation complete. Files can be found at " + outputDirectory);

        return 0;

    }

    RunApp would output the logo and copyright info and give you the ugly Usage() message (which wasn't too useful) if you didn't pass in at least a project name. You could pass in a version to build (2003, 2005, or 2008) but the unit test framework was hard coded to NUnit. Like I said, not too useful.

    After taking a quick glance at what the Adaptive Console Framework (ACF) could do I decided to transform the Tree Surgeon console runner using it and see what we could get.

    The ACF basically has two steps to it (this is overly simplifying it but you'll see it's pretty easy). First you make a slight adjustment to your main console application method, then you get down and dirty by creating option contracts (via attributes, classes, and properties). This has a big bonus that I immediately saw which was to move the command line options into a separate assembly and class which meant I could test it without actualy having to run the application and secondly it would take care of most of the heavy lifting of dealing with command line syntax.

    So the first thing I did was to ditch that RunApp method and replace the call to have the ConsoleApplicationManager class from the ACF do my work. Here's the updated Main method from the Tree Surgeon console app:

    [STAThread]

    private static void Main(string[] args)

    {

        try

        {

            ConsoleApplicationManager.RunApplication(args);

        }

        catch (Exception e)

        {

            Console.WriteLine("Unhandled Exception thrown. Details follow:");

            Console.WriteLine(e.Message);

            Console.WriteLine(e.StackTrace);

        }

    }

    Next I created a new assembly (called TreeSurgeonConsoleApplication.dll) and added an app.config file to the console app so the ACF could find my option contracts and added a reference to the ACF assembly. Here's the newly added app.config file:

    <?xml version="1.0" encoding="utf-8" ?>

    <configuration>

      <configSections>

        <section name="AdaptiveConsole"

                 type="AdaptiveConsole.Config.AdaptiveConsoleConfigHandler, AdaptiveConsole"/>

      </configSections>

      <AdaptiveConsole provider="TreeSurgeonConsoleApplication.TreeSurgeon, TreeSurgeonConsoleApplication"

                       contractRepository="TreeSurgeonConsoleApplication"/>

    </configuration>

    The app.config file just tells the ACF two things. The name and location of my console provider and the assembly where to find the option contracts. That was all I had to do in my TreeSurgeonConsole project so after removing the reference to the Core project (where the actual Tree Generation would happen) I closed down the console app project. Thinking about it, with the app.config file you could really use a generic console application project for any console app since there's nothing specific in here anymore. Nice.

    The console provider is a class derived from ConsoleApplicationBase in the ACF and has two string overrides you provide, a logo and a description. Here's the TreeSurgeon class that we just specified in our app.config file:

    public class TreeSurgeon : ConsoleApplicationBase

    {

        public TreeSurgeon(string[] args) : base(args)

        {

        }

     

        protected override string Logo

        {

            get

            {

                var sb = new StringBuilder();

                sb.AppendFormat("TreeSurgeon version 2.0{0}", Environment.NewLine);

                sb.AppendFormat("Copyright (C) 2007 - 2008 Bil Simser{0}", Environment.NewLine);

                sb.Append("Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc.");

                return sb.ToString();

            }

        }

     

        protected override string Description

        {

            get { return "Creates a .NET development tree"; }

        }

    }

    We're emulating part of the old RunApp method here. When I run the console app now I get this:

    TreeSurgeon version 2.0
    Copyright (C) 2007 - 2008 Bil Simser
    Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc.

    Creates a .NET development tree

    Looks pretty much the same however like I said, I can now test the TreeSurgeon class (for example make sure the logo is set correctly because I might decide down the road to make the property a little more dynamic like fetching values using Reflection). I'm also not actually running anything yet so if I was building my app using TDD this fits nicely with that approach.

    That's it for this part of the conversion but like I said, I don't have it running my TreeSurgeonFrontEnd class yet or generating the development tree or verifying the command line or displaying help. That now comes with our options.

    With the ACF you define your command line options through something called "Option Contracts". There are four types in the ACF: None, Exact, Patternized, and Free. For Tree Surgeon I want the user to be able to run the application using these options:

    • Display help if the user enters nothing
    • Provide the name of the project to generate (required)
    • Provide an optional version of the system to generate (with a default)
    • Provide an optional unit test framework to use (with a default)

    We'll only look at the None Contract and the Patternized contract types.

    The None Contract is a class that you inherit from OptionContractBase. It will be executed if the user provides no command line arguments to the application. Create a class that derives from OptionContractBase in your contract assembly. Here's the None contract for Tree Surgeon:

    [OptionContract(

        Type = ContractType.None,

        Description = "Prints the help information on the screen.")]

    public class TreeSurgeonEmptyContract : OptionContractBase

    {

        public override void Execute(

            ConsoleApplicationBase consoleApplication,

            IList<ArgumentInfo> args)

        {

            consoleApplication.PrintHelpMessage();

        }

    }

    The class is decorated with an OptionContractAttribute that let's you specify the type of contract (None, Exact, Free, Patternized) and a description. Note we haven't done anything anywhere else in the system (the app.config file is done, the console Main method is done, and the ConsoleApplicationBase class is baked). All we're doing is adding a new class to the assembly we specified as our contractRepository in our app.config file.

    Here's the output of the app now when no arguments are passed to it:

    TreeSurgeon version 2.0
    Copyright (C) 2007 - 2008 Bil Simser
    Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc.

    Creates a .NET development tree


    > Calling the application without arguments
      Prints the help information on the screen.

    Sweet. Now let's start adding our options for actually running the app.

    We'll add a new class called TreeSurgeonCommandsContract (again derived from OptionContractBase). This time rather than specifying the type as "None" we'll use "Patternized". The Patternized type is a contract type where your console application requires a complex command line argument. You can define the options that are mandatory or not within the contract, you can define the options that carry a list of values and you can even define the switches in the patternized contracts. here's our TreeSurgeonCommandsContract class:

    [OptionContract(

        Type = ContractType.Patternized,

        Description = "Generates a new .NET development tree for a given project name.")]

    public class TreeSurgeonCommandsContract : OptionContractBase

    The main thing we need to capture is the project name that we want to generate the tree for. We'll do this by creating a property (called ProjectName) and decorating it with the OptionAttribute:

    [Option(

        Type = OptionType.SingleValue,

        Name = "/p;/project",

        Required = true,

        Description = "Specifies the project name.\r\n\t" +

                      "Please note - project name must not contain spaces.\r\n\t" +

                      "We recommend you use CamelCase for project names.")]

    public string ProjectName { get; set; }

    This tells the ACF that a) this option has a single value b) it's specified by either "/p:" or "/project:" and c) it's required. There's also a description we provide which will be displayed in our output that looks like this now:

    TreeSurgeon version 2.0
    Copyright (C) 2007 - 2008 Bil Simser
    Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc.

    Creates a .NET development tree

    TreeSurgeonConsole.exe </p|/project:>

    > Calling the application without arguments
      Prints the help information on the screen.

    > Generates a new .NET development tree for a given project name.
      /p|/project:value (required):
            Specifies the project name.
            Please note - project name must not contain spaces.
            We recommend you use CamelCase for project names.

    Notice that we now have the application name (TreeSurgeonConsole.exe) along with a required property. And the help is displayed for that property. Again, pretty damn simple so far. At this point we could actually implement the required Execute method on the TreeSurgeonCommandsContract class and call out to our TreeSurgeonFrontEnd, passing it the ProjectName property. We would generate a developement tree just like the original system and we're done. However we're only about 20 minutes into our conversion so we can do a lot more.

    First we'll add a property to specify the version of the development tree we want to generate. This is again just a string property in our TreeSurgeonCommandsContract class decorated with the OptionAttribute. We'll make this optional and provide a default value for it along with instructions:

    [Option(

        Type = OptionType.SingleValue,

        Name = "/v;/version",

        Required = false,

        Default = "2008",

        Description = "Specifies the Visual Studio version to generate.\r\n\t" +

                      "Valid options are: \"2003\", \"2005\", or \"2008\"\r\n\t" +

                      "Default is \"2008\"")]

    public string Version { get; set; }

    Then we'll do the same for our UnitTestFramework we want to specify (NUnit or MbUnit):

    [Option(

        Type = OptionType.SingleValue,

        Name = "/t;/test",

        Required = false,

        Default = "NUnit",

        CaseSensitive = true,

        Description = "Specifies the Unit Test framework to use when generating the tree.\r\n\t" +

              "Valid options are: \"NUnit\", or \"MbUnit\"\r\n\t" +

              "Default is \"NUnit\"")]

    public string UnitTestFramework { get; set; }

    Now we can run our app and see the help the ACF is providing:

    TreeSurgeon version 2.0
    Copyright (C) 2007 - 2008 Bil Simser
    Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc.

    Creates a .NET development tree

    TreeSurgeonConsole.exe </p|/project:> [/v|/version:] [/t|/test:]

    > Calling the application without arguments
      Prints the help information on the screen.

    > Generates a new .NET development tree for a given project name.
      /p|/project:value (required):
            Specifies the project name.
            Please note - project name must not contain spaces.
            We recommend you use CamelCase for project names.

      /v|/version:value :
            Specifies the Visual Studio version to generate.
            Valid options are: "2003", "2005", or "2008"
            Default is "2008"

      /t|/test:value :
            Specifies the Unit Test framework to use when generating the tree.
            Valid options are: "NUnit", or "MbUnit"
            Default is "NUnit"

    Lots of great stuff here and all we've done was specify some attributes around a few properties. What I really like are a few things we got for free:

    • Our required parameters are specified here and included in the help message
    • Optional parameters are surrounded by "[xxx]" in our command line syntax display
    • We're able to add varations to our command line options ("/t" or "/test") just by specifying the values in the OptionAttribute

    Now we'll actually implement the code to run our generator and use whatever values you pass along in the command line.

    To get the framework to do our bidding, we implement the Execute method in our TreeSurgeonCommandsContract class. This method passes in a copy of the ConsoleApplicationBase class (we specified above as TreeSurgeon) and an IList of ArgumentInfo values which were passed into the application. This is more than just a string so we can get information from our arguments like what type of argument they are.

    For Tree Surgeon, we need at least one option (the project name). We'll use a little LINQ to get the list of options from our passed in parameter and check to make sure that a) we have at least 1 option and b) we have a project name:

    var options = from arg in args

                where arg.Type == ArgumentType.Option

                select arg;

     

    if(options.Count() < 1 || string.IsNullOrEmpty(ProjectName))

    {

        consoleApplication.PrintHelpMessage();

        return;

    }

    Now that we've got a valid command line we'll reproduce what our old RunApp method did, namely invoke the TreeSurgeonFrontEnd class which will generate our development tree for us. We'll make it a little more interesting than version 1.1 and print out a little more information on what options we're using to generate the tree. Here's our Execute method so far:

    public override void Execute(ConsoleApplicationBase consoleApplication, IList<ArgumentInfo> args)

    {

        var options = from arg in args

                    where arg.Type == ArgumentType.Option

                    select arg;

     

        if(options.Count() < 1 || string.IsNullOrEmpty(ProjectName))

        {

            consoleApplication.PrintHelpMessage();

            return;

        }

     

        consoleApplication.PrintLogo();

     

        Console.WriteLine("Starting Tree Generation{0}", Environment.NewLine);

     

        Console.WriteLine("       Project Name: \"{0}\"", ProjectName);

        Console.WriteLine("            Version: \"{0}\"", Version);

        Console.WriteLine("Unit Test Framework: \"{0}\"", UnitTestFramework);

     

        Console.WriteLine();

     

        var frontEnd = new TreeSurgeonFrontEnd(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Version);

        var outputDirectory = frontEnd.GenerateDevelopmentTree(ProjectName, UnitTestFramework);

        Console.WriteLine("Tree Generation complete.{0}{0}Files can be found at:{0}\"{1}\"", Environment.NewLine, outputDirectory);

    }

    And here's the output using the command line "treesurgeonconsole.exe /p:test":

    TreeSurgeon version 2.0
    Copyright (C) 2007 - 2008 Bil Simser
    Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc.

    Creates a .NET development tree

    Starting Tree Generation

           Project Name: "test"
                Version: "2008"
    Unit Test Framework: "NUnit"

    Tree Generation complete.

    Files can be found at:
    "C:\Documents and Settings\simserb\My Documents\TreeSurgeon\test"

    Wait! We're only 45 minutes into our conversion and there's more features we can take on. Most apps let you turn off the silly logo/copyright info (usually with a "/nologo" switch). The ACF has a nice feature to specify switches on properties. You just add a boolean property to your class and decorate accordingly. Here's our "/nologo" switch:

    [Option(

        Type = OptionType.Switch,

        Name = "/nologo",

        Description = "When turned on, the logo and description\r\n\t" +

                      "information will not be displayed.")]

    public bool NoLogo { get; set; }

    Now that we have a bool property if the user adds "/nologo" to the command line we should not print out the header info:

    if(!NoLogo)

    {

        consoleApplication.PrintLogo();

    }

    Finally one last thing before we're done. A bug in the old system was that if you tried to generate a new tree over top of an existing directory, it would bomb out with something like this:

    TreeSurgeon version 1.1
    Copyright (C) 2007 - 2008 Bil Simser
    Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc

    Starting Tree Generation for test

    Unhandled Exception thrown. Details follow:
    Can't generate directory [C:\Documents and Settings\simserb\My Documents\TreeSurgeon\test] since it already exists on disk. Wait until a later version, or delete the existing directory!
       at ThoughtWorks.TreeSurgeon.Core.SimpleDirectoryBuilder.CreateDirectory(String directoryName) in C:\Development\TreeSurgeon-2000.source\src\Core\SimpleDirectoryBuilder.cs:line 12
       at ThoughtWorks.TreeSurgeon.Core.TreeSurgeonFrontEnd.GenerateDevelopmentTree(String projectName, String unitTestName) in C:\Development\TreeSurgeon-2
    000.source\src\Core\TreeSurgeonFrontEnd.cs:line 42
       at ThoughtWorks.TreeSurgeon.TreeSurgeonConsole.TreeSurgeonConsoleMain.RunApp(String[] args) in C:\Development\TreeSurgeon-2000.source\src\TreeSurgeonConsole\TreeSurgeonConsoleMain.cs:line 44
       at ThoughtWorks.TreeSurgeon.TreeSurgeonConsole.TreeSurgeonConsoleMain.Main(String[] args) in C:\Development\TreeSurgeon-2
    000.source\src\TreeSurgeonConsole\TreeSurgeonConsoleMain.cs:line 15

    Highly useful. Let's add a new feature to our command line, an "/overwrite" swtich. It'll be just like the "/nologo" switch except that if it's specified, we'll delete the directory before we generate the tree:

    [Option(

        Type = OptionType.Switch,

        Name = "/overwrite",

        Description = "When turned on, any project with the same name\r\n\t" +

              "will be deleted.")]

    public bool Overwrite { get; set; }

    And here's the updated tree generation code with the check to see if we should delete the output directory first: 

    var frontEnd = new TreeSurgeonFrontEnd(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Version);

     

    if (Overwrite)

    {

        Directory.Delete(frontEnd.GetOutputPath(ProjectName), true);

    }

     

    var outputDirectory = frontEnd.GenerateDevelopmentTree(ProjectName, UnitTestFramework);

    Console.WriteLine("Tree Generation complete.{0}{0}Files can be found at:{0}\"{1}\"", Environment.NewLine, outputDirectory);

    That's it! In under 60 minutes we were able to totally transform the command line tool into something a little more robust and testable (and even add a new feature to fix an old bug). Now when we run the Tree Surgeon console app we get a rich descriptive help screen:

    TreeSurgeon version 2.0
    Copyright (C) 2007 - 2008 Bil Simser
    Copyright (C) 2005 - 2006 Mike Roberts, ThoughtWorks, Inc.

    Creates a .NET development tree

    TreeSurgeonConsole.exe </p|/project:> [/v|/version:] [/nologo] [/overwrite] [/t|/test:]

    > Calling the application without arguments
      Prints the help information on the screen.

    > Generates a new .NET development tree for a given project name.
      /p|/project:value (required):
            Specifies the project name.
            Please note - project name must not contain spaces.
            We recommend you use CamelCase for project names.

      /v|/version:value :
            Specifies the Visual Studio version to generate.
            Valid options are: "2003", "2005", or "2008"
            Default is "2008"

      [/nologo]:
            When turned on, the logo and description
            information will not be displayed.

      [/overwrite]:
            When turned on, any project with the same name
            will be deleted.

      /t|/test:value :
            Specifies the Unit Test framework to use when generating the tree.
            Valid options are: "NUnit", or "MbUnit"
            Default is "NUnit"

    A few benefits I got from this conversion:

    • Options can be specified in any order. In the original code args[0] was the project name and args[1] was the version number. Now the user can specify the project name anywhere
    • The old system would bomb out if we tried to overwrite an existing directory. It will still do that, but we now have an "/overwrite" option that was added using one property and 3 lines of code
    • A highly descriptive help message is displayed to the user so discoverabilyt of what options are available is now there and didn't cost me anything in formatting
    • Users can specify options using long names "/version" or short "/v". Also I could add a new variation just by updating the attribute
    • My options are now fully testable and I don't have to run the app or even mock or fake it out 
    • I have a highly flexible command line runner that I can extend with ease

    So, if you've got a console application sitting around you might want to give the ACF a spin and try it out. Or if you're building a new app take a look at it. It was low impact and high value for my investment and gave me a better end result that's now testable and easy to extend. You might find it useful like I did. Many thanks to Sunny Chen for putting this library together, it's a great tool.

    Enjoy!