HOA Forum Software Developed

by breeve 15. January 2010 16:25

Over the last months in my spare time, I developed a web based HOA (Home Owners Association) forum tailored to HOA organizations. I have posted all the code—above the screenshot—with instructions on how to get the software up and running should you care to run or modify it. You can do whatever you want with the software—it is now open source software.

The project started as a desire to play with ASP.NET MVC, LINQ to SQL, and jQuery. As the functionality increased, I tried my hand at pitching it to different neighborhoods. No interest. Not even for free. Most already had forums running like phpBB. I would be lying if I didn’t admit that part of my motivation was to develop a better looking forum than phpBB and hopefully banish it from all existence in the HOA space. Nathan Philpot helped with design work to make it look great but even that didn’t help as HOA organizations seemed just fine running their existing forums. The switching costs were just too much.

In reality, switching costs where just one reason it wasn’t adopted. HOA organizations, I found out, have very different ideas of what they want forcing the market into fragmentation. In fact, many build their own solutions in house.

Features Include:

  • Posted topics can be voted on which allows popular issues to rise to the top.
  • Each topic can show a voting history graph
  • Search topics
  • Each user has a page that lists:
    • Their posted topics and replies
    • Their reputation (gained by others voting on their topics or replies)
  • Topics can be flagged as spam or inappropriate by anyone, allowing an administrator to delete off color topics.
  • Posted topics are sorted by 4 main tabs
    • Recent - topics listed by date
    • Popular - topics listed by votes
    • Hot - topics listed by recent activity
    • No Replies - topics with no replies
  • When HOA members post, a special HOA icon is displayed notifying readers of a more official response
  • The HOA corner area on the right allows HOA members to post ideas, status, or announcements
  • RSS / Email updates

Hoa.zip

Tags:

Software

Developer Decision Space

by breeve 31. December 2009 11:14

Software Developers build things. Not physical things that people can admire from afar but rather invisible instructions that travel along wires as pulses of electrons. These instructional bursts can move data around from one place to another, add subtract multiply and divide numbers, and even draw complicated objects on the screen. A computer is like a piano; by itself it just sits there. It takes someone with enough persistence to sit and learn the thing to make it do something.

Persistence is a rare quality but one that is sorely needed when programming a computer. After all, programmers must painstakingly assemble instruction after instruction. Never was this more true than in the old days when programmers had to think in terms of ones and zeros. 10010101 could mean store a number, for example, while 10111010 might mean add two numbers. Programming this way was the only way to get things done and it drove many to the point of insanity. It is the equivalent of building a house by molding your own nails and milling your own two by fours. In other words, it took a lot of grunt work to get anything useful done.

Programmers understood this and in time became determined to make programming more time efficient by introducing what they like to call abstractions. Abstractions increase productivity by abstracting away the details of how something works. For example, compilers were introduced that would take high level languages like the C programming language and compile it into ones and zeros that the CPU understood. Now you could program with nails and two by fours already cut which was a fantastic performance boost.

Taking the idea of abstractions further, libraries were developed. A library consists of many routines—commonly called functions—that a programmer can call to get something done. Want to draw a circle on the screen or do a FFT; chances are some other programmer already created a library to do that and all you really have to do is call it with the right parameters. Forget about ones and zeros, programming became a task of calling a bunch of libraries.

Today there are hundreds of programming languages in addition to C and many more libraries that do everything from request a web page to move a robot some determined distance forward. Programming these days has become a game of how well you know a given set of libraries—typically called frameworks—and what programming languages you know to call those libraries. It’s a world of abstraction upon abstraction designed to make life better.

Before you get the idea that programming today is easy because there are all these cushy libraries to call and high level languages to do your every bidding, think again. Although things are generally easier, the shear amount of frameworks available makes it very hard to know what is going on under the hood. Most of the time you don’t care and frankly don‘t have to. However, every so often you will undoubtedly encounter a library that is not doing what you would expect. A kind of bad behaving library if you will. This is were the trouble usually starts.

During these times, the questions come flooding into your mind. What is causing the issue? Are you calling it correctly? Does it just not work? Should you try to use something else? A road block has presented itself in the otherwise smooth journey of programming bliss and you must get around it and continue on the journey. Spend any time next to a group of developers deeply concentrated in coding work, and it will become painfully clear that navigating road blocks happen on a daily basis. The clues come in the form of expressions like: “That doesn’t make any sense” or “What were they thinking” to more colorful expressions of the four letter kind.

