| @@ -1,3 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Autoextend css plugin | |||
| """ | |||
| @@ -29,7 +30,7 @@ class AutoExtendPlugin(Plugin): | |||
| except AttributeError: | |||
| pass | |||
| if layout: | |||
| extends_pattern = self.template.extends_pattern | |||
| extends_pattern = self.template.patterns['extends'] | |||
| if not re.search(extends_pattern, text): | |||
| extended_text = self.template.get_extends_statement(layout) | |||
| @@ -1,34 +1,39 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Blockdown css plugin | |||
| Blockdown plugin | |||
| """ | |||
| from hyde.plugin import Plugin | |||
| from hyde.fs import File, Folder | |||
| from hyde.ext.plugins.texty import TextyPlugin | |||
| import re | |||
| from functools import partial | |||
| class BlockdownPlugin(Plugin): | |||
| class BlockdownPlugin(TextyPlugin): | |||
| """ | |||
| The plugin class for less css | |||
| The plugin class for block text replacement. | |||
| """ | |||
| def __init__(self, site): | |||
| super(BlockdownPlugin, self).__init__(site) | |||
| try: | |||
| self.open_pattern = site.config.blockdown.open_pattern | |||
| except AttributeError: | |||
| self.open_pattern = '^\s*===+\s*([A-Za-z0-9_\-.]+)\s*=*\s*$' | |||
| try: | |||
| self.close_pattern = site.config.blockdown.close_pattern | |||
| except AttributeError: | |||
| self.close_pattern = '^\s*===+\s*/+\s*=*/*([A-Za-z0-9_\-.]*)[\s=/]*$' | |||
| @property | |||
| def tag_name(self): | |||
| """ | |||
| The block tag. | |||
| """ | |||
| return 'block' | |||
| def template_loaded(self, template): | |||
| self.template = template | |||
| @property | |||
| def default_open_pattern(self): | |||
| """ | |||
| The default pattern for block open text. | |||
| """ | |||
| return '^\s*===+\s*([A-Za-z0-9_\-\.]+)\s*=*\s*$' | |||
| @property | |||
| def default_close_pattern(self): | |||
| """ | |||
| The default pattern for block close text. | |||
| """ | |||
| return '^\s*===+\s*/+\s*=*/*([A-Za-z0-9_\-\.]*)[\s=/]*$' | |||
| def begin_text_resource(self, resource, text): | |||
| def text_to_tag(self, match, start=True): | |||
| """ | |||
| Replace open pattern (default:===[====]blockname[===========]) | |||
| with | |||
| @@ -37,15 +42,4 @@ class BlockdownPlugin(Plugin): | |||
| with | |||
| {% block blockname %} or equivalent | |||
| """ | |||
| blocktag_open = re.compile(self.open_pattern, re.MULTILINE) | |||
| blocktag_close = re.compile(self.close_pattern, re.MULTILINE) | |||
| def blockdown_to_block(match, start_block=True): | |||
| if not match.lastindex: | |||
| return '' | |||
| block_name = match.groups(1)[0] | |||
| return (self.template.get_block_open_statement(block_name) | |||
| if start_block | |||
| else self.template.get_block_close_statement(block_name)) | |||
| text = blocktag_open.sub(blockdown_to_block, text) | |||
| text = blocktag_close.sub(partial(blockdown_to_block, start_block=False), text) | |||
| return text | |||
| return super(BlockdownPlugin, self).text_to_tag(match, start) | |||
| @@ -1,3 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Less css plugin | |||
| """ | |||
| @@ -0,0 +1,47 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Markings plugin | |||
| """ | |||
| from hyde.ext.plugins.texty import TextyPlugin | |||
| class MarkingsPlugin(TextyPlugin): | |||
| """ | |||
| The plugin class for mark text replacement. | |||
| """ | |||
| def __init__(self, site): | |||
| super(MarkingsPlugin, self).__init__(site) | |||
| @property | |||
| def tag_name(self): | |||
| """ | |||
| The mark tag. | |||
| """ | |||
| return 'mark' | |||
| @property | |||
| def default_open_pattern(self): | |||
| """ | |||
| The default pattern for mark open text. | |||
| """ | |||
| return u'^§§+\s*([A-Za-z0-9_\-]+)\s*$' | |||
| @property | |||
| def default_close_pattern(self): | |||
| """ | |||
| The default pattern for mark close text. | |||
| """ | |||
| return u'^§§+\s*([A-Za-z0-9_\-]*)\s*\.\s*$' | |||
| def text_to_tag(self, match, start=True): | |||
| """ | |||
| Replace open pattern (default:§§ CSS) | |||
| with | |||
| {% mark CSS %} or equivalent and | |||
| Replace close pattern (default: §§ CSS.) | |||
| with | |||
| {% endmark %} or equivalent | |||
| """ | |||
| text = super(MarkingsPlugin, self).text_to_tag(match, start) | |||
| print text | |||
| return text | |||
| @@ -1,3 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Contains classes and utilities related to meta data in hyde. | |||
| """ | |||
| @@ -1,3 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Contains classes and utilities related to sortin | |||
| resources and nodes in hyde. | |||
| @@ -0,0 +1,98 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Provides classes and utilities that allow text | |||
| to be replaced before the templates are | |||
| rendered. | |||
| """ | |||
| from hyde.plugin import Plugin | |||
| import abc | |||
| import re | |||
| from functools import partial | |||
| class TextyPlugin(Plugin): | |||
| """ | |||
| Base class for text preprocessing plugins. | |||
| Plugins that desire to provide syntactic sugar for | |||
| commonly used hyde functions for various templates | |||
| can inherit from this class. | |||
| """ | |||
| __metaclass__ = abc.ABCMeta | |||
| def __init__(self, site): | |||
| super(TextyPlugin, self).__init__(site) | |||
| self.open_pattern = self.default_open_pattern | |||
| self.close_pattern = self.default_close_pattern | |||
| self.template = None | |||
| config = getattr(site.config, self.plugin_name, None) | |||
| if config and hasattr(config, 'open_pattern'): | |||
| self.open_pattern = config.open_pattern | |||
| if config and hasattr(config, 'close_pattern'): | |||
| self.open_pattern = config.close_pattern | |||
| @property | |||
| def plugin_name(self): | |||
| """ | |||
| The name of the plugin. Makes an intelligent guess. | |||
| """ | |||
| return self.__class__.__name__.replace('Plugin', '').lower() | |||
| @abc.abstractproperty | |||
| def tag_name(self): | |||
| """ | |||
| The tag that this plugin tries add syntactic sugar for. | |||
| """ | |||
| return self.plugin_name | |||
| @abc.abstractproperty | |||
| def default_open_pattern(self): | |||
| """ | |||
| The default pattern for opening the tag. | |||
| """ | |||
| return None | |||
| @abc.abstractproperty | |||
| def default_close_pattern(self): | |||
| """ | |||
| The default pattern for closing the tag. | |||
| """ | |||
| return None | |||
| def template_loaded(self, template): | |||
| """ | |||
| Handles the template loaded event to keep | |||
| a reference to the template object. | |||
| """ | |||
| self.template = template | |||
| @abc.abstractmethod | |||
| def text_to_tag(self, match, start=True): | |||
| """ | |||
| Replaces the matched text with tag statement | |||
| given by the template. | |||
| """ | |||
| if not match.lastindex: | |||
| return '' | |||
| params = match.groups(1)[0] | |||
| return (self.template.get_open_tag(self.tag_name, params) | |||
| if start | |||
| else self.template.get_close_tag(self.tag_name, params)) | |||
| def begin_text_resource(self, resource, text): | |||
| """ | |||
| Replace a text base pattern with a template statement. | |||
| """ | |||
| text_open = re.compile(self.open_pattern, re.MULTILINE) | |||
| text_close = re.compile(self.close_pattern, re.MULTILINE) | |||
| text = text_open.sub(self.text_to_tag, text) | |||
| text = text_close.sub( | |||
| partial(self.text_to_tag, start=False), text) | |||
| return text | |||
| @@ -1,3 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Jinja template utilties | |||
| """ | |||
| @@ -296,6 +297,45 @@ class Jinja2Template(Template): | |||
| """ | |||
| return TemplateError | |||
| @property | |||
| def patterns(self): | |||
| """ | |||
| The pattern for matching selected template statements. | |||
| """ | |||
| return { | |||
| "block_open": '\s*\{\%\s*block\s*([^\s]+)\s*\%\}', | |||
| "block_close": '\s*\{\%\s*endblock\s*([^\s]*)\s*\%\}', | |||
| "include": '\s*\{\%\s*include\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}', | |||
| "extends": '\s*\{\%\s*extends\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}' | |||
| } | |||
| def get_include_statement(self, path_to_include): | |||
| """ | |||
| Returns an include statement for the current template, | |||
| given the path to include. | |||
| """ | |||
| return '{%% include \'%s\' %%}' % path_to_include | |||
| def get_extends_statement(self, path_to_extend): | |||
| """ | |||
| Returns an extends statement for the current template, | |||
| given the path to extend. | |||
| """ | |||
| return '{%% extends \'%s\' %%}' % path_to_extend | |||
| def get_open_tag(self, tag, params): | |||
| """ | |||
| Returns an open tag statement. | |||
| """ | |||
| return '{%% %s %s %%}' % (tag, params) | |||
| def get_close_tag(self, tag, params): | |||
| """ | |||
| Returns an open tag statement. | |||
| """ | |||
| return '{%% end%s %%}' % tag | |||
| def render(self, text, context): | |||
| """ | |||
| Renders the given resource using the context | |||
| @@ -1,3 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| The generator class and related utility functions. | |||
| """ | |||
| @@ -1,3 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Generic loader of extensions (plugins & templates) | |||
| """ | |||
| @@ -1,8 +1,8 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Contains data structures and utilities for hyde. | |||
| """ | |||
| class Expando(object): | |||
| """ | |||
| A generic expando class that creates attributes from | |||
| @@ -1,3 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Contains classes and utilities for serving a site | |||
| generated from hyde. | |||
| @@ -6,6 +6,8 @@ Abstract classes and utilities for template engines | |||
| from hyde.exceptions import HydeException | |||
| from hyde.util import getLoggerWithNullHandler | |||
| import abc | |||
| class HtmlWrap(object): | |||
| """ | |||
| A wrapper class for raw html. | |||
| @@ -38,10 +40,13 @@ class Template(object): | |||
| the following interface must be implemented. | |||
| """ | |||
| __metaclass__ = abc.ABCMeta | |||
| def __init__(self, sitepath): | |||
| self.sitepath = sitepath | |||
| self.logger = getLoggerWithNullHandler(self.__class__.__name__) | |||
| @abc.abstractmethod | |||
| def configure(self, site, engine): | |||
| """ | |||
| The site object should contain a config attribute. The config object is | |||
| @@ -64,7 +69,7 @@ class Template(object): | |||
| context object that is populated with the appropriate variables for the given | |||
| path. | |||
| """ | |||
| abstract | |||
| return | |||
| def get_dependencies(self, text): | |||
| """ | |||
| @@ -73,77 +78,55 @@ class Template(object): | |||
| """ | |||
| return None | |||
| @abc.abstractmethod | |||
| def render(self, text, context): | |||
| """ | |||
| Given the text, and the context, this function must return the | |||
| rendered string. | |||
| """ | |||
| abstract | |||
| return '' | |||
| @property | |||
| @abc.abstractproperty | |||
| def exception_class(self): | |||
| return HydeException | |||
| @property | |||
| def include_pattern(self): | |||
| @abc.abstractproperty | |||
| def patterns(self): | |||
| """ | |||
| The pattern for matching include statements | |||
| Patterns for matching selected template statements. | |||
| """ | |||
| return {} | |||
| return '\s*\{\%\s*include\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}' | |||
| @abc.abstractmethod | |||
| def get_include_statement(self, path_to_include): | |||
| """ | |||
| Returns an include statement for the current template, | |||
| given the path to include. | |||
| """ | |||
| return "{%% include '%s' %%}" % path_to_include | |||
| @property | |||
| def block_open_pattern(self): | |||
| """ | |||
| The pattern for matching include statements | |||
| """ | |||
| return '\s*\{\%\s*block\s*([^\s]+)\s*\%\}' | |||
| @property | |||
| def block_close_pattern(self): | |||
| """ | |||
| The pattern for matching include statements | |||
| """ | |||
| return '\s*\{\%\s*endblock\s*([^\s]*)\s*\%\}' | |||
| return '{%% include \'%s\' %%}' % path_to_include | |||
| def get_block_open_statement(self, block_name): | |||
| """ | |||
| Returns a open block statement for the current template, | |||
| given the block name. | |||
| """ | |||
| return "{%% block %s %%}" % block_name | |||
| def get_block_close_statement(self, block_name): | |||
| @abc.abstractmethod | |||
| def get_extends_statement(self, path_to_extend): | |||
| """ | |||
| Returns a close block statement for the current template, | |||
| given the block name. | |||
| Returns an extends statement for the current template, | |||
| given the path to extend. | |||
| """ | |||
| return "{%% endblock %s %%}" % block_name | |||
| return '{%% extends \'%s\' %%}' % path_to_extend | |||
| @property | |||
| def extends_pattern(self): | |||
| @abc.abstractmethod | |||
| def get_open_tag(self, tag, params): | |||
| """ | |||
| The pattern for matching include statements | |||
| Returns an open tag statement. | |||
| """ | |||
| return '{%% %s %s %%}' % (tag, params) | |||
| return '\s*\{\%\s*extends\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}' | |||
| def get_extends_statement(self, path_to_extend): | |||
| @abc.abstractmethod | |||
| def get_close_tag(self, tag, params): | |||
| """ | |||
| Returns an extends statement for the current template, | |||
| given the path to extend. | |||
| Returns an open tag statement. | |||
| """ | |||
| return "{%% extends '%s' %%}" % path_to_extend | |||
| return '{%% end%s %%}' % tag | |||
| @staticmethod | |||
| def find_template(site): | |||
| @@ -0,0 +1,69 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Use nose | |||
| `$ pip install nose` | |||
| `$ nosetests` | |||
| """ | |||
| from hyde.fs import File, Folder | |||
| from hyde.generator import Generator | |||
| from hyde.site import Site | |||
| from pyquery import PyQuery | |||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | |||
| class TestMarkings(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_markings(self): | |||
| text = u""" | |||
| === | |||
| is_processable: False | |||
| === | |||
| {% filter markdown|typogrify %} | |||
| §§ heading | |||
| This is a heading | |||
| ================= | |||
| §§ heading. | |||
| §§ content | |||
| Hyde & Jinja. | |||
| §§ . | |||
| {% endfilter %} | |||
| """ | |||
| text2 = """ | |||
| {% refer to "inc.md" as inc %} | |||
| {% filter markdown|typogrify %} | |||
| {{ inc.heading }} | |||
| {{ inc.content }} | |||
| {% endfilter %} | |||
| """ | |||
| site = Site(TEST_SITE) | |||
| site.config.plugins = [ | |||
| 'hyde.ext.plugins.meta.MetaPlugin', | |||
| 'hyde.ext.plugins.markings.MarkingsPlugin'] | |||
| inc = File(TEST_SITE.child('content/inc.md')) | |||
| inc.write(text) | |||
| site.load() | |||
| gen = Generator(site) | |||
| gen.load_template_if_needed() | |||
| template = gen.template | |||
| html = template.render(text2, {}).strip() | |||
| assert html | |||
| q = PyQuery(html) | |||
| assert "is_processable" not in html | |||
| assert "This is a" in q("h1").text() | |||
| assert "heading" in q("h1").text() | |||
| assert q(".amp").length == 1 | |||
| assert "mark" not in html | |||
| assert "reference" not in html | |||