Testing Differences by Testing Similarities

Today I was writing some unit tests, and had a little flash of inspiration that I thought I’d share.

The project I’m working on involves the propagation of some pretty complex domain entities between two applications. There’s a bunch of serialization/deserialization issues, and it doesn’t help matters that the two applications do not use a shared library to manage these objects (yet!).

So needless to say, I’ve written a lot of classes to convert objects from one form to another. And obviously, these need to be tested…thoroughly…to make sure that the object sent to the receiving application is identical to the one being sent by the first.

Consider the following domain:

enum Temperature {
    WARM,
    COLD;
}
 
class WavelengthRange {
    public float lowerBoundary;
    public float upperBoundary;
 
    public WavelengthRange(float lowerBoundary, float upperBoundary) {
        this.lowerBoundary = lowerBoundary;
        this.upperBoundary = upperBoundary;
    }
}
 
class Color {
 
    public String name;
    public int number;
    public boolean primary;
    public Temperature temperature;
    public String[] variants;
    public WavelengthRange wavelengthRange;
 
    public Color(String name, int number, boolean primary, Temperature temperature, 
            String[] variants, WavelengthRange wavelengthRange) {
        this.name = name;
        this.number = number;
        this.primary = primary;
        this.temperature = temperature;
        this.variants = variants;
        this.wavelengthRange = wavelengthRange;
    }
}

If I were to test on equality here, I might do something like this:

@Test(dataProvider = "same")
public void testSame(Color actual, Color expected) {
 
    // These are pretty normal
    Assert.assertEquals(actual.name, expected.name);
    Assert.assertEquals(actual.number, expected.number);
    Assert.assertEquals(actual.primary, expected.primary);
    Assert.assertEquals(actual.temperature, expected.temperature);
 
    // A little bit more complex...
    // Check if the lengths are the same:
    Assert.assertEquals(actual.variants.length, expected.variants.length);
    // Make sure everything in "expected" is reflected in "actual":
    for (String expectedVariant : expected.variants) {
        boolean found = false;
        for (String actualVariant : actual.variants) {
            if (actualVariant.equals(expectedVariant)) {
                found = true;
            }
        }
        Assert.assertTrue(found, String.format("Missing '%s' in actual variants", expectedVariant));
    }
    // Then do the reverse:
    for (String actualVariant : expected.variants) {
        boolean found = false;
        for (String expectedVariant : expected.variants) {
            if (expectedVariant.equals(actualVariant)) {
                found = true;
            }
        }
        Assert.assertTrue(found, String.format("Missing '%s' in expected variants", actualVariant));
    }
 
    // Yikes... Getting really complex... I think you get the point, so I won't even bother here...
    // Assert.assertEquals(actual.wavelengthRange, expected.wavelengthRange);
}

Now, when I throw some data at it, it can run through these assertions, and verify “equality”.

@DataProvider(name = "same")
public Object[][] same() {
    return new Object[][] {
            {
                    new Color("blue", 5, true, Temperature.COLD, new String[] {"aqua", "turquoise"}, new WavelengthRange(450f, 495f)),
                    new Color("blue", 5, true, Temperature.COLD, new String[] {"aqua", "turquoise"}, new WavelengthRange(450f, 495f))
            },
            {
                    new Color("red", 1, true, Temperature.WARM, new String[] {"maroon", "crimson"}, new WavelengthRange(620f, 740f)),
                    new Color("red", 1, true, Temperature.WARM, new String[] {"maroon", "crimson"}, new WavelengthRange(620f, 740f)),
            }
    };
}

(By the way, if these tests look silly, they are. The real ones involve converting the actual values from different classes. Omitting that here for the sake of simplicity.)

So that’s great… Everything passes just fine. In addition to testing for equivalence between these two entities, however, I also want to test for differences. So I create some differing data:

@DataProvider(name = "different")
public Object[][] different() {
    return new Object[][] {
            {
                    new Color("red", 1, true, Temperature.WARM, new String[] {"maroon", "crimson"}, new WavelengthRange(620f, 740f)),
                    new Color("blue", 5, true, Temperature.COLD, new String[] {"aqua", "turquoise"}, new WavelengthRange(450f, 495f))
            },
            {
                    new Color("yellow", 3, true, Temperature.WARM, new String[] {"lemon"}, new WavelengthRange(570f, 590f)),
                    new Color("green", 4, false, Temperature.COLD, new String[] {"olive", "kelly"}, new WavelengthRange(520f, 570f))
            }
    };
}

But I don’t want to rewrite all of the logic that I’d just written for my testSame() method. So I have a few options. I could rewrite testDifferent() so that it fails only when all values are the same. Seems a little hard to pull off… Or I could extract a method to test the equality of two objects… That sounds reasonable.

But in the end, what I really want to do is simply run testSame() again, except with completely opposite expectations. I don’t want to avoid an exception… Rather, I *want* an exception to be thrown. In particular, I want an AssertionError to be thrown. That means that my testSame() method deemed it “not the same”… Which is exactly what I’m looking for here.

::thinks:: Hey, TestNG/JUnit have constructs for that!

@Test(expectedExceptions = {AssertionError.class})
public void testDifferent(Object actual, Object expected) throws Exception {
    this.testSame(actual, expected);
}

This way my test logic stays in my test methods. It’s easy to read, and it forces my testSame() method to have sole responsibility over determining whether something is the same, or it is different.

In the end, it probably would have been just as easy, and maybe more appropriate, just to split this logic out into its own method. But I thought this was pretty clever, so I think I’ll keep it ;)

(Gist here)

Swift Mailer on Gmail and GoDaddy

I recently worked on a WordPress plugin that allows visitors to subscribe to posts by entering their email address. Now, if you know me in a development context, it’s really no secret that I’m not the biggest fan of WordPress development. This has especially been the case after having done a good deal of work in Symfony, which is easily the neatest PHP framework out there.

So when it came to writing this plugin, I obviously wanted to be as Symfonic as possible (side note: why has nobody written a bridge between the two??). I implemented my own [extremely] pared-down ORM, did some basic templating, and lastly, I used Swift Mailer to manage the actual emails.

Until this project, I’d only used Google as the SMTP server for outgoing emails. My client, however, is using GoDaddy as their web host. GoDaddy is great for first-timers and people who otherwise don’t really know a good deal about building and managing sites. But unfortunately, it seems they fall short on good documentation on the more technical concepts…like setting up email relays.

And while I found endless forum posts on the subject, none of them actually gave me a working solution. Rather, I had to take a brute force let’s-try-this-and-see-if-it-works approach. And on one of my attempts — it finally worked.

So in the spirit of helping out people who might be facing the same issue, here’s what I ended up using:

$email = "someone@something.com";
$password = "pAsSwOrD";
 
$transport = new \Swift_SmtpTransport("smtpout.secureserver.net", 80);
$transport->setUsername($email);
$transport->setPassword($password);
 
$mailer = new \Swift_Mailer($transport);
 
$message = new \Swift_Message();
// ...configure message...
$message->setFrom($email);
 
$mailer->send($message);

For the sake of contrast, here is how to do it with Gmail:

$email = "someone@gmail.com"; // Complete Gmail address
$password = "PaSsWoRd";       // Gmail password
 
// (Note that Gmail requires encryption...)
$transport = new \Swift_SmtpTransport("smtp.gmail.com", 465, "ssl");
 
// ...the rest is the same

It’s worth noting that GoDaddy currently limits you to 250 emails per day, in an effort to reduce spam. So if you’ve got a bigger client base, you’ll need to buy an upgraded plan. Meanwhile, if you were lucky enough to sign up for Google Apps back when it was free, or if you just want to use a Gmail account, then I believe you’re only subject to the normal sending limit of 2,000 emails per day (but don’t quote me on that).

Either way, happy SMTPing!

Goals for 2013