Call it what you want but road blocks are a fact of life and developers have a similar set of terms for there solutions. Some issues will often have a well know solution called a “workaround”. A workaround is like an official response from the library creator in effect saying we screwed up and this is how you get around the issue. If the problem is less known it becomes the task of the developer to creatively implement a way around the problem in what may, depending on the degree of elegance, be referred to as a “kludge”—a kind of less elegant quick and dirty solution. Regardless of how the issue is solved, in the end the developer must make a choice. Economics have a term for this called opportunity cost. By choosing one thing you forgo the potential benefit of choosing the other.

What makes this so fascinating is after lots of workarounds or kludges you could look back at the web of decisions made and wonder if you made the most optimal path. The shear amount of paths through the web of choices is what I call the developer decision space. Two programmers may achieve the same result through different paths but one will be more efficient. The new game becomes navigate through all the pitfalls and landmines with the most optimal path possible.

To get a feel for how this plays out, I have a recent example. For work, I have been tasked with trying to get some of my code to run on the new Microsoft framework called Azure. Azure is a cloud platform that allows you to host your code on Microsoft servers rather than buying your own.

Everything was going smoothly until I hit an issue with uploading my app to the cloud. Staying as light as possible on the technical jargon, I had some loose .NET assemblies in a folder under the root application that were not getting included into the Azure package.

My first attempt to resolve the issue fell into the kludge category. I had found out about a way to create the Azure package unencrypted that would allow me to manually put my assemblies there. This turned out to not work as I received an error when uploading it to the Azure cloud—I contributed this to some check sum they must have in the Azure package. With this part of the decision space blocked, I focused my attention on other ideas. I decided to included the assemblies into the ASP.NET project which solved the issue but caused a new problem—my included assemblies were Silverlight ones and ASP.NET was trying to load them on startup. That forced me to try a different idea of copying the assemblies into the Azure local storage on startup which ultimately worked.

I shared this experience not to show that I am some technical genius who solved a problem but rather to illustrate the complex interplays of the decision space because an interesting thing happened when I uploaded my final solution to the Azure cloud. I got an error; an eerily familiar error about an invalid package similar to the error I received when uploading my first attempt with the unencrypted package. It turns out the reason for the error is a bug in Azure where you have to delete your entire Azure project and re-add it rather that upgrading it. After I did that my final solution worked but I couldn’t shake the feeling that I had missed the most optimal path.

The pestering feeling was the result of discovering new information that I didn’t posses at the time of my first failed attempt. It wasn’t until I was down other paths, trying new things, that this new information came to light which had the potential to open up other paths that I had previously closed.

In my case, I wondered if the error I got on the unencrypted package, my first attempt, was related to the bug in Azure that I discovered later and not a checksum thing after all. If that was the case my first attempt, which was a couple of branches higher in the decision space, was most optimal.

This is what makes programming more of an art than a dull mindless practice most chalk it up to be. It requires the ability to make seemingly unconnected insights and connect them. It is like solving some kind of big puzzle with limited information. As you start to solve the puzzle you learn more and that new information has the potential to unlock a number of issues you thought were previously hopeless.

I am convinced the great programmers have a heightened ability to see these connections. Not only a couple of layers above, like mere mortals, but through most of the decision space. This is even more remarkable when you consider that each decision has an opportunity cost and keeping track of all those decisions that effect other decisions is hard if not close to impossible. But the better these can be tracked mentally the greater the potential breakthrough insight; like Niels Bohr unlocking the secrets of the atom that others failed to see.

Unfortunately, there are only so many Niels Bohrs in the world. However, I believe more developers should put greater effort into understanding the developer decision space. Too often developers make their way through the space by making a decision then immediately relieving their tired brains of the circumstances and implications that justified that decision and move on to the next one. Continuing this pattern a couple more times and it becomes blatantly clear that they have no hope of applying new insights to old decisions and breakthroughs will never happen. Instead what commonly happens is they find themselves wondering in strange roads and thinking to themselves: How did I get here?

I don’t know if this is from pure laziness or simple unawareness. Software development experience tends to help some because after a couple of times traveling strange roads one starts to remember how they got there so they can find their way out. But experience alone doesn’t mean the skill will automatically develop since some experienced developers don’t seem to possess it. If it is unawareness, consider yourself informed. If it is laziness, then you probably haven’t made if this far in the article anyways.

Tags:

Essays

Eight Foot High Corn Field

by breeve 14. December 2009 16:42

During my later college years in Utah, each year around Halloween people became enamored with corn mazes. The most popular time to go was at night and during one of those nights I found myself staring at a dark corn field entrance illuminated by flood lights.

To the scientific thinker a corn maze can be an incredibly frustrating experience. One must navigate through endless corridors to somehow arrive at the one opening on the far end. I didn’t have any strategy in mind–after all my wife told me this was supposed to be fun–so I went with the flow.

