Hashes and creates files now.
This commit is contained in:
@@ -5,6 +5,7 @@ from django.conf import settings
|
||||
MEDIA_URL = getattr(settings, 'COMPRESS_URL', settings.MEDIA_URL)
|
||||
MEDIA_ROOT = getattr(settings, 'COMPRESS_ROOT', settings.MEDIA_ROOT)
|
||||
PREFIX = getattr(settings, 'COMPRESS_PREFIX', 'compressed')
|
||||
OUTPUT_DIR = getattr(settings, 'COMPRESS_OUTPUT_DIR', 'COMPRESSOR_CACHE')
|
||||
|
||||
COMPRESS = getattr(settings, 'COMPRESS', not settings.DEBUG)
|
||||
COMPRESS_CSS_FILTERS = getattr(settings, 'COMPRESS_CSS_FILTERS', [])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class FilterBase:
|
||||
def __init__(self, verbose):
|
||||
def __init__(self, verbose=0):
|
||||
self.verbose = verbose
|
||||
|
||||
def filter_css(self, css):
|
||||
@@ -11,4 +11,33 @@ class FilterError(Exception):
|
||||
"""
|
||||
This exception is raised when a filter fails
|
||||
"""
|
||||
pass
|
||||
pass
|
||||
|
||||
def get_class(class_string):
|
||||
"""
|
||||
Convert a string version of a function name to the callable object.
|
||||
"""
|
||||
|
||||
if not hasattr(class_string, '__bases__'):
|
||||
|
||||
try:
|
||||
class_string = class_string.encode('ascii')
|
||||
mod_name, class_name = get_mod_func(class_string)
|
||||
if class_name != '':
|
||||
cls = getattr(__import__(mod_name, {}, {}, ['']), class_name)
|
||||
except (ImportError, AttributeError):
|
||||
raise FilterError('Failed to import filter %s' % class_string)
|
||||
|
||||
return cls
|
||||
|
||||
def get_mod_func(callback):
|
||||
"""
|
||||
Converts 'django.views.news.stories.story_detail' to
|
||||
('django.views.news.stories', 'story_detail')
|
||||
"""
|
||||
|
||||
try:
|
||||
dot = callback.rindex('.')
|
||||
except ValueError:
|
||||
return callback, ''
|
||||
return callback[:dot], callback[dot+1:]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from compress.filters.jsmin.jsmin import jsmin
|
||||
from compressor.filters.jsmin.jsmin import jsmin
|
||||
from compressor.filters import FilterBase
|
||||
|
||||
class JSMinFilter(FilterBase):
|
||||
|
||||
@@ -1 +1 @@
|
||||
<link href="{{ url }}" rel="stylesheet" type="text/css"{% if media %} media="{{ media }}"{% endif %}{% if title %} title="{{ title|default:"all" }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %} />
|
||||
<link rel="stylesheet" href="{{ url }}" type="text/css"{% if media %} media="{{ media }}"{% endif %} charset="utf-8">
|
||||
@@ -1,8 +1,12 @@
|
||||
import os
|
||||
from hashlib import sha1 as hash
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
|
||||
from django import template
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from compressor.conf import settings
|
||||
from compressor import filters
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -12,7 +16,6 @@ class CompressedNode(template.Node):
|
||||
def __init__(self, content, ouput_prefix="compressed"):
|
||||
self.content = content
|
||||
self.ouput_prefix = ouput_prefix
|
||||
self.hunks = []
|
||||
self.split_content = []
|
||||
self.soup = BeautifulSoup(self.content)
|
||||
|
||||
@@ -33,30 +36,76 @@ class CompressedNode(template.Node):
|
||||
return filename
|
||||
|
||||
def get_hunks(self):
|
||||
if self.hunks:
|
||||
return self.hunks
|
||||
if getattr(self, '_hunks', ''):
|
||||
return self._hunks
|
||||
self._hunks = []
|
||||
for k, v in self.split_contents():
|
||||
if k == 'hunk':
|
||||
self.hunks.append(v)
|
||||
self._hunks.append(v)
|
||||
if k == 'file':
|
||||
fd = open(v, 'rb')
|
||||
self.hunks.append(fd.read())
|
||||
self._hunks.append(fd.read())
|
||||
fd.close()
|
||||
return self.hunks
|
||||
return self._hunks
|
||||
hunks = property(get_hunks)
|
||||
|
||||
def concat(self):
|
||||
return "\n".join(self.get_hunks())
|
||||
|
||||
def get_output(self):
|
||||
if getattr(self, '_output', ''):
|
||||
return self._output
|
||||
output = self.concat()
|
||||
filter_method = getattr(self, 'filter_method', None)
|
||||
if filter_method and self.filters:
|
||||
for f in self.filters:
|
||||
filter = getattr(filters.get_class(f)(), filter_method)
|
||||
if callable(filter):
|
||||
output = filter(output)
|
||||
self._output = output
|
||||
return self._output
|
||||
output = property(get_output)
|
||||
|
||||
def get_hash(self):
|
||||
return hash(self.output).hexdigest()[:12]
|
||||
hash = property(get_hash)
|
||||
|
||||
def get_new_filepath(self):
|
||||
filename = "".join([self.hash, self.extension])
|
||||
filepath = "%s/%s/%s" % (settings.OUTPUT_DIR.strip('/'), self.ouput_prefix, filename)
|
||||
return filepath
|
||||
new_filepath = property(get_new_filepath)
|
||||
|
||||
def save_file(self):
|
||||
filename = "%s/%s" % (settings.MEDIA_ROOT.rstrip('/'), self.new_filepath)
|
||||
if os.path.exists(filename):
|
||||
return False
|
||||
dirname = os.path.dirname(filename)
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
fd = open(filename, 'wb+')
|
||||
fd.write(self.output)
|
||||
fd.close()
|
||||
return True
|
||||
|
||||
def render(self):
|
||||
if not settings.COMPRESS:
|
||||
return self.content
|
||||
return "fail"
|
||||
url = "%s/%s" % (settings.MEDIA_URL.rstrip('/'), self.new_filepath)
|
||||
self.save_file()
|
||||
context = getattr(self, 'extra_context', {})
|
||||
context['url'] = url
|
||||
return render_to_string(self.template_name, context)
|
||||
|
||||
|
||||
class CompressedCssNode(CompressedNode):
|
||||
|
||||
def __init__(self, content, ouput_prefix="css", media="all"):
|
||||
self.media = media
|
||||
self.extra_context = { 'media': media }
|
||||
self.extension = ".css"
|
||||
self.template_name = "compressor/css.html"
|
||||
self.filters = settings.COMPRESS_CSS_FILTERS
|
||||
self.filter_method = 'filter_css'
|
||||
super(CompressedCssNode, self).__init__(content, ouput_prefix)
|
||||
|
||||
def split_contents(self):
|
||||
@@ -74,6 +123,10 @@ class CompressedCssNode(CompressedNode):
|
||||
class CompressedJsNode(CompressedNode):
|
||||
|
||||
def __init__(self, content, ouput_prefix="js"):
|
||||
self.extension = ".js"
|
||||
self.template_name = "compressor/js.html"
|
||||
self.filters = settings.COMPRESS_JS_FILTERS
|
||||
self.filter_method = 'filter_js'
|
||||
super(CompressedJsNode, self).__init__(content, ouput_prefix)
|
||||
|
||||
def split_contents(self):
|
||||
|
||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
@@ -14,14 +14,12 @@ class CompressedNodeTestCase(TestCase):
|
||||
<style type="text/css" media="screen">p { border:5px solid green;}</style>
|
||||
<link rel="stylesheet" href="/media/css/two.css" type="text/css" media="screen" charset="utf-8">
|
||||
"""
|
||||
self.css_output = '<link rel="stylesheet" href="/media/compressed/cssHASH.css" type="text/css" media="all" charset="utf-8">'
|
||||
self.cssNode = CompressedCssNode(self.css)
|
||||
|
||||
self.js = """
|
||||
<script src="/media/js/one.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script type="text/javascript" charset="utf-8">obj.value = "value";</script>
|
||||
"""
|
||||
self.js_output = '<script src="/media/compressed/jsHASH.js" charset="utf-8"></script>'
|
||||
self.jsNode = CompressedJsNode(self.js)
|
||||
|
||||
def test_css_split(self):
|
||||
@@ -34,18 +32,22 @@ class CompressedNodeTestCase(TestCase):
|
||||
|
||||
def test_css_hunks(self):
|
||||
out = ['body { background:#990; }', u'p { border:5px solid green;}', 'body { color:#fff; }']
|
||||
self.assertEqual(out, self.cssNode.get_hunks())
|
||||
self.assertEqual(out, self.cssNode.hunks)
|
||||
|
||||
def test_css_concat(self):
|
||||
def test_css_output(self):
|
||||
out = u'body { background:#990; }\np { border:5px solid green;}\nbody { color:#fff; }'
|
||||
self.assertEqual(out, self.cssNode.concat())
|
||||
self.assertEqual(out, self.cssNode.output)
|
||||
|
||||
def test_css_return_if_off(self):
|
||||
settings.COMPRESS = False
|
||||
self.assertEqual(self.css, self.cssNode.render())
|
||||
|
||||
def test_css_hash(self):
|
||||
self.assertEqual('f7c661b7a124', self.cssNode.hash)
|
||||
|
||||
def test_css_return_if_on(self):
|
||||
self.assertEqual(self.css_output, self.cssNode.render())
|
||||
output = u'<link rel="stylesheet" href="/media/COMPRESSOR_CACHE/css/f7c661b7a124.css" type="text/css" media="all" charset="utf-8">'
|
||||
self.assertEqual(output, self.cssNode.render())
|
||||
|
||||
|
||||
def test_js_split(self):
|
||||
@@ -57,15 +59,33 @@ class CompressedNodeTestCase(TestCase):
|
||||
|
||||
def test_js_hunks(self):
|
||||
out = ['obj = {};', u'obj.value = "value";']
|
||||
self.assertEqual(out, self.jsNode.get_hunks())
|
||||
self.assertEqual(out, self.jsNode.hunks)
|
||||
|
||||
def test_js_concat(self):
|
||||
out = u'obj = {};\nobj.value = "value";'
|
||||
self.assertEqual(out, self.jsNode.concat())
|
||||
|
||||
def test_js_output(self):
|
||||
out = u'obj={};obj.value="value";'
|
||||
self.assertEqual(out, self.jsNode.output)
|
||||
|
||||
def test_js_return_if_off(self):
|
||||
settings.COMPRESS = False
|
||||
self.assertEqual(self.js, self.jsNode.render())
|
||||
|
||||
def test_js_return_if_on(self):
|
||||
self.assertEqual(self.js_output, self.jsNode.render())
|
||||
output = u'<script type="text/javascript" src="/media/COMPRESSOR_CACHE/js/3f33b9146e12.js" charset="utf-8"></script>'
|
||||
self.assertEqual(output, self.jsNode.render())
|
||||
|
||||
|
||||
class CssAbsolutizingTestCase(TestCase):
|
||||
def setUp(self):
|
||||
settings.COMPRESS = True
|
||||
self.css = """
|
||||
<link rel="stylesheet" href="/media/css/backgrounded.css" type="text/css" media="screen" charset="utf-8">
|
||||
"""
|
||||
self.cssNode = CompressedCssNode(self.css)
|
||||
|
||||
def test_fail(self):
|
||||
settings.COMPRESS = False
|
||||
self.assertEqual(self.js, self.jsNode.render())
|
||||
|
||||
@@ -3,7 +3,6 @@ from os.path import dirname, abspath, join
|
||||
TEST_DIR = dirname(abspath(__file__))
|
||||
|
||||
DEBUG = True
|
||||
XDADD = ""
|
||||
|
||||
ROOT_URLCONF = 'urls'
|
||||
|
||||
@@ -11,7 +10,7 @@ MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = join(TEST_DIR, 'media')
|
||||
|
||||
DATABASE_ENGINE = 'sqlite3'
|
||||
DATABASE_NAME = 'django_inlines_tests.db'
|
||||
DATABASE_NAME = 'django_compressor_tests.db'
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'core',
|
||||
|
||||
Reference in New Issue
Block a user