Transforming Tribal Knowledge Into Learning Trees: Part 1 -Getting Jekyll and Squirrel-U Running Locally in Visual Studio

Last weekend I attended CodeStock 2016 and listened to many speakers tout their wares and tell their software engineering stories. One of the most interesting, and entertaining sessions I attended was entitled “Transforming Tribal Knowledge Into Learning Trees” given by Jim D Kohl and his team. I thoroughly expected the talk to be a soft skills lesson on convincing the tribal knowledge sinks to make their knowledge public by some means of documentation. Jim exceeded my expectations completely.

What he presented, by way of a humorous and enjoyable children’s story, was Squirrel-U. It is a set of markdown pages, that are run through gh-pages on GitHub which uses Jekyll to compile the markdown into linked, searchable HTML pages. These pages are not only easy to curate, but also allow for QA by way of the Git repository’s forking and pull request workflows. Jim’s team’s tooling included an on site GitHub instance and IntelliJ, so they were in need on someone to give setup for other IDEs, most notably Visual Studio.

This post will be the first in a series of posts about my journey in configuring Jekyll and Visual Studio to stand up an instance of  Squirrel-U in a Microsoft environment instead of a Java/GitHub environment. The goal by the end is to have an instance of Squirrel-U operating on an IIS server, that listens to a Git repository residing in Visual Studio Team Services, and updates itself on the server when approved changes are pushed to the production branch of Squirrel-U.

Forking Squirrel-U

The first step to the process is just like any other project in Git. Fork the Squirrel-U Git repository located here. If you have never worked with an open source project on GitHub, go here to find directions on forking a GitHub repository, after you create a free GitHub account.

Adding Squirrel-U to a Visual Studio Solution

After you have forked the Squirrel-U Source Code, do the following to set-up the Visual Studio environment (2010 or later) to receive the Squirrel-U code.

  1. Configure Visual Studio Source Control to use GitHub (if not already done)
    • Open Visual Studio from any new or existing project
    • Open Tools -> Options -> Source Control -> Plug-in Selection
    • Select Microsoft Git Provider
    • In Team Explorer Home, select Settings -> Git Settings
    • Enter your user name, email address, and default repository location
  2. Windows Users: Install Git to your machine
  3. Clone the forked repository to a local repository by doing the following
    1. Click the Connect to Team Projects icon on the Team Explorer pane
    2. Expand the Clone drop down under Local Git Repositories
    3. Fill in the URL for the forked repository.
    4. Fill in the directory where you want the local repository placed. THIS DIRECTORY SHOULD NOT EXIST YET
    5. Click Clone to create the local directory and clone the repository.
  4. Create a new Visual Studio project from Existing Code File -> New -> Project From Existing Code
    • Select any project type you want, this does not effect the outcome, Click next.
    • Specify the project details as follows:
      • Where are the files?: Navigate to the location of the local repository
      • Name: Name the project as you see fit.
      • Output Type: Class Library
    • Click Finish

Install Jekyll on the Local Machine

Too many people have installed Jekyll on a Windows machine before for me to write in depth instructions on how to get Jekyll installed on your development machine. These instructions where very helpful to me. This will get the Ruby SDKs needed, along with Jekyll installed on your local machine. When starting Squirrel-U for the first time, you may get a warning that Jekyll can’t find the gem jemoji. As long as you have Ruby installed in your PATH environmental variable, just type gem install jemoji in the command prompt and it will fix you right up.

Start up Jekyll and see if you can get Squirrel-U started Locally

The following instructions will use Jekyll to build the Squirrel-U site and serve it locally so that you can see how you are doing.

  1. Open a command window and navigate to the local squirrel-u repository.
  2. Enter the command jekyll build --destination <destinationdir>, where <destinationdir> is the directory that you want the site to be built in.
  3. Navigate to the <destinationdir>
  4. Start the jekyll server by entering the command jekyll serve you should see a message in the terminal saying something similar to the following:
    done in 67.513 seconds.
    Auto-regeneration: enabled for 'C:/git/squirrel-u'
    Configuration file: C:/git/squirrel-u/_config.yml
    Server address: http://127.0.0.1:4000
    Server running... press ctrl-c to stop.
    
  5. You can now go to http://127.0.0.1:4000 and see your squirrel-u running locally!

Up Next

As long as you have followed these instructions, you should be moving along swimmingly. You now have a Visual Studio Project containing the Squirrel-U source that you can serve locally after running the Jekyll build manually. In the next installment, we will look at installing Grunt.js so that we can keep track of changed files and re-publish Squirrel-U automatically when the pages in specific directories have changed.

Professional Development – 3/14 through 7/17/2016

Software Development

Leadership

Productivity

Professional Development 2/29 through 3/13/2016

Software Development

Professional Development 2/1 through 2/28/2016

Software Development

Sharepoint Development

 

Professional Development 1/25 through 1/31/2016

Software Development

Professional Development 1/18 through 1/24/2016

Software Development

Ditching the Switch/Case

