Made Compressor.concat a cached property instead of a method and use it in the hash and combined properties. Refactored Compressor.outline method to proxy to mode specific methods.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
build
|
||||
compressor/tests/media/CACHE
|
||||
compressor/tests/media/custom
|
||||
compressor/tests/media/js/3f33b9146e12.js
|
||||
dist
|
||||
MANIFEST
|
||||
*.pyc
|
||||
|
@@ -9,7 +9,7 @@ from django.template.loader import render_to_string
|
||||
|
||||
from compressor.cache import get_hexdigest, get_mtime
|
||||
from compressor.conf import settings
|
||||
from compressor.exceptions import UncompressableFileError
|
||||
from compressor.exceptions import CompressorError, UncompressableFileError
|
||||
from compressor.filters import CompilerFilter
|
||||
from compressor.storage import default_storage
|
||||
from compressor.utils import get_class, cached_property
|
||||
@@ -27,6 +27,7 @@ class Compressor(object):
|
||||
self.output_prefix = output_prefix
|
||||
self.charset = settings.DEFAULT_CHARSET
|
||||
self.precompilers = settings.COMPRESS_PRECOMPILERS
|
||||
self.storage = default_storage
|
||||
self.split_content = []
|
||||
self.extra_context = {}
|
||||
|
||||
@@ -73,10 +74,6 @@ class Compressor(object):
|
||||
return "django_compressor.%s.%s" % (socket.gethostname(),
|
||||
get_hexdigest(cachestr)[:12])
|
||||
|
||||
@cached_property
|
||||
def storage(self):
|
||||
return default_storage
|
||||
|
||||
@cached_property
|
||||
def hunks(self):
|
||||
for kind, value, elem in self.split_contents():
|
||||
@@ -102,8 +99,9 @@ class Compressor(object):
|
||||
charset = attribs.get("charset", self.charset)
|
||||
yield unicode(content, charset)
|
||||
|
||||
@cached_property
|
||||
def concat(self):
|
||||
return "\n".join((hunk.encode(self.charset) for hunk in self.hunks))
|
||||
return '\n'.join((hunk.encode(self.charset) for hunk in self.hunks))
|
||||
|
||||
def matches_patterns(self, path, patterns=[]):
|
||||
"""
|
||||
@@ -159,37 +157,65 @@ class Compressor(object):
|
||||
|
||||
@cached_property
|
||||
def combined(self):
|
||||
return self.filter(self.concat(), method="output")
|
||||
return self.filter(self.concat, method="output")
|
||||
|
||||
@cached_property
|
||||
def hash(self):
|
||||
return get_hexdigest(self.concat())[:12]
|
||||
def hash(self, content):
|
||||
return get_hexdigest(content)[:12]
|
||||
|
||||
@cached_property
|
||||
def new_filepath(self):
|
||||
def filepath(self, content):
|
||||
return os.path.join(settings.COMPRESS_OUTPUT_DIR.strip(os.sep),
|
||||
self.output_prefix, "%s.%s" % (self.hash, self.type))
|
||||
self.output_prefix, "%s.%s" % (self.hash(content), self.type))
|
||||
|
||||
def save_file(self):
|
||||
if self.storage.exists(self.new_filepath):
|
||||
return False
|
||||
self.storage.save(self.new_filepath, ContentFile(self.combined))
|
||||
return True
|
||||
|
||||
def output(self, forced=False):
|
||||
if not settings.COMPRESS_ENABLED and not forced:
|
||||
return self.content
|
||||
context = {
|
||||
"saved": self.save_file(),
|
||||
"url": self.storage.url(self.new_filepath),
|
||||
}
|
||||
context.update(self.extra_context)
|
||||
return render_to_string(self.template_name, context)
|
||||
|
||||
def output_inline(self):
|
||||
if settings.COMPRESS_ENABLED:
|
||||
def output(self, mode='file', forced=False):
|
||||
"""
|
||||
The general output method, override in subclass if you need to do
|
||||
any custom modification. Calls other mode specific methods or simply
|
||||
returns the content directly.
|
||||
"""
|
||||
# First check whether we should do the full compression,
|
||||
# including precompilation (or if it's forced)
|
||||
if settings.COMPRESS_ENABLED or forced:
|
||||
content = self.combined
|
||||
elif self.precompilers:
|
||||
# or concatting it, if pre-compilation is enabled
|
||||
content = self.concat
|
||||
else:
|
||||
content = self.concat()
|
||||
context = dict(content=content, **self.extra_context)
|
||||
return render_to_string(self.template_name_inline, context)
|
||||
# or just doing nothing, when neither
|
||||
# compression nor compilation is enabled
|
||||
return self.content
|
||||
# Then check for the appropriate output method and call it
|
||||
output_func = getattr(self, "output_%s" % mode, None)
|
||||
if callable(output_func):
|
||||
return output_func(mode, content)
|
||||
# Total failure, raise a general exception
|
||||
raise CompressorError(
|
||||
"Couldn't find output method for mode '%s'" % mode)
|
||||
|
||||
def output_file(self, mode, content):
|
||||
"""
|
||||
The output method that saves the content to a file and renders
|
||||
the appropriate template with the file's URL.
|
||||
"""
|
||||
new_filepath = self.filepath(content)
|
||||
if not self.storage.exists(new_filepath):
|
||||
self.storage.save(new_filepath, ContentFile(content))
|
||||
url = self.storage.url(new_filepath)
|
||||
return self.render_output(mode, {"url": url})
|
||||
|
||||
def output_inline(self, mode, content):
|
||||
"""
|
||||
The output method that directly returns the content for inline
|
||||
display.
|
||||
"""
|
||||
return self.render_output(mode, {"content": content})
|
||||
|
||||
def render_output(self, mode, context=None):
|
||||
"""
|
||||
Renders the compressor output with the appropriate template for
|
||||
the given mode and template context.
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
context.update(self.extra_context)
|
||||
return render_to_string(
|
||||
"compressor/%s_%s.html" % (self.type, mode), context)
|
||||
|
@@ -42,14 +42,14 @@ class CssCompressor(Compressor):
|
||||
self.media_nodes.append((media, node))
|
||||
return self.split_content
|
||||
|
||||
def output(self, forced=False):
|
||||
def output(self, *args, **kwargs):
|
||||
self.split_contents()
|
||||
if not hasattr(self, 'media_nodes'):
|
||||
return super(CssCompressor, self).output(forced=forced)
|
||||
if not settings.COMPRESS_ENABLED and not forced:
|
||||
return self.content
|
||||
ret = []
|
||||
for media, subnode in self.media_nodes:
|
||||
subnode.extra_context.update({'media': media})
|
||||
ret.append(subnode.output(forced=forced))
|
||||
return "".join(ret)
|
||||
return super(CssCompressor, self).output(*args, **kwargs)
|
||||
if settings.COMPRESS_ENABLED or kwargs.get('forced', False):
|
||||
ret = []
|
||||
for media, subnode in self.media_nodes:
|
||||
subnode.extra_context.update({'media': media})
|
||||
ret.append(subnode.output(*args, **kwargs))
|
||||
return "".join(ret)
|
||||
return self.content
|
||||
|
@@ -1,3 +1,9 @@
|
||||
class CompressorError(Exception):
|
||||
"""
|
||||
A general error of the compressor
|
||||
"""
|
||||
pass
|
||||
|
||||
class UncompressableFileError(Exception):
|
||||
"""
|
||||
This exception is raised when a file cannot be compressed
|
||||
|
@@ -1 +1 @@
|
||||
<link rel="stylesheet" href="{{ url }}" type="text/css"{% if media %} media="{{ media }}"{% endif %}>
|
||||
{# left fot backwards compatibility #}{% include "compressor/css_file.html" %}
|
1
compressor/templates/compressor/css_file.html
Normal file
1
compressor/templates/compressor/css_file.html
Normal file
@@ -0,0 +1 @@
|
||||
<link rel="stylesheet" href="{{ url }}" type="text/css"{% if media %} media="{{ media }}"{% endif %}>
|
@@ -1 +1 @@
|
||||
<script type="text/javascript" src="{{ url }}" charset="utf-8"></script>
|
||||
{# left fot backwards compatibility #}{% include "compressor/js_file.html" %}
|
1
compressor/templates/compressor/js_file.html
Normal file
1
compressor/templates/compressor/js_file.html
Normal file
@@ -0,0 +1 @@
|
||||
<script type="text/javascript" src="{{ url }}" charset="utf-8"></script>
|
@@ -51,8 +51,7 @@ class CompressorNode(template.Node):
|
||||
def render(self, context, forced=False):
|
||||
if (settings.COMPRESS_ENABLED and
|
||||
settings.COMPRESS_OFFLINE) and not forced:
|
||||
key = get_offline_cachekey(self.nodelist)
|
||||
content = cache.get(key)
|
||||
content = cache.get(get_offline_cachekey(self.nodelist))
|
||||
if content:
|
||||
return content
|
||||
content = self.nodelist.render(context)
|
||||
@@ -64,9 +63,7 @@ class CompressorNode(template.Node):
|
||||
output = self.cache_get(cachekey)
|
||||
if output is None or forced:
|
||||
try:
|
||||
if self.mode == OUTPUT_INLINE:
|
||||
return compressor.output_inline()
|
||||
output = compressor.output(forced=forced)
|
||||
output = compressor.output(self.mode, forced=forced)
|
||||
self.cache_set(cachekey, output)
|
||||
except:
|
||||
if settings.DEBUG:
|
||||
|
@@ -25,6 +25,7 @@ class CompressorTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
settings.COMPRESS_ENABLED = True
|
||||
settings.PRECOMPILERS = {}
|
||||
self.css = """
|
||||
<link rel="stylesheet" href="/media/css/one.css" type="text/css" charset="utf-8">
|
||||
<style type="text/css">p { border:5px solid green;}</style>
|
||||
@@ -71,7 +72,7 @@ class CompressorTestCase(TestCase):
|
||||
self.assert_(is_cachekey.match(self.css_node.cachekey), "cachekey is returning something that doesn't look like r'django_compressor\.%s\.\w{12}'" % host_name)
|
||||
|
||||
def test_css_hash(self):
|
||||
self.assertEqual('f7c661b7a124', self.css_node.hash)
|
||||
self.assertEqual('f7c661b7a124', self.css_node.hash(self.css_node.concat))
|
||||
|
||||
def test_css_return_if_on(self):
|
||||
output = u'<link rel="stylesheet" href="/media/CACHE/css/f7c661b7a124.css" type="text/css">'
|
||||
@@ -91,7 +92,7 @@ class CompressorTestCase(TestCase):
|
||||
|
||||
def test_js_concat(self):
|
||||
out = u'obj = {};\nobj.value = "value";'
|
||||
self.assertEqual(out, self.js_node.concat())
|
||||
self.assertEqual(out, self.js_node.concat)
|
||||
|
||||
def test_js_output(self):
|
||||
out = u'obj={};obj.value="value";'
|
||||
@@ -384,10 +385,10 @@ class OfflineGenerationTestCase(TestCase):
|
||||
def test_offline(self):
|
||||
count, result = CompressCommand().compress()
|
||||
self.assertEqual(2, count)
|
||||
self.assertEqual(result, [
|
||||
self.assertEqual([
|
||||
u'<link rel="stylesheet" href="/media/CACHE/css/a55e1cf95000.css" type="text/css">\n',
|
||||
u'<script type="text/javascript" src="/media/CACHE/js/bf53fa5b13e2.js" charset="utf-8"></script>',
|
||||
])
|
||||
], result)
|
||||
|
||||
def test_offline_with_context(self):
|
||||
self._old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT
|
||||
@@ -396,8 +397,8 @@ class OfflineGenerationTestCase(TestCase):
|
||||
}
|
||||
count, result = CompressCommand().compress()
|
||||
self.assertEqual(2, count)
|
||||
self.assertEqual(result, [
|
||||
self.assertEqual([
|
||||
u'<link rel="stylesheet" href="/media/CACHE/css/8a2405e029de.css" type="text/css">\n',
|
||||
u'<script type="text/javascript" src="/media/CACHE/js/bf53fa5b13e2.js" charset="utf-8"></script>',
|
||||
])
|
||||
], result)
|
||||
settings.COMPRESS_OFFLINE_CONTEXT = self._old_offline_context
|
||||
|
Reference in New Issue
Block a user