Wednesday, June 29, 2011

Tracking users via cookies

Disclaimer: this is a "work in progress" or a "I don't know if this is good" post.

I have a simple web app called Sea Salt that serves a javascript game. I'd like to keep track of users at a very low level- just track them across requests, and maybe track if they come to the site again the next day but that's not super important. I don't want to make them log in or anything.

And it has a splash screen. I want the following behavior:
- first time you come to /, you get "welcome to this app, click to start"
- if you click that, you go to /play and I create a User entry for you.
- if you come to / again, you get "you've already started. click here to continue OR if that wasn't you, click here to restart."
- if you restart, I create a new User entry for you.

Very simplified App Engine python server code:
application = webapp.WSGIApplication([('/', Intro),
                                      ('/restart', Restart),
                                      ('/play', Play), ...
Set up the URL mappings.

class Intro(webapp.RequestHandler):
    def get(self):
        cookie_id = self.request.cookies.get('sea_salt_id')
        if cookie_id and User.get_by_id(int(cookie_id)):
            #(render already_started.html)
        else:
            #(render index.html)
Pretty simple. If you go to /, first check the cookie. If your cookie corresponds to a real user, then you must have been here before. already_started.html contains links to /restart and /play. Otherwise, you haven't been here, so show you the splash screen, which has just a form that posts to /play.

class Restart(webapp.RequestHandler):
    def get(self):
        self.response.headers.add_header(
            'Set-Cookie',
            'sea_salt_id=-1; expires=Thu, 01-Jan-1970 00:00:01 GMT')
        self.redirect('/')

If you to go to /restart, delete your cookie, and send you back to /.

class Play(webapp.RequestHandler):
    def get(self):
        cookie_id = self.request.cookies.get('sea_salt_id')
        if not cookie_id or not User.get_by_id(int(cookie_id)):
            self.redirect('/')
        #(render game.html)

    def post(self):
        cookie_id = self.request.cookies.get('sea_salt_id')
        if not cookie_id or not User.get_by_id(int(cookie_id)):
            user = User.create()
            user.put()
            id = user.key().id()
            self.response.headers.add_header(
                'Set-Cookie',
                'sea_salt_id=%d; expires=Fri, 31-Dec-2020 23:59:59 GMT' % id)
        #(render game.html)

This is the trickiest. If you go to /play via a GET (like typing it in the address bar), either let you keep playing (if you've already started) or redirect you to /. If you go to /play via a POST, either let you keep playing (if you've already started) or create a user for you and then let you play. I think this is right, because GETs should be read-only while POSTs can write, right?

This all seems a little too complex for its own good, but it seems to work. If you have any better ideas (or if I've made any mistakes), I'd love to hear them. Thanks!

Wednesday, June 1, 2011

.vimrc: colorcolumn

80 character line? No problem!

" displays a red column at 80 characters
set colorcolumn=80

Thanks to this stack overflow post.

EDIT: you should probably surround it with an "if exists" to avoid annoyance if you port your .vimrc to another machine that has an older version of vim (colorcolumn is new in 7.3). Here's the syntax:


if exists('+colorcolumn')
  set colorcolumn=80
endif


Thanks to this answer on that same stack overflow post.