Last year, I started the year off with 10 goals that I’d hoped to keep throughout the year. In the end, I kept 5, and I was actually pretty happy about that. But the most important part was that I had something to strive for during the year. With that in mind, I’m continuing the tradition into 2013.

I’ve had most of these goals in my head for a few months now, and I promised myself I’d get them down in writing sometime during the first quarter of the year. Ever the procrastinator (not really–I’ve just been too busy with work), I’m finally getting around to it on the first day of the second quarter (and no, this is not an April Fool’s joke…).

This year, I’m dividing the goals into categories. Last year, I put way too much focus into my career-oriented goals, so I’m separating them out this year to ensure focus in four areas: Career, Health, Financial, and Social.

Career

  • 5k on Stack Overflow

    This was one of my favorite goals from last year, and I want to do it again. The idea here is that I’ve finally gotten to the point where I’m an experienced software developer, and as such, I can start to “pay it forward” to the community that helped me learn and grow. Stack Overflow is a great place to find answers to questions on software, and it’s been awesome to start to help provide those answers. Last year, I made my goal of 2,000 reputation points; this year the goal is to end the year with 5,000.

  • Get Open

    I use open-source software every single day of my life. From programming languages, to database systems, to frameworks of all kinds… I’ve experienced a great deal of growth from using this stuff. It’s time for me to start giving back. Design a feature, fix a bug, write some documentation… The goal for 2012 is to do something that contributes to the open-source landscape.

  • Blog x 10

    I frequently find myself in situations where I’ve spent a significant amount of time trying to figure out a particular process, or fix a bug, or design a solution to what seems like a common problem. In several of these cases, I’ve found almost no good information online, and so I ended up figuring things out by good old-fashioned trial and error. But what good is this information if it remains in my head? The goal here is to write at least 10 blog posts detailing my own experiences, so that they might help others down the road.

Health

  • Bike 500 Miles

    I failed this one last year, plain and simple. And my health suffered…a lot. So I’m upping this year’s goal from 200 to 500 miles. It shouldn’t be too hard, as long as I start commuting to work by bike.

  • 10 Pounds

    Weight Stasis was a major failure last year. This year, the goal is to lose the weight that I ended up gaining last year.

  • Gym x 60

    Last year, one of my goals was to become mayor of my gym on Foursquare. I gave up on this for two reasons. First, since the current mayor has around 40 visits in a 60-day period, I had absolutely bitten off more than I could chew. Second, I didn’t like that the goal was not only dependent on me, but also on some stranger known to me only as George Philip B. This year, I needed something more manageable. So I’m going with 60 gym visits. And, if I oust George in the process, all the better.

Financial

  • Debt-Free

    I’ve been in credit debt ever since my first credit card in 2000. That’s ridiculous, and there’s no reason for it…not now, anyway. This goal requires me to have zero credit debt on December 31.

Social

  • May 25

    This year, Augustine and I will celebrate the 10th anniversary of our very first date. At this point, I have a bad habit of taking him for granted (and rightly so…I mean, he’s been by my side since I was in college!). And I have a worse habit of not making a big deal out of our anniversary. But this is a big milestone, and we need to do something super special for it. Don’t know yet what it is, but I’m reserving an entire 2013 goal to make sure that May 25 is a day to remember.

  • Hanging x 100

    Another bad habit of mine? Not spending enough time with my friends and family. These are the people who have always been there for me… I need to be there for them too. This goal is to spend at least 100 days hanging out with family, friends, or colleagues. Yes, it might sound a little forced, but I’m an introvert who’s wired to be hyper-focused on his work. I need to actively force myself into social situations, because it’s not going to happen on its own.

  • Photo a Week

    There was once a time when Flickr such a good online community that I was meeting new people online left and right, and some in person too. In the end I formed some really cool friendships that endure, in some form or another, to this day. I want more of that. I’ve signed up with a Flickr group to take a photo a week for the entire year. The goal is that I end the year with 52 awesome photos, and maybe make some new photographer friends along the way.

