08
Mar
10

Castlecasts.com is live!

After a few evenings hard work, I can now announce that castlecasts.com is now live!

Currently there is only one screencast to watch but I’ll be building on this in the coming weeks. If you have any ideas for future episodes or feedback for the site feel free to post them on the forum.

I’ll be adding a submission form soon so you can submit your own screencasts for inclusion in the site. If you want to submit a Castlecast before this is ready, just email me the details at: andy[at]castlecasts.com.

Now a big thank you to the Castle team, especially Henry Conceição for space on the Castle build server and also for the hosting of the site. You guys rock!

If you’re interested in how the site was built then the source is available on GitHub so feel free to take a look and if you fancy sending me a pull request, I’d be happy to receive them ;o)

28
Feb
10

Introducing Castlecasts

It’s been a very long time since I lasted blogged but I’ve found some time and would like to take this opportunity to announce Castlecasts.

After doing a lot of learning in Ruby on Rails, one of the best resources that I found was the www.railscasts.com site. For those who don’t know, it contains over 200 screencasts about various aspects of programming in RoR. So I posted the idea of creating a similar site for the CastleProject which seemed to have some interest.

I found some spare time and have decided to start castlecasts.com. I’ve been looking for a way to give back to the Castle team for a while as I’ve been using their components for over 2 years. I think this could be an excellent resource for the Castle community, if you have any questions or comments please let me know.

The site isn’t ready yet but I have created the first screencast. It’s hosted on Vimeo (for now) to keep hosting costs down. Once the site is ready I’ll let you know, but in the meantime, here is the first episode:

Getting Started With MonoRail from Andy Pike on Vimeo.

You can get the source code for this episode on github: http://github.com/andypike/Castlecasts

I plan on adding a whole bunch more episodes, so if you have a suggestion for something to cover or have a screencast you would like to add, DM me on twitter: @andypike

I’ll be on the look out for some hosting, so if you feel like sponsoring castlecasts.com, donation of hosting would be cool ;o) I’m working on the site now, should have something simple up and running soon. Then I’ll start creating more episodes.

08
Aug
09

Building .NET Solution with Rake and TeamCity

I’ve been reading through my Ruby books and came across Rake. Rake is a Ruby build program similar to NAnt or MSBuild but instead of writing XML, you use Ruby instead. Now that opens all sorts of doors, so I thought I’d see if I could get Rake to build a .NET solution and take it from there.

First things first, if you want to do this you will need to download and install Ruby, luckily for us, Rake is included. You will need to reboot after to ensure your PATH is updated.

Now we are ready to rumble. First thing I wanted to try was just compiling a solution – nice and simple. There were a few samples around the interweb, and I soon created the following:

DOT_NET_PATH = "#{ENV["SystemRoot"]}\\Microsoft.NET\\Framework\\v3.5"
SOURCE_PATH = "../src"
CONFIG = "Release"

task :default => ["build:all"]

namespace :build do

    task :all => [:compile]

    desc "Build solution using MSBuild."
    task :compile do
        puts "Compiling Solution..."
        solutions = FileList["#{SOURCE_PATH}/**/*.sln"]
        solutions.each do |solution|
          sh "#{DOT_NET_PATH}/msbuild.exe /p:Configuration=#{CONFIG} #{solution} /t:Rebuild"
        end
    end

end

Save this file as “rakefile.rb” and put it in a folder called “build” which is at the same level as the “src” folder. Then at a command prompt, navigate to the build folder and use the following command:

C:\MyRakeSample\build>rake

It worked!

Inside the Rake file we define the default build task as the :all task that is inside our build namespace. This task currently consists of one task :compile. The :compile task searches the src folder for any solution files and the builds them using MSBuild in release.

That was pretty easy and worked without a hitch. So next, I wanted to list the things I wanted my new build script to do:

  1. Get the current SVN revision number
  2. Update all AssemblyInfo.cs files with a new version number that contains this SVN rev number
  3. Build the solution (as above)
  4. Run NUnit tests
  5. Create test coverage report
  6. Copy all the files that need to be deployed (excluding those that don’t)
  7. Zip up these files into a deployment package
  8. Integrate as much as possible with TeamCity
  9. Should be able to run the same build script locally as well as with TeamCity

