@@ -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 |