Sunday, October 31, 2010

List<T>: .Clear() vs. new; Variable Passing in C#

A colleague asked me a C# question on Twitter:

C# - list<T>.clear() or list<T> = new list<T>()?

I had some trouble responding in 140 characters, so I replied with a few tweets that really didn’t do the question justice. There are a few issues at hand: style and correctness.

First, when it comes to style, the best guiding principle in programming is clarity. You should express your intent as clearly as possible. If .Clear() represents your intent better than newing up a List, go with it.

The bigger issue is, of course, correctness. Who cares if your code expresses your intent if it’s wrong? In most situations, it probably doesn’t matter which you choose because each will have the effect you wanted. However, these have a few differences. Off the top of my head there’s the pointer/reference issue (noted in my first tweet) and I’m betting something to do with Capacity which I’ll test in a minute.

C# doesn’t really have pointers per se. Instead, it feels like every non-value type thing—object (e.g. List)—gets passed around kind of like you’re using pointers, you just don’t know it. This becomes really important when you start newing up objects in helper methods.

For example, what do you suppose happens in this code?:

void Main()
{
    string A = "aaa";
    string B = "bbb";
    
    Console.WriteLine("Before Swap: A is '{0}', B is '{1}'", A, B);
    
    Swap(A, B);
    
    Console.WriteLine("After Swap:  A is '{0}', B is '{1}'", A, B);
}

void Swap(string a, string b){
    string tmp = a;
    a = b;
    b = tmp;
}

Does it work? Of course since I’m asking, it probably doesn’t! Here’s the output:

Before Swap: A is 'aaa', B is 'bbb'
After Swap:  A is 'aaa', B is 'bbb'

The reason this doesn’t work is that what we’re passing to the Swap function are just a couple of pointers to the memory location that holds the string data “aaa” and “bbb”. The objects are not passed by reference, but rather references to the objects are passed by value.

It seems nitpicky, but it really is important to understanding how this stuff works. Here’s the correct implementation:

void Main()
{
    string A = "aaa";
    string B = "bbb";
    
    Console.WriteLine("Before Swap: A is '{0}', B is '{1}'", A, B);
    
    Swap(ref A, ref B);
    
    Console.WriteLine("After Swap:  A is '{0}', B is '{1}'", A, B);
}

void Swap(ref string a, ref string b){
    string tmp = a;
    a = b;
    b = a;
}

Output:

Before Swap: A is 'aaa', B is 'bbb'
After Swap:  A is 'bbb', B is 'bbb'

The only difference is the “ref” keyword added to the method and its call. C# does a great thing for us by making this very explicit (probably because it’s rarely what you actually want).

So back to the original question. “.Clear()” or “new” for a List<T>? As I’ve shown, once the style issue is resolved, it’s merely a matter of correctness. Suppose you had a method for refreshing a list. Which of these is best?:

// return { "1", "2", "3" }
void RefreshNew(List<string> list)
{
    list = new List<string>() { "1", "2", "3"};
}

// return { "1", "2", "3" }
void RefreshClear(List<string> list)
{
    list.Clear();
    list.Add("1");
    list.Add("2");
    list.Add("3");
}

Hopefully it’s starting to sink in at this point that the first one—RefreshNew—doesn’t actually work. Here’s what happens:

void Main()
{
    List<string> Strings = new List<string>(){ "a", "b", "c" };

    Console.Write("Before Refresh: ");
    Strings.ForEach(x=>Console.Write(x));
    
    RefreshNew(Strings);
    Console.Write("\nAfter Refresh New:   ");
    Strings.ForEach(x=>Console.Write(x));

    RefreshClear(Strings);
    Console.Write("\nAfter Refresh Clear: ");
    Strings.ForEach(x=>Console.Write(x));
}

And the output:

Before Refresh: abc
After Refresh New:   abc
After Refresh Clear: 123

Again we see that repointing references that were passed by value does not have the desired effect. We could update the first implementation to pass its list as a ref parameter but I discourage that. I like the .Clear approach instead. The reason .Clear/.Add works is that you are working with the right object by the time you start manipulating it. You could also sidestep the whole problem by changing the method name to “Rebuild” and have it return a new list.

If that’s just crazy and not making any sense to you, I suggest checking out some of the textbook diagrams that explain this, or read this excellent post by Jon Skeet on the same topic. Both do a far better job at this than me.

This is a topic that was incorrectly taught (or understood) to me in college, so don’t be surprised if you learn something new as I did.


There’s that tiny issue of Capacity that I hinted at before. Internally Lists are stored as arrays or whatever—it doesn’t really matter. For performance reasons, since arrays are immutable, they are allocated larger than necessary. This way, you can add elements to them without reallocating the entire thing. The system makes a trade off in avoiding allocating too much by doubling the size of the allocation every time it runs out of space. I suspect that clearing a list does not affect its capacity. Let’s see:

var List = new List<int>();

Console.WriteLine("Building list...");
for(int i = 0; i<9; i++){
    List.Add(i);
    Console.WriteLine("List count: {0}, list capacity: {1}", List.Count, List.Capacity);
}

