Repository: django-compressor/django-compressor

Issue:
Manifest file incorrect when running multiple template engines
When I run the following

```
python manage.py compress --engine django
python manage.py compress --engine jinja2
```

the resulting manifest file only contains entries for the jinja2 engine and causes 

```
OfflineGenerationError at /dashboard/

You have offline compression enabled but key "e328d39c961b947e653b1a4712ce85e8" is missing from offline manifest. You may need to run "python manage.py compress".
```

when the django templates are used.

Manifest file incorrect when running multiple template engines
When I run the following

```
python manage.py compress --engine django
python manage.py compress --engine jinja2
```

the resulting manifest file only contains entries for the jinja2 engine and causes 

```
OfflineGenerationError at /dashboard/

You have offline compression enabled but key "e328d39c961b947e653b1a4712ce85e8" is missing from offline manifest. You may need to run "python manage.py compress".
```

when the django templates are used.



Discussion:
Yeah, multiple engines is not supported at the moment, it's an edge case : most of the time you're going to pick one template engine and just have the other for templates from generic apps that you don't control anyway, like when you use jinja2 but want the django admin to work.

@diox That is the case that I needed multiple template engines for.  A 3rd party app uses django templates with compressor while the rest of the project is using jinja2 templates with compressor.  I submitted a pull request for a simple management command that compresses multiple engines and updates the manifest file appropriately.  I can see where it would probably be preferable to add the logic into the main compress command but this is an immediate fix that works for me

@diox I just realized that I submitted the pull request to my own repo.  The management command at https://github.com/thenewguy/django_compressor/pull/1/files seems to work just fine for handling multiple engines.  I think it would be pretty simple to modify the compress command's handle method to wrap multiple engines.  Are there any particular pain points that prevented something like this from being implemented?

@diox I have a little bit of time and was able to find a charger for my tablet so I could put it together if it is something that would be accepted

I would prefer for the original compress command to parse the `--engine` parameter and accept multiple engines separated by commas, e.g. `--engine django,jinja2`  (or if you prefer, allow the parameter to be specified more than once, adding to a list of engines, e.g. `--engine django --engine jinja2`). I'd gladly review a pull request doing that as long as it has tests and documentation.

@diox yes with the original command.  The other was just to check the concept and get me by immediately

Yeah, multiple engines is not supported at the moment, it's an edge case : most of the time you're going to pick one template engine and just have the other for templates from generic apps that you don't control anyway, like when you use jinja2 but want the django admin to work.

@diox That is the case that I needed multiple template engines for.  A 3rd party app uses django templates with compressor while the rest of the project is using jinja2 templates with compressor.  I submitted a pull request for a simple management command that compresses multiple engines and updates the manifest file appropriately.  I can see where it would probably be preferable to add the logic into the main compress command but this is an immediate fix that works for me

@diox I just realized that I submitted the pull request to my own repo.  The management command at https://github.com/thenewguy/django_compressor/pull/1/files seems to work just fine for handling multiple engines.  I think it would be pretty simple to modify the compress command's handle method to wrap multiple engines.  Are there any particular pain points that prevented something like this from being implemented?

@diox I have a little bit of time and was able to find a charger for my tablet so I could put it together if it is something that would be accepted

I would prefer for the original compress command to parse the `--engine` parameter and accept multiple engines separated by commas, e.g. `--engine django,jinja2`  (or if you prefer, allow the parameter to be specified more than once, adding to a list of engines, e.g. `--engine django --engine jinja2`). I'd gladly review a pull request doing that as long as it has tests and documentation.

@diox yes with the original command.  The other was just to check the concept and get me by immediately


Resolving patch:
diff --git a/compressor/management/commands/compress.py b/compressor/management/commands/compress.py
--- a/compressor/management/commands/compress.py
+++ b/compressor/management/commands/compress.py
@@ -16,7 +16,7 @@
 from django.template.loader import get_template  # noqa Leave this in to preload template locations
 from django.template import engines
 
-from compressor.cache import get_offline_hexdigest, write_offline_manifest
+from compressor.cache import get_offline_hexdigest, write_offline_manifest, get_offline_manifest
 from compressor.conf import settings
 from compressor.exceptions import (OfflineGenerationError, TemplateSyntaxError,
                                    TemplateDoesNotExist)
@@ -49,9 +49,11 @@ def add_arguments(self, parser):
                                  "(which defaults to STATIC_ROOT). Be aware that using this "
                                  "can lead to infinite recursion if a link points to a parent "
                                  "directory of itself.", dest='follow_links')
