Bulk Registrar
Biz: ,
1 comment

Does anyone have a favorite registrar for bulk registration and transfer? Ideally, I’d like to give it a list of a couple dozen domain names and auth codes to transfer over at once. When maintaining them, I’d like to be able to select a swath to edit all of their DNS/whois/etc. settings at once. I’d also like to see free whois privacy (it’s a rip-off to actually pay money for it).

I’ve been poking around and the registrars I can find are wildly overpriced ($9-20 per name), charge for private whois (which costs them nothing), or simply don’t support bulk operations.

Don’t say GoDaddy. I don’t support torture.

Django Template Tag for Dictionary Access
Code: , , ,

About a million times when writing Django pages I’ve been iterating through a list of objects and wanted to look up a value in a dictionary keyed by object.id. But you can’t, the built-in tags don’t allow it.

The standard workaround is to loop over the list and zip the hash data into some kind of bigger list, but this is expensive if the list is big and just plain annoying, especially if you’re on Django 0.96 and can’t unpack tuples with your for tag.

So I finally hacked up a template tag to give me access to dictionary contents:

def hash(h, key):
    return h[key]

And it’s used like:

{% for o in objects %}
  <li>{{ dictionary|hash:o.id }}</li>
{% endfor %}

It may not be beautiful, but it mostly works. Annoyingly, you can’t do dictionary|hash:o.id|hash:'foo' to get at an inner hash — anyone know why?

2011-02-19: Good criticism of this tag on a wontfix Django bug.

My Four-Year Patch
No comments

I’d actually forgotten it and had to scratch my head a bit when I got the message my ticket was closed. In August of 2003 I submitted a patch to Fluxbox to allow users to hold Alt and middle-click to push a window to the bottom of the stack. The source to Fluxbox has changed so much I can’t even find the section of code that the patch was written for, so I suspect that my ticket was closed when a developer swept through old tickets and recognized this as done.

NearbyGamers Advertising
Biz: ,

I posted this to NearbyGamers, but lots more people read my blog than read the forums there, so I wanted to crosspost real quick.

I’m stepping up the NearbyGamers advertising and could use help coming up with attention-getting things to say in the ads. Currently I have a static ad that just say “You should play more tabletop games.” and an animated ad saying “Need another _______?” and rotates in the words GM, player, opponent, LARPer, nemesis.

If you’ve got a minute, please post a comment with a suggestion or two.

Painless Upgrade to Rails 2.0
Code: , , , , , , , , , , ,
No comments

I spent a dead-easy 2.5 hours last night updating NearbyGamers to Rails 2.0. My svn commit message read (with links added here for convenience):

Updated to Rails 2.0.1

  • rm’d lib/slash_urls.rb: Rails switched from ; to / to separate actions
  • rm’d lib/resource_requirements.rb: now included in ActiveResources
  • rm’d app/helpers/tags_helper.rb and gamers_helper.rb that had old controller names and were unused anyways
  • app/controllers/gamers_controller.rb: documented and fixed raw post variable extraction
  • updated post_path linkers in many views, controllers, and tests
  • renamed discussion_anchor_post_path to anchor_discussion_post_path to match the new ordering for nested resource urls
  • fix tag sorting on tag index
  • move tags from /tags/Board+Games to /tags/Board_Games, as Rails fixed the bug that encoded ‘ ‘ as ‘+’ in non-get variable parts of the URL

The Details

The first two files I deleted were my patches to URL generation that Rails now includes. The next two helper files I never used, but had had old (singular noun) names.

The bit about documentation: the way to get at raw post variables changed, so I tweaked code and left myself a reminder of why I’m doing it (I had to look at the changelog, which means it wasn’t self-evident).

Nested resources (I have nested map.resources :discussions { |d| d.resources :posts }) changed from discussion_edit_post_url to edit_discussion_post_url, so there was a bunch of places to change that. The next change about anchor is me following this convention with my own convenience method.

Tag sorting on the index page was a bug that Snarky pointed out to me. When I made some performance tweaks to that page I forgot to keep sorting tags as they came out of the database. This arguably should’ve been a revision of its own as it’s unrelated to the upgrade, but it was an easy one-line bugfix so I let it in.