After some time in the maze, I was hopelessly lost and not having much fun. I was seeing the same people over and over again. I remembered hearing my dad say once that when lost in a maze just put your hand on a wall and follow it until you eventually arrive at the end. I followed that strategy for a time–not really thinking it through in my head to verify if it really works–until I got bored and did what every corn maze zealot would shrink with disdain at: I walked through the walls until I was outside the maze and made my way to the entrance lights.

Some years later I found myself in my first programming gig writing graphs, meters and knob components for scientific applications in .NET/C# and Windows Forms. My first assignment was to develop a program to benchmark how fast an early prototyped .NET graph would run. I took the assignment in stride–not knowing much about the .NET framework–and implemented an application that worked. My boss on the UI team, to which I belonged, sat down and had a code review with me. It was my first official code review and it didn‘t go well.

I was crushed. However, as the code review went on I began to see where I could improve. After all, I had never developed a professional application before and had honestly never seriously studied one. Most of my projects in school were little in scope designed to prove I knew how to create a linked list, code a quick sort, or create Huffman code trees. They were not designed to be extended or maintained by someone down the road.

In the months that followed, I worked double hard to prove I belonged. I studied the code base and learned all kinds of code patterns I didn’t know existed. Visitor, Observer, and other patterns–that oddly were never mentioned in school–that I would later read about in the famous Design Patterns book. Over time my code got better.

Which brings me back to the corn maze. Like someone navigating a corn maze, when you are starting out as a programmer you tend to make decisions one at a time without any greater context to guide you. Function A throws an exception so that means it must not work so use Function B instead. These decisions are made one by one, in isolation, until a program is assembled into some kind of greater whole that runs. A more senior programmer–who hopefully has the ability to look at the maze from high above–might wonder why you took the long route or worse never arrived at the finish.

If the goal is to become a more effective programmer by seeing the maze from above, how does one get there? Before we explore that goal, we should become comfortable with a sometimes not understood secret in the programming field. Not everyone with experience can see the corn maze from above. Some, try as they might, can never seem to get their heads above the corn. Others find they can get their heads a few feet above the maze which delights them to no end. Fewer still are able to successfully position themselves effectively over the maze.

To reach the programming pinnacle of understanding above the maze, you must position yourself in a work environment with others who are able to at least get a glimpse into the maze. Learn how they think, how they code, and why they make the decisions they do. As you progress in your career it will become vital to learn from those who possess greater powers of levitation. Finding these types are hard, but once you find one–like finding traces of gold–the chances of finding other high performers is high. Good programmers like to work with other good programmers and its in these pockets of intellect where you will make the greatest strides.

Tags:

Essays

The #1 Debugging Lesson

by breeve 2. December 2009 15:45

If you ever find yourself driving along US 20 just Northeast of Idaho Falls, Idaho be sure to look to the South at the small rolling hills because if you look closely, you might be able to pick out some small brown buildings huddled together amidst all the potato farms. It was in those buildings, early into my CS degree, where I learned my first valuable lesson in debugging.

I was sitting in the small Computer Science lab agonizing over a CGI Perl script. I had a problem. My variable was losing its value and I had no idea why. I would print out the value and then print it out again and the value was lost. I checked the code between the printings and nothing was doing anything suspicious to the value. As the hours passed, I was falling deeper and deeper into frustration. I had a depressing thought: How could I possible succeed at this profession when my variables can’t keep their values.

Looking for ideas, I did what every self respecting programmer did before Google existed and looked for some human help. No one was in the lab so I sheepishly made my way over to the professor’s office which was conveniently located just off the computer lab. This, after all, was a small college where professors weren’t self absorbed in their own research and actually talked to students.

Luckily, he was there and willing to help. We pulled up my program—which was a very busy thing with lots of variables. I jumped in trying to explain things to gain credibility. He immediately began to pull out chucks of my program and put them into a new Perl program to run. Then we would verify the piece ran as it should. We continued to do this until the problem was discovered; my variable was misspelled ever so slightly. Perl, of course, is a dynamic language and had simply created a new variable instead of using the one I wanted.

I was embarrassed—especially considering the problem was discovered in under 5 minutes—but I learned a lesson. When something is not working and the program or function has lots of working parts you must simplify. Creating a new program from scratch and pulling over code to run eliminates all the noise from the surrounding code.

I have used this technique effectively countless times since. Still, I am surprised sometimes by professional programmers not using this technique when trouble shooting issues. They will toil with the whole beast of a program trying to fix the issue but it is hopeless because there is just too much noise. Simplify.

Tags:

