Thursday, May 23, 2013

Migrating from Graffiti CMS to Blogger


I set this blog up in 2008 and have blogged on and off for the last 5 or so years on technology. I selected Graffiti CMS after Rob Howard from Telligent, its creator, gave a talk at our local AZ Groups .Net user group and he gave out a bunch of licenses to a this product which was then going to be sold at $150. It has since become open source.

Graffiti CMS has treated me very well and is a remarkably good product. However, these were the drawbacks that made me decide to move my blog off Graffiti:
  • Very small number of users and one very kind and competent developer maintaining it.
  • I hosted it on my dedicated server. This meant that it was eating up resources that other web apps needed.
  • I had to upgrade it and maintain it and patch it myself and a security vulnerability might impact other applications on the server.
I moved it to Blogger because I already have a blog on Blogger and as such I'm familiar with the tools but I'm sure that other blogging platforms like Wordpress would have had as much as I needed as well.

The migration process went like this:
  • Found some code that someone had written to export the blog into BlogML and tried it out. This failed as Blogger couldn't read it in.
  • Spent a few hours modifying the code to get the export format I wanted but was unsuccessful. Knowing that I don't have much time on my hands to do this type of thing I threw some money at it and asked the Graffiti developer to create an export for me.
  • He kindly did this and here I am, 300 or so migrated posts in Blogger.
The original blog ran off http://guyellisrocks.com/ (no www) and I've migrated it to http://www.guyellisrocks.com/ so that I can redirect to the new url pattern. I've written a web app which is now sitting at the non-www version and when it gets a request it then reaches into the database and pulls out the date for that post and generates the new url. This is then used for redirection to the new (this) site.

What's still to be done?
  • Some of the URLs did not migrate in the predictive fashion I'd expected. I'm logging those and will make changes to the redirection code as I go along.
  • Images have not come across so I'll do that manually as I find time.
  • There are references to the non-www version in the new www version and even though they will redirect correctly they should be fixed as eventually I'd like to get rid of the redirector. I'll do this by checking the referrer header and if it's from the www version I'll log this so that I can find the posts with those links.

Tuesday, May 14, 2013

ShipIt Day at Go Daddy

Shipping

I manage a team of developers in the Presence and Commerce division of Go Daddy. We're responsible for providing small businesses with their Digital Identity. Our two main products are Website Builder and Quick Shopping Cart. Last Friday, May 10, 2013 we had our first ShipIt Day.

ShipIt Days were created by Atlassian. In their words:

Every quarter, we give employees the chance to work on anything that relates to our products, and deliver it during ShipIt Day, our 24-hour hackathon. Been wanting to build that plugin, redesign that interface, or completely rethink that feature that’s been bugging you? You’ve got 24 hours...go!

Our first ShipIt Day pretty much followed the format described by that statement.

We picked a Friday to do it because Fridays are usually less busy than other days because we try and avoid them for deployments. The teams self-organized which included the skill sets of Quality Assurance, Developers, DBAs and Business Analysts. We found a room where everyone could come together and work. Tip: When you do this bring power strips, duct tape and extension cords to make sure everyone has power throughout the day. Also make sure that the wireless is working well and has the bandwidth needed.

During the day we made sure that the team was sugared up on sodas and snacks and brought in lunch and towards the end of the day we brought in some adult beverages when the brain numbing complexity of the coding was done. This allowed everyone to relax and celebrate their work and cruise into the weekend.

We delayed the judging and demos until first thing on Monday morning to allow weekend travelers to get home.

Each team had six minutes on Monday morning to show what they'd created and the Product Managers and other non-participating managers joined in the judging. As score collector I asked the judges to use whatever criteria they through appropriate but to roll it all up into a single score from zero to ten and give that to me. Decimal places were allowed and one of the judges used five criteria and sent me scores to two decimal places.

Based on the generally high scores given out by all the judges, the quality and usefulness of the work done was exceptionally high. I cannot list the features because we will probably ship all of them in one of the two products already mentioned. However, once they're shipped I'll come back here and talk about them some more.