Console.WriteLine("\nClearing list...");
List.Clear();
Console.WriteLine("List count: {0}, list capacity: {1}", List.Count, List.Capacity);

And the output:

Building list...
List count: 1, list capacity: 4
List count: 2, list capacity: 4
List count: 3, list capacity: 4
List count: 4, list capacity: 4
List count: 5, list capacity: 8
List count: 6, list capacity: 8
List count: 7, list capacity: 8
List count: 8, list capacity: 8
List count: 9, list capacity: 16

Clearing list...
List count: 0, list capacity: 16

So it’s as expected: clearing the list does not affect the list’s capacity. This implies that if you are creating a new list which is expected to significantly differ in size, than “Clear()” might not be the way to go. It’s probably a micro-optimization, but I’d say the doubling nature of lists is probably something worth knowing.

Ten Commandments of Trick or Treat

  1. Thou shalt wear a costume
  2. Thou shalt say “trick or treat” and “thank you”
  3. Thou shalt take one piece of candy
  4. Thou shalt not complain about candy selection
  5. Thou shalt walk, not drive
  6. Thou shalt not collect candy for thy absent brother (sister, baby, friend, etc.)
  7. Thou shalt not revisit and thereby re-treat the same house
  8. Thou shalt not be a teenager
  9. Thou shalt not partake of treats until thou hast finished
  10. Thou shalt share treats with thy father

thing 1thing 2

Friday, October 29, 2010

Opt Out Means Opt Out

I got an email from Best Buy just now informing me (with way more words than necessary) that I am now opted in to their marketing material, even though (as they acknowledge) I chose to opt out. Here’s the email (highlighting added):

You are receiving this e-mail because Best Buy® has changed the way marketing e-mail communications are managed. Currently you are registered to receive marketing communications from the Reward Zone® program, but have opted to not receive marketing communications related to Best Buy generally.

We are writing to let you know that Best Buy has changed the way it manages opt-out preferences. Going forward, opting out of either Reward Zone or Best Buy marketing communications will result in being removed from both marketing lists. In order to honor your request to receive Reward Zone program e-mails containing special offers, invitations to events and account updates, you have been opted-in to receiving Best Buy marketing communications generally. If you do not wish to receive these e-mail communications, you will need to update your opt-out preferences. Please note that if you do opt-out of Best Buy marketing communications, you will also opt-out of marketing communications from Reward Zone. You would continue to receive e-mails regarding your accounts and purchases at Best Buy, services with Geek Squad and Reward Zone certificates.

Thank you for being a valued Reward Zone member.

I appreciate that they’re letting me know—I really do. What I don’t appreciate is that they’re changing my preferences in the first place. It’s a pretty silly thing to be irked by, for sure, but there’s just something about a company deliberately not doing the right thing that bugs me. (It would also help if the opt-out link they provided was actually up.)

This would have been far better if they had decided to lean the other direction and say, “You opted out of marketing so we’re going to honor that. If you still want your RZ emails, you’ll have to opt in to everything.” Of course if they were going to do that, they’d simply decide to do nothing because it’d be a loss for them!

It’s times like this that a simple mantra helps, like Google’s informal slogan “Don’t Be Evil”. I’m not suggesting anything about Google, just that I love “Don’t Be Evil”. Best Buy’s action must have given people within the organization pause and a simple test like that could have helped.

Best Buy may also wish to review what happened when Yahoo did something similar in 2002.

imageI’m reminded of a related case with StackOverflow, the awesome Q&A site. Way back in February of 2009 they retroactively enabled email notifications for a subset of users:

“We have taken the liberty of turning on the ‘opt-in’ email flag for any users who haven’t been on the site for more than 60 days, and have a valid email address and a reputation score of at least 25. We did this to reconnect with Stack Overflow users who have been gone so long they might not know they have a bunch of new answers and comment replies to their posts. (Not to mention all the awesome new features on the website since then.) That was about 3,000 users in total.”

That bothered me. The SO creators, Jeff Atwood and Joel Spolsky are very well respected for being The Good Guys. Stackoverflow, after all, was built to be the antithesis of the evil “hyphen site”. They wanted to be better. And they are better!

I’d bet lunch that this decision wasn’t reached without hesitation and that a tiny piece of whoever made it regrets it.

That’s a pretty stark contrast between how I perceive Best Buy and StackOverflow right there. I am a total unashamed SO fan boy. I believe in them and the cause. I believe they have uncommon things like feelings, and consciences. I don’t personify Best Buy with any of the same flattering characteristics.

Tuesday, October 26, 2010

Target Is Crazy, And Random Pictures

We love Target. It’s cheap like Walmart but clean, with happy and friendly employees (and customers), and fast checkouts. Sometimes they’re crazy, though. While they’re famous for their buying in bulk penalties, we discovered some odd instant substitutes recently:

IMAG0429IMAG0430IMAG0431IMAG0432

So I guess if you wanted something like this:

image

Instead of getting one of the other, almost identical comforters, you should get this:

IMAG0437

It looks like we have a few more years before these machines take over and we’re totally screwed. Oh wait.

In other news, here’s a collection of random pics from my recent travels:

IMAG0031

(I wasn’t moving when I took that, but she was while texting.) Don’t text and drive, kids. It can wait.

IMAG0149-1

I’m not sure that listing three things it cleans is enough to really call it “Triple ACTION”. If that’s all it takes, I’m totally coming out with Quadruple ACTION soap. It cleans hands, dishes, sinks, and makes your sponge stinky.

While at the airport:

IMAG0191

I saw this next to my airplane:

image

(enhance!)

image[7]

I hope they’re not using disks (on the dashboard) for anything related to my plane.

Changing venues:

IMAG0200

Um, yeah this is pretty gross to look at. Now imagine what problem they were trying to solve with all that caulk…

My hotel was very nice but I admit that this was a little creepy:

IMAG0205

Later I saw this bumper sticker, which I loved:

image

When I got home the city decided to remove my street. Luckily they put it back later.

IMAG0289

I returned to the office just in time to participate in a strange contest:

IMAG0291

Why is that a question?

Another example of crazy target pricing (200 gels is cheaper than 110 gels):

image

The best part is that Sarah found them in another isle for $5. Identical.

We were in Cereal City so bizarre sites like this were common:

image

Antivirus being helpful and totally not ruining the Weather Channel, though to be fair, it’s accuracy is about on par with the forecast (zing—weather joke!):

IMAG0375

And finally, my favorite item of the month:

IMAG0401

That’s right, folks, alcoholic whipped cream.

IMAG0404[4] IMAG0405[3]

Oh, and it’s sold with the apples. Naturally.

IMAG0402

Sunday, October 24, 2010

Project Noodle: Initial Database Design

Tip! If you are disoriented, you might want to browse more posts about this project

First off: this will change as I go.

Here’s my first pass at the database tables for Project Noodle:

image

It’s definitely not fully normalized (e.g. Interviews.{CriterionValue1,2,3})—some of that’s intentional. Database design is always a balance.

At this stage, I am really interested in two key things:

  1. Does my schema actually capture all the information I think I need to store?
  2. Are the queries I expect to write easy to write?

And so, being the semi-disciplined person I am (or at least when I’m blogging about a recommendation), I will check these two things.

Does my schema cover everything?

In another window I brought up the mockups. Right off the bat, I’m finding a gap in what I need to capture and what my schema supports:

  • I don’t have anywhere to store why, when, or where a candidate is to be interviewed
  • I don’t have anywhere to store a candidates picture, or resume
  • I don’t have anywhere to store a candidates actual offer or hire status

So let’s fix that together. It looks like I need something akin to an “interview master” table which will store the details of a block of interviews. This can’t really go in the candidates table since a single candidate could come in multiple times.

On the other hand, since HR will likely want to schedule interviews in advance, perhaps I could just create an interview record for each user when the interviews are setup. Then, I can group them by date on the Upcoming Interviews screen.

Having been down this road before, I’m opting for a master table. I’ve found that it helps solve a lot of problems that may not be obvious just yet and it’s probably The Right Thing To Do. Here we go:

image

A more enterprisey solution would probably break out the ratings from the Interviews table, move the photo and resume columns from Candidates into a full blown files table, add a Locations table to hold conference rooms, and a CandidateTypes table for values like “SW Coop”.

I’m opting for the much simpler structure above, deferring that refactoring until a significant problem demands that they be made.

Are my obvious queries easy?

I know I want to display a list of interviews with who/when/why/where information. It looks like I can get all that from the InterviewMasters and Candidates tables. It now occurs to me that I don’t have a way to archive interviews. I’ll ignore that for now and come back to it if it becomes an issue.

I also need to be able to CRU interview details (rankings). This should be very simple with the Interviews table and a couple easy joins.

Finally, I need to be able to aggregate interview feedback from many interviewers into a single view. This also looks very straight forward.

It looks like I haven’t gone overboard with the normalization because I don’t see any red flags so far. I’ve also opted to do no history or versioning, which has helped to avoid a lot of complexity with all of the above queries.

Next

Next up is probably some actual screen development and code.

Some Thoughts on Antivirus and Why I Now Use It

In my experience, antivirus software has been a near-total waste of time and resources for most people, especially in IT. I have two major issues with it:

  1. It doesn’t work well
  2. It spreads FUD

When I’ve seen it detect malware, it often couldn’t do anything about it, and most of the time it’s just nagging users with false positives or completely inappropriately technical messages.

Viruses so far have been really disappointing on the 'disable the internet' front, and time is running out.  When Linux/Mac win in a decade or so the game will be over.

Although, it used to be very important. There once was a time where our poor Windows machines were basically teeming cesspools of digital disease with virtually no defenses. These things needed help. It’s laughable to recall that Windows XP pre SP2 and recent versions of OS X didn’t even have a firewall enabled by default.

You’d be in better shape if you had a hardware router since it probably acted as a firewall, too, but those without and most dialup users would be very vulnerable while online.