Essays

Unit Tests vs. Integration Tests

by breeve 23. November 2009 15:32

Unit tests are all the rage today. Talk to anyone who has been reading the unit test literature and they will talk with such passion you might for a split second think you were sitting in a town hall health care meeting.

To understand the arguments for unit tests, we must first understand how code is typically tested. Code is usually tested by an integration test. Integration tests run against the entire stack of dependencies when testing your code. Integration tests are hard to set up because all dependencies must be installed correctly for the test to run. They are associated with long test times. This can be especially bad for continuous integration systems which are brought to their knees by running them on every check in. If a test fails, the bug might be in a dependency or in the component under test making it hard to tell where the issue really is.

Unit tests aim to fix these issues. A unit test is written to test one specific unit of code and differ from integration tests in one critical way; unit tests do not test dependant code. To eliminate the dependencies, they are mocked—often with mock frameworks—to return certain hard coded values to your code. Your code is tested to function properly given the mocked implementation of the dependencies. The tests run fast and if a test fails it is guaranteed to be in your code.

Let’s say I have a library that is responsible for storing data. It stores the data in the cloud by calling a web service. Unit test advocates want the web service dependency eliminated for testing purposes. To accomplish this, the web service dependency is made into an interface—or what is typically called a seam—allowing me to plug in a mock implementation. Something like:

public interface IStorage
{
    public void SaveAsync(string name, Stream stream, Action<Boolean> callback);

Now, I can mock up an implementation of the IStorage interface for my tests without having to go to the actual network. If the behavior of different parameter values into the function are well defined, I can write many unit tests for all combinations of input. Technically, with all combination of inputs covered, when the code goes into production with the real web service implementation it should just work.

This seems splendid. In reality, chances are the real web service has a bug in it because the author doesn’t believe in unit testing or the API behavior was not well defined because you thought passing a null name was ok but apparently it isn’t because the server barfed when it couldn’t find the name in the XML. This brings me to my first negative about unit tests.

Unit tests desperately depend on strong API contracts

There are two types of API contracts: compile time and runtime behavior. The first, compile time, is self explanatory. The second, runtime behavior, simply means the function has well defined behavior for certain values that are passed in. For example, if a function returns 0 for errors and then is changed to return negative numbers for errors it is a behavior change and will break code that uses it.

Behavior changes are devastating for unit tests. Remember, a mock simply mimics what the dependency is supposed to do. If behavior is changing in the dependencies, a unit test is essentially worthless until the mocks are updated with the changed behavior. Worse still, if communication is poor between API owners about behavior changes, out of date mocks can remain in unit tests creating false negatives (tests don’t fail when they should).

When false negatives exist in unit tests—hiding like an explosive IED alongside an Iraqi roadway—there might be 100% unit test coverage in your code but that won’t prevent your boss from hunting your butt down when your code doesn’t work with the product as a whole. Even the slightest behavior changes in an API can bring an entire product down.

Mocking up dependencies is not always trivial

It is possible to mock every external dependency to your component if you have a lot of time. Many components have not just one but many dependencies with large API footprints. To make matters worse, many external components are not easily mocked.

If developers simply apply a brute force approach to mock all dependencies and create wrappers for ones that aren’t easily mocked, weeks will go by without much to show. Meanwhile your boss is in his office looking at budget numbers scratching his head wondering how you are creating customer value.

Unit tests don’t catch funky issues

Because unit tests run against a fake implementation of the dependencies, real product integration issues are never found. I am talking about those hard core issues that everyone pretends don’t exist like having 10 concurrent threads running in God knows what through 15 different API layers implemented in 15 different programming languages.

So what’s a time deprived developer to do?

This is not an easy question to answer. For reasons stated above, even if you have extensive unit tests you will still need integration tests. I believe the balance between the two depends heavily on stability of dependant APIs and how well they are tested. Developing against unstable buggy APIs, cover your butt with integration tests. Stable well tested API’s like the .NET framework, unit test away.

Tags:

Essays

Programmatic Publish for an ASP.NET Project

by breeve 19. November 2009 16:07

I recently needed to automate an ASP.NET project build so that it copies itself to a directory. This is accomplished in Visual Studio 2008 by right clicking the ASP.NET project and selecting Publish. For ASP.NET projects, the Publish command copies all files needed for the site.

After searching some, I found this can be done by using msbuild. Below is my solution that copies all files needed to C:\Data\Projects\TestDeploy. It invokes msbuild to build the web application then uses an existing task called _CopyWebApplication to copy all the files over.

msbuild /t:Build;_CopyWebApplication /p:Configuration=Release
/p:OutDir=C:\Data\Projects\TestDeploy\bin\
/p:WebProjectOutputDir=C:\Data\Projects\TestDeploy
C:\src\Branches\HOA\HomeOwners.Mvc\HomeOwners.Mvc.csproj

Note that this solution does not precompile the site. To precompile, use aspnet_compiler.

Tags:

Software

Measure Twice Cut Once

by breeve 10. November 2009 16:40

Ever wonder what is going on behind the scenes when you install a Microsoft product and it asks: "Do you want to help make Microsoft products better?" I don’t check that option but apparently a lot of people do.

That checkbox allows Microsoft to gather user statistics on how you use the software. Everything from where you click to what features you use to what keys you press. The data is sent back to Microsoft in a user anonymous way and digested by the giant.

The data helps Microsoft learn how customers use the software.  In the brilliant presentation “The Story of the Ribbon” given during Microsoft’s MIX conference, we get a glimpse into how the data is used. During one part of the presentation, a slide displays all the features in Microsoft Word—collected by 3 billion user sessions—and ranked according to use. What they found is most people use only a small fraction of the features. In fact, a lot of features have been used only once and some not at all.

The most used features are what you might expect like copy, past, save, undo, bold, underline, and font size. What is shocking is how fast the feature use falls after those. Ever heard of the feature: “Change shape to lightning bolt”. This was clicked only once, undoubtedly, by some poor soul lost in the maze of menus.

Who is to blame for all these unused features ideas? The answer is not entirely clear as there seems to be some finger pointing. Program Managers typically have the role of proposing new features and in a field of work where a career gets a boost from a well thought out feature, the stakes are high. Unfortunately, most of the feature ideas are little more than gut instinct and turn out to be lemons.

Software engineers, whether they admit it or not, are not innocent bystanders. The thing with engineers is they are smart and they know it. They like to dream up scenarios in their heads all day about how a user is typing into Microsoft Word, somewhere in the world, and has an image that they want to copy into their document exactly 100 times and how frustrating that must be. So they purpose a copy image 100 times feature to their boss. Meeting after meeting ensues until everyone becomes convinced this is a huge usability issue. The feature is implemented and the software engineer gets a great deal of satisfaction knowing the nagging issue is put to rest.

Letting futile features slip into a product can lead to the real possibility of losing touch with customers’ needs. Adding to the fire, once a feature is implemented, the battle internally over the features usefulness is over. We just assume the feature is being used and adding value. So more features are added and added—engineers say recursively—until the program becomes so bloated with features that users don’t know how to use the program anymore and the engineers can’t make simple changes without breaking all the existing features so everyone is complaining and eventually the engineers want to rewrite the product from scratch because then it would be simpler like it was before all those features were added.

The ugly reality is almost all programs follow this lifecycle pattern. What makes the “Story of the Ribbon” presentation so fascinating is that Microsoft noticed this pattern with the Office products and took a step back to look at what features were actually being used by real live paying customers. This of course led to the famous ribbon interface by making heavily used features more prevalent and most importantly removing hardly used ones.

Tags:

Essays

JQuery AJAX and ASP.NET MVC Texas Hold’em.

by breeve 1. November 2009 12:13

I wrote this little app to show how easy it is to do Ajax calls with jQuery. It can be downloaded here. Below is the HTML that defines the tables the cards are a part of. There are three main div sections with id attributes of actions, dealer, and players. These divs define the main regions of the game above.

<div id="main">
     <input type="hidden" />
     <div id="actions">
        <table>
            <tr>
                <th colspan="4">Actions</th>
            </tr>
            <tr>
                <td><button>Deal</button></td>
                <td><button disabled="disabled">Flop</button></td>
                <td><button disabled="disabled">Turn</button></td>
                <td><button disabled="disabled">River</button></td>
            </tr>
        </table>
     </div>
     <div id="dealer">
        <table>
            <tr>
                <th colspan="3">Flop</th>
                <th>Turn</th>
                <th>River</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
    </div>
    <div id="players">
        <table>
            <tr>
                <th colspan="2">Player 1</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
        <table>
            <tr>
                <th colspan="2">Player 2</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
        <table>
            <tr>
                <th colspan="2">Player 3</th>
            </tr>
            <tr>
                <td><img src="/Images/b1fv.png" /></td>
                <td><img src="/Images/b1fv.png" /></td>
            </tr>
        </table>
    </div>
</div>

Server Side

My shuffling algorithm is very simple but let's pretend it is a highly secretive new shuffling technique that we want to keep a secret; so I do it server side. Listed below are the three services and a nested helper class called Game that keeps the state of the game. They are all members of the default HomeController ASP.NET MVC creates when you do a new MVC project.

The first service, the method called Deal, takes the number of players and returns the unique gameID—that the client will use to call the other two services—and the players initial dealt cards. The cards are integers from 1 to 52 that match the corresponding png card images. In the last line of all three services, I take advantage of the ability of ASP.NET MVC to take .NET objects and turn them into Json.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Deal(int playerCount)
{
    Game game = new Game();

    IDictionary<string, int[]> playerCards = game.Deal(playerCount);
    HttpContext.Cache[game.ID.ToString()] = game;

    return Json(new {gameID = game.ID, cards = playerCards});
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Flop(string gameID)
{
    Game game = HttpContext.Cache[gameID] as Game;
    if (game == null)
        throw new InvalidOperationException("can't find game");

    return Json(game.Flop());
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DrawCard(string gameID)
{
    Game game = HttpContext.Cache[gameID] as Game;
    if (game == null)
        throw new InvalidOperationException("can't find game");

    return Json(game.DrawCard());
}

private class Game
{
    private Guid _guid;
    private List<int> _cards;
    private Random _rand;

    public Game()
    {
        _rand = new Random((int)DateTime.Now.Ticks);
        _guid = Guid.NewGuid();
        _cards = new List<int>();
        for (int i = 1; i <= 52; i++)
            _cards.Add(i);
    }

    public Guid ID
    {
        get
        {
            return _guid;
        }
    }

    public int DrawCard()
    {
        int index = _rand.Next(0, _cards.Count - 1);
        int value = _cards[index];
        _cards.Remove(value);
        return value;
    }

    public IDictionary<string, int[]> Deal(int playerCount)
    {
        Dictionary<string, int[]> playerCards = new Dictionary<string, int[]>();
        for (int i = 0; i < playerCount; i++)
        {
            int[] cards = new int[] { DrawCard(), DrawCard() };
            playerCards.Add(i.ToString(), cards);
        }

        return playerCards;
    }

    public int[] Flop()
    {
        return new int[] { DrawCard(), DrawCard(), DrawCard() };
    }
}

Client Side

Listed below is the jQuery and JavaScript. The $().ready function is jQuery’s way of calling me to run code once the page is loaded. In that function I use jQuery's selectors to subscribe to the Deal, Flop, Turn, and River button click events.

The Deal button selector $("#actions button:eq(0)"), for example, says pick the first button under the element with an id=action. This will give me the button labeled “Deal”. I then hook up to the click event of the Deal button. Once the Deal button is clicked, I set all img elements in the page to the blank card to reset the game. Then I disable/enable other action buttons and set up the AJAX call. I set up the AJAX call by telling jQuery to do a HTTP post to the URL '/Home/Deal' and pass it the playerCount.

If there is no error on the server, jQuery will call the function set to the success property. In my case, if there is no error on the server, I set the gameID to a hidden element in the page so other service calls can use it and then go through the returned cards setting the images.

function SetPlayersCards(selector, cards){
    for (i = 0; i < cards.length; i++) {
        var imgPath = "/Images/" + cards[i] + ".png";
        selector.eq(i).attr("src", imgPath);
    }
}

$().ready(function(){

    //deal button
    $("#actions button:eq(0)").click(function(){
        $("img").attr("src", "/Images/b1fv.png");
        $("#actions button:gt(1)").attr("disabled", "disabled");
        $("#actions button:eq(1)").removeAttr("disabled");
        $.ajax({
            type: "Post",
            url: '/Home/Deal',
            data: {playerCount:3},
            dataType: "json",
            success: function(data) {
                $("#main :hidden").val(data.gameID);
                //player 1
                SetPlayersCards($("#players img:lt(2)"), data.cards[0]);
                //player 2
                SetPlayersCards($("#players img:gt(1):lt(2)"), data.cards[1]);
                //player 3
                SetPlayersCards($("#players img:gt(3)"), data.cards[2]);
            }
        });
    });
   
    //flop button
    $("#actions button:eq(1)").click(function(){
        $("#actions button:eq(1)").attr("disabled", "disabled");
        $("#actions button:eq(2)").removeAttr("disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/Flop',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var flopCards = $("#dealer img:lt(3)");
                for(i = 0; i < data.length; i++){
                     var imgPath = "/Images/" + data[i] + ".png";
                     flopCards.eq(i).attr("src", imgPath);
                }
            }
        });
    });
   
    //turn button
    $("#actions button:eq(2)").click(function(){
        $("#actions button:eq(2)").attr("disabled", "disabled");
        $("#actions button:eq(3)").removeAttr("disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/DrawCard',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var imgPath = "/Images/" + data + ".png";
                $("#dealer img:eq(3)").attr("src", imgPath);
            }
        });
    });
   