The other day I was exploring some code that I was preparing to edit in order to provide new functionality. It is a web service that takes values from the calling application, populates a PDF with the values, and publishes the PDF to deliver to the calling application. These are legal documents, so there are a large number of fields on each of the documents to populate. Each of those fields is mapped to a space on the PDF template in an XML metadata file. The trick is using the metadata to map the input to the PDF. My predecessor took the obvious route, and this is how I found the data mapping was being done:

 private void SetFieldValue(PdfStamper writer, Field field, PdfInput input)
{
    if (!writer.AcroFields.Fields.ContainsKey(field.FieldName))
    {
        return;
    }
    switch (field.PropertyName)
    {
        case "Is12MonthLoanChecked":
            if (input.Is12MonthLoanChecked)
            {
                writer.AcroFields.SetField(field.FieldName, GetCheckedState(writer, field.FieldName));
            }
            else
            {
                writer.AcroFields.SetField(field.FieldName, GetUncheckedState(writer, field.FieldName));
            }
            break;
        case "Is24MonthLoanChecked":
            if (input.Is24MonthLoanChecked)
            {
                writer.AcroFields.SetField(field.FieldName, GetCheckedState(writer, field.FieldName));
            }
            else
            {
                writer.AcroFields.SetField(field.FieldName, GetUncheckedState(writer, field.FieldName));
            }
            break;
        case "Is36MonthLoanChecked":
            if (input.Is36MonthLoanChecked)
            {
                writer.AcroFields.SetField(field.FieldName, GetCheckedState(writer, field.FieldName));
            }
            else
            {
                writer.AcroFields.SetField(field.FieldName, GetUncheckedState(writer, field.FieldName));
            }
            break;

     ......

This switch case continues for another 66 cases, and this is only the mapping for one of the documents. Although this works perfectly well, two things bothered me about this solution:

  1. Due to the number of cases and the use of strings for the cases, this implementation creates a potential maintenance nightmare of misspelled magic strings that are not unit testable.
  2. It just made me feel dirty.

As a big proponent of The Boy Scout Rule, I had to make sure this was not in the code base when I was done adding the newest entry fields. I was uninspired on solutions and so I searched the internet and found an article by Chris Brandsma at ElegantCode.com called Refactoring A Switch Statement. Brandsma’s solution is elegant, but I still found problems with the management of the delegates that do the “work.” Brandsma uses a dictionary that maps the cases to delegates that fulfill each case. This was better but still required the management of a large number of strings arranged in a way which is barely testable and open for spelling mistakes. i.e.:

 public Dictionary<string, Func<string, string>> CreateDictionary()
{
    var dictionary = new Dictionary<string, Func<string, string>>
                         {
                             {"Chris", _valueProcessor.Chris},
                             {"David", _valueProcessor.David},
                             {"Jason", _valueProcessor.Jason},
                             {"Scott", _valueProcessor.Scott},
                             {"Tony", _valueProcessor.Tony}
                         };
    return dictionary;
}
 

Remembering the introduction of the CallerMemberName Attribute in the .NET from my WPF days, and how it reduced the problems with the use of magic strings with the FirePropertyChanged() method, I decided the best way to make this refactor testable was to create a custom attribute so that I could use reflection to build my delegate map at run time. The custom attribute is very simple:

[AttributeUsage(AttributeTargets.Method)]
public class FieldProcessorMethodName : Attribute
{
    private readonly string _name;

    public FieldProcessorMethodName(string name)
    {
        _name = name;
    }
 
    public string Name { get { return _name; } }
}

I added this attribute to each of my delegates, assigning the delegate the name of the specified element in the XML metadata file:

[FieldProcessorMethodName("Is12MonthLoanChecked")]
internal void Set12MonthLoanCheckBoxState(PdfStamper writer, Field field, PdfInput input)
{
    SetCheckboxState(writer, field, input.Is12MonthLoanChecked);
}

This allowed the creation of my delegate mapping through reflection by doing the following:

public Dictionary<string, FieldProcessorDelegate> CreateDelegateDictionary()
{
    var delegateDictionary = new Dictionary<string, FieldProcessorDelegate>();

    var classMethods = GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
 
    foreach (var method in classMethods)
    {
        var attributes = method.GetCustomAttributes(true);
        foreach (var attribute in attributes)
        {
            var fieldProcessorNameAttribute = attribute as FieldProcessorMethodName;

            if (fieldProcessorNameAttribute != null)
            {
                var fieldAlias = fieldProcessorNameAttribute.Name;
                var fieldDelegate = (FieldProcessorDelegate) Delegate.CreateDelegate(typeof (FieldProcessorDelegate), this, method);
 
                delegateDictionary.Add(fieldAlias,fieldDelegate);
             }
        }
    }
 
    return delegateDictionary;
}

This method reduced the number of places that needed to be updated every time a field is added to the PDF from 4 places to 2 places, and it allowed me to test to make sure the spelling of the element names was correct in the code by checking each of the keys in the dictionary against the list of element names generated from the XML file.

Now if I can generalize the PdfInput from each of the PDFs, I can apply this approach to the case statements of all the other PDF generation logic.