diff --git a/compressor/conf/settings.py b/compressor/conf/settings.py
index a106cb4..f165801 100644
--- a/compressor/conf/settings.py
+++ b/compressor/conf/settings.py
@@ -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', [])
diff --git a/compressor/filters/__init__.py b/compressor/filters/__init__.py
index 9b98531..1b30057 100644
--- a/compressor/filters/__init__.py
+++ b/compressor/filters/__init__.py
@@ -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
\ No newline at end of file
+ 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:]
diff --git a/compressor/filters/jsmin/__init__.py b/compressor/filters/jsmin/__init__.py
index 93ca6bf..a4acea7 100644
--- a/compressor/filters/jsmin/__init__.py
+++ b/compressor/filters/jsmin/__init__.py
@@ -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):
diff --git a/compressor/templates/compressor/css.html b/compressor/templates/compressor/css.html
index 3751a4a..c82b161 100644
--- a/compressor/templates/compressor/css.html
+++ b/compressor/templates/compressor/css.html
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/compressor/templatetags/compress.py b/compressor/templatetags/compress.py
index de4043b..f4aef4b 100644
--- a/compressor/templatetags/compress.py
+++ b/compressor/templatetags/compress.py
@@ -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):
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/core/tests.py b/tests/core/tests.py
index 5ff30a5..41a357d 100644
--- a/tests/core/tests.py
+++ b/tests/core/tests.py
@@ -14,14 +14,12 @@ class CompressedNodeTestCase(TestCase):
"""
- self.css_output = ''
self.cssNode = CompressedCssNode(self.css)
self.js = """
"""
- self.js_output = ''
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''
+ 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''
+ self.assertEqual(output, self.jsNode.render())
+
+
+class CssAbsolutizingTestCase(TestCase):
+ def setUp(self):
+ settings.COMPRESS = True
+ self.css = """
+
+ """
+ self.cssNode = CompressedCssNode(self.css)
+
+ def test_fail(self):
+ settings.COMPRESS = False
+ self.assertEqual(self.js, self.jsNode.render())
diff --git a/tests/settings.py b/tests/settings.py
index 57deef7..10a9c4b 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -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',