Last, I renamed the tag pages. The tag model’s to_param just returns self.name and Rails would turn “Board Games” into “Board+Games”. It’s common but technically incorrect, as + only means space in encoded GET/POST variables. The proper encoding of “Board%20Games” was really noisy, so I took a page from Wikipedia’s pagebook and use underscores instead, like “Board_Games”. Was one line in the to_param and one line in the controller’s load_tag before_filter, a dozen lines of tweaking old tests, and seven lines of new tests. Eeeeeasy.

The Verdict

Tests, tests, tests. If I didn’t have a solid test suite, I’d be noticing bugs two months from now in the least-frequently-exercised bits of code. Not only would I not have remembered to test some of the functionality, I wouldn’t have remembered the corner cases — or maybe I’d just have gotten bored of clicking through pages over and over and ignored the corner cases.

None of these were hard fixes and none were frustrating. I’d probably have finished even sooner if I hadn’t also been poking lolcats and chatting with friends. And now I have a big list of shiny new Rails 2.0 features I can put to work in NearbyGamers.

A Very Short Review of Portal and Steam
Games: , , , ,

Portal: 0/10

Crashes after the Valve logo. Apparently it fails to detect that it doesn’t support my video card. This is the first time I’ve bought commercial software in about five years, and it’s pretty sad that free, open source software has better forums and FAQs and such when I have to grovel around online because something blew up.

Steam: 0/10

Steam is the hoop you have to jump through to get Portal. At no time when installing Steam, running Steam, browsing for games, viewing Portal’s listing, purchasing Portal, downloading Portal, or installing Portal did it notice that they don’t support my video card.

There is room for these scores to be adjusted: for example, I might play Portal at a friend’s house. And Steam’s score could go negative if they fail to give a refund.

Drifting Into Test-Driven Development
1 comment

About two years ago I first read about Test-Driven Development on the c2 wiki. It’s a simple plan: before you write code, write the tests that will exercise it.

My thoughts: this is exactly backwards.

But I really liked the pages I read about automated testing and started putting that in practice almost immediately. Rather than sitting there and hitting reload and filling in forms and clicking buttons and waiting for pages and forgetting to check what happens when you leave half the form blank and… well, rather than all that error-prone tedium, I wrote code to test my code. And that’s great. It saves me from a lot of “oh yeah, I forgot that thing over there called this code” bugs.

I kept hearing about TDD, though. Books would mention it, blogs would tout it it. I guess it’s squirmed its way into my habits.

In the last few months of coding on NearbyGamers I’d hack at the models a bit, write some tests for them, and hey, as long as I’m writing tests, add some tests for the controller code I was about to build on top of that. And then maybe, after I implemented the basic feature, I’d write a test for an improvement and then tweak that code. In the last two weeks I’ve been writing code for the messages and forums and I just recognized that my tests have snuck in front of my code almost every time.