Well, that’s it. These are the official goals for 2013. Come back next year to see how I did.

2012 Retrospective

At the beginning of 2012, I made 10 goals for myself. Let’s see how I did…

  • 1. Read a novel

    Part-way through the year, I was in New York visiting Darcy. She loaned me Girl with the Dragon Tattoo, and I promised myself I’d read it. But I kept putting it off, and in the end had no time to read it, so I didn’t even start. This happened despite the fact that the last two weeks were spent lounging around in Canada with essentially nothing to do. Total failure.

  • 2. Weight Stasis

    Another failure. I was doing alright for most of the year, but spending Christmas in Canada gained me around 9.5 pounds. Obviously, I didn’t have time to recover from that when I returned on December 30. In the end, I think the reason this goal was a failure was because it didn’t feel like I was working toward something. I will remember this for my 2013 resolutions.

  • 3. Bike 200 miles

    I biked about 100 miles. Pathetic. The biggest reason was that I wasn’t commuting to work by bike. Instead, I’d go by the T. The ride was easy, and I was able to add about half an hour of uninterrupted work each way. That’s a pretty big incentive. Also, I didn’t make any concerted effort to actually get out and bike on longer trips during the summer. Shame on me.

  • 4. Mayor of the YMCA

    I went to the gym a total of 12 times during the year. Yikes.

  • 5. Escape the Continent

    Kind of got this one for free when my company asked me if I’d spend a month training our partner in Tokyo :o) Had a great time… Grew both personally and professionally. All in all, a pretty awesome experience.

  • 6. Build a Computer

    Did it. And it’s awesome. Take a look!

  • 7. Learn 100 Words in Tagalog

    I learned a few, but not 100. My favorite: gagamba (it means “spider”).

  • 8. Save 5%

    Yup, did it. All in my 401(k). And I’m not touching my contribution percentage, so I’ll continue saving into 2013 and beyond as well.

  • 9. 2,000 Reputation of Stack Overflow

    Accomplished this very gradually throughout the year. I’d say that I’m pretty proud of my commitment on this one, but truth be told, it ended up being way too fun and addicting :o) See my progress.

  • 10. Write a WordPress Plug-in

    It’s not pretty, but it’s done. I took advantage of my company’s second hackathon to build a plug-in to help place advertisements on mobile versions of a WordPress blog. It was fun, and I learned a lot about the WordPress platform. Also learned a bit about git, which ended up becoming pretty helpful later on.

Conclusion

In 2012 I accomplished roughly half of what I’d hoped to. And that’s ok… This is the first time I’ve done actual legitimate resolutions, so I’m willing to cut myself some slack :o)

Among the accomplishments, there were 2 that didn’t require any effort at all (Escape the Continent & Save 5%). But what I find really interesting is that the other 3 all fell within the technical/career realm. And expanding my technical abilities and furthering my career have been my biggest priorities for the last few years. All of the other 5 resolutions were somewhat haphazard (“I should start reading again…”, “I need to bike more often.”). They represented things that I’d like to do, but not necessarily things that mattered to me (at least at this point in my life).

Overall, this was a successful exercise, so I plan on doing it again in 2013. This time, I’ll be taking a hard look at my actual priorities, and building those into resolutions. Taking this into account, I’m sure that I can turn out more than a 50% success rate :o)

More to come…

Downloading the WordPress Codex

My company is shipping me off to Japan on Saturday for a month (possibly more) to train one of our partners on how best to use our system. It’s going to be an interesting experience. Not only will I be conversing regularly with people whose language I do not speak, but I’ll be completely immersed in a different culture about which I know very little. I mean, until now Japanese culture has played only two roles in my life: Pokémon and Dance Dance Revolution. Obviously, there’s much more to it, and I’m totally excited to learn all about it.

So far, there’s just one thing I’m nervous about: that dreaded plane ride.

