Apprenticeship Day 3
On day 3, I got to put the previous days' learning into practice, as I tried implementing Atom functionality in Vellum, Endjin's Azure-based Content Management System.
This happened under the watchful eye of Matthew, who guided me through the process remotely through a combination of Skype and TeamViewer. This worked remarkably well – via TeamViewer both of us could access Visual Studio on his machine, with his access as host taking priority – a bit like a driving instructor with the master set of pedals.
Atom is an XML-based protocol used to create content feeds. One aim of introducing Atom functionality to the CMS is to allow integration with products such as Windows LiveWriter, which provides a Word-like environment for writing blogs. However, to begin with, we simply wanted to add the ability to create an Atom Feed from a selection of a site's pages, and display this to the user.
Fortunately for me, something like this had already been created for RSS, so we knew which new classes we'd have to create. Vellum uses a Model View Controller (MVC) architecture, so there was a Controller class which dealt with requests to create the RSS Feed.
The main work of fetching pages and creating a feed was carried out in a separate Task class. Tasks encapsulate any long running jobs needed by the application. They are neatly filed away in the Solution in folders describing their function. Because we want to produce loosely coupled code, the Task implemented an Interface. And there it was – three classes:
We got started creating a Controller, Task Interface and Task for producing Atom feeds from a set of web pages. More keyboard shortcuts here: after selecting a project or folder in the Solution Explorer, Alt + Insert lets you quickly insert new classes, folders etc. The RSS Controller class didn't need many changes to be suitable for Atom.
However, this was a chance for me to get familiar with ReSharper, and practice the concept of dependency injection which I'd read about on day 1. I learnt that if you copy a method to a new class, ReSharper will (with informed oversight), guide you through the process of:
Setting up fields for any entities used by the method
Creating a parameterised constructor which initialises these fields on construction of the Controller
Tidying up the field visibility.
This entire process involved hovering over the highlighted areas, and clicking Alt + Enter, three times. Very neat. ReSharper also took care of importing any namespaces that were needed.
The method exposed through the Controller needed to be asynchronous, as it involved calling an asynchronous method in the Task class. I hope to develop my understanding of asynchronicity in .NET, and it'll have to happen sharpish in this place!
Howard had explained that .NET 4.5 made it far easier to use this feature, which should ideally be put to use for any logic requiring I/O such as network access or reading / writing to a file system or device. Finally, to complete the Controller, following .NET MVC conventions, we needed to register it in the Project's WebConfig file.
Moving on to the Task, we created first the Interface, then the Task Class. Another best practice moment occurred when we noticed that a helper method in the RSS Task class required no changes for Atom.
It was swiftly transferred into a separate Utility class. Matthew explained that this not only promoted code re-use, but it also made unit testing of our new Atom Task far easier, as the helper method used static methods of HttpContext and HttpRuntime, which required the whole web runtime to test.
As with the Controller, we used ReSharper to quickly create private fields and initialise them through the constructor. Matthew left me with an exercise of altering the logic which created RSS items to create Atom items. We were using the Argotic.Syndication Framework for both RSS and Atom, so first stop was to check the documentation, and find the equivalent Atom properties.
It turns out that the Atom equivalent of an RSSItem is an AtomEntry, which, unsurprisingly, has slightly different properties. For example, where an RSSItem has a Guid, an AtomEntry has a URI-based Id. I was worried that this might be a bad idea in case the site structure changed in future (we'd just been using slugs to create the Guids for RSSItems), but it turned out not to matter as Atom is just for published content.
Some head-scratching later, and I reached the wonderful point where the solution ran and produced the desired Atom feed. I must admit my head was swimming at the end of the day, but it felt great to be working on real project, and I'd learnt a huge amount.