-        parser.add_argument('--engine', default="django", action="store",
-                            help="Specifies the templating engine. jinja2 or django",
-                            dest="engine")
+        parser.add_argument('--engine', default="[]", action="append",
+                            help="Specifies the templating engine. jinja2 and django are "
+                                 "supported. It may be a specified more than once for "
+                                 "multiple engines. If not specified, django engine is used.",
+                            dest="engines")
 
     def get_loaders(self):
         template_source_loaders = []
@@ -291,8 +293,16 @@ def handle(self, **options):
                 raise CommandError(
                     "Offline compression is disabled. Set "
                     "COMPRESS_OFFLINE or use the --force to override.")
-        self.compress(sys.stdout, **options)
 
+        options.setdefault("log", sys.stdout)
 
+        manifest = {}
+        engines = [e.strip() for e in options.get("engines", [])] or ["django"]
+        for engine in engines:
+            opts = options.copy()
+            opts["engine"] = engine
+            self.compress(**opts)
+            manifest.update(get_offline_manifest())
+        write_offline_manifest(manifest)
 
 Command.requires_system_checks = False


Test patch:
diff --git a/compressor/tests/test_offline.py b/compressor/tests/test_offline.py
--- a/compressor/tests/test_offline.py
+++ b/compressor/tests/test_offline.py
@@ -10,6 +10,7 @@
 from mock import patch
 from unittest import SkipTest, skipIf
 
+from django.core.management import call_command
 from django.core.management.base import CommandError
 from django.template import Template, Context
 from django.test import TestCase
@@ -699,3 +700,43 @@ def _test_offline(self, engine):
         count, result = CompressCommand().compress(
             log=self.log, verbosity=self.verbosity, engine=engine)
         self.assertEqual(count, 1)
+
+
+@skipIf(not _TEST_JINJA2, "Test only run if we are testing Jinja2")
+class TestCompressCommand(OfflineTestCaseMixin, TestCase):
+    templates_dir = "test_compress_command"
+
+    def _test_offline(self, engine):
+        raise SkipTest("Not utilized for this test case")
+
+    def _build_expected_manifest(self, expected):
+        return dict([
+            (k, ('<script type="text/javascript" src="/static/CACHE/js/'
+            '%s.js"></script>' % (v, ))) for k, v in expected.items()
+        ])
+
+    def test_multiple_engines(self):
+        opts = {
+            "force": True,
+            "verbosity": 0,
+            "log": StringIO(),
+        }
+
+        call_command('compress', engines=["django"], **opts)
+        manifest_django = get_offline_manifest()
+        manifest_django_expected = self._build_expected_manifest(
+            {'8464063aa0729700fca0452e009582af': '662b9ce354e4'})
+        self.assertEqual(manifest_django, manifest_django_expected)
+
+        call_command('compress', engines=["jinja2"], **opts)
+        manifest_jinja2 = get_offline_manifest()
+        manifest_jinja2_expected = self._build_expected_manifest(
+            {'0ec631f01496b28bbecad129c5532db4': '3cd63e8c4360'})
+        self.assertEqual(manifest_jinja2, manifest_jinja2_expected)
+
+        call_command('compress', engines=["django", "jinja2"], **opts)
+        manifest_both = get_offline_manifest()
+        manifest_both_expected = self._build_expected_manifest(
+            {'8464063aa0729700fca0452e009582af': '662b9ce354e4',
+             '0ec631f01496b28bbecad129c5532db4': '3cd63e8c4360'})
+        self.assertEqual(manifest_both, manifest_both_expected)
diff --git a/compressor/tests/test_templates/test_compress_command/test_compressor_offline.html b/compressor/tests/test_templates/test_compress_command/test_compressor_offline.html
new file mode 100644
--- /dev/null
+++ b/compressor/tests/test_templates/test_compress_command/test_compressor_offline.html
@@ -0,0 +1,8 @@
+{% load compress %}{% spaceless %}
+
+{% compress js %}
+    <script type="text/javascript">
+        alert("Hello world from django template");
+    </script>
+{% endcompress %}
+{% endspaceless %}
diff --git a/compressor/tests/test_templates_jinja2/test_compress_command/test_compressor_offline.html b/compressor/tests/test_templates_jinja2/test_compress_command/test_compressor_offline.html
new file mode 100644
--- /dev/null
+++ b/compressor/tests/test_templates_jinja2/test_compress_command/test_compressor_offline.html
@@ -0,0 +1,8 @@
+{% spaceless %}
+
+{% compress js %}
+    <script type="text/javascript">
+        alert("Hello world from jinja2 template");
+    </script>
+{% endcompress %}
+{% endspaceless %}