All of the members of the top three teams each received a prize to recognize their innovation. It was a tough call because the scores were close together as all the features were awesome.

Tips for a successful ShipIt Day (not saying we did all of these but in future ShipIt Days we hope to cover them all):

  • Make sure you have power, wireless and a comfortable place for everyone to work.
  • Make sure the team understands what they're doing. (i.e. a feature to enhance the product that they're working on but it doesn't have to come from the backlog.)
  • Let the team know the ShipIt Day date well ahead of time so they can start forming their teams and ideas around what they'll work on and cancel that vacation to Hawaii so they can be there.
  • Make sure they have drinks, snacks, and lunch during the day and of course an adult beverage at the end of the day.
  • Get a budget for prizes, who doesn't like to be recognized?

I'm very lucky to be working with such an awesome team of developers. They are constantly going above-and-beyond in their efforts to make world class products and it's a privilege to be part of this team.

We are always looking for talented individuals. If you would like to join a winning team and do cool and exciting events like ShipIt Days feel free to browse Go Daddy's Job Board.

 

Tuesday, February 19, 2013

Codility Equi Task

Codility is a site that tests coders. Their demo problem task is called Equi. You can read the full description of the problem and solutions in a number of different languages on their blog post about it: Solutions for task Equi

I was somewhat surprise at the complexity of the code required in many languages. Using C#'s LINQ syntax makes it obvious and simple to write:

static int equi(int[] A)
{
    for (int i = 0; i < A.Length; i++)
    {
        if (A.Take(i).Sum() == A.Skip(i + 1).Sum())
        {
            return i;
        }
    }
    return -1;
}

It's also not difficult to modify that function to return all the equilibrium indexes: 

static IEnumerable<int> equi2(int[] A)
{
    for (int i = 0; i < A.Length; i++)
    {
        if (A.Take(i).Sum() == A.Skip(i+1).Sum())
        {
            yield return i;
        }
    }
}

Note that this solution although simple and elegant to write is not performant because the full array get summed in each loop. i.e. the time complexity of this solution is O(n^2). For a time linear solution you would increase and decrease a left and right sum variable as you moved through the index and compare those values.

Sunday, November 18, 2012

Overloading methods in TypeScript

At yesterday's presentation at Desert Code Camp I felt that I gave an inadequate explanation on how you would implement an overloaded method in TypeScript. Hopefully this code snippet will address that:

interface Thing {
    a: number;
    b: string;
    foo(x: string): string;
    foo(n: number): number;
}

function myfunction(myParam: Thing) {
    var stringResult: string = myParam.foo(myParam.b);
    var numberResult: number = myParam.foo(myParam.a);

    console.log(stringResult);
    console.log(numberResult);
}


var myObj = {
    a: 16,
    b: "My String",
    foo: (x: any) => {
        if (x && typeof x === 'string') {
            return x.length.toString();
        } else if (x && typeof x === 'number') {
            return x * x;
        } else {
            if (x) {
                throw { message: "null parameter is unsupported" };
            } else {
                throw { message: "unsupported type: " + typeof x };
            }
        }
    }
}

myfunction(myObj);

Note that in myfunction() you would not be able to call myParam.foo() with any other type of parameter than string or number because the compiler will complain about that. So even though the implementation checks for another type in the final else you cannot pass in another type (e.g. object) because TypeScript won't let you.

Friday, November 16, 2012

Toastmasters Timer in TypeScript

I recently wrote a Toastmasters Timer in TypeScript for two reasons:

  1. The other timers that I found out on the web didn't quite do what I wanted them to do and
  2. I needed a small project to learn TypeScript

All of the timers I found followed the same approximate format as this one created by Stan Birdwell (Central Toastmasters 2277-31) and Michael K. Heney (Goddard Toastmasters 3496-36):

Old Toastmasters Timer

I was looking for something that would should the time in a large font on the page and change the color of a large portion of the page at each stage. I needed this to be able to constantly monitor the time and color from a distance while I am pacing around the room practicing a speech. I didn't want to have to lean forward to see the time. This is the final result: Toastmasters Timer