It took a little but I got it working. You can see the full file here. Below is a breakdown of each section:

Getting the current SVN revision number

First off, you’ll need to get the SVN command line client downloaded and installed. You will need to reboot after to ensure your PATH is updated.

I decided to add a new :init task that should clean up, create folders and get the SVN HEAD revision number. So here is the new code I’ve added:

require 'rexml/document'

....

OUTPUT_PATH = "output"
SVN_LOG_PATH = "#{OUTPUT_PATH}/svn_log.xml"
ARTIFACTS_PATH = "#{OUTPUT_PATH}/artifacts"
SVN_URL = "http://aardvark-cms.googlecode.com/svn/trunk/"
VERSION_MAJOR_MINOR_BUILD = "0.1.0"

namespace :build do

    task :all => [:init, :compile]

    ....

    desc "Perform the required initial tasks."
    task :init do
        puts "Performing Initialization..."

        puts "Creating folders..."
        Dir.mkdir(OUTPUT_PATH)
        Dir.mkdir(ARTIFACTS_PATH)

        puts "Getting SVN revision number and saving to #{SVN_LOG_PATH}..."

        sh "svn log --xml --revision HEAD #{SVN_URL} > \"#{SVN_LOG_PATH}\""

        log = REXML::Document.new File.new(SVN_LOG_PATH)
        logEntry = log.root.elements["logentry"]
        $svn_revision = logEntry.attributes["revision"]
        $svn_message = logEntry.elements["msg"].text
        VERSION = "#{VERSION_MAJOR_MINOR_BUILD}.#{$svn_revision}"

        puts "Revision #{$svn_revision} found with message: #{$svn_message}"
    end
end

There are a bunch of new constants and I’ve included the an xml parser at the top. Then I’ve added a new task called :init and added it to the :all task before the :compile task.

Inside the :init task we create some folders, then we use the SVN command line tool to request the HEAD revision information from the repository url and save it as xml to the output folder that is inside our build folder.

Once there, we can read the details from this xml file and create a version number for our application.

Updating AssemblyInfo.cs with new version number

Now that we have the SVN revision number and have generated a version number for the current build we need to apply it to our assemblies. We do that by rewriting the AssemblyInfo.cs file for each assembly in our solution. That calls for a new task:

PROJECT_NAME = "Aardvark"
COPYRIGHT = "Copyright 2009 1minus1 Ltd. All rights reserved."

namespace :build do

    task :all => [:init, :writeAssemblyInfo, :compile]

    ....

    desc "Write AssemblyInfo.cs with current svn revision."
    task :writeAssemblyInfo => [:init] do
    puts "Writing AssemblyInfo Files..."

        assemblyInfoFiles = FileList["#{SOURCE_PATH}/**/AssemblyInfo.cs"]
        assemblyInfoFiles.each do |filePath|
            puts "AssemblyInfo file found at: #{filePath}"
            file = File.new(filePath, "w")

            file.puts "//This file was generated by the rakefile.rb build script"
            file.puts "using System.Reflection;"
            file.puts "using System.Runtime.InteropServices;\r\n"

            file.puts "[assembly: AssemblyTitle(\"#{PROJECT_NAME} for .NET 3.5\")]"
            file.puts "[assembly: AssemblyDescription(\"\")]"
            file.puts "[assembly: AssemblyConfiguration(\"\")]"
            file.puts "[assembly: AssemblyCompany(\"\")]"
            file.puts "[assembly: AssemblyProduct(\"#{PROJECT_NAME}\")]"
            file.puts "[assembly: AssemblyCopyright(\"#{COPYRIGHT}\")]"
            file.puts "[assembly: AssemblyTrademark(\"\")]"
            file.puts "[assembly: AssemblyCulture(\"\")]"
            file.puts "[assembly: ComVisible(false)]"
            file.puts "[assembly: AssemblyVersion(\"#{VERSION}\")]"
            file.puts "[assembly: AssemblyFileVersion(\"#{VERSION}\")]"

            file.close
        end
    end
