Back
Apr 3, 2011

Sprite cache invalidation

When we use css-sprites it's important to make browser cache them for longest period possible. On other hand, we need to refresh them when they are updated. This is especially visible when all icons are stored in single sprite. When it's outdated - entire site becomes ugly.

To solve this task I've implemented this small script that adds file's hash to url:

background-image: url(images/icons.png);
background-image: url(images/icons.png?a3844c660);
#!/usr/bin/python

import os.path
import re
import hashlib


def file_hash(filename, block_size=2**20):
    md5 = hashlib.md5()
    with open(filename, 'rb') as f:
        for data in iter(lambda: f.read(block_size), ''):
            md5.update(data)
    return md5.hexdigest()


def find_replace(match, basedir):
    match = match.replace("'", '')
    if '?' in match or '#' in match or match.startswith('http://'):
        print 'skipping: ', match
        return None

    filename = os.path.join(basedir, match)
    return '%s?%s' % (match, file_hash(filename)[:10])


def insert_hash(filename, debug=False):
    basedir = os.path.dirname(filename)
    css = open(filename).read()
    matches = re.findall(r"url\((.*?)\)", css)
    replacements = dict([
        (match, find_replace(match, basedir))
        for match in matches
    ])

    if debug:
        for key, value in replacements.iteritems():
            print key, '    =>    ', value
    else:
        for match, repl in replacements.iteritems():
            if repl:
                css = css.replace(match, repl)

        out = open(filename, 'w')
        out.write(css)
        out.close()


if __name__ == '__main__':
    from optparse import OptionParser
    usage = 'usage: %prog filename.css'

    parser = OptionParser(usage=usage)
    parser.add_option("", "--debug", action="store_true", dest="debug", default=False,
                      help="When set, just output list of substitutions.")

    (options, args) = parser.parse_args()

    if len(args) < 1:
        print 'You must provide file name.'

    insert_hash(os.path.realpath(args[0]), options.debug)

Update

This is pretty old article. Now it's better to use something integrated to your build process - grunt, webassets, etc.

Subscribe for the news and updates

More thoughts
Jul 21, 2022Technology
Codemirror: unit-testing codemirror react components

One of our recent projects includes the functionality of an inline code editor. This code editor needed to be highly extensible and have custom features. To address this, we chose Codemirror v6 due to its peculiar architecture - it is highly customizable, and all the additional features are provided into codemirror engine as Extension objects.

Apr 19, 2022Technology
Improve efficiency of your SELECT queries

SQL is a fairly complicated language with a steep learning curve. For a large number of people who make use of SQL, learning to apply it efficiently takes lots of trials and errors. Here are some tips on how you can make your SELECT queries better. The majority of tips should be applicable to any relational database management system, but the terminology and exact namings will be taken from PostgreSQL.

Feb 12, 2020Technology
5 Best Payment Gateways For 2020

We reviewed the best payment gateways in 2020. Here’s our comparison of their features, advantages, and disadvantages.

Mar 4, 2011Technology
Css sprite generation

I've created this small sprite to create css sprites. It glues images from directory directory into single file and generates corresponding css.

Oct 11, 2010Technology
Char search in Emacs as in Vim

In VIM there is a command for char search: f. After first use it can be repeated with ;. I like to navigate in line with it. You see that you need to go to bracket in a middle of a line - you press f( and one-two ; and you are there. There's no such command in Emacs, so I had to write my own. I've managed even to implement repetition with ;.

Mar 6, 2010Technology
Ajax form validation

There was a task to submit form with ajax, with server side validation of course. Obvious solution is to do validation and return json with erros. I didn't like idea of writing separate view for validation and then inserting errors in form html on client side. Especially since I already had a generic template for django form with errors display. In this article I'll describe how I solved the task.