But today, we have automatic updates, multiple firewalls, and better security all around. With the infrastructure improvements, the attack surface for most people narrowed considerably. That is, your computer could contract a virus through the following means:

  1. Physical access (e.g. infected USB stick)
  2. Network access
  3. Web browser
  4. Downloading crap off the Internet
  5. Opening random email attachments

Vector one, physical access, is pretty easy to neutralize by refusing to share thumb drives.

Vector two, network access, is pretty well handled by using the built in firewall that is enabled by default on most systems. This itself has done wonders to stunt the spread of viruses on large corporate networks.

Vector three, the web browser, is mostly mitigated by using a decent browser (i.e. not Internet Explorer 6 or 7).

Vectors 4 and 5 are the big ones. These directly involve humans and as such are hard to control. If you download junk from random websites, you’re just asking for it. Instead, you should download only things that don’t seem scammy (e.g. not computer speedup tools, illegally sourced software, or money making schemes) from sites that you trust. Similarly, if someone sends you something suspicious (e.g. “chek out dis pic.scr”), hit delete! If a colleague sends you something out of the blue, ask for confirmation that they actually sent you something before opening it to make sure they sent it.

So that’s how I operated without antivirus software for the last 10 years or so. I used a firewall, a decent browser, and I didn’t open random crap. I didn’t use antivirus. I didn’t get viruses (I checked occasionally).

Then I saw this PSA:

image

It seems that drive-by attacks are occurring with no user error necessary in up-to-date Google Chrome, via a vulnerable Java plugin. I checked my laptop with Microsoft’s free, well-rated virus scanner Security Essentials and found that I was infected. I became infected via my browser (really a plugin, but that’s splitting hairs) and that left me a little shaken. Fortunately, the scanner politely cleaned up the infection without making a big fuss, which is both more effective and less braggy than I remember of other scanners.

virusI also followed the advice of the above tweet (disabled the rarely used Java plugin), and now I run the free virus scanner regularly. This is really a change in direction for me, and not a turn I’ve taken lightly. And while AV tools will rarely protect us from emerging threats, they do detect thousands of old viruses pretty reliably. The ease of use of Security Essentials and it’s non-FUDiness certainly helped me change my mind.

In conclusion, I am now updating my antivirus position by recommending that all Windows users (not just casual users as I previously recommended) run a free antivirus program like the Microsoft option I mentioned above.

Herein Lies A Graphic Description of Toilet Punishment

You’ve been warned. I’d never been a knowing victim of food poisoning until yesterday. Yesterday. It actually hurts a little bit to remember the pain that was 24 hours ago.

Let’s go back. If this were a soon-to-be-canceled television show, you’d hear that harpsichord noise they make when they head into the past to remember something. We (the family) were all out at the park doing pictures and the day got away from us (I’d give a hat tip to our amazing photographer but I’ll wait for a better context). Wife had the amazing idea to pick up some Main Moon Chinese Food on the way home, which is a special treat for me (I love it). We had no idea what was to transpire a mere 45 minutes later.

sad toiletWe got home, got the kids some food and settled in to chow down. About 30 minutes into the meal, wife had some delicate intestinal issues which required immediate attention and ventilation. While cleaning up dinner (15 minutes later), I, too, suddenly found myself punishing the porcelain throne with wave after wave of shockingly intense and worrisome damage. It was very strange. I found myself back in the same position 15 minutes later. The nausea continued for a couple of hours, but my poor commode suffered no more. For some very odd reason, wife couldn’t stop laughing for a solid 10 minutes. I honestly don’t get it. I tried explaining to her that this could be pretty serious but she’d have none of it. I guess toilet humor is her thing…? (All these years I’ve been trying too hard to be funny.)

After doing a little Internet research wife discovered that we might have food poisoning but that we probably weren’t going to die (optimism!). We concluded that complaining to the restaurant was unlikely to go well so we dropped that idea, too. It was around this time that we made pumpkin milk shakes. They were awesome (really!).

shakeShortly after finishing my extra large pumpkin ice cream milkshake I did my own Internet research (I was still feeling a little nauseated). I found something wife neglected to share about what to do if you think you have food poisoning:

  • Don't eat solid foods until the diarrhea has passed, and avoid dairy products, which can worsen diarrhea (due to a temporary state of lactose intolerance).
  • Drink any fluid (except milk or caffeinated beverages) to replace fluids lost by diarrhea and vomiting.

So yeah, we were drinking soda before and after dinner and each had milkshakes. Nice.

And the kids? Fortunately for us, Thing 1 whined her way through dinner and didn’t actually eat anything and Thing 2 slept. Each child ate quite a bit of a substitute dish after all the above occurred. This (them not getting explosive diarrhea) is the best thing that has happened to us this year, without a doubt.

That’s really just a long way to say that we need a new Chinese Food place in the Kent  area. Any suggestions?

The Best Computer Upgrade Ever: Solid State Disk

Last Fall I upgraded my laptop’s hard drive to an Intel X25-M G2 solid state hard drive. Now about a year later, it is the single best upgrade I have ever made.

