Approaching jQuery popup callback hell

I wrote this post on the jQuery mailing list and nobody replied, so I’m pasting it here. I could really use some advice.

I’m using a modal dialogs and I love them, but I haven’t found a really elegant way to handle actions in the dialog window that require changes to the parent page.

Here’s an example. I have a monthly calendar page that lists employee names on the days they are supposed to work. Clicking on an employee name opens a “shift detail” page in a modal dialog.

That shift detail page has more information like the specific tasks planned for the day, the start time and stop time of the shift, etc. From this shift detail screen, I can remove this particular employee from the schedule by submitting an AJAX POST from this popup.

After I remove the employee, I need to update both the popup window and the original page that hosted the link to the popup window. Right now I do this by adding a callback that fires when the AJAX POST succeeds. That callback then updates both pages. The callback is named “after_remove_employee”.

This system gets really nasty when I use the “shift detail” popup on different screens. For example, in addition to the monthly view, I also have a weekly view with more information. So after an employee is removed from the schedule on the weekly view, I need to do some different things in the callback.

Right now, the way I handle this is that I define the same callback twice. I define “var after_remove_employee = function (data) {…} on the weekly view to do what it needs there, and then I define it differently on the monthly view.

I’ve simplified the problem to help explain it. In reality, I have lots of different popups on lots of different pages, and in each popup, there are many different possible actions.

I’m sure I’m not the only one that’s been in this scenario. What is an elegant solution?

I’m thinking about using custom events. So, the callback after a successful AJAX POST would just fire an “employee removed” event, and everybody subscribed would get a reference to the event object and do whatever they want.

However, I’ve never used JS events before, and I don’t know if this is even possible.

Please, any feedback is welcome.

virtualenvwrapper postactivate and screen is a wonderful combination

I switched to virtualenvwrapper from plain old virtualenv a while back because I found out that virtualenvwrapper has a postactivate hook I can define. Now when I do

$ workon pitz

to start my work day, I get a customized work environment exactly the way I want it. In addition to activating a virtualenv named pitz, the postactivate script changes me into the top of my pitz checkout directory and then it starts up or reattaches a screen session customized just for pitz.

Setting this up was trivially easy. Here’s what my postactivate script looks like:
$ cat ~/.virtualenvs/pitz/bin/postactivate
cd /home/matt/projects/pitz
screen -S pitz -c ~/.screenrc-pitz -d -R

And here is that .screenrc-pitz file:
$ cat ~/.screenrc-pitz
# Matt's homemade .screenrc.

# Draw that blue and red bar across the bottom of the terminal, with the cute
# stuff like the window names and system CPU load.
hardstatus on
hardstatus alwayslastline
hardstatus string "%{.bW}%-w%{.rW}%n %t%{-}%+w %=%{..G} %H %l %{..Y} %m/%d %C%a "

# Let me scroll back a thousand lines or so.
defscrollback 1024

# terminfo and termcap for nice 256 color terminal
# allow bold colors - necessary for some reason
attrcolor b ".I"

# tell screen how to set colors. AB = background, AF=foreground
termcapinfo mlterm 'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm'

# erase background with current bg color
defbce "on"

# Automatically start up a bunch of windows.
screen -t "shell" 0 pitz-shell
screen -t "code" 1
screen -t "tests" 2
chdir docs
screen -t "docs" 3

Everything from the top down to the comment # Automatically start up a bunch of windows. just tweaks how screen works. The last part creates a bunch of named windows. In the first wiindow, it doesn’t start a bash prompt. Instead it starts up the pitz-shell.

One tiny detail that may not be obvious is that if I do $ workon pitz in one terminal, and then maybe I ssh in and rerun $ workon pitz again later, I won’t cause a second screen to start up. Instead, I’ll remote-detach the original session and reconnect to it.

In practice, all my remote servers have highly customized screen sessions already waiting for me. I just need to log in and resume them, and I have all the interesting log files, database connections, and monitoring scripts already up. For example, I know my apache log is open in a pager in window 5. Window 2 has a psql session running. Window 1 has a supervisorctl client running.

This setup shaves off precious seconds when I’m trying to figure stuff out during an emergency.

This stuff isn’t rocket science, but I hope it helps somebody out.

How I cook kohlrabi

I like to cut kohlrabi into matchsticks of about a quarter inch wide. Sometimes I peel off the outer layer because it can be too fibrous.

I dump the match sticks into already-boiling water and cook for about 10-14 minutes, or until I like the texture. I constantly snack on the matchsticks while cooking them.

Once they’re soft enough, I drain it all through a colander and then put everything back in the pot and add a few tablespoons of butter, salt, pepper, and maybe some parmesan cheese, and maybe some red pepper flakes.

If you got it with the leaves, they can be cooked like greens. When my parents were in town, my mom cooked the leaves like this:

  1. cook some onions in butter
  2. chop and steam greens separately
  3. add some sugar and vinegar to the onions
  4. throw in the greens and stir them around

In conclusion, kohlrabi is an underrated vegetable.

Python niceties

nicety (plural niceties)

  1. A small detail that is nice or polite to have but isn’t necessary.

Make a comma-separated string

>>> x = ['a', 'b', 'c']
>>> ', '.join(x)
'a, b, c'

Isn’t that prettier than some big loop?

If you want to join a list of elements that aren’t strings, you’ll have to add a step:>>> x = [1, 2, 3]
>>> ', '.join(map(str, x))
'1, 2, 3'

I’m using map to convert every element to a string first.

Choose a random element from a sequence

>>> from random import choice
>>> choice(x) # doctest: +SKIP
1

The doctest: +SKIP text is just there because the element returned is randomly chosen, and so I can’t predict what the value will be.

