Ruby/Rack and Multiple Value Request Param Pain — Part One

Today, in quick succession, I encountered a frustrating situation with each of two tools. It was the same problem in both cases—dealing with multiple value request parameters. There’s something you think about every day. Anyway I was tracking down a problem that has been causing us grief for weeks now, but we never spent the time to actually deal with it. What makes this even more annoying is that both these tools (Rack and Patron) are otherwise really pretty good.

Here’s the situation. The HTML select element supports multiple selections. When the browser posts to the server, the multiple selections are passed back as key-value pairs with the key repeated once for each selection.This is standard HTTP POST behaviour.

I’m going to pick on Rack in Part One. This was especially annoying since a) Rack is so solid; b) Rack is infrastructure that you expect to just work. In other words, this was a surprise.

Rack wants to store parameters in a hash map, which is a perfectly reasonable Ruby thing to do. The thing is, hash maps have unique keys. So if you simply assign the keys and values sent from the browser you’ll write over the previous value, and in the end have only one value (the last one selected). This is what Rack does unless you use naming conventions to signal your desire for an array result. So if you named your <select> as ‘several’ then you’ll never get more than one key-value in the Rack params object. Doesn’t matter how many there were, you get zero or one. If, however, you named your select as ‘several[]‘ you’d get a key mapped to an array with all of the values (or so it seems, I didn’t bother trying). There is a more complex naming convention supported as well, but I’ve got little interest in that.

This is not what I expected. And it sure isn’t what I need.

This approach presumes you actually have control over the names of those select elements. As it happens in the application we’re developing we don’t have any such control. Moreover, if you are trying to implement a REST API that uses HTTP post for communication (rather than something like XML or JSON) then you’ve got the potential for similar difficulties, and there’s no way you’ll have control over the names on a published API.

So how to fix this? Well, this is where Ruby comes to the rescue, well supported by the rather clean implementation of Rack.

You monkey patch Rack. A lot of people consider monkey patching a Very Bad Thing. They are wrong. So what’s monkey patching? When a client of a library (that’d be our application, or the examples in this gist) redefines a method, or extends a Class, or something similar, the client is monkey patching. In Ruby we’ve had a lot of mileage out of Ruby’s support for this. It is used to extend the language, and to fix things. Like this problem.

The relevant lines are 15 through 22 in the gist. In Rack’s code base these lines correspond to a single line that overwrites existing key-value mappings. In the monkey patch, I’m allowing for a mapping from a key to an array of values. The rest of the code is untouched and so the other naming conventions will be as before.

The other two files are just two test cases, one without, the other with the monkey patch.

Anyway, that fixes the first issue. The second, I’ll talk about in Part Two.