diff --git a/hyde/ext/plugins/combine.py b/hyde/ext/plugins/combine.py new file mode 100644 index 0000000..c4b2a82 --- /dev/null +++ b/hyde/ext/plugins/combine.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +""" +Contains classes to combine files into one +""" + +from fnmatch import fnmatch + +from hyde.model import Expando +from hyde.plugin import Plugin + +class CombinePlugin(Plugin): + """ + To use this combine, the following configuration should be added + to meta data:: + combine: + files: + - ns1.*.js + - ns2.*.js + where: top + remove: yes + + `files` is a list of resources (or just a resource) that should be + combined. Globbing is performed. `where` indicate where the + combination should be done. This could be `top` or `bottom` of the + file. `remove` tell if we should remove resources that have been + combined into the resource. + """ + + def __init__(self, site): + super(CombinePlugin, self).__init__(site) + + def _combined(self, resource): + """ + Return the list of resources to combine to build this one. + """ + try: + config = resource.meta.combine + except AttributeError: + return [] # Not a combined resource + try: + files = config.files + except AttributeError: + raise AttributeError("No resources to combine for [%s]" % resource) + if type(files) is str: + files = [ files ] + + # Grab resources to combine + resources = [] + for r in resource.node.resources: + for f in files: + if fnmatch(r.name, f): + resources.append(r) + break + if not resources: + self.logger.debug("No resources to combine for [%s]" % resource) + return [] + + return sorted(resources, key=lambda r: r.name) + + def begin_site(self): + """ + Initialize the plugin and search for the combined resources + """ + for node in self.site.content.walk(): + for resource in node.resources: + resources = self._combined(resource) + if not resources: + continue + + # Build depends + if not hasattr(resource, 'depends'): + resource.depends = [] + resource.depends.extend( + [r.relative_path for r in resources + if r.relative_path not in resource.depends]) + + # Remove combined resources if needed + if hasattr(resource.meta.combine, "remove") and \ + resource.meta.combine.remove: + for r in resources: + self.logger.debug( + "Resource [%s] removed because combined" % r) + r.is_processable = False + + def begin_text_resource(self, resource, text): + """ + When generating a resource, add combined file if needed. + """ + resources = self._combined(resource) + if not resources: + return + + where = "bottom" + try: + where = resource.meta.combine.where + except AttributeError: + pass + + if where not in [ "top", "bottom" ]: + raise ValueError("%r should be either `top` or `bottom`" % where) + + self.logger.debug( + "Combining %d resources for [%s]" % (len(resources), + resource)) + if where == "top": + return "".join([r.source.read_all() for r in resources] + [text]) + else: + return "".join([text] + [r.source.read_all() for r in resources]) diff --git a/hyde/generator.py b/hyde/generator.py index f7bb8de..df3d06b 100644 --- a/hyde/generator.py +++ b/hyde/generator.py @@ -161,7 +161,7 @@ class Generator(object): if not target.exists or target.older_than(resource.source_file): logger.debug("Found changes in %s" % resource) return True - if resource.source_file.is_binary or not resource.uses_template: + if resource.source_file.is_binary: logger.debug("No Changes found in %s" % resource) return False deps = self.get_dependencies(resource) diff --git a/hyde/tests/ext/combine/script.1.js b/hyde/tests/ext/combine/script.1.js new file mode 100644 index 0000000..2e644cd --- /dev/null +++ b/hyde/tests/ext/combine/script.1.js @@ -0,0 +1 @@ +var a = 1 + 2; diff --git a/hyde/tests/ext/combine/script.2.js b/hyde/tests/ext/combine/script.2.js new file mode 100644 index 0000000..6e74290 --- /dev/null +++ b/hyde/tests/ext/combine/script.2.js @@ -0,0 +1 @@ +var b = a + 3; diff --git a/hyde/tests/ext/combine/script.3.js b/hyde/tests/ext/combine/script.3.js new file mode 100644 index 0000000..ef4b9de --- /dev/null +++ b/hyde/tests/ext/combine/script.3.js @@ -0,0 +1 @@ +var c = a + 5; diff --git a/hyde/tests/ext/test_combine.py b/hyde/tests/ext/test_combine.py new file mode 100644 index 0000000..2d3dbd8 --- /dev/null +++ b/hyde/tests/ext/test_combine.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +""" +Use nose +`$ pip install nose` +`$ nosetests` +""" +from hyde.fs import File, Folder +from hyde.model import Expando +from hyde.generator import Generator +from hyde.site import Site + +COMBINE_SOURCE = File(__file__).parent.child_folder('combine') +TEST_SITE = File(__file__).parent.parent.child_folder('_test') + + +class TestCombine(object): + + def setUp(self): + TEST_SITE.make() + TEST_SITE.parent.child_folder( + 'sites/test_jinja').copy_contents_to(TEST_SITE) + TEST_SITE.child_folder('content/media/js').make() + COMBINE_SOURCE.copy_contents_to(TEST_SITE.child('content/media/js')) + + def tearDown(self): + TEST_SITE.delete() + + def _test_combine(self, content): + s = Site(TEST_SITE) + s.config.plugins = [ + 'hyde.ext.plugins.meta.MetaPlugin', + 'hyde.ext.plugins.combine.CombinePlugin'] + source = TEST_SITE.child('content/media/js/script.js') + target = File(Folder(s.config.deploy_root_path).child('media/js/script.js')) + File(source).write(content) + + gen = Generator(s) + gen.generate_resource_at_path(source) + + assert target.exists + text = target.read_all() + return text, s + + def test_combine_top(self): + text, _ = self._test_combine(""" +--- +combine: + files: script.*.js + where: top +--- + +Last line""") + assert text == """var a = 1 + 2; +var b = a + 3; +var c = a + 5; +Last line""" + return + + def test_combine_bottom(self): + text, _ = self._test_combine(""" +--- +combine: + files: script.*.js + where: bottom +--- + +First line +""") + assert text == """First line +var a = 1 + 2; +var b = a + 3; +var c = a + 5;""" + return + + def test_combine_remove(self): + _, s = self._test_combine(""" +--- +combine: + files: script.*.js + remove: yes +--- + +First line""") + for i in range(1,4): + assert not File(Folder(s.config.deploy_root_path). + child('media/js/script.%d.js' % i)).exists