Recent Posts

Compiling Nginx With Cache Purging Support

I've been experimenting with different caching methods ever since reading this post on the performance differences between no caching, built-in Django caching, memcached, static files, and Varnish. Varnish and static files were the fastest by a large margin.

Since I'd like to avoid adding another proxy layer to my setup (already using both Nginx and Apache), I looked into the caching capabilities built-in to Nginx. It seems that originally if you wanted to use caching with Nginx you used ncache. This is no longer the case since that project has been built into the Nginx core.

To add support for purging a cached item, you need to install the ngx_cache_purge module. Unfortunately, installing an Nginx module isn't as easy as installing an Apache module. It requires a re-compile and when you're used to just installing your software through apt-get, that can be a little intimidating. I also didn't want to lose the special setup that the package installer provided including using the /sites-available/ and /sites-enabled/ folders, the init script, etc.

Here's a quick guide on re-compiling while still remaining compatible with the default package:

Install Nginx through aptitude

This is really easy (and you probably already did this):

apt-get install nginx

Install compile tools

A few of the compile libraries needed by Nginx aren't installed by default. To install them, use the following command:

aptitude -y install build-essential libc6 libpcre3 libpcre3-dev libpcrecpp0 libssl0.9.8 libssl-dev zlib1g zlib1g-dev lsb-base

Download and extract the source

Download and extract the source of both the newest stable Nginx and the cache_purge module:

cd /usr/src/
wget http://www.nginx.org/download/nginx-0.7.65.tar.gz
wget http://labs.frickle.com/files/ngx_cache_purge-1.0.tar.gz
tar -xvf nginx-0.7.65.tar.gz
tar -xvf ngx_cache_purge-1.0.tar.gz
cd nginx-0.7.65/

Configure compile options

You can view the packaged Nginx's compile options by using:

nginx -V

For my server, I kept generally the same options but added the cache_purge module to the end using --add-module:

./configure --sbin-path=/usr/sbin --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --with-debug --with-http_stub_status_module --with-http_flv_module --with-http_ssl_module --with-http_dav_module --with-ipv6 --add-module=/usr/src/ngx_cache_purge-1.0

This command will write out a summary of the configured options.

Compile Nginx

Compiling Nginx can be done with this command:

make && make install

A lot of text will scroll by during the compilation. In my case, I hadn't stopped my Nginx service before compiling and an error occurred when the install process attempted to copy the new Nginx executable over the current one. To fix this, I had to manually stop, copy and restart Nginx:

/etc/init.d/nginx stop
cp objs/nginx /usr/sbin/nginx
/etc/init.d/nginx start

If everything worked, you can view the current version and compile information of the running Nginx by using:

nginx -V

And the final result should be:

nginx version: nginx/0.7.65
built by gcc 4.4.1 (Ubuntu 4.4.1-4ubuntu8)
TLS SNI support enabled
configure arguments: --sbin-path=/usr/sbin --conf-path=/etc/nginx/nginx.conf 
--error-log-path=/var/log/nginx/error.log --pid-path=/var/run/nginx.pid 
--lock-path=/var/lock/nginx.lock --http-log-path=/var/log/nginx/access.log 
--http-client-body-temp-path=/var/lib/nginx/body 
--http-proxy-temp-path=/var/lib/nginx/proxy 
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi --with-debug 
--with-http_stub_status_module --with-http_flv_module 
--with-http_ssl_module --with-http_dav_module --with-ipv6 
--add-module=/usr/src/ngx_cache_purge-1.0

Categories: Linux, Nginx

Creating a Django Gravatar Template Tag (Part 2)

This post is a continuation of the first post on creating a gravatar template tag.

My test script was fairly straight forward to get working as a template tag. The only gotcha was not realizing that any values passed into the template tag are simple strings and that it was my job to resolve any variables being passed in. Here's an example:

Template:

{% gravatar comment.user_email 64 as gravatar %}

Tag code:

class GravatarNode(Node):   
    def __init__(self, email, size, varname):
        self.email = email

    def render(self, context):
        return self.email

@register.tag
def gravatar(parser, token):
    bits = token.contents.split()
    return GravatarNode(bits[1], bits[2], bits[4])

This template tag will render:

comment.user_email

Since I was hashing this value and passing it to the gravatar service it took me a little while to figure out what was happening.