At the time I had the choice between a brand new laptop or an SSD (SSDs were much more expensive at the time) and I definitely made the right decision. In fact, if I was offered a new laptop today, I wouldn’t take it unless I could take my SSD with me.

Why is this thing so awesome? It has no moving parts like it’s rotating platter, savage ancestors of days old. As a result, it’s ridiculously fast. Here is my simple benchmark (check Google for dozens of much more rigorous, helpful benchmarks).

This is a random read of various file sizes before the SSD upgrade:

before-1

Getting about 50 IOPS, 20ms, and awful MB/s. And after the SSD upgrade:

ssd-rand

These are on the very same system. The before shot was made an hour before I cloned it onto the new SSD. Here’s a simple summary between the two:

  More Ops Less Time More MB/s
512b 17228% -99% 17125%
4kb 12556% -99% 12482%
64kb 3149% -97% 3126%
1mb 457% -82% 449%
random 660% -86% 638%

Yes, that’s a 17 thousand percent improvement in reading 512b blocks with a corresponding 99% reduction in access time. Even in the SSD’s worst case of large files, which traditional disks tend to do well with, the improvements are 400%-500%.

Just for kicks, I repeated the test again today (1 year later) and got similar (if just slightly degraded) results:

image

In summary, I went from crappy {iops, mb/s, ms} to zomg! {iops, mb/s, ms}.

Interested? The good news for you is that these puppies are faster and even more affordable today. Shop around and check the latest benchmarks to see what models are the best (this info changes so often that I’m not linking to anything specific).

If you want a much cheaper shot in the arm, a memory upgrade for those with less than 2gb already is a sure fire way to boost performance (we’re talking less than $30-50).

Saturday, October 23, 2010

The Konami Code In JS

Goal: detect the Konami code when entered and do something cool. If you're not familiar with the Konami, do some research. If you have no interest in code, you can enter the Konami code on this site, chuckle or balk, and go on about your day.

To implement this we’ll need two basic components: a sequence detector to figure out when the code has been entered, and the "something cool".

Sequence Detector

First, we need to determine what sequence we're interested in in terms of key codes:

var DesiredSequence = [
              38 // up
            , 38 // up
            , 40 // down
            , 40 // down
            , 37 // left
            , 39 // right
            , 37 // left
            , 39 // right
            , 66 // b
            , 65 // a
        ];

And then we need a very, very simple state machine to keep track of where we are in the sequence:

var SequenceIndex = -1;

$(document).keydown(function (e) {
    if (e.which == DesiredSequence[SequenceIndex + 1]) {
        SequenceIndex++;

        if (SequenceIndex == DesiredSequence.length - 1) {

            // do something cool (not shown yet) and reset the counter
            SequenceIndex = -1;
        }
    }
    else {
        // if user hit the first key, keep it, otherwise, start back at nothing
        SequenceIndex = e.which == DesiredSequence[0] ? 0 : -1;
    }
});

This is about as na├»ve as it gets and would not be appropriate for sequences with large repeating blocks. But we don’t have that so we’re good. (For example, if the user hits “up, up, up”, we could argue that the next key should be an “up” or a “down”, or even “up, up”, depending on how much history we consider. A serious state machine would take care of this but we do none of that here, because we’re not being very serious.)

So let's fill in that do-something-cool block.

Something Cool

A lot of sites do neat things for this code. My take on it will involve Ninja, just like Google Reader, but I'll put a different spin on it. Here's the code:

if (SequenceIndex == DesiredSequence.length - 1) {
    SequenceIndex = -1;
            
    // replace all headers
    $('h1,h2,h3,h4').text('Konami Strikes!');
    $('body').css('background-color', '#E51E2B');

    // add shurikens
    $('<img/>')
                .appendTo('body')
                .attr('src', 'ninja.png')
                .css({
                    position: 'fixed',
                    left: '-126px',
                    top: '400px'
                })
                .animate({ left: '0px' });

    for (var i = 0; i < 20; i++) {
        $('<img/>')
                    .css({ position: 'fixed' })
                    .appendTo('body')
                    .attr('src', i % 2 == 0 
                        ? 'shuriken-1.gif' 
                        : 'shuriken-2.png')
                    .each(function () { DoFlyby(this); });
    }
}

That leverages this function, defined elsewhere:

    function DoFlyby(elem) {
        var Depth = .25 + Math.random() * 5;

        $(elem).css({
            left: (-1 * Depth * 32) + 'px',
            // the ninja is centered at 308px, so this should be offset 
            // up by half the images width
            top: (308 - (.5 * 32 * Depth)) + 'px',
            width: 32 * Depth + 'px'
        })
        .animate({
            left: '2000px',
            top: -200 + Math.random() * 1200 + 'px' // end b/t [10,90]% of the screen
        },
            10000 / Depth, // big stars move faster than slow stars
            function () { DoFlyby(elem); }
        );
    }

Of course you could just give it a try on my site. (By the way, if you’re using a weaker browser like IE7, this might look awful.)

