diff --git a/AUTHORS b/AUTHORS index 7a1b35cc..2f859f78 100644 --- a/AUTHORS +++ b/AUTHORS @@ -51,6 +51,7 @@ or just made Pipeline more awesome. * Jared Scott * Jaromir Fojtu * Jon Dufresne + * Jonas Lundberg * Josh Braegger * Joshua Kehn * Julien Hartmann diff --git a/docs/compilers.rst b/docs/compilers.rst index aefb4afb..46f39483 100644 --- a/docs/compilers.rst +++ b/docs/compilers.rst @@ -173,6 +173,29 @@ To use it add this to your ``PIPELINE_COMPILERS`` :: Defaults to ``''``. +.. _StaticFilesCompilerMixin: + +Static files compiler mixin +=========================== + +When using compilers together with a remote storage, like S3, +you need to extend the compiler of choice to use staticfiles instead of the remote storage. + +To do so, you just have to create your own compiler class that inherits from +``pipeline.compilers.StaticFilesCompilerMixin`` and your desired compiler. + +Example +------- + +A custom less compiler extended with staticfiles usage :: + + from pipeline.compilers import StaticFilesCompilerMixin + from pipeline.compilers.less import LessCompiler + + class LocalLessCompiler(StaticFilesCompilerMixin, LessCompiler): + pass + + Write your own compiler class ============================= diff --git a/docs/storages.rst b/docs/storages.rst index 4b3f2321..fdfe00a8 100644 --- a/docs/storages.rst +++ b/docs/storages.rst @@ -95,6 +95,10 @@ unless you don't have access to the local filesystem in which case you should us class S3PipelineCachedStorage(PipelineMixin, CachedFilesMixin, S3BotoStorage): pass +.. note:: + When using custom storage in combination with compilers you may also need to use ``StaticFilesCompilerMixin`` + (see StaticFilesCompilerMixin_ for more details). + Using Pipeline with Bower ========================= diff --git a/pipeline/compilers/__init__.py b/pipeline/compilers/__init__.py index 9667a329..2b6d0f89 100644 --- a/pipeline/compilers/__init__.py +++ b/pipeline/compilers/__init__.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import os +from datetime import datetime try: from shlex import quote @@ -34,14 +35,13 @@ def _compile(input_path): compiler = compiler(verbose=self.verbose, storage=self.storage) if compiler.match_file(input_path): output_path = self.output_path(input_path, compiler.output_extension) - try: - infile = self.storage.path(input_path) - except NotImplementedError: - infile = finders.find(input_path) + infile = compiler.path(input_path) outfile = self.output_path(infile, compiler.output_extension) outdated = compiler.is_outdated(input_path, output_path) compiler.compile_file(quote(infile), quote(outfile), outdated=outdated, force=force) + if hasattr(compiler, 'post_process'): + compiler.post_process(outfile, name=output_path) return output_path else: return input_path @@ -71,6 +71,12 @@ def match_file(self, filename): def compile_file(self, infile, outfile, outdated=False, force=False): raise NotImplementedError + def path(self, name): + try: + return self.storage.path(name) + except NotImplementedError: + return finders.find(name) + def save_file(self, path, content): return self.storage.save(path, ContentFile(smart_str(content))) @@ -103,3 +109,27 @@ def execute_command(self, command, content=None, cwd=None): if pipe.returncode != 0: raise CompilerError("Command '{0}' returned non-zero exit status {1}".format(command, pipe.returncode)) return stdout + + +class StaticFilesCompilerMixin(object): + def path(self, name): + return finders.find(name) + + def read_file(self, path): + file = open(path, 'rb') + content = file.read() + file.close() + return content + + def modified_time(self, name): + return datetime.fromtimestamp(os.path.getmtime(self.path(name))) + + def is_outdated(self, infile, outfile): + try: + return self.modified_time(infile) > self.storage.modified_time(outfile) + except (OSError, NotImplementedError): + return True + + def post_process(self, outfile, name): + content = self.read_file(outfile) + self.save_file(name, content)