New Toastmasters Timer

 

Here is the TypeScript source code for that timer (the compiled JS, HTML and CSS can be pulled from the page):

/// <reference path="jquery.d.ts" />

class SpeechType {
    constructor (public name: string, public greenTime: string, public yellowTime: string, public redTime: string, public id: string) {
    }
}

class TSTimer {
    timerToken: number;
    speeches: SpeechType[];
    started: bool;
    startTime: Date;
    stopTime: Date;
    green: number;
    yellow: number;
    red: number;

    constructor (speeches: SpeechType[]) {
        this.started = false;
        this.speeches = speeches;

        $.each(this.speeches, (indexInArray: number, valueOfElement: SpeechType) => {
            var newButton = $('<span>')
                .attr('id', valueOfElement.id)
                .addClass('speech-type')
                .html(valueOfElement.name);
            newButton.click( (event) => {
                this.activateSpeech($(event.target).attr('id'));
            });
            newButton.appendTo('#buttons');
        });
       
        $(window).resize( () => {
            this.resizeTime();
        });
        // call on initialization in case the browser starts off narrow
        this.resizeTime();

        $('#btnReset').click( () => {
            this.resetButton();
        });

        $('#btnStart').click(() => {
            this.startButton();
        });

    }

    resetButton() {
        this.stop();
        $('#trafficlight').text('0:00');
        $('#body').css('background-color', '#EFEEEF');
        this.startTime = null;
    }

    startButton() {
        if (this.started) {
            this.stop();
        } else {
            this.start();
        }
    };

    resizeTime() {
        var width = $(window).width();
        var x: number = Math.floor((width < 900) ? (width / 900) * 28 : 28);
        $('#trafficlight').css('font-size', x + 'em');
    }

    setElementText(elapsedSeconds: number) {
        $('#trafficlight').text(this.formatTime(elapsedSeconds));
        if (elapsedSeconds >= this.red) {
            $('#body').css('background-color', '#FF4040');
        } else if (elapsedSeconds >= this.yellow) {
            $('#body').css('background-color', '#FCDC3B');
        } else if (elapsedSeconds >= this.green) {
            $('#body').css('background-color', '#A7DA7E');
        }
    }

    timerEvent() {
        if (!this.startTime) {
            this.startTime = new Date();
        }
        var timeNow = new Date();
        var elapsedSeconds: number = this.timeDiffInSeconds(this.startTime, timeNow);
        this.setElementText(elapsedSeconds);
    }

    // Returns the difference in seconds between
    timeDiffInSeconds(earlyTime: Date, lateTime: Date): number {
        var diff: number = lateTime.getTime() - earlyTime.getTime();
        return Math.floor(diff / 1000);
    }

    formatTime(elapsedSeconds: number) {
        var minutes: number = Math.floor(elapsedSeconds / 60);
        var seconds: number = elapsedSeconds % 60;
        return minutes + ":" + ((seconds < 10) ? "0" + seconds.toString() : seconds.toString());
    }

    start() {
        $('#btnStart').val('Stop');
        this.started = true;
        if (this.startTime) {
            // i.e. no reset since the last time it was started so adjust the start time
            // to reflect the time that's elapsed since it was stopped.
            var newStartTime = new Date().getTime() - (this.stopTime.getTime() - this.startTime.getTime());
            this.startTime.setTime(newStartTime);
        }
        this.green = this.getSecondsFromTextBox('#green-light');
        this.yellow = this.getSecondsFromTextBox('#yellow-light');
        this.red = this.getSecondsFromTextBox('#red-light');
        this.timerToken = setInterval(() => this.timerEvent(), 1000);
    }

    stop() {
        $('#btnStart').val('Start');
        this.started = false;
        this.stopTime = new Date();
        clearTimeout(this.timerToken);
    }
   

    getSecondsFromTextBox(id: string): number {
        var greenLight: string = $(id).val();
        return parseInt(greenLight.split(':')[0]) * 60 + parseInt(greenLight.split(':')[1]);
    }