In preparing for this trip, I’ve been creating massive to-do lists (Astrid is an amazing tool for this, by the way!). And I’ve identified some technical tasks that would be ideal for the 12+ hours I’ll be spending in the air. One of those tasks: convert a friend’s website from static HTML into a WordPress site. It’s pretty simple on the outset… However, WordPress has some complexity when it comes to templates and plug-ins, and so I’d like to have the WordPress documentation available to me while I’m offline.

The problem: WordPress documentation is a set of HTML pages, and cannot be easily downloaded. I searched around a bit, and it seems this question’s been asked before. And the answer is usually: use a tool to download a local copy of the website. The tool of choice: HTTrack. I’m on Ubuntu, so installing was easy: sudo apt-get install httrack

Once installed, I gave it a shot:

tkelley:~/> httrack http://codex.wordpress.org/
Mirror launched on Wed, 09 May 2012 13:48:30 by HTTrack Website Copier/3.44-1+libhtsjava.so.2
mirroring http://codex.wordpress.org/ with the wizard help..
Done.codex.wordpress.org/ (162 bytes) - 403
Thanks for using HTTrack!

…but that exited pretty quickly. Not what I was expecting for a full site download. During the execution, HTTrack generated a log. The log ended with this:

tkelley:~/> tail -n2 hts-log.txt
13:52:56	Error: 	"Forbidden" (403) at link codex.wordpress.org/ (from primary/primary)
13:52:56	Info: 	No data seems to have been transfered during this session! : restoring previous one!

403 (“forbidden”) error? That’s weird… I’m able to view it in my browser and via wget/curl without problems:

tkelley:~/> curl -G codex.wordpress.org --write-out %{http_code}"\n" -s -o /dev/null
200
tkelley:~/> wget codex.wordpress.org 2>&1 | egrep HTTP
HTTP request sent, awaiting response... 200 OK

HTTrack must be sending something that WordPress doesn’t like. Of the usual suspects, I’ve found that the most common is User Agent. In this case, It turns out that HTTrack passes its own user agent of “Mozilla/4.5 (compatible; HTTrack 3.0x; Windows 98)”, and… wouldn’t you know… WordPress isn’t a fan:

tkelley:~/> wget -U "Mozilla/4.5 (compatible; HTTrack 3.0x; Windows 98)" codex.wordpress.org 2>&1 | egrep HTTP
HTTP request sent, awaiting response... 403 Forbidden

Now that we know what’s causing it, all we need to do is play make-believe. Pass a “good” user agent string (from a browser that WordPress accepts, say, Chromium) using the -F flag, and we’re good to go:

tkelley:~/> httrack "http://codex.wordpress.org/" -F "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/11.10 Chromium/18.0.1025.168"

I honestly don’t know why WordPress would block HTTrack. After all, WordPress is licensed GPLv2, and I imagine its documentation is as well (derivative work?). So anyone should be able to download the entire thing for their own use. Perhaps it becomes a strain on their servers? If so, there are certainly better ways of blocking repeated requests from the same IP address. Anyone out there have any ideas? I’d love to hear them!

Airside

Well, it is a sad day indeed. My very favorite creative agency ever has finally closed up shop.

Airside got its start in 1998, back when web design was in its infancy, and the idea of pursuing a career in graphic arts was capricious at best. Things have changed over the last 14 years. The web looks totally different (anyone remember what Yahoo! looked like in 1998?), and now graphic design is a great career to have…especially if you’re good at it.

…and no one is better at it than Airside. They’ve already shut down their website, so it’s not easy to find a portfolio, but here are a few things I’ve found here and there:

That last one might look familiar. That’s because it was featured on the album cover for Lost Horizons by Lemon Jelly. In fact, Lemon Jelly’s Fred Deakin was one of the founding members of Airside, and the band and agency have collaborated a lot. Basically, all of Lemon Jelly’s music videos were done by Airside. A few of my favorites:

Anyway… As it turns out, Airside didn’t call it quits because of business or personal differences. Actually, they say that business has never been better, and that they are still very good friends. Instead, they say they are closing so they can pursue their own personal projects.