And it doesn’t feel backwards anymore. It just makes sense to think about my interface first, and to spend a minute pondering what sort of devious corner cases I can throw at it. I’m writing more tests, letting fewer bugs into production, and having more fun. I`ve broken the “Ehhh… it works, I’ll check it in and write some tests tomorrow” habit.

So this is my little bit of cheerleading for Test-Driven Development, a little more exposure for your unconscious mind to chew on.

Choose Your Candidate
Biz: , , ,

I haven’t posted a work update in a while, because mostly I’ve had a lull between projects and a new coworkers, so we’ve been cleaning out the bug and minor feature list. There’s a really neat project I want to link to called
Choose Your Candidate that I was only peripherally involved with (I gave advice for how to do the ranking math).

I really like this app: it’s entirely about the differences between the candidates in their own words. I find it orders of magnitude more interesting than coverage of campaigning and verbal jousting and polling. I went in with two favorite candidates. In my results, they ranked second and last. I barely have researched my new top-ranked candidate, so I’m happy to have more reading to do and a new favorite.

If you do take this quiz, try both the Republican or Democrat edition, or at least read the answers in the other party’s. I was struck that every single candidate said that “securing the borders” was their top priority for immigration. Apparently the 49th parallel is so dangerous and that Canadians are such cause for concern that everything else in immigration is unimportant.

An Academic Inconvenience of Python

Sometimes Python’s roots in academia bug me. Lots of functions have a computer science feel instead of a software development feel. Here’s an example I just ran into: I wanted to fit as many sentences as possible from a long text into 255 characters. So I wrote:

s = s[:255][:max(s.rindex('.'), s.rindex('!'), s.rindex('?')) + 1]

This snippet chops it down to the 255 max, finds the ., !, or ? marking the end of the last sentence, and chops there. Great, right? Except it doesn’t work.

Instead of returning None when it can’t match the substring, rindex throws ValueError. So unless the first 255 characters of the string contain a ., !, and ? it’ll throw an exception. OK, let’s try:

rightmost = -1
    rightmost = s.rindex('.')
except ValueError:
    rightmost = max(rightmost, s.rindex('!'))
except ValueError:
    rightmost = max(rightmost, s.rindex('?'))
except ValueError:
s = s[:255][:rightmost + 1]

Eww. OK, let’s encapsulate that redundancy:

def no_exception_rindex(s, substring):
        return s.rindex(substring)
    except ValueError:
        return None
s = s[:255][:max(no_exception_rindex(s, '.'), no_exception_rindex(s, '!'), no_exception_rindex(s, '?')) + 1]

That’s… well, it’s at least a little better. Lucky that max doesn’t mind seeing None, I could imagine it throwing its own ValueError. But I wouldn’t call this code good, we’ve been forced to switch out of object-oriented code because we can’t add our no_exception_rindex to the string objects.

Here’s another approach:

def rightmost_punctuation(s):
    index = len(s) - 1
    while index > 0 and s[index] not in ['.', '!', '?']:
        index -= 1
    return index
s = s[:255][:rightmost_punctuation(s) + 1]

I’d actually call this one worse, as it’s not immediately obvious what it’s doing. And anytime I create a variable and then tinker with it inside a loop I feel like I want to rewrite that code to use map and/or reduce.

Tomorrow I’ll redo this example in Ruby to talk about open classes, but for today does anyone have a better approach in Python?

Because Internet Explorer is a Failure, That’s Why
Code: , , , , , ,

About once a month since Firefox came out and was promptly recognized as a six-gallon bucket of awesome I read a blog post about how developers are lazy, shiftless bastards because they don’t want to support Internet Explorer anymore. Most recently I read Brian Reindel make this claim, so I’m going to pick on him while I rebut this insult.

Yahoo defined the term “graded browser support“: popular browsers get A-grade support, less common ones get C-grade support, and a tiny number of almost-unused browsers get X-grade support (which is an obfuscated way to say “no support”). It makes sense to formally specify where you’ll spend your time. Reindel manages to turn this whole definition around, though. He writes about A-grade browser support, then drifts into talking about “A-grade browsers”. These are not at all the same thing.

Developers can choose to give A-grade browser support to Internet Explorer because it enjoys a huge (if declining) market share. Users can’t tell whether they’re seeing IE’s misfeatures, flaws, and bugs or our website’s, so we work late into the evening to make IE behave enough that we don’t look bad. Sometimes developers decide that IE isn’t worth the trouble, and Reindel opines that it’s because they’re unprofessional or incompetent. No, it’s because Internet Explorer is a failure, what’s why.

Internet Explorer is not an “A-grade browser”. It has a large number of painful and common bugs that were left to fester for five years between IE 6 and 7 — and then many still are unfixed. The rise of Firefox (and, to a lesser extent, Opera and Safari) has shown developers how good browsers can be, to say nothing of how well browsers can support developers. It’s normal and healthy that developers reduce IE support. Aside from the pure professional joy of tools that work, it doesn’t make business sense to keep spending the majority of your time working around the problems caused by one browser unless that one browser has a strong majority in your userbase.

When building the moderately-complex layouts for NearbyGamers I realized I was wasting time supporting IE for a tech-savvy audience who likely wouldn’t be using it. Even though NG is a professional site and the current highlight of my portfolio, with IE’s 32% share it would be a mistake to spend time tearing my hair out over it instead of improving the site in general. My analytics show that IE users view the same number of pages, visit for the same length of time, and convert in the same numbers as users of A-grade browsers. Yes, in IE the fonts are weird, elements are mispositioned, text spacing is inelegant, and there are probably many other small bugs I haven’t even bothered looking for. The site isn’t hemorrhaging IE visitors, so IE is getting more support than it’s earned.

This behavior isn’t the straw-man “laziness” that Reindel attacks, it’s the highest professional business sense and commitment to productive quality. Microsoft ignored our half-decade of “constant complaining” because they thought they had a lock on the market. Developers who publicly reduce their support for IE are using their voice and their practice to push for better support from the browsers.

Fatal error: Call to undefined function twentyseventeen_get_svg() in /home/malaprop/push.cx/wp-content/themes/pushcx/archive.php on line 45