    //river button
    $("#actions button:eq(3)").click(function(){
        $("#actions button:eq(3)").attr("disabled", "disabled");
        var gameID = $("#main :hidden").val();
        $.ajax({
            type: "Post",
            url: '/Home/DrawCard',
            data: {gameID:gameID},
            dataType: "json",
            success: function(data) {
                var imgPath = "/Images/" + data + ".png";
                $("#dealer img:eq(4)").hide().attr("src", imgPath).fadeIn(1000);
            }
        });
    });

});

Using fiddler, I can see what is sent on the wire when the Deal button is pressed:

/****Request****/

POST /Home/Deal HTTP/1.1
Host: localhost:49220
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7 (.NET CLR 3.5.30729)
Accept: application/json, text/javascript, */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:49220/
Content-Length: 13
Cookie: filterTags=
Pragma: no-cache
Cache-Control: no-cache

playerCount=3

/****Response****/

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Sun, 01 Nov 2009 16:58:39 GMT
X-AspNet-Version: 2.0.50727
X-AspNetMvc-Version: 1.0
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 94
Connection: Close

{"gameID":"2ce44c42-20b8-4819-b796-3f07c305fc9e","cards":{"0":[47,32],"1":[18,9],"2":[16,41]}}

By looking at the Json in the response, I can verify that the gameID is a GUID and the cards for all three players are there. The other two services send the data in different Json formats but the same pattern is there.

