├── LICENSE
├── README.md
├── flask_cache_bust
└── __init__.py
└── setup.py
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Christopher Mitchell, CloudBolt Software
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Flask-CacheBust is a Flask extension that adds a hash to the URL of each static
2 | file. This lets you safely declare your static resources as indefinitely
3 | cacheable because they automatically get new URLs when their contents change.
4 |
5 | # Usage
6 |
7 | Install Flask-CacheBust by placing the "flask_cache_bust" folder somewhere
8 | importable from Python. Import the extension and use it to augment your app:
9 |
10 | ```python
11 | from flask.ext import cache_bust
12 |
13 | # ...
14 |
15 | cache_bust.init_cache_busting(app)
16 | ```
17 |
18 | The `url_for` function will now cache-bust your static files. For example, this
19 | template:
20 |
21 | ```html
22 |
23 | ```
24 |
25 | will render like this:
26 |
27 | ```html
28 |
29 | ```
30 |
31 | The "1fc6e32" part will change whenever "main.min.js" changes. Now you can
32 | configure long cache expiration dates on your static files with a header like
33 | `Cache-Control: max-age=31536000`!
34 |
35 | # Motivation
36 |
37 | This was originally written for www.cloudboltsoftware.com while exercising an
38 | obsession for minimizing page load time and delivering a snappy browsing
39 | experience. It improves over Last-Modified or ETag cache schemes by enabling an
40 | expiration date to be set, which removes round-trip checks to see if a
41 | browser's cached version of a resource is still valid.
42 |
--------------------------------------------------------------------------------
/flask_cache_bust/__init__.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 | import os
3 |
4 |
5 | def init_cache_busting(app):
6 | """
7 | Configure `app` to so that `url_for` adds a unique prefix to URLs generated
8 | for the `'static'` endpoint. Also make the app able to serve cache-busted
9 | static files.
10 |
11 | This allows setting long cache expiration values on static resources
12 | because whenever the resource changes, so does its URL.
13 | """
14 |
15 | static_folder = app.static_folder # the rooted path to the static file folder
16 |
17 | bust_table = {} # map from an unbusted filename to a busted one
18 | unbust_table = {} # map from a busted filename to an unbusted one
19 |
20 | app.logger.debug('Computing cache-busting values...')
21 | # compute (un)bust tables.
22 | for dirpath, dirnames, filenames in os.walk(static_folder):
23 | for filename in filenames:
24 | # compute version component
25 | rooted_filename = os.path.join(dirpath, filename)
26 | with open(rooted_filename, 'r') as f:
27 | version = hashlib.md5(f.read()).hexdigest()[:7]
28 |
29 | # add version
30 | unbusted = os.path.relpath(rooted_filename, static_folder)
31 | busted = os.path.join(version, unbusted)
32 |
33 | # save computation to tables
34 | bust_table[unbusted] = busted
35 | unbust_table[busted] = unbusted
36 | app.logger.debug('Finished computing cache-busting values')
37 |
38 | def bust_filename(filename):
39 | return bust_table.get(filename, filename)
40 |
41 | def unbust_filename(filename):
42 | return unbust_table.get(filename, filename)
43 |
44 | @app.url_defaults
45 | def reverse_to_cache_busted_url(endpoint, values):
46 | """
47 | Make `url_for` produce busted filenames when using the 'static' endpoint.
48 | """
49 | if endpoint == 'static':
50 | values['filename'] = bust_filename(values['filename'])
51 |
52 | def debusting_static_view(filename):
53 | """
54 | Serve a request for a static file having a busted name.
55 | """
56 | return original_static_view(filename=unbust_filename(filename))
57 |
58 | # Replace the default static file view with our debusting view.
59 | original_static_view = app.view_functions['static']
60 | app.view_functions['static'] = debusting_static_view
61 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | setup(
3 | name='Flask-CacheBust',
4 | version='1.0.0',
5 | description='Flask extension that cache-busts static files',
6 | packages=['flask_cache_bust'],
7 | license='MIT',
8 | url='https://github.com/ChrisTM/Flask-CacheBust',
9 | install_requires=[
10 | 'Flask',
11 | ],
12 | )
13 |
--------------------------------------------------------------------------------