| @@ -1,133 +0,0 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | |||||
| Contains classes to combine files into one | |||||
| """ | |||||
| from fnmatch import fnmatch | |||||
| from hyde.plugin import Plugin | |||||
| import operator | |||||
| class CombinePlugin(Plugin): | |||||
| """ | |||||
| To use this combine, the following configuration should be added | |||||
| to meta data:: | |||||
| combine: | |||||
| sort: false #Optional. Defaults to true. | |||||
| root: content/media #Optional. Path must be relative to content folder - default current folder | |||||
| recurse: true #Optional. Default false. | |||||
| 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 | |||||
| # select site root | |||||
| try: | |||||
| root = self.site.content.node_from_relative_path(resource.meta.combine.root) | |||||
| except AttributeError: | |||||
| root = resource.node | |||||
| # select walker | |||||
| try: | |||||
| recurse = resource.meta.combine.recurse | |||||
| except AttributeError: | |||||
| recurse = False | |||||
| walker = root.walk_resources() if recurse else root.resources | |||||
| # Must we sort? | |||||
| try: | |||||
| sort = resource.meta.combine.sort | |||||
| except AttributeError: | |||||
| sort = True | |||||
| if sort: | |||||
| resources = sorted([r for r in walker if any(fnmatch(r.name, f) for f in files)], | |||||
| key=operator.attrgetter('name')) | |||||
| else: | |||||
| resources = [(f, r) for r in walker for f in files if fnmatch(r.name, f)] | |||||
| resources = [r[1] for f in files for r in resources if f in r] | |||||
| if not resources: | |||||
| self.logger.debug("No resources to combine for [%s]" % resource) | |||||
| return [] | |||||
| return resources | |||||
| 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]) | |||||
| @@ -7,6 +7,9 @@ from hyde.plugin import Plugin | |||||
| from fswrap import Folder | from fswrap import Folder | ||||
| from fnmatch import fnmatch | |||||
| import operator | |||||
| class FlattenerPlugin(Plugin): | class FlattenerPlugin(Plugin): | ||||
| """ | """ | ||||
| The plugin class for flattening nested folders. | The plugin class for flattening nested folders. | ||||
| @@ -43,3 +46,128 @@ class FlattenerPlugin(Plugin): | |||||
| for child in node.walk(): | for child in node.walk(): | ||||
| child.relative_deploy_path = target.path | child.relative_deploy_path = target.path | ||||
| class CombinePlugin(Plugin): | |||||
| """ | |||||
| To use this combine, the following configuration should be added | |||||
| to meta data:: | |||||
| combine: | |||||
| sort: false #Optional. Defaults to true. | |||||
| root: content/media #Optional. Path must be relative to content folder - default current folder | |||||
| recurse: true #Optional. Default false. | |||||
| 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 | |||||
| # select site root | |||||
| try: | |||||
| root = self.site.content.node_from_relative_path(resource.meta.combine.root) | |||||
| except AttributeError: | |||||
| root = resource.node | |||||
| # select walker | |||||
| try: | |||||
| recurse = resource.meta.combine.recurse | |||||
| except AttributeError: | |||||
| recurse = False | |||||
| walker = root.walk_resources() if recurse else root.resources | |||||
| # Must we sort? | |||||
| try: | |||||
| sort = resource.meta.combine.sort | |||||
| except AttributeError: | |||||
| sort = True | |||||
| if sort: | |||||
| resources = sorted([r for r in walker if any(fnmatch(r.name, f) for f in files)], | |||||
| key=operator.attrgetter('name')) | |||||
| else: | |||||
| resources = [(f, r) for r in walker for f in files if fnmatch(r.name, f)] | |||||
| resources = [r[1] for f in files for r in resources if f in r] | |||||
| if not resources: | |||||
| self.logger.debug("No resources to combine for [%s]" % resource) | |||||
| return [] | |||||
| return resources | |||||
| 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]) | |||||
| @@ -17,7 +17,7 @@ class CombineTester(object): | |||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.plugins = [ | s.config.plugins = [ | ||||
| 'hyde.ext.plugins.meta.MetaPlugin', | 'hyde.ext.plugins.meta.MetaPlugin', | ||||
| 'hyde.ext.plugins.combine.CombinePlugin'] | |||||
| 'hyde.ext.plugins.structure.CombinePlugin'] | |||||
| source = TEST_SITE.child('content/media/js/script.js') | source = TEST_SITE.child('content/media/js/script.js') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/js/script.js')) | target = File(Folder(s.config.deploy_root_path).child('media/js/script.js')) | ||||
| File(source).write(content) | File(source).write(content) | ||||