From 27bb87d233af54bc33d53a45757e0ce54939954c Mon Sep 17 00:00:00 2001 From: Lakshmi Vyasarajan Date: Fri, 7 Jan 2011 10:03:59 +0530 Subject: [PATCH] added sorter plugin --- hyde/ext/plugins/sorter.py | 106 +++++++++++++++++++++++++++++++ hyde/site.py | 29 --------- hyde/tests/ext/test_sorter.py | 116 ++++++++++++++++++++++++++++++++++ hyde/tests/test_site.py | 38 ----------- 4 files changed, 222 insertions(+), 67 deletions(-) create mode 100644 hyde/ext/plugins/sorter.py create mode 100644 hyde/tests/ext/test_sorter.py diff --git a/hyde/ext/plugins/sorter.py b/hyde/ext/plugins/sorter.py new file mode 100644 index 0000000..f9d63f5 --- /dev/null +++ b/hyde/ext/plugins/sorter.py @@ -0,0 +1,106 @@ +""" +Contains classes and utilities related to sortin +resources and nodes in hyde. +""" +import re +from hyde.plugin import Plugin +from hyde.site import Node, Resource + +from functools import partial +from itertools import ifilter +from operator import attrgetter + +import logging +from logging import NullHandler +logger = logging.getLogger('hyde.engine') +logger.addHandler(NullHandler()) + + +def filter_method(item, settings=None): + """ + Returns true if all the filters in the + given settings evaluate to True. + """ + + all_match = True + if settings and hasattr(settings, 'filters'): + filters = settings.filters + for field, value in filters.__dict__.items(): + try: + res = attrgetter(field)(item) + except: + res = None + if res != value: + all_match = False + break + return all_match + +def sort_method(node, settings=None): + """ + Sorts the resources in the given node based on the + given settings. + """ + attr = 'name' + if settings and hasattr(settings, 'attr') and settings.attr: + attr = settings.attr + filter_ = partial(filter_method, settings=settings) + resources = ifilter(filter_, node.walk_resources()) + return sorted(resources, key=attrgetter(attr)) + +def make_method(method_name, method_): + def method__(self): + return method_(self) + method__.__name__ = method_name + return method__ + +def add_method(obj, method_name, method_, settings): + m = make_method(method_name, partial(method_, settings=settings)) + setattr(obj, method_name, m) + + +class SorterPlugin(Plugin): + """ + Sorter plugin for hyde. Adds the ability to do + sophisticated sorting by expanding the site objects + to support prebuilt sorting methods. These methods + can be used in the templates directly. + + Configuration example + --------------------- + #yaml + + sorter: + kind: + # Sorts by this attribute name + # Uses `attrgetter` on the resource object + attr: source_file.kind + + # The filters to be used before sorting + # This can be used to remove all the items + # that do not apply. For example, + # filtering non html content + filters: + source_file.kind: html + is_processable: True + meta.is_listable: True + """ + + def __init__(self, site): + super(SorterPlugin, self).__init__(site) + + def begin_site(self): + """ + Initialize plugin. Add a sort and match method + for every configuration mentioned in site settings + """ + + config = self.site.config + if not hasattr(config, 'sorter'): + return + + for name, settings in config.sorter.__dict__.items(): + logger.info("Adding sort methods for [%s]" % name) + sort_method_name = 'walk_resources_sorted_by_%s' % name + add_method(Node, sort_method_name, sort_method, settings) + match_method_name = 'is_%s' % name + add_method(Resource, match_method_name, filter_method, settings) diff --git a/hyde/site.py b/hyde/site.py index e7511f2..f6bfaf8 100644 --- a/hyde/site.py +++ b/hyde/site.py @@ -169,35 +169,6 @@ class Node(Processable): for resource in node.resources: yield resource - def walk_resources_sorted(self, attr='name', reverse=False, default=None): - """ - Walks the resources in this hierarchy sorted by - the given key. - """ - from operator import attrgetter - - def safe_attrgetter(*items): - f = attrgetter(*items) - - def wrapper(obj): - res = None - try: - res = f(obj) - except: - logger.error("Cannot get the requested items[%s]" - " from the object [%s]" % - (items, obj)) - res = default - return res - - return wrapper - - sorted_resources = sorted(self.walk_resources(), - key=safe_attrgetter(attr), - reverse=reverse) - for resource in sorted_resources: - yield resource - @property def relative_path(self): """ diff --git a/hyde/tests/ext/test_sorter.py b/hyde/tests/ext/test_sorter.py new file mode 100644 index 0000000..805d526 --- /dev/null +++ b/hyde/tests/ext/test_sorter.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +""" +Use nose +`$ pip install nose` +`$ nosetests` +""" +from hyde.ext.plugins.sorter import SorterPlugin +from hyde.fs import File, Folder +from hyde.generator import Generator +from hyde.site import Site +from hyde.model import Config, Expando +import yaml + +TEST_SITE = File(__file__).parent.parent.child_folder('_test') + + +class TestMeta(object): + + def setUp(self): + TEST_SITE.make() + TEST_SITE.parent.child_folder( + 'sites/test_jinja').copy_contents_to(TEST_SITE) + + def tearDown(self): + TEST_SITE.delete() + + def test_walk_resources_sorted(self): + s = Site(TEST_SITE) + s.load() + s.config.plugins = ['hyde.ext.sorter.SorterPlugin'] + s.config.sorter = Expando(dict(kind=dict(attr='source_file.kind'))) + + SorterPlugin(s).begin_site() + + assert hasattr(s.content, 'walk_resources_sorted_by_kind') + expected = ["404.html", + "about.html", + "apple-touch-icon.png", + "merry-christmas.html", + "crossdomain.xml", + "favicon.ico", + "robots.txt", + "site.css" + ] + + pages = [page.name for page in + s.content.walk_resources_sorted_by_kind()] + + + assert pages == sorted(expected, key=lambda f: File(f).kind) + + def test_walk_resources_sorted_with_filters(self): + s = Site(TEST_SITE) + cfg = """ + plugins: + - hyde.ext.sorter.SorterPlugin + sorter: + kind2: + filters: + source_file.kind: html + """ + s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) + s.load() + SorterPlugin(s).begin_site() + + assert hasattr(s.content, 'walk_resources_sorted_by_kind2') + expected = ["404.html", + "about.html", + "merry-christmas.html" + ] + + pages = [page.name for page in s.content.walk_resources_sorted_by_kind2()] + + assert pages == sorted(expected) + + def test_walk_resources_sorted_using_generator(self): + s = Site(TEST_SITE) + cfg = """ + meta: + time: !!timestamp 2010-10-23 + title: NahNahNah + plugins: + - hyde.ext.plugins.meta.MetaPlugin + - hyde.ext.plugins.sorter.SorterPlugin + sorter: + time: + attr: meta.time + filters: + source_file.kind: html + """ + s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) + text = """ + --- + time: !!timestamp 2010-12-31 + title: YayYayYay + --- + {% extends "base.html" %} + + {% block main %} + {% set latest = site.content.walk_resources_sorted_by_time()|reverse|first %} + {{ latest.meta.title }} + {% endblock %} + """ + + about2 = File(TEST_SITE.child('content/about2.html')) + about2.write(text) + gen = Generator(s) + gen.generate_all() + + from pyquery import PyQuery + target = File(Folder(s.config.deploy_root_path).child('about2.html')) + text = target.read_all() + q = PyQuery(text) + + assert q('span.latest').text() == 'YayYayYay' + diff --git a/hyde/tests/test_site.py b/hyde/tests/test_site.py index 8a00119..75e62c5 100644 --- a/hyde/tests/test_site.py +++ b/hyde/tests/test_site.py @@ -103,44 +103,6 @@ def test_walk_resources(): expected.sort() assert pages == expected -def test_walk_resources_sorted(): - s = Site(TEST_SITE_ROOT) - s.load() - pages = [page.name for page in s.content.walk_resources_sorted(attr='name')] - expected = ["404.html", - "about.html", - "apple-touch-icon.png", - "merry-christmas.html", - "crossdomain.xml", - "favicon.ico", - "robots.txt", - "site.css" - ] - assert pages == sorted(expected) - pages = [page.name for page in - s.content.walk_resources_sorted(attr='name', reverse=True)] - assert pages == sorted(expected, reverse=True) - pages = [page.name for page in - s.content.walk_resources_sorted(attr='source_file.kind')] - assert pages == sorted(expected, key=lambda f: File(f).kind) - - from datetime import datetime, timedelta - - d = {} - t = datetime.now() - for name in expected: - d[name] = t - t = t - timedelta(days=1) - - for page in s.content.walk_resources(): - page.meta = Expando(dict(time=d[page.name])) - - pages = [page.name for page in - s.content.walk_resources_sorted(attr='meta.time', reverse=True)] - assert pages == expected - - - def test_contains_resource(): s = Site(TEST_SITE_ROOT) s.load()