| @@ -1,3 +1,4 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | """ | ||||
| Autoextend css plugin | Autoextend css plugin | ||||
| """ | """ | ||||
| @@ -29,7 +30,7 @@ class AutoExtendPlugin(Plugin): | |||||
| except AttributeError: | except AttributeError: | ||||
| pass | pass | ||||
| if layout: | if layout: | ||||
| extends_pattern = self.template.extends_pattern | |||||
| extends_pattern = self.template.patterns['extends'] | |||||
| if not re.search(extends_pattern, text): | if not re.search(extends_pattern, text): | ||||
| extended_text = self.template.get_extends_statement(layout) | 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): | def __init__(self, site): | ||||
| super(BlockdownPlugin, self).__init__(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[===========]) | Replace open pattern (default:===[====]blockname[===========]) | ||||
| with | with | ||||
| @@ -37,15 +42,4 @@ class BlockdownPlugin(Plugin): | |||||
| with | with | ||||
| {% block blockname %} or equivalent | {% 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 | 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. | Contains classes and utilities related to meta data in hyde. | ||||
| """ | """ | ||||
| @@ -1,3 +1,4 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | """ | ||||
| Contains classes and utilities related to sortin | Contains classes and utilities related to sortin | ||||
| resources and nodes in hyde. | 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 | Jinja template utilties | ||||
| """ | """ | ||||
| @@ -296,6 +297,45 @@ class Jinja2Template(Template): | |||||
| """ | """ | ||||
| return TemplateError | 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): | def render(self, text, context): | ||||
| """ | """ | ||||
| Renders the given resource using the context | Renders the given resource using the context | ||||
| @@ -1,3 +1,4 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | """ | ||||
| The generator class and related utility functions. | The generator class and related utility functions. | ||||
| """ | """ | ||||
| @@ -1,3 +1,4 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | """ | ||||
| Generic loader of extensions (plugins & templates) | Generic loader of extensions (plugins & templates) | ||||
| """ | """ | ||||
| @@ -1,8 +1,8 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | """ | ||||
| Contains data structures and utilities for hyde. | Contains data structures and utilities for hyde. | ||||
| """ | """ | ||||
| class Expando(object): | class Expando(object): | ||||
| """ | """ | ||||
| A generic expando class that creates attributes from | A generic expando class that creates attributes from | ||||
| @@ -1,3 +1,4 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | """ | ||||
| Contains classes and utilities for serving a site | Contains classes and utilities for serving a site | ||||
| generated from hyde. | generated from hyde. | ||||
| @@ -6,6 +6,8 @@ Abstract classes and utilities for template engines | |||||
| from hyde.exceptions import HydeException | from hyde.exceptions import HydeException | ||||
| from hyde.util import getLoggerWithNullHandler | from hyde.util import getLoggerWithNullHandler | ||||
| import abc | |||||
| class HtmlWrap(object): | class HtmlWrap(object): | ||||
| """ | """ | ||||
| A wrapper class for raw html. | A wrapper class for raw html. | ||||
| @@ -38,10 +40,13 @@ class Template(object): | |||||
| the following interface must be implemented. | the following interface must be implemented. | ||||
| """ | """ | ||||
| __metaclass__ = abc.ABCMeta | |||||
| def __init__(self, sitepath): | def __init__(self, sitepath): | ||||
| self.sitepath = sitepath | self.sitepath = sitepath | ||||
| self.logger = getLoggerWithNullHandler(self.__class__.__name__) | self.logger = getLoggerWithNullHandler(self.__class__.__name__) | ||||
| @abc.abstractmethod | |||||
| def configure(self, site, engine): | def configure(self, site, engine): | ||||
| """ | """ | ||||
| The site object should contain a config attribute. The config object is | 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 | context object that is populated with the appropriate variables for the given | ||||
| path. | path. | ||||
| """ | """ | ||||
| abstract | |||||
| return | |||||
| def get_dependencies(self, text): | def get_dependencies(self, text): | ||||
| """ | """ | ||||
| @@ -73,77 +78,55 @@ class Template(object): | |||||
| """ | """ | ||||
| return None | return None | ||||
| @abc.abstractmethod | |||||
| def render(self, text, context): | def render(self, text, context): | ||||
| """ | """ | ||||
| Given the text, and the context, this function must return the | Given the text, and the context, this function must return the | ||||
| rendered string. | rendered string. | ||||
| """ | """ | ||||
| abstract | |||||
| return '' | |||||
| @property | |||||
| @abc.abstractproperty | |||||
| def exception_class(self): | def exception_class(self): | ||||
| return HydeException | 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): | def get_include_statement(self, path_to_include): | ||||
| """ | """ | ||||
| Returns an include statement for the current template, | Returns an include statement for the current template, | ||||
| given the path to include. | 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 | @staticmethod | ||||
| def find_template(site): | 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 | |||||