This is an easter egg.

Friday, October 22, 2010

Building a Prettier Search Box

Note: if you’re reading this off-site (feed reader, facebook, etc.) and it looks funny, click through for a better view.

Let's take a quick look at a typical search box:

It's boring, plain, and takes up a lot of space. But, the code is about as simple as it gets:

<input />
<
input type="submit" />

We can improve things a great deal by adding a shiny graphic:

That's most easily accomplished by changing the input type from "submit" to "image" like so (this could be accomplished in CSS with a bit more effort):

<input />
<
input src="search.gif" type="image" />

Now we're talking. Let's go a little further:

Here we've overlaid the image with the box for a very clean effect. This uses the same HTML (I just added classes) but adds just a teenie bit of CSS:

/* overlay search button on the actual box */
input.search-button { position:relative; left: -20px; top:1px } 

/* pad out the input so a user's search won't go under the search button */
input.search-box { padding-right:20px; }

Clean and simple.

It has one minor drawback in that it doesn't support CSS sprites. If you want that, you'll need to use one of the background-image tricks at the link I mentioned earlier.

Of course once Google's Instant Search sweeps the web this will all be a cow's opinion: moo.

Wednesday, October 20, 2010

First Look at NuPack: A VS-Integrated Package Management Tool

Installation

Couldn’t be easier:

First Run

Fire up Visual Studio 2010 and open the Package Manager Console under View > Other Windows > Package Manager Console (or press C-W, C-Z):

image

From here you get a nice, friendly command line window:

PM>

This is a PowerShell console so the standard pattern of commands is available with the “-package” suffix. For example, to see what’s available, try List-Package:

  
PM> List-Package
Id Version Description
-- ------- -----------
Adam.JSGenerator 1.1.0.0 Adam.JSGenerator helps producing snippets...
Agatha-rrsl 1.2.0 Request/Response Service Layer for .NET
AntiXSS 4.0.1 AntiXSS is an encoding library which uses...
Antlr 3.1.1 ANother Tool for Language Recognition...
Antlr 3.1.3 ANother Tool for Language Recognition...
<snip> 

PM>

Nice! OK, Let’s open up a new ASP.NET MVC App:

And throw together a simple MVC app:

Model:

namespace TechTalk7.Models
{
    public class Idea
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
}

Controller:

namespace TechTalk7.Controllers
{

    public partial class IdeaController : Controller
    {
        private static List<Idea> Ideas = new List<Idea>();

        public IdeaController()
        {
            Ideas = new List<Idea>() { 
                new Idea() { ID = 1, Name = "Brush your teeth" }, 
                new Idea() { ID = 2, Name = "Study hard" } 
            };
        }

        public virtual ActionResult Index()
        {
            return View(Ideas);
        }
    }
}

View:

    <h2>Index</h2>
    <ol>
    <% foreach (TechTalk7.Models.Idea I in Model)
       { %>

       <li>
        <!--old way, without T4
        <%=Html.ActionLink(I.Name, "Details", new { ID = I.ID }) %> -->
       </li>

    <%} %>
    </ol>

Output:

image

It works! Now let’s improve that view code by trying out that fancy T4MVC stuff I’ve heard so much about:

PM> Add-Package t4mvc
Successfully added 'T4MVC 2.6.30' to TechTalk7
PM>

That was easy. But what just happened? It looks like some files were added:

Sweet! Now I have T4MVC all ready to go and can do neat things like this:

    <ol>
    <% foreach (TechTalk7.Models.Idea I in Model)
       { %>

       <li>
        <!-- old way, without T4-->
        <%=Html.ActionLink(I.Name, "Details", new { ID = I.ID }) %>

        <!--new way, *with* T4-->
        <%=Html.ActionLink(I.Name, MVC.Idea.Details(1)) %>
       </li>

    <%} %>
    </ol>

Better. No more typo-prone strings, clunky object initializers, and it’s even a little shorter.

Soon I’ll be checking out the Spark View Engine, too. With that, my code above could be replaced with something like this:

<viewdata Ideas="List[[TechTalk7.Models.Idea]]"/>
<h2>Index</h2>
<ol>
    <li each="var I in Ideas">
        ${Html.ActionLink(I.Name, MVC.Idea.Details(1))}
    </li>
</ol>

That looks pretty nice.

Lies, Damned Lies, And Internet Speed Charts

In the market from some high-speed Internet? You will no doubt need to select how high the high-speed Internet should be. Fortunately the providers are here to help with some useful feature matrices:

U-Verse:

image

Time Warner:

image

Wide Open West:

image

There are two problems with all this:

  1. The recommendations are completely crap
  2. They rarely give you information on what actually matters: your upstream—how fast you can send stuff:

The Recommendations Are Completely Crap

They suggest that if you are to have more than two people online at a time, play games, buy concert tickets, or watch videos you need a 15mbps connection. That’s a load of c r a p. My office of 150 engineers (more bandwidth heavy than your average user, I’d guess) share a 15mbps connection and it’s plenty speedy.

