From d0f5875f66b1a80b6011aeb626bb4ebe430ccdd0 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 22 Oct 2012 23:04:25 +0200 Subject: [PATCH 01/13] remove the LessCSSGenerator and the related config option --- pelican/__init__.py | 5 +---- pelican/generators.py | 46 ------------------------------------------- pelican/settings.py | 1 - 3 files changed, 1 insertion(+), 51 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 804fe5b4..443d330b 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -9,8 +9,7 @@ from pelican import signals from pelican.generators import (ArticlesGenerator, PagesGenerator, StaticGenerator, PdfGenerator, - LessCSSGenerator, SourceFileGenerator, - TemplatePagesGenerator) + SourceFileGenerator, TemplatePagesGenerator) from pelican.log import init from pelican.settings import read_settings from pelican.utils import (clean_output_dir, files_changed, file_changed, @@ -177,8 +176,6 @@ class Pelican(object): generators.append(TemplatePagesGenerator) if self.settings['PDF_GENERATOR']: generators.append(PdfGenerator) - if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc - generators.append(LessCSSGenerator) if self.settings['OUTPUT_SOURCES']: generators.append(SourceFileGenerator) diff --git a/pelican/generators.py b/pelican/generators.py index 01f4701c..33d0ff20 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -571,49 +571,3 @@ class SourceFileGenerator(Generator): logger.info(u' Generating source files...') for object in chain(self.context['articles'], self.context['pages']): self._create_source(object, self.output_path) - -class LessCSSGenerator(Generator): - """Compile less css files.""" - - def _compile(self, less_file, source_dir, dest_dir): - base = os.path.relpath(less_file, source_dir) - target = os.path.splitext( - os.path.join(dest_dir, base))[0] + '.css' - target_dir = os.path.dirname(target) - - if not os.path.exists(target_dir): - try: - os.makedirs(target_dir) - except OSError: - logger.error("Couldn't create the less css output folder in " + - target_dir) - - subprocess.call([self._lessc, less_file, target]) - logger.info(u' [ok] compiled %s' % base) - - def generate_output(self, writer=None): - logger.info(u' Compiling less css') - - # store out compiler here, so it won't be evaulted on each run of - # _compile - lg = self.settings['LESS_GENERATOR'] - self._lessc = lg if isinstance(lg, basestring) else 'lessc' - - # walk static paths - for static_path in self.settings['STATIC_PATHS']: - for f in self.get_files( - os.path.join(self.path, static_path), - extensions=['less']): - - self._compile(f, self.path, self.output_path) - - # walk theme static paths - theme_output_path = os.path.join(self.output_path, 'theme') - - for static_path in self.settings['THEME_STATIC_PATHS']: - theme_static_path = os.path.join(self.theme, static_path) - for f in self.get_files( - theme_static_path, - extensions=['less']): - - self._compile(f, theme_static_path, theme_output_path) diff --git a/pelican/settings.py b/pelican/settings.py index 35752fee..fa58d8b1 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -75,7 +75,6 @@ _DEFAULT_CONFIG = {'PATH': '.', 'DEFAULT_STATUS': 'published', 'ARTICLE_PERMALINK_STRUCTURE': '', 'TYPOGRIFY': False, - 'LESS_GENERATOR': False, 'SUMMARY_MAX_LENGTH': 50, 'WEBASSETS': False, 'PLUGINS': [], From 5f51cffe0810c0d450c1dcac2288b5da6a0f38fb Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 22 Oct 2012 23:09:59 +0200 Subject: [PATCH 02/13] update docs: remove mentions of lesscss compiling and replace with webassets --- README.rst | 4 ++-- docs/index.rst | 4 ++-- docs/settings.rst | 5 ----- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 018f73ba..b2648bf1 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ Pelican currently supports: * Publication of articles in multiple languages * Atom/RSS feeds * Code syntax highlighting -* Compilation of `LESS CSS`_ (optional) +* Asset management with `webassets`_ (optional) * Import from WordPress, Dotclear, or RSS feeds * Integration with external tools: Twitter, Google Analytics, etc. (optional) @@ -62,9 +62,9 @@ client handy, use the webchat_ for quick feedback. .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Markdown: http://daringfireball.net/projects/markdown/ .. _Jinja2: http://jinja.pocoo.org/ -.. _`LESS CSS`: http://lesscss.org/ .. _`Pelican documentation`: http://docs.getpelican.com/latest/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html .. _`#pelican on Freenode`: irc://irc.freenode.net/pelican .. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 .. _contribute: http://docs.getpelican.com/en/latest/contribute.html +.. _webassets: https://github.com/miracle2k/webassets diff --git a/docs/index.rst b/docs/index.rst index 3fc1cf9f..fd99b449 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,7 +23,7 @@ Pelican currently supports: * Publication of articles in multiple languages * Atom/RSS feeds * Code syntax highlighting -* Compilation of `LESS CSS`_ (optional) +* Asset management with `webassets`_ (optional) * Import from WordPress, Dotclear, or RSS feeds * Integration with external tools: Twitter, Google Analytics, etc. (optional) @@ -75,8 +75,8 @@ A French version of the documentation is available at :doc:`fr/index`. .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Markdown: http://daringfireball.net/projects/markdown/ .. _Jinja2: http://jinja.pocoo.org/ -.. _`LESS CSS`: http://lesscss.org/ .. _`Pelican documentation`: http://docs.getpelican.com/latest/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html .. _`#pelican on Freenode`: irc://irc.freenode.net/pelican .. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 +.. _webassets: https://github.com/miracle2k/webassets diff --git a/docs/settings.rst b/docs/settings.rst index 6bcc7292..ca2c7b0f 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -105,9 +105,6 @@ Setting name (default value) What doe incorporated into the generated HTML via the `Typogrify `_ library, which can be installed via: ``pip install typogrify`` -`LESS_GENERATOR` (``FALSE``) Set to True or complete path to `lessc` (if not - found in system PATH) to enable compiling less - css files. Requires installation of `less css`_. `DIRECT_TEMPLATES` (``('index', 'tags', 'categories', 'archives')``) List of templates that are used directly to render content. Typically direct templates are used to generate index pages for collections of content e.g. tags and @@ -128,8 +125,6 @@ Setting name (default value) What doe .. [#] Default is the system locale. -.. _less css: http://lesscss.org/ - URL settings ------------ From b66a37ccb1e0b69ec786c6f9a4397cc87e482ecf Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 24 Oct 2012 22:51:45 +0200 Subject: [PATCH 03/13] Unit tests for webassets remove the unit tests for the lesscss generator (TestLessCSSGenerator class) and replace it with tests for webassets: - add a sample scss file - run pelican with example files - compare the generated css file with a reference one - look in the html code for the correct link tag --- tests/test_generators.py | 85 ++++++++++---------- tests/themes/assets/static/css/style.min.css | 1 + tests/themes/assets/static/css/style.scss | 19 +++++ tests/themes/assets/templates/base.html | 7 ++ 4 files changed, 68 insertions(+), 44 deletions(-) create mode 100644 tests/themes/assets/static/css/style.min.css create mode 100644 tests/themes/assets/static/css/style.scss create mode 100644 tests/themes/assets/templates/base.html diff --git a/tests/test_generators.py b/tests/test_generators.py index accdb699..ba923980 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -1,15 +1,18 @@ # -*- coding: utf-8 -*- from mock import MagicMock +import hashlib import os -import re + +from codecs import open from tempfile import mkdtemp from shutil import rmtree -from pelican.generators import ArticlesGenerator, LessCSSGenerator, \ - PagesGenerator, TemplatePagesGenerator +from pelican import Pelican +from pelican.generators import ArticlesGenerator, PagesGenerator, \ + TemplatePagesGenerator from pelican.writers import Writer -from pelican.settings import _DEFAULT_CONFIG +from pelican.settings import _DEFAULT_CONFIG, read_settings from .support import unittest, skipIfNoExecutable CUR_DIR = os.path.dirname(__file__) @@ -240,53 +243,47 @@ class TestTemplatePagesGenerator(unittest.TestCase): self.assertEquals(output_file.read(), 'foo: bar') -class TestLessCSSGenerator(unittest.TestCase): - - LESS_CONTENT = """ - @color: #4D926F; - - #header { - color: @color; - } - h2 { - color: @color; - } +@skipIfNoExecutable(['scss', '-v']) +@skipIfNoExecutable(['cssmin', '--version']) +class TestWebAssets(unittest.TestCase): + """ + scss style.scss style.ref.css """ def setUp(self): - self.temp_content = mkdtemp() - self.temp_output = mkdtemp() + self.temp_path = mkdtemp() + self.theme_dir = os.path.join(CUR_DIR, 'themes', 'assets') + + self.settings = read_settings(override={ + 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), + 'OUTPUT_PATH': self.temp_path, + 'WEBASSETS': True, + 'THEME': self.theme_dir, + }) + pelican = Pelican(settings=self.settings) + pelican.run() + + self.css_ref = open(os.path.join(self.theme_dir, 'static', 'css', + 'style.min.css')).read() + self.version = hashlib.md5(self.css_ref).hexdigest()[0:8] def tearDown(self): - rmtree(self.temp_content) - rmtree(self.temp_output) + rmtree(self.temp_path) - @skipIfNoExecutable('lessc') - def test_less_compiler(self): + def test_compilation(self): + "Compare the compiled css with the reference" - settings = _DEFAULT_CONFIG.copy() - settings['STATIC_PATHS'] = ['static'] - settings['LESS_GENERATOR'] = True + gen_file = os.path.join(self.temp_path, 'theme', 'gen', + 'style.{0}.min.css'.format(self.version)) - generator = LessCSSGenerator(None, settings, self.temp_content, - _DEFAULT_CONFIG['THEME'], self.temp_output, None) + self.assertTrue(os.path.isfile(gen_file)) + css_new = open(gen_file).read() + self.assertEqual(css_new, self.css_ref) - # create a dummy less file - less_dir = os.path.join(self.temp_content, 'static', 'css') - less_filename = os.path.join(less_dir, 'test.less') + def test_template(self): + "Look in the output index.html file for the link tag" - less_output = os.path.join(self.temp_output, 'static', 'css', - 'test.css') - - os.makedirs(less_dir) - with open(less_filename, 'w') as less_file: - less_file.write(self.LESS_CONTENT) - - generator.generate_output() - - # we have the file ? - self.assertTrue(os.path.exists(less_output)) - - # was it compiled ? - self.assertIsNotNone(re.search(r'^\s+color:\s*#4D926F;$', - open(less_output).read(), re.MULTILINE | re.IGNORECASE)) + link_tag = ''.format(self.version) + html = open(os.path.join(self.temp_path, 'index.html')).read() + self.assertRegexpMatches(html, link_tag) diff --git a/tests/themes/assets/static/css/style.min.css b/tests/themes/assets/static/css/style.min.css new file mode 100644 index 00000000..daf9c3cb --- /dev/null +++ b/tests/themes/assets/static/css/style.min.css @@ -0,0 +1 @@ +body{font:14px/1.5 "Droid Sans",sans-serif;background-color:#e4e4e4;color:#242424}a{color:red}a:hover{color:orange} \ No newline at end of file diff --git a/tests/themes/assets/static/css/style.scss b/tests/themes/assets/static/css/style.scss new file mode 100644 index 00000000..10cd05be --- /dev/null +++ b/tests/themes/assets/static/css/style.scss @@ -0,0 +1,19 @@ +/* -*- scss-compile-at-save: nil -*- */ + +$baseFontFamily : "Droid Sans", sans-serif; +$textColor : #242424; +$bodyBackground : #e4e4e4; + +body { + font: 14px/1.5 $baseFontFamily; + background-color: $bodyBackground; + color: $textColor; +} + +a { + color: red; + + &:hover { + color: orange; + } +} diff --git a/tests/themes/assets/templates/base.html b/tests/themes/assets/templates/base.html new file mode 100644 index 00000000..13b3fb10 --- /dev/null +++ b/tests/themes/assets/templates/base.html @@ -0,0 +1,7 @@ +{% extends "!simple/base.html" %} + +{% block head %} + {% assets filters="scss,cssmin", output="gen/style.%(version)s.min.css", "css/style.scss" %} + + {% endassets %} +{% endblock %} From 1b20319074d1a187ad60e3bcc4c567429d09198d Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 24 Oct 2012 23:34:38 +0200 Subject: [PATCH 04/13] add a warning for the users of the LESS_GENERATOR setting --- pelican/settings.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pelican/settings.py b/pelican/settings.py index fa58d8b1..2c318997 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -132,7 +132,7 @@ def configure_settings(settings): """ if not 'PATH' in settings or not os.path.isdir(settings['PATH']): raise Exception('You need to specify a path containing the content' - ' (see pelican --help for more information)') + ' (see pelican --help for more information)') # find the theme in pelican.theme if the given one does not exists if not os.path.isdir(settings['THEME']): @@ -142,7 +142,7 @@ def configure_settings(settings): settings['THEME'] = theme_path else: raise Exception("Impossible to find the theme %s" - % settings['THEME']) + % settings['THEME']) # if locales is not a list, make it one locales = settings['LOCALE'] @@ -185,12 +185,17 @@ def configure_settings(settings): "http://docs.notmyidea.org/alexis/pelican/settings.html#timezone " "for more information") + if 'LESS_GENERATOR' in settings: + logger.warn("The LESS_GENERATOR setting has been removed in favor " + "of WEBASSETS") + if 'WEBASSETS' in settings and settings['WEBASSETS'] is not False: try: from webassets.ext.jinja2 import AssetsExtension settings['JINJA_EXTENSIONS'].append(AssetsExtension) except ImportError: - logger.warn("You must install the webassets module to use WEBASSETS.") + logger.warn("You must install the webassets module to use " + "WEBASSETS") settings['WEBASSETS'] = False if 'OUTPUT_SOURCES_EXTENSION' in settings: From 4cfb0cd24ef1ddc2de6951bb5e0f74ae1f2d7c71 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 5 Nov 2012 23:40:38 +0100 Subject: [PATCH 05/13] Move Webassets in a plugin --- pelican/__init__.py | 5 --- pelican/generators.py | 31 ------------------- pelican/plugins/assets.py | 64 +++++++++++++++++++++++++++++++++++++++ pelican/settings.py | 12 +------- tests/test_generators.py | 2 +- 5 files changed, 66 insertions(+), 48 deletions(-) create mode 100644 pelican/plugins/assets.py diff --git a/pelican/__init__.py b/pelican/__init__.py index 443d330b..c0f33687 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -158,11 +158,6 @@ class Pelican(object): writer = self.get_writer() - # pass the assets environment to the generators - if self.settings['WEBASSETS']: - generators[1].env.assets_environment = generators[0].assets_env - generators[2].env.assets_environment = generators[0].assets_env - for p in generators: if hasattr(p, 'generate_output'): p.generate_output(writer) diff --git a/pelican/generators.py b/pelican/generators.py index 33d0ff20..00b0d6d3 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -465,37 +465,6 @@ class StaticGenerator(Generator): copy(path, source, os.path.join(output_path, destination), final_path, overwrite=True) - def generate_context(self): - - if self.settings['WEBASSETS']: - from webassets import Environment as AssetsEnvironment - - # Define the assets environment that will be passed to the - # generators. The StaticGenerator must then be run first to have - # the assets in the output_path before generating the templates. - - # Let ASSET_URL honor Pelican's RELATIVE_URLS setting. - # Hint for templates: - # Current version of webassets seem to remove any relative - # paths at the beginning of the URL. So, if RELATIVE_URLS - # is on, ASSET_URL will start with 'theme/', regardless if we - # set assets_url here to './theme/' or to 'theme/'. - # XXX However, this breaks the ASSET_URL if user navigates to - # a sub-URL, e.g. if he clicks on a category. To workaround this - # issue, I use - # - # instead of - # - if self.settings.get('RELATIVE_URLS'): - assets_url = './theme/' - else: - assets_url = self.settings['SITEURL'] + '/theme/' - assets_src = os.path.join(self.output_path, 'theme') - self.assets_env = AssetsEnvironment(assets_src, assets_url) - - if logging.getLevelName(logger.getEffectiveLevel()) == "DEBUG": - self.assets_env.debug = True - def generate_output(self, writer): self._copy_paths(self.settings['STATIC_PATHS'], self.path, diff --git a/pelican/plugins/assets.py b/pelican/plugins/assets.py new file mode 100644 index 00000000..58f9f863 --- /dev/null +++ b/pelican/plugins/assets.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +""" +Asset management plugin for Pelican +=================================== + +This plugin allows you to use the `webassets`_ module to manage assets such as +CSS and JS files. + +Hint for templates: Current version of webassets seems to remove any relative +paths at the beginning of the URL. So, if ``RELATIVE_URLS`` is on, +``ASSET_URL`` will start with ``theme/``, regardless if we set ``assets_url`` +here to ``./theme/`` or to ``theme/``. + +However, this breaks the ``ASSET_URL`` if user navigates to a sub-URL, e.g. if +he clicks on a category. A workaround for this issue is to use:: + + + +instead of:: + + + +.. _webassets: https://webassets.readthedocs.org/ + +""" + +import os +import logging + +from pelican import signals +from webassets import Environment +from webassets.ext.jinja2 import AssetsExtension + + +def add_jinja2_ext(pelican): + """Add Webassets to Jinja2 extensions in Pelican settings.""" + + pelican.settings['JINJA_EXTENSIONS'].append(AssetsExtension) + + +def create_assets_env(generator): + """Define the assets environment and pass it to the generator.""" + + logger = logging.getLogger(__name__) + + # Let ASSET_URL honor Pelican's RELATIVE_URLS setting. + if generator.settings.get('RELATIVE_URLS'): + assets_url = './theme/' + else: + assets_url = generator.settings['SITEURL'] + '/theme/' + assets_src = os.path.join(generator.output_path, 'theme') + + generator.env.assets_environment = Environment(assets_src, assets_url) + + if logging.getLevelName(logger.getEffectiveLevel()) == "DEBUG": + generator.env.assets_environment.debug = True + + +def register(): + """Plugin registration.""" + + signals.initialized.connect(add_jinja2_ext) + signals.article_generator_init.connect(create_assets_env) + signals.pages_generator_init.connect(create_assets_env) diff --git a/pelican/settings.py b/pelican/settings.py index 2c318997..b4fe04f4 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -76,7 +76,6 @@ _DEFAULT_CONFIG = {'PATH': '.', 'ARTICLE_PERMALINK_STRUCTURE': '', 'TYPOGRIFY': False, 'SUMMARY_MAX_LENGTH': 50, - 'WEBASSETS': False, 'PLUGINS': [], 'MARKDOWN_EXTENSIONS': ['toc', ], 'TEMPLATE_PAGES': {} @@ -187,16 +186,7 @@ def configure_settings(settings): if 'LESS_GENERATOR' in settings: logger.warn("The LESS_GENERATOR setting has been removed in favor " - "of WEBASSETS") - - if 'WEBASSETS' in settings and settings['WEBASSETS'] is not False: - try: - from webassets.ext.jinja2 import AssetsExtension - settings['JINJA_EXTENSIONS'].append(AssetsExtension) - except ImportError: - logger.warn("You must install the webassets module to use " - "WEBASSETS") - settings['WEBASSETS'] = False + "of the Webassets plugin") if 'OUTPUT_SOURCES_EXTENSION' in settings: if not isinstance(settings['OUTPUT_SOURCES_EXTENSION'], str): diff --git a/tests/test_generators.py b/tests/test_generators.py index ba923980..6b714082 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -257,7 +257,7 @@ class TestWebAssets(unittest.TestCase): self.settings = read_settings(override={ 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), 'OUTPUT_PATH': self.temp_path, - 'WEBASSETS': True, + 'PLUGINS': ['pelican.plugins.assets', ], 'THEME': self.theme_dir, }) pelican = Pelican(settings=self.settings) From 4c15ec9f86e499d2dc9b39087ea1aaf1795f25e6 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 6 Nov 2012 00:04:45 +0100 Subject: [PATCH 06/13] Move webassets doc to the plugins page --- docs/plugins.rst | 89 ++++++++++++++++++++++++++++++++++++++++++----- docs/settings.rst | 72 +------------------------------------- 2 files changed, 81 insertions(+), 80 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index c275c57c..83ef264c 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -59,7 +59,7 @@ Signal Arguments Description ============================= ============================ =========================================================================== initialized pelican object finalized pelican object invoked after all the generators are executed and just before pelican exits - usefull for custom post processing actions, such as: + usefull for custom post processing actions, such as: - minifying js/css assets. - notify/ping search engines with an updated sitemap. article_generate_context article_generator, metadata @@ -75,23 +75,23 @@ pages_generator_init pages_generator invoked in the P The list is currently small, don't hesitate to add signals and make a pull request if you need them! -.. note:: - - The signal ``content_object_init`` can send different type of object as +.. note:: + + The signal ``content_object_init`` can send different type of object as argument. If you want to register only one type of object then you will need to specify the sender when you are connecting to the signal. - + :: - + from pelican import signals from pelican import contents - + def test(sender, instance): print "%s : %s content initialized !!" % (sender, instance) - + def register(): signals.content_object_init.connect(test, sender=contents.Article) - + List of plugins @@ -99,6 +99,7 @@ List of plugins The following plugins are currently included with Pelican under ``pelican.plugins``: +* `Asset management`_ * `GitHub activity`_ * `Global license`_ * `Gravatar`_ @@ -114,6 +115,76 @@ Ideas for plugins that haven't been written yet: Plugin descriptions =================== +Asset management +---------------- + +This plugin allows you to use the `webassets`_ module to manage assets such as +CSS and JS files. The module must first be installed:: + + pip install webassets + +The `webassets` module allows you to perform a number of useful asset management +functions, including: + +* CSS minifier (`cssmin`, `yuicompressor`, ...) +* CSS compiler (`less`, `sass`, ...) +* JS minifier (`uglifyjs`, `yuicompressor`, `closure`, ...) + +Others filters include gzip compression, integration of images in CSS via data +URIs, and more. `webassets` can also append a version identifier to your asset +URL to convince browsers to download new versions of your assets when you use +far-future expires headers. Please refer to the `webassets documentation`_ for +more information. + +When using with Pelican, `webassets` is configured to process assets in the +``OUTPUT_PATH/theme`` directory. You can use `webassets` in your templates by +including one or more template tags. For example... + +.. code-block:: jinja + + {% assets filters="cssmin", output="css/style.min.css", "css/inuit.css", "css/pygment-monokai.css", "css/main.css" %} + + {% endassets %} + +... will produce a minified css file with a version identifier: + +.. code-block:: html + + + +These filters can be combined. Here is an example that uses the SASS compiler +and minifies the output: + +.. code-block:: jinja + + {% assets filters="sass,cssmin", output="css/style.min.css", "css/style.scss" %} + + {% endassets %} + +Another example for Javascript: + +.. code-block:: jinja + + {% assets filters="uglifyjs,gzip", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %} + + {% endassets %} + +The above will produce a minified and gzipped JS file: + +.. code-block:: html + + + +Pelican's debug mode is propagated to `webassets` to disable asset packaging +and instead work with the uncompressed assets. However, this also means that +the LESS and SASS files are not compiled. This should be fixed in a future +version of `webassets` (cf. the related `bug report +`_). + +.. _webassets: https://github.com/miracle2k/webassets +.. _webassets documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html + + GitHub activity --------------- diff --git a/docs/settings.rst b/docs/settings.rst index ca2c7b0f..c4ac3584 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -52,7 +52,7 @@ Setting name (default value) What doe the datetime.datetime constructor. `DELETE_OUTPUT_DIRECTORY` (``False``) Delete the content of the output directory before generating new files. -`FILES_TO_COPY` (``()``) A list of files to copy from the source (inside the content +`FILES_TO_COPY` (``()``) A list of files to copy from the source (inside the content directory) to the destination (inside the output directory). For example: ``(('extra/robots.txt', 'robots.txt'),)``. `JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use. @@ -426,7 +426,6 @@ Setting name (default value) What does it do? value is `static`, but if your theme has other static paths, you can put them here. `CSS_FILE` (``'main.css'``) Specify the CSS file you want to load. -`WEBASSETS` (``False``) Asset management with `webassets` (see below) ================================================ ===================================================== @@ -487,75 +486,6 @@ adding the following to your configuration:: CSS_FILE = "wide.css" -Asset management ----------------- - -The `WEBASSETS` setting allows you to use the `webassets`_ module to manage -assets such as CSS and JS files. The module must first be installed:: - - pip install webassets - -The `webassets` module allows you to perform a number of useful asset management -functions, including: - -* CSS minifier (`cssmin`, `yuicompressor`, ...) -* CSS compiler (`less`, `sass`, ...) -* JS minifier (`uglifyjs`, `yuicompressor`, `closure`, ...) - -Others filters include gzip compression, integration of images in CSS via data -URIs, and more. `webassets` can also append a version identifier to your asset -URL to convince browsers to download new versions of your assets when you use -far-future expires headers. Please refer to the `webassets documentation`_ for -more information. - -When using with Pelican, `webassets` is configured to process assets in the -``OUTPUT_PATH/theme`` directory. You can use `webassets` in your templates by -including one or more template tags. For example... - -.. code-block:: jinja - - {% assets filters="cssmin", output="css/style.min.css", "css/inuit.css", "css/pygment-monokai.css", "css/main.css" %} - - {% endassets %} - -... will produce a minified css file with a version identifier: - -.. code-block:: html - - - -These filters can be combined. Here is an example that uses the SASS compiler -and minifies the output: - -.. code-block:: jinja - - {% assets filters="sass,cssmin", output="css/style.min.css", "css/style.scss" %} - - {% endassets %} - -Another example for Javascript: - -.. code-block:: jinja - - {% assets filters="uglifyjs,gzip", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %} - - {% endassets %} - -The above will produce a minified and gzipped JS file: - -.. code-block:: html - - - -Pelican's debug mode is propagated to `webassets` to disable asset packaging -and instead work with the uncompressed assets. However, this also means that -the LESS and SASS files are not compiled. This should be fixed in a future -version of `webassets` (cf. the related `bug report -`_). - -.. _webassets: https://github.com/miracle2k/webassets -.. _webassets documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html - Example settings ================ From 7088135f5e31202cac4894cf6d7f3514c4a86d61 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 6 Nov 2012 01:00:28 +0100 Subject: [PATCH 07/13] Add tests for webassets: test absolute url and Jinja configuration --- tests/test_generators.py | 85 +++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/tests/test_generators.py b/tests/test_generators.py index 6b714082..8a839ba1 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -8,6 +8,11 @@ from codecs import open from tempfile import mkdtemp from shutil import rmtree +try: + import webassets +except ImportError: + webassets = None + from pelican import Pelican from pelican.generators import ArticlesGenerator, PagesGenerator, \ TemplatePagesGenerator @@ -243,6 +248,7 @@ class TestTemplatePagesGenerator(unittest.TestCase): self.assertEquals(output_file.read(), 'foo: bar') +@unittest.skipUnless(webassets, "webassets isn't installed") @skipIfNoExecutable(['scss', '-v']) @skipIfNoExecutable(['cssmin', '--version']) class TestWebAssets(unittest.TestCase): @@ -250,28 +256,52 @@ class TestWebAssets(unittest.TestCase): scss style.scss style.ref.css """ - def setUp(self): - self.temp_path = mkdtemp() - self.theme_dir = os.path.join(CUR_DIR, 'themes', 'assets') + @classmethod + def setUpClass(cls): + """Run pelican with two settings (absolute and relative urls).""" - self.settings = read_settings(override={ + cls.theme_dir = os.path.join(CUR_DIR, 'themes', 'assets') + + cls.temp_path = mkdtemp() + cls.settings = read_settings(override={ 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), - 'OUTPUT_PATH': self.temp_path, + 'OUTPUT_PATH': cls.temp_path, 'PLUGINS': ['pelican.plugins.assets', ], - 'THEME': self.theme_dir, + 'THEME': cls.theme_dir, }) - pelican = Pelican(settings=self.settings) + pelican = Pelican(settings=cls.settings) pelican.run() - self.css_ref = open(os.path.join(self.theme_dir, 'static', 'css', - 'style.min.css')).read() - self.version = hashlib.md5(self.css_ref).hexdigest()[0:8] + # run Pelican a second time with absolute urls + cls.temp_path2 = mkdtemp() + cls.settings2 = read_settings(override={ + 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), + 'OUTPUT_PATH': cls.temp_path2, + 'PLUGINS': ['pelican.plugins.assets', ], + 'THEME': cls.theme_dir, + 'RELATIVE_URLS': False, + 'SITEURL': 'http://localhost' + }) + pelican2 = Pelican(settings=cls.settings2) + pelican2.run() - def tearDown(self): - rmtree(self.temp_path) + cls.css_ref = open(os.path.join(cls.theme_dir, 'static', 'css', + 'style.min.css')).read() + cls.version = hashlib.md5(cls.css_ref).hexdigest()[0:8] + + @classmethod + def tearDownClass(cls): + rmtree(cls.temp_path) + rmtree(cls.temp_path2) + + def test_jinja2_ext(self): + """Test that the Jinja2 extension was correctly added.""" + + from webassets.ext.jinja2 import AssetsExtension + self.assertIn(AssetsExtension, self.settings['JINJA_EXTENSIONS']) def test_compilation(self): - "Compare the compiled css with the reference" + """Compare the compiled css with the reference.""" gen_file = os.path.join(self.temp_path, 'theme', 'gen', 'style.{0}.min.css'.format(self.version)) @@ -280,10 +310,29 @@ class TestWebAssets(unittest.TestCase): css_new = open(gen_file).read() self.assertEqual(css_new, self.css_ref) - def test_template(self): - "Look in the output index.html file for the link tag" + def check_link_tag(self, css_file, html_file): + """Check the presence of `css_file` in `html_file`.""" - link_tag = ''.format(self.version) - html = open(os.path.join(self.temp_path, 'index.html')).read() + link_tag = ''.\ + format(css_file=css_file) + html = open(html_file).read() self.assertRegexpMatches(html, link_tag) + + def test_template(self): + """Look in the output index.html file for the link tag.""" + + css_file = 'theme/gen/style.{0}.min.css'.format(self.version) + html_files = ['index.html', 'archives.html', + 'this-is-an-article-with-category.html'] + for f in html_files: + self.check_link_tag(css_file, os.path.join(self.temp_path, f)) + + def test_absolute_url(self): + """Look in the output index.html file for the link tag with abs url.""" + + css_file = 'http://localhost/theme/gen/style.{0}.min.css'.\ + format(self.version) + html_files = ['index.html', 'archives.html', + 'this-is-an-article-with-category.html'] + for f in html_files: + self.check_link_tag(css_file, os.path.join(self.temp_path2, f)) From 7ff0d0e6867abbc0f35afb4cc7404ec4da17bc84 Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 17 Nov 2012 01:00:44 +0100 Subject: [PATCH 08/13] Move webassets tests in their own file. --- tests/support.py | 20 ++++++-- tests/test_generators.py | 101 +-------------------------------------- tests/test_webassets.py | 98 +++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 103 deletions(-) create mode 100644 tests/test_webassets.py diff --git a/tests/support.py b/tests/support.py index 13bbfb15..52a410ff 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,11 +3,12 @@ __all__ = [ 'unittest', ] -import os -import subprocess -import re -import sys import cStringIO +import importlib +import os +import re +import subprocess +import sys from functools import wraps from contextlib import contextmanager @@ -138,3 +139,14 @@ def skipIfNoExecutable(executable): return unittest.skip('{0} executable not found'.format(executable)) return lambda func: func + + +def module_exists(module_name): + """Test if a module is importable.""" + + try: + importlib.import_module(module_name) + except ImportError: + return False + else: + return True diff --git a/tests/test_generators.py b/tests/test_generators.py index 8a839ba1..95a55cdb 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -1,24 +1,17 @@ # -*- coding: utf-8 -*- from mock import MagicMock -import hashlib import os from codecs import open from tempfile import mkdtemp from shutil import rmtree -try: - import webassets -except ImportError: - webassets = None - -from pelican import Pelican from pelican.generators import ArticlesGenerator, PagesGenerator, \ TemplatePagesGenerator from pelican.writers import Writer -from pelican.settings import _DEFAULT_CONFIG, read_settings -from .support import unittest, skipIfNoExecutable +from pelican.settings import _DEFAULT_CONFIG +from .support import unittest CUR_DIR = os.path.dirname(__file__) @@ -246,93 +239,3 @@ class TestTemplatePagesGenerator(unittest.TestCase): # output content is correct with open(output_filename, 'r') as output_file: self.assertEquals(output_file.read(), 'foo: bar') - - -@unittest.skipUnless(webassets, "webassets isn't installed") -@skipIfNoExecutable(['scss', '-v']) -@skipIfNoExecutable(['cssmin', '--version']) -class TestWebAssets(unittest.TestCase): - """ - scss style.scss style.ref.css - """ - - @classmethod - def setUpClass(cls): - """Run pelican with two settings (absolute and relative urls).""" - - cls.theme_dir = os.path.join(CUR_DIR, 'themes', 'assets') - - cls.temp_path = mkdtemp() - cls.settings = read_settings(override={ - 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), - 'OUTPUT_PATH': cls.temp_path, - 'PLUGINS': ['pelican.plugins.assets', ], - 'THEME': cls.theme_dir, - }) - pelican = Pelican(settings=cls.settings) - pelican.run() - - # run Pelican a second time with absolute urls - cls.temp_path2 = mkdtemp() - cls.settings2 = read_settings(override={ - 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), - 'OUTPUT_PATH': cls.temp_path2, - 'PLUGINS': ['pelican.plugins.assets', ], - 'THEME': cls.theme_dir, - 'RELATIVE_URLS': False, - 'SITEURL': 'http://localhost' - }) - pelican2 = Pelican(settings=cls.settings2) - pelican2.run() - - cls.css_ref = open(os.path.join(cls.theme_dir, 'static', 'css', - 'style.min.css')).read() - cls.version = hashlib.md5(cls.css_ref).hexdigest()[0:8] - - @classmethod - def tearDownClass(cls): - rmtree(cls.temp_path) - rmtree(cls.temp_path2) - - def test_jinja2_ext(self): - """Test that the Jinja2 extension was correctly added.""" - - from webassets.ext.jinja2 import AssetsExtension - self.assertIn(AssetsExtension, self.settings['JINJA_EXTENSIONS']) - - def test_compilation(self): - """Compare the compiled css with the reference.""" - - gen_file = os.path.join(self.temp_path, 'theme', 'gen', - 'style.{0}.min.css'.format(self.version)) - - self.assertTrue(os.path.isfile(gen_file)) - css_new = open(gen_file).read() - self.assertEqual(css_new, self.css_ref) - - def check_link_tag(self, css_file, html_file): - """Check the presence of `css_file` in `html_file`.""" - - link_tag = ''.\ - format(css_file=css_file) - html = open(html_file).read() - self.assertRegexpMatches(html, link_tag) - - def test_template(self): - """Look in the output index.html file for the link tag.""" - - css_file = 'theme/gen/style.{0}.min.css'.format(self.version) - html_files = ['index.html', 'archives.html', - 'this-is-an-article-with-category.html'] - for f in html_files: - self.check_link_tag(css_file, os.path.join(self.temp_path, f)) - - def test_absolute_url(self): - """Look in the output index.html file for the link tag with abs url.""" - - css_file = 'http://localhost/theme/gen/style.{0}.min.css'.\ - format(self.version) - html_files = ['index.html', 'archives.html', - 'this-is-an-article-with-category.html'] - for f in html_files: - self.check_link_tag(css_file, os.path.join(self.temp_path2, f)) diff --git a/tests/test_webassets.py b/tests/test_webassets.py new file mode 100644 index 00000000..0f7a59ce --- /dev/null +++ b/tests/test_webassets.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- + +import hashlib +import os +from codecs import open +from tempfile import mkdtemp +from shutil import rmtree + +from pelican import Pelican +from pelican.settings import read_settings +from .support import unittest, skipIfNoExecutable, module_exists + +CUR_DIR = os.path.dirname(__file__) + + +@unittest.skipUnless(module_exists('webassets'), "webassets isn't installed") +@skipIfNoExecutable(['scss', '-v']) +@skipIfNoExecutable(['cssmin', '--version']) +class TestWebAssets(unittest.TestCase): + + def setUp(self): + """Run pelican with two settings (absolute and relative urls).""" + + self.theme_dir = os.path.join(CUR_DIR, 'themes', 'assets') + + self.temp_path = mkdtemp() + self.settings = read_settings(override={ + 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), + 'OUTPUT_PATH': self.temp_path, + 'PLUGINS': ['pelican.plugins.assets', ], + 'THEME': self.theme_dir, + }) + pelican = Pelican(settings=self.settings) + pelican.run() + + # run Pelican a second time with absolute urls + self.temp_path2 = mkdtemp() + self.settings2 = read_settings(override={ + 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), + 'OUTPUT_PATH': self.temp_path2, + 'PLUGINS': ['pelican.plugins.assets', ], + 'THEME': self.theme_dir, + 'RELATIVE_URLS': False, + 'SITEURL': 'http://localhost' + }) + pelican2 = Pelican(settings=self.settings2) + pelican2.run() + + self.css_ref = open(os.path.join(self.theme_dir, 'static', 'css', + 'style.min.css')).read() + self.version = hashlib.md5(self.css_ref).hexdigest()[0:8] + + def tearDown(self): + rmtree(self.temp_path) + rmtree(self.temp_path2) + + def test_jinja2_ext(self): + """Test that the Jinja2 extension was correctly added.""" + + from webassets.ext.jinja2 import AssetsExtension + self.assertIn(AssetsExtension, self.settings['JINJA_EXTENSIONS']) + + def test_compilation(self): + """Compare the compiled css with the reference.""" + + gen_file = os.path.join(self.temp_path, 'theme', 'gen', + 'style.{0}.min.css'.format(self.version)) + + self.assertTrue(os.path.isfile(gen_file)) + css_new = open(gen_file).read() + self.assertEqual(css_new, self.css_ref) + + def check_link_tag(self, css_file, html_file): + """Check the presence of `css_file` in `html_file`.""" + + link_tag = ''.\ + format(css_file=css_file) + html = open(html_file).read() + self.assertRegexpMatches(html, link_tag) + + def test_template(self): + """Look in the output index.html file for the link tag.""" + + css_file = 'theme/gen/style.{0}.min.css'.format(self.version) + html_files = ['index.html', 'archives.html', + 'this-is-an-article-with-category.html'] + for f in html_files: + self.check_link_tag(css_file, os.path.join(self.temp_path, f)) + + def test_absolute_url(self): + """Look in the output index.html file for the link tag with abs url.""" + + css_file = 'http://localhost/theme/gen/style.{0}.min.css'.\ + format(self.version) + html_files = ['index.html', 'archives.html', + 'this-is-an-article-with-category.html'] + for f in html_files: + self.check_link_tag(css_file, os.path.join(self.temp_path2, f)) From 09c893f3a354e6ae6928dc3123fa3d96ff0555ad Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 20 Nov 2012 00:07:44 +0100 Subject: [PATCH 09/13] Add a new signal `generator_init`, invoked at the end of `Generator.__init__`. --- docs/plugins.rst | 1 + pelican/generators.py | 2 ++ pelican/signals.py | 1 + 3 files changed, 4 insertions(+) diff --git a/docs/plugins.rst b/docs/plugins.rst index 83ef264c..94b22bca 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -62,6 +62,7 @@ finalized pelican object invoked after al usefull for custom post processing actions, such as: - minifying js/css assets. - notify/ping search engines with an updated sitemap. +generator_init generator invoked in the Generator.__init__ article_generate_context article_generator, metadata article_generator_init article_generator invoked in the ArticlesGenerator.__init__ article_generator_finalized article_generator invoked at the end of ArticlesGenerator.generate_context diff --git a/pelican/generators.py b/pelican/generators.py index 00b0d6d3..43cec021 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -61,6 +61,8 @@ class Generator(object): custom_filters = self.settings.get('JINJA_FILTERS', {}) self.env.filters.update(custom_filters) + signals.generator_init.send(self) + def get_template(self, name): """Return the template by name. Use self.theme to get the templates to use, and return a list of diff --git a/pelican/signals.py b/pelican/signals.py index 73e718b5..dd5724dc 100644 --- a/pelican/signals.py +++ b/pelican/signals.py @@ -2,6 +2,7 @@ from blinker import signal initialized = signal('pelican_initialized') finalized = signal('pelican_finalized') +generator_init = signal('generator_init') article_generate_context = signal('article_generate_context') article_generator_init = signal('article_generator_init') article_generator_finalized = signal('article_generate_finalized') From 00d1ea6157f61a82365e72518740378eee87d4c2 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 20 Nov 2012 00:09:52 +0100 Subject: [PATCH 10/13] Use the new generator_init signal for the assets plugin. This allows to have the assets_environment in all generators. --- pelican/plugins/assets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pelican/plugins/assets.py b/pelican/plugins/assets.py index 58f9f863..b32eee82 100644 --- a/pelican/plugins/assets.py +++ b/pelican/plugins/assets.py @@ -60,5 +60,4 @@ def register(): """Plugin registration.""" signals.initialized.connect(add_jinja2_ext) - signals.article_generator_init.connect(create_assets_env) - signals.pages_generator_init.connect(create_assets_env) + signals.generator_init.connect(create_assets_env) From 67af48eed4c61168d9e8c16452d6e8c9fa6d4924 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 20 Nov 2012 00:22:18 +0100 Subject: [PATCH 11/13] travis configuration for webassets tests: install webassets, cssmin and sass. --- .travis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ea134da4..bb9a22e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,13 +2,18 @@ language: python python: - "2.6" - "2.7" +before_install: + - sudo apt-get update -qq + - sudo apt-get install -qq ruby-sass install: - pip install nose unittest2 mock --use-mirrors - pip install . --use-mirrors - pip install Markdown + - pip install webassets + - pip install cssmin script: nosetests -s tests notifications: irc: - channels: + channels: - "irc.freenode.org#pelican" on_success: change From f413a8da79754f9b78274ba8a16b41c906cf4e25 Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 20 Nov 2012 23:39:06 +0100 Subject: [PATCH 12/13] Change webassets configuration for relative urls. Set the ASSET_URL to be relative to the 'theme/' url, which requires to use {{ SITEURL }}/{{ ASSET_URL }} in the template and make it works with both relative and absolute urls. --- docs/plugins.rst | 11 +++++++---- pelican/plugins/assets.py | 23 ++++------------------- tests/test_webassets.py | 6 +++++- tests/themes/assets/templates/base.html | 2 +- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 94b22bca..d2e44337 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -139,12 +139,15 @@ more information. When using with Pelican, `webassets` is configured to process assets in the ``OUTPUT_PATH/theme`` directory. You can use `webassets` in your templates by -including one or more template tags. For example... +including one or more template tags. The jinja variable ``{{ ASSET_URL }}`` to +use in the templates is configured to be relative to the ``theme/`` url. +Hence, it must be used with the ``{{ SITEURL }}`` variable which allows to +have relative urls. For example... .. code-block:: jinja {% assets filters="cssmin", output="css/style.min.css", "css/inuit.css", "css/pygment-monokai.css", "css/main.css" %} - + {% endassets %} ... will produce a minified css file with a version identifier: @@ -159,7 +162,7 @@ and minifies the output: .. code-block:: jinja {% assets filters="sass,cssmin", output="css/style.min.css", "css/style.scss" %} - + {% endassets %} Another example for Javascript: @@ -167,7 +170,7 @@ Another example for Javascript: .. code-block:: jinja {% assets filters="uglifyjs,gzip", output="js/packed.js", "js/jquery.js", "js/base.js", "js/widgets.js" %} - + {% endassets %} The above will produce a minified and gzipped JS file: diff --git a/pelican/plugins/assets.py b/pelican/plugins/assets.py index b32eee82..b5d1cf76 100644 --- a/pelican/plugins/assets.py +++ b/pelican/plugins/assets.py @@ -6,20 +6,11 @@ Asset management plugin for Pelican This plugin allows you to use the `webassets`_ module to manage assets such as CSS and JS files. -Hint for templates: Current version of webassets seems to remove any relative -paths at the beginning of the URL. So, if ``RELATIVE_URLS`` is on, -``ASSET_URL`` will start with ``theme/``, regardless if we set ``assets_url`` -here to ``./theme/`` or to ``theme/``. - -However, this breaks the ``ASSET_URL`` if user navigates to a sub-URL, e.g. if -he clicks on a category. A workaround for this issue is to use:: +The ASSET_URL is set to a relative url to honor Pelican's RELATIVE_URLS +setting. This requires the use of SITEURL in the templates:: -instead of:: - - - .. _webassets: https://webassets.readthedocs.org/ """ @@ -41,17 +32,11 @@ def add_jinja2_ext(pelican): def create_assets_env(generator): """Define the assets environment and pass it to the generator.""" - logger = logging.getLogger(__name__) - - # Let ASSET_URL honor Pelican's RELATIVE_URLS setting. - if generator.settings.get('RELATIVE_URLS'): - assets_url = './theme/' - else: - assets_url = generator.settings['SITEURL'] + '/theme/' + assets_url = 'theme/' assets_src = os.path.join(generator.output_path, 'theme') - generator.env.assets_environment = Environment(assets_src, assets_url) + logger = logging.getLogger(__name__) if logging.getLevelName(logger.getEffectiveLevel()) == "DEBUG": generator.env.assets_environment.debug = True diff --git a/tests/test_webassets.py b/tests/test_webassets.py index 0f7a59ce..e47e7f81 100644 --- a/tests/test_webassets.py +++ b/tests/test_webassets.py @@ -81,12 +81,16 @@ class TestWebAssets(unittest.TestCase): def test_template(self): """Look in the output index.html file for the link tag.""" - css_file = 'theme/gen/style.{0}.min.css'.format(self.version) + css_file = './theme/gen/style.{0}.min.css'.format(self.version) html_files = ['index.html', 'archives.html', 'this-is-an-article-with-category.html'] for f in html_files: self.check_link_tag(css_file, os.path.join(self.temp_path, f)) + self.check_link_tag( + '.././theme/gen/style.{0}.min.css'.format(self.version), + os.path.join(self.temp_path, 'category/misc.html')) + def test_absolute_url(self): """Look in the output index.html file for the link tag with abs url.""" diff --git a/tests/themes/assets/templates/base.html b/tests/themes/assets/templates/base.html index 13b3fb10..05a32d06 100644 --- a/tests/themes/assets/templates/base.html +++ b/tests/themes/assets/templates/base.html @@ -2,6 +2,6 @@ {% block head %} {% assets filters="scss,cssmin", output="gen/style.%(version)s.min.css", "css/style.scss" %} - + {% endassets %} {% endblock %} From 816e8d88e0b6622cf7eb7d4a9357e22dbaff12f2 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 22 Nov 2012 13:22:49 +0100 Subject: [PATCH 13/13] Change webasset tests organization to avoid running pelican twice for each test. As setUp is run for each test, the previous implementation was running pelican twice (once for relative urls and once for absolute) for each test, which was not really optimal :-). Solution: make a base class which is inherated by a class for relative urls and another for absolute urls. --- tests/test_webassets.py | 85 ++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/tests/test_webassets.py b/tests/test_webassets.py index e47e7f81..52a7fa17 100644 --- a/tests/test_webassets.py +++ b/tests/test_webassets.py @@ -11,48 +11,47 @@ from pelican.settings import read_settings from .support import unittest, skipIfNoExecutable, module_exists CUR_DIR = os.path.dirname(__file__) +THEME_DIR = os.path.join(CUR_DIR, 'themes', 'assets') +CSS_REF = open(os.path.join(THEME_DIR, 'static', 'css', + 'style.min.css')).read() +CSS_HASH = hashlib.md5(CSS_REF).hexdigest()[0:8] @unittest.skipUnless(module_exists('webassets'), "webassets isn't installed") @skipIfNoExecutable(['scss', '-v']) @skipIfNoExecutable(['cssmin', '--version']) class TestWebAssets(unittest.TestCase): + """Base class for testing webassets.""" - def setUp(self): - """Run pelican with two settings (absolute and relative urls).""" - - self.theme_dir = os.path.join(CUR_DIR, 'themes', 'assets') - + def setUp(self, override=None): self.temp_path = mkdtemp() - self.settings = read_settings(override={ + settings = { 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), 'OUTPUT_PATH': self.temp_path, 'PLUGINS': ['pelican.plugins.assets', ], - 'THEME': self.theme_dir, - }) + 'THEME': THEME_DIR, + } + if override: + settings.update(override) + + self.settings = read_settings(override=settings) pelican = Pelican(settings=self.settings) pelican.run() - # run Pelican a second time with absolute urls - self.temp_path2 = mkdtemp() - self.settings2 = read_settings(override={ - 'PATH': os.path.join(CUR_DIR, 'content', 'TestCategory'), - 'OUTPUT_PATH': self.temp_path2, - 'PLUGINS': ['pelican.plugins.assets', ], - 'THEME': self.theme_dir, - 'RELATIVE_URLS': False, - 'SITEURL': 'http://localhost' - }) - pelican2 = Pelican(settings=self.settings2) - pelican2.run() - - self.css_ref = open(os.path.join(self.theme_dir, 'static', 'css', - 'style.min.css')).read() - self.version = hashlib.md5(self.css_ref).hexdigest()[0:8] - def tearDown(self): rmtree(self.temp_path) - rmtree(self.temp_path2) + + def check_link_tag(self, css_file, html_file): + """Check the presence of `css_file` in `html_file`.""" + + link_tag = ''.\ + format(css_file=css_file) + html = open(html_file).read() + self.assertRegexpMatches(html, link_tag) + + +class TestWebAssetsRelativeURLS(TestWebAssets): + """Test pelican with relative urls.""" def test_jinja2_ext(self): """Test that the Jinja2 extension was correctly added.""" @@ -64,39 +63,39 @@ class TestWebAssets(unittest.TestCase): """Compare the compiled css with the reference.""" gen_file = os.path.join(self.temp_path, 'theme', 'gen', - 'style.{0}.min.css'.format(self.version)) - + 'style.{0}.min.css'.format(CSS_HASH)) self.assertTrue(os.path.isfile(gen_file)) + css_new = open(gen_file).read() - self.assertEqual(css_new, self.css_ref) - - def check_link_tag(self, css_file, html_file): - """Check the presence of `css_file` in `html_file`.""" - - link_tag = ''.\ - format(css_file=css_file) - html = open(html_file).read() - self.assertRegexpMatches(html, link_tag) + self.assertEqual(css_new, CSS_REF) def test_template(self): - """Look in the output index.html file for the link tag.""" + """Look in the output files for the link tag.""" - css_file = './theme/gen/style.{0}.min.css'.format(self.version) + css_file = './theme/gen/style.{0}.min.css'.format(CSS_HASH) html_files = ['index.html', 'archives.html', 'this-is-an-article-with-category.html'] for f in html_files: self.check_link_tag(css_file, os.path.join(self.temp_path, f)) self.check_link_tag( - '.././theme/gen/style.{0}.min.css'.format(self.version), + '.././theme/gen/style.{0}.min.css'.format(CSS_HASH), os.path.join(self.temp_path, 'category/misc.html')) + +class TestWebAssetsAbsoluteURLS(TestWebAssets): + """Test pelican with absolute urls.""" + + def setUp(self): + TestWebAssets.setUp(self, override={'RELATIVE_URLS': False, + 'SITEURL': 'http://localhost'}) + def test_absolute_url(self): - """Look in the output index.html file for the link tag with abs url.""" + """Look in the output files for the link tag with absolute url.""" css_file = 'http://localhost/theme/gen/style.{0}.min.css'.\ - format(self.version) + format(CSS_HASH) html_files = ['index.html', 'archives.html', 'this-is-an-article-with-category.html'] for f in html_files: - self.check_link_tag(css_file, os.path.join(self.temp_path2, f)) + self.check_link_tag(css_file, os.path.join(self.temp_path, f))