Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
added new file_proxy feature (will blog about it later)
  • Loading branch information
Peter Bengtsson committed Jun 12, 2010
1 parent aa90f72 commit 05898e9
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 41 deletions.
55 changes: 53 additions & 2 deletions README.md
Expand Up @@ -93,7 +93,58 @@ In your template:
And you get this result:

<img src="http://static.example.com/foo.1247785534.png"/>



Advanced configuration with DJANGO_STATIC_FILE_PROXY
----------------------------------------------------

If you enable, in your settings, a variable called
`DJANGO_STATIC_FILE_PROXY` you can make all static URIs that
`django_static` generates go though one function. So that you, for
example, can do something with the information such as uploading to a
CDN. To get started set the config:

DJANGO_STATIC_FILE_PROXY = 'mycdn.cdn_uploader_file_proxy'
This is expected to be the equivalent of this import statement:

from mycdn import cdn_uploader_file_proxy
Where `mycdn` is a python module (e.g. `mycdn.py`) and
`cdn_uploader_file_proxy` is a regular python function. Here's the
skeleton for that function:

def cdn_uploader_file_proxy(uri, **kwargs):
return uri
Now, it's inside those keyword arguments that you get the juicy gossip
about what `django_static` has done with the file. These are the
pieces of information you will always get inside those keyword
argments:

new = False
checked = False
changed = False
notfound = False
The names hopefully speak for themselves. They become `True` depending
on what `django_static` has done. For example, if you change your
`foo.js` and re-run the template it's not `new` but it will be `checked`
and `changed`. The possibly most important keyword argument you might
get is `filepath`. This is set whenever `django_static` actually does
its magic on a static file. So, for example you might write a function
like this:

on_my_cdn = {}

def cdn_uploader_file_proxy(uri, filepath=None, new=False,
changed=False, **kwargs):
if filepath and (new or changed):
on_my_cdn[uri] = upload_to_my_cdn(filepath)

return on_my_cdn.get(uri, uri)



Using Google Closure Compiler
-----------------------------
Expand Down Expand Up @@ -132,7 +183,7 @@ Using the slimmer
package that is capable of whitespace optimizing CSS, HTML, XHTML and
Javascript. It's faster than the YUI Compressor and Google Closure but
that speed difference is due to the start-stop time of bridging the
Java files.
Java files.

How to hook this up with nginx
------------------------------
Expand Down
2 changes: 1 addition & 1 deletion django_static/__init__.py
@@ -1 +1 @@
__version__ = '1.3.7' # remember to match with setup.py
__version__ = '1.3.8' # remember to match with setup.py
13 changes: 7 additions & 6 deletions django_static/templatetags/django_static.py
Expand Up @@ -59,16 +59,16 @@ def _load_file_proxy():
return getattr(file_proxy_module, _function_name)

except AttributeError:
def file_proxy_nothing(path, *args, **kwargs):
return path
def file_proxy_nothing(uri, *args, **kwargs):
return uri
return file_proxy_nothing


file_proxy = _load_file_proxy()

# this defines what keyword arguments you can always expect to get from in the
# file proxy function you've defined.
fp_default_kwargs = dict(new=False, changed=False, checked=False)
fp_default_kwargs = dict(new=False, changed=False, checked=False, notfound=False)


class SlimContentNode(template.Node):
Expand Down Expand Up @@ -370,7 +370,7 @@ def wrap_up(filename):
new_filename, m_time = _FILE_MAP.get(map_key, (None, None))

# we might already have done a conversion but the question is
# if the javascript or css file has changed. This we only want
# if the file has changed. This we only want
# to bother with when in DEBUG mode because it adds one more
# unnecessary operation.
if new_filename:
Expand Down Expand Up @@ -419,7 +419,8 @@ def wrap_up(filename):
if not os.path.isfile(filepath):
if warn_no_file:
warnings.warn("Can't find file %s" % filepath)
return file_proxy(wrap_up(filename), **dict(fp_default_kwargs, filepath=filepath))
return file_proxy(wrap_up(filename),
**dict(fp_default_kwargs, filepath=filepath, notfound=True))

new_m_time = os.stat(filepath)[stat.ST_MTIME]

Expand All @@ -430,7 +431,7 @@ def wrap_up(filename):
m_time = None
else:
# ...and it hasn't changed!
return wrap_up(old_new_filename)
return file_proxy(wrap_up(old_new_filename))

if not m_time:
# We did not have the filename in the map OR it has changed
Expand Down
86 changes: 55 additions & 31 deletions django_static/tests.py
Expand Up @@ -597,7 +597,7 @@ def test_slimfile_multiple_debug_on_with_name_prefix(self):
filenames = ('/test_X.js', '/test_Y.js')
codes = ('function (var1, var2) { return var1+var2; }',
'var xxxxx = "yyyy" ;')

self._test_slimfile_multiple(filenames, codes, name_prefix='/infinity')

def test_slimfile_multiple_debug_off_with_name_prefix(self):
Expand Down Expand Up @@ -1399,55 +1399,79 @@ def test_load_file_proxy(self):
template = Template(template_as_string)
context = Context()
rendered = template.render(context).strip()
self.assertEqual(_last_fake_file_path[0], rendered)
self.assertEqual(_last_fake_file_uri, rendered)