end

This is pretty straightforward, we search for all the AssemblyInfo.cs files and then loop through them overwriting them as we go with a new set of attributes. We’ve defined a couple of new constants at the top to use, but the important thing is the use of the newly generated version number.

We add this new :writeAssemblyInfo task to the :all build task before we compile to ensure that every build will include the correct revision number.

Run NUnit Tests

Now we have built our solution, we need to run the tests. Here we need to do something a bit extra as we want to use the TeamCity NUint runner if it is TeamCity running the build. Otherwise we want to use the normal NUnit console for when we are running the build locally.

TEAMCITY_NUNIT_RUNNER = ENV["teamcity.dotnet.nunitlauncher"]
NUNIT_EXE = "../tools/NUnit/nunit-console.exe"

namespace :build do

    task :all => [:init, :writeAssemblyInfo, :compile, :test]

    ...

    desc "Runs tests with NUnit only (without coverage)."
    task :test => [:compile] do
        puts "Running Tests..."
        tests = FileList["#{SOURCE_PATH}/**/#{CONFIG}/*.Tests.dll"].exclude(/obj\//)

        #Select the correct test runner
        if(TEAMCITY_NUNIT_RUNNER == nil)
            sh "#{NUNIT_EXE} #{tests} /nologo /exclude:Acceptance /xml=#{OUTPUT_PATH}/TestResults.xml"
        else
            sh "#{TEAMCITY_NUNIT_RUNNER} v2.0 x86 NUnit-2.4.6 /category-exclude:Acceptance #{tests}"
        end
    end

end

In this task we first get a list of all the test assemblies based on our naming convention (they end in .Tests.dll) and make sure we exclude any from the obj folders. Then if the TeamCity NUnit runner environment variable is available we use that to run the tests, otherwise, use the NUnit console program that we store in a tools folder. At present we are excluding out WatiN acceptance tests, but more on this later.

Using the TeamCity NUnit runner is important to us as it will ensure that the TeamCity test UI and data are updated correctly.

Create test coverage report

This is where it gets interesting for me. Adding test coverage reports is a good way to ensure that you (or someone else) hasn’t been lazy and written some code without a test first. I don’t think that you should have a fixed percentage that must be covered, be pragmatic.

NCOVER_EXE = "../tools/NCover/NCover.Console.exe"
NCOVER_EXPLORER_EXE = "../tools/NCoverExplorer/NCoverExplorer.Console.exe"
COVERAGE_ASSEMBLIES = "OneMinusOne.Aardvark.Web;OneMinusOne.Aardvark.Framework;OneMinusOne.Aardvark.Entities;OneMinusOne.Aardvark.Core"

task :default => ["build:all"]

namespace :build do

    task :all => [:init, :writeAssemblyInfo, :compile, :test, :coverage]

    ...

    desc "Run tests with NUnit and NCover for coverage. NCoverExplorer is used to format the coverage results into html."
    task :coverage => [:compile] do
        puts "Running Tests with Coverage..."
        tests = FileList["#{SOURCE_PATH}/**/#{CONFIG}/*.Tests.dll"].exclude(/obj\//)
        sh "#{NCOVER_EXE} #{NUNIT_EXE} #{tests} /nologo /exclude:Acceptance /xml=#{OUTPUT_PATH}/TestResults.xml //reg //x #{OUTPUT_PATH}/Coverage.xml //l #{OUTPUT_PATH}/Coverage.log //ea System.CodeDom.Compiler.GeneratedCodeAttribute //a #{COVERAGE_ASSEMBLIES}"
        sh "#{NCOVER_EXPLORER_EXE} #{OUTPUT_PATH}/Coverage.xml /r:ModuleClassFunctionSummary /p:#{PROJECT_NAME} /q /h /so:2 /m:80 /h:#{OUTPUT_PATH}/CoverageReport.html"
    end
end

This is a pretty short task. First we use NCover to profile the code while it’s tests are run, then we use NCover Explorer to generate a nice html report. We’ve stored these two programs in our tools folder so you don’t need them installed and it’s easier to get the build running.

You need to do a few extra things in TeamCity to get this to work and show up on a new tab. Here’s what you need to do:

  1. Ensure that the build agent service is logged in as an administrator. Otherwise NCover will not be able to profile the tests and you’ll see the error “Profiler connection not established” in your build log.
  2. Add the html report that NCover Explorer generates to your TeamCity artifacts under a folder called Reports.
  3. Edit the TeamCity main-config.xml file as shown here to add a tab for your html report.

Create a deployment package

The last thing that I want my Rake build to do is to collect together the files I need to deploy and zip them up for me.

ARTIFACTS_PATH = "#{OUTPUT_PATH}/artifacts"
WEB_PROJECT_PATH = "../src/OneMinusOne.Aardvark.Web"
ZIP_EXE = "../tools/7Zip/7za.exe"

task :default => ["build:all"]

namespace :build do

    task :all => [:init, :writeAssemblyInfo, :compile, :test, :coverage, :package]

    ...

    desc "Package the required artifacts."
    task :package => [:compile] do
        puts "Preparing package files..."

        Dir.mkdir "#{ARTIFACTS_PATH}/bin"

        FileUtils.cp_r FileList["#{WEB_PROJECT_PATH}/bin/*.dll"], "#{ARTIFACTS_PATH}/bin"
        FileUtils.cp_r "#{WEB_PROJECT_PATH}/Content", "#{ARTIFACTS_PATH}"
        FileUtils.cp_r "#{WEB_PROJECT_PATH}/Views", "#{ARTIFACTS_PATH}"
        FileUtils.cp_r FileList["#{WEB_PROJECT_PATH}/*.aspx"], "#{ARTIFACTS_PATH}"
        FileUtils.cp_r FileList["#{WEB_PROJECT_PATH}/*.asax"], "#{ARTIFACTS_PATH}"
        FileUtils.cp_r FileList["#{WEB_PROJECT_PATH}/*.config"], "#{ARTIFACTS_PATH}"

        delete_svn_folders(ARTIFACTS_PATH)

        puts "Creating zip package"

        zipFolder(ARTIFACTS_PATH, "#{OUTPUT_PATH}/#{PROJECT_NAME}-v#{VERSION}.zip");
    end

    def delete_svn_folders(folder)
        Find.find(folder) do |path|
            if(path =~ /\.svn$/ || path =~ /\_svn$/)
                puts "Deleting: #{path}"
                FileUtils.rm_r path, :force => true
            end
        end
    end

    def zipFolder(folderPath, zipPath)
        sh "#{ZIP_EXE} a #{zipPath} .\\#{folderPath}\\*"
    end
end

As this is a Castle MonoRail application, I start this new :package task by copying all the required web files to an artifacts folder that is inside the output folder we created earlier. This ensures that we only have the files required to run the application and we exclude all other files.

Once the files are copied I noticed that because we were copying folders, the hidden .svn folders were being copied too. The only way I could see was to then delete these folders from the artifacts folder. This is where the delete_svn_folders function comes in. It just loops through and deletes any path that ends with either .svn or _svn as either can be used.

The final thing is to zip these files up. I use the 7zip command line tool to do this and I’ve added this to the tools folder again to make it as easy as possible to run the build. The zip file is saved to the output folder and is named with the project name and version number.

Finishing up

That’s it really, you can see the full build file here at Google Code. There are some extra bits and pieces in this build like coping the files to the development server, requesting an installer page on the site that will install the database and populate it with data but that was a bit specific for our needs so I didn’t add it here.

The advantage I can see here using Rake over NAnt or MSBuild is that you can program your build using code rather than xml which I find a lot easier and a lot more powerful.

The one thing that is missing from the build is the ability to run our acceptance tests that are written using WatiN. I found this post on Stack Overflow and this post on JetBrains forum which may point me in the right direction.

I hope that has helped, Rake is the future…

17
Jul
09

Learning Ruby

I haven’t posted in a long time, so I thought it was about time that I did.

I decided to take a proper look at Ruby. Currently I know virtually nothing, so I thought I’d start blogging about my progress which should help me learn and may also help other who are going down the same path.

Just so you know, I am a .NET developer and all my stuff is Windows based, so all the stuff below works on Windows.

Getting Started

  1. First download and install Ruby, I used the Ruby 1.8.6 One-Click Installer.
  2. Read this tutorial on the Ruby site: Ruby in Twenty Minutes
  3. Next I asked a respected friend (@benlovell) what books are worth a look. He listed the following: The Well-Grounded Rubyist, Design Patterns in Ruby and Agile Web Development with Rails. I’ve now purchased these, and they look good.
  4. Download and install RubyMine – this is an IDE by JetBrains that is very nice and as I use Resharper in Visual Studio, the same features are available.
  5. I then took a look at The RSpec Book and purchased that. It’s pretty good and makes a lot of sense. This will help as I’m from a TDD background anyway.

That’s about as far as I’ve got at the moment, in the next post I hope to get some simple RSpec tests in and passing. Wish me luck…

07
Mar
09

Thank you DevEvening

Last Thursday night I gave my first ever technical talk. The subject was “An Introduction to TDD” and I thought it went very well. So a big thank you to everyone that came along and I hope you found the talk and demonstration interesting.

So as promised here are the files from Thursday night:

Also as promised here are a bunch of links to the various tools I used and spoke about. The ones in bold are the ones that I use, but feel free to look at the others:

Unit Testing Frameworks

Test Runners

Mocking Frameworks

UI Testing Frameworks

Build Servers

In addition to this list, I used an AutoHotKey script that when run will type an underscore instead of a space which is really handy when writing test and fixture names. You can download it here.

So a big thank again to everyone that came and I hope you learnt something new.

02
Feb
09

Presenting at DevEvening

So I went to my first DevEvening last week and found it very useful. I spoke to a few people about the tools and frameworks I’ve been working with and found that most people have either never heard of them or have no idea how to use them. After listening to the speakers, I was then inspired to present some of the things I do/use during day-to-day development.

So I am going to be presenting at the next DevEvening: “An Introduction To TDD” on Thursday 5th March at 7:00pm. I am slightly nervous about this because I haven’t done any public speaking since I got married 5 years ago and have never presented a technical session of this kind before.

This session will just be an introduction to the subject of Test Driven Development with some general points and a demonstration of my work flow. So if you fancy joining me, please sign up for this free event in Woking and get some more information at http://devevening.co.uk. Also if there is any area of TDD that you think I should cover for the beginners, please feel free to comment below.

For those that do come, please go easy on me and please no awkward questions ;o)

12
Jan
09

Integrating Gravatar with Castle MonoRail

As part of my experimental Castle MonoRail project that I mentioned last time. I now wanted to allow users to include an image (avatar) within their profile. Obviously I could have just allowed them to upload an image, store it and then shown it when required. But I thought I’d integrate with Gravatar instead.

Gravatar is basically a free service where anyone can store an avatar image against an email address. They provide a very simple API for us developer types to integrate with given a user’s email address. So without further ado, here is what I did:

1. I signed up for a Gravatar account and uploaded an image ready for testing.

2. I created a test (TDD style) for a GravatarHelper class that would at first just return the correct image URL for an email address. I took the test data from their documentation so I knew it was correct:

[TestFixture]
public class When_using_the_gravatar_helper
{
    [Test]
    public void Should_build_a_basic_image_url()
    {
        var gravatar = new GravatarHelper();
        string url = gravatar.Url(" iHaveAn@email.com ");

        Assert.That(url, Is.EqualTo("http://www.gravatar.com/avatar/3b3be63a4c2a439b013787725dfce802.jpg"));
    }
}

OK, so pretty straightforward there then. The email address is of mixed case and has spaces around it on purpose as the documentation specifies that emails need to be trimmed and lowercased before hashing.

3. Now I have my failing test, I went right ahead and started implementing it:

public class GravatarHelper : AbstractHelper
{
    private const string PROTOCOL = "http";
    private const string DOMAIN = "www.gravatar.com";
    private const string PATH = "avatar";
    private const string EXTENSION = "jpg";

    public string Url(string email)
    {
        email = email.ToLower().Trim();

        MD5 md5 = new MD5CryptoServiceProvider();
        byte[] hash = md5.ComputeHash(Encoding.ASCII.GetBytes(email));

        var hashAsHex = new StringBuilder();
        Array.ForEach(hash, b => hashAsHex.Append(b.ToString("X2")));

        return string.Format("{0}://{1}/{2}/{3}.{4}", PROTOCOL, DOMAIN, PATH, hashAsHex, EXTENSION).ToLower();
    }
}

Again, no rocket science here. I just lowercase and trim the email address then hash it using the built-in .NET MD5 hash provider. Then I have to convert each byte in the hash into hex and add it to a string. Lastly I return the URL that has been built up from the various parts (and lowercased for good measure).

4. Next, just add the helper to my controller:

[Helper(typeof(GravatarHelper), "gravatar")]
public class AccountController : SmartDispatcherController
{
    ...
}

5. Then the simple task of using the helper in my view (not my real email):

<img src="$gravatar.url('someone@somewhere.com')" />

And hey presto, my Gravatar of my son appeared on my page, wahoo:

gravatar

Now that is all very good, but there are some extra parameters that the Gravatar API support. These are size, rating and default (in case the email address isn’t registered with them). I also wanted the helper to generate the image tag for me. So I went about creating a new helper test:

[Test]
public void Should_build_an_image_url_inside_an_image_tag_with_parameters()
{
    var gravatar = new GravatarHelper { ServerUtility = new StubServerUtility() };
    IDictionary parameters = new Hashtable
                                 {
                                     { "size", "48" },
                                     { "default", "http://example.com/images/example.jpg" },
                                     { "rating", "g" }
                                 };
    string tag = gravatar.Image(" iHaveAn@email.com ", parameters);
    Assert.That(tag, Is.EqualTo("<img src=\"http://www.gravatar.com/avatar/3b3be63a4c2a439b013787725dfce802.jpg?size=48&default=http%3a%2f%2fexample.com%2fimages%2fexample.jpg&rating=g\" alt=\"Gravatar\" />"));
}

I’m using an IDictionary here so that I can leverage the support for it in NVelocity. So about these parameters: The size parameter is the size in pixels that you would like the image (1 to 512) which defaults to 80. The default parameter is a URL of an image in case the email address supplied isn’t known by Gravatar. The rating parameter can be g, pg, r or x. Also notice in the test the need to set the ServerUtility property of the helper, I just using the StubServerUtility provided by Castle.

Now for the implementation:

private const string IMAGE_TAG_FORMAT = "<img src=\"{0}\" alt=\"Gravatar\" />";

public string Url(string email, IDictionary parameters)
{
    string url = Url(email);
    string queryString = CommonUtils.BuildQueryString(ServerUtility, parameters, false);

    return string.Concat(url, "?", queryString);
}

public string Image(string email, IDictionary parameters)
{
    return string.Format(IMAGE_TAG_FORMAT, Url(email, parameters));
}

The eagle-eyed amongst you will notice that I’ve added an overload to the Url method as well that supports parameters (I wrote a test for this first, so don’t panic). So you can now either create a url with parameters or a full image tag. So now in my view I can do this:


$gravatar.image("someone@somewhere.com", "%{size='48', rating='g', default='http://example.com/default.jpg'}")

That about wraps it up for now. I’ve committed all my code into my Google Code repository, so feel free to have a look: http://code.google.com/p/andypike/

kick it on DotNetKicks.com




Twitter

My Archives

My Categories


Follow

Get every new post delivered to your Inbox.