In fact, Sarah and I are dangerously addicted to tech, including streaming television, two laptops and two WIFI phones running pretty much all the time and we’re screaming fast on a 6mb connection.

In the old days, I used to play Doom with friends across a 0.014mbps connection (a 14.4kbps modem).

The real issue here is that we don’t have much use for speeds in excess of 10mb. There are certainly services on the horizon that would benefit from faster speeds, but they aren’t on those charts. Plus, faster services would need something else, too, which brings me to…

What Really Matters Is Upstream

The most frustrating speed-related problem on the Internet today (for home users) is uploading files. Sending large pictures, uploading videos, posting to blogs, video chatting, etc. all require you to send a lot of data through a very small pipe.

The reason this is a problem is that the speeds normally advertised are asynchronous: different up vs. down. Digging into the vendors a little deeper reveals the upload speeds:

U-Verse (I couldn’t find this anywhere except Wikipedia):

image

Time Warner (credit to TW for making this easy to find):

image

Props to Wide Open West for providing that information in the initial graphic, though their recommendation that 15mbps is needed for gaming is silly.

The sad fact is that even if you pay for ridiculous internet like Time Warner’s best offer, or the other vendors top choices, your upload is still far, far slower. This matters because sending content goes at the slower upload speed.

I would rather pay $10 to improve my upload speed from 1 to 2 mbps than improve my download speed from 6 to 12 mbps.

Final Thoughts

I appreciate that vendors are trying to make this stuff easier to buy—that’s great, this stuff is confusing. My objection is that for today’s internet, “Basic”, “Pro” or anything around 5mbps is far and away sufficient for most people. The average live Hulu stream is under 1.5mbps. The average website is plenty fast on the smallest connection you can buy. Vendors are not doing a good job justifying the upsell with any honesty.

I personally have a 6mbps line and find it more than adequate. If I can surf, stream Hulu, and blog while Sarah uploads all those pictures, I think six ought to be enough for most people.

Monday, October 18, 2010

Google’s Search Preview Is Impressive

There is no half-assing it over at Google. When they bring something to market, it’s generally a well-polished solution. Like Search Preview which just started showing up for me.

I noticed that search results became shaded as I hovered:

image

And when clicked (or if that magnifying glass is clicked), a preview image appears:

image

This preview shows what the site I’m about to go to looks like along with the area I’m probably interested in enlarged with my search terms highlighted. Doing this once is impressive in its own right, but doing it to scale—for millions of queries—is an incredible feat.

Google has once again taken a feature from other sites and replaced the silly novelty of it with elegance and usefulness.

Sunday, October 17, 2010

Road Journal 10/12/2010 – 10/15/2010

I went out to Battle Creek again this past week to attend some more meetings. These are the kind of meetings where I have something in my head that they need. So far, the most efficient way we’ve found to extract the nonsense is to have me brain dump onto whiteboard after whiteboard. I’d love to share my drawings but they are all proprietary to the customer and wouldn’t really make any sense here anyway.

Driving Out

I’m getting ahead of myself. Before the meetings, we had to drive there. We crammed everything into the car and left almost on-time. We really should have taken the van—it’s much more comfortable for long trips—but the gas mileage of the Fit is too persuasive.

After basking in the awe of the EZPass, we were on the turnpike headed West. It wasn’t long before we saw our first interesting sight:

IMAG0346

Look closer…

IMAG0346

What the hell is that? I didn’t get a very good profile picture of it but this should leave you curious:

It was also around this time that we popped in our first audio book, “Armageddon in Retrospect” by Kurt Vonnegut. We lasted about 15 minutes before the ramblings—however hilarious—were not the engaging material we needed to distract us from the backseat choir.

About halfway through we stopped in Perrysburg for a couple hours so I could meet with a client. Apparently the kids had a fun time playing outside and wandering around:

After my meeting, which went really well considering that the guy I met with put in his retirement papers that same day, we hit the road again. Except for the common stresses of traveling with young children, we arrived unharmed by early evening.

Checking In

imageThe hotel desk clerk was an interesting lady. Let’s just say she was very…open. While checking in she had deep conversations with Sarah, me, and each child (never mind that thing 2 doesn’t talk). Although she didn’t explain why her hair had ruby-red highlights, she did explain why she keeps a half-dozen pens in it.

She also explained that “everyone asks me why I have a Toledo phone number when I live in Battle Creek and I’m like, because my ex-boyfriend lived in Toledo”, much to our bewilderment.

I signed the papers, including a very threatening “you cannot smoke in this smoke-free-hotel (well you can, but it will cost you $100)” warning and got the keycards. We loaded up our giant wagon full of stuff and pushed it into the room, which reeked of cigarette smoke.

I went downstairs to ask about switching rooms.

     “Hey, hi! I was wondering if you might have another room we could try…306 smells like smoke.”

     “This is a smoke-free hotel!”

     “Yes I know…and yet the smoke…is.”

     “We’re completely booked. Let’s see, try 304. You didn’t…touch anything…move anything did you?”

     “No, nothing.”