So that leaves me wondering… Lemon Jelly reunion?
::crossing my fingers soooo damn tightly::
:oD

Goals for 2012

We’re almost two months in to the new year, and I’ve been meaning to write a post on New Year’s resolutions (already off to a bad start, I know…). I’ve had some interesting resolutions in the past — some I’ve attained, some I haven’t. Last year’s fell under the latter category. I had two resolutions last year: Develop an iPhone application, and read the entire series of James Bond books. In terms of iOS development, I got about two chapters into a coding book, and wrote a “Hello, world” app. In terms of James Bond, I didn’t even crack open a single book.

Any business grad worth his salt knows that goals need to be specific and measurable, and that’s how I designed those two resolutions. But I forgot something: goals also need to be attainable. These two goals were wildly ambitious, especially considering that I spend a good 70-80% of my waking hours either working, or learning things that will help me expand my career.

So this year, I’m taking a different approach. Smaller goals, and more of them. Ten, in fact. Ten specific, measurable, attainable goals. Here goes:

  • 1. Read a novel

    Ian Fleming wrote 12 James Bond books. How I ever thought I could find the time to read them all in a year is beyond me. That said, I need to start reading again. So for this year, a single novel will do.

  • 2. Weight Stasis

    I’ve come to the realization that I’m never going to really lose weight again. I don’t have the time or energy to put into it, and it’s just not high enough on my list of priorities. So for the time being, I’d like to just maintain my current weight of 220. If I lose anything, then that’s a bonus. But I’ll seriously be happy ending the year no fatter than I started it.

  • 3. Bike 200 Miles

    Ever since I moved to a company that gives laptops to their employees, my subway commute has become a valuable time resource for me. I can usually get an extra hour of uninterrupted work done on my way to and from my job. The side effect: I’ve stopped biking entirely. I think I can count on one hand the times I took my bike out last year. That needs to change.

  • 4. Mayor of the YMCA

    The last fitness-related resolution, I swear! Becoming the FourSquare mayor of my gym means that I’ll need to go more than any other FourSquare user over the course of a month. That’s doable, right?

  • 5. Escape the Continent

    Don’t get me wrong… North America is great. Love it to pieces. But I just want to leave for a week or three. In fact, I have five weeks of vacation that need to be used by the end of the year, so I really have no excuse not to.

  • 6. Build a Computer

    Something I’ve always wanted to do. And now I work with a ton of people who can guide me along the way. Plus, my old HP is totally busted. It’s time for my inner geek to shine through.

  • 7. Learn 100 words in Tagalog

    What? I want to impress the in-laws!

  • 8. Save 5%

    Ever since I moved to a company that doesn’t match 401(k) contributions, I’ve stopped saving for retirement altogether. I’m a few years behind now, so I really need to start investing again. I’ve already done my time as a Wal-Mart door greeter; I don’t want to have to do it again when I retire.

  • 9. 2,000 Reputation on Stack Overflow

    Stack Overflow is a site where coders help out other coders. I’ve learned so much from reading other people’s questions, and I’m finally getting to a point where I have enough knowledge to help others. It’s a pretty cool feeling. Anyway, the community rewards good answers with “reputation points”. I had 608 at the beginning of the year, and two months into the year, I’m at 813. Off to a good start already!

  • 10. Write a WordPress plug-in

    One of the reasons last year’s iPhone app didn’t work out was that not only was I going to have to learn the iOS SDK, but I’d have to learn an entirely new language (one that’s known for being difficult to get used to). I was in unfamiliar territory on all fronts. But WordPress? Now that’s more like it! I’ve been using WordPress for years, and I understand its API. Plus, I’m a pro at PHP. So this year, I’d like to publish a WordPress plug-in. Don’t know yet what it will be, so I’m open to ideas!

So there you have it — my 10 goals for the year. They’re all definitely attainable, so I’m really hoping for 100% success here. Just need to make sure I’m watching the calendar. Otherwise, you’ll be sure to see me at the gym on December 31, darting my eyes back and forth between the Casino Royale book in my left hand, and the WordPress API documentation on the phone in my right.

How Secure is Your Password?

Just saw this site on a friend’s Google+ feed:

http://howsecureismypassword.net/

No surprises as to what it does… You enter a password, and it tells you how long it would take a modern desktop computer to crack it using a brute force attack.

(Side note: I had some initial reservations about entering my password into a site like this, where it could so easily be captured. However, I checked Firebug for asynchronous network calls, and it turns out that everything is happening client-side, so the password actually never leaves your browser.)

Anyway, as it turns out, I’ve got a pretty strong password… It’d take about 6,000 years to crack. 6,000 years ago was when civilizations were first popping up in Mesopotamia. So I feel pretty safe.

But I’m curious… What if my password were as strong as a SHA or MD5 hash? I’m sure these calculations have been made a million times before, but here goes…

Start with the most common password of them all: “password”. The site tells me that it would be hacked “almost instantly”.

Apply an MD5 hash to “password” to get “5f4dcc3b5aa765d61d8327deb882cf99″

tkelley:~/> echo -n "password" | md5sum
5f4dcc3b5aa765d61d8327deb882cf99  -

…and already “almost instantly” becomes “About 8 decillion years”. Wow.

SHA1(“password”) = “5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8″

tkelley:~/> echo -n "password" | sha1sum
5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8  -

…about 22 quattuordecillion years.

The SHA-2 hash comes in four flavors: SHA-224, SHA-256, SHA-384, and SHA-512.
SHA2(“password”, 224) = “d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01″

tkelley:~/> echo -n "password" | shasum -a 224
d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01  -

About 180 duovigintillion years

After this, the site’s JavaScript starts crapping out… It tells me it’ll take 302 quattuorvigintillion years to crack each of the 256-, 384-, and 512-bit hashes. Just to provide some color here, that’s
30,200,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 years.

But let’s be honest, given that I won’t live to see the 22nd century, I’m certainly not concerned about the 30 x 10^75′th one. I think I can rest easy with my 6,000 years. Want to hack me? Go ahead and start now… I’ll check back with you in 8011.

Changes

So it’s been a few months since my last post. Long story short, I lucked out in landing a role as a general coder at a mobile advertising company. I say “lucked out” because I didn’t really have much tangible experience. Of course, I’d worked on dozens of projects in both my personal life and professionally… But I had neither a degree, nor a job title, to reflect that experience. I was good in theory, but I was not good on paper.

So Jumptap took a chance on me back in March. And given the fact that I was the July employee of the month, I’d say their risk is paying off ;o)

That isn’t to say that it hasn’t been difficult for me. This company hasn’t bitten the Microsoft bug the way I did, so I’ve had to drop C# entirely. And that’s OK by me. In fact, at this company, I’ve been able to expand into more of the web development that I love doing. I’ve been programming a lot in PHP and JavaScript, I’m getting pretty awesome at MySQL, and I’m a total pro at jQuery…a JavaScript library I’d always been too afraid to touch.

Unfortunately, for various reasons this blog has sort of fallen to the wayside. First, my job is keeping me incredibly busy. Every day I come up with a new idea for an app or plug-in or extension or library. But I’m working in the most fast-paced environment I’ve ever known. So these ideas get shelved in favor of bigger things. Second, I’d been using this blog as a sort of resume-builder. A place to show off my code so that companies would consider me for a coding position. That’s no longer an issue; I’ve got plenty of portfolio work to show off now.

So I’m changing the format of this blog.

I’m still going to post bits of code whenever I can find the time to come up with something. But more so, this is going to become an aggregation of all my various online presences. I tweet, I put photos on Flickr, I post YouTube videos, I share links on Tumblr… It’s becoming too much. So TomKel.com is the place where this all comes together. A log of my life… Let’s see how it goes.