Use keyword parameters in your larger string templates

Well-named keywords make big string templates MUCH easier to deal with:>>> tmpl = """
... Dear %(display_name)s,
...
... Greetings from %(origin_country)s! I have millions of
... %(currency_type)s in my bank account and I can send it all to you if
... you first deposit %(amount)s %(currency_type)s into my account
... first.
...
... Sincerely,
... %(alias)s
... %(origin_country)s
... """

Seeing the key names makes it much easier to remember what gets interpolated. Also, it makes it easier to use the same values more than once.

Usually I pass in a dictionary to the template, but sometimes I do this instead:>>> display_name = 'Matt'
>>> origin_country = 'Nigeria',
>>> currency_type = 'US dollars'
>>> amount = '250.00'
>>> s = tmpl % locals()

python contextmanagers, subprocesses, and an ascii spinner

I have a command line script that takes too long to run. I profiled the script and the problem is in some fairly complex third-party code. So instead of fixing that stuff, I’m going to distract users with a pretty ascii spinner.

Run this script to see the spinner in action (you’ll have to kill the process afterward):

$ cat spin_forever.py
# vim: set expandtab ts=4 sw=4 filetype=python:

import sys, time

def draw_ascii_spinner(delay=0.2):
for char in '/-\\\\|': # there should be a backslash in here.
sys.stdout.write(char)
sys.stdout.flush()
time.sleep(delay)
sys.stdout.write('\\\\r') # this should be backslash r.

while "forever":
draw_ascii_spinner()

Next I made a context manager (this is the name of the things you use with the new python “with” keyword) that uses the subprocess module to fork off another process to run the spinner:

$ cat distraction.py
# vim: set expandtab ts=4 sw=4 filetype=python:

import contextlib, subprocess, time

@contextlib.contextmanager
def spinning_distraction():
p = subprocess.Popen(['python', 'spin_forever.py'])
yield
p.terminate()

Finally I added my new context manager to the code that takes so long to run:

def main():

with spinning_distraction():
time.sleep(3) # pretend this is real work here.

if __name__ == '__main__':
main()

If you’re following along at home, you can run python distraction.py and you’ll see the spinner go until the main application finishes sleeping.

The main thing I don’t like is how I’m using an external file named spin_forever.py. I don’t want to worry about the path to where spin_forever.py lives. I would prefer to use some function defined in a module that I can import.

My favorite software quality metric is the income statement

I’ve been working on an open-source project for the better part of a year. It is very complex, very fancy, and has no users except for me. But I love it. It sharpens my skills and gives me a chance to discover puzzles and solve them in the most elegant solution possible.

But in the business world, this approach is downright silly. Nobody writes blank checks to vendors. Nothing in nature works like this either.

When a shark chases down and eats an unlucky spring breaker, the shark burns some calories during the swim, and then gets some calories back during the meal.

On average, the shark has to extract at least many calories while eating as it burned during the chase.

So on to the income statement. The shark makes a good metaphor for a software business. The hunt is the process of acquiring customers. The calories are the firm’s revenues and expenses. A lot of the quality of a software team can be measured in the income statement.

The perfect product is written once and it solves all problems and never needs any updates or extensions or bug fixes. The worst product has to be rewritten from scratch for every new customer and requires lots of bug fixes. Really well-written software falls somewhere between. It supports extensions, but they are quick and safe to do. Bugs can be quickly patched.

It wasn’t obvious to me initially, but this aspect is easy to measure in the cost of goods sold. If a firm lands a deal for $60k, but burns 160 hours rushing some tweaks through, then those hours drive up the cost of goods sold and drive down the firm’s margins.

You can look at the firm’s margins over time and watch whether the app is getting better or worse. Sure, there are mitigating factors, but in the long run, you can’t maintain an attractive income statement and a shitty code base.

How to write a resume for an internship

I’ve been on both sides of internships. I’ve puffed up a skinny resume to look more interesting, and I’ve scanned through resumes looking for a candidate. Here’s some tips:

  • Emphasize coursework. Don’t just list your major. Talk about what you’ve done in various courses and what you liked. Describe interesting stuff you studied.
  • Don’t mention your crappy high school jobs. I don’t care if you managed a Taco Bell. In fact, I will hold it against you because Taco Bell is disgusting.
  • Mention any leadership positions in volunteer organizations. Coaching little league, running an Amnesty International chapter, or anything like that all show you know how to interact politely.
  • Help me figure out what the hell to do with you. Do you want to publish an article? Do you want to go on lots of sales calls? Do you want to learn how to program? If you don’t tell me what you want to do, then I have to guess. Or maybe I won’t guess, and I’ll give you a bunch of tedious work instead.

Is this pylint error message valid or bogus?

The vast majority of pythoners use None, but I prefer descriptive strings for default values, like this:
from datetime import datetime, timedelta

def midnight_next_day(initial_time="use today's date"):

if initial_time == "use today's date":
initial_time = datetime.now()

return initial_time.date() + timedelta(days=1)

Pylint doesn’t like it though:

$ pylint -e f.py
No config file found, using default configuration
************* Module f
E: 10:midnight_next_day: Instance of 'str' has no 'date' member (but some types could not be inferred)

I changed from using a string as the default to None, and then pylint
didn’t mind:

$ cat f.py
from datetime import datetime
def midnight_next_day(initial_time=None):

if initial_time is None:
initial_time = datetime.now()

return initial_time.date() + timedelta(days=1)

$ pylint -e f.py
No config file found, using default configuration

Checking where a variable is None is probably much faster than doing string comparison, but I find the string more informative.

Looking for comments. I already posted this to comp.lang.python here and got some good feedback.