To get it to resolve the actual value of the comment.user_email variable, you have to wrap the string variable in a template.Variable() and then call the resolve(context) method of the variable inside the tag's render() method:

class GravatarNode(Node):   
    def __init__(self, email, size, varname):
        self.email = template.Variable(email)

    def render(self, context):
        return self.email.resolve(context)

@register.tag
def gravatar(parser, token):
    bits = token.contents.split()
    return GravatarNode(bits[1], bits[2], bits[4])

Now it will return the comment user's e-mail address.

Here's the completed code for the template tag. You can see how it's used in a template based on the main tag's comments:

from django import template
from django.template import Library, Node, TemplateSyntaxError
import urllib, hashlib, httplib

register = Library()

GRAVATAR_DOMAIN = 'gravatar.com'
GRAVATAR_PATH = '/avatar/'

class GravatarNode(Node):   
    def __init__(self, email, size, varname):
        self.email = template.Variable(email)
        self.size = int(size)
        self.varname = varname

    def render(self, context):
        hash = hashlib.md5(self.email.resolve(context)).hexdigest()
        context[self.varname] = get_gravatar(hash, self.size)
        return ''

def is_default(hash):
    '''
    Returns whether the specified gravatar is returning the default icon.
    '''
    try:
        query = urllib.urlencode({
            'gravatar_id': hash,
            's': 1, # Smallest size available
            'default': '/' # Causes a re-direct when the gravatar is missing
        })
        full_path = '%s?%s' % (GRAVATAR_PATH, query)

        # Create connection and test for 302 redirect
        conn = httplib.HTTPConnection(GRAVATAR_DOMAIN)
        conn.request('HEAD', full_path)
        response = conn.getresponse()

        return response.status == 302
    except:
        return True

def get_gravatar(hash, size):
    '''
    Returns an object with the gravatar information.
    '''
    query = urllib.urlencode({
        'gravatar_id': hash,
        's': size
    })

    return {
        'src': 'http://%s%s?%s' % (GRAVATAR_DOMAIN, GRAVATAR_PATH, query),
        'width': size,
        'height': size,
        'is_default': is_default(hash)
    }

@register.tag
def gravatar(parser, token):
    '''
    This tag is used for rendering a gravatar icon.

    You can also check that the gravatar exists by checking the is_default property.

    Usage::
        {% gravatar email_address size as variable_name %}

    Example::
        {% gravatar user.email 64 as icon %}
        {% if not icon.is_default %}
            <img src="{{ icon.src }}" width="{{ icon.width }}" 
            height="{{ icon.height}}" alt=""/>
        {% endif %}
    '''
    bits = token.contents.split()
    if len(bits) != 5:
        raise TemplateSyntaxError, 'gravatar takes exactly four arguments'
    if bits[3] != 'as':
        raise TemplateSyntaxError, 'third argument must be "as"'
    return GravatarNode(bits[1], bits[2], bits[4])

You can download full template tag here. Since I'm still fairly new to Django and Python I would appreciate any feedback anyone has to offer. Please add a comment or contact me directly. Thanks and enjoy!

Categories: Django, Python

Creating a Django Gravatar Template Tag (Part 1)

I spent the last couple of days implementing Django's comment framework for my blog. Since making a comment requires the user's e-mail address, I wanted to display the user's gravatar icon next to their name (but only if they have a gravatar setup).

How Gravatar Works

Gravatar

Gravatar is a free service that lets a user associate a graphic with their e-mail address. The only thing required for sites to implement the service is to create a md5 hash of the user's e-mail address. You then make a request to retrieve that user's icon:

http://gravatar.com/avatar/?gravatar_id=1440826ae9908cf7c415dbfd3a66b240&s=64
Lance McNearney

The above request returns my gravatar icon at 64x64 pixels (the little graphic to the left).

Existing Django/Python Implementations

Searching for existing implementations of Gravatar template tags turned up the following two results:

The first project is aimed at extending Django's User model. It also renders the gravatar as an img tag without providing any context variables for me to integrate into my own template HTML.

The blog post by Drew Engelson was a much better implementation. The only piece missing was that I wanted to check to see if a gravatar exists for an e-mail address instead of just providing a default image.

Since neither one of these solutions supported checking if a gravatar exists, I set out to create my own template tag.

Does a Gravatar Exist?