    setDefault() {
        this.activateSpeech('st-standard');
    }

    activateSpeech(speechId: string) {
        $.each(this.speeches, function (indexInArray: number, valueOfElement: SpeechType) {
            if (valueOfElement.id === speechId) {
                $('#green-light').val(valueOfElement.greenTime);
                $('#yellow-light').val(valueOfElement.yellowTime);
                $('#red-light').val(valueOfElement.redTime);
            }
        });
        $('.active-speech').removeClass('active-speech');
        $('#' + speechId).addClass('active-speech');
    }
}


$(document).ready(function () {
    var speeches = [];
    speeches.push(new SpeechType("Table&nbsp;Topics", "1:00", "1:30", "2:00", "st-table-topics"));
    speeches.push(new SpeechType("Evaluation", "2:00", "2:30", "3:00", "st-evaluation"));
    speeches.push(new SpeechType("Icebreaker", "4:00", "5:00", "6:00", "st-icebreaker"));
    speeches.push(new SpeechType("Standard", "5:00", "6:00", "7:00", "st-standard"));
    speeches.push(new SpeechType("Advanced", "8:00", "9:00", "10:00", "st-advanced"));
    speeches.push(new SpeechType("Test", "0:02", "0:04", "0:06", "st-test"));
    var timer = new TSTimer(speeches);

    timer.setDefault();
});


 

 

Saturday, November 10, 2012

TypeScript template doesn't work in FireFox

If you're using Visual Studio 2012 and you've added the TypeScript for Microsoft Visual Studio 2012 extension then you will be able to jump start a TypeScript project using the template. If you click File > New > Project and look under the Visual C# templates you'll see a template called HTML Application with TypeScript. Selecting this template will generate a new project with a TypeScript file with the following contents:

class Greeter {
    element: HTMLElement;
    span: HTMLElement;
    timerToken: number;
   
    constructor (element: HTMLElement) {
        this.element = element;
        this.element.innerText += "The time is: ";
        this.span = document.createElement('span');
        this.element.appendChild(this.span);
        this.span.innerText = new Date().toUTCString();
    }

    start() {
        this.timerToken = setInterval(() =>
          this.span.innerText = new Date().toUTCString(), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
    }
}

window.onload = () => {
    var el = document.getElementById('content');
    var greeter = new Greeter(el);
    greeter.start();
};

The problem with this code is the use of the .innerText property which is called .textContent in FireFox.

Here is a replacement to the above template that will work in FireFox and other browsers to get you moving forward again.

Tested on:
FireFox 16.0.2
Safari for Windows 5.1.7
IE9
Chrome 23.0.1271.64 m
Opera 12.10

class Greeter {
    element: HTMLElement;
    span: HTMLElement;
    timerToken: number;
    hasInnerText: bool;
   
    constructor (element: HTMLElement) {
        this.element = element;
        // Firefox uses textContent
        this.hasInnerText = !this.element.textContent;
        this.appendElementText(this.element, "The time is: ");
        this.span = document.createElement('span');
        this.element.appendChild(this.span);
        this.setElementText(this.span, new Date().toUTCString());
    }

    setElementText(elem: HTMLElement, value: string) {
        if (this.hasInnerText) {
            elem.innerText = value;
        } else {
            elem.textContent = value;
        }
    }

    appendElementText(elem: HTMLElement, value: string) {
        if (this.hasInnerText) {
            elem.innerText += value;
        } else {
            elem.textContent += value;
        }
    }

    start() {
        this.timerToken = setInterval(() =>
          this.setElementText(this.span, new Date().toUTCString()), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
    }   
}

window.onload = () => {
    var el = document.getElementById('content');
    var greeter = new Greeter(el);
    greeter.start();
};


 

 

Friday, November 2, 2012

TypeScript

I'm going to be giving a presentation called TypeScript, what's all the fuss? at the Fall 2012 Desert Code Camp on November 17, 2012. I'll be adding a list of resources and my slides to this blog post.

TypeScript Resources:

Here's an example of method overloading in TypeScript which I didn't cover adequately in this presentation.