Our new room was much better. Sarah figured out later that they were full, but she switched us with someone else who hadn’t checked in yet (next time I’m totally rearranging the furniture and putting confusing magnets on the refrigerator). The room was ok, and the internet was actually great (turns out there was a Shriners convention in town, which could be related to the good Internet). Here’s my hotel review:

image

We ordered pizza from the place across the street. I asked for the hotel special (it was posted on the window) and the guy asked me how much it was since they had “like a million coupons out there.” I guess this is like the Priceline of pizza. My guess worked and dinner was delivered 20 minutes later. It wasn’t very good but we were very hungry so it was good enough. Here’s my review:

image

Working

The next day saw lots of super fun meetings wherein I did all the brain dumping I mentioned earlier and the clients and I  enjoyed an awesome lunch at Arcadia Brewery. That night we risked public embarrassment by going out to eat as a family, to Don Pablos. Nothing too eventful there, which is awesome. We also walked through the soon-to-be-wasteland of Barnes and Noble. We picked up a sweet (i.e. hilarious) white-elephant gift from the $2 cart and wandered around the mall.

The following day was pretty much the same with a great lunch at Griffin’s and some more pizza for dinner (from the Pizza Hut this time).

On Friday I met with one of my clients for a breakfast meeting before heading back home. Restaurant breakfast is pretty much the best thing ever, and I’m learning that it’s seemingly impossible to screw up. Perhaps ketchup, salt, and tubs of sugar syrup cover all culinary errors?

On the way back things were going…poorly. The kids were really doing great considering the journey and Sarah was amazing (thanks for driving!)—the problem was me. My head felt like it was going to explode. I came down with something awful on Sunday and managed to beat it back all week during the business travel. I finally went to an urgent care on Thursday and got some meds, but it was too late for the ride back on Friday (this is why I haven’t been blogging this week—I’ve been sleeping 12 hours/day).

Urgent Care

(feel free to skip to the pictures at this point)

Urgent care is something of a misnomer. My care was neither urgently needed nor administered. In fact, the place was seemingly empty of patients but it took quite a while to be seen. I explained when I checked in that this was basically out of pocket for me since I have a high-deductible and the receptionist assured me it’d be $72—or $112 if the exam was extensive.

Someone eventually ushered me in and took my vitals. The doctor was friendly enough, though I didn’t get the impression he was really listening to me. Once he saw that I had a generic cold, he seemed to tune everything else out. He ran a three-minute strep test despite my not-firm-enough protest and disappeared for 30 minutes while I waited for the results. I waited while the nurses gossiped about their other patients, including one poor woman who they turned away because they, I learned, can’t set broken noses and hers was “seriously in the wrong spot.” It was completely inappropriate but very amusing.

A nurse eventually came in with my negative strep result and delivered internet sourced instructions for how to care for a sore throat. She gave me a script for antibiotics with strict instructions to use it only if I don’t get better in two days, and a script for Tylenol 3 w/ Codeine for the pain. I asked if there was anything else I could do because it really, really hurt.

     “Are you asking if you can take the antibiotics now?”

     “No.”

     “Because you can do that.”

     “….OK (what was that about…nevermind). But there’s nothing I can do to help me today, besides the Tylenol?”

     “No.”

     “And I shouldn’t take this with Nyquil because they both have Acetaminophen in them, right”

     “Uh…I guess not.” (?!)

So after 90 minutes gone for a couple of nurses or techs and a doctor to spend five minutes with me, I had a prescription that I shouldn’t/should fill and no real immediate relief. Oh, and my bill was $162 because of my “extensive exam” and unwanted strep test…only 2.25x what I thought it’d be. I won’t normally bend over for things like this but I was exhausted and in pain so I lightly protested and left.

And so, on Friday my body fell apart. I hadn’t been sleeping well at all that week—the pain in my throat kept waking me—but the trip back was a new level of awful. I filled the scripts but I think the pharmacist accidentally filled the Tylenol 3 prescription with migraine-inducing poison. Before Friday I had never had a migraine before, but on the drive back, wow. I was pretty sure my eyes and ears were bleeding.

Fortunately, as long as I drink lots of water, I’m in pretty good shape now. Thank you so much to my lovely wife for taking such great care of me.

Driving Back

One bright spot on the way back was our lunch break at a rest stop. We found a nearly empty dining room in which to eat our packed lunch and enjoyed a leisurely half-hour with the girls, who were in goofy spirits. Thing 2 has really taken to her doll babies and insisted on nursing one during the meal.

IMAG0382

And some water, too:

IMAG0385

Thing 1 enjoyed the sun on the trip back, and by “enjoyed” I mean “hated with all her being.” This was an uncharacteristic moment, shot through my head rest at a lounging thing 1:

IMAG0391

Home

Having saved up all their energy while in the car, the kids were bursting when we got home. They decided to spend most of it doing somersaults (note how she so appropriately claps for herself afterwards):

More giggling and silliness:

And a final tumble with a delightful “whoa!”:

We’re all back in one piece and order is restored.