The gravatar service will return its own "default" image for users who don't have a gravatar setup. You can also pass a "default" parameter to the service with your request and it will issue a 302 re-direct to that address instead of returning the service's default image.

My goal was to issue a simple HEAD request to the service with a default parameter and if it issued a 302 re-direct response I knew the gravatar didn't exist for the specified e-mail address. Unfortunately, the urllib library in Python doesn't support HEAD requests and it also automatically follows re-directs to the new address (and doesn't tell you that it followed the re-direct). Because of this, I needed to use the lower level httplib library.

Here's a simple test script that implements the functionality I wanted to achieve:

#!/usr/bin/env python
import sys
import urllib, hashlib, httplib

GRAVATAR_DOMAIN = 'gravatar.com'
GRAVATAR_PATH = '/avatar/'

def main():
    email = sys.argv[1]
    hash = hashlib.md5(email).hexdigest()
    print email + ' = ' + hash

    query = urllib.urlencode({
        'gravatar_id': hash,
        's': 1, # Smallest size available
        'default': '/' # Causes a re-direct when the gravatar is missing
    })
    full_path = '%s?%s' % (GRAVATAR_PATH, query)
    print full_path

    # Create connection and test for 302 redirect
    conn = httplib.HTTPConnection(GRAVATAR_DOMAIN)
    conn.request('HEAD', full_path)
    response = conn.getresponse()
    print response.status

    if response.status == 302:
        print 'No gravatar :('
    else:
        print 'Gravatar found :)'

if __name__ == '__main__':
    main()

You can download the full gravatar test script if you're interested.

With my e-mail address passed to the script it found a valid gravatar:

$ gravatar.py lance@mcnearney.net
lance@mcnearney.net = 1440826ae9908cf7c415dbfd3a66b240
/avatar/?default=%2F&s=1&gravatar_id=1440826ae9908cf7c415dbfd3a66b240
200
Gravatar found :)

And testing with a bogus e-mail address returned the following:

$ gravatar.py bogus@example.com
bogus@example.com = 42d33ed1a79b32726b43ade0460c7ff8
/avatar/?default=%2F&s=1&gravatar_id=42d33ed1a79b32726b43ade0460c7ff8
302
No gravatar :(

Now I just needed to turn this example code into a Django template tag...

Read part two of this post for the template tag implementation.

Categories: Django, Python

Markdown + Pygments + Django = Easy

Pygments

I've gotten used to using markdown because of Stack Overflow. One of the coolest features is being able to have your code automatically highlighted. I discovered this is really easy to implement in Django:

Download Libraries

Install the following two libraries:

I did it the easy way by using easy_install:

easy_install pygments
easy_install markdown

Update Models

You need to import the markdown library in your models.py. In my case, I added an additional TextField (body_html) to my Post model to store the rendered HTML. This field will get updated every time the Post is saved. The codehilite extension for the markdown library adds the pygments code highlighting support:

from markdown import markdown

class Post(models.Model):
    title = models.CharField(max_length=128)
    slug = models.SlugField(unique_for_date='publish')
    body = models.TextField()
    body_html = models.TextField(editable=False, blank=True, null=True)

    def save(self):
        self.body_html = markdown(self.body, ['codehilite'])
        super(Post, self).save()

CSS Styling

Take a look at the pygments demo to find a color scheme that you find appealing. I picked the "tango" theme.

The next step is to generate a stylesheet file for your theme:

Since I downloaded the files, I had to do a search/replace for ".highlight" and replace it with ".codehilite" since that is the class that wraps the <pre> code when code blocks are generated.

I then included the stylesheet in my base template for my site:

<link type="text/css" rel="stylesheet" href="{{ MEDIA_URL }}css/pygments/tango.css"/>

I also added some additional styling to the make the code blocks look better in my blog posts (this is SASS code):

.codehilite
    pre
        +round_less
        padding: .5em
        margin: 1em 0
        background-color: #fafafa
        border: solid 1px #ddd
        width: auto
        overflow: auto
        font-family: Consolas, Monaco, Courier New

All Done

It was really that simple. I think I spent more time tweaking the css styling than actually implementing the code.

Categories: Django

Stack Overflow DevDays 2009 Review

DevDays 2009

I attended Stack Overflow DevDays 2009 in San Francisco this last October. Here is my review of the conference (in chronological order over the course of the day):

  • No breakfast - There weren't any large banners or friendly greeters for the conference but we found it. The website and e-mail we received for the conference indicated there would be breakfast but all we found was coffee. We had to run down the street and visit a Safeway to grab something to eat.

  • Keynote by Joel - The videos and his speech were very good and exciting. Joel is a great speaker and seems like a great leader. His general message of "elegant design" was spot on although it's been done before. My fellow developer (Tyler Allen) commented that he saw a similar presentation with the exact same design screen shot examples when we went to Adobe MAX a few weeks earlier. Still great though.

  • Python with Mark Harrison - I've been really interested in Python and Mark showed a single screen of code that implemented a spell checker. I thought it pretty obfuscated and hard to understand by itself but luckily he expanded each line of code into a more readable format and I think everyone was able to follow along. Personally, I felt the code was kind of a brute force approach to spell checking but if it worked why use something more complicated? It just goes to show how fast computers have become where we're able to generate such huge sets of data in memory and throw them away. I enjoyed his presentation and wish he would have had time for more questions.

  • IPhone with Rory Blyth - This was an eye opening presentation. We're a Windows shop but have been looking forward to moving to the Apple platform and dabbling in IPhone applications. Honestly, after seeing what it took to create such a simple application in XCode we were kind of shocked. Simple things like hooking up button click events required dragging and dropping lines from the code file to the controls and then adding further code on the back end to actually implement the events. It may be that we're just spoiled with Visual Studio but the development process seemed pretty horrible and un-intuitive (very non-Apple like). After the XCode example he launched MonoTouch (which we didn't even know existed) and created the same application with a lot less hassle. Very cool.

  • Lunch - The lunch and snacks for the event were pretty good. They had a large variety of Boudin lunches (sourdough sandwiches), cookies, fruit, soft drinks, etc. We sat out in the hall and sat next to Jeff Atwood and Mark Harrison while they were eating lunch. It was interesting to hear Jeff talk about some caching issues with SQL Server queries that I've run into in the past.

  • FogBugz with Joel - I missed this presentation because I was outside getting a demo of the FogBugz software. It's a nice system and we've been attempting to work it into our workflow since visiting the conference.

  • ASP.Net MVC with Scott Hanselman - Scott was a fun presenter. He seemed to poke fun at himself and Microsoft a lot. He acknowledged a lot of the problems and missing features of their MVC framework and while I don't think he converted anyone away from Django or Ruby on Rails he may have pointed out that their MVC framework is a good step up from the standard ASP.Net forms model.

  • Stack Overflow with Jeff Atwood - I wish his presentation was longer and that he would have answered more questions. Everyone was really interested in the Stack Overflow application (obviously) and more time should have been given to covering it.

  • Qt with Daniel Rocha - I'm a long time reader of Slashdot and have heard a lot about Qt and Trolltech in the past. It seems like a major engineering feat to create a cross platform UI framework like Qt. Unfortunately, Nokia seems to have bought up the product and shoehorned it into creating mobile applications. The sample application he was showing off looked horrible compared to current IPhone or Android applications. I think half of the people in the room excused themselves and walked out because the presentation felt so much like an outdated sales pitch and just didn't feel relevant to the current market. Thumbs down.

  • Android with James Yum - I thought James did okay considering he was only given 2 days notice to come up with his presentation. His examples seemed to highlight more of the problem of developing multi-threaded applications than Android applications but I think everyone got the idea. I see now why Apple locks down the IPhone platform so much. I did feel a little shortchanged by this presentation in general. James couldn't answer a lot of questions asked by the audience and seemed kind of inexperienced. I was glad to see that Google hires mortal developers and not just development gods with 30 years of experience and triple doctorates on their resume. It gives me hope that maybe one day I could apply there.

  • jQuery with Yehuda Katz - I was kind of disappointed in this presentation because it felt like an introduction to jQuery. I was hoping to learn some advanced techniques. About the only thing I gained from it was that the 1.4 release would let me reference the $(this) object for each element without having to do a .each() loop. Oh well, still a good primer for anyone without jQuery experience and a nice presentation.

Overall, I thought the conference went very well and showcased a good number of technologies. I look forward to attending it again next year!

This review was originally posted on meta.stackoverflow.com back in November of 2009.

Categories: Conferences, Stack Overflow