# we can expect that a keyword argument called 'filepath' was used
self.assertTrue('filepath' in _last_fake_file_keyword_arguments[0])
self.assertTrue('filepath' in _last_fake_file_keyword_arguments)
# the filepath should point to the real file
self.assertTrue(os.path.isfile(_last_fake_file_keyword_arguments[0]['filepath']))
self.assertTrue(os.path.isfile(_last_fake_file_keyword_arguments['filepath']))

self.assertTrue('new' in _last_fake_file_keyword_arguments[0])
self.assertTrue(_last_fake_file_keyword_arguments[0]['new'])
self.assertTrue('new' in _last_fake_file_keyword_arguments)
self.assertTrue(_last_fake_file_keyword_arguments['new'])

self.assertTrue('changed' in _last_fake_file_keyword_arguments[0])
self.assertFalse(_last_fake_file_keyword_arguments[0]['changed'])
self.assertTrue('changed' in _last_fake_file_keyword_arguments)
self.assertFalse(_last_fake_file_keyword_arguments['changed'])

self.assertTrue('checked' in _last_fake_file_keyword_arguments[0])
self.assertTrue(_last_fake_file_keyword_arguments[0]['checked'])
self.assertTrue('checked' in _last_fake_file_keyword_arguments)
self.assertTrue(_last_fake_file_keyword_arguments['checked'])

# if you run it again, because we're not in debug mode the second time
# the file won't be checked if it has changed
assert not settings.DEBUG
rendered = template.render(context).strip()
self.assertEqual(_last_fake_file_path[0], rendered)
self.assertFalse(_last_fake_file_keyword_arguments[0]['new'])
self.assertFalse(_last_fake_file_keyword_arguments[0]['checked'])
self.assertFalse(_last_fake_file_keyword_arguments[0]['changed'])
self.assertEqual(_last_fake_file_uri, rendered)
self.assertFalse(_last_fake_file_keyword_arguments['new'])
self.assertFalse(_last_fake_file_keyword_arguments['checked'])
self.assertFalse(_last_fake_file_keyword_arguments['changed'])

# What if DJANGO_STATIC = False
# It should still go through the configured file proxy function
settings.DJANGO_STATIC = False
rendered = template.render(context).strip()
assert rendered == '/img100.gif'
self.assertFalse(_last_fake_file_keyword_arguments['checked'])
self.assertFalse(_last_fake_file_keyword_arguments['changed'])

# CONTINUE HERE. TEST WITH DJANGO_STATIC=FALSE

def test_file_proxy_with_name_prefix(self):
# Test it with a name prefix
settings.DEBUG = True
settings.DJANGO_STATIC = True
settings.DJANGO_STATIC_NAME_PREFIX = '/love-cache'

open(settings.MEDIA_ROOT + '/imgXXX.gif', 'w').write(_GIF_CONTENT)

# set up the file proxy
settings.DJANGO_STATIC_FILE_PROXY = 'django_static.tests.fake_file_proxy'
func = django_static.templatetags.django_static._load_file_proxy
proxy_function = func()
# but that's not enough, now we need to "monkey patch" this usage
django_static.templatetags.django_static.file_proxy = proxy_function

template_as_string = """{% load django_static %}
{% staticfile "/imgXXX.gif" %}
"""
template = Template(template_as_string)
context = Context()
rendered = template.render(context).strip()

self.assertEqual(_last_fake_file_uri, rendered)
self.assertTrue(_last_fake_file_keyword_arguments['new'])
self.assertTrue('filepath' in _last_fake_file_keyword_arguments)


# These have to be mutable so that we can record that they have been used as
# global variables.
_last_fake_file_path = []
_last_fake_file_arguments = []
_last_fake_file_keyword_arguments = []
_last_fake_file_uri = None
_last_fake_file_keyword_arguments = None

def fake_file_proxy(path, *a, **k):
def fake_file_proxy(uri, **k):
# reset the global mutables used to check that file_proxy() was called
[_last_fake_file_path.pop() for x
in range(len(_last_fake_file_path))]
[_last_fake_file_arguments.pop() for x
in range(len(_last_fake_file_arguments))]
[_last_fake_file_keyword_arguments.pop() for x
in range(len(_last_fake_file_keyword_arguments))]

_last_fake_file_path.append(path)
_last_fake_file_arguments.append(a)
_last_fake_file_keyword_arguments.append(k)
return path
global _last_fake_file_uri
global _last_fake_file_keyword_arguments

_last_fake_file_uri = uri
_last_fake_file_keyword_arguments = k
return uri



2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -3,7 +3,7 @@


# django-static doesn't have a version but this setup.py does
VERSION = '1.3.7' # remember to match with django_static/__init__.py
VERSION = '1.3.8' # remember to match with django_static/__init__.py

import os
long_description = open(os.path.join(os.path.dirname(__file__),
Expand Down

0 comments on commit 05898e9

Please sign in to comment.