Tags:

Software

Your Business Made a Mistake, Admit it

by breeve 27. October 2009 16:36

On Saturday, September 14th 2009, the rains started light at first and then increased rapidly to a steady downpour. For one homeowner, the rain was problematic because water begin to leak inside soaking drywall. A frantic call was placed to the roofing company’s emergency line. No response. Sunday came around, still no response.

On Monday, the unhappy homeowner placed a scathing post on the neighborhood Yahoo newsgroup which has 1900 subscribers. The post gathered quite a following with 18 responses in a 2 day period. In it, many were saying they were considering using the company but now weren’t so sure.

This post was of interested to me because we were in negotiations with the company to get our roof replaced. When the homeowner first posted about the leak on Monday, I was shocked and felt for the homeowner. What if that happened to me? Was I going with the right company?

Returning back to work, the company got the phone message. Crews were dispatched to the home to fix the issue. The president of the company, who just happens to live in the neighborhood, posted on the forums that Tuesday detailing how his company had screwed up badly and what they were doing about it. Apparently, the emergency phone system had been down and the leak was caused by a misplaced metal vent.

The President’s reply on Tuesday was refreshing. He didn’t make excuses or say there wasn’t anything he could have done because the phone system was out and his wife was yelling at him that weekend for golfing all day with his buddies and how we should all stop our whining because there were plenty of other roofing companies out there and just see how much better they could do if their phone system was out and everyone was bad mouthing them on the newsgroup. Instead, he detailed how he screwed up and what steps were being taken to fix the homeowner’s house and how in the future this problem would be prevented.

After reading the post, an odd thing happened. I found myself trusting the company even more than I had before. Somehow in the post I saw the human side of the business and how their owner cared about roofs. We all know businesses screw up–it is how they react to their screw ups that matter.

I am not saying if you own a business to go out and purposely do a bad job and then swoop in like Tom Silva on steroids and fix it. What I am saying is the world is in desperate need to see the human side of a business.

Speaking with the sales agent about the post a week later, he said jobs inquiries have actually picked up. On my street, I noticed two new roofing signs using the company. Sometimes it literally pays to be honest.

Tags:

Essays

Symbol Server Setup

by breeve 23. October 2009 15:57

I went through the process of setting up a symbol server for our team at work. We develop code in .NET. Different teams were exporting .NET dlls for my team and others to use. We wanted the ability to debug the source. Without the symbols server we would have to sync the source to the correct version of the exported dll and build just to debug. This went on for some time and after much complaining, I looked for other options.

A symbol server ensures that you are able to get pdbs and source code for your dlls or executables from any machine.

Let’s get started

To get started, you will need to have your source stored in source code control. The source control products supported by symbol server out of the box are: Perforce, Visual SourceSafe, Team Foundation Server, Concurrent Versions System, and Subversion. Next, download the Debugging tools for windows which installs to the Program Files directory by default.

Now that you have the debugging tools installed, let’s get familiar with some of the tools you will need to use:

  • C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\ssindex.cmd - This is a batch file wrapper around perl scripts that support the souce code control products listed above. This tool is responsible for going through the pdbs and changing the source code file paths inside the pdbs to paths that can be found in source code control. This process is called indexing the pdb.
  • C:\Program Files\Debugging Tools for Windows (x86)\symstore.exe - This tool adds the indexed pdbs and dlls to a directory of your choosing. The tool creates version specific directories where the pdbs and dlls are stored. This directory is commonly set up as a file share. This location is where others will point their hungry Visual Studio to download the much needed symbols.
  • C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\srctool.exe - This is used mostly to find out if your pdbs were indexed correctly.
  • C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\srcsrv.doc - Documentation. Yes, we all read documentation.

Index the pdb

I am going to create a simple windows forms app that everyone on my team is itching to debug. I have checked it into perforce (the source code control we use) and it is ready to be indexed and added to the symbols store.

Before I run ssindex.cmd I must edit C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\srcsrv.ini to tell it where my source code control server lives. It should be straight forward because it is a well documented file.

Now I am ready to run ssindex.cmd like this:

ssindex.cmd -system=p4 -symbols=C:\p4root\addon\Crystal\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug -source=C:\p4root\addon\Crystal\WindowsFormsApplication1\WindowsFormsApplication1 /debug

The -symbols switch is the directory where my dll and pdb are. The -source switch is the root directory of my source. The /debug just tells it to be verbose (print out stuff). I can tell this worked correctly because it says it wrote the pdb file. For the curious, the way this works is ssindex.cmd calls the command “p4 have” for each source file on the local disk. This returns the path in perforce and version of that file. This is then written into the pdb. We can see these paths if we run srctool.exe on the pdb like:

srctool c:\p4root\addon\Crystal\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug\WindowsFormsApplication1.pdb

The first thing to notice is the bottom line which reports that 5 source files where indexed. Above that it lists each file that was indexed along with the “p4 print” command and the perforce path. When you are debugging, it will pull the source from perforce using the print command.

Now that I am confident my source files were indexed properly it is time to add them to a shared folder where my greedy debuggers can get started.

Add to Symbols Store

The tool to use here is symstore.exe. I use it like this:

symstore add /r /f C:\p4root\addon\Crystal\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug /s C:\Symbols /t app

The symbol store is nothing but a directory structure. The /s switch specifies the root folder to put symbols under. Symstore reports that I have successfully added 3 files to the symbol store. In this case, I am storing the symbols in the directory c:\Symbols as described above by the /s switch. If I look there I will see three directories for each symbol and an admin directory used by the tool for bookkeeping.

If I look inside the pdb dir (the directory highlighted above) I see a guid looking directory. Inside the .exe there is a matching GUID which is how the .exe finds the correct pdb. For the devoted who want to see that value inside the exe or dll, you can run dumpbin /headers on the exe or dll and look for something like:

Debug from another Machine

I want to debug this exe on another box. Let’s fire up Visual Studio 2008 and add a few things. Check enable source server support in the options dialog found in Tools>>Options as shown below.

Go into the symbols option under debugging and add the path to the share where the symbols are located. Also add the directory on your machine where the symbols will be downloaded to. Hit OK.

Now, I will run the exe on my machine. Remember, all I have is the exe and nothing else.

I want to debug what happens when I press the button on my buggy application above. I attach to the exe using Visual Studio. Make sure you have manged code selected in the attach to box.

After I click attach, I can see the symbols have been loaded for the exe using the output window. There is also a modules window accessible from Debug>>Windows in Visual Studio. This will list every dll and whether symbols were loaded. If you right click on a loaded module there are some options to see where symbols were loaded from. This is good for debugging when something goes wrong.

I have successfully attached at this point and now it is time to set a breakpoint using the breakpoint window for the button click event handler. The breakpoint window is under Debug>>Windows>>Breakpoints. Once you have the breakpoint window up, click New then break at function.

When I click on the button in the exe, I get a prompt telling me that VS wants to download the source. I click run.

The source is downloaded and I am debugging just as if the source was built on my machine.

Silverlight Gotcha

If you are wanting to do this with silverlight you will have to add the registry key below for every machine that wants to pull symbols from the symbol store.

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\AD7Metrics\Engine{00000000-0000-0000-0000-000000000000} 
Set RequireFullTrustForSourceServer to 0 (REG_DWORD)

I spent a week with Microsoft support on this one.

Tags:

Software

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

About Me

I am a Software Engineer with 8 years experience developing and releasing software products. I started developing in C/C++ then moved into .NET and C# for the last 6 years and have tech lead multiple projects. I have developed products in Windows Forms, ASP.NET, Silverlight, and WPF. I currently reside in Austin, Texas.

Currently Reading