PageRenderTime 42ms CodeModel.GetById 31ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/hyde/ext/plugins/combine.py

http://github.com/hyde/hyde
Python | 134 lines | 76 code | 20 blank | 38 comment | 45 complexity | 8963813a9ff0a42affbcf78f86178e18 MD5 | raw file
  1# -*- coding: utf-8 -*-
  2"""
  3Contains classes to combine files into one
  4"""
  5
  6from fnmatch import fnmatch
  7
  8from hyde.model import Expando
  9from hyde.plugin import Plugin
 10import operator
 11
 12class CombinePlugin(Plugin):
 13    """
 14    To use this combine, the following configuration should be added
 15    to meta data::
 16         combine:
 17            sort: false #Optional. Defaults to true.
 18            root: content/media #Optional. Path must be relative to content folder - default current folder
 19            recurse: true #Optional. Default false.
 20            files:
 21                - ns1.*.js
 22                - ns2.*.js
 23            where: top
 24            remove: yes
 25
 26    `files` is a list of resources (or just a resource) that should be
 27    combined. Globbing is performed. `where` indicate where the
 28    combination should be done. This could be `top` or `bottom` of the
 29    file. `remove` tell if we should remove resources that have been
 30    combined into the resource.
 31    """
 32
 33    def __init__(self, site):
 34        super(CombinePlugin, self).__init__(site)
 35
 36    def _combined(self, resource):
 37        """
 38        Return the list of resources to combine to build this one.
 39        """
 40        try:
 41            config = resource.meta.combine
 42        except AttributeError:
 43            return []    # Not a combined resource
 44        try:
 45            files = config.files
 46        except AttributeError:
 47            raise AttributeError("No resources to combine for [%s]" % resource)
 48        if type(files) is str:
 49            files = [ files ]
 50
 51        # Grab resources to combine
 52
 53        # select site root
 54        try:
 55            root = self.site.content.node_from_relative_path(resource.meta.combine.root)
 56        except AttributeError:
 57            root = resource.node
 58
 59        # select walker
 60        try:
 61            recurse = resource.meta.combine.recurse
 62        except AttributeError:
 63            recurse = False
 64
 65        walker = root.walk_resources() if recurse else root.resources
 66
 67        # Must we sort?
 68        try:
 69            sort = resource.meta.combine.sort
 70        except AttributeError:
 71            sort = True
 72
 73        if sort:
 74            resources = sorted([r for r in walker if any(fnmatch(r.name, f) for f in files)],
 75                                                    key=operator.attrgetter('name'))
 76        else:
 77            resources = [(f, r) for r in walker for f in files if fnmatch(r.name, f)]
 78            resources = [r[1] for f in files for r in resources if f in r]
 79
 80        if not resources:
 81            self.logger.debug("No resources to combine for [%s]" % resource)
 82            return []
 83
 84        return resources
 85
 86    def begin_site(self):
 87        """
 88        Initialize the plugin and search for the combined resources
 89        """
 90        for node in self.site.content.walk():
 91            for resource in node.resources:
 92                resources = self._combined(resource)
 93                if not resources:
 94                    continue
 95
 96                # Build depends
 97                if not hasattr(resource, 'depends'):
 98                    resource.depends = []
 99                resource.depends.extend(
100                    [r.relative_path for r in resources
101                     if r.relative_path not in resource.depends])
102
103                # Remove combined resources if needed
104                if hasattr(resource.meta.combine, "remove") and \
105                        resource.meta.combine.remove:
106                    for r in resources:
107                        self.logger.debug(
108                            "Resource [%s] removed because combined" % r)
109                        r.is_processable = False
110
111    def begin_text_resource(self, resource, text):
112        """
113        When generating a resource, add combined file if needed.
114        """
115        resources = self._combined(resource)
116        if not resources:
117            return
118
119        where = "bottom"
120        try:
121            where = resource.meta.combine.where
122        except AttributeError:
123            pass
124
125        if where not in [ "top", "bottom" ]:
126            raise ValueError("%r should be either `top` or `bottom`" % where)
127
128        self.logger.debug(
129            "Combining %d resources for [%s]" % (len(resources),
130                                                 resource))
131        if where == "top":
132            return "".join([r.source.read_all() for r in resources] + [text])
133        else:
134            return "".join([text] + [r.source.read_all() for r in resources])