Archive for July 2008

Easy examples of redundancy

2008-07-29-Tue

The following are some examples of simple redundancies in procedural programming. They are given in Python. I typically run into these when I simplify my code after writing it for the first time or after making edits.

Using if-else to assign a Boolean:

if condition:
    x = True
else:
    x = False

Simplified:

x = condition

(Requires that condition is a Boolean.)

Returning immediately after an assignment:

y = value
return y

Simplified:

return value

Common code among branches:

if condition:
    group of statements A
    group of statements B
    group of statements D
else:
    group of statements A
    group of statements C
    group of statements D

Simplified:

group of statements A
if condition:
    group of statements B
else:
    group of statements C
group of statements D

Nested if-statements:

if condition0:
    if condition1:
        statements

Simplified:

if condition0 and condition1:
    statements

Requires that the outer if-statement does not have an else-clause.

If-statements with the same actions:

if condition0:
    statements (same)
if condition1:
    statements (same)

Simplified:

if condition0 or condition1:
    statements (same)

Requires condition0 and condition1 to be disjoint, or statements to be idempotent.

Department of redundancy department: Python lambdas

2008-07-29-Tue

A coworker recently pointed out the lambda (anonymous function) in this piece of code that I wrote was redundant:

project_key_regex = re.compile(r"^project(\d+)$")
for key in filter(lambda k: project_key_regex.match(k), req.args.keys()):
    ...

It would be improved like this:

project_key_regex = re.compile(r"^project(\d+)$")
for key in filter(project_key_regex.match, req.args.keys()):
    ...

Apparently, methods of an object can be thought of as functions that have one variable bound to the object.

A bit of info about the phrase “Department of Redundancy Department” on Everything2: http://everything2.com/title/Department%2520of%2520Redundancy%2520Department

The revised create users and create projects doublet

2008-07-28-Mon

A picture is worth a few hundred thousand bytes, so without further ado, this is what’s new:

Although it isn’t apparent from the screenshot, a new memberships row (with “select a user/project” and “select a role”) is added whenever the final row is filled.

Compare these to the interfaces that we had before:

Create Multiple Users: Error handling

2008-07-23-Wed

The new Create Multiple Users page has a back-end that gives information on all sorts of errors:

Great bug writers think alike

2008-07-10-Thu

a.k.a. “A subtle bug from a perfect storm of HTTP, HTML, JavaScript, and DOM

My current work

I’m currently in the midst of implementing a new interface for creating new users. This is how it currently looks like:

The project membership table is created on the client side using JavaScript. The administrator adds and removes memberships (rows) without submitting the form.

HTML forms and HTTP, revisited

(Recall A simplified overview of HTML forms.) When the form is submitted over HTTP, all that is sent is a dictionary of key-value pairs. Each form element (text box, checkbox, select, etc.) generates at most one item for the dictionary, where the key is the name of the element (given by the name attribute) and the value is the string value of the element (“alice”, “pA55w0rd”, “on”, “viewer”, etc.).

Perhaps surprisingly, the dictionary’s keys need not be unique. For example, if there are two text boxes named “foo”, one with the value “bar” and one with the value “baz”, then the submitted form data would be “foo=bar, foo=baz” (or possibly “foo=baz, foo=bar”).

The tricky stuff: Client side

So for the form pictured above, the submitted information will be something along the lines of “username=roule, email=roulette@localhost, realname=Roulette, affiliation=Pani, password=01234567″… But wait! What about the membership table?

The first (but incorrect) idea that comes to mind is to give the same name for all elements in a column. For example, all the project selects would be named “project”, and all the role selects would be named “role”. The problem with this is that in the presence of multiple “project” and “role” keys, we don’t know which one was assoicated with which. A concrete example: Suppose John is a viewer in Alpha and a developer in Beta. The submitted form data would look like “project=Alpha, project=Beta, role=viewer, role=developer”, but with the entries being presented to the server in an arbitrary order. So it would be impossible to associate projects and roles uniquely.

Clearly then, the solution is to give each field a unique name. A simple method is to add a counter after the name – like “project0”, “project1”, “role0”, “role1”, etc.

The tricky stuff: Server side

How would the server process this information when it’s submitted? Assume that the client does not send the count of the number of rows. One way for the server to handle the variable number of rows is to start from a prearranged fixed number – 0 in this example – and process form fields and count upwards until the first n such that the submitted field “projectn” does not exist.

So far so good, right? But notice that on the client side, membership rows can be removed. Suppose that for simplicity, we don’t re-enumerate the remaining form fields. So if we had “project0”, “project1”, “project2” and then removed the middle entry, then we’d be left with “project0” and “project2”. Clearly, this interacts badly with the server side handler just described, since “project2” will be ignored. (Because “project1” does not exist, the server stops processing.)

Solutions

There are two relatively simple solutions to this problem:

  • Keep the client-side behaviour and modify the server-side behaviour. For each new row, give an auto-incrementing numerical suffix for the form fields. Do nothing special when removing rows. On the server, iterate through all form fields that have a name of the form “projectN“, where N is a number string. Ignore “gaps” in the numbering.
  • Modify the client-side behaviour and keep the server-side behaviour. When a row is removed, re-enumerate the rows that follow it. This is the solution that I picked, for no particular strong reason.

Great bug writers think alike

Now, if this problem were merely hypothetical, I wouldn’t have a good reason to write this rant-like-lengthed post about it. But it does already exist in DrProject! Namely, it manifests itself in the “Membership Preferences” section of the Edit User page and also on the current user’s Preferences page.

The offending server-side form handler code is found in register.py. (The complicated process of pinpointing this piece of code will be another story.)

Final role editor (for now)

2008-07-09-Wed

This is the current state of the role editor. Development on it has wrapped up a few days ago. It is available at (SVN) https://drproject.org/svn/drproject/DrProject/branches/newadmin.

Initial screen:

Adding a role:

Role added:

Editing a role (click on pencil and paper icon):

Done editing a role:

Removing a role that is still in use (click red X, get redirected to this page):

Note that this role editor is AJAX-powered. The actions of adding, editing, and removing a role are immediately performed on the